@blamejs/core 0.18.41 → 0.18.42

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 CHANGED
@@ -8,6 +8,16 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.18.x
10
10
 
11
+ - v0.18.42 (2026-08-20) — **`b.middleware.botGuard` no longer refuses a request for omitting Accept-Language, which was returning 403 to every search-engine crawler.** `b.middleware.botGuard` treated a missing `Accept-Language` header as grounds for a 403 in its default blocking mode. Google documents that Googlebot sends requests without setting that header, and bingbot behaves the same, so every content page of a site running the default middleware chain answered 403 to a crawler while a browser was served normally. The header's absence is now an advisory signal, matching how the same middleware already treats a missing `Sec-Fetch-Mode`. **Changed:** *Vendored `@blamejs/pki` 0.5.16 to 0.5.17* — A failed integrity check now destroys the plaintext it had recovered rather than returning it, `AuthEnvelopedData` validates the attributes it is asked to authenticate, and the refusal for a content cipher in the wrong container cites the rule it actually applies — RFC 5083 §2 defines `AuthEnvelopedData` in terms of authenticated encryption and never names GCM, so the previous message sent operators to argue with a specification that says the opposite. **Fixed:** *A missing `Accept-Language` tags a request instead of refusing it* — The check was `if (!headers["accept-language"]) return "missing-accept-language"`, and `mode` defaults to `"block"`, so the request was answered 403. Whole client families omit the header — every major search-engine crawler, and with them uptime monitors, link previewers and feed readers — which made a site unreachable to all of them. On a deployment fronted by a cache this is easy to miss from the outside: the cached home page still answers 200, so the site looks partly indexed while every other URL is refused.
12
+
13
+ It now sets `req.suspectedBot` in `mode: "tag"` and never blocks, which is exactly how the middleware already treats a missing `Sec-Fetch-Mode` — absent for Safari before 16.4 and for every plain-HTTP non-localhost origin — and how `b.middleware.fetchMetadata` already treats a missing `Sec-Fetch-Site` through its `allowMissing` default. A header a whole client family omits is not evidence of automation.
14
+
15
+ Requests are still refused on positive evidence: the `User-Agent` deny-list (curl, wget, python-requests, axios, Go-http-client and the rest) is unchanged, and operators can extend it with `blockedAgents`. Separating a crawler from an abuser is a rate-limiting question rather than a header one, and `b.rateLimit` answers it.
16
+
17
+ If you were relying on the old behaviour, `mode: "tag"` surfaces the same signal on `req.suspectedBot` for you to act on. **Detectors:** *No single absent header may block, asserted against the middleware* — `bot-guard.test.js` now starts from a complete browser request and removes one header at a time, asserting that no single omission refuses the request. It covers the headers the middleware inspects — `User-Agent`, `Accept`, `Accept-Language`, `Accept-Encoding` and the three `Sec-Fetch-*` — so the rule holds for whichever one a future change reaches for, not only the one that was wrong.
18
+
19
+ It is a behavioural check rather than a source-pattern one deliberately. Written as a pattern check first, it was wrong six different ways: anchored to the first expression in the condition, bounded at the first `)`, bounded to a single line, defeated by a statement before the return, by a guard that was OR'd rather than AND'd, and by a `)` inside a string literal. Each fix was a step further into parsing JavaScript with text matching. Running the middleware decides all six, because it asks what the code does rather than how it is written. **References:** [How Google crawls locale-adaptive pages — Googlebot and Accept-Language](https://developers.google.com/search/docs/specialty/international/locale-adaptive-pages)
20
+
11
21
  - v0.18.41 (2026-08-20) — **Logout can now clear the session cookie on a plain-HTTP origin, and every Set-Cookie the framework writes goes through one validating writer.** `b.session.logout` built its expiry cookie by string concatenation with `Secure` hardcoded. A browser refuses a `Secure` cookie that arrives over plain HTTP, so on a cleartext deployment the header was discarded and the session cookie the logout existed to clear stayed in the jar. `b.middleware.csrfProtect` had its own cookie formatter with the same shape and a different consequence: it interpolated the configured `cookie.path` into the response header with no CRLF scrub. Both now compose `b.cookies`, which is the framework's single Set-Cookie writer. **Added:** *`b.cookies.assertAppendable(res)`* — Throws unless a response can carry an appended `Set-Cookie` — the same check `appendSetCookie` performs, exposed so a caller can run it before doing something it cannot undo. A response has to be readable as well as writable to be appended to: without `appendHeader` the merge happens in the framework and needs to see what is already queued, and a response carrying only `setHeader` would have its existing cookies silently replaced. `b.session.logout` calls it up front, because it revokes the session row before queueing the expiry cookie — a refusal at queue time would leave the session destroyed, the browser still holding its cookie, and the request failing. · *`b.cookies.appendSetCookie(res, header)`* — Queues one `Set-Cookie` header without discarding the ones already queued. `res.setHeader("Set-Cookie", value)` replaces the header, so a route that issues a session cookie and then a CSRF cookie sends only the second; `Set-Cookie` is the one response header that is legitimately repeated. The appender uses `res.appendHeader` where the runtime offers it and array-merges where it does not.
12
22
 
13
23
  It pairs with `b.cookies.serialize`, which validates and builds the header string. `b.cookies.create().write()` and `.clear()` compose both; reach for the two separately when you need to build the header at one point in a flow and queue it at another — validating early, emitting only once the side effect it accompanies has succeeded, which is what `b.session.logout` does with the session row. **Changed:** *`b.session.logout` queues its expiry cookie rather than overwriting the header* — It called `res.setHeader("Set-Cookie", ...)`, which discarded any cookie the route had already queued — a rotated CSRF token, a locale. It now appends. Code reading `res.getHeader("Set-Cookie")` after a logout sees an array of header strings rather than a single string; Node accepts either form on the way out.
package/NOTICE CHANGED
@@ -68,7 +68,7 @@ Used for: FIPS 203 ML-KEM (ml_kem_512 / ml_kem_768 / ml_kem_1024),
68
68
  reference implementation.
69
69
  --------------------------------------------------------------------------------
70
70
  Component: @blamejs/pki
71
- Version: 0.5.16
71
+ Version: 0.5.17
72
72
  Source: https://github.com/blamejs/pki
73
73
  License: Apache-2.0
74
74
  Copyright: Copyright (c) blamejs contributors
package/README.md CHANGED
@@ -322,7 +322,7 @@ All runtime dependencies are committed to the repo — no transitive npm install
322
322
  | [`@noble/hashes`](https://github.com/paulmillr/noble-hashes) | 2.3.0 | [Paul Miller](https://github.com/paulmillr) | Browser (ESM) build only — SHAKE256 / SHA-3 / SHA-2 / HMAC / HKDF for the client half of a hybrid exchange. The server side reaches all of these through `node:crypto`, so there is no server bundle |
323
323
  | [`@noble/curves`](https://github.com/paulmillr/noble-curves) | 2.3.0 (bundles @noble/hashes 2.3.0) | [Paul Miller](https://github.com/paulmillr) | RFC 9497 Oblivious Pseudo-Random Function (OPRF / VOPRF / POPRF) over ristretto255 / P-256 / P-384 / P-521, behind `b.crypto.oprf` |
324
324
  | [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) | 0.7.0 (bundles @noble/hashes, @noble/curves, @noble/ciphers 2.3.0) | [Paul Miller](https://github.com/paulmillr) | Pure-JS FIPS 203 ML-KEM (`ml_kem_512` / `ml_kem_768` / `ml_kem_1024`), FIPS 204 ML-DSA (`ml_dsa_44/65/87`), FIPS 205 SLH-DSA (`slh_dsa_*`). First-class on both server-side and client-side via `b.pqcSoftware` — security-first defaults pin to the highest cat-5 levels (ML-KEM-1024, ML-DSA-87, SLH-DSA-SHAKE-256f); interoperable with Node's built-in WebCrypto ML-KEM that `b.crypto.encrypt` / `b.middleware.apiEncrypt` use. A browser (ESM) build ships beside it carrying the KEM suites only — a client half encapsulates and does not sign |
325
- | [`@blamejs/pki`](https://github.com/blamejs/pki) | 0.5.16 | [blamejs](https://github.com/blamejs) | Zero-dependency pure-JS X.509 / CRL / PKCS#12 / CSR / CMS toolkit backing `b.mtlsCa` — ML-DSA-87 (FIPS 204) post-quantum + ECDSA-P384 cert signing, PBMAC1 PKCS#12 packaging, chain validation (no openssl CLI) — and the WebAuthn attestation / assertion verification behind `b.auth.passkey` |
325
+ | [`@blamejs/pki`](https://github.com/blamejs/pki) | 0.5.17 | [blamejs](https://github.com/blamejs) | Zero-dependency pure-JS X.509 / CRL / PKCS#12 / CSR / CMS toolkit backing `b.mtlsCa` — ML-DSA-87 (FIPS 204) post-quantum + ECDSA-P384 cert signing, PBMAC1 PKCS#12 packaging, chain validation (no openssl CLI) — and the WebAuthn attestation / assertion verification behind `b.auth.passkey` |
326
326
  | [`SecLists` 10k-most-common.txt](https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10k-most-common.txt) | master snapshot | [Daniel Miessler / SecLists contributors](https://github.com/danielmiessler/SecLists) (CC-BY-3.0) | Top-10000 common-password dictionary read by `b.auth.password.policy()` for the NIST 800-63B §5.1.1.2 "previously breached" check |
327
327
  | [`prismjs`](https://prismjs.com/) | 1.30.0 | [Lea Verou + contributors](https://github.com/PrismJS/prism) | Syntax highlighting in the example wiki's code blocks (browser-side) |
328
328
 
@@ -7,7 +7,13 @@
7
7
  * authentication, but catches drive-by scrapers and most low-effort bots.
8
8
  *
9
9
  * Heuristics (all combined):
10
- * - Missing Accept-Language header (real browsers always send one)
10
+ * - Missing Accept-Language header ADVISORY ONLY (never blocks). Tagged
11
+ * in mode:"tag". It cannot block because the header is absent for entire
12
+ * client families: every major search-engine crawler omits it (Google
13
+ * documents that Googlebot "sends HTTP requests without setting
14
+ * Accept-Language"), as do uptime monitors, link previewers and feed
15
+ * readers — a 403 on it alone made a site's every page unreachable to
16
+ * Googlebot while a browser sailed through.
11
17
  * - Missing Sec-Fetch-Mode header — ADVISORY ONLY (never blocks). Tagged
12
18
  * in mode:"tag" on secure-context HTML GETs where a modern browser
13
19
  * would have sent it. It cannot block because the header is absent for
@@ -90,13 +96,15 @@ function _coerceAgentPattern(r, where) {
90
96
  * Cheap fingerprint-based detection of obviously-non-browser requests.
91
97
  * Constructed via `b.middleware.botGuard(opts)`; the resulting
92
98
  * middleware has the `(req, res, next)` shape shown above.
93
- * Two blocking heuristicsmissing `Accept-Language` and a User-Agent
94
- * regex match against a default list (curl / wget / python-requests /
95
- * axios / etc.) plus one advisory signal: a missing `Sec-Fetch-Mode`
96
- * on a secure-context HTML GET sets `req.suspectedBot` in `mode: "tag"`
97
- * but NEVER blocks (the header is absent for Safari < 16.4 and every
98
- * plain-HTTP non-localhost origin, so blocking on it refuses real
99
- * users). Not
99
+ * One blocking heuristica User-Agent regex match against a default
100
+ * list (curl / wget / python-requests / axios / etc.) — plus two
101
+ * advisory signals that set `req.suspectedBot` in `mode: "tag"` and
102
+ * NEVER block: a missing `Accept-Language` (absent from every major
103
+ * search-engine crawler, so blocking on it makes a site unreachable to
104
+ * them) and a missing `Sec-Fetch-Mode` on a secure-context HTML GET
105
+ * (absent for Safari < 16.4 and every plain-HTTP non-localhost origin).
106
+ * A header a whole client family omits is not evidence of automation.
107
+ * Not
100
108
  * a substitute for proper authentication — catches drive-by scrapers
101
109
  * and low-effort bots. In `mode: "block"` (default) the request is
102
110
  * refused; in `mode: "tag"` `req.suspectedBot = true` is set and the
@@ -206,14 +214,29 @@ function create(opts) {
206
214
  // Skip browser-fingerprint checks for API routes
207
215
  return null;
208
216
  }
209
- if (!headers["accept-language"]) return "missing-accept-language";
217
+ // Missing Accept-Language NEVER blocks, for the reason the Sec-Fetch-Mode
218
+ // check below does not either: the header is absent for entire CLIENT
219
+ // families, so a 403 on it alone refuses them wholesale. Every major
220
+ // search-engine crawler omits it — Google documents that Googlebot "sends
221
+ // HTTP requests without setting Accept-Language in the request header",
222
+ // and bingbot behaves the same — so blocking on it made every content page
223
+ // of a blamejs site answer 403 to a crawler while a browser sailed
224
+ // through. Measured on a live deployment: a 337-URL sitemap of which only
225
+ // the cached homepage was reachable. Uptime monitors, link previewers and
226
+ // feed readers are refused the same way.
227
+ //
228
+ // It survives as an advisory TAG, so an operator can still rate-limit or
229
+ // log on it. Automation libraries remain blocked by the User-Agent
230
+ // deny-list, which is what actually distinguishes them; separating a
231
+ // crawler from an abuser is a rate-limiting question, not a header one.
232
+ if (mode === "tag" && !headers["accept-language"]) return "missing-accept-language";
210
233
  // Missing Sec-Fetch-Mode NEVER blocks: the header is absent for entire
211
234
  // browser families (Safari < 16.4 omits Fetch Metadata even over HTTPS)
212
235
  // and for every plain-HTTP non-localhost origin (Umbrel, LAN / *.local
213
236
  // reverse proxies), so a 403 on it alone refuses real users. It survives
214
237
  // only as an advisory TAG in mode:"tag", and even then only in a secure
215
238
  // context where a modern browser would have sent it. Drive-by bots are
216
- // still blocked by missing Accept-Language + the User-Agent deny-list.
239
+ // still blocked by the User-Agent deny-list.
217
240
  if (mode === "tag" && req.method === "GET" && _isSecureContext(req) && !headers["sec-fetch-mode"]) return "missing-sec-fetch-mode";
218
241
  return null;
219
242
  }
@@ -21,7 +21,7 @@
21
21
  "server": "sha256:f3325f480cb8eb814fcb0baaa19336cbbf2b993f48624c6aa9600ffd69d0be5e",
22
22
  "browser": "sha256:0ffd91540bcb586a29b56e52ee1c29df69097b50776beb4036a07558f7a4e12e"
23
23
  },
24
- "refreshedAt": "2026-08-20T04:54:41.606Z"
24
+ "refreshedAt": "2026-08-20T15:14:48.311Z"
25
25
  },
26
26
  "@noble/hashes": {
27
27
  "version": "2.3.0",
@@ -48,7 +48,7 @@
48
48
  "hashes": {
49
49
  "browser": "sha256:dfe4b7ae3c9880e388c8da4b68f44742b229b53afacd1e674179527e33da62b0"
50
50
  },
51
- "refreshedAt": "2026-08-20T04:54:41.606Z"
51
+ "refreshedAt": "2026-08-20T15:14:48.311Z"
52
52
  },
53
53
  "@noble/curves": {
54
54
  "version": "2.3.0",
@@ -70,7 +70,7 @@
70
70
  "hashes": {
71
71
  "server": "sha256:b5fe88d1ea780d0581dee6145d666f89d46fc9531b5db35db2e5b16627840890"
72
72
  },
73
- "refreshedAt": "2026-08-20T04:54:41.606Z",
73
+ "refreshedAt": "2026-08-20T15:14:48.311Z",
74
74
  "components": {
75
75
  "@noble/hashes": {
76
76
  "url": "https://github.com/paulmillr/noble-hashes",
@@ -114,7 +114,7 @@
114
114
  "server": "sha256:fab7ebe5737793862c473444f4ee5912f79dd1edec86683acbb4eecbca0f5892",
115
115
  "browser": "sha256:cae1d5bbdc7184b202b6ca68df6e1db7b0d0f668c77809ded189ca7f271accc9"
116
116
  },
117
- "refreshedAt": "2026-08-20T04:54:41.606Z",
117
+ "refreshedAt": "2026-08-20T15:14:48.311Z",
118
118
  "components": {
119
119
  "@noble/hashes": {
120
120
  "url": "https://github.com/paulmillr/noble-hashes",
@@ -148,7 +148,7 @@
148
148
  },
149
149
  "runtime_artifact": "lib/vendor/common-passwords-top-10000.data.js",
150
150
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
151
- "refreshedAt": "2026-08-20T04:54:41.606Z"
151
+ "refreshedAt": "2026-08-20T15:14:48.311Z"
152
152
  },
153
153
  "bimi-trust-anchors": {
154
154
  "version": "operator-managed",
@@ -173,7 +173,7 @@
173
173
  },
174
174
  "runtime_artifact": "lib/vendor/bimi-trust-anchors.data.js",
175
175
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
176
- "refreshedAt": "2026-08-20T04:54:41.606Z"
176
+ "refreshedAt": "2026-08-20T15:14:48.311Z"
177
177
  },
178
178
  "publicsuffix-list": {
179
179
  "version": "master",
@@ -193,10 +193,10 @@
193
193
  },
194
194
  "runtime_artifact": "lib/vendor/public-suffix-list.data.js",
195
195
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
196
- "refreshedAt": "2026-08-20T04:54:41.606Z"
196
+ "refreshedAt": "2026-08-20T15:14:48.311Z"
197
197
  },
198
198
  "@blamejs/pki": {
199
- "version": "0.5.16",
199
+ "version": "0.5.17",
200
200
  "license": "Apache-2.0",
201
201
  "author": "blamejs",
202
202
  "source": "https://github.com/blamejs/pki",
@@ -216,12 +216,12 @@
216
216
  "server": "lib/vendor/blamejs-pki.cjs"
217
217
  },
218
218
  "bundler": "esbuild --format=cjs --platform=node --external:crypto --external:node:crypto",
219
- "bundledAt": "2026-08-19T00:00:00Z",
220
- "cpe": "cpe:2.3:a:blamejs:pki:0.5.16:*:*:*:*:node.js:*:*",
219
+ "bundledAt": "2026-08-20T00:00:00Z",
220
+ "cpe": "cpe:2.3:a:blamejs:pki:0.5.17:*:*:*:*:node.js:*:*",
221
221
  "hashes": {
222
- "server": "sha256:96861fcf18c319d2e984cf48aaa0e496e903e447c491c6efcee677e15e87c24e"
222
+ "server": "sha256:dbedb80e1725747a24fdbd3dec9fcab31b507ac048b923462382b6ac117937a0"
223
223
  },
224
- "refreshedAt": "2026-08-20T04:54:41.606Z"
224
+ "refreshedAt": "2026-08-20T15:14:48.311Z"
225
225
  }
226
226
  }
227
227
  }
@@ -1,4 +1,4 @@
1
- // @blamejs/pki v0.5.16 — vendored (Apache-2.0). Zero-dep pure CJS.
1
+ // @blamejs/pki v0.5.17 — vendored (Apache-2.0). Zero-dep pure CJS.
2
2
  // https://github.com/blamejs/pki Exports: x509, crl, pkcs12, key, webcrypto, schema, csr, cms, ...
3
3
  // Backs lib/mtls-engine-default.js (PQC-capable CA + PKCS#12 engine).
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -141,7 +141,7 @@ var require_package = __commonJS({
141
141
  "node_modules/@blamejs/pki/package.json"(exports2, module2) {
142
142
  module2.exports = {
143
143
  name: "@blamejs/pki",
144
- version: "0.5.16",
144
+ version: "0.5.17",
145
145
  description: "Pure-JavaScript PKI toolkit that owns its stack \u2014 X.509, ASN.1/DER, CMS, PQC-first.",
146
146
  license: "Apache-2.0",
147
147
  author: "blamejs contributors",
@@ -2512,7 +2512,7 @@ var require_webcrypto = __commonJS({
2512
2512
  return _withSecretBytes(key, function(kb) {
2513
2513
  var cipher = nodeCrypto.createCipheriv("aes-" + key.algorithm.length + "-gcm", kb, iv, { authTagLength: (alg.tagLength || 128) / 8 });
2514
2514
  if (aad) cipher.setAAD(aad);
2515
- var ct = Buffer.concat([cipher.update(buf), cipher.final()]);
2515
+ var ct = guard.secret.cipherFinish(cipher, buf, WebCryptoError, "webcrypto/operation", "an AES-GCM encrypt intermediate");
2516
2516
  return Buffer.concat([ct, cipher.getAuthTag()]);
2517
2517
  });
2518
2518
  }, "encrypt"));
@@ -2522,7 +2522,7 @@ var require_webcrypto = __commonJS({
2522
2522
  return _toArrayBuffer(_runCipher(function() {
2523
2523
  return _withSecretBytes(key, function(kb) {
2524
2524
  var c2 = nodeCrypto.createCipheriv("aes-" + key.algorithm.length + "-cbc", kb, cbcIv);
2525
- return Buffer.concat([c2.update(buf), c2.final()]);
2525
+ return guard.secret.cipherFinish(c2, buf, WebCryptoError, "webcrypto/operation", "an AES-CBC encrypt intermediate");
2526
2526
  });
2527
2527
  }, "encrypt"));
2528
2528
  }
@@ -2532,7 +2532,7 @@ var require_webcrypto = __commonJS({
2532
2532
  return _toArrayBuffer(_runCipher(function() {
2533
2533
  return _withSecretBytes(key, function(kb) {
2534
2534
  var c3 = nodeCrypto.createCipheriv("aes-" + key.algorithm.length + "-ctr", kb, ctrCounter);
2535
- return Buffer.concat([c3.update(buf), c3.final()]);
2535
+ return guard.secret.cipherFinish(c3, buf, WebCryptoError, "webcrypto/operation", "an AES-CTR encrypt intermediate");
2536
2536
  });
2537
2537
  }, "encrypt"));
2538
2538
  }
@@ -2570,7 +2570,7 @@ var require_webcrypto = __commonJS({
2570
2570
  var d = nodeCrypto.createDecipheriv("aes-" + key.algorithm.length + "-gcm", kb, iv, { authTagLength: tagLen });
2571
2571
  if (gcmAad) d.setAAD(gcmAad);
2572
2572
  d.setAuthTag(tag);
2573
- return Buffer.concat([d.update(ct), d.final()]);
2573
+ return guard.secret.cipherFinish(d, ct, WebCryptoError, "webcrypto/operation", "the recovered AES-GCM plaintext");
2574
2574
  });
2575
2575
  }, "decrypt"));
2576
2576
  }
@@ -2579,7 +2579,7 @@ var require_webcrypto = __commonJS({
2579
2579
  return _toArrayBuffer(_runCipher(function() {
2580
2580
  return _withSecretBytes(key, function(kb) {
2581
2581
  var d2 = nodeCrypto.createDecipheriv("aes-" + key.algorithm.length + "-cbc", kb, cbcIv2);
2582
- return Buffer.concat([d2.update(buf), d2.final()]);
2582
+ return guard.secret.cipherFinish(d2, buf, WebCryptoError, "webcrypto/operation", "the recovered AES-CBC plaintext");
2583
2583
  });
2584
2584
  }, "decrypt"));
2585
2585
  }
@@ -2589,7 +2589,7 @@ var require_webcrypto = __commonJS({
2589
2589
  return _toArrayBuffer(_runCipher(function() {
2590
2590
  return _withSecretBytes(key, function(kb) {
2591
2591
  var d3 = nodeCrypto.createDecipheriv("aes-" + key.algorithm.length + "-ctr", kb, ctrCounter2);
2592
- return Buffer.concat([d3.update(buf), d3.final()]);
2592
+ return guard.secret.cipherFinish(d3, buf, WebCryptoError, "webcrypto/operation", "the recovered AES-CTR plaintext");
2593
2593
  });
2594
2594
  }, "decrypt"));
2595
2595
  }
@@ -2809,7 +2809,7 @@ var require_webcrypto = __commonJS({
2809
2809
  try {
2810
2810
  return _withSecretBytes(wrappingKey, function(wkBytes) {
2811
2811
  var c = nodeCrypto.createCipheriv("aes" + wrappingKey.algorithm.length + "-wrap", wkBytes, Buffer.from("A6A6A6A6A6A6A6A6", "hex"));
2812
- return _toArrayBuffer(Buffer.concat([c.update(bytes), c.final()]));
2812
+ return _toArrayBuffer(guard.secret.cipherFinish(c, bytes, WebCryptoError, "webcrypto/operation", "an AES-KW wrap intermediate"));
2813
2813
  });
2814
2814
  } catch (e) {
2815
2815
  throw new WebCryptoError("webcrypto/operation", "wrapKey: AES-KW key wrap failed", e);
@@ -2835,7 +2835,7 @@ var require_webcrypto = __commonJS({
2835
2835
  try {
2836
2836
  bytes = _withSecretBytes(unwrappingKey, function(kwBytes) {
2837
2837
  var d = nodeCrypto.createDecipheriv("aes" + unwrappingKey.algorithm.length + "-wrap", kwBytes, Buffer.from("A6A6A6A6A6A6A6A6", "hex"));
2838
- return Buffer.concat([d.update(wrapped), d.final()]);
2838
+ return guard.secret.cipherFinish(d, wrapped, WebCryptoError, "webcrypto/operation", "the unwrapped key material");
2839
2839
  });
2840
2840
  } catch (e) {
2841
2841
  throw new WebCryptoError("webcrypto/operation", "unwrapKey: AES-KW key unwrap failed (integrity or length)", e);
@@ -3419,6 +3419,7 @@ var require_guard_secret = __commonJS({
3419
3419
  var bytes = require_guard_bytes();
3420
3420
  var intrinsic = require_guard_intrinsic();
3421
3421
  var _fill = intrinsic.uncurry(Uint8Array.prototype.fill);
3422
+ var _concat = intrinsic.bufferConcat;
3422
3423
  function zeroize(value, ErrorClass, code, label) {
3423
3424
  if (value === null || value === void 0) return value;
3424
3425
  var view = bytes.view(value, ErrorClass, code, label);
@@ -3430,7 +3431,18 @@ var require_guard_secret = __commonJS({
3430
3431
  for (var i = 0; i < list.length; i++) zeroize(list[i], ErrorClass, code, label);
3431
3432
  return list;
3432
3433
  }
3433
- module2.exports = { zeroize, zeroizeAll };
3434
+ function cipherFinish(transform, input, ErrorClass, code, label) {
3435
+ var head = null, tail = null;
3436
+ try {
3437
+ head = transform.update(input);
3438
+ tail = transform.final();
3439
+ return _concat([head, tail]);
3440
+ } finally {
3441
+ zeroize(head, ErrorClass, code, label);
3442
+ zeroize(tail, ErrorClass, code, label);
3443
+ }
3444
+ }
3445
+ module2.exports = { zeroize, zeroizeAll, cipherFinish };
3434
3446
  }
3435
3447
  });
3436
3448
 
@@ -18943,13 +18955,13 @@ var require_pbes2 = __commonJS({
18943
18955
  }
18944
18956
  return { salt, iterations, prfNode };
18945
18957
  }
18946
- function cbcEncrypt(key, iv, plaintext, keyBits) {
18958
+ function cbcEncrypt(key, iv, plaintext, keyBits, E, code) {
18947
18959
  var c = nodeCrypto.createCipheriv("aes-" + keyBits + "-cbc", key, iv);
18948
- return Buffer.concat([c.update(plaintext), c.final()]);
18960
+ return guard.secret.cipherFinish(c, plaintext, E, code, "a content-encryption intermediate");
18949
18961
  }
18950
- function cbcDecrypt(key, iv, ct, keyBits) {
18962
+ function cbcDecrypt(key, iv, ct, keyBits, E, code) {
18951
18963
  var d = nodeCrypto.createDecipheriv("aes-" + keyBits + "-cbc", key, iv);
18952
- return Buffer.concat([d.update(ct), d.final()]);
18964
+ return guard.secret.cipherFinish(d, ct, E, code, "the partially recovered plaintext");
18953
18965
  }
18954
18966
  function pbes2Encrypt(pwBytes, plaintext, opts, E, prefix) {
18955
18967
  opts = opts || {};
@@ -18963,7 +18975,7 @@ var require_pbes2 = __commonJS({
18963
18975
  var iv = opts.iv != null ? opts.iv : nodeCrypto.randomBytes(16);
18964
18976
  var key = nodeCrypto.pbkdf2Sync(pwBytes, salt, iterations, keyBits / 8, prfNode);
18965
18977
  try {
18966
- return { algId: pbes2AlgId(salt, iterations, prf, cipherName, iv), ct: cbcEncrypt(key, iv, plaintext, keyBits) };
18978
+ return { algId: pbes2AlgId(salt, iterations, prf, cipherName, iv), ct: cbcEncrypt(key, iv, plaintext, keyBits, E, prefix + "/bad-input") };
18967
18979
  } finally {
18968
18980
  guard.secret.zeroize(key, E, prefix + "/bad-input", "the password-derived encryption key");
18969
18981
  }
@@ -18994,7 +19006,7 @@ var require_pbes2 = __commonJS({
18994
19006
  }
18995
19007
  var dk = nodeCrypto.pbkdf2Sync(pwBytes, pb.salt, pb.iterations, keyBits / 8, pb.prfNode);
18996
19008
  try {
18997
- return cbcDecrypt(dk, iv, ciphertext, keyBits);
19009
+ return cbcDecrypt(dk, iv, ciphertext, keyBits, E, prefix + "/decrypt-failed");
18998
19010
  } catch (_e) {
18999
19011
  throw E(prefix + "/decrypt-failed", "decryption failed");
19000
19012
  } finally {
@@ -20495,7 +20507,7 @@ var require_cms_sign = __commonJS({
20495
20507
  function _buildSignedAttrs(pairs) {
20496
20508
  var seenTypes = {};
20497
20509
  var attrs = pairs.map(function(p) {
20498
- if (seenTypes[p.type]) throw _err("cms/bad-input", "signedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
20510
+ if (guard.intrinsic.hasOwn(seenTypes, p.type)) throw _err("cms/bad-input", "signedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
20499
20511
  seenTypes[p.type] = 1;
20500
20512
  return b.sequence([b.oid(p.type), b.set(p.values)]);
20501
20513
  });
@@ -20524,8 +20536,8 @@ var require_cms_sign = __commonJS({
20524
20536
  var pairs = _resolveAttrPairs(list, "an unsigned attribute value");
20525
20537
  var seen = {};
20526
20538
  pairs.forEach(function(p) {
20527
- if (UNSIGNED_FORBIDDEN[p.type]) throw _err("cms/bad-input", "the " + UNSIGNED_FORBIDDEN[p.type] + " attribute must not appear as an unsigned attribute (RFC 5652 sec. 11)");
20528
- if (seen[p.type]) throw _err("cms/bad-input", "unsignedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
20539
+ if (guard.intrinsic.hasOwn(UNSIGNED_FORBIDDEN, p.type)) throw _err("cms/bad-input", "the " + UNSIGNED_FORBIDDEN[p.type] + " attribute must not appear as an unsigned attribute (RFC 5652 sec. 11)");
20540
+ if (guard.intrinsic.hasOwn(seen, p.type)) throw _err("cms/bad-input", "unsignedAttrs must not repeat an attribute type (RFC 5652 sec. 5.3): " + p.type);
20529
20541
  seen[p.type] = 1;
20530
20542
  });
20531
20543
  var setOf = b.set(pairs.map(function(p) {
@@ -21187,11 +21199,11 @@ var require_cms_encrypt = __commonJS({
21187
21199
  try {
21188
21200
  var c1 = nodeCrypto.createCipheriv("aes-256-cbc", kek, iv);
21189
21201
  c1.setAutoPadding(false);
21190
- var pass1 = Buffer.concat([c1.update(wk), c1.final()]);
21202
+ var pass1 = guard.secret.cipherFinish(c1, wk, CmsError, "cms/bad-input", "the PWRI first-pass ciphertext");
21191
21203
  var iv2 = pass1.subarray(pass1.length - 16);
21192
21204
  var c2 = nodeCrypto.createCipheriv("aes-256-cbc", kek, iv2);
21193
21205
  c2.setAutoPadding(false);
21194
- return Buffer.concat([c2.update(pass1), c2.final()]);
21206
+ return guard.secret.cipherFinish(c2, pass1, CmsError, "cms/bad-input", "the PWRI wrapped key");
21195
21207
  } finally {
21196
21208
  guard.secret.zeroize(wk, CmsError, "cms/bad-input", "the PWRI plaintext block");
21197
21209
  }
@@ -21258,10 +21270,37 @@ var require_cms_encrypt = __commonJS({
21258
21270
  }
21259
21271
  function _envelopedData(contentBytes, cek, ca, contentType, riNodes, recips) {
21260
21272
  var iv = nodeCrypto.randomBytes(16);
21261
- var enc = pbes2.cbcEncrypt(cek, iv, contentBytes, ca.keyBits);
21273
+ var enc = pbes2.cbcEncrypt(cek, iv, contentBytes, ca.keyBits, CmsError, "cms/bad-input");
21262
21274
  var eci = b.sequence([b.oid(O(contentType)), b.sequence([b.oid(O(ca.oid)), b.octetString(iv)]), b.contextPrimitive(0, enc)]);
21263
21275
  return b.sequence([b.integer(BigInt(_envelopedVersion(recips, false))), b.setOf(riNodes), eci]);
21264
21276
  }
21277
+ function _assertAuthAttrs(pairs, forbidden) {
21278
+ var seenTypes = {};
21279
+ pairs.forEach(function(p) {
21280
+ var node;
21281
+ try {
21282
+ node = asn1.decode(p);
21283
+ } catch (e) {
21284
+ throw _err("cms/bad-input", "an authenticated attribute is not well-formed DER", e);
21285
+ }
21286
+ if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children || node.children.length !== 2 || node.children[1].tagClass !== "universal" || node.children[1].tagNumber !== asn1.TAGS.SET || !node.children[1].children || node.children[1].children.length < 1) {
21287
+ throw _err("cms/bad-input", "an authenticated attribute must be an Attribute SEQUENCE { type, non-empty SET OF value } (RFC 5652)");
21288
+ }
21289
+ var t;
21290
+ try {
21291
+ t = asn1.read.oid(node.children[0]);
21292
+ } catch (e) {
21293
+ throw _err("cms/bad-input", "an authenticated attribute type is not an OBJECT IDENTIFIER", e);
21294
+ }
21295
+ if (guard.intrinsic.hasOwn(seenTypes, t)) throw _err("cms/bad-input", "authenticated attributes must not repeat an attribute type (RFC 5652): " + t);
21296
+ seenTypes[t] = 1;
21297
+ if (forbidden && t === forbidden.oid) throw _err("cms/bad-input", forbidden.why);
21298
+ });
21299
+ }
21300
+ var AUTH_ENVELOPED_FORBIDDEN = {
21301
+ oid: O("messageDigest"),
21302
+ why: "authAttrs must not carry the message-digest attribute in an AuthEnvelopedData: its value is the unencrypted hash of the plaintext, which enables content tracking and confirmation of a guessed plaintext (RFC 5083 sec. 2.1, sec. 5)"
21303
+ };
21265
21304
  function _authEnvelopedData(contentBytes, cek, ca, contentType, opts, riNodes, recips) {
21266
21305
  var nonce = nodeCrypto.randomBytes(12);
21267
21306
  var authAttrsDer = null, aad = Buffer.alloc(0);
@@ -21269,6 +21308,7 @@ var require_cms_encrypt = __commonJS({
21269
21308
  throw _err("cms/bad-input", "AuthEnvelopedData with a non-data contentType requires authAttrs (RFC 5083 sec. 2.1)");
21270
21309
  }
21271
21310
  if (opts.authAttrs && opts.authAttrs.length) {
21311
+ _assertAuthAttrs(opts.authAttrs, AUTH_ENVELOPED_FORBIDDEN);
21272
21312
  var setOf = b.setOf(opts.authAttrs);
21273
21313
  aad = setOf;
21274
21314
  authAttrsDer = b.contextConstructed(1, setOf.subarray(_tlvHeaderLen(setOf)));
@@ -21294,7 +21334,7 @@ var require_cms_encrypt = __commonJS({
21294
21334
  } else {
21295
21335
  throw _err("cms/bad-input", "EncryptedData needs a single { cek } or { password } descriptor");
21296
21336
  }
21297
- var enc = pbes2.cbcEncrypt(encKey, iv, contentBytes, ca.keyBits);
21337
+ var enc = pbes2.cbcEncrypt(encKey, iv, contentBytes, ca.keyBits, CmsError, "cms/bad-input");
21298
21338
  var eci = b.sequence([b.oid(O(contentType)), contentAlgNode, b.contextPrimitive(0, enc)]);
21299
21339
  var inner = b.sequence([b.integer(0n), eci]);
21300
21340
  return _emit(inner, "encryptedData", opts);
@@ -21308,7 +21348,7 @@ var require_cms_encrypt = __commonJS({
21308
21348
  var key = nodeCrypto.pbkdf2Sync(password, salt, iterations, ca.keyBits / 8, pbes2.prfNodeByName(prf, _err, "cms"));
21309
21349
  if (pwOwn2.owned) guard.secret.zeroize(password, CmsError, "cms/bad-input", "the password encoding");
21310
21350
  try {
21311
- var enc = pbes2.cbcEncrypt(key, iv, contentBytes, ca.keyBits);
21351
+ var enc = pbes2.cbcEncrypt(key, iv, contentBytes, ca.keyBits, CmsError, "cms/bad-input");
21312
21352
  var contentAlg = pbes2.pbes2AlgId(salt, iterations, prf, ca.oid, iv);
21313
21353
  var eci = b.sequence([b.oid(O(contentType)), contentAlg, b.contextPrimitive(0, enc)]);
21314
21354
  var inner = b.sequence([b.integer(0n), eci]);
@@ -21320,7 +21360,7 @@ var require_cms_encrypt = __commonJS({
21320
21360
  function _gcmEncrypt(key, nonce, plaintext, aad, keyBits, tagLen) {
21321
21361
  var c = nodeCrypto.createCipheriv("aes-" + keyBits + "-gcm", key, nonce, { authTagLength: tagLen });
21322
21362
  if (aad && aad.length) c.setAAD(aad);
21323
- var ct = Buffer.concat([c.update(plaintext), c.final()]);
21363
+ var ct = guard.secret.cipherFinish(c, plaintext, CmsError, "cms/bad-input", "a content-encryption intermediate");
21324
21364
  return { ct, tag: c.getAuthTag() };
21325
21365
  }
21326
21366
  function _tlvHeaderLen(der) {
@@ -21360,26 +21400,7 @@ var require_cms_encrypt = __commonJS({
21360
21400
  b.sequence([b.oid(O("messageDigest")), b.setOf([b.octetString(mdDigest)])])
21361
21401
  ];
21362
21402
  if (opts.authAttrs && opts.authAttrs.length) pairs = pairs.concat(opts.authAttrs);
21363
- var seenTypes = {};
21364
- pairs.forEach(function(p) {
21365
- var node;
21366
- try {
21367
- node = asn1.decode(p);
21368
- } catch (e) {
21369
- throw _err("cms/bad-input", "an authenticated attribute is not well-formed DER", e);
21370
- }
21371
- if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children || node.children.length !== 2 || node.children[1].tagClass !== "universal" || node.children[1].tagNumber !== asn1.TAGS.SET || !node.children[1].children || node.children[1].children.length < 1) {
21372
- throw _err("cms/bad-input", "an authenticated attribute must be an Attribute SEQUENCE { type, non-empty SET OF value } (RFC 5652)");
21373
- }
21374
- var t;
21375
- try {
21376
- t = asn1.read.oid(node.children[0]);
21377
- } catch (e) {
21378
- throw _err("cms/bad-input", "an authenticated attribute type is not an OBJECT IDENTIFIER", e);
21379
- }
21380
- if (seenTypes[t]) throw _err("cms/bad-input", "authenticated attributes must not repeat an attribute type (RFC 5652): " + t);
21381
- seenTypes[t] = 1;
21382
- });
21403
+ _assertAuthAttrs(pairs, null);
21383
21404
  var setOf = b.setOf(pairs);
21384
21405
  preimage = setOf;
21385
21406
  authAttrsDer = b.contextConstructed(2, setOf.subarray(_tlvHeaderLen(setOf)));
@@ -21429,6 +21450,7 @@ var require_cms_decrypt = __commonJS({
21429
21450
  var CmsError = frameworkError.CmsError;
21430
21451
  var WRAP_KEK_LENGTHS = schemaCms.WRAP_KEK_LENGTHS;
21431
21452
  var KEM_CT_LENGTHS = schemaCms.KEM_CT_LENGTHS;
21453
+ var AEAD_ALGS = schemaCms.AEAD_ALGS;
21432
21454
  function O(n) {
21433
21455
  return oid.byName(n);
21434
21456
  }
@@ -21724,16 +21746,24 @@ var require_cms_decrypt = __commonJS({
21724
21746
  }
21725
21747
  function _assertContentCipherMode(eci, ct) {
21726
21748
  var oidStr = eci.contentEncryptionAlgorithm.oid;
21727
- if (!CONTENT_MODE[oidStr]) return;
21728
- var wantMode = ct === "authEnvelopedData" ? "gcm" : "cbc";
21729
- if (CONTENT_MODE[oidStr] !== wantMode) {
21730
- throw _err("cms/unsupported-algorithm", "contentEncryptionAlgorithm " + oidStr + " is not a " + wantMode.toUpperCase() + " cipher, which " + ct + " requires (RFC 5083 sec. 2.1 / RFC 5084 sec. 3)");
21749
+ var isAead = guard.intrinsic.hasOwn(AEAD_ALGS, oidStr);
21750
+ if (!isAead && !guard.intrinsic.hasOwn(CONTENT_MODE, oidStr)) return;
21751
+ if (ct === "authEnvelopedData") {
21752
+ if (isAead) return;
21753
+ throw _err("cms/unsupported-algorithm", "contentEncryptionAlgorithm " + oidStr + " is not an authenticated encryption algorithm, which authEnvelopedData requires (RFC 5083 sec. 2)");
21731
21754
  }
21755
+ if (!isAead) return;
21756
+ throw _err("cms/unsupported-algorithm", "contentEncryptionAlgorithm " + oidStr + " is an authenticated encryption algorithm, which belongs in an authEnvelopedData rather than a " + ct + " (RFC 5083 sec. 2)");
21732
21757
  }
21733
21758
  async function _openContent(parsed, eci, cek, ct) {
21734
21759
  var alg = eci.contentEncryptionAlgorithm;
21735
- var keyBits = CONTENT_KEYBITS[alg.oid];
21736
- if (!keyBits) throw _err("cms/unsupported-algorithm", "unsupported contentEncryptionAlgorithm " + alg.oid);
21760
+ var keyBits = guard.intrinsic.hasOwn(CONTENT_KEYBITS, alg.oid) ? CONTENT_KEYBITS[alg.oid] : 0;
21761
+ if (!keyBits) {
21762
+ if (guard.intrinsic.hasOwn(AEAD_ALGS, alg.oid) && AEAD_ALGS[alg.oid] === "ccm") {
21763
+ throw _err("cms/unsupported-algorithm", "contentEncryptionAlgorithm " + alg.oid + " is AES-CCM. RFC 5084 sec. 3.1 permits it for this content type; this toolkit implements AES-GCM only for content encryption");
21764
+ }
21765
+ throw _err("cms/unsupported-algorithm", "unsupported contentEncryptionAlgorithm " + alg.oid);
21766
+ }
21737
21767
  if (eci.encryptedContent == null) throw _err("cms/no-encrypted-content", "the message has no encryptedContent (detached; supply it out of band)");
21738
21768
  var substitute = null;
21739
21769
  if (cek == null || cek.length !== keyBits / 8) {
@@ -21747,7 +21777,7 @@ var require_cms_decrypt = __commonJS({
21747
21777
  }
21748
21778
  var iv = asn1.read.octetString(asn1.decode(alg.parameters));
21749
21779
  if (iv.length !== 16) throw _fail();
21750
- return pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits);
21780
+ return pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed");
21751
21781
  } catch (e) {
21752
21782
  if (e instanceof CmsError && e.code !== "cms/decrypt-failed") throw e;
21753
21783
  throw _fail();
@@ -21760,7 +21790,7 @@ var require_cms_decrypt = __commonJS({
21760
21790
  var d = nodeCrypto.createDecipheriv("aes-" + keyBits + "-gcm", cek, nonce, { authTagLength: icvLen });
21761
21791
  d.setAuthTag(tag);
21762
21792
  if (aad && aad.length) d.setAAD(aad);
21763
- return Buffer.concat([d.update(ct), d.final()]);
21793
+ return guard.secret.cipherFinish(d, ct, CmsError, "cms/decrypt-failed", "the recovered content");
21764
21794
  }
21765
21795
  async function _decryptEncryptedData(parsed, km, opts) {
21766
21796
  var eci = parsed.encryptedContentInfo;
@@ -21774,7 +21804,7 @@ var require_cms_decrypt = __commonJS({
21774
21804
  if (cek.length !== keyBits / 8) throw _err("cms/bad-input", "the supplied cek length does not match the content algorithm");
21775
21805
  var iv = asn1.read.octetString(asn1.decode(alg.parameters));
21776
21806
  try {
21777
- return _originFields({ content: pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits), contentType: eci.contentType, contentTypeName: oid.name(eci.contentType) || eci.contentType, recipientType: "cek", recipientIndex: -1, contentEncryptionAlgorithm: alg.name || alg.oid, authenticated: false }, null, null);
21807
+ return _originFields({ content: pbes2.cbcDecrypt(cek, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed"), contentType: eci.contentType, contentTypeName: oid.name(eci.contentType) || eci.contentType, recipientType: "cek", recipientIndex: -1, contentEncryptionAlgorithm: alg.name || alg.oid, authenticated: false }, null, null);
21778
21808
  } catch (_e) {
21779
21809
  throw _fail();
21780
21810
  }
@@ -21804,7 +21834,7 @@ var require_cms_decrypt = __commonJS({
21804
21834
  if (pwE.owned) guard.secret.zeroize(pwE.bytes, CmsError, "cms/bad-input", "the password encoding");
21805
21835
  }
21806
21836
  try {
21807
- return _originFields({ content: pbes2.cbcDecrypt(key, iv, eci.encryptedContent, keyBits), contentType: eci.contentType, contentTypeName: oid.name(eci.contentType) || eci.contentType, recipientType: "password", recipientIndex: -1, contentEncryptionAlgorithm: oid.name(encOid) || encOid, authenticated: false }, null, null);
21837
+ return _originFields({ content: pbes2.cbcDecrypt(key, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed"), contentType: eci.contentType, contentTypeName: oid.name(eci.contentType) || eci.contentType, recipientType: "password", recipientIndex: -1, contentEncryptionAlgorithm: oid.name(encOid) || encOid, authenticated: false }, null, null);
21808
21838
  } catch (_e) {
21809
21839
  throw _fail();
21810
21840
  } finally {
@@ -21841,18 +21871,19 @@ var require_cms_decrypt = __commonJS({
21841
21871
  var blk = 16, alg = "aes-" + keyBits + "-cbc";
21842
21872
  if (wrapped.length < 2 * blk || wrapped.length % blk !== 0) throw _fail();
21843
21873
  var n = wrapped.length;
21844
- var ecb = nodeCrypto.createDecipheriv("aes-" + keyBits + "-ecb", kek, Buffer.alloc(0));
21845
- ecb.setAutoPadding(false);
21846
- var lastDec = Buffer.concat([ecb.update(wrapped.subarray(n - blk)), ecb.final()]);
21847
- var iv2 = Buffer.alloc(blk);
21848
- for (var i = 0; i < blk; i++) iv2[i] = lastDec[i] ^ wrapped[n - 2 * blk + i];
21849
- var d1 = nodeCrypto.createDecipheriv(alg, kek, iv2);
21850
- d1.setAutoPadding(false);
21851
- var pass1 = Buffer.concat([d1.update(wrapped), d1.final()]);
21852
- var d2 = nodeCrypto.createDecipheriv(alg, kek, iv);
21853
- d2.setAutoPadding(false);
21854
- var body = Buffer.concat([d2.update(pass1), d2.final()]);
21855
- try {
21874
+ var lastDec = null, pass1 = null, body = null;
21875
+ try {
21876
+ var ecb = nodeCrypto.createDecipheriv("aes-" + keyBits + "-ecb", kek, Buffer.alloc(0));
21877
+ ecb.setAutoPadding(false);
21878
+ lastDec = guard.secret.cipherFinish(ecb, wrapped.subarray(n - blk), CmsError, "cms/decrypt-failed", "the recovered CBC last block");
21879
+ var iv2 = Buffer.alloc(blk);
21880
+ for (var i = 0; i < blk; i++) iv2[i] = lastDec[i] ^ wrapped[n - 2 * blk + i];
21881
+ var d1 = nodeCrypto.createDecipheriv(alg, kek, iv2);
21882
+ d1.setAutoPadding(false);
21883
+ pass1 = guard.secret.cipherFinish(d1, wrapped, CmsError, "cms/decrypt-failed", "the PWRI first-pass plaintext");
21884
+ var d2 = nodeCrypto.createDecipheriv(alg, kek, iv);
21885
+ d2.setAutoPadding(false);
21886
+ body = guard.secret.cipherFinish(d2, pass1, CmsError, "cms/decrypt-failed", "the PWRI plaintext block");
21856
21887
  var count = body[0];
21857
21888
  if (count < 1 || count + 4 > body.length) throw _fail();
21858
21889
  var cek = body.subarray(4, 4 + count);
@@ -21861,7 +21892,7 @@ var require_cms_decrypt = __commonJS({
21861
21892
  if (bad !== 0) throw _fail();
21862
21893
  return Buffer.from(cek);
21863
21894
  } finally {
21864
- guard.secret.zeroizeAll([body, pass1], CmsError, "cms/bad-input", "the PWRI plaintext block");
21895
+ guard.secret.zeroizeAll([body, pass1, lastDec], CmsError, "cms/bad-input", "the PWRI plaintext block");
21865
21896
  }
21866
21897
  }
21867
21898
  function _oaepHashFromParams(paramsBytes) {
@@ -30341,7 +30372,7 @@ var require_key = __commonJS({
30341
30372
  if (pwK.owned) guard.secret.zeroize(pwK.bytes, KeyError, "key/bad-input", "the password encoding");
30342
30373
  }
30343
30374
  try {
30344
- var ciphertext = pbes2.cbcEncrypt(dk, iv, der, keyBits);
30375
+ var ciphertext = pbes2.cbcEncrypt(dk, iv, der, keyBits, KeyError, "key/bad-input");
30345
30376
  var epki = b.sequence([pbes2.pbes2AlgId(salt, iterations, prf, cipherName, iv), b.octetString(ciphertext)]);
30346
30377
  pkcs8.parseEncrypted(epki);
30347
30378
  return opts.pem ? pkcs8.pemEncode(epki, "ENCRYPTED PRIVATE KEY") : epki;
@@ -31434,7 +31465,7 @@ var require_pkcs12_build = __commonJS({
31434
31465
  try {
31435
31466
  if (scheme.rc2) return rc2.cbcDecrypt(keyM, scheme.rc2, iv, ct, _err, "pkcs12/decrypt-failed");
31436
31467
  var d = nodeCrypto.createDecipheriv(scheme.cipher, keyM, iv);
31437
- return Buffer.concat([d.update(ct), d.final()]);
31468
+ return guard.secret.cipherFinish(d, ct, Pkcs12Error, "pkcs12/bad-input", "the recovered safe contents");
31438
31469
  } catch (_e) {
31439
31470
  throw _err("pkcs12/decrypt-failed", "decryption failed");
31440
31471
  } finally {
@@ -32183,7 +32214,7 @@ var require_hpke = __commonJS({
32183
32214
  var nonce = this._nonce();
32184
32215
  var c = nodeCrypto.createCipheriv(aead.cipher, this._key, nonce, { authTagLength: aead.Nt });
32185
32216
  if (aad && aad.length) c.setAAD(aad);
32186
- var body = concat([c.update(_buf(pt)), c.final()]);
32217
+ var body = guard.secret.cipherFinish(c, _buf(pt), HpkeError, "hpke/bad-input", "a seal intermediate");
32187
32218
  var ct = concat([body, c.getAuthTag()]);
32188
32219
  this._inc();
32189
32220
  return ct;
@@ -32200,7 +32231,7 @@ var require_hpke = __commonJS({
32200
32231
  d.setAuthTag(ct.subarray(ct.length - aead.Nt));
32201
32232
  var pt;
32202
32233
  try {
32203
- pt = concat([d.update(ct.subarray(0, ct.length - aead.Nt)), d.final()]);
32234
+ pt = guard.secret.cipherFinish(d, ct.subarray(0, ct.length - aead.Nt), HpkeError, "hpke/bad-input", "the recovered plaintext");
32204
32235
  } catch (e) {
32205
32236
  throw _err("hpke/open-failed", "AEAD authentication failed (RFC 9180 sec. 5.2)", e);
32206
32237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.18.41",
3
+ "version": "0.18.42",
4
4
  "description": "The Node framework that owns its stack.",
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:a9c950dd-01b0-4088-b83e-2dfd36c75675",
5
+ "serialNumber": "urn:uuid:035db9d2-b000-46ab-bdef-fe4bff353228",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-08-20T14:26:30.885Z",
8
+ "timestamp": "2026-08-20T17:21:22.951Z",
9
9
  "lifecycles": [
10
10
  {
11
11
  "phase": "build"
@@ -19,14 +19,14 @@
19
19
  }
20
20
  ],
21
21
  "component": {
22
- "bom-ref": "@blamejs/core@0.18.41",
22
+ "bom-ref": "@blamejs/core@0.18.42",
23
23
  "type": "application",
24
24
  "name": "blamejs",
25
- "version": "0.18.41",
25
+ "version": "0.18.42",
26
26
  "scope": "required",
27
27
  "author": "blamejs contributors",
28
28
  "description": "The Node framework that owns its stack.",
29
- "purl": "pkg:npm/%40blamejs/core@0.18.41",
29
+ "purl": "pkg:npm/%40blamejs/core@0.18.42",
30
30
  "properties": [],
31
31
  "externalReferences": [
32
32
  {
@@ -54,7 +54,7 @@
54
54
  "components": [],
55
55
  "dependencies": [
56
56
  {
57
- "ref": "@blamejs/core@0.18.41",
57
+ "ref": "@blamejs/core@0.18.42",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]