def test_convert_seed_to_mnemonic_raises_validation_error_if_invalid_language( valid_bip39_seed): with pytest.raises(IdentityValidationError) as err_wrp: KeyPairSecretsHelper.seed_bip39_to_mnemonic(valid_bip39_seed, lang='invalid_lang') assert 'Invalid language for mnemonic:' in str(err_wrp.value) assert isinstance(err_wrp.value.__cause__, FileNotFoundError)
def test_get_private_key_from_key_pair_secrets_raises_validation_error_if_invalid_method( valid_key_pair_secrets): key_pair_secret = KeyPairSecrets(valid_key_pair_secrets.seed, valid_key_pair_secrets.path, seed_method='plop', password='') with pytest.raises(IdentityValidationError): KeyPairSecretsHelper.get_private_key(key_pair_secret)
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 new_registered_identity(self, purpose: DIDType, key_pair_secrets: KeyPairSecrets, name: str = None, override_doc: bool = False) -> RegisteredIdentity: """ Create and register a new registered identity and its associated register document against the resolver. :param key_pair_secrets: new registered identity owner secrets :param name: Optional new registered identity name (default: '#<purpose>-0') following this pattern: '#[a-zA-Z\\-\\_0-9]{1, 24}' :param purpose: registered identity purpose (HOST, TWIN, USER or AGENT) :param override_doc: override registered identity document if already exist (default False) :return: registered identity :raises: IdentityValidationError: if invalid secrets IdentityValidationError: if invalid name IdentityInvalidDocumentError: if document build error IdentityResolverConflictError: register document already exists with different owners IdentityResolverError: if can not interact with the resolver IdentityDependencyError: if incompatible library dependency """ key_pair = KeyPairSecretsHelper.get_key_pair(key_pair_secrets) did = AdvancedIdentityLocalApi.create_identifier(key_pair.public_bytes) name = name or f'{ISSUER_SEPARATOR}{purpose}-0' issuer = Issuer.build(did, name) if override_doc: self.register_new_doc(key_pair_secrets, issuer, purpose) return RegisteredIdentity(key_pair_secrets, issuer) issuer_key = IssuerKey.build(issuer.did, issuer.name, key_pair.public_base58) return self.register_new_identity_if_not_exists(issuer_key, key_pair_secrets, purpose)
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 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 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_cannot_reuse_did_delegation_proof(get_delegation_method): twin1_secrets, twin1_issuer, twin1_doc = get_new_document('#Twin1') twin2_secrets, twin2_issuer, twin2_doc = get_new_document('#Twin2') agent_secrets, agent1_issuer, agent_doc = get_new_document('#Agent1') resolver_client = ResolverClientTest( docs={ twin1_doc.did: twin1_doc, twin2_doc.did: twin2_doc, agent_doc.did: agent_doc, }) api = AdvancedIdentityRegisterApi(resolver_client) # Create an agent did delegation proof (targeting a single delegating document: twin1) _, proof = AdvancedIdentityLocalApi.create_delegation_proof( delegating_issuer=twin1_issuer, subject_doc=agent_doc, subject_secrets=agent_secrets) # Add proof to twin1 document as a delegation proof - the document is valid get_delegation_method(api)( proof=proof, subject_issuer=agent1_issuer, delegation_name='#Deleg1', doc_owner_issuer=twin1_issuer, doc_owner_key_pair=KeyPairSecretsHelper.get_key_pair(twin1_secrets), ) assert is_validator_run_success(api.validate_register_document, resolver_client.docs[twin1_doc.did]) # Add proof to twin2 document as a delegation proof - the document is invalid get_delegation_method(api)( proof=proof, subject_issuer=agent1_issuer, delegation_name='#Deleg2', doc_owner_issuer=twin2_issuer, doc_owner_key_pair=KeyPairSecretsHelper.get_key_pair(twin2_secrets), ) with pytest.raises(IdentityInvalidDocumentDelegationError): api.validate_register_document(resolver_client.docs[twin2_doc.did])
def set_document_creator(self, identity: RegisteredIdentity, creator: str): """ Set creator to the register document associated to the provided registered identity. :param identity: registered identity :param creator: register document creator decentralised identifier :raises: IdentityValidationError: if invalid registered identity IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ key_pair = KeyPairSecretsHelper.get_key_pair(identity.key_pair_secrets) self.advanced_api.set_document_creator(key_pair, identity.issuer, creator)
def get_ownership_of_twin_from_registered_identity( self, twin_registered_identity: RegisteredIdentity, new_owner_registered_identity: RegisteredIdentity, new_owner_key_name: str): """ Get Ownership of a twin using a registered identity you owned. :param twin_registered_identity: twin registered identity :param new_owner_registered_identity: new owner registered identity :param new_owner_key_name: new owner key name :raises: IdentityValidationError: if invalid registered identities or name IdentityRegisterDocumentKeyConflictError: if new owner key name is not unique within the register document IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ new_owner_key_pair = KeyPairSecretsHelper.get_key_pair( new_owner_registered_identity.key_pair_secrets) current_owner_key_pair = KeyPairSecretsHelper.get_key_pair( twin_registered_identity.key_pair_secrets) self.advanced_api.add_public_key_to_document( new_owner_key_name, new_owner_key_pair.public_base58, current_owner_key_pair, twin_registered_identity.issuer)
def set_document_revoked(self, identity: RegisteredIdentity, revoked: bool): """ Set register document associated to the provided registered identity revoke field. :param identity: registered identity :param revoked: is register document revoked :raises: IdentityValidationError: if invalid registered identity IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ key_pair = KeyPairSecretsHelper.get_key_pair(identity.key_pair_secrets) self.advanced_api.set_document_revoked(key_pair, identity.issuer, revoked)
def build(key_pair: KeyPairSecrets, issuer: Issuer, content: bytes) -> 'Proof': """ Build a proof. :param key_pair: secrets used to build the proof signature :param issuer: proof issuer :param content: proof content :return: proof :raises: IdentityValidationError: if invalid secrets IdentityDependencyError: if incompatible library dependency """ private_key = KeyPairSecretsHelper.get_private_key(key_pair) sig = private_key.sign(content, ec.ECDSA(hashes.SHA256())) proof = base64.b64encode(sig).decode('ascii') return Proof(issuer=issuer, content=content, signature=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 create_generic_delegation_proof(subject_doc: RegisterDocument, subject_secrets: KeyPairSecrets) -> Tuple[Issuer, APIGenericDelegationProof]: """ Create a delegation proof that can be re-used to setup a delegation from several delegating issuers. :param subject_doc: subject/delegated register document :param subject_secrets: subject/delegated secrets :return: subject/delegated issuer and proof :raises: IdentityValidationError: if invalid secrets IdentityRegisterIssuerNotFoundError: if the issuer matching the subject/delegated secrets is not found IdentityDependencyError: if incompatible library dependency """ subject_key_pair = KeyPairSecretsHelper.get_key_pair(subject_secrets) subject_issuer = AdvancedIdentityLocalApi.get_issuer_by_public_key(subject_doc, subject_key_pair.public_base58) proof = APIGenericDelegationProof.build(subject_secrets, subject_issuer) return subject_issuer, proof
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 remove_ownership( self, removed_owner_issuer: Issuer, existing_owner_registered_identity: RegisteredIdentity): """ Remove owner from a register document. :param removed_owner_issuer: register document owner issuer to remove :param existing_owner_registered_identity: other existing doc owner registered identity :return: removed register document owner issuer :raises: IdentityValidationError: if invalid owner IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ existing_owner_key_pair = KeyPairSecretsHelper.get_key_pair( existing_owner_registered_identity.key_pair_secrets) return self.advanced_api.remove_public_key_from_document( removed_owner_issuer, existing_owner_key_pair, existing_owner_registered_identity.issuer)
def get_key_pair_from_twin( twin_seed: bytes, twin_key_name: str, seed_method: SeedMethod = SeedMethod.SEED_METHOD_BIP39, password: str = '') -> KeyPair: """ Get key pair from twin secrets :param twin_seed: twin seed (secrets) :param twin_key_name: twin key name (secrets) :param password: Optional twin password (secrets) :param seed_method: seed method (SEED_METHOD_NONE or SEED_METHOD_BIP39) default=SEED_METHOD_BIP39 :return: twin key pair :raises: IdentityValidationError: if invalid twin key seed IdentityValidationError: if invalid twin key name """ key_pair_secrets = build_twin_secrets(twin_seed, twin_key_name, seed_method, password) return KeyPairSecretsHelper.get_key_pair(key_pair_secrets)
def get_valid_delegated_doc_and_deleg_proof(seed: bytes, issuer_name: str, delegating_doc_id: str, deleg_name: str): secrets = KeyPairSecrets.build(seed, 'iotics/0/something/twindeleg') public_base58 = KeyPairSecretsHelper.get_public_key_base58_from_key_pair_secrets( secrets) public_bytes = base58.b58decode(public_base58) doc_id = make_identifier(public_bytes) issuer = Issuer.build(doc_id, issuer_name) proof = Proof.build(secrets, issuer, content=doc_id.encode()) deleg_key = get_delegation_register_proof( subject_key_pair_secrets=secrets, content=delegating_doc_id.encode(), p_type=DelegationProofType.DID, subject_issuer=Issuer.build(doc_id, issuer_name), deleg_key_name=deleg_name) delegated_doc = RegisterDocumentBuilder() \ .add_public_key_obj(RegisterPublicKey(issuer_name, public_base58, revoked=False)) \ .build(doc_id, DIDType.TWIN, proof=proof.signature, revoked=False) return delegated_doc, deleg_key
def create_agent_auth_token(agent_registered_identity: RegisteredIdentity, user_did: str, duration: int, audience: str = 'default') -> str: """ Create an agent authentication token. :param agent_registered_identity: agent registered identity :param user_did: user register document decentralised identifier :param duration: token duration in seconds :param audience: Optional token audience :return: encoded jwt token :raises: IdentityValidationError: if invalid agent identity IdentityValidationError: if invalid token data IdentityDependencyError: if incompatible library dependency """ agent_key_pair = KeyPairSecretsHelper.get_key_pair( agent_registered_identity.key_pair_secrets) return AdvancedIdentityLocalApi.create_agent_auth_token( agent_key_pair, agent_registered_identity.issuer, user_did, duration, audience)
def add_new_owner(self, new_owner_name: str, new_owner_public_key_base58: str, doc_registered_identity: RegisteredIdentity) -> Issuer: """ Add new register document owner. :param new_owner_name: new owner name :param new_owner_public_key_base58: new owner public base 58 key :param doc_registered_identity: doc owner registered identity :return: new register document owner issuer :raises: IdentityValidationError: if invalid new owner name IdentityValidationError: if invalid doc owner registered identity IdentityRegisterDocumentKeyConflictError: if new owner name is not unique within the register document IdentityInvalidDocumentError: if invalid register document IdentityResolverError: if resolver error """ key_pair = KeyPairSecretsHelper.get_key_pair( doc_registered_identity.key_pair_secrets) return self.advanced_api.add_public_key_to_document( new_owner_name, new_owner_public_key_base58, key_pair, doc_registered_identity.issuer)
def test_can_convert_mnemonic_to_seed_with_spanish( valid_bip39_mnemonic_spanish, valid_bip39_seed): seed = KeyPairSecretsHelper.mnemonic_bip39_to_seed( valid_bip39_mnemonic_spanish, lang='spanish') assert seed == valid_bip39_seed
def test_can_get_public_base58_key(valid_key_pair_secrets, valid_public_base58): public_base58_key = KeyPairSecretsHelper.get_public_key_base58_from_key_pair_secrets( valid_key_pair_secrets) assert public_base58_key == valid_public_base58
def test_can_convert_seed_to_mnemonic(valid_bip39_seed, valid_bip39_mnemonic_english): mnemonic = KeyPairSecretsHelper.seed_bip39_to_mnemonic(valid_bip39_seed) assert mnemonic == valid_bip39_mnemonic_english
def test_can_get_private_key_from_key_pair_secrets(valid_key_pair_secrets): private_key = KeyPairSecretsHelper.get_private_key(valid_key_pair_secrets) assert private_key.key_size == ec.SECP256K1().key_size assert private_key.curve.name == ec.SECP256K1().name
def test_validate_bip39_seed_should_raise_if_invalid_seed(): with pytest.raises(IdentityValidationError): KeyPairSecretsHelper.validate_bip39_seed(seed=b'invalid')
def test_validate_bip39_seed_should_not_raise_if_valid_seed(valid_bip39_seed): KeyPairSecretsHelper.validate_bip39_seed(valid_bip39_seed)
def test_convert_mnemonic_to_seed_raises_validation_error_if_invalid_language( valid_bip39_mnemonic_english): with pytest.raises(IdentityValidationError) as err_wrp: KeyPairSecretsHelper.mnemonic_bip39_to_seed( valid_bip39_mnemonic_english, lang='invalid_lang') assert isinstance(err_wrp.value.__cause__, FileNotFoundError)
def test_convert_mnemonic_to_seed_raises_validation_error_if_invalid_seed( invalid_mnemonic): with pytest.raises(IdentityValidationError): KeyPairSecretsHelper.mnemonic_bip39_to_seed(invalid_mnemonic)
def test_can_convert_seed_to_mnemonic_with_spanish( valid_bip39_seed, valid_bip39_mnemonic_spanish): mnemonic = KeyPairSecretsHelper.seed_bip39_to_mnemonic(valid_bip39_seed, lang='spanish') assert mnemonic == valid_bip39_mnemonic_spanish
def test_convert_seed_to_mnemonic_raises_validation_error_if_invalid_seed( invalid_seed, error_val): with pytest.raises(IdentityValidationError) as err_wrp: KeyPairSecretsHelper.seed_bip39_to_mnemonic(invalid_seed) assert error_val in str(err_wrp.value)