@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.
- package/CHANGELOG.md +36 -0
- package/MIGRATING.md +103 -0
- package/README.md +5 -5
- package/lib/alt-sig.js +3 -3
- package/lib/attrcert-sign.js +5 -5
- package/lib/cmc-verify.js +13 -9
- package/lib/cmp-build.js +2 -2
- package/lib/cmp-verify.js +1 -1
- package/lib/cms-sign.js +21 -0
- package/lib/cms-verify.js +5 -3
- package/lib/constants.js +3 -0
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/csr-sign.js +4 -2
- package/lib/est.js +93 -21
- package/lib/guard-identifier.js +110 -0
- package/lib/guard-name.js +91 -4
- package/lib/inspect.js +4 -4
- package/lib/lint.js +2 -2
- package/lib/mime.js +124 -17
- package/lib/ocsp-verify.js +1 -1
- package/lib/pki-build.js +14 -0
- package/lib/possession.js +20 -14
- package/lib/related-cert.js +72 -11
- package/lib/schema-attrcert.js +5 -5
- package/lib/schema-c509.js +42 -6
- package/lib/schema-cmp.js +3 -3
- package/lib/schema-pkix.js +62 -3
- package/lib/schema-trustanchor.js +4 -4
- package/lib/sigstore.js +1 -1
- package/lib/smime.js +18 -7
- package/lib/trust.js +1 -1
- package/lib/trustanchor-build.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-pkix.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
1916
|
-
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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)
|
|
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 (
|
|
463
|
-
|
|
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: "
|
|
1159
|
+
* certPath: { taName: "Example Root" } } }] });
|
|
1160
1160
|
* pki.trust.parseTrustAnchorList(der).anchors.length; // -> 1
|
|
1161
1161
|
*/
|
|
1162
1162
|
function parseTrustAnchorList(input, opts) {
|
package/lib/trustanchor-build.js
CHANGED
|
@@ -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: "
|
|
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
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:a3e510f1-a551-49fe-aabf-02db532556c6",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
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.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.9.3",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.9.
|
|
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.
|
|
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.
|
|
57
|
+
"ref": "@blamejs/pki@0.9.3",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|