44import json
55from typing import Generic , Optional , Sequence , Union
66
7+ import base58
78from pydantic import AnyUrl
89from pydid import VerificationMethod
910from pydid .service import DIDCommV1Service
1011
11- from didcomm_messaging .crypto import SecretsManager , P , S
12+ from didcomm_messaging .crypto import P , S , SecretsManager
1213from didcomm_messaging .legacy .base import LegacyCryptoService
1314from didcomm_messaging .legacy .packaging import LegacyPackagingService
15+ from didcomm_messaging .multiformats import multibase , multicodec
1416from didcomm_messaging .resolver import DIDResolver
1517
1618
@@ -57,6 +59,13 @@ class Target:
5759class 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:
0 commit comments