@bedrock/vc-verifier 9.0.0 → 12.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/README.md +4 -4
- package/lib/challenges.js +1 -3
- package/lib/documentLoader.js +8 -8
- package/lib/http.js +8 -12
- package/lib/index.js +1 -1
- package/lib/status.js +8 -10
- package/package.json +28 -23
- package/.eslintrc.cjs +0 -12
- package/.github/workflows/main.yml +0 -77
- package/CHANGELOG.md +0 -162
- package/test/mocha/.eslintrc.cjs +0 -9
- package/test/mocha/10-provision.js +0 -868
- package/test/mocha/20-verify.js +0 -390
- package/test/mocha/30-credential-status.js +0 -488
- package/test/mocha/cert.pem +0 -18
- package/test/mocha/helpers.js +0 -230
- package/test/mocha/key.pem +0 -28
- package/test/mocha/mock-credential.json +0 -39
- package/test/mocha/mock.data.js +0 -21
- package/test/package.json +0 -72
- package/test/test.config.js +0 -40
- package/test/test.js +0 -40
package/test/mocha/20-verify.js
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2020-2022 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
-
*/
|
|
4
|
-
import * as helpers from './helpers.js';
|
|
5
|
-
import {agent} from '@bedrock/https-agent';
|
|
6
|
-
import {createRequire} from 'node:module';
|
|
7
|
-
import {documentLoader as brDocLoader} from '@bedrock/jsonld-document-loader';
|
|
8
|
-
import {httpClient} from '@digitalbazaar/http-client';
|
|
9
|
-
import {klona} from 'klona';
|
|
10
|
-
import {mockData} from './mock.data.js';
|
|
11
|
-
const require = createRequire(import.meta.url);
|
|
12
|
-
const {CapabilityAgent} = require('@digitalbazaar/webkms-client');
|
|
13
|
-
const didKeyDriver = require('@digitalbazaar/did-method-key').driver();
|
|
14
|
-
const {Ed25519Signature2020} = require('@digitalbazaar/ed25519-signature-2020');
|
|
15
|
-
const vc = require('@digitalbazaar/vc');
|
|
16
|
-
|
|
17
|
-
const {baseUrl} = mockData;
|
|
18
|
-
const serviceType = 'vc-verifier';
|
|
19
|
-
|
|
20
|
-
// NOTE: using embedded context in mockCredential:
|
|
21
|
-
// https://www.w3.org/2018/credentials/examples/v1
|
|
22
|
-
const mockCredential = require('./mock-credential');
|
|
23
|
-
|
|
24
|
-
describe('verify APIs', () => {
|
|
25
|
-
let capabilityAgent;
|
|
26
|
-
let verifierConfig;
|
|
27
|
-
let verifierId;
|
|
28
|
-
let rootZcap;
|
|
29
|
-
const zcaps = {};
|
|
30
|
-
beforeEach(async () => {
|
|
31
|
-
const secret = '53ad64ce-8e1d-11ec-bb12-10bf48838a41';
|
|
32
|
-
const handle = 'test';
|
|
33
|
-
capabilityAgent = await CapabilityAgent.fromSecret({secret, handle});
|
|
34
|
-
|
|
35
|
-
// create keystore for capability agent
|
|
36
|
-
const keystoreAgent = await helpers.createKeystoreAgent(
|
|
37
|
-
{capabilityAgent});
|
|
38
|
-
|
|
39
|
-
// create EDV for storage (creating hmac and kak in the process)
|
|
40
|
-
const {
|
|
41
|
-
edvConfig,
|
|
42
|
-
hmac,
|
|
43
|
-
keyAgreementKey
|
|
44
|
-
} = await helpers.createEdv({capabilityAgent, keystoreAgent});
|
|
45
|
-
|
|
46
|
-
// get service agent to delegate to
|
|
47
|
-
const serviceAgentUrl =
|
|
48
|
-
`${baseUrl}/service-agents/${encodeURIComponent(serviceType)}`;
|
|
49
|
-
const {data: serviceAgent} = await httpClient.get(serviceAgentUrl, {
|
|
50
|
-
agent
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// delegate edv, hmac, and key agreement key zcaps to service agent
|
|
54
|
-
const {id: edvId} = edvConfig;
|
|
55
|
-
zcaps.edv = await helpers.delegate({
|
|
56
|
-
controller: serviceAgent.id,
|
|
57
|
-
delegator: capabilityAgent,
|
|
58
|
-
invocationTarget: edvId
|
|
59
|
-
});
|
|
60
|
-
const {keystoreId} = keystoreAgent;
|
|
61
|
-
zcaps.hmac = await helpers.delegate({
|
|
62
|
-
capability: `urn:zcap:root:${encodeURIComponent(keystoreId)}`,
|
|
63
|
-
controller: serviceAgent.id,
|
|
64
|
-
invocationTarget: hmac.id,
|
|
65
|
-
delegator: capabilityAgent
|
|
66
|
-
});
|
|
67
|
-
zcaps.keyAgreementKey = await helpers.delegate({
|
|
68
|
-
capability: `urn:zcap:root:${encodeURIComponent(keystoreId)}`,
|
|
69
|
-
controller: serviceAgent.id,
|
|
70
|
-
invocationTarget: keyAgreementKey.kmsId,
|
|
71
|
-
delegator: capabilityAgent
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// create verifier instance
|
|
75
|
-
verifierConfig = await helpers.createConfig({capabilityAgent, zcaps});
|
|
76
|
-
verifierId = verifierConfig.id;
|
|
77
|
-
rootZcap = `urn:zcap:root:${encodeURIComponent(verifierId)}`;
|
|
78
|
-
});
|
|
79
|
-
describe('/challenges', () => {
|
|
80
|
-
it('create a challenge', async () => {
|
|
81
|
-
let err;
|
|
82
|
-
let result;
|
|
83
|
-
try {
|
|
84
|
-
result = await helpers.createChallenge({capabilityAgent, verifierId});
|
|
85
|
-
} catch(e) {
|
|
86
|
-
err = e;
|
|
87
|
-
}
|
|
88
|
-
assertNoError(err);
|
|
89
|
-
should.exist(result.data);
|
|
90
|
-
result.status.should.equal(200);
|
|
91
|
-
result.data.should.have.keys(['challenge']);
|
|
92
|
-
result.data.challenge.should.be.a('string');
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
describe('/credentials/verify', () => {
|
|
96
|
-
it('verifies a valid credential', async () => {
|
|
97
|
-
const verifiableCredential = klona(mockCredential);
|
|
98
|
-
let error;
|
|
99
|
-
let result;
|
|
100
|
-
try {
|
|
101
|
-
const zcapClient = helpers.createZcapClient({capabilityAgent});
|
|
102
|
-
result = await zcapClient.write({
|
|
103
|
-
url: `${verifierId}/credentials/verify`,
|
|
104
|
-
capability: rootZcap,
|
|
105
|
-
json: {
|
|
106
|
-
options: {
|
|
107
|
-
checks: ['proof'],
|
|
108
|
-
},
|
|
109
|
-
verifiableCredential
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
} catch(e) {
|
|
113
|
-
error = e;
|
|
114
|
-
}
|
|
115
|
-
assertNoError(error);
|
|
116
|
-
should.exist(result.data.verified);
|
|
117
|
-
result.data.verified.should.be.a('boolean');
|
|
118
|
-
result.data.verified.should.equal(true);
|
|
119
|
-
const {checks} = result.data;
|
|
120
|
-
checks.should.be.an('array');
|
|
121
|
-
checks.should.have.length(1);
|
|
122
|
-
const [check] = checks;
|
|
123
|
-
check.should.be.a('string');
|
|
124
|
-
check.should.equal('proof');
|
|
125
|
-
should.exist(result.data.results);
|
|
126
|
-
result.data.results.should.be.an('array');
|
|
127
|
-
result.data.results.should.have.length(1);
|
|
128
|
-
const [r] = result.data.results;
|
|
129
|
-
r.verified.should.be.a('boolean');
|
|
130
|
-
r.verified.should.equal(true);
|
|
131
|
-
});
|
|
132
|
-
it('does not verify an invalid credential', async () => {
|
|
133
|
-
const badCredential = klona(mockCredential);
|
|
134
|
-
// change the degree name
|
|
135
|
-
badCredential.credentialSubject.degree.name =
|
|
136
|
-
'Bachelor of Science in Nursing';
|
|
137
|
-
|
|
138
|
-
let error;
|
|
139
|
-
let result;
|
|
140
|
-
try {
|
|
141
|
-
const zcapClient = helpers.createZcapClient({capabilityAgent});
|
|
142
|
-
result = await zcapClient.write({
|
|
143
|
-
url: `${verifierId}/credentials/verify`,
|
|
144
|
-
capability: rootZcap,
|
|
145
|
-
json: {
|
|
146
|
-
options: {
|
|
147
|
-
checks: ['proof'],
|
|
148
|
-
},
|
|
149
|
-
verifiableCredential: badCredential
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
} catch(e) {
|
|
153
|
-
error = e;
|
|
154
|
-
}
|
|
155
|
-
should.exist(error);
|
|
156
|
-
should.not.exist(result);
|
|
157
|
-
should.exist(error.data);
|
|
158
|
-
error.data.should.be.an('object');
|
|
159
|
-
error.data.verified.should.be.a('boolean');
|
|
160
|
-
error.data.verified.should.equal(false);
|
|
161
|
-
error.data.error.name.should.equal('VerificationError');
|
|
162
|
-
error.data.error.errors[0].message.should.equal('Invalid signature.');
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('/presentations/verify', () => {
|
|
167
|
-
it('verifies a valid presentation', async () => {
|
|
168
|
-
// get signing key
|
|
169
|
-
const {methodFor} = await didKeyDriver.generate();
|
|
170
|
-
const signingKey = methodFor({purpose: 'assertionMethod'});
|
|
171
|
-
const suite = new Ed25519Signature2020({key: signingKey});
|
|
172
|
-
|
|
173
|
-
const verifiableCredential = klona(mockCredential);
|
|
174
|
-
const presentation = vc.createPresentation({
|
|
175
|
-
holder: 'did:test:foo',
|
|
176
|
-
id: 'urn:uuid:3e793029-d699-4096-8e74-5ebd956c3137',
|
|
177
|
-
verifiableCredential
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// get challenge from verifier
|
|
181
|
-
const {data: {challenge}} = await helpers.createChallenge(
|
|
182
|
-
{capabilityAgent, verifierId});
|
|
183
|
-
|
|
184
|
-
await vc.signPresentation({
|
|
185
|
-
presentation,
|
|
186
|
-
suite,
|
|
187
|
-
challenge,
|
|
188
|
-
documentLoader: brDocLoader
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
let error;
|
|
192
|
-
let result;
|
|
193
|
-
try {
|
|
194
|
-
const zcapClient = helpers.createZcapClient({capabilityAgent});
|
|
195
|
-
result = await zcapClient.write({
|
|
196
|
-
url: `${verifierId}/presentations/verify`,
|
|
197
|
-
capability: rootZcap,
|
|
198
|
-
json: {
|
|
199
|
-
options: {
|
|
200
|
-
challenge,
|
|
201
|
-
checks: ['proof'],
|
|
202
|
-
},
|
|
203
|
-
verifiablePresentation: presentation
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
} catch(e) {
|
|
207
|
-
error = e;
|
|
208
|
-
}
|
|
209
|
-
assertNoError(error);
|
|
210
|
-
should.exist(result.data.checks);
|
|
211
|
-
const {checks} = result.data;
|
|
212
|
-
checks.should.be.an('array');
|
|
213
|
-
checks.should.have.length(1);
|
|
214
|
-
checks[0].should.be.a('string');
|
|
215
|
-
checks[0].should.equal('proof');
|
|
216
|
-
should.exist(result.data.verified);
|
|
217
|
-
result.data.verified.should.be.a('boolean');
|
|
218
|
-
result.data.verified.should.equal(true);
|
|
219
|
-
should.exist(result.data.presentationResult);
|
|
220
|
-
result.data.presentationResult.should.be.an('object');
|
|
221
|
-
should.exist(result.data.presentationResult.verified);
|
|
222
|
-
result.data.presentationResult.verified.should.be.a('boolean');
|
|
223
|
-
result.data.presentationResult.verified.should.equal(true);
|
|
224
|
-
should.exist(result.data.credentialResults);
|
|
225
|
-
const {data: {credentialResults}} = result;
|
|
226
|
-
credentialResults.should.be.an('array');
|
|
227
|
-
credentialResults.should.have.length(1);
|
|
228
|
-
const [credentialResult] = credentialResults;
|
|
229
|
-
should.exist(credentialResult.verified);
|
|
230
|
-
credentialResult.verified.should.be.a('boolean');
|
|
231
|
-
credentialResult.verified.should.equal(true);
|
|
232
|
-
});
|
|
233
|
-
it('returns an error if bad challenge is specified', async () => {
|
|
234
|
-
// get signing key
|
|
235
|
-
const {methodFor} = await didKeyDriver.generate();
|
|
236
|
-
const signingKey = methodFor({purpose: 'assertionMethod'});
|
|
237
|
-
const suite = new Ed25519Signature2020({key: signingKey});
|
|
238
|
-
|
|
239
|
-
const verifiableCredential = klona(mockCredential);
|
|
240
|
-
const presentation = vc.createPresentation({
|
|
241
|
-
holder: 'foo',
|
|
242
|
-
id: 'urn:uuid:3e793029-d699-4096-8e74-5ebd956c3137',
|
|
243
|
-
verifiableCredential
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// expired / bad challenge
|
|
247
|
-
const challenge = 'z1A9b6RjuUzVWC3VcvsFX5fPb';
|
|
248
|
-
|
|
249
|
-
await vc.signPresentation({
|
|
250
|
-
presentation, suite, challenge, documentLoader: brDocLoader
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
let error;
|
|
254
|
-
let result;
|
|
255
|
-
try {
|
|
256
|
-
const zcapClient = helpers.createZcapClient({capabilityAgent});
|
|
257
|
-
result = await zcapClient.write({
|
|
258
|
-
url: `${verifierId}/presentations/verify`,
|
|
259
|
-
capability: rootZcap,
|
|
260
|
-
json: {
|
|
261
|
-
options: {
|
|
262
|
-
challenge,
|
|
263
|
-
checks: ['proof'],
|
|
264
|
-
},
|
|
265
|
-
verifiablePresentation: presentation
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
} catch(e) {
|
|
269
|
-
error = e;
|
|
270
|
-
}
|
|
271
|
-
should.exist(error);
|
|
272
|
-
should.exist(error.data);
|
|
273
|
-
should.not.exist(result);
|
|
274
|
-
error.data.should.be.an('object');
|
|
275
|
-
error.data.verified.should.be.a('boolean');
|
|
276
|
-
error.data.verified.should.equal(false);
|
|
277
|
-
error.data.error.message.should.equal('Invalid or expired challenge.');
|
|
278
|
-
error.data.error.name.should.equal('DataError');
|
|
279
|
-
});
|
|
280
|
-
it('returns an error if challenge is not specified', async () => {
|
|
281
|
-
// get signing key
|
|
282
|
-
const {methodFor} = await didKeyDriver.generate();
|
|
283
|
-
const signingKey = methodFor({purpose: 'assertionMethod'});
|
|
284
|
-
const suite = new Ed25519Signature2020({key: signingKey});
|
|
285
|
-
|
|
286
|
-
const verifiableCredential = klona(mockCredential);
|
|
287
|
-
const presentation = vc.createPresentation({
|
|
288
|
-
holder: 'foo',
|
|
289
|
-
id: 'urn:uuid:3e793029-d699-4096-8e74-5ebd956c3137',
|
|
290
|
-
verifiableCredential
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// get challenge from verifier
|
|
294
|
-
const {data: {challenge}} = await helpers.createChallenge(
|
|
295
|
-
{capabilityAgent, verifierId});
|
|
296
|
-
|
|
297
|
-
await vc.signPresentation({
|
|
298
|
-
presentation, suite, challenge, documentLoader: brDocLoader
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
let error;
|
|
302
|
-
let result;
|
|
303
|
-
try {
|
|
304
|
-
const zcapClient = helpers.createZcapClient({capabilityAgent});
|
|
305
|
-
result = await zcapClient.write({
|
|
306
|
-
url: `${verifierId}/presentations/verify`,
|
|
307
|
-
capability: rootZcap,
|
|
308
|
-
json: {
|
|
309
|
-
options: {
|
|
310
|
-
// intentionally omit challenge
|
|
311
|
-
checks: ['proof'],
|
|
312
|
-
},
|
|
313
|
-
verifiablePresentation: presentation
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
} catch(e) {
|
|
317
|
-
error = e;
|
|
318
|
-
}
|
|
319
|
-
should.exist(error);
|
|
320
|
-
should.exist(error.data);
|
|
321
|
-
should.not.exist(result);
|
|
322
|
-
error.data.should.be.an('object');
|
|
323
|
-
error.data.verified.should.be.a('boolean');
|
|
324
|
-
error.data.verified.should.equal(false);
|
|
325
|
-
error.data.error.message.should.equal('"options.challenge" is required.');
|
|
326
|
-
error.data.error.name.should.equal('TypeError');
|
|
327
|
-
});
|
|
328
|
-
it('does not verify a presentation with a bad credential', async () => {
|
|
329
|
-
// get signing key
|
|
330
|
-
const {methodFor} = await didKeyDriver.generate();
|
|
331
|
-
const signingKey = methodFor({purpose: 'assertionMethod'});
|
|
332
|
-
const suite = new Ed25519Signature2020({key: signingKey});
|
|
333
|
-
|
|
334
|
-
const badCredential = klona(mockCredential);
|
|
335
|
-
// change the degree name
|
|
336
|
-
badCredential.credentialSubject.degree.name =
|
|
337
|
-
'Bachelor of Science in Nursing';
|
|
338
|
-
const presentation = vc.createPresentation({
|
|
339
|
-
id: 'urn:uuid:3e793029-d699-4096-8e74-5ebd956c3137',
|
|
340
|
-
verifiableCredential: badCredential
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
// get challenge from verifier
|
|
344
|
-
const {data: {challenge}} = await helpers.createChallenge(
|
|
345
|
-
{capabilityAgent, verifierId});
|
|
346
|
-
|
|
347
|
-
await vc.signPresentation({
|
|
348
|
-
presentation, suite, challenge, documentLoader: brDocLoader
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
let error;
|
|
352
|
-
let result;
|
|
353
|
-
try {
|
|
354
|
-
const zcapClient = helpers.createZcapClient({capabilityAgent});
|
|
355
|
-
result = await zcapClient.write({
|
|
356
|
-
url: `${verifierId}/presentations/verify`,
|
|
357
|
-
capability: rootZcap,
|
|
358
|
-
json: {
|
|
359
|
-
options: {
|
|
360
|
-
challenge,
|
|
361
|
-
checks: ['proof'],
|
|
362
|
-
},
|
|
363
|
-
verifiablePresentation: presentation
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
} catch(e) {
|
|
367
|
-
error = e;
|
|
368
|
-
}
|
|
369
|
-
should.exist(error);
|
|
370
|
-
should.not.exist(result);
|
|
371
|
-
should.exist(error.data.checks);
|
|
372
|
-
const {checks} = error.data;
|
|
373
|
-
checks.should.be.an('array');
|
|
374
|
-
checks.should.have.length(1);
|
|
375
|
-
checks[0].should.be.an('object');
|
|
376
|
-
checks[0].check.should.eql(['proof']);
|
|
377
|
-
should.exist(error.data.verified);
|
|
378
|
-
error.data.verified.should.be.a('boolean');
|
|
379
|
-
error.data.verified.should.equal(false);
|
|
380
|
-
should.exist(error.data.error);
|
|
381
|
-
error.data.error.errors.should.be.an('array');
|
|
382
|
-
error.data.error.errors.should.have.length(1);
|
|
383
|
-
error.data.error.name.should.equal('VerificationError');
|
|
384
|
-
const e = error.data.error.errors[0];
|
|
385
|
-
e.should.be.an('object');
|
|
386
|
-
should.exist(e.name);
|
|
387
|
-
e.message.should.equal('Invalid signature.');
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
});
|