@blamejs/pki 0.9.5 → 0.9.6
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/CHANGELOG.md +37 -0
- package/README.md +7 -6
- package/bin/pki.js +110 -34
- package/lib/acme.js +91 -46
- package/lib/attrcert-sign.js +4 -3
- package/lib/cmp-build.js +16 -6
- package/lib/cmp-session.js +19 -3
- package/lib/cmp-verify.js +25 -2
- package/lib/cms-compress.js +3 -3
- package/lib/cms-digest.js +3 -3
- package/lib/cms-verify.js +18 -8
- package/lib/constants.js +1 -0
- package/lib/crl-sign.js +3 -3
- package/lib/csr-sign.js +1 -1
- package/lib/ct.js +16 -3
- package/lib/est.js +44 -29
- package/lib/guard-all.js +2 -0
- package/lib/guard-distrust.js +224 -0
- package/lib/guard-json.js +1 -1
- package/lib/http-transport.js +299 -36
- package/lib/jose.js +1 -1
- package/lib/lint.js +67 -7
- package/lib/ocsp-verify.js +9 -3
- package/lib/ocsp.js +8 -4
- package/lib/oid.js +4 -0
- package/lib/path-validate.js +112 -16
- package/lib/possession.js +6 -2
- package/lib/related-cert.js +2 -2
- package/lib/revocation-fetch.js +38 -9
- package/lib/scep.js +28 -13
- package/lib/schema-pkix.js +3 -3
- package/lib/sigstore.js +11 -6
- package/lib/smime.js +18 -13
- package/lib/tls-cert-compress.js +6 -5
- package/lib/trust.js +121 -20
- package/lib/trustanchor-build.js +16 -1
- package/lib/tsp-sign.js +7 -1
- package/lib/webauthn-mds.js +29 -8
- package/lib/webauthn.js +46 -14
- package/lib/x509-sign.js +8 -4
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/webauthn.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (c) blamejs contributors
|
|
3
3
|
"use strict";
|
|
4
|
+
// codebase-patterns:allow-file caller-option-presence-tested-against-undefined
|
|
5
|
+
/** @internal The `expected*` and `require*` options are SECURITY EXPECTATIONS, so presence
|
|
6
|
+
* is asked against `undefined` alone and a `null` is not read as an omission. A caller whose
|
|
7
|
+
* `expectedChallenge` arrives null, from an expired session or a missing field, must be
|
|
8
|
+
* refused rather than have the ceremony verified with the challenge comparison skipped:
|
|
9
|
+
* read as absence the check does not run, the verdict reports `checked.challenge` false, and
|
|
10
|
+
* the call still succeeds. `expectedTopOrigin: null` is a stated value on top of that, naming
|
|
11
|
+
* an unframed ceremony (WebAuthn sec. 5.8.1). */
|
|
4
12
|
/**
|
|
5
13
|
* @module pki.webauthn
|
|
6
14
|
* @nav Validation
|
|
@@ -413,7 +421,7 @@ function _aaguidIsSigned(fmt) { return _AAGUID_SIGNED_BY_FMT[fmt] !== false; }
|
|
|
413
421
|
var _VERIFY_OPTS = intrinsic.assign(intrinsic.create(null), {
|
|
414
422
|
time: 1, metadata: 1, tpmPolicy: 1, safetyNetRoots: 1, verifySafetyNetJws: 1, requireCtsProfileMatch: 1,
|
|
415
423
|
expectedRpId: 1, requireUserPresence: 1, requireUserVerification: 1, allowedAlgorithms: 1,
|
|
416
|
-
rootCertificates: 1,
|
|
424
|
+
rootCertificates: 1, distrustedKeys: 1,
|
|
417
425
|
clientDataJSON: 1, expectedChallenge: 1, expectedOrigin: 1, expectedTopOrigin: 1,
|
|
418
426
|
});
|
|
419
427
|
|
|
@@ -484,12 +492,12 @@ function _applyCallerRoots(res, supplied, vopts, onlyPaths) {
|
|
|
484
492
|
throw _err("webauthn/anchor-not-applicable",
|
|
485
493
|
"opts.rootCertificates was supplied, but this attestation carries no trust path to anchor (format '" + res.fmt + "')");
|
|
486
494
|
}
|
|
487
|
-
var at = vopts.time
|
|
495
|
+
var at = vopts.time != null ? vopts.time : new intrinsic.Date();
|
|
488
496
|
var anchoredAll = intrinsic.reduce(paths, function (p, info) {
|
|
489
497
|
return intrinsic.chain(p, function () {
|
|
490
|
-
var pathAt = vopts.time
|
|
498
|
+
var pathAt = vopts.time != null ? vopts.time : (info.at || at);
|
|
491
499
|
return mds.chainToAnchor(_reverse(intrinsic.copyList(info.tp)), roots, pathAt,
|
|
492
|
-
"attestation trust path (against the roots supplied as opts.rootCertificates)");
|
|
500
|
+
"attestation trust path (against the roots supplied as opts.rootCertificates)", undefined, vopts.distrustedKeys);
|
|
493
501
|
});
|
|
494
502
|
}, _promiseResolve(_Promise, undefined));
|
|
495
503
|
return intrinsic.chain(anchoredAll, function () {
|
|
@@ -559,6 +567,7 @@ function _applyBindings(authData, coseKey, opts) {
|
|
|
559
567
|
"signature made by this credential would use SHA-1, so it is refused unless opts.allowedAlgorithms " +
|
|
560
568
|
"names -65535 explicitly");
|
|
561
569
|
}
|
|
570
|
+
// allow:caller-option-presence-tested-against-undefined -- absence means every supported algorithm is accepted, so a null must be refused rather than drop the allowlist
|
|
562
571
|
if (opts.allowedAlgorithms !== undefined) {
|
|
563
572
|
/** @internal The entries are required to be the list's OWN. A hole answers from the prototype at
|
|
564
573
|
* that index, and the universal test skips it, so a sparse list with a number installed on
|
|
@@ -720,7 +729,7 @@ var VERIFIERS = intrinsic.assign(intrinsic.create(null), {
|
|
|
720
729
|
if (!intrinsic.isArray(roots) || roots.length === 0) {
|
|
721
730
|
throw _err("webauthn/safetynet-no-root", "verifying an android-safetynet attestation requires opts.safetyNetRoots -- the Google root(s) to anchor the x5c chain to; this library bundles none (WebAuthn 8.5)");
|
|
722
731
|
}
|
|
723
|
-
if (opts.time
|
|
732
|
+
if (opts.time != null) guard.time.assertValid(opts.time, _err, "webauthn/bad-input", "opts.time");
|
|
724
733
|
|
|
725
734
|
_requireAttShape(att.attStmt, ["ver", "response"], ["ver", "response"]);
|
|
726
735
|
_attRead(att.attStmt, "ver", cbor.read.textString, "a text string");
|
|
@@ -783,10 +792,10 @@ var VERIFIERS = intrinsic.assign(intrinsic.create(null), {
|
|
|
783
792
|
var jwsTrusted = intrinsic.chain(_verifySig(-257, sigBytes, leaf.subjectPublicKeyInfo.bytes,
|
|
784
793
|
intrinsic.bufferFrom(segs[0] + "." + segs[1], "ascii"), _err), function (ok) {
|
|
785
794
|
if (!ok) throw _err("webauthn/verify-failed", "the android-safetynet JWS signature does not verify under the x5c leaf key");
|
|
786
|
-
chainAt = opts.time
|
|
795
|
+
chainAt = opts.time != null ? opts.time
|
|
787
796
|
: (typeof payload.timestampMs === "number" && intrinsic.isFinite(payload.timestampMs) && payload.timestampMs > 0
|
|
788
797
|
? new intrinsic.Date(payload.timestampMs) : undefined);
|
|
789
|
-
return _safetyNetChainTrusted(chain, roots, chainAt);
|
|
798
|
+
return _safetyNetChainTrusted(chain, roots, chainAt, opts.distrustedKeys);
|
|
790
799
|
});
|
|
791
800
|
return intrinsic.chain(jwsTrusted, function () {
|
|
792
801
|
var res = guard.verdict.of(_result("android-safetynet", "Basic", chain, att), { safetyNet: signals });
|
|
@@ -879,7 +888,7 @@ function _safetyNetHostnameOk(leaf) {
|
|
|
879
888
|
});
|
|
880
889
|
}
|
|
881
890
|
|
|
882
|
-
function _safetyNetChainTrusted(chain, roots, time) {
|
|
891
|
+
function _safetyNetChainTrusted(chain, roots, time, distrusted) {
|
|
883
892
|
var anchors;
|
|
884
893
|
try {
|
|
885
894
|
anchors = intrinsic.mapList(roots, function (root, i) {
|
|
@@ -892,7 +901,7 @@ function _safetyNetChainTrusted(chain, roots, time) {
|
|
|
892
901
|
});
|
|
893
902
|
} catch (e) { return _promiseReject(_Promise, e); }
|
|
894
903
|
return mds.chainToAnchor(chain, anchors, time === undefined ? new intrinsic.Date() : time,
|
|
895
|
-
"android-safetynet x5c certificate chain", "webauthn/safetynet-cert-untrusted");
|
|
904
|
+
"android-safetynet x5c certificate chain", "webauthn/safetynet-cert-untrusted", distrusted);
|
|
896
905
|
}
|
|
897
906
|
|
|
898
907
|
function _result(fmt, attestationType, chain, att) {
|
|
@@ -1015,6 +1024,9 @@ function _checkAndroidKeyDescription(cert, clientDataHash) {
|
|
|
1015
1024
|
* allowedAlgorithms -- the COSE algorithms this relying party accepts
|
|
1016
1025
|
* metadata -- a verifyMetadataBlob result; the model's own roots govern
|
|
1017
1026
|
* rootCertificates -- trust anchors you pin, for the models no catalogue lists
|
|
1027
|
+
* distrustedKeys -- SHA-256 digests of SubjectPublicKeyInfo DER (64 hex characters
|
|
1028
|
+
* or 32 bytes); a listed key on a trust path refuses that path on
|
|
1029
|
+
* the metadata, rootCertificates and safetyNetRoots routes
|
|
1018
1030
|
* time -- the instant certificate validity is judged at
|
|
1019
1031
|
* tpmPolicy -- required TPM key properties; refuses a non-TPM attestation
|
|
1020
1032
|
* safetyNetRoots / verifySafetyNetJws / requireCtsProfileMatch -- the
|
|
@@ -1045,11 +1057,16 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
1045
1057
|
/** @internal A caller who passes no options gets a record with NO prototype. Defaulted to `{}` it
|
|
1046
1058
|
* inherits, so a property installed on `Object.prototype` reads as an option this call never set. */
|
|
1047
1059
|
opts = opts || intrinsic.create(null);
|
|
1060
|
+
var distrusted;
|
|
1048
1061
|
try {
|
|
1049
1062
|
if (!_isPlainObject(opts)) throw _err("webauthn/bad-input", "opts must be an object");
|
|
1050
1063
|
guard.identifier.assertKnownKeys(opts, _VERIFY_OPTS, _err, "webauthn/bad-input", "opts has an unknown key ");
|
|
1051
1064
|
opts = guard.identifier.ownOptions(opts);
|
|
1052
|
-
if (opts.time
|
|
1065
|
+
if (opts.time != null) guard.time.assertValid(opts.time, _err, "webauthn/bad-input", "opts.time");
|
|
1066
|
+
/** @internal Typed here, before any chain is walked, because the walk turns a validation
|
|
1067
|
+
* rejection into an untrusted verdict, and a malformed list must be reported as the caller's
|
|
1068
|
+
* input fault on every route. */
|
|
1069
|
+
distrusted = guard.distrust.keys(opts.distrustedKeys, _err, "webauthn/bad-input", "opts.distrustedKeys");
|
|
1053
1070
|
/** @internal The walk comes from the capture. Each pass of it is a REFUSAL of a mistyped option,
|
|
1054
1071
|
* and a mistyped one that survives is read for truthiness later where the rule wants exactly
|
|
1055
1072
|
* `true`, so a walk that visits nothing admits `"true"` as the opt-in. */
|
|
@@ -1061,7 +1078,7 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
1061
1078
|
var attBytes, cdh, clientData = null;
|
|
1062
1079
|
try {
|
|
1063
1080
|
attBytes = _bytesArg(attestationObject, "attestationObject");
|
|
1064
|
-
var haveJson = opts.clientDataJSON
|
|
1081
|
+
var haveJson = opts.clientDataJSON != null, haveHash = clientDataHash !== undefined;
|
|
1065
1082
|
if (haveJson === haveHash) {
|
|
1066
1083
|
throw _err("webauthn/bad-input", "verify takes exactly one of clientDataJSON or the clientDataHash argument");
|
|
1067
1084
|
}
|
|
@@ -1075,6 +1092,9 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
1075
1092
|
});
|
|
1076
1093
|
cdh = _sha("sha256", cdjBytes);
|
|
1077
1094
|
} else {
|
|
1095
|
+
/** @internal `expectedTopOrigin: null` states that the ceremony must be unframed, so
|
|
1096
|
+
* it is an expectation the caller supplied and counts as supplied here too. */
|
|
1097
|
+
// allow:caller-option-presence-tested-against-undefined
|
|
1078
1098
|
if (opts.expectedChallenge !== undefined || opts.expectedOrigin !== undefined ||
|
|
1079
1099
|
opts.expectedTopOrigin !== undefined) {
|
|
1080
1100
|
throw _err("webauthn/bad-input",
|
|
@@ -1093,6 +1113,7 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
1093
1113
|
}
|
|
1094
1114
|
var verifier = VERIFIERS[att.fmt];
|
|
1095
1115
|
if (!verifier) return _promiseReject(_Promise, _err("webauthn/unsupported-format", "attestation statement format '" + att.fmt + "' is not supported"));
|
|
1116
|
+
// allow:caller-option-presence-tested-against-undefined -- absence means no TPM policy is applied, so a null must be refused rather than drop it
|
|
1096
1117
|
if (opts.tpmPolicy !== undefined && !_formatCarries(att, "tpm")) {
|
|
1097
1118
|
return _promiseReject(_Promise, _err("webauthn/tpm-policy", "opts.tpmPolicy requires a TPM attestation, but this attestation is format '" + att.fmt + "', which carries no TPM public area"));
|
|
1098
1119
|
}
|
|
@@ -1105,6 +1126,7 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
1105
1126
|
vopts = guard.identifier.ownOptions(opts);
|
|
1106
1127
|
vopts.rootCertificates = _snapshotRoots(opts.rootCertificates);
|
|
1107
1128
|
vopts.safetyNetRoots = _snapshotRoots(opts.safetyNetRoots);
|
|
1129
|
+
vopts.distrustedKeys = distrusted;
|
|
1108
1130
|
} catch (e) { return _promiseReject(_Promise, e); }
|
|
1109
1131
|
var ranVerifier = intrinsic.chain(_promiseResolve(_Promise, undefined),
|
|
1110
1132
|
function () { return verifier(att, clientDataHash, vopts); });
|
|
@@ -1112,7 +1134,9 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
1112
1134
|
return guard.verdict.of(res, { clientData: clientData });
|
|
1113
1135
|
});
|
|
1114
1136
|
var anchoredRes = intrinsic.chain(withClientData, function (res) {
|
|
1137
|
+
// allow:caller-option-presence-tested-against-undefined -- a trust input: absence falls back to signature-only verification, so a null must be refused rather than skip anchoring
|
|
1115
1138
|
if (vopts.metadata === undefined) {
|
|
1139
|
+
// allow:caller-option-presence-tested-against-undefined -- a trust input: absence falls back to signature-only verification, so a null must be refused rather than skip anchoring
|
|
1116
1140
|
if (vopts.rootCertificates !== undefined) return _applyCallerRoots(res, vopts.rootCertificates, vopts);
|
|
1117
1141
|
var anchored = guard.verdict.of(res, { anchoredTo: _anchoredRoutes(res, null) });
|
|
1118
1142
|
var sn = _safetyNetAnchored(anchored);
|
|
@@ -1127,6 +1151,7 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
1127
1151
|
function () { return _applyMetadata(res, att, vopts); });
|
|
1128
1152
|
return intrinsic.chain(applyMd,
|
|
1129
1153
|
function (out) { return guard.verdict.of(out, { anchoredTo: _anchoredRoutes(out, "metadata") }); }, function (e) {
|
|
1154
|
+
// allow:caller-option-presence-tested-against-undefined -- a trust input: absence falls back to signature-only verification, so a null must be refused rather than skip anchoring
|
|
1130
1155
|
if (!e || e.code !== "webauthn/metadata-not-found" || vopts.rootCertificates === undefined) throw e;
|
|
1131
1156
|
return intrinsic.chain(_promiseResolve(_Promise, _applyCallerRoots(res, vopts.rootCertificates, vopts, e.missedPaths)),
|
|
1132
1157
|
function (withRoots) {
|
|
@@ -1186,7 +1211,7 @@ function _applyMetadata(res, att, opts) {
|
|
|
1186
1211
|
guard.list.append(applied, { entry: entry, anchors: anchors, identifier: identifier });
|
|
1187
1212
|
return { anchors: anchors, identifier: identifier };
|
|
1188
1213
|
}
|
|
1189
|
-
var at = opts.time
|
|
1214
|
+
var at = opts.time != null ? opts.time : new intrinsic.Date();
|
|
1190
1215
|
mds.assertFresh(md, at, "the metadata supplied as opts.metadata");
|
|
1191
1216
|
var governed = [];
|
|
1192
1217
|
var missed = null;
|
|
@@ -1207,9 +1232,9 @@ function _applyMetadata(res, att, opts) {
|
|
|
1207
1232
|
var governedAnchored = intrinsic.reduce(governed, function (p, item) {
|
|
1208
1233
|
return intrinsic.chain(p, function () {
|
|
1209
1234
|
var g = item.g, info = item.info;
|
|
1210
|
-
var pathAt = opts.time
|
|
1235
|
+
var pathAt = opts.time != null ? opts.time : (info.at || at);
|
|
1211
1236
|
return mds.chainToAnchor(_reverse(intrinsic.copyList(info.tp)), g.anchors, pathAt,
|
|
1212
|
-
"attestation trust path for " + g.identifier + " (against the roots its metadata entry registers)");
|
|
1237
|
+
"attestation trust path for " + g.identifier + " (against the roots its metadata entry registers)", undefined, opts.distrustedKeys);
|
|
1213
1238
|
});
|
|
1214
1239
|
}, _promiseResolve(_Promise, undefined));
|
|
1215
1240
|
return intrinsic.chain(governedAnchored,
|
|
@@ -1272,6 +1297,9 @@ void constants;
|
|
|
1272
1297
|
* @opts
|
|
1273
1298
|
* - `rootCertificates` -- REQUIRED. The trust anchors the BLOB's own signing chain
|
|
1274
1299
|
* must reach. A certificate, PEM, or DER bytes.
|
|
1300
|
+
* - `distrustedKeys` -- SHA-256 digests of SubjectPublicKeyInfo DER (64 hex characters
|
|
1301
|
+
* or 32 bytes). A listed key anywhere on the BLOB's signing chain, the root
|
|
1302
|
+
* included, refuses the BLOB as `webauthn/metadata-untrusted`.
|
|
1275
1303
|
* - `time` -- the instant freshness is judged at. Defaults to now.
|
|
1276
1304
|
* - `previousNo` -- the sequence number of the BLOB you already hold. A BLOB whose
|
|
1277
1305
|
* `no` is not greater is refused as a rollback.
|
|
@@ -1507,6 +1535,10 @@ function parseClientData(bytes, opts) {
|
|
|
1507
1535
|
}
|
|
1508
1536
|
checked.origin = true;
|
|
1509
1537
|
}
|
|
1538
|
+
/** @internal `null` is a VALUE here, not absence: it states that the ceremony must be
|
|
1539
|
+
* unframed, which the branch below checks. So presence is asked the one way that keeps
|
|
1540
|
+
* it, and an option left out is the only form that skips the check. */
|
|
1541
|
+
// allow:caller-option-presence-tested-against-undefined -- see above
|
|
1510
1542
|
if (opts.expectedTopOrigin !== undefined) {
|
|
1511
1543
|
var wantTop = opts.expectedTopOrigin;
|
|
1512
1544
|
var framed = doc.crossOrigin === true;
|
package/lib/x509-sign.js
CHANGED
|
@@ -971,14 +971,18 @@ function _extensionRow(name) {
|
|
|
971
971
|
if (oid.isDottedDecimal(name)) return intrinsic.hasOwn(EXTENSION_BY_OID, name) ? EXTENSION_BY_OID[name] : null;
|
|
972
972
|
throw _err("x509/bad-input", "unknown extension " + guard.text.showValue(name) + "; the names spec.extensions takes are accepted, and any other extension is named by its dotted OID with its extnValue pre-encoded");
|
|
973
973
|
}
|
|
974
|
+
/** @internal `stated` is the caller's option, carried here from the door that typed it,
|
|
975
|
+
* so absence has the same two forms there and here: a `null` states no criticality and
|
|
976
|
+
* takes the row's default, the way an omitted option does. Read as stated, it was
|
|
977
|
+
* returned AS the criticality and reached the emitter as a non-boolean. */
|
|
974
978
|
function _extensionCritical(row, opts) {
|
|
975
979
|
var rule = row.rule, stated = opts.critical;
|
|
976
980
|
if (rule.inValue) {
|
|
977
|
-
if (stated
|
|
981
|
+
if (stated != null) throw _err("x509/bad-input", row.name + " carries its criticality in the value ({ cA, pathLen, critical }), so opts.critical does not apply");
|
|
978
982
|
return null;
|
|
979
983
|
}
|
|
980
|
-
if (rule.knob) return stated
|
|
981
|
-
if (stated
|
|
984
|
+
if (rule.knob) return stated == null ? rule.dflt : stated;
|
|
985
|
+
if (stated != null && stated !== rule.fixed) {
|
|
982
986
|
throw _err("x509/bad-input", row.name + " is emitted " + (rule.fixed ? "critical" : "non-critical") + " (" + rule.cite + "), so opts.critical cannot say otherwise");
|
|
983
987
|
}
|
|
984
988
|
return rule.fixed;
|
|
@@ -1037,7 +1041,7 @@ function extension(name, value, opts) {
|
|
|
1037
1041
|
function _extension(name, value, opts) {
|
|
1038
1042
|
opts = guard.identifier.optionsObject(opts, _err, "x509/bad-input", "pki.x509.extension options");
|
|
1039
1043
|
guard.identifier.assertKnownKeys(opts, KNOWN_EXTENSION_OPTS, _err, "x509/bad-input", "pki.x509.extension has an unknown option ");
|
|
1040
|
-
if (opts.critical
|
|
1044
|
+
if (opts.critical != null && typeof opts.critical !== "boolean") throw _err("x509/bad-input", "opts.critical must be a boolean");
|
|
1041
1045
|
if (value === undefined || value === null) throw _err("x509/bad-input", "the extension value is required");
|
|
1042
1046
|
var row = _extensionRow(name);
|
|
1043
1047
|
if (row === null) {
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:114691ca-67be-44e1-9f29-b49116312f56",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-21T08:20:31.429Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/pki@0.9.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.9.6",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.9.
|
|
25
|
+
"version": "0.9.6",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/pki@0.9.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.9.6",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/pki@0.9.
|
|
57
|
+
"ref": "@blamejs/pki@0.9.6",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|