@blamejs/pki 0.9.6 → 0.9.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/lib/trust.js CHANGED
@@ -1350,6 +1350,39 @@ function spkiDigest(cert) {
1350
1350
  return guard.distrust.keyDigest(spki);
1351
1351
  }
1352
1352
 
1353
+ /**
1354
+ * @primitive pki.trust.knownCompromisedKeys
1355
+ * @signature pki.trust.knownCompromisedKeys() -> Array<{ spkiSha256, name, reason, sources }>
1356
+ * @since 0.9.7
1357
+ * @status stable
1358
+ * @spec CA/Browser Forum TLS BR
1359
+ * @related pki.trust.spkiDigest, pki.path.validate, pki.transport.https, pki.x509.sign
1360
+ *
1361
+ * The keys every verb in this toolkit refuses because their private key is public. Each record holds
1362
+ * `spkiSha256`, the `pki.trust.spkiDigest` value of the key; `name`, the subject common name of a
1363
+ * certificate that carries it, or the product's CA name for a key published without a certificate;
1364
+ * `reason`, how the private key became public; and `sources`, the URLs where that is recorded. Each
1365
+ * record is frozen, and the array is a new one on every call.
1366
+ *
1367
+ * The toolkit also refuses an elliptic curve key on P-256, P-384, P-521 or secp256k1 whose private key
1368
+ * is an integer below 256, or below 256 short of the group order, whether or not it is listed here.
1369
+ *
1370
+ * Path validation fails a chain whose trust anchor, intermediate or target carries such a key with
1371
+ * `path/compromised-key`. `pki.trust.systemAnchors` skips such a certificate with the reason
1372
+ * `trust/compromised-key`. `pki.transport.https` refuses a server whose certificate chain carries one
1373
+ * with `transport/server-key-compromised`, and a proxy with `transport/proxy-key-compromised`.
1374
+ * `pki.x509.sign`, `pki.csr.sign` and `pki.crmf.build` refuse to certify, sign with or request a
1375
+ * certificate for one, with `x509/compromised-key`, `csr/compromised-key` and `crmf/compromised-key`.
1376
+ * None of these refusals can be turned off.
1377
+ *
1378
+ * @example
1379
+ * var listed = pki.trust.knownCompromisedKeys();
1380
+ * listed[0].spkiSha256.length; // 64
1381
+ */
1382
+ function knownCompromisedKeys() {
1383
+ return guard.distrust.knownCompromised();
1384
+ }
1385
+
1353
1386
  /**
1354
1387
  * @primitive pki.trust.systemAnchors
1355
1388
  * @signature pki.trust.systemAnchors(opts?) -> { anchors, intermediates, source, path, skipped }
@@ -1375,8 +1408,10 @@ function spkiDigest(cert) {
1375
1408
  * neither list.
1376
1409
  *
1377
1410
  * A certificate whose key is listed in `opts.distrustedKeys` is neither an anchor nor an
1378
- * intermediate; it is listed in `skipped` with the reason `trust/distrusted-key`. A bundle left with
1379
- * no anchor is refused with `trust/no-anchors`.
1411
+ * intermediate; it is listed in `skipped` with the reason `trust/distrusted-key`. A certificate whose
1412
+ * private key is public (see `pki.trust.knownCompromisedKeys`) is skipped the same way with the reason
1413
+ * `trust/compromised-key`, whether or not a list is given. A bundle left with no anchor is refused
1414
+ * with `trust/no-anchors`.
1380
1415
  *
1381
1416
  * A host whose roots are not in a readable PEM file is refused, and the refusal names the command
1382
1417
  * that exports them. macOS keeps its roots in a binary keychain and Windows in a registry-backed
@@ -1465,6 +1500,8 @@ function systemAnchors(opts) {
1465
1500
  * in an attribute value) is reported rather than thrown, so one malformed entry does not refuse the
1466
1501
  * whole bundle. */
1467
1502
  function _systemEntryRefusal(cert, distrusted) {
1503
+ var compromised = guard.distrust.compromised(cert.subjectPublicKeyInfo.bytes);
1504
+ if (compromised !== null) return { intermediate: false, reason: "trust/compromised-key: " + compromised };
1468
1505
  if (guard.distrust.has(distrusted, cert.subjectPublicKeyInfo.bytes)) {
1469
1506
  return { intermediate: false, reason: "trust/distrusted-key: the certificate's key is listed in opts.distrustedKeys" };
1470
1507
  }
@@ -1629,4 +1666,5 @@ module.exports = intrinsic.freeze({
1629
1666
  discoversBundles: discoversBundles,
1630
1667
  anchor: anchor,
1631
1668
  spkiDigest: spkiDigest,
1669
+ knownCompromisedKeys: knownCompromisedKeys,
1632
1670
  });
@@ -391,8 +391,9 @@ function _chainToAnchor(chain, anchors, at, what, code, distrusted) {
391
391
  var strippedAnchor = false;
392
392
  if (path.length && _isAnchorItself(path[0], anchor)) { path = intrinsic.copyList(path, 1); strippedAnchor = true; }
393
393
  if (path.length === 0) {
394
- if (strippedAnchor && guard.distrust.has(distrusted, anchor.subjectPublicKeyInfo.bytes)) {
395
- lastFault = _err(faultCode, "the " + subject + " is a trust anchor whose key is listed in opts.distrustedKeys");
394
+ var anchorRefusal = strippedAnchor ? guard.distrust.refusal(distrusted, anchor.subjectPublicKeyInfo.bytes, "opts.distrustedKeys") : null;
395
+ if (anchorRefusal !== null) {
396
+ lastFault = _err(faultCode, "the " + subject + " is a trust anchor whose key " + anchorRefusal);
396
397
  return false;
397
398
  }
398
399
  return strippedAnchor;
package/lib/x509-sign.js CHANGED
@@ -384,11 +384,13 @@ function _resolveAltSigning(issuer, extSpec, issuerCert, selfSigned) {
384
384
  KNOWN_ALT_ISSUER_KEYS, _err, "x509/bad-input", "unknown alternative issuer field: ");
385
385
  var subjectAltSpki = _reqDer(subjectAltRaw, "extensions.subjectAltPublicKeyInfo (the subject's alternative SPKI DER)");
386
386
  _assertValidSpki(subjectAltSpki, "extensions.subjectAltPublicKeyInfo");
387
+ guard.distrust.refuseCompromised(subjectAltSpki, _err, "x509/compromised-key", "extensions.subjectAltPublicKeyInfo");
387
388
  if (issuer.altPublicKey == null) {
388
389
  throw _err("x509/bad-input", "an issuer altKey needs issuer.altPublicKey, the SPKI of its public half, to resolve the alternative signature algorithm");
389
390
  }
390
391
  var issuerAltSpki = _reqDer(issuer.altPublicKey, "issuer.altPublicKey (the issuer's alternative SPKI DER)");
391
392
  _assertValidSpki(issuerAltSpki, "issuer.altPublicKey");
393
+ guard.distrust.refuseCompromised(issuerAltSpki, _err, "x509/compromised-key", "issuer.altPublicKey");
392
394
  /** @internal Bound to the issuer, the way the native signature already is: its key is read OUT of
393
395
  * issuer.cert, while this one arrives beside the certificate, so nothing otherwise stops a CA signing
394
396
  * with a second alternative pair. Clause 9.8.4 has a relying party check an alternative signature with
@@ -555,7 +557,9 @@ function _hasCriticalSan(extSpec) {
555
557
  * fail-closed into its form (`"host.example"`, `"a@b.example"`, `"10.0.0.1"`, `"https://host.example/"`).
556
558
  * The signature algorithm is resolved from the signing key: RSA (PKCS#1 v1.5
557
559
  * or PSS via `opts.pss`), ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm, so every algorithm the
558
- * toolkit signs with is available here without a per-algorithm branch.
560
+ * toolkit signs with is available here without a per-algorithm branch. A subject key, issuer key or
561
+ * alternative key whose private key is public (see `pki.trust.knownCompromisedKeys`) is refused with
562
+ * `x509/compromised-key`.
559
563
  *
560
564
  * The version is derived from the field set (v3 when extensions are present, else v1). Two
561
565
  * extensions are emitted without being named, because RFC 5280 places them on the issuing CA: a
@@ -741,6 +745,7 @@ function _sign(spec, issuer, opts) {
741
745
 
742
746
  var spki = _reqDer(spec.subjectPublicKey, "spec.subjectPublicKey (the SPKI DER of the certified key)");
743
747
  _assertValidSpki(spki, "spec.subjectPublicKey");
748
+ guard.distrust.refuseCompromised(spki, _err, "x509/compromised-key", "spec.subjectPublicKey");
744
749
  var subjectDer = _encodeName(spec.subject == null ? [] : spec.subject);
745
750
  var subjectEmpty = _isEmptyName(subjectDer);
746
751
 
@@ -754,10 +759,12 @@ function _sign(spec, issuer, opts) {
754
759
  issuerPathLen = _assertIssuerIsCa(issuerCert);
755
760
  issuerDer = pkiBuild.tbsNameField(issuerCert, "subject");
756
761
  issuerSpki = issuerCert.subjectPublicKeyInfo.bytes;
762
+ guard.distrust.refuseCompromised(issuerSpki, _err, "x509/compromised-key", "the key of issuer.cert");
757
763
  } else {
758
764
  issuerDer = _encodeName(issuer.name == null ? [] : issuer.name);
759
765
  issuerSpki = _reqDer(issuer.publicKey, "issuer.publicKey (the issuer SPKI DER)");
760
766
  _assertValidSpki(issuerSpki, "issuer.publicKey");
767
+ guard.distrust.refuseCompromised(issuerSpki, _err, "x509/compromised-key", "issuer.publicKey");
761
768
  }
762
769
  if (_isEmptyName(issuerDer)) throw _err("x509/bad-issuer", "issuer must be a non-empty distinguished name");
763
770
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
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:114691ca-67be-44e1-9f29-b49116312f56",
5
+ "serialNumber": "urn:uuid:1f2b6ec8-3dcd-4f4f-abc6-92aeb5ba8795",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-10-21T08:20:31.429Z",
8
+ "timestamp": "2026-10-21T20:18:37.023Z",
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.6",
22
+ "bom-ref": "@blamejs/pki@0.9.7",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.9.6",
25
+ "version": "0.9.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.9.6",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.9.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.9.6",
57
+ "ref": "@blamejs/pki@0.9.7",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]