@blamejs/pki 0.5.20 → 0.5.21

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 CHANGED
@@ -4,6 +4,26 @@ All notable changes to `@blamejs/pki` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
5
5
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## v0.5.21 — 2026-08-21
8
+
9
+ The check that holds the crypto engine to load-time captures could be satisfied around, so nine modules had taken the safe primitive at one site and were never held to it anywhere else.
10
+
11
+ ### Added
12
+
13
+ - `guard.intrinsic.bufferEquals`, the captured byte-identity comparison the bindings above are decided with.
14
+
15
+ ### Changed
16
+
17
+ - Taking the captures has one spelling again. The guard orchestrator no longer re-exports them, so a module reaches them by importing the module that holds them, and the check reads that off the loaded module graph rather than off the text. This is the enforcement change: a module that adopts one safe primitive is held to the rule everywhere, and no way of writing the import, or of writing something that resembles one, changes the answer.
18
+ - The seven modules the widened check surfaced carry a declared count of remaining reads rather than an exemption. A new live read pushes a module past its figure and fails, and converting reads without lowering the figure fails too, so the number keeps naming the real count. A module reaching zero leaves the list and is held to zero.
19
+
20
+ ### Fixed
21
+
22
+ - An OCSP response is bound to the certificate it answers for with captured byte comparisons. The CertID says the response concerns this issuer and the responder ID says this key signed it, and both were decided by a `Buffer.prototype.equals` read at call time: replaced after load it reports every hash equal, and a `good` signed by a different CA answers for the certificate under check.
23
+ - The signer's key is matched to its certificate under a captured case fold. WebCrypto algorithm names compare case-insensitively, so the check folds both sides; dispatched live, a replaced fold reported two different algorithm names equal and a key whose algorithm does not match the certificate passed the gate that exists to catch exactly that.
24
+ - `pki.sign` decides the RSASSA-PSS parameters, the digest tables and its key-shape tests on captured operations, so a caller who replaces one after this package loads cannot change which algorithm a signature runs under.
25
+ - A signing key is classified by asking the value what it is rather than by `instanceof` against a global. The old test read the `Uint8Array` binding at call time, and it answered wrongly in both directions on its own terms: a real typed array from another realm was not one, and an object built on the prototype was.
26
+
7
27
  ## v0.5.20 — 2026-08-20
8
28
 
9
29
  Five verdicts that turned on list membership now decide it by comparison, so replacing an array method after the module loads cannot change what a certificate, a responder or a signed message is found to be.
@@ -23,6 +23,7 @@ var schemaCms = require("./schema-cms");
23
23
  var webcrypto = require("./webcrypto");
24
24
  var frameworkError = require("./framework-error");
25
25
  var guard = require("./guard-all");
26
+ var intrinsic = require("./guard-intrinsic");
26
27
  var pbes2 = require("./pbes2");
27
28
  var b = asn1.build;
28
29
  var subtle = webcrypto.webcrypto.subtle;
@@ -415,8 +416,8 @@ async function _kemriCek(ri, km) {
415
416
  // why, by name. An unresolvable OID is left to _openContent too, as the unsupported algorithm it is.
416
417
  function _assertContentCipherMode(eci, ct) {
417
418
  var oidStr = eci.contentEncryptionAlgorithm.oid;
418
- var isAead = guard.intrinsic.hasOwn(AEAD_ALGS, oidStr);
419
- if (!isAead && !guard.intrinsic.hasOwn(CONTENT_MODE, oidStr)) return;
419
+ var isAead = intrinsic.hasOwn(AEAD_ALGS, oidStr);
420
+ if (!isAead && !intrinsic.hasOwn(CONTENT_MODE, oidStr)) return;
420
421
  if (ct === "authEnvelopedData") {
421
422
  if (isAead) return;
422
423
  throw _err("cms/unsupported-algorithm", "contentEncryptionAlgorithm " + oidStr + " is not an authenticated encryption algorithm, which authEnvelopedData requires (RFC 5083 sec. 2)");
@@ -428,14 +429,14 @@ function _assertContentCipherMode(eci, ct) {
428
429
  // ---- stage 3: open the content (uniform failure) ---------------------------
429
430
  async function _openContent(parsed, eci, cek, ct) {
430
431
  var alg = eci.contentEncryptionAlgorithm;
431
- var keyBits = guard.intrinsic.hasOwn(CONTENT_KEYBITS, alg.oid) ? CONTENT_KEYBITS[alg.oid] : 0;
432
+ var keyBits = intrinsic.hasOwn(CONTENT_KEYBITS, alg.oid) ? CONTENT_KEYBITS[alg.oid] : 0;
432
433
  if (!keyBits) {
433
434
  // AES-CCM parses completely here -- registered OIDs, the sec. 3.1 nonce bound, the seven legal
434
435
  // ICV lengths -- and is then declined, because AES-GCM is the only AEAD this toolkit implements
435
436
  // for content encryption. Saying only "unsupported <oid>" is what an operator also sees for an
436
437
  // OID nothing in the toolkit recognizes, and the two call for different things: one is a
437
438
  // capability gap to route around, the other is a malformed or exotic message.
438
- if (guard.intrinsic.hasOwn(AEAD_ALGS, alg.oid) && AEAD_ALGS[alg.oid] === "ccm") {
439
+ if (intrinsic.hasOwn(AEAD_ALGS, alg.oid) && AEAD_ALGS[alg.oid] === "ccm") {
439
440
  throw _err("cms/unsupported-algorithm", "contentEncryptionAlgorithm " + alg.oid + " is AES-CCM. RFC 5084 sec. 3.1 permits it for this content type; this toolkit implements AES-GCM only for content encryption");
440
441
  }
441
442
  throw _err("cms/unsupported-algorithm", "unsupported contentEncryptionAlgorithm " + alg.oid);
@@ -23,6 +23,7 @@ var schemaCms = require("./schema-cms");
23
23
  var webcrypto = require("./webcrypto");
24
24
  var frameworkError = require("./framework-error");
25
25
  var guard = require("./guard-all");
26
+ var intrinsic = require("./guard-intrinsic");
26
27
  var pbes2 = require("./pbes2");
27
28
  var b = asn1.build;
28
29
  var subtle = webcrypto.webcrypto.subtle;
@@ -565,7 +566,7 @@ function _assertAuthAttrs(pairs, forbidden) {
565
566
  }
566
567
  var t;
567
568
  try { t = asn1.read.oid(node.children[0]); } catch (e) { throw _err("cms/bad-input", "an authenticated attribute type is not an OBJECT IDENTIFIER", e); }
568
- if (guard.intrinsic.hasOwn(seenTypes, t)) throw _err("cms/bad-input", "authenticated attributes must not repeat an attribute type (RFC 5652): " + t);
569
+ if (intrinsic.hasOwn(seenTypes, t)) throw _err("cms/bad-input", "authenticated attributes must not repeat an attribute type (RFC 5652): " + t);
569
570
  seenTypes[t] = 1;
570
571
  if (forbidden && t === forbidden.oid) throw _err("cms/bad-input", forbidden.why);
571
572
  });
package/lib/cms-sign.js CHANGED
@@ -22,6 +22,7 @@ var frameworkError = require("./framework-error");
22
22
  var webcrypto = require("./webcrypto");
23
23
  var signScheme = require("./sign-scheme");
24
24
  var guard = require("./guard-all");
25
+ var intrinsic = require("./guard-intrinsic");
25
26
  var pkiBuild = require("./pki-build"); // the shared post-sign self-check, among other producing helpers
26
27
  var cms = require("./schema-cms");
27
28
 
@@ -117,7 +118,7 @@ function _buildSid(cert, useSki) {
117
118
  function _buildSignedAttrs(pairs) {
118
119
  var seenTypes = {};
119
120
  var attrs = pairs.map(function (p) {
120
- if (guard.intrinsic.hasOwn(seenTypes, p.type)) throw _err("cms/bad-input", "signedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
121
+ if (intrinsic.hasOwn(seenTypes, p.type)) throw _err("cms/bad-input", "signedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
121
122
  seenTypes[p.type] = 1;
122
123
  return b.sequence([b.oid(p.type), b.set(p.values)]);
123
124
  });
@@ -155,8 +156,8 @@ function _buildUnsignedAttrs(list) {
155
156
  var pairs = _resolveAttrPairs(list, "an unsigned attribute value");
156
157
  var seen = {};
157
158
  pairs.forEach(function (p) {
158
- if (guard.intrinsic.hasOwn(UNSIGNED_FORBIDDEN, p.type)) throw _err("cms/bad-input", "the " + UNSIGNED_FORBIDDEN[p.type] + " attribute must not appear as an unsigned attribute (RFC 5652 sec. 11)");
159
- if (guard.intrinsic.hasOwn(seen, p.type)) throw _err("cms/bad-input", "unsignedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
159
+ if (intrinsic.hasOwn(UNSIGNED_FORBIDDEN, p.type)) throw _err("cms/bad-input", "the " + UNSIGNED_FORBIDDEN[p.type] + " attribute must not appear as an unsigned attribute (RFC 5652 sec. 11)");
160
+ if (intrinsic.hasOwn(seen, p.type)) throw _err("cms/bad-input", "unsignedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
160
161
  seen[p.type] = 1;
161
162
  });
162
163
  var setOf = b.set(pairs.map(function (p) { return b.sequence([b.oid(p.type), b.set(p.values)]); }));
package/lib/cms-verify.js CHANGED
@@ -46,6 +46,7 @@ var SLHDSA_BY_OID = signScheme.SLHDSA_BY_OID; // shared SLH-DS
46
46
  var validator = require("./validator-all");
47
47
  var compositeSig = require("./composite-sig");
48
48
  var guard = require("./guard-all");
49
+ var intrinsic = require("./guard-intrinsic");
49
50
  var frameworkError = require("./framework-error");
50
51
  var pkix = require("./schema-pkix");
51
52
 
@@ -73,7 +74,7 @@ var DIGEST_HASH = {
73
74
  // foreign fault and loses this module's own unsupported-algorithm verdict.
74
75
  var SIG_HASH = { sha1: "SHA-1", sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" };
75
76
  // Is `name` a message-digest algorithm this verifier supports?
76
- function _supportedDigest(name) { return guard.intrinsic.hasOwn(DIGEST_HASH, name) && !!DIGEST_HASH[name]; }
77
+ function _supportedDigest(name) { return intrinsic.hasOwn(DIGEST_HASH, name) && !!DIGEST_HASH[name]; }
77
78
  // The digest of `content` under the named algorithm, resolved to a Buffer.
78
79
  function _computeDigest(name, content) {
79
80
  return subtle.digest(DIGEST_HASH[name], content).then(function (d) { return Buffer.from(d); });
package/lib/guard-all.js CHANGED
@@ -68,7 +68,11 @@ var compress = require("./guard-compress");
68
68
  var secret = require("./guard-secret");
69
69
  var parsed = require("./guard-parsed");
70
70
  var async_ = require("./guard-async");
71
- var intrinsic = require("./guard-intrinsic");
71
+ // guard-intrinsic is deliberately NOT re-exported here. Re-exporting it gave taking the captures a
72
+ // second spelling, and the check that holds a module to the discipline could then be satisfied
73
+ // around: a module reached them as `guard.intrinsic.<x>` and never entered scope, so its remaining
74
+ // live reads were never named. With one spelling, a module's own require IS the fact, and the check
75
+ // reads it off the loaded module graph rather than guessing at the text.
72
76
 
73
77
  module.exports = {
74
78
  bytes: bytes,
@@ -88,5 +92,4 @@ module.exports = {
88
92
  secret: secret,
89
93
  parsed: parsed,
90
94
  async: async_,
91
- intrinsic: intrinsic,
92
95
  };
@@ -55,6 +55,10 @@ var _bufferConcat = Buffer.concat;
55
55
  var _bufferIsBuffer = Buffer.isBuffer;
56
56
  var _bufferByteLength = Buffer.byteLength;
57
57
  var _bufferCompare = Buffer.compare;
58
+ // `Buffer.prototype.equals` is the byte-identity test a binding decides on: an OCSP CertID says the
59
+ // response is about this issuer, a responder ID says this key signed it. Dispatched live, one
60
+ // answering true makes a response about a DIFFERENT certificate match the one being asked about.
61
+ var _bufferEquals = uncurry(Buffer.prototype.equals);
58
62
 
59
63
  // The rest of what a guard reads from the runtime while it is deciding something. These are
60
64
  // grouped here rather than captured file by file so the set is enumerable in one place and a guard
@@ -268,6 +272,7 @@ module.exports = {
268
272
  isBuffer: _bufferIsBuffer,
269
273
  byteLength: _bufferByteLength,
270
274
  compare: _bufferCompare,
275
+ bufferEquals: _bufferEquals,
271
276
  isView: _isView,
272
277
  isInteger: _isInteger,
273
278
  isSafeInteger: _isSafeInteger,
@@ -29,6 +29,7 @@
29
29
 
30
30
  var asn1 = require("./asn1-der");
31
31
  var guard = require("./guard-all");
32
+ var intrinsic = require("./guard-intrinsic");
32
33
  var oid = require("./oid");
33
34
  var x509 = require("./schema-x509");
34
35
  var compositeSig = require("./composite-sig");
@@ -48,11 +49,16 @@ var OID_OCSP_SIGNING = oid.byName("ocspSigning");
48
49
  // Does this extended-key-usage list assert the given purpose? The answer IS an authorization
49
50
  // decision, so it goes through the membership guard rather than a prototype method. See guard-list.
50
51
  var _asserts = guard.list.contains;
52
+ // Captured at load. Each of these answers a binding question -- does this response concern the
53
+ // certificate being asked about, did this key sign it -- so a replacement made after this module
54
+ // loaded would decide the answer while every check around it still runs.
55
+ var _bufferFrom = intrinsic.bufferFrom;
56
+ var _bytesEqual = intrinsic.bufferEquals;
51
57
  var OID_OCSP_NOCHECK = oid.byName("ocspNoCheck");
52
58
  var OID_EKU = oid.byName("extKeyUsage");
53
59
  var OID_KEY_USAGE = oid.byName("keyUsage");
54
60
 
55
- function ocspDigest(alg, buf) { return subtle.digest(alg, buf).then(function (h) { return Buffer.from(h); }); }
61
+ function ocspDigest(alg, buf) { return subtle.digest(alg, buf).then(function (h) { return _bufferFrom(h); }); }
56
62
 
57
63
  // The subjectPublicKey BIT STRING value (past the unused-bits octet) of an SPKI DER: the exact
58
64
  // bytes an OCSP CertID issuerKeyHash / byKey KeyHash hash over (RFC 6960 sec. 4.1.1). Throws on a
@@ -108,12 +114,12 @@ function makeOcspVerify(deps) {
108
114
  // 5280 sec. 7.1-equal byte encoding of the validated issuer DN to try.
109
115
  async function ocspCertIdMatches(certID, cert, issuerNameCandidates, issuerKeyBits) {
110
116
  if (certID.serialNumberHex !== cert.serialNumberHex) return false;
111
- var hashName = guard.intrinsic.hasOwn(OCSP_CERTID_HASHES, certID.hashAlgorithm.oid) ? OCSP_CERTID_HASHES[certID.hashAlgorithm.oid] : null;
117
+ var hashName = intrinsic.hasOwn(OCSP_CERTID_HASHES, certID.hashAlgorithm.oid) ? OCSP_CERTID_HASHES[certID.hashAlgorithm.oid] : null;
112
118
  if (!hashName) return false;
113
119
  var keyHash = await ocspDigest(hashName, issuerKeyBits);
114
- if (!certID.issuerKeyHash.equals(keyHash)) return false;
120
+ if (!_bytesEqual(certID.issuerKeyHash, keyHash)) return false;
115
121
  for (var i = 0; i < issuerNameCandidates.length; i++) {
116
- if (certID.issuerNameHash.equals(await ocspDigest(hashName, issuerNameCandidates[i]))) return true;
122
+ if (_bytesEqual(certID.issuerNameHash, await ocspDigest(hashName, issuerNameCandidates[i]))) return true;
117
123
  }
118
124
  return false;
119
125
  }
@@ -126,7 +132,7 @@ function makeOcspVerify(deps) {
126
132
  var matchesIssuer = false;
127
133
  try {
128
134
  if (rid.byName) matchesIssuer = dnEqual(rid.byName.rdns, cert.issuer.rdns);
129
- else if (rid.byKey) matchesIssuer = rid.byKey.equals(await ocspDigest("SHA-1", issuerKeyBits));
135
+ else if (rid.byKey) matchesIssuer = _bytesEqual(rid.byKey, await ocspDigest("SHA-1", issuerKeyBits));
130
136
  } catch (_e) { matchesIssuer = false; }
131
137
  if (matchesIssuer) return issuer.workingPublicKey;
132
138
 
@@ -137,7 +143,7 @@ function makeOcspVerify(deps) {
137
143
  var identifies = false;
138
144
  try {
139
145
  if (rid.byName) identifies = dnEqual(rid.byName.rdns, rc.subject.rdns);
140
- else if (rid.byKey) identifies = rid.byKey.equals(await ocspDigest("SHA-1", ocspKeyValue(rc.subjectPublicKeyInfo.bytes)));
146
+ else if (rid.byKey) identifies = _bytesEqual(rid.byKey, await ocspDigest("SHA-1", ocspKeyValue(rc.subjectPublicKeyInfo.bytes)));
141
147
  } catch (_e) { identifies = false; }
142
148
  if (!identifies) continue;
143
149
  // The delegate MUST be issued directly by the CA that issued the target.
@@ -48,6 +48,7 @@ var cmpVerify = require("./cmp-verify");
48
48
  var cmsVerify = require("./cms-verify");
49
49
  var cmpSession = require("./cmp-session");
50
50
  var guard = require("./guard-all");
51
+ var intrinsic = require("./guard-intrinsic");
51
52
  var constants = require("./constants");
52
53
  var validator = require("./validator-all");
53
54
  var cms = require("./schema-cms");
@@ -1108,8 +1109,8 @@ function unrecognizedCriticalExtension(cert, isTarget) {
1108
1109
  for (var i = 0; i < cert.extensions.length; i++) {
1109
1110
  var ext = cert.extensions[i];
1110
1111
  if (!ext.critical) continue;
1111
- if (!guard.intrinsic.hasOwn(PROCESSED_EXTENSIONS, ext.oid)) return ext.oid;
1112
- if (isTarget && guard.intrinsic.hasOwn(TARGET_UNPROCESSED_IF_CRITICAL, ext.oid)) return ext.oid;
1112
+ if (!intrinsic.hasOwn(PROCESSED_EXTENSIONS, ext.oid)) return ext.oid;
1113
+ if (isTarget && intrinsic.hasOwn(TARGET_UNPROCESSED_IF_CRITICAL, ext.oid)) return ext.oid;
1113
1114
  }
1114
1115
  return null;
1115
1116
  }
@@ -1492,7 +1493,7 @@ async function validate(path, opts) {
1492
1493
  anchorPurposeApplied = true;
1493
1494
  // The map is the operator's RESTRICTION, so it is asked for its own entry: an inherited
1494
1495
  // `true` would grant the purpose on every anchor whose map does not name it.
1495
- if (!guard.intrinsic.hasOwn(ta.purposes, checkPurpose) || ta.purposes[checkPurpose] !== true) {
1496
+ if (!intrinsic.hasOwn(ta.purposes, checkPurpose) || ta.purposes[checkPurpose] !== true) {
1496
1497
  checks.push({ name: "purposeTrust", ok: false, code: "path/purpose-not-trusted" }); failed = true;
1497
1498
  }
1498
1499
  }
@@ -2633,7 +2634,7 @@ function _hasPurposeScopedMetadata(ta) {
2633
2634
  }
2634
2635
 
2635
2636
  function assertAnchorConstraints(ta, checkPurpose) {
2636
- var d = (checkPurpose && ta && ta.distrustAfter && guard.intrinsic.hasOwn(ta.distrustAfter, checkPurpose))
2637
+ var d = (checkPurpose && ta && ta.distrustAfter && intrinsic.hasOwn(ta.distrustAfter, checkPurpose))
2637
2638
  ? ta.distrustAfter[checkPurpose] : null;
2638
2639
  if (d == null) return null;
2639
2640
  return guard.time.assertValid(d, E, "path/bad-input", "trustAnchor.distrustAfter." + checkPurpose);
@@ -2754,9 +2755,9 @@ function _pushCandidates(frame, scored, stack, counter) {
2754
2755
  // Loop detection and the dedupe sets below are resource bounds, so they are asked through the
2755
2756
  // captured methods: a replaced `Set.prototype.has` answering false never prunes and the build
2756
2757
  // walks a cycle until a cap stops it.
2757
- if (guard.intrinsic.setHas(frame.keys, candKey)) continue;
2758
+ if (intrinsic.setHas(frame.keys, candKey)) continue;
2758
2759
  var childKeys = new Set(frame.keys);
2759
- guard.intrinsic.setAdd(childKeys, candKey);
2760
+ intrinsic.setAdd(childKeys, candKey);
2760
2761
  stack.push({ chain: [cand].concat(frame.chain), hop: frame.hop + 1, keys: childKeys });
2761
2762
  }
2762
2763
  return n;
@@ -2847,16 +2848,16 @@ async function _fetchAiaIssuers(current, aia) {
2847
2848
  // IP literals only (already validated above), never a hostname whose resolved address we cannot verify.
2848
2849
  if (net.isIP(u.hostname.replace(/^\[(.*)\]$/, "$1")) === 0 && !aia.transportGuardsAddresses) continue;
2849
2850
  u.hash = ""; // the fragment is never sent on the wire -> not part of the request/dedupe identity
2850
- if (guard.intrinsic.setHas(aia.fetchedUrls, u.href) || guard.intrinsic.setHas(seenThisCert, u.href)) continue; // dedupe (build-wide OR same-cert) on the normalized URL
2851
+ if (intrinsic.setHas(aia.fetchedUrls, u.href) || intrinsic.setHas(seenThisCert, u.href)) continue; // dedupe (build-wide OR same-cert) on the normalized URL
2851
2852
  if (seenThisCert.size >= aia.maxPerCert) break; // per-cert DISTINCT-url cap, checked BEFORE appending so maxAiaPerCert:0 collects nothing (no fetch at all)
2852
- guard.intrinsic.setAdd(seenThisCert, u.href);
2853
+ intrinsic.setAdd(seenThisCert, u.href);
2853
2854
  uris.push(u.href);
2854
2855
  }
2855
2856
  var out = [];
2856
2857
  for (var k = 0; k < uris.length; k++) {
2857
- if (guard.intrinsic.setHas(aia.fetchedUrls, uris[k])) continue; // a same-cert normalization-equal duplicate
2858
+ if (intrinsic.setHas(aia.fetchedUrls, uris[k])) continue; // a same-cert normalization-equal duplicate
2858
2859
  if (aia.fetches >= aia.maxFetches) break; // total budget reached -> STOP fetching (a SILENT cap, never a throw that denies a buildable path)
2859
- guard.intrinsic.setAdd(aia.fetchedUrls, uris[k]); // mark BEFORE the fetch -> fetched at most once, even on failure
2860
+ intrinsic.setAdd(aia.fetchedUrls, uris[k]); // mark BEFORE the fetch -> fetched at most once, even on failure
2860
2861
  aia.fetches += 1;
2861
2862
  var certs;
2862
2863
  try { certs = await _aiaFetchOne(uris[k], aia); }
@@ -2984,7 +2985,7 @@ async function build(leaf, opts) {
2984
2985
  // repeating CMS returning the same issuer) is appended AT MOST ONCE -- it otherwise inflates the pool, charging
2985
2986
  // the candidate budget and the ceiling for each copy before the issuer is ever evaluated.
2986
2987
  var poolDerKeys = new Set();
2987
- for (var pdk = 0; pdk < pool.length; pdk++) guard.intrinsic.setAdd(poolDerKeys, certDerKey(pool[pdk]));
2988
+ for (var pdk = 0; pdk < pool.length; pdk++) intrinsic.setAdd(poolDerKeys, certDerKey(pool[pdk]));
2988
2989
 
2989
2990
  if (!Array.isArray(opts.trustAnchors) || opts.trustAnchors.length === 0) throw E("path/bad-input", "build: opts.trustAnchors must be a non-empty array of anchor tuples or root certificates");
2990
2991
  var anchors = opts.trustAnchors.map(toAnchor);
@@ -3146,8 +3147,8 @@ async function build(leaf, opts) {
3146
3147
  var fetched = await _fetchAiaIssuers(fbCur, aiaCtx); // append newly-fetched issuer(s) to the SHARED pool
3147
3148
  for (var fj = 0; fj < fetched.length; fj++) {
3148
3149
  var fdk = certDerKey(fetched[fj]);
3149
- if (guard.intrinsic.setHas(poolDerKeys, fdk)) continue; // a byte-duplicate (mirror URL / repeating CMS) -> add once, never re-charge the budget/ceiling
3150
- guard.intrinsic.setAdd(poolDerKeys, fdk);
3150
+ if (intrinsic.setHas(poolDerKeys, fdk)) continue; // a byte-duplicate (mirror URL / repeating CMS) -> add once, never re-charge the budget/ceiling
3151
+ intrinsic.setAdd(poolDerKeys, fdk);
3151
3152
  if (pool.length < poolCeiling) pool.push(fetched[fj]);
3152
3153
  }
3153
3154
  }
@@ -48,6 +48,7 @@ var cmsDecrypt = require("./cms-decrypt"); // the @internal decryptEnvelopedDa
48
48
  var schemaPkcs12 = require("./schema-pkcs12");
49
49
  var pkix = require("./schema-pkix");
50
50
  var guard = require("./guard-all");
51
+ var intrinsic = require("./guard-intrinsic");
51
52
  var frameworkError = require("./framework-error");
52
53
  var C = require("./constants");
53
54
 
@@ -368,7 +369,7 @@ function _buildBag(bag, opts, depth) {
368
369
  if (!bag || typeof bag !== "object") throw _err("pkcs12/bad-input", "each bag must be an object with a type");
369
370
  // Checked against THIS type's table. An unrecognized type falls through to the switch below,
370
371
  // which is the one place that names the permitted set.
371
- if (guard.intrinsic.hasOwn(_BAG_KEYS_BY_TYPE, bag.type)) {
372
+ if (intrinsic.hasOwn(_BAG_KEYS_BY_TYPE, bag.type)) {
372
373
  guard.identifier.assertKnownKeys(bag, _BAG_KEYS_BY_TYPE[bag.type], _err, "pkcs12/bad-input", function (k) {
373
374
  return "unknown field " + JSON.stringify(k) + " on a " + JSON.stringify(bag.type) +
374
375
  " bag; that field is not read for this bag type";
@@ -588,7 +589,7 @@ async function _buildMacData(macOpts, sharedPassword, authSafeDer) {
588
589
  function _specForm(spec) {
589
590
  var isObject = !!spec && typeof spec === "object" && !Buffer.isBuffer(spec);
590
591
  var safeContents = isObject ? spec.safeContents : null;
591
- var full = guard.intrinsic.isArray(safeContents);
592
+ var full = intrinsic.isArray(safeContents);
592
593
  return { isObject: isObject, full: full, safeContents: full ? safeContents : null };
593
594
  }
594
595
 
@@ -599,7 +600,7 @@ function _normalizeSpec(spec, opts, form) {
599
600
  // be handed different values.
600
601
  var recipientCerts = opts.recipientCerts;
601
602
  var enveloped = recipientCerts != null;
602
- if (enveloped && (!guard.intrinsic.isArray(recipientCerts) || !recipientCerts.length)) throw _err("pkcs12/bad-input", "opts.recipientCerts must be a non-empty array of recipient certificates");
603
+ if (enveloped && (!intrinsic.isArray(recipientCerts) || !recipientCerts.length)) throw _err("pkcs12/bad-input", "opts.recipientCerts must be a non-empty array of recipient certificates");
603
604
  var certBags = [];
604
605
  if (spec.cert != null) certBags.push({ type: "cert", cert: spec.cert, friendlyName: spec.friendlyName, localKeyId: spec.localKeyId });
605
606
  if (spec.ca != null && !Array.isArray(spec.ca)) throw _err("pkcs12/bad-input", "spec.ca must be an array of certificates");
@@ -749,7 +750,7 @@ async function _build(spec, opts) {
749
750
  // array reached _buildMacData as an empty descriptor and produced the DEFAULT MAC. Excluded by
750
751
  // the same test opts.integrity already applies, read through the load-time capture so a
751
752
  // replaced Array.isArray cannot reopen it.
752
- if (opts.mac != null && opts.mac !== false && (typeof opts.mac !== "object" || guard.intrinsic.isArray(opts.mac))) throw _err("pkcs12/bad-input", "opts.mac must be false or a { algorithm, hash, salt, iterations, keyLength } object");
753
+ if (opts.mac != null && opts.mac !== false && (typeof opts.mac !== "object" || intrinsic.isArray(opts.mac))) throw _err("pkcs12/bad-input", "opts.mac must be false or a { algorithm, hash, salt, iterations, keyLength } object");
753
754
  if (opts.mac != null && opts.mac !== false) {
754
755
  // The algorithm selects the derivation, so the table that runs is the one belonging to it.
755
756
  // An unrecognized spelling keeps the union; _buildMacData refuses it by name a moment later.
@@ -23,10 +23,28 @@ var webcrypto = require("./webcrypto");
23
23
  var subtle = webcrypto.webcrypto.subtle;
24
24
  var validator = require("./validator-all");
25
25
  var guard = require("./guard-all");
26
+ var intrinsic = require("./guard-intrinsic");
26
27
  var compositeSig = require("./composite-sig");
27
28
  var b = asn1.build;
28
29
  function O(name) { return oid.byName(name); }
29
30
 
31
+ // Captured at load. This module decides which algorithm a signature runs under and whether the
32
+ // caller's key matches the certificate it claims, so an operation read at call time is one the
33
+ // caller answers: a replaced case fold reports two different algorithm names equal, and a replaced
34
+ // `Buffer.isBuffer` admits a key that is not bytes.
35
+ var _BigInt = intrinsic.BigInt;
36
+ var _String = intrinsic.String;
37
+ var _filter = intrinsic.filter;
38
+ var _stringify = intrinsic.stringify;
39
+ var _toUpperCase = intrinsic.toUpperCase;
40
+ var _isBuffer = intrinsic.isBuffer;
41
+ var _bufferFrom = intrinsic.bufferFrom;
42
+ // Asks the value's internal slot rather than `instanceof`, which reads the `Uint8Array` global at
43
+ // call time and answers about whichever constructor is bound then. It also settles the two cases
44
+ // `instanceof` gets wrong on their own terms: a real typed array from another realm is one, and an
45
+ // object merely built on the prototype is not.
46
+ var _isU8 = intrinsic.types.isUint8Array;
47
+
30
48
  var HASH = { sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512" };
31
49
  var NODE_DIGEST = { sha256: "sha256", sha384: "sha384", sha512: "sha512", shake128: "shake128", shake256: "shake256" };
32
50
  var PSS_SALT = { "SHA-256": 32, "SHA-384": 48, "SHA-512": 64 };
@@ -61,9 +79,9 @@ var MLDSA_SUITABLE_DIGEST = {
61
79
  // legitimate one; the RED vectors (a below-strength digest still refused with its name planted on
62
80
  // Object.prototype, on both the sign and verify paths) are the guard.
63
81
  function mldsaDigestSuitable(mlName, md) {
64
- if (!guard.intrinsic.hasOwn(MLDSA_SUITABLE_DIGEST, mlName)) return false;
82
+ if (!intrinsic.hasOwn(MLDSA_SUITABLE_DIGEST, mlName)) return false;
65
83
  var row = MLDSA_SUITABLE_DIGEST[mlName];
66
- return !!row && guard.intrinsic.hasOwn(row, md) && !!row[md];
84
+ return !!row && intrinsic.hasOwn(row, md) && !!row[md];
67
85
  }
68
86
 
69
87
  var SLHDSA_BY_OID = {};
@@ -79,7 +97,7 @@ function _algId(name, shape) { return shape === "null" ? b.sequence([b.oid(O(nam
79
97
  function _pssAlgId(digestName) {
80
98
  var hashAlg = b.sequence([b.oid(O(digestName)), b.nullValue()]);
81
99
  var mgf = b.sequence([b.oid(O("mgf1")), hashAlg]);
82
- var params = b.sequence([b.explicit(0, hashAlg), b.explicit(1, mgf), b.explicit(2, b.integer(BigInt(PSS_SALT[HASH[digestName]])))]);
100
+ var params = b.sequence([b.explicit(0, hashAlg), b.explicit(1, mgf), b.explicit(2, b.integer(_BigInt(PSS_SALT[HASH[digestName]])))]);
83
101
  return b.sequence([b.oid(O("rsassaPss")), params]);
84
102
  }
85
103
  // The hash an id-RSASSA-PSS SPKI restricts its key to, or null when it restricts none.
@@ -104,7 +122,7 @@ function _pssHashFromSpki(cert, E) {
104
122
  if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children) {
105
123
  throw E("unsupported-algorithm", "the id-RSASSA-PSS key parameters are not an RSASSA-PSS-params SEQUENCE (RFC 4055 sec. 3.1)");
106
124
  }
107
- var hashField = node.children.filter(function (c) { return c.tagClass === "context" && c.tagNumber === 0; })[0];
125
+ var hashField = _filter(node.children, function (c) { return c.tagClass === "context" && c.tagNumber === 0; })[0];
108
126
  // DEFAULT sha1Identifier -- an omitted hashAlgorithm names SHA-1, which this toolkit does not
109
127
  // sign or verify with, so it is reported as the pin it is and refused by the caller's own table.
110
128
  if (!hashField) return "sha1";
@@ -150,15 +168,15 @@ function resolveSignScheme(cert, so, noSignedAttrs, E) {
150
168
  var comp = compositeSig.COMPOSITE_ALGS[keyOid];
151
169
  if (comp) {
152
170
  if (comp.trad.unsupported) throw E("unsupported-algorithm", "composite " + comp.name + ": " + comp.trad.unsupported);
153
- if (so.digestAlgorithm && so.digestAlgorithm !== comp.phCms) throw E("bad-input", "composite " + comp.name + " fixes the digestAlgorithm to " + comp.phCms + " (draft-ietf-lamps-cms-composite-sigs sec. 3.4); " + JSON.stringify(so.digestAlgorithm) + " conflicts");
171
+ if (so.digestAlgorithm && so.digestAlgorithm !== comp.phCms) throw E("bad-input", "composite " + comp.name + " fixes the digestAlgorithm to " + comp.phCms + " (draft-ietf-lamps-cms-composite-sigs sec. 3.4); " + _stringify(so.digestAlgorithm) + " conflicts");
154
172
  return { composite: comp, digest: comp.phCms, digestAlgId: _algId(comp.phCms, "absent"), sigAlgId: _algId(comp.name, "absent") };
155
173
  }
156
174
  if (keyOid === O("rsaEncryption") || keyOid === O("rsassaPss")) {
157
175
  var isPssKey = keyOid === O("rsassaPss");
158
176
  var pinned = isPssKey ? _pssHashFromSpki(cert, E) : null;
159
- if (pinned && so.digestAlgorithm && so.digestAlgorithm !== pinned) throw E("bad-input", "the signer key restricts the RSASSA-PSS digest to " + pinned + ", but digestAlgorithm " + JSON.stringify(so.digestAlgorithm) + " was requested");
177
+ if (pinned && so.digestAlgorithm && so.digestAlgorithm !== pinned) throw E("bad-input", "the signer key restricts the RSASSA-PSS digest to " + pinned + ", but digestAlgorithm " + _stringify(so.digestAlgorithm) + " was requested");
160
178
  var d = so.digestAlgorithm || pinned || "sha256";
161
- if (!HASH[d]) throw E("unsupported-algorithm", "unsupported RSA digest algorithm " + JSON.stringify(d));
179
+ if (!HASH[d]) throw E("unsupported-algorithm", "unsupported RSA digest algorithm " + _stringify(d));
162
180
  if (so.pss || isPssKey) return { digest: d, digestAlgId: _algId(d, "absent"), sigAlgId: _pssAlgId(d), imp: { name: "RSA-PSS", hash: HASH[d] }, sign: { name: "RSA-PSS", saltLength: PSS_SALT[HASH[d]] }, ecdsaDer: false };
163
181
  var rsaSigAlgId = so.combinedRsaSig ? _algId(RSA_PKCS1_SIG[d], "null") : _algId("rsaEncryption", "null");
164
182
  return { digest: d, digestAlgId: _algId(d, "absent"), sigAlgId: rsaSigAlgId, imp: { name: "RSASSA-PKCS1-v1_5", hash: HASH[d] }, sign: { name: "RSASSA-PKCS1-v1_5" }, ecdsaDer: false };
@@ -170,13 +188,13 @@ function resolveSignScheme(cert, so, noSignedAttrs, E) {
170
188
  var ec = EC_BY_CURVE_OID[curveOid];
171
189
  if (!ec) throw E("unsupported-algorithm", "the signer key is on an unsupported EC curve");
172
190
  var de = so.digestAlgorithm || "sha256";
173
- if (!HASH[de]) throw E("unsupported-algorithm", "unsupported ECDSA digest algorithm " + JSON.stringify(de));
191
+ if (!HASH[de]) throw E("unsupported-algorithm", "unsupported ECDSA digest algorithm " + _stringify(de));
174
192
  return { digest: de, digestAlgId: _algId(de, "absent"), sigAlgId: _algId(ECDSA_ALG[de], "absent"), imp: { name: "ECDSA", namedCurve: ec.curve }, sign: { name: "ECDSA", hash: HASH[de] }, ecdsaDer: true, coordLen: ec.coordLen };
175
193
  }
176
194
  if (keyOid === O("Ed25519") || keyOid === O("Ed448")) {
177
195
  var name = keyOid === O("Ed25519") ? "Ed25519" : "Ed448";
178
196
  var dd = so.digestAlgorithm || (name === "Ed25519" ? "sha512" : "shake256");
179
- if (!NODE_DIGEST[dd]) throw E("unsupported-algorithm", "unsupported " + name + " digest algorithm " + JSON.stringify(dd));
197
+ if (!NODE_DIGEST[dd]) throw E("unsupported-algorithm", "unsupported " + name + " digest algorithm " + _stringify(dd));
180
198
  return { digest: dd, digestAlgId: _algId(dd, "absent"), sigAlgId: _algId(name, "absent"), imp: { name: name }, sign: { name: name }, ecdsaDer: false };
181
199
  }
182
200
  if (MLDSA_BY_OID[keyOid]) {
@@ -186,14 +204,14 @@ function resolveSignScheme(cert, so, noSignedAttrs, E) {
186
204
  md = "sha512";
187
205
  } else {
188
206
  md = so.digestAlgorithm || "sha512";
189
- if (!NODE_DIGEST[md]) throw E("unsupported-algorithm", "unsupported ML-DSA message digest " + JSON.stringify(md));
207
+ if (!NODE_DIGEST[md]) throw E("unsupported-algorithm", "unsupported ML-DSA message digest " + _stringify(md));
190
208
  if (!mldsaDigestSuitable(mlName, md)) throw E("unsupported-algorithm", "the " + md + " message digest is below the security strength of " + mlName + " (RFC 9882 sec. 3.3)");
191
209
  }
192
210
  return { digest: md, digestAlgId: _algId(md, "absent"), sigAlgId: _algId(oid.name(keyOid), "absent"), imp: { name: mlName }, sign: { name: mlName }, ecdsaDer: false };
193
211
  }
194
212
  if (SLHDSA_BY_OID[keyOid]) {
195
213
  var slh = SLHDSA_BY_OID[keyOid];
196
- if (so.digestAlgorithm && so.digestAlgorithm !== slh.digest) throw E("bad-input", "SLH-DSA " + slh.wc + " requires the " + slh.digest + " message digest (RFC 9814 sec. 4); digestAlgorithm " + JSON.stringify(so.digestAlgorithm) + " conflicts");
214
+ if (so.digestAlgorithm && so.digestAlgorithm !== slh.digest) throw E("bad-input", "SLH-DSA " + slh.wc + " requires the " + slh.digest + " message digest (RFC 9814 sec. 4); digestAlgorithm " + _stringify(so.digestAlgorithm) + " conflicts");
197
215
  return { digest: slh.digest, digestAlgId: _algId(slh.digest, "absent"), sigAlgId: _algId(oid.name(keyOid), "absent"), imp: { name: slh.wc }, sign: { name: slh.wc }, ecdsaDer: false };
198
216
  }
199
217
  throw E("unsupported-algorithm", "unsupported signer key algorithm " + keyOid);
@@ -204,7 +222,7 @@ function _assertKeyMatchesScheme(key, imp, E) {
204
222
  var ka = key.algorithm || {};
205
223
  // WebCrypto algorithm names are ASCII-case-insensitive (RSASSA-PKCS1-v1_5 and RSASSA-PKCS1-V1_5 are one
206
224
  // algorithm), so compare case-folded; a genuine mismatch (e.g. RSASSA-PKCS1-v1_5 vs ECDSA) still fails.
207
- if (String(ka.name).toUpperCase() !== String(imp.name).toUpperCase()) throw E("bad-input", "the signer CryptoKey algorithm (" + ka.name + ") does not match the certificate's key algorithm (" + imp.name + ")");
225
+ if (_toUpperCase(_String(ka.name)) !== _toUpperCase(_String(imp.name))) throw E("bad-input", "the signer CryptoKey algorithm (" + ka.name + ") does not match the certificate's key algorithm (" + imp.name + ")");
208
226
  if (imp.hash && (!ka.hash || ka.hash.name !== imp.hash)) throw E("bad-input", "the signer CryptoKey hash (" + (ka.hash && ka.hash.name) + ") does not match the signing digest (" + imp.hash + ")");
209
227
  if (imp.namedCurve && ka.namedCurve !== imp.namedCurve) throw E("bad-input", "the signer CryptoKey curve (" + ka.namedCurve + ") does not match the certificate curve (" + imp.namedCurve + ")");
210
228
  }
@@ -212,19 +230,19 @@ function _normPkcs8(k, label, E) {
212
230
  // A caller's own Buffer is borrowed, not copied, the same rule _importKey states below. Every
213
231
  // other form is copied, and neither copy is duplicated further, so a private key never gains a
214
232
  // plaintext duplicate this module cannot account for.
215
- if (Buffer.isBuffer(k)) return guard.bytes.view(k, E, "bad-input", label);
216
- if (k instanceof Uint8Array) return guard.bytes.snapshot(k, E, "bad-input", label);
233
+ if (_isBuffer(k)) return guard.bytes.view(k, E, "bad-input", label);
234
+ if (_isU8(k)) return guard.bytes.snapshot(k, E, "bad-input", label);
217
235
  if (typeof k === "string") { try { return pkcs8.pemDecode(k); } catch (e) { throw E("bad-input", label + " PEM could not be decoded", e); } }
218
236
  throw E("bad-input", label + " must be a PKCS#8 DER Buffer, Uint8Array, or PEM string");
219
237
  }
220
238
  function _normCompositeKeys(key, comp, E) {
221
- if (!key || typeof key !== "object" || Buffer.isBuffer(key) || key instanceof Uint8Array || key.mldsa == null || key.trad == null) {
239
+ if (!key || typeof key !== "object" || _isBuffer(key) || _isU8(key) || key.mldsa == null || key.trad == null) {
222
240
  throw E("bad-input", "a composite " + comp.name + " signer key must be { mldsa: <PKCS#8>, trad: <PKCS#8> }");
223
241
  }
224
242
  return { mldsa: _normPkcs8(key.mldsa, "the composite ML-DSA component key", E), trad: _normPkcs8(key.trad, "the composite traditional component key", E) };
225
243
  }
226
244
  function _importKey(key, imp, E) {
227
- if (key && typeof key === "object" && !Buffer.isBuffer(key) && !(key instanceof Uint8Array) && key.type === "private") {
245
+ if (key && typeof key === "object" && !_isBuffer(key) && !(_isU8(key)) && key.type === "private") {
228
246
  _assertKeyMatchesScheme(key, imp, E);
229
247
  // `key` is documented as taking a CryptoKey, so one created outside this engine signs here too --
230
248
  // it is re-imported through this engine, because it carries none of the key material this engine
@@ -236,8 +254,8 @@ function _importKey(key, imp, E) {
236
254
  // only thing that reads it. A caller's own Buffer is passed through untouched: they hold a live
237
255
  // reference and will use it again, so clearing it would destroy their key.
238
256
  var der, owned = false;
239
- if (Buffer.isBuffer(key)) der = guard.bytes.view(key, E, "bad-input", "the signer private key");
240
- else if (key instanceof Uint8Array) { der = guard.bytes.snapshot(key, E, "bad-input", "the signer private key"); owned = true; }
257
+ if (_isBuffer(key)) der = guard.bytes.view(key, E, "bad-input", "the signer private key");
258
+ else if (_isU8(key)) { der = guard.bytes.snapshot(key, E, "bad-input", "the signer private key"); owned = true; }
241
259
  else if (typeof key === "string") {
242
260
  try { der = pkcs8.pemDecode(key); }
243
261
  catch (e) { throw E("bad-input", "the signer PEM private key could not be decoded", e); }
@@ -260,11 +278,11 @@ function _importKey(key, imp, E) {
260
278
  // path signs both component keys and returns mldsaSig || tradSig (composite-sig.js owns it).
261
279
  function signOverTbs(scheme, key, signedBytes, E) {
262
280
  if (scheme.composite) {
263
- return compositeSig.compositeSign(scheme.composite, _normCompositeKeys(key, scheme.composite, E), signedBytes).then(function (sig) { return Buffer.from(sig); });
281
+ return compositeSig.compositeSign(scheme.composite, _normCompositeKeys(key, scheme.composite, E), signedBytes).then(function (sig) { return _bufferFrom(sig); });
264
282
  }
265
283
  return _importKey(key, scheme.imp, E).then(function (priv) {
266
284
  return subtle.sign(scheme.sign, priv, signedBytes).then(function (sigRaw) {
267
- var sig = Buffer.from(sigRaw);
285
+ var sig = _bufferFrom(sigRaw);
268
286
  if (scheme.ecdsaDer) sig = validator.sig.rawToEcdsaDer(sig, scheme.coordLen);
269
287
  return sig;
270
288
  });
package/lib/tsp-sign.js CHANGED
@@ -25,6 +25,7 @@ var pkiX509 = require("./schema-x509");
25
25
  var smime = require("./schema-smime");
26
26
  var schemaTsp = require("./schema-tsp");
27
27
  var guard = require("./guard-all");
28
+ var intrinsic = require("./guard-intrinsic");
28
29
  var frameworkError = require("./framework-error");
29
30
 
30
31
  var pkix = require("./schema-pkix");
@@ -507,7 +508,7 @@ var _BIND_DIGEST = { sha256: "sha256", sha384: "sha384", sha512: "sha512" };
507
508
  // tsp/* verdict code; a config-shape error throws (the caller's contract).
508
509
  function _imprintMatches(mi, data) {
509
510
  var name = mi.hashAlgorithm && mi.hashAlgorithm.name;
510
- var nodeAlg = (name && guard.intrinsic.hasOwn(_VERIFY_DIGEST, name)) ? _VERIFY_DIGEST[name] : null;
511
+ var nodeAlg = (name && intrinsic.hasOwn(_VERIFY_DIGEST, name)) ? _VERIFY_DIGEST[name] : null;
511
512
  if (!nodeAlg) return "tsp/unsupported-algorithm"; // cannot recompute -> never assume a match
512
513
  var wantLen = _VERIFY_DIGEST_LEN[name];
513
514
  // The token's messageImprint MUST be a full-length digest for its algorithm. A short/empty
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.5.20",
3
+ "version": "0.5.21",
4
4
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
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:e979b712-f27c-4c30-ba8e-4e9ab88715fd",
5
+ "serialNumber": "urn:uuid:353e5368-910e-4a85-a222-14a2add84cee",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-08-20T23:29:06.438Z",
8
+ "timestamp": "2026-08-21T02:50:54.975Z",
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.20",
22
+ "bom-ref": "@blamejs/pki@0.5.21",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.5.20",
25
+ "version": "0.5.21",
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.20",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.5.21",
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.20",
57
+ "ref": "@blamejs/pki@0.5.21",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]