@blamejs/pki 0.5.5 → 0.5.7
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 +42 -0
- package/MIGRATING.md +63 -0
- package/lib/acme.js +47 -19
- package/lib/attrcert-sign.js +5 -1
- package/lib/cmc-build.js +8 -13
- package/lib/cmc-verify.js +2 -2
- package/lib/cmp-build.js +7 -2
- package/lib/cmp-session.js +4 -2
- package/lib/cmp-verify.js +1 -1
- package/lib/cms-compress.js +1 -2
- package/lib/cms-decrypt.js +2 -4
- package/lib/cms-encrypt.js +1 -2
- package/lib/cms-sign.js +42 -4
- package/lib/cms-verify.js +68 -17
- package/lib/crl-sign.js +9 -3
- package/lib/crmf-sign.js +5 -1
- package/lib/csr-sign.js +5 -1
- package/lib/est.js +5 -4
- package/lib/guard-all.js +2 -0
- package/lib/guard-async.js +37 -0
- package/lib/guard-bytes.js +368 -5
- package/lib/guard-parsed.js +117 -9
- package/lib/ocsp.js +24 -6
- package/lib/pbes2.js +11 -1
- package/lib/pkcs12-build.js +90 -27
- package/lib/pki-build.js +2 -3
- package/lib/schema-cms.js +150 -2
- package/lib/sign-scheme.js +7 -4
- package/lib/sigstore.js +10 -0
- package/lib/tsp-sign.js +116 -19
- package/lib/validator-tpm.js +1 -1
- package/lib/webauthn-mds.js +1 -1
- package/lib/webauthn.js +1 -1
- package/lib/x509-sign.js +11 -2
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/tsp-sign.js
CHANGED
|
@@ -27,18 +27,40 @@ var guard = require("./guard-all");
|
|
|
27
27
|
var frameworkError = require("./framework-error");
|
|
28
28
|
|
|
29
29
|
var pkix = require("./schema-pkix");
|
|
30
|
+
var pkiBuild = require("./pki-build");
|
|
30
31
|
var TspError = frameworkError.TspError;
|
|
31
32
|
var _NS = pkix.makeNS("tsp", TspError, oid);
|
|
32
33
|
var b = asn1.build;
|
|
33
34
|
function _err(code, message, cause) { return new TspError(code, message, cause); }
|
|
34
35
|
function O(name) { return oid.byName(name); }
|
|
35
36
|
|
|
37
|
+
// The shared authoring-side builder, for the same pre-encoded-Extension validation every other
|
|
38
|
+
// request builder uses (a CSR's extensionRequest, a CRMF CertTemplate, a CRL's entry extensions).
|
|
39
|
+
var _b = pkiBuild.makeBuilder({
|
|
40
|
+
ErrorClass: TspError, prefix: "tsp", O: O, NS: _NS,
|
|
41
|
+
NAME_SCHEMA: pkix.name(_NS), SPKI_SCHEMA: pkix.spki(_NS),
|
|
42
|
+
});
|
|
43
|
+
|
|
36
44
|
// Digest names whose imprint / certHash this producer supports (the SHA-2 family).
|
|
37
45
|
var NODE_DIGEST = { sha256: "sha256", sha384: "sha384", sha512: "sha512" };
|
|
38
46
|
// The message-imprint hash MUST be exactly the digest algorithm's output length (RFC 3161 sec.
|
|
39
47
|
// 2.4.1 -- hashedMessage is "the hash of the datum to be time-stamped").
|
|
40
48
|
var HASH_LEN = { sha256: 32, sha384: 48, sha512: 64 };
|
|
41
49
|
|
|
50
|
+
// How much a `pki.path.validate` revocationChecked outcome ESTABLISHED, ordered. `false` is "no
|
|
51
|
+
// checker ran at all"; "undetermined" is "one ran and could not answer"; "waived" is an
|
|
52
|
+
// undetermined one a caller chose to pass; "determined" is an explicit good-or-revoked answer.
|
|
53
|
+
// Ranking them lets a caller trying several candidate paths keep the most established outcome any
|
|
54
|
+
// of them reached, rather than whichever happened to run last.
|
|
55
|
+
var _REVOCATION_RANK = { "false": 0, "undetermined": 1, "waived": 2, "determined": 3 };
|
|
56
|
+
function _rankRevocation(v) {
|
|
57
|
+
var r = _REVOCATION_RANK[String(v)];
|
|
58
|
+
return r === undefined ? 0 : r; // an unrecognized value establishes nothing
|
|
59
|
+
}
|
|
60
|
+
function _moreEstablished(candidate, current) {
|
|
61
|
+
return _rankRevocation(candidate) > _rankRevocation(current);
|
|
62
|
+
}
|
|
63
|
+
|
|
42
64
|
// A hash AlgorithmIdentifier { OID, NULL } -- messageImprint and ESSCertIDv2 hash algorithms
|
|
43
65
|
// carry an explicit NULL parameter (the form RFC 3161 / RFC 5035 producers emit).
|
|
44
66
|
function _hashAlgId(name) {
|
|
@@ -109,7 +131,17 @@ function _signingCertV2(certDer, hashName) {
|
|
|
109
131
|
* var token = await pki.tsp.sign(imprint, { cert: signerCertDer, key: signerKeyPkcs8 }, { policy: "1.3.6.1.4.1.1", serialNumber: 1 });
|
|
110
132
|
* (await pki.cms.verify(token)).valid; // true
|
|
111
133
|
*/
|
|
134
|
+
// Documented `-> Promise`, so a fault leaves as a REJECTION (guard-async); the checks stay
|
|
135
|
+
// synchronous because they read the caller's mutable imprint, TSA material and options.
|
|
112
136
|
function sign(messageImprint, tsa, opts) {
|
|
137
|
+
// Every caller-owned argument copied at entry and released when the call settles -- see the note
|
|
138
|
+
// on the same call in x509-sign. The imprint matters most: it is the digest the token attests to.
|
|
139
|
+
return guard.bytes.fixedCall(TspError, "tsp/bad-input", [
|
|
140
|
+
[messageImprint, "the messageImprint"], [tsa, "the TSA"], [opts, "pki.tsp.sign options"],
|
|
141
|
+
], _sign);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function _sign(messageImprint, tsa, opts) {
|
|
113
145
|
opts = opts || {};
|
|
114
146
|
if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("tsp/bad-input", "pki.tsp.sign options must be an object");
|
|
115
147
|
var mi = messageImprint || {};
|
|
@@ -130,10 +162,14 @@ function sign(messageImprint, tsa, opts) {
|
|
|
130
162
|
// non-Date or an Invalid Date that would encode a garbage GeneralizedTime).
|
|
131
163
|
if (opts.genTime != null) guard.time.assertValid(opts.genTime, _err, "tsp/bad-input", "genTime");
|
|
132
164
|
var genTime = opts.genTime instanceof Date ? opts.genTime : new Date();
|
|
133
|
-
|
|
165
|
+
// A caller-authored INTEGER is coerced through the shared guard, so a value that is not one is a
|
|
166
|
+
// typed tsp/bad-input rather than a raw SyntaxError or RangeError out of BigInt() -- an untyped
|
|
167
|
+
// fault escaping a public verb, which a caller cannot catch by code.
|
|
168
|
+
var serial = guard.range.authoredInteger(opts.serialNumber, _err, "tsp/bad-input", "serialNumber");
|
|
169
|
+
var fields = [b.integer(1n), _policy(opts.policy), imprint, b.integer(serial), b.generalizedTime(genTime)];
|
|
134
170
|
if (opts.accuracy) fields.push(_accuracy(opts.accuracy));
|
|
135
171
|
if (opts.ordering === true) fields.push(b.boolean(true));
|
|
136
|
-
if (opts.nonce != null) fields.push(b.integer(
|
|
172
|
+
if (opts.nonce != null) fields.push(b.integer(guard.range.authoredInteger(opts.nonce, _err, "tsp/bad-input", "nonce")));
|
|
137
173
|
var tstInfo = b.sequence(fields);
|
|
138
174
|
|
|
139
175
|
var signCert = { type: "signingCertificateV2", values: [_signingCertV2(certDer, certHashAlg)] };
|
|
@@ -237,12 +273,26 @@ function request(messageImprint, opts) {
|
|
|
237
273
|
// extensions in schema order. certReq DEFAULT FALSE -> only an explicit TRUE is encoded (DER).
|
|
238
274
|
var fields = [b.integer(1n), imprint];
|
|
239
275
|
if (opts.reqPolicy != null) fields.push(_policy(opts.reqPolicy));
|
|
240
|
-
if (opts.nonce != null) fields.push(b.integer(
|
|
276
|
+
if (opts.nonce != null) fields.push(b.integer(guard.range.authoredInteger(opts.nonce, _err, "tsp/bad-input", "nonce")));
|
|
241
277
|
if (opts.certReq != null && typeof opts.certReq !== "boolean") throw _err("tsp/bad-input", "certReq must be a boolean");
|
|
242
278
|
if (opts.certReq === true) fields.push(b.boolean(true));
|
|
243
279
|
if (opts.extensions != null) {
|
|
244
280
|
if (!Array.isArray(opts.extensions) || !opts.extensions.every(function (e) { return Buffer.isBuffer(e) || e instanceof Uint8Array; })) throw _err("tsp/bad-input", "extensions must be an array of encoded Extension DER buffers");
|
|
245
|
-
|
|
281
|
+
// Each pre-encoded Extension is validated as one, and its extnID must not repeat -- the same
|
|
282
|
+
// gate every other request builder applies. Spliced in unchecked, a caller relaying a blob it
|
|
283
|
+
// did not author put fully chosen bytes inside [0] and this encoder emitted DER its own
|
|
284
|
+
// parseRequest refuses: an undecodable value, a duplicate extnID, an explicit critical=FALSE
|
|
285
|
+
// the DER DEFAULT rule forbids.
|
|
286
|
+
var seenExt = {};
|
|
287
|
+
var encoded = opts.extensions.map(function (e, i) {
|
|
288
|
+
var der = Buffer.from(e);
|
|
289
|
+
_b.assertValidExtension(der, i);
|
|
290
|
+
var extnId = asn1.read.oid(asn1.decode(der).children[0]);
|
|
291
|
+
if (seenExt[extnId]) throw _err("tsp/bad-input", "duplicate request extension " + extnId + " (RFC 5280 sec. 4.2)");
|
|
292
|
+
seenExt[extnId] = true;
|
|
293
|
+
return der;
|
|
294
|
+
});
|
|
295
|
+
if (encoded.length) fields.push(b.contextConstructed(0, Buffer.concat(encoded)));
|
|
246
296
|
}
|
|
247
297
|
var der = b.sequence(fields);
|
|
248
298
|
return opts.pem ? schemaTsp.pemEncode(der, "TIMESTAMP REQUEST") : der;
|
|
@@ -564,7 +614,8 @@ function _buildTsaChains(leaf, pool) {
|
|
|
564
614
|
* PEM (never a parsed object -- every checked field is read from the CMS-verified eContent, so a
|
|
565
615
|
* mutated parsed structure cannot desynchronize the checks from the signed bytes). `data` is the
|
|
566
616
|
* original bytes (hashed under the token's messageImprint algorithm) or a precomputed
|
|
567
|
-
* `{ hashAlgorithm, hashedMessage }`. Returns `{ valid, trusted,
|
|
617
|
+
* `{ hashAlgorithm, hashedMessage }`. Returns `{ valid, trusted, revocationChecked,
|
|
618
|
+
* anchorConstraints, genTime, accuracy, serialNumber,
|
|
568
619
|
* serialNumberHex, policy, nonce, tsa, tstInfo, signer, code?, reason? }`. `valid` is true only
|
|
569
620
|
* when the CMS signature, the imprint match, the eContentType, the ESSCertID(V2) binding, the
|
|
570
621
|
* RFC 3161 sec. 2.3 critical single-`timeStamping` extendedKeyUsage rule, the requested nonce (when
|
|
@@ -579,11 +630,19 @@ function _buildTsaChains(leaf, pool) {
|
|
|
579
630
|
* accepting one. A timestamp is archived precisely to be re-read years later, and one boolean
|
|
580
631
|
* cannot answer both questions then.
|
|
581
632
|
*
|
|
633
|
+
* `revocationChecked` is the third claim, for the same reason. Revocation runs only when a
|
|
634
|
+
* `revocationChecker` is supplied, so a `trusted` token whose TSA was never checked against a CRL
|
|
635
|
+
* or an OCSP responder reads identically to one established un-revoked -- unless the verdict says
|
|
636
|
+
* which. It is `false` whenever no path ran at all. `anchorConstraints` carries whatever the anchor
|
|
637
|
+
* itself constrained, from `pki.path.validate`.
|
|
638
|
+
*
|
|
582
639
|
* @opts trustAnchor Anchor `{ name, publicKey, algorithm }` -- runs `pki.path.validate` on the
|
|
583
640
|
* TSA certificate chain ordered from the token's embedded certificates
|
|
584
|
-
* (validity at genTime, requiredEku timeStamping, revocation
|
|
585
|
-
*
|
|
586
|
-
*
|
|
641
|
+
* (validity at genTime, requiredEku timeStamping, and revocation when a
|
|
642
|
+
* `revocationChecker` is supplied -- `revocationChecked` reports which), so a
|
|
643
|
+
* TSA under an intermediate CA validates, not only one directly under the
|
|
644
|
+
* anchor. Omit to verify signature + imprint + binding + EKU only and anchor
|
|
645
|
+
* the cert yourself.
|
|
587
646
|
* @opts nonce Require the token's TSTInfo.nonce to equal this (a number/BigInt).
|
|
588
647
|
* @opts reqPolicy Require the token's policy to equal this (an OID name or dotted string).
|
|
589
648
|
* @opts certs Out-of-band TSA certificates (an array of DER `Buffer`s) added to the signer
|
|
@@ -630,8 +689,15 @@ async function verify(token, data, opts) {
|
|
|
630
689
|
// `res.trusted` must get an answer on both branches -- an undefined on the failure path is the
|
|
631
690
|
// same "cannot tell what was checked" the field was added to remove, and `!res.trusted` reading
|
|
632
691
|
// true by accident is not the same as its reading true because nothing anchored the TSA.
|
|
692
|
+
// What a path established travels with the REFUSAL as well. Hardcoding these to false/null here
|
|
693
|
+
// would misreport the one case the field matters most for: a path that ran, established the TSA
|
|
694
|
+
// REVOKED, and refused on that basis did check revocation, and saying it did not is the same
|
|
695
|
+
// "cannot tell what was checked" the field was added to remove. They stay false/null until a path
|
|
696
|
+
// actually produces them, which is the honest answer before one runs.
|
|
697
|
+
var revocationChecked = false;
|
|
698
|
+
var anchorConstraints = null;
|
|
633
699
|
function fail(code, reason) {
|
|
634
|
-
return { valid: false, trusted: false, code: code, reason: reason || null, genTime: tst.genTime, accuracy: tst.accuracy, serialNumber: tst.serialNumber, serialNumberHex: tst.serialNumberHex, policy: tst.policy, nonce: tst.nonce, tsa: tst.tsa, tstInfo: tst, signer: null };
|
|
700
|
+
return { valid: false, trusted: false, revocationChecked: revocationChecked, anchorConstraints: anchorConstraints, code: code, reason: reason || null, genTime: tst.genTime, accuracy: tst.accuracy, serialNumber: tst.serialNumber, serialNumberHex: tst.serialNumberHex, policy: tst.policy, nonce: tst.nonce, tsa: tst.tsa, tstInfo: tst, signer: null };
|
|
635
701
|
}
|
|
636
702
|
// M12 -- the CMS signature over the exact RFC 5652 sec. 5.4 preimage (message-digest bound to the
|
|
637
703
|
// authenticated eContent). cms.verify re-parses the same bytes; a failure is a fail-closed verdict.
|
|
@@ -647,7 +713,7 @@ async function verify(token, data, opts) {
|
|
|
647
713
|
if (mi !== true) return fail(mi);
|
|
648
714
|
// M14 -- if a request nonce is supplied, the token MUST echo it (BigInt-exact).
|
|
649
715
|
if (opts.nonce != null) {
|
|
650
|
-
var wantNonce =
|
|
716
|
+
var wantNonce = guard.range.authoredInteger(opts.nonce, _err, "tsp/bad-input", "opts.nonce");
|
|
651
717
|
if (tst.nonce == null || tst.nonce !== wantNonce) return fail("tsp/nonce-mismatch");
|
|
652
718
|
}
|
|
653
719
|
// M15 -- if the requested policy is supplied, the token's policy MUST equal it.
|
|
@@ -713,23 +779,54 @@ async function verify(token, data, opts) {
|
|
|
713
779
|
// end of the chain and not the other, and a root explicitly distrusted for timestamping would
|
|
714
780
|
// still answer trusted. The purpose is not a caller choice here: this verb validates timestamp
|
|
715
781
|
// tokens and nothing else, so there is exactly one purpose its anchors can be judged under.
|
|
782
|
+
// EVERY path result goes through this one function, so none can be produced without being
|
|
783
|
+
// accumulated. The alternative -- validating in several places and accumulating afterwards --
|
|
784
|
+
// is what let a result be overwritten before it was counted, first across candidate chains and
|
|
785
|
+
// then across the two endpoints of one candidate. There is now one door and it always counts.
|
|
786
|
+
//
|
|
787
|
+
// The two fields accumulate INDEPENDENTLY because they are independent facts. Whether
|
|
788
|
+
// revocation was established and which anchor constraints were consulted do not imply each
|
|
789
|
+
// other: an anchor that rejects the TSA on its own purposes or distrustAfter metadata, with no
|
|
790
|
+
// revocationChecker configured, establishes constraints while establishing nothing about
|
|
791
|
+
// revocation.
|
|
792
|
+
async function validateAt(chain, when) {
|
|
793
|
+
var res = await pathValidate.validate(chain, {
|
|
794
|
+
time: when, trustAnchor: opts.trustAnchor, requiredEku: ["timeStamping"], checkPurpose: "timeStamping", revocationChecker: opts.revocationChecker,
|
|
795
|
+
});
|
|
796
|
+
// The most any attempt ESTABLISHED, kept across every attempt: backtracking tries several
|
|
797
|
+
// chains for one TSA certificate and a fractional genTime validates each at two endpoints,
|
|
798
|
+
// so an attempt that fails earlier than another must not erase what that other established.
|
|
799
|
+
if (_moreEstablished(res.revocationChecked, revocationChecked)) revocationChecked = res.revocationChecked;
|
|
800
|
+
// Every attempt validates against the SAME opts.trustAnchor under the same purpose, so any
|
|
801
|
+
// non-null value describes that one anchor; the first to report them is as good as the last.
|
|
802
|
+
if (anchorConstraints == null && res.anchorConstraints != null) anchorConstraints = res.anchorConstraints;
|
|
803
|
+
return res;
|
|
804
|
+
}
|
|
716
805
|
var chains = _buildTsaChains(pkiX509.parse(tsaCertDer), pool);
|
|
717
806
|
for (var ci = 0; ci < chains.length && !(pathRes && pathRes.valid); ci++) {
|
|
718
|
-
pathRes = await
|
|
719
|
-
|
|
720
|
-
});
|
|
721
|
-
if (pathRes.valid && ceilT !== floorT) {
|
|
722
|
-
pathRes = await pathValidate.validate(chains[ci], {
|
|
723
|
-
time: ceilT, trustAnchor: opts.trustAnchor, requiredEku: ["timeStamping"], checkPurpose: "timeStamping", revocationChecker: opts.revocationChecker,
|
|
724
|
-
});
|
|
725
|
-
}
|
|
807
|
+
pathRes = await validateAt(chains[ci], floorT);
|
|
808
|
+
if (pathRes.valid && ceilT !== floorT) pathRes = await validateAt(chains[ci], ceilT);
|
|
726
809
|
}
|
|
727
810
|
} catch (e) { return fail("tsp/untrusted-tsa", (e && e.message) || String(e)); }
|
|
811
|
+
// On a REFUSAL the question is whether ANYTHING established the TSA's revocation status, so the
|
|
812
|
+
// accumulated best answers it: a candidate that reached the checker and was rejected as revoked
|
|
813
|
+
// did check revocation, and saying otherwise would misdescribe the reason for the refusal.
|
|
728
814
|
if (!pathRes || !pathRes.valid) return fail("tsp/untrusted-tsa", "the TSA certificate did not validate to the trust anchor at genTime");
|
|
815
|
+
// On ACCEPTANCE the question is narrower and the accumulated best would OVERCLAIM: what matters
|
|
816
|
+
// is what was checked on the path actually accepted, not what some rejected candidate managed.
|
|
817
|
+
// The loop stops at the first valid path, so `pathRes` is that path.
|
|
818
|
+
revocationChecked = pathRes.revocationChecked;
|
|
819
|
+
anchorConstraints = pathRes.anchorConstraints;
|
|
820
|
+
// Reduced to a bare `trusted: true`, this answered the same way whether the TSA was established
|
|
821
|
+
// un-revoked or revocation was never consulted at all -- and revocation only runs when a
|
|
822
|
+
// revocationChecker is supplied, so the second case is the DEFAULT. A caller archiving a
|
|
823
|
+
// timestamp verdict could not tell the two apart later, which is the distinction the
|
|
824
|
+
// valid/trusted split exists to keep.
|
|
729
825
|
trusted = true;
|
|
730
826
|
}
|
|
731
827
|
return {
|
|
732
|
-
valid: true, trusted: trusted,
|
|
828
|
+
valid: true, trusted: trusted, revocationChecked: revocationChecked, anchorConstraints: anchorConstraints,
|
|
829
|
+
genTime: tst.genTime, accuracy: tst.accuracy,
|
|
733
830
|
serialNumber: tst.serialNumber, serialNumberHex: tst.serialNumberHex,
|
|
734
831
|
policy: tst.policy, policyName: tst.policyName, nonce: tst.nonce, tsa: tst.tsa,
|
|
735
832
|
tstInfo: tst, signer: { cert: tsaCertDer, sid: signer.sid },
|
package/lib/validator-tpm.js
CHANGED
|
@@ -225,7 +225,7 @@ function normalizeObjectAttributePolicy(policy, E, code) {
|
|
|
225
225
|
// canonical round-trip -- the same three checks written here by hand, and now written
|
|
226
226
|
// once. It also decodes, so the hex path cannot validate one string and decode another.
|
|
227
227
|
allow = ap.allow.map(function (entry, i) {
|
|
228
|
-
if (Buffer.isBuffer(entry)) return entry;
|
|
228
|
+
if (Buffer.isBuffer(entry)) return guard.bytes.snapshot(entry, E, code, "opts.tpmPolicy.authPolicy.allow[" + i + "]");
|
|
229
229
|
var label = "opts.tpmPolicy.authPolicy.allow[" + i + "]";
|
|
230
230
|
if (typeof entry !== "string" || entry.length === 0) {
|
|
231
231
|
throw new E(code, label + " must be a Buffer or an even-length hex string");
|
package/lib/webauthn-mds.js
CHANGED
|
@@ -233,7 +233,7 @@ function _asCert(v, label) {
|
|
|
233
233
|
// with this toolkit: which metadata authority to trust is the operator's choice, and a verifier
|
|
234
234
|
// that bundled its own would be deciding trust on the caller's behalf.
|
|
235
235
|
function verifyMetadataBlob(blob, opts) {
|
|
236
|
-
return
|
|
236
|
+
return guard.async.deferred(function () { return _verifyMetadataBlob(blob, opts); });
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
function _verifyMetadataBlob(blob, opts) {
|
package/lib/webauthn.js
CHANGED
|
@@ -531,7 +531,7 @@ var _FORMAT_SCOPED_BOOLEAN_OPTS = ["verifySafetyNetJws", "requireCtsProfileMatch
|
|
|
531
531
|
// object cannot recurse without end.
|
|
532
532
|
function _cloneParsed(v, depth) {
|
|
533
533
|
if (depth > 64) throw _err("webauthn/bad-input", "opts.rootCertificates[] is nested too deeply to be a parsed certificate");
|
|
534
|
-
if (Buffer.isBuffer(v) || v instanceof Uint8Array) return
|
|
534
|
+
if (Buffer.isBuffer(v) || v instanceof Uint8Array) return guard.bytes.snapshot(v, _err, "webauthn/bad-input", "a byte field of opts.rootCertificates[]");
|
|
535
535
|
if (Array.isArray(v)) return v.map(function (x) { return _cloneParsed(x, depth + 1); });
|
|
536
536
|
if (v instanceof Date) return new Date(v.getTime());
|
|
537
537
|
if (v && typeof v === "object") {
|
package/lib/x509-sign.js
CHANGED
|
@@ -78,7 +78,7 @@ function _skiValueOf(caCert) {
|
|
|
78
78
|
return null;
|
|
79
79
|
}
|
|
80
80
|
function _akiKeyId(val, ctx) {
|
|
81
|
-
if (Buffer.isBuffer(val)) return val;
|
|
81
|
+
if (Buffer.isBuffer(val)) return guard.bytes.snapshot(val, CertificateError, "x509/bad-input", "the authorityKeyIdentifier keyIdentifier");
|
|
82
82
|
if (val === true) {
|
|
83
83
|
if (ctx.issuerCert) { var ski = _skiValueOf(ctx.issuerCert); if (ski) return ski; }
|
|
84
84
|
return _spkiKeyId(ctx.issuerSpki);
|
|
@@ -265,7 +265,16 @@ function _hasCriticalSan(extSpec) {
|
|
|
265
265
|
* pki.schema.x509.parse(root).subject.dn; // "CN=Example Root CA"
|
|
266
266
|
*/
|
|
267
267
|
function sign(spec, issuer, opts) {
|
|
268
|
-
|
|
268
|
+
// EVERY caller-owned argument is copied before a field of any of them is read, and every copy is
|
|
269
|
+
// cleared when the call settles. The checks below run NOW, and the values they approved are
|
|
270
|
+
// encoded, signed and emitted several promise turns afterwards, while the caller still owns all
|
|
271
|
+
// three objects -- so a spec, an option or a signer read again after the first turn need not be
|
|
272
|
+
// the one that passed. Copying only some of them leaves the rest reachable; copying without the
|
|
273
|
+
// release leaves a duplicate of whatever secret was nested in them. guard.bytes.fixArguments is
|
|
274
|
+
// both halves, and is what every producing verb in the toolkit opens with.
|
|
275
|
+
return guard.bytes.fixedCall(CertificateError, "x509/bad-input", [
|
|
276
|
+
[spec, "the certificate spec"], [issuer, "the issuer"], [opts, "pki.x509.sign options"],
|
|
277
|
+
], _sign);
|
|
269
278
|
}
|
|
270
279
|
|
|
271
280
|
function _sign(spec, issuer, opts) {
|
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:a8c014a2-3631-443a-b33d-f10501d992fb",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-16T09:48:05.398Z",
|
|
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.7",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.5.
|
|
25
|
+
"version": "0.5.7",
|
|
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.7",
|
|
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.7",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|