@blamejs/pki 0.5.30 → 0.5.32
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 +28 -0
- package/README.md +2 -2
- package/lib/acme.js +347 -90
- package/lib/asn1-der.js +97 -33
- package/lib/attrcert-sign.js +1 -1
- package/lib/cmc-build.js +19 -8
- package/lib/cmc-verify.js +4 -4
- package/lib/cmp-build.js +36 -6
- package/lib/cmp-session.js +5 -5
- package/lib/cmp-verify.js +86 -16
- package/lib/cms-compress.js +2 -2
- package/lib/cms-decrypt.js +3 -3
- package/lib/cms-encrypt.js +1 -1
- package/lib/cms-sign.js +7 -7
- package/lib/cms-verify.js +9 -4
- package/lib/crl-sign.js +5 -5
- package/lib/crmf-sign.js +2 -1
- package/lib/csr-sign.js +1 -4
- package/lib/ct.js +8 -4
- package/lib/est.js +234 -34
- package/lib/framework-error.js +20 -6
- package/lib/guard-bytes.js +24 -3
- package/lib/http-digest.js +69 -16
- package/lib/http-retry-after.js +68 -24
- package/lib/http-transport.js +60 -6
- package/lib/inspect.js +13 -2
- package/lib/ip-utils.js +102 -25
- package/lib/key.js +2 -2
- package/lib/lint.js +1 -1
- package/lib/mime.js +27 -4
- package/lib/ocsp.js +7 -7
- package/lib/oid.js +16 -0
- package/lib/path-validate.js +76 -18
- package/lib/pbes2.js +2 -2
- package/lib/pkcs12-build.js +2 -2
- package/lib/pki-build.js +320 -21
- package/lib/rfc3339.js +75 -17
- package/lib/schema-c509.js +36 -9
- package/lib/schema-cmc.js +3 -3
- package/lib/schema-pkix.js +174 -25
- package/lib/schema-tsp.js +10 -1
- package/lib/sign-scheme.js +27 -7
- package/lib/sigstore.js +52 -9
- package/lib/smime.js +86 -6
- package/lib/trust.js +114 -14
- package/lib/tsp-sign.js +39 -27
- package/lib/validator-tpm.js +2 -2
- package/lib/webauthn-mds.js +24 -6
- package/lib/webauthn.js +3 -3
- package/lib/webcrypto.js +1 -1
- package/lib/x509-sign.js +9 -5
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/trust.js
CHANGED
|
@@ -44,7 +44,10 @@ var intrinsic = require("./guard-intrinsic");
|
|
|
44
44
|
// Own-key membership through an operation taken at load. Written out, the question reads two
|
|
45
45
|
// replaceable properties to answer one thing, and here it decides which trust bits an anchor carries.
|
|
46
46
|
var _hasOwn = intrinsic.hasOwn;
|
|
47
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
48
|
+
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
47
49
|
var x509 = require("./schema-x509");
|
|
50
|
+
var pkix = require("./schema-pkix");
|
|
48
51
|
|
|
49
52
|
var TrustError = errors.TrustError;
|
|
50
53
|
var LIMITS = constants.LIMITS;
|
|
@@ -75,7 +78,104 @@ var CKT_DELEGATOR = "CKT_NSS_TRUSTED_DELEGATOR";
|
|
|
75
78
|
// An attribute line: `CKA_<NAME> <type> [<value>]`. The value grammar is
|
|
76
79
|
// per-type (a quoted UTF8 string, a single token, or nothing for
|
|
77
80
|
// MULTILINE_OCTAL whose payload follows on its own lines).
|
|
78
|
-
|
|
81
|
+
// ---- certdata scanners by character (no regex) ----------------------------------------------------
|
|
82
|
+
function _isNameChar(c) { return (c >= 65 && c <= 90) || (c >= 48 && c <= 57) || c === 95; } // A-Z 0-9 _
|
|
83
|
+
function _isDigitCode(c) { return c >= 48 && c <= 57; }
|
|
84
|
+
function _isDateSepCode(c) { return c === 46 || c === 47 || c === 45; } // . / -
|
|
85
|
+
function _isOctalDigit(c) { return c >= 48 && c <= 55; } // 0-7
|
|
86
|
+
function _isWsCode(c) { return pkix.isJsWhitespace(c); }
|
|
87
|
+
function _containsWs(s) { for (var i = 0; i < s.length; i++) { if (_isWsCode(_charCodeAt(s, i))) return true; } return false; } // /\s/.test(s)
|
|
88
|
+
|
|
89
|
+
// An attribute line `CKA_<NAME> [<type> [<value>]]`, matching
|
|
90
|
+
// /^(CKA_[A-Z0-9_]+)(?:\s+(\S+)(?:\s+(.*))?)?$/. Returns [name, type|undefined, value|undefined] or null.
|
|
91
|
+
function _parseAttrLine(s) {
|
|
92
|
+
var n = s.length;
|
|
93
|
+
if (_strSlice(s, 0, 4) !== "CKA_") return null;
|
|
94
|
+
var p = 4;
|
|
95
|
+
while (p < n && _isNameChar(_charCodeAt(s, p))) p += 1;
|
|
96
|
+
if (p === 4) return null;
|
|
97
|
+
var name = _strSlice(s, 0, p);
|
|
98
|
+
if (p === n) return [name, undefined, undefined];
|
|
99
|
+
if (!_isWsCode(_charCodeAt(s, p))) return null;
|
|
100
|
+
while (p < n && _isWsCode(_charCodeAt(s, p))) p += 1;
|
|
101
|
+
var typeStart = p;
|
|
102
|
+
while (p < n && !_isWsCode(_charCodeAt(s, p))) p += 1;
|
|
103
|
+
if (p === typeStart) return null;
|
|
104
|
+
var type = _strSlice(s, typeStart, p);
|
|
105
|
+
if (p === n) return [name, type, undefined];
|
|
106
|
+
if (!_isWsCode(_charCodeAt(s, p))) return null;
|
|
107
|
+
while (p < n && _isWsCode(_charCodeAt(s, p))) p += 1;
|
|
108
|
+
return [name, type, _strSlice(s, p)];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// A single double-quoted value with no interior quote (matching /^"([^"]*)"$/): the inner content, or
|
|
112
|
+
// null. Empty content ("\"\"") returns "" (a valid value), distinct from the null no-match.
|
|
113
|
+
function _unquote(s) {
|
|
114
|
+
var n = s.length;
|
|
115
|
+
if (n < 2 || _charCodeAt(s, 0) !== 34 || _charCodeAt(s, n - 1) !== 34) return null; // surrounding "
|
|
116
|
+
for (var i = 1; i < n - 1; i++) if (_charCodeAt(s, i) === 34) return null; // no interior "
|
|
117
|
+
return _strSlice(s, 1, n - 1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// String.prototype.split(/\s+/): the pieces between whitespace runs (a leading/trailing run yields a
|
|
121
|
+
// "" element; "" -> [""]). Index assignment (not .push) so the scanner adds no live intrinsic read.
|
|
122
|
+
function _splitWhitespace(s) {
|
|
123
|
+
var out = [], i = 0, n = s.length, pieceStart = 0;
|
|
124
|
+
while (i < n) {
|
|
125
|
+
if (_isWsCode(_charCodeAt(s, i))) {
|
|
126
|
+
out[out.length] = _strSlice(s, pieceStart, i);
|
|
127
|
+
while (i < n && _isWsCode(_charCodeAt(s, i))) i += 1;
|
|
128
|
+
pieceStart = i;
|
|
129
|
+
} else i += 1;
|
|
130
|
+
}
|
|
131
|
+
out[out.length] = _strSlice(s, pieceStart);
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Exactly three octal digits (matching /^[0-7]{3}$/).
|
|
136
|
+
function _isOctal3(d) {
|
|
137
|
+
if (d.length !== 3) return false;
|
|
138
|
+
for (var i = 0; i < 3; i++) { if (!_isOctalDigit(_charCodeAt(d, i))) return false; }
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// String.prototype.split(/\r\n|\n|\r/): split on CRLF, LF, or CR line terminators.
|
|
143
|
+
function _splitLines(text) {
|
|
144
|
+
var out = [], start = 0, i = 0, n = text.length;
|
|
145
|
+
while (i < n) {
|
|
146
|
+
var c = _charCodeAt(text, i);
|
|
147
|
+
if (c === 13) { // CR (consume a following LF as one)
|
|
148
|
+
out[out.length] = _strSlice(text, start, i);
|
|
149
|
+
if (i + 1 < n && _charCodeAt(text, i + 1) === 10) i += 1;
|
|
150
|
+
i += 1; start = i;
|
|
151
|
+
} else if (c === 10) { // LF
|
|
152
|
+
out[out.length] = _strSlice(text, start, i);
|
|
153
|
+
i += 1; start = i;
|
|
154
|
+
} else i += 1;
|
|
155
|
+
}
|
|
156
|
+
out[out.length] = _strSlice(text, start);
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// A Y-M-D date with dotted/dashed/slashed separators, matching /^(\d{4})[./-](\d{1,2})[./-](\d{1,2})$/.
|
|
161
|
+
// Returns [year, month, day] (raw digit strings) or null.
|
|
162
|
+
function _parseYmd(s) {
|
|
163
|
+
var n = s.length;
|
|
164
|
+
for (var a = 0; a < 4; a++) if (!_isDigitCode(_charCodeAt(s, a))) return null;
|
|
165
|
+
var year = _strSlice(s, 0, 4), p = 4;
|
|
166
|
+
if (!_isDateSepCode(_charCodeAt(s, p))) return null; p += 1;
|
|
167
|
+
var ms = p;
|
|
168
|
+
while (p < n && p - ms < 2 && _isDigitCode(_charCodeAt(s, p))) p += 1;
|
|
169
|
+
if (p === ms) return null;
|
|
170
|
+
var month = _strSlice(s, ms, p);
|
|
171
|
+
if (!_isDateSepCode(_charCodeAt(s, p))) return null; p += 1;
|
|
172
|
+
var ds = p;
|
|
173
|
+
while (p < n && p - ds < 2 && _isDigitCode(_charCodeAt(s, p))) p += 1;
|
|
174
|
+
if (p === ds) return null;
|
|
175
|
+
var day = _strSlice(s, ds, p);
|
|
176
|
+
if (p !== n) return null;
|
|
177
|
+
return [year, month, day];
|
|
178
|
+
}
|
|
79
179
|
|
|
80
180
|
// ---------------------------------------------------------------------------
|
|
81
181
|
// certdata.txt lexer -- line-oriented, C.LIMITS-bounded, fail-closed
|
|
@@ -93,7 +193,7 @@ function _readOctalBody(lines, idx) {
|
|
|
93
193
|
var t = lines[i].trim();
|
|
94
194
|
if (t === "END") return { bytes: Buffer.from(out), next: i + 1 };
|
|
95
195
|
if (t === "") throw E("trust/bad-octal", "MULTILINE_OCTAL body interrupted by a blank line before END");
|
|
96
|
-
var chunks = t
|
|
196
|
+
var chunks = _splitWhitespace(t);
|
|
97
197
|
for (var c = 0; c < chunks.length; c++) {
|
|
98
198
|
var chunk = chunks[c];
|
|
99
199
|
for (var k = 0; k < chunk.length; k += 4) {
|
|
@@ -101,7 +201,7 @@ function _readOctalBody(lines, idx) {
|
|
|
101
201
|
throw E("trust/bad-octal", "MULTILINE_OCTAL expects \\ooo escapes, got " + JSON.stringify(chunk.slice(k, k + 4)));
|
|
102
202
|
}
|
|
103
203
|
var d = chunk.slice(k + 1, k + 4);
|
|
104
|
-
if (
|
|
204
|
+
if (!_isOctal3(d)) {
|
|
105
205
|
throw E("trust/bad-octal", "a MULTILINE_OCTAL escape is a backslash + exactly three octal digits, got " + JSON.stringify("\\" + d));
|
|
106
206
|
}
|
|
107
207
|
if (d.charCodeAt(0) > 51 /* '3' */) {
|
|
@@ -120,7 +220,7 @@ function _readOctalBody(lines, idx) {
|
|
|
120
220
|
// `CKA_* <type> <value>` attribute lines. Returns [{ attrs }] where attrs maps
|
|
121
221
|
// the attribute name to { type, value | bytes }.
|
|
122
222
|
function _lexObjects(text) {
|
|
123
|
-
var lines = text
|
|
223
|
+
var lines = _splitLines(text);
|
|
124
224
|
var n = lines.length;
|
|
125
225
|
var i = 0, sawBegin = false;
|
|
126
226
|
for (; i < n; i++) {
|
|
@@ -137,10 +237,10 @@ function _lexObjects(text) {
|
|
|
137
237
|
i++; continue;
|
|
138
238
|
}
|
|
139
239
|
if (t.charAt(0) === "#") { i++; continue; }
|
|
140
|
-
var m =
|
|
240
|
+
var m = _parseAttrLine(t);
|
|
141
241
|
if (!m) throw E("trust/bad-block", "unexpected line in the certdata object stream: " + JSON.stringify(t.slice(0, 64)));
|
|
142
|
-
var name = m[
|
|
143
|
-
var rawVal = m[
|
|
242
|
+
var name = m[0], type = m[1];
|
|
243
|
+
var rawVal = m[2] !== undefined ? m[2].trim() : undefined;
|
|
144
244
|
if (!type) throw E("trust/bad-block", "attribute line is missing its type token: " + name);
|
|
145
245
|
if (!cur) {
|
|
146
246
|
// Refuse the block bomb before lexing another block's payloads.
|
|
@@ -161,15 +261,15 @@ function _lexObjects(text) {
|
|
|
161
261
|
if (rawVal !== "CK_TRUE" && rawVal !== "CK_FALSE") throw E("trust/bad-block", name + " CK_BBOOL must be CK_TRUE or CK_FALSE");
|
|
162
262
|
entry = { type: type, value: rawVal === "CK_TRUE" };
|
|
163
263
|
} else if (type === "UTF8") {
|
|
164
|
-
var q = rawVal !== undefined ?
|
|
165
|
-
if (
|
|
166
|
-
entry = { type: type, value: q
|
|
264
|
+
var q = rawVal !== undefined ? _unquote(rawVal) : null;
|
|
265
|
+
if (q === null) throw E("trust/bad-block", name + " UTF8 must carry one double-quoted value");
|
|
266
|
+
entry = { type: type, value: q };
|
|
167
267
|
} else if (type === "CK_TRUST") {
|
|
168
|
-
if (rawVal === undefined ||
|
|
268
|
+
if (rawVal === undefined || _containsWs(rawVal)) throw E("trust/bad-block", name + " CK_TRUST must carry a single token value");
|
|
169
269
|
if (!CKT_RECOGNIZED[rawVal]) throw E("trust/bad-trust-value", "unrecognized CK_TRUST value " + JSON.stringify(rawVal) + " on " + name);
|
|
170
270
|
entry = { type: type, value: rawVal };
|
|
171
271
|
} else if (type === "CK_OBJECT_CLASS" || type === "CK_CERTIFICATE_TYPE") {
|
|
172
|
-
if (rawVal === undefined ||
|
|
272
|
+
if (rawVal === undefined || _containsWs(rawVal)) throw E("trust/bad-block", name + " " + type + " must carry a single token value");
|
|
173
273
|
entry = { type: type, value: rawVal };
|
|
174
274
|
} else {
|
|
175
275
|
// An unknown value type cannot be lexed safely (its value grammar is
|
|
@@ -625,10 +725,10 @@ function _trustBits(cell) {
|
|
|
625
725
|
function _csvDate(cell, label) {
|
|
626
726
|
var s = String(cell).trim();
|
|
627
727
|
if (s === "") return null;
|
|
628
|
-
var m =
|
|
728
|
+
var m = _parseYmd(s);
|
|
629
729
|
if (!m) throw E("trust/bad-csv", label + " must be a Y-M-D date, got " + JSON.stringify(s));
|
|
630
730
|
var pad = function (x) { return x.length === 1 ? "0" + x : x; };
|
|
631
|
-
var payload = Buffer.from(m[
|
|
731
|
+
var payload = Buffer.from(m[0] + pad(m[1]) + pad(m[2]) + "235959Z", "latin1");
|
|
632
732
|
// _strictTime tags every rejection (a rolled-over month/day among them) with the code passed here,
|
|
633
733
|
// so the re-throw carries it unchanged; the wrap normalizes any other failure to the same typed code
|
|
634
734
|
// so a bad row is always trust/bad-csv, never an untyped exception escaping parseCcadbCsv.
|
package/lib/tsp-sign.js
CHANGED
|
@@ -26,6 +26,7 @@ var smime = require("./schema-smime");
|
|
|
26
26
|
var schemaTsp = require("./schema-tsp");
|
|
27
27
|
var guard = require("./guard-all");
|
|
28
28
|
var intrinsic = require("./guard-intrinsic");
|
|
29
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
29
30
|
var frameworkError = require("./framework-error");
|
|
30
31
|
|
|
31
32
|
var pkix = require("./schema-pkix");
|
|
@@ -34,6 +35,12 @@ var TspError = frameworkError.TspError;
|
|
|
34
35
|
var _NS = pkix.makeNS("tsp", TspError, oid);
|
|
35
36
|
var b = asn1.build;
|
|
36
37
|
function _err(code, message, cause) { return new TspError(code, message, cause); }
|
|
38
|
+
|
|
39
|
+
// Any ASCII digit 1-9 present (a non-zero fractional digit), by character scan -- no regex.
|
|
40
|
+
function _hasNonZeroDigit(s) {
|
|
41
|
+
for (var i = 0; i < s.length; i++) { var c = _charCodeAt(s, i); if (c >= 49 && c <= 57) return true; }
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
37
44
|
function O(name) { return oid.byName(name); }
|
|
38
45
|
|
|
39
46
|
// The shared authoring-side builder, for the same pre-encoded-Extension validation every other
|
|
@@ -72,17 +79,20 @@ function _hashAlgId(name) {
|
|
|
72
79
|
// A policy identifier: an OID name or a dotted-decimal string.
|
|
73
80
|
function _policy(p) {
|
|
74
81
|
if (typeof p !== "string") throw _err("tsp/bad-input", "the timestamp policy must be an OID name or dotted string");
|
|
75
|
-
return
|
|
82
|
+
return oid.isDottedDecimal(p) ? b.oid(p) : b.oid(O(p));
|
|
76
83
|
}
|
|
77
|
-
// The signer certificate DER (DER
|
|
84
|
+
// The signer certificate DER (a DER BufferSource / PEM string) -- for the ESSCertIDv2 hash.
|
|
78
85
|
function _certDer(c) {
|
|
79
86
|
if (c == null) throw _err("tsp/bad-input", "the TSA signer requires a certificate (cert)");
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
if (guard.bytes.isByteSource(c)) { // any BufferSource -> a byte view
|
|
88
|
+
var buf = guard.bytes.source(c, TspError, "tsp/bad-input", "the TSA certificate");
|
|
89
|
+
if (buf[0] === 0x30) return buf; // DER as-is
|
|
90
|
+
c = buf.toString("latin1"); // else decode as PEM
|
|
91
|
+
}
|
|
92
|
+
if (typeof c !== "string") throw _err("tsp/bad-input", "the TSA certificate must be a DER BufferSource or a PEM string");
|
|
93
|
+
var der = pkix.pemDecodeLenient(c, "CERTIFICATE");
|
|
94
|
+
if (der === null) throw _err("tsp/bad-input", "the TSA certificate PEM is not a CERTIFICATE block");
|
|
95
|
+
return der;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
// The RFC 5035 SigningCertificateV2 signed-attribute value binding the token to the TSA cert:
|
|
@@ -177,8 +187,9 @@ function _sign(messageImprint, tsa, opts) {
|
|
|
177
187
|
}
|
|
178
188
|
var mi = messageImprint || {};
|
|
179
189
|
if (!mi.hashAlgorithm || !NODE_DIGEST[mi.hashAlgorithm]) throw _err("tsp/unsupported-algorithm", "messageImprint.hashAlgorithm must be a supported hash name");
|
|
180
|
-
if (!
|
|
181
|
-
|
|
190
|
+
if (!guard.bytes.isByteSource(mi.hashedMessage)) throw _err("tsp/bad-input", "messageImprint.hashedMessage must be a BufferSource");
|
|
191
|
+
var _signHmLen = guard.bytes.source(mi.hashedMessage, TspError, "tsp/bad-input", "messageImprint.hashedMessage").length;
|
|
192
|
+
if (_signHmLen !== HASH_LEN[mi.hashAlgorithm]) throw _err("tsp/bad-input", "messageImprint.hashedMessage length (" + _signHmLen + ") does not match the " + mi.hashAlgorithm + " digest length (" + HASH_LEN[mi.hashAlgorithm] + ")");
|
|
182
193
|
if (!opts.policy) throw _err("tsp/bad-input", "a timestamp token requires a policy identifier (opts.policy)");
|
|
183
194
|
if (opts.serialNumber == null) throw _err("tsp/bad-input", "a timestamp token requires a serialNumber (opts.serialNumber)");
|
|
184
195
|
|
|
@@ -188,7 +199,7 @@ function _sign(messageImprint, tsa, opts) {
|
|
|
188
199
|
|
|
189
200
|
// TSTInfo (RFC 3161 sec. 2.4.2): version(1), policy, messageImprint, serialNumber, genTime,
|
|
190
201
|
// then the OPTIONAL accuracy / ordering / nonce in ascending order.
|
|
191
|
-
var imprint = b.sequence([_hashAlgId(mi.hashAlgorithm), b.octetString(
|
|
202
|
+
var imprint = b.sequence([_hashAlgId(mi.hashAlgorithm), b.octetString(guard.bytes.source(mi.hashedMessage, TspError, "tsp/bad-input", "messageImprint.hashedMessage"))]);
|
|
192
203
|
// genTime defaults to now; a supplied value MUST be a valid Date (never a silently-ignored
|
|
193
204
|
// non-Date or an Invalid Date that would encode a garbage GeneralizedTime).
|
|
194
205
|
if (opts.genTime != null) guard.time.assertValid(opts.genTime, _err, "tsp/bad-input", "genTime");
|
|
@@ -270,16 +281,16 @@ function _failInfoBits(names) {
|
|
|
270
281
|
// Coerce a timeStampToken (a CMS ContentInfo the token producer emits) to DER for embedding in a
|
|
271
282
|
// TimeStampResp -- a DER Buffer as-is, a Uint8Array copied, or a PEM string de-armored.
|
|
272
283
|
function _tokenDer(t) {
|
|
273
|
-
if (
|
|
274
|
-
if (t instanceof Uint8Array && !Buffer.isBuffer(t)) { var u = Buffer.from(t); if (u[0] === 0x30) return u; }
|
|
284
|
+
if (guard.bytes.isByteSource(t)) { var u = guard.bytes.source(t, TspError, "tsp/bad-input", "the timeStampToken"); if (u[0] === 0x30) return u; }
|
|
275
285
|
if (typeof t === "string") return schemaTsp.pemDecode(t);
|
|
276
|
-
throw _err("tsp/bad-input", "the timeStampToken must be a DER
|
|
286
|
+
throw _err("tsp/bad-input", "the timeStampToken must be a DER BufferSource or a PEM string");
|
|
277
287
|
}
|
|
278
288
|
|
|
279
289
|
function _assertImprint(mi) {
|
|
280
290
|
if (!mi.hashAlgorithm || !NODE_DIGEST[mi.hashAlgorithm]) throw _err("tsp/unsupported-algorithm", "messageImprint.hashAlgorithm must be a supported hash name");
|
|
281
|
-
if (!
|
|
282
|
-
|
|
291
|
+
if (!guard.bytes.isByteSource(mi.hashedMessage)) throw _err("tsp/bad-input", "messageImprint.hashedMessage must be a BufferSource");
|
|
292
|
+
var hmLen = guard.bytes.source(mi.hashedMessage, TspError, "tsp/bad-input", "messageImprint.hashedMessage").length;
|
|
293
|
+
if (hmLen !== HASH_LEN[mi.hashAlgorithm]) throw _err("tsp/bad-input", "messageImprint.hashedMessage length (" + hmLen + ") does not match the " + mi.hashAlgorithm + " digest length (" + HASH_LEN[mi.hashAlgorithm] + ")");
|
|
283
294
|
}
|
|
284
295
|
|
|
285
296
|
/**
|
|
@@ -328,7 +339,7 @@ function request(messageImprint, opts) {
|
|
|
328
339
|
});
|
|
329
340
|
}
|
|
330
341
|
_assertImprint(mi);
|
|
331
|
-
var imprint = b.sequence([_hashAlgId(mi.hashAlgorithm), b.octetString(
|
|
342
|
+
var imprint = b.sequence([_hashAlgId(mi.hashAlgorithm), b.octetString(guard.bytes.source(mi.hashedMessage, TspError, "tsp/bad-input", "messageImprint.hashedMessage"))]);
|
|
332
343
|
// TimeStampReq: version(1), messageImprint, then OPTIONAL reqPolicy / nonce / certReq /
|
|
333
344
|
// extensions in schema order. certReq DEFAULT FALSE -> only an explicit TRUE is encoded (DER).
|
|
334
345
|
var fields = [b.integer(1n), imprint];
|
|
@@ -337,7 +348,7 @@ function request(messageImprint, opts) {
|
|
|
337
348
|
if (opts.certReq != null && typeof opts.certReq !== "boolean") throw _err("tsp/bad-input", "certReq must be a boolean");
|
|
338
349
|
if (opts.certReq === true) fields.push(b.boolean(true));
|
|
339
350
|
if (opts.extensions != null) {
|
|
340
|
-
if (!Array.isArray(opts.extensions) || !guard.list.allMatch(opts.extensions, function (e) { return
|
|
351
|
+
if (!Array.isArray(opts.extensions) || !guard.list.allMatch(opts.extensions, function (e) { return guard.bytes.isByteSource(e); })) throw _err("tsp/bad-input", "extensions must be an array of encoded Extension DER buffers");
|
|
341
352
|
// Each pre-encoded Extension is validated as one, and its extnID must not repeat -- the same
|
|
342
353
|
// gate every other request builder applies. Spliced in unchecked, a caller relaying a blob it
|
|
343
354
|
// did not author put fully chosen bytes inside [0] and this encoder emitted DER its own
|
|
@@ -345,7 +356,7 @@ function request(messageImprint, opts) {
|
|
|
345
356
|
// the DER DEFAULT rule forbids.
|
|
346
357
|
var seenExt = {};
|
|
347
358
|
var encoded = opts.extensions.map(function (e, i) {
|
|
348
|
-
var der =
|
|
359
|
+
var der = guard.bytes.source(e, TspError, "tsp/bad-input", "an extension");
|
|
349
360
|
_b.assertValidExtension(der, i);
|
|
350
361
|
var extnId = asn1.read.oid(asn1.decode(der).children[0]);
|
|
351
362
|
if (seenExt[extnId]) throw _err("tsp/bad-input", "duplicate request extension " + extnId + " (RFC 5280 sec. 4.2)");
|
|
@@ -516,14 +527,15 @@ function _imprintMatches(mi, data) {
|
|
|
516
527
|
// matching short precomputed imprint must never make it verify (RFC 3161 sec. 2.4.1).
|
|
517
528
|
if (mi.hashedMessage.length !== wantLen) return "tsp/imprint-mismatch";
|
|
518
529
|
var actual;
|
|
519
|
-
if (
|
|
520
|
-
actual = nodeCrypto.createHash(nodeAlg).update(
|
|
521
|
-
} else if (data && typeof data === "object" &&
|
|
530
|
+
if (guard.bytes.isByteSource(data)) {
|
|
531
|
+
actual = nodeCrypto.createHash(nodeAlg).update(guard.bytes.source(data, TspError, "tsp/bad-input", "data")).digest();
|
|
532
|
+
} else if (data && typeof data === "object" && guard.bytes.isByteSource(data.hashedMessage)) {
|
|
522
533
|
if (data.hashAlgorithm !== name) return "tsp/imprint-mismatch"; // a precomputed imprint under a different algorithm
|
|
523
534
|
// a caller-supplied precomputed imprint of the wrong length is a config error (the sign path
|
|
524
535
|
// rejects it identically) -- throw rather than silently mismatch (three-tier: caller input).
|
|
525
|
-
|
|
526
|
-
|
|
536
|
+
var dhmLen = guard.bytes.source(data.hashedMessage, TspError, "tsp/bad-input", "precomputed imprint hashedMessage").length;
|
|
537
|
+
if (dhmLen !== wantLen) throw _err("tsp/bad-input", "precomputed imprint hashedMessage length (" + dhmLen + ") does not match the " + name + " digest length (" + wantLen + ")");
|
|
538
|
+
actual = guard.bytes.source(data.hashedMessage, TspError, "tsp/bad-input", "precomputed imprint hashedMessage");
|
|
527
539
|
} else {
|
|
528
540
|
throw _err("tsp/bad-input", "pki.tsp.verify data must be a Buffer or a { hashAlgorithm, hashedMessage } imprint");
|
|
529
541
|
}
|
|
@@ -749,7 +761,7 @@ async function verify(token, data, opts) {
|
|
|
749
761
|
// nothing to notice it by. Naming the difference at the boundary is the only place it is cheap.
|
|
750
762
|
guard.identifier.assertKnownKeys(opts, _VERIFY_OPTS, _err, "tsp/bad-input",
|
|
751
763
|
"pki.tsp.verify has an unknown option (note the anchor option here is `trustAnchor`, singular, an anchor tuple -- not the `trustAnchors` certificate list pki.cms.verify takes) ");
|
|
752
|
-
if (opts.certs != null && (!Array.isArray(opts.certs) || !guard.list.allMatch(opts.certs, function (c) { return
|
|
764
|
+
if (opts.certs != null && (!Array.isArray(opts.certs) || !guard.list.allMatch(opts.certs, function (c) { return guard.bytes.isByteSource(c); }))) {
|
|
753
765
|
throw _err("tsp/bad-input", "pki.tsp.verify opts.certs must be an array of DER certificate Buffers"); // a bad element is a caller error, never silently dropped
|
|
754
766
|
}
|
|
755
767
|
var tokenDer = _tokenDer(token); // DER Buffer / PEM -> DER; a non-token input throws tsp/bad-input
|
|
@@ -790,7 +802,7 @@ async function verify(token, data, opts) {
|
|
|
790
802
|
}
|
|
791
803
|
// M15 -- if the requested policy is supplied, the token's policy MUST equal it.
|
|
792
804
|
if (opts.reqPolicy != null) {
|
|
793
|
-
var wantPolicy =
|
|
805
|
+
var wantPolicy = oid.isDottedDecimal(opts.reqPolicy) ? opts.reqPolicy : O(opts.reqPolicy);
|
|
794
806
|
if (tst.policy !== wantPolicy) return fail("tsp/policy-mismatch");
|
|
795
807
|
}
|
|
796
808
|
// A TSTInfo carrying a CRITICAL extension the verifier cannot process is unusable: RFC 3161 sec.
|
|
@@ -835,7 +847,7 @@ async function verify(token, data, opts) {
|
|
|
835
847
|
// notBefore is rejected at the floor (ceil >= notBefore but floor < notBefore). Certificate
|
|
836
848
|
// validity is seconds-precise (RFC 5280 sec. 4.1.2.5), so this never over-rejects a valid token.
|
|
837
849
|
var floorT = tst.genTime;
|
|
838
|
-
var ceilT = (tst.genTimeFraction != null &&
|
|
850
|
+
var ceilT = (tst.genTimeFraction != null && _hasNonZeroDigit(tst.genTimeFraction.slice(3))) ? new Date(guard.time.instantOf(floorT) + 1) : floorT;
|
|
839
851
|
try {
|
|
840
852
|
// parseToken surfaces each embedded cert as { bytes, tagClass, ... }; take the X.509 certs
|
|
841
853
|
// (universal SEQUENCE), skipping any attribute-certificate choices, and parse their raw bytes.
|
package/lib/validator-tpm.js
CHANGED
|
@@ -226,10 +226,10 @@ function normalizeObjectAttributePolicy(policy, E, code) {
|
|
|
226
226
|
// and now live in one place. It also decodes, so the hex path cannot validate one string
|
|
227
227
|
// and decode another.
|
|
228
228
|
allow = ap.allow.map(function (entry, i) {
|
|
229
|
-
if (
|
|
229
|
+
if (guard.bytes.isByteSource(entry)) return guard.bytes.snapshotSource(entry, E, code, "opts.tpmPolicy.authPolicy.allow[" + i + "]");
|
|
230
230
|
var label = "opts.tpmPolicy.authPolicy.allow[" + i + "]";
|
|
231
231
|
if (typeof entry !== "string" || entry.length === 0) {
|
|
232
|
-
throw new E(code, label + " must be a
|
|
232
|
+
throw new E(code, label + " must be a BufferSource or an even-length hex string");
|
|
233
233
|
}
|
|
234
234
|
return guard.encoding.hex(entry, null, function (c, m) { return new E(c, m); }, code,
|
|
235
235
|
label + " must be a Buffer or an even-length hex string --");
|
package/lib/webauthn-mds.js
CHANGED
|
@@ -26,6 +26,7 @@ var intrinsic = require("./guard-intrinsic");
|
|
|
26
26
|
// Own-key membership through an operation taken at load. Written out, the question reads two
|
|
27
27
|
// replaceable properties to answer one thing, and here it decides which status rows an entry carries.
|
|
28
28
|
var _hasOwn = intrinsic.hasOwn;
|
|
29
|
+
var _charAt = intrinsic.uncurry(String.prototype.charAt);
|
|
29
30
|
var jose = require("./jose");
|
|
30
31
|
var rfc3339 = require("./rfc3339");
|
|
31
32
|
var constants = require("./constants");
|
|
@@ -40,6 +41,10 @@ var pkix = require("./schema-pkix");
|
|
|
40
41
|
var WebauthnError = frameworkError.WebauthnError;
|
|
41
42
|
var _KU_NS = pkix.makeNS("webauthn", WebauthnError, oid);
|
|
42
43
|
function _err(code, message, cause) { return new WebauthnError(code, message, cause); }
|
|
44
|
+
// A 40-character certificate-key-identifier: SHA-1 hex. One accepts either case (/^[0-9a-fA-F]{40}$/), the
|
|
45
|
+
// lookup form is lower-case only (/^[0-9a-f]{40}$/).
|
|
46
|
+
var _KEYID_HEX_CI = pkix.charTable("0123456789abcdefABCDEF");
|
|
47
|
+
var _KEYID_HEX_LOWER = pkix.charTable("0123456789abcdef");
|
|
43
48
|
var C = constants.LIMITS;
|
|
44
49
|
|
|
45
50
|
// Which signature schemes each certificate key type may perform, keyed by the SPKI algorithm the
|
|
@@ -176,7 +181,20 @@ function _deepFreeze(v, depth) {
|
|
|
176
181
|
Object.keys(v).forEach(function (k) { _deepFreeze(v[k], depth + 1); });
|
|
177
182
|
return v;
|
|
178
183
|
}
|
|
179
|
-
|
|
184
|
+
// The AAGUID canonical form (lowercase hex 8-4-4-4-12 with dashes), matching
|
|
185
|
+
// /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, decided by character (no regex).
|
|
186
|
+
function _isAaguid(s) {
|
|
187
|
+
if (s.length !== 36) return false;
|
|
188
|
+
for (var i = 0; i < 36; i++) {
|
|
189
|
+
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
|
190
|
+
if (_charAt(s, i) !== "-") return false;
|
|
191
|
+
} else {
|
|
192
|
+
var c = _charAt(s, i);
|
|
193
|
+
if (!((c >= "0" && c <= "9") || (c >= "a" && c <= "f"))) return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
180
198
|
// "This authenticator declares no model identity": the AAGUID a U2F authenticator carries, since
|
|
181
199
|
// U2F has no such concept. It is a sentinel, never a lookup key, and lives in one place so the
|
|
182
200
|
// lookup and the dispatch that decides which key space applies cannot disagree about it.
|
|
@@ -281,7 +299,7 @@ function _verifyMetadataBlob(blob, opts) {
|
|
|
281
299
|
// network, and the ordinary way to hold a fetched body is an ArrayBuffer, so the form an
|
|
282
300
|
// operator most naturally arrives with was the one form this refused. `byteLength` is declared by
|
|
283
301
|
// an ArrayBuffer and by every view over one, so the ceiling below still bites before any copy.
|
|
284
|
-
var raw =
|
|
302
|
+
var raw = guard.bytes.isByteSource(blob)
|
|
285
303
|
? guard.bytes.source(blob, WebauthnError, "webauthn/bad-input", "the metadata BLOB") : null;
|
|
286
304
|
// A string's `.length` counts UTF-16 code units, not the UTF-8 bytes the conversion produces, so
|
|
287
305
|
// measuring it would let a string of multi-byte characters sit under the ceiling and then expand
|
|
@@ -600,7 +618,7 @@ function _parsePayload(seg, at, opts) {
|
|
|
600
618
|
});
|
|
601
619
|
var aaguid = null;
|
|
602
620
|
if (e.aaguid !== undefined) {
|
|
603
|
-
if (typeof e.aaguid !== "string" || !
|
|
621
|
+
if (typeof e.aaguid !== "string" || !_isAaguid(e.aaguid.toLowerCase())) {
|
|
604
622
|
throw _err("webauthn/bad-metadata-blob", "metadata entry " + i + " has a malformed aaguid");
|
|
605
623
|
}
|
|
606
624
|
aaguid = e.aaguid.toLowerCase();
|
|
@@ -634,7 +652,7 @@ function _parsePayload(seg, at, opts) {
|
|
|
634
652
|
// matches -- an entry that silently cannot be found. sec. 3.1.1 requires lower case; the
|
|
635
653
|
// value is canonicalized, not refused, so a catalogue that differs only in letter
|
|
636
654
|
// case still resolves instead of denying service for every authenticator it lists.
|
|
637
|
-
if (typeof k !== "string" ||
|
|
655
|
+
if (typeof k !== "string" || k.length !== 40 || !pkix.allCharsIn(k, _KEYID_HEX_CI)) {
|
|
638
656
|
throw _err("webauthn/bad-metadata-blob", "metadata entry " + i + " has a malformed attestation certificate key identifier");
|
|
639
657
|
}
|
|
640
658
|
var lower = k.toLowerCase();
|
|
@@ -692,11 +710,11 @@ function metadataFor(metadata, identifier) {
|
|
|
692
710
|
if (!isVerifiedResult(metadata)) throw _err("webauthn/bad-input", "metadataFor expects a verifyMetadataBlob result -- an object that merely resembles one, such as a catalogue restored from a cache, has not been through the signature and chain checks");
|
|
693
711
|
if (typeof identifier !== "string") return null;
|
|
694
712
|
var key = identifier.toLowerCase();
|
|
695
|
-
if (
|
|
713
|
+
if (_isAaguid(key)) {
|
|
696
714
|
if (key === ZERO_AAGUID) return null;
|
|
697
715
|
return metadata.byAaguid[key] || null;
|
|
698
716
|
}
|
|
699
|
-
if (
|
|
717
|
+
if (key.length === 40 && pkix.allCharsIn(key, _KEYID_HEX_LOWER)) return metadataForKeyIdentifier(metadata, key);
|
|
700
718
|
return null;
|
|
701
719
|
}
|
|
702
720
|
|
package/lib/webauthn.js
CHANGED
|
@@ -536,7 +536,7 @@ var _FORMAT_SCOPED_BOOLEAN_OPTS = ["verifySafetyNetJws", "requireCtsProfileMatch
|
|
|
536
536
|
// object cannot recurse without end.
|
|
537
537
|
function _cloneParsed(v, depth) {
|
|
538
538
|
if (depth > 64) throw _err("webauthn/bad-input", "opts.rootCertificates[] is nested too deeply to be a parsed certificate");
|
|
539
|
-
if (
|
|
539
|
+
if (guard.bytes.isByteSource(v)) return guard.bytes.snapshotSource(v, _err, "webauthn/bad-input", "a byte field of opts.rootCertificates[]");
|
|
540
540
|
if (Array.isArray(v)) return v.map(function (x) { return _cloneParsed(x, depth + 1); });
|
|
541
541
|
if (guard.time.isDate(v)) return new Date(guard.time.instantOf(v));
|
|
542
542
|
if (v && typeof v === "object") {
|
|
@@ -559,7 +559,7 @@ function _cloneParsed(v, depth) {
|
|
|
559
559
|
//
|
|
560
560
|
// Anything that is not a BufferSource is refused BY NAME, rather than passed down to be described
|
|
561
561
|
// by whichever parser reaches it first.
|
|
562
|
-
function _isBufferSource(v) { return
|
|
562
|
+
function _isBufferSource(v) { return guard.bytes.isByteSource(v); }
|
|
563
563
|
function _bytesArg(v, label) {
|
|
564
564
|
if (_isBufferSource(v)) {
|
|
565
565
|
return guard.bytes.snapshotSource(v, WebauthnError, "webauthn/bad-input", label);
|
|
@@ -2313,7 +2313,7 @@ function verifyAssertion(input) {
|
|
|
2313
2313
|
// store already holds the COSE bytes. Accepting only the object made a caller fabricate an
|
|
2314
2314
|
// authenticatorData that never existed just to reach their own key.
|
|
2315
2315
|
var coseKey = input.credentialPublicKey;
|
|
2316
|
-
if (
|
|
2316
|
+
if (guard.bytes.isByteSource(coseKey)) {
|
|
2317
2317
|
coseKey = parseCoseKey(coseKey);
|
|
2318
2318
|
} else if (_isPlainObject(coseKey)) {
|
|
2319
2319
|
// The OBJECT form is held to the same rules about the KEY as the bytes form. The bytes went
|
package/lib/webcrypto.js
CHANGED
|
@@ -760,7 +760,7 @@ async function _deriveBitsRaw(alg, key, length) {
|
|
|
760
760
|
// caveat covers, so it is wiped once the derivation has consumed it.
|
|
761
761
|
return _withSecretBytes(key, function (ikm) {
|
|
762
762
|
var derived = nodeCrypto.hkdfSync(_hashNode(alg.hash, "HKDF"), ikm, _toBuf(alg.salt, "HKDF salt"), _toBuf(alg.info || _bufferAlloc(0), "HKDF info"), length / 8);
|
|
763
|
-
return derived
|
|
763
|
+
return _types.isArrayBuffer(derived) ? derived : _toArrayBuffer(_bufferFrom(derived));
|
|
764
764
|
});
|
|
765
765
|
}
|
|
766
766
|
if (name === "PBKDF2") {
|
package/lib/x509-sign.js
CHANGED
|
@@ -105,7 +105,7 @@ function _skiValueOf(caCert) {
|
|
|
105
105
|
return null;
|
|
106
106
|
}
|
|
107
107
|
function _akiKeyId(val, ctx) {
|
|
108
|
-
if (
|
|
108
|
+
if (guard.bytes.isByteSource(val)) return guard.bytes.snapshotSource(val, CertificateError, "x509/bad-input", "the authorityKeyIdentifier keyIdentifier");
|
|
109
109
|
if (val === true) {
|
|
110
110
|
if (ctx.issuerCert) { var ski = _skiValueOf(ctx.issuerCert); if (ski) return ski; }
|
|
111
111
|
return _spkiKeyId(ctx.issuerSpki);
|
|
@@ -264,7 +264,12 @@ function _hasCriticalSan(extSpec) {
|
|
|
264
264
|
* key being certified), `notBefore` / `notAfter` (`Date`s), an optional `serialNumber`, and an optional
|
|
265
265
|
* `extensions` object. `issuer` is the signing side: `{ key }` alone issues a self-signed certificate
|
|
266
266
|
* (issuer = subject, signed with the subject's own key); `{ name, publicKey, key }` or `{ cert, key }`
|
|
267
|
-
* issues a CA-signed one. The
|
|
267
|
+
* issues a CA-signed one. The signing key `key` is a WebCrypto `CryptoKey` -- a `pki.key.generate`
|
|
268
|
+
* private key passed directly, without exporting -- or a PKCS#8 private key as DER (`Buffer`) or PEM
|
|
269
|
+
* (`string`); a public or secret CryptoKey, or a `node:crypto` KeyObject, is refused. A `subjectAltName`
|
|
270
|
+
* (or any `GeneralName`) entry may be a form object (`{ dNSName: "..." }`) or a bare string classified
|
|
271
|
+
* fail-closed into its form (`"host.example"`, `"a@b.example"`, `"10.0.0.1"`, `"https://host.example/"`).
|
|
272
|
+
* The signature algorithm is resolved from the signing key: RSA (PKCS#1 v1.5
|
|
268
273
|
* or PSS via `opts.pss`), ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm, so every algorithm the
|
|
269
274
|
* toolkit signs with is available here without a per-algorithm branch.
|
|
270
275
|
*
|
|
@@ -283,12 +288,11 @@ function _hasCriticalSan(extSpec) {
|
|
|
283
288
|
* @example
|
|
284
289
|
* var pair = await pki.key.generate("Ed25519");
|
|
285
290
|
* var signerSpki = await pki.key.export(pair.publicKey);
|
|
286
|
-
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
287
291
|
* var root = await pki.x509.sign(
|
|
288
292
|
* { subject: "Example Root CA", subjectPublicKey: signerSpki,
|
|
289
293
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
290
294
|
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"], subjectKeyIdentifier: true } },
|
|
291
|
-
* { key:
|
|
295
|
+
* { key: pair.privateKey }); // a WebCrypto CryptoKey signs directly (or pass a PKCS#8 DER Buffer / PEM string)
|
|
292
296
|
* pki.schema.x509.parse(root).subject.dn; // "CN=Example Root CA"
|
|
293
297
|
*/
|
|
294
298
|
function sign(spec, issuer, opts) {
|
|
@@ -324,7 +328,7 @@ function _sign(spec, issuer, opts) {
|
|
|
324
328
|
: "explicit form; the issuer takes { key, name, publicKey }, or { key, cert } to take both from a certificate");
|
|
325
329
|
});
|
|
326
330
|
guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "x509/bad-input", "pki.x509.sign has an unknown option ");
|
|
327
|
-
if (issuer.key == null) throw _err("x509/bad-input", "a signing key (issuer.key, a PKCS#8 private key) is required");
|
|
331
|
+
if (issuer.key == null) throw _err("x509/bad-input", "a signing key (issuer.key, a WebCrypto CryptoKey or a PKCS#8 private key DER/PEM) is required");
|
|
328
332
|
|
|
329
333
|
var spki = _reqDer(spec.subjectPublicKey, "spec.subjectPublicKey (the SPKI DER of the certified key)");
|
|
330
334
|
_assertValidSpki(spki, "spec.subjectPublicKey");
|
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:e0315ce7-f8af-453b-be94-cae61e3f47ea",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-28T18:08:22.906Z",
|
|
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.5.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.5.32",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.5.
|
|
25
|
+
"version": "0.5.32",
|
|
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.5.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.5.32",
|
|
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.5.
|
|
57
|
+
"ref": "@blamejs/pki@0.5.32",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|