@bedrock/vc-verifier 8.0.0 → 11.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # bedrock-vc-verifier ChangeLog
2
2
 
3
+ ## 11.0.0 - 2022-06-05
4
+
5
+ ### Changed
6
+ - **BREAKING** Use `@digitalbazaar/vc-status-list` v4.0. If `statusPurpose`
7
+ in credential does not match the `statusPurpose` of status list credential,
8
+ an error will be thrown.
9
+
10
+ ## 10.0.0 - 2022-05-17
11
+
12
+ ### Changed
13
+ - **BREAKING**: Use `@bedrock-service-context-store@7` to cause migration of
14
+ old EDV context documents to the new EDV attribute version.
15
+
16
+ ## 9.0.0 - 2022-05-05
17
+
18
+ ### Changed
19
+ - **BREAKING**: Update peer deps:
20
+ - `@bedrock/service-agent@5`
21
+ - `@bedrock/service-context-store@6`.
22
+ - **BREAKING**: The updated peer dependencies use a new EDV client with a
23
+ new blind attribute version. This version is incompatible with previous
24
+ versions and a manual migration must be performed to update all
25
+ EDV documents to use the new blind attribute version -- or a new
26
+ deployment is required.
27
+
3
28
  ## 8.0.0 - 2022-04-29
4
29
 
5
30
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/vc-verifier",
3
- "version": "8.0.0",
3
+ "version": "11.0.0",
4
4
  "type": "module",
5
5
  "description": "Bedrock VC Verifier",
6
6
  "main": "./lib/index.js",
@@ -24,7 +24,7 @@
24
24
  "@digitalbazaar/ed25519-signature-2018": "^2.1.0",
25
25
  "@digitalbazaar/ed25519-signature-2020": "^3.0.0",
26
26
  "@digitalbazaar/vc": "^2.1.0",
27
- "@digitalbazaar/vc-status-list": "^3.0.0",
27
+ "@digitalbazaar/vc-status-list": "^4.0.0",
28
28
  "assert-plus": "^1.0.0",
29
29
  "bnid": "^2.1.0",
30
30
  "body-parser": "^1.19.0",
@@ -41,8 +41,8 @@
41
41
  "@bedrock/jsonld-document-loader": "^3.0.0",
42
42
  "@bedrock/mongodb": "^10.0.0",
43
43
  "@bedrock/security-context": "^7.0.0",
44
- "@bedrock/service-agent": "^4.0.0",
45
- "@bedrock/service-context-store": "^5.0.0",
44
+ "@bedrock/service-agent": "^5.0.0",
45
+ "@bedrock/service-context-store": "^7.0.0",
46
46
  "@bedrock/service-core": "^5.0.0",
47
47
  "@bedrock/vc-status-list-context": "^4.0.0",
48
48
  "@bedrock/vc-revocation-list-context": "^3.0.0",
@@ -39,8 +39,11 @@ const encodedList100KWith50KthRevoked =
39
39
  const key = fs.readFileSync(__dirname + '/key.pem');
40
40
  const cert = fs.readFileSync(__dirname + '/cert.pem');
41
41
 
42
- let slCredential;
43
- let unsignedCredentialSl2021Type;
42
+ let slCredentialRevocation;
43
+ let unsignedCredentialSl2021TypeRevocation;
44
+ let slCredentialSuspension;
45
+ let unsignedCredentialSl2021TypeSuspension;
46
+ let unsignedCredentialSl2021WithUnmatchingStatusPurpose;
44
47
  let revokedSlCredential;
45
48
  let revokedUnsignedCredential;
46
49
  let rlCredential;
@@ -71,8 +74,8 @@ function _startServer({app}) {
71
74
  testServerBaseUrl = BASE_URL;
72
75
  console.log(`Test server listening at ${BASE_URL}`);
73
76
 
74
- // Status List 2021 Credential
75
- slCredential = {
77
+ // Status List 2021 Credential with statusPurpose `revocation`
78
+ slCredentialRevocation = {
76
79
  '@context': [
77
80
  'https://www.w3.org/2018/credentials/v1',
78
81
  VC_SL_CONTEXT_URL
@@ -84,12 +87,14 @@ function _startServer({app}) {
84
87
  credentialSubject: {
85
88
  id: `${BASE_URL}/status/748a7d8e-9111-11ec-a934-10bf48838a41#list`,
86
89
  type: 'StatusList2021',
90
+ statusPurpose: 'revocation',
87
91
  encodedList: encodedList100k
88
92
  }
89
93
  };
90
94
 
91
- // Unsigned 2021 Credential
92
- unsignedCredentialSl2021Type = {
95
+ // Unsigned 2021 Credential with "credentialStatus.statusPurpose"
96
+ // `revocation`
97
+ unsignedCredentialSl2021TypeRevocation = {
93
98
  '@context': [
94
99
  'https://www.w3.org/2018/credentials/v1',
95
100
  VC_SL_CONTEXT_URL,
@@ -106,13 +111,82 @@ function _startServer({app}) {
106
111
  type: 'StatusList2021Entry',
107
112
  statusPurpose: 'revocation',
108
113
  statusListIndex: '67342',
109
- statusListCredential: slCredential.id
114
+ statusListCredential: slCredentialRevocation.id
110
115
  },
111
- issuer: slCredential.issuer,
116
+ issuer: slCredentialRevocation.issuer,
117
+ };
118
+
119
+ // Status List 2021 Credential with statusPurpose `suspension`
120
+ slCredentialSuspension = {
121
+ '@context': [
122
+ 'https://www.w3.org/2018/credentials/v1',
123
+ VC_SL_CONTEXT_URL
124
+ ],
125
+ id: `${BASE_URL}/status/5d3e7a97-1121-11ec-9b38-10bf48838a41`,
126
+ issuer: 'did:key:z6Mktpn6cXks1PBKLMgZH2VaahvCtBMF6K8eCa7HzrnuYLZv',
127
+ issuanceDate: '2022-01-10T04:24:12.164Z',
128
+ type: ['VerifiableCredential', 'StatusList2021Credential'],
129
+ credentialSubject: {
130
+ id: `${BASE_URL}/status/5d3e7a97-1121-11ec-9b38-10bf48838a41#list`,
131
+ type: 'StatusList2021',
132
+ statusPurpose: 'suspension',
133
+ encodedList: encodedList100k
134
+ }
135
+ };
136
+
137
+ // Unsigned 2021 Credential with "credentialStatus.statusPurpose"
138
+ // `suspension`
139
+ unsignedCredentialSl2021TypeSuspension = {
140
+ '@context': [
141
+ 'https://www.w3.org/2018/credentials/v1',
142
+ VC_SL_CONTEXT_URL,
143
+ 'https://w3id.org/security/suites/ed25519-2020/v1'
144
+ ],
145
+ id: 'urn:uuid:a0418a78-7924-11ea-8a23-10bf48838a41',
146
+ type: ['VerifiableCredential', 'example:TestCredential'],
147
+ credentialSubject: {
148
+ id: 'urn:uuid:4886029a-7925-11ea-9274-10bf48838a41',
149
+ 'example:test': 'foo'
150
+ },
151
+ credentialStatus: {
152
+ id: `${BASE_URL}/status/5d3e7a97-1121-11ec-9b38-10bf48838a41#67342`,
153
+ type: 'StatusList2021Entry',
154
+ statusPurpose: 'suspension',
155
+ statusListIndex: '67342',
156
+ statusListCredential: slCredentialSuspension.id
157
+ },
158
+ issuer: slCredentialSuspension.issuer,
159
+ };
160
+
161
+ // Unsigned 2021 Credential with unmatching status purpose
162
+ unsignedCredentialSl2021WithUnmatchingStatusPurpose = {
163
+ '@context': [
164
+ 'https://www.w3.org/2018/credentials/v1',
165
+ VC_SL_CONTEXT_URL,
166
+ 'https://w3id.org/security/suites/ed25519-2020/v1'
167
+ ],
168
+ id: 'urn:uuid:a0418a78-7924-11ea-8a23-10bf48838a41',
169
+ type: ['VerifiableCredential', 'example:TestCredential'],
170
+ credentialSubject: {
171
+ id: 'urn:uuid:4886029a-7925-11ea-9274-10bf48838a41',
172
+ 'example:test': 'foo'
173
+ },
174
+ credentialStatus: {
175
+ id: `${BASE_URL}/status/748a7d8e-9111-11ec-a934-10bf48838a41#67342`,
176
+ type: 'StatusList2021Entry',
177
+ // intentionally set status purpose that does not match status purpose
178
+ // of sl credential that it fetches.
179
+ statusPurpose: 'suspension',
180
+ statusListIndex: '67342',
181
+ // intentionally point `statusListCredential` to a sl credential
182
+ // with status purpose `revocation`.
183
+ statusListCredential: slCredentialRevocation.id
184
+ },
185
+ issuer: slCredentialRevocation.issuer,
112
186
  };
113
187
 
114
188
  // Revoked Status List 2021 Credential
115
- revokedSlCredential = klona(slCredential);
189
+ revokedSlCredential = klona(slCredentialRevocation);
116
190
 
117
191
  revokedSlCredential.id =
118
192
  `${BASE_URL}/status/8ec30054-9111-11ec-9ab5-10bf48838a41`,
@@ -122,7 +196,7 @@ function _startServer({app}) {
122
196
  `${BASE_URL}/status/8ec30054-9111-11ec-9ab5-10bf48838a41#list`;
123
197
 
124
198
  // Revoked Unsigned 2021 Credential
125
- revokedUnsignedCredential = klona(unsignedCredentialSl2021Type);
199
+ revokedUnsignedCredential = klona(unsignedCredentialSl2021TypeRevocation);
126
200
  revokedUnsignedCredential.credentialStatus.id =
127
201
  `${revokedSlCredential.id}#50000`;
128
202
  revokedUnsignedCredential.credentialStatus.statusListIndex = 50000;
@@ -202,7 +276,13 @@ app.get('/status/748a7d8e-9111-11ec-a934-10bf48838a41',
202
276
  // eslint-disable-next-line no-unused-vars
203
277
  (req, res, next) => {
204
278
  // responds with a valid status list 2021 type credential
205
- res.json(slCredential);
279
+ res.json(slCredentialRevocation);
280
+ });
281
+ app.get('/status/5d3e7a97-1121-11ec-9b38-10bf48838a41',
282
+ // eslint-disable-next-line no-unused-vars
283
+ (req, res, next) => {
284
+ // responds with a valid status list 2021 type credential
285
+ res.json(slCredentialSuspension);
206
286
  });
207
287
  app.get('/status/8ec30054-9111-11ec-9ab5-10bf48838a41',
208
288
  // eslint-disable-next-line no-unused-vars
@@ -301,14 +381,60 @@ describe('verify credential status', () => {
301
381
  verifierId = verifierConfig.id;
302
382
  rootZcap = `urn:zcap:root:${encodeURIComponent(verifierId)}`;
303
383
  });
304
- it('should verify "StatusList2021Credential" type', async () => {
305
- slCredential = await vc.issue({
306
- credential: slCredential,
384
+ it('should verify "StatusList2021Credential" type with "statusPurpose" ' +
385
+ 'revocation', async () => {
386
+ slCredentialRevocation = await vc.issue({
387
+ credential: slCredentialRevocation,
388
+ documentLoader: _documentLoader,
389
+ suite
390
+ });
391
+ const verifiableCredential = await vc.issue({
392
+ credential: unsignedCredentialSl2021TypeRevocation,
393
+ documentLoader: _documentLoader,
394
+ suite
395
+ });
396
+ let error;
397
+ let result;
398
+ try {
399
+ const zcapClient = helpers.createZcapClient({capabilityAgent});
400
+ result = await zcapClient.write({
401
+ url: `${verifierId}/credentials/verify`,
402
+ capability: rootZcap,
403
+ json: {
404
+ options: {
405
+ checks: ['proof', 'credentialStatus'],
406
+ },
407
+ verifiableCredential
408
+ }
409
+ });
410
+ } catch(e) {
411
+ error = e;
412
+ }
413
+ assertNoError(error);
414
+ should.exist(result.data.verified);
415
+ result.data.verified.should.be.a('boolean');
416
+ result.data.verified.should.equal(true);
417
+ const {checks} = result.data;
418
+ checks.should.be.an('array');
419
+ checks.should.have.length(2);
420
+ checks.should.be.an('array');
421
+ checks.should.eql(['proof', 'credentialStatus']);
422
+ should.exist(result.data.results);
423
+ result.data.results.should.be.an('array');
424
+ result.data.results.should.have.length(1);
425
+ const [r] = result.data.results;
426
+ r.verified.should.be.a('boolean');
427
+ r.verified.should.equal(true);
428
+ });
429
+ it('should verify "StatusList2021Credential" type with "statusPurpose" ' +
430
+ 'suspension', async () => {
431
+ slCredentialSuspension = await vc.issue({
432
+ credential: slCredentialSuspension,
307
433
  documentLoader: _documentLoader,
308
434
  suite
309
435
  });
310
436
  const verifiableCredential = await vc.issue({
311
- credential: unsignedCredentialSl2021Type,
437
+ credential: unsignedCredentialSl2021TypeSuspension,
312
438
  documentLoader: _documentLoader,
313
439
  suite
314
440
  });
@@ -345,6 +471,43 @@ describe('verify credential status', () => {
345
471
  r.verified.should.be.a('boolean');
346
472
  r.verified.should.equal(true);
347
473
  });
474
+ it('should throw error if "statusPurpose" of the slCredential does not ' +
475
+ 'match the "statusPurpose" of the credentialStatus', async () => {
476
+ slCredentialRevocation = await vc.issue({
477
+ credential: slCredentialRevocation,
478
+ documentLoader: _documentLoader,
479
+ suite
480
+ });
481
+ const verifiableCredential = await vc.issue({
482
+ credential: unsignedCredentialSl2021WithUnmatchingStatusPurpose,
483
+ documentLoader: _documentLoader,
484
+ suite
485
+ });
486
+ let error;
487
+ let result;
488
+ try {
489
+ const zcapClient = helpers.createZcapClient({capabilityAgent});
490
+ result = await zcapClient.write({
491
+ url: `${verifierId}/credentials/verify`,
492
+ capability: rootZcap,
493
+ json: {
494
+ options: {
495
+ checks: ['proof', 'credentialStatus'],
496
+ },
497
+ verifiableCredential
498
+ }
499
+ });
500
+ } catch(e) {
501
+ error = e;
502
+ }
503
+ should.exist(error);
504
+ should.not.exist(result);
505
+ error.data.verified.should.equal(false);
506
+ const {error: {cause: errorCause}} = error.data;
507
+ errorCause.should.equal(
508
+ 'The status purpose "revocation" of the status list credential ' +
509
+ 'does not match the status purpose "suspension" in the credential.');
510
+ });
348
511
  it('should fail to verify a revoked "StatusList2021Credential" type',
349
512
  async () => {
350
513
  revokedSlCredential = await vc.issue({
@@ -7,16 +7,18 @@ import {createRequire} from 'module';
7
7
  import {didIo} from '@bedrock/did-io';
8
8
  import {getAppIdentity} from '@bedrock/app-identity';
9
9
  import {mockData} from './mock.data.js';
10
+ import {httpClient} from '@digitalbazaar/http-client';
10
11
  const require = createRequire(import.meta.url);
11
12
  const {Ed25519Signature2020} = require('@digitalbazaar/ed25519-signature-2020');
12
13
  const {EdvClient} = require('@digitalbazaar/edv-client');
13
- const {httpClient} = require('@digitalbazaar/http-client');
14
14
  const {KeystoreAgent, KmsClient} = require('@digitalbazaar/webkms-client');
15
15
  const {ZcapClient} = require('@digitalbazaar/ezcap');
16
16
 
17
17
  const edvBaseUrl = `${mockData.baseUrl}/edvs`;
18
18
  const kmsBaseUrl = `${mockData.baseUrl}/kms`;
19
19
 
20
+ const FIVE_MINUTES = 1000 * 60 * 5;
21
+
20
22
  export async function createMeter({capabilityAgent, serviceType} = {}) {
21
23
  // create signer using the application's capability invocation key
22
24
  const {keys: {capabilityInvocationKey}} = getAppIdentity();
@@ -204,7 +206,7 @@ export async function delegate({
204
206
  }) {
205
207
  const zcapClient = createZcapClient({capabilityAgent: delegator});
206
208
  expires = expires || (capability && capability.expires) ||
207
- new Date(Date.now() + 5000).toISOString().slice(0, -5) + 'Z';
209
+ new Date(Date.now() + FIVE_MINUTES).toISOString().slice(0, -5) + 'Z';
208
210
  return zcapClient.delegate({
209
211
  capability, controller, expires, invocationTarget, allowedActions
210
212
  });
package/test/package.json CHANGED
@@ -35,8 +35,8 @@
35
35
  "@bedrock/package-manager": "^3.0.0",
36
36
  "@bedrock/security-context": "^7.0.0",
37
37
  "@bedrock/server": "^5.0.0",
38
- "@bedrock/service-agent": "^4.0.0",
39
- "@bedrock/service-context-store": "^5.0.0",
38
+ "@bedrock/service-agent": "^5.0.0",
39
+ "@bedrock/service-context-store": "^7.0.0",
40
40
  "@bedrock/service-core": "5.0.0",
41
41
  "@bedrock/ssm-mongodb": "^9.0.0",
42
42
  "@bedrock/test": "^8.0.0",
@@ -49,7 +49,7 @@
49
49
  "@digitalbazaar/did-method-key": "^2.0.0",
50
50
  "@digitalbazaar/ed25519-signature-2020": "^3.0.0",
51
51
  "@digitalbazaar/ed25519-verification-key-2020": "^3.2.0",
52
- "@digitalbazaar/edv-client": "^13.0.0",
52
+ "@digitalbazaar/edv-client": "^14.0.0",
53
53
  "@digitalbazaar/ezcap": "^2.0.2",
54
54
  "@digitalbazaar/http-client": "^3.0.1",
55
55
  "@digitalbazaar/vc": "^2.1.0",