@bedrock/vc-verifier 23.5.0 → 23.6.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/iso18013-7.js +54 -0
- package/lib/mdl.js +38 -72
- package/lib/status.js +9 -1
- package/package.json +3 -2
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Digital Bazaar, Inc. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import {oid4vp} from '@digitalbazaar/oid4-client';
|
|
5
|
+
|
|
6
|
+
// returns session transcript bytes
|
|
7
|
+
export async function getEncodedSessionTranscript({
|
|
8
|
+
options, domain, challenge
|
|
9
|
+
} = {}) {
|
|
10
|
+
const {mdl} = options;
|
|
11
|
+
|
|
12
|
+
if(!mdl?.sessionTranscript) {
|
|
13
|
+
// no `mdl` session transcript given
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const {sessionTranscript} = mdl;
|
|
18
|
+
|
|
19
|
+
// if `mdl.sessionTranscript` is a base64url string, decode and return it
|
|
20
|
+
if(typeof sessionTranscript === 'string') {
|
|
21
|
+
return Buffer.from(sessionTranscript, 'base64url');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// backwards compatibility case: `sessionTranscript` is an object with
|
|
25
|
+
// Annex B parameters; convert it
|
|
26
|
+
if(sessionTranscript && typeof sessionTranscript === 'object') {
|
|
27
|
+
const handover = {
|
|
28
|
+
type: 'AnnexBHandover',
|
|
29
|
+
responseUri: domain,
|
|
30
|
+
...sessionTranscript
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// `mdocGeneratedNonce` and `verifierGeneratedNonce` are
|
|
34
|
+
// base64url strings that must be converted to UTF-8 strings; the others
|
|
35
|
+
// are passthrough strings
|
|
36
|
+
for(const prop of ['mdocGeneratedNonce', 'verifierGeneratedNonce']) {
|
|
37
|
+
if(handover[prop]) {
|
|
38
|
+
handover[prop] = Buffer
|
|
39
|
+
.from(handover[prop], 'base64url')
|
|
40
|
+
.toString('utf8');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// add `challenge` as `verifierGeneratedNonce` if not specified
|
|
45
|
+
if(!handover.verifierGeneratedNonce) {
|
|
46
|
+
handover.verifierGeneratedNonce = challenge;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return oid4vp.mdl.encodeSessionTranscript({handover});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// no acceptable session transcript
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
package/lib/mdl.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2025 Digital Bazaar, Inc. All rights reserved.
|
|
2
|
+
* Copyright (c) 2025-2026 Digital Bazaar, Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import * as bedrock from '@bedrock/core';
|
|
5
5
|
import {addDocumentRoutes, documentStores} from '@bedrock/service-agent';
|
|
6
6
|
import {
|
|
7
7
|
createMdlCaStoreBody, updateMdlCaStoreBody
|
|
8
8
|
} from '../schemas/bedrock-vc-verifier.js';
|
|
9
|
-
import {
|
|
9
|
+
import {getEncodedSessionTranscript} from './iso18013-7.js';
|
|
10
|
+
import {Verifier} from '@auth0/mdl';
|
|
10
11
|
|
|
11
12
|
const {util: {BedrockError}} = bedrock;
|
|
12
13
|
|
|
@@ -51,14 +52,23 @@ export async function verifyEnvelopedPresentation({
|
|
|
51
52
|
// decoded `contents` is the mDL device response
|
|
52
53
|
const deviceResponse = Buffer.from(contents, 'base64url');
|
|
53
54
|
|
|
54
|
-
// session transcription must be provided
|
|
55
|
+
// session transcription must be provided as base64url string or as an
|
|
56
|
+
// object in `options.mdl.sessionTranscript` with Annex B parameters modulo:
|
|
55
57
|
// `domain` which is used as the `responseUri`
|
|
56
58
|
// `challenge` which is used as the `verifierGeneratedNonce`
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const encodedSessionTranscript = await getEncodedSessionTranscript({
|
|
60
|
+
options, domain, challenge
|
|
61
|
+
});
|
|
62
|
+
if(!encodedSessionTranscript) {
|
|
63
|
+
throw new BedrockError(
|
|
64
|
+
'No usable mdoc session transcript provided in verification options.', {
|
|
65
|
+
name: 'DataError',
|
|
66
|
+
details: {
|
|
67
|
+
httpStatusCode: 400,
|
|
68
|
+
public: true
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
62
72
|
|
|
63
73
|
// fetch CA store(s)
|
|
64
74
|
const {documentStore} = await documentStores.get({config, serviceType});
|
|
@@ -71,18 +81,32 @@ export async function verifyEnvelopedPresentation({
|
|
|
71
81
|
doc.content.trustedCertificates.forEach(caStore.add, caStore);
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
return
|
|
75
|
-
deviceResponse,
|
|
84
|
+
return _verifyDeviceResponse({
|
|
85
|
+
deviceResponse, encodedSessionTranscript, trustedCertificates: [...caStore]
|
|
76
86
|
});
|
|
77
87
|
}
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
async function _getCAStoreDocument({documentStore, url}) {
|
|
90
|
+
const doc = await documentStore.get({id: url});
|
|
91
|
+
if(doc.meta.type !== MDL_CA_STORE_TYPE) {
|
|
92
|
+
// wrong meta type; treat as not found error
|
|
93
|
+
throw new BedrockError(`Document "${url}" not found.`, {
|
|
94
|
+
name: 'NotFoundError',
|
|
95
|
+
details: {
|
|
96
|
+
url,
|
|
97
|
+
httpStatusCode: 404,
|
|
98
|
+
public: true
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return doc;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function _verifyDeviceResponse({
|
|
106
|
+
deviceResponse, encodedSessionTranscript, trustedCertificates
|
|
81
107
|
} = {}) {
|
|
82
108
|
try {
|
|
83
109
|
const verifier = new Verifier(trustedCertificates);
|
|
84
|
-
const encodedSessionTranscript = _encodeSessionTranscript(
|
|
85
|
-
sessionTranscript);
|
|
86
110
|
|
|
87
111
|
// uncomment to debug
|
|
88
112
|
/*
|
|
@@ -133,61 +157,3 @@ export async function verifyPresentation({
|
|
|
133
157
|
return {verified: false, error};
|
|
134
158
|
}
|
|
135
159
|
}
|
|
136
|
-
|
|
137
|
-
// returns a full or partial session transcript
|
|
138
|
-
function _getSessionTranscriptFromOptions({options}) {
|
|
139
|
-
if(!options.mdl?.sessionTranscript) {
|
|
140
|
-
// no `mdl` session transcription options given
|
|
141
|
-
return {};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// `mdocGeneratedNonce` and `verifierGeneratedNonce` are base64url-encoded
|
|
145
|
-
// values; the others are passthrough strings
|
|
146
|
-
const sessionTranscript = {...options.mdl.sessionTranscript};
|
|
147
|
-
if(sessionTranscript.mdocGeneratedNonce) {
|
|
148
|
-
sessionTranscript.mdocGeneratedNonce = Buffer
|
|
149
|
-
.from(sessionTranscript.mdocGeneratedNonce, 'base64url')
|
|
150
|
-
// note: ISO 18013-7 requires `verifierGeneratedNonce` to be a string
|
|
151
|
-
.toString('utf8');
|
|
152
|
-
}
|
|
153
|
-
if(sessionTranscript.verifierGeneratedNonce) {
|
|
154
|
-
sessionTranscript.verifierGeneratedNonce = Buffer
|
|
155
|
-
.from(sessionTranscript.verifierGeneratedNonce, 'base64url')
|
|
156
|
-
// note: ISO 18013-7 requires `verifierGeneratedNonce` to be a string
|
|
157
|
-
.toString('utf8');
|
|
158
|
-
}
|
|
159
|
-
return sessionTranscript;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function _encodeSessionTranscript(sessionTranscript) {
|
|
163
|
-
const {
|
|
164
|
-
mdocGeneratedNonce,
|
|
165
|
-
clientId,
|
|
166
|
-
responseUri,
|
|
167
|
-
verifierGeneratedNonce
|
|
168
|
-
} = sessionTranscript;
|
|
169
|
-
const encoded = DataItem.fromData([
|
|
170
|
-
// deviceEngagementBytes
|
|
171
|
-
null,
|
|
172
|
-
// eReaderKeyBytes
|
|
173
|
-
null,
|
|
174
|
-
[mdocGeneratedNonce, clientId, responseUri, verifierGeneratedNonce],
|
|
175
|
-
]);
|
|
176
|
-
return DataItem.fromData(encoded).buffer;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function _getCAStoreDocument({documentStore, url}) {
|
|
180
|
-
const doc = await documentStore.get({id: url});
|
|
181
|
-
if(doc.meta.type !== MDL_CA_STORE_TYPE) {
|
|
182
|
-
// wrong meta type; treat as not found error
|
|
183
|
-
throw new BedrockError(`Document "${url}" not found.`, {
|
|
184
|
-
name: 'NotFoundError',
|
|
185
|
-
details: {
|
|
186
|
-
url,
|
|
187
|
-
httpStatusCode: 404,
|
|
188
|
-
public: true
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
return doc;
|
|
193
|
-
}
|
package/lib/status.js
CHANGED
|
@@ -62,6 +62,14 @@ export function createCheckStatus({config} = {}) {
|
|
|
62
62
|
const credentialStatuses = Array.isArray(credentialStatus) ?
|
|
63
63
|
credentialStatus : [credentialStatus];
|
|
64
64
|
|
|
65
|
+
// handle case that terse status has no published revocation or
|
|
66
|
+
// suspension lists, so credential status is empty, i.e., no status
|
|
67
|
+
// to check; a future revision might return the expanded BSL entries
|
|
68
|
+
// with a default `false` status value for each status purpose
|
|
69
|
+
if(credentialStatuses.length === 0) {
|
|
70
|
+
return {verified: true};
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
// combination of different status types not supported at this time
|
|
66
74
|
const expectedType = credentialStatuses?.[0]?.type;
|
|
67
75
|
if(credentialStatuses.some(({type}) => type !== expectedType)) {
|
|
@@ -240,7 +248,7 @@ async function _fetchStatusListIfExists({expanded, cache}) {
|
|
|
240
248
|
cache.set(statusListCredential, document);
|
|
241
249
|
return true;
|
|
242
250
|
} catch(e) {
|
|
243
|
-
if(e.
|
|
251
|
+
if(e.name === 'NotFoundError') {
|
|
244
252
|
// ok for a terse bitstring list to not exist
|
|
245
253
|
return false;
|
|
246
254
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bedrock/vc-verifier",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bedrock VC Verifier",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@digitalbazaar/eddsa-2022-cryptosuite": "^1.0.0",
|
|
42
42
|
"@digitalbazaar/eddsa-jcs-2022-cryptosuite": "^1.0.0",
|
|
43
43
|
"@digitalbazaar/eddsa-rdfc-2022-cryptosuite": "^1.2.0",
|
|
44
|
+
"@digitalbazaar/oid4-client": "^5.8.0",
|
|
44
45
|
"@digitalbazaar/pdf417-dl-canonicalizer": "^1.2.1",
|
|
45
46
|
"@digitalbazaar/vc": "^7.2.0",
|
|
46
47
|
"@digitalbazaar/vc-bitstring-status-list": "^2.0.1",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"@bedrock/did-io": "^10.4.0",
|
|
64
65
|
"@bedrock/express": "^8.3.1",
|
|
65
66
|
"@bedrock/https-agent": "^4.1.0",
|
|
66
|
-
"@bedrock/jsonld-document-loader": "^5.2.
|
|
67
|
+
"@bedrock/jsonld-document-loader": "^5.2.1",
|
|
67
68
|
"@bedrock/mongodb": "^11.0.0",
|
|
68
69
|
"@bedrock/multikey-context": "^3.0.0",
|
|
69
70
|
"@bedrock/security-context": "^9.0.0",
|