1
1
import os
2
+ import ast
2
3
import sys
4
+ import base64
3
5
import inspect
4
6
import urllib .parse
5
7
from typing import Optional , Callable
6
8
9
+ import multibase
10
+ import multicodec
11
+ import cryptography .hazmat .primitives .asymmetric .ec
12
+
7
13
8
14
def did_web_to_url (
9
15
did_web_string : str ,
@@ -18,18 +24,233 @@ def did_web_to_url(
18
24
)
19
25
20
26
27
+ class DIDKeyInvalidPublicKeyLengthError (ValueError ):
28
+ """
29
+ If the byte length of rawPublicKeyBytes does not match the expected public
30
+ key length for the associated multicodecValue, an invalidPublicKeyLength
31
+ error MUST be raised.
32
+ """
33
+
34
+
21
35
class DIDKeyDecoderNotFoundError (NotImplementedError ):
22
36
"""
23
37
Raised when we don't have a function implemented to decode the given key
24
38
"""
25
39
26
40
41
+ class DIDKeyDecoderError (Exception ):
42
+ """
43
+ Raised when we failed to decode a key from a did:key DID method
44
+ """
45
+
46
+
47
+ class DIDKeyInvalidPublicKeyError (DIDKeyDecoderError ):
48
+ """
49
+ Raised when the raw bytes of a key are invalid during decode
50
+ """
51
+
52
+
27
53
DID_KEY_METHOD = "did:key:"
28
54
29
55
30
- def did_key_to_jwk_dict_is_p_384_startswith_z82 (did_key : str ) -> dict [str , str ]:
31
- did_key = did_key .replace (DID_KEY_METHOD , "" , 1 )
32
- return
56
+ def did_key_decode_public_key (multibase_value : str ) -> dict [str , str ]:
57
+ # 3.1.2.3
58
+ # Decode multibaseValue using the base58-btc multibase alphabet and set
59
+ # multicodecValue to the multicodec header for the decoded value.
60
+ multibase_value_decoded = multibase .decode (multibase_value )
61
+ # Implementers are cautioned to ensure that the multicodecValue is set to
62
+ # the result after performing varint decoding.
63
+ multicodec_value = multicodec .extract_prefix (multibase_value_decoded )
64
+ # Set the rawPublicKeyBytes to the bytes remaining after the multicodec
65
+ # header.
66
+ raw_public_key_bytes = multicodec .remove_prefix (multibase_value_decoded )
67
+ # Return multicodecValue and rawPublicKeyBytes as the decodedPublicKey.
68
+ return multicodec_value , raw_public_key_bytes
69
+
70
+
71
+ class _MULTICODEC_VALUE_NOT_FOUND_IN_TABLE :
72
+ pass
73
+
74
+
75
+ MULTICODEC_VALUE_NOT_FOUND_IN_TABLE = _MULTICODEC_VALUE_NOT_FOUND_IN_TABLE ()
76
+
77
+ # Multicodec hexadecimal value, public key, byte length, Description
78
+ MULTICODEC_HEX_SECP256K1_PUBLIC_KEY = 0xE7
79
+ MULTICODEC_HEX_X25519_PUBLIC_KEY = 0xEC
80
+ MULTICODEC_HEX_ED25519_PUBLIC_KEY = 0xED
81
+ MULTICODEC_HEX_P256_PUBLIC_KEY = 0x1200
82
+ MULTICODEC_HEX_P384_PUBLIC_KEY = 0x1201
83
+ MULTICODEC_HEX_P521_PUBLIC_KEY = 0x1202
84
+ MULTICODEC_HEX_RSA_PUBLIC_KEY = 0x1205
85
+
86
+ MULTICODEC_VALUE_TABLE = {
87
+ MULTICODEC_HEX_SECP256K1_PUBLIC_KEY : 33 , # secp256k1-pub - Secp256k1 public key (compressed)
88
+ MULTICODEC_HEX_X25519_PUBLIC_KEY : 32 , # x25519-pub - Curve25519 public key
89
+ MULTICODEC_HEX_ED25519_PUBLIC_KEY : 32 , # ed25519-pub - Ed25519 public key
90
+ MULTICODEC_HEX_P256_PUBLIC_KEY : 33 , # p256-pub - P-256 public key (compressed)
91
+ MULTICODEC_HEX_P384_PUBLIC_KEY : 49 , # p384-pub - P-384 public key (compressed)
92
+ MULTICODEC_HEX_P521_PUBLIC_KEY : None , # p521-pub - P-521 public key (compressed)
93
+ MULTICODEC_HEX_RSA_PUBLIC_KEY : None , # rsa-pub - RSA public key. DER-encoded ASN.1 type RSAPublicKey according to IETF RFC 8017 (PKCS #1)
94
+ }
95
+
96
+
97
+ def did_key_signature_method_creation (
98
+ multibase_value : hex ,
99
+ raw_public_key_bytes : bytes ,
100
+ ) -> dict [str , str ]:
101
+ # Creating a did:key value consists of creating a cryptographic key pair and
102
+ # encoding the public key using the format provided in Section § 2.
103
+ #
104
+ # NOTE The did:key Format. The creation of a DID Document is also
105
+ # performed by taking the public key value and expanding it into DID
106
+ # Document.
107
+ #
108
+ # An example is given below that expands the ed25519 did:key
109
+ # did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK into its
110
+ # associated DID Document:
111
+ #
112
+ # {
113
+ # "@context": [
114
+ # "https://www.w3.org/ns/did/v1",
115
+ # "https://w3id.org/security/suites/ed25519-2020/v1",
116
+ # "https://w3id.org/security/suites/x25519-2020/v1"
117
+ # ],
118
+ # "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
119
+ # "verificationMethod": [{
120
+ # "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
121
+ # "type": "Ed25519VerificationKey2020",
122
+ # "controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
123
+ # "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
124
+ # }],
125
+ # "authentication": [
126
+ # "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
127
+ # ],
128
+ # "assertionMethod": [
129
+ # "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
130
+ # ],
131
+ # "capabilityDelegation": [
132
+ # "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
133
+ # ],
134
+ # "capabilityInvocation": [
135
+ # "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
136
+ # ],
137
+ # "keyAgreement": [{
138
+ # "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
139
+ # "type": "X25519KeyAgreementKey2020",
140
+ # "controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
141
+ # "publicKeyMultibase": "z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p"
142
+ # }]
143
+ # }
144
+
145
+ # 3.1.2 https://w3c-ccg.github.io/did-method-key/#signature-method-creation-algorithm
146
+ # Initialize verificationMethod to an empty object.
147
+ verification_method = {}
148
+
149
+ # Set multicodecValue and rawPublicKeyBytes to the result of passing
150
+ # multibaseValue and options to § 3.1.3 Decode Public Key Algorithm.
151
+ # Ensure the proper key length of rawPublicKeyBytes based on the
152
+ # multicodecValue table provided below:
153
+
154
+ # Multicodec hexadecimal value public key byte length Description
155
+ # 0xe7 33 bytes secp256k1-pub - Secp256k1 public key (compressed)
156
+ # 0xec 32 bytes x25519-pub - Curve25519 public key
157
+ # 0xed 32 bytes ed25519-pub - Ed25519 public key
158
+ # 0x1200 33 bytes p256-pub - P-256 public key (compressed)
159
+ # 0x1201 49 bytes p384-pub - P-384 public key (compressed)
160
+ # 0x1202 ?? bytes p521-pub - P-521 public key (compressed)
161
+ # 0x1205 ?? bytes rsa-pub - RSA public key. DER-encoded ASN.1 type RSAPublicKey according to IETF RFC 8017 (PKCS #1)
162
+ public_key_length_MUST_be = MULTICODEC_VALUE_TABLE .get (
163
+ multibase_value , MULTICODEC_VALUE_NOT_FOUND_IN_TABLE
164
+ )
165
+ if public_key_length_MUST_be is MULTICODEC_VALUE_NOT_FOUND_IN_TABLE :
166
+ raise DIDKeyDecoderNotFoundError (
167
+ f"multibase_value { multibase_value !r} not in MULTICODEC_VALUE_NOT_FOUND_IN_TABLE { MULTICODEC_VALUE_NOT_FOUND_IN_TABLE !r} "
168
+ )
169
+
170
+ # If the byte length of rawPublicKeyBytes does not match the expected public key length for the associated multicodecValue, an invalidPublicKeyLength error MUST be raised.
171
+ if public_key_length_MUST_be is not None and public_key_length_MUST_be != len (
172
+ raw_public_key_bytes
173
+ ):
174
+ raise DIDKeyInvalidPublicKeyLengthError (
175
+ f"public_key_length_MUST_be: { public_key_length_MUST_be } != len(raw_public_key_bytes): { len (raw_public_key_bytes )} "
176
+ )
177
+
178
+ # Ensure the rawPublicKeyBytes are a proper encoding of the public key type
179
+ # as specified by the multicodecValue. This validation is often done by a
180
+ # cryptographic library when importing the public key by, for example,
181
+ # ensuring that an Elliptic Curve public key is a specific coordinate that
182
+ # exists on the elliptic curve. If an invalid public key value is detected,
183
+ # an invalidPublicKey error MUST be raised.
184
+ #
185
+ # SPEC ISSUE: Request for feedback on implementability: It is not clear if
186
+ # this particular check is implementable across all public key types. The
187
+ # group is accepting feedback on the implementability of this particular
188
+ # feature.
189
+ try :
190
+ if multibase_value in (
191
+ MULTICODEC_HEX_P256_PUBLIC_KEY ,
192
+ MULTICODEC_HEX_P384_PUBLIC_KEY ,
193
+ MULTICODEC_HEX_P521_PUBLIC_KEY ,
194
+ ):
195
+ public_key = cryptography .hazmat .primitives .asymmetric .ec .EllipticCurvePublicKey .from_encoded_point (
196
+ cryptography .hazmat .primitives .asymmetric .ec .SECP384R1 (),
197
+ raw_public_key_bytes ,
198
+ )
199
+ else :
200
+ raise DIDKeyDecoderNotFoundError (
201
+ f"No importer for multibase_value { multibase_value !r} "
202
+ )
203
+ except Exception as e :
204
+ raise DIDKeyInvalidPublicKeyError (
205
+ f"invalid raw_public_key_bytes: { raw_public_key_bytes !r} "
206
+ ) from e
207
+
208
+ return public_key
209
+
210
+ serialized_public = public_key .public_bytes (
211
+ encoding = serialization .Encoding .PEM ,
212
+ format = serialization .PublicFormat .SubjectPublicKeyInfo ,
213
+ )
214
+
215
+ # TODO
216
+
217
+ # Set the verificationMethod.id value by concatenating identifier, a hash
218
+ # character (#), and the multicodecValue. If verificationMethod.id is not a
219
+ # valid DID URL, an invalidDidUrl error MUST be raised.
220
+
221
+ # Set the publicKeyFormat value to the options.publicKeyFormat value.
222
+
223
+ # If publicKeyFormat is not known to the implementation, an
224
+ # unsupportedPublicKeyType error MUST be raised.
225
+
226
+ # If options.enableExperimentalPublicKeyTypes is set to false and
227
+ # publicKeyFormat is not Multikey, JsonWebKey2020, or
228
+ # Ed25519VerificationKey2020, an invalidPublicKeyType error MUST be raised.
229
+
230
+ # Set verificationMethod.type to the publicKeyFormat value.
231
+
232
+ # Set verificationMethod.controller to the identifier value. If
233
+ # verificationMethod.controller is not a valid DID, an invalidDid error MUST
234
+ # be raised.
235
+
236
+ # If publicKeyFormat is Multikey or Ed25519VerificationKey2020, set the
237
+ # verificationMethod.publicKeyMultibase value to multibaseValue.
238
+
239
+ # If publicKeyFormat is JsonWebKey2020, set the
240
+ # verificationMethod.publicKeyJwk value to the result of passing
241
+ # multicodecValue and rawPublicKeyBytes to § 3.1.4 Encode JWK Algorithm.
242
+
243
+ # Return verificationMethod.
244
+ return verification_method
245
+
246
+ import jwcrypto .jwk
247
+
248
+ return jwcrypto .jwk .JWK (
249
+ kty = "EC" ,
250
+ crv = "P-384" ,
251
+ x = decoded_key .x ,
252
+ y = decoded_key .y ,
253
+ ).to_dict ()
33
254
34
255
35
256
def did_key_to_jwk_dict (
@@ -46,10 +267,14 @@ def did_key_to_jwk_dict(
46
267
47
268
Examples
48
269
270
+ - P-384: https://github.com/w3c-ccg/did-method-key/blob/f5abee840c31e92cd1ac11737e0b62103ab99d21/test-vectors/nist-curves.json#L112-L166
271
+
49
272
>>> did_key_to_jwk_dict("did:key:invalid")
50
273
Traceback (most recent call last):
51
274
DIDKeyDecoderNotFoundError: ...
52
- >>> did_key_to_jwk_dict("did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54")
275
+ >>> public_key = did_key_to_jwk_dict("did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9")
276
+ >>> public_key.__class__
277
+ <class 'cryptography.hazmat.backends.openssl.ec._EllipticCurvePublicKey'>
53
278
"""
54
279
if decoders_by_prefix is None :
55
280
decoders_by_prefix = {
@@ -61,8 +286,16 @@ def did_key_to_jwk_dict(
61
286
)
62
287
}
63
288
64
- for prefix , decoder in decoders_by_prefix .items ():
65
- if did_key .startswith (DID_KEY_METHOD + prefix ):
66
- return decoder (did_key )
289
+ try :
290
+ multibase_value , raw_public_key_bytes = did_key_decode_public_key (
291
+ did_key .replace (DID_KEY_METHOD , "" , 1 )
292
+ )
293
+ except Exception as e :
294
+ raise DIDKeyDecoderNotFoundError (did_key ) from e
295
+
296
+ try :
297
+ return did_key_signature_method_creation (multibase_value , raw_public_key_bytes )
298
+ except Exception as e :
299
+ raise DIDKeyDecoderError (did_key ) from e
67
300
68
301
raise DIDKeyDecoderNotFoundError (did_key )
0 commit comments