@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/CHANGELOG.md +29 -0
- package/README.md +3 -3
- package/lib/asn1-der.js +6 -5
- package/lib/cmp-session.js +10 -3
- package/lib/cmp-verify.js +2 -3
- package/lib/compromised-keys.js +114 -0
- package/lib/crmf-sign.js +2 -0
- package/lib/csr-sign.js +4 -0
- package/lib/framework-error.js +20 -7
- package/lib/guard-distrust.js +128 -8
- package/lib/guard-verdict.js +16 -3
- package/lib/http-transport.js +128 -20
- package/lib/lint.js +25 -43
- package/lib/path-validate.js +23 -4
- package/lib/revocation-fetch.js +3 -2
- package/lib/trust.js +40 -2
- package/lib/webauthn-mds.js +3 -2
- package/lib/x509-sign.js +8 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
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
|
|
1379
|
-
*
|
|
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
|
});
|
package/lib/webauthn-mds.js
CHANGED
|
@@ -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
|
-
|
|
395
|
-
|
|
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
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:1f2b6ec8-3dcd-4f4f-abc6-92aeb5ba8795",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
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.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.9.7",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.9.
|
|
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.
|
|
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.
|
|
57
|
+
"ref": "@blamejs/pki@0.9.7",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|