@twin.org/identity-connector-entity-storage 0.0.1-next.3 → 0.0.1-next.31

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.
@@ -3,6 +3,7 @@
3
3
  var entity = require('@twin.org/entity');
4
4
  var core = require('@twin.org/core');
5
5
  var crypto = require('@twin.org/crypto');
6
+ var dataJsonLd = require('@twin.org/data-json-ld');
6
7
  var entityStorageModels = require('@twin.org/entity-storage-models');
7
8
  var identityModels = require('@twin.org/identity-models');
8
9
  var standardsW3cDid = require('@twin.org/standards-w3c-did');
@@ -118,14 +119,34 @@ class EntityStorageIdentityConnector {
118
119
  _vaultConnector;
119
120
  /**
120
121
  * Create a new instance of EntityStorageIdentityConnector.
121
- * @param options The dependencies for the identity connector.
122
- * @param options.didDocumentEntityStorageType The entity storage for the did documents, defaults to "identity-document".
123
- * @param options.vaultConnectorType The vault for the private keys, defaults to "vault".
122
+ * @param options The options for the identity connector.
124
123
  */
125
124
  constructor(options) {
126
125
  this._didDocumentEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.didDocumentEntityStorageType ?? "identity-document");
127
126
  this._vaultConnector = vaultModels.VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
128
127
  }
128
+ /**
129
+ * Build the key name to access the specified key in the vault.
130
+ * @param identity The identity of the user to access the vault keys.
131
+ * @returns The vault key.
132
+ * @internal
133
+ */
134
+ static buildVaultKey(identity, key) {
135
+ return `${identity}/${key}`;
136
+ }
137
+ /**
138
+ * Verify the document in storage.
139
+ * @param didDocument The did document that was stored.
140
+ * @internal
141
+ */
142
+ static async verifyDocument(didDocument, vaultConnector) {
143
+ const stringifiedDocument = core.JsonHelper.canonicalize(didDocument.document);
144
+ const docBytes = core.Converter.utf8ToBytes(stringifiedDocument);
145
+ const verified = await vaultConnector.verify(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, "did"), docBytes, core.Converter.base64ToBytes(didDocument.signature));
146
+ if (!verified) {
147
+ throw new core.GeneralError("EntityStorageIdentityResolverConnector", "signatureVerificationFailed");
148
+ }
149
+ }
129
150
  /**
130
151
  * Create a new document.
131
152
  * @param controller The controller of the identity who can make changes.
@@ -135,10 +156,11 @@ class EntityStorageIdentityConnector {
135
156
  core.Guards.stringValue(this.CLASS_NAME, "controller", controller);
136
157
  try {
137
158
  const did = `did:${EntityStorageIdentityConnector.NAMESPACE}:${core.Converter.bytesToHex(core.RandomHelper.generate(32), true)}`;
138
- await this._vaultConnector.createKey(this.buildVaultKey(did, did), vaultModels.VaultKeyType.Ed25519);
159
+ await this._vaultConnector.createKey(EntityStorageIdentityConnector.buildVaultKey(did, "did"), vaultModels.VaultKeyType.Ed25519);
139
160
  const bitString = new core.BitString(EntityStorageIdentityConnector._REVOCATION_BITS_SIZE);
140
161
  const compressed = await core.Compression.compress(bitString.getBits(), core.CompressionType.Gzip);
141
162
  const didDocument = {
163
+ "@context": standardsW3cDid.DidContexts.Context,
142
164
  id: did,
143
165
  service: [
144
166
  {
@@ -155,26 +177,6 @@ class EntityStorageIdentityConnector {
155
177
  throw new core.GeneralError(this.CLASS_NAME, "createDocumentFailed", undefined, error);
156
178
  }
157
179
  }
158
- /**
159
- * Resolve a document from its id.
160
- * @param documentId The id of the document to resolve.
161
- * @returns The resolved document.
162
- * @throws NotFoundError if the id can not be resolved.
163
- */
164
- async resolveDocument(documentId) {
165
- core.Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
166
- try {
167
- const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
168
- if (core.Is.undefined(didIdentityDocument)) {
169
- throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", documentId);
170
- }
171
- await this.verifyDocument(didIdentityDocument);
172
- return didIdentityDocument.document;
173
- }
174
- catch (error) {
175
- throw new core.GeneralError(this.CLASS_NAME, "resolveDocumentFailed", undefined, error);
176
- }
177
- }
178
180
  /**
179
181
  * Add a verification method to the document in JSON Web key Format.
180
182
  * @param controller The controller of the identity who can make changes.
@@ -194,10 +196,10 @@ class EntityStorageIdentityConnector {
194
196
  if (core.Is.undefined(didIdentityDocument)) {
195
197
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", documentId);
196
198
  }
197
- await this.verifyDocument(didIdentityDocument);
199
+ await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
198
200
  const didDocument = didIdentityDocument.document;
199
- const tempKeyId = `temp-${core.Converter.bytesToBase64Url(core.RandomHelper.generate(32))}`;
200
- const verificationPublicKey = await this._vaultConnector.createKey(this.buildVaultKey(didDocument.id, tempKeyId), vaultModels.VaultKeyType.Ed25519);
201
+ const tempKeyId = `temp-vm-${core.Converter.bytesToBase64Url(core.RandomHelper.generate(16))}`;
202
+ const verificationPublicKey = await this._vaultConnector.createKey(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, tempKeyId), vaultModels.VaultKeyType.Ed25519);
201
203
  const jwkParams = {
202
204
  alg: "EdDSA",
203
205
  kty: "OKP",
@@ -206,7 +208,7 @@ class EntityStorageIdentityConnector {
206
208
  };
207
209
  const kid = core.Converter.bytesToBase64Url(crypto.Sha256.sum256(core.Converter.utf8ToBytes(JSON.stringify(jwkParams))));
208
210
  const methodId = `${documentId}#${verificationMethodId ?? kid}`;
209
- await this._vaultConnector.renameKey(this.buildVaultKey(didDocument.id, tempKeyId), this.buildVaultKey(didDocument.id, methodId));
211
+ await this._vaultConnector.renameKey(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, tempKeyId), EntityStorageIdentityConnector.buildVaultKey(didDocument.id, verificationMethodId ?? kid));
210
212
  const methods = this.getAllMethods(didDocument);
211
213
  const existingMethodIndex = methods.findIndex(m => {
212
214
  if (core.Is.string(m.method)) {
@@ -214,7 +216,7 @@ class EntityStorageIdentityConnector {
214
216
  }
215
217
  return m.method.id === methodId;
216
218
  });
217
- if (existingMethodIndex >= 0) {
219
+ if (existingMethodIndex !== -1) {
218
220
  const methodArray = didDocument[methods[existingMethodIndex].arrayKey];
219
221
  if (core.Is.array(methodArray)) {
220
222
  methodArray.splice(existingMethodIndex, 1);
@@ -250,15 +252,15 @@ class EntityStorageIdentityConnector {
250
252
  core.Guards.stringValue(this.CLASS_NAME, "controller", controller);
251
253
  core.Guards.stringValue(this.CLASS_NAME, "verificationMethodId", verificationMethodId);
252
254
  try {
253
- const idParts = identityModels.DocumentHelper.parse(verificationMethodId);
254
- if (core.Is.empty(idParts.hash)) {
255
+ const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
256
+ if (core.Is.empty(idParts.fragment)) {
255
257
  throw new core.NotFoundError(this.CLASS_NAME, "missingDid", verificationMethodId);
256
258
  }
257
259
  const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
258
260
  if (core.Is.undefined(didIdentityDocument)) {
259
261
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", idParts.id);
260
262
  }
261
- await this.verifyDocument(didIdentityDocument);
263
+ await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
262
264
  const didDocument = didIdentityDocument.document;
263
265
  const methods = this.getAllMethods(didDocument);
264
266
  const existingMethodIndex = methods.findIndex(m => {
@@ -267,7 +269,7 @@ class EntityStorageIdentityConnector {
267
269
  }
268
270
  return m.method.id === verificationMethodId;
269
271
  });
270
- if (existingMethodIndex >= 0) {
272
+ if (existingMethodIndex !== -1) {
271
273
  const methodArray = didDocument[methods[existingMethodIndex].arrayKey];
272
274
  if (core.Is.array(methodArray)) {
273
275
  methodArray.splice(existingMethodIndex, 1);
@@ -299,19 +301,29 @@ class EntityStorageIdentityConnector {
299
301
  core.Guards.stringValue(this.CLASS_NAME, "controller", controller);
300
302
  core.Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
301
303
  core.Guards.stringValue(this.CLASS_NAME, "serviceId", serviceId);
302
- core.Guards.stringValue(this.CLASS_NAME, "serviceType", serviceType);
303
- core.Guards.stringValue(this.CLASS_NAME, "serviceEndpoint", serviceEndpoint);
304
+ if (core.Is.array(serviceType)) {
305
+ core.Guards.arrayValue(this.CLASS_NAME, "serviceType", serviceType);
306
+ }
307
+ else {
308
+ core.Guards.stringValue(this.CLASS_NAME, "serviceType", serviceType);
309
+ }
310
+ if (core.Is.array(serviceEndpoint)) {
311
+ core.Guards.arrayValue(this.CLASS_NAME, "serviceEndpoint", serviceEndpoint);
312
+ }
313
+ else {
314
+ core.Guards.stringValue(this.CLASS_NAME, "serviceEndpoint", serviceEndpoint);
315
+ }
304
316
  try {
305
317
  const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
306
318
  if (core.Is.undefined(didIdentityDocument)) {
307
319
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", documentId);
308
320
  }
309
- await this.verifyDocument(didIdentityDocument);
321
+ await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
310
322
  const didDocument = didIdentityDocument.document;
311
323
  const fullServiceId = serviceId.includes("#") ? serviceId : `${documentId}#${serviceId}`;
312
324
  if (core.Is.array(didDocument.service)) {
313
325
  const existingServiceIndex = didDocument.service.findIndex(s => s.id === fullServiceId);
314
- if (existingServiceIndex >= 0) {
326
+ if (existingServiceIndex !== -1) {
315
327
  didDocument.service?.splice(existingServiceIndex, 1);
316
328
  }
317
329
  }
@@ -340,19 +352,19 @@ class EntityStorageIdentityConnector {
340
352
  core.Guards.stringValue(this.CLASS_NAME, "controller", controller);
341
353
  core.Guards.stringValue(this.CLASS_NAME, "serviceId", serviceId);
342
354
  try {
343
- const idParts = identityModels.DocumentHelper.parse(serviceId);
344
- if (core.Is.empty(idParts.hash)) {
355
+ const idParts = identityModels.DocumentHelper.parseId(serviceId);
356
+ if (core.Is.empty(idParts.fragment)) {
345
357
  throw new core.NotFoundError(this.CLASS_NAME, "missingDid", serviceId);
346
358
  }
347
359
  const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
348
360
  if (core.Is.undefined(didIdentityDocument)) {
349
361
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", idParts.id);
350
362
  }
351
- await this.verifyDocument(didIdentityDocument);
363
+ await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
352
364
  const didDocument = didIdentityDocument.document;
353
365
  if (core.Is.array(didDocument.service)) {
354
366
  const existingServiceIndex = didDocument.service.findIndex(s => s.id === serviceId);
355
- if (existingServiceIndex >= 0) {
367
+ if (existingServiceIndex !== -1) {
356
368
  didDocument.service?.splice(existingServiceIndex, 1);
357
369
  if (didDocument.service?.length === 0) {
358
370
  delete didDocument.service;
@@ -372,51 +384,29 @@ class EntityStorageIdentityConnector {
372
384
  * Create a verifiable credential for a verification method.
373
385
  * @param controller The controller of the identity who can make changes.
374
386
  * @param verificationMethodId The verification method id to use.
375
- * @param credentialId The id of the credential.
376
- * @param types The type for the data stored in the verifiable credential.
377
- * @param subject The subject data to store for the credential.
378
- * @param contexts Additional contexts to include in the credential.
387
+ * @param id The id of the credential.
388
+ * @param subject The credential subject to store in the verifiable credential.
379
389
  * @param revocationIndex The bitmap revocation index of the credential, if undefined will not have revocation status.
380
390
  * @returns The created verifiable credential and its token.
381
391
  * @throws NotFoundError if the id can not be resolved.
382
392
  */
383
- async createVerifiableCredential(controller, verificationMethodId, credentialId, types, subject, contexts, revocationIndex) {
393
+ async createVerifiableCredential(controller, verificationMethodId, id, subject, revocationIndex) {
384
394
  core.Guards.stringValue(this.CLASS_NAME, "controller", controller);
385
395
  core.Guards.stringValue(this.CLASS_NAME, "verificationMethodId", verificationMethodId);
386
- if (!core.Is.undefined(credentialId)) {
387
- core.Guards.stringValue(this.CLASS_NAME, "credentialId", credentialId);
388
- }
389
- if (core.Is.array(types)) {
390
- core.Guards.array(this.CLASS_NAME, "types", types);
391
- }
392
- else if (!core.Is.undefined(types)) {
393
- core.Guards.stringValue(this.CLASS_NAME, "types", types);
394
- }
395
- if (core.Is.array(subject)) {
396
- core.Guards.arrayValue(this.CLASS_NAME, "subject", subject);
397
- }
398
- else {
399
- core.Guards.object(this.CLASS_NAME, "subject", subject);
400
- }
401
- if (core.Is.array(contexts)) {
402
- core.Guards.array(this.CLASS_NAME, "contexts", contexts);
403
- }
404
- else if (!core.Is.undefined(contexts)) {
405
- core.Guards.stringValue(this.CLASS_NAME, "contexts", contexts);
406
- }
396
+ core.Guards.object(this.CLASS_NAME, "subject", subject);
407
397
  if (!core.Is.undefined(revocationIndex)) {
408
398
  core.Guards.number(this.CLASS_NAME, "revocationIndex", revocationIndex);
409
399
  }
410
400
  try {
411
- const idParts = identityModels.DocumentHelper.parse(verificationMethodId);
412
- if (core.Is.empty(idParts.hash)) {
401
+ const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
402
+ if (core.Is.empty(idParts.fragment)) {
413
403
  throw new core.NotFoundError(this.CLASS_NAME, "missingDid", verificationMethodId);
414
404
  }
415
405
  const issuerIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
416
406
  if (core.Is.undefined(issuerIdentityDocument)) {
417
407
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", idParts.id);
418
408
  }
419
- await this.verifyDocument(issuerIdentityDocument);
409
+ await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
420
410
  const issuerDidDocument = issuerIdentityDocument.document;
421
411
  const methods = this.getAllMethods(issuerDidDocument);
422
412
  const methodAndArray = methods.find(m => {
@@ -426,32 +416,30 @@ class EntityStorageIdentityConnector {
426
416
  return m.method.id === verificationMethodId;
427
417
  });
428
418
  if (!methodAndArray) {
429
- throw new core.GeneralError(this.CLASS_NAME, "methodMissing");
419
+ throw new core.GeneralError(this.CLASS_NAME, "methodMissing", { method: verificationMethodId });
430
420
  }
431
421
  const verificationDidMethod = methodAndArray.method;
432
422
  if (!core.Is.stringValue(verificationDidMethod.publicKeyJwk?.x)) {
433
- throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing");
423
+ throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing", {
424
+ method: verificationMethodId
425
+ });
434
426
  }
435
427
  const revocationService = issuerDidDocument.service?.find(s => s.id.endsWith("#revocation"));
436
- const finalTypes = ["VerifiableCredential"];
437
- if (core.Is.array(types)) {
438
- finalTypes.push(...types);
439
- }
440
- else if (core.Is.stringValue(types)) {
441
- finalTypes.push(types);
442
- }
443
- const finalContexts = ["https://www.w3.org/2018/credentials/v1"];
444
- if (core.Is.array(contexts)) {
445
- finalContexts.push(...contexts);
446
- }
447
- else if (core.Is.stringValue(contexts)) {
448
- finalContexts.push(contexts);
428
+ const subjectClone = core.ObjectHelper.clone(subject);
429
+ const finalTypes = [standardsW3cDid.DidTypes.VerifiableCredential];
430
+ const credContext = core.ObjectHelper.extractProperty(subjectClone, [
431
+ "@context"
432
+ ]);
433
+ const credId = core.ObjectHelper.extractProperty(subjectClone, ["@id", "id"], false);
434
+ const credType = core.ObjectHelper.extractProperty(subjectClone, ["@type", "type"]);
435
+ if (core.Is.stringValue(credType)) {
436
+ finalTypes.push(credType);
449
437
  }
450
438
  const verifiableCredential = {
451
- "@context": finalContexts,
452
- id: credentialId,
439
+ "@context": dataJsonLd.JsonLdProcessor.combineContexts(standardsW3cDid.DidContexts.ContextVCv2, credContext),
440
+ id,
453
441
  type: finalTypes,
454
- credentialSubject: subject,
442
+ credentialSubject: subjectClone,
455
443
  issuer: issuerDidDocument.id,
456
444
  issuanceDate: new Date().toISOString(),
457
445
  credentialStatus: revocationService && !core.Is.undefined(revocationIndex)
@@ -488,15 +476,10 @@ class EntityStorageIdentityConnector {
488
476
  iss: idParts.id,
489
477
  nbf: Math.floor(Date.now() / 1000),
490
478
  jti: verifiableCredential.id,
491
- sub: core.Is.array(subject)
492
- ? core.ObjectHelper.propertyGet(subject[0], "id")
493
- : core.ObjectHelper.propertyGet(subject, "id"),
479
+ sub: credId,
494
480
  vc: jwtVc
495
481
  };
496
- const signature = await web.Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (alg, key, payload) => {
497
- const sig = await this._vaultConnector.sign(this.buildVaultKey(idParts.id, verificationMethodId), payload);
498
- return sig;
499
- });
482
+ const signature = await web.Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (header, payload) => vaultModels.VaultConnectorHelper.jwtSigner(this._vaultConnector, EntityStorageIdentityConnector.buildVaultKey(idParts.id, idParts.fragment ?? ""), header, payload));
500
483
  return {
501
484
  verifiableCredential,
502
485
  jwt: signature
@@ -529,7 +512,7 @@ class EntityStorageIdentityConnector {
529
512
  if (core.Is.undefined(issuerIdentityDocument)) {
530
513
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", issuerDocumentId);
531
514
  }
532
- await this.verifyDocument(issuerIdentityDocument);
515
+ await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
533
516
  const issuerDidDocument = issuerIdentityDocument.document;
534
517
  const methods = this.getAllMethods(issuerDidDocument);
535
518
  const methodAndArray = methods.find(m => {
@@ -539,16 +522,13 @@ class EntityStorageIdentityConnector {
539
522
  return m.method.id === jwtHeader.kid;
540
523
  });
541
524
  if (!methodAndArray) {
542
- throw new core.GeneralError(this.CLASS_NAME, "methodMissing");
525
+ throw new core.GeneralError(this.CLASS_NAME, "methodMissing", { method: jwtHeader.kid });
543
526
  }
544
527
  const didMethod = methodAndArray.method;
545
528
  if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
546
- throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing");
547
- }
548
- const verified = web.Jwt.verifySignature(jwtHeader, jwtPayload, jwtSignature, core.Converter.base64UrlToBytes(didMethod.publicKeyJwk.x));
549
- if (!verified) {
550
- throw new core.GeneralError(this.CLASS_NAME, "jwkSignatureFailed");
529
+ throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing", { method: jwtHeader.kid });
551
530
  }
531
+ await web.Jwt.verifySignature(credentialJwt, await web.Jwk.toCryptoKey(didMethod.publicKeyJwk));
552
532
  const verifiableCredential = jwtPayload.vc;
553
533
  if (core.Is.object(verifiableCredential)) {
554
534
  if (core.Is.string(jwtPayload.jti)) {
@@ -568,7 +548,19 @@ class EntityStorageIdentityConnector {
568
548
  core.ObjectHelper.propertySet(verifiableCredential.credentialSubject, "id", jwtPayload.sub);
569
549
  }
570
550
  }
571
- const revoked = await this.checkRevocation(issuerDidDocument, verifiableCredential.credentialStatus?.revocationBitmapIndex);
551
+ const credentialStatus = verifiableCredential.credentialStatus;
552
+ let revoked = false;
553
+ if (core.Is.object(credentialStatus)) {
554
+ revoked = await this.checkRevocation(issuerDidDocument, credentialStatus.revocationBitmapIndex);
555
+ }
556
+ else if (core.Is.arrayValue(credentialStatus)) {
557
+ for (let i = 0; i < credentialStatus.length; i++) {
558
+ revoked = await this.checkRevocation(issuerDidDocument, credentialStatus[i].revocationBitmapIndex);
559
+ if (revoked) {
560
+ break;
561
+ }
562
+ }
563
+ }
572
564
  return {
573
565
  revoked,
574
566
  verifiableCredential: revoked ? undefined : verifiableCredential
@@ -594,7 +586,7 @@ class EntityStorageIdentityConnector {
594
586
  if (core.Is.undefined(issuerIdentityDocument)) {
595
587
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", issuerDocumentId);
596
588
  }
597
- await this.verifyDocument(issuerIdentityDocument);
589
+ await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
598
590
  const issuerDidDocument = issuerIdentityDocument.document;
599
591
  const revocationService = issuerDidDocument.service?.find(s => s.id.endsWith("#revocation"));
600
592
  if (revocationService &&
@@ -634,7 +626,7 @@ class EntityStorageIdentityConnector {
634
626
  if (core.Is.undefined(issuerIdentityDocument)) {
635
627
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", issuerDocumentId);
636
628
  }
637
- await this.verifyDocument(issuerIdentityDocument);
629
+ await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
638
630
  const issuerDidDocument = issuerIdentityDocument.document;
639
631
  const revocationService = issuerDidDocument.service?.find(s => s.id.endsWith("#revocation"));
640
632
  if (revocationService &&
@@ -661,17 +653,18 @@ class EntityStorageIdentityConnector {
661
653
  /**
662
654
  * Create a verifiable presentation from the supplied verifiable credentials.
663
655
  * @param controller The controller of the identity who can make changes.
664
- * @param presentationMethodId The method to associate with the presentation.
656
+ * @param verificationMethodId The method to associate with the presentation.
657
+ * @param presentationId The id of the presentation.
658
+ * @param contexts The contexts for the data stored in the verifiable credential.
665
659
  * @param types The types for the data stored in the verifiable credential.
666
660
  * @param verifiableCredentials The credentials to use for creating the presentation in jwt format.
667
- * @param contexts Additional contexts to include in the presentation.
668
661
  * @param expiresInMinutes The time in minutes for the presentation to expire.
669
662
  * @returns The created verifiable presentation and its token.
670
663
  * @throws NotFoundError if the id can not be resolved.
671
664
  */
672
- async createVerifiablePresentation(controller, presentationMethodId, types, verifiableCredentials, contexts, expiresInMinutes) {
665
+ async createVerifiablePresentation(controller, verificationMethodId, presentationId, contexts, types, verifiableCredentials, expiresInMinutes) {
673
666
  core.Guards.stringValue(this.CLASS_NAME, "controller", controller);
674
- core.Guards.stringValue(this.CLASS_NAME, "presentationMethodId", presentationMethodId);
667
+ core.Guards.stringValue(this.CLASS_NAME, "verificationMethodId", verificationMethodId);
675
668
  if (core.Is.array(types)) {
676
669
  core.Guards.arrayValue(this.CLASS_NAME, "types", types);
677
670
  }
@@ -679,56 +672,46 @@ class EntityStorageIdentityConnector {
679
672
  core.Guards.stringValue(this.CLASS_NAME, "types", types);
680
673
  }
681
674
  core.Guards.arrayValue(this.CLASS_NAME, "verifiableCredentials", verifiableCredentials);
682
- if (core.Is.array(contexts)) {
683
- core.Guards.arrayValue(this.CLASS_NAME, "contexts", contexts);
684
- }
685
- else if (core.Is.string(contexts)) {
686
- core.Guards.stringValue(this.CLASS_NAME, "contexts", contexts);
687
- }
688
675
  if (!core.Is.undefined(expiresInMinutes)) {
689
676
  core.Guards.integer(this.CLASS_NAME, "expiresInMinutes", expiresInMinutes);
690
677
  }
691
678
  try {
692
- const idParts = identityModels.DocumentHelper.parse(presentationMethodId);
693
- if (core.Is.empty(idParts.hash)) {
694
- throw new core.NotFoundError(this.CLASS_NAME, "missingDid", presentationMethodId);
679
+ const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
680
+ if (core.Is.empty(idParts.fragment)) {
681
+ throw new core.NotFoundError(this.CLASS_NAME, "missingDid", verificationMethodId);
695
682
  }
696
683
  const holderIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
697
684
  if (core.Is.undefined(holderIdentityDocument)) {
698
685
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", idParts.id);
699
686
  }
700
- await this.verifyDocument(holderIdentityDocument);
687
+ await EntityStorageIdentityConnector.verifyDocument(holderIdentityDocument, this._vaultConnector);
701
688
  const holderDidDocument = holderIdentityDocument.document;
702
689
  const methods = this.getAllMethods(holderDidDocument);
703
690
  const methodAndArray = methods.find(m => {
704
691
  if (core.Is.string(m.method)) {
705
- return m.method === presentationMethodId;
692
+ return m.method === verificationMethodId;
706
693
  }
707
- return m.method.id === presentationMethodId;
694
+ return m.method.id === verificationMethodId;
708
695
  });
709
696
  if (!methodAndArray) {
710
- throw new core.GeneralError(this.CLASS_NAME, "methodMissing");
697
+ throw new core.GeneralError(this.CLASS_NAME, "methodMissing", { method: verificationMethodId });
711
698
  }
712
699
  const didMethod = methodAndArray.method;
713
700
  if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
714
- throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing");
701
+ throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing", {
702
+ method: verificationMethodId
703
+ });
715
704
  }
716
- const finalTypes = ["VerifiablePresentation"];
705
+ const finalTypes = [standardsW3cDid.DidTypes.VerifiablePresentation];
717
706
  if (core.Is.array(types)) {
718
707
  finalTypes.push(...types);
719
708
  }
720
709
  else if (core.Is.stringValue(types)) {
721
710
  finalTypes.push(types);
722
711
  }
723
- const finalContexts = ["https://www.w3.org/2018/credentials/v1"];
724
- if (core.Is.array(contexts)) {
725
- finalContexts.push(...contexts);
726
- }
727
- else if (core.Is.stringValue(contexts)) {
728
- finalContexts.push(contexts);
729
- }
730
712
  const verifiablePresentation = {
731
- "@context": finalContexts,
713
+ "@context": dataJsonLd.JsonLdProcessor.combineContexts(standardsW3cDid.DidContexts.ContextVCv2, contexts),
714
+ id: presentationId,
732
715
  type: finalTypes,
733
716
  verifiableCredential: verifiableCredentials,
734
717
  holder: idParts.id
@@ -752,10 +735,7 @@ class EntityStorageIdentityConnector {
752
735
  const expiresInSeconds = expiresInMinutes * 60;
753
736
  jwtPayload.exp = Math.floor(Date.now() / 1000) + expiresInSeconds;
754
737
  }
755
- const signature = await web.Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (alg, key, payload) => {
756
- const sig = await this._vaultConnector.sign(this.buildVaultKey(idParts.id, presentationMethodId), payload);
757
- return sig;
758
- });
738
+ const signature = await web.Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (header, payload) => vaultModels.VaultConnectorHelper.jwtSigner(this._vaultConnector, EntityStorageIdentityConnector.buildVaultKey(idParts.id, idParts.fragment ?? ""), header, payload));
759
739
  return {
760
740
  verifiablePresentation,
761
741
  jwt: signature
@@ -788,29 +768,54 @@ class EntityStorageIdentityConnector {
788
768
  if (core.Is.undefined(holderIdentityDocument)) {
789
769
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", holderDocumentId);
790
770
  }
791
- await this.verifyDocument(holderIdentityDocument);
771
+ await EntityStorageIdentityConnector.verifyDocument(holderIdentityDocument, this._vaultConnector);
792
772
  const issuers = [];
793
773
  const tokensRevoked = [];
794
774
  const verifiablePresentation = jwtPayload?.vp;
795
775
  if (core.Is.object(verifiablePresentation) &&
796
776
  core.Is.array(verifiablePresentation.verifiableCredential)) {
797
777
  for (const vcJwt of verifiablePresentation.verifiableCredential) {
798
- const jwt = await web.Jwt.decode(vcJwt);
799
778
  let revoked = true;
800
- if (core.Is.string(jwt.payload?.iss)) {
801
- const issuerDocumentId = jwt.payload.iss;
802
- verifiablePresentation.holder = issuerDocumentId;
803
- const issuerDidDocument = await this._didDocumentEntityStorage.get(issuerDocumentId);
804
- if (core.Is.undefined(issuerDidDocument)) {
805
- throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", issuerDocumentId);
806
- }
807
- await this.verifyDocument(issuerDidDocument);
808
- issuers.push(issuerDidDocument);
809
- const vc = jwt.payload.vc;
810
- if (core.Is.object(vc)) {
811
- revoked = await this.checkRevocation(issuerDidDocument, vc.credentialStatus?.revocationBitmapIndex);
779
+ if (core.Is.stringValue(vcJwt)) {
780
+ const jwt = await web.Jwt.decode(vcJwt);
781
+ if (core.Is.string(jwt.payload?.iss)) {
782
+ const issuerDocumentId = jwt.payload.iss;
783
+ verifiablePresentation.holder = issuerDocumentId;
784
+ const issuerDidDocument = await this._didDocumentEntityStorage.get(issuerDocumentId);
785
+ if (core.Is.undefined(issuerDidDocument)) {
786
+ throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", issuerDocumentId);
787
+ }
788
+ await EntityStorageIdentityConnector.verifyDocument(issuerDidDocument, this._vaultConnector);
789
+ issuers.push({
790
+ "@context": standardsW3cDid.DidContexts.Context,
791
+ ...issuerDidDocument
792
+ });
793
+ const vc = jwt.payload.vc;
794
+ if (core.Is.object(vc)) {
795
+ const credentialStatus = vc.credentialStatus;
796
+ if (core.Is.object(credentialStatus)) {
797
+ revoked = await this.checkRevocation({
798
+ "@context": standardsW3cDid.DidContexts.Context,
799
+ ...issuerDidDocument
800
+ }, credentialStatus.revocationBitmapIndex);
801
+ }
802
+ else if (core.Is.arrayValue(credentialStatus)) {
803
+ for (let i = 0; i < credentialStatus.length; i++) {
804
+ revoked = await this.checkRevocation({
805
+ "@context": standardsW3cDid.DidContexts.Context,
806
+ ...issuerDidDocument
807
+ }, credentialStatus[i].revocationBitmapIndex);
808
+ if (revoked) {
809
+ break;
810
+ }
811
+ }
812
+ }
813
+ }
812
814
  }
813
815
  }
816
+ else {
817
+ revoked = false;
818
+ }
814
819
  tokensRevoked.push(revoked);
815
820
  }
816
821
  }
@@ -833,23 +838,25 @@ class EntityStorageIdentityConnector {
833
838
  * Create a proof for arbitrary data with the specified verification method.
834
839
  * @param controller The controller of the identity who can make changes.
835
840
  * @param verificationMethodId The verification method id to use.
836
- * @param bytes The data bytes to sign.
837
- * @returns The proof signature type and value.
841
+ * @param proofType The type of proof to create.
842
+ * @param unsecureDocument The unsecure document to create the proof for.
843
+ * @returns The proof.
838
844
  */
839
- async createProof(controller, verificationMethodId, bytes) {
845
+ async createProof(controller, verificationMethodId, proofType, unsecureDocument) {
840
846
  core.Guards.stringValue(this.CLASS_NAME, "controller", controller);
841
847
  core.Guards.stringValue(this.CLASS_NAME, "verificationMethodId", verificationMethodId);
842
- core.Guards.uint8Array(this.CLASS_NAME, "bytes", bytes);
848
+ core.Guards.arrayOneOf(this.CLASS_NAME, "proofType", proofType, Object.values(standardsW3cDid.ProofTypes));
849
+ core.Guards.object(this.CLASS_NAME, "unsecureDocument", unsecureDocument);
843
850
  try {
844
- const idParts = identityModels.DocumentHelper.parse(verificationMethodId);
845
- if (core.Is.empty(idParts.hash)) {
851
+ const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
852
+ if (core.Is.empty(idParts.fragment)) {
846
853
  throw new core.NotFoundError(this.CLASS_NAME, "missingDid", verificationMethodId);
847
854
  }
848
855
  const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
849
856
  if (core.Is.undefined(didIdentityDocument)) {
850
857
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", idParts.id);
851
858
  }
852
- await this.verifyDocument(didIdentityDocument);
859
+ await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
853
860
  const didDocument = didIdentityDocument.document;
854
861
  const methods = this.getAllMethods(didDocument);
855
862
  const methodAndArray = methods.find(m => {
@@ -859,17 +866,18 @@ class EntityStorageIdentityConnector {
859
866
  return m.method.id === verificationMethodId;
860
867
  });
861
868
  if (!methodAndArray) {
862
- throw new core.GeneralError(this.CLASS_NAME, "methodMissing");
869
+ throw new core.GeneralError(this.CLASS_NAME, "methodMissing", { method: verificationMethodId });
863
870
  }
864
871
  const didMethod = methodAndArray.method;
865
872
  if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
866
- throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing");
873
+ throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing", {
874
+ method: verificationMethodId
875
+ });
867
876
  }
868
- const signature = await this._vaultConnector.sign(this.buildVaultKey(didDocument.id, verificationMethodId), bytes);
869
- return {
870
- type: "Ed25519",
871
- value: signature
872
- };
877
+ const vaultKey = EntityStorageIdentityConnector.buildVaultKey(didDocument.id, idParts.fragment ?? "");
878
+ const key = await this._vaultConnector.getKey(vaultKey);
879
+ const signedProof = await standardsW3cDid.ProofHelper.createProof(proofType, unsecureDocument, standardsW3cDid.ProofHelper.createUnsignedProof(proofType, verificationMethodId), await web.Jwk.fromEd25519Private(key.privateKey));
880
+ return signedProof;
873
881
  }
874
882
  catch (error) {
875
883
  throw new core.GeneralError(this.CLASS_NAME, "createProofFailed", undefined, error);
@@ -877,43 +885,44 @@ class EntityStorageIdentityConnector {
877
885
  }
878
886
  /**
879
887
  * Verify proof for arbitrary data with the specified verification method.
880
- * @param verificationMethodId The verification method id to use.
881
- * @param bytes The data bytes to verify.
882
- * @param signatureType The type of the signature for the proof.
883
- * @param signatureValue The value of the signature for the proof.
884
- * @returns True if the signature is valid.
888
+ * @param document The document to verify.
889
+ * @param proof The proof to verify.
890
+ * @returns True if the proof is verified.
885
891
  */
886
- async verifyProof(verificationMethodId, bytes, signatureType, signatureValue) {
887
- core.Guards.stringValue(this.CLASS_NAME, "verificationMethodId", verificationMethodId);
888
- core.Guards.uint8Array(this.CLASS_NAME, "bytes", bytes);
889
- core.Guards.stringValue(this.CLASS_NAME, "signatureType", signatureType);
890
- core.Guards.uint8Array(this.CLASS_NAME, "signatureValue", signatureValue);
892
+ async verifyProof(document, proof) {
893
+ core.Guards.object(this.CLASS_NAME, "document", document);
894
+ core.Guards.object(this.CLASS_NAME, "proof", proof);
895
+ core.Guards.stringValue(this.CLASS_NAME, "proof.verificationMethod", proof.verificationMethod);
891
896
  try {
892
- const idParts = identityModels.DocumentHelper.parse(verificationMethodId);
893
- if (core.Is.empty(idParts.hash)) {
894
- throw new core.NotFoundError(this.CLASS_NAME, "missingDid", verificationMethodId);
897
+ const idParts = identityModels.DocumentHelper.parseId(proof.verificationMethod);
898
+ if (core.Is.empty(idParts.fragment)) {
899
+ throw new core.NotFoundError(this.CLASS_NAME, "missingDid", proof.verificationMethod);
895
900
  }
896
901
  const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
897
902
  if (core.Is.undefined(didIdentityDocument)) {
898
903
  throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", idParts.id);
899
904
  }
900
- await this.verifyDocument(didIdentityDocument);
905
+ await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
901
906
  const didDocument = didIdentityDocument.document;
902
907
  const methods = this.getAllMethods(didDocument);
903
908
  const methodAndArray = methods.find(m => {
904
909
  if (core.Is.string(m.method)) {
905
- return m.method === verificationMethodId;
910
+ return m.method === proof.verificationMethod;
906
911
  }
907
- return m.method.id === verificationMethodId;
912
+ return m.method.id === proof.verificationMethod;
908
913
  });
909
914
  if (!methodAndArray) {
910
- throw new core.GeneralError(this.CLASS_NAME, "methodMissing");
915
+ throw new core.GeneralError(this.CLASS_NAME, "methodMissing", {
916
+ method: proof.verificationMethod
917
+ });
911
918
  }
912
919
  const didMethod = methodAndArray.method;
913
920
  if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
914
- throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing");
921
+ throw new core.GeneralError(this.CLASS_NAME, "publicKeyJwkMissing", {
922
+ method: proof.verificationMethod
923
+ });
915
924
  }
916
- return this._vaultConnector.verify(this.buildVaultKey(didIdentityDocument.id, verificationMethodId), bytes, signatureValue);
925
+ return standardsW3cDid.ProofHelper.verifyProof(document, proof, didMethod.publicKeyJwk);
917
926
  }
918
927
  catch (error) {
919
928
  throw new core.GeneralError(this.CLASS_NAME, "verifyProofFailed", undefined, error);
@@ -966,19 +975,6 @@ class EntityStorageIdentityConnector {
966
975
  }
967
976
  return false;
968
977
  }
969
- /**
970
- * Verify the document in storage.
971
- * @param didDocument The did document that was stored.
972
- * @internal
973
- */
974
- async verifyDocument(didDocument) {
975
- const stringifiedDocument = core.JsonHelper.canonicalize(didDocument.document);
976
- const docBytes = core.Converter.utf8ToBytes(stringifiedDocument);
977
- const verified = await this._vaultConnector.verify(this.buildVaultKey(didDocument.id, didDocument.id), docBytes, core.Converter.base64ToBytes(didDocument.signature));
978
- if (!verified) {
979
- throw new core.GeneralError(this.CLASS_NAME, "signatureVerificationFailed");
980
- }
981
- }
982
978
  /**
983
979
  * Update the document in storage.
984
980
  * @param controller The controller of the document.
@@ -988,7 +984,7 @@ class EntityStorageIdentityConnector {
988
984
  async updateDocument(controller, didDocument) {
989
985
  const stringifiedDocument = core.JsonHelper.canonicalize(didDocument);
990
986
  const docBytes = core.Converter.utf8ToBytes(stringifiedDocument);
991
- const signature = await this._vaultConnector.sign(this.buildVaultKey(didDocument.id, didDocument.id), docBytes);
987
+ const signature = await this._vaultConnector.sign(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, "did"), docBytes);
992
988
  await this._didDocumentEntityStorage.set({
993
989
  id: didDocument.id,
994
990
  document: didDocument,
@@ -996,15 +992,6 @@ class EntityStorageIdentityConnector {
996
992
  controller
997
993
  });
998
994
  }
999
- /**
1000
- * Build the key name to access the specified key in the vault.
1001
- * @param identity The identity of the user to access the vault keys.
1002
- * @returns The vault key.
1003
- * @internal
1004
- */
1005
- buildVaultKey(identity, key) {
1006
- return `${identity}/${key}`;
1007
- }
1008
995
  }
1009
996
 
1010
997
  // Copyright 2024 IOTA Stiftung.
@@ -1027,9 +1014,8 @@ class EntityStorageIdentityProfileConnector {
1027
1014
  */
1028
1015
  _profileEntityStorage;
1029
1016
  /**
1030
- * Create a new instance of Identity.
1031
- * @param options The dependencies for the identity service.
1032
- * @param options.profileEntityStorageType The storage connector for the profiles, default to "identity-profile".
1017
+ * Create a new instance of EntityStorageIdentityProfileConnector.
1018
+ * @param options The options for the identity service.
1033
1019
  */
1034
1020
  constructor(options) {
1035
1021
  this._profileEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.profileEntityStorageType ?? "identity-profile");
@@ -1201,6 +1187,60 @@ class EntityStorageIdentityProfileConnector {
1201
1187
  }
1202
1188
  }
1203
1189
 
1190
+ // Copyright 2024 IOTA Stiftung.
1191
+ // SPDX-License-Identifier: Apache-2.0.
1192
+ /**
1193
+ * Class for performing identity operations using entity storage.
1194
+ */
1195
+ class EntityStorageIdentityResolverConnector {
1196
+ /**
1197
+ * The namespace supported by the identity connector.
1198
+ */
1199
+ static NAMESPACE = "entity-storage";
1200
+ /**
1201
+ * Runtime name for the class.
1202
+ */
1203
+ CLASS_NAME = "EntityStorageIdentityResolverConnector";
1204
+ /**
1205
+ * The entity storage for identities.
1206
+ * @internal
1207
+ */
1208
+ _didDocumentEntityStorage;
1209
+ /**
1210
+ * The vault for the keys.
1211
+ * @internal
1212
+ */
1213
+ _vaultConnector;
1214
+ /**
1215
+ * Create a new instance of EntityStorageIdentityResolverConnector.
1216
+ * @param options The options for the identity connector.
1217
+ */
1218
+ constructor(options) {
1219
+ this._didDocumentEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.didDocumentEntityStorageType ?? "identity-document");
1220
+ this._vaultConnector = vaultModels.VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
1221
+ }
1222
+ /**
1223
+ * Resolve a document from its id.
1224
+ * @param documentId The id of the document to resolve.
1225
+ * @returns The resolved document.
1226
+ * @throws NotFoundError if the id can not be resolved.
1227
+ */
1228
+ async resolveDocument(documentId) {
1229
+ core.Guards.stringValue(this.CLASS_NAME, "documentId", documentId);
1230
+ try {
1231
+ const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
1232
+ if (core.Is.undefined(didIdentityDocument)) {
1233
+ throw new core.NotFoundError(this.CLASS_NAME, "documentNotFound", documentId);
1234
+ }
1235
+ await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
1236
+ return didIdentityDocument.document;
1237
+ }
1238
+ catch (error) {
1239
+ throw new core.GeneralError(this.CLASS_NAME, "resolveDocumentFailed", undefined, error);
1240
+ }
1241
+ }
1242
+ }
1243
+
1204
1244
  // Copyright 2024 IOTA Stiftung.
1205
1245
  // SPDX-License-Identifier: Apache-2.0.
1206
1246
  /**
@@ -1220,4 +1260,5 @@ function initSchema(options) {
1220
1260
 
1221
1261
  exports.EntityStorageIdentityConnector = EntityStorageIdentityConnector;
1222
1262
  exports.EntityStorageIdentityProfileConnector = EntityStorageIdentityProfileConnector;
1263
+ exports.EntityStorageIdentityResolverConnector = EntityStorageIdentityResolverConnector;
1223
1264
  exports.initSchema = initSchema;