def test_private_identity_converted_to_delegate() -> None: """Creates private identity and converts it to a delegate.""" identity = PrivateIdentity( CryptoProtocol.SHA256WithRSA.generate_private_key(), "1234567" ) delegate = identity.delegate_of("abcdefgh") assert delegate.identity_id == "1234567" assert isinstance(delegate.public_key, PublicKey) assert delegate.delegator_identity_id == "abcdefgh"
def test_create_identity_add_delegate_content(identity: PrivateIdentity) -> None: """Request content to add a delegate to an identity.""" delegate = identity.delegate_of("abcdefgh") bytes_content, authorisation = identity.sign_entity(delegate) content = json.loads(bytes_content) assert content["_type"] == "AddDelegateRequest" assert content["delegateIdentityId"] == delegate.identity_id assert content["delegatorIdentityId"] == delegate.delegator_identity_id
def test_create_request_with_content_claims( client: Client, endorser: PrivateIdentity, mocked_requests_200: respx.MockTransport, entity: Entity, ) -> None: """Create endorsement request provided by a 3rd party endorser.""" claims = [b"claim-1", b"claim-2"] # Create endorsement request with authorisation of endorser content, authorisation = endorser.endorse(entity, claims) # This will also add the subject holders authorisation client.put( entity, claims=claims, content=content, authorisations=[authorisation], create_claims=True # endorse=True # TODO - provide test that this does not have any effect ) http_request, _ = mocked_requests_200["create_entity"].calls[0] claims_header = json.loads( iov42_decode(http_request.headers["x-iov42-claims"])) assert claims_header == {hashed_claim(c): c.decode() for c in claims}
def test_create_request_with_content( client: Client, endorser: PrivateIdentity, mocked_requests_200: respx.MockTransport, entity: Entity, ) -> None: """Create endorsement request provided by a 3rd party endorser.""" claims = [b"claim-1", b"claim-2"] # Create endorsement request with authorisation of endorser content, authorisation = endorser.endorse(entity, claims) # This will also add the subject holders authorisation client.put( entity, claims=claims, content=content, authorisations=[authorisation], # endorse=True # TODO - provide test that this does not have any effect ) http_request, _ = mocked_requests_200["create_entity"].calls[0] request_id = json.loads(content.decode())["requestId"] assert http_request.url.path.rsplit("/", 1)[1] == request_id claims_header = json.loads( iov42_decode(http_request.headers["x-iov42-claims"])) assert claims_header == {} authorisations = json.loads( iov42_decode(http_request.headers["x-iov42-authorisations"].encode())) expected_identities = [a["identityId"] for a in authorisations] assert client.identity.identity_id in expected_identities assert endorser.identity_id in expected_identities
def main() -> None: """Create identity, asset type and register products.""" product_data = read_product_data("nfc-tags.csv") # Usually we would store the identity (ID and key) on a safe place. manufacturer = PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key()) with Client(cfg.iov42_platform["url"], manufacturer) as client: # Create the identity client.put(manufacturer.public_identity) print(f"Created manufacturer identity: {manufacturer.identity_id}") # Create the asset typ used for the NFC tags. tag_type = AssetType() client.put(tag_type) print(f"Created tag asset type: {tag_type}") # Register the NFC tags on the distributed ledger in parallel. with ThreadPoolExecutor(max_workers=20) as executor: _ = executor.map( register_product, [client] * len(product_data), [tag_type] * len(product_data), product_data, ) executor.shutdown(wait=True)
def test_raise_identity_already_exists_2(client: Client) -> None: """Raise exception when an identity (with an other key) already exists.""" client.identity = PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key(), "test-1234") with respx.mock(base_url=PLATFORM_URL) as respx_mock: respx_mock.put( re.compile("/api/v1/requests/.*$"), status_code=400, content= ('{"errors":[{"errorCode":2503,"errorType":"Authorisation",' '"message":"Signature L-IjTeba3wvJn4hHR40GPCG-H7iIeDWOzBo3hCK7x1mLZgif' "SdgR-YVxOZtvPzHaI86WdhIL3y-sNOwYUf2c0j7OfT31dAX71W9le-Cp2Mx1PgjjqI09f" "i0Nku-h5lgipQ07VKAm3gUx0foeG9GdDQe_I85QuCqtJsaAXWDVc8r0NeWpa3dnQEflIm" "W0-gecjO6pYDeyXPALcvp9h8Q_TxkuGVvreqpWvgKzdPMlXHMbN3wYoLNNLM3gpqrqAp" "Eze1aTqtlK6gCQUuhsJlKe4Bb2Nj8MRxXXXNpxIJqjJHM0IRps5J0U8gsnEEcny8Zf0tB" 'h7NGkTteNv554QUbNVA cannot be verified with public credentials of identity test-1234."},' '{"errorCode":2503,"errorType":"Authorisation","message":"Signature ' "L2PIREIx1MZsjV-j0fSMoN3u1eHP2wyqUpAs1mOWdp8k8yrnoBTbyH2Uxw8_9zYTzDHrz" "rI16fNKeRFuLlHosWqzoUf41M0Nip5zbW6gmPYiL05AWPdH1pg9qS-cgQa9IFXiMUkZh9" "EZltT7HHl9aRn35kcwoJYAoPm96Up1YPI0JWISx1iXXEAcxVOA1N_k-l0tT5Tb7lWNOI4" "5eh6flW_vVEeBQDjQhkl94rlP3qDFlDYZ9HZS2A3lTkiIo6MsU57pxeTD9FqwZ8uofJ3O" "Yx05TJKl106GPsscf2mnpnQGEzgS20QsJyqUs_u7dpZbAcjfBsaHucVz8gwkz_PoNg " 'cannot be verified with public credentials of identity test-1234."},' '{"errorCode":2602,"errorType":"AssetExistence",' '"message":"Another identity with address test-1234 already exists"}],' '"requestId":"23343439","proof":"/api/v1/proofs/23343439"}'), ) with pytest.raises(EntityAlreadyExists) as excinfo: client.put(client.identity.public_identity, request_id="1234567") assert str(excinfo.value) == "identity 'test-1234' already exists" assert excinfo.value.request_id == "1234567"
def test_add_delegate_to_identity( alice: PrivateIdentity, bob: PrivateIdentity, alice_client: Client, ) -> None: """Adds a delegate to an identity.""" delegate = bob.delegate_of(alice.identity_id) content, authorisation = bob.sign_entity(delegate) response = alice_client.put(delegate, content=content, authorisations=[authorisation]) for r in response.resources: # type: ignore[union-attr] if "identities/" in r: assert alice.identity_id in r
def test_private_identity() -> None: """Creates private identity with ID.""" identity = PrivateIdentity( CryptoProtocol.SHA256WithRSA.generate_private_key(), "1234567" ) assert identity.identity_id == "1234567" assert isinstance(identity.private_key, PrivateKey)
def delegate(identity: PrivateIdentity) -> PrivateIdentity: """Generate a private delegate identity for test runs.""" return PrivateIdentity( private_key=identity.private_key, identity_id=identity.identity_id, delegate_identity_id="abcdefgh", )
def test_private_identity_invalid_delegate_identity_id(invalid_id: str) -> None: """Raises ValueError in case the provided ID is invalid.""" with pytest.raises(ValueError) as excinfo: PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key(), "123456", invalid_id ) assert ( str(excinfo.value) == f"invalid identifier '{invalid_id}' - valid characters are [a-zA-Z0-9._\\-+]" )
def create_identity(ctx: click.core.Context, identity_id: str, crypto_protocol: str) -> None: """Create an identity.""" private_key = generate_private_key(crypto_protocol) identity = PrivateIdentity(private_key, identity_id) client = Client(ctx.obj["url"], identity) _ = client.put(identity.public_identity, request_id=ctx.obj["request_id"]) print(_identity_json(identity))
def test_create_identity_endorsements_content(identity: PrivateIdentity) -> None: """Request type to create claims and endorsements against an identity.""" request_id = "123456" claims = [b"claim-1", b"claim-2"] content = json.loads( identity.public_identity.put_request_content( request_id=request_id, claims=claims, endorser=identity ) ) # Signatures are always different, we have to verify the signature endorsements = content.pop("endorsements") assert content == { "_type": "CreateIdentityEndorsementsRequest", "subjectId": identity.identity_id, "endorserId": identity.identity_id, "requestId": request_id, } for c, s in endorsements.items(): identity.verify_signature(s, ";".join((identity.identity_id, c)).encode())
def test_content_create_endorsements(identity: PrivateIdentity) -> None: """Request content to create claims and endorsements for an unique asset.""" request_id = "123456" claims = [b"claim-1", b"claim-2"] asset = Asset(asset_type_id="98765") content = json.loads( asset.put_request_content( claims=claims, endorser=identity, request_id=request_id ) ) endorsements = content.pop("endorsements") assert content == { "_type": "CreateAssetEndorsementsRequest", "subjectId": asset.asset_id, "subjectTypeId": asset.asset_type_id, "endorserId": identity.identity_id, "requestId": request_id, } for c, s in endorsements.items(): identity.verify_signature( s, ";".join((asset.asset_id, asset.asset_type_id, c)).encode() )
def test_raise_identity_already_exists(client: Client) -> None: """Raise exception when an identity already exists.""" client.identity = PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key(), "test-1234") with respx.mock(base_url=PLATFORM_URL) as respx_mock: respx_mock.put( re.compile("/api/v1/requests/.*$"), status_code=400, content= ('{"errors":[{"errorCode":2602,"errorType":"AssetExistence",' '"message":"Another identity with address test-1234 already exists"}],' '"requestId":"1234567","proof":"/api/v1/proofs/23343456"}'), ) with pytest.raises(EntityAlreadyExists) as excinfo: client.put(client.identity.public_identity, request_id="1234567") assert str(excinfo.value) == "identity 'test-1234' already exists" assert excinfo.value.request_id == "1234567"
def test_3rd_party_endorsements_on_new_claims( alice_client: Client, existing_asset: Asset, bob: PrivateIdentity, ) -> None: """Provide endorsements on someone elses claims which do not exist yet.""" new_claims = [b"alice-claim-100", b"alice-claims-200"] content, authorisation = bob.endorse(existing_asset, new_claims) # Content and authorisation has to be handed over from the endorser to the # identity owning the asset. The asset owner creates the request to add the # claims with endorsements which implicitely adds also the owner's authorisation. response = alice_client.put( existing_asset, claims=new_claims, content=content, authorisations=[authorisation], create_claims=True, ) for r in response.resources: # type: ignore[union-attr] if "endorsements/" in r: assert bob.identity_id in r
def identity(private_key: PrivateKey) -> PrivateIdentity: """Generate private identity for test runs.""" return PrivateIdentity(private_key)
def _load_identity(identity: str) -> PrivateIdentity: with open(identity) as identity_file: id = json.load(identity_file) return PrivateIdentity(load_private_key(id["private_key"]), id["identity_id"])
def endorser() -> PrivateIdentity: """Mock an identity used to endorse claims on different entities.""" return PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key())
def test__private_identity_raises_typerror() -> None: """Raise TypeError in case no private key is provided.""" with pytest.raises(TypeError) as excinfo: PrivateIdentity("123456") # type: ignore[arg-type] assert str(excinfo.value) == "must be PrivateKey, not str"
def test_private_identity_generated_id() -> None: """If no ID is provided generate an UUID as ID.""" identity = PrivateIdentity(CryptoProtocol.SHA256WithECDSA.generate_private_key()) assert uuid.UUID(identity.identity_id)
def alice() -> PrivateIdentity: """Returns Alice's new identity with which we create stuff.""" # TODO: identity = Identity(CryptoProtocol.SHA256WithECDSA) return PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key())
def bob() -> PrivateIdentity: """Create Bob's identity used to endorse claims on Alice or her assets.""" return PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key())
import typing import uuid import pytest from iov42.core import CryptoProtocol from iov42.core import hashed_claim from iov42.core import PrivateIdentity from iov42.core import PrivateKey from iov42.core import PublicIdentity from iov42.core import PublicKey identities = [ PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key(), identity_id="my.private.identity", ), PublicIdentity(identity_id="my.public.identity"), ] def test_private_identity() -> None: """Creates private identity with ID.""" identity = PrivateIdentity( CryptoProtocol.SHA256WithRSA.generate_private_key(), "1234567" ) assert identity.identity_id == "1234567" assert isinstance(identity.private_key, PrivateKey) def test_private_identity_with_delegate_identity_id() -> None:
import pytest import respx from iov42.core import Asset from iov42.core import AssetType from iov42.core import Client from iov42.core import CryptoProtocol from iov42.core import Entity from iov42.core import hashed_claim from iov42.core import InvalidSignature from iov42.core import PrivateIdentity from iov42.core._crypto import iov42_decode entities = [ PrivateIdentity( CryptoProtocol.SHA256WithECDSA.generate_private_key()).public_identity, AssetType(), Asset(asset_type_id="123456"), ] def id_class_name(value: typing.Any) -> str: """Provide class name for test identifier.""" return str(value.__class__.__name__) def test_hased_claim() -> None: """Hash of a claim.""" assert "RIREN5QE4J55V0aOmXdmRWOoSV_EIUtf0o_tdF4hInM" == hashed_claim( b"claim-1")