@blamejs/pki 0.3.26 → 0.3.27
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 +8 -0
- package/README.md +2 -3
- package/index.js +2 -2
- package/lib/acme.js +4 -12
- package/lib/cmp-build.js +3 -1
- package/lib/cmp-session.js +1045 -0
- package/lib/cmp-verify.js +23 -5
- package/lib/path-validate.js +3 -0
- package/lib/sleep.js +23 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cmp-verify.js
CHANGED
|
@@ -94,7 +94,7 @@ function _verdict(m, type, protectionAlg, valid, trusted, code, reason, signer)
|
|
|
94
94
|
trusted: trusted,
|
|
95
95
|
protectionType: type,
|
|
96
96
|
protectionAlg: protectionAlg ? { oid: protectionAlg.oid, name: protectionAlg.name || null } : null,
|
|
97
|
-
signer: signer ? { cert: signer.der, spki: signer.spki, subject: signer.subject } : null,
|
|
97
|
+
signer: signer ? { cert: signer.der, spki: signer.spki, subject: signer.subject, chain: signer.chain || null } : null,
|
|
98
98
|
transactionID: m.header.transactionID || null,
|
|
99
99
|
senderNonce: m.header.senderNonce || null,
|
|
100
100
|
recipNonce: m.header.recipNonce || null,
|
|
@@ -496,6 +496,10 @@ async function _verifySignature(m, protectedPart, protectionAlg, protection, opt
|
|
|
496
496
|
// the caller to anchor. With a trust store the signer cert gets the FULL RFC 5280 sec. 6.1 path gates.
|
|
497
497
|
if (opts.trustAnchors == null) return _ok(m, "signature", protectionAlg, false, signer);
|
|
498
498
|
var trust = await _chainSigner(signer, m, opts, extra);
|
|
499
|
+
// Surface the ACTUAL validated chain (signer + the intermediates path.build used) on a trusted verdict, so a
|
|
500
|
+
// caller can cache exactly the certificates that established trust rather than the UNSIGNED extraCerts a peer
|
|
501
|
+
// can pad -- an appended-but-unused certificate never enters this chain.
|
|
502
|
+
if (trust.chain) signer.chain = trust.chain;
|
|
499
503
|
return _verdict(m, "signature", protectionAlg, true, trust.trusted, trust.trusted ? null : "cmp/untrusted-signer", trust.reason, signer);
|
|
500
504
|
}
|
|
501
505
|
|
|
@@ -545,7 +549,17 @@ async function _chainSigner(signer, m, opts, extra) {
|
|
|
545
549
|
try {
|
|
546
550
|
var res = await _engine.build(signer.der, buildOpts);
|
|
547
551
|
if (!res || res.valid !== true) return { trusted: false, reason: "the signer certificate did not chain to a supplied trust anchor" };
|
|
548
|
-
|
|
552
|
+
// res.path is the ordered certificates path.build assembled (signer + the intermediates it used), as PARSED
|
|
553
|
+
// objects whose byte fields are subarrays of the (possibly multi-MB) response allocation. Map each back to its
|
|
554
|
+
// source DER (the signer, or a DER pool entry) and return an INDEPENDENT copy, so a caller caching the chain
|
|
555
|
+
// holds standalone buffers -- never slices pinning the whole response. Only certs on the trusted path enter it,
|
|
556
|
+
// so unsigned extraCerts padding is excluded. (A path cert sourced from an already-parsed intermediate carries
|
|
557
|
+
// no DER to copy and is omitted; for a Buffer/DER pool -- the cmp.session case -- the chain is complete.)
|
|
558
|
+
var byKey = Object.create(null);
|
|
559
|
+
var skey = _certKey(signer.parsed); if (skey) byKey[skey] = signer.der;
|
|
560
|
+
pool.forEach(function (c) { if (Buffer.isBuffer(c) || c instanceof Uint8Array) { var k = _certKey(c); if (k && !byKey[k]) byKey[k] = c; } });
|
|
561
|
+
var chain = res.path.map(function (pc) { var k = _certKey(pc); var d = k ? byKey[k] : null; return d ? Buffer.from(d) : null; }).filter(Boolean);
|
|
562
|
+
return { trusted: true, reason: null, chain: chain };
|
|
549
563
|
} catch (e) {
|
|
550
564
|
// A config-tier fault from path.build (an invalid opts.time, an empty / malformed trustAnchors or
|
|
551
565
|
// intermediate) is a DEPLOYMENT error, not an untrusted signer -- rethrow it as cmp/bad-input so it is
|
|
@@ -669,9 +683,12 @@ async function _verify(message, opts) {
|
|
|
669
683
|
* Returns a verdict (never a bare boolean): `{ valid, trusted, protectionType, protectionAlg, signer,
|
|
670
684
|
* transactionID, senderNonce, recipNonce, header, body, code?, reason? }`. `valid` is whether the
|
|
671
685
|
* protection is cryptographically intact under the declared algorithm; `trusted` is whether a MAC secret
|
|
672
|
-
* matched or a signature signer certificate chained to a supplied trust anchor.
|
|
673
|
-
*
|
|
674
|
-
*
|
|
686
|
+
* matched or a signature signer certificate chained to a supplied trust anchor. On a trusted signature
|
|
687
|
+
* verdict `signer.chain` is the validated certificate path as independent DER buffers (the signer plus the
|
|
688
|
+
* intermediates that chained it to the anchor) -- the certificates actually used, never the unsigned
|
|
689
|
+
* `extraCerts` a peer can pad, and never a slice pinning the response allocation. A
|
|
690
|
+
* well-formed but unverifiable message is a `{ valid: false }` verdict carrying a `cmp/*` code, not a throw;
|
|
691
|
+
* only malformed input (a non-PKIMessage, a bad required opt, a flavor/credential mismatch) throws a typed `CmpError`.
|
|
675
692
|
*
|
|
676
693
|
* @opts
|
|
677
694
|
* - `sharedSecret` (string|Buffer) -- the PBMAC1 secret; REQUIRED for a MAC-protected message (UTF-8).
|
|
@@ -702,4 +719,5 @@ module.exports = {
|
|
|
702
719
|
wellKnownUrl: cmpBuild.wellKnownUrl,
|
|
703
720
|
verify: verify,
|
|
704
721
|
setEngine: setEngine,
|
|
722
|
+
senderBoundToCert: _senderBoundToCert,
|
|
705
723
|
};
|
package/lib/path-validate.js
CHANGED
|
@@ -41,6 +41,7 @@ var ocsp = require("./schema-ocsp");
|
|
|
41
41
|
var ocspVerify = require("./ocsp-verify");
|
|
42
42
|
var crlVerify = require("./crl-verify");
|
|
43
43
|
var cmpVerify = require("./cmp-verify");
|
|
44
|
+
var cmpSession = require("./cmp-session");
|
|
44
45
|
var guard = require("./guard-all");
|
|
45
46
|
var constants = require("./constants");
|
|
46
47
|
var validator = require("./validator-all");
|
|
@@ -1887,6 +1888,8 @@ var ocspCore = ocspVerify.makeOcspVerify({
|
|
|
1887
1888
|
// out-of-path signer certificate through the FULL RFC 5280 sec. 6.1 path validation -- without exposing any
|
|
1888
1889
|
// of it on the public pki.path surface (index.js exports the whole path-validate module).
|
|
1889
1890
|
cmpVerify.setEngine({ verifyWithSpki: _verifyWithSpki, build: build, validate: validate });
|
|
1891
|
+
// pki.cmp.session validates the ISSUED leaf certificate (its signature + chain) through the same engine.
|
|
1892
|
+
cmpSession.setEngine({ build: build, validate: validate, toAnchor: toAnchor, coerceCert: coerceCert });
|
|
1890
1893
|
|
|
1891
1894
|
/**
|
|
1892
1895
|
* @primitive pki.path.ocspChecker
|
package/lib/sleep.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
//
|
|
5
|
+
// @internal -- the shared bounded poll sleeper for the stateful network clients (pki.acme.client and
|
|
6
|
+
// pki.cmp.session). A delay above Node's 32-bit setTimeout ceiling is SPLIT into chained maximum-size
|
|
7
|
+
// chunks: a bare setTimeout(fn, > 2^31-1) is silently clamped to 1 ms (a TimeoutOverflowWarning) and would
|
|
8
|
+
// then rapidly re-poll instead of waiting the full interval. Each client's opts.sleep overrides this in
|
|
9
|
+
// tests, so this default -- the ONLY sleeper that touches a real timer -- is never driven by a test wait.
|
|
10
|
+
|
|
11
|
+
var SETTIMEOUT_MAX_MS = 2147483647; // 2^31 - 1: Node's setTimeout delay ceiling
|
|
12
|
+
|
|
13
|
+
// sleep(ms) -> Promise resolved after `ms` milliseconds, chunking a delay past the timer ceiling.
|
|
14
|
+
function sleep(ms) {
|
|
15
|
+
return new Promise(function (resolve) {
|
|
16
|
+
(function step(remaining) {
|
|
17
|
+
if (remaining <= SETTIMEOUT_MAX_MS) { setTimeout(resolve, remaining); return; }
|
|
18
|
+
setTimeout(function () { step(remaining - SETTIMEOUT_MAX_MS); }, SETTIMEOUT_MAX_MS);
|
|
19
|
+
})(ms);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { sleep: sleep, SETTIMEOUT_MAX_MS: SETTIMEOUT_MAX_MS };
|
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:98a7ec9f-9442-4c90-b486-f206cdde5325",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-07-
|
|
8
|
+
"timestamp": "2026-07-31T20:40:18.144Z",
|
|
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.3.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.3.27",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.3.
|
|
25
|
+
"version": "0.3.27",
|
|
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.3.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.3.27",
|
|
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.3.
|
|
57
|
+
"ref": "@blamejs/pki@0.3.27",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|