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,380 @@
|
|
|
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/sign.ts
|
|
23
|
+
//
|
|
24
|
+
// FIPS 204 §6.2 Algorithm 7, ML-DSA.Sign_internal. Drives the
|
|
25
|
+
// rejection-sampling loop; wipe contract per §3.6.3 (see
|
|
26
|
+
// docs/mldsa.md#wipe-discipline).
|
|
27
|
+
//
|
|
28
|
+
// Slot allocation through one iteration body:
|
|
29
|
+
// POLYVEC_SLOT_0 ŝ₁ (NTT, persistent through loop)
|
|
30
|
+
// POLYVEC_SLOT_1 ŝ₂ (NTT, persistent)
|
|
31
|
+
// POLYVEC_SLOT_2 t̂₀ (NTT, persistent)
|
|
32
|
+
// POLYVEC_SLOT_3 y_time → ⟨cs₂⟩ → r₀ → ⟨ct₀⟩ → h
|
|
33
|
+
// POLYVEC_SLOT_4 y_ntt → w₁ → ⟨cs₁⟩ → z
|
|
34
|
+
// POLYVEC_SLOT_5 w_ntt → w_time → (w − ⟨cs₂⟩)
|
|
35
|
+
// POLY_SLOT_0 signs (sample_in_ball signsOff)
|
|
36
|
+
// POLY_SLOT_1 c, then ĉ (NTT-domain)
|
|
37
|
+
// POLY_SLOT_7 polyvec_pointwise_acc_montgomery scratch
|
|
38
|
+
//
|
|
39
|
+
// XOF/PRF carries ExpandMask, w1Encode, and SampleInBall bytes; each
|
|
40
|
+
// single-use per iteration.
|
|
41
|
+
import { wipe } from '../utils.js';
|
|
42
|
+
import { sha3Absorb, shake256HashConcat } from './sha3-helpers.js';
|
|
43
|
+
import { expandA, expandMask } from './expand.js';
|
|
44
|
+
import { getOid } from './hashvariant.js';
|
|
45
|
+
import { constructMPrimeHash } from './format.js';
|
|
46
|
+
const POLY_BYTES = 1024;
|
|
47
|
+
const D = 13;
|
|
48
|
+
const Q = 8380417;
|
|
49
|
+
const SHAKE256_RATE = 136;
|
|
50
|
+
// FIPS 204 Appendix C: expected 4-7 iterations, spec minimum bound 814.
|
|
51
|
+
// 1000 is a liveness bound, not a probability tail-cut.
|
|
52
|
+
const MAX_SIGN_ITERATIONS = 1000;
|
|
53
|
+
function bitlen(n) {
|
|
54
|
+
let b = 0;
|
|
55
|
+
let x = n;
|
|
56
|
+
while (x > 0) {
|
|
57
|
+
b++;
|
|
58
|
+
x >>>= 1;
|
|
59
|
+
}
|
|
60
|
+
return b;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* ML-DSA.Sign_internal, FIPS 204 Algorithm 7.
|
|
64
|
+
*
|
|
65
|
+
* Inputs:
|
|
66
|
+
* sk , encoded signing key (skBytes per parameter set)
|
|
67
|
+
* MPrime, domain-separated message bytes (caller-built via constructMPrime)
|
|
68
|
+
* rnd , 32-byte randomness (random for hedged, all-zero for deterministic,
|
|
69
|
+
* caller-supplied for derand/CAVP)
|
|
70
|
+
*
|
|
71
|
+
* Output: σ (sigBytes), encoded per Algorithm 26 (sigEncode).
|
|
72
|
+
*
|
|
73
|
+
* Wipe contract: every WASM region that held a secret or secret-derived
|
|
74
|
+
* intermediate is zeroed before return. TS-side scratch (μ, ρ'', c̃, the
|
|
75
|
+
* w1 byte slice) wipes via try/finally even on early throw.
|
|
76
|
+
*/
|
|
77
|
+
export function mldsaSignInternal(mx, sx, params, sk, MPrime, rnd) {
|
|
78
|
+
const { k, l, eta, tau, lambda, gamma1, gamma2, beta, omega, sigBytes } = params;
|
|
79
|
+
const lambdaOver4 = lambda >>> 2; // 32 / 48 / 64
|
|
80
|
+
const etaBitlen = bitlen(2 * eta); // 3 (η=2) or 4 (η=4)
|
|
81
|
+
const etaPolyBytes = (256 * etaBitlen) >> 3; // 96 or 128
|
|
82
|
+
const t0PolyBytes = (256 * D) >> 3; // 416
|
|
83
|
+
const t0LowEdge = (1 << (D - 1)) - 1; // 4095
|
|
84
|
+
const t0HighEdge = (1 << (D - 1)); // 4096
|
|
85
|
+
const c = 1 + bitlen(gamma1 - 1); // 18 or 20
|
|
86
|
+
const zPolyBytes = 32 * c; // 576 or 640
|
|
87
|
+
// w₁ field: m = (q − 1) / (2γ₂); width = bitlen(m − 1).
|
|
88
|
+
// γ₂=(q-1)/88 ⇒ m=44 ⇒ width=6; γ₂=(q-1)/32 ⇒ m=16 ⇒ width=4.
|
|
89
|
+
const w1Bitlen = bitlen(((Q - 1) / (2 * gamma2)) - 1);
|
|
90
|
+
const w1PolyBytes = (256 * w1Bitlen) >> 3;
|
|
91
|
+
const w1TotalBytes = k * w1PolyBytes;
|
|
92
|
+
// ── WASM offsets ─────────────────────────────────────────────────────
|
|
93
|
+
const matOff = mx.getMatrixSlot();
|
|
94
|
+
const slot0 = mx.getPolyvecSlot0(); // ŝ₁
|
|
95
|
+
const slot1 = mx.getPolyvecSlot1(); // ŝ₂
|
|
96
|
+
const slot2 = mx.getPolyvecSlot2(); // t̂₀
|
|
97
|
+
const slot3 = mx.getPolyvecSlot3(); // y_time / ⟨cs₂⟩ / r₀ / ⟨ct₀⟩ / h
|
|
98
|
+
const slot4 = mx.getPolyvecSlot4(); // y_ntt / w₁ / ⟨cs₁⟩ / z
|
|
99
|
+
const slot5 = mx.getPolyvecSlot5(); // w / w − ⟨cs₂⟩
|
|
100
|
+
const polySlotBase = mx.getPolySlotBase();
|
|
101
|
+
const polySlot0 = mx.getPolySlot0(); // signs (8 bytes)
|
|
102
|
+
const polySlot1 = mx.getPolySlot1(); // c → ĉ
|
|
103
|
+
const sigOff = mx.getSigOffset();
|
|
104
|
+
const xofOff = mx.getXofPrfOffset();
|
|
105
|
+
const seedOff = mx.getSeedOffset();
|
|
106
|
+
const trOff = mx.getTrOffset();
|
|
107
|
+
const skOff = mx.getSkOffset();
|
|
108
|
+
const cTildeOff = mx.getCTildeOffset();
|
|
109
|
+
const msgRepOff = mx.getMsgRepOffset();
|
|
110
|
+
const mlMem = new Uint8Array(mx.memory.buffer);
|
|
111
|
+
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
112
|
+
const sha3OutOff = sx.getOutOffset();
|
|
113
|
+
// ── TS-side sensitive buffers, wiped in finally ─────────────────────
|
|
114
|
+
let mu;
|
|
115
|
+
let rhoPP;
|
|
116
|
+
let cTilde;
|
|
117
|
+
let w1Bytes;
|
|
118
|
+
try {
|
|
119
|
+
// ── skDecode, FIPS 204 §7.2 Algorithm 25 ────────────────────────
|
|
120
|
+
// sk = ρ ‖ K ‖ tr ‖ s₁ ‖ s₂ ‖ t₀ (offsets per Algorithm 24).
|
|
121
|
+
const rho = sk.subarray(0, 32);
|
|
122
|
+
const K = sk.subarray(32, 64);
|
|
123
|
+
const tr = sk.subarray(64, 128);
|
|
124
|
+
let off = 128;
|
|
125
|
+
// s₁, ℓ polynomials, BitUnpack(η, η) per Alg 25 line 4
|
|
126
|
+
for (let r = 0; r < l; r++) {
|
|
127
|
+
mlMem.set(sk.subarray(off, off + etaPolyBytes), xofOff);
|
|
128
|
+
mx.bit_unpack(slot0 + r * POLY_BYTES, xofOff, eta, eta);
|
|
129
|
+
off += etaPolyBytes;
|
|
130
|
+
}
|
|
131
|
+
// FIPS 204 §7.2 / Alg 25 line 5: s₁ coefficients must lie in [-η, η].
|
|
132
|
+
// bit_unpack at width bitlen(2η) overshoots that range for η ∈ {2,4}
|
|
133
|
+
// (η=2 yields [-5, 2]; η=4 yields [-11, 4]) when sk bytes are tampered
|
|
134
|
+
// or corrupted, reject before producing a wrong signature.
|
|
135
|
+
if (mx.polyvec_chknorm(slot0, eta + 1, l) !== 0)
|
|
136
|
+
throw new RangeError('leviathan-crypto: signing key s₁ coefficient out of [-η, η]');
|
|
137
|
+
// s₂, k polynomials, BitUnpack(η, η)
|
|
138
|
+
for (let r = 0; r < k; r++) {
|
|
139
|
+
mlMem.set(sk.subarray(off, off + etaPolyBytes), xofOff);
|
|
140
|
+
mx.bit_unpack(slot1 + r * POLY_BYTES, xofOff, eta, eta);
|
|
141
|
+
off += etaPolyBytes;
|
|
142
|
+
}
|
|
143
|
+
if (mx.polyvec_chknorm(slot1, eta + 1, k) !== 0)
|
|
144
|
+
throw new RangeError('leviathan-crypto: signing key s₂ coefficient out of [-η, η]');
|
|
145
|
+
// t₀, k polynomials, BitUnpack(2^(d-1)-1, 2^(d-1)), Alg 25 line 6
|
|
146
|
+
// bit_unpack(2^(d-1)-1, 2^(d-1)) decodes exactly to [-(2^(d-1)-1), 2^(d-1)]
|
|
147
|
+
// which matches the spec range, no caddq-style range check needed.
|
|
148
|
+
for (let r = 0; r < k; r++) {
|
|
149
|
+
mlMem.set(sk.subarray(off, off + t0PolyBytes), xofOff);
|
|
150
|
+
mx.bit_unpack(slot2 + r * POLY_BYTES, xofOff, t0LowEdge, t0HighEdge);
|
|
151
|
+
off += t0PolyBytes;
|
|
152
|
+
}
|
|
153
|
+
// ── FIPS 204 Alg 7 lines 2-5: ŝ₁ ← NTT(s₁); ŝ₂ ← NTT(s₂);
|
|
154
|
+
// t̂₀ ← NTT(t₀); Â ← ExpandA(ρ) ────
|
|
155
|
+
// Each NTT'd polyvec is then tomont'd so subsequent
|
|
156
|
+
// poly_pointwise_montgomery products with a regular-form ĉ yield
|
|
157
|
+
// regular (non-Montgomery) results, matches the keygen tomont
|
|
158
|
+
// pattern (src/ts/mldsa/keygen.ts step 4 (c)). Without tomont the
|
|
159
|
+
// post-invNTT values carry an extra R⁻¹ factor and ACVP fails.
|
|
160
|
+
mx.polyvec_ntt(slot0, l);
|
|
161
|
+
mx.polyvec_tomont(slot0, l);
|
|
162
|
+
mx.polyvec_ntt(slot1, k);
|
|
163
|
+
mx.polyvec_tomont(slot1, k);
|
|
164
|
+
mx.polyvec_ntt(slot2, k);
|
|
165
|
+
mx.polyvec_tomont(slot2, k);
|
|
166
|
+
expandA(mx, sx, params, rho, matOff);
|
|
167
|
+
// ── Line 6: μ ← H(BytesToBits(tr) ‖ M', 64) ───────────────────────
|
|
168
|
+
// Byte-oriented SHAKE wrapper: BytesToBits is a no-op (we absorb tr
|
|
169
|
+
// directly). ACVP gates byte-equality of μ implicitly through σ
|
|
170
|
+
// equality on the deterministic-rnd vectors.
|
|
171
|
+
mu = shake256HashConcat(sx, [tr, MPrime], 64);
|
|
172
|
+
// ── Line 7: ρ'' ← H(K ‖ rnd ‖ μ, 64) ──────────────────────────────
|
|
173
|
+
rhoPP = shake256HashConcat(sx, [K, rnd, mu], 64);
|
|
174
|
+
// ── Line 8: κ ← 0 ────────────────────────────────────────────────
|
|
175
|
+
let kappa = 0;
|
|
176
|
+
cTilde = new Uint8Array(lambdaOver4);
|
|
177
|
+
let success = false;
|
|
178
|
+
// ── Line 10-31: rejection-sampling loop ──────────────────────────
|
|
179
|
+
for (let iter = 0; iter < MAX_SIGN_ITERATIONS; iter++) {
|
|
180
|
+
// Line 11: y ← ExpandMask(ρ'', κ) at slot3 (time domain) ──────
|
|
181
|
+
expandMask(mx, sx, params, rhoPP, kappa, slot3);
|
|
182
|
+
// Line 12: w ← NTT⁻¹(Â · NTT(y))
|
|
183
|
+
// Copy y_time → slot4, NTT in place, matrix product → slot5,
|
|
184
|
+
// inverse NTT → time domain, caddq → canonical for HighBits.
|
|
185
|
+
mlMem.copyWithin(slot4, slot3, slot3 + l * POLY_BYTES);
|
|
186
|
+
mx.polyvec_ntt(slot4, l); // ŷ at slot4 (regular)
|
|
187
|
+
mx.polyvec_tomont(slot4, l); // ŷ ← ŷ·R so the matrix kernel's R⁻¹ leaves regular result
|
|
188
|
+
mx.polyvec_matrix_pointwise_montgomery(slot5, matOff, slot4, k, l);
|
|
189
|
+
mx.polyvec_invntt(slot5, k);
|
|
190
|
+
mx.polyvec_caddq(slot5, k); // w canonical at slot5
|
|
191
|
+
// Line 13: w₁ ← HighBits(w) at slot4 (overwrites ŷ, no longer needed)
|
|
192
|
+
mx.polyvec_highbits(slot4, slot5, k, gamma2);
|
|
193
|
+
// Line 14: c̃ ← H(μ ‖ w₁Encode(w₁), λ/4) ─────────────────────
|
|
194
|
+
// w₁Encode = Σ SimpleBitPack(w₁[i], (q-1)/(2γ₂) - 1), width per param set.
|
|
195
|
+
for (let r = 0; r < k; r++) {
|
|
196
|
+
mx.simple_bit_pack(xofOff + r * w1PolyBytes, slot4 + r * POLY_BYTES, w1Bitlen);
|
|
197
|
+
}
|
|
198
|
+
// w₁ from a rejected iteration is secret-derived (HighBits
|
|
199
|
+
// of NTT⁻¹(Â · NTT(y)); y came from ρ''). Wipe the prior
|
|
200
|
+
// slice before reslicing fresh bytes for this iteration.
|
|
201
|
+
if (w1Bytes)
|
|
202
|
+
wipe(w1Bytes);
|
|
203
|
+
w1Bytes = mlMem.slice(xofOff, xofOff + w1TotalBytes);
|
|
204
|
+
const cTildeNew = shake256HashConcat(sx, [mu, w1Bytes], lambdaOver4);
|
|
205
|
+
cTilde.set(cTildeNew);
|
|
206
|
+
// cTildeNew is public-derivable (c̃ ships inside σ) but TS-side
|
|
207
|
+
// hygiene matches the rest of the per-iteration scratch.
|
|
208
|
+
wipe(cTildeNew);
|
|
209
|
+
// Line 15: c ← SampleInBall(c̃) at polySlot1 (time domain) ──
|
|
210
|
+
// FIPS 204 Algorithm 29: SHAKE256(c̃) drives sign-bits (first
|
|
211
|
+
// 8 squeeze bytes) and position selection (subsequent bytes).
|
|
212
|
+
// Resumable kernel, squeeze further blocks until all τ samples
|
|
213
|
+
// land. polySlot0 is signs scratch; xofOff is position bytes.
|
|
214
|
+
mlMem.fill(0, polySlot1, polySlot1 + POLY_BYTES);
|
|
215
|
+
sx.shake256Init();
|
|
216
|
+
sha3Absorb(sx, cTilde);
|
|
217
|
+
sx.shakePad();
|
|
218
|
+
sx.shakeSqueezeBlock();
|
|
219
|
+
mlMem.set(sha3Mem.subarray(sha3OutOff, sha3OutOff + 8), polySlot0);
|
|
220
|
+
mlMem.set(sha3Mem.subarray(sha3OutOff + 8, sha3OutOff + SHAKE256_RATE), xofOff);
|
|
221
|
+
let sampleI = mx.sample_in_ball(polySlot1, polySlot0, xofOff, SHAKE256_RATE - 8, tau, 256 - tau);
|
|
222
|
+
while (sampleI < 256) {
|
|
223
|
+
sx.shakeSqueezeBlock();
|
|
224
|
+
mlMem.set(sha3Mem.subarray(sha3OutOff, sha3OutOff + SHAKE256_RATE), xofOff);
|
|
225
|
+
sampleI = mx.sample_in_ball(polySlot1, polySlot0, xofOff, SHAKE256_RATE, tau, sampleI);
|
|
226
|
+
}
|
|
227
|
+
// Line 16: ĉ ← NTT(c), single polynomial in place ─────────
|
|
228
|
+
mx.ntt(polySlot1);
|
|
229
|
+
// Line 17: ⟨cs₁⟩ ← NTT⁻¹(ĉ ∘ ŝ₁) at slot4 (overwrites w₁) ──
|
|
230
|
+
// The WASM polynomial layer exposes poly_pointwise_montgomery
|
|
231
|
+
// (per-poly) and polyvec_pointwise_montgomery (per-vec,
|
|
232
|
+
// expects same-length input). For c·s₁ where c is one
|
|
233
|
+
// polynomial and s₁ is a length-ℓ vec, drive a TS-side loop
|
|
234
|
+
// over the poly variant.
|
|
235
|
+
for (let r = 0; r < l; r++) {
|
|
236
|
+
mx.poly_pointwise_montgomery(slot4 + r * POLY_BYTES, polySlot1, slot0 + r * POLY_BYTES);
|
|
237
|
+
}
|
|
238
|
+
mx.polyvec_invntt(slot4, l);
|
|
239
|
+
// Line 19: z ← y + ⟨cs₁⟩ at slot4 ──────────────────────────
|
|
240
|
+
mx.polyvec_add(slot4, slot3, slot4, l);
|
|
241
|
+
// ‖z‖∞ check needs centered residues. polyvec_reduce produces
|
|
242
|
+
// (-q/2, q/2]; chknorm is correct on that form per the
|
|
243
|
+
// WASM polynomial-layer contract.
|
|
244
|
+
mx.polyvec_reduce(slot4, l);
|
|
245
|
+
// Line 21 (first half): ‖z‖∞ ≥ γ₁ − β ⇒ reject ─────────────
|
|
246
|
+
if (mx.polyvec_chknorm(slot4, gamma1 - beta, l) !== 0) {
|
|
247
|
+
kappa += l;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
// Line 18: ⟨cs₂⟩ ← NTT⁻¹(ĉ ∘ ŝ₂) at slot3 (overwrites y_time) ─
|
|
251
|
+
for (let r = 0; r < k; r++) {
|
|
252
|
+
mx.poly_pointwise_montgomery(slot3 + r * POLY_BYTES, polySlot1, slot1 + r * POLY_BYTES);
|
|
253
|
+
}
|
|
254
|
+
mx.polyvec_invntt(slot3, k);
|
|
255
|
+
mx.polyvec_reduce(slot3, k);
|
|
256
|
+
mx.polyvec_caddq(slot3, k); // canonical for sub→reduce→caddq round-trip
|
|
257
|
+
// Line 20: r₀ ← LowBits(w − ⟨cs₂⟩). Compute (w − ⟨cs₂⟩) at
|
|
258
|
+
// slot5 (overwrites w, we no longer need w independently;
|
|
259
|
+
// downstream both r₀ and h derive from w − ⟨cs₂⟩).
|
|
260
|
+
mx.polyvec_sub(slot5, slot5, slot3, k);
|
|
261
|
+
mx.polyvec_reduce(slot5, k);
|
|
262
|
+
mx.polyvec_caddq(slot5, k); // canonical for lowbits + make_hint
|
|
263
|
+
// r₀ at slot3 (overwrites ⟨cs₂⟩, no longer needed)
|
|
264
|
+
mx.polyvec_lowbits(slot3, slot5, k, gamma2);
|
|
265
|
+
// Line 21 (second half): ‖r₀‖∞ ≥ γ₂ − β ⇒ reject ──────────
|
|
266
|
+
if (mx.polyvec_chknorm(slot3, gamma2 - beta, k) !== 0) {
|
|
267
|
+
kappa += l;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
// Line 24: ⟨ct₀⟩ ← NTT⁻¹(ĉ ∘ t̂₀) at slot3 (overwrites r₀) ─
|
|
271
|
+
for (let r = 0; r < k; r++) {
|
|
272
|
+
mx.poly_pointwise_montgomery(slot3 + r * POLY_BYTES, polySlot1, slot2 + r * POLY_BYTES);
|
|
273
|
+
}
|
|
274
|
+
mx.polyvec_invntt(slot3, k);
|
|
275
|
+
mx.polyvec_reduce(slot3, k); // centered for chknorm
|
|
276
|
+
// Line 26 (first half): ‖⟨ct₀⟩‖∞ ≥ γ₂ ⇒ reject. Note the
|
|
277
|
+
// bound is γ₂ (NOT γ₂ − β), the missing β subtract is a
|
|
278
|
+
// common copy/paste error.
|
|
279
|
+
if (mx.polyvec_chknorm(slot3, gamma2, k) !== 0) {
|
|
280
|
+
kappa += l;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
mx.polyvec_caddq(slot3, k); // canonical for make_hint z input
|
|
284
|
+
// Line 25: h ← MakeHint(−⟨ct₀⟩, w − ⟨cs₂⟩ + ⟨ct₀⟩).
|
|
285
|
+
// MakeHint(z, r) = [HighBits(r) ≠ HighBits(r + z)] is symmetric
|
|
286
|
+
// in (r, r+z), so passing z = ⟨ct₀⟩ and r = w − ⟨cs₂⟩ produces
|
|
287
|
+
// h[i] = [HighBits(w − ⟨cs₂⟩) ≠ HighBits(w − ⟨cs₂⟩ + ⟨ct₀⟩)],
|
|
288
|
+
// the same predicate. Avoids the explicit −⟨ct₀⟩ canonicalise.
|
|
289
|
+
// polyvec_make_hint aliases-safe with z; h overwrites slot3.
|
|
290
|
+
const popcount = mx.polyvec_make_hint(slot3, slot3, slot5, k, gamma2);
|
|
291
|
+
// Line 26 (second half): popcount > ω ⇒ reject ─────────────
|
|
292
|
+
if (popcount > omega) {
|
|
293
|
+
kappa += l;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
// All four hard checks passed, encode signature.
|
|
297
|
+
// Line 32: σ ← sigEncode(c̃, z mod± q, h), Algorithm 26.
|
|
298
|
+
mlMem.set(cTilde, sigOff); // c̃ (λ/4 bytes)
|
|
299
|
+
const sigZOff = sigOff + lambdaOver4;
|
|
300
|
+
for (let r = 0; r < l; r++) {
|
|
301
|
+
// z is in centered residues post polyvec_reduce; bit_pack(γ₁-1, γ₁)
|
|
302
|
+
// expects [-(γ₁-1), γ₁]. ‖z‖∞ < γ₁ - β guarantees range.
|
|
303
|
+
mx.bit_pack(sigZOff + r * zPolyBytes, slot4 + r * POLY_BYTES, gamma1 - 1, gamma1);
|
|
304
|
+
}
|
|
305
|
+
mx.hint_bit_pack(sigZOff + l * zPolyBytes, slot3, k, omega);
|
|
306
|
+
success = true;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
if (!success) {
|
|
310
|
+
throw new Error(`leviathan-crypto: ML-DSA signing exceeded ${MAX_SIGN_ITERATIONS} rejection-sample iterations`
|
|
311
|
+
+ ' (FIPS 204 Appendix C suggests min 814; bound here gives ~20% headroom)');
|
|
312
|
+
}
|
|
313
|
+
// Slice σ before any wipes, slice copies, so wipes after this
|
|
314
|
+
// don't touch the returned Uint8Array.
|
|
315
|
+
const sig = mlMem.slice(sigOff, sigOff + sigBytes);
|
|
316
|
+
return sig;
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
// Wipe scratch per FIPS 204 §3.6.3. Runs in finally so success
|
|
320
|
+
// and early-throw paths both clear. See
|
|
321
|
+
// docs/mldsa.md#wipe-discipline.
|
|
322
|
+
mlMem.fill(0, mx.getPolyvecSlotBase(), mx.getPolyvecSlotBase() + 6 * mx.getPolyvecSlotSize());
|
|
323
|
+
// Poly slots: signs (POLY_SLOT_0, public, c̃ derived), c (POLY_SLOT_1,
|
|
324
|
+
// public, derivable from c̃), and POLY_SLOT_7 holding the last
|
|
325
|
+
// matrix-vector product partial (secret-derived via y_ntt). Wipe all
|
|
326
|
+
// 8 contiguous slots in one fill, cheap and avoids residue gaps.
|
|
327
|
+
mlMem.fill(0, polySlotBase, polySlotBase + 8 * POLY_BYTES);
|
|
328
|
+
// XOF/PRF region last held SampleInBall position bytes (public, c̃-derived)
|
|
329
|
+
// or ExpandMask outputs (secret, ρ''-derived) on the rejected path.
|
|
330
|
+
mlMem.fill(0, xofOff, xofOff + 8192);
|
|
331
|
+
// Public-derivable but cheap to wipe for hygiene:
|
|
332
|
+
mlMem.fill(0, cTildeOff, cTildeOff + 64);
|
|
333
|
+
mlMem.fill(0, msgRepOff, msgRepOff + 64);
|
|
334
|
+
// Defensive: SEED, TR, SK regions, Sign_internal does not write
|
|
335
|
+
// to these (we extract sk components via TS subarrays), but a
|
|
336
|
+
// prior op may have left residue. Cheap wipe closes that window.
|
|
337
|
+
mlMem.fill(0, seedOff, seedOff + 128);
|
|
338
|
+
mlMem.fill(0, trOff, trOff + 64);
|
|
339
|
+
mlMem.fill(0, skOff, skOff + params.skBytes);
|
|
340
|
+
// SHA3 module: STATE / INPUT / OUT all carried K, μ, ρ'', c̃, and
|
|
341
|
+
// the SampleInBall stream. Wipe before returning.
|
|
342
|
+
sx.wipeBuffers();
|
|
343
|
+
// TS-side scratch, wipe last so any wipe()-throws don't skip
|
|
344
|
+
// the WASM cleanup above.
|
|
345
|
+
if (mu)
|
|
346
|
+
wipe(mu);
|
|
347
|
+
if (rhoPP)
|
|
348
|
+
wipe(rhoPP);
|
|
349
|
+
if (cTilde)
|
|
350
|
+
wipe(cTilde);
|
|
351
|
+
if (w1Bytes)
|
|
352
|
+
wipe(w1Bytes);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* HashML-DSA sign, post-prehash. FIPS 204 §5.4 Algorithm 4 lines 22-24.
|
|
357
|
+
* Builds M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(algo) ‖ prehash and drives
|
|
358
|
+
* Sign_internal with the caller-supplied rnd.
|
|
359
|
+
*
|
|
360
|
+
* The caller owns `prehash` (it may be a slice of WASM memory, a
|
|
361
|
+
* caller-controlled digest, or an internally-computed PH_M); this helper
|
|
362
|
+
* never wipes it. The caller also owns `rnd` (hedged: caller wipes a
|
|
363
|
+
* freshly-generated value; deterministic: rnd is zeros, no wipe; derand:
|
|
364
|
+
* caller-supplied per FIPS 204 §3.4 contract).
|
|
365
|
+
*
|
|
366
|
+
* The 12 approved pre-hash functions (`algo`) and their OIDs are FIPS 204
|
|
367
|
+
* §5.4.1's full catalog. Domain-sep byte 0x01 inside the M' construction
|
|
368
|
+
* separates HashML-DSA signatures from pure-ML-DSA signatures on the same
|
|
369
|
+
* key per §3.6.4.
|
|
370
|
+
*/
|
|
371
|
+
export function signWithPrehash(mx, sx, params, sk, prehash, algo, ctx, rnd) {
|
|
372
|
+
const oid = getOid(algo);
|
|
373
|
+
const MPrime = constructMPrimeHash(ctx, oid, prehash);
|
|
374
|
+
try {
|
|
375
|
+
return mldsaSignInternal(mx, sx, params, sk, MPrime, rnd);
|
|
376
|
+
}
|
|
377
|
+
finally {
|
|
378
|
+
wipe(MPrime);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export interface MlDsaExports {
|
|
2
|
+
memory: WebAssembly.Memory;
|
|
3
|
+
getModuleId: () => number;
|
|
4
|
+
getMemoryPages: () => number;
|
|
5
|
+
getPolySlotBase: () => number;
|
|
6
|
+
getPolySlotSize: () => number;
|
|
7
|
+
getPolySlot0: () => number;
|
|
8
|
+
getPolySlot1: () => number;
|
|
9
|
+
getPolySlot2: () => number;
|
|
10
|
+
getPolySlot3: () => number;
|
|
11
|
+
getPolySlot4: () => number;
|
|
12
|
+
getPolySlot5: () => number;
|
|
13
|
+
getPolySlot6: () => number;
|
|
14
|
+
getPolySlot7: () => number;
|
|
15
|
+
getMatrixSlot: () => number;
|
|
16
|
+
getMatrixSlotSize: () => number;
|
|
17
|
+
getPolyvecSlotBase: () => number;
|
|
18
|
+
getPolyvecSlotSize: () => number;
|
|
19
|
+
getPolyvecSlot0: () => number;
|
|
20
|
+
getPolyvecSlot1: () => number;
|
|
21
|
+
getPolyvecSlot2: () => number;
|
|
22
|
+
getPolyvecSlot3: () => number;
|
|
23
|
+
getPolyvecSlot4: () => number;
|
|
24
|
+
getPolyvecSlot5: () => number;
|
|
25
|
+
getPolyvecSlot6: () => number;
|
|
26
|
+
getPolyvecSlot7: () => number;
|
|
27
|
+
getSeedOffset: () => number;
|
|
28
|
+
getTrOffset: () => number;
|
|
29
|
+
getMsgRepOffset: () => number;
|
|
30
|
+
getCTildeOffset: () => number;
|
|
31
|
+
getPkOffset: () => number;
|
|
32
|
+
getSkOffset: () => number;
|
|
33
|
+
getSigOffset: () => number;
|
|
34
|
+
getXofPrfOffset: () => number;
|
|
35
|
+
wipeBuffers: () => void;
|
|
36
|
+
montgomery_reduce: (a: bigint) => number;
|
|
37
|
+
barrett_reduce: (a: number) => number;
|
|
38
|
+
fqmul: (a: number, b: number) => number;
|
|
39
|
+
getZetasOffset: () => number;
|
|
40
|
+
getZeta: (i: number) => number;
|
|
41
|
+
BitRev8: (m: number) => number;
|
|
42
|
+
ntt: (polyOff: number) => void;
|
|
43
|
+
invntt: (polyOff: number) => void;
|
|
44
|
+
poly_add: (rOff: number, aOff: number, bOff: number) => void;
|
|
45
|
+
poly_sub: (rOff: number, aOff: number, bOff: number) => void;
|
|
46
|
+
poly_reduce: (polyOff: number) => void;
|
|
47
|
+
poly_caddq: (polyOff: number) => void;
|
|
48
|
+
poly_pointwise_montgomery: (rOff: number, aOff: number, bOff: number) => void;
|
|
49
|
+
poly_freeze: (polyOff: number) => void;
|
|
50
|
+
poly_chknorm: (polyOff: number, bound: number) => number;
|
|
51
|
+
poly_tomont: (polyOff: number) => void;
|
|
52
|
+
simple_bit_pack: (rByteOff: number, polyOff: number, bitlen: number) => void;
|
|
53
|
+
bit_pack: (rByteOff: number, polyOff: number, a: number, b: number) => void;
|
|
54
|
+
simple_bit_unpack: (polyOff: number, vByteOff: number, bitlen: number) => void;
|
|
55
|
+
bit_unpack: (polyOff: number, vByteOff: number, a: number, b: number) => void;
|
|
56
|
+
hint_bit_pack: (rByteOff: number, hPvOff: number, k: number, omega: number) => void;
|
|
57
|
+
hint_bit_unpack: (hPvOff: number, vByteOff: number, k: number, omega: number) => number;
|
|
58
|
+
power2round: (r1Off: number, r0Off: number, aOff: number) => void;
|
|
59
|
+
decompose: (r1Off: number, r0Off: number, aOff: number, gamma2: number) => void;
|
|
60
|
+
highbits: (rOff: number, aOff: number, gamma2: number) => void;
|
|
61
|
+
lowbits: (rOff: number, aOff: number, gamma2: number) => void;
|
|
62
|
+
make_hint: (hOff: number, zOff: number, rOff: number, gamma2: number) => void;
|
|
63
|
+
use_hint: (rOff: number, hOff: number, aOff: number, gamma2: number) => void;
|
|
64
|
+
polyvec_add: (rOff: number, aOff: number, bOff: number, len: number) => void;
|
|
65
|
+
polyvec_sub: (rOff: number, aOff: number, bOff: number, len: number) => void;
|
|
66
|
+
polyvec_reduce: (pvOff: number, len: number) => void;
|
|
67
|
+
polyvec_caddq: (pvOff: number, len: number) => void;
|
|
68
|
+
polyvec_freeze: (pvOff: number, len: number) => void;
|
|
69
|
+
polyvec_tomont: (pvOff: number, len: number) => void;
|
|
70
|
+
polyvec_ntt: (pvOff: number, len: number) => void;
|
|
71
|
+
polyvec_invntt: (pvOff: number, len: number) => void;
|
|
72
|
+
polyvec_pointwise_montgomery: (rOff: number, aOff: number, bOff: number, len: number) => void;
|
|
73
|
+
polyvec_pointwise_acc_montgomery: (rPolyOff: number, aPvOff: number, bPvOff: number, len: number) => void;
|
|
74
|
+
polyvec_matrix_pointwise_montgomery: (rPvOff: number, matOff: number, vPvOff: number, k: number, l: number) => void;
|
|
75
|
+
polyvec_chknorm: (pvOff: number, bound: number, len: number) => number;
|
|
76
|
+
polyvec_power2round: (r1pvOff: number, r0pvOff: number, aPvOff: number, len: number) => void;
|
|
77
|
+
polyvec_decompose: (r1pvOff: number, r0pvOff: number, aPvOff: number, len: number, gamma2: number) => void;
|
|
78
|
+
polyvec_highbits: (rPvOff: number, aPvOff: number, len: number, gamma2: number) => void;
|
|
79
|
+
polyvec_lowbits: (rPvOff: number, aPvOff: number, len: number, gamma2: number) => void;
|
|
80
|
+
polyvec_make_hint: (hPvOff: number, zPvOff: number, rPvOff: number, len: number, gamma2: number) => number;
|
|
81
|
+
polyvec_use_hint: (rPvOff: number, hPvOff: number, aPvOff: number, len: number, gamma2: number) => void;
|
|
82
|
+
rej_ntt_poly: (polyOff: number, ctrStart: number, bufOff: number, bufLen: number) => number;
|
|
83
|
+
rej_bounded_poly: (polyOff: number, ctrStart: number, bufOff: number, bufLen: number, eta: number) => number;
|
|
84
|
+
sample_in_ball: (polyOff: number, signsOff: number, posBytesOff: number, posBytesLen: number, tau: number, startI: number) => number;
|
|
85
|
+
}
|
|
86
|
+
export type { Sha3Exports } from '../mlkem/types.js';
|
|
87
|
+
/** ML-DSA key pair returned by keygen / keygenDerand. */
|
|
88
|
+
export interface MlDsaKeyPair {
|
|
89
|
+
verificationKey: Uint8Array;
|
|
90
|
+
signingKey: Uint8Array;
|
|
91
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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/types.ts
|
|
23
|
+
//
|
|
24
|
+
// ML-DSA type definitions: WASM export interfaces and signature API types.
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { MlDsaParams } from './params.js';
|
|
2
|
+
import { type PreHashAlgorithm } from './hashvariant.js';
|
|
3
|
+
/**
|
|
4
|
+
* FIPS 204 §5.2 / §5.3 line 1, ctx must be ≤ 255 bytes (the byte that
|
|
5
|
+
* follows the domain separator in M' is ctx.length, so longer values
|
|
6
|
+
* cannot be encoded).
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateContext(ctx: Uint8Array): void;
|
|
9
|
+
/**
|
|
10
|
+
* FIPS 204 §3.6.2, verification key must be exactly pkBytes long for
|
|
11
|
+
* its parameter set. Throws here; the public verify() method catches
|
|
12
|
+
* the throw and returns false so wrong-length pk reads as "not a valid
|
|
13
|
+
* signature" rather than a caller error.
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateVerificationKey(vk: Uint8Array, params: MlDsaParams): void;
|
|
16
|
+
/**
|
|
17
|
+
* Signing key must be exactly skBytes long for its parameter set. Wrong
|
|
18
|
+
* length is a caller error (the caller produced this sk via keygen* or
|
|
19
|
+
* loaded it from storage they own); throw RangeError unconditionally.
|
|
20
|
+
*/
|
|
21
|
+
export declare function validateSigningKey(sk: Uint8Array, params: MlDsaParams): void;
|
|
22
|
+
/**
|
|
23
|
+
* FIPS 204 §3.6.2, signature must be exactly sigBytes long for its
|
|
24
|
+
* parameter set. Throws here; the public verify() method catches and
|
|
25
|
+
* returns false (same protocol shape as wrong-length pk).
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateSignature(sig: Uint8Array, params: MlDsaParams): void;
|
|
28
|
+
/**
|
|
29
|
+
* FIPS 204 Algorithm 7 line 1, rnd must be 32 bytes. Used by signDerand
|
|
30
|
+
* (the testing/CAVP API). Hedged sign supplies rnd internally; deterministic
|
|
31
|
+
* sign uses zeros; only signDerand exposes rnd to the caller.
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateRnd(rnd: Uint8Array): void;
|
|
34
|
+
/**
|
|
35
|
+
* Confirms M is a Uint8Array. FIPS 204 places no length restriction on
|
|
36
|
+
* the message, M is absorbed into a SHAKE256 sponge (μ derivation,
|
|
37
|
+
* §6.2/§6.3) so any byte length is admissible. The bit-vs-byte
|
|
38
|
+
* distinction visible in §5.2/§5.3 (BytesToBits in M' construction)
|
|
39
|
+
* collapses at the byte boundary inside our SHAKE wrapper.
|
|
40
|
+
*/
|
|
41
|
+
export declare function validateMessage(M: Uint8Array): void;
|
|
42
|
+
/**
|
|
43
|
+
* FIPS 204 §5.4.1, the prehash PH_M passed to HashML-DSA Sign_internal /
|
|
44
|
+
* Verify_internal must be exactly the digest size of `algo`. Used by the
|
|
45
|
+
* `*Prehashed` family where the caller computes PH externally; the
|
|
46
|
+
* non-prehashed family produces PH internally so this check is implicit.
|
|
47
|
+
*
|
|
48
|
+
* Throws `SigningError('sig-malformed-input')` on mismatch. The verify
|
|
49
|
+
* surface intercepts this to return false (a wrong-size digest is a
|
|
50
|
+
* structural mismatch, indistinguishable from a wrong signature), while
|
|
51
|
+
* the sign surface lets the throw propagate (the caller supplied bad
|
|
52
|
+
* input, that is a contract violation per the FIPS 204 §5.4 input
|
|
53
|
+
* contract).
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateDigest(digest: Uint8Array, algo: PreHashAlgorithm): void;
|