@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/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
- return { trusted: true, reason: null };
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. A well-formed but
673
- * unverifiable message is a `{ valid: false }` verdict carrying a `cmp/*` code, not a throw; only malformed
674
- * input (a non-PKIMessage, a bad required opt, a flavor/credential mismatch) throws a typed `CmpError`.
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
  };
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.3.26",
3
+ "version": "0.3.27",
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:2d9fa8bb-e93e-450a-991c-ef2f3b3fb9e2",
5
+ "serialNumber": "urn:uuid:98a7ec9f-9442-4c90-b486-f206cdde5325",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-07-30T20:38:00.777Z",
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.26",
22
+ "bom-ref": "@blamejs/pki@0.3.27",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.3.26",
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.26",
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.26",
57
+ "ref": "@blamejs/pki@0.3.27",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]