@bedrock/vc-verifier 21.2.3 → 22.1.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/lib/config.js +3 -2
- package/lib/di.js +5 -3
- package/lib/documentLoader.js +4 -4
- package/lib/envelopes.js +56 -23
- package/lib/index.js +7 -7
- package/lib/suites.js +16 -11
- package/lib/vcb.js +172 -0
- package/lib/verify.js +11 -6
- package/package.json +31 -27
package/lib/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2019-
|
|
2
|
+
* Copyright (c) 2019-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import {config} from '@bedrock/core';
|
|
5
5
|
import '@bedrock/app-identity';
|
|
@@ -35,7 +35,8 @@ cfg.supportedSuites = [
|
|
|
35
35
|
'eddsa-rdfc-2022',
|
|
36
36
|
'ecdsa-jcs-2019',
|
|
37
37
|
'eddsa-jcs-2022',
|
|
38
|
-
'ecdsa-sd-2023'
|
|
38
|
+
'ecdsa-sd-2023',
|
|
39
|
+
'ecdsa-xi-2023'
|
|
39
40
|
];
|
|
40
41
|
|
|
41
42
|
cfg.routes = {
|
package/lib/di.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2018-
|
|
2
|
+
* Copyright (c) 2018-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as vc from '@digitalbazaar/vc';
|
|
5
5
|
import {checkStatus as _checkStatus} from './status.js';
|
|
6
6
|
import {createDocumentLoader} from './documentLoader.js';
|
|
7
7
|
import {createSuites} from './suites.js';
|
|
8
8
|
|
|
9
|
-
export async function verifyCredential({
|
|
9
|
+
export async function verifyCredential({
|
|
10
|
+
config, credential, options, checks
|
|
11
|
+
} = {}) {
|
|
10
12
|
const documentLoader = await createDocumentLoader({config});
|
|
11
|
-
const suite = createSuites();
|
|
13
|
+
const suite = createSuites({options});
|
|
12
14
|
|
|
13
15
|
// only check credential status when option is set
|
|
14
16
|
const checkStatus = checks.includes('credentialStatus') ?
|
package/lib/documentLoader.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2019-
|
|
2
|
+
* Copyright (c) 2019-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
5
|
import {
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
} from '@bedrock/jsonld-document-loader';
|
|
10
10
|
import {createContextDocumentLoader} from '@bedrock/service-context-store';
|
|
11
11
|
import {didIo} from '@bedrock/did-io';
|
|
12
|
-
import {klona} from 'klona';
|
|
13
12
|
import '@bedrock/credentials-context';
|
|
14
13
|
import '@bedrock/data-integrity-context';
|
|
15
14
|
import '@bedrock/did-context';
|
|
16
15
|
import '@bedrock/did-io';
|
|
17
16
|
import '@bedrock/multikey-context';
|
|
18
17
|
import '@bedrock/security-context';
|
|
18
|
+
import '@bedrock/vc-barcodes-context';
|
|
19
19
|
import '@bedrock/vc-revocation-list-context';
|
|
20
20
|
import '@bedrock/vc-status-list-context';
|
|
21
21
|
import '@bedrock/veres-one-context';
|
|
@@ -151,7 +151,7 @@ function _getNode({didDocument, id}) {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
return {
|
|
154
|
-
'@context':
|
|
155
|
-
...
|
|
154
|
+
'@context': structuredClone(didDocument['@context']),
|
|
155
|
+
...structuredClone(match)
|
|
156
156
|
};
|
|
157
157
|
}
|
package/lib/envelopes.js
CHANGED
|
@@ -1,30 +1,55 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2019-
|
|
2
|
+
* Copyright (c) 2019-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
|
+
import * as vcb from './vcb.js';
|
|
5
6
|
import * as vcjwt from './vcjwt.js';
|
|
6
7
|
|
|
7
8
|
const {util: {BedrockError}} = bedrock;
|
|
8
9
|
|
|
9
|
-
export async function verifyEnvelopedCredential({
|
|
10
|
+
export async function verifyEnvelopedCredential({
|
|
11
|
+
config, envelopedCredential, checks
|
|
12
|
+
} = {}) {
|
|
13
|
+
let format;
|
|
10
14
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
const parseResult = _parseEnvelope({envelope: envelopedCredential});
|
|
16
|
+
const {contents} = parseResult;
|
|
17
|
+
format = parseResult.format;
|
|
18
|
+
|
|
19
|
+
let result;
|
|
20
|
+
if(format.typeAndSubType === 'application/jwt') {
|
|
21
|
+
result = await vcjwt.verifyEnvelopedCredential({jwt: contents});
|
|
22
|
+
} else if(format.typeAndSubType === 'application/vcb') {
|
|
23
|
+
result = await vcb.verifyEnvelopedCredential({
|
|
24
|
+
config, contents, format, checks
|
|
25
|
+
});
|
|
26
|
+
} else {
|
|
27
|
+
_throwUnknownFormat(format);
|
|
28
|
+
}
|
|
29
|
+
return {...result, format};
|
|
15
30
|
} catch(error) {
|
|
16
|
-
return {verified: false, error};
|
|
31
|
+
return {verified: false, error, format};
|
|
17
32
|
}
|
|
18
33
|
}
|
|
19
34
|
|
|
20
35
|
export async function verifyEnvelopedPresentation({
|
|
21
36
|
envelopedPresentation, challenge, domain
|
|
22
37
|
} = {}) {
|
|
38
|
+
let format;
|
|
23
39
|
try {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
40
|
+
const parseResult = _parseEnvelope({envelope: envelopedPresentation});
|
|
41
|
+
const {contents} = parseResult;
|
|
42
|
+
format = parseResult.format;
|
|
43
|
+
|
|
44
|
+
let result;
|
|
45
|
+
if(format.typeAndSubType === 'application/jwt') {
|
|
46
|
+
result = await vcjwt.verifyEnvelopedPresentation({
|
|
47
|
+
jwt: contents, challenge, domain
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
_throwUnknownFormat(format);
|
|
51
|
+
}
|
|
52
|
+
return {...result, format};
|
|
28
53
|
} catch(error) {
|
|
29
54
|
return {verified: false, error};
|
|
30
55
|
}
|
|
@@ -32,20 +57,28 @@ export async function verifyEnvelopedPresentation({
|
|
|
32
57
|
|
|
33
58
|
function _parseEnvelope({envelope}) {
|
|
34
59
|
const {id} = envelope;
|
|
35
|
-
|
|
60
|
+
const format = {};
|
|
36
61
|
const comma = id.indexOf(',');
|
|
37
62
|
if(id.startsWith('data:') && comma !== -1) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
public: true
|
|
47
|
-
},
|
|
48
|
-
});
|
|
63
|
+
const mediaType = id.slice('data:'.length, comma);
|
|
64
|
+
const parts = mediaType.split(';');
|
|
65
|
+
format.mediaType = mediaType;
|
|
66
|
+
format.typeAndSubType = parts.shift();
|
|
67
|
+
const [type, subType] = format.typeAndSubType.split('/');
|
|
68
|
+
format.type = type;
|
|
69
|
+
format.subType = subType;
|
|
70
|
+
format.parameters = new Map(parts.map(s => s.trim().split('=')));
|
|
49
71
|
}
|
|
50
72
|
return {contents: id.slice(comma + 1), format};
|
|
51
73
|
}
|
|
74
|
+
|
|
75
|
+
function _throwUnknownFormat(format) {
|
|
76
|
+
throw new BedrockError(
|
|
77
|
+
`Unknown envelope format "${format.mediaType}".`, {
|
|
78
|
+
name: 'DataError',
|
|
79
|
+
details: {
|
|
80
|
+
httpStatusCode: 400,
|
|
81
|
+
public: true
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2021-
|
|
2
|
+
* Copyright (c) 2021-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
|
-
import {createService, schemas} from '@bedrock/service-core';
|
|
6
5
|
import {
|
|
7
|
-
|
|
6
|
+
addCborldRoutes, addContextRoutes
|
|
8
7
|
} from '@bedrock/service-context-store';
|
|
8
|
+
import {createService, schemas} from '@bedrock/service-core';
|
|
9
9
|
import {addRoutes} from './http.js';
|
|
10
10
|
import {initializeServiceAgent} from '@bedrock/service-agent';
|
|
11
|
-
import {klona} from 'klona';
|
|
12
11
|
import {verifyOptions} from '../schemas/bedrock-vc-verifier.js';
|
|
13
12
|
|
|
14
13
|
// load config defaults
|
|
@@ -18,8 +17,8 @@ const serviceType = 'vc-verifier';
|
|
|
18
17
|
|
|
19
18
|
bedrock.events.on('bedrock.init', async () => {
|
|
20
19
|
// add customizations to config validators...
|
|
21
|
-
const createConfigBody =
|
|
22
|
-
const updateConfigBody =
|
|
20
|
+
const createConfigBody = structuredClone(schemas.createConfigBody);
|
|
21
|
+
const updateConfigBody = structuredClone(schemas.updateConfigBody);
|
|
23
22
|
const schemasToUpdate = [createConfigBody, updateConfigBody];
|
|
24
23
|
for(const schema of schemasToUpdate) {
|
|
25
24
|
// verify options
|
|
@@ -52,7 +51,8 @@ bedrock.events.on('bedrock.init', async () => {
|
|
|
52
51
|
});
|
|
53
52
|
|
|
54
53
|
bedrock.events.on('bedrock-express.configure.routes', async app => {
|
|
55
|
-
await
|
|
54
|
+
await addCborldRoutes({app, service});
|
|
55
|
+
await addContextRoutes({app, service});
|
|
56
56
|
await addRoutes({app, service});
|
|
57
57
|
});
|
|
58
58
|
|
package/lib/suites.js
CHANGED
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
createVerifyCryptosuite as createEcdsaSd2023VerifyCryptosuite
|
|
13
13
|
} from '@digitalbazaar/ecdsa-sd-2023-cryptosuite';
|
|
14
|
+
import {
|
|
15
|
+
createCryptosuite as createEcdsaXi2023Cryptosuite
|
|
16
|
+
} from '@digitalbazaar/ecdsa-xi-2023-cryptosuite';
|
|
14
17
|
import {
|
|
15
18
|
createVerifyCryptosuite as createEddsaJcs2022VerifyCryptoSuite
|
|
16
19
|
} from '@digitalbazaar/eddsa-jcs-2022-cryptosuite';
|
|
@@ -32,12 +35,13 @@ import {
|
|
|
32
35
|
|
|
33
36
|
// DataIntegrityProof should work for multiple cryptosuites
|
|
34
37
|
const SUPPORTED_CRYPTOSUITES = new Map([
|
|
35
|
-
['bbs-2023', createBbs2023VerifyCryptosuite()],
|
|
36
|
-
['ecdsa-rdfc-2019', ecdsaRdfc2019CryptoSuite],
|
|
37
|
-
['eddsa-rdfc-2022', eddsaRdfc2022CryptoSuite],
|
|
38
|
-
['ecdsa-jcs-2019', createEcdsaJcs2019VerifyCryptoSuite()],
|
|
39
|
-
['eddsa-jcs-2022', createEddsaJcs2022VerifyCryptoSuite()],
|
|
40
|
-
['ecdsa-sd-2023', createEcdsaSd2023VerifyCryptosuite()]
|
|
38
|
+
['bbs-2023', () => createBbs2023VerifyCryptosuite()],
|
|
39
|
+
['ecdsa-rdfc-2019', () => ecdsaRdfc2019CryptoSuite],
|
|
40
|
+
['eddsa-rdfc-2022', () => eddsaRdfc2022CryptoSuite],
|
|
41
|
+
['ecdsa-jcs-2019', () => createEcdsaJcs2019VerifyCryptoSuite()],
|
|
42
|
+
['eddsa-jcs-2022', () => createEddsaJcs2022VerifyCryptoSuite()],
|
|
43
|
+
['ecdsa-sd-2023', () => createEcdsaSd2023VerifyCryptosuite()],
|
|
44
|
+
['ecdsa-xi-2023', options => createEcdsaXi2023Cryptosuite(options)]
|
|
41
45
|
]);
|
|
42
46
|
|
|
43
47
|
const SUPPORTED_LEGACY_CRYPTOSUITES = new Map([
|
|
@@ -50,10 +54,15 @@ const SUPPORTED_LEGACY_SUITES = new Map([
|
|
|
50
54
|
['Ed25519Signature2020', Ed25519Signature2020]
|
|
51
55
|
]);
|
|
52
56
|
|
|
53
|
-
export function createSuites() {
|
|
57
|
+
export function createSuites({options} = {}) {
|
|
54
58
|
const cfg = bedrock.config['vc-verifier'];
|
|
55
59
|
const {supportedSuites} = cfg;
|
|
56
60
|
const suite = supportedSuites.map(supportedSuite => {
|
|
61
|
+
const createCryptosuite = SUPPORTED_CRYPTOSUITES.get(supportedSuite);
|
|
62
|
+
if(createCryptosuite) {
|
|
63
|
+
const cryptosuite = createCryptosuite(options);
|
|
64
|
+
return new DataIntegrityProof({cryptosuite});
|
|
65
|
+
}
|
|
57
66
|
const LegacySuite = SUPPORTED_LEGACY_SUITES.get(supportedSuite);
|
|
58
67
|
if(LegacySuite) {
|
|
59
68
|
return new LegacySuite();
|
|
@@ -64,10 +73,6 @@ export function createSuites() {
|
|
|
64
73
|
cryptosuite: LegacyCryptosuite, legacyContext: true
|
|
65
74
|
});
|
|
66
75
|
}
|
|
67
|
-
const cryptosuite = SUPPORTED_CRYPTOSUITES.get(supportedSuite);
|
|
68
|
-
if(cryptosuite) {
|
|
69
|
-
return new DataIntegrityProof({cryptosuite});
|
|
70
|
-
}
|
|
71
76
|
throw new Error(`Unsupported suite ${supportedSuite}`);
|
|
72
77
|
});
|
|
73
78
|
return suite;
|
package/lib/vcb.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import * as bedrock from '@bedrock/core';
|
|
5
|
+
import * as cborld from '@digitalbazaar/cborld';
|
|
6
|
+
import * as di from './di.js';
|
|
7
|
+
import {aamva} from '@digitalbazaar/pdf417-dl-canonicalizer';
|
|
8
|
+
import {createCborldTypeTableLoader} from '@bedrock/service-context-store';
|
|
9
|
+
import {createDocumentLoader} from './documentLoader.js';
|
|
10
|
+
import {util} from '@digitalbazaar/vpqr';
|
|
11
|
+
|
|
12
|
+
const {util: {BedrockError}} = bedrock;
|
|
13
|
+
|
|
14
|
+
const VC_CONTEXT_2 = 'https://www.w3.org/ns/credentials/v2';
|
|
15
|
+
const VCB_CONTEXT_1 = 'https://w3id.org/vc-barcodes/v1';
|
|
16
|
+
|
|
17
|
+
// map of AAMVA issuer identification number to VCB location
|
|
18
|
+
const AAMVA_IIN_TO_VCB_LOCATION = new Map([
|
|
19
|
+
// VCB spec, i.e., test vector intentionally invalid issuer
|
|
20
|
+
['000000', {subfile: 'ZZ', field: 'ZZA', encoding: 'base64url'}],
|
|
21
|
+
// US state of CA
|
|
22
|
+
['636014', {subfile: 'ZC', field: 'ZCE', encoding: 'base64'}],
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const SUPPORTED_BARCODE_FORMATS = new Set([
|
|
26
|
+
'qr_code',
|
|
27
|
+
'pdf417'
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
export async function verifyEnvelopedCredential({
|
|
31
|
+
config, contents, format, checks
|
|
32
|
+
} = {}) {
|
|
33
|
+
// handle base64 encoding
|
|
34
|
+
let {parameters} = format;
|
|
35
|
+
if(parameters.has('base64')) {
|
|
36
|
+
contents = new Uint8Array(Buffer.from(contents, 'base64'));
|
|
37
|
+
parameters = new Map(parameters);
|
|
38
|
+
parameters.delete('base64');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// only parameter understood is `barcode-format` with values of:
|
|
42
|
+
// 'qr_code' (default) or 'pdf417'
|
|
43
|
+
const barcodeFormat = parameters.size === 1 ?
|
|
44
|
+
parameters.get('barcode-format') : (parameters.size === 0 && 'qr_code');
|
|
45
|
+
if(!SUPPORTED_BARCODE_FORMATS.has(barcodeFormat)) {
|
|
46
|
+
_throwUnknownFormat(format);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// create loaders for JSON-LD contexts and CBOR-LD type tables
|
|
50
|
+
const documentLoader = await createDocumentLoader({config});
|
|
51
|
+
const typeTableLoader = await createCborldTypeTableLoader({
|
|
52
|
+
config, serviceType: 'vc-verifier'
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// parse credential and any verification options from contents...
|
|
56
|
+
let credential;
|
|
57
|
+
let options;
|
|
58
|
+
if(barcodeFormat === 'qr_code') {
|
|
59
|
+
({credential, options} = await _parseQrCodeEnvelope({
|
|
60
|
+
contents, documentLoader, typeTableLoader
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
if(barcodeFormat === 'pdf417') {
|
|
64
|
+
({credential, options} = await _parsePdf417Envelope({
|
|
65
|
+
contents, documentLoader, typeTableLoader
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// verify VC
|
|
70
|
+
const result = await di.verifyCredential({
|
|
71
|
+
config, credential, options, checks
|
|
72
|
+
});
|
|
73
|
+
return {...result, credential};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function _parseQrCodeEnvelope({
|
|
77
|
+
contents, documentLoader, typeTableLoader
|
|
78
|
+
}) {
|
|
79
|
+
const {jsonldDocument: credential} = await util.fromQrCode({
|
|
80
|
+
text: contents,
|
|
81
|
+
documentLoader,
|
|
82
|
+
typeTableLoader,
|
|
83
|
+
expectedHeader: 'VC1-'
|
|
84
|
+
});
|
|
85
|
+
return {credential};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function _parsePdf417Envelope({
|
|
89
|
+
contents, documentLoader, typeTableLoader
|
|
90
|
+
}) {
|
|
91
|
+
// parse AAMVA object from scanned PDF417 data; if contents are a string,
|
|
92
|
+
// presume UTF-8 encoding
|
|
93
|
+
const object = aamva.decode({data: contents, encoding: 'utf8'});
|
|
94
|
+
|
|
95
|
+
// find VCB in AAMVA object
|
|
96
|
+
const credential = await findVcb({object, documentLoader, typeTableLoader});
|
|
97
|
+
const componentIndex = credential.credentialSubject?.protectedComponentIndex;
|
|
98
|
+
|
|
99
|
+
// select AAMVA DL or ID document
|
|
100
|
+
const document = await aamva.select({
|
|
101
|
+
object,
|
|
102
|
+
selector: {
|
|
103
|
+
subfile: ['DL', 'ID'],
|
|
104
|
+
componentIndex
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// hash document and store it as `extraInformation` for verification
|
|
108
|
+
const options = {
|
|
109
|
+
extraInformation: await aamva.hash({document})
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return {credential, options};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function findVcb({object, documentLoader, typeTableLoader}) {
|
|
116
|
+
const {issuerIdentificationNumber} = object;
|
|
117
|
+
const location = AAMVA_IIN_TO_VCB_LOCATION.get(issuerIdentificationNumber);
|
|
118
|
+
if(!location) {
|
|
119
|
+
throw new BedrockError(
|
|
120
|
+
'Unknown envelope format; verifiable credential barcode not found.', {
|
|
121
|
+
name: 'DataError',
|
|
122
|
+
details: {httpStatusCode: 400, public: true}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const selection = await aamva.select({
|
|
127
|
+
object,
|
|
128
|
+
selector: {
|
|
129
|
+
subfile: [location.subfile],
|
|
130
|
+
fields: [location.field]
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const encoded = selection.get(location.field);
|
|
135
|
+
if(!encoded) {
|
|
136
|
+
throw new BedrockError('Verifiable credential barcode not found.', {
|
|
137
|
+
name: 'DataError',
|
|
138
|
+
details: {httpStatusCode: 400, public: true}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const cborldBytes = new Uint8Array(Buffer.from(encoded, location.encoding));
|
|
143
|
+
|
|
144
|
+
// decode CBOR-LD bytes into JSON-LD VCB
|
|
145
|
+
const credential = await cborld.decode({
|
|
146
|
+
cborldBytes,
|
|
147
|
+
documentLoader,
|
|
148
|
+
typeTableLoader
|
|
149
|
+
});
|
|
150
|
+
const contexts = credential['@context'];
|
|
151
|
+
if(!Array.isArray(contexts) &&
|
|
152
|
+
contexts[0] === VC_CONTEXT_2 &&
|
|
153
|
+
contexts.includes(VCB_CONTEXT_1)) {
|
|
154
|
+
throw new BedrockError('Verifiable credential barcode not found.', {
|
|
155
|
+
name: 'DataError',
|
|
156
|
+
details: {httpStatusCode: 400, public: true}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return credential;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function _throwUnknownFormat(format) {
|
|
164
|
+
throw new BedrockError(
|
|
165
|
+
`Unknown envelope format "${format.mediaType}".`, {
|
|
166
|
+
name: 'DataError',
|
|
167
|
+
details: {
|
|
168
|
+
httpStatusCode: 400,
|
|
169
|
+
public: true
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
package/lib/verify.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2018-
|
|
2
|
+
* Copyright (c) 2018-2025 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as di from './di.js';
|
|
5
5
|
import {
|
|
@@ -12,11 +12,14 @@ export async function verifyCredential({config, credential, checks} = {}) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const result = await verifyEnvelopedCredential({
|
|
15
|
-
envelopedCredential: credential, checks
|
|
15
|
+
config, envelopedCredential: credential, checks
|
|
16
16
|
});
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
// if credential envelope is verified, credential has a `proof` field, and
|
|
19
|
+
// format is JWT, do DI verification
|
|
18
20
|
let {verified} = result;
|
|
19
|
-
if(verified && result.credential.proof
|
|
21
|
+
if(verified && result.credential.proof &&
|
|
22
|
+
result.format.typeAndSubType === 'application/jwt') {
|
|
20
23
|
const proofResult = await di.verifyCredential({
|
|
21
24
|
config, credential: result.credential, checks
|
|
22
25
|
});
|
|
@@ -74,8 +77,10 @@ export async function verifyPresentation({
|
|
|
74
77
|
let credentialResults;
|
|
75
78
|
if(!verified) {
|
|
76
79
|
credentialResults = [];
|
|
77
|
-
} else if(presentationResult.presentation?.proof
|
|
78
|
-
|
|
80
|
+
} else if(presentationResult.presentation?.proof &&
|
|
81
|
+
presentationResult.format.typeAndSubType === 'application/jwt') {
|
|
82
|
+
// presentation in the envelope has a `proof` and envelope format is JWT,
|
|
83
|
+
// so recurse to check `proof` field
|
|
79
84
|
const proofResult = await verifyPresentation({
|
|
80
85
|
config, presentation: presentationResult.presentation,
|
|
81
86
|
challenge, domain, checks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bedrock/vc-verifier",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "22.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bedrock VC Verifier",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -26,56 +26,60 @@
|
|
|
26
26
|
"homepage": "https://github.com/digitalbazaar/bedrock-vc-verifier",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@digitalbazaar/bbs-2023-cryptosuite": "^2.0.1",
|
|
29
|
-
"@digitalbazaar/
|
|
29
|
+
"@digitalbazaar/cborld": "^7.2.0",
|
|
30
|
+
"@digitalbazaar/data-integrity": "^2.5.0",
|
|
30
31
|
"@digitalbazaar/ecdsa-2019-cryptosuite": "^2.0.0",
|
|
31
32
|
"@digitalbazaar/ecdsa-jcs-2019-cryptosuite": "^1.0.0",
|
|
32
|
-
"@digitalbazaar/ecdsa-multikey": "^1.
|
|
33
|
-
"@digitalbazaar/ecdsa-rdfc-2019-cryptosuite": "^1.
|
|
33
|
+
"@digitalbazaar/ecdsa-multikey": "^1.8.0",
|
|
34
|
+
"@digitalbazaar/ecdsa-rdfc-2019-cryptosuite": "^1.2.0",
|
|
34
35
|
"@digitalbazaar/ecdsa-sd-2023-cryptosuite": "^3.4.1",
|
|
35
|
-
"@digitalbazaar/
|
|
36
|
-
"@digitalbazaar/ed25519-
|
|
36
|
+
"@digitalbazaar/ecdsa-xi-2023-cryptosuite": "^1.1.0",
|
|
37
|
+
"@digitalbazaar/ed25519-multikey": "^1.3.1",
|
|
38
|
+
"@digitalbazaar/ed25519-signature-2018": "^4.1.0",
|
|
37
39
|
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
|
|
38
40
|
"@digitalbazaar/eddsa-2022-cryptosuite": "^1.0.0",
|
|
39
41
|
"@digitalbazaar/eddsa-jcs-2022-cryptosuite": "^1.0.0",
|
|
40
|
-
"@digitalbazaar/eddsa-rdfc-2022-cryptosuite": "^1.
|
|
41
|
-
"@digitalbazaar/
|
|
42
|
-
"@digitalbazaar/vc
|
|
42
|
+
"@digitalbazaar/eddsa-rdfc-2022-cryptosuite": "^1.2.0",
|
|
43
|
+
"@digitalbazaar/pdf417-dl-canonicalizer": "^1.2.1",
|
|
44
|
+
"@digitalbazaar/vc": "^7.1.2",
|
|
45
|
+
"@digitalbazaar/vc-bitstring-status-list": "^2.0.1",
|
|
43
46
|
"@digitalbazaar/vc-revocation-list": "^7.0.0",
|
|
44
|
-
"@digitalbazaar/vc-status-list": "^8.0.
|
|
47
|
+
"@digitalbazaar/vc-status-list": "^8.0.1",
|
|
48
|
+
"@digitalbazaar/vpqr": "^5.1.0",
|
|
45
49
|
"assert-plus": "^1.0.0",
|
|
46
50
|
"bnid": "^3.0.0",
|
|
47
|
-
"body-parser": "^1.20.
|
|
51
|
+
"body-parser": "^1.20.3",
|
|
48
52
|
"cors": "^2.8.5",
|
|
49
|
-
"jose": "^
|
|
50
|
-
"
|
|
51
|
-
"serialize-error": "^11.0.3"
|
|
53
|
+
"jose": "^6.0.10",
|
|
54
|
+
"serialize-error": "^12.0.0"
|
|
52
55
|
},
|
|
53
56
|
"peerDependencies": {
|
|
54
57
|
"@bedrock/app-identity": "^4.0.0",
|
|
55
|
-
"@bedrock/core": "^6.
|
|
56
|
-
"@bedrock/credentials-context": "^5.0.
|
|
58
|
+
"@bedrock/core": "^6.3.0",
|
|
59
|
+
"@bedrock/credentials-context": "^5.0.3",
|
|
57
60
|
"@bedrock/data-integrity-context": "^4.0.3",
|
|
58
61
|
"@bedrock/did-context": "^6.0.0",
|
|
59
|
-
"@bedrock/did-io": "^10.
|
|
62
|
+
"@bedrock/did-io": "^10.4.0",
|
|
60
63
|
"@bedrock/express": "^8.3.1",
|
|
61
64
|
"@bedrock/https-agent": "^4.1.0",
|
|
62
|
-
"@bedrock/jsonld-document-loader": "^5.
|
|
63
|
-
"@bedrock/mongodb": "^
|
|
65
|
+
"@bedrock/jsonld-document-loader": "^5.2.0",
|
|
66
|
+
"@bedrock/mongodb": "^11.0.0",
|
|
64
67
|
"@bedrock/multikey-context": "^3.0.0",
|
|
65
68
|
"@bedrock/security-context": "^9.0.0",
|
|
66
|
-
"@bedrock/service-agent": "^
|
|
67
|
-
"@bedrock/service-context-store": "^
|
|
68
|
-
"@bedrock/service-core": "^
|
|
69
|
-
"@bedrock/validation": "^7.1.
|
|
69
|
+
"@bedrock/service-agent": "^10.0.0",
|
|
70
|
+
"@bedrock/service-context-store": "^13.1.0",
|
|
71
|
+
"@bedrock/service-core": "^11.0.0",
|
|
72
|
+
"@bedrock/validation": "^7.1.1",
|
|
73
|
+
"@bedrock/vc-barcodes-context": "^1.0.0",
|
|
70
74
|
"@bedrock/vc-revocation-list-context": "^5.0.0",
|
|
71
|
-
"@bedrock/vc-status-list-context": "^6.0.
|
|
75
|
+
"@bedrock/vc-status-list-context": "^6.0.3",
|
|
72
76
|
"@bedrock/veres-one-context": "^16.0.0"
|
|
73
77
|
},
|
|
74
78
|
"devDependencies": {
|
|
75
|
-
"eslint": "^8.57.
|
|
79
|
+
"eslint": "^8.57.1",
|
|
76
80
|
"eslint-config-digitalbazaar": "^5.2.0",
|
|
77
|
-
"eslint-plugin-jsdoc": "^
|
|
78
|
-
"eslint-plugin-unicorn": "^
|
|
81
|
+
"eslint-plugin-jsdoc": "^50.6.8",
|
|
82
|
+
"eslint-plugin-unicorn": "^56.0.1"
|
|
79
83
|
},
|
|
80
84
|
"engines": {
|
|
81
85
|
"node": ">=18"
|