leviathan-crypto 2.0.1 → 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 +88 -281
- package/LICENSE +4 -0
- package/README.md +275 -87
- 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.d.ts +10 -0
- package/dist/chacha20/cipher-suite.js +98 -13
- package/dist/chacha20/generator.d.ts +12 -0
- package/dist/chacha20/generator.js +91 -0
- package/dist/chacha20/index.d.ts +100 -3
- package/dist/chacha20/index.js +169 -35
- package/dist/chacha20/ops.d.ts +57 -6
- package/dist/chacha20/ops.js +107 -27
- package/dist/chacha20/pool-worker.js +14 -0
- package/dist/chacha20/types.d.ts +1 -32
- package/dist/cte-wasm.d.ts +1 -0
- package/dist/cte-wasm.js +3 -0
- package/dist/cte.wasm +0 -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 -0
- package/dist/embedded/chacha20-pool-worker.js +5 -0
- 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 -0
- package/dist/embedded/serpent-pool-worker.js +5 -0
- 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 +18 -12
- package/dist/fortuna.js +166 -99
- package/dist/index.d.ts +42 -11
- package/dist/index.js +65 -20
- package/dist/init.d.ts +1 -3
- package/dist/init.js +73 -7
- 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 -19
- package/dist/loader.js +26 -32
- 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 +48 -48
- package/dist/mlkem/index.d.ts +37 -0
- package/dist/{kyber → mlkem}/index.js +41 -31
- package/dist/mlkem/kem.d.ts +21 -0
- package/dist/{kyber → mlkem}/kem.js +48 -13
- 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 +4 -3
- package/dist/{kyber → mlkem}/types.js +1 -1
- package/dist/mlkem/validate.d.ts +23 -0
- package/dist/{kyber → mlkem}/validate.js +24 -20
- package/dist/{kyber.wasm → mlkem.wasm} +0 -0
- package/dist/p256.wasm +0 -0
- package/dist/ratchet/index.d.ts +8 -0
- package/dist/ratchet/index.js +38 -0
- package/dist/ratchet/kdf-chain.d.ts +13 -0
- package/dist/ratchet/kdf-chain.js +85 -0
- package/dist/ratchet/ratchet-keypair.d.ts +9 -0
- package/dist/ratchet/ratchet-keypair.js +61 -0
- package/dist/ratchet/root-kdf.d.ts +4 -0
- package/dist/ratchet/root-kdf.js +124 -0
- package/dist/ratchet/skipped-key-store.d.ts +14 -0
- package/dist/ratchet/skipped-key-store.js +154 -0
- package/dist/ratchet/types.d.ts +36 -0
- package/dist/ratchet/types.js +26 -0
- package/dist/serpent/cipher-suite.d.ts +10 -0
- package/dist/serpent/cipher-suite.js +144 -56
- package/dist/serpent/generator.d.ts +12 -0
- package/dist/serpent/generator.js +97 -0
- package/dist/serpent/index.d.ts +62 -1
- package/dist/serpent/index.js +97 -21
- package/dist/serpent/pool-worker.js +28 -102
- package/dist/serpent/serpent-cbc.d.ts +16 -6
- package/dist/serpent/serpent-cbc.js +58 -37
- package/dist/serpent/shared-ops.d.ts +63 -0
- package/dist/serpent/shared-ops.js +178 -0
- package/dist/serpent/types.d.ts +1 -5
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hash.d.ts +2 -0
- package/dist/sha2/hash.js +53 -0
- package/dist/sha2/hkdf.js +5 -5
- package/dist/sha2/index.d.ts +22 -1
- package/dist/sha2/index.js +80 -11
- package/dist/sha2/types.d.ts +41 -2
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/hash.d.ts +2 -0
- package/dist/sha3/hash.js +53 -0
- package/dist/sha3/index.d.ts +87 -3
- package/dist/sha3/index.js +317 -19
- 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 +8 -8
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/open-stream.js +65 -22
- package/dist/stream/seal-stream-pool.d.ts +2 -0
- package/dist/stream/seal-stream-pool.js +100 -33
- package/dist/stream/seal-stream.d.ts +1 -1
- package/dist/stream/seal-stream.js +48 -19
- 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 +22 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +9 -10
- package/dist/utils.js +84 -59
- package/dist/wasm-source.d.ts +9 -8
- 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 +123 -64
- package/SECURITY.md +0 -276
- package/dist/ct-wasm.d.ts +0 -1
- package/dist/ct-wasm.js +0 -3
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +0 -323
- package/dist/docs/architecture.md +0 -932
- package/dist/docs/argon2id.md +0 -302
- package/dist/docs/chacha20.md +0 -674
- package/dist/docs/exports.md +0 -241
- package/dist/docs/fortuna.md +0 -313
- package/dist/docs/init.md +0 -302
- package/dist/docs/loader.md +0 -161
- package/dist/docs/serpent.md +0 -519
- package/dist/docs/sha2.md +0 -613
- package/dist/docs/sha3.md +0 -546
- package/dist/docs/types.md +0 -276
- package/dist/docs/utils.md +0 -367
- 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 -13
- package/dist/kyber/validate.d.ts +0 -19
|
@@ -19,12 +19,12 @@
|
|
|
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
|
-
// ── SHA3 helpers
|
|
27
|
+
// ── SHA3 helpers ────────────────────────────────────────────────────────────
|
|
28
28
|
// All operate directly on raw Sha3Exports, no init() system involved.
|
|
29
29
|
/** Absorb msg into the sha3 sponge in 168-byte chunks (max rate). */
|
|
30
30
|
function sha3Absorb(sx, msg) {
|
|
@@ -77,12 +77,12 @@ export function shake256Hash(sx, msg, n) {
|
|
|
77
77
|
}
|
|
78
78
|
return out;
|
|
79
79
|
}
|
|
80
|
-
// ── Matrix generation
|
|
80
|
+
// ── Matrix generation ───────────────────────────────────────────────────────
|
|
81
81
|
/**
|
|
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,19 +115,19 @@ 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
|
}
|
|
122
122
|
}
|
|
123
|
-
// ── Noise generation
|
|
123
|
+
// ── Noise generation ────────────────────────────────────────────────────────
|
|
124
124
|
/**
|
|
125
125
|
* CBD noise polyvec: SHAKE256(σ || nonce) for each entry.
|
|
126
126
|
* FIPS 203 Algorithm 7: PRF_η(σ, N) = SHAKE256(σ || N)[0..64η-1].
|
|
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);
|
|
@@ -185,15 +185,15 @@ function noisePoly(kx, sx, polyOff, sigma, nonce, eta) {
|
|
|
185
185
|
wipe(prfInput);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
-
// ── IND-CPA functions
|
|
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,32 +19,22 @@
|
|
|
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
|
|
26
|
-
import { getInstance, initModule, isInitialized } from '../init.js';
|
|
24
|
+
// ML-KEM public API, MlKem512, MlKem768, MlKem1024 classes.
|
|
25
|
+
// Uses the init() module cache, call init({ mlkem: ..., sha3: ... }) before constructing.
|
|
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 {
|
|
47
|
-
// ── Layout assertion
|
|
36
|
+
export { MlKemSuite } from './suite.js';
|
|
37
|
+
// ── Layout assertion ────────────────────────────────────────────────────────
|
|
48
38
|
function assertLayout(kx, p) {
|
|
49
39
|
const pk = kx.getPkOffset();
|
|
50
40
|
const sk = kx.getSkOffset();
|
|
@@ -52,32 +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
|
-
// ── Base class
|
|
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) {
|
|
71
|
+
_assertNotOwned('sha3');
|
|
72
|
+
_assertNotOwned('mlkem');
|
|
81
73
|
if (d.length !== 32)
|
|
82
74
|
throw new RangeError(`d seed must be 32 bytes (got ${d.length})`);
|
|
83
75
|
if (z.length !== 32)
|
|
@@ -96,10 +88,14 @@ export class MlKemBase {
|
|
|
96
88
|
}
|
|
97
89
|
}
|
|
98
90
|
encapsulateDerand(ek, m) {
|
|
91
|
+
_assertNotOwned('sha3');
|
|
92
|
+
_assertNotOwned('mlkem');
|
|
99
93
|
if (ek.length !== this.params.ekBytes)
|
|
100
94
|
throw new RangeError(`encapsulation key must be ${this.params.ekBytes} bytes (got ${ek.length})`);
|
|
101
95
|
if (m.length !== 32)
|
|
102
96
|
throw new RangeError(`randomness m must be 32 bytes (got ${m.length})`);
|
|
97
|
+
if (!checkEncapsulationKey(this.kx, this.params, ek))
|
|
98
|
+
throw new RangeError('leviathan-crypto: encapsulation key failed FIPS 203 §7.2 validity check');
|
|
103
99
|
return kemEncapsulateDerand(this.kx, this.sx, this.params, ek, m);
|
|
104
100
|
}
|
|
105
101
|
encapsulate(ek) {
|
|
@@ -112,37 +108,51 @@ export class MlKemBase {
|
|
|
112
108
|
}
|
|
113
109
|
}
|
|
114
110
|
decapsulate(dk, c) {
|
|
111
|
+
_assertNotOwned('sha3');
|
|
112
|
+
_assertNotOwned('mlkem');
|
|
115
113
|
if (dk.length !== this.params.dkBytes)
|
|
116
114
|
throw new RangeError(`decapsulation key must be ${this.params.dkBytes} bytes (got ${dk.length})`);
|
|
117
115
|
if (c.length !== this.params.ctBytes)
|
|
118
116
|
throw new RangeError(`ciphertext must be ${this.params.ctBytes} bytes (got ${c.length})`);
|
|
117
|
+
if (!checkDecapsulationKey(this.kx, this.sx, this.params, dk))
|
|
118
|
+
throw new RangeError('leviathan-crypto: decapsulation key failed FIPS 203 §7.3 validity check');
|
|
119
119
|
return kemDecapsulate(this.kx, this.sx, this.params, dk, c);
|
|
120
120
|
}
|
|
121
121
|
checkEncapsulationKey(ek) {
|
|
122
|
+
_assertNotOwned('sha3');
|
|
123
|
+
_assertNotOwned('mlkem');
|
|
122
124
|
return checkEncapsulationKey(this.kx, this.params, ek);
|
|
123
125
|
}
|
|
124
126
|
checkDecapsulationKey(dk) {
|
|
127
|
+
_assertNotOwned('sha3');
|
|
128
|
+
_assertNotOwned('mlkem');
|
|
125
129
|
return checkDecapsulationKey(this.kx, this.sx, this.params, dk);
|
|
126
130
|
}
|
|
127
131
|
dispose() {
|
|
128
132
|
this.kx.wipeBuffers();
|
|
129
|
-
this.sx.wipeBuffers()
|
|
133
|
+
// MlKemBase does not own the sha3 module, wiping this.sx.wipeBuffers()
|
|
134
|
+
// here would clobber any SHAKE128/SHAKE256 instance live at the time
|
|
135
|
+
// of dispose(). The wipe is not needed: every public mlkem op
|
|
136
|
+
// (keygen, encapsulate, decapsulate, checkDecapsulationKey) calls
|
|
137
|
+
// sx.wipeBuffers() before returning, under the _assertNotOwned('sha3')
|
|
138
|
+
// guard it holds for the duration. sha3 scratch therefore carries no
|
|
139
|
+
// residue across a mlkem op boundary, secret-derived or otherwise.
|
|
130
140
|
}
|
|
131
141
|
}
|
|
132
|
-
// ── Public classes
|
|
133
|
-
/** ML-KEM-512
|
|
142
|
+
// ── Public classes ──────────────────────────────────────────────────────────
|
|
143
|
+
/** ML-KEM-512, k=2, η₁=3, η₂=2, dᵤ=10, dᵥ=4. */
|
|
134
144
|
export class MlKem512 extends MlKemBase {
|
|
135
145
|
constructor() {
|
|
136
146
|
super(MLKEM512);
|
|
137
147
|
}
|
|
138
148
|
}
|
|
139
|
-
/** ML-KEM-768
|
|
149
|
+
/** ML-KEM-768, k=3, η₁=2, η₂=2, dᵤ=10, dᵥ=4. */
|
|
140
150
|
export class MlKem768 extends MlKemBase {
|
|
141
151
|
constructor() {
|
|
142
152
|
super(MLKEM768);
|
|
143
153
|
}
|
|
144
154
|
}
|
|
145
|
-
/** ML-KEM-1024
|
|
155
|
+
/** ML-KEM-1024, k=4, η₁=2, η₂=2, dᵤ=11, dᵥ=5. */
|
|
146
156
|
export class MlKem1024 extends MlKemBase {
|
|
147
157
|
constructor() {
|
|
148
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,6 +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 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);
|
|
36
43
|
const h = sha3_256Hash(sx, ekCpa);
|
|
37
44
|
try {
|
|
38
45
|
const dk = new Uint8Array(params.dkBytes);
|
|
@@ -40,6 +47,7 @@ export function kemKeypairDerand(kx, sx, params, d, z) {
|
|
|
40
47
|
dk.set(ekCpa, params.skCpaBytes);
|
|
41
48
|
dk.set(h, params.skCpaBytes + params.ekBytes);
|
|
42
49
|
dk.set(z, params.skCpaBytes + params.ekBytes + 32);
|
|
50
|
+
sx.wipeBuffers();
|
|
43
51
|
return {
|
|
44
52
|
encapsulationKey: ekCpa,
|
|
45
53
|
decapsulationKey: dk,
|
|
@@ -68,6 +76,18 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
|
|
|
68
76
|
const K = gOut.slice(0, 32);
|
|
69
77
|
r = gOut.slice(32, 64);
|
|
70
78
|
const c = indcpaEncrypt(kx, sx, params, ek, m, r);
|
|
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);
|
|
90
|
+
sx.wipeBuffers();
|
|
71
91
|
return { ciphertext: c, sharedSecret: K };
|
|
72
92
|
}
|
|
73
93
|
finally {
|
|
@@ -82,8 +102,8 @@ export function kemEncapsulateDerand(kx, sx, params, ek, m) {
|
|
|
82
102
|
/**
|
|
83
103
|
* ML-KEM.Decaps_internal (FIPS 203 Algorithm 17).
|
|
84
104
|
*
|
|
85
|
-
* Constant-time: uses ct_verify and ct_cmov from
|
|
86
|
-
* 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.
|
|
87
107
|
*/
|
|
88
108
|
export function kemDecapsulate(kx, sx, params, dk, c) {
|
|
89
109
|
const { skCpaBytes, ekBytes, ctBytes } = params;
|
|
@@ -115,25 +135,40 @@ export function kemDecapsulate(kx, sx, params, dk, c) {
|
|
|
115
135
|
jInput.set(z, 0);
|
|
116
136
|
jInput.set(c, 32);
|
|
117
137
|
kBar = shake256Hash(sx, jInput, 32);
|
|
118
|
-
// 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
|
|
119
139
|
cPrime = indcpaEncrypt(kx, sx, params, ek, mPrime, rPrime);
|
|
120
|
-
// Constant-time comparison and conditional select via
|
|
121
|
-
const
|
|
140
|
+
// Constant-time comparison and conditional select via mlkem WASM
|
|
141
|
+
const mlkemMem = new Uint8Array(kx.memory.buffer);
|
|
122
142
|
const ctOff = kx.getCtOffset();
|
|
123
143
|
const ctPrimeOff = kx.getCtPrimeOffset();
|
|
124
|
-
// Write c and c' into named
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
// Write c and c' into named mlkem memory regions
|
|
145
|
+
mlkemMem.set(c, ctOff);
|
|
146
|
+
mlkemMem.set(cPrime, ctPrimeOff);
|
|
127
147
|
// Write K' and K̄ into poly slots (512B each, 32B needed)
|
|
128
148
|
const kPrimeOff = kx.getPolySlot0();
|
|
129
149
|
const kBarOff = kx.getPolySlot1();
|
|
130
|
-
|
|
131
|
-
|
|
150
|
+
mlkemMem.set(kPrime, kPrimeOff);
|
|
151
|
+
mlkemMem.set(kBar, kBarOff);
|
|
132
152
|
// fail = 0 if c == c', non-zero if different
|
|
133
153
|
const fail = kx.ct_verify(ctOff, ctPrimeOff, ctBytes);
|
|
134
154
|
// If fail != 0 (mismatch): K' ← K̄
|
|
135
155
|
kx.ct_cmov(kPrimeOff, kBarOff, 32, fail);
|
|
136
|
-
|
|
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)
|
|
170
|
+
sx.wipeBuffers();
|
|
171
|
+
return sharedSecret;
|
|
137
172
|
}
|
|
138
173
|
finally {
|
|
139
174
|
if (mPrime)
|
|
@@ -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
|
+
};
|