def test_can_remove_public_key_building_from_existing_doc( doc_did, doc_proof, doc_keys, remove_key_name, get_key_set): existing_doc = RegisterDocumentBuilder() \ .add_public_key_obj(doc_keys['#pub_key1']) \ .add_public_key_obj(doc_keys['#pub_key2']) \ .add_authentication_key_obj(doc_keys['#auth_key1']) \ .add_authentication_key_obj(doc_keys['#auth_key2']) \ .add_control_delegation_obj(doc_keys['#deleg_control_key1']) \ .add_control_delegation_obj(doc_keys['#deleg_control_key2']) \ .add_authentication_delegation_obj(doc_keys['#deleg_auth_key1']) \ .add_authentication_delegation_obj(doc_keys['#deleg_auth_key2']) \ .build(did=doc_did, purpose=DIDType.TWIN, proof=doc_proof, revoked=True) new_doc = RegisterDocumentBuilder() \ .set_keys_from_existing(existing_doc) \ .remove_key(remove_key_name) \ .build_from_existing(existing_doc, populate_with_doc_keys=False) existing_doc_key_set = get_key_set(existing_doc) assert len(existing_doc_key_set) == 2 assert remove_key_name in existing_doc_key_set new_doc_key_set = get_key_set(new_doc) assert len(new_doc_key_set) == 1 assert remove_key_name not in new_doc_key_set existing_doc_key_set.pop(remove_key_name) assert existing_doc_key_set == new_doc_key_set
def test_building_a_register_doc_from_dict_raises_a_validation_error_if_invalid_data( full_doc, invalid_data_mutation): doc_as_dict = full_doc.to_dict() invalid_data_mutation(doc_as_dict) with pytest.raises(IdentityValidationError): RegisterDocumentBuilder() \ .build_from_dict(doc_as_dict)
def test_can_build_a_register_doc_with_min_data(doc_did, doc_proof, min_doc_owner_pub_key): now_before_create = get_unix_time_ms() new_doc = RegisterDocumentBuilder() \ .add_public_key_obj(min_doc_owner_pub_key) \ .build(did=doc_did, purpose=DIDType.USER, proof=doc_proof, revoked=False) assert new_doc.did == doc_did assert new_doc.proof == doc_proof assert new_doc.purpose == DIDType.USER assert not new_doc.revoked # Default values assert new_doc.spec_version == DOCUMENT_VERSION assert new_doc.metadata == Metadata() assert not new_doc.creator assert now_before_create <= new_doc.update_time <= get_unix_time_ms() assert not new_doc.controller assert new_doc.public_keys == { min_doc_owner_pub_key.name: min_doc_owner_pub_key } assert not new_doc.auth_keys assert not new_doc.auth_delegation_proof assert not new_doc.control_delegation_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_can_validate_document_with_delegation_against_resolver(valid_doc): delegated_doc1, deleg_key1 = get_valid_delegated_doc_and_deleg_proof( new_seed(), '#issuer1', delegating_doc_id=valid_doc.did, deleg_name='#DelegDoc1') delegated_doc2, deleg_key2 = get_valid_delegated_doc_and_deleg_proof( new_seed(), '#issuer2', delegating_doc_id=valid_doc.did, deleg_name='#DelegDoc2') valid_doc = RegisterDocumentBuilder() \ .add_control_delegation_obj(deleg_key1) \ .add_control_delegation_obj(deleg_key2) \ .add_authentication_delegation(deleg_key1.name + 'auth', deleg_key1.controller, deleg_key1.proof, deleg_key1.revoked) \ .add_authentication_delegation(deleg_key2.name + 'auth', deleg_key2.controller, deleg_key2.proof, deleg_key2.revoked) \ .build_from_existing(valid_doc) resolver_client = ResolverClientTest( docs={ valid_doc.did: valid_doc, delegated_doc1.did: delegated_doc1, delegated_doc2.did: delegated_doc2 }) assert is_validator_run_success( DocumentValidation.validate_document_against_resolver, resolver_client, valid_doc)
def register_doc(valid_key_pair_secrets, valid_issuer): proof = Proof.build(valid_key_pair_secrets, valid_issuer, content=valid_issuer.did.encode()) key_pair = KeyPairSecretsHelper.get_key_pair(valid_key_pair_secrets) return RegisterDocumentBuilder() \ .add_public_key(valid_issuer.name, key_pair.public_base58, revoked=False) \ .build(valid_issuer.did, DIDType.USER, proof.signature, revoked=False)
def test_building_a_register_doc_raises_a_validation_error_if_no_controller_and_no_pub_key( doc_did, doc_proof): with pytest.raises(IdentityInvalidDocumentError): RegisterDocumentBuilder() \ .build(did=doc_did, purpose=DIDType.USER, proof=doc_proof, revoked=False)
def test_building_a_register_doc_raises_if_same_key_is_added_twice(doc_keys): duplicate_pub_key1 = RegisterPublicKey( name=doc_keys['#pub_key1'].name, base58=get_public_base_58_key(), revoked=doc_keys['#pub_key1'].revoked) with pytest.raises(IdentityRegisterDocumentKeyConflictError): RegisterDocumentBuilder() \ .add_public_key_obj(doc_keys['#pub_key1']) \ .add_public_key_obj(duplicate_pub_key1)
def test_can_remove_not_existing_key_without_error(doc_did, doc_proof, doc_keys, get_key_set): existing_doc = RegisterDocumentBuilder() \ .add_public_key_obj(doc_keys['#pub_key1']) \ .add_authentication_key_obj(doc_keys['#auth_key1']) \ .add_control_delegation_obj(doc_keys['#deleg_control_key1']) \ .add_authentication_delegation_obj(doc_keys['#deleg_auth_key1']) \ .build(did=doc_did, purpose=DIDType.TWIN, proof=doc_proof, revoked=True) new_doc = RegisterDocumentBuilder() \ .set_keys_from_existing(existing_doc) \ .remove_key('#NotExistingKey') \ .build_from_existing(existing_doc, populate_with_doc_keys=False) existing_doc_key_set = get_key_set(existing_doc) new_doc_key_set = get_key_set(new_doc) assert existing_doc_key_set == new_doc_key_set
def test_building_a_register_doc_raises_a_validation_error_if_unsupported_version( doc_did, doc_proof, min_doc_owner_pub_key): with pytest.raises(IdentityInvalidDocumentError): RegisterDocumentBuilder() \ .add_public_key_obj(min_doc_owner_pub_key) \ .build(did=doc_did, purpose=DIDType.USER, proof=doc_proof, revoked=False, spec_version='!!unsupported!!')
def remove_doc_key(doc: RegisterDocument, key_name: str) -> RegisterDocument: """ Remove a register key from a register document :param doc: register document :param key_name: register key name :return: """ return RegisterDocumentBuilder() \ .set_keys_from_existing(doc) \ .remove_key(key_name) \ .build_from_existing(doc, populate_with_doc_keys=False)
def invalid_doc_invalid_proof(valid_doc, doc_invalid_proof, valid_key_pair_secrets): public_base58 = KeyPairSecretsHelper.get_public_key_base58_from_key_pair_secrets( valid_key_pair_secrets) doc_id = valid_doc.did return RegisterDocumentBuilder() \ .add_public_key_obj(RegisterPublicKey('#Owner', public_base58, revoked=False)) \ .build(doc_id, DIDType.TWIN, proof=doc_invalid_proof.signature, revoked=False)
def test_can_build_a_register_doc_with_full_data(doc_keys, doc_did, doc_proof, doc_controller): now_before_create = get_unix_time_ms() metadata = Metadata.build('a label', 'a comment', 'http://a/url') spec_version = SUPPORTED_VERSIONS[0] creator = 'did:iotics:iotHHHHKpPGWyEC4FFo4d6oyzVVk6MEEEEgY' new_doc = RegisterDocumentBuilder() \ .add_public_key(doc_keys['#pub_key1'].name, doc_keys['#pub_key1'].base58, doc_keys['#pub_key1'].revoked) \ .add_public_key_obj(doc_keys['#pub_key2']) \ .add_authentication_key(doc_keys['#auth_key1'].name, doc_keys['#auth_key1'].base58, doc_keys['#auth_key1'].revoked) \ .add_authentication_key_obj(doc_keys['#auth_key2']) \ .add_control_delegation(doc_keys['#deleg_control_key1'].name, doc_keys['#deleg_control_key1'].controller, doc_keys['#deleg_control_key1'].proof, doc_keys['#deleg_control_key1'].revoked) \ .add_control_delegation_obj(doc_keys['#deleg_control_key2']) \ .add_authentication_delegation(doc_keys['#deleg_auth_key1'].name, doc_keys['#deleg_auth_key1'].controller, doc_keys['#deleg_auth_key1'].proof, doc_keys['#deleg_auth_key1'].revoked) \ .add_authentication_delegation_obj(doc_keys['#deleg_auth_key2']) \ .build(did=doc_did, purpose=DIDType.TWIN, proof=doc_proof, revoked=True, metadata=metadata, creator=creator, spec_version=spec_version, controller=doc_controller) assert new_doc.did == doc_did assert new_doc.proof == doc_proof assert new_doc.purpose == DIDType.TWIN assert new_doc.revoked assert new_doc.spec_version == spec_version assert new_doc.metadata == metadata assert new_doc.creator == creator assert now_before_create <= new_doc.update_time <= get_unix_time_ms() assert new_doc.controller == doc_controller assert len(new_doc.public_keys) == 2 compare_key(doc_keys['#pub_key1'], new_doc.public_keys['#pub_key1']) compare_key(doc_keys['#pub_key2'], new_doc.public_keys['#pub_key2']) assert len(new_doc.auth_keys) == 2 compare_key(doc_keys['#auth_key1'], new_doc.auth_keys['#auth_key1']) compare_key(doc_keys['#auth_key2'], new_doc.auth_keys['#auth_key2']) assert len(new_doc.control_delegation_proof) == 2 compare_delegation(doc_keys['#deleg_control_key1'], new_doc.control_delegation_proof['#deleg_control_key1']) compare_delegation(doc_keys['#deleg_control_key2'], new_doc.control_delegation_proof['#deleg_control_key2']) assert len(new_doc.public_keys) == 2 compare_delegation(doc_keys['#deleg_auth_key1'], new_doc.auth_delegation_proof['#deleg_auth_key1']) compare_delegation(doc_keys['#deleg_auth_key2'], new_doc.auth_delegation_proof['#deleg_auth_key2'])
def invalid_doc_no_owner_key(valid_doc, valid_issuer, valid_key_pair_secrets, other_key_pair_secrets): public_base58 = KeyPairSecretsHelper.get_public_key_base58_from_key_pair_secrets( other_key_pair_secrets) doc_id = valid_doc.did return RegisterDocumentBuilder() \ .add_public_key_obj(RegisterPublicKey('#KeyNotFromOwner', public_base58, revoked=False)) \ .build(doc_id, DIDType.TWIN, proof=Proof.build(valid_key_pair_secrets, valid_issuer, content=doc_id.encode()).signature, revoked=False)
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_build_a_register_doc_with_controller_and_not_owner_public_key( doc_did, doc_proof): controller = 'did:iotics:iotHHHHKpPGWWWC4FFo4d6oyzVVk6MXLmEgY#AController' new_doc = RegisterDocumentBuilder() \ .build(did=doc_did, purpose=DIDType.USER, proof=doc_proof, revoked=False, controller=controller) assert not new_doc.public_keys assert new_doc.controller == controller
def test_can_decode_doc_token(valid_issuer, valid_private_key, register_doc): audience = 'http://somehting' token = JwtTokenHelper.create_doc_token(issuer=valid_issuer, audience=audience, doc=register_doc, private_key=valid_private_key) decoded = JwtTokenHelper.decode_token(token) assert decoded['iss'] == str(valid_issuer) assert decoded['aud'] == audience decoded_doc = RegisterDocumentBuilder().build_from_dict(decoded['doc']) compare_doc(register_doc, decoded_doc)
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 test_building_a_register_doc_raises_if_same_delegation_is_added_twice( doc_keys): duplicate_deleg_control_key1 = RegisterDelegationProof( name=doc_keys['#deleg_control_key1'].name, controller=Issuer('did:iotics:iotHjrmKpPGWyEC4FFo4d6oyzVVk6MXFFFFF', '#AController'), proof=doc_keys['#deleg_control_key1'].proof, revoked=doc_keys['#deleg_control_key1'].revoked) with pytest.raises(IdentityRegisterDocumentKeyConflictError): RegisterDocumentBuilder() \ .add_control_delegation_obj(doc_keys['#deleg_control_key1']) \ .add_control_delegation_obj(duplicate_deleg_control_key1)
def add_doc_key(self, doc: RegisterDocument, key: RegisterKeyBase) -> RegisterDocument: """ Add a new register key to the register document :param doc: register document :param key: register key base :raises: IdentityInvalidDocumentError: if invalid document IdentityRegisterDocumentKeyConflictError: if key name is not unique """ builder = RegisterDocumentBuilder() self.add_key_to_builder(builder, key) return builder.build_from_existing(doc)
def doc_delegating_authentication(delegating_issuer_name, delegating_secrets, allowed_issuer_secrets, allowed_issuer): doc = get_valid_document_from_secret(delegating_secrets, delegating_issuer_name) delegating_issuer = Issuer.build(doc.did, delegating_issuer_name) proof = Proof.build(allowed_issuer_secrets, allowed_issuer, content=delegating_issuer.did.encode()) return RegisterDocumentBuilder() \ .add_authentication_delegation_obj(RegisterDelegationProof.build('#ADeleg', controller=allowed_issuer, proof=proof.signature)) \ .build_from_existing(doc)
def set_document_controller(self, doc_owner_key_pair: KeyPair, doc_owner_issuer: Issuer, controller: Issuer): """ Set register document controller issuer. :param controller: register document controller issuer :param doc_owner_key_pair: register document owner key pair :param doc_owner_issuer: register document owner issuer :raises: IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ self._update_doc(doc_owner_key_pair, doc_owner_issuer, get_updated_doc=RegisterDocumentBuilder().build_from_existing, controller=controller)
def set_document_revoked(self, doc_owner_key_pair: KeyPair, doc_owner_issuer: Issuer, revoked: bool): """ Set register document revoke field. :param revoked: is register document revoked :param doc_owner_key_pair: register document owner key pair :param doc_owner_issuer: register document owner issuer :raises: IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ self._update_doc(doc_owner_key_pair, doc_owner_issuer, get_updated_doc=RegisterDocumentBuilder().build_from_existing, revoked=revoked)
def set_document_creator(self, doc_owner_key_pair: KeyPair, doc_owner_issuer: Issuer, creator: str): """ Set register document creator. :param creator: register document creator decentralised identifier :param doc_owner_key_pair: register document owner key pair :param doc_owner_issuer: register document owner issuer :raises: IdentityValidationError: if invalid creator decentralised identifier IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ self._update_doc(doc_owner_key_pair, doc_owner_issuer, get_updated_doc=RegisterDocumentBuilder().build_from_existing, creator=creator)
def test_can_build_a_register_doc_from_minimal_dict(minimal_doc): doc_as_dict = minimal_doc.to_dict() doc_as_dict.pop('authentication') doc_as_dict.pop('delegateControl') doc_as_dict.pop('delegateAuthentication') new_doc = RegisterDocumentBuilder().build_from_dict(doc_as_dict) assert new_doc.did == minimal_doc.did assert new_doc.purpose == minimal_doc.purpose assert new_doc.proof == minimal_doc.proof assert new_doc.revoked == minimal_doc.revoked assert new_doc.spec_version == minimal_doc.spec_version assert new_doc.metadata == minimal_doc.metadata assert new_doc.public_keys == minimal_doc.public_keys assert not new_doc.auth_keys assert not new_doc.control_delegation_proof assert not new_doc.auth_delegation_proof
def get_valid_document_from_secret(secrets: KeyPairSecrets, issuer_name: str, controller: Issuer = None): public_base58 = KeyPairSecretsHelper.get_public_key_base58_from_key_pair_secrets( secrets) public_bytes = base58.b58decode(public_base58) doc_id = make_identifier(public_bytes) proof = Proof.build(secrets, Issuer.build(doc_id, issuer_name), content=doc_id.encode()) return RegisterDocumentBuilder() \ .add_public_key_obj(RegisterPublicKey(issuer_name, public_base58, revoked=False)) \ .build(doc_id, DIDType.TWIN, proof=proof.signature, revoked=False, controller=controller)
def get_doc_with_keys(public_keys: Iterable[RegisterPublicKey] = None, auth_keys: Iterable[RegisterAuthenticationPublicKey] = None, deleg_control: Iterable[RegisterDelegationProof] = None, deleg_auth: Iterable[RegisterDelegationProof] = None, controller: str = None, did: str = None) -> RegisterDocument: builder = RegisterDocumentBuilder() _ = [builder.add_public_key_obj(k) for k in (public_keys or ())] _ = [builder.add_authentication_key_obj(k) for k in (auth_keys or ())] _ = [builder.add_control_delegation_obj(k) for k in (deleg_control or ())] _ = [builder.add_authentication_delegation_obj(k) for k in (deleg_auth or ())] return builder.build(did=did or 'did:iotics:iotHHHHKpPGWyEC4FFo4d6oyzVVk6MXLmEgY', purpose=DIDType.TWIN, proof='a proof', revoked=True, controller=controller)
def test_can_build_a_register_doc_from_an_other_doc(minimal_doc, full_doc, is_minimal): doc = minimal_doc if is_minimal else full_doc new_doc = RegisterDocumentBuilder().build_from_existing(doc) assert new_doc.did == doc.did assert new_doc.purpose == doc.purpose assert new_doc.proof == doc.proof assert new_doc.revoked == doc.revoked assert new_doc.spec_version == doc.spec_version assert new_doc.metadata == doc.metadata assert new_doc.creator == doc.creator assert new_doc.controller == doc.controller assert new_doc.public_keys == doc.public_keys assert new_doc.auth_keys == doc.auth_keys assert new_doc.control_delegation_proof == doc.control_delegation_proof assert new_doc.auth_delegation_proof == doc.auth_delegation_proof assert new_doc.update_time >= doc.update_time
def register_new_doc(self, key_pair_secrets: KeyPairSecrets, issuer: Issuer, purpose: DIDType): """ Create and register a new document against the resolver. :param key_pair_secrets: new register document owner secrets :param issuer: new register document owner issuer :param purpose: register document purpose (HOST, TWIN, USER or AGENT) :raises: IdentityValidationError: if invalid secrets IdentityInvalidDocumentError: if document build error IdentityDependencyError: if incompatible library dependency """ key_pair = KeyPairSecretsHelper.get_key_pair(key_pair_secrets) proof = AdvancedIdentityLocalApi.create_proof(key_pair_secrets, issuer, content=issuer.did.encode()) doc = RegisterDocumentBuilder() \ .add_public_key(issuer.name, key_pair.public_base58, revoked=False) \ .build(issuer.did, purpose, proof.signature, revoked=False) self.register_updated_document(doc, key_pair, issuer)
def test_validate_document_against_resolver_raises_validation_error_if_resolver_error( valid_doc): _, deleg_key = get_valid_delegated_doc_and_deleg_proof( new_seed(), '#issuer1', delegating_doc_id=valid_doc.did, deleg_name='#DelegDoc1') valid_doc = RegisterDocumentBuilder() \ .add_control_delegation_obj(deleg_key) \ .build_from_existing(valid_doc) # Initialised without the delegation doc so a not found will be raised resolver_client = ResolverClientTest(docs={valid_doc.did: valid_doc}) with pytest.raises(IdentityInvalidDocumentError) as err_wrapper: is_validator_run_success( DocumentValidation.validate_document_against_resolver, resolver_client, valid_doc) assert isinstance(err_wrapper.value.__cause__, IdentityResolverError)