@blamejs/pki 0.9.0 → 0.9.3

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.
@@ -1811,6 +1811,12 @@ function _acceptExtensionRecord(ext, E, code, key) {
1811
1811
  function keyUsagePermits(ns, allowed) {
1812
1812
  var kuOid = ns.oid.byName("keyUsage");
1813
1813
  var decoders = certExtensionDecoders(ns).byOid;
1814
+ /** @internal `allowed` may be a FUNCTION of the certificate, for a caller whose permitted set
1815
+ * depends on the subject key: an establishment bit authorizes nothing where the key's own family
1816
+ * cannot perform that establishment mechanism, and the certificate is the only place that says
1817
+ * which family it is. Every name the set names must be one the KeyUsage decoder defines, since
1818
+ * the record it answers from inherits nothing. */
1819
+ var perCert = typeof allowed === "function";
1814
1820
  return function (parsedCert) {
1815
1821
  var exts = (parsedCert && parsedCert.extensions) || [];
1816
1822
  for (var i = 0; i < exts.length; i++) {
@@ -1818,13 +1824,60 @@ function keyUsagePermits(ns, allowed) {
1818
1824
  var ku;
1819
1825
  try { ku = decoders[kuOid](exts[i].value); }
1820
1826
  catch (e) { void e; return false; }
1821
- for (var a = 0; a < allowed.length; a++) if (ku[allowed[a]] === true) return true;
1827
+ var names = perCert ? allowed(parsedCert) : allowed;
1828
+ for (var a = 0; a < names.length; a++) if (ku[names[a]] === true) return true;
1822
1829
  return false;
1823
1830
  }
1824
1831
  return true;
1825
1832
  };
1826
1833
  }
1827
1834
 
1835
+ /** @internal Which key-establishment bit a subject key's own family can carry, as one table the
1836
+ * callers that read an establishment usage share. The governing documents state it per family:
1837
+ * RFC 3279 sec. 2.3.1 lists keyEncipherment for an RSA key and does not list keyAgreement; RFC 5480
1838
+ * sec. 3 lists keyAgreement for id-ecPublicKey, id-ecDH and id-ecMQV, and RFC 8813 sec. 3 makes
1839
+ * keyEncipherment and dataEncipherment a MUST NOT for all three; RFC 8410 sec. 5 makes keyAgreement
1840
+ * a MUST for id-X25519 and id-X448; and RFC 9935 sec. 5 has an ML-KEM certificate assert
1841
+ * keyEncipherment as its only usage.
1842
+ *
1843
+ * A family in NEITHER column answers null, which is the refusing direction: an establishment bit
1844
+ * then authorizes nothing, and a signature bit is what the certificate has to assert. */
1845
+ var KEY_TRANSPORT_KEY_NAMES = ["rsaEncryption", "rsaesOaep"];
1846
+ /** @internal The three FIPS 203 sets and the composite KEM arms, matched by the prefix the registry
1847
+ * files them under rather than listed one at a time, so a set the registry gains later belongs to
1848
+ * this rule without a second edit. */
1849
+ var KEY_TRANSPORT_KEY_PREFIXES = ["id-ml-kem-", "id-MLKEM"];
1850
+ var KEY_AGREEMENT_KEY_NAMES = ["ecPublicKey", "X25519", "X448", "dhpublicnumber"];
1851
+
1852
+ var _estabBitByNs = new _WeakMapI();
1853
+
1854
+ function keyEstablishmentBit(ns) {
1855
+ var table = _weakGet(_estabBitByNs, ns.oid);
1856
+ if (table === undefined) {
1857
+ table = _assign(_create(null), {});
1858
+ var put = function (bit) {
1859
+ return function (n) {
1860
+ var dotted = ns.oid.byName(n);
1861
+ if (dotted !== undefined && dotted !== null) table[dotted] = bit;
1862
+ };
1863
+ };
1864
+ _forEach(KEY_TRANSPORT_KEY_NAMES, put("keyEncipherment"));
1865
+ _forEach(KEY_AGREEMENT_KEY_NAMES, put("keyAgreement"));
1866
+ var registry = ns.oid.all();
1867
+ _forEach(intrinsic.keys(registry), function (dotted) {
1868
+ var n = registry[dotted];
1869
+ if (typeof n !== "string") return;
1870
+ _forEach(KEY_TRANSPORT_KEY_PREFIXES, function (p) {
1871
+ if (_strIndexOf(n, p) === 0) table[dotted] = "keyEncipherment";
1872
+ });
1873
+ });
1874
+ _weakSet(_estabBitByNs, ns.oid, table);
1875
+ }
1876
+ return function (dotted) {
1877
+ return typeof dotted === "string" && _hasOwn(table, dotted) ? table[dotted] : null;
1878
+ };
1879
+ }
1880
+
1828
1881
  /** @internal The subject-key algorithms that can only ever SIGN, as one table the linter and the verifier
1829
1882
  * share. RFC 9883 sec. 6 states "The privateKeyPossessionStatement attribute MUST NOT be used to obtain a
1830
1883
  * signature certificate", and sec. 4 states the same requirement as a property of the request: "the
@@ -1912,8 +1965,13 @@ function certExtensionDecoders(ns) {
1912
1965
  for (var z = 0; z < bs.bytes.length; z++) { if (bs.bytes[z] !== 0) { anyBit = true; break; } }
1913
1966
  if (!anyBit) throw ns.E(C, "KeyUsage must assert at least one bit (RFC 5280 sec. 4.2.1.3)");
1914
1967
  schema.assertMinimalNamedBits(bs.unusedBits, bs.bytes, function (m) { throw ns.E(C, m); });
1915
- // allow:registry-table-inherits-object-prototype -- the decoded KeyUsage record, whose keys are the fixed KU_BITS names the schema declares; the certificate supplies none of them
1916
- var out = {};
1968
+ /** @internal The record inherits nothing. The exemption this carried rested on its keys being
1969
+ * the fixed KU_BITS names, and `keyUsagePermits` is called with a name that is NOT one of them:
1970
+ * `contentCommitment` is the X.509 spelling of the nonRepudiation bit and the BUILDER accepts
1971
+ * it, so an authorization gate asked for a name this decoder never defines and whatever
1972
+ * answered for it decided. Measured: a certificate confined to cRLSign went from maySign=false
1973
+ * to maySign=true under one assignment to `Object.prototype.contentCommitment`. */
1974
+ var out = _create(null);
1917
1975
  _forEach(KU_BITS, function (nm, bit) {
1918
1976
  var byte = bit >> 3, mask = 0x80 >> (bit & 7);
1919
1977
  out[nm] = byte < bs.bytes.length && (bs.bytes[byte] & mask) !== 0;
@@ -2725,6 +2783,7 @@ module.exports = intrinsic.freeze({
2725
2783
  crlEntryFixedCriticality: crlEntryFixedCriticality,
2726
2784
  certExtensionDecoders: certExtensionDecoders,
2727
2785
  keyUsagePermits: keyUsagePermits,
2786
+ keyEstablishmentBit: keyEstablishmentBit,
2728
2787
  signatureOnlyKeyOid: signatureOnlyKeyOid,
2729
2788
  scopedCertDecoder: scopedCertDecoder,
2730
2789
  crlExtensionDecoders: crlExtensionDecoders,
@@ -359,7 +359,7 @@ var TRUST_ANCHOR_LIST = schema.seqOf(TRUST_ANCHOR_CHOICE, {
359
359
  * var pair = await pki.key.generate("Ed25519");
360
360
  * var spki = await pki.key.export(pair.publicKey);
361
361
  * var der = pki.trustanchor.build({ anchors: [{ taInfo: {
362
- * pubKey: spki, keyId: Buffer.alloc(20, 1), certPath: { taName: "CN=Example Root" } } }] });
362
+ * pubKey: spki, keyId: Buffer.alloc(20, 1), certPath: { taName: "Example Root" } } }] });
363
363
  * pki.schema.trustanchor.parse(der).anchors[0].kind; // -> "taInfo"
364
364
  */
365
365
  var parse = pkix.makeParser({
@@ -385,7 +385,7 @@ var parse = pkix.makeParser({
385
385
  * var pair = await pki.key.generate("Ed25519");
386
386
  * var spki = await pki.key.export(pair.publicKey);
387
387
  * var list = pki.schema.trustanchor.parse(pki.trustanchor.build({ anchors: [{ taInfo: {
388
- * pubKey: spki, keyId: Buffer.alloc(20, 1), certPath: { taName: "CN=Example Root" } } }] }));
388
+ * pubKey: spki, keyId: Buffer.alloc(20, 1), certPath: { taName: "Example Root" } } }] }));
389
389
  * list.anchors[0].taInfo.certPath.taName.dn; // -> "CN=Example Root"
390
390
  */
391
391
  var parseInfo = pkix.makeParser({
@@ -407,7 +407,7 @@ var parseInfo = pkix.makeParser({
407
407
  * var pair = await pki.key.generate("Ed25519");
408
408
  * var der = pki.trustanchor.build({ anchors: [{ taInfo: {
409
409
  * pubKey: await pki.key.export(pair.publicKey), keyId: Buffer.alloc(20, 1),
410
- * certPath: { taName: "CN=Example Root" } } }] });
410
+ * certPath: { taName: "Example Root" } } }] });
411
411
  * pki.schema.trustanchor.pemDecode(pki.schema.trustanchor.pemEncode(der)).equals(der); // -> true
412
412
  */
413
413
  function pemDecode(text, label) { return pkix.pemDecode(text, label || "TRUST ANCHOR LIST", PemError); }
@@ -426,7 +426,7 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "TRUST AN
426
426
  * var pair = await pki.key.generate("Ed25519");
427
427
  * var der = pki.trustanchor.build({ anchors: [{ taInfo: {
428
428
  * pubKey: await pki.key.export(pair.publicKey), keyId: Buffer.alloc(20, 1),
429
- * certPath: { taName: "CN=Example Root" } } }] });
429
+ * certPath: { taName: "Example Root" } } }] });
430
430
  * pki.schema.trustanchor.pemEncode(der).indexOf("-----BEGIN TRUST ANCHOR LIST-----"); // -> 0
431
431
  */
432
432
  function pemEncode(der, label) { return pkix.pemEncode(der, label || "TRUST ANCHOR LIST", PemError); }
package/lib/sigstore.js CHANGED
@@ -1383,7 +1383,7 @@ async function _verifyEmbeddedScts(leaf, leafDer, issuer, logs, timeMs, atMs) {
1383
1383
  /** @internal A receipt the verifier refuses structurally counted as verified would be the whole leg
1384
1384
  * lost, so a fault is an unverified receipt and the next one is still tried. */
1385
1385
  function _sctVerifies(entry, sct, spki) {
1386
- return _ctVerifySct(entry, sct, spki).then(function (ok) { return ok === true; }, function () { return false; });
1386
+ return intrinsic.chain(_ctVerifySct(entry, sct, spki), function (ok) { return ok === true; }, function () { return false; });
1387
1387
  }
1388
1388
 
1389
1389
  /** @internal A receipt's own instant, in milliseconds. The parser's `timestampMs` is this same value
package/lib/smime.js CHANGED
@@ -269,7 +269,6 @@ function _declaresHp(content) {
269
269
 
270
270
  function _noneSurface() { return { protectedHeaders: null, headerProtection: { present: false, mode: null, fromMismatch: null, confidential: [], legacy: null } }; }
271
271
 
272
- function _contentTypeCount(headers) { var n = 0; headers.forEach(function (h) { if (h.lname === "content-type") n++; }); return n; }
273
272
 
274
273
  var RFC5322_SINGLETON = new Set(["date", "from", "sender", "reply-to", "to", "cc", "bcc", "message-id", "in-reply-to", "references", "subject", "return-path"]);
275
274
 
@@ -334,7 +333,6 @@ function _legacyHpSurface(canon, outerEnt, mode, enabled) {
334
333
  if (partC.contentType.type !== "message/rfc822") return _noneSurface();
335
334
  partD = mime.parse(partC.body, SmimeError, "smime/bad-header-protection");
336
335
  } catch (_e) { return _noneSurface(); }
337
- if (_contentTypeCount(partC.headers) > 1 || _contentTypeCount(partD.headers) > 1) return _noneSurface();
338
336
  if (mime.hasParam(partD.contentType.value, "hp")) return _noneSurface();
339
337
  if (_isCryptoLayer(partD.contentType.type)) return _noneSurface();
340
338
  var ex = _extractProtected(partD.headers);
@@ -459,8 +457,24 @@ function _readDescriptor(s, position) {
459
457
  var names = Object.getOwnPropertyNames(proto);
460
458
  for (var n = 0; n < names.length; n++) {
461
459
  var name = names[n];
462
- if ((name === "constructor" && proto !== s) || seen[name] === 1) continue;
463
- if (proto !== s && _isMethod(proto, name)) continue;
460
+ if (seen[name] === 1) continue;
461
+ /** @internal The three names this walk declines: a prototype level's `constructor`, which is
462
+ * the class rather than a field; a prototype level's method, likewise; and a callable
463
+ * descriptor's own function machinery, which `guard.identifier` knows the names of and which
464
+ * copying turned a legitimate callable into a descriptor `pki.cms.sign`'s own unknown-field
465
+ * door then refused.
466
+ *
467
+ * A declined name is marked SEEN, because the value `s[name]` reads is the one the NEAREST
468
+ * level holds and a deeper property of that name is shadowed and unreadable. Measured before
469
+ * the marking: a derived class's own `length` was declined and then read off the base class
470
+ * as a field; a generator's own `prototype` was declined and then its generator-prototype
471
+ * OBJECT was walked, which read as a signer nested too deeply to copy; and a prototype-level
472
+ * method was declined and then a data property of the same name one level up was copied,
473
+ * which set the field to the method shadowing it and reached the digest resolver as
474
+ * `unsupported RSA digest algorithm undefined`. */
475
+ if ((name === "constructor" && proto !== s) ||
476
+ (proto !== s && _isMethod(proto, name)) ||
477
+ (proto === s && guard.identifier.isOwnMachineryName(s, name))) { seen[name] = 1; continue; }
464
478
  seen[name] = 1;
465
479
  if (++fields > DESCRIPTOR_MAX_FIELDS) throw _err("smime/bad-input", "signer " + position + " carries more than " + DESCRIPTOR_MAX_FIELDS + " fields");
466
480
  copy[name] = s[name];
@@ -553,9 +567,6 @@ function _hpSurface(content, outerEnt, expectedMode, authenticated, legacyEnable
553
567
  var canon = mime.canonicalizeText(content);
554
568
  if (!_declaresHp(canon)) return _legacyHpSurface(canon, outerEnt, expectedMode, legacyEnabled);
555
569
  var inner = mime.parse(canon, SmimeError, "smime/bad-header-protection");
556
- var ctCount = _contentTypeCount(inner.headers);
557
- if (ctCount > 1) throw _err("smime/bad-header-protection", "a header-protected payload must carry exactly one Content-Type field (found " + ctCount + ")");
558
- if (mime.paramNameCount(inner.contentType.value, "hp") > 1) throw _err("smime/bad-header-protection", "a header-protected payload declares the hp parameter more than once");
559
570
  var raw = inner.contentType.params.hp;
560
571
  if (raw === undefined) throw _err("smime/bad-header-protection", "a header-protected payload declares a bare hp parameter with no value (expected hp=\"clear\" or hp=\"cipher\")");
561
572
  var hp = raw.toLowerCase();
package/lib/trust.js CHANGED
@@ -1156,7 +1156,7 @@ function _subtreeOf(st) { return { tag: st.base.tagNumber, base: st.base.value }
1156
1156
  * var pair = await pki.key.generate("Ed25519");
1157
1157
  * var der = pki.trustanchor.build({ anchors: [{ taInfo: {
1158
1158
  * pubKey: await pki.key.export(pair.publicKey), keyId: Buffer.alloc(20, 1),
1159
- * certPath: { taName: "CN=Example Root" } } }] });
1159
+ * certPath: { taName: "Example Root" } } }] });
1160
1160
  * pki.trust.parseTrustAnchorList(der).anchors.length; // -> 1
1161
1161
  */
1162
1162
  function parseTrustAnchorList(input, opts) {
@@ -249,7 +249,7 @@ function _anchorDer(spec, i) {
249
249
  * pubKey: await pki.key.export(pair.publicKey),
250
250
  * keyId: Buffer.alloc(20, 1),
251
251
  * taTitle: "Example Root CA",
252
- * certPath: { taName: "CN=Example Root", pathLenConstraint: 2 } } }] });
252
+ * certPath: { taName: "Example Root", pathLenConstraint: 2 } } }] });
253
253
  * pki.schema.trustanchor.parse(der).anchors[0].taInfo.certPath.pathLenConstraint; // -> 2
254
254
  */
255
255
  function build(spec, opts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.9.0",
3
+ "version": "0.9.3",
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:f52bf92f-41f7-4879-bff9-6077f2cf84f5",
5
+ "serialNumber": "urn:uuid:a3e510f1-a551-49fe-aabf-02db532556c6",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-10-08T01:34:26.318Z",
8
+ "timestamp": "2026-10-09T01:08:02.322Z",
9
9
  "lifecycles": [
10
10
  {
11
11
  "phase": "build"
@@ -19,14 +19,14 @@
19
19
  }
20
20
  ],
21
21
  "component": {
22
- "bom-ref": "@blamejs/pki@0.9.0",
22
+ "bom-ref": "@blamejs/pki@0.9.3",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.9.0",
25
+ "version": "0.9.3",
26
26
  "scope": "required",
27
27
  "author": "blamejs contributors",
28
28
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
29
- "purl": "pkg:npm/%40blamejs/pki@0.9.0",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.9.3",
30
30
  "properties": [],
31
31
  "externalReferences": [
32
32
  {
@@ -54,7 +54,7 @@
54
54
  "components": [],
55
55
  "dependencies": [
56
56
  {
57
- "ref": "@blamejs/pki@0.9.0",
57
+ "ref": "@blamejs/pki@0.9.3",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]