From 75791c168d810164fe69311b940ca68802bfef92 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Wed, 29 Jul 2020 14:30:18 -0700 Subject: [PATCH 1/4] Add did_key util and tests Signed-off-by: Nicholas Rempel --- aries_cloudagent/wallet/tests/test_util.py | 14 ++++++++++++++ aries_cloudagent/wallet/util.py | 18 ++++++++++++++++++ requirements.txt | 3 ++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/wallet/tests/test_util.py b/aries_cloudagent/wallet/tests/test_util.py index 827cef78e5..a032a44671 100644 --- a/aries_cloudagent/wallet/tests/test_util.py +++ b/aries_cloudagent/wallet/tests/test_util.py @@ -11,6 +11,8 @@ str_to_b64, set_urlsafe_b64, unpad, + naked_to_did_key, + did_key_to_naked, ) @@ -62,3 +64,15 @@ def test_pad(self): def test_b58(self): b58 = bytes_to_b58(BYTES) assert b58_to_bytes(b58) == BYTES + + def test_naked_to_did_key(self): + assert ( + naked_to_did_key("8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K") + == "did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th" + ) + + def test_did_key_to_naked(self): + assert ( + did_key_to_naked("did:key:z6MkmjY8GnV5i9YTDtPETC2uUAW6ejw3nk5mXF5yci5ab7th") + == "8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K" + ) diff --git a/aries_cloudagent/wallet/util.py b/aries_cloudagent/wallet/util.py index 808391172c..7987bbaf51 100644 --- a/aries_cloudagent/wallet/util.py +++ b/aries_cloudagent/wallet/util.py @@ -3,6 +3,8 @@ import base58 import base64 +from multicodec import add_prefix, remove_prefix + def pad(val: str) -> str: """Pad base64 values if need be: JWT calls to omit trailing padding.""" @@ -57,3 +59,19 @@ def b58_to_bytes(val: str) -> bytes: def bytes_to_b58(val: bytes) -> str: """Convert a byte string to base 58.""" return base58.b58encode(val).decode("ascii") + + +def naked_to_did_key(key: str) -> str: + """Convert a naked ed25519 verkey to did:key format.""" + key_bytes = b58_to_bytes(key) + prefixed_key_bytes = add_prefix("ed25519-pub", key_bytes) + did_key = f"did:key:z{bytes_to_b58(prefixed_key_bytes)}" + return did_key + + +def did_key_to_naked(did_key: str) -> str: + """Convert a did:key to naked ed25519 verkey format.""" + stripped_key = did_key.split("did:key:z").pop() + stripped_key_bytes = b58_to_bytes(stripped_key) + naked_key_bytes = remove_prefix(stripped_key_bytes) + return bytes_to_b58(naked_key_bytes) diff --git a/requirements.txt b/requirements.txt index b5f5c6e37d..ea89d1c2fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,5 @@ msgpack~=0.6.1 prompt_toolkit~=2.0.9 pynacl~=1.3.0 requests~=2.23.0 -pyld==2.0.1 \ No newline at end of file +pyld==2.0.1 +py_multicodec==0.2.1 \ No newline at end of file From 8e9174cacc1477d2816a558021284a1ef3e2dd9e Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Wed, 29 Jul 2020 15:05:55 -0700 Subject: [PATCH 2/4] Transform to and from naked key in oob manager Signed-off-by: Nicholas Rempel --- .../protocols/out_of_band/v1_0/manager.py | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 532d7fcd0c..bfa09a4098 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -6,6 +6,7 @@ from ....config.injection_context import InjectionContext from ....core.error import BaseError from ....ledger.base import BaseLedger +from ....wallet.util import did_key_to_naked, naked_to_did_key from ....protocols.connections.v1_0.manager import ConnectionManager from ....protocols.connections.v1_0.messages.connection_invitation import ( ConnectionInvitation, @@ -129,7 +130,7 @@ async def create_invitation( # We plug into existing connection structure during migration phase if use_public_did: # service = (await wallet.get_public_did()).did - service = connection_invitation.did + service = f"did:sov:{connection_invitation.did}" else: # connection_key = await wallet.create_signing_key() # service = ServiceMessage( @@ -142,8 +143,17 @@ async def create_invitation( service = ServiceMessage( _id="#inline", _type="did-communication", - recipient_keys=connection_invitation.recipient_keys, - routing_keys=connection_invitation.routing_keys, + recipient_keys=[ + naked_to_did_key(key) + for key in connection_invitation.recipient_keys + ] + if connection_invitation.recipient_keys + else None, + routing_keys=[ + naked_to_did_key(key) for key in connection_invitation.routing_keys + ] + if connection_invitation.routing_keys + else None, service_endpoint=connection_invitation.endpoint, ).validate() @@ -196,9 +206,20 @@ async def receive_invitation( # Get the single service item if invitation_message.service_blocks: service = invitation_message.service_blocks[0] + service.recipient_keys = ( + [did_key_to_naked(key) for key in service.recipient_keys] + if service.recipient_keys + else None + ) + service.routing_keys = ( + [did_key_to_naked(key) for key in service.routing_keys] + if service.routing_keys + else None + ) + else: # If it's in the did format, we need to convert to a full service block - service_did = invitation_message.service_dids[0] + service_did = invitation_message.service_dids[0].split("did:sov:").pop() async with ledger: verkey = await ledger.get_key_for_did(service_did) endpoint = await ledger.get_endpoint_for_did(service_did) From 97bf19260fe88e7651e9f416bf8002aaa1c67327 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Wed, 29 Jul 2020 15:28:53 -0700 Subject: [PATCH 3/4] Apply key transforms in oob manager Signed-off-by: Nicholas Rempel --- .../protocols/out_of_band/v1_0/manager.py | 33 ++++++++++--------- .../out_of_band/v1_0/messages/service.py | 6 ++-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index bfa09a4098..1d614484d0 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -130,7 +130,7 @@ async def create_invitation( # We plug into existing connection structure during migration phase if use_public_did: # service = (await wallet.get_public_did()).did - service = f"did:sov:{connection_invitation.did}" + service = connection_invitation.did else: # connection_key = await wallet.create_signing_key() # service = ServiceMessage( @@ -148,12 +148,12 @@ async def create_invitation( for key in connection_invitation.recipient_keys ] if connection_invitation.recipient_keys - else None, + else [], routing_keys=[ naked_to_did_key(key) for key in connection_invitation.routing_keys ] if connection_invitation.routing_keys - else None, + else [], service_endpoint=connection_invitation.endpoint, ).validate() @@ -206,28 +206,19 @@ async def receive_invitation( # Get the single service item if invitation_message.service_blocks: service = invitation_message.service_blocks[0] - service.recipient_keys = ( - [did_key_to_naked(key) for key in service.recipient_keys] - if service.recipient_keys - else None - ) - service.routing_keys = ( - [did_key_to_naked(key) for key in service.routing_keys] - if service.routing_keys - else None - ) else: # If it's in the did format, we need to convert to a full service block - service_did = invitation_message.service_dids[0].split("did:sov:").pop() + service_did = invitation_message.service_dids[0] async with ledger: verkey = await ledger.get_key_for_did(service_did) + did_key = naked_to_did_key(verkey) endpoint = await ledger.get_endpoint_for_did(service_did) service = ServiceMessage.deserialize( { "id": "#inline", "type": "did-communication", - "recipientKeys": [verkey], + "recipientKeys": [did_key], "routingKeys": [], "serviceEndpoint": endpoint, } @@ -245,6 +236,18 @@ async def receive_invitation( "request block must be empty for invitation message type." ) + # Transform back to 'naked' verkey + service.recipient_keys = ( + [did_key_to_naked(key) for key in service.recipient_keys] + if service.recipient_keys + else [] + ) + service.routing_keys = ( + [did_key_to_naked(key) for key in service.routing_keys] + if service.routing_keys + else [] + ) + # Convert to the old message format connection_invitation = ConnectionInvitation.deserialize( { diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/messages/service.py b/aries_cloudagent/protocols/out_of_band/v1_0/messages/service.py index 1393a43609..f2cb1e571c 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/messages/service.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/messages/service.py @@ -5,7 +5,7 @@ from marshmallow import fields from .....messaging.models.base import BaseModel, BaseModelSchema -from .....messaging.valid import INDY_DID, INDY_RAW_PUBLIC_KEY +from .....messaging.valid import INDY_DID, DID_KEY class Service(BaseModel): @@ -58,14 +58,14 @@ class Meta: did = fields.Str(required=False, description="", **INDY_DID) recipient_keys = fields.List( - fields.Str(description="Recipient public key", **INDY_RAW_PUBLIC_KEY), + fields.Str(description="Recipient public key", **DID_KEY), data_key="recipientKeys", required=False, description="List of recipient keys", ) routing_keys = fields.List( - fields.Str(description="Routing key", **INDY_RAW_PUBLIC_KEY), + fields.Str(description="Routing key", **DID_KEY), data_key="routingKeys", required=False, description="List of routing keys", From 950d96a19477e46272602090a0d2d8af0716f2c9 Mon Sep 17 00:00:00 2001 From: Nicholas Rempel Date: Tue, 11 Aug 2020 11:27:31 -0700 Subject: [PATCH 4/4] Cleaner list comprehensions Signed-off-by: Nicholas Rempel --- .../protocols/out_of_band/v1_0/manager.py | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 1d614484d0..aa3cb14435 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -145,15 +145,12 @@ async def create_invitation( _type="did-communication", recipient_keys=[ naked_to_did_key(key) - for key in connection_invitation.recipient_keys - ] - if connection_invitation.recipient_keys - else [], + for key in connection_invitation.recipient_keys or [] + ], routing_keys=[ - naked_to_did_key(key) for key in connection_invitation.routing_keys - ] - if connection_invitation.routing_keys - else [], + naked_to_did_key(key) + for key in connection_invitation.routing_keys or [] + ], service_endpoint=connection_invitation.endpoint, ).validate() @@ -237,16 +234,12 @@ async def receive_invitation( ) # Transform back to 'naked' verkey - service.recipient_keys = ( - [did_key_to_naked(key) for key in service.recipient_keys] - if service.recipient_keys - else [] - ) - service.routing_keys = ( - [did_key_to_naked(key) for key in service.routing_keys] - if service.routing_keys - else [] - ) + service.recipient_keys = [ + did_key_to_naked(key) for key in service.recipient_keys or [] + ] + service.routing_keys = [ + did_key_to_naked(key) for key in service.routing_keys + ] or [] # Convert to the old message format connection_invitation = ConnectionInvitation.deserialize(