@digitalbazaar/oid4-client 4.3.0 → 5.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/lib/util.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Copyright (c) 2022-2024 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2022-2025 Digital Bazaar, Inc. All rights reserved.
3
3
  */
4
4
  import * as base64url from 'base64url-universal';
5
5
  import {httpClient} from '@digitalbazaar/http-client';
@@ -21,6 +21,28 @@ export function assertOptional(x, name, type) {
21
21
  return assert(x, name, type, true);
22
22
  }
23
23
 
24
+ export function base64Decode(str) {
25
+ if(Uint8Array.fromBase64) {
26
+ return Uint8Array.fromBase64(str);
27
+ }
28
+ return base64url.decode(
29
+ str.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'));
30
+ }
31
+
32
+ export function base64Encode(data) {
33
+ if(data.toBase64) {
34
+ return data.toBase64();
35
+ }
36
+ // note: this is base64-no-pad; will only work with specific data lengths
37
+ base64url.encode(data).replace(/-/g, '+').replace(/_/g, '/');
38
+ }
39
+
40
+ export function createNamedError({message, name, cause} = {}) {
41
+ const error = new Error(message, {cause});
42
+ error.name = name;
43
+ return error;
44
+ }
45
+
24
46
  export async function discoverIssuer({issuerConfigUrl, agent} = {}) {
25
47
  try {
26
48
  assert(issuerConfigUrl, 'issuerConfigUrl', 'string');
@@ -251,6 +273,49 @@ export async function robustDiscoverIssuer({issuer, agent} = {}) {
251
273
  throw error;
252
274
  }
253
275
 
276
+ export function selectJwk({keys, kid, alg, kty, crv, use} = {}) {
277
+ /* Example JWKs "keys":
278
+ "jwks": {
279
+ "keys": [
280
+ {
281
+ "kty": "EC",
282
+ "use": "enc",
283
+ "crv": "P-256",
284
+ "x": "...",
285
+ "y": "...",
286
+ "alg": "ECDH-ES",
287
+ "kid": "..."
288
+ }
289
+ ]
290
+ } */
291
+ if(!Array.isArray(keys)) {
292
+ return;
293
+ }
294
+
295
+ // match `kid` exactly if given
296
+ if(kid !== undefined) {
297
+ return keys.find(jwk => jwk?.kid === kid);
298
+ }
299
+
300
+ return keys.find(jwk => {
301
+ // default unspecified search values to whatever is in `jwk`
302
+ const alg1 = alg ?? jwk.alg;
303
+ const kty1 = kty ?? jwk.kty;
304
+ const crv1 = crv ?? jwk.crv;
305
+ const use1 = use ?? jwk.use;
306
+ const {
307
+ // default missing `alg` value in `jwk` to search value
308
+ alg: alg2 = alg1,
309
+ kty: kty2,
310
+ crv: crv2,
311
+ // default missing `use` value in `jwk` to search value
312
+ use: use2 = use1
313
+ } = jwk;
314
+ // return if `jwk` matches computed values
315
+ return alg1 === alg2 && kty1 === kty2 && crv1 === crv2 && use1 === use2;
316
+ });
317
+ }
318
+
254
319
  export async function signJWT({payload, protectedHeader, signer} = {}) {
255
320
  // encode payload and protected header
256
321
  const b64Payload = base64url.encode(JSON.stringify(payload));
package/lib/x509.js ADDED
@@ -0,0 +1,61 @@
1
+ /*!
2
+ * Copyright (c) 2023-2025 Digital Bazaar, Inc. All rights reserved.
3
+ */
4
+ import {
5
+ Certificate,
6
+ CertificateChainValidationEngine,
7
+ id_SubjectAltName
8
+ } from 'pkijs';
9
+ import {base64Decode} from './util.js';
10
+
11
+ export function fromPemOrBase64(str) {
12
+ const tag = 'CERTIFICATE';
13
+ const pattern = new RegExp(
14
+ `-{5}BEGIN ${tag}-{5}([a-zA-Z0-9=+\\/\\n\\r]+)-{5}END ${tag}-{5}`, 'g');
15
+ const matches = pattern.exec(str);
16
+ if(!matches) {
17
+ throw new Error('No PEM or Base64-formatted certificate found.');
18
+ }
19
+ const b64 = matches[1].replace(/\r/g, '').replace(/\n/g, '');
20
+ return _fromBase64(b64);
21
+ }
22
+
23
+ export function hasDomainSubjectAltName({certificate, name} = {}) {
24
+ const subjectAltNames = new Set();
25
+ for(const extension of certificate.extensions) {
26
+ if(extension.extnID === id_SubjectAltName) {
27
+ for(const altName of extension.parsedValue.altNames) {
28
+ // `domain` type
29
+ if(altName.type === 2) {
30
+ subjectAltNames.add(altName.value);
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return subjectAltNames.has(name);
36
+ }
37
+
38
+ export function parseCertificateChain({x5c} = {}) {
39
+ return x5c.map(c => Certificate.fromBER(base64Decode(c)));
40
+ }
41
+
42
+ export async function verifyCertificateChain({
43
+ chain, trustedCertificates
44
+ } = {}) {
45
+ if(!(chain?.length > 0)) {
46
+ throw new Error('No matching certificate.');
47
+ }
48
+
49
+ const chainEngine = new CertificateChainValidationEngine({
50
+ certs: chain.map(c => typeof c === 'string' ? fromPemOrBase64(c) : c),
51
+ trustedCerts: trustedCertificates.map(
52
+ c => typeof c === 'string' ? fromPemOrBase64(c) : c)
53
+ });
54
+
55
+ const verifyResult = await chainEngine.verify();
56
+ return verifyResult;
57
+ }
58
+
59
+ function _fromBase64(str) {
60
+ return Certificate.fromBER(base64Decode(str));
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitalbazaar/oid4-client",
3
- "version": "4.3.0",
3
+ "version": "5.0.0",
4
4
  "description": "An OID4 (VC + VP) client",
5
5
  "homepage": "https://github.com/digitalbazaar/oid4-client",
6
6
  "author": {
@@ -25,15 +25,15 @@
25
25
  "dependencies": {
26
26
  "@digitalbazaar/http-client": "^4.0.0",
27
27
  "base64url-universal": "^2.0.0",
28
- "jose": "^5.9.4",
29
- "jsonpath-plus": "^10.0.0",
28
+ "jose": "^6.0.13",
29
+ "jsonpath-plus": "^10.3.0",
30
30
  "jsonpointer": "^5.0.1",
31
- "uuid": "^10.0.0"
31
+ "pkijs": "^3.2.5"
32
32
  },
33
33
  "devDependencies": {
34
- "c8": "^7.11.3",
34
+ "c8": "^10.1.3",
35
35
  "chai": "^4.3.6",
36
- "cross-env": "^7.0.3",
36
+ "cross-env": "^10.0.0",
37
37
  "eslint": "^8.41.0",
38
38
  "eslint-config-digitalbazaar": "^5.0.1",
39
39
  "eslint-plugin-jsdoc": "^50.4.1",
@@ -49,7 +49,7 @@
49
49
  "karma-webpack": "^5.0.0",
50
50
  "mocha": "^10.0.0",
51
51
  "mocha-lcov-reporter": "^1.3.0",
52
- "webpack": "^5.73.0"
52
+ "webpack": "^5.101.3"
53
53
  },
54
54
  "c8": {
55
55
  "reporter": [
@@ -59,7 +59,7 @@
59
59
  ]
60
60
  },
61
61
  "engines": {
62
- "node": ">=18"
62
+ "node": ">=20"
63
63
  },
64
64
  "keywords": [
65
65
  "OID4",