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,125 @@
|
|
|
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/validate.ts
|
|
23
|
+
//
|
|
24
|
+
// ML-DSA caller-side input validation. Pure length / type checks, no
|
|
25
|
+
// content validation here. Semantic content validity (e.g. malformed hint
|
|
26
|
+
// encoding) is handled inside Verify_internal via hint_bit_unpack's -1
|
|
27
|
+
// sentinel (FIPS 204 Algorithm 21 / §D.3).
|
|
28
|
+
//
|
|
29
|
+
// Length checks throw RangeError. The public verify() method intercepts
|
|
30
|
+
// pk/sig length mismatches and returns false instead of propagating the
|
|
31
|
+
// throw, that is FIPS 204 §3.6.2 protocol shape, not a contract violation.
|
|
32
|
+
// validateContext, validateSigningKey, validateRnd ARE contract violations
|
|
33
|
+
// and propagate the throw.
|
|
34
|
+
import { SigningError } from '../errors.js';
|
|
35
|
+
import { digestSize } from './hashvariant.js';
|
|
36
|
+
/**
|
|
37
|
+
* FIPS 204 §5.2 / §5.3 line 1, ctx must be ≤ 255 bytes (the byte that
|
|
38
|
+
* follows the domain separator in M' is ctx.length, so longer values
|
|
39
|
+
* cannot be encoded).
|
|
40
|
+
*/
|
|
41
|
+
export function validateContext(ctx) {
|
|
42
|
+
if (!(ctx instanceof Uint8Array))
|
|
43
|
+
throw new TypeError('leviathan-crypto: ctx must be a Uint8Array');
|
|
44
|
+
if (ctx.length > 255)
|
|
45
|
+
throw new RangeError(`leviathan-crypto: ctx must be ≤ 255 bytes (got ${ctx.length})`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* FIPS 204 §3.6.2, verification key must be exactly pkBytes long for
|
|
49
|
+
* its parameter set. Throws here; the public verify() method catches
|
|
50
|
+
* the throw and returns false so wrong-length pk reads as "not a valid
|
|
51
|
+
* signature" rather than a caller error.
|
|
52
|
+
*/
|
|
53
|
+
export function validateVerificationKey(vk, params) {
|
|
54
|
+
if (!(vk instanceof Uint8Array))
|
|
55
|
+
throw new TypeError('leviathan-crypto: verification key must be a Uint8Array');
|
|
56
|
+
if (vk.length !== params.pkBytes)
|
|
57
|
+
throw new RangeError(`leviathan-crypto: verification key must be ${params.pkBytes} bytes for ${params.paramSet} `
|
|
58
|
+
+ `(got ${vk.length})`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Signing key must be exactly skBytes long for its parameter set. Wrong
|
|
62
|
+
* length is a caller error (the caller produced this sk via keygen* or
|
|
63
|
+
* loaded it from storage they own); throw RangeError unconditionally.
|
|
64
|
+
*/
|
|
65
|
+
export function validateSigningKey(sk, params) {
|
|
66
|
+
if (!(sk instanceof Uint8Array))
|
|
67
|
+
throw new TypeError('leviathan-crypto: signing key must be a Uint8Array');
|
|
68
|
+
if (sk.length !== params.skBytes)
|
|
69
|
+
throw new RangeError(`leviathan-crypto: signing key must be ${params.skBytes} bytes for ${params.paramSet} `
|
|
70
|
+
+ `(got ${sk.length})`);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* FIPS 204 §3.6.2, signature must be exactly sigBytes long for its
|
|
74
|
+
* parameter set. Throws here; the public verify() method catches and
|
|
75
|
+
* returns false (same protocol shape as wrong-length pk).
|
|
76
|
+
*/
|
|
77
|
+
export function validateSignature(sig, params) {
|
|
78
|
+
if (!(sig instanceof Uint8Array))
|
|
79
|
+
throw new TypeError('leviathan-crypto: signature must be a Uint8Array');
|
|
80
|
+
if (sig.length !== params.sigBytes)
|
|
81
|
+
throw new RangeError(`leviathan-crypto: signature must be ${params.sigBytes} bytes for ${params.paramSet} `
|
|
82
|
+
+ `(got ${sig.length})`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* FIPS 204 Algorithm 7 line 1, rnd must be 32 bytes. Used by signDerand
|
|
86
|
+
* (the testing/CAVP API). Hedged sign supplies rnd internally; deterministic
|
|
87
|
+
* sign uses zeros; only signDerand exposes rnd to the caller.
|
|
88
|
+
*/
|
|
89
|
+
export function validateRnd(rnd) {
|
|
90
|
+
if (!(rnd instanceof Uint8Array))
|
|
91
|
+
throw new TypeError('leviathan-crypto: rnd must be a Uint8Array');
|
|
92
|
+
if (rnd.length !== 32)
|
|
93
|
+
throw new RangeError(`leviathan-crypto: rnd must be 32 bytes (got ${rnd.length})`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Confirms M is a Uint8Array. FIPS 204 places no length restriction on
|
|
97
|
+
* the message, M is absorbed into a SHAKE256 sponge (μ derivation,
|
|
98
|
+
* §6.2/§6.3) so any byte length is admissible. The bit-vs-byte
|
|
99
|
+
* distinction visible in §5.2/§5.3 (BytesToBits in M' construction)
|
|
100
|
+
* collapses at the byte boundary inside our SHAKE wrapper.
|
|
101
|
+
*/
|
|
102
|
+
export function validateMessage(M) {
|
|
103
|
+
if (!(M instanceof Uint8Array))
|
|
104
|
+
throw new TypeError('leviathan-crypto: message must be a Uint8Array');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* FIPS 204 §5.4.1, the prehash PH_M passed to HashML-DSA Sign_internal /
|
|
108
|
+
* Verify_internal must be exactly the digest size of `algo`. Used by the
|
|
109
|
+
* `*Prehashed` family where the caller computes PH externally; the
|
|
110
|
+
* non-prehashed family produces PH internally so this check is implicit.
|
|
111
|
+
*
|
|
112
|
+
* Throws `SigningError('sig-malformed-input')` on mismatch. The verify
|
|
113
|
+
* surface intercepts this to return false (a wrong-size digest is a
|
|
114
|
+
* structural mismatch, indistinguishable from a wrong signature), while
|
|
115
|
+
* the sign surface lets the throw propagate (the caller supplied bad
|
|
116
|
+
* input, that is a contract violation per the FIPS 204 §5.4 input
|
|
117
|
+
* contract).
|
|
118
|
+
*/
|
|
119
|
+
export function validateDigest(digest, algo) {
|
|
120
|
+
if (!(digest instanceof Uint8Array))
|
|
121
|
+
throw new SigningError('sig-malformed-input', 'digest must be a Uint8Array');
|
|
122
|
+
const expected = digestSize(algo);
|
|
123
|
+
if (digest.length !== expected)
|
|
124
|
+
throw new SigningError('sig-malformed-input', `digest length ${digest.length} != ${expected} for ${algo}`);
|
|
125
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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.Verify_internal, FIPS 204 Algorithm 8.
|
|
6
|
+
*
|
|
7
|
+
* Inputs (all caller-validated for length by the public verify() method):
|
|
8
|
+
* vk , encoded verification key (pkBytes)
|
|
9
|
+
* MPrime, domain-separated message bytes
|
|
10
|
+
* sig , encoded signature (sigBytes)
|
|
11
|
+
*
|
|
12
|
+
* Returns: boolean.
|
|
13
|
+
* true , signature authenticates the message under vk.
|
|
14
|
+
* false, wrong sig, malformed hint encoding, or norm check failure.
|
|
15
|
+
*/
|
|
16
|
+
export declare function mldsaVerifyInternal(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, vk: Uint8Array, MPrime: Uint8Array, sig: Uint8Array): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* HashML-DSA verify, post-prehash. FIPS 204 §5.4 Algorithm 5 lines 17-19.
|
|
19
|
+
* Builds M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(algo) ‖ prehash and drives
|
|
20
|
+
* Verify_internal.
|
|
21
|
+
*
|
|
22
|
+
* Same return / throw posture as `mldsaVerifyInternal`: returns a pure
|
|
23
|
+
* boolean for every signature outcome. Caller (in index.ts) is expected
|
|
24
|
+
* to have already filtered wrong-length vk / sig / digest with the
|
|
25
|
+
* appropriate verdict (false) before calling this helper.
|
|
26
|
+
*
|
|
27
|
+
* The caller owns `prehash`; this helper never wipes it.
|
|
28
|
+
*/
|
|
29
|
+
export declare function verifyWithPrehash(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, vk: Uint8Array, prehash: Uint8Array, sig: Uint8Array, algo: PreHashAlgorithm, ctx: Uint8Array): boolean;
|
|
@@ -0,0 +1,269 @@
|
|
|
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/verify.ts
|
|
23
|
+
//
|
|
24
|
+
// FIPS 204 §6.3 Algorithm 8, ML-DSA.Verify_internal.
|
|
25
|
+
//
|
|
26
|
+
// Verify is a pure boolean predicate: returns true iff the signature
|
|
27
|
+
// passes the FIPS 204 norm check (‖z‖∞ < γ₁ − β) AND the constant-time
|
|
28
|
+
// comparison of the recomputed c̃' against the σ-supplied c̃. There is
|
|
29
|
+
// no analog to the FO transform's implicit-rejection branch (ML-KEM's
|
|
30
|
+
// `return pseudorandom shared secret on failure`); ML-DSA verification
|
|
31
|
+
// is binary.
|
|
32
|
+
//
|
|
33
|
+
// SUF-CMA-critical preconditions enforced here:
|
|
34
|
+
// 1. Length check on pk and σ, caller (verify() in index.ts) returns
|
|
35
|
+
// false on mismatch BEFORE invoking this internal function, per
|
|
36
|
+
// FIPS 204 §3.6.2.
|
|
37
|
+
// 2. HintBitUnpack-malformed → false (FIPS 204 §D.3 / Algorithm 21
|
|
38
|
+
// lines 4, 9, 17). The kernel returns -1; we propagate as false.
|
|
39
|
+
// 3. Constant-time c̃ ↔ c̃' comparison via leviathan-crypto's
|
|
40
|
+
// `constantTimeEqual` (SIMD WASM XOR-accumulate, no early-exit).
|
|
41
|
+
//
|
|
42
|
+
// Slot allocation:
|
|
43
|
+
// POLYVEC_SLOT_0 t₁ → t₁·2^d (regular) → t̂₁·2^d (regular) → tomont
|
|
44
|
+
// POLYVEC_SLOT_1 z (time-domain) → ẑ (regular) → ẑ tomont
|
|
45
|
+
// POLYVEC_SLOT_2 h (hint polyvec; alive through use_hint)
|
|
46
|
+
// POLYVEC_SLOT_3 Â · ẑ → w'_approx → w'₁ (in place via use_hint aliasing)
|
|
47
|
+
// POLYVEC_SLOT_4 ĉ · t̂₁·2^d intermediate
|
|
48
|
+
// POLY_SLOT_0 signs (8 bytes, sample_in_ball signsOff)
|
|
49
|
+
// POLY_SLOT_1 c → ĉ
|
|
50
|
+
// POLY_SLOT_7 reserved scratch for polyvec_pointwise_acc_montgomery
|
|
51
|
+
import { constantTimeEqual, wipe } from '../utils.js';
|
|
52
|
+
import { sha3Absorb, shake256Hash, shake256HashConcat } from './sha3-helpers.js';
|
|
53
|
+
import { expandA } from './expand.js';
|
|
54
|
+
import { getOid } from './hashvariant.js';
|
|
55
|
+
import { constructMPrimeHash } from './format.js';
|
|
56
|
+
const POLY_BYTES = 1024;
|
|
57
|
+
const D = 13;
|
|
58
|
+
const Q = 8380417;
|
|
59
|
+
const SHAKE256_RATE = 136;
|
|
60
|
+
function bitlen(n) {
|
|
61
|
+
let b = 0;
|
|
62
|
+
let x = n;
|
|
63
|
+
while (x > 0) {
|
|
64
|
+
b++;
|
|
65
|
+
x >>>= 1;
|
|
66
|
+
}
|
|
67
|
+
return b;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* ML-DSA.Verify_internal, FIPS 204 Algorithm 8.
|
|
71
|
+
*
|
|
72
|
+
* Inputs (all caller-validated for length by the public verify() method):
|
|
73
|
+
* vk , encoded verification key (pkBytes)
|
|
74
|
+
* MPrime, domain-separated message bytes
|
|
75
|
+
* sig , encoded signature (sigBytes)
|
|
76
|
+
*
|
|
77
|
+
* Returns: boolean.
|
|
78
|
+
* true , signature authenticates the message under vk.
|
|
79
|
+
* false, wrong sig, malformed hint encoding, or norm check failure.
|
|
80
|
+
*/
|
|
81
|
+
export function mldsaVerifyInternal(mx, sx, params, vk, MPrime, sig) {
|
|
82
|
+
const { k, l, tau, lambda, gamma1, gamma2, beta, omega } = params;
|
|
83
|
+
const lambdaOver4 = lambda >>> 2; // 32 / 48 / 64
|
|
84
|
+
const c = 1 + bitlen(gamma1 - 1); // 18 or 20
|
|
85
|
+
const zPolyBytes = 32 * c;
|
|
86
|
+
const t1Bitlen = bitlen(Q - 1) - D; // 23 − 13 = 10
|
|
87
|
+
const t1PolyBytes = (256 * t1Bitlen) >> 3; // 320
|
|
88
|
+
const w1Bitlen = bitlen(((Q - 1) / (2 * gamma2)) - 1);
|
|
89
|
+
const w1PolyBytes = (256 * w1Bitlen) >> 3;
|
|
90
|
+
const w1TotalBytes = k * w1PolyBytes;
|
|
91
|
+
const t1ScalarShift = D; // multiply t₁ by 2^d in time domain
|
|
92
|
+
// ── WASM offsets ─────────────────────────────────────────────────────
|
|
93
|
+
const matOff = mx.getMatrixSlot();
|
|
94
|
+
const slot0 = mx.getPolyvecSlot0(); // t₁ then t̂₁·2^d (tomont)
|
|
95
|
+
const slot1 = mx.getPolyvecSlot1(); // z then ẑ (tomont)
|
|
96
|
+
const slot2 = mx.getPolyvecSlot2(); // h
|
|
97
|
+
const slot3 = mx.getPolyvecSlot3(); // ·ẑ → w'_approx → w'₁
|
|
98
|
+
const slot4 = mx.getPolyvecSlot4(); // ĉ·t̂₁·2^d
|
|
99
|
+
const polySlotBase = mx.getPolySlotBase();
|
|
100
|
+
const polySlot0 = mx.getPolySlot0(); // signs
|
|
101
|
+
const polySlot1 = mx.getPolySlot1(); // c → ĉ
|
|
102
|
+
const xofOff = mx.getXofPrfOffset();
|
|
103
|
+
const mlMem = new Uint8Array(mx.memory.buffer);
|
|
104
|
+
const sha3Mem = new Uint8Array(sx.memory.buffer);
|
|
105
|
+
const sha3OutOff = sx.getOutOffset();
|
|
106
|
+
// Public-derivable but wipe in finally for hygiene + symmetry with sign.
|
|
107
|
+
let mu;
|
|
108
|
+
let tr;
|
|
109
|
+
let cTilde;
|
|
110
|
+
let cTildeNew;
|
|
111
|
+
let w1Bytes;
|
|
112
|
+
try {
|
|
113
|
+
// ── Step 1: pkDecode (FIPS 204 §7.2 Algorithm 23) ────────────────
|
|
114
|
+
const rho = vk.subarray(0, 32);
|
|
115
|
+
for (let r = 0; r < k; r++) {
|
|
116
|
+
const srcOff = 32 + r * t1PolyBytes;
|
|
117
|
+
mlMem.set(vk.subarray(srcOff, srcOff + t1PolyBytes), xofOff);
|
|
118
|
+
mx.simple_bit_unpack(slot0 + r * POLY_BYTES, xofOff, t1Bitlen);
|
|
119
|
+
}
|
|
120
|
+
// ── Step 2: sigDecode (FIPS 204 §7.2 Algorithm 27) ────────────────
|
|
121
|
+
// σ = c̃ ‖ z_packed ‖ h_packed
|
|
122
|
+
cTilde = sig.slice(0, lambdaOver4);
|
|
123
|
+
let off = lambdaOver4;
|
|
124
|
+
for (let r = 0; r < l; r++) {
|
|
125
|
+
mlMem.set(sig.subarray(off, off + zPolyBytes), xofOff);
|
|
126
|
+
mx.bit_unpack(slot1 + r * POLY_BYTES, xofOff, gamma1 - 1, gamma1);
|
|
127
|
+
off += zPolyBytes;
|
|
128
|
+
}
|
|
129
|
+
// HintBitUnpack, FIPS 204 Algorithm 21. Returns -1 on any of the
|
|
130
|
+
// three malformed-input checks (lines 4, 9, 17). Propagate as
|
|
131
|
+
// false: this is the SUF-CMA fix from FIPS 204 §D.3.
|
|
132
|
+
mlMem.set(sig.subarray(off, off + omega + k), xofOff);
|
|
133
|
+
if (mx.hint_bit_unpack(slot2, xofOff, k, omega) < 0)
|
|
134
|
+
return false;
|
|
135
|
+
// ── Step (Alg 8 line 4) Â ← ExpandA(ρ) ─────────────────────────
|
|
136
|
+
expandA(mx, sx, params, rho, matOff);
|
|
137
|
+
// ── Step (Alg 8 line 5) tr ← H(BytesToBits(pk), 64) ──────────────
|
|
138
|
+
tr = shake256Hash(sx, vk, 64);
|
|
139
|
+
// ── Step (Alg 8 line 6) μ ← H(BytesToBits(tr) ‖ M', 64) ──────────
|
|
140
|
+
mu = shake256HashConcat(sx, [tr, MPrime], 64);
|
|
141
|
+
// ── Step (Alg 8 line 7) c ← SampleInBall(c̃) ───────────────────
|
|
142
|
+
mlMem.fill(0, polySlot1, polySlot1 + POLY_BYTES);
|
|
143
|
+
sx.shake256Init();
|
|
144
|
+
sha3Absorb(sx, cTilde);
|
|
145
|
+
sx.shakePad();
|
|
146
|
+
sx.shakeSqueezeBlock();
|
|
147
|
+
mlMem.set(sha3Mem.subarray(sha3OutOff, sha3OutOff + 8), polySlot0);
|
|
148
|
+
mlMem.set(sha3Mem.subarray(sha3OutOff + 8, sha3OutOff + SHAKE256_RATE), xofOff);
|
|
149
|
+
let sampleI = mx.sample_in_ball(polySlot1, polySlot0, xofOff, SHAKE256_RATE - 8, tau, 256 - tau);
|
|
150
|
+
while (sampleI < 256) {
|
|
151
|
+
sx.shakeSqueezeBlock();
|
|
152
|
+
mlMem.set(sha3Mem.subarray(sha3OutOff, sha3OutOff + SHAKE256_RATE), xofOff);
|
|
153
|
+
sampleI = mx.sample_in_ball(polySlot1, polySlot0, xofOff, SHAKE256_RATE, tau, sampleI);
|
|
154
|
+
}
|
|
155
|
+
// ── Step (Alg 8 line 8) w'_approx ← NTT⁻¹(Â ∘ NTT(z) − NTT(c) ∘ NTT(t₁·2^d))
|
|
156
|
+
// (a) NTT z, then tomont so the matrix kernel's R⁻¹ leaves regular result.
|
|
157
|
+
mx.polyvec_ntt(slot1, l);
|
|
158
|
+
mx.polyvec_tomont(slot1, l);
|
|
159
|
+
// (b) Compute t₁·2^d in time domain. t₁[i] ∈ [0, 1023]; t₁[i]·2^13
|
|
160
|
+
// ≤ 1023 · 8192 = 8,380,416 < q = 8,380,417, so no mod needed
|
|
161
|
+
// and no overflow. Use Int32Array view over the slot region.
|
|
162
|
+
const t1View = new Int32Array(mlMem.buffer, slot0, k * 256);
|
|
163
|
+
for (let i = 0; i < k * 256; i++)
|
|
164
|
+
t1View[i] <<= t1ScalarShift;
|
|
165
|
+
mx.polyvec_ntt(slot0, k);
|
|
166
|
+
mx.polyvec_tomont(slot0, k);
|
|
167
|
+
// (c) ĉ ← NTT(c), single polynomial in place.
|
|
168
|
+
mx.ntt(polySlot1);
|
|
169
|
+
// (d) Â · ẑ → slot3 (regular form in NTT domain)
|
|
170
|
+
mx.polyvec_matrix_pointwise_montgomery(slot3, matOff, slot1, k, l);
|
|
171
|
+
// (e) ĉ · t̂₁·2^d → slot4, TS-side per-poly loop; ĉ regular, t̂₁·2^d
|
|
172
|
+
// tomont so the kernel's R⁻¹ leaves the result regular.
|
|
173
|
+
for (let r = 0; r < k; r++) {
|
|
174
|
+
mx.poly_pointwise_montgomery(slot4 + r * POLY_BYTES, polySlot1, slot0 + r * POLY_BYTES);
|
|
175
|
+
}
|
|
176
|
+
// (f) w'_approx (NTT domain) ← ·ẑ − ĉ·t̂₁·2^d
|
|
177
|
+
mx.polyvec_sub(slot3, slot3, slot4, k);
|
|
178
|
+
mx.polyvec_invntt(slot3, k);
|
|
179
|
+
mx.polyvec_caddq(slot3, k); // canonical for use_hint
|
|
180
|
+
// ── Step (Alg 8 line 9) w'₁ ← UseHint(h, w'_approx) ─────────────
|
|
181
|
+
// use_hint is alias-safe between r and a (read both before write
|
|
182
|
+
// per coefficient), so overwrite w'_approx with w'₁ in slot3.
|
|
183
|
+
mx.polyvec_use_hint(slot3, slot2, slot3, k, gamma2);
|
|
184
|
+
// ── Step (Alg 8 line 10) c̃' ← H(μ ‖ w₁Encode(w'₁), λ/4) ─────────
|
|
185
|
+
for (let r = 0; r < k; r++) {
|
|
186
|
+
mx.simple_bit_pack(xofOff + r * w1PolyBytes, slot3 + r * POLY_BYTES, w1Bitlen);
|
|
187
|
+
}
|
|
188
|
+
w1Bytes = mlMem.slice(xofOff, xofOff + w1TotalBytes);
|
|
189
|
+
cTildeNew = shake256HashConcat(sx, [mu, w1Bytes], lambdaOver4);
|
|
190
|
+
// ── Step (Alg 8 line 11) return ‖z‖∞ < γ₁ − β AND c̃ = c̃' ───────
|
|
191
|
+
// The sigDecode bit_unpack output is in centered residues
|
|
192
|
+
// [-(γ₁-1), γ₁], chknorm consumes that form directly without
|
|
193
|
+
// polyvec_reduce. We must check norm AFTER the NTT path because
|
|
194
|
+
// our slot1 holds NTT-domain ẑ now. Re-decode z into a fresh slot
|
|
195
|
+
// for the norm check.
|
|
196
|
+
//
|
|
197
|
+
// Re-decode is cheap (bit_unpack is a single-pass kernel). Use
|
|
198
|
+
// slot4 (already overwritten above) as the destination.
|
|
199
|
+
off = lambdaOver4;
|
|
200
|
+
for (let r = 0; r < l; r++) {
|
|
201
|
+
mlMem.set(sig.subarray(off, off + zPolyBytes), xofOff);
|
|
202
|
+
mx.bit_unpack(slot4 + r * POLY_BYTES, xofOff, gamma1 - 1, gamma1);
|
|
203
|
+
off += zPolyBytes;
|
|
204
|
+
}
|
|
205
|
+
const normFail = mx.polyvec_chknorm(slot4, gamma1 - beta, l);
|
|
206
|
+
// Constant-time comparison via the SIMD ct WASM module. We
|
|
207
|
+
// evaluate normFail and the c̃ comparison both before returning,
|
|
208
|
+
// so that an attacker who can observe timing cannot distinguish
|
|
209
|
+
// "norm failed" from "c̃ mismatch", both run to completion before
|
|
210
|
+
// the boolean reduction. (The c̃ != c̃' branch is taken regardless
|
|
211
|
+
// of normFail; both signals are public-input-derived so this is
|
|
212
|
+
// not a CT requirement, but it preserves the symmetry.)
|
|
213
|
+
const cTildeEq = constantTimeEqual(cTilde, cTildeNew);
|
|
214
|
+
return normFail === 0 && cTildeEq;
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// FIPS 204 verify is a pure predicate. Any unexpected exception
|
|
218
|
+
// (out-of-memory, kernel argument errors) is treated as "did not
|
|
219
|
+
// authenticate", never propagate. The SUF-CMA risk of swallowing
|
|
220
|
+
// is bounded by validate*.ts callers: the ones that throw on
|
|
221
|
+
// caller contract violations (oversize ctx) run BEFORE this
|
|
222
|
+
// function executes.
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
// Wipe TS-side scratch.
|
|
227
|
+
if (mu)
|
|
228
|
+
wipe(mu);
|
|
229
|
+
if (tr)
|
|
230
|
+
wipe(tr);
|
|
231
|
+
if (cTilde)
|
|
232
|
+
wipe(cTilde);
|
|
233
|
+
if (cTildeNew)
|
|
234
|
+
wipe(cTildeNew);
|
|
235
|
+
if (w1Bytes)
|
|
236
|
+
wipe(w1Bytes);
|
|
237
|
+
// Wipe WASM scratch, verify operates on public inputs (vk, sig,
|
|
238
|
+
// M, ctx all public; t₁, ẑ, w'_approx, h all public-derivable),
|
|
239
|
+
// but the discipline mirrors sign for review consistency. Cheap.
|
|
240
|
+
mlMem.fill(0, mx.getPolyvecSlotBase(), mx.getPolyvecSlotBase() + 5 * mx.getPolyvecSlotSize());
|
|
241
|
+
mlMem.fill(0, polySlotBase, polySlotBase + 8 * POLY_BYTES);
|
|
242
|
+
mlMem.fill(0, xofOff, xofOff + 8192);
|
|
243
|
+
// SHA3 module: wipe state across op boundary, same convention as
|
|
244
|
+
// keygen / sign. Holds vk (public), tr (public), μ (public-derivable).
|
|
245
|
+
sx.wipeBuffers();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* HashML-DSA verify, post-prehash. FIPS 204 §5.4 Algorithm 5 lines 17-19.
|
|
250
|
+
* Builds M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID(algo) ‖ prehash and drives
|
|
251
|
+
* Verify_internal.
|
|
252
|
+
*
|
|
253
|
+
* Same return / throw posture as `mldsaVerifyInternal`: returns a pure
|
|
254
|
+
* boolean for every signature outcome. Caller (in index.ts) is expected
|
|
255
|
+
* to have already filtered wrong-length vk / sig / digest with the
|
|
256
|
+
* appropriate verdict (false) before calling this helper.
|
|
257
|
+
*
|
|
258
|
+
* The caller owns `prehash`; this helper never wipes it.
|
|
259
|
+
*/
|
|
260
|
+
export function verifyWithPrehash(mx, sx, params, vk, prehash, sig, algo, ctx) {
|
|
261
|
+
const oid = getOid(algo);
|
|
262
|
+
const MPrime = constructMPrimeHash(ctx, oid, prehash);
|
|
263
|
+
try {
|
|
264
|
+
return mldsaVerifyInternal(mx, sx, params, vk, MPrime, sig);
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
wipe(MPrime);
|
|
268
|
+
}
|
|
269
|
+
}
|
package/dist/mldsa.wasm
ADDED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WASM_GZ_BASE64 as mlkemWasm } from '../embedded/mlkem.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
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/mlkem/embedded.ts
|
|
23
|
+
//
|
|
24
|
+
// Exports the gzip+base64 ML-KEM WASM blob for use as a WasmSource.
|
|
25
|
+
// This is the only file in the mlkem subpath that references the embedded blob.
|
|
26
|
+
// Import via `leviathan-crypto/mlkem/embedded`.
|
|
27
|
+
export { WASM_GZ_BASE64 as mlkemWasm } from '../embedded/mlkem.js';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { MlKemExports, Sha3Exports } from './types.js';
|
|
2
|
+
import type { MlKemParams } from './params.js';
|
|
3
|
+
/** SHA3-512(msg) → 64 bytes. Resets sha3 state. */
|
|
4
|
+
export declare function sha3_512Hash(sx: Sha3Exports, msg: Uint8Array): Uint8Array;
|
|
5
|
+
/** SHA3-256(msg) → 32 bytes. Resets sha3 state. */
|
|
6
|
+
export declare function sha3_256Hash(sx: Sha3Exports, msg: Uint8Array): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* SHAKE256(msg, n) → n bytes. Resets sha3 state.
|
|
9
|
+
* Used for J function (z || c) and PRF seeding in kem.ts.
|
|
10
|
+
*/
|
|
11
|
+
export declare function shake256Hash(sx: Sha3Exports, msg: Uint8Array, n: number): Uint8Array;
|
|
12
|
+
/**
|
|
13
|
+
* K-PKE.KeyGen (FIPS 203 Algorithm 12), deterministic.
|
|
14
|
+
*
|
|
15
|
+
* Slot map:
|
|
16
|
+
* pvec0, current row of  (overwritten per row)
|
|
17
|
+
* pvec1, ŝ (noise, persistent through dot products)
|
|
18
|
+
* pvec2, ê (noise)
|
|
19
|
+
* pvec3, t̂ = ·ŝ + ê (output)
|
|
20
|
+
*/
|
|
21
|
+
export declare function indcpaKeypairDerand(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, d: Uint8Array): {
|
|
22
|
+
ekCpa: Uint8Array;
|
|
23
|
+
skCpa: Uint8Array;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* K-PKE.Encrypt (FIPS 203 Algorithm 13), deterministic.
|
|
27
|
+
*
|
|
28
|
+
* Slot map:
|
|
29
|
+
* pvec0, current row of Â^T (transposed, overwritten per row)
|
|
30
|
+
* pvec1, r̂ = NTT(r)
|
|
31
|
+
* pvec2, e₁ (noise)
|
|
32
|
+
* pvec3, u = invNTT(Â^T · r̂) + e₁
|
|
33
|
+
* pvec4, t̂ (unpacked from ek)
|
|
34
|
+
* poly1 , e₂ (noise)
|
|
35
|
+
* poly2 , v = invNTT(t̂^T · r̂) + e₂ + msg
|
|
36
|
+
* poly3 , message polynomial
|
|
37
|
+
*/
|
|
38
|
+
export declare function indcpaEncrypt(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, ek: Uint8Array, m: Uint8Array, coins: Uint8Array): Uint8Array;
|
|
39
|
+
/**
|
|
40
|
+
* K-PKE.Decrypt (FIPS 203 Algorithm 14).
|
|
41
|
+
*
|
|
42
|
+
* Slot map:
|
|
43
|
+
* pvec0, û (decompressed from ct)
|
|
44
|
+
* pvec1, ŝ (from sk)
|
|
45
|
+
* poly0 , v (decompressed from ct)
|
|
46
|
+
* poly1 , w = invNTT(ŝ^T · NTT(û))
|
|
47
|
+
* poly2 , m' = v - w
|
|
48
|
+
*/
|
|
49
|
+
export declare function indcpaDecrypt(kx: MlKemExports, params: MlKemParams, skCpa: Uint8Array, ct: Uint8Array): Uint8Array;
|