11"""Askar backend for DIDComm Messaging."""
22from collections import OrderedDict
33import json
4- from typing import Mapping , Optional , Union
5- from didcomm_messaging .crypto import KMS , SecretsManager
4+ from typing import Mapping , Optional , Sequence , Union
5+
6+ from pydid import VerificationMethod
7+ from didcomm_messaging .crypto import SecretsManager
68from didcomm_messaging .jwe import (
79 JweBuilder ,
810 JweEnvelope ,
911 JweRecipient ,
1012 b64url ,
1113)
12- from didcomm_messaging .kms import CryptoService , CryptoServiceError , PublicKey , SecretKey
14+ from didcomm_messaging .crypto import (
15+ CryptoService ,
16+ CryptoServiceError ,
17+ PublicKey ,
18+ SecretKey ,
19+ )
1320from didcomm_messaging .multiformats import multibase , multicodec
1421
1522try :
@@ -102,39 +109,27 @@ def _expected_alg_and_material_to_key(
102109 raise ValueError ("Failed to parse key" )
103110
104111 @classmethod
105- def from_verification_method (cls , vm : dict ) -> "PublicKey " :
112+ def from_verification_method (cls , vm : VerificationMethod ) -> "AskarKey " :
106113 """Create a Key instance from a DID Document Verification Method."""
107- vm_type = vm .get ("type" )
108- ident = vm .get ("id" )
109- controller = vm .get ("controller" )
110- if not vm_type :
111- raise ValueError ("Verification method missing type" )
112-
113- if not ident :
114- raise ValueError ("Verification method missing id" )
115-
116- if not controller :
117- raise ValueError ("Verification method missing controller" )
118-
119- if ident .startswith ("#" ):
120- kid = f"{ controller } #{ ident } "
114+ if not vm .id .did :
115+ kid = vm .id .as_absolute (vm .controller )
121116 else :
122- kid = ident
117+ kid = vm . id
123118
124- if vm_type == "Multikey" :
125- multikey = vm .get ( "publicKeyMultiBase" )
119+ if vm . type == "Multikey" :
120+ multikey = vm .public_key_multibase
126121 if not multikey :
127122 raise ValueError ("Multikey verification method missing key" )
128123
129124 key = cls ._multikey_to_key (multikey )
130125 return cls (key , kid )
131126
132- alg = cls .type_to_alg .get (vm_type )
127+ alg = cls .type_to_alg .get (vm . type )
133128 if not alg :
134129 raise ValueError ("Unsupported verification method type: {vm_type}" )
135130
136- base58 = vm .get ( "publicKeyBase58" )
137- multi = vm .get ( "publicKeyMultiBase" )
131+ base58 = vm .public_key_base58
132+ multi = vm .public_key_multibase
138133 key = cls ._expected_alg_and_material_to_key (
139134 alg , public_key_base58 = base58 , public_key_multibase = multi
140135 )
@@ -165,12 +160,11 @@ def kid(self) -> str:
165160 return self ._kid
166161
167162
168-
169163class AskarCryptoService (CryptoService [AskarKey , AskarSecretKey ]):
170- """Askar backend for DIDComm Messaging ."""
164+ """CryptoService backend implemented using Askar ."""
171165
172166 async def ecdh_es_encrypt (
173- self , to_keys : Mapping [ str , AskarKey ], message : bytes
167+ self , to_keys : Sequence [ AskarKey ], message : bytes
174168 ) -> bytes :
175169 """Encode a message into DIDComm v2 anonymous encryption."""
176170 builder = JweBuilder (with_flatten_recipients = False )
@@ -188,7 +182,7 @@ async def ecdh_es_encrypt(
188182 except AskarError :
189183 raise CryptoServiceError ("Error creating content encryption key" )
190184
191- for kid , recip_key in to_keys . items () :
185+ for recip_key in to_keys :
192186 try :
193187 epk = Key .generate (recip_key .key .algorithm , ephemeral = True )
194188 except AskarError :
@@ -199,7 +193,7 @@ async def ecdh_es_encrypt(
199193 builder .add_recipient (
200194 JweRecipient (
201195 encrypted_key = enc_key .ciphertext ,
202- header = {"kid" : kid , "epk" : epk .get_jwk_public ()},
196+ header = {"kid" : recip_key . kid , "epk" : epk .get_jwk_public ()},
203197 )
204198 )
205199
@@ -222,7 +216,7 @@ async def ecdh_es_encrypt(
222216 async def ecdh_es_decrypt (
223217 self ,
224218 wrapper : Union [JweEnvelope , str , bytes ],
225- recip_key : AskarKey ,
219+ recip_key : AskarSecretKey ,
226220 ) -> bytes :
227221 """Decode a message from DIDComm v2 anonymous encryption."""
228222 if isinstance (wrapper , bytes ):
@@ -235,7 +229,9 @@ async def ecdh_es_decrypt(
235229 if alg_id and alg_id in ("ECDH-ES+A128KW" , "ECDH-ES+A256KW" ):
236230 wrap_alg = alg_id [8 :]
237231 else :
238- raise CryptoServiceError (f"Missing or unsupported ECDH-ES algorithm: { alg_id } " )
232+ raise CryptoServiceError (
233+ f"Missing or unsupported ECDH-ES algorithm: { alg_id } "
234+ )
239235
240236 recip = wrapper .get_recipient (recip_key .kid )
241237 if not recip :
@@ -249,7 +245,9 @@ async def ecdh_es_decrypt(
249245 "A256CBC-HS512" ,
250246 "XC20P" ,
251247 ):
252- raise CryptoServiceError (f"Unsupported ECDH-ES content encryption: { enc_alg } " )
248+ raise CryptoServiceError (
249+ f"Unsupported ECDH-ES content encryption: { enc_alg } "
250+ )
253251
254252 epk_header = recip .header .get ("epk" )
255253 if not epk_header :
@@ -289,9 +287,8 @@ async def ecdh_es_decrypt(
289287
290288 async def ecdh_1pu_encrypt (
291289 self ,
292- to_keys : Mapping [str , AskarKey ],
293- sender_kid : str ,
294- sender_key : AskarKey ,
290+ to_keys : Sequence [AskarKey ],
291+ sender_key : AskarSecretKey ,
295292 message : bytes ,
296293 ) -> bytes :
297294 """Encode a message into DIDComm v2 authenticated encryption."""
@@ -316,15 +313,15 @@ async def ecdh_1pu_encrypt(
316313 except AskarError :
317314 raise CryptoServiceError ("Error creating ephemeral key" )
318315
319- apu = b64url (sender_kid )
316+ apu = b64url (sender_key . kid )
320317 apv = []
321- for kid , recip_key in to_keys . items () :
318+ for recip_key in to_keys :
322319 if agree_alg :
323320 if agree_alg != recip_key .key .algorithm :
324321 raise CryptoServiceError ("Recipient key types must be consistent" )
325322 else :
326323 agree_alg = recip_key .key .algorithm
327- apv .append (kid )
324+ apv .append (recip_key . kid )
328325 apv .sort ()
329326 apv = b64url ("." .join (apv ))
330327
@@ -336,7 +333,7 @@ async def ecdh_1pu_encrypt(
336333 ("apu" , apu ),
337334 ("apv" , apv ),
338335 ("epk" , json .loads (epk .get_jwk_public ())),
339- ("skid" , sender_kid ),
336+ ("skid" , sender_key . kid ),
340337 ]
341338 )
342339 )
@@ -346,20 +343,22 @@ async def ecdh_1pu_encrypt(
346343 raise CryptoServiceError ("Error encrypting message payload" )
347344 builder .set_payload (payload .ciphertext , payload .nonce , payload .tag )
348345
349- for kid , recip_key in to_keys . items () :
346+ for recip_key in to_keys :
350347 enc_key = ecdh .Ecdh1PU (alg_id , apu , apv ).sender_wrap_key (
351348 wrap_alg , epk , sender_key .key , recip_key .key , cek , cc_tag = payload .tag
352349 )
353350 builder .add_recipient (
354- JweRecipient (encrypted_key = enc_key .ciphertext , header = {"kid" : kid })
351+ JweRecipient (
352+ encrypted_key = enc_key .ciphertext , header = {"kid" : recip_key .kid }
353+ )
355354 )
356355
357356 return builder .build ().to_json ().encode ("utf-8" )
358357
359358 async def ecdh_1pu_decrypt (
360359 self ,
361360 wrapper : Union [JweEnvelope , str , bytes ],
362- recip_key : AskarKey ,
361+ recip_key : AskarSecretKey ,
363362 sender_key : AskarKey ,
364363 ):
365364 """Decode a message from DIDComm v2 authenticated encryption."""
@@ -376,7 +375,9 @@ async def ecdh_1pu_decrypt(
376375
377376 enc_alg = wrapper .protected .get ("enc" )
378377 if not enc_alg or enc_alg not in ("A128CBC-HS256" , "A256CBC-HS512" ):
379- raise CryptoServiceError (f"Unsupported ECDH-1PU content encryption: { enc_alg } " )
378+ raise CryptoServiceError (
379+ f"Unsupported ECDH-1PU content encryption: { enc_alg } "
380+ )
380381
381382 recip = wrapper .get_recipient (recip_key .kid )
382383 if not recip :
@@ -420,14 +421,20 @@ async def ecdh_1pu_decrypt(
420421
421422 return plaintext
422423
423- class AskarWithStore (KMS , AskarCryptoService , SecretsManager ):
424+ @classmethod
425+ def verification_method_to_public_key (cls , vm : VerificationMethod ) -> AskarKey :
426+ """Convert a verification method into a public key."""
427+ return AskarKey .from_verification_method (vm )
428+
429+
430+ class AskarSecretsManager (SecretsManager [AskarSecretKey ]):
424431 """Askar KMS with an Askar Store for secrets management."""
425432
426433 def __init__ (self , store : Store ):
427434 """Initialize a new Askar instance."""
428435 self .store = store
429436
430- async def fetch_key_by_kid (self , kid : str ) -> Optional [AskarSecretKey ]:
437+ async def get_secret_by_kid (self , kid : str ) -> Optional [AskarSecretKey ]:
431438 """Fetch a public key by key ID."""
432439 async with self .store .session () as session :
433440 key_entry = await session .fetch_key (kid )
@@ -436,15 +443,3 @@ async def fetch_key_by_kid(self, kid: str) -> Optional[AskarSecretKey]:
436443
437444 # cached_property doesn't play nice with pyright
438445 return AskarKey (key_entry .key , kid ) # type: ignore
439-
440-
441- class AskarDelegatedSecrets (KMS , AskarCryptoService , SecretsManager ):
442- """Askar KMS with delegated secrets management."""
443-
444- def __init__ (self , secrets : SecretsManager ):
445- """Initialize a new AskarDelegatedSecrets instance."""
446- self .secrets = secrets
447-
448- async def get_secret_by_kid (self , kid : str ) -> Optional [SecretKey ]:
449- """Get a secret key by its kid."""
450- return await self .secrets .get_secret_by_kid (kid )
0 commit comments