@digitalbazaar/vc 6.1.0 → 6.3.0

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.
Files changed (3) hide show
  1. package/README.md +214 -10
  2. package/lib/index.js +7 -3
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -48,7 +48,7 @@ the following:
48
48
 
49
49
  ## Install
50
50
 
51
- - Browsers and Node.js 14+ are supported.
51
+ - Browsers and Node.js 18+ are supported.
52
52
 
53
53
  To install from NPM:
54
54
 
@@ -116,18 +116,161 @@ const signedVC = await vc.issue({credential, suite, documentLoader});
116
116
  console.log(JSON.stringify(signedVC, null, 2));
117
117
  ```
118
118
 
119
+ ### Issuing a Selective Disclosure Verifiable Credential
120
+
121
+ Pre-requisites:
122
+
123
+ * You have a private key (with id and controller) and corresponding suite
124
+ * You have are using a cryptosuite that supports selective disclosure, such
125
+ as `ecdsa-sd-2023` or `bbs-2023`
126
+ * If you're using a custom `@context`, make sure it's resolvable
127
+ * (Recommended) You have a strategy for where to publish your Controller
128
+ Document and Public Key
129
+
130
+ Issuing using `ecdsa-sd-2023`:
131
+
132
+ ```js
133
+ import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey';
134
+ import * as ecdsaSd2023Cryptosuite from
135
+ '@digitalbazaar/ecdsa-sd-2023-cryptosuite';
136
+ import * as vc from '@digitalbazaar/vc';
137
+ import {DataIntegrityProof} from '@digitalbazaar/data-integrity';
138
+
139
+ const ecdsaKeyPair = await EcdsaMultikey.generate({
140
+ curve: 'P-256',
141
+ id: 'https://example.edu/issuers/keys/2',
142
+ controller: 'https://example.edu/issuers/565049'
143
+ });
144
+
145
+ // sample exported key pair
146
+ /*
147
+ {
148
+ "@context": "https://w3id.org/security/multikey/v1",
149
+ "id": "https://example.edu/issuers/keys/2",
150
+ "type": "Multikey",
151
+ "controller": "https://example.edu/issuers/565049",
152
+ "publicKeyMultibase": "zDnaeWJjGpXnQAbEpRur3kSWFapGZbwGnFCkzyhiq7nDeXXrM",
153
+ "secretKeyMultibase": "z42trzSpncjWFaB9cKE2Gg5hxtbuAQa5mVJgGwjrugHMacdM"
154
+ }
155
+ */
156
+
157
+ // sample unsigned credential
158
+ const credential = {
159
+ "@context": [
160
+ "https://www.w3.org/2018/credentials/v1",
161
+ "https://www.w3.org/2018/credentials/examples/v1"
162
+ ],
163
+ "id": "https://example.com/credentials/1872",
164
+ "type": ["VerifiableCredential", "AlumniCredential"],
165
+ "issuer": "https://example.edu/issuers/565049",
166
+ "issuanceDate": "2010-01-01T19:23:24Z",
167
+ "credentialSubject": {
168
+ "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
169
+ "alumniOf": "Example University"
170
+ }
171
+ };
172
+
173
+ // setup ecdsa-sd-2023 suite for signing selective disclosure VCs
174
+ const suite = new DataIntegrityProof({
175
+ signer: ecdsaKeyPair.signer(),
176
+ cryptosuite: createSignCryptosuite({
177
+ // require the `issuer` and `issuanceDate` fields to always be disclosed
178
+ // by the holder (presenter)
179
+ mandatoryPointers: [
180
+ '/issuanceDate',
181
+ '/issuer'
182
+ ]
183
+ })
184
+ });
185
+ // use a proof ID to enable it to be found and transformed into a disclosure
186
+ // proof by the holder later
187
+ const proofId = `urn:uuid:${uuid()}`;
188
+ suite.proof = {id: proofId};
189
+
190
+ const signedVC = await vc.issue({credential, suite, documentLoader});
191
+ console.log(JSON.stringify(signedVC, null, 2));
192
+ ```
193
+
194
+ Issuing using `bbs-2023`:
195
+
196
+ ```js
197
+ import * as bbs2023Cryptosuite from '@digitalbazaar/bbs-2023-cryptosuite';
198
+ import * as bls12381Multikey from '@digitalbazaar/bls12-381-multikey';
199
+ import * as vc from '@digitalbazaar/vc';
200
+ import {DataIntegrityProof} from '@digitalbazaar/data-integrity';
201
+
202
+ const bbsKeyPair = await bls12381Multikey.generate({
203
+ algorithm: 'BBS-BLS12-381-SHA-256';
204
+ id: 'https://example.edu/issuers/keys/3',
205
+ controller: 'https://example.edu/issuers/565049'
206
+ });
207
+
208
+ // sample exported key pair
209
+ /*
210
+ {
211
+ "@context": "https://w3id.org/security/multikey/v1",
212
+ "id": "https://example.edu/issuers/keys/3",
213
+ "type": "Multikey",
214
+ "controller": "https://example.edu/issuers/565049",
215
+ "publicKeyMultibase": "zUC72jQrt2BfyE57AVgHgThKCsH6HNo85X9SLNpAJaHb42cNDXhsRWL2KkrFtaiztPbbZjfDVQnQQMw2nMqAPUHnaQ3xEr7kUmcnBgv7S2wQSbRbr7mqsP153nU7yMh3ZN4ZryL",
216
+ "secretKeyMultibase": "z488y1niFCWnaV2i86q1raaa7qwBWZ6WTLeS1W1PrsbcsoNg"
217
+ }
218
+ */
219
+
220
+ // sample unsigned credential
221
+ const credential = {
222
+ "@context": [
223
+ "https://www.w3.org/2018/credentials/v1",
224
+ "https://www.w3.org/2018/credentials/examples/v1"
225
+ ],
226
+ // omit `id` to enable unlinkable disclosure
227
+ "type": ["VerifiableCredential", "AlumniCredential"],
228
+ "issuer": "https://example.edu/issuers/565049",
229
+ // use less precise date that is shared by a sufficiently large group
230
+ // of VCs to enable unlinkable disclosure
231
+ "issuanceDate": "2010-01-01T01:00:00Z",
232
+ "credentialSubject": {
233
+ // omit `id` to enable unlinkable disclosure
234
+ "alumniOf": "Example University"
235
+ }
236
+ };
237
+
238
+ // setup bbs-2023 suite for signing unlinkable selective disclosure VCs
239
+ const suite = new DataIntegrityProof({
240
+ signer: bbsKeyPair.signer(),
241
+ cryptosuite: createSignCryptosuite({
242
+ // require the `issuer` and `issuanceDate` fields to always be disclosed
243
+ // by the holder (presenter)
244
+ mandatoryPointers: [
245
+ '/issuanceDate',
246
+ '/issuer'
247
+ ]
248
+ })
249
+ });
250
+ // note: do not include a proof ID to enable unlinkable selective disclosure
251
+
252
+ const signedVC = await vc.issue({credential, suite, documentLoader});
253
+ console.log(JSON.stringify(signedVC, null, 2));
254
+ ```
255
+
119
256
  ### Deriving a Selective Disclosure Verifiable Credential
120
257
 
258
+ Note: This step is performed as a holder of a verifiable credential, not as
259
+ an issuer.
260
+
121
261
  Pre-requisites:
122
262
 
123
263
  * You have a verifiable credential that was issued using a cryptosuite that
124
- support selective disclosure, such as `ecdsa-sd-2023`
264
+ supports selective disclosure, such as `ecdsa-sd-2023` or `bbs-2023`
125
265
  * If you're using a custom `@context`, make sure it's resolvable
126
266
 
267
+ Deriving using `ecdsa-sd-2023`:
268
+
127
269
  ```js
128
- import * as vc from '@digitalbazaar/vc';
270
+ import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey';
129
271
  import * as ecdsaSd2023Cryptosuite from
130
272
  '@digitalbazaar/ecdsa-sd-2023-cryptosuite';
273
+ import * as vc from '@digitalbazaar/vc';
131
274
  import {DataIntegrityProof} from '@digitalbazaar/data-integrity';
132
275
 
133
276
  const {
@@ -136,8 +279,8 @@ const {
136
279
  createVerifyCryptosuite
137
280
  } = ecdsaSd2023Cryptosuite;
138
281
 
139
- // Sample signed credential
140
- const credential = {
282
+ // sample VC
283
+ const verifiableCredential = {
141
284
  "@context": [
142
285
  "https://www.w3.org/2018/credentials/v1",
143
286
  "https://www.w3.org/2018/credentials/examples/v1",
@@ -155,19 +298,19 @@ const credential = {
155
298
  "alumniOf": "<span lang=\"en\">Example University</span>"
156
299
  },
157
300
  "proof": {
158
- "id": "urn:uuid:2ef8c7ce-a4da-44b4-ba7f-3d43eaf1e50c",
301
+ "id": "urn:uuid:318d9dce-bc7b-40b9-a956-c9160bf910db",
159
302
  "type": "DataIntegrityProof",
160
- "created": "2023-11-13T22:58:06Z",
303
+ "created": "2024-01-12T21:53:11Z",
161
304
  "verificationMethod": "https://example.edu/issuers/keys/2",
162
305
  "cryptosuite": "ecdsa-sd-2023",
163
306
  "proofPurpose": "assertionMethod",
164
- "proofValue": "u2V0AhVhAtYPKUQxwULXzMdsAfqtipsiX6YEPURYSBFYxoFY-v0vCPyAs1Ckyy61Wtk3xZWyBGNaEr3w0wQiJHHd5B9uR-1gjgCQCVtFPMk-ECi0CJFYv_GTjCChf8St0FQjuExTAnwP0-ipYIOHSun3YqabOfNe2DYFkHBTZa0Csf1a7YUDW8hhsOHqTglhA8aqnyanT-Ybo2-aHBTcI-UmHX0iluGb2IxoHLLhQoOPm2rDW0eB04Fa2Dh6WMKoOl_Bz3wZZDGQ31XoGrQvgIlhAo8qspvC-QQ-xI3KADiA12sO5LRsZ7hl9ozoJEECVsDOKlxWd-dhices5b2ZQIiiRE9XxxJx8YuwCMoD2bRLbOIJtL2lzc3VhbmNlRGF0ZWcvaXNzdWVy"
307
+ "proofValue": "u2V0AhVhAsl6PQKYE15R0O5Qd267ntwHGNH6JRvZ1y8A-fTCQLUoupP8SCZzzmyc0a1AnabHEVKhpHtYV8j9Kapp-fHFBtFgjgCQCIMn2L1R7D5VPnNn_2foxdj8qvsuUTGFqA34YBkguzCpYILfJ-qNQpn6_dJGpkG24FynqbHpnzoHWVJc2kiLqEKHRglhAUmZtstR9MOLrZjcR8J303MXFvRiE6J3bbaPT1_I9-6578-Wj-eydv2TEGBq_dmsjxsOh4_2Va0etw8CXXMAzaVhA9fr7_Sl9D67AfvLhkJTZ0uJCAXcbL2MaS-DmoC7K-ABxroL1_wj119J8yTMlazxzYBwYkihrdp4ZWJZxraX9tIJtL2lzc3VhbmNlRGF0ZWcvaXNzdWVy"
165
308
  }
166
309
  };
167
310
 
168
311
  // note no `signer` needed; the selective disclosure credential will be
169
312
  // derived from the base proof already provided by the issuer
170
- const ecdsaSdDeriveSuite = new DataIntegrityProof({
313
+ const suite = new DataIntegrityProof({
171
314
  cryptosuite: createDiscloseCryptosuite({
172
315
  // the ID of the base proof to convert to a disclosure proof
173
316
  proofId: 'urn:uuid:da088899-3439-41ea-a580-af3f1cf98cd3',
@@ -181,7 +324,68 @@ const ecdsaSdDeriveSuite = new DataIntegrityProof({
181
324
  })
182
325
  });
183
326
 
184
- const derivedVC = await vc.derive({credential, suite, documentLoader});
327
+ const derivedVC = await vc.derive({
328
+ verifiableCredential, suite, documentLoader
329
+ });
330
+ console.log(JSON.stringify(derivedVC, null, 2));
331
+ ```
332
+
333
+ Deriving using `bbs-2023`:
334
+
335
+ ```js
336
+ import * as bbs2023Cryptosuite from '@digitalbazaar/bbs-2023-cryptosuite';
337
+ import * as bls12381Multikey from '@digitalbazaar/bls12-381-multikey';
338
+ import * as vc from '@digitalbazaar/vc';
339
+ import {DataIntegrityProof} from '@digitalbazaar/data-integrity';
340
+
341
+ const {
342
+ createDiscloseCryptosuite,
343
+ createSignCryptosuite,
344
+ createVerifyCryptosuite
345
+ } = bbs2023Cryptosuite;
346
+
347
+ // sample VC
348
+ const verifiableCredential = {
349
+ "@context": [
350
+ "https://www.w3.org/2018/credentials/v1",
351
+ "https://www.w3.org/2018/credentials/examples/v1",
352
+ "https://w3id.org/security/data-integrity/v2"
353
+ ],
354
+ "type": [
355
+ "VerifiableCredential",
356
+ "AlumniCredential"
357
+ ],
358
+ "issuer": "https://example.edu/issuers/565049",
359
+ "issuanceDate": "2010-01-01T01:00:00Z",
360
+ "credentialSubject": {
361
+ "alumniOf": "<span lang=\"en\">Example University</span>"
362
+ },
363
+ "proof": {
364
+ "type": "DataIntegrityProof",
365
+ "verificationMethod": "https://example.edu/issuers/keys/3",
366
+ "cryptosuite": "bbs-2023",
367
+ "proofPurpose": "assertionMethod",
368
+ "proofValue": "u2V0ChVhQp1smqO-Qmc-1KpNkShjevTeylTdVlpH_RNXeJ_cNniErWPbEWILvsoH5mYjnun5ibZHq0m7BEIaLv8sfMtLfcmgPj6tbAFwDWvEcbRWg7CFYQGWqCAnvTpL_Aao3aVCg5svdzFuvKqnvneA0UwaN0lagvGpWT7fCDGgcYPyNPKaCX94Xo06aTcSwOXgyGUbtN1xYYIU6t5wv20lVdESfzkYOFXTxIZa1HSBAZYWDyEgQ3A3ajzWX5qeFc3cwmnnrGUfJYwawgGLQAY3vBi3LTM2i3jCOPvxCEJALPIjK4tEmWb6uFjT4PWLlIEeTtYj_0yEv91ggsm9vw1PPlK6q8wQiw2i2joZ-OKkvHz7rDSxPYfmQNrqCbS9pc3N1YW5jZURhdGVnL2lzc3Vlcg"
369
+ }
370
+ };
371
+
372
+ // note no `signer` needed; the selective disclosure credential will be
373
+ // derived from the base proof already provided by the issuer
374
+ const suite = new DataIntegrityProof({
375
+ cryptosuite: createDiscloseCryptosuite({
376
+ // selectively disclose the entire credential subject; different JSON
377
+ // pointers could be provided to selectively disclose different information;
378
+ // the issuer will have mandatory fields that will be automatically
379
+ // disclosed such as the `issuer` and `issuanceDate` fields
380
+ selectivePointers: [
381
+ '/credentialSubject'
382
+ ]
383
+ })
384
+ });
385
+
386
+ const derivedVC = await vc.derive({
387
+ verifiableCredential, suite, documentLoader
388
+ });
185
389
  console.log(JSON.stringify(derivedVC, null, 2));
186
390
  ```
187
391
 
package/lib/index.js CHANGED
@@ -167,7 +167,8 @@ export async function derive({
167
167
  documentLoader = defaultDocumentLoader
168
168
  } = {}) {
169
169
  if(!verifiableCredential) {
170
- throw new TypeError('"credential" parameter is required for deriving.');
170
+ throw new TypeError(
171
+ '"verifiableCredential" parameter is required for deriving.');
171
172
  }
172
173
  if(!suite) {
173
174
  throw new TypeError('"suite" parameter is required for deriving.');
@@ -654,10 +655,13 @@ export function _checkCredential({
654
655
  }
655
656
 
656
657
  if('credentialStatus' in credential) {
657
- if(!credential.credentialStatus.id) {
658
+ const {credentialStatus} = credential;
659
+ if(Array.isArray(credentialStatus) ?
660
+ credentialStatus.some(cs => !cs.id) : !credentialStatus.id) {
658
661
  throw new Error('"credentialStatus" must include an id.');
659
662
  }
660
- if(!credential.credentialStatus.type) {
663
+ if(Array.isArray(credentialStatus) ?
664
+ credentialStatus.some(cs => !cs.type) : !credentialStatus.type) {
661
665
  throw new Error('"credentialStatus" must include a type.');
662
666
  }
663
667
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalbazaar/vc",
3
- "version": "6.1.0",
3
+ "version": "6.3.0",
4
4
  "description": "Verifiable Credentials JavaScript library.",
5
5
  "homepage": "https://github.com/digitalbazaar/vc",
6
6
  "author": {
@@ -33,6 +33,8 @@
33
33
  "jsonld-signatures": "^11.2.1"
34
34
  },
35
35
  "devDependencies": {
36
+ "@digitalbazaar/bbs-2023-cryptosuite": "^1.0.0",
37
+ "@digitalbazaar/bls12-381-multikey": "^1.1.1",
36
38
  "@digitalbazaar/credentials-examples-context": "^1.0.0",
37
39
  "@digitalbazaar/data-integrity": "^2.0.0",
38
40
  "@digitalbazaar/data-integrity-context": "^2.0.0",
@@ -72,7 +74,7 @@
72
74
  ]
73
75
  },
74
76
  "engines": {
75
- "node": ">=14"
77
+ "node": ">=18"
76
78
  },
77
79
  "keywords": [
78
80
  "JSON",