def test_unpack_metadata_statement_uri(): s = signer[OA['sunet']] req = MetadataStatement(issuer='https://example.org/op') # Not intermediate ms = s.create_signed_metadata_statement(req, 'discovery', single=True) jb = FSJWKSBundle('', None, 'fo_jwks', key_conv={ 'to': quote_plus, 'from': unquote_plus }) mds = MetaDataStore('msd') op = Operator(jwks_bundle=jb) op.httpcli = MockHTTPClient(mds) res = op.unpack_metadata_statement(jwt_ms=ms) assert len(res.parsed_statement) == 3 loel = op.evaluate_metadata_statement(res.result) assert len(loel) == 3 assert set([l.fo for l in loel]) == { 'https://swamid.sunet.se', 'https://edugain.com', 'https://www.feide.no' }
def make_ms(desc, leaf, operator, sup=None): """ Construct a signed metadata statement :param desc: A description of who wants who to signed what. represented as a dictionary containing: 'request', 'requester', 'signer' and 'signer_add'. :param leaf: if the requester is the entity operator/agent :param operator: A dictionary containing Operator instance as values. :param ms: Metadata statements to be added, dict. The values are signed MetadataStatements. :param ms_uris: Metadata Statement URIs to be added. Note that ms and ms_uris can not be present at the same time. It can be one of them or none. :return: A dictionary with the FO ID as key and the signed metadata statement as value. """ req = MetadataStatement(**desc['request']) _requester = operator[desc['requester']] req['signing_keys'] = _requester.signing_keys_as_jwks() _signer = operator[desc['signer']] if sup is None: sup = {} _fo = _signer.iss try: _ms = sup['ms'] except KeyError: pass else: req['metadata_statements'] = dict(_ms.items()) if len(_ms): _fo = list(_ms.keys())[0] else: _fo = '' try: _ms_uri = sup['ms_uri'] except KeyError: pass else: req['metadata_statement_uris'] = dict(_ms_uri.items()) if len(_ms_uri): _fo = list(_ms_uri.keys())[0] else: _fo = '' req.update(desc['signer_add']) if leaf: jwt_args = {'aud': [_requester.iss]} else: jwt_args = {} ms = _signer.pack_metadata_statement(req, jwt_args=jwt_args) return {_fo: ms}
def test_create_client_metadata_statement(): ms = MetadataStatement(signing_keys=KEYS['org']['jwks']) ms_jwt = ms.to_jwt(KEYS['fo']['keyjar'].get_signing_key('rsa')) cms = ClientMetadataStatement( metadata_statements=Message(**{ISSUER['org']: ms_jwt}), contacts=['*****@*****.**']) assert cms
def test_request_signed_by_signing_keys(): kj = KeyJar() kj.issuer_keys['abc'] = KEYJAR.issuer_keys[''] msreq = MetadataStatement(signing_keys=JWKS) smsreq = request_signed_by_signing_keys(kj, msreq, 'abc', 3600) assert smsreq res = verify_request_signed_by_signing_keys(smsreq) assert set(res.keys()) == {'ms', 'iss'} assert res['iss'] == 'abc'
def test_pack_metadata_statement_other_alg(): _keyjar = build_keyjar(KEYDEFS)[1] op = Operator(keyjar=_keyjar, iss='https://example.com/') req = MetadataStatement(issuer='https://example.org/op') sms = op.pack_metadata_statement(req, alg='ES256') assert sms # Should be a signed JWT _jwt = factory(sms) _body = json.loads(as_unicode(_jwt.jwt.part[1])) assert _body['iss'] == 'https://example.com/' # verify signature r = _jwt.verify_compact(sms, _keyjar.get_signing_key()) assert r
def test_get_metadata_statement(): jb = JWKSBundle('') for iss in ['https://example.org/', 'https://example.com/']: jb[iss] = build_keyjar(KEYDEFS)[1] op = Operator(keyjar=jb['https://example.com/'], iss='https://example.com/') req = MetadataStatement(foo='bar') sms = op.pack_metadata_statement(req, alg='RS256') sms_dir = {'https://example.com': sms} req['metadata_statements'] = Message(**sms_dir) ent = FederationEntity(None, fo_bundle=jb) loe = ent.get_metadata_statement(req) assert loe
def ace(self, req, fos, context): """ Add signing keys, create metadata statement and extend request. :param req: Request :param fos: List of Federation Operator IDs :param context: One of :py:data:`fedoidc.CONTEXTS` """ _cms = MetadataStatement() _cms.update(req) _cms = self.add_signing_keys(_cms) sms = self.signer.create_signed_metadata_statement(_cms, context, fos=fos) self.extend_with_ms(req, sms)
def test_pack_metadata_statement(): jb = FSJWKSBundle('', None, 'fo_jwks', key_conv={'to': quote_plus, 'from': unquote_plus}) _keyjar = build_keyjar(KEYDEFS)[1] op = Operator(keyjar=_keyjar, jwks_bundle=jb, iss='https://example.com/') req = MetadataStatement(issuer='https://example.org/op') sms = op.pack_metadata_statement(req) assert sms # Should be a signed JWT _jwt = factory(sms) assert _jwt assert _jwt.jwt.headers['alg'] == 'RS256' _body = json.loads(as_unicode(_jwt.jwt.part[1])) assert _body['iss'] == op.iss assert _body['issuer'] == 'https://example.org/op' # verify signature r = _jwt.verify_compact(sms, _keyjar.get_signing_key()) assert r
def test_ace(): jb = JWKSBundle('') for iss in ['https://example.org/', 'https://example.com/']: jb[iss] = build_keyjar(KEYDEFS)[1] kj = build_keyjar(KEYDEFS)[1] sign_serv = InternalSigningService('https://signer.example.com', signing_keys=kj) signer = Signer(sign_serv) signer.metadata_statements['response'] = { 'https://example.org/': 'https://example.org/sms1' } ent = FederationEntity(None, keyjar=kj, signer=signer, fo_bundle=jb) req = MetadataStatement(foo='bar') ent.ace(req, ['https://example.org/'], 'response') assert 'metadata_statements' in req assert 'signing_keys' not in req
def index(self, **kwargs): if cherrypy.request.process_request_body is True: _json_doc = cherrypy.request.body.read() else: raise cherrypy.HTTPError(400, 'Missing Client registration body') if _json_doc == b'': raise cherrypy.HTTPError(400, 'Missing Client registration body') _args = json.loads(as_unicode(_json_doc)) _mds = MetadataStatement(**_args) try: _mds.verify() except (MessageException, VerificationError) as err: raise cherrypy.CherryPyException(str(err)) else: _jwt = self.signer.create_signed_metadata_statement(_mds, single=True) cherrypy.response.headers['Content-Type'] = 'application/jwt' return as_bytes(_jwt)
def verify_request_signed_by_signing_keys(smsreq): """ Verify that a JWT is signed with a key that is inside the JWT. :param smsreq: Signed Metadata Statement signing request :return: Dictionary containing 'ms' (the signed request) and 'iss' (the issuer of the JWT). """ _jws = factory(smsreq) _json = _jws.jwt.part[1] _body = json.loads(as_unicode(_json)) iss = _body['iss'] _jwks = _body['signing_keys'] _kj = jwks_to_keyjar(_jwks, iss) try: _kid = _jws.jwt.headers['kid'] except KeyError: _keys = _kj.get_signing_key(owner=iss) else: _keys = _kj.get_signing_key(owner=iss, kid=_kid) _ver = _jws.verify_compact(smsreq, _keys) # remove the JWT specific claims for k in JasonWebToken.c_param.keys(): try: del _ver[k] except KeyError: pass try: del _ver['kid'] except KeyError: pass return {'ms': MetadataStatement(**_ver), 'iss': iss}
def index(self, signer='', context='discovery', **kwargs): if not signer: raise cherrypy.HTTPError(400, 'Missing signer') if signer not in self.signer: raise cherrypy.HTTPError(400, 'unknown signer') if cherrypy.request.process_request_body is True: _json_doc = cherrypy.request.body.read() else: raise cherrypy.HTTPError(400, 'Missing Client registration body') if _json_doc == b'': raise cherrypy.HTTPError(400, 'Missing Client registration body') try: _args = json.loads(as_unicode(_json_doc)) except json.JSONDecodeError as err: raise cherrypy.HTTPError( message="JSON decode error: {}".format(str(err))) _mds = MetadataStatement(**_args) try: _mds.verify() except (MessageException, VerificationError) as err: raise cherrypy.HTTPError( message="Message verification error: {}".format(str(err))) else: _sign = self.signer[signer] try: _resp = _sign.create_signed_metadata_statement(_mds, context) except (KeyError, SigningServiceError) as err: raise cherrypy.HTTPError(message=str(err)) else: _jwt = list(_resp.values())[0] cherrypy.response.headers['Content-Type'] = 'application/jwt' return as_bytes(_jwt)
def register(self, url): if cherrypy.request.process_request_body is True: _json_doc = cherrypy.request.body.read() else: raise cherrypy.HTTPError(400, 'Missing Client registration body') if _json_doc == b'': raise cherrypy.HTTPError(400, 'Missing Client registration body') _args = json.loads(as_unicode(_json_doc)) _mds = MetadataStatement(**_args) try: _mds.verify() except (MessageException, VerificationError) as err: raise cherrypy.CherryPyException(str(err)) else: res = requests.post(url, json=_mds.to_json()) if 200 <= res.status_code < 300: self.signer.metadata_statements[url] = res.text cherrypy.response.headers['Content-Type'] = 'application/jwt' return as_bytes(res.text) else: raise cherrypy.HTTPError(message=res.text)
def test_create_metadata_statement_simple(): ms = MetadataStatement(signing_keys=KEYS['org']['jwks']) assert ms assert len(ms['signing_keys']['keys']) == 2
#!/usr/bin/env python3 import argparse import json from fedoidc import MetadataStatement, read_jwks_file from fedoidc.operator import Operator parser = argparse.ArgumentParser() parser.add_argument('-j', dest='jwks', help="A JWKS containing the signers private keys") parser.add_argument('-i', dest='iss', help='The identifier of the signer') parser.add_argument('-r', dest='req', help='The message to sign') parser.add_argument('-l', dest='lifetime', default=86400, type=int, help="The lifetime of the signature") parser.add_argument('-f', dest='fo', help="The identifier of the federation") args = parser.parse_args() kj = read_jwks_file(args.jwks) op = Operator(keyjar=kj, iss=args.iss, lifetime=args.lifetime) _req = json.loads(open(args.req).read()) req = MetadataStatement(**_req) if args.fo: print('{}:{}'.format(args.fo, op.pack_metadata_statement(req))) else: print('{}:{}'.format(args.iss, op.pack_metadata_statement(req)))
from fedoidc import MetadataStatement from fedoidc.signing_service import InternalSigningService from fedoidc.signing_service import Signer from oic.utils.keyio import build_keyjar KEYDEFS= [{"type": "RSA", "key": "keys/{}.key", "use": ["sig"]}] parser = argparse.ArgumentParser() parser.add_argument('-i', dest='iss') parser.add_argument('-m', dest='ms_dir', default='ms_dir') parser.add_argument(dest="statement") args = parser.parse_args() _keydefs = [] for spec in KEYDEFS: spec['key'] = spec['key'].format(quote_plus(args.iss)) _keydefs.append(spec) sig_keys = build_keyjar(KEYDEFS)[1] signing_service = InternalSigningService(iss=args.iss, signing_keys=sig_keys) signer = Signer(signing_service, args.ms_dir) _args = json.loads(open(args.statement,'r').read()) _mds = MetadataStatement(**_args) _mds.verify() print(signer.create_signed_metadata_statement(_mds, single=True))
_dir = os.path.join('ms_path', quote_plus(name), 'discovery') metadata_statements = FileSystem(_dir, key_conv={ 'to': quote_plus, 'from': unquote_plus }) fo = "https://edugain.com" # What I want to create is something like # (ms_OA + SK[X]) _kj = build_keyjar(config.KEY_DEFS)[1] req = MetadataStatement( federation_usage='discovery', signing_keys={'keys': [x.serialize() for x in _kj.get_signing_key()]}) # FO(ms_OA + SK[X]) _fo_signer = operator[fo] ms_0 = _fo_signer.pack_metadata_statement(req) # OA(ms_IA + SK[IA] + FO(ms_OA + SK[X])) _ia_signer = operator["https://bogus.example.org"] req = MetadataStatement(tos_uri='https://example.org/tos', metadata_statements={fo: ms_0}, signing_keys=_ia_signer.signing_keys_as_jwks()) oa = "https://example.org" _oa_signer = operator[oa] ms_1 = _oa_signer.pack_metadata_statement(req)
parser = argparse.ArgumentParser() parser.add_argument('-i', dest='issuer', help="issuer id of the OP") parser.add_argument('-c', dest='context', help="OIDC operation") parser.add_argument('-t', dest='target') parser.add_argument(dest="filename") args = parser.parse_args() oa = args.issuer qpoa = quote_plus(oa) _kj = KeyJar() _jwks = json.loads(open(os.path.join('fo_jwks', qpoa)).read()) _kj.import_jwks(_jwks, oa) sign_serv = InternalSigningService(iss=oa, signing_keys=_kj) signer = Signer(sign_serv, ms_dir=os.path.join('ms', qpoa)) _req = open(args.filename, 'r').read() _msg = MetadataStatement() _msg.from_json(_req) _res = signer.create_signed_metadata_statement(_msg, context=args.context) for iss, sms in _res.items(): _qp = quote_plus(iss) _dn = os.path.join(args.target, qpoa, args.context) if not os.path.isdir(_dn): os.makedirs(_dn) _fn = os.path.join(_dn, _qp) _fp = open(_fn, 'w') _fp.write(sms) _fp.close()
def test_create_sms(): s = signer[OA['sunet']] req = MetadataStatement(issuer='https://example.org/op') r = s.create_signed_metadata_statement(req, 'discovery') assert r