Skip to content

Commit 3f9a6e4

Browse files
committed
refactor: make services more flexible
By passing in all dependencies to each method that requires it rather than capturing it in the instance state. This should better support more use cases than the previous structure. This is a backwards compatible change; the more flexible services are wrapped by the `DIDCommMessaging` class to behave exactly the same way as before. To use the new, more flexible services, use `DIDCommMessagingService` class. Signed-off-by: Daniel Bluhm <dbluhm@pm.me>
1 parent 3fc3e98 commit 3f9a6e4

4 files changed

Lines changed: 171 additions & 82 deletions

File tree

didcomm_messaging/__init__.py

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,9 @@ class UnpackResult:
4646
sender_kid: Optional[str] = None
4747

4848

49-
class DIDCommMessaging(Generic[P, S]):
49+
class DIDCommMessagingService(Generic[P, S]):
5050
"""Main entrypoint for DIDComm Messaging."""
5151

52-
def __init__(
53-
self,
54-
crypto: CryptoService[P, S],
55-
secrets: SecretsManager[S],
56-
resolver: DIDResolver,
57-
packaging: PackagingService[P, S],
58-
routing: RoutingService,
59-
):
60-
"""Initialize the DIDComm Messaging service."""
61-
self.crypto = crypto
62-
self.secrets = secrets
63-
self.resolver = resolver
64-
self.packaging = packaging
65-
self.routing = routing
66-
6752
def service_to_target(self, service: DIDCommV2Service) -> str:
6853
"""Convert a service to a target uri.
6954
@@ -76,20 +61,49 @@ def service_to_target(self, service: DIDCommV2Service) -> str:
7661

7762
return service_endpoint.uri
7863

79-
async def pack(self, message: dict, to: str, frm: Optional[str] = None, **options):
64+
async def pack(
65+
self,
66+
crypto: CryptoService[P, S],
67+
resolver: DIDResolver,
68+
secrets: SecretsManager[S],
69+
packaging: PackagingService[P, S],
70+
routing: RoutingService,
71+
message: dict,
72+
to: str,
73+
frm: Optional[str] = None,
74+
**options,
75+
):
8076
"""Pack a message."""
8177
# TODO crypto layer permits packing to multiple recipients; should we as well?
8278

83-
encoded_message = await self.packaging.pack(
84-
json.dumps(message).encode(), [to], frm, **options
79+
encoded_message = await packaging.pack(
80+
crypto,
81+
resolver,
82+
secrets,
83+
json.dumps(message).encode(),
84+
[to],
85+
frm,
86+
**options,
8587
)
8688

87-
forward, services = await self.routing.prepare_forward(to, encoded_message)
89+
forward, services = await routing.prepare_forward(
90+
crypto, packaging, resolver, secrets, to, encoded_message
91+
)
8892
return PackResult(forward, services)
8993

90-
async def unpack(self, encoded_message: bytes, **options) -> UnpackResult:
94+
async def unpack(
95+
self,
96+
crypto: CryptoService[P, S],
97+
resolver: DIDResolver,
98+
secrets: SecretsManager[S],
99+
packaging: PackagingService[P, S],
100+
encoded_message: bytes,
101+
**options,
102+
) -> UnpackResult:
91103
"""Unpack a message."""
92-
unpacked, metadata = await self.packaging.unpack(encoded_message, **options)
104+
unpacked, metadata = await packaging.unpack(
105+
crypto, resolver, secrets, encoded_message, **options
106+
)
93107
message = json.loads(unpacked.decode())
94108
return UnpackResult(
95109
message,
@@ -98,3 +112,58 @@ async def unpack(self, encoded_message: bytes, **options) -> UnpackResult:
98112
recipient_kid=metadata.recip_key.kid,
99113
sender_kid=metadata.sender_kid,
100114
)
115+
116+
117+
class DIDCommMessaging(Generic[P, S]):
118+
"""Main entrypoint for DIDComm Messaging."""
119+
120+
def __init__(
121+
self,
122+
crypto: CryptoService[P, S],
123+
secrets: SecretsManager[S],
124+
resolver: DIDResolver,
125+
packaging: PackagingService[P, S],
126+
routing: RoutingService,
127+
):
128+
"""Initialize the DIDComm Messaging service."""
129+
self.crypto = crypto
130+
self.secrets = secrets
131+
self.resolver = resolver
132+
self.packaging = packaging
133+
self.routing = routing
134+
self.dmp = DIDCommMessagingService()
135+
136+
async def pack(
137+
self,
138+
message: dict,
139+
to: str,
140+
frm: Optional[str] = None,
141+
**options,
142+
) -> PackResult:
143+
"""Pack a message."""
144+
return await self.dmp.pack(
145+
self.crypto,
146+
self.resolver,
147+
self.secrets,
148+
self.packaging,
149+
self.routing,
150+
message,
151+
to,
152+
frm,
153+
**options,
154+
)
155+
156+
async def unpack(
157+
self,
158+
encoded_message: bytes,
159+
**options,
160+
) -> UnpackResult:
161+
"""Unpack a message."""
162+
return await self.dmp.unpack(
163+
self.crypto,
164+
self.resolver,
165+
self.secrets,
166+
self.packaging,
167+
encoded_message,
168+
**options,
169+
)

didcomm_messaging/packaging.py

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,8 @@ class PackagingServiceError(Exception):
2828
class PackagingService(Generic[P, S]):
2929
"""DIDComm Messaging interface."""
3030

31-
def __init__(
32-
self,
33-
resolver: DIDResolver,
34-
crypto: CryptoService[P, S],
35-
secrets: SecretsManager[S],
36-
):
37-
"""Initialize the KMS."""
38-
self.resolver = resolver
39-
self.crypto = crypto
40-
self.secrets = secrets
41-
4231
async def extract_packed_message_metadata( # noqa: C901
43-
self, enc_message: Union[str, bytes]
32+
self, enc_message: Union[str, bytes], secrets: SecretsManager[S]
4433
) -> PackedMessageMetadata:
4534
"""Extract metadata from a packed DIDComm message."""
4635
try:
@@ -61,7 +50,7 @@ async def extract_packed_message_metadata( # noqa: C901
6150
sender_kid = None
6251
recip_key = None
6352
for kid in wrapper.recipient_key_ids:
64-
recip_key = await self.secrets.get_secret_by_kid(kid)
53+
recip_key = await secrets.get_secret_by_kid(kid)
6554
if recip_key:
6655
break
6756

@@ -97,40 +86,42 @@ async def extract_packed_message_metadata( # noqa: C901
9786
return PackedMessageMetadata(wrapper, method, recip_key, sender_kid)
9887

9988
async def unpack(
100-
self, enc_message: Union[str, bytes]
89+
self,
90+
crypto: CryptoService[P, S],
91+
resolver: DIDResolver,
92+
secrets: SecretsManager[S],
93+
enc_message: Union[str, bytes],
10194
) -> Tuple[bytes, PackedMessageMetadata]:
10295
"""Unpack a DIDComm message."""
103-
metadata = await self.extract_packed_message_metadata(enc_message)
96+
metadata = await self.extract_packed_message_metadata(enc_message, secrets)
10497

10598
if metadata.method == "ECDH-ES":
10699
return (
107-
await self.crypto.ecdh_es_decrypt(enc_message, metadata.recip_key),
100+
await crypto.ecdh_es_decrypt(enc_message, metadata.recip_key),
108101
metadata,
109102
)
110103

111104
if not metadata.sender_kid:
112105
raise PackagingServiceError("Missing sender key ID")
113106

114-
sender_vm = await self.resolver.resolve_and_dereference_verification_method(
107+
sender_vm = await resolver.resolve_and_dereference_verification_method(
115108
metadata.sender_kid
116109
)
117-
sender_key = self.crypto.verification_method_to_public_key(sender_vm)
110+
sender_key = crypto.verification_method_to_public_key(sender_vm)
118111

119112
return (
120-
await self.crypto.ecdh_1pu_decrypt(
121-
enc_message, metadata.recip_key, sender_key
122-
),
113+
await crypto.ecdh_1pu_decrypt(enc_message, metadata.recip_key, sender_key),
123114
metadata,
124115
)
125116

126-
async def recip_for_kid_or_default_for_did(self, kid_or_did: str) -> P:
117+
async def recip_for_kid_or_default_for_did(
118+
self, crypto: CryptoService[P, S], resolver: DIDResolver, kid_or_did: str
119+
) -> P:
127120
"""Resolve a verification method for a kid or return default recip."""
128121
if "#" in kid_or_did:
129-
vm = await self.resolver.resolve_and_dereference_verification_method(
130-
kid_or_did
131-
)
122+
vm = await resolver.resolve_and_dereference_verification_method(kid_or_did)
132123
else:
133-
doc = await self.resolver.resolve_and_parse(kid_or_did)
124+
doc = await resolver.resolve_and_parse(kid_or_did)
134125
if not doc.key_agreement:
135126
raise PackagingServiceError(
136127
"No key agreement methods found; cannot determine recipient"
@@ -146,14 +137,14 @@ async def recip_for_kid_or_default_for_did(self, kid_or_did: str) -> P:
146137
else:
147138
vm = default
148139

149-
return self.crypto.verification_method_to_public_key(vm)
140+
return crypto.verification_method_to_public_key(vm)
150141

151-
async def default_sender_kid_for_did(self, did: str) -> str:
142+
async def default_sender_kid_for_did(self, resolver: DIDResolver, did: str) -> str:
152143
"""Determine the kid of the default sender key for a DID."""
153144
if "#" in did:
154145
return did
155146

156-
doc = await self.resolver.resolve_and_parse(did)
147+
doc = await resolver.resolve_and_parse(did)
157148
if not doc.key_agreement:
158149
raise PackagingServiceError(
159150
"No key agreement methods found; cannot determine recipient"
@@ -175,21 +166,27 @@ async def default_sender_kid_for_did(self, did: str) -> str:
175166

176167
async def pack(
177168
self,
169+
crypto: CryptoService[P, S],
170+
resolver: DIDResolver,
171+
secrets: SecretsManager[S],
178172
message: bytes,
179173
to: Sequence[str],
180174
frm: Optional[str] = None,
181175
**options,
182176
):
183177
"""Pack a DIDComm message."""
184-
recip_keys = [await self.recip_for_kid_or_default_for_did(kid) for kid in to]
185-
sender_kid = await self.default_sender_kid_for_did(frm) if frm else None
186-
sender_key = (
187-
await self.secrets.get_secret_by_kid(sender_kid) if sender_kid else None
178+
recip_keys = [
179+
await self.recip_for_kid_or_default_for_did(crypto, resolver, kid)
180+
for kid in to
181+
]
182+
sender_kid = (
183+
await self.default_sender_kid_for_did(resolver, frm) if frm else None
188184
)
185+
sender_key = await secrets.get_secret_by_kid(sender_kid) if sender_kid else None
189186
if frm and not sender_key:
190187
raise PackagingServiceError("No sender key found")
191188

192189
if sender_key:
193-
return await self.crypto.ecdh_1pu_encrypt(recip_keys, sender_key, message)
190+
return await crypto.ecdh_1pu_encrypt(recip_keys, sender_key, message)
194191
else:
195-
return await self.crypto.ecdh_es_encrypt(recip_keys, message)
192+
return await crypto.ecdh_es_encrypt(recip_keys, message)

0 commit comments

Comments
 (0)