@blamejs/core 0.18.55 → 0.18.56

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.
@@ -253,7 +253,7 @@ function _writeReject(req, res, message, reason, onDeny, problemMode) {
253
253
  * protocolResolver: function(req): "http"|"https", // own the HTTPS decision
254
254
  * trustProxy: boolean|number, // legacy; refused unless paired with trustedProxies/protocolResolver (spoofable)
255
255
  * audit: boolean,
256
- * skipStateless: boolean, // default false — skip validation for Authorization-header / cookieless (not-CSRF-able) requests
256
+ * skipStateless: boolean, // default false — skip the token check for cookieless (not-CSRF-able) requests. Turns on the absence of the ambient credential only: an Authorization header is not part of the test, and it never waives `checkOrigin`.
257
257
  * onDeny: function(req, res, info): void, // own the 403; info = { status, reason }
258
258
  * problemDetails: boolean, // default false — emit RFC 9457 application/problem+json instead of the default JSON envelope
259
259
  * }
@@ -363,17 +363,21 @@ function create(opts) {
363
363
  // is opt-in rather than silent.
364
364
  var requireOriginOpt = opts.requireOrigin === true;
365
365
 
366
- // skipStateless — skip token VALIDATION for requests that carry an
367
- // Authorization header (bearer / token auth) or no Cookie header at
368
- // all. Such requests are not CSRF-able: CSRF abuses a victim's ambient
369
- // cookie credential, and a token-authenticated or cookieless request
370
- // has none to abuse. The token is still ISSUED on safe methods so a
371
- // later cookie-authenticated browser flow on the same app works. Default
372
- // false (strict every state-changing request is validated). createApp
373
- // wires its default csrf with this on so mixed browser-form + token-API
374
- // surfaces don't reject legitimate API clients. Cross-site form CSRF is
375
- // unaffected: the browser auto-sends the victim's cookies, so the attack
376
- // request always carries a Cookie header and is validated.
366
+ // skipStateless — skip token VALIDATION for requests that carry no Cookie
367
+ // header at all. Such requests are not CSRF-able: CSRF abuses a victim's
368
+ // ambient cookie credential, and a request that sends none has nothing to
369
+ // abuse. The token is still ISSUED on safe methods so a later
370
+ // cookie-authenticated browser flow on the same app works. Default false
371
+ // (strict every state-changing request is validated). createApp wires its
372
+ // default csrf with this on so mixed browser-form + token-API surfaces don't
373
+ // reject legitimate API clients. Cross-site form CSRF is unaffected: the
374
+ // browser auto-sends the victim's cookies, so the attack request always
375
+ // carries a Cookie header and is validated.
376
+ //
377
+ // An `Authorization` header is NOT part of the test, and a bearer client that
378
+ // also sends an unrelated cookie is validated like anything else. Deciding
379
+ // otherwise needs the auth layer's verdict about which credential
380
+ // authenticated the request, which header presence cannot supply.
377
381
  var skipStateless = opts.skipStateless === true;
378
382
 
379
383
  // Per-path exemption (string-prefix / RegExp / skip predicate), validated at
@@ -548,14 +552,6 @@ function create(opts) {
548
552
 
549
553
  if (methods.indexOf(req.method) === -1) return next();
550
554
 
551
- // Stateless / token-authenticated requests are not CSRF-able — the
552
- // token was still issued above for any later browser flow.
553
- if (skipStateless) {
554
- var hasAuthHeader = !!(req.headers && req.headers.authorization);
555
- var hasCookieHeader = !!(req.headers && req.headers.cookie);
556
- if (hasAuthHeader || !hasCookieHeader) return next();
557
- }
558
-
559
555
  // requireJsonContentType — refuse before the token check.
560
556
  if (requireJsonCt) {
561
557
  var ct = req.headers && req.headers["content-type"];
@@ -578,6 +574,27 @@ function create(opts) {
578
574
  }
579
575
  }
580
576
 
577
+ // Stateless requests are not CSRF-able — the token was still issued above
578
+ // for any later browser flow.
579
+ //
580
+ // The test is the ABSENCE of the ambient credential, and nothing else. CSRF
581
+ // spends a cookie the browser attaches on its own; a request carrying none
582
+ // has nothing to abuse. This used to fire on an `Authorization` header too,
583
+ // which was two mistakes at once. Presence is not authenticity — an
584
+ // attacker composing a cross-site request writes their own headers, so
585
+ // `Authorization: Bearer nonsense` satisfied it by being typed. And the
586
+ // header says nothing about which credential authenticated the request:
587
+ // attachUser with tokenFrom: "both" reads the cookie FIRST, so a request
588
+ // carrying both was authenticated by exactly the ambient credential this
589
+ // gate protects, and skipped the gate on a header nobody had read.
590
+ //
591
+ // It also sits BELOW the origin cross-check now rather than above it. A
592
+ // consumer that asked for checkOrigin asked for something the token compare
593
+ // does not give them, and there is no reading of "stateless" under which a
594
+ // cross-origin state change becomes acceptable — the branch that waived the
595
+ // first line of defence was waiving the second one with it.
596
+ if (skipStateless && !(req.headers && req.headers.cookie)) return next();
597
+
581
598
  if (!cookieCfg) {
582
599
  // Session-stored mode — operator's tokenLookup is the source.
583
600
  expected = opts.tokenLookup(req);
@@ -29,9 +29,12 @@
29
29
  * schema (sidHash PRIMARY KEY, userId, userIdHash, data, createdAt,
30
30
  * expiresAt, lastActivity) plus the indexes session-side queries
31
31
  * need (userIdHash for `destroyAllForUser`, expiresAt for
32
- * `purgeExpired`). Operators typically point `file` at tmpfs (e.g.
33
- * `/dev/shm/blamejs-sessions.db`) so session inserts run RAM-fast
34
- * and don't compete with the main DB's encryption-flush cycle.
32
+ * `purgeExpired`). Operators typically point `file` at tmpfs
33
+ * (`/dev/shm/blamejs-sessions.db` on Linux, an in-memory volume on
34
+ * Windows) so session inserts run RAM-fast and don't compete with
35
+ * the main DB's encryption-flush cycle. The path is yours to name —
36
+ * a leading slash is drive-relative on Windows, where `/dev/shm`
37
+ * means `C:\dev\shm` and is ordinary disk.
35
38
  *
36
39
  * Wire it once at boot, before the first session call:
37
40
  *
@@ -2,7 +2,7 @@
2
2
  "_comment": "Vendored dependencies — no npm runtime packages. Use scripts/vendor-update.sh to update.",
3
3
  "packages": {
4
4
  "@noble/ciphers": {
5
- "version": "2.3.0",
5
+ "version": "2.4.0",
6
6
  "license": "MIT",
7
7
  "author": "Paul Miller",
8
8
  "source": "https://github.com/paulmillr/noble-ciphers",
@@ -15,16 +15,16 @@
15
15
  "browser": "lib/vendor/browser/noble-ciphers.mjs"
16
16
  },
17
17
  "bundler": "esbuild --format=cjs --platform=node (server), esbuild --format=esm --platform=browser (browser)",
18
- "bundledAt": "2026-08-10T00:00:00Z",
19
- "cpe": "cpe:2.3:a:paulmillr:noble-ciphers:2.3.0:*:*:*:*:node.js:*:*",
18
+ "bundledAt": "2026-08-27T00:00:00Z",
19
+ "cpe": "cpe:2.3:a:paulmillr:noble-ciphers:2.4.0:*:*:*:*:node.js:*:*",
20
20
  "hashes": {
21
- "server": "sha256:f3325f480cb8eb814fcb0baaa19336cbbf2b993f48624c6aa9600ffd69d0be5e",
22
- "browser": "sha256:0ffd91540bcb586a29b56e52ee1c29df69097b50776beb4036a07558f7a4e12e"
21
+ "server": "sha256:acdcf1ba4bde8177cbfcfef22b543022284a1529afb68bc8329303837c8c021e",
22
+ "browser": "sha256:fc422d85a65c5dc1208066b0156e100c4790be3c8791f644b80524c25d4bc3ee"
23
23
  },
24
- "refreshedAt": "2026-08-25T10:45:22.083Z"
24
+ "refreshedAt": "2026-08-28T04:49:21.011Z"
25
25
  },
26
26
  "@noble/hashes": {
27
- "version": "2.3.0",
27
+ "version": "2.4.0",
28
28
  "license": "MIT",
29
29
  "author": "Paul Miller",
30
30
  "source": "https://github.com/paulmillr/noble-hashes",
@@ -43,15 +43,15 @@
43
43
  "browser": "lib/vendor/browser/noble-hashes.mjs"
44
44
  },
45
45
  "bundler": "esbuild --format=esm --platform=browser (browser only)",
46
- "bundledAt": "2026-08-10T00:00:00Z",
47
- "cpe": "cpe:2.3:a:paulmillr:noble-hashes:2.3.0:*:*:*:*:node.js:*:*",
46
+ "bundledAt": "2026-08-27T00:00:00Z",
47
+ "cpe": "cpe:2.3:a:paulmillr:noble-hashes:2.4.0:*:*:*:*:node.js:*:*",
48
48
  "hashes": {
49
- "browser": "sha256:dfe4b7ae3c9880e388c8da4b68f44742b229b53afacd1e674179527e33da62b0"
49
+ "browser": "sha256:0e135cbb496a1b7d15bf36b8c611a8feaad49ced60a03bf916bada78b589aa66"
50
50
  },
51
- "refreshedAt": "2026-08-25T10:45:22.083Z"
51
+ "refreshedAt": "2026-08-28T04:49:21.011Z"
52
52
  },
53
53
  "@noble/curves": {
54
- "version": "2.3.0",
54
+ "version": "2.4.0",
55
55
  "license": "MIT",
56
56
  "author": "Paul Miller",
57
57
  "source": "https://github.com/paulmillr/noble-curves",
@@ -65,21 +65,21 @@
65
65
  "server": "lib/vendor/noble-curves.cjs"
66
66
  },
67
67
  "bundler": "esbuild --format=cjs --platform=node",
68
- "bundledAt": "2026-08-21T00:00:00Z",
69
- "cpe": "cpe:2.3:a:paulmillr:noble-curves:2.3.0:*:*:*:*:node.js:*:*",
68
+ "bundledAt": "2026-08-27T00:00:00Z",
69
+ "cpe": "cpe:2.3:a:paulmillr:noble-curves:2.4.0:*:*:*:*:node.js:*:*",
70
70
  "hashes": {
71
- "server": "sha256:b5fe88d1ea780d0581dee6145d666f89d46fc9531b5db35db2e5b16627840890"
71
+ "server": "sha256:477359f445eed241ad4ededb84921483c3c97618a160ef78b74bb1a421c17da7"
72
72
  },
73
- "refreshedAt": "2026-08-25T10:45:22.083Z",
73
+ "refreshedAt": "2026-08-28T04:49:21.011Z",
74
74
  "components": {
75
75
  "@noble/hashes": {
76
76
  "url": "https://github.com/paulmillr/noble-hashes",
77
- "version": "2.3.0"
77
+ "version": "2.4.0"
78
78
  }
79
79
  }
80
80
  },
81
81
  "@noble/post-quantum": {
82
- "version": "0.7.0",
82
+ "version": "0.7.1",
83
83
  "license": "MIT",
84
84
  "author": "Paul Miller",
85
85
  "source": "https://github.com/paulmillr/noble-post-quantum",
@@ -108,25 +108,25 @@
108
108
  "browser": "lib/vendor/browser/noble-post-quantum.mjs"
109
109
  },
110
110
  "bundler": "esbuild --format=cjs --platform=node (server), esbuild --format=esm --platform=browser (browser)",
111
- "bundledAt": "2026-08-10T00:00:00Z",
112
- "cpe": "cpe:2.3:a:paulmillr:noble-post-quantum:0.7.0:*:*:*:*:node.js:*:*",
111
+ "bundledAt": "2026-08-27T00:00:00Z",
112
+ "cpe": "cpe:2.3:a:paulmillr:noble-post-quantum:0.7.1:*:*:*:*:node.js:*:*",
113
113
  "hashes": {
114
- "server": "sha256:fab7ebe5737793862c473444f4ee5912f79dd1edec86683acbb4eecbca0f5892",
115
- "browser": "sha256:cae1d5bbdc7184b202b6ca68df6e1db7b0d0f668c77809ded189ca7f271accc9"
114
+ "server": "sha256:04eaaa4838b43162912c851dd2cab52ab82f56decad8542934171247017a6893",
115
+ "browser": "sha256:30ceef750bc57ae5f59c5aa7454eafb7aad55d30c261a1b5c538a66c569fc031"
116
116
  },
117
- "refreshedAt": "2026-08-25T10:45:22.083Z",
117
+ "refreshedAt": "2026-08-28T04:49:21.011Z",
118
118
  "components": {
119
119
  "@noble/hashes": {
120
120
  "url": "https://github.com/paulmillr/noble-hashes",
121
- "version": "2.3.0"
121
+ "version": "2.4.0"
122
122
  },
123
123
  "@noble/curves": {
124
124
  "url": "https://github.com/paulmillr/noble-curves",
125
- "version": "2.3.0"
125
+ "version": "2.4.0"
126
126
  },
127
127
  "@noble/ciphers": {
128
128
  "url": "https://github.com/paulmillr/noble-ciphers",
129
- "version": "2.3.0"
129
+ "version": "2.4.0"
130
130
  }
131
131
  }
132
132
  },
@@ -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-25T10:45:22.083Z"
151
+ "refreshedAt": "2026-08-28T04:49:21.011Z"
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-25T10:45:22.083Z"
176
+ "refreshedAt": "2026-08-28T04:49:21.011Z"
177
177
  },
178
178
  "publicsuffix-list": {
179
179
  "version": "master",
@@ -193,7 +193,7 @@
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-25T10:45:22.083Z"
196
+ "refreshedAt": "2026-08-28T04:49:21.011Z"
197
197
  },
198
198
  "@blamejs/pki": {
199
199
  "version": "0.5.31",
@@ -221,7 +221,7 @@
221
221
  "hashes": {
222
222
  "server": "sha256:8cc54fdfb7ac5277fb30f054f57500a9e77ce6b2d2b8f6846a14766d217770db"
223
223
  },
224
- "refreshedAt": "2026-08-25T10:45:22.083Z"
224
+ "refreshedAt": "2026-08-28T04:49:21.011Z"
225
225
  }
226
226
  }
227
227
  }
@@ -1,4 +1,4 @@
1
- // XChaCha20-Poly1305 — vendored from @noble/ciphers v2.3.0 by Paul Miller
1
+ // XChaCha20-Poly1305 — vendored from @noble/ciphers v2.4.0 by Paul Miller
2
2
  // License: MIT — https://github.com/paulmillr/noble-ciphers
3
3
  // Browser build (ESM), bundled with esbuild from the same install as the
4
4
  // server bundle beside it. Exports: xchacha20poly1305
@@ -71,6 +71,17 @@ function byteSwap32(arr) {
71
71
  return arr;
72
72
  }
73
73
  var swap32IfBE = isLE ? (u) => u : byteSwap32;
74
+ function overlapBytes(a, b) {
75
+ if (!a.byteLength || !b.byteLength)
76
+ return false;
77
+ return a.buffer === b.buffer && // best we can do, may fail with an obscure Proxy
78
+ a.byteOffset < b.byteOffset + b.byteLength && // a starts before b end
79
+ b.byteOffset < a.byteOffset + a.byteLength;
80
+ }
81
+ function complexOverlapBytes(input, output) {
82
+ if (overlapBytes(input, output) && input.byteOffset < output.byteOffset)
83
+ throw new Error("complex overlap of input and output is not supported");
84
+ }
74
85
  function checkOpts(defaults, opts) {
75
86
  aobject(defaults, "defaults");
76
87
  aobject(opts, "opts");
@@ -238,7 +249,10 @@ function createCipher(core, opts) {
238
249
  abytes(nonce, void 0, "nonce");
239
250
  abytes(data, void 0, "data");
240
251
  const len = data.length;
252
+ const hasOutput = output !== void 0;
241
253
  output = getOutput(len, output, false);
254
+ if (hasOutput)
255
+ complexOverlapBytes(data, output);
242
256
  anumber(counter);
243
257
  if (counter < 0 || counter >= MAX_COUNTER)
244
258
  throw new Error("arx: counter overflow");
@@ -1,4 +1,4 @@
1
- // @noble/hashes v2.3.0 — vendored from Paul Miller
1
+ // @noble/hashes v2.4.0 — vendored from Paul Miller
2
2
  // License: MIT — https://github.com/paulmillr/noble-hashes
3
3
  // Browser build (ESM), bundled with esbuild. The server side uses node:crypto
4
4
  // for these, so there is no .cjs beside it.
@@ -89,6 +89,14 @@ var aobject = (value, label) => {
89
89
  if (value === null || typeof value !== "object" || Array.isArray(value))
90
90
  throw new TypeError((label === "object" ? "" : `"${label}" `) + "expected object, got type=" + typeof value);
91
91
  };
92
+ var aopts = (value, label) => {
93
+ aobject(value, label);
94
+ const proto = Object.getPrototypeOf(value);
95
+ if (proto !== Object.prototype && proto !== null)
96
+ throw new TypeError(`"${label}" expected plain object`);
97
+ if (Object.hasOwn(value, "__proto__"))
98
+ throw new TypeError(`"${label}.__proto__" is not allowed`);
99
+ };
92
100
  function aexists(instance, checkFinished = true) {
93
101
  if (instance.destroyed)
94
102
  throw new Error("hash was destroyed");
@@ -128,10 +136,10 @@ function byteSwap32(arr) {
128
136
  }
129
137
  var swap32IfBE = isLE ? (u) => u : byteSwap32;
130
138
  function checkOpts(defaults, opts, title = "opts") {
131
- aobject(defaults, "defaults");
139
+ aopts(defaults, "defaults");
132
140
  if (opts !== void 0)
133
- aobject(opts, title);
134
- const merged = Object.assign(defaults, opts);
141
+ aopts(opts, title);
142
+ const merged = Object.assign(/* @__PURE__ */ Object.create(null), defaults, opts);
135
143
  return merged;
136
144
  }
137
145
  function createHasher(hashCons, info = {}) {
@@ -1,4 +1,4 @@
1
- // @noble/post-quantum v0.7.0 — vendored from Paul Miller
1
+ // @noble/post-quantum v0.7.1 — vendored from Paul Miller
2
2
  // License: MIT — https://github.com/paulmillr/noble-post-quantum
3
3
  // Browser build (ESM), bundled with esbuild from the same install as the
4
4
  // server bundle beside it. The KEM suites only — a client half encapsulates
@@ -57,6 +57,14 @@ var aobject = (value, label) => {
57
57
  if (value === null || typeof value !== "object" || Array.isArray(value))
58
58
  throw new TypeError((label === "object" ? "" : `"${label}" `) + "expected object, got type=" + typeof value);
59
59
  };
60
+ var aopts = (value, label) => {
61
+ aobject(value, label);
62
+ const proto = Object.getPrototypeOf(value);
63
+ if (proto !== Object.prototype && proto !== null)
64
+ throw new TypeError(`"${label}" expected plain object`);
65
+ if (Object.hasOwn(value, "__proto__"))
66
+ throw new TypeError(`"${label}.__proto__" is not allowed`);
67
+ };
60
68
  function aexists(instance, checkFinished = true) {
61
69
  if (instance.destroyed)
62
70
  throw new Error("hash was destroyed");
@@ -90,10 +98,10 @@ function byteSwap32(arr) {
90
98
  }
91
99
  var swap32IfBE = isLE ? (u) => u : byteSwap32;
92
100
  function checkOpts(defaults, opts2, title = "opts") {
93
- aobject(defaults, "defaults");
101
+ aopts(defaults, "defaults");
94
102
  if (opts2 !== void 0)
95
- aobject(opts2, title);
96
- const merged = Object.assign(defaults, opts2);
103
+ aopts(opts2, title);
104
+ const merged = Object.assign(/* @__PURE__ */ Object.create(null), defaults, opts2);
97
105
  return merged;
98
106
  }
99
107
  function createHasher(hashCons, info = {}) {
@@ -509,7 +517,7 @@ function equalBytes(a, b) {
509
517
  return diff === 0;
510
518
  }
511
519
  function copyBytes(bytes) {
512
- return Uint8Array.from(abytes(bytes));
520
+ return new Uint8Array(abytes(bytes));
513
521
  }
514
522
  function splitCoder(label, ...lengths) {
515
523
  const getLength = (c) => typeof c === "number" ? c : c.bytesLen;
@@ -937,8 +945,9 @@ var genKPKE = (opts_) => {
937
945
  for (let i = 0; i < K; i++)
938
946
  polyAdd(tmp, MultiplyNTTs(sk[i], crystals.NTT.encode(u[i])));
939
947
  polySub(v, crystals.NTT.decode(tmp));
940
- cleanBytes(tmp, sk, u);
941
- return poly1.encode(v);
948
+ const res = poly1.encode(v);
949
+ cleanBytes(tmp, sk, u, v);
950
+ return res;
942
951
  }
943
952
  };
944
953
  };
@@ -968,32 +977,55 @@ function createKyber(opts2) {
968
977
  return Object.freeze({
969
978
  info: Object.freeze({ type: "ml-kem" }),
970
979
  lengths: kemLengths,
971
- keygen: (seed = randomBytes2(seedLen)) => {
972
- abytesDoc(seed, seedLen, "seed");
973
- const { publicKey, secretKey: sk } = KPKE.keygen(seed.subarray(0, 32));
974
- const publicKeyHash = HASH256(publicKey);
975
- const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, seed.subarray(32)]);
976
- cleanBytes(sk, publicKeyHash);
977
- return {
978
- publicKey,
979
- secretKey
980
- };
980
+ keygen: (seed) => {
981
+ const ownSeed = seed === void 0;
982
+ const s = ownSeed ? randomBytes2(seedLen) : seed;
983
+ let sk;
984
+ let publicKeyHash;
985
+ try {
986
+ abytesDoc(s, seedLen, "seed");
987
+ const keys = KPKE.keygen(s.subarray(0, 32));
988
+ const publicKey = keys.publicKey;
989
+ sk = keys.secretKey;
990
+ publicKeyHash = HASH256(publicKey);
991
+ const secretKey = secretCoder.encode([sk, publicKey, publicKeyHash, s.subarray(32)]);
992
+ return {
993
+ publicKey,
994
+ secretKey
995
+ };
996
+ } finally {
997
+ if (sk !== void 0)
998
+ cleanBytes(sk);
999
+ if (publicKeyHash !== void 0)
1000
+ cleanBytes(publicKeyHash);
1001
+ if (ownSeed)
1002
+ cleanBytes(s);
1003
+ }
981
1004
  },
982
1005
  getPublicKey: (secretKey) => {
983
1006
  const [_sk, publicKey, _publicKeyHash, _z] = secretCoder.decode(secretKey);
984
1007
  return Uint8Array.from(publicKey);
985
1008
  },
986
- encapsulate: (publicKey, msg = randomBytes2(msgLen)) => {
987
- abytesDoc(publicKey, lengths.publicKey, "publicKey");
988
- abytesDoc(msg, msgLen, "message");
989
- validateModulus(publicKey, "encapsulate");
990
- const kr = HASH512.create().update(msg).update(HASH256(publicKey)).digest();
991
- const cipherText = KPKE.encrypt(publicKey, msg, kr.subarray(32, 64));
992
- cleanBytes(kr.subarray(32));
993
- return {
994
- cipherText,
995
- sharedSecret: kr.subarray(0, 32)
996
- };
1009
+ encapsulate: (publicKey, msg) => {
1010
+ const ownMsg = msg === void 0;
1011
+ const m = ownMsg ? randomBytes2(msgLen) : msg;
1012
+ let kr;
1013
+ try {
1014
+ abytesDoc(publicKey, lengths.publicKey, "publicKey");
1015
+ abytesDoc(m, msgLen, "message");
1016
+ validateModulus(publicKey, "encapsulate");
1017
+ kr = HASH512.create().update(m).update(HASH256(publicKey)).digest();
1018
+ const cipherText = KPKE.encrypt(publicKey, m, kr.subarray(32, 64));
1019
+ return {
1020
+ cipherText,
1021
+ sharedSecret: kr.subarray(0, 32)
1022
+ };
1023
+ } finally {
1024
+ if (kr !== void 0)
1025
+ cleanBytes(kr.subarray(32));
1026
+ if (ownMsg)
1027
+ cleanBytes(m);
1028
+ }
997
1029
  },
998
1030
  decapsulate: (cipherText, secretKey) => {
999
1031
  abytesDoc(secretKey, secretCoder.bytesLen, "secretKey");
@@ -1026,15 +1058,24 @@ function createKyber(opts2) {
1026
1058
  const cached = KPKE.prepare(ek);
1027
1059
  return Object.freeze({
1028
1060
  publicKey: ek,
1029
- encapsulate: (msg = randomBytes2(msgLen)) => {
1030
- abytesDoc(msg, msgLen, "message");
1031
- const kr = HASH512.create().update(msg).update(publicKeyHash).digest();
1032
- const cipherText = cached.encrypt(msg, kr.subarray(32, 64));
1033
- cleanBytes(kr.subarray(32));
1034
- return {
1035
- cipherText,
1036
- sharedSecret: kr.subarray(0, 32)
1037
- };
1061
+ encapsulate: (msg) => {
1062
+ const ownMsg = msg === void 0;
1063
+ const m = ownMsg ? randomBytes2(msgLen) : msg;
1064
+ let kr;
1065
+ try {
1066
+ abytesDoc(m, msgLen, "message");
1067
+ kr = HASH512.create().update(m).update(publicKeyHash).digest();
1068
+ const cipherText = cached.encrypt(m, kr.subarray(32, 64));
1069
+ return {
1070
+ cipherText,
1071
+ sharedSecret: kr.subarray(0, 32)
1072
+ };
1073
+ } finally {
1074
+ if (kr !== void 0)
1075
+ cleanBytes(kr.subarray(32));
1076
+ if (ownMsg)
1077
+ cleanBytes(m);
1078
+ }
1038
1079
  },
1039
1080
  decapsulate: (cipherText, secretKey) => {
1040
1081
  abytesDoc(secretKey, secretCoder.bytesLen, "secretKey");
@@ -1,4 +1,4 @@
1
- // XChaCha20-Poly1305 — vendored from @noble/ciphers v2.3.0 by Paul Miller
1
+ // XChaCha20-Poly1305 — vendored from @noble/ciphers v2.4.0 by Paul Miller
2
2
  // License: MIT — https://github.com/paulmillr/noble-ciphers
3
3
  // Bundled with esbuild. Exports: xchacha20poly1305
4
4
  var __defProp = Object.defineProperty;
@@ -95,6 +95,17 @@ function byteSwap32(arr) {
95
95
  return arr;
96
96
  }
97
97
  var swap32IfBE = isLE ? (u) => u : byteSwap32;
98
+ function overlapBytes(a, b) {
99
+ if (!a.byteLength || !b.byteLength)
100
+ return false;
101
+ return a.buffer === b.buffer && // best we can do, may fail with an obscure Proxy
102
+ a.byteOffset < b.byteOffset + b.byteLength && // a starts before b end
103
+ b.byteOffset < a.byteOffset + a.byteLength;
104
+ }
105
+ function complexOverlapBytes(input, output) {
106
+ if (overlapBytes(input, output) && input.byteOffset < output.byteOffset)
107
+ throw new Error("complex overlap of input and output is not supported");
108
+ }
98
109
  function checkOpts(defaults, opts) {
99
110
  aobject(defaults, "defaults");
100
111
  aobject(opts, "opts");
@@ -262,7 +273,10 @@ function createCipher(core, opts) {
262
273
  abytes(nonce, void 0, "nonce");
263
274
  abytes(data, void 0, "data");
264
275
  const len = data.length;
276
+ const hasOutput = output !== void 0;
265
277
  output = getOutput(len, output, false);
278
+ if (hasOutput)
279
+ complexOverlapBytes(data, output);
266
280
  anumber(counter);
267
281
  if (counter < 0 || counter >= MAX_COUNTER)
268
282
  throw new Error("arx: counter overflow");