@dwn-protocol/id-sdk 0.2.5 → 0.2.6
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/package.json +2 -3
- package/src/agent/app-data-store.ts +0 -365
- package/src/agent/did-manager.ts +0 -393
- package/src/agent/dwn-manager.ts +0 -548
- package/src/agent/identity-manager.ts +0 -165
- package/src/agent/index.ts +0 -19
- package/src/agent/json-rpc.ts +0 -107
- package/src/agent/key-manager.ts +0 -302
- package/src/agent/kms-local.ts +0 -412
- package/src/agent/outbox.ts +0 -128
- package/src/agent/rpc-client.ts +0 -223
- package/src/agent/store-managed-did.ts +0 -295
- package/src/agent/store-managed-identity.ts +0 -243
- package/src/agent/store-managed-key.ts +0 -754
- package/src/agent/sync-manager.ts +0 -631
- package/src/agent/test-managed-agent.ts +0 -299
- package/src/agent/types/agent.ts +0 -145
- package/src/agent/types/managed-key.ts +0 -442
- package/src/agent/utils.ts +0 -190
- package/src/common/convert.ts +0 -424
- package/src/common/index.ts +0 -9
- package/src/common/multicodec.ts +0 -176
- package/src/common/object.ts +0 -43
- package/src/common/stores.ts +0 -125
- package/src/common/stream-node.ts +0 -381
- package/src/common/stream.ts +0 -406
- package/src/common/type-utils.ts +0 -117
- package/src/common/types.ts +0 -48
- package/src/credentials/credential-bbs.ts +0 -419
- package/src/credentials/credential.ts +0 -324
- package/src/credentials/index.ts +0 -5
- package/src/credentials/presentation.ts +0 -182
- package/src/credentials/status-list.ts +0 -365
- package/src/credentials/utils.ts +0 -58
- package/src/credentials/validators.ts +0 -52
- package/src/crypto/algorithms-api/aes/base.ts +0 -49
- package/src/crypto/algorithms-api/aes/ctr.ts +0 -51
- package/src/crypto/algorithms-api/aes/index.ts +0 -2
- package/src/crypto/algorithms-api/crypto-algorithm.ts +0 -127
- package/src/crypto/algorithms-api/crypto-key.ts +0 -56
- package/src/crypto/algorithms-api/ec/base.ts +0 -39
- package/src/crypto/algorithms-api/ec/ecdh.ts +0 -53
- package/src/crypto/algorithms-api/ec/ecdsa.ts +0 -37
- package/src/crypto/algorithms-api/ec/eddsa.ts +0 -30
- package/src/crypto/algorithms-api/ec/index.ts +0 -4
- package/src/crypto/algorithms-api/errors.ts +0 -29
- package/src/crypto/algorithms-api/index.ts +0 -6
- package/src/crypto/algorithms-api/pbkdf/index.ts +0 -1
- package/src/crypto/algorithms-api/pbkdf/pbkdf2.ts +0 -91
- package/src/crypto/crypto-algorithms/aes-ctr.ts +0 -70
- package/src/crypto/crypto-algorithms/bbs.ts +0 -110
- package/src/crypto/crypto-algorithms/ecdh.ts +0 -115
- package/src/crypto/crypto-algorithms/ecdsa.ts +0 -111
- package/src/crypto/crypto-algorithms/eddsa.ts +0 -110
- package/src/crypto/crypto-algorithms/index.ts +0 -6
- package/src/crypto/crypto-algorithms/pbkdf2.ts +0 -54
- package/src/crypto/crypto-primitives/aes-ctr.ts +0 -131
- package/src/crypto/crypto-primitives/aes-gcm.ts +0 -138
- package/src/crypto/crypto-primitives/bbs.ts +0 -183
- package/src/crypto/crypto-primitives/concat-kdf.ts +0 -207
- package/src/crypto/crypto-primitives/ed25519.ts +0 -201
- package/src/crypto/crypto-primitives/index.ts +0 -10
- package/src/crypto/crypto-primitives/pbkdf2.ts +0 -78
- package/src/crypto/crypto-primitives/secp256k1.ts +0 -322
- package/src/crypto/crypto-primitives/x25519.ts +0 -101
- package/src/crypto/crypto-primitives/xchacha20-poly1305.ts +0 -46
- package/src/crypto/crypto-primitives/xchacha20.ts +0 -34
- package/src/crypto/index.ts +0 -8
- package/src/crypto/jose.ts +0 -948
- package/src/crypto/types/crypto-key.ts +0 -4
- package/src/crypto/types/iddwn-crypto.ts +0 -119
- package/src/crypto/utils.ts +0 -200
- package/src/did-api.ts +0 -72
- package/src/dids/dht.ts +0 -412
- package/src/dids/did-dht.ts +0 -436
- package/src/dids/did-ion.ts +0 -613
- package/src/dids/did-key.ts +0 -791
- package/src/dids/did-resolver.ts +0 -107
- package/src/dids/index.ts +0 -9
- package/src/dids/resolver-cache-level.ts +0 -82
- package/src/dids/resolver-cache-noop.ts +0 -25
- package/src/dids/types.ts +0 -278
- package/src/dids/utils.ts +0 -129
- package/src/dwn-api.ts +0 -584
- package/src/iddwn.ts +0 -241
- package/src/identity-agent/index.ts +0 -270
- package/src/index.ts +0 -26
- package/src/interfaces/metadata.ts +0 -163
- package/src/interfaces/queue.ts +0 -108
- package/src/interfaces/services.ts +0 -122
- package/src/interfaces/transactions.ts +0 -220
- package/src/protocol.ts +0 -68
- package/src/proxy-agent/index.ts +0 -255
- package/src/record.ts +0 -521
- package/src/service-options.ts +0 -62
- package/src/typings/decentralized-identity__ion-pow-sdk.d.ts +0 -7
- package/src/user-agent/index.ts +0 -295
- package/src/utils.ts +0 -29
- package/src/vc-api.ts +0 -505
package/src/dids/did-ion.ts
DELETED
|
@@ -1,613 +0,0 @@
|
|
|
1
|
-
import type { JwkKeyPair, PrivateKeyJwk, PublicKeyJwk, IDCrypto } from '../crypto/index.js';
|
|
2
|
-
import type { IonDocumentModel, IonPublicKeyModel, JwkEd25519, JwkEs256k } from '@decentralized-identity/ion-sdk';
|
|
3
|
-
|
|
4
|
-
import { Convert, universalTypeOf } from '../common/index.js';
|
|
5
|
-
import IonProofOfWork from '@decentralized-identity/ion-pow-sdk';
|
|
6
|
-
import { EcdsaAlgorithm, EdDsaAlgorithm, Jose } from '../crypto/index.js';
|
|
7
|
-
import { IonDid, IonPublicKeyPurpose, IonRequest } from '@decentralized-identity/ion-sdk';
|
|
8
|
-
|
|
9
|
-
import type { DidDocument, DidKeySetVerificationMethodKey, DidMethod, DidResolutionOptions, DidResolutionResult, DidService, DwnServiceEndpoint, PortableDid } from './types.js';
|
|
10
|
-
|
|
11
|
-
import { getServices, isDwnServiceEndpoint, parseDid } from './utils.js';
|
|
12
|
-
|
|
13
|
-
export type DidIonAnchorOptions = {
|
|
14
|
-
challengeEnabled?: boolean;
|
|
15
|
-
challengeEndpoint?: string;
|
|
16
|
-
operationsEndpoint?: string;
|
|
17
|
-
keySet: DidIonKeySet;
|
|
18
|
-
services: DidService[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type DidIonCreateOptions = {
|
|
22
|
-
anchor?: boolean;
|
|
23
|
-
keyAlgorithm?: typeof SupportedCryptoAlgorithms[number];
|
|
24
|
-
keySet?: DidIonKeySet;
|
|
25
|
-
services?: DidService[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type DidIonKeySet = {
|
|
29
|
-
recoveryKey?: JwkKeyPair;
|
|
30
|
-
updateKey?: JwkKeyPair;
|
|
31
|
-
verificationMethodKeys?: DidKeySetVerificationMethodKey[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
enum OperationType {
|
|
35
|
-
Create = 'create',
|
|
36
|
-
Update = 'update',
|
|
37
|
-
Deactivate = 'deactivate',
|
|
38
|
-
Recover = 'recover'
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Data model representing a public key in the DID Document.
|
|
43
|
-
*/
|
|
44
|
-
export interface IonCreateRequestModel {
|
|
45
|
-
type: OperationType;
|
|
46
|
-
suffixData: {
|
|
47
|
-
deltaHash: string;
|
|
48
|
-
recoveryCommitment: string;
|
|
49
|
-
};
|
|
50
|
-
delta: {
|
|
51
|
-
updateCommitment: string;
|
|
52
|
-
patches: {
|
|
53
|
-
action: string;
|
|
54
|
-
document: IonDocumentModel;
|
|
55
|
-
}[];
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const SupportedCryptoAlgorithms = [
|
|
60
|
-
'Ed25519',
|
|
61
|
-
'secp256k1'
|
|
62
|
-
] as const;
|
|
63
|
-
|
|
64
|
-
const VerificationRelationshipToIonPublicKeyPurpose = {
|
|
65
|
-
assertionMethod : IonPublicKeyPurpose.AssertionMethod,
|
|
66
|
-
authentication : IonPublicKeyPurpose.Authentication,
|
|
67
|
-
capabilityDelegation : IonPublicKeyPurpose.CapabilityDelegation,
|
|
68
|
-
capabilityInvocation : IonPublicKeyPurpose.CapabilityInvocation,
|
|
69
|
-
keyAgreement : IonPublicKeyPurpose.KeyAgreement
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export class DidIonMethod implements DidMethod {
|
|
73
|
-
/**
|
|
74
|
-
* Name of the DID method
|
|
75
|
-
*/
|
|
76
|
-
public static methodName = 'ion';
|
|
77
|
-
|
|
78
|
-
public static async anchor(options: {
|
|
79
|
-
services: DidService[],
|
|
80
|
-
keySet: DidIonKeySet,
|
|
81
|
-
challengeEnabled?: boolean,
|
|
82
|
-
challengeEndpoint?: string,
|
|
83
|
-
operationsEndpoint?: string
|
|
84
|
-
}): Promise<DidResolutionResult | undefined> {
|
|
85
|
-
const {
|
|
86
|
-
challengeEnabled = true,
|
|
87
|
-
challengeEndpoint = 'https://beta.ion.msidentity.com/api/v1.0/proof-of-work-challenge',
|
|
88
|
-
keySet,
|
|
89
|
-
services,
|
|
90
|
-
operationsEndpoint = 'https://beta.ion.msidentity.com/api/v1.0/operations'
|
|
91
|
-
} = options;
|
|
92
|
-
|
|
93
|
-
// Create ION Document.
|
|
94
|
-
const ionDocument = await DidIonMethod.createIonDocument({
|
|
95
|
-
keySet: keySet,
|
|
96
|
-
services
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const createRequest = await DidIonMethod.getIonCreateRequest({
|
|
100
|
-
ionDocument,
|
|
101
|
-
recoveryPublicKeyJwk : keySet.recoveryKey.publicKeyJwk,
|
|
102
|
-
updatePublicKeyJwk : keySet.updateKey.publicKeyJwk
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
let resolutionResult: DidResolutionResult;
|
|
106
|
-
|
|
107
|
-
if (challengeEnabled) {
|
|
108
|
-
const response = await IonProofOfWork.submitIonRequest(
|
|
109
|
-
challengeEndpoint,
|
|
110
|
-
operationsEndpoint,
|
|
111
|
-
JSON.stringify(createRequest)
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
if (response !== undefined && universalTypeOf(response) === 'String') {
|
|
115
|
-
resolutionResult = JSON.parse(response);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
} else {
|
|
119
|
-
const response = await fetch(operationsEndpoint, {
|
|
120
|
-
method : 'POST',
|
|
121
|
-
mode : 'cors',
|
|
122
|
-
body : JSON.stringify(createRequest),
|
|
123
|
-
headers : {
|
|
124
|
-
'Content-Type': 'application/json'
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
if (response.ok) {
|
|
129
|
-
resolutionResult = await response.json();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return resolutionResult;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public static async create(options?: DidIonCreateOptions): Promise<PortableDid> {
|
|
137
|
-
let { anchor, keyAlgorithm, keySet, services } = options ?? { };
|
|
138
|
-
|
|
139
|
-
// Begin constructing a PortableDid.
|
|
140
|
-
const did: Partial<PortableDid> = {};
|
|
141
|
-
|
|
142
|
-
// If any member of the key set is missing, generate the keys.
|
|
143
|
-
did.keySet = await DidIonMethod.generateKeySet({ keyAlgorithm, keySet });
|
|
144
|
-
|
|
145
|
-
// Generate Long Form DID URI.
|
|
146
|
-
did.did = await DidIonMethod.getLongFormDid({
|
|
147
|
-
keySet: did.keySet,
|
|
148
|
-
services
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Get short form DID.
|
|
152
|
-
did.canonicalId = await DidIonMethod.getShortFormDid({ didUrl: did.did });
|
|
153
|
-
|
|
154
|
-
let didResolutionResult: DidResolutionResult | undefined;
|
|
155
|
-
if (anchor) {
|
|
156
|
-
// Attempt to anchor the DID.
|
|
157
|
-
didResolutionResult = await DidIonMethod.anchor({
|
|
158
|
-
keySet: did.keySet,
|
|
159
|
-
services
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
} else {
|
|
163
|
-
// If anchoring was not requested, then resolve the long form DID.
|
|
164
|
-
didResolutionResult = await DidIonMethod.resolve({ didUrl: did.did });
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Store the DID Document.
|
|
168
|
-
did.document = didResolutionResult.didDocument;
|
|
169
|
-
|
|
170
|
-
return did as PortableDid;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
public static async decodeLongFormDid(options: {
|
|
174
|
-
didUrl: string
|
|
175
|
-
}): Promise<IonCreateRequestModel> {
|
|
176
|
-
const { didUrl } = options;
|
|
177
|
-
|
|
178
|
-
const parsedDid = parseDid({ didUrl });
|
|
179
|
-
|
|
180
|
-
if (!parsedDid) {
|
|
181
|
-
throw new Error(`DidIonMethod: Unable to parse DID: ${didUrl}`);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const decodedLongFormDid = Convert.base64Url(
|
|
185
|
-
parsedDid.id.split(':').pop()
|
|
186
|
-
).toObject() as Pick<IonCreateRequestModel, 'delta' | 'suffixData'>;
|
|
187
|
-
|
|
188
|
-
const createRequest: IonCreateRequestModel = {
|
|
189
|
-
...decodedLongFormDid,
|
|
190
|
-
type: OperationType.Create
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return createRequest;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Generates two key pairs used for authorization and encryption purposes
|
|
198
|
-
* when interfacing with DWNs. The IDs of these keys are referenced in the
|
|
199
|
-
* service object that includes the dwnUrls provided.
|
|
200
|
-
*/
|
|
201
|
-
public static async generateDwnOptions(options: {
|
|
202
|
-
encryptionKeyId?: string,
|
|
203
|
-
serviceEndpointNodes: string[],
|
|
204
|
-
serviceId?: string,
|
|
205
|
-
signingKeyAlgorithm?: typeof SupportedCryptoAlgorithms[number]
|
|
206
|
-
signingKeyId?: string,
|
|
207
|
-
}): Promise<DidIonCreateOptions> {
|
|
208
|
-
const {
|
|
209
|
-
signingKeyAlgorithm = 'Ed25519', // Generate Ed25519 key pairs, by default.
|
|
210
|
-
serviceId = '#dwn', // Use default ID value, unless overridden.
|
|
211
|
-
signingKeyId = '#dwn-sig', // Use default key ID value, unless overridden.
|
|
212
|
-
encryptionKeyId = '#dwn-enc', // Use default key ID value, unless overridden.
|
|
213
|
-
serviceEndpointNodes } = options;
|
|
214
|
-
|
|
215
|
-
const signingKeyPair = await DidIonMethod.generateJwkKeyPair({
|
|
216
|
-
keyAlgorithm : signingKeyAlgorithm,
|
|
217
|
-
keyId : signingKeyId
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
/** Currently, `id` has only implemented support for record
|
|
221
|
-
* encryption using the `ECIES-ES256K` crypto algorithm. Until the
|
|
222
|
-
* DWN SDK supports ECIES with EdDSA, the encryption key pair must
|
|
223
|
-
* use secp256k1. */
|
|
224
|
-
const encryptionKeyPair = await DidIonMethod.generateJwkKeyPair({
|
|
225
|
-
keyAlgorithm : 'secp256k1',
|
|
226
|
-
keyId : encryptionKeyId
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const keySet: DidIonKeySet = {
|
|
230
|
-
verificationMethodKeys: [
|
|
231
|
-
{ ...signingKeyPair, relationships: ['authentication'] },
|
|
232
|
-
{ ...encryptionKeyPair, relationships: ['keyAgreement'] }
|
|
233
|
-
]
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const serviceEndpoint: DwnServiceEndpoint = {
|
|
237
|
-
encryptionKeys : [encryptionKeyId],
|
|
238
|
-
nodes : serviceEndpointNodes,
|
|
239
|
-
signingKeys : [signingKeyId]
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const services: DidService[] = [{
|
|
243
|
-
id : serviceId,
|
|
244
|
-
serviceEndpoint,
|
|
245
|
-
type : 'DecentralizedWebNode',
|
|
246
|
-
}];
|
|
247
|
-
|
|
248
|
-
return { keySet, services };
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
public static async generateJwkKeyPair(options: {
|
|
252
|
-
keyAlgorithm: typeof SupportedCryptoAlgorithms[number],
|
|
253
|
-
keyId?: string
|
|
254
|
-
}): Promise<JwkKeyPair> {
|
|
255
|
-
const { keyAlgorithm, keyId } = options;
|
|
256
|
-
|
|
257
|
-
let cryptoKeyPair: IDCrypto.CryptoKeyPair;
|
|
258
|
-
|
|
259
|
-
switch (keyAlgorithm) {
|
|
260
|
-
case 'Ed25519': {
|
|
261
|
-
cryptoKeyPair = await new EdDsaAlgorithm().generateKey({
|
|
262
|
-
algorithm : { name: 'EdDSA', namedCurve: 'Ed25519' },
|
|
263
|
-
extractable : true,
|
|
264
|
-
keyUsages : ['sign', 'verify']
|
|
265
|
-
});
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
case 'secp256k1': {
|
|
270
|
-
cryptoKeyPair = await new EcdsaAlgorithm().generateKey({
|
|
271
|
-
algorithm : { name: 'ECDSA', namedCurve: 'secp256k1' },
|
|
272
|
-
extractable : true,
|
|
273
|
-
keyUsages : ['sign', 'verify']
|
|
274
|
-
});
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
default: {
|
|
279
|
-
throw new Error(`Unsupported crypto algorithm: '${keyAlgorithm}'`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Convert the CryptoKeyPair to JwkKeyPair.
|
|
284
|
-
const jwkKeyPair = await Jose.cryptoKeyToJwkPair({ keyPair: cryptoKeyPair });
|
|
285
|
-
|
|
286
|
-
// Set kid values.
|
|
287
|
-
if (keyId) {
|
|
288
|
-
jwkKeyPair.privateKeyJwk.kid = keyId;
|
|
289
|
-
jwkKeyPair.publicKeyJwk.kid = keyId;
|
|
290
|
-
} else {
|
|
291
|
-
// If a key ID is not specified, generate RFC 7638 JWK thumbprint.
|
|
292
|
-
const jwkThumbprint = await Jose.jwkThumbprint({ key: jwkKeyPair.publicKeyJwk });
|
|
293
|
-
jwkKeyPair.privateKeyJwk.kid = jwkThumbprint;
|
|
294
|
-
jwkKeyPair.publicKeyJwk.kid = jwkThumbprint;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return jwkKeyPair;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
public static async generateKeySet(options?: {
|
|
301
|
-
keyAlgorithm?: typeof SupportedCryptoAlgorithms[number],
|
|
302
|
-
keySet?: DidIonKeySet
|
|
303
|
-
}): Promise<DidIonKeySet> {
|
|
304
|
-
// Generate Ed25519 authentication key pair, by default.
|
|
305
|
-
let { keyAlgorithm = 'Ed25519', keySet = {} } = options ?? {};
|
|
306
|
-
|
|
307
|
-
// If keySet lacks verification method keys, generate one.
|
|
308
|
-
if (keySet.verificationMethodKeys === undefined) {
|
|
309
|
-
const authenticationkeyPair = await DidIonMethod.generateJwkKeyPair({
|
|
310
|
-
keyAlgorithm,
|
|
311
|
-
keyId: 'dwn-sig'
|
|
312
|
-
});
|
|
313
|
-
keySet.verificationMethodKeys = [{
|
|
314
|
-
...authenticationkeyPair,
|
|
315
|
-
relationships: ['authentication', 'assertionMethod']
|
|
316
|
-
}];
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// If keySet lacks recovery key, generate one.
|
|
320
|
-
if (keySet.recoveryKey === undefined) {
|
|
321
|
-
// Note: ION/Sidetree only supports secp256k1 recovery keys.
|
|
322
|
-
keySet.recoveryKey = await DidIonMethod.generateJwkKeyPair({
|
|
323
|
-
keyAlgorithm : 'secp256k1',
|
|
324
|
-
keyId : 'ion-recovery-1'
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// If keySet lacks update key, generate one.
|
|
329
|
-
if (keySet.updateKey === undefined) {
|
|
330
|
-
// Note: ION/Sidetree only supports secp256k1 update keys.
|
|
331
|
-
keySet.updateKey = await DidIonMethod.generateJwkKeyPair({
|
|
332
|
-
keyAlgorithm : 'secp256k1',
|
|
333
|
-
keyId : 'ion-update-1'
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Generate RFC 7638 JWK thumbprints if `kid` is missing from any key.
|
|
338
|
-
for (const key of [...keySet.verificationMethodKeys, keySet.recoveryKey, keySet.updateKey]) {
|
|
339
|
-
if ('publicKeyJwk' in key) key.publicKeyJwk.kid ??= await Jose.jwkThumbprint({ key: key.publicKeyJwk });
|
|
340
|
-
if ('privateKeyJwk' in key) key.privateKeyJwk.kid ??= await Jose.jwkThumbprint({ key: key.privateKeyJwk });
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return keySet;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Given the W3C DID Document of a `did:ion` DID, return the identifier of
|
|
348
|
-
* the verification method key that will be used for signing messages and
|
|
349
|
-
* credentials, by default.
|
|
350
|
-
*
|
|
351
|
-
* @param document = DID Document to get the default signing key from.
|
|
352
|
-
* @returns Verification method identifier for the default signing key.
|
|
353
|
-
*/
|
|
354
|
-
public static async getDefaultSigningKey(options: {
|
|
355
|
-
didDocument: DidDocument
|
|
356
|
-
}): Promise<string | undefined> {
|
|
357
|
-
const { didDocument } = options;
|
|
358
|
-
|
|
359
|
-
if (!didDocument.id) {
|
|
360
|
-
throw new Error(`DidIonMethod: DID document is missing 'id' property`);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** If the DID document contains a DWN service endpoint in the expected
|
|
364
|
-
* format, return the first entry in the `signingKeys` array. */
|
|
365
|
-
const [dwnService] = getServices({ didDocument, type: 'DecentralizedWebNode' });
|
|
366
|
-
if (isDwnServiceEndpoint(dwnService?.serviceEndpoint)) {
|
|
367
|
-
const [verificationMethodId] = dwnService.serviceEndpoint.signingKeys;
|
|
368
|
-
const did = didDocument.id;
|
|
369
|
-
const signingKeyId = `${did}${verificationMethodId}`;
|
|
370
|
-
return signingKeyId;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/** Otherwise, fallback to a naive approach of returning the first key ID
|
|
374
|
-
* in the `authentication` verification relationships array. */
|
|
375
|
-
if (didDocument.authentication
|
|
376
|
-
&& Array.isArray(didDocument.authentication)
|
|
377
|
-
&& didDocument.authentication.length > 0
|
|
378
|
-
&& typeof didDocument.authentication[0] === 'string') {
|
|
379
|
-
const [verificationMethodId] = didDocument.authentication;
|
|
380
|
-
const did = didDocument.id;
|
|
381
|
-
const signingKeyId = `${did}${verificationMethodId}`;
|
|
382
|
-
return signingKeyId;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
public static async getLongFormDid(options: {
|
|
387
|
-
services: DidService[],
|
|
388
|
-
keySet: DidIonKeySet
|
|
389
|
-
}): Promise<string> {
|
|
390
|
-
const { services = [], keySet } = options;
|
|
391
|
-
|
|
392
|
-
// Create ION Document.
|
|
393
|
-
const ionDocument = await DidIonMethod.createIonDocument({
|
|
394
|
-
keySet: keySet,
|
|
395
|
-
services
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
// Filter JWK to only those properties expected by ION/Sidetree.
|
|
399
|
-
const recoveryKey = DidIonMethod.jwkToIonJwk({ key: keySet.recoveryKey.publicKeyJwk }) as JwkEs256k;
|
|
400
|
-
const updateKey = DidIonMethod.jwkToIonJwk({ key: keySet.updateKey.publicKeyJwk }) as JwkEs256k;
|
|
401
|
-
|
|
402
|
-
// Create an ION DID create request operation.
|
|
403
|
-
const did = await IonDid.createLongFormDid({
|
|
404
|
-
document: ionDocument,
|
|
405
|
-
recoveryKey,
|
|
406
|
-
updateKey
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
return did;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
public static async getShortFormDid(options: {
|
|
413
|
-
didUrl: string
|
|
414
|
-
}): Promise<string> {
|
|
415
|
-
const { didUrl } = options;
|
|
416
|
-
|
|
417
|
-
const parsedDid = parseDid({ didUrl });
|
|
418
|
-
|
|
419
|
-
if (!parsedDid) {
|
|
420
|
-
throw new Error(`DidIonMethod: Unable to parse DID: ${didUrl}`);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const shortFormDid = parsedDid.did.split(':', 3).join(':');
|
|
424
|
-
|
|
425
|
-
return shortFormDid;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
public static async resolve(options: {
|
|
429
|
-
didUrl: string,
|
|
430
|
-
resolutionOptions?: DidResolutionOptions
|
|
431
|
-
}): Promise<DidResolutionResult> {
|
|
432
|
-
// TODO: add resolutionOptions as defined in https://www.w3.org/TR/did-core/#did-resolution
|
|
433
|
-
const { didUrl, resolutionOptions = {} } = options;
|
|
434
|
-
|
|
435
|
-
const parsedDid = parseDid({ didUrl });
|
|
436
|
-
if (!parsedDid) {
|
|
437
|
-
return {
|
|
438
|
-
'@context' : 'https://w3id.org/did-resolution/v1',
|
|
439
|
-
didDocument : undefined,
|
|
440
|
-
didDocumentMetadata : {},
|
|
441
|
-
didResolutionMetadata : {
|
|
442
|
-
contentType : 'application/did+json',
|
|
443
|
-
error : 'invalidDid',
|
|
444
|
-
errorMessage : `Cannot parse DID: ${didUrl}`
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (parsedDid.method !== 'ion') {
|
|
450
|
-
return {
|
|
451
|
-
'@context' : 'https://w3id.org/did-resolution/v1',
|
|
452
|
-
didDocument : undefined,
|
|
453
|
-
didDocumentMetadata : {},
|
|
454
|
-
didResolutionMetadata : {
|
|
455
|
-
contentType : 'application/did+json',
|
|
456
|
-
error : 'methodNotSupported',
|
|
457
|
-
errorMessage : `Method not supported: ${parsedDid.method}`
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
const { resolutionEndpoint = 'https://discover.did.msidentity.com/1.0/identifiers/' } = resolutionOptions;
|
|
463
|
-
const normalizeUrl = (url: string): string => url.endsWith('/') ? url : url + '/';
|
|
464
|
-
const resolutionUrl = `${normalizeUrl(resolutionEndpoint)}${parsedDid.did}`;
|
|
465
|
-
|
|
466
|
-
const response = await fetch(resolutionUrl);
|
|
467
|
-
|
|
468
|
-
let resolutionResult: DidResolutionResult | object;
|
|
469
|
-
try {
|
|
470
|
-
resolutionResult = await response.json();
|
|
471
|
-
} catch (error) {
|
|
472
|
-
resolutionResult = {};
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
if (response.ok) {
|
|
476
|
-
return resolutionResult as DidResolutionResult;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Response was not "OK" (HTTP 4xx-5xx status code)
|
|
480
|
-
|
|
481
|
-
// Return result if it contains DID resolution metadata.
|
|
482
|
-
if ('didResolutionMetadata' in resolutionResult) {
|
|
483
|
-
return resolutionResult;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Set default error code and message.
|
|
487
|
-
let error = 'internalError';
|
|
488
|
-
let errorMessage = `DID resolver responded with HTTP status code: ${response.status}`;
|
|
489
|
-
|
|
490
|
-
/** The Microsoft resolution endpoint does not return a valid DidResolutionResult
|
|
491
|
-
* when an ION DID is "not found" so normalization is needed. */
|
|
492
|
-
if ('error' in resolutionResult &&
|
|
493
|
-
typeof resolutionResult.error === 'object' &&
|
|
494
|
-
'code' in resolutionResult.error &&
|
|
495
|
-
typeof resolutionResult.error.code === 'string' &&
|
|
496
|
-
'message' in resolutionResult.error &&
|
|
497
|
-
typeof resolutionResult.error.message === 'string') {
|
|
498
|
-
error = resolutionResult.error.code.includes('not_found') ? 'notFound' : error;
|
|
499
|
-
errorMessage = resolutionResult.error.message ?? errorMessage;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return {
|
|
503
|
-
'@context' : 'https://w3id.org/did-resolution/v1',
|
|
504
|
-
didDocument : undefined,
|
|
505
|
-
didDocumentMetadata : {},
|
|
506
|
-
didResolutionMetadata : {
|
|
507
|
-
contentType: 'application/did+json',
|
|
508
|
-
error,
|
|
509
|
-
errorMessage
|
|
510
|
-
}
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
public static async createIonDocument(options: {
|
|
515
|
-
keySet: DidIonKeySet,
|
|
516
|
-
services?: DidService[]
|
|
517
|
-
}): Promise<IonDocumentModel> {
|
|
518
|
-
const { services = [], keySet } = options;
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* STEP 1: Convert key set verification method keys to ION SDK format.
|
|
522
|
-
*/
|
|
523
|
-
|
|
524
|
-
const ionPublicKeys: IonPublicKeyModel[] = [];
|
|
525
|
-
|
|
526
|
-
for (const key of keySet.verificationMethodKeys) {
|
|
527
|
-
// Map W3C DID verification relationship names to ION public key purposes.
|
|
528
|
-
const ionPurposes: IonPublicKeyPurpose[] = [];
|
|
529
|
-
for (const relationship of key.relationships) {
|
|
530
|
-
ionPurposes.push(
|
|
531
|
-
VerificationRelationshipToIonPublicKeyPurpose[relationship]
|
|
532
|
-
);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/** During certain ION operations, JWK validation will throw an error
|
|
536
|
-
* if key IDs provided as input are prefixed with `#`. ION operation
|
|
537
|
-
* outputs and DID document resolution always include the `#` prefix
|
|
538
|
-
* for key IDs resulting in a confusing mismatch between inputs and
|
|
539
|
-
* outputs. To improve the developer experience, this inconsistency
|
|
540
|
-
* is addressed by normalizing input key IDs before being passed
|
|
541
|
-
* to ION SDK methods. */
|
|
542
|
-
const publicKeyId = (key.publicKeyJwk.kid.startsWith('#'))
|
|
543
|
-
? key.publicKeyJwk.kid.substring(1)
|
|
544
|
-
: key.publicKeyJwk.kid;
|
|
545
|
-
|
|
546
|
-
// Convert public key JWK to ION format.
|
|
547
|
-
const publicKey: IonPublicKeyModel = {
|
|
548
|
-
id : publicKeyId,
|
|
549
|
-
publicKeyJwk : DidIonMethod.jwkToIonJwk({ key: key.publicKeyJwk }),
|
|
550
|
-
purposes : ionPurposes,
|
|
551
|
-
type : 'JsonWebKey2020'
|
|
552
|
-
};
|
|
553
|
-
|
|
554
|
-
ionPublicKeys.push(publicKey);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* STEP 2: Convert service entries, if any, to ION SDK format.
|
|
559
|
-
*/
|
|
560
|
-
const ionServices = services.map(service => ({
|
|
561
|
-
...service,
|
|
562
|
-
id: service.id.startsWith('#') ? service.id.substring(1) : service.id
|
|
563
|
-
}));
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* STEP 3: Format as ION document.
|
|
567
|
-
*/
|
|
568
|
-
|
|
569
|
-
const ionDocumentModel: IonDocumentModel = {
|
|
570
|
-
publicKeys : ionPublicKeys,
|
|
571
|
-
services : ionServices
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
return ionDocumentModel;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
public static async getIonCreateRequest(options: {
|
|
578
|
-
ionDocument: IonDocumentModel,
|
|
579
|
-
recoveryPublicKeyJwk: PublicKeyJwk,
|
|
580
|
-
updatePublicKeyJwk: PublicKeyJwk
|
|
581
|
-
}): Promise<IonCreateRequestModel> {
|
|
582
|
-
const { ionDocument, recoveryPublicKeyJwk, updatePublicKeyJwk } = options;
|
|
583
|
-
|
|
584
|
-
// Create an ION DID create request operation.
|
|
585
|
-
const createRequest = await IonRequest.createCreateRequest({
|
|
586
|
-
document : ionDocument,
|
|
587
|
-
recoveryKey : DidIonMethod.jwkToIonJwk({ key: recoveryPublicKeyJwk }) as JwkEs256k,
|
|
588
|
-
updateKey : DidIonMethod.jwkToIonJwk({ key: updatePublicKeyJwk }) as JwkEs256k
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
return createRequest;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
private static jwkToIonJwk({ key }: { key: PrivateKeyJwk | PublicKeyJwk }): JwkEd25519 | JwkEs256k {
|
|
595
|
-
let ionJwk: Partial<JwkEd25519 | JwkEs256k> = { };
|
|
596
|
-
|
|
597
|
-
if ('crv' in key) {
|
|
598
|
-
ionJwk.crv = key.crv;
|
|
599
|
-
ionJwk.kty = key.kty;
|
|
600
|
-
ionJwk.x = key.x;
|
|
601
|
-
if ('d' in key) ionJwk.d = key.d;
|
|
602
|
-
|
|
603
|
-
if ('y' in key && key.y) {
|
|
604
|
-
// secp256k1 JWK.
|
|
605
|
-
return { ...ionJwk, y: key.y} as JwkEs256k;
|
|
606
|
-
}
|
|
607
|
-
// Ed25519 JWK.
|
|
608
|
-
return { ...ionJwk } as JwkEd25519;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
throw new Error(`jwkToIonJwk: Unsupported key algorithm.`);
|
|
612
|
-
}
|
|
613
|
-
}
|