def test_update_metadata_statement(): make_signing_sequence(['https://op.sunet.se', 'https://sunet.se', 'https://swamid.sunet.se'], ENTITY) op = ENTITY['https://op.sunet.se'] metadata_statement = MetadataStatement(foo='bar') metadata_statement = op.update_metadata_statement(metadata_statement) assert metadata_statement assert set(metadata_statement.keys()) == {'foo', 'metadata_statements'} swamid = ENTITY['https://swamid.sunet.se'] # on the RP side rp = FederationEntityOOB(None, 'https://rp.sunet.se') # Need the FO bundle, which in this case only needs Swamid's key jb = JWKSBundle('https://rp.sunet.se') _kj = KeyJar() _kj.import_jwks(swamid.self_signer.public_keys(), swamid.iss) jb['https://swamid.sunet.se'] = _kj rp.jwks_bundle = jb l = rp.get_metadata_statement(metadata_statement, MetadataStatement, 'discovery') assert l[0].iss == 'https://op.sunet.se' assert l[0].fo == 'https://swamid.sunet.se' assert l[0].le == {'foo':'bar'}
def make_signing_sequence(entity_id, entity_dict, context='discovery', lifetime=86400): """ Signing sequence with nothing but keys no actual content :param entity_id: A list of entity IDs :param entity_dict: A dictionayr with entity IDs as keys and :py:class:`fedoidcmsg.entity.FederationEntity` instances as values. :param context: :param lifetime: The lifetime of the JWT signatures :return: A signed compounded metadata statement """ n = len(entity_id) i = n-1 fo = entity_dict[entity_id[i]].iss sms = None i -= 1 while i >= 0: metadata_statement = MetadataStatement() ent = entity_dict[entity_id[i]] ent.add_signing_keys(metadata_statement) # sends metadata to superior for signing sup = entity_dict[entity_id[i+1]] sup.add_sms_spec_to_request(metadata_statement, context=context) sms = sup.self_signer.sign(metadata_statement, ent.iss, lifetime=lifetime) # superior returns signed metadata statement the subordinate stores it try: ent.metadata_statements[context][fo] = sms except KeyError: ent.metadata_statements[context] = {fo: sms} i -= 1 return sms
def test_sequence(): config = { 'self_signer': { 'private_path': '{}/private_jwks'.format(root_dir), 'key_defs': KEYDEFS, 'public_path': '{}/public_jwks'.format(root_dir) }, 'sms_dir': '{}/ms/https%3A%2F%2Fsunet.se'.format(root_dir), 'fo_bundle': { 'private_path': '{}/fo_bundle_signing_keys'.format(root_dir), 'key_defs': KEYDEFS, 'public_path': '{}/pub_fo_bundle_signing_keys'.format(root_dir), 'dir': '{}/fo_jwks'.format(root_dir) }, 'context': 'discovery' } fe = make_federation_entity(config, 'https://op.example.com') req = MetadataStatement(foo='bar') fe.add_sms_spec_to_request(req) fe.add_signing_keys(req) updated_req = fe.self_sign(req, 'https://example.com') assert updated_req assert set(updated_req.keys()) == {'foo', 'signing_keys', 'metadata_statements'}
def test_add_signing_keys(): kj = build_keyjar(KEYDEFS)[1] sign_serv = InternalSigningService('https://signer.example.com', keyjar=kj) ent = FederationEntityOOB(None, self_signer=sign_serv) req = MetadataStatement(foo='bar') ent.add_signing_keys(req) assert 'signing_keys' in req
def test_updating_metadata_no_superior(): op = ENTITY['https://op.sunet.se'] op.metadata_statements['discovery'] = {} metadata_statement = MetadataStatement(foo='bar') metadata_statement = op.update_metadata_statement(metadata_statement) assert metadata_statement assert set(metadata_statement.keys()) == {'foo', 'metadata_statements'} # swamid = ENTITY['https://swamid.sunet.se'] # on the RP side rp = FederationEntityOOB(None, 'https://rp.sunet.se') l = rp.get_metadata_statement(metadata_statement, MetadataStatement, 'discovery') assert l[0].iss == 'https://op.sunet.se' assert l[0].fo == 'https://op.sunet.se' assert l[0].le == {'foo':'bar'}
def test_create_client_metadata_statement(): ms = MetadataStatement() ORGOP.add_signing_keys(ms) sms = FOP.pack_metadata_statement(ms) cms = ClientMetadataStatement( metadata_statements=Message(**{FOP.iss: sms}), contacts=['*****@*****.**']) assert cms
def metadata(self): cherrypy.response.headers['Content-Type'] = 'application/jws' metadata_statement = MetadataStatement() fe = self.endpoint_context.federation_entity fe.add_signing_keys(metadata_statement) try: sms = fe.self_signer.sign(metadata_statement, aud=fe.fo_priority) except Exception as err: logger.exception(err) raise return as_bytes(sms)
def metadata(self): cherrypy.response.headers['Content-Type'] = 'application/jws' _info = self.rph.client_configs['']['client_preferences'] metadata_statement = MetadataStatement(**_info) fe = self.rph.extra['federation_entity'] fe.add_signing_keys(metadata_statement) try: sms = fe.self_signer.sign(metadata_statement, aud=fe.fo_priority) except Exception as err: logger.exception(err) raise return as_bytes(sms)
def test_request_signed_by_signing_keys(): kj = KeyJar() kj.issuer_keys['abc'] = KEYJAR.issuer_keys[''] msreq = MetadataStatement(signing_keys=json.dumps(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_get_metadata_statement(): jb = JWKSBundle('') for iss in ['https://example.org/', 'https://example.com/']: jb[iss] = build_keyjar(KEYDEFS)[1] self_signer = InternalSigningService(keyjar=jb['https://example.com/'], iss='https://example.com/') op = Operator(self_signer=self_signer, iss='https://example.com/') req = MetadataStatement(foo='bar') sms = op.pack_metadata_statement(req, sign_alg='RS256') sms_dir = {'https://example.com': sms} req['metadata_statements'] = Message(**sms_dir) ent = FederationEntity(None, fo_bundle=public_jwks_bundle(jb)) loe = ent.get_metadata_statement(req) assert loe
def test_pack_metadata_statement_other_alg(): _keyjar = build_keyjar(KEYDEFS)[1] self_signer = InternalSigningService('https://example.com/op', keyjar=_keyjar) op = Operator(self_signer=self_signer, iss=self_signer.iss) req = MetadataStatement(issuer='https://example.org/op') sms = op.pack_metadata_statement(req, sign_alg='ES256') assert sms # Should be a signed JWT _jwt = factory(sms) _body = json.loads(as_unicode(_jwt.jwt.part[1])) assert _body['iss'] == self_signer.iss # verify signature _kj = public_keys_keyjar(_keyjar, '', None, op.iss) r = _jwt.verify_compact(sms, _kj.get_signing_key(owner=op.iss)) assert r
def test_add_sms_spec_to_request(): 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', keyjar=kj) ent = FederationEntityOOB(None, self_signer=sign_serv, fo_bundle=public_jwks_bundle(jb), context='response') ent.metadata_statements = { 'response': { 'https://example.org/': 'https://example.org/sms1' } } req = MetadataStatement(foo='bar') ent.add_sms_spec_to_request(req, ['https://example.org/']) assert 'metadata_statement_uris' in req
def process(incoming, outgoing, fe): for fo in os.listdir(incoming): _dir = os.path.join(incoming, fo) fed = unquote(fo) if os.path.isdir(_dir): for entity_id in os.listdir(_dir): _fname = os.path.join(_dir, entity_id) if os.path.isfile(_fname): _ms = MetadataStatement().from_json(open(_fname).read()) fe.add_sms_spec_to_request(_ms, federation=fed, context=ctx) sms = fe.self_signer.sign(_ms, iss=fe.entity_id) _out_dir = os.path.join(outgoing, fo) if not os.path.isdir(_out_dir): os.makedirs(_out_dir) out = os.path.join(outgoing, fo, entity_id) with atomic_write(out, overwrite=True) as f: f.write(sms)
def test_pack_metadata_statement(): jb = FSJWKSBundle('', None, 'fo_jwks', key_conv={'to': quote_plus, 'from': unquote_plus}) _keyjar = build_keyjar(KEYDEFS)[1] self_signer = InternalSigningService('https://example.com/op', keyjar=_keyjar) op = Operator(self_signer=self_signer, jwks_bundle=jb, iss='https://example.com/op') 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 _kj = public_keys_keyjar(_keyjar, '', None, op.iss) r = _jwt.verify_compact(sms, _kj.get_signing_key(owner=op.iss)) assert r
def create_provider_info_response(fo): sunet_metadata = MetadataStatement() pi_response = ProviderConfigurationResponse( issuer=SUNET_OP.iss, response_types_supported=['code'], grant_types_supported=['Bearer'], subject_types_supported=['pairwise'], authorization_endpoint='https://example.com/op/authz', jwks_uri='https://example.com/op/jwks.json', token_endpoint='https://example.com/op/token', id_token_signing_alg_values_supported=['RS256', 'RS384', 'RS512'], userinfo_signing_alg_values_supported=['RS256', 'RS384', 'RS512']) clear_metadata_statements(FEDENT.values()) _ = create_compounded_metadata_statement( [SUNET_OP.iss, ORG_SUNET.iss, fo.iss], FEDENT, { ORG_SUNET.iss: sunet_metadata, SUNET_OP.iss: pi_response }) # clean copy pi_response = ProviderConfigurationResponse( issuer=SUNET_OP.iss, response_types_supported=['code'], grant_types_supported=['Bearer'], subject_types_supported=['pairwise'], authorization_endpoint='https://example.com/op/authz', jwks_uri='https://example.com/op/jwks.json', token_endpoint='https://example.com/op/token', id_token_signing_alg_values_supported=['RS256', 'RS384', 'RS512'], userinfo_signing_alg_values_supported=['RS256', 'RS384', 'RS512']) SUNET_OP.add_sms_spec_to_request(pi_response) resp = SUNET_OP.self_sign(pi_response) return resp.to_json()
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 JsonWebToken.c_param.keys(): try: del _ver[k] except KeyError: pass try: del _ver['kid'] except KeyError: pass return {'ms': MetadataStatement(**_ver), 'iss': iss}
def test_create_metadata_statement_simple(): ms = MetadataStatement() ORGOP.add_signing_keys(ms) assert ms sig_keys = ms['signing_keys'] assert len(sig_keys['keys']) == 2