@enbox/dids 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +1 -0
- package/dist/browser.js +77 -0
- package/dist/browser.js.map +7 -0
- package/dist/browser.mjs +77 -0
- package/dist/browser.mjs.map +7 -0
- package/dist/cjs/index.js +6303 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/utils.js +245 -0
- package/dist/cjs/utils.js.map +7 -0
- package/dist/esm/bearer-did.js +201 -0
- package/dist/esm/bearer-did.js.map +1 -0
- package/dist/esm/did-error.js +62 -0
- package/dist/esm/did-error.js.map +1 -0
- package/dist/esm/did.js +114 -0
- package/dist/esm/did.js.map +1 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/methods/did-dht.js +1241 -0
- package/dist/esm/methods/did-dht.js.map +1 -0
- package/dist/esm/methods/did-ion.js +570 -0
- package/dist/esm/methods/did-ion.js.map +1 -0
- package/dist/esm/methods/did-jwk.js +298 -0
- package/dist/esm/methods/did-jwk.js.map +1 -0
- package/dist/esm/methods/did-key.js +983 -0
- package/dist/esm/methods/did-key.js.map +1 -0
- package/dist/esm/methods/did-method.js +53 -0
- package/dist/esm/methods/did-method.js.map +1 -0
- package/dist/esm/methods/did-web.js +83 -0
- package/dist/esm/methods/did-web.js.map +1 -0
- package/dist/esm/resolver/resolver-cache-level.js +101 -0
- package/dist/esm/resolver/resolver-cache-level.js.map +1 -0
- package/dist/esm/resolver/resolver-cache-noop.js +24 -0
- package/dist/esm/resolver/resolver-cache-noop.js.map +1 -0
- package/dist/esm/resolver/universal-resolver.js +187 -0
- package/dist/esm/resolver/universal-resolver.js.map +1 -0
- package/dist/esm/types/did-core.js +51 -0
- package/dist/esm/types/did-core.js.map +1 -0
- package/dist/esm/types/did-resolution.js +12 -0
- package/dist/esm/types/did-resolution.js.map +1 -0
- package/dist/esm/types/multibase.js +2 -0
- package/dist/esm/types/multibase.js.map +1 -0
- package/dist/esm/types/portable-did.js +2 -0
- package/dist/esm/types/portable-did.js.map +1 -0
- package/dist/esm/utils.js +458 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/types/bearer-did.d.ts +143 -0
- package/dist/types/bearer-did.d.ts.map +1 -0
- package/dist/types/did-error.d.ts +50 -0
- package/dist/types/did-error.d.ts.map +1 -0
- package/dist/types/did.d.ts +125 -0
- package/dist/types/did.d.ts.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/methods/did-dht.d.ts +682 -0
- package/dist/types/methods/did-dht.d.ts.map +1 -0
- package/dist/types/methods/did-ion.d.ts +492 -0
- package/dist/types/methods/did-ion.d.ts.map +1 -0
- package/dist/types/methods/did-jwk.d.ts +236 -0
- package/dist/types/methods/did-jwk.d.ts.map +1 -0
- package/dist/types/methods/did-key.d.ts +499 -0
- package/dist/types/methods/did-key.d.ts.map +1 -0
- package/dist/types/methods/did-method.d.ts +238 -0
- package/dist/types/methods/did-method.d.ts.map +1 -0
- package/dist/types/methods/did-web.d.ts +37 -0
- package/dist/types/methods/did-web.d.ts.map +1 -0
- package/dist/types/resolver/resolver-cache-level.d.ts +86 -0
- package/dist/types/resolver/resolver-cache-level.d.ts.map +1 -0
- package/dist/types/resolver/resolver-cache-noop.d.ts +9 -0
- package/dist/types/resolver/resolver-cache-noop.d.ts.map +1 -0
- package/dist/types/resolver/universal-resolver.d.ts +109 -0
- package/dist/types/resolver/universal-resolver.d.ts.map +1 -0
- package/dist/types/types/did-core.d.ts +523 -0
- package/dist/types/types/did-core.d.ts.map +1 -0
- package/dist/types/types/did-resolution.d.ts +85 -0
- package/dist/types/types/did-resolution.d.ts.map +1 -0
- package/dist/types/types/multibase.d.ts +28 -0
- package/dist/types/types/multibase.d.ts.map +1 -0
- package/dist/types/types/portable-did.d.ts +59 -0
- package/dist/types/types/portable-did.d.ts.map +1 -0
- package/dist/types/utils.d.ts +378 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/utils.js +28 -0
- package/dist/utils.js.map +7 -0
- package/package.json +116 -0
- package/src/bearer-did.ts +287 -0
- package/src/did-error.ts +75 -0
- package/src/did.ts +186 -0
- package/src/index.ts +21 -0
- package/src/methods/did-dht.ts +1637 -0
- package/src/methods/did-ion.ts +887 -0
- package/src/methods/did-jwk.ts +410 -0
- package/src/methods/did-key.ts +1248 -0
- package/src/methods/did-method.ts +276 -0
- package/src/methods/did-web.ts +96 -0
- package/src/resolver/resolver-cache-level.ts +163 -0
- package/src/resolver/resolver-cache-noop.ts +26 -0
- package/src/resolver/universal-resolver.ts +238 -0
- package/src/types/did-core.ts +580 -0
- package/src/types/did-resolution.ts +93 -0
- package/src/types/multibase.ts +29 -0
- package/src/types/portable-did.ts +64 -0
- package/src/utils.ts +532 -0
|
@@ -0,0 +1,887 @@
|
|
|
1
|
+
import type { CryptoApi, Jwk, KeyIdentifier, KeyImporterExporter, KmsExportKeyParams, KmsImportKeyParams } from '@enbox/crypto';
|
|
2
|
+
import type {
|
|
3
|
+
JwkEs256k,
|
|
4
|
+
IonDocumentModel,
|
|
5
|
+
IonPublicKeyModel,
|
|
6
|
+
IonPublicKeyPurpose,
|
|
7
|
+
} from '@decentralized-identity/ion-sdk';
|
|
8
|
+
|
|
9
|
+
import { IonDid, IonRequest } from '@decentralized-identity/ion-sdk';
|
|
10
|
+
import { LocalKeyManager, computeJwkThumbprint } from '@enbox/crypto';
|
|
11
|
+
|
|
12
|
+
import type { PortableDid } from '../types/portable-did.js';
|
|
13
|
+
import type { DidCreateOptions, DidCreateVerificationMethod, DidRegistrationResult } from '../methods/did-method.js';
|
|
14
|
+
import type {
|
|
15
|
+
DidService,
|
|
16
|
+
DidDocument,
|
|
17
|
+
DidResolutionResult,
|
|
18
|
+
DidResolutionOptions,
|
|
19
|
+
DidVerificationMethod,
|
|
20
|
+
DidVerificationRelationship,
|
|
21
|
+
} from '../types/did-core.js';
|
|
22
|
+
|
|
23
|
+
import { Did } from '../did.js';
|
|
24
|
+
import { BearerDid } from '../bearer-did.js';
|
|
25
|
+
import { DidMethod } from '../methods/did-method.js';
|
|
26
|
+
import { DidError, DidErrorCode } from '../did-error.js';
|
|
27
|
+
import { getVerificationRelationshipsById } from '../utils.js';
|
|
28
|
+
import { EMPTY_DID_RESOLUTION_RESULT } from '../types/did-resolution.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for creating a Decentralized Identifier (DID) using the DID ION method.
|
|
32
|
+
*/
|
|
33
|
+
export interface DidIonCreateOptions<TKms> extends DidCreateOptions<TKms> {
|
|
34
|
+
/**
|
|
35
|
+
* Optional. The URI of a server involved in executing DID method operations. In the context of
|
|
36
|
+
* DID creation, the endpoint is expected to be a Sidetree node. If not specified, a default
|
|
37
|
+
* gateway node is used.
|
|
38
|
+
*/
|
|
39
|
+
gatewayUri?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Optional. Determines whether the created DID should be published to a Sidetree node.
|
|
43
|
+
*
|
|
44
|
+
* If set to `true` or omitted, the DID is publicly discoverable. If `false`, the DID is not
|
|
45
|
+
* published and cannot be resolved by others. By default, newly created DIDs are published.
|
|
46
|
+
*
|
|
47
|
+
* @see {@link https://identity.foundation/sidetree/spec/#create | Sidetree Protocol Specification, § Create}
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const did = await DidIon.create({
|
|
52
|
+
* options: {
|
|
53
|
+
* publish: false
|
|
54
|
+
* };
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
publish?: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Optional. An array of service endpoints associated with the DID.
|
|
61
|
+
*
|
|
62
|
+
* Services are used in DID documents to express ways of communicating with the DID subject or
|
|
63
|
+
* associated entities. A service can be any type of service the DID subject wants to advertise,
|
|
64
|
+
* including decentralized identity management services for further discovery, authentication,
|
|
65
|
+
* authorization, or interaction.
|
|
66
|
+
*
|
|
67
|
+
* @see {@link https://www.w3.org/TR/did-core/#services | DID Core Specification, § Services}
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* const did = await DidIon.create({
|
|
72
|
+
* options: {
|
|
73
|
+
* services: [
|
|
74
|
+
* {
|
|
75
|
+
* id: 'dwn',
|
|
76
|
+
* type: 'DecentralizedWebNode',
|
|
77
|
+
* serviceEndpoint: ['https://example.com/dwn1', 'https://example/dwn2']
|
|
78
|
+
* }
|
|
79
|
+
* ]
|
|
80
|
+
* };
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
services?: DidService[];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Optional. An array of verification methods to be included in the DID document.
|
|
87
|
+
*
|
|
88
|
+
* By default, a newly created DID ION document will contain a single Ed25519 verification method.
|
|
89
|
+
* Additional verification methods can be added to the DID document using the
|
|
90
|
+
* `verificationMethods` property.
|
|
91
|
+
*
|
|
92
|
+
* @see {@link https://www.w3.org/TR/did-core/#verification-methods | DID Core Specification, § Verification Methods}
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* const did = await DidIon.create({
|
|
97
|
+
* options: {
|
|
98
|
+
* verificationMethods: [
|
|
99
|
+
* {
|
|
100
|
+
* algorithm: 'Ed25519',
|
|
101
|
+
* purposes: ['authentication', 'assertionMethod']
|
|
102
|
+
* },
|
|
103
|
+
* {
|
|
104
|
+
* algorithm: 'Ed25519',
|
|
105
|
+
* id: 'dwn-sig',
|
|
106
|
+
* purposes: ['authentication', 'assertionMethod']
|
|
107
|
+
* }
|
|
108
|
+
* ]
|
|
109
|
+
* };
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
verificationMethods?: DidCreateVerificationMethod<TKms>[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Represents the request model for managing DID documents within the ION network, according to the
|
|
117
|
+
* Sidetree protocol specification.
|
|
118
|
+
*/
|
|
119
|
+
export interface DidIonCreateRequest {
|
|
120
|
+
/** The type of operation to perform, which is always 'create' for a Create Operation. */
|
|
121
|
+
type: 'create';
|
|
122
|
+
|
|
123
|
+
/** Contains properties related to the initial state of the DID document. */
|
|
124
|
+
suffixData: {
|
|
125
|
+
/** A hash of the `delta` object, representing the initial changes to the DID document. */
|
|
126
|
+
deltaHash: string;
|
|
127
|
+
/** A commitment value used for future recovery operations, hashed for security. */
|
|
128
|
+
recoveryCommitment: string;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/** Details the changes to be applied to the DID document in this operation. */
|
|
132
|
+
delta: {
|
|
133
|
+
/** A commitment value used for the next update operation, hashed for security. */
|
|
134
|
+
updateCommitment: string;
|
|
135
|
+
/** An array of patch objects specifying the modifications to apply to the DID document. */
|
|
136
|
+
patches: {
|
|
137
|
+
/** The type of modification to perform (e.g., adding or removing public keys or service
|
|
138
|
+
* endpoints). */
|
|
139
|
+
action: string;
|
|
140
|
+
/** The document state or partial state to apply with this patch. */
|
|
141
|
+
document: IonDocumentModel;
|
|
142
|
+
}[];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Represents a {@link DidVerificationMethod | DID verification method} in the context of DID ION
|
|
148
|
+
* create, update, deactivate, and resolve operations.
|
|
149
|
+
*
|
|
150
|
+
* Unlike the DID Core standard {@link DidVerificationMethod} interface, this type is specific to
|
|
151
|
+
* the ION method operations and only includes the `id`, `publicKeyJwk`, and `purposes` properties:
|
|
152
|
+
* - The `id` property is optional and specifies the identifier fragment of the verification method.
|
|
153
|
+
* - The `publicKeyJwk` property is required and represents the public key in JWK format.
|
|
154
|
+
* - The `purposes` property is required and specifies the purposes for which the verification
|
|
155
|
+
* method can be used.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* const verificationMethod: DidIonVerificationMethod = {
|
|
160
|
+
* id : 'sig',
|
|
161
|
+
* publicKeyJwk : {
|
|
162
|
+
* kty : 'OKP',
|
|
163
|
+
* crv : 'Ed25519',
|
|
164
|
+
* x : 'o40shZrsco-CfEqk6mFsXfcP94ly3Az3gm84PzAUsXo',
|
|
165
|
+
* kid : 'BDp0xim82GswlxnPV8TPtBdUw80wkGIF8gjFbw1x5iQ',
|
|
166
|
+
* },
|
|
167
|
+
* purposes: ['authentication', 'assertionMethod']
|
|
168
|
+
* };
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export interface DidIonVerificationMethod {
|
|
172
|
+
/**
|
|
173
|
+
* Optionally specify the identifier fragment of the verification method.
|
|
174
|
+
*
|
|
175
|
+
* If not specified, the method's ID will be generated from the key's ID or thumbprint.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```ts
|
|
179
|
+
* const verificationMethod: DidIonVerificationMethod = {
|
|
180
|
+
* id: 'sig',
|
|
181
|
+
* ...
|
|
182
|
+
* };
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
id?: string;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A public key in JWK format.
|
|
189
|
+
*
|
|
190
|
+
* A JSON Web Key (JWK) that conforms to {@link https://datatracker.ietf.org/doc/html/rfc7517 | RFC 7517}.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```ts
|
|
194
|
+
* const verificationMethod: DidIonVerificationMethod = {
|
|
195
|
+
* publicKeyJwk: {
|
|
196
|
+
* kty : "OKP",
|
|
197
|
+
* crv : "X25519",
|
|
198
|
+
* x : "7XdJtNmJ9pV_O_3mxWdn6YjiHJ-HhNkdYQARzVU_mwY",
|
|
199
|
+
* kid : "xtsuKULPh6VN9fuJMRwj66cDfQyLaxuXHkMlmAe_v6I"
|
|
200
|
+
* },
|
|
201
|
+
* ...
|
|
202
|
+
* };
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
publicKeyJwk: Jwk;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Specify the purposes for which a verification method is intended to be used in a DID document.
|
|
209
|
+
*
|
|
210
|
+
* The `purposes` property defines the specific
|
|
211
|
+
* {@link DidVerificationRelationship | verification relationships} between the DID subject and
|
|
212
|
+
* the verification method. This enables the verification method to be utilized for distinct
|
|
213
|
+
* actions such as authentication, assertion, key agreement, capability delegation, and others. It
|
|
214
|
+
* is important for verifiers to recognize that a verification method must be associated with the
|
|
215
|
+
* relevant purpose in the DID document to be valid for that specific use case.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```ts
|
|
219
|
+
* const verificationMethod: DidIonVerificationMethod = {
|
|
220
|
+
* purposes: ['authentication', 'assertionMethod'],
|
|
221
|
+
* ...
|
|
222
|
+
* };
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
purposes: (DidVerificationRelationship | keyof typeof DidVerificationRelationship)[];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* `IonPortableDid` interface extends the {@link PortableDid} interface.
|
|
230
|
+
*
|
|
231
|
+
* It represents a Decentralized Identifier (DID) that is portable and can be used across different
|
|
232
|
+
* domains, including the ION specific recovery and update keys.
|
|
233
|
+
*/
|
|
234
|
+
export interface IonPortableDid extends PortableDid {
|
|
235
|
+
/** The JSON Web Key (JWK) used for recovery purposes. */
|
|
236
|
+
recoveryKey: Jwk;
|
|
237
|
+
|
|
238
|
+
/** The JSON Web Key (JWK) used for updating the DID. */
|
|
239
|
+
updateKey: Jwk;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Enumerates the types of keys that can be used in a DID ION document.
|
|
244
|
+
*
|
|
245
|
+
* The DID ION method supports various cryptographic key types. These key types are essential for
|
|
246
|
+
* the creation and management of DIDs and their associated cryptographic operations like signing
|
|
247
|
+
* and encryption.
|
|
248
|
+
*/
|
|
249
|
+
export enum DidIonRegisteredKeyType {
|
|
250
|
+
/**
|
|
251
|
+
* Ed25519: A public-key signature system using the EdDSA (Edwards-curve Digital Signature
|
|
252
|
+
* Algorithm) and Curve25519.
|
|
253
|
+
*/
|
|
254
|
+
Ed25519 = 'Ed25519',
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* secp256k1: A cryptographic curve used for digital signatures in a range of decentralized
|
|
258
|
+
* systems.
|
|
259
|
+
*/
|
|
260
|
+
secp256k1 = 'secp256k1',
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* secp256r1: Also known as P-256 or prime256v1, this curve is used for cryptographic operations
|
|
264
|
+
* and is widely supported in various cryptographic libraries and standards.
|
|
265
|
+
*/
|
|
266
|
+
secp256r1 = 'secp256r1',
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* X25519: A Diffie-Hellman key exchange algorithm using Curve25519.
|
|
270
|
+
*/
|
|
271
|
+
X25519 = 'X25519'
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Private helper that maps algorithm identifiers to their corresponding DID ION
|
|
276
|
+
* {@link DidIonRegisteredKeyType | registered key type}.
|
|
277
|
+
*/
|
|
278
|
+
const AlgorithmToKeyTypeMap = {
|
|
279
|
+
Ed25519 : DidIonRegisteredKeyType.Ed25519,
|
|
280
|
+
ES256K : DidIonRegisteredKeyType.secp256k1,
|
|
281
|
+
ES256 : DidIonRegisteredKeyType.secp256r1,
|
|
282
|
+
'P-256' : DidIonRegisteredKeyType.secp256r1,
|
|
283
|
+
secp256k1 : DidIonRegisteredKeyType.secp256k1,
|
|
284
|
+
secp256r1 : DidIonRegisteredKeyType.secp256r1
|
|
285
|
+
} as const;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* The default node to use as a gateway to the Sidetree newtork when anchoring, updating, and
|
|
289
|
+
* resolving DID documents.
|
|
290
|
+
*/
|
|
291
|
+
const DEFAULT_GATEWAY_URI = 'https://ion.tbd.engineering';
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The `DidIon` class provides an implementation of the `did:ion` DID method.
|
|
295
|
+
*
|
|
296
|
+
* Features:
|
|
297
|
+
* - DID Creation: Create new `did:ion` DIDs.
|
|
298
|
+
* - DID Key Management: Instantiate a DID object from an existing key in a Key Management System
|
|
299
|
+
* (KMS). If supported by the KMS, a DID's key can be exported to a portable
|
|
300
|
+
* DID format.
|
|
301
|
+
* - DID Resolution: Resolve a `did:ion` to its corresponding DID Document stored in the Sidetree
|
|
302
|
+
* network.
|
|
303
|
+
* - Signature Operations: Sign and verify messages using keys associated with a DID.
|
|
304
|
+
*
|
|
305
|
+
* @see {@link https://identity.foundation/sidetree/spec/ | Sidetree Protocol Specification}
|
|
306
|
+
* @see {@link https://github.com/decentralized-identity/ion/blob/master/docs/design.md | ION Design Document}
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```ts
|
|
310
|
+
* // DID Creation
|
|
311
|
+
* const did = await DidIon.create();
|
|
312
|
+
*
|
|
313
|
+
* // DID Creation with a KMS
|
|
314
|
+
* const keyManager = new LocalKeyManager();
|
|
315
|
+
* const did = await DidIon.create({ keyManager });
|
|
316
|
+
*
|
|
317
|
+
* // DID Resolution
|
|
318
|
+
* const resolutionResult = await DidIon.resolve({ did: did.uri });
|
|
319
|
+
*
|
|
320
|
+
* // Signature Operations
|
|
321
|
+
* const signer = await did.getSigner();
|
|
322
|
+
* const signature = await signer.sign({ data: new TextEncoder().encode('Message') });
|
|
323
|
+
* const isValid = await signer.verify({ data: new TextEncoder().encode('Message'), signature });
|
|
324
|
+
*
|
|
325
|
+
* // Key Management
|
|
326
|
+
*
|
|
327
|
+
* // Instantiate a DID object for a published DID with existing keys in a KMS
|
|
328
|
+
* const did = await DidIon.fromKeyManager({
|
|
329
|
+
* didUri: 'did:ion:EiAzB7K-xDIKc1csXo5HX2eNBoemK9feNhL3cKwfukYOug',
|
|
330
|
+
* keyManager
|
|
331
|
+
* });
|
|
332
|
+
*
|
|
333
|
+
* // Convert a DID object to a portable format
|
|
334
|
+
* const portableDid = await DidIon.toKeys({ did });
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
|
|
338
|
+
export class DidIon extends DidMethod {
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Name of the DID method, as defined in the DID ION specification.
|
|
342
|
+
*/
|
|
343
|
+
public static methodName = 'ion';
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Creates a new DID using the `did:ion` method formed from a newly generated key.
|
|
347
|
+
*
|
|
348
|
+
* Notes:
|
|
349
|
+
* - If no `options` are given, by default a new Ed25519 key will be generated.
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```ts
|
|
353
|
+
* // DID Creation
|
|
354
|
+
* const did = await DidIon.create();
|
|
355
|
+
*
|
|
356
|
+
* // DID Creation with a KMS
|
|
357
|
+
* const keyManager = new LocalKeyManager();
|
|
358
|
+
* const did = await DidIon.create({ keyManager });
|
|
359
|
+
* ```
|
|
360
|
+
*
|
|
361
|
+
* @param params - The parameters for the create operation.
|
|
362
|
+
* @param params.keyManager - Optionally specify a Key Management System (KMS) used to generate
|
|
363
|
+
* keys and sign data.
|
|
364
|
+
* @param params.options - Optional parameters that can be specified when creating a new DID.
|
|
365
|
+
* @returns A Promise resolving to a {@link BearerDid} object representing the new DID.
|
|
366
|
+
*/
|
|
367
|
+
public static async create<TKms extends CryptoApi | undefined = undefined>({
|
|
368
|
+
keyManager = new LocalKeyManager(),
|
|
369
|
+
options = {}
|
|
370
|
+
}: {
|
|
371
|
+
keyManager?: TKms;
|
|
372
|
+
options?: DidIonCreateOptions<TKms>;
|
|
373
|
+
} = {}): Promise<BearerDid> {
|
|
374
|
+
// Before processing the create operation, validate DID-method-specific requirements to prevent
|
|
375
|
+
// keys from being generated unnecessarily.
|
|
376
|
+
|
|
377
|
+
// Check 1: Validate that the algorithm for any given verification method is supported by the
|
|
378
|
+
// DID ION specification.
|
|
379
|
+
if (options.verificationMethods?.some(vm => !(vm.algorithm in AlgorithmToKeyTypeMap))) {
|
|
380
|
+
throw new Error('One or more verification method algorithms are not supported');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Check 2: Validate that the ID for any given verification method is unique.
|
|
384
|
+
const methodIds = options.verificationMethods?.filter(vm => 'id' in vm).map(vm => vm.id);
|
|
385
|
+
if (methodIds && methodIds.length !== new Set(methodIds).size) {
|
|
386
|
+
throw new Error('One or more verification method IDs are not unique');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Check 3: Validate that the required properties for any given services are present.
|
|
390
|
+
if (options.services?.some(s => !s.id || !s.type || !s.serviceEndpoint)) {
|
|
391
|
+
throw new Error('One or more services are missing required properties');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// If no verification methods were specified, generate a default Ed25519 verification method.
|
|
395
|
+
const defaultVerificationMethod: DidCreateVerificationMethod<TKms> = {
|
|
396
|
+
algorithm : 'Ed25519' as any,
|
|
397
|
+
purposes : ['authentication', 'assertionMethod', 'capabilityDelegation', 'capabilityInvocation']
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const verificationMethodsToAdd: DidIonVerificationMethod[] = [];
|
|
401
|
+
|
|
402
|
+
// Generate random key material for additional verification methods, if any.
|
|
403
|
+
for (const vm of options.verificationMethods ?? [defaultVerificationMethod]) {
|
|
404
|
+
// Generate a random key for the verification method.
|
|
405
|
+
const keyUri = await keyManager.generateKey({ algorithm: vm.algorithm });
|
|
406
|
+
const publicKey = await keyManager.getPublicKey({ keyUri });
|
|
407
|
+
|
|
408
|
+
// Add the verification method to the DID document.
|
|
409
|
+
verificationMethodsToAdd.push({
|
|
410
|
+
id : vm.id,
|
|
411
|
+
publicKeyJwk : publicKey,
|
|
412
|
+
purposes : vm.purposes ?? ['authentication', 'assertionMethod', 'capabilityDelegation', 'capabilityInvocation']
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Generate a random key for the ION Recovery Key. Sidetree requires secp256k1 recovery keys.
|
|
417
|
+
const recoveryKeyUri = await keyManager.generateKey({ algorithm: DidIonRegisteredKeyType.secp256k1 });
|
|
418
|
+
const recoveryKey = await keyManager.getPublicKey({ keyUri: recoveryKeyUri });
|
|
419
|
+
|
|
420
|
+
// Generate a random key for the ION Update Key. Sidetree requires secp256k1 update keys.
|
|
421
|
+
const updateKeyUri = await keyManager.generateKey({ algorithm: DidIonRegisteredKeyType.secp256k1 });
|
|
422
|
+
const updateKey = await keyManager.getPublicKey({ keyUri: updateKeyUri });
|
|
423
|
+
|
|
424
|
+
// Compute the Long Form DID URI from the keys and services, if any.
|
|
425
|
+
const longFormDidUri = await DidIonUtils.computeLongFormDidUri({
|
|
426
|
+
recoveryKey,
|
|
427
|
+
updateKey,
|
|
428
|
+
services : options.services ?? [],
|
|
429
|
+
verificationMethods : verificationMethodsToAdd
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Expand the DID URI string to a DID document.
|
|
433
|
+
const { didDocument, didResolutionMetadata } = await DidIon.resolve(longFormDidUri, { gatewayUri: options.gatewayUri });
|
|
434
|
+
if (didDocument === null) {
|
|
435
|
+
throw new Error(`Unable to resolve DID during creation: ${didResolutionMetadata?.error}`);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Create the BearerDid object, including the "Short Form" of the DID URI, the ION update and
|
|
439
|
+
// recovery keys, and specifying that the DID has not yet been published.
|
|
440
|
+
const did = new BearerDid({
|
|
441
|
+
uri : longFormDidUri,
|
|
442
|
+
document : didDocument,
|
|
443
|
+
metadata : {
|
|
444
|
+
published : false,
|
|
445
|
+
canonicalId : longFormDidUri.split(':', 3).join(':'),
|
|
446
|
+
recoveryKey,
|
|
447
|
+
updateKey
|
|
448
|
+
},
|
|
449
|
+
keyManager
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// By default, publish the DID document to a Sidetree node unless explicitly disabled.
|
|
453
|
+
if (options.publish ?? true) {
|
|
454
|
+
const registrationResult = await DidIon.publish({ did, gatewayUri: options.gatewayUri });
|
|
455
|
+
did.metadata = registrationResult.didDocumentMetadata;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return did;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Given the W3C DID Document of a `did:ion` DID, return the verification method that will be used
|
|
463
|
+
* for signing messages and credentials. If given, the `methodId` parameter is used to select the
|
|
464
|
+
* verification method. If not given, the first verification method in the authentication property
|
|
465
|
+
* in the DID Document is used.
|
|
466
|
+
*
|
|
467
|
+
* @param params - The parameters for the `getSigningMethod` operation.
|
|
468
|
+
* @param params.didDocument - DID Document to get the verification method from.
|
|
469
|
+
* @param params.methodId - ID of the verification method to use for signing.
|
|
470
|
+
* @returns Verification method to use for signing.
|
|
471
|
+
*/
|
|
472
|
+
public static async getSigningMethod({ didDocument, methodId }: {
|
|
473
|
+
didDocument: DidDocument;
|
|
474
|
+
methodId?: string;
|
|
475
|
+
}): Promise<DidVerificationMethod> {
|
|
476
|
+
// Verify the DID method is supported.
|
|
477
|
+
const parsedDid = Did.parse(didDocument.id);
|
|
478
|
+
if (parsedDid && parsedDid.method !== this.methodName) {
|
|
479
|
+
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported: ${parsedDid.method}`);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Get the verification method with either the specified ID or the first assertion method.
|
|
483
|
+
const verificationMethod = didDocument.verificationMethod?.find(
|
|
484
|
+
vm => vm.id === (methodId ?? didDocument.assertionMethod?.[0])
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
if (!(verificationMethod && verificationMethod.publicKeyJwk)) {
|
|
488
|
+
throw new DidError(DidErrorCode.InternalError, 'A verification method intended for signing could not be determined from the DID Document');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return verificationMethod;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Instantiates a {@link BearerDid} object for the DID ION method from a given {@link PortableDid}.
|
|
496
|
+
*
|
|
497
|
+
* This method allows for the creation of a `BearerDid` object using a previously created DID's
|
|
498
|
+
* key material, DID document, and metadata.
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* ```ts
|
|
502
|
+
* // Export an existing BearerDid to PortableDid format.
|
|
503
|
+
* const portableDid = await did.export();
|
|
504
|
+
* // Reconstruct a BearerDid object from the PortableDid.
|
|
505
|
+
* const did = await DidIon.import({ portableDid });
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* @param params - The parameters for the import operation.
|
|
509
|
+
* @param params.portableDid - The PortableDid object to import.
|
|
510
|
+
* @param params.keyManager - Optionally specify an external Key Management System (KMS) used to
|
|
511
|
+
* generate keys and sign data. If not given, a new
|
|
512
|
+
* {@link LocalKeyManager} instance will be created and
|
|
513
|
+
* used.
|
|
514
|
+
* @returns A Promise resolving to a `BearerDid` object representing the DID formed from the
|
|
515
|
+
* provided PortableDid.
|
|
516
|
+
* @throws An error if the DID document does not contain any verification methods or the keys for
|
|
517
|
+
* any verification method are missing in the key manager.
|
|
518
|
+
*/
|
|
519
|
+
public static async import({ portableDid, keyManager = new LocalKeyManager() }: {
|
|
520
|
+
keyManager?: CryptoApi & KeyImporterExporter<KmsImportKeyParams, KeyIdentifier, KmsExportKeyParams>;
|
|
521
|
+
portableDid: PortableDid;
|
|
522
|
+
}): Promise<BearerDid> {
|
|
523
|
+
// Verify the DID method is supported.
|
|
524
|
+
const parsedDid = Did.parse(portableDid.uri);
|
|
525
|
+
if (parsedDid?.method !== DidIon.methodName) {
|
|
526
|
+
throw new DidError(DidErrorCode.MethodNotSupported, `Method not supported`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const did = await BearerDid.import({ portableDid, keyManager });
|
|
530
|
+
|
|
531
|
+
return did;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Publishes a DID to a Sidetree node, making it publicly discoverable and resolvable.
|
|
536
|
+
*
|
|
537
|
+
* This method handles the publication of a DID Document associated with a `did:ion` DID to a
|
|
538
|
+
* Sidetree node.
|
|
539
|
+
*
|
|
540
|
+
* @remarks
|
|
541
|
+
* - This method is typically invoked automatically during the creation of a new DID unless the
|
|
542
|
+
* `publish` option is set to `false`.
|
|
543
|
+
* - For existing, unpublished DIDs, it can be used to publish the DID Document to a Sidetree node.
|
|
544
|
+
* - The method relies on the specified Sidetree node to interface with the network.
|
|
545
|
+
*
|
|
546
|
+
* @param params - The parameters for the `publish` operation.
|
|
547
|
+
* @param params.did - The `BearerDid` object representing the DID to be published.
|
|
548
|
+
* @param params.gatewayUri - Optional. The URI of a server involved in executing DID
|
|
549
|
+
* method operations. In the context of publishing, the
|
|
550
|
+
* endpoint is expected to be a Sidetree node. If not
|
|
551
|
+
* specified, a default node is used.
|
|
552
|
+
* @returns A Promise resolving to a boolean indicating whether the publication was successful.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* // Generate a new DID and keys but explicitly disable publishing.
|
|
557
|
+
* const did = await DidIon.create({ options: { publish: false } });
|
|
558
|
+
* // Publish the DID to the Sidetree network.
|
|
559
|
+
* const isPublished = await DidIon.publish({ did });
|
|
560
|
+
* // `isPublished` is true if the DID was successfully published.
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
public static async publish({ did, gatewayUri = DEFAULT_GATEWAY_URI }: {
|
|
564
|
+
did: BearerDid;
|
|
565
|
+
gatewayUri?: string;
|
|
566
|
+
}): Promise<DidRegistrationResult> {
|
|
567
|
+
// Construct an ION verification method made up of the id, public key, and purposes from each
|
|
568
|
+
// verification method in the DID document.
|
|
569
|
+
const verificationMethods: DidIonVerificationMethod[] = did.document.verificationMethod?.map(
|
|
570
|
+
vm => ({
|
|
571
|
+
id : vm.id,
|
|
572
|
+
publicKeyJwk : vm.publicKeyJwk!,
|
|
573
|
+
purposes : getVerificationRelationshipsById({ didDocument: did.document, methodId: vm.id })
|
|
574
|
+
})
|
|
575
|
+
) ?? [];
|
|
576
|
+
|
|
577
|
+
// Create the ION document.
|
|
578
|
+
const ionDocument = await DidIonUtils.createIonDocument({
|
|
579
|
+
services: did.document.service ?? [],
|
|
580
|
+
verificationMethods
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// Construct the ION Create Operation request.
|
|
584
|
+
const createOperation = await DidIonUtils.constructCreateRequest({
|
|
585
|
+
ionDocument,
|
|
586
|
+
recoveryKey : did.metadata.recoveryKey,
|
|
587
|
+
updateKey : did.metadata.updateKey
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
try {
|
|
591
|
+
// Construct the URL of the SideTree node's operations endpoint.
|
|
592
|
+
const operationsUrl = DidIonUtils.appendPathToUrl({
|
|
593
|
+
baseUrl : gatewayUri,
|
|
594
|
+
path : `/operations`
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// Submit the Create Operation to the operations endpoint.
|
|
598
|
+
const response = await fetch(operationsUrl, {
|
|
599
|
+
method : 'POST',
|
|
600
|
+
mode : 'cors',
|
|
601
|
+
headers : { 'Content-Type': 'application/json' },
|
|
602
|
+
body : JSON.stringify(createOperation)
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Return the result of processing the Create operation, including the updated DID metadata
|
|
606
|
+
// with the publishing result.
|
|
607
|
+
return {
|
|
608
|
+
didDocument : did.document,
|
|
609
|
+
didDocumentMetadata : {
|
|
610
|
+
...did.metadata,
|
|
611
|
+
published: response.ok,
|
|
612
|
+
},
|
|
613
|
+
didRegistrationMetadata: {}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
} catch (error: any) {
|
|
617
|
+
return {
|
|
618
|
+
didDocument : null,
|
|
619
|
+
didDocumentMetadata : {
|
|
620
|
+
published: false,
|
|
621
|
+
},
|
|
622
|
+
didRegistrationMetadata: {
|
|
623
|
+
error : DidErrorCode.InternalError,
|
|
624
|
+
errorMessage : `Failed to publish DID document for: ${did.uri}`
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Resolves a `did:ion` identifier to its corresponding DID document.
|
|
632
|
+
*
|
|
633
|
+
* This method performs the resolution of a `did:ion` DID, retrieving its DID Document from the
|
|
634
|
+
* Sidetree-based DID overlay network. The process involves querying a Sidetree node to retrieve
|
|
635
|
+
* the DID Document that corresponds to the given DID identifier.
|
|
636
|
+
*
|
|
637
|
+
* @remarks
|
|
638
|
+
* - If a `gatewayUri` option is not specified, a default node is used to access the Sidetree
|
|
639
|
+
* network.
|
|
640
|
+
* - It decodes the DID identifier and retrieves the associated DID Document and metadata.
|
|
641
|
+
* - In case of resolution failure, appropriate error information is returned.
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```ts
|
|
645
|
+
* const resolutionResult = await DidIon.resolve('did:ion:example');
|
|
646
|
+
* ```
|
|
647
|
+
*
|
|
648
|
+
* @param didUri - The DID to be resolved.
|
|
649
|
+
* @param options - Optional parameters for resolving the DID. Unused by this DID method.
|
|
650
|
+
* @returns A Promise resolving to a {@link DidResolutionResult} object representing the result of the resolution.
|
|
651
|
+
*/
|
|
652
|
+
public static async resolve(didUri: string, options: DidResolutionOptions = {}): Promise<DidResolutionResult> {
|
|
653
|
+
// Attempt to parse the DID URI.
|
|
654
|
+
const parsedDid = Did.parse(didUri);
|
|
655
|
+
|
|
656
|
+
// If parsing failed, the DID is invalid.
|
|
657
|
+
if (!parsedDid) {
|
|
658
|
+
return {
|
|
659
|
+
...EMPTY_DID_RESOLUTION_RESULT,
|
|
660
|
+
didResolutionMetadata: { error: 'invalidDid' }
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// If the DID method is not "ion", return an error.
|
|
665
|
+
if (parsedDid.method !== DidIon.methodName) {
|
|
666
|
+
return {
|
|
667
|
+
...EMPTY_DID_RESOLUTION_RESULT,
|
|
668
|
+
didResolutionMetadata: { error: 'methodNotSupported' }
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// To execute the read method operation, use the given gateway URI or a default Sidetree node.
|
|
673
|
+
const gatewayUri = options?.gatewayUri ?? DEFAULT_GATEWAY_URI;
|
|
674
|
+
|
|
675
|
+
try {
|
|
676
|
+
// Construct the URL to be used in the resolution request.
|
|
677
|
+
const resolutionUrl = DidIonUtils.appendPathToUrl({
|
|
678
|
+
baseUrl : gatewayUri,
|
|
679
|
+
path : `/identifiers/${didUri}`
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// Attempt to retrieve the DID document and metadata from the Sidetree node.
|
|
683
|
+
const response = await fetch(resolutionUrl);
|
|
684
|
+
|
|
685
|
+
// If the DID document was not found, return an error.
|
|
686
|
+
if (!response.ok) {
|
|
687
|
+
throw new DidError(DidErrorCode.NotFound, `Unable to find DID document for: ${didUri}`);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// If the DID document was retrieved successfully, return it.
|
|
691
|
+
const { didDocument, didDocumentMetadata } = await response.json() as DidResolutionResult;
|
|
692
|
+
return {
|
|
693
|
+
...EMPTY_DID_RESOLUTION_RESULT,
|
|
694
|
+
...didDocument && { didDocument },
|
|
695
|
+
didDocumentMetadata: {
|
|
696
|
+
published: didDocumentMetadata?.method?.published,
|
|
697
|
+
...didDocumentMetadata
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
} catch (error: any) {
|
|
702
|
+
// Rethrow any unexpected errors that are not a `DidError`.
|
|
703
|
+
if (!(error instanceof DidError)) throw new Error(error);
|
|
704
|
+
|
|
705
|
+
// Return a DID Resolution Result with the appropriate error code.
|
|
706
|
+
return {
|
|
707
|
+
...EMPTY_DID_RESOLUTION_RESULT,
|
|
708
|
+
didResolutionMetadata: {
|
|
709
|
+
error: error.code,
|
|
710
|
+
...error.message && { errorMessage: error.message }
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* The `DidIonUtils` class provides utility functions to support operations in the DID ION method.
|
|
719
|
+
*/
|
|
720
|
+
export class DidIonUtils {
|
|
721
|
+
/**
|
|
722
|
+
* Appends a specified path to a base URL, ensuring proper formatting of the resulting URL.
|
|
723
|
+
*
|
|
724
|
+
* This method is useful for constructing URLs for accessing various endpoints, such as Sidetree
|
|
725
|
+
* nodes in the ION network. It handles the nuances of URL path concatenation, including the
|
|
726
|
+
* addition or removal of leading/trailing slashes, to create a well-formed URL.
|
|
727
|
+
*
|
|
728
|
+
* @param params - The parameters for URL construction.
|
|
729
|
+
* @param params.baseUrl - The base URL to which the path will be appended.
|
|
730
|
+
* @param params.path - The path to append to the base URL.
|
|
731
|
+
* @returns The fully constructed URL string with the path appended to the base URL.
|
|
732
|
+
*/
|
|
733
|
+
public static appendPathToUrl({ baseUrl, path }: {
|
|
734
|
+
baseUrl: string;
|
|
735
|
+
path: string;
|
|
736
|
+
}): string {
|
|
737
|
+
const url = new URL(baseUrl);
|
|
738
|
+
url.pathname = url.pathname.endsWith('/') ? url.pathname : url.pathname + '/';
|
|
739
|
+
url.pathname += path.startsWith('/') ? path.substring(1) : path;
|
|
740
|
+
|
|
741
|
+
return url.toString();
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Computes the Long Form DID URI given an ION DID's recovery key, update key, services, and
|
|
746
|
+
* verification methods.
|
|
747
|
+
*
|
|
748
|
+
* @param params - The parameters for computing the Long Form DID URI.
|
|
749
|
+
* @param params.recoveryKey - The ION Recovery Key.
|
|
750
|
+
* @param params.updateKey - The ION Update Key.
|
|
751
|
+
* @param params.services - An array of services associated with the DID.
|
|
752
|
+
* @param params.verificationMethods - An array of verification methods associated with the DID.
|
|
753
|
+
* @returns A Promise resolving to the Long Form DID URI.
|
|
754
|
+
*/
|
|
755
|
+
public static async computeLongFormDidUri({ recoveryKey, updateKey, services, verificationMethods }: {
|
|
756
|
+
recoveryKey: Jwk;
|
|
757
|
+
updateKey: Jwk;
|
|
758
|
+
services: DidService[];
|
|
759
|
+
verificationMethods: DidIonVerificationMethod[];
|
|
760
|
+
}): Promise<string> {
|
|
761
|
+
// Create the ION document.
|
|
762
|
+
const ionDocument = await DidIonUtils.createIonDocument({ services, verificationMethods });
|
|
763
|
+
|
|
764
|
+
// Normalize JWK to onnly include specific members and in lexicographic order.
|
|
765
|
+
const normalizedRecoveryKey = DidIonUtils.normalizeJwk(recoveryKey);
|
|
766
|
+
const normalizedUpdateKey = DidIonUtils.normalizeJwk(updateKey);
|
|
767
|
+
|
|
768
|
+
// Compute the Long Form DID URI.
|
|
769
|
+
const longFormDidUri = await IonDid.createLongFormDid({
|
|
770
|
+
document : ionDocument,
|
|
771
|
+
recoveryKey : normalizedRecoveryKey as JwkEs256k,
|
|
772
|
+
updateKey : normalizedUpdateKey as JwkEs256k
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
return longFormDidUri;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Constructs a Sidetree Create Operation request for a DID document within the ION network.
|
|
780
|
+
*
|
|
781
|
+
* This method prepares the necessary payload for submitting a Create Operation to a Sidetree
|
|
782
|
+
* node, encapsulating the details of the DID document, recovery key, and update key.
|
|
783
|
+
*
|
|
784
|
+
* @param params - Parameters required to construct the Create Operation request.
|
|
785
|
+
* @param params.ionDocument - The DID document model containing public keys and service endpoints.
|
|
786
|
+
* @param params.recoveryKey - The recovery public key in JWK format.
|
|
787
|
+
* @param params.updateKey - The update public key in JWK format.
|
|
788
|
+
* @returns A promise resolving to the ION Create Operation request model, ready for submission to a Sidetree node.
|
|
789
|
+
*/
|
|
790
|
+
public static async constructCreateRequest({ ionDocument, recoveryKey, updateKey }: {
|
|
791
|
+
ionDocument: IonDocumentModel,
|
|
792
|
+
recoveryKey: Jwk,
|
|
793
|
+
updateKey: Jwk
|
|
794
|
+
}): Promise<DidIonCreateRequest> {
|
|
795
|
+
// Create an ION DID create request operation.
|
|
796
|
+
const createRequest = await IonRequest.createCreateRequest({
|
|
797
|
+
document : ionDocument,
|
|
798
|
+
recoveryKey : DidIonUtils.normalizeJwk(recoveryKey) as JwkEs256k,
|
|
799
|
+
updateKey : DidIonUtils.normalizeJwk(updateKey) as JwkEs256k
|
|
800
|
+
}) as DidIonCreateRequest;
|
|
801
|
+
|
|
802
|
+
return createRequest;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Assembles an ION document model from provided services and verification methods
|
|
807
|
+
*
|
|
808
|
+
* This model serves as the foundation for a DID document in the ION network, facilitating the
|
|
809
|
+
* creation and management of decentralized identities. It translates service endpoints and
|
|
810
|
+
* public keys into a format compatible with the Sidetree protocol, ensuring the resulting DID
|
|
811
|
+
* document adheres to the required specifications for ION DIDs. This method is essential for
|
|
812
|
+
* constructing the payload needed to register or update DIDs within the ION network.
|
|
813
|
+
*
|
|
814
|
+
* @param params - The parameters containing the services and verification methods to include in the ION document.
|
|
815
|
+
* @param params.services - A list of service endpoints to be included in the DID document, specifying ways to interact with the DID subject.
|
|
816
|
+
* @param params.verificationMethods - A list of verification methods to be included, detailing the cryptographic keys and their intended uses within the DID document.
|
|
817
|
+
* @returns A Promise resolving to an `IonDocumentModel`, ready for use in Sidetree operations like DID creation and updates.
|
|
818
|
+
*/
|
|
819
|
+
public static async createIonDocument({ services, verificationMethods }: {
|
|
820
|
+
services: DidService[];
|
|
821
|
+
verificationMethods: DidIonVerificationMethod[]
|
|
822
|
+
}): Promise<IonDocumentModel> {
|
|
823
|
+
/**
|
|
824
|
+
* STEP 1: Convert verification methods to ION SDK format.
|
|
825
|
+
*/
|
|
826
|
+
const ionPublicKeys: IonPublicKeyModel[] = [];
|
|
827
|
+
|
|
828
|
+
for (const vm of verificationMethods) {
|
|
829
|
+
// Use the given ID, the key's ID, or the key's thumbprint as the verification method ID.
|
|
830
|
+
let methodId = vm.id ?? vm.publicKeyJwk.kid ?? await computeJwkThumbprint({ jwk: vm.publicKeyJwk });
|
|
831
|
+
methodId = `${methodId.split('#').pop()}`; // Remove fragment prefix, if any.
|
|
832
|
+
|
|
833
|
+
// Convert public key JWK to ION format.
|
|
834
|
+
const publicKey: IonPublicKeyModel = {
|
|
835
|
+
id : methodId,
|
|
836
|
+
publicKeyJwk : DidIonUtils.normalizeJwk(vm.publicKeyJwk),
|
|
837
|
+
purposes : vm.purposes as IonPublicKeyPurpose[],
|
|
838
|
+
type : 'JsonWebKey2020'
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
ionPublicKeys.push(publicKey);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* STEP 2: Convert service entries, if any, to ION SDK format.
|
|
846
|
+
*/
|
|
847
|
+
const ionServices = services.map(service => ({
|
|
848
|
+
...service,
|
|
849
|
+
id: `${service.id.split('#').pop()}` // Remove fragment prefix, if any.
|
|
850
|
+
}));
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* STEP 3: Format as ION document.
|
|
854
|
+
*/
|
|
855
|
+
const ionDocumentModel: IonDocumentModel = {
|
|
856
|
+
publicKeys : ionPublicKeys,
|
|
857
|
+
services : ionServices
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
return ionDocumentModel;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Normalize the given JWK to include only specific members and in lexicographic order.
|
|
865
|
+
*
|
|
866
|
+
* @param jwk - The JWK to normalize.
|
|
867
|
+
* @returns The normalized JWK.
|
|
868
|
+
*/
|
|
869
|
+
private static normalizeJwk(jwk: Jwk): Jwk {
|
|
870
|
+
const keyType = jwk.kty;
|
|
871
|
+
let normalizedJwk: Jwk;
|
|
872
|
+
|
|
873
|
+
if (keyType === 'EC') {
|
|
874
|
+
normalizedJwk = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
|
|
875
|
+
} else if (keyType === 'oct') {
|
|
876
|
+
normalizedJwk = { k: jwk.k, kty: jwk.kty };
|
|
877
|
+
} else if (keyType === 'OKP') {
|
|
878
|
+
normalizedJwk = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
|
|
879
|
+
} else if (keyType === 'RSA') {
|
|
880
|
+
normalizedJwk = { e: jwk.e, kty: jwk.kty, n: jwk.n };
|
|
881
|
+
} else {
|
|
882
|
+
throw new Error(`Unsupported key type: ${keyType}`);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return normalizedJwk;
|
|
886
|
+
}
|
|
887
|
+
}
|