def _delegate(self, delegating_secrets: KeyPairSecrets, delegating_did: str, subject_secrets: KeyPairSecrets, subject_did: str, delegation_name: str, proof_type: DelegationProofType, delegation_proof_add_method: Callable): delegating_doc = self.get_register_document(delegating_did) subject_doc = self.get_register_document(subject_did) delegating_key_pair = KeyPairSecretsHelper.get_key_pair(delegating_secrets) delegating_issuer = AdvancedIdentityLocalApi.get_issuer_by_public_key(delegating_doc, delegating_key_pair.public_base58) if proof_type == DelegationProofType.GENERIC: subject_issuer, proof = AdvancedIdentityLocalApi.create_generic_delegation_proof(subject_doc, subject_secrets) else: subject_issuer, proof = AdvancedIdentityLocalApi.create_delegation_proof(delegating_issuer, subject_doc, subject_secrets) existing_delegation = RegisterDocumentHelper.get_register_delegation_proof_by_controller(subject_issuer, delegating_doc, True) if existing_delegation and ( not delegation_name or delegation_name == existing_delegation.name ): # Found an existing delegation with matching controller and name. Nothing to do. return if not delegation_name: delegation_name = RegisterDocumentHelper.new_random_name_for_document(subject_doc) delegation_proof_add_method(proof, subject_issuer, delegation_name, delegating_issuer, delegating_key_pair)
def test_can_get_issuer_from_auth_keys(auth_keys, min_doc_owner_pub_key): doc = get_doc_with_keys(auth_keys=auth_keys.values(), public_keys=[min_doc_owner_pub_key]) issuer_name = '#AuthKey2' issuer_key = RegisterDocumentHelper.get_issuer_register_key( issuer_name, doc, include_auth=True) assert issuer_key == auth_keys[issuer_name]
def verify_authentication(resolver_client: ResolverClient, token: str) -> dict: """ Verify if the authentication token is allowed for authentication. :param resolver_client: resolver client interface :param token: jwt authentication token :return: decoded verified authentication token :raises: IdentityAuthenticationFailed: if not allowed for authentication """ try: unverified_token = JwtTokenHelper.decode_token(token) for field in ('iss', 'sub', 'aud', 'iat', 'exp'): if field not in unverified_token: raise IdentityValidationError(f'Invalid token, missing {field} field') issuer = Issuer.from_string(unverified_token['iss']) doc = resolver_client.get_document(issuer.did) get_controller_doc = resolver_client.get_document issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_auth(doc, issuer.name, get_controller_doc) if not issuer_key: raise IdentityInvalidRegisterIssuerError(f'Invalid issuer {issuer}') verified_token = JwtTokenHelper.decode_and_verify_token(token, issuer_key.public_key_base58, unverified_token['aud']) IdentityAuthValidation.validate_allowed_for_auth(resolver_client, issuer_key.issuer, verified_token['sub']) return {'iss': verified_token['iss'], 'sub': verified_token['sub'], 'aud': verified_token['aud'], 'iat': verified_token['iat'], 'exp': verified_token['exp']} except (IdentityValidationError, IdentityResolverError, IdentityInvalidRegisterIssuerError, IdentityNotAllowed) as err: raise IdentityAuthenticationFailed('Not authenticated') from err
def from_challenge_token(resolver_client: ResolverClient, challenge_token: str) -> 'Proof': """ Build proof from challenge token. :param resolver_client: resolver client to get the registered documents :param challenge_token: jwt challenge token :return: valid proof :raises: IdentityValidationError: if invalid challenge token """ decoded_token = JwtTokenHelper.decode_token(challenge_token) iss = decoded_token.get('iss') aud = decoded_token.get('aud') if not iss or not aud: raise IdentityValidationError( 'Invalid challenge token, missing \'iss\' or \'aud\'') issuer = Issuer.from_string(iss) doc = resolver_client.get_document(issuer.did) get_controller_doc = resolver_client.get_document issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_control_only( doc, issuer.name, get_controller_doc) if not issuer_key: raise IdentityInvalidRegisterIssuerError( f'Invalid issuer {issuer}') verified_token = JwtTokenHelper.decode_and_verify_token( challenge_token, issuer_key.public_key_base58, aud) return Proof(issuer_key.issuer, aud.encode('ascii'), verified_token['proof'])
def get_valid_doc_from_token( token: str, get_controller_doc: GetControllerDocFunc) -> RegisterDocument: """ Get a valid RegisterDocument from a resolver token. :param token: resolver token :param get_controller_doc: get controller register document function :return: valid register document :raises: IdentityResolverError: if invalid token IdentityResolverError: if invalid document """ try: unverified = JwtTokenHelper.decode_token(token) doc = RegisterDocumentBuilder().build_from_dict(unverified['doc']) issuer = Issuer.from_string(unverified['iss']) issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_control_only( doc, issuer.name, get_controller_doc) if not issuer_key: raise IdentityInvalidRegisterIssuerError( f'Invalid issuer {issuer}') JwtTokenHelper.decode_and_verify_token( token, issuer_key.public_key_base58, unverified['aud']) return doc except (KeyError, ValueError, IdentityValidationError, IdentityInvalidRegisterIssuerError) as exc: raise IdentityResolverError( f'Can not deserialized invalid resolver token: \'{exc}\'' ) from exc
def test_get_issuer_from_auth_keys_returns_none_if_not_found( auth_keys, min_doc_owner_pub_key): doc = get_doc_with_keys(auth_keys=auth_keys.values(), public_keys=[min_doc_owner_pub_key]) issuer_name = '#DoesNotExist' issuer_key = RegisterDocumentHelper.get_issuer_register_key( issuer_name, doc, include_auth=True) assert not issuer_key
def test_can_get_issuer_from_auth_delegation(auth_deleg_proof, min_doc_owner_pub_key): doc = get_doc_with_keys(deleg_auth=auth_deleg_proof.values(), public_keys=[min_doc_owner_pub_key]) issuer_name = '#DelegAuthKey2' issuer_key = RegisterDocumentHelper.get_issuer_register_delegation_proof( issuer_name, doc, include_auth=True) assert issuer_key == auth_deleg_proof[issuer_name]
def test_get_issuer_from_control_delegation_returns_none_if_not_found( control_deleg_proof, min_doc_owner_pub_key): doc = get_doc_with_keys(deleg_control=control_deleg_proof.values(), public_keys=[min_doc_owner_pub_key]) issuer_name = '#DoesNotExist' issuer_key = RegisterDocumentHelper.get_issuer_register_delegation_proof( issuer_name, doc, include_auth=False) assert not issuer_key
def test_can_get_auth_delegation_by_controller(auth_deleg_proof, min_doc_owner_pub_key): doc = get_doc_with_keys(deleg_auth=auth_deleg_proof.values(), public_keys=[min_doc_owner_pub_key]) delegation_name = '#DelegAuthKey2' expected_deleg_proof = auth_deleg_proof[delegation_name] deleg_proof = RegisterDocumentHelper.get_register_delegation_proof_by_controller( expected_deleg_proof.controller, doc, include_auth=True) assert deleg_proof == expected_deleg_proof
def test_get_issuer_from_auth_delegation_returns_none_if_in_auth_keys_but_auth_not_included( auth_deleg_proof, min_doc_owner_pub_key): doc = get_doc_with_keys(deleg_auth=auth_deleg_proof.values(), public_keys=[min_doc_owner_pub_key]) issuer_name = '#DelegAuthKey2' assert issuer_name in doc.auth_delegation_proof issuer_key = RegisterDocumentHelper.get_issuer_register_delegation_proof( issuer_name, doc, include_auth=False) assert not issuer_key
def test_get_issuer_from_auth_keys_returns_none_if_in_auth_keys_but_auth_not_included( auth_keys, min_doc_owner_pub_key): doc = get_doc_with_keys(auth_keys=auth_keys.values(), public_keys=[min_doc_owner_pub_key]) issuer_name = '#AuthKey2' assert issuer_name in doc.auth_keys issuer_key = RegisterDocumentHelper.get_issuer_register_key( issuer_name, doc, include_auth=False) assert not issuer_key
def test_get_auth_delegation_by_controller_returns_none_if_not_found( auth_deleg_proof, min_doc_owner_pub_key): doc = get_doc_with_keys(deleg_auth=auth_deleg_proof.values(), public_keys=[min_doc_owner_pub_key]) issuer = Issuer.build('did:iotics:iotHHHHKpPGWyEC4FFo4d6oyzVVk6MXLmEgY', '#DoesNotExist') deleg_proof = RegisterDocumentHelper.get_register_delegation_proof_by_controller( issuer, doc, include_auth=True) assert not deleg_proof
def test_get_valid_issuer_for_auth_returns_none_if_not_found( issuer_name, register_doc_and_deleg_doc): def get_ctrl_doc(did: str): assert did.startswith(did) return deleg_doc doc, deleg_doc = register_doc_and_deleg_doc issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_auth( doc, issuer_name, get_ctrl_doc) assert not issuer_key
def test_get_owner_public_key_returns_none_if_not_found( valid_key_pair, other_key_pair): doc_id = make_identifier(valid_key_pair.public_bytes) doc = RegisterDocumentBuilder() \ .add_public_key_obj(RegisterPublicKey('#NotOwner', other_key_pair.public_base58, revoked=False)) \ .build(doc_id, DIDType.TWIN, proof='a proof, does not matter here', revoked=False) key = RegisterDocumentHelper.get_owner_register_public_key(doc) assert not key
def test_can_get_issuer_by_public_key(valid_key_pair): doc_did = make_identifier(valid_key_pair.public_bytes) key_issuer = Issuer.build(doc_did, '#AnIssuer') doc = RegisterDocumentBuilder() \ .add_public_key_obj(RegisterPublicKey(key_issuer.name, valid_key_pair.public_base58, revoked=False)) \ .build(key_issuer.did, DIDType.TWIN, proof='a proof, does not matter here', revoked=False) issuer = RegisterDocumentHelper.get_issuer_from_public_key( doc, valid_key_pair.public_base58) assert issuer == key_issuer
def is_allowed_for(issuer: Issuer, issuer_doc: RegisterDocument, subject_doc: RegisterDocument, include_auth: bool) -> bool: """ Check if the issuer is allowed for control (authentication if include_auth = True) on the subject register document. Issuer is allowed if both the issuer and subject register document are not revoked AND ( ( the issuer is the owner of the subject register document OR if a include_auth=True the issuer is in the authentication public keys of the subject register document ) OR the issuer is delegated for control (authentication if include_auth = True) with a valid delegation proof on the subject registered document ) :param issuer: issuer :param issuer_doc: issuer register document :param subject_doc: subject register document :param include_auth: include authentication keys and delegation proof is set to True :return: True is allowed else False """ if issuer_doc.revoked or subject_doc.revoked: return False if is_same_identifier(issuer.did, subject_doc.did): # it is the same document issuer_key = RegisterDocumentHelper.get_issuer_register_key(issuer.name, subject_doc, include_auth) if issuer_key and not issuer_key.revoked: return True delegation_proof = RegisterDocumentHelper.get_register_delegation_proof_by_controller(issuer, subject_doc, include_auth) if delegation_proof: try: DelegationValidation.validate_delegation_from_doc(subject_doc.did, issuer_doc, delegation_proof) except IdentityInvalidDocumentDelegationError: return False return not delegation_proof.revoked return False
def get_issuer_by_public_key(doc: RegisterDocument, public_base58: str) -> Issuer: """ Get issuer matching the public key from a register document public keys. :param doc: register document :param public_base58: issuer public key base 58 :return: corresponding issuer :raises: IdentityRegisterIssuerNotFoundError: if the issuer matching the public key is not found """ issuer = RegisterDocumentHelper.get_issuer_from_public_key(doc, public_base58) if not issuer: raise IdentityRegisterIssuerNotFoundError(f'User secrets not allowed to update \'{doc.did}\'') return issuer
def test_can_get_valid_issuer_for_control_only(issuer_name, register_doc_and_deleg_doc): doc, deleg_doc = register_doc_and_deleg_doc def get_ctrl_doc(did: str): assert did.startswith(did) return deleg_doc assert issuer_name in doc.public_keys or issuer_name in deleg_doc.public_keys issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_control_only( doc, issuer_name, get_ctrl_doc) assert issuer_key.issuer == Issuer.build(doc.did, issuer_name) expected_base58 = doc.public_keys.get( issuer_name, deleg_doc.public_keys.get(issuer_name)) assert expected_base58, f'test setup error, {issuer_name} should be in one of the docs public keys' assert issuer_key.public_key_base58 == expected_base58.base58
def validate_new_document_proof(doc: RegisterDocument): """ Validate register document proof. :param doc: register document. :raises: IdentityInvalidDocumentError: if register document initial owner public key has been removed IdentityInvalidDocumentError: if register document proof is invalid """ try: key = RegisterDocumentHelper.get_owner_register_public_key(doc) if not key: raise IdentityInvalidDocumentError( f'Invalid document \'{doc.did}\', no owner public key') ProofValidation.validate_proof( Proof(Issuer.build(doc.did, key.name), doc.did.encode('ascii'), doc.proof), key.base58) except IdentityInvalidProofError as err: raise IdentityInvalidDocumentError( f'Invalid document \'{doc.did}\' proof: {err}') from err
def test_can_get_valid_issuer_for_auth(issuer_name, register_doc_and_deleg_doc): doc, deleg_doc = register_doc_and_deleg_doc def get_ctrl_doc(did: str): assert did.startswith(did) return deleg_doc all_keys = list(doc.public_keys) + list(doc.auth_keys) + list( deleg_doc.public_keys) + list(deleg_doc.auth_keys) assert issuer_name in all_keys issuer_key = RegisterDocumentHelper.get_valid_issuer_key_for_auth( doc, issuer_name, get_ctrl_doc) assert issuer_key.issuer == Issuer.build(doc.did, issuer_name) exp_base58 = doc.public_keys.get(issuer_name, doc.auth_keys.get(issuer_name)) exp_base58 = exp_base58 or deleg_doc.public_keys.get( issuer_name, deleg_doc.auth_keys.get(issuer_name)) assert exp_base58, f'test setup error, {issuer_name} should be in one of the docs public or auth keys' assert issuer_key.public_key_base58 == exp_base58.base58
def test_is_issuer_in_keys_returns_true_if_issuer_in_auth_keys( public_keys, auth_keys, issuer_name, include_auth, expected_res): doc = get_doc_with_keys(public_keys.values(), auth_keys.values()) assert RegisterDocumentHelper.is_issuer_in_keys( issuer_name, doc, include_auth) == expected_res
def test_can_get_issuer_from_public_keys(public_keys): doc = get_doc_with_keys(public_keys=public_keys.values()) issuer_name = '#Key2' issuer_key = RegisterDocumentHelper.get_issuer_register_key( issuer_name, doc, include_auth=False) assert issuer_key == public_keys[issuer_name]
def test_get_issuer_from_public_keys_returns_none_if_not_found(public_keys): doc = get_doc_with_keys(public_keys=public_keys.values()) issuer_name = '#DoesNotExist' issuer_key = RegisterDocumentHelper.get_issuer_register_key( issuer_name, doc, include_auth=False) assert not issuer_key