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
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// ▄▄▄▄▄▄▄▄▄▄
|
|
2
|
+
// ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
|
|
3
|
+
// ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
|
|
4
|
+
// ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
|
|
5
|
+
// ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
|
|
6
|
+
// ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
|
|
7
|
+
// ███████▌ ▀██▀ ███
|
|
8
|
+
// ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
|
|
9
|
+
// ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
|
|
10
|
+
// ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
|
|
11
|
+
// ▀████▄ ▄██▄
|
|
12
|
+
// ▐████ ▐███ Author: xero (https://x-e.ro)
|
|
13
|
+
// ▄▄██████████ ▐███ ▄▄ License: MIT
|
|
14
|
+
// ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
|
|
15
|
+
// ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
|
|
16
|
+
// ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
|
|
17
|
+
// ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
|
|
18
|
+
// █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
|
|
19
|
+
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
|
+
// ▀█████▀▀
|
|
21
|
+
//
|
|
22
|
+
// src/ts/mldsa/keygen.ts
|
|
23
|
+
//
|
|
24
|
+
// FIPS 204 §6.1 Algorithm 6, ML-DSA.KeyGen_internal.
|
|
25
|
+
// Implements the deterministic xi-seeded key generator used by both the
|
|
26
|
+
// public keygen() (random ξ) and keygenDerand(ξ) entry points. Output is
|
|
27
|
+
// (pk, sk) byte-encoded per Algorithms 22 (pkEncode) and 24 (skEncode).
|
|
28
|
+
//
|
|
29
|
+
// Slot map (matrix slot + 6 polyvec slots):
|
|
30
|
+
// MATRIX_SLOT Â (matrix, k×ℓ polynomials in NTT domain, public)
|
|
31
|
+
// POLYVEC_SLOT_0 s₁ (time domain, preserved for skEncode bit_pack)
|
|
32
|
+
// POLYVEC_SLOT_1 s₂ (time domain, preserved for skEncode bit_pack)
|
|
33
|
+
// POLYVEC_SLOT_2 t = NTT⁻¹(Â · ŝ₁) + s₂ (intermediate, secret-derived)
|
|
34
|
+
// POLYVEC_SLOT_3 t₁ (high bits of t, public, encoded into pk)
|
|
35
|
+
// POLYVEC_SLOT_4 t₀ (low bits of t, secret, encoded into sk)
|
|
36
|
+
// POLYVEC_SLOT_5 ŝ₁ (NTT-domain Montgomery-form copy of s₁; consumed by
|
|
37
|
+
// the matrix product, then wiped). Time-domain s₁
|
|
38
|
+
// must survive in slot 0 for the sk BitPack step.
|
|
39
|
+
//
|
|
40
|
+
// POLY_SLOT_7 is reserved as scratch by polyvec_pointwise_acc_montgomery
|
|
41
|
+
// (called via polyvec_matrix_pointwise_montgomery); we never touch it.
|
|
42
|
+
import { wipe } from '../utils.js';
|
|
43
|
+
import { shake256HashConcat } from './sha3-helpers.js';
|
|
44
|
+
import { expandA, expandS } from './expand.js';
|
|
45
|
+
const POLY_BYTES = 1024;
|
|
46
|
+
const D = 13; // FIPS 204 §4 Table 1, d=13 for all sets
|
|
47
|
+
// Bitlen helper. bitlen(n) = floor(log2(n)) + 1 for n > 0. For ML-DSA we
|
|
48
|
+
// only ever feed it positive n, so the n=0 branch is a defensive return.
|
|
49
|
+
function bitlen(n) {
|
|
50
|
+
let b = 0;
|
|
51
|
+
let x = n;
|
|
52
|
+
while (x > 0) {
|
|
53
|
+
b++;
|
|
54
|
+
x >>>= 1;
|
|
55
|
+
}
|
|
56
|
+
return b;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* ML-DSA.KeyGen_internal, FIPS 204 Algorithm 6.
|
|
60
|
+
*
|
|
61
|
+
* Input ξ (32 bytes): the keygen seed. Produced internally by `keygen()`
|
|
62
|
+
* via `randomBytes(32)`, or supplied by `keygenDerand`.
|
|
63
|
+
* Output (pk, sk) byte-encoded per Alg 22 (pkEncode) and Alg 24 (skEncode).
|
|
64
|
+
*
|
|
65
|
+
* Wipe contract on return: every WASM region that held a secret or
|
|
66
|
+
* secret-derived intermediate is zeroed. Public regions (matrix Â, t₁,
|
|
67
|
+
* pk, ρ) are deliberately not wiped, they can be re-derived from the
|
|
68
|
+
* returned pk/sk anyway. The caller is expected to wipe the local ξ
|
|
69
|
+
* buffer it allocated; this function does not own that buffer.
|
|
70
|
+
*/
|
|
71
|
+
export function mldsaKeygenInternal(mx, sx, params, xi) {
|
|
72
|
+
const { k, l, eta, paramSet, pkBytes, skBytes } = params;
|
|
73
|
+
const mlMem = new Uint8Array(mx.memory.buffer);
|
|
74
|
+
// ── Slot offsets ────────────────────────────────────────────────────
|
|
75
|
+
const matOff = mx.getMatrixSlot();
|
|
76
|
+
const s1Off = mx.getPolyvecSlot0();
|
|
77
|
+
const s2Off = mx.getPolyvecSlot1();
|
|
78
|
+
const tOff = mx.getPolyvecSlot2();
|
|
79
|
+
const t1Off = mx.getPolyvecSlot3();
|
|
80
|
+
const t0Off = mx.getPolyvecSlot4();
|
|
81
|
+
const s1NttOff = mx.getPolyvecSlot5();
|
|
82
|
+
const seedOff = mx.getSeedOffset();
|
|
83
|
+
const pkOff = mx.getPkOffset();
|
|
84
|
+
const skOff = mx.getSkOffset();
|
|
85
|
+
const trOff = mx.getTrOffset();
|
|
86
|
+
const xofOff = mx.getXofPrfOffset();
|
|
87
|
+
// Layout cross-check at runtime: confirm the matrix-slot sizing covers
|
|
88
|
+
// this parameter set's k·ℓ polys. Cheap ratchet against future changes
|
|
89
|
+
// to buffers.ts that might shrink the region.
|
|
90
|
+
const matSize = mx.getMatrixSlotSize();
|
|
91
|
+
if (k * l * POLY_BYTES > matSize)
|
|
92
|
+
throw new Error(`leviathan-crypto: mldsa MATRIX_SLOT too small for ${paramSet} `
|
|
93
|
+
+ `(needs ${k * l * POLY_BYTES}, have ${matSize})`);
|
|
94
|
+
let rho;
|
|
95
|
+
let rhoPrime;
|
|
96
|
+
let kRand;
|
|
97
|
+
let seed128;
|
|
98
|
+
try {
|
|
99
|
+
// ── Step 1: H(ξ ‖ k_byte ‖ ℓ_byte, 1024 bits) → ρ‖ρ′‖K ─────────
|
|
100
|
+
// FIPS 204 §6.1 Algorithm 6 line 1. The k/ℓ domain-separator bytes
|
|
101
|
+
// are the post-IPD addition (FIPS 204 §D.3) defending against
|
|
102
|
+
// cross-parameter-set seed reuse, IntegerToBytes(k,1) and
|
|
103
|
+
// IntegerToBytes(ℓ,1).
|
|
104
|
+
const kByte = new Uint8Array([k & 0xFF]);
|
|
105
|
+
const lByte = new Uint8Array([l & 0xFF]);
|
|
106
|
+
seed128 = shake256HashConcat(sx, [xi, kByte, lByte], 128);
|
|
107
|
+
// Mirror H output into the WASM SEED region. This isn't strictly
|
|
108
|
+
// required (we only consume ρ/ρ′/K via TS-side slices below) but
|
|
109
|
+
// it lets the keygen-scratch-wipe gate verify that the SEED region
|
|
110
|
+
// is wiped, regardless of whether we pre-staged it. Wiped on exit.
|
|
111
|
+
mlMem.set(seed128, seedOff);
|
|
112
|
+
// Split: ρ(32) ‖ ρ′(64) ‖ K(32). Slice copies isolate each piece
|
|
113
|
+
// from the seed128 buffer so we can scrub each at known life-end.
|
|
114
|
+
rho = seed128.slice(0, 32);
|
|
115
|
+
rhoPrime = seed128.slice(32, 96);
|
|
116
|
+
kRand = seed128.slice(96, 128);
|
|
117
|
+
// ── Step 2: Â ← ExpandA(ρ) ────────────────────────────────────
|
|
118
|
+
// FIPS 204 Algorithm 32. Output is in NTT domain, regular form.
|
|
119
|
+
expandA(mx, sx, params, rho, matOff);
|
|
120
|
+
// ── Step 3: (s₁, s₂) ← ExpandS(ρ′) ────────────────────────────
|
|
121
|
+
// FIPS 204 Algorithm 33. Time-domain. expandS wipes its local
|
|
122
|
+
// seed scratch on exit.
|
|
123
|
+
expandS(mx, sx, params, rhoPrime, s1Off, s2Off);
|
|
124
|
+
// ── Step 4: t ← NTT⁻¹(Â · NTT(s₁)) + s₂ ───────────────────────
|
|
125
|
+
// FIPS 204 Algorithm 6 line 5. Stages:
|
|
126
|
+
// (a) Copy s₁ → ŝ₁-slot. Time-domain s₁ in slot_0 must survive
|
|
127
|
+
// for the BitPack step in skEncode (Algorithm 24); the
|
|
128
|
+
// NTT/tomont/multiply chain destroys its argument.
|
|
129
|
+
// (b) NTT(ŝ₁) in place, regular form.
|
|
130
|
+
// (c) tomont(ŝ₁): each coefficient ×R so that the subsequent
|
|
131
|
+
// pointwise_montgomery's R⁻¹ leaves a regular-form product.
|
|
132
|
+
// (d) Matrix-vector product ·ŝ₁ → polyvec_slot_2.
|
|
133
|
+
// (e) NTT⁻¹ in place → time-domain ·s₁.
|
|
134
|
+
// (f) Add s₂ coefficient-wise.
|
|
135
|
+
// (g) Reduce + caddq so coefficients are canonical [0, q-1],
|
|
136
|
+
// required by power2round per the WASM polynomial-layer contract.
|
|
137
|
+
mlMem.copyWithin(s1NttOff, s1Off, s1Off + l * POLY_BYTES);
|
|
138
|
+
mx.polyvec_ntt(s1NttOff, l);
|
|
139
|
+
mx.polyvec_tomont(s1NttOff, l);
|
|
140
|
+
mx.polyvec_matrix_pointwise_montgomery(tOff, matOff, s1NttOff, k, l);
|
|
141
|
+
mx.polyvec_invntt(tOff, k);
|
|
142
|
+
mx.polyvec_add(tOff, tOff, s2Off, k);
|
|
143
|
+
mx.polyvec_reduce(tOff, k);
|
|
144
|
+
mx.polyvec_caddq(tOff, k);
|
|
145
|
+
// ── Step 5: (t₁, t₀) ← Power2Round(t, d) ──────────────────────
|
|
146
|
+
// FIPS 204 Algorithm 35. Per-coefficient on the canonical-residue
|
|
147
|
+
// polyvec t.
|
|
148
|
+
mx.polyvec_power2round(t1Off, t0Off, tOff, k);
|
|
149
|
+
// ── Step 6: pk ← pkEncode(ρ, t₁) ──────────────────────────────
|
|
150
|
+
// FIPS 204 Algorithm 22. pk = ρ ‖ Σ SimpleBitPack(t₁[i], 2^c-1)
|
|
151
|
+
// where c = bitlen(q-1) - d = 23 - 13 = 10 → 320 bytes per poly.
|
|
152
|
+
const t1Bitlen = bitlen(8380417 - 1) - D; // 23 - 13 = 10
|
|
153
|
+
const t1PolyBytes = (256 * t1Bitlen) >> 3; // 320
|
|
154
|
+
mlMem.set(rho, pkOff);
|
|
155
|
+
for (let i = 0; i < k; i++) {
|
|
156
|
+
mx.simple_bit_pack(pkOff + 32 + i * t1PolyBytes, t1Off + i * POLY_BYTES, t1Bitlen);
|
|
157
|
+
}
|
|
158
|
+
// ── Step 7: tr ← H(pk, 512 bits) ──────────────────────────────
|
|
159
|
+
// 64-byte SHAKE256 of the public key. Cached in sk so signing
|
|
160
|
+
// doesn't have to re-derive it.
|
|
161
|
+
const pkBytesView = mlMem.subarray(pkOff, pkOff + pkBytes);
|
|
162
|
+
const tr = shake256HashConcat(sx, [pkBytesView], 64);
|
|
163
|
+
mlMem.set(tr, trOff);
|
|
164
|
+
wipe(tr);
|
|
165
|
+
// ── Step 8: sk ← skEncode(ρ, K, tr, s₁, s₂, t₀) ───────────────
|
|
166
|
+
// FIPS 204 Algorithm 24. Layout:
|
|
167
|
+
// ρ(32) ‖ K(32) ‖ tr(64) ‖
|
|
168
|
+
// BitPack(s₁[i], η, η) × ℓ each = 32·bitlen(2η)
|
|
169
|
+
// BitPack(s₂[i], η, η) × k
|
|
170
|
+
// BitPack(t₀[i], 2^(d-1)-1, 2^(d-1)) × k each = 32·d = 416
|
|
171
|
+
const etaBitlen = bitlen(2 * eta); // 3 (η=2) or 4 (η=4)
|
|
172
|
+
const etaPolyBytes = (256 * etaBitlen) >> 3; // 96 or 128
|
|
173
|
+
const t0PolyBytes = (256 * D) >> 3; // 416
|
|
174
|
+
const t0LowEdge = (1 << (D - 1)) - 1; // 4095
|
|
175
|
+
const t0HighEdge = (1 << (D - 1)); // 4096
|
|
176
|
+
mlMem.set(rho, skOff);
|
|
177
|
+
mlMem.set(kRand, skOff + 32);
|
|
178
|
+
mlMem.set(mlMem.subarray(trOff, trOff + 64), skOff + 64);
|
|
179
|
+
let off = skOff + 32 + 32 + 64; // = skOff + 128
|
|
180
|
+
for (let i = 0; i < l; i++) {
|
|
181
|
+
mx.bit_pack(off + i * etaPolyBytes, s1Off + i * POLY_BYTES, eta, eta);
|
|
182
|
+
}
|
|
183
|
+
off += l * etaPolyBytes;
|
|
184
|
+
for (let i = 0; i < k; i++) {
|
|
185
|
+
mx.bit_pack(off + i * etaPolyBytes, s2Off + i * POLY_BYTES, eta, eta);
|
|
186
|
+
}
|
|
187
|
+
off += k * etaPolyBytes;
|
|
188
|
+
for (let i = 0; i < k; i++) {
|
|
189
|
+
mx.bit_pack(off + i * t0PolyBytes, t0Off + i * POLY_BYTES, t0LowEdge, t0HighEdge);
|
|
190
|
+
}
|
|
191
|
+
off += k * t0PolyBytes;
|
|
192
|
+
// Sanity: encoded sk length must match the parameter-set size.
|
|
193
|
+
// A miscompute here is the kind of silent failure ACVP eventually
|
|
194
|
+
// catches but the layout assertion catches faster.
|
|
195
|
+
if (off - skOff !== skBytes)
|
|
196
|
+
throw new Error(`leviathan-crypto: mldsa skEncode length mismatch for ${paramSet} `
|
|
197
|
+
+ `(wrote ${off - skOff}, expected ${skBytes})`);
|
|
198
|
+
// ── Step 9: slice public outputs out of WASM memory ───────────
|
|
199
|
+
// Use slice() (copy) so the returned arrays are independent of
|
|
200
|
+
// the WASM linear memory we are about to wipe.
|
|
201
|
+
const pk = mlMem.slice(pkOff, pkOff + pkBytes);
|
|
202
|
+
const sk = mlMem.slice(skOff, skOff + skBytes);
|
|
203
|
+
// Wipe scratch per FIPS 204 §3.6.3. See
|
|
204
|
+
// docs/mldsa.md#wipe-discipline for the per-region severity
|
|
205
|
+
// ranking.
|
|
206
|
+
mlMem.fill(0, seedOff, seedOff + 128); // ρ ‖ ρ′ ‖ K
|
|
207
|
+
mlMem.fill(0, trOff, trOff + 64); // tr (public-derived but no need to keep)
|
|
208
|
+
mlMem.fill(0, skOff, skOff + skBytes); // encoded sk
|
|
209
|
+
mlMem.fill(0, s1Off, s1Off + l * POLY_BYTES); // s₁ (time-domain)
|
|
210
|
+
mlMem.fill(0, s1NttOff, s1NttOff + l * POLY_BYTES); // ŝ₁ (NTT/Montgomery)
|
|
211
|
+
mlMem.fill(0, s2Off, s2Off + k * POLY_BYTES); // s₂
|
|
212
|
+
mlMem.fill(0, tOff, tOff + k * POLY_BYTES); // t
|
|
213
|
+
mlMem.fill(0, t0Off, t0Off + k * POLY_BYTES); // t₀
|
|
214
|
+
mlMem.fill(0, xofOff, xofOff + 8192); // XOF/PRF scratch
|
|
215
|
+
// SHA3 module's input/state/output regions held ρ′ chunks and
|
|
216
|
+
// the H(ξ‖k‖ℓ) output. Wipe before returning so no residue
|
|
217
|
+
// persists across the public-API boundary.
|
|
218
|
+
sx.wipeBuffers();
|
|
219
|
+
return { verificationKey: pk, signingKey: sk };
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
// TS-side scratch, wipe even on early throw.
|
|
223
|
+
if (seed128)
|
|
224
|
+
wipe(seed128);
|
|
225
|
+
if (rho)
|
|
226
|
+
wipe(rho);
|
|
227
|
+
if (rhoPrime)
|
|
228
|
+
wipe(rhoPrime);
|
|
229
|
+
if (kRand)
|
|
230
|
+
wipe(kRand);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface MlDsaParams {
|
|
2
|
+
paramSet: 'ML-DSA-44' | 'ML-DSA-65' | 'ML-DSA-87';
|
|
3
|
+
k: number;
|
|
4
|
+
l: number;
|
|
5
|
+
eta: number;
|
|
6
|
+
tau: number;
|
|
7
|
+
lambda: number;
|
|
8
|
+
gamma1: number;
|
|
9
|
+
gamma2: number;
|
|
10
|
+
omega: number;
|
|
11
|
+
beta: number;
|
|
12
|
+
pkBytes: number;
|
|
13
|
+
skBytes: number;
|
|
14
|
+
sigBytes: number;
|
|
15
|
+
}
|
|
16
|
+
/** ML-DSA-44, FIPS 204 §4 Table 1 (NIST security category 2). */
|
|
17
|
+
export declare const MLDSA44: MlDsaParams;
|
|
18
|
+
/** ML-DSA-65, FIPS 204 §4 Table 1 (NIST security category 3). */
|
|
19
|
+
export declare const MLDSA65: MlDsaParams;
|
|
20
|
+
/** ML-DSA-87, FIPS 204 §4 Table 1 (NIST security category 5). */
|
|
21
|
+
export declare const MLDSA87: MlDsaParams;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// ▄▄▄▄▄▄▄▄▄▄
|
|
2
|
+
// ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
|
|
3
|
+
// ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
|
|
4
|
+
// ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
|
|
5
|
+
// ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
|
|
6
|
+
// ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
|
|
7
|
+
// ███████▌ ▀██▀ ███
|
|
8
|
+
// ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
|
|
9
|
+
// ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
|
|
10
|
+
// ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
|
|
11
|
+
// ▀████▄ ▄██▄
|
|
12
|
+
// ▐████ ▐███ Author: xero (https://x-e.ro)
|
|
13
|
+
// ▄▄██████████ ▐███ ▄▄ License: MIT
|
|
14
|
+
// ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
|
|
15
|
+
// ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
|
|
16
|
+
// ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
|
|
17
|
+
// ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
|
|
18
|
+
// █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
|
|
19
|
+
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
|
+
// ▀█████▀▀
|
|
21
|
+
//
|
|
22
|
+
// src/ts/mldsa/params.ts
|
|
23
|
+
//
|
|
24
|
+
// ML-DSA (FIPS 204) parameter sets, values from §4 Table 1 and Table 2.
|
|
25
|
+
//
|
|
26
|
+
// Sizes are derived per parameter set:
|
|
27
|
+
// pk = 32 + k · 32 · (bitlen(q-1) - d) , Alg 22 pkEncode
|
|
28
|
+
// sk = 32 + 32 + 64 + 32·((ℓ+k)·bitlen(2η) + d·k) , Alg 24 skEncode
|
|
29
|
+
// sig = λ/4 + ℓ·32·(1 + bitlen(γ₁ - 1)) + ω + k , Alg 26 sigEncode
|
|
30
|
+
//
|
|
31
|
+
// β = τ · η is precomputed; it bounds ‖cs1‖∞ and ‖cs2‖∞.
|
|
32
|
+
/** ML-DSA-44, FIPS 204 §4 Table 1 (NIST security category 2). */
|
|
33
|
+
export const MLDSA44 = {
|
|
34
|
+
paramSet: 'ML-DSA-44',
|
|
35
|
+
k: 4, l: 4, eta: 2, tau: 39, lambda: 128,
|
|
36
|
+
gamma1: 1 << 17, gamma2: ((8380417 - 1) / 88) | 0,
|
|
37
|
+
omega: 80, beta: 39 * 2,
|
|
38
|
+
pkBytes: 1312, skBytes: 2560, sigBytes: 2420,
|
|
39
|
+
};
|
|
40
|
+
/** ML-DSA-65, FIPS 204 §4 Table 1 (NIST security category 3). */
|
|
41
|
+
export const MLDSA65 = {
|
|
42
|
+
paramSet: 'ML-DSA-65',
|
|
43
|
+
k: 6, l: 5, eta: 4, tau: 49, lambda: 192,
|
|
44
|
+
gamma1: 1 << 19, gamma2: ((8380417 - 1) / 32) | 0,
|
|
45
|
+
omega: 55, beta: 49 * 4,
|
|
46
|
+
pkBytes: 1952, skBytes: 4032, sigBytes: 3309,
|
|
47
|
+
};
|
|
48
|
+
/** ML-DSA-87, FIPS 204 §4 Table 1 (NIST security category 5). */
|
|
49
|
+
export const MLDSA87 = {
|
|
50
|
+
paramSet: 'ML-DSA-87',
|
|
51
|
+
k: 8, l: 7, eta: 2, tau: 60, lambda: 256,
|
|
52
|
+
gamma1: 1 << 19, gamma2: ((8380417 - 1) / 32) | 0,
|
|
53
|
+
omega: 75, beta: 60 * 2,
|
|
54
|
+
pkBytes: 2592, skBytes: 4896, sigBytes: 4627,
|
|
55
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Sha3Exports } from '../mlkem/types.js';
|
|
2
|
+
/** Absorb msg into the sponge in ≤168-byte chunks. Caller must have
|
|
3
|
+
* already invoked the appropriate Init function (shake128Init etc.). */
|
|
4
|
+
export declare function sha3Absorb(sx: Sha3Exports, msg: Uint8Array): void;
|
|
5
|
+
/** SHAKE256(msg, n), fixed-length output. Resets sha3 state. */
|
|
6
|
+
export declare function shake256Hash(sx: Sha3Exports, msg: Uint8Array, n: number): Uint8Array;
|
|
7
|
+
/** SHAKE256(p₀ ‖ p₁ ‖ … ‖ p_{n-1}, outLen). Avoids the temporary buffer
|
|
8
|
+
* callers would otherwise allocate just to concatenate fixed-size pieces.
|
|
9
|
+
* Used for keygen's H(ξ ‖ k_byte ‖ ℓ_byte, 128). */
|
|
10
|
+
export declare function shake256HashConcat(sx: Sha3Exports, parts: readonly Uint8Array[], outLen: number): Uint8Array;
|
|
11
|
+
/**
|
|
12
|
+
* Set up SHAKE128 over `seed` and return an incremental block-squeezer.
|
|
13
|
+
* Each call returns the next 168-byte block as a Uint8Array view into the
|
|
14
|
+
* sha3 OUT region (lifetime: until the next squeeze call). Callers copy
|
|
15
|
+
* into the consuming module's XOF buffer before invoking the rejection
|
|
16
|
+
* sampler. Used by ExpandA (FIPS 204 Algorithm 32, RejNTTPoly driver).
|
|
17
|
+
*/
|
|
18
|
+
export declare function shake128Squeezer(sx: Sha3Exports, seed: Uint8Array): {
|
|
19
|
+
rate: number;
|
|
20
|
+
squeeze: () => Uint8Array;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Set up SHAKE256 over `seed` and return an incremental block-squeezer.
|
|
24
|
+
* Same shape as shake128Squeezer with rate 136. Used by ExpandS
|
|
25
|
+
* (FIPS 204 Algorithm 33, RejBoundedPoly driver).
|
|
26
|
+
*/
|
|
27
|
+
export declare function shake256Squeezer(sx: Sha3Exports, seed: Uint8Array): {
|
|
28
|
+
rate: number;
|
|
29
|
+
squeeze: () => Uint8Array;
|
|
30
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// ▄▄▄▄▄▄▄▄▄▄
|
|
2
|
+
// ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
|
|
3
|
+
// ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
|
|
4
|
+
// ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
|
|
5
|
+
// ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
|
|
6
|
+
// ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
|
|
7
|
+
// ███████▌ ▀██▀ ███
|
|
8
|
+
// ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
|
|
9
|
+
// ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
|
|
10
|
+
// ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
|
|
11
|
+
// ▀████▄ ▄██▄
|
|
12
|
+
// ▐████ ▐███ Author: xero (https://x-e.ro)
|
|
13
|
+
// ▄▄██████████ ▐███ ▄▄ License: MIT
|
|
14
|
+
// ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
|
|
15
|
+
// ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
|
|
16
|
+
// ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
|
|
17
|
+
// ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
|
|
18
|
+
// █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
|
|
19
|
+
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
|
+
// ▀█████▀▀
|
|
21
|
+
//
|
|
22
|
+
// src/ts/mldsa/sha3-helpers.ts
|
|
23
|
+
//
|
|
24
|
+
// SHAKE-driving helpers shared by ExpandA / ExpandS / keygen / sign / verify.
|
|
25
|
+
// All operate directly on raw Sha3Exports; no init() system involved.
|
|
26
|
+
//
|
|
27
|
+
// ML-DSA uses both SHAKE rates (FIPS 202): SHAKE128 (rate 168) for ExpandA
|
|
28
|
+
// and SHAKE256 (rate 136) for the rest of the scheme. The absorb path is
|
|
29
|
+
// rate-agnostic, keccakAbsorb consumes whatever buffer length the caller
|
|
30
|
+
// hands it as long as length ≤ 168 (the wider rate). One-shot SHAKE256
|
|
31
|
+
// helpers and the incremental SHAKE128 / SHAKE256 squeezers below cover
|
|
32
|
+
// every call shape ML-DSA needs.
|
|
33
|
+
const SHAKE128_RATE = 168;
|
|
34
|
+
const SHAKE256_RATE = 136;
|
|
35
|
+
/** Absorb msg into the sponge in ≤168-byte chunks. Caller must have
|
|
36
|
+
* already invoked the appropriate Init function (shake128Init etc.). */
|
|
37
|
+
export function sha3Absorb(sx, msg) {
|
|
38
|
+
const mem = new Uint8Array(sx.memory.buffer);
|
|
39
|
+
const inOff = sx.getInputOffset();
|
|
40
|
+
let pos = 0;
|
|
41
|
+
while (pos < msg.length) {
|
|
42
|
+
const chunk = Math.min(msg.length - pos, SHAKE128_RATE);
|
|
43
|
+
mem.set(msg.subarray(pos, pos + chunk), inOff);
|
|
44
|
+
sx.keccakAbsorb(chunk);
|
|
45
|
+
pos += chunk;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** SHAKE256(msg, n), fixed-length output. Resets sha3 state. */
|
|
49
|
+
export function shake256Hash(sx, msg, n) {
|
|
50
|
+
sx.shake256Init();
|
|
51
|
+
sha3Absorb(sx, msg);
|
|
52
|
+
sx.shakePad();
|
|
53
|
+
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
54
|
+
const outOff = sx.getOutOffset();
|
|
55
|
+
const out = new Uint8Array(n);
|
|
56
|
+
let pos = 0;
|
|
57
|
+
while (pos < n) {
|
|
58
|
+
sx.shakeSqueezeBlock();
|
|
59
|
+
const take = Math.min(n - pos, SHAKE256_RATE);
|
|
60
|
+
out.set(sha3Mem.subarray(outOff, outOff + take), pos);
|
|
61
|
+
pos += take;
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/** SHAKE256(p₀ ‖ p₁ ‖ … ‖ p_{n-1}, outLen). Avoids the temporary buffer
|
|
66
|
+
* callers would otherwise allocate just to concatenate fixed-size pieces.
|
|
67
|
+
* Used for keygen's H(ξ ‖ k_byte ‖ ℓ_byte, 128). */
|
|
68
|
+
export function shake256HashConcat(sx, parts, outLen) {
|
|
69
|
+
sx.shake256Init();
|
|
70
|
+
for (const p of parts)
|
|
71
|
+
sha3Absorb(sx, p);
|
|
72
|
+
sx.shakePad();
|
|
73
|
+
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
74
|
+
const outOff = sx.getOutOffset();
|
|
75
|
+
const out = new Uint8Array(outLen);
|
|
76
|
+
let pos = 0;
|
|
77
|
+
while (pos < outLen) {
|
|
78
|
+
sx.shakeSqueezeBlock();
|
|
79
|
+
const take = Math.min(outLen - pos, SHAKE256_RATE);
|
|
80
|
+
out.set(sha3Mem.subarray(outOff, outOff + take), pos);
|
|
81
|
+
pos += take;
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Set up SHAKE128 over `seed` and return an incremental block-squeezer.
|
|
87
|
+
* Each call returns the next 168-byte block as a Uint8Array view into the
|
|
88
|
+
* sha3 OUT region (lifetime: until the next squeeze call). Callers copy
|
|
89
|
+
* into the consuming module's XOF buffer before invoking the rejection
|
|
90
|
+
* sampler. Used by ExpandA (FIPS 204 Algorithm 32, RejNTTPoly driver).
|
|
91
|
+
*/
|
|
92
|
+
export function shake128Squeezer(sx, seed) {
|
|
93
|
+
sx.shake128Init();
|
|
94
|
+
sha3Absorb(sx, seed);
|
|
95
|
+
sx.shakePad();
|
|
96
|
+
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
97
|
+
const outOff = sx.getOutOffset();
|
|
98
|
+
return {
|
|
99
|
+
rate: SHAKE128_RATE,
|
|
100
|
+
squeeze: () => {
|
|
101
|
+
sx.shakeSqueezeBlock();
|
|
102
|
+
return sha3Mem.subarray(outOff, outOff + SHAKE128_RATE);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Set up SHAKE256 over `seed` and return an incremental block-squeezer.
|
|
108
|
+
* Same shape as shake128Squeezer with rate 136. Used by ExpandS
|
|
109
|
+
* (FIPS 204 Algorithm 33, RejBoundedPoly driver).
|
|
110
|
+
*/
|
|
111
|
+
export function shake256Squeezer(sx, seed) {
|
|
112
|
+
sx.shake256Init();
|
|
113
|
+
sha3Absorb(sx, seed);
|
|
114
|
+
sx.shakePad();
|
|
115
|
+
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
116
|
+
const outOff = sx.getOutOffset();
|
|
117
|
+
return {
|
|
118
|
+
rate: SHAKE256_RATE,
|
|
119
|
+
squeeze: () => {
|
|
120
|
+
sx.shakeSqueezeBlock();
|
|
121
|
+
return sha3Mem.subarray(outOff, outOff + SHAKE256_RATE);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { MlDsaExports, Sha3Exports } from './types.js';
|
|
2
|
+
import type { MlDsaParams } from './params.js';
|
|
3
|
+
import { type PreHashAlgorithm } from './hashvariant.js';
|
|
4
|
+
/**
|
|
5
|
+
* ML-DSA.Sign_internal, FIPS 204 Algorithm 7.
|
|
6
|
+
*
|
|
7
|
+
* Inputs:
|
|
8
|
+
* sk , encoded signing key (skBytes per parameter set)
|
|
9
|
+
* MPrime, domain-separated message bytes (caller-built via constructMPrime)
|
|
10
|
+
* rnd , 32-byte randomness (random for hedged, all-zero for deterministic,
|
|
11
|
+
* caller-supplied for derand/CAVP)
|
|
12
|
+
*
|
|
13
|
+
* Output: σ (sigBytes), encoded per Algorithm 26 (sigEncode).
|
|
14
|
+
*
|
|
15
|
+
* Wipe contract: every WASM region that held a secret or secret-derived
|
|
16
|
+
* intermediate is zeroed before return. TS-side scratch (μ, ρ'', c̃, the
|
|
17
|
+
* w1 byte slice) wipes via try/finally even on early throw.
|
|
18
|
+
*/
|
|
19
|
+
export declare function mldsaSignInternal(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, sk: Uint8Array, MPrime: Uint8Array, rnd: Uint8Array): Uint8Array;
|
|
20
|
+
/**
|
|
21
|
+
* HashML-DSA sign, post-prehash. FIPS 204 §5.4 Algorithm 4 lines 22-24.
|
|
22
|
+
* Builds M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(algo) ‖ prehash and drives
|
|
23
|
+
* Sign_internal with the caller-supplied rnd.
|
|
24
|
+
*
|
|
25
|
+
* The caller owns `prehash` (it may be a slice of WASM memory, a
|
|
26
|
+
* caller-controlled digest, or an internally-computed PH_M); this helper
|
|
27
|
+
* never wipes it. The caller also owns `rnd` (hedged: caller wipes a
|
|
28
|
+
* freshly-generated value; deterministic: rnd is zeros, no wipe; derand:
|
|
29
|
+
* caller-supplied per FIPS 204 §3.4 contract).
|
|
30
|
+
*
|
|
31
|
+
* The 12 approved pre-hash functions (`algo`) and their OIDs are FIPS 204
|
|
32
|
+
* §5.4.1's full catalog. Domain-sep byte 0x01 inside the M' construction
|
|
33
|
+
* separates HashML-DSA signatures from pure-ML-DSA signatures on the same
|
|
34
|
+
* key per §3.6.4.
|
|
35
|
+
*/
|
|
36
|
+
export declare function signWithPrehash(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, sk: Uint8Array, prehash: Uint8Array, algo: PreHashAlgorithm, ctx: Uint8Array, rnd: Uint8Array): Uint8Array;
|