@bedrock/vc-verifier 22.3.0 → 23.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/di.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) 2018-2025 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import * as vc from '@digitalbazaar/vc';
5
- import {checkStatus as _checkStatus} from './status.js';
5
+ import {createCheckStatus} from './status.js';
6
6
  import {createDocumentLoader} from './documentLoader.js';
7
7
  import {createSuites} from './suites.js';
8
8
 
@@ -14,7 +14,7 @@ export async function verifyCredential({
14
14
 
15
15
  // only check credential status when option is set
16
16
  const checkStatus = checks.includes('credentialStatus') ?
17
- _checkStatus : () => ({verified: true});
17
+ createCheckStatus({config}) : () => ({verified: true});
18
18
 
19
19
  const result = await vc.verifyCredential({
20
20
  credential,
@@ -59,7 +59,7 @@ export async function verifyPresentation({
59
59
  documentLoader: await createDocumentLoader({config}),
60
60
  suite: createSuites(),
61
61
  unsignedPresentation: !checks.includes('proof'),
62
- checkStatus: _checkStatus,
62
+ checkStatus: createCheckStatus({config}),
63
63
  includeCredentials: true
64
64
  };
65
65
  return vc.verify(verifyOptions);
@@ -46,10 +46,12 @@ bedrock.events.on('bedrock.init', () => {
46
46
  *
47
47
  * @param {object} options - The options to use.
48
48
  * @param {object} options.config - The verifier instance config.
49
+ * @param {Set} [options.remoteUrlAllowList] - Remote URLs that are
50
+ * specifically allowed to be loaded (used for status list checks).
49
51
  *
50
52
  * @returns {Promise<Function>} The document loader.
51
53
  */
52
- export async function createDocumentLoader({config} = {}) {
54
+ export async function createDocumentLoader({config, remoteUrlAllowList} = {}) {
53
55
  const contextDocumentLoader = await createContextDocumentLoader(
54
56
  {config, serviceType});
55
57
 
@@ -84,10 +86,12 @@ export async function createDocumentLoader({config} = {}) {
84
86
  // try to resolve URL through context doc loader
85
87
  return await contextDocumentLoader(url);
86
88
  } catch(e) {
87
- // use web loader if configured and instance config allows it and
88
- // the url starts with `http`, and the core config allows it
89
+ // use web loader if configured and instance config allows it (or it is
90
+ // allowed by an allow list) and the url starts with `http` (and the core
91
+ // config allows it, i.e., `webLoader` exists)
89
92
  const allowRemoteContexts = !config.verifyOptions?.documentLoader ||
90
- config.verifyOptions.documentLoader.allowRemoteContexts;
93
+ config.verifyOptions.documentLoader.allowRemoteContexts ||
94
+ remoteUrlAllowList?.has(url);
91
95
  if(allowRemoteContexts &&
92
96
  url.startsWith('http') && e.name === 'NotFoundError' && webLoader) {
93
97
  return webLoader(url);
package/lib/envelopes.js CHANGED
@@ -33,7 +33,7 @@ export async function verifyEnvelopedCredential({
33
33
  }
34
34
 
35
35
  export async function verifyEnvelopedPresentation({
36
- envelopedPresentation, challenge, domain
36
+ config, envelopedPresentation, challenge, domain, checks
37
37
  } = {}) {
38
38
  let format;
39
39
  try {
@@ -46,6 +46,10 @@ export async function verifyEnvelopedPresentation({
46
46
  result = await vcjwt.verifyEnvelopedPresentation({
47
47
  jwt: contents, challenge, domain
48
48
  });
49
+ } else if(format.typeAndSubType === 'application/vcb') {
50
+ result = await vcb.verifyEnvelopedPresentation({
51
+ config, contents, format, challenge, checks
52
+ });
49
53
  } else {
50
54
  _throwUnknownFormat(format);
51
55
  }
package/lib/index.js CHANGED
@@ -21,7 +21,6 @@ bedrock.events.on('bedrock.init', async () => {
21
21
  const updateConfigBody = structuredClone(schemas.updateConfigBody);
22
22
  const schemasToUpdate = [createConfigBody, updateConfigBody];
23
23
  for(const schema of schemasToUpdate) {
24
- // verify options
25
24
  schema.properties.verifyOptions = verifyOptions;
26
25
  }
27
26
 
@@ -36,6 +35,7 @@ bedrock.events.on('bedrock.init', async () => {
36
35
  validation: {
37
36
  createConfigBody,
38
37
  updateConfigBody,
38
+ validateConfigFn,
39
39
  // require these zcaps (by reference ID)
40
40
  zcapReferenceIds: [{
41
41
  referenceId: 'edv',
@@ -66,3 +66,20 @@ bedrock.events.on('bedrock.init', async () => {
66
66
  await initializeServiceAgent({serviceType});
67
67
  });
68
68
  });
69
+
70
+ async function validateConfigFn({config} = {}) {
71
+ try {
72
+ // set default `verifyOptions` if not given
73
+ const {verifyOptions} = config;
74
+ if(verifyOptions === undefined) {
75
+ config.verifyOptions = {
76
+ documentLoader: {
77
+ allowRemoteContexts: false
78
+ }
79
+ };
80
+ }
81
+ } catch(error) {
82
+ return {valid: false, error};
83
+ }
84
+ return {valid: true};
85
+ }
package/lib/status.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  statusTypeMatches as statusList2020StatusTypeMatches
15
15
  } from '@digitalbazaar/vc-status-list';
16
16
  import assert from 'assert-plus';
17
+ import {createDocumentLoader} from './documentLoader.js';
17
18
 
18
19
  const handlerMap = new Map();
19
20
  handlerMap.set('BitstringStatusListEntry', {
@@ -29,26 +30,41 @@ handlerMap.set('StatusList2021Entry', {
29
30
  statusTypeMatches: statusList2020StatusTypeMatches
30
31
  });
31
32
 
32
- export async function checkStatus(options = {}) {
33
- assert.object(options, 'options');
34
- assert.object(options.credential, 'options.credential');
33
+ export function createCheckStatus({config} = {}) {
34
+ return async function checkStatus(options = {}) {
35
+ assert.object(options, 'options');
36
+ assert.object(options.credential, 'options.credential');
35
37
 
36
- try {
37
- const {credential} = options;
38
- const {credentialStatus} = credential;
39
- if(!credentialStatus) {
40
- // no status to check
41
- return {verified: true};
42
- }
38
+ try {
39
+ const {credential} = options;
40
+ const {credentialStatus} = credential;
41
+ if(!credentialStatus) {
42
+ // no status to check
43
+ return {verified: true};
44
+ }
43
45
 
44
- const handlers = handlerMap.get(credentialStatus.type);
45
- if(!(handlers && handlers.statusTypeMatches({credential}))) {
46
- throw new Error(
47
- `Unsupported credentialStatus type "${credentialStatus.type}".`);
48
- }
46
+ const handlers = handlerMap.get(credentialStatus.type);
47
+ if(!(handlers && handlers.statusTypeMatches({credential}))) {
48
+ throw new Error(
49
+ `Unsupported credentialStatus type "${credentialStatus.type}".`);
50
+ }
49
51
 
50
- return await handlers.checkStatus(options);
51
- } catch(error) {
52
- return {verified: false, error};
53
- }
52
+ // document loader needs to only allow web loading of status
53
+ // list VCs, nothing else
54
+ const documentLoader = await createDocumentLoader({
55
+ config,
56
+ remoteUrlAllowList: new Set([
57
+ credentialStatus.statusListCredential ??
58
+ credentialStatus.revocationListCredential
59
+ ])
60
+ });
61
+ options = {
62
+ ...options,
63
+ documentLoader
64
+ };
65
+ return await handlers.checkStatus(options);
66
+ } catch(error) {
67
+ return {verified: false, error};
68
+ }
69
+ };
54
70
  }
package/lib/vcb.js CHANGED
@@ -29,6 +29,41 @@ const SUPPORTED_BARCODE_FORMATS = new Set([
29
29
 
30
30
  const TEXT_DECODER = new TextDecoder();
31
31
 
32
+ export async function verifyEnvelopedPresentation({
33
+ config, contents, format, challenge, checks
34
+ } = {}) {
35
+ // handle base64 encoding
36
+ let {parameters} = format;
37
+ if(parameters.has('base64')) {
38
+ contents = new Uint8Array(Buffer.from(contents, 'base64'));
39
+ parameters = new Map(parameters);
40
+ parameters.delete('base64');
41
+ }
42
+ // only parameter understood is `barcode-format` with values of:
43
+ // 'qr_code' (default)
44
+ const barcodeFormat = parameters.size === 1 ?
45
+ parameters.get('barcode-format') : (parameters.size === 0 && 'qr_code');
46
+ if(barcodeFormat !== 'qr_code') {
47
+ _throwUnknownFormat(format);
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
+ // parse credential and any verification options from contents...
55
+ const {jsonldDocument: presentation, options} = await _parseQrCodeEnvelope({
56
+ contents, documentLoader, typeTableLoader, expectedHeader: 'VP1-'
57
+ });
58
+ // checks for VCB VPs only applies if there is actually a proof to check
59
+ checks = presentation.proof ? checks : [];
60
+ // verify VP
61
+ const result = await di.verifyPresentation({
62
+ config, presentation, options, challenge, checks
63
+ });
64
+ return {...result, presentation};
65
+ }
66
+
32
67
  export async function verifyEnvelopedCredential({
33
68
  config, contents, format, checks
34
69
  } = {}) {
@@ -58,8 +93,8 @@ export async function verifyEnvelopedCredential({
58
93
  let credential;
59
94
  let options;
60
95
  if(barcodeFormat === 'qr_code') {
61
- ({credential, options} = await _parseQrCodeEnvelope({
62
- contents, documentLoader, typeTableLoader
96
+ ({jsonldDocument: credential, options} = await _parseQrCodeEnvelope({
97
+ contents, documentLoader, typeTableLoader, expectedHeader: 'VC1-'
63
98
  }));
64
99
  }
65
100
  if(barcodeFormat === 'pdf417') {
@@ -76,19 +111,19 @@ export async function verifyEnvelopedCredential({
76
111
  }
77
112
 
78
113
  async function _parseQrCodeEnvelope({
79
- contents, documentLoader, typeTableLoader
114
+ contents, documentLoader, typeTableLoader, expectedHeader
80
115
  }) {
81
116
  // `fromQrCode` requires text, so convert to text as needed
82
117
  if(contents instanceof Uint8Array) {
83
118
  contents = TEXT_DECODER.decode(contents);
84
119
  }
85
- const {jsonldDocument: credential} = await util.fromQrCode({
120
+ const {jsonldDocument} = await util.fromQrCode({
86
121
  text: contents,
87
122
  documentLoader,
88
123
  typeTableLoader,
89
- expectedHeader: 'VC1-'
124
+ expectedHeader
90
125
  });
91
- return {credential};
126
+ return {jsonldDocument};
92
127
  }
93
128
 
94
129
  async function _parsePdf417Envelope({
package/lib/verify.js CHANGED
@@ -74,7 +74,7 @@ export async function verifyPresentation({
74
74
  }
75
75
 
76
76
  const presentationResult = await verifyEnvelopedPresentation({
77
- envelopedPresentation: presentation, challenge, domain
77
+ config, envelopedPresentation: presentation, challenge, domain, checks
78
78
  });
79
79
  // verify each `verifiableCredential` in the resulting VP
80
80
  let verified = presentationResult.verified;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/vc-verifier",
3
- "version": "22.3.0",
3
+ "version": "23.1.0",
4
4
  "type": "module",
5
5
  "description": "Bedrock VC Verifier",
6
6
  "main": "./lib/index.js",