Skip to content

Commit f731fa0

Browse files
committed
fix: multiple fixes after more testing
Signed-off-by: Daniel Bluhm <[email protected]>
1 parent 8f0d317 commit f731fa0

File tree

8 files changed

+205
-110
lines changed

8 files changed

+205
-110
lines changed

didcomm_messaging/crypto/base.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ def key_bytes_from_verification_method(cls, vm: VerificationMethod) -> bytes:
4646
return decoded
4747
else:
4848
codec, decoded = multicodec.unwrap(decoded)
49-
expected_codec = cls.type_to_codec.get(vm.type)
50-
if not expected_codec:
51-
raise ValueError("Unsupported verification method type")
52-
if codec.name != expected_codec:
53-
raise ValueError("Type and codec mismatch")
49+
if vm.type != "Multikey":
50+
expected_codec = cls.type_to_codec.get(vm.type)
51+
if not expected_codec:
52+
raise ValueError("Unsupported verification method type")
53+
if codec.name != expected_codec:
54+
raise ValueError("Type and codec mismatch")
5455
return decoded
5556

5657
if vm.public_key_base58:

didcomm_messaging/crypto/jwe.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ def _deserialize(cls, parsed: Mapping[str, Any]) -> "JweEnvelope": # noqa: C901
312312
inst = cls(
313313
recipients=recipients,
314314
protected=protected,
315-
protected_b64=protected_b64,
315+
protected_b64=protected_b64.encode(),
316316
unprotected=unprotected,
317317
ciphertext=ciphertext,
318318
iv=iv,

didcomm_messaging/legacy/messaging.py

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import json
55
from typing import Generic, Optional, Sequence, Union
66

7+
import base58
78
from pydantic import AnyUrl
89
from pydid import VerificationMethod
910
from pydid.service import DIDCommV1Service
1011

11-
from didcomm_messaging.crypto import SecretsManager, P, S
12+
from didcomm_messaging.crypto import P, S, SecretsManager
1213
from didcomm_messaging.legacy.base import LegacyCryptoService
1314
from didcomm_messaging.legacy.packaging import LegacyPackagingService
15+
from didcomm_messaging.multiformats import multibase, multicodec
1416
from didcomm_messaging.resolver import DIDResolver
1517

1618

@@ -57,6 +59,13 @@ class Target:
5759
class LegacyDIDCommMessagingService(Generic[P, S]):
5860
"""Main entrypoint for DIDComm Messaging."""
5961

62+
def multikey_to_kid(self, multikey: str) -> str:
63+
"""Return a kid from a multikey."""
64+
codec, data = multicodec.unwrap(multibase.decode(multikey))
65+
if codec != multicodec.multicodec("ed25519-pub"):
66+
raise LegacyDIDCommMessagingError("DIDComm v1 requires ed25519 keys")
67+
return base58.b58encode(data).decode()
68+
6069
async def did_to_target(
6170
self, crypto: LegacyCryptoService[P, S], resolver: DIDResolver, did: str
6271
) -> Target:
@@ -72,27 +81,55 @@ async def did_to_target(
7281
target = services[0]
7382

7483
recipient_keys = [
75-
crypto.verification_method_to_public_key(
76-
doc.dereference_as(VerificationMethod, recip)
77-
).kid
84+
self.multikey_to_kid(
85+
crypto.verification_method_to_public_key(
86+
doc.dereference_as(VerificationMethod, recip)
87+
).multikey
88+
)
7889
for recip in target.recipient_keys
7990
]
8091
routing_keys = [
81-
crypto.verification_method_to_public_key(
82-
doc.dereference_as(VerificationMethod, routing_key)
83-
).kid
92+
self.multikey_to_kid(
93+
crypto.verification_method_to_public_key(
94+
doc.dereference_as(VerificationMethod, routing_key)
95+
).multikey
96+
)
8497
for routing_key in target.routing_keys
8598
]
8699
endpoint = target.service_endpoint
87100
if isinstance(endpoint, AnyUrl):
88101
endpoint = str(endpoint)
89-
if not endpoint.startswith("http") or not endpoint.startswith("ws"):
102+
if not endpoint.startswith("http") and not endpoint.startswith("ws"):
90103
raise LegacyDIDCommMessagingError(
91104
f"Unable to send message to endpoint {endpoint}"
92105
)
93106

94107
return Target(recipient_keys, routing_keys, endpoint)
95108

109+
async def from_did_to_kid(
110+
self, crypto: LegacyCryptoService[P, S], resolver: DIDResolver, did: str
111+
) -> str:
112+
"""Resolve our DID to a kid to be used by crypto layers."""
113+
doc = await resolver.resolve_and_parse(did)
114+
services = [
115+
service
116+
for service in doc.service or []
117+
if isinstance(service, DIDCommV1Service)
118+
]
119+
if not services:
120+
raise LegacyDIDCommMessagingError(f"Unable to send message to DID {did}")
121+
target = services[0]
122+
123+
recipient_keys = [
124+
self.multikey_to_kid(
125+
crypto.verification_method_to_public_key(
126+
doc.dereference_as(VerificationMethod, recip)
127+
).multikey
128+
)
129+
for recip in target.recipient_keys
130+
]
131+
return recipient_keys[0]
132+
96133
def forward_wrap(self, to: str, msg: str) -> bytes:
97134
"""Wrap a message in a forward."""
98135
forward = {
@@ -109,7 +146,7 @@ async def pack(
109146
secrets: SecretsManager[S],
110147
packaging: LegacyPackagingService[P, S],
111148
message: Union[dict, str, bytes],
112-
to: str,
149+
to: Union[str, Target],
113150
frm: Optional[str] = None,
114151
**options,
115152
):
@@ -121,9 +158,10 @@ async def pack(
121158
secrets: secrets manager to use to look up private key material
122159
packaging: packaging service
123160
routing: routing service
124-
message: to send
125-
to: recipient of the message, expressed as a DID
126-
frm: the sender of the message, expressed as a DID
161+
message: to send; must be str, bytes, or json serializable dict
162+
to: recipient of the message, expressed as a DID or Target
163+
frm: the sender of the message, expressed as a DID or kid (base58 encoded
164+
public key)
127165
options: arbitrary values to pass to the packaging service
128166
129167
Returns:
@@ -139,7 +177,13 @@ async def pack(
139177
else:
140178
raise TypeError("message must be bytes, str, or dict")
141179

142-
target = await self.did_to_target(crypto, resolver, to)
180+
if isinstance(to, str):
181+
target = await self.did_to_target(crypto, resolver, to)
182+
else:
183+
target = to
184+
185+
if frm and frm.startswith("did:"):
186+
frm = await self.from_did_to_kid(crypto, resolver, frm) if frm else None
143187

144188
encoded_message = await packaging.pack(
145189
crypto,
@@ -202,18 +246,17 @@ def __init__(
202246
async def pack(
203247
self,
204248
message: Union[dict, str, bytes],
205-
to: str,
249+
to: Union[str, Target],
206250
frm: Optional[str] = None,
207251
**options,
208252
) -> LegacyPackResult:
209253
"""Pack a message.
210254
211255
Args:
212-
message: to send
213-
to: recipient of the message, expressed as a KID which is a Base58
214-
encoded Ed25519 public key
215-
frm: the sender of the message, expressed as a KID which is a Base58
216-
encoded Ed25519 public key
256+
message: to send; must be str, bytes, or json serializable dict
257+
to: recipient of the message, expressed as a DID or Target
258+
frm: the sender of the message, expressed as a DID or kid (base58 encoded
259+
public key)
217260
options: arbitrary values to pass to the packaging service
218261
219262
Returns:

didcomm_messaging/legacy/packaging.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ def extract_pack_recipients(self, recipients: Sequence[JweRecipient]):
3131
ValueError: If the recipients block is malformed
3232
3333
"""
34-
result = {}
34+
seen_recips = []
3535
for recip in recipients:
3636
recip_vk_b58 = recip.header.get("kid")
3737
if not recip_vk_b58:
3838
raise ValueError("Blank recipient key")
39-
if recip_vk_b58 in result:
39+
if recip_vk_b58 in seen_recips:
4040
raise ValueError("Duplicate recipient key")
41+
seen_recips.append(recip_vk_b58)
4142

4243
sender_b64 = recip.header.get("sender")
4344
enc_sender = self.b64url.decode(sender_b64) if sender_b64 else None
@@ -50,7 +51,6 @@ def extract_pack_recipients(self, recipients: Sequence[JweRecipient]):
5051
nonce = self.b64url.decode(nonce_b64) if nonce_b64 else None
5152

5253
yield RecipData(recip_vk_b58, enc_sender, nonce, recip.encrypted_key)
53-
return result
5454

5555
async def extract_packed_message_metadata(
5656
self, secrets: SecretsManager[S], wrapper: JweEnvelope

didcomm_messaging/resolver/peer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Peer2(DIDResolver):
2020

2121
async def is_resolvable(self, did: str) -> bool:
2222
"""Check to see if a DID is resolvable."""
23-
return peer_2_pattern.match(did)
23+
return bool(peer_2_pattern.match(did))
2424

2525
async def resolve(self, did: str) -> dict:
2626
"""Resolve a did:peer:2 DID."""
@@ -32,7 +32,9 @@ class Peer4(DIDResolver):
3232

3333
async def is_resolvable(self, did: str) -> bool:
3434
"""Check to see if a DID is resolvable."""
35-
return peer_4_pattern_short.match(did) or peer_4_pattern_long.match(did)
35+
return bool(peer_4_pattern_short.match(did)) or bool(
36+
peer_4_pattern_long.match(did)
37+
)
3638

3739
async def resolve(self, did: str) -> dict:
3840
"""Resolve a did:peer:4 DID."""

tests/legacy/conftest.py

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1+
from aries_askar import Key, KeyAlg, Store
2+
import base58
3+
from did_peer_4.input_doc import KeySpec, input_doc_from_keys_and_services
4+
import did_peer_4
15
import pytest
26
import pytest_asyncio
3-
import base58
47

5-
from didcomm_messaging.crypto.backend.askar import AskarSecretsManager
8+
from didcomm_messaging.crypto.backend.askar import AskarKey, AskarSecretsManager
69
from didcomm_messaging.legacy.askar import AskarLegacyCryptoService, AskarSecretKey
7-
from didcomm_messaging.legacy.nacl import InMemSecretsManager, NaclLegacyCryptoService
8-
9-
from aries_askar import Key, KeyAlg, Store
10-
10+
from didcomm_messaging.legacy.messaging import LegacyDIDCommMessaging
11+
from didcomm_messaging.legacy.nacl import (
12+
EdPublicKey,
13+
InMemSecretsManager,
14+
KeyPair,
15+
NaclLegacyCryptoService,
16+
)
1117
from didcomm_messaging.legacy.packaging import LegacyPackagingService
18+
from didcomm_messaging.resolver import DIDResolver
19+
from didcomm_messaging.resolver.peer import Peer4
1220

1321

1422
@pytest.fixture
@@ -38,38 +46,80 @@ async def askar_secrets(store: Store):
3846

3947

4048
@pytest.fixture
41-
def alice(nacl_secrets: InMemSecretsManager):
42-
"""Generate alice's keys."""
43-
yield nacl_secrets.create()
49+
def packer():
50+
yield LegacyPackagingService()
4451

4552

4653
@pytest.fixture
47-
def bob(nacl_secrets: InMemSecretsManager):
48-
"""Generate bob's keys."""
49-
yield nacl_secrets.create()
54+
def resolver():
55+
yield Peer4()
5056

5157

5258
@pytest.fixture
53-
def packer():
54-
yield LegacyPackagingService()
55-
59+
def alice(
60+
nacl: NaclLegacyCryptoService,
61+
nacl_secrets: InMemSecretsManager,
62+
packer: LegacyPackagingService,
63+
resolver: DIDResolver,
64+
):
65+
yield LegacyDIDCommMessaging(nacl, nacl_secrets, resolver, packer)
5666

57-
@pytest_asyncio.fixture
58-
async def askarlice(store: Store):
59-
"""Generate alice's keys."""
6067

61-
key = Key.generate(KeyAlg.ED25519)
62-
kid = base58.b58encode(key.get_public_bytes()).decode()
63-
async with store.session() as session:
64-
await session.insert_key(kid, key)
65-
return AskarSecretKey(key, kid)
68+
@pytest.fixture
69+
def bob(
70+
askar: AskarLegacyCryptoService,
71+
askar_secrets: AskarSecretsManager,
72+
resolver: DIDResolver,
73+
packer: LegacyPackagingService,
74+
):
75+
yield LegacyDIDCommMessaging(askar, askar_secrets, resolver, packer)
6676

6777

6878
@pytest_asyncio.fixture
69-
async def bobskar(store: Store):
79+
async def bob_key(store: Store):
7080
"""Generate bob's keys."""
7181
key = Key.generate(KeyAlg.ED25519)
7282
kid = base58.b58encode(key.get_public_bytes()).decode()
7383
async with store.session() as session:
7484
await session.insert_key(kid, key)
7585
return AskarSecretKey(key, kid)
86+
87+
88+
@pytest.fixture
89+
def alice_key(nacl_secrets: InMemSecretsManager):
90+
"""Generate alice's keys."""
91+
yield nacl_secrets.create()
92+
93+
94+
@pytest.fixture
95+
def alice_did(alice_key: KeyPair):
96+
alice_pub = EdPublicKey(alice_key.verkey)
97+
input_doc = input_doc_from_keys_and_services(
98+
[KeySpec(alice_pub.multikey, relationships=["authentication"])],
99+
[
100+
{
101+
"id": "#didcomm",
102+
"type": "did-communication",
103+
"recipientKeys": ["#key-0"],
104+
"serviceEndpoint": "https://example.com",
105+
}
106+
],
107+
)
108+
return did_peer_4.encode(input_doc, validate=True)
109+
110+
111+
@pytest.fixture
112+
def bob_did(bob_key: AskarSecretKey):
113+
bob_pub = AskarKey(bob_key.key, bob_key.kid)
114+
input_doc = input_doc_from_keys_and_services(
115+
[KeySpec(bob_pub.multikey, relationships=["authentication"])],
116+
[
117+
{
118+
"id": "#didcomm",
119+
"type": "did-communication",
120+
"recipientKeys": ["#key-0"],
121+
"serviceEndpoint": "https://example.com",
122+
}
123+
],
124+
)
125+
return did_peer_4.encode(input_doc, validate=True)

tests/legacy/test_crypto.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
"""Test Pack and Unpack."""
22

3+
import pytest
34
from didcomm_messaging.legacy import crypto
45
from didcomm_messaging.legacy.nacl import KeyPair
56

67

8+
@pytest.fixture
9+
def alice():
10+
"""Generate alice's keys."""
11+
yield KeyPair(*crypto.create_keypair())
12+
13+
14+
@pytest.fixture
15+
def bob():
16+
"""Generate bob's keys."""
17+
yield KeyPair(*crypto.create_keypair())
18+
19+
720
def test_pack_unpack_auth(alice: KeyPair, bob: KeyPair):
821
"""Test the pack-unpack loop with authcrypt."""
922
msg = "hello world"

0 commit comments

Comments
 (0)