@twin.org/identity-connector-entity-storage 0.0.1-next.5 → 0.0.1-next.51

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