@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.
- package/CHANGELOG.md +36 -0
- package/NOTICE +4 -4
- package/README.md +6 -6
- package/lib/audit.js +26 -24
- package/lib/chain-writer.js +17 -0
- package/lib/mail-server-managesieve.js +15 -5
- package/lib/mail-server-mx.js +201 -9
- package/lib/mail-server-tls.js +23 -8
- package/lib/middleware/csrf-protect.js +37 -20
- package/lib/session-stores.js +6 -3
- package/lib/vendor/MANIFEST.json +30 -30
- package/lib/vendor/browser/noble-ciphers.mjs +15 -1
- package/lib/vendor/browser/noble-hashes.mjs +12 -4
- package/lib/vendor/browser/noble-post-quantum.mjs +78 -37
- package/lib/vendor/noble-ciphers.cjs +15 -1
- package/lib/vendor/noble-curves.cjs +46 -21
- package/lib/vendor/noble-post-quantum.cjs +184 -75
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
|
@@ -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
|
|
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
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
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);
|
package/lib/session-stores.js
CHANGED
|
@@ -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
|
|
33
|
-
* `/dev/shm/blamejs-sessions.db`
|
|
34
|
-
* and don't compete with
|
|
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
|
*
|
package/lib/vendor/MANIFEST.json
CHANGED
|
@@ -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.
|
|
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-
|
|
19
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-ciphers:2.
|
|
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:
|
|
22
|
-
"browser": "sha256:
|
|
21
|
+
"server": "sha256:acdcf1ba4bde8177cbfcfef22b543022284a1529afb68bc8329303837c8c021e",
|
|
22
|
+
"browser": "sha256:fc422d85a65c5dc1208066b0156e100c4790be3c8791f644b80524c25d4bc3ee"
|
|
23
23
|
},
|
|
24
|
-
"refreshedAt": "2026-08-
|
|
24
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
25
25
|
},
|
|
26
26
|
"@noble/hashes": {
|
|
27
|
-
"version": "2.
|
|
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-
|
|
47
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-hashes:2.
|
|
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:
|
|
49
|
+
"browser": "sha256:0e135cbb496a1b7d15bf36b8c611a8feaad49ced60a03bf916bada78b589aa66"
|
|
50
50
|
},
|
|
51
|
-
"refreshedAt": "2026-08-
|
|
51
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
52
52
|
},
|
|
53
53
|
"@noble/curves": {
|
|
54
|
-
"version": "2.
|
|
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-
|
|
69
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-curves:2.
|
|
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:
|
|
71
|
+
"server": "sha256:477359f445eed241ad4ededb84921483c3c97618a160ef78b74bb1a421c17da7"
|
|
72
72
|
},
|
|
73
|
-
"refreshedAt": "2026-08-
|
|
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.
|
|
77
|
+
"version": "2.4.0"
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
"@noble/post-quantum": {
|
|
82
|
-
"version": "0.7.
|
|
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-
|
|
112
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-post-quantum:0.7.
|
|
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:
|
|
115
|
-
"browser": "sha256:
|
|
114
|
+
"server": "sha256:04eaaa4838b43162912c851dd2cab52ab82f56decad8542934171247017a6893",
|
|
115
|
+
"browser": "sha256:30ceef750bc57ae5f59c5aa7454eafb7aad55d30c261a1b5c538a66c569fc031"
|
|
116
116
|
},
|
|
117
|
-
"refreshedAt": "2026-08-
|
|
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.
|
|
121
|
+
"version": "2.4.0"
|
|
122
122
|
},
|
|
123
123
|
"@noble/curves": {
|
|
124
124
|
"url": "https://github.com/paulmillr/noble-curves",
|
|
125
|
-
"version": "2.
|
|
125
|
+
"version": "2.4.0"
|
|
126
126
|
},
|
|
127
127
|
"@noble/ciphers": {
|
|
128
128
|
"url": "https://github.com/paulmillr/noble-ciphers",
|
|
129
|
-
"version": "2.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
139
|
+
aopts(defaults, "defaults");
|
|
132
140
|
if (opts !== void 0)
|
|
133
|
-
|
|
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.
|
|
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
|
-
|
|
101
|
+
aopts(defaults, "defaults");
|
|
94
102
|
if (opts2 !== void 0)
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
941
|
-
|
|
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
|
|
972
|
-
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
|
1030
|
-
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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.
|
|
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");
|