leviathan-crypto 2.1.0 → 3.0.0
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/CLAUDE.md +86 -443
- package/README.md +198 -65
- package/dist/aes/aes-cbc.d.ts +40 -0
- package/dist/aes/aes-cbc.js +158 -0
- package/dist/aes/aes-ctr.d.ts +50 -0
- package/dist/aes/aes-ctr.js +141 -0
- package/dist/aes/aes-gcm-siv.d.ts +67 -0
- package/dist/aes/aes-gcm-siv.js +217 -0
- package/dist/aes/aes-gcm.d.ts +61 -0
- package/dist/aes/aes-gcm.js +226 -0
- package/dist/aes/cipher-suite.d.ts +21 -0
- package/dist/aes/cipher-suite.js +179 -0
- package/dist/aes/embedded.d.ts +1 -0
- package/dist/aes/embedded.js +26 -0
- package/dist/aes/generator.d.ts +14 -0
- package/dist/aes/generator.js +103 -0
- package/dist/aes/index.d.ts +58 -0
- package/dist/aes/index.js +125 -0
- package/dist/aes/ops.d.ts +60 -0
- package/dist/aes/ops.js +164 -0
- package/dist/aes/pool-worker.d.ts +1 -0
- package/dist/aes/pool-worker.js +92 -0
- package/dist/aes/types.d.ts +1 -0
- package/dist/aes/types.js +23 -0
- package/dist/aes.wasm +0 -0
- package/dist/blake3/embedded.d.ts +1 -0
- package/dist/blake3/embedded.js +26 -0
- package/dist/blake3/index.d.ts +143 -0
- package/dist/blake3/index.js +620 -0
- package/dist/blake3/types.d.ts +102 -0
- package/dist/blake3/types.js +31 -0
- package/dist/blake3/validate.d.ts +29 -0
- package/dist/blake3/validate.js +80 -0
- package/dist/blake3.wasm +0 -0
- package/dist/chacha20/cipher-suite.js +47 -25
- package/dist/chacha20/generator.d.ts +2 -2
- package/dist/chacha20/generator.js +4 -4
- package/dist/chacha20/index.d.ts +16 -15
- package/dist/chacha20/index.js +52 -46
- package/dist/chacha20/ops.d.ts +7 -7
- package/dist/chacha20/ops.js +34 -34
- package/dist/chacha20/pool-worker.js +5 -3
- package/dist/cte-wasm.d.ts +1 -0
- package/dist/cte-wasm.js +3 -0
- package/dist/curve25519.wasm +0 -0
- package/dist/ecdsa/der.d.ts +23 -0
- package/dist/ecdsa/der.js +192 -0
- package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
- package/dist/ecdsa/ecprivatekey-der.js +230 -0
- package/dist/ecdsa/embedded.d.ts +1 -0
- package/dist/ecdsa/embedded.js +25 -0
- package/dist/ecdsa/index.d.ts +124 -0
- package/dist/ecdsa/index.js +366 -0
- package/dist/ecdsa/types.d.ts +31 -0
- package/dist/ecdsa/types.js +28 -0
- package/dist/ecdsa/validate.d.ts +18 -0
- package/dist/ecdsa/validate.js +92 -0
- package/dist/ed25519/embedded.d.ts +1 -0
- package/dist/ed25519/embedded.js +31 -0
- package/dist/ed25519/index.d.ts +70 -0
- package/dist/ed25519/index.js +308 -0
- package/dist/ed25519/types.d.ts +27 -0
- package/dist/ed25519/types.js +27 -0
- package/dist/ed25519/validate.d.ts +7 -0
- package/dist/ed25519/validate.js +77 -0
- package/dist/embedded/aes-pool-worker.d.ts +1 -0
- package/dist/embedded/aes-pool-worker.js +5 -0
- package/dist/embedded/aes.d.ts +1 -0
- package/dist/embedded/aes.js +3 -0
- package/dist/embedded/blake3.d.ts +1 -0
- package/dist/embedded/blake3.js +3 -0
- package/dist/embedded/chacha20-pool-worker.d.ts +1 -1
- package/dist/embedded/chacha20-pool-worker.js +2 -2
- package/dist/embedded/chacha20.d.ts +1 -1
- package/dist/embedded/chacha20.js +2 -2
- package/dist/embedded/curve25519.d.ts +1 -0
- package/dist/embedded/curve25519.js +3 -0
- package/dist/embedded/mldsa.d.ts +1 -0
- package/dist/embedded/mldsa.js +3 -0
- package/dist/embedded/mlkem.d.ts +1 -0
- package/dist/embedded/mlkem.js +3 -0
- package/dist/embedded/p256.d.ts +1 -0
- package/dist/embedded/p256.js +3 -0
- package/dist/embedded/serpent-pool-worker.d.ts +1 -1
- package/dist/embedded/serpent-pool-worker.js +2 -2
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +2 -2
- package/dist/embedded/sha2.d.ts +1 -1
- package/dist/embedded/sha2.js +2 -2
- package/dist/embedded/sha3.d.ts +1 -1
- package/dist/embedded/sha3.js +2 -2
- package/dist/embedded/slhdsa.d.ts +1 -0
- package/dist/embedded/slhdsa.js +3 -0
- package/dist/errors.d.ts +92 -1
- package/dist/errors.js +111 -1
- package/dist/fortuna.d.ts +5 -5
- package/dist/fortuna.js +37 -64
- package/dist/index.d.ts +38 -9
- package/dist/index.js +63 -19
- package/dist/init.d.ts +1 -1
- package/dist/init.js +11 -25
- package/dist/keccak/embedded.js +1 -1
- package/dist/keccak/index.d.ts +2 -0
- package/dist/keccak/index.js +4 -2
- package/dist/loader.d.ts +1 -24
- package/dist/loader.js +13 -16
- package/dist/merkle/blake3-tree.d.ts +35 -0
- package/dist/merkle/blake3-tree.js +187 -0
- package/dist/merkle/checkpoint.d.ts +58 -0
- package/dist/merkle/checkpoint.js +217 -0
- package/dist/merkle/index.d.ts +19 -0
- package/dist/merkle/index.js +37 -0
- package/dist/merkle/merkle-log.d.ts +130 -0
- package/dist/merkle/merkle-log.js +207 -0
- package/dist/merkle/merkle-verifier.d.ts +126 -0
- package/dist/merkle/merkle-verifier.js +296 -0
- package/dist/merkle/proof.d.ts +70 -0
- package/dist/merkle/proof.js +300 -0
- package/dist/merkle/sha256-tree.d.ts +33 -0
- package/dist/merkle/sha256-tree.js +145 -0
- package/dist/merkle/signed-log.d.ts +156 -0
- package/dist/merkle/signed-log.js +356 -0
- package/dist/merkle/signed-note.d.ts +309 -0
- package/dist/merkle/signed-note.js +648 -0
- package/dist/merkle/sth.d.ts +31 -0
- package/dist/merkle/sth.js +31 -0
- package/dist/merkle/storage.d.ts +40 -0
- package/dist/merkle/storage.js +71 -0
- package/dist/merkle/tree.d.ts +68 -0
- package/dist/merkle/tree.js +94 -0
- package/dist/mldsa/embedded.d.ts +1 -0
- package/dist/{kyber → mldsa}/embedded.js +5 -5
- package/dist/mldsa/expand.d.ts +53 -0
- package/dist/mldsa/expand.js +188 -0
- package/dist/mldsa/format.d.ts +16 -0
- package/dist/mldsa/format.js +68 -0
- package/dist/mldsa/hashvariant.d.ts +32 -0
- package/dist/mldsa/hashvariant.js +248 -0
- package/dist/mldsa/index.d.ts +142 -0
- package/dist/mldsa/index.js +463 -0
- package/dist/mldsa/keygen.d.ts +16 -0
- package/dist/mldsa/keygen.js +232 -0
- package/dist/mldsa/params.d.ts +21 -0
- package/dist/mldsa/params.js +55 -0
- package/dist/mldsa/sha3-helpers.d.ts +30 -0
- package/dist/mldsa/sha3-helpers.js +124 -0
- package/dist/mldsa/sign.d.ts +36 -0
- package/dist/mldsa/sign.js +380 -0
- package/dist/mldsa/types.d.ts +91 -0
- package/dist/mldsa/types.js +25 -0
- package/dist/mldsa/validate.d.ts +55 -0
- package/dist/mldsa/validate.js +125 -0
- package/dist/mldsa/verify.d.ts +29 -0
- package/dist/mldsa/verify.js +269 -0
- package/dist/mldsa.wasm +0 -0
- package/dist/mlkem/embedded.d.ts +1 -0
- package/dist/mlkem/embedded.js +27 -0
- package/dist/mlkem/indcpa.d.ts +49 -0
- package/dist/{kyber → mlkem}/indcpa.js +44 -44
- package/dist/mlkem/index.d.ts +37 -0
- package/dist/{kyber → mlkem}/index.js +24 -34
- package/dist/mlkem/kem.d.ts +21 -0
- package/dist/{kyber → mlkem}/kem.js +44 -64
- package/dist/{kyber → mlkem}/params.d.ts +4 -4
- package/dist/{kyber → mlkem}/params.js +2 -2
- package/dist/mlkem/suite.d.ts +12 -0
- package/dist/{kyber → mlkem}/suite.js +17 -12
- package/dist/{kyber → mlkem}/types.d.ts +3 -3
- package/dist/{kyber → mlkem}/types.js +1 -1
- package/dist/{kyber → mlkem}/validate.d.ts +7 -7
- package/dist/{kyber → mlkem}/validate.js +7 -7
- package/dist/{kyber.wasm → mlkem.wasm} +0 -0
- package/dist/p256.wasm +0 -0
- package/dist/ratchet/index.d.ts +2 -0
- package/dist/ratchet/index.js +1 -0
- package/dist/ratchet/kdf-chain.js +3 -3
- package/dist/ratchet/ratchet-keypair.js +2 -2
- package/dist/ratchet/root-kdf.js +7 -7
- package/dist/ratchet/skipped-key-store.js +4 -4
- package/dist/ratchet/types.d.ts +1 -1
- package/dist/serpent/cipher-suite.js +20 -17
- package/dist/serpent/generator.d.ts +1 -1
- package/dist/serpent/generator.js +2 -2
- package/dist/serpent/index.d.ts +8 -7
- package/dist/serpent/index.js +18 -27
- package/dist/serpent/pool-worker.js +7 -5
- package/dist/serpent/serpent-cbc.d.ts +4 -4
- package/dist/serpent/serpent-cbc.js +11 -8
- package/dist/serpent/shared-ops.d.ts +3 -23
- package/dist/serpent/shared-ops.js +50 -85
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hkdf.js +5 -5
- package/dist/sha2/index.d.ts +21 -1
- package/dist/sha2/index.js +65 -10
- package/dist/sha2/types.d.ts +41 -2
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/index.d.ts +72 -3
- package/dist/sha3/index.js +240 -14
- package/dist/sha3/kmac.d.ts +121 -0
- package/dist/sha3/kmac.js +800 -0
- package/dist/sha3.wasm +0 -0
- package/dist/shared/pkcs7.d.ts +22 -0
- package/dist/shared/pkcs7.js +84 -0
- package/dist/sign/ctx.d.ts +41 -0
- package/dist/sign/ctx.js +102 -0
- package/dist/sign/envelope.d.ts +45 -0
- package/dist/sign/envelope.js +152 -0
- package/dist/sign/hasher.d.ts +9 -0
- package/dist/sign/hasher.js +132 -0
- package/dist/sign/index.d.ts +11 -0
- package/dist/sign/index.js +34 -0
- package/dist/sign/sign-stream.d.ts +25 -0
- package/dist/sign/sign-stream.js +112 -0
- package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
- package/dist/sign/suites/ecdsa-p256.js +120 -0
- package/dist/sign/suites/ed25519.d.ts +3 -0
- package/dist/sign/suites/ed25519.js +165 -0
- package/dist/sign/suites/hybrid-classical.d.ts +23 -0
- package/dist/sign/suites/hybrid-classical.js +526 -0
- package/dist/sign/suites/hybrid-pq.d.ts +4 -0
- package/dist/sign/suites/hybrid-pq.js +234 -0
- package/dist/sign/suites/mldsa.d.ts +7 -0
- package/dist/sign/suites/mldsa.js +161 -0
- package/dist/sign/suites/slhdsa.d.ts +7 -0
- package/dist/sign/suites/slhdsa.js +176 -0
- package/dist/sign/types.d.ts +106 -0
- package/dist/sign/types.js +28 -0
- package/dist/sign/verify-stream.d.ts +30 -0
- package/dist/sign/verify-stream.js +227 -0
- package/dist/slhdsa/embedded.d.ts +1 -0
- package/dist/slhdsa/embedded.js +26 -0
- package/dist/slhdsa/index.d.ts +149 -0
- package/dist/slhdsa/index.js +493 -0
- package/dist/slhdsa/params.d.ts +26 -0
- package/dist/slhdsa/params.js +70 -0
- package/dist/slhdsa/prehash.d.ts +68 -0
- package/dist/slhdsa/prehash.js +307 -0
- package/dist/slhdsa/sign.d.ts +39 -0
- package/dist/slhdsa/sign.js +116 -0
- package/dist/slhdsa/types.d.ts +129 -0
- package/dist/slhdsa/types.js +27 -0
- package/dist/slhdsa/validate.d.ts +60 -0
- package/dist/slhdsa/validate.js +127 -0
- package/dist/slhdsa/verify.d.ts +32 -0
- package/dist/slhdsa/verify.js +107 -0
- package/dist/slhdsa.wasm +0 -0
- package/dist/stream/header.js +3 -3
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/open-stream.js +31 -10
- package/dist/stream/seal-stream-pool.d.ts +1 -0
- package/dist/stream/seal-stream-pool.js +63 -26
- package/dist/stream/seal-stream.d.ts +1 -1
- package/dist/stream/seal-stream.js +20 -9
- package/dist/stream/seal.js +6 -6
- package/dist/stream/types.d.ts +3 -1
- package/dist/stream/types.js +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +46 -54
- package/dist/wasm-source.d.ts +7 -7
- package/dist/wasm-source.js +1 -1
- package/dist/x25519/embedded.d.ts +1 -0
- package/dist/x25519/embedded.js +31 -0
- package/dist/x25519/index.d.ts +43 -0
- package/dist/x25519/index.js +159 -0
- package/dist/x25519/types.d.ts +25 -0
- package/dist/x25519/types.js +27 -0
- package/dist/x25519/validate.d.ts +2 -0
- package/dist/x25519/validate.js +39 -0
- package/package.json +70 -26
- package/SECURITY.md +0 -163
- package/dist/ct-wasm.d.ts +0 -1
- package/dist/ct-wasm.js +0 -3
- package/dist/docs/aead.md +0 -363
- package/dist/docs/architecture.md +0 -1011
- package/dist/docs/argon2id.md +0 -305
- package/dist/docs/chacha20.md +0 -781
- package/dist/docs/exports.md +0 -277
- package/dist/docs/fortuna.md +0 -530
- package/dist/docs/init.md +0 -301
- package/dist/docs/loader.md +0 -256
- package/dist/docs/serpent.md +0 -617
- package/dist/docs/sha2.md +0 -671
- package/dist/docs/sha3.md +0 -612
- package/dist/docs/types.md +0 -416
- package/dist/docs/utils.md +0 -457
- package/dist/embedded/kyber.d.ts +0 -1
- package/dist/embedded/kyber.js +0 -3
- package/dist/kyber/embedded.d.ts +0 -1
- package/dist/kyber/indcpa.d.ts +0 -49
- package/dist/kyber/index.d.ts +0 -38
- package/dist/kyber/kem.d.ts +0 -21
- package/dist/kyber/suite.d.ts +0 -12
- /package/dist/{ct.wasm → cte.wasm} +0 -0
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
20
|
// ▀█████▀▀
|
|
21
21
|
//
|
|
22
|
-
// src/ts/
|
|
22
|
+
// src/ts/mlkem/indcpa.ts
|
|
23
23
|
//
|
|
24
|
-
// ML-KEM IND-CPA PKE scheme
|
|
25
|
-
// Orchestrates
|
|
24
|
+
// ML-KEM IND-CPA PKE scheme, FIPS 203 Algorithms 12, 13, 14 (K-PKE).
|
|
25
|
+
// Orchestrates mlkem WASM (polynomial math) and sha3 WASM (Keccak sponge).
|
|
26
26
|
import { wipe } from '../utils.js';
|
|
27
27
|
// ── SHA3 helpers ────────────────────────────────────────────────────────────
|
|
28
28
|
// All operate directly on raw Sha3Exports, no init() system involved.
|
|
@@ -82,7 +82,7 @@ export function shake256Hash(sx, msg, n) {
|
|
|
82
82
|
* Generate row `rowI` of matrix  (or Â^T) into polyvec slot `pvecSlot`.
|
|
83
83
|
*
|
|
84
84
|
* FIPS 203 Algorithm 6 (SampleNTT): each entry Â[i][j] = XOF(ρ, j, i).
|
|
85
|
-
* The matrix entries are already in NTT domain by construction
|
|
85
|
+
* The matrix entries are already in NTT domain by construction, no separate
|
|
86
86
|
* NTT call is needed after rej_uniform.
|
|
87
87
|
*
|
|
88
88
|
* transposed=false (keygen): XOF input = ρ || j || i → Â[i][j]
|
|
@@ -90,7 +90,7 @@ export function shake256Hash(sx, msg, n) {
|
|
|
90
90
|
*/
|
|
91
91
|
function genMatrixRow(kx, sx, k, rho, transposed, pvecSlot, rowI) {
|
|
92
92
|
const xofPrfOff = kx.getXofPrfOffset();
|
|
93
|
-
const
|
|
93
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
94
94
|
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
95
95
|
const outOff = sx.getOutOffset();
|
|
96
96
|
// XOF seed buffer: ρ(32) || byte0 || byte1
|
|
@@ -115,7 +115,7 @@ function genMatrixRow(kx, sx, k, rho, transposed, pvecSlot, rowI) {
|
|
|
115
115
|
let ctr = 0;
|
|
116
116
|
while (ctr < 256) {
|
|
117
117
|
sx.shakeSqueezeBlock(); // 168 bytes at sha3 OUT_OFFSET
|
|
118
|
-
|
|
118
|
+
mlkemMem.set(sha3Mem.subarray(outOff, outOff + 168), xofPrfOff);
|
|
119
119
|
ctr += kx.rej_uniform(polyOff, ctr, xofPrfOff, 168);
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -127,7 +127,7 @@ function genMatrixRow(kx, sx, k, rho, transposed, pvecSlot, rowI) {
|
|
|
127
127
|
*/
|
|
128
128
|
function noisePolyvec(kx, sx, pvSlot, k, sigma, nonceStart, eta) {
|
|
129
129
|
const xofPrfOff = kx.getXofPrfOffset();
|
|
130
|
-
const
|
|
130
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
131
131
|
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
132
132
|
const outOff = sx.getOutOffset();
|
|
133
133
|
const prfLen = eta * 64; // 128 for η=2, 192 for η=3
|
|
@@ -145,7 +145,7 @@ function noisePolyvec(kx, sx, pvSlot, k, sigma, nonceStart, eta) {
|
|
|
145
145
|
while (pos < prfLen) {
|
|
146
146
|
sx.shakeSqueezeBlock();
|
|
147
147
|
const take = Math.min(prfLen - pos, rate);
|
|
148
|
-
|
|
148
|
+
mlkemMem.set(sha3Mem.subarray(outOff, outOff + take), xofPrfOff + pos);
|
|
149
149
|
pos += take;
|
|
150
150
|
}
|
|
151
151
|
kx.poly_getnoise(pvSlot + i * 512, xofPrfOff, eta);
|
|
@@ -160,7 +160,7 @@ function noisePolyvec(kx, sx, pvSlot, k, sigma, nonceStart, eta) {
|
|
|
160
160
|
*/
|
|
161
161
|
function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
|
|
162
162
|
const xofPrfOff = kx.getXofPrfOffset();
|
|
163
|
-
const
|
|
163
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
164
164
|
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
165
165
|
const outOff = sx.getOutOffset();
|
|
166
166
|
const prfLen = eta * 64;
|
|
@@ -176,7 +176,7 @@ function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
|
|
|
176
176
|
while (pos < prfLen) {
|
|
177
177
|
sx.shakeSqueezeBlock();
|
|
178
178
|
const take = Math.min(prfLen - pos, rate);
|
|
179
|
-
|
|
179
|
+
mlkemMem.set(sha3Mem.subarray(outOff, outOff + take), xofPrfOff + pos);
|
|
180
180
|
pos += take;
|
|
181
181
|
}
|
|
182
182
|
kx.poly_getnoise(polyOff, xofPrfOff, eta);
|
|
@@ -187,13 +187,13 @@ function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
|
|
|
187
187
|
}
|
|
188
188
|
// ── IND-CPA functions ───────────────────────────────────────────────────────
|
|
189
189
|
/**
|
|
190
|
-
* K-PKE.KeyGen (FIPS 203 Algorithm 12)
|
|
190
|
+
* K-PKE.KeyGen (FIPS 203 Algorithm 12), deterministic.
|
|
191
191
|
*
|
|
192
192
|
* Slot map:
|
|
193
|
-
* pvec0
|
|
194
|
-
* pvec1
|
|
195
|
-
* pvec2
|
|
196
|
-
* pvec3
|
|
193
|
+
* pvec0, current row of  (overwritten per row)
|
|
194
|
+
* pvec1, ŝ (noise, persistent through dot products)
|
|
195
|
+
* pvec2, ê (noise)
|
|
196
|
+
* pvec3, t̂ = ·ŝ + ê (output)
|
|
197
197
|
*/
|
|
198
198
|
export function indcpaKeypairDerand(kx, sx, params, d) {
|
|
199
199
|
const { k, eta1 } = params;
|
|
@@ -219,7 +219,7 @@ export function indcpaKeypairDerand(kx, sx, params, d) {
|
|
|
219
219
|
kx.polyvec_ntt(pvec1, k);
|
|
220
220
|
kx.polyvec_ntt(pvec2, k);
|
|
221
221
|
// Step 5: For each row i, t̂[i] = Â[i] · ŝ
|
|
222
|
-
const
|
|
222
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
223
223
|
for (let i = 0; i < k; i++) {
|
|
224
224
|
genMatrixRow(kx, sx, k, rho, false, pvec0, i);
|
|
225
225
|
kx.polyvec_basemul_acc_montgomery(pvec3 + i * 512, pvec0, pvec1, k);
|
|
@@ -230,12 +230,12 @@ export function indcpaKeypairDerand(kx, sx, params, d) {
|
|
|
230
230
|
kx.polyvec_reduce(pvec3, k);
|
|
231
231
|
// Step 8: ek = polyvec_tobytes(t̂) || ρ
|
|
232
232
|
kx.polyvec_tobytes(pkOff, pvec3, k);
|
|
233
|
-
|
|
233
|
+
mlkemMem.set(rho, pkOff + k * 384);
|
|
234
234
|
// Step 9: sk = polyvec_tobytes(ŝ)
|
|
235
235
|
kx.polyvec_tobytes(skOff, pvec1, k);
|
|
236
236
|
return {
|
|
237
|
-
ekCpa:
|
|
238
|
-
skCpa:
|
|
237
|
+
ekCpa: mlkemMem.slice(pkOff, pkOff + params.ekBytes),
|
|
238
|
+
skCpa: mlkemMem.slice(skOff, skOff + params.skCpaBytes),
|
|
239
239
|
};
|
|
240
240
|
}
|
|
241
241
|
finally {
|
|
@@ -244,21 +244,21 @@ export function indcpaKeypairDerand(kx, sx, params, d) {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
/**
|
|
247
|
-
* K-PKE.Encrypt (FIPS 203 Algorithm 13)
|
|
247
|
+
* K-PKE.Encrypt (FIPS 203 Algorithm 13), deterministic.
|
|
248
248
|
*
|
|
249
249
|
* Slot map:
|
|
250
|
-
* pvec0
|
|
251
|
-
* pvec1
|
|
252
|
-
* pvec2
|
|
253
|
-
* pvec3
|
|
254
|
-
* pvec4
|
|
255
|
-
* poly1
|
|
256
|
-
* poly2
|
|
257
|
-
* poly3
|
|
250
|
+
* pvec0, current row of Â^T (transposed, overwritten per row)
|
|
251
|
+
* pvec1, r̂ = NTT(r)
|
|
252
|
+
* pvec2, e₁ (noise)
|
|
253
|
+
* pvec3, u = invNTT(Â^T · r̂) + e₁
|
|
254
|
+
* pvec4, t̂ (unpacked from ek)
|
|
255
|
+
* poly1 , e₂ (noise)
|
|
256
|
+
* poly2 , v = invNTT(t̂^T · r̂) + e₂ + msg
|
|
257
|
+
* poly3 , message polynomial
|
|
258
258
|
*/
|
|
259
259
|
export function indcpaEncrypt(kx, sx, params, ek, m, coins) {
|
|
260
260
|
const { k, eta1, eta2, du, dv } = params;
|
|
261
|
-
const
|
|
261
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
262
262
|
const pvec0 = kx.getPolyvecSlot0();
|
|
263
263
|
const pvec1 = kx.getPolyvecSlot1();
|
|
264
264
|
const pvec2 = kx.getPolyvecSlot2();
|
|
@@ -270,8 +270,8 @@ export function indcpaEncrypt(kx, sx, params, ek, m, coins) {
|
|
|
270
270
|
const pkOff = kx.getPkOffset();
|
|
271
271
|
const ctOff = kx.getCtOffset();
|
|
272
272
|
const msgOff = kx.getMsgOffset();
|
|
273
|
-
// Step 1: Unpack ek
|
|
274
|
-
|
|
273
|
+
// Step 1: Unpack ek, t̂ → pvec4, ρ from ek tail
|
|
274
|
+
mlkemMem.set(ek, pkOff);
|
|
275
275
|
kx.polyvec_frombytes(pvec4, pkOff, k);
|
|
276
276
|
const rho = ek.slice(k * 384, k * 384 + 32);
|
|
277
277
|
// Steps 2-4: Generate noise r, e₁, e₂ from coins
|
|
@@ -296,31 +296,31 @@ export function indcpaEncrypt(kx, sx, params, ek, m, coins) {
|
|
|
296
296
|
kx.polyvec_basemul_acc_montgomery(poly2, pvec4, pvec1, k);
|
|
297
297
|
kx.poly_invntt(poly2);
|
|
298
298
|
// Step 12: decode message
|
|
299
|
-
|
|
299
|
+
mlkemMem.set(m, msgOff);
|
|
300
300
|
kx.poly_frommsg(poly3, msgOff);
|
|
301
301
|
// Steps 13-15: v = v + e₂ + msg, reduce
|
|
302
302
|
kx.poly_add(poly2, poly2, poly1);
|
|
303
303
|
kx.poly_add(poly2, poly2, poly3);
|
|
304
304
|
kx.poly_reduce(poly2);
|
|
305
|
-
// Step 16: pack ciphertext
|
|
305
|
+
// Step 16: pack ciphertext, Compress_du(u) || Compress_dv(v)
|
|
306
306
|
const pvecCompBytes = k * du * 32;
|
|
307
307
|
kx.polyvec_compress(ctOff, pvec3, k, du);
|
|
308
308
|
kx.poly_compress(ctOff + pvecCompBytes, poly2, dv);
|
|
309
|
-
return
|
|
309
|
+
return mlkemMem.slice(ctOff, ctOff + params.ctBytes);
|
|
310
310
|
}
|
|
311
311
|
/**
|
|
312
312
|
* K-PKE.Decrypt (FIPS 203 Algorithm 14).
|
|
313
313
|
*
|
|
314
314
|
* Slot map:
|
|
315
|
-
* pvec0
|
|
316
|
-
* pvec1
|
|
317
|
-
* poly0
|
|
318
|
-
* poly1
|
|
319
|
-
* poly2
|
|
315
|
+
* pvec0, û (decompressed from ct)
|
|
316
|
+
* pvec1, ŝ (from sk)
|
|
317
|
+
* poly0 , v (decompressed from ct)
|
|
318
|
+
* poly1 , w = invNTT(ŝ^T · NTT(û))
|
|
319
|
+
* poly2 , m' = v - w
|
|
320
320
|
*/
|
|
321
321
|
export function indcpaDecrypt(kx, params, skCpa, ct) {
|
|
322
322
|
const { k, du, dv } = params;
|
|
323
|
-
const
|
|
323
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
324
324
|
const pvec0 = kx.getPolyvecSlot0();
|
|
325
325
|
const pvec1 = kx.getPolyvecSlot1();
|
|
326
326
|
const poly0 = kx.getPolySlot0();
|
|
@@ -329,9 +329,9 @@ export function indcpaDecrypt(kx, params, skCpa, ct) {
|
|
|
329
329
|
const ctOff = kx.getCtOffset();
|
|
330
330
|
const skOff = kx.getSkOffset();
|
|
331
331
|
const msgOff = kx.getMsgOffset();
|
|
332
|
-
// Load ct and sk into
|
|
333
|
-
|
|
334
|
-
|
|
332
|
+
// Load ct and sk into mlkem memory
|
|
333
|
+
mlkemMem.set(ct, ctOff);
|
|
334
|
+
mlkemMem.set(skCpa, skOff);
|
|
335
335
|
// Steps 1-2: Decompress û and v
|
|
336
336
|
const pvecCompBytes = k * du * 32;
|
|
337
337
|
kx.polyvec_decompress(pvec0, ctOff, k, du);
|
|
@@ -348,5 +348,5 @@ export function indcpaDecrypt(kx, params, skCpa, ct) {
|
|
|
348
348
|
kx.poly_reduce(poly2);
|
|
349
349
|
// Step 9-10: poly_tomsg → 32-byte message
|
|
350
350
|
kx.poly_tomsg(msgOff, poly2);
|
|
351
|
-
return
|
|
351
|
+
return mlkemMem.slice(msgOff, msgOff + 32);
|
|
352
352
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isInitialized } from '../init.js';
|
|
2
|
+
import type { WasmSource } from '../wasm-source.js';
|
|
3
|
+
import type { MlKemExports, Sha3Exports, MlKemKeyPair, MlKemEncapsulation } from './types.js';
|
|
4
|
+
import { MlKemParams, MLKEM512, MLKEM768, MLKEM1024 } from './params.js';
|
|
5
|
+
export declare function mlkemInit(source: WasmSource): Promise<void>;
|
|
6
|
+
export type { WasmSource };
|
|
7
|
+
export type { MlKemKeyPair, MlKemEncapsulation, MlKemExports, Sha3Exports };
|
|
8
|
+
export { MLKEM512, MLKEM768, MLKEM1024 };
|
|
9
|
+
export type { MlKemParams };
|
|
10
|
+
export { isInitialized };
|
|
11
|
+
export { MlKemSuite } from './suite.js';
|
|
12
|
+
export declare class MlKemBase {
|
|
13
|
+
readonly params: MlKemParams;
|
|
14
|
+
constructor(params: MlKemParams);
|
|
15
|
+
private get kx();
|
|
16
|
+
private get sx();
|
|
17
|
+
keygenDerand(d: Uint8Array, z: Uint8Array): MlKemKeyPair;
|
|
18
|
+
keygen(): MlKemKeyPair;
|
|
19
|
+
encapsulateDerand(ek: Uint8Array, m: Uint8Array): MlKemEncapsulation;
|
|
20
|
+
encapsulate(ek: Uint8Array): MlKemEncapsulation;
|
|
21
|
+
decapsulate(dk: Uint8Array, c: Uint8Array): Uint8Array;
|
|
22
|
+
checkEncapsulationKey(ek: Uint8Array): boolean;
|
|
23
|
+
checkDecapsulationKey(dk: Uint8Array): boolean;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
}
|
|
26
|
+
/** ML-KEM-512, k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
|
|
27
|
+
export declare class MlKem512 extends MlKemBase {
|
|
28
|
+
constructor();
|
|
29
|
+
}
|
|
30
|
+
/** ML-KEM-768, k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
|
|
31
|
+
export declare class MlKem768 extends MlKemBase {
|
|
32
|
+
constructor();
|
|
33
|
+
}
|
|
34
|
+
/** ML-KEM-1024, k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
|
|
35
|
+
export declare class MlKem1024 extends MlKemBase {
|
|
36
|
+
constructor();
|
|
37
|
+
}
|
|
@@ -19,31 +19,21 @@
|
|
|
19
19
|
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
20
|
// ▀█████▀▀
|
|
21
21
|
//
|
|
22
|
-
// src/ts/
|
|
22
|
+
// src/ts/mlkem/index.ts
|
|
23
23
|
//
|
|
24
|
-
// ML-KEM public API
|
|
25
|
-
// Uses the init() module cache
|
|
24
|
+
// ML-KEM public API, MlKem512, MlKem768, MlKem1024 classes.
|
|
25
|
+
// Uses the init() module cache, call init({ mlkem: ..., sha3: ... }) before constructing.
|
|
26
26
|
import { getInstance, initModule, isInitialized, _assertNotOwned } from '../init.js';
|
|
27
27
|
import { randomBytes, wipe } from '../utils.js';
|
|
28
28
|
import { MLKEM512, MLKEM768, MLKEM1024 } from './params.js';
|
|
29
29
|
import { kemKeypairDerand, kemEncapsulateDerand, kemDecapsulate } from './kem.js';
|
|
30
30
|
import { checkEncapsulationKey, checkDecapsulationKey } from './validate.js';
|
|
31
|
-
export async function
|
|
32
|
-
return initModule('
|
|
33
|
-
}
|
|
34
|
-
export function _kyberReady() {
|
|
35
|
-
try {
|
|
36
|
-
getInstance('kyber');
|
|
37
|
-
getInstance('sha3');
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
31
|
+
export async function mlkemInit(source) {
|
|
32
|
+
return initModule('mlkem', source);
|
|
43
33
|
}
|
|
44
34
|
export { MLKEM512, MLKEM768, MLKEM1024 };
|
|
45
35
|
export { isInitialized };
|
|
46
|
-
export {
|
|
36
|
+
export { MlKemSuite } from './suite.js';
|
|
47
37
|
// ── Layout assertion ────────────────────────────────────────────────────────
|
|
48
38
|
function assertLayout(kx, p) {
|
|
49
39
|
const pk = kx.getPkOffset();
|
|
@@ -52,34 +42,34 @@ function assertLayout(kx, p) {
|
|
|
52
42
|
const ctPrime = kx.getCtPrimeOffset();
|
|
53
43
|
const xof = kx.getXofPrfOffset();
|
|
54
44
|
if (pk + p.ekBytes > sk)
|
|
55
|
-
throw new Error('leviathan-crypto:
|
|
45
|
+
throw new Error('leviathan-crypto: mlkem buffer overflow, ek overflows into SK region');
|
|
56
46
|
if (sk + p.skCpaBytes > ct)
|
|
57
|
-
throw new Error('leviathan-crypto:
|
|
47
|
+
throw new Error('leviathan-crypto: mlkem buffer overflow, sk overflows into CT region');
|
|
58
48
|
if (ct + p.ctBytes > ctPrime)
|
|
59
|
-
throw new Error('leviathan-crypto:
|
|
49
|
+
throw new Error('leviathan-crypto: mlkem buffer overflow, ct overflows into CT_PRIME region');
|
|
60
50
|
if (ctPrime + p.ctBytes > xof)
|
|
61
|
-
throw new Error('leviathan-crypto:
|
|
51
|
+
throw new Error('leviathan-crypto: mlkem buffer overflow, ct_prime overflows into XOF region');
|
|
62
52
|
}
|
|
63
53
|
// ── Base class ──────────────────────────────────────────────────────────────
|
|
64
54
|
export class MlKemBase {
|
|
65
55
|
params;
|
|
66
56
|
constructor(params) {
|
|
67
|
-
if (!isInitialized('
|
|
68
|
-
throw new Error('leviathan-crypto: call init({
|
|
57
|
+
if (!isInitialized('mlkem'))
|
|
58
|
+
throw new Error('leviathan-crypto: call init({ mlkem: ... }) before using MlKem classes');
|
|
69
59
|
if (!isInitialized('sha3'))
|
|
70
60
|
throw new Error('leviathan-crypto: call init({ sha3: ... }) before using MlKem classes');
|
|
71
61
|
this.params = params;
|
|
72
62
|
assertLayout(this.kx, params);
|
|
73
63
|
}
|
|
74
64
|
get kx() {
|
|
75
|
-
return getInstance('
|
|
65
|
+
return getInstance('mlkem').exports;
|
|
76
66
|
}
|
|
77
67
|
get sx() {
|
|
78
68
|
return getInstance('sha3').exports;
|
|
79
69
|
}
|
|
80
70
|
keygenDerand(d, z) {
|
|
81
71
|
_assertNotOwned('sha3');
|
|
82
|
-
_assertNotOwned('
|
|
72
|
+
_assertNotOwned('mlkem');
|
|
83
73
|
if (d.length !== 32)
|
|
84
74
|
throw new RangeError(`d seed must be 32 bytes (got ${d.length})`);
|
|
85
75
|
if (z.length !== 32)
|
|
@@ -99,7 +89,7 @@ export class MlKemBase {
|
|
|
99
89
|
}
|
|
100
90
|
encapsulateDerand(ek, m) {
|
|
101
91
|
_assertNotOwned('sha3');
|
|
102
|
-
_assertNotOwned('
|
|
92
|
+
_assertNotOwned('mlkem');
|
|
103
93
|
if (ek.length !== this.params.ekBytes)
|
|
104
94
|
throw new RangeError(`encapsulation key must be ${this.params.ekBytes} bytes (got ${ek.length})`);
|
|
105
95
|
if (m.length !== 32)
|
|
@@ -119,7 +109,7 @@ export class MlKemBase {
|
|
|
119
109
|
}
|
|
120
110
|
decapsulate(dk, c) {
|
|
121
111
|
_assertNotOwned('sha3');
|
|
122
|
-
_assertNotOwned('
|
|
112
|
+
_assertNotOwned('mlkem');
|
|
123
113
|
if (dk.length !== this.params.dkBytes)
|
|
124
114
|
throw new RangeError(`decapsulation key must be ${this.params.dkBytes} bytes (got ${dk.length})`);
|
|
125
115
|
if (c.length !== this.params.ctBytes)
|
|
@@ -130,39 +120,39 @@ export class MlKemBase {
|
|
|
130
120
|
}
|
|
131
121
|
checkEncapsulationKey(ek) {
|
|
132
122
|
_assertNotOwned('sha3');
|
|
133
|
-
_assertNotOwned('
|
|
123
|
+
_assertNotOwned('mlkem');
|
|
134
124
|
return checkEncapsulationKey(this.kx, this.params, ek);
|
|
135
125
|
}
|
|
136
126
|
checkDecapsulationKey(dk) {
|
|
137
127
|
_assertNotOwned('sha3');
|
|
138
|
-
_assertNotOwned('
|
|
128
|
+
_assertNotOwned('mlkem');
|
|
139
129
|
return checkDecapsulationKey(this.kx, this.sx, this.params, dk);
|
|
140
130
|
}
|
|
141
131
|
dispose() {
|
|
142
132
|
this.kx.wipeBuffers();
|
|
143
|
-
// MlKemBase does not own the sha3 module
|
|
133
|
+
// MlKemBase does not own the sha3 module, wiping this.sx.wipeBuffers()
|
|
144
134
|
// here would clobber any SHAKE128/SHAKE256 instance live at the time
|
|
145
|
-
// of dispose(). The wipe is not needed: every public
|
|
135
|
+
// of dispose(). The wipe is not needed: every public mlkem op
|
|
146
136
|
// (keygen, encapsulate, decapsulate, checkDecapsulationKey) calls
|
|
147
137
|
// sx.wipeBuffers() before returning, under the _assertNotOwned('sha3')
|
|
148
138
|
// guard it holds for the duration. sha3 scratch therefore carries no
|
|
149
|
-
// residue across a
|
|
139
|
+
// residue across a mlkem op boundary, secret-derived or otherwise.
|
|
150
140
|
}
|
|
151
141
|
}
|
|
152
142
|
// ── Public classes ──────────────────────────────────────────────────────────
|
|
153
|
-
/** ML-KEM-512
|
|
143
|
+
/** ML-KEM-512, k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
|
|
154
144
|
export class MlKem512 extends MlKemBase {
|
|
155
145
|
constructor() {
|
|
156
146
|
super(MLKEM512);
|
|
157
147
|
}
|
|
158
148
|
}
|
|
159
|
-
/** ML-KEM-768
|
|
149
|
+
/** ML-KEM-768, k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
|
|
160
150
|
export class MlKem768 extends MlKemBase {
|
|
161
151
|
constructor() {
|
|
162
152
|
super(MLKEM768);
|
|
163
153
|
}
|
|
164
154
|
}
|
|
165
|
-
/** ML-KEM-1024
|
|
155
|
+
/** ML-KEM-1024, k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
|
|
166
156
|
export class MlKem1024 extends MlKemBase {
|
|
167
157
|
constructor() {
|
|
168
158
|
super(MLKEM1024);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MlKemExports, Sha3Exports, MlKemKeyPair, MlKemEncapsulation } from './types.js';
|
|
2
|
+
import type { MlKemParams } from './params.js';
|
|
3
|
+
/**
|
|
4
|
+
* ML-KEM.KeyGen_internal (FIPS 203 Algorithm 15).
|
|
5
|
+
*
|
|
6
|
+
* dk = skCpa || ek || H(ek) || z
|
|
7
|
+
*/
|
|
8
|
+
export declare function kemKeypairDerand(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, d: Uint8Array, z: Uint8Array): MlKemKeyPair;
|
|
9
|
+
/**
|
|
10
|
+
* ML-KEM.Encaps_internal (FIPS 203 Algorithm 16).
|
|
11
|
+
*
|
|
12
|
+
* (K, r) = G(m || H(ek)), c = K-PKE.Encrypt(ek, m, r)
|
|
13
|
+
*/
|
|
14
|
+
export declare function kemEncapsulateDerand(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, ek: Uint8Array, m: Uint8Array): MlKemEncapsulation;
|
|
15
|
+
/**
|
|
16
|
+
* ML-KEM.Decaps_internal (FIPS 203 Algorithm 17).
|
|
17
|
+
*
|
|
18
|
+
* Constant-time: uses ct_verify and ct_cmov from mlkem WASM.
|
|
19
|
+
* MUST NOT branch on secret data in JS, all comparison via WASM primitives.
|
|
20
|
+
*/
|
|
21
|
+
export declare function kemDecapsulate(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, dk: Uint8Array, c: Uint8Array): Uint8Array;
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
20
|
// ▀█████▀▀
|
|
21
21
|
//
|
|
22
|
-
// src/ts/
|
|
22
|
+
// src/ts/mlkem/kem.ts
|
|
23
23
|
//
|
|
24
|
-
// ML-KEM KEM layer
|
|
24
|
+
// ML-KEM KEM layer, Fujisaki-Okamoto transform.
|
|
25
25
|
// FIPS 203 Algorithms 15, 16, 17 (ML-KEM internal).
|
|
26
26
|
import { indcpaKeypairDerand, indcpaEncrypt, indcpaDecrypt, sha3_256Hash, sha3_512Hash, shake256Hash, } from './indcpa.js';
|
|
27
27
|
import { wipe } from '../utils.js';
|
|
@@ -33,20 +33,13 @@ import { wipe } from '../utils.js';
|
|
|
33
33
|
export function kemKeypairDerand(kx, sx, params, d, z) {
|
|
34
34
|
// indcpaKeypairDerand handles its own sigma wipe
|
|
35
35
|
const { ekCpa, skCpa } = indcpaKeypairDerand(kx, sx, params, d);
|
|
36
|
-
// Wipe
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// domain. XOF_PRF_OFFSET holds the last PRF output block. POLYVEC_SLOT_3
|
|
44
|
-
// (t̂) and POLYVEC_SLOT_0 (Â rows) are public and intentionally skipped.
|
|
45
|
-
const kyberMem = new Uint8Array(kx.memory.buffer);
|
|
46
|
-
kyberMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + params.skCpaBytes);
|
|
47
|
-
kyberMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
|
|
48
|
-
kyberMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
|
|
49
|
-
kyberMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
|
|
36
|
+
// Wipe CPA sk + keygen noise + PRF scratch. See
|
|
37
|
+
// docs/mlkem.md#wipe-discipline.
|
|
38
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
39
|
+
mlkemMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + params.skCpaBytes);
|
|
40
|
+
mlkemMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
|
|
41
|
+
mlkemMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
|
|
42
|
+
mlkemMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
|
|
50
43
|
const h = sha3_256Hash(sx, ekCpa);
|
|
51
44
|
try {
|
|
52
45
|
const dk = new Uint8Array(params.dkBytes);
|
|
@@ -83,26 +76,17 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
|
|
|
83
76
|
const K = gOut.slice(0, 32);
|
|
84
77
|
r = gOut.slice(32, 64);
|
|
85
78
|
const c = indcpaEncrypt(kx, sx, params, ek, m, r);
|
|
86
|
-
// Wipe
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const kyberMem = new Uint8Array(kx.memory.buffer);
|
|
98
|
-
kyberMem.fill(0, kx.getMsgOffset(), kx.getMsgOffset() + 32);
|
|
99
|
-
kyberMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
|
|
100
|
-
kyberMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
|
|
101
|
-
kyberMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048);
|
|
102
|
-
kyberMem.fill(0, kx.getPolySlot1(), kx.getPolySlot1() + 512);
|
|
103
|
-
kyberMem.fill(0, kx.getPolySlot2(), kx.getPolySlot2() + 512);
|
|
104
|
-
kyberMem.fill(0, kx.getPolySlot3(), kx.getPolySlot3() + 512);
|
|
105
|
-
kyberMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
|
|
79
|
+
// Wipe m + r + e1/e2/u/v + m-poly + PRF scratch. See
|
|
80
|
+
// docs/mlkem.md#wipe-discipline.
|
|
81
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
82
|
+
mlkemMem.fill(0, kx.getMsgOffset(), kx.getMsgOffset() + 32);
|
|
83
|
+
mlkemMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048);
|
|
84
|
+
mlkemMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048);
|
|
85
|
+
mlkemMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048);
|
|
86
|
+
mlkemMem.fill(0, kx.getPolySlot1(), kx.getPolySlot1() + 512);
|
|
87
|
+
mlkemMem.fill(0, kx.getPolySlot2(), kx.getPolySlot2() + 512);
|
|
88
|
+
mlkemMem.fill(0, kx.getPolySlot3(), kx.getPolySlot3() + 512);
|
|
89
|
+
mlkemMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024);
|
|
106
90
|
sx.wipeBuffers();
|
|
107
91
|
return { ciphertext: c, sharedSecret: K };
|
|
108
92
|
}
|
|
@@ -118,8 +102,8 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
|
|
|
118
102
|
/**
|
|
119
103
|
* ML-KEM.Decaps_internal (FIPS 203 Algorithm 17).
|
|
120
104
|
*
|
|
121
|
-
* Constant-time: uses ct_verify and ct_cmov from
|
|
122
|
-
* MUST NOT branch on secret data in JS
|
|
105
|
+
* Constant-time: uses ct_verify and ct_cmov from mlkem WASM.
|
|
106
|
+
* MUST NOT branch on secret data in JS, all comparison via WASM primitives.
|
|
123
107
|
*/
|
|
124
108
|
export function kemDecapsulate(kx, sx, params, dk, c) {
|
|
125
109
|
const { skCpaBytes, ekBytes, ctBytes } = params;
|
|
@@ -151,42 +135,38 @@ export function kemDecapsulate(kx, sx, params, dk, c) {
|
|
|
151
135
|
jInput.set(z, 0);
|
|
152
136
|
jInput.set(c, 32);
|
|
153
137
|
kBar = shake256Hash(sx, jInput, 32);
|
|
154
|
-
// Re-encrypt c' = K-PKE.Encrypt(ek, m', r')
|
|
138
|
+
// Re-encrypt c' = K-PKE.Encrypt(ek, m', r'), indcpaEncrypt handles its own prfInput wipe
|
|
155
139
|
cPrime = indcpaEncrypt(kx, sx, params, ek, mPrime, rPrime);
|
|
156
|
-
// Constant-time comparison and conditional select via
|
|
157
|
-
const
|
|
140
|
+
// Constant-time comparison and conditional select via mlkem WASM
|
|
141
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
158
142
|
const ctOff = kx.getCtOffset();
|
|
159
143
|
const ctPrimeOff = kx.getCtPrimeOffset();
|
|
160
|
-
// Write c and c' into named
|
|
161
|
-
|
|
162
|
-
|
|
144
|
+
// Write c and c' into named mlkem memory regions
|
|
145
|
+
mlkemMem.set(c, ctOff);
|
|
146
|
+
mlkemMem.set(cPrime, ctPrimeOff);
|
|
163
147
|
// Write K' and K̄ into poly slots (512B each, 32B needed)
|
|
164
148
|
const kPrimeOff = kx.getPolySlot0();
|
|
165
149
|
const kBarOff = kx.getPolySlot1();
|
|
166
|
-
|
|
167
|
-
|
|
150
|
+
mlkemMem.set(kPrime, kPrimeOff);
|
|
151
|
+
mlkemMem.set(kBar, kBarOff);
|
|
168
152
|
// fail = 0 if c == c', non-zero if different
|
|
169
153
|
const fail = kx.ct_verify(ctOff, ctPrimeOff, ctBytes);
|
|
170
154
|
// If fail != 0 (mismatch): K' ← K̄
|
|
171
155
|
kx.ct_cmov(kPrimeOff, kBarOff, 32, fail);
|
|
172
|
-
const sharedSecret =
|
|
173
|
-
// Wipe
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
kyberMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048); // e₁ (noise polyvec for u)
|
|
187
|
-
kyberMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048); // uncompressed u polyvec from FO re-encryption
|
|
188
|
-
kyberMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024); // last PRF output block
|
|
189
|
-
kyberMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + skCpaBytes); // CPA secret key (long-lived — highest severity residual)
|
|
156
|
+
const sharedSecret = mlkemMem.slice(kPrimeOff, kPrimeOff + 32);
|
|
157
|
+
// Wipe CPA sk + m' + K'/K_bar + noise + PRF scratch. skCpa is the
|
|
158
|
+
// highest-severity residual (compromises every ciphertext under the
|
|
159
|
+
// corresponding ek). See docs/mlkem.md#wipe-discipline.
|
|
160
|
+
mlkemMem.fill(0, kx.getMsgOffset(), kx.getMsgOffset() + 32); // m' (bytes)
|
|
161
|
+
mlkemMem.fill(0, kPrimeOff, kPrimeOff + 32); // K' (final shared secret)
|
|
162
|
+
mlkemMem.fill(0, kBarOff, kBarOff + 512); // K̄ (first 32B) + e₂ poly tail
|
|
163
|
+
mlkemMem.fill(0, kx.getPolySlot2(), kx.getPolySlot2() + 512); // m'-poly / v residual
|
|
164
|
+
mlkemMem.fill(0, kx.getPolySlot3(), kx.getPolySlot3() + 512); // indcpa message poly
|
|
165
|
+
mlkemMem.fill(0, kx.getPolyvecSlot1(), kx.getPolyvecSlot1() + 2048); // r (NTT-domain noise polyvec)
|
|
166
|
+
mlkemMem.fill(0, kx.getPolyvecSlot2(), kx.getPolyvecSlot2() + 2048); // e₁ (noise polyvec for u)
|
|
167
|
+
mlkemMem.fill(0, kx.getPolyvecSlot3(), kx.getPolyvecSlot3() + 2048); // uncompressed u polyvec from FO re-encryption
|
|
168
|
+
mlkemMem.fill(0, kx.getXofPrfOffset(), kx.getXofPrfOffset() + 1024); // last PRF output block
|
|
169
|
+
mlkemMem.fill(0, kx.getSkOffset(), kx.getSkOffset() + skCpaBytes); // CPA secret key (long-lived, highest severity residual)
|
|
190
170
|
sx.wipeBuffers();
|
|
191
171
|
return sharedSecret;
|
|
192
172
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface MlKemParams {
|
|
2
2
|
k: number;
|
|
3
3
|
eta1: number;
|
|
4
4
|
eta2: number;
|
|
@@ -9,6 +9,6 @@ export interface KyberParams {
|
|
|
9
9
|
ctBytes: number;
|
|
10
10
|
skCpaBytes: number;
|
|
11
11
|
}
|
|
12
|
-
export declare const MLKEM512:
|
|
13
|
-
export declare const MLKEM768:
|
|
14
|
-
export declare const MLKEM1024:
|
|
12
|
+
export declare const MLKEM512: MlKemParams;
|
|
13
|
+
export declare const MLKEM768: MlKemParams;
|
|
14
|
+
export declare const MLKEM1024: MlKemParams;
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
20
|
// ▀█████▀▀
|
|
21
21
|
//
|
|
22
|
-
// src/ts/
|
|
22
|
+
// src/ts/mlkem/params.ts
|
|
23
23
|
//
|
|
24
24
|
// ML-KEM (FIPS 203) parameter set configurations.
|
|
25
|
-
// FIPS 203 Table 2
|
|
25
|
+
// FIPS 203 Table 2, Parameter sets for ML-KEM.
|
|
26
26
|
export const MLKEM512 = {
|
|
27
27
|
k: 2, eta1: 3, eta2: 2, du: 10, dv: 4,
|
|
28
28
|
ekBytes: 800, dkBytes: 1632, ctBytes: 768, skCpaBytes: 768,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CipherSuite } from '../stream/types.js';
|
|
2
|
+
import type { MlKemKeyPair, MlKemEncapsulation } from './types.js';
|
|
3
|
+
import type { MlKemParams } from './params.js';
|
|
4
|
+
export interface MlKemLike {
|
|
5
|
+
readonly params: MlKemParams;
|
|
6
|
+
encapsulate(ek: Uint8Array): MlKemEncapsulation;
|
|
7
|
+
decapsulate(dk: Uint8Array, c: Uint8Array): Uint8Array;
|
|
8
|
+
keygen(): MlKemKeyPair;
|
|
9
|
+
}
|
|
10
|
+
export declare function MlKemSuite(kem: MlKemLike, inner: CipherSuite): CipherSuite & {
|
|
11
|
+
keygen(): MlKemKeyPair;
|
|
12
|
+
};
|