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,31 @@
|
|
|
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/merkle/sth.ts
|
|
23
|
+
//
|
|
24
|
+
// SignedTreeHead type: a c2sp.org/tlog-checkpoint body plus the
|
|
25
|
+
// signature lines that authenticate it. The body and signatures pair
|
|
26
|
+
// is the wire-format unit a SignedLog (built on top of MerkleTree in
|
|
27
|
+
// a later phase) produces and a verifier consumes. The byte-stable
|
|
28
|
+
// serialization is `emitSignedNote(serializeCheckpointBody(c), sigs)`;
|
|
29
|
+
// the parse side is `parseSignedNote` followed by `parseCheckpointBody`
|
|
30
|
+
// on the resulting body region.
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimum surface a backend exposes to drive a MerkleTree. Sync
|
|
3
|
+
* everywhere: the merkle layer is synchronous and consumers that need
|
|
4
|
+
* async IO wrap externally.
|
|
5
|
+
*
|
|
6
|
+
* Storage records only perfect aligned subtree hashes. Roots of
|
|
7
|
+
* partial right-edge subtrees are recomputed on demand by the tree.
|
|
8
|
+
*/
|
|
9
|
+
export interface MerkleStorage {
|
|
10
|
+
/** Number of leaves appended so far. */
|
|
11
|
+
size(): number;
|
|
12
|
+
/** Record a leaf hash at the given index. The implementation must reject out-of-order indices. */
|
|
13
|
+
appendLeaf(leafIndex: number, leafHash: Uint8Array): void;
|
|
14
|
+
/** Read the leaf hash at index. Throws if absent. */
|
|
15
|
+
getLeaf(leafIndex: number): Uint8Array;
|
|
16
|
+
/** Record an internal node hash at the given (level, index). */
|
|
17
|
+
putNode(level: number, index: number, hash: Uint8Array): void;
|
|
18
|
+
/** Read an internal-node hash. Throws if absent. */
|
|
19
|
+
getNode(level: number, index: number): Uint8Array;
|
|
20
|
+
/** Probe an internal-node slot without throwing. */
|
|
21
|
+
hasNode(level: number, index: number): boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* In-process storage backed by a Map keyed on `${level}:${index}`. Sync.
|
|
25
|
+
* Suitable for tests, witnesses without persistent storage, and the
|
|
26
|
+
* MerkleVerifier short-lived flow. Production logs that need durability
|
|
27
|
+
* implement MerkleStorage over a file or DB and feed it to a
|
|
28
|
+
* MerkleTree the same way.
|
|
29
|
+
*/
|
|
30
|
+
export declare class MemoryStorage implements MerkleStorage {
|
|
31
|
+
private leafCount;
|
|
32
|
+
private readonly nodes;
|
|
33
|
+
private static key;
|
|
34
|
+
size(): number;
|
|
35
|
+
appendLeaf(leafIndex: number, leafHash: Uint8Array): void;
|
|
36
|
+
getLeaf(leafIndex: number): Uint8Array;
|
|
37
|
+
putNode(level: number, index: number, hash: Uint8Array): void;
|
|
38
|
+
getNode(level: number, index: number): Uint8Array;
|
|
39
|
+
hasNode(level: number, index: number): boolean;
|
|
40
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
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/merkle/storage.ts
|
|
23
|
+
//
|
|
24
|
+
// MerkleStorage abstracts the per-node persistence layer a tree uses
|
|
25
|
+
// to materialise leaves and recomputed internal nodes. Two-axis key
|
|
26
|
+
// (level, index): level 0 is the leaf row, level >= 1 stores the hash
|
|
27
|
+
// of a perfect aligned subtree covering [index*2^level, (index+1)*2^level).
|
|
28
|
+
//
|
|
29
|
+
// MemoryStorage is the in-process implementation. File and database
|
|
30
|
+
// backends are extension surface and ship as consumer code.
|
|
31
|
+
/**
|
|
32
|
+
* In-process storage backed by a Map keyed on `${level}:${index}`. Sync.
|
|
33
|
+
* Suitable for tests, witnesses without persistent storage, and the
|
|
34
|
+
* MerkleVerifier short-lived flow. Production logs that need durability
|
|
35
|
+
* implement MerkleStorage over a file or DB and feed it to a
|
|
36
|
+
* MerkleTree the same way.
|
|
37
|
+
*/
|
|
38
|
+
export class MemoryStorage {
|
|
39
|
+
leafCount = 0;
|
|
40
|
+
nodes = new Map();
|
|
41
|
+
static key(level, index) {
|
|
42
|
+
return `${level}:${index}`;
|
|
43
|
+
}
|
|
44
|
+
size() {
|
|
45
|
+
return this.leafCount;
|
|
46
|
+
}
|
|
47
|
+
appendLeaf(leafIndex, leafHash) {
|
|
48
|
+
if (leafIndex !== this.leafCount)
|
|
49
|
+
throw new RangeError(`MemoryStorage.appendLeaf: out-of-order index ${leafIndex}, expected ${this.leafCount}`);
|
|
50
|
+
this.nodes.set(MemoryStorage.key(0, leafIndex), leafHash);
|
|
51
|
+
this.leafCount++;
|
|
52
|
+
}
|
|
53
|
+
getLeaf(leafIndex) {
|
|
54
|
+
const v = this.nodes.get(MemoryStorage.key(0, leafIndex));
|
|
55
|
+
if (!v)
|
|
56
|
+
throw new RangeError(`MemoryStorage.getLeaf: no leaf at index ${leafIndex}`);
|
|
57
|
+
return v;
|
|
58
|
+
}
|
|
59
|
+
putNode(level, index, hash) {
|
|
60
|
+
this.nodes.set(MemoryStorage.key(level, index), hash);
|
|
61
|
+
}
|
|
62
|
+
getNode(level, index) {
|
|
63
|
+
const v = this.nodes.get(MemoryStorage.key(level, index));
|
|
64
|
+
if (!v)
|
|
65
|
+
throw new RangeError(`MemoryStorage.getNode: no node at (${level}, ${index})`);
|
|
66
|
+
return v;
|
|
67
|
+
}
|
|
68
|
+
hasNode(level, index) {
|
|
69
|
+
return this.nodes.has(MemoryStorage.key(level, index));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimum surface a hash function must expose to drive RFC 9162
|
|
3
|
+
* (Certificate Transparency Version 2.0) §2.1.1, Merkle Hash Trees.
|
|
4
|
+
*
|
|
5
|
+
* Implementations are `const` objects (no instantiation); each call site
|
|
6
|
+
* acquires the underlying WASM module fresh, runs the operation, and
|
|
7
|
+
* disposes. There is no long-lived state on a Hasher; concurrent users
|
|
8
|
+
* are serialised by the per-module exclusivity guard at the WASM layer.
|
|
9
|
+
*/
|
|
10
|
+
export interface Hasher {
|
|
11
|
+
/** Display name, used in error messages and the export catalog. */
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** Bytes per hash output. */
|
|
14
|
+
readonly outputSize: number;
|
|
15
|
+
/** WASM module ids this Hasher exercises during `init()`. */
|
|
16
|
+
readonly wasmModules: readonly string[];
|
|
17
|
+
/** RFC 9162 §2.1.1: MTH({}) = HASH(), the hash of the empty input. */
|
|
18
|
+
hashEmpty(): Uint8Array;
|
|
19
|
+
/** RFC 9162 §2.1.1: leaf domain separator `0x00` prefix. */
|
|
20
|
+
hashLeaf(leaf: Uint8Array): Uint8Array;
|
|
21
|
+
/** RFC 9162 §2.1.1: internal-node domain separator `0x01` prefix. */
|
|
22
|
+
hashInternal(left: Uint8Array, right: Uint8Array): Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Stateful Merkle tree with pluggable storage. Append a leaf, query
|
|
26
|
+
* size + root, build inclusion and consistency proofs. The tree owns
|
|
27
|
+
* the hash function via `hasher`; consumers do not pass it per call.
|
|
28
|
+
*/
|
|
29
|
+
export interface MerkleTree {
|
|
30
|
+
readonly hasher: Hasher;
|
|
31
|
+
size(): number;
|
|
32
|
+
rootHash(): Uint8Array;
|
|
33
|
+
append(leafBytes: Uint8Array): {
|
|
34
|
+
leafIndex: number;
|
|
35
|
+
leafHash: Uint8Array;
|
|
36
|
+
};
|
|
37
|
+
getInclusionProof(leafIndex: number, treeSize?: number): Uint8Array[];
|
|
38
|
+
getConsistencyProof(oldSize: number, newSize: number): Uint8Array[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* RFC 9162 §2.1.4, Consistency Proof Verification: "k is the largest
|
|
42
|
+
* power of two smaller than n". The split point at which an n-leaf
|
|
43
|
+
* tree decomposes into a perfect left subtree of size k and a right
|
|
44
|
+
* subtree of size n - k. Defined for n >= 2.
|
|
45
|
+
*
|
|
46
|
+
* Invariant for n >= 2: k < n <= 2*k.
|
|
47
|
+
*/
|
|
48
|
+
export declare function splitPoint(n: number): number;
|
|
49
|
+
/**
|
|
50
|
+
* `bits.Len64(x)` analogue: position of the most-significant set bit
|
|
51
|
+
* of x, with `bitLen(0) = 0`. Used by the §2.1.3 / §2.1.4 inclusion
|
|
52
|
+
* and consistency verifiers to split a proof into inner and border
|
|
53
|
+
* segments.
|
|
54
|
+
*/
|
|
55
|
+
export declare function bitLen(x: number): number;
|
|
56
|
+
/**
|
|
57
|
+
* Popcount of a non-negative integer (`bits.OnesCount64` analogue).
|
|
58
|
+
* Used to compute the "border" length of an inclusion proof per the
|
|
59
|
+
* RFC 9162 §2.1.3 decomposition.
|
|
60
|
+
*/
|
|
61
|
+
export declare function popcount(x: number): number;
|
|
62
|
+
/**
|
|
63
|
+
* Number of trailing zero bits in a positive integer
|
|
64
|
+
* (`bits.TrailingZeros64` analogue). Used by RFC 9162 §2.1.4 to step
|
|
65
|
+
* the consistency verifier past the levels covered by the size1
|
|
66
|
+
* subtree. Defined for x >= 1.
|
|
67
|
+
*/
|
|
68
|
+
export declare function trailingZeros(x: number): number;
|
|
@@ -0,0 +1,94 @@
|
|
|
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/merkle/tree.ts
|
|
23
|
+
//
|
|
24
|
+
// MerkleTree + Hasher interfaces and the spec-anchored node-index math.
|
|
25
|
+
// Hash-agnostic by design: every hash-touching surface (the tree class,
|
|
26
|
+
// the free-function proof verifiers, the proof builders) is parameterised
|
|
27
|
+
// by a `Hasher`, so SHA-256 and BLAKE3 trees share the same algorithmic
|
|
28
|
+
// core and the same proof wire format.
|
|
29
|
+
/**
|
|
30
|
+
* RFC 9162 §2.1.4, Consistency Proof Verification: "k is the largest
|
|
31
|
+
* power of two smaller than n". The split point at which an n-leaf
|
|
32
|
+
* tree decomposes into a perfect left subtree of size k and a right
|
|
33
|
+
* subtree of size n - k. Defined for n >= 2.
|
|
34
|
+
*
|
|
35
|
+
* Invariant for n >= 2: k < n <= 2*k.
|
|
36
|
+
*/
|
|
37
|
+
export function splitPoint(n) {
|
|
38
|
+
if (!Number.isInteger(n) || n < 2)
|
|
39
|
+
throw new RangeError(`splitPoint: n must be an integer >= 2, got ${n}`);
|
|
40
|
+
// Largest power of two strictly less than n: for n=2 -> 1, n=8 -> 4,
|
|
41
|
+
// n=2^k -> 2^(k-1). Equivalent to 1 << (bitLength(n - 1) - 1).
|
|
42
|
+
let k = 1;
|
|
43
|
+
while (k * 2 < n)
|
|
44
|
+
k *= 2;
|
|
45
|
+
return k;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `bits.Len64(x)` analogue: position of the most-significant set bit
|
|
49
|
+
* of x, with `bitLen(0) = 0`. Used by the §2.1.3 / §2.1.4 inclusion
|
|
50
|
+
* and consistency verifiers to split a proof into inner and border
|
|
51
|
+
* segments.
|
|
52
|
+
*/
|
|
53
|
+
export function bitLen(x) {
|
|
54
|
+
if (!Number.isInteger(x) || x < 0)
|
|
55
|
+
throw new RangeError(`bitLen: x must be a non-negative integer, got ${x}`);
|
|
56
|
+
let n = 0;
|
|
57
|
+
while (x > 0) {
|
|
58
|
+
x = Math.floor(x / 2);
|
|
59
|
+
n++;
|
|
60
|
+
}
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Popcount of a non-negative integer (`bits.OnesCount64` analogue).
|
|
65
|
+
* Used to compute the "border" length of an inclusion proof per the
|
|
66
|
+
* RFC 9162 §2.1.3 decomposition.
|
|
67
|
+
*/
|
|
68
|
+
export function popcount(x) {
|
|
69
|
+
if (!Number.isInteger(x) || x < 0)
|
|
70
|
+
throw new RangeError(`popcount: x must be a non-negative integer, got ${x}`);
|
|
71
|
+
let n = 0;
|
|
72
|
+
while (x > 0) {
|
|
73
|
+
if (x & 1)
|
|
74
|
+
n++;
|
|
75
|
+
x = Math.floor(x / 2);
|
|
76
|
+
}
|
|
77
|
+
return n;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Number of trailing zero bits in a positive integer
|
|
81
|
+
* (`bits.TrailingZeros64` analogue). Used by RFC 9162 §2.1.4 to step
|
|
82
|
+
* the consistency verifier past the levels covered by the size1
|
|
83
|
+
* subtree. Defined for x >= 1.
|
|
84
|
+
*/
|
|
85
|
+
export function trailingZeros(x) {
|
|
86
|
+
if (!Number.isInteger(x) || x < 1)
|
|
87
|
+
throw new RangeError(`trailingZeros: x must be a positive integer, got ${x}`);
|
|
88
|
+
let n = 0;
|
|
89
|
+
while ((x & 1) === 0) {
|
|
90
|
+
x = Math.floor(x / 2);
|
|
91
|
+
n++;
|
|
92
|
+
}
|
|
93
|
+
return n;
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WASM_GZ_BASE64 as mldsaWasm } from '../embedded/mldsa.js';
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
20
|
// ▀█████▀▀
|
|
21
21
|
//
|
|
22
|
-
// src/ts/
|
|
22
|
+
// src/ts/mldsa/embedded.ts
|
|
23
23
|
//
|
|
24
|
-
// Exports the gzip+base64
|
|
25
|
-
// This is the only file in the
|
|
26
|
-
// Import via `leviathan-crypto/
|
|
27
|
-
export { WASM_GZ_BASE64 as
|
|
24
|
+
// Exports the gzip+base64 mldsa WASM blob for use as a WasmSource.
|
|
25
|
+
// This is the only file in the mldsa subpath that references the embedded blob.
|
|
26
|
+
// Import via `leviathan-crypto/mldsa/embedded`.
|
|
27
|
+
export { WASM_GZ_BASE64 as mldsaWasm } from '../embedded/mldsa.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { MlDsaExports, Sha3Exports } from './types.js';
|
|
2
|
+
import type { MlDsaParams } from './params.js';
|
|
3
|
+
/**
|
|
4
|
+
* ExpandA, FIPS 204 Algorithm 32.
|
|
5
|
+
*
|
|
6
|
+
* For (i, j) ∈ [0, k) × [0, ℓ):
|
|
7
|
+
* s ← ρ ‖ IntegerToBytes(j, 1) ‖ IntegerToBytes(i, 1)
|
|
8
|
+
* Â[i, j] ← RejNTTPoly(SHAKE128(s))
|
|
9
|
+
*
|
|
10
|
+
* Output is row-major: Â[i, j] sits at matrixOff + (i·ℓ + j) · 1024.
|
|
11
|
+
* This matches `polyvec_matrix_pointwise_montgomery`'s row-stride contract.
|
|
12
|
+
*
|
|
13
|
+
* ρ is the public seed; the rej_ntt_poly inner loop has data-dependent
|
|
14
|
+
* branching but only on ρ-derived bytes (public), so no CT concern.
|
|
15
|
+
*/
|
|
16
|
+
export declare function expandA(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, rho: Uint8Array, matrixOff: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* ExpandS, FIPS 204 Algorithm 33.
|
|
19
|
+
*
|
|
20
|
+
* For r ∈ [0, ℓ): s₁[r] ← RejBoundedPoly(SHAKE256(ρ' ‖ IntegerToBytes(r, 2)))
|
|
21
|
+
* For r ∈ [0, k): s₂[r] ← RejBoundedPoly(SHAKE256(ρ' ‖ IntegerToBytes(r+ℓ, 2)))
|
|
22
|
+
*
|
|
23
|
+
* Note the index is 2 bytes (little-endian per FIPS 204 §7.1 Alg 11), mlkem
|
|
24
|
+
* uses 1 byte because k ≤ 4, but ML-DSA's max index is k+ℓ-1 = 14 (still
|
|
25
|
+
* ≤ 255 in practice but the spec mandates 2 bytes).
|
|
26
|
+
*
|
|
27
|
+
* ρ' is secret. The local seed scratch is wiped on exit; the caller is
|
|
28
|
+
* responsible for the WASM-resident ρ' source buffer.
|
|
29
|
+
*/
|
|
30
|
+
export declare function expandS(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, rhoPrime: Uint8Array, s1Off: number, s2Off: number): void;
|
|
31
|
+
/**
|
|
32
|
+
* ExpandMask, FIPS 204 Algorithm 34.
|
|
33
|
+
*
|
|
34
|
+
* For r ∈ [0, ℓ):
|
|
35
|
+
* v ← SHAKE256(ρ'' ‖ IntegerToBytes(κ + r, 2), 32·c)
|
|
36
|
+
* y[r] ← BitUnpack(v, γ₁ − 1, γ₁)
|
|
37
|
+
*
|
|
38
|
+
* where c = 1 + bitlen(γ₁ − 1) is the per-coefficient byte width
|
|
39
|
+
* (18 when γ₁ = 2¹⁷, 20 when γ₁ = 2¹⁹). Output coefficients land in
|
|
40
|
+
* [-(γ₁ − 1), γ₁]; bit_unpack(a=γ₁−1, b=γ₁) covers exactly that range.
|
|
41
|
+
*
|
|
42
|
+
* y is produced in time domain at `yPvOff`. Sign_internal applies
|
|
43
|
+
* polyvec_ntt to y before the matrix-vector product  · NTT(y).
|
|
44
|
+
*
|
|
45
|
+
* ρ'' is secret (derived from K ‖ rnd ‖ μ). This function uses one
|
|
46
|
+
* one-shot SHAKE256 per polynomial (caller's full input is ≤ 168 B,
|
|
47
|
+
* fits in a single shake256HashConcat call); the SHAKE state is reset
|
|
48
|
+
* each iteration via shake256Init inside the helper, so no state
|
|
49
|
+
* carries between r values. The squeeze output lands in a TS-side
|
|
50
|
+
* buffer and is set into the WASM XOF/PRF region only long enough for
|
|
51
|
+
* bit_unpack to consume it.
|
|
52
|
+
*/
|
|
53
|
+
export declare function expandMask(mx: MlDsaExports, sx: Sha3Exports, params: MlDsaParams, rhoPrimePrime: Uint8Array, kappa: number, yPvOff: number): void;
|
|
@@ -0,0 +1,188 @@
|
|
|
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/expand.ts
|
|
23
|
+
//
|
|
24
|
+
// FIPS 204 Algorithms 32 (ExpandA), 33 (ExpandS), 34 (ExpandMask),
|
|
25
|
+
// pseudorandom expansion of seeds into the matrix  (public), the noise
|
|
26
|
+
// polyvecs s₁/s₂ (secret, time domain), and the masking polyvec y (per
|
|
27
|
+
// signing iteration, time domain).
|
|
28
|
+
//
|
|
29
|
+
// ExpandA samples  directly in NTT domain (RejNTTPoly produces NTT
|
|
30
|
+
// coefficients). ExpandS / ExpandMask sample in time domain; the
|
|
31
|
+
// orchestration layer applies polyvec_ntt where needed for the matrix-
|
|
32
|
+
// vector products in Sign_internal and Verify_internal.
|
|
33
|
+
import { wipe } from '../utils.js';
|
|
34
|
+
import { shake128Squeezer, shake256Squeezer, shake256HashConcat } from './sha3-helpers.js';
|
|
35
|
+
const POLY_BYTES = 1024; // 256 × i32
|
|
36
|
+
/**
|
|
37
|
+
* ExpandA, FIPS 204 Algorithm 32.
|
|
38
|
+
*
|
|
39
|
+
* For (i, j) ∈ [0, k) × [0, ℓ):
|
|
40
|
+
* s ← ρ ‖ IntegerToBytes(j, 1) ‖ IntegerToBytes(i, 1)
|
|
41
|
+
* Â[i, j] ← RejNTTPoly(SHAKE128(s))
|
|
42
|
+
*
|
|
43
|
+
* Output is row-major: Â[i, j] sits at matrixOff + (i·ℓ + j) · 1024.
|
|
44
|
+
* This matches `polyvec_matrix_pointwise_montgomery`'s row-stride contract.
|
|
45
|
+
*
|
|
46
|
+
* ρ is the public seed; the rej_ntt_poly inner loop has data-dependent
|
|
47
|
+
* branching but only on ρ-derived bytes (public), so no CT concern.
|
|
48
|
+
*/
|
|
49
|
+
export function expandA(mx, sx, params, rho, matrixOff) {
|
|
50
|
+
const { k, l } = params;
|
|
51
|
+
const xofPrfOff = mx.getXofPrfOffset();
|
|
52
|
+
const mlMem = new Uint8Array(mx.memory.buffer);
|
|
53
|
+
// XOF input: ρ(32) ‖ jByte ‖ iByte (FIPS 204 §7.3 Alg 32 line 4)
|
|
54
|
+
const xofSeed = new Uint8Array(34);
|
|
55
|
+
xofSeed.set(rho, 0);
|
|
56
|
+
for (let i = 0; i < k; i++) {
|
|
57
|
+
for (let j = 0; j < l; j++) {
|
|
58
|
+
xofSeed[32] = j;
|
|
59
|
+
xofSeed[33] = i;
|
|
60
|
+
const sq = shake128Squeezer(sx, xofSeed);
|
|
61
|
+
const polyOff = matrixOff + (i * l + j) * POLY_BYTES;
|
|
62
|
+
let ctr = 0;
|
|
63
|
+
while (ctr < 256) {
|
|
64
|
+
const block = sq.squeeze();
|
|
65
|
+
mlMem.set(block, xofPrfOff);
|
|
66
|
+
ctr += mx.rej_ntt_poly(polyOff, ctr, xofPrfOff, sq.rate);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* ExpandS, FIPS 204 Algorithm 33.
|
|
73
|
+
*
|
|
74
|
+
* For r ∈ [0, ℓ): s₁[r] ← RejBoundedPoly(SHAKE256(ρ' ‖ IntegerToBytes(r, 2)))
|
|
75
|
+
* For r ∈ [0, k): s₂[r] ← RejBoundedPoly(SHAKE256(ρ' ‖ IntegerToBytes(r+ℓ, 2)))
|
|
76
|
+
*
|
|
77
|
+
* Note the index is 2 bytes (little-endian per FIPS 204 §7.1 Alg 11), mlkem
|
|
78
|
+
* uses 1 byte because k ≤ 4, but ML-DSA's max index is k+ℓ-1 = 14 (still
|
|
79
|
+
* ≤ 255 in practice but the spec mandates 2 bytes).
|
|
80
|
+
*
|
|
81
|
+
* ρ' is secret. The local seed scratch is wiped on exit; the caller is
|
|
82
|
+
* responsible for the WASM-resident ρ' source buffer.
|
|
83
|
+
*/
|
|
84
|
+
export function expandS(mx, sx, params, rhoPrime, s1Off, s2Off) {
|
|
85
|
+
const { k, l, eta } = params;
|
|
86
|
+
const xofPrfOff = mx.getXofPrfOffset();
|
|
87
|
+
const mlMem = new Uint8Array(mx.memory.buffer);
|
|
88
|
+
// PRF input: ρ'(64) ‖ idx_lo ‖ idx_hi
|
|
89
|
+
const seed = new Uint8Array(66);
|
|
90
|
+
seed.set(rhoPrime, 0);
|
|
91
|
+
try {
|
|
92
|
+
// s₁: ℓ polynomials at indices 0..ℓ-1
|
|
93
|
+
for (let r = 0; r < l; r++) {
|
|
94
|
+
seed[64] = r & 0xFF;
|
|
95
|
+
seed[65] = (r >>> 8) & 0xFF;
|
|
96
|
+
const sq = shake256Squeezer(sx, seed);
|
|
97
|
+
const off = s1Off + r * POLY_BYTES;
|
|
98
|
+
let ctr = 0;
|
|
99
|
+
while (ctr < 256) {
|
|
100
|
+
const block = sq.squeeze();
|
|
101
|
+
mlMem.set(block, xofPrfOff);
|
|
102
|
+
ctr += mx.rej_bounded_poly(off, ctr, xofPrfOff, sq.rate, eta);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// s₂: k polynomials at indices ℓ..ℓ+k-1
|
|
106
|
+
for (let r = 0; r < k; r++) {
|
|
107
|
+
const idx = r + l;
|
|
108
|
+
seed[64] = idx & 0xFF;
|
|
109
|
+
seed[65] = (idx >>> 8) & 0xFF;
|
|
110
|
+
const sq = shake256Squeezer(sx, seed);
|
|
111
|
+
const off = s2Off + r * POLY_BYTES;
|
|
112
|
+
let ctr = 0;
|
|
113
|
+
while (ctr < 256) {
|
|
114
|
+
const block = sq.squeeze();
|
|
115
|
+
mlMem.set(block, xofPrfOff);
|
|
116
|
+
ctr += mx.rej_bounded_poly(off, ctr, xofPrfOff, sq.rate, eta);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
// Local seed buffer carries ρ' for the duration of this call; wipe
|
|
122
|
+
// even on early throw so it never persists in TS heap.
|
|
123
|
+
wipe(seed);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Bitlen helper, bitlen(n) for n > 0 is floor(log2(n)) + 1, used to size
|
|
127
|
+
// the BitUnpack output width inside ExpandMask. Identical to the helper in
|
|
128
|
+
// keygen.ts; keeping a private copy here avoids cross-importing the keygen
|
|
129
|
+
// module just for one tiny utility.
|
|
130
|
+
function bitlen(n) {
|
|
131
|
+
let b = 0;
|
|
132
|
+
let x = n;
|
|
133
|
+
while (x > 0) {
|
|
134
|
+
b++;
|
|
135
|
+
x >>>= 1;
|
|
136
|
+
}
|
|
137
|
+
return b;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* ExpandMask, FIPS 204 Algorithm 34.
|
|
141
|
+
*
|
|
142
|
+
* For r ∈ [0, ℓ):
|
|
143
|
+
* v ← SHAKE256(ρ'' ‖ IntegerToBytes(κ + r, 2), 32·c)
|
|
144
|
+
* y[r] ← BitUnpack(v, γ₁ − 1, γ₁)
|
|
145
|
+
*
|
|
146
|
+
* where c = 1 + bitlen(γ₁ − 1) is the per-coefficient byte width
|
|
147
|
+
* (18 when γ₁ = 2¹⁷, 20 when γ₁ = 2¹⁹). Output coefficients land in
|
|
148
|
+
* [-(γ₁ − 1), γ₁]; bit_unpack(a=γ₁−1, b=γ₁) covers exactly that range.
|
|
149
|
+
*
|
|
150
|
+
* y is produced in time domain at `yPvOff`. Sign_internal applies
|
|
151
|
+
* polyvec_ntt to y before the matrix-vector product  · NTT(y).
|
|
152
|
+
*
|
|
153
|
+
* ρ'' is secret (derived from K ‖ rnd ‖ μ). This function uses one
|
|
154
|
+
* one-shot SHAKE256 per polynomial (caller's full input is ≤ 168 B,
|
|
155
|
+
* fits in a single shake256HashConcat call); the SHAKE state is reset
|
|
156
|
+
* each iteration via shake256Init inside the helper, so no state
|
|
157
|
+
* carries between r values. The squeeze output lands in a TS-side
|
|
158
|
+
* buffer and is set into the WASM XOF/PRF region only long enough for
|
|
159
|
+
* bit_unpack to consume it.
|
|
160
|
+
*/
|
|
161
|
+
export function expandMask(mx, sx, params, rhoPrimePrime, kappa, yPvOff) {
|
|
162
|
+
const { l, gamma1 } = params;
|
|
163
|
+
const c = 1 + bitlen(gamma1 - 1); // 18 or 20
|
|
164
|
+
const polyBytesV = 32 * c; // 576 or 640
|
|
165
|
+
const xofPrfOff = mx.getXofPrfOffset();
|
|
166
|
+
const mlMem = new Uint8Array(mx.memory.buffer);
|
|
167
|
+
const idxBytes = new Uint8Array(2);
|
|
168
|
+
for (let r = 0; r < l; r++) {
|
|
169
|
+
const n = kappa + r;
|
|
170
|
+
idxBytes[0] = n & 0xFF;
|
|
171
|
+
idxBytes[1] = (n >>> 8) & 0xFF;
|
|
172
|
+
// SHAKE256(ρ'' ‖ idx, 32·c). For γ₁ ≤ 2¹⁹ the output never exceeds
|
|
173
|
+
// 640 bytes, well within the 8192-byte XOF region.
|
|
174
|
+
const v = shake256HashConcat(sx, [rhoPrimePrime, idxBytes], polyBytesV);
|
|
175
|
+
try {
|
|
176
|
+
mlMem.set(v, xofPrfOff);
|
|
177
|
+
// bit_unpack(a=γ₁-1, b=γ₁): bitlen(a+b) = bitlen(2γ₁-1) = c.
|
|
178
|
+
// Decodes to coefficients in [-(γ₁-1), γ₁], Alg 34 / Alg 19.
|
|
179
|
+
mx.bit_unpack(yPvOff + r * 1024, xofPrfOff, gamma1 - 1, gamma1);
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
// v held the SHAKE256(ρ'' ‖ idx) squeeze bytes, ρ''-derived,
|
|
183
|
+
// i.e. secret. These bytes become y for this iteration, so
|
|
184
|
+
// recovery would unmask the signing nonce on the rejected path.
|
|
185
|
+
wipe(v);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build M' = domSep ‖ |ctx| ‖ ctx ‖ M.
|
|
3
|
+
*
|
|
4
|
+
* domSep = 0x00 for pure ML-DSA, 0x01 for HashML-DSA.
|
|
5
|
+
* Caller has already validated ctx.length ≤ 255.
|
|
6
|
+
*/
|
|
7
|
+
export declare function constructMPrime(domSep: number, ctx: Uint8Array, M: Uint8Array): Uint8Array;
|
|
8
|
+
/**
|
|
9
|
+
* Build the HashML-DSA M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID ‖ PH_M.
|
|
10
|
+
*
|
|
11
|
+
* FIPS 204 §5.4 / Algorithm 4 line 23 (sign) and Algorithm 5 line 18 (verify).
|
|
12
|
+
* The leading byte is 0x01 (vs 0x00 for pure ML-DSA), domain separation
|
|
13
|
+
* across pure / pre-hash modes per FIPS 204 §3.6.4. Caller has already
|
|
14
|
+
* validated ctx.length ≤ 255.
|
|
15
|
+
*/
|
|
16
|
+
export declare function constructMPrimeHash(ctx: Uint8Array, oid: Uint8Array, PH_M: Uint8Array): Uint8Array;
|
|
@@ -0,0 +1,68 @@
|
|
|
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/format.ts
|
|
23
|
+
//
|
|
24
|
+
// External-API M' construction. Lives in its own file because HashML-DSA
|
|
25
|
+
// reuses the same byte layout with a different domain separator (0x01)
|
|
26
|
+
// and a hash-of-message tail.
|
|
27
|
+
//
|
|
28
|
+
// FIPS 204 Algorithm 2 line 10 (Sign) and Algorithm 3 line 5 (Verify):
|
|
29
|
+
// M' ← BytesToBits(IntegerToBytes(domSep, 1)
|
|
30
|
+
// ‖ IntegerToBytes(|ctx|, 1)
|
|
31
|
+
// ‖ ctx
|
|
32
|
+
// ‖ <message-bytes>)
|
|
33
|
+
//
|
|
34
|
+
// In a byte-oriented SHAKE wrapper, BytesToBits is a no-op, the absorbed
|
|
35
|
+
// bytes are the same. So we hand the absorber a contiguous Uint8Array
|
|
36
|
+
// laid out exactly as the spec describes, plus the domain-separator byte
|
|
37
|
+
// up front.
|
|
38
|
+
/**
|
|
39
|
+
* Build M' = domSep ‖ |ctx| ‖ ctx ‖ M.
|
|
40
|
+
*
|
|
41
|
+
* domSep = 0x00 for pure ML-DSA, 0x01 for HashML-DSA.
|
|
42
|
+
* Caller has already validated ctx.length ≤ 255.
|
|
43
|
+
*/
|
|
44
|
+
export function constructMPrime(domSep, ctx, M) {
|
|
45
|
+
const out = new Uint8Array(2 + ctx.length + M.length);
|
|
46
|
+
out[0] = domSep & 0xFF;
|
|
47
|
+
out[1] = ctx.length & 0xFF;
|
|
48
|
+
out.set(ctx, 2);
|
|
49
|
+
out.set(M, 2 + ctx.length);
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build the HashML-DSA M' = 0x01 ‖ |ctx| ‖ ctx ‖ OID ‖ PH_M.
|
|
54
|
+
*
|
|
55
|
+
* FIPS 204 §5.4 / Algorithm 4 line 23 (sign) and Algorithm 5 line 18 (verify).
|
|
56
|
+
* The leading byte is 0x01 (vs 0x00 for pure ML-DSA), domain separation
|
|
57
|
+
* across pure / pre-hash modes per FIPS 204 §3.6.4. Caller has already
|
|
58
|
+
* validated ctx.length ≤ 255.
|
|
59
|
+
*/
|
|
60
|
+
export function constructMPrimeHash(ctx, oid, PH_M) {
|
|
61
|
+
const out = new Uint8Array(2 + ctx.length + oid.length + PH_M.length);
|
|
62
|
+
out[0] = 0x01;
|
|
63
|
+
out[1] = ctx.length & 0xFF;
|
|
64
|
+
out.set(ctx, 2);
|
|
65
|
+
out.set(oid, 2 + ctx.length);
|
|
66
|
+
out.set(PH_M, 2 + ctx.length + oid.length);
|
|
67
|
+
return out;
|
|
68
|
+
}
|