leviathan-crypto 2.1.0 → 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 +86 -443
- package/README.md +198 -65
- 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.js +47 -25
- package/dist/chacha20/generator.d.ts +2 -2
- package/dist/chacha20/generator.js +4 -4
- package/dist/chacha20/index.d.ts +16 -15
- package/dist/chacha20/index.js +52 -46
- package/dist/chacha20/ops.d.ts +7 -7
- package/dist/chacha20/ops.js +34 -34
- package/dist/chacha20/pool-worker.js +5 -3
- package/dist/cte-wasm.d.ts +1 -0
- package/dist/cte-wasm.js +3 -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 -1
- package/dist/embedded/chacha20-pool-worker.js +2 -2
- 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 -1
- package/dist/embedded/serpent-pool-worker.js +2 -2
- 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 +5 -5
- package/dist/fortuna.js +37 -64
- package/dist/index.d.ts +38 -9
- package/dist/index.js +63 -19
- package/dist/init.d.ts +1 -1
- package/dist/init.js +11 -25
- 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 -24
- package/dist/loader.js +13 -16
- 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 +44 -44
- package/dist/mlkem/index.d.ts +37 -0
- package/dist/{kyber → mlkem}/index.js +24 -34
- package/dist/mlkem/kem.d.ts +21 -0
- package/dist/{kyber → mlkem}/kem.js +44 -64
- 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 +3 -3
- package/dist/{kyber → mlkem}/types.js +1 -1
- package/dist/{kyber → mlkem}/validate.d.ts +7 -7
- package/dist/{kyber → mlkem}/validate.js +7 -7
- package/dist/{kyber.wasm → mlkem.wasm} +0 -0
- package/dist/p256.wasm +0 -0
- package/dist/ratchet/index.d.ts +2 -0
- package/dist/ratchet/index.js +1 -0
- package/dist/ratchet/kdf-chain.js +3 -3
- package/dist/ratchet/ratchet-keypair.js +2 -2
- package/dist/ratchet/root-kdf.js +7 -7
- package/dist/ratchet/skipped-key-store.js +4 -4
- package/dist/ratchet/types.d.ts +1 -1
- package/dist/serpent/cipher-suite.js +20 -17
- package/dist/serpent/generator.d.ts +1 -1
- package/dist/serpent/generator.js +2 -2
- package/dist/serpent/index.d.ts +8 -7
- package/dist/serpent/index.js +18 -27
- package/dist/serpent/pool-worker.js +7 -5
- package/dist/serpent/serpent-cbc.d.ts +4 -4
- package/dist/serpent/serpent-cbc.js +11 -8
- package/dist/serpent/shared-ops.d.ts +3 -23
- package/dist/serpent/shared-ops.js +50 -85
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hkdf.js +5 -5
- package/dist/sha2/index.d.ts +21 -1
- package/dist/sha2/index.js +65 -10
- package/dist/sha2/types.d.ts +41 -2
- package/dist/sha2.wasm +0 -0
- package/dist/sha3/index.d.ts +72 -3
- package/dist/sha3/index.js +240 -14
- 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 +3 -3
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/open-stream.js +31 -10
- package/dist/stream/seal-stream-pool.d.ts +1 -0
- package/dist/stream/seal-stream-pool.js +63 -26
- package/dist/stream/seal-stream.d.ts +1 -1
- package/dist/stream/seal-stream.js +20 -9
- 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 +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +46 -54
- package/dist/wasm-source.d.ts +7 -7
- 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 +70 -26
- package/SECURITY.md +0 -163
- package/dist/ct-wasm.d.ts +0 -1
- package/dist/ct-wasm.js +0 -3
- package/dist/docs/aead.md +0 -363
- package/dist/docs/architecture.md +0 -1011
- package/dist/docs/argon2id.md +0 -305
- package/dist/docs/chacha20.md +0 -781
- package/dist/docs/exports.md +0 -277
- package/dist/docs/fortuna.md +0 -530
- package/dist/docs/init.md +0 -301
- package/dist/docs/loader.md +0 -256
- package/dist/docs/serpent.md +0 -617
- package/dist/docs/sha2.md +0 -671
- package/dist/docs/sha3.md +0 -612
- package/dist/docs/types.md +0 -416
- package/dist/docs/utils.md +0 -457
- 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 -12
- /package/dist/{ct.wasm → cte.wasm} +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Hasher, MerkleTree } from './tree.js';
|
|
2
|
+
import type { MerkleStorage } from './storage.js';
|
|
3
|
+
/**
|
|
4
|
+
* RFC 9162 §2.1.1, Merkle Hash Trees. The CT-flavoured SHA-256 hash
|
|
5
|
+
* function: empty-tree value `MTH({}) = SHA-256()`, leaf prefix `0x00`,
|
|
6
|
+
* internal-node prefix `0x01`.
|
|
7
|
+
*
|
|
8
|
+
* Stateless and reentrant: each method takes the sha2 module fresh,
|
|
9
|
+
* runs SHA-256 once, and releases. No `dispose()` is needed.
|
|
10
|
+
*/
|
|
11
|
+
export declare const Sha256Hasher: Hasher;
|
|
12
|
+
/**
|
|
13
|
+
* Stateful SHA-256 Merkle log. Stores leaf hashes and every perfect
|
|
14
|
+
* aligned internal subtree's hash via the injected `MerkleStorage`;
|
|
15
|
+
* partial right-edge subtrees are recomputed on demand from the
|
|
16
|
+
* stored perfect subtrees.
|
|
17
|
+
*
|
|
18
|
+
* Constructed empty. `append` is the only mutator and is the leaf-hash
|
|
19
|
+
* factory; consumers feed leaf bytes, not pre-computed leaf hashes.
|
|
20
|
+
*/
|
|
21
|
+
export declare class Sha256Tree implements MerkleTree {
|
|
22
|
+
readonly hasher: Hasher;
|
|
23
|
+
private readonly storage;
|
|
24
|
+
constructor(storage: MerkleStorage);
|
|
25
|
+
size(): number;
|
|
26
|
+
rootHash(): Uint8Array;
|
|
27
|
+
append(leafBytes: Uint8Array): {
|
|
28
|
+
leafIndex: number;
|
|
29
|
+
leafHash: Uint8Array;
|
|
30
|
+
};
|
|
31
|
+
getInclusionProof(leafIndex: number, treeSize?: number): Uint8Array[];
|
|
32
|
+
getConsistencyProof(oldSize: number, newSize: number): Uint8Array[];
|
|
33
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
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/sha256-tree.ts
|
|
23
|
+
//
|
|
24
|
+
// SHA-256 specialisation of the Hasher / MerkleTree interfaces. Wraps
|
|
25
|
+
// the existing `SHA256` class from the sha2 module under the RFC 9162
|
|
26
|
+
// (Certificate Transparency Version 2.0) §2.1.1 leaf and internal-node
|
|
27
|
+
// domain separators.
|
|
28
|
+
//
|
|
29
|
+
// Per-call WASM lifecycle: every Sha256Hasher method instantiates a
|
|
30
|
+
// fresh SHA256 inside a try / finally + dispose pattern. There is no
|
|
31
|
+
// long-lived module ownership; concurrent users are serialised by the
|
|
32
|
+
// per-module exclusivity guard in the sha2 substrate. This mirrors
|
|
33
|
+
// the SignatureSuite factories under src/ts/sign/suites/.
|
|
34
|
+
import { SHA256 } from '../sha2/index.js';
|
|
35
|
+
import { buildConsistencyProof, buildInclusionProof, subtreeHash, } from './proof.js';
|
|
36
|
+
// ── Sha256Hasher const ──────────────────────────────────────────────────────
|
|
37
|
+
const SHA256_OUTPUT = 32;
|
|
38
|
+
const LEAF_PREFIX = new Uint8Array([0x00]);
|
|
39
|
+
const INTERNAL_PREFIX = new Uint8Array([0x01]);
|
|
40
|
+
const SHA256_WASM_MODULES = Object.freeze(['sha2']);
|
|
41
|
+
function sha256Hash(input) {
|
|
42
|
+
const h = new SHA256();
|
|
43
|
+
try {
|
|
44
|
+
return h.hash(input);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
h.dispose();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* RFC 9162 §2.1.1, Merkle Hash Trees. The CT-flavoured SHA-256 hash
|
|
52
|
+
* function: empty-tree value `MTH({}) = SHA-256()`, leaf prefix `0x00`,
|
|
53
|
+
* internal-node prefix `0x01`.
|
|
54
|
+
*
|
|
55
|
+
* Stateless and reentrant: each method takes the sha2 module fresh,
|
|
56
|
+
* runs SHA-256 once, and releases. No `dispose()` is needed.
|
|
57
|
+
*/
|
|
58
|
+
export const Sha256Hasher = Object.freeze({
|
|
59
|
+
name: 'sha256',
|
|
60
|
+
outputSize: SHA256_OUTPUT,
|
|
61
|
+
wasmModules: SHA256_WASM_MODULES,
|
|
62
|
+
hashEmpty() {
|
|
63
|
+
// RFC 9162 §2.1.1: MTH({}) is the hash of an empty bit-string.
|
|
64
|
+
return sha256Hash(new Uint8Array(0));
|
|
65
|
+
},
|
|
66
|
+
hashLeaf(leaf) {
|
|
67
|
+
// RFC 9162 §2.1.1: MTH({d}) = HASH(0x00 || d). The 0x00 prefix
|
|
68
|
+
// is the domain separator that prevents an internal-node hash
|
|
69
|
+
// from being mistaken for a leaf hash.
|
|
70
|
+
const buf = new Uint8Array(1 + leaf.length);
|
|
71
|
+
buf.set(LEAF_PREFIX, 0);
|
|
72
|
+
buf.set(leaf, 1);
|
|
73
|
+
return sha256Hash(buf);
|
|
74
|
+
},
|
|
75
|
+
hashInternal(left, right) {
|
|
76
|
+
// RFC 9162 §2.1.1: MTH(D[n]) = HASH(0x01 || MTH(D[0:k]) ||
|
|
77
|
+
// MTH(D[k:n])). The 0x01 prefix is the other half of the
|
|
78
|
+
// second-preimage-resistance domain separator.
|
|
79
|
+
const buf = new Uint8Array(1 + left.length + right.length);
|
|
80
|
+
buf.set(INTERNAL_PREFIX, 0);
|
|
81
|
+
buf.set(left, 1);
|
|
82
|
+
buf.set(right, 1 + left.length);
|
|
83
|
+
return sha256Hash(buf);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
// ── Sha256Tree class ────────────────────────────────────────────────────────
|
|
87
|
+
/**
|
|
88
|
+
* Stateful SHA-256 Merkle log. Stores leaf hashes and every perfect
|
|
89
|
+
* aligned internal subtree's hash via the injected `MerkleStorage`;
|
|
90
|
+
* partial right-edge subtrees are recomputed on demand from the
|
|
91
|
+
* stored perfect subtrees.
|
|
92
|
+
*
|
|
93
|
+
* Constructed empty. `append` is the only mutator and is the leaf-hash
|
|
94
|
+
* factory; consumers feed leaf bytes, not pre-computed leaf hashes.
|
|
95
|
+
*/
|
|
96
|
+
export class Sha256Tree {
|
|
97
|
+
hasher = Sha256Hasher;
|
|
98
|
+
storage;
|
|
99
|
+
constructor(storage) {
|
|
100
|
+
this.storage = storage;
|
|
101
|
+
}
|
|
102
|
+
size() {
|
|
103
|
+
return this.storage.size();
|
|
104
|
+
}
|
|
105
|
+
rootHash() {
|
|
106
|
+
const n = this.storage.size();
|
|
107
|
+
if (n === 0)
|
|
108
|
+
return this.hasher.hashEmpty();
|
|
109
|
+
const getNode = (level, index) => this.storage.getNode(level, index);
|
|
110
|
+
return subtreeHash(this.hasher, 0, n, getNode);
|
|
111
|
+
}
|
|
112
|
+
append(leafBytes) {
|
|
113
|
+
const leafIndex = this.storage.size();
|
|
114
|
+
const leafHash = this.hasher.hashLeaf(leafBytes);
|
|
115
|
+
this.storage.appendLeaf(leafIndex, leafHash);
|
|
116
|
+
// Propagate completed internal nodes up the right edge. RFC 9162
|
|
117
|
+
// §2.1.1 makes the tree fill left-to-right; whenever a node lands
|
|
118
|
+
// at an odd index its left sibling already exists, so the parent
|
|
119
|
+
// becomes computable for free.
|
|
120
|
+
let level = 0;
|
|
121
|
+
let idx = leafIndex;
|
|
122
|
+
while ((idx & 1) === 1) {
|
|
123
|
+
const left = this.storage.getNode(level, idx - 1);
|
|
124
|
+
const right = this.storage.getNode(level, idx);
|
|
125
|
+
const parent = this.hasher.hashInternal(left, right);
|
|
126
|
+
this.storage.putNode(level + 1, idx >>> 1, parent);
|
|
127
|
+
idx = idx >>> 1;
|
|
128
|
+
level++;
|
|
129
|
+
}
|
|
130
|
+
return { leafIndex, leafHash };
|
|
131
|
+
}
|
|
132
|
+
getInclusionProof(leafIndex, treeSize) {
|
|
133
|
+
const ts = treeSize ?? this.storage.size();
|
|
134
|
+
if (!Number.isInteger(ts) || ts < 1 || ts > this.storage.size())
|
|
135
|
+
throw new RangeError(`Sha256Tree.getInclusionProof: treeSize ${ts} out of range [1, ${this.storage.size()}]`);
|
|
136
|
+
const getNode = (level, index) => this.storage.getNode(level, index);
|
|
137
|
+
return buildInclusionProof({ hasher: this.hasher, leafIndex, treeSize: ts, getNode });
|
|
138
|
+
}
|
|
139
|
+
getConsistencyProof(oldSize, newSize) {
|
|
140
|
+
if (!Number.isInteger(newSize) || newSize < 0 || newSize > this.storage.size())
|
|
141
|
+
throw new RangeError(`Sha256Tree.getConsistencyProof: newSize ${newSize} out of range [0, ${this.storage.size()}]`);
|
|
142
|
+
const getNode = (level, index) => this.storage.getNode(level, index);
|
|
143
|
+
return buildConsistencyProof({ hasher: this.hasher, oldSize, newSize, getNode });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { SignedTreeHead } from './sth.js';
|
|
2
|
+
import type { MerkleTree } from './tree.js';
|
|
3
|
+
import type { SignatureSuite } from '../sign/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Constructor options for `SignedLog`.
|
|
6
|
+
*/
|
|
7
|
+
export interface SignedLogOpts<S extends SignatureSuite> {
|
|
8
|
+
/** Underlying Merkle tree, holds the stateful append + proof surface. */
|
|
9
|
+
tree: MerkleTree;
|
|
10
|
+
/**
|
|
11
|
+
* Signature suite. Must have an entry in the C2SP cosignature
|
|
12
|
+
* algorithm-byte registry (currently `Ed25519Suite` and
|
|
13
|
+
* `MlDsa44Suite`); other suites throw `SigningError`.
|
|
14
|
+
*/
|
|
15
|
+
suite: S;
|
|
16
|
+
/**
|
|
17
|
+
* Log identity, the first line of every checkpoint body. Validated
|
|
18
|
+
* at construction (non-empty, no whitespace, no plus characters)
|
|
19
|
+
* per c2sp.org/tlog-checkpoint §Note text.
|
|
20
|
+
*/
|
|
21
|
+
origin: string;
|
|
22
|
+
/**
|
|
23
|
+
* Signing key, exactly `suite.skSize` bytes. The SignedLog stores
|
|
24
|
+
* a private copy; `dispose()` zeroes that copy. The caller's view
|
|
25
|
+
* of the buffer is left untouched.
|
|
26
|
+
*/
|
|
27
|
+
signingKey: Uint8Array;
|
|
28
|
+
/**
|
|
29
|
+
* Public key, exactly `suite.pkSize` bytes. Used to derive the
|
|
30
|
+
* 4-byte keyId stamped on every emitted signature line and to
|
|
31
|
+
* match incoming signature lines during verify.
|
|
32
|
+
*/
|
|
33
|
+
pubkey: Uint8Array;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Signed transparency log substrate. Combines a `MerkleTree` with a
|
|
37
|
+
* registered cosignature `SignatureSuite` and an origin string;
|
|
38
|
+
* exposes append, proof, and cosignature sign / verify operations.
|
|
39
|
+
*
|
|
40
|
+
* Per-call WASM lifecycle is enforced by the suite itself (see the
|
|
41
|
+
* SignatureSuite factories under `src/ts/sign/suites/`). `SignedLog`
|
|
42
|
+
* does not wrap additional try/finally around `suite.sign` /
|
|
43
|
+
* `suite.verify` because the suite already does. Internally the
|
|
44
|
+
* SignedLog owns a private copy of the signing key wiped by
|
|
45
|
+
* `dispose()`.
|
|
46
|
+
*/
|
|
47
|
+
export declare class SignedLog<S extends SignatureSuite> {
|
|
48
|
+
readonly tree: MerkleTree;
|
|
49
|
+
readonly suite: S;
|
|
50
|
+
readonly origin: string;
|
|
51
|
+
readonly pubkey: Uint8Array;
|
|
52
|
+
readonly wasmModules: readonly string[];
|
|
53
|
+
private readonly _algoEntry;
|
|
54
|
+
private readonly _keyId;
|
|
55
|
+
private _signingKey;
|
|
56
|
+
private _disposed;
|
|
57
|
+
constructor(opts: SignedLogOpts<S>);
|
|
58
|
+
/**
|
|
59
|
+
* Append a leaf to the underlying tree and return the new leaf's
|
|
60
|
+
* index, hash, and inclusion proof against the post-append tree size.
|
|
61
|
+
*/
|
|
62
|
+
append(leafBytes: Uint8Array): {
|
|
63
|
+
leafIndex: number;
|
|
64
|
+
leafHash: Uint8Array;
|
|
65
|
+
inclusionProof: Uint8Array[];
|
|
66
|
+
};
|
|
67
|
+
size(): number;
|
|
68
|
+
rootHash(): Uint8Array;
|
|
69
|
+
getInclusionProof(leafIndex: number, treeSize?: number): Uint8Array[];
|
|
70
|
+
getConsistencyProof(oldSize: number, newSize: number): Uint8Array[];
|
|
71
|
+
/**
|
|
72
|
+
* Issue a cosignature over the current checkpoint and emit the
|
|
73
|
+
* signed-note envelope per c2sp.org/signed-note §Format. The
|
|
74
|
+
* signature line carries the `timestamped_signature` payload
|
|
75
|
+
* from c2sp.org/tlog-cosignature §Format; the bytes the suite
|
|
76
|
+
* signs are dispatched on the algorithm's
|
|
77
|
+
* `messageConstruction`:
|
|
78
|
+
*
|
|
79
|
+
* - `'cosig'` → `buildCosigSignedMessage(body, ts)`
|
|
80
|
+
* (Ed25519, §"Ed25519 signed message")
|
|
81
|
+
* - `'cosigned-message'` → `buildCosignedMessage(...)`
|
|
82
|
+
* (ML-DSA-44, §"ML-DSA-44 signed message")
|
|
83
|
+
*
|
|
84
|
+
* `timestamp` defaults to current wall-clock POSIX seconds. The
|
|
85
|
+
* c2sp.org/tlog-witness `add-checkpoint` rule mandates a non-zero
|
|
86
|
+
* timestamp on production cosignatures; `0` is accepted by this
|
|
87
|
+
* function for test reproducibility but witness verifiers will
|
|
88
|
+
* reject envelopes that carry it. Tests and vector generators
|
|
89
|
+
* pass an explicit value to lock byte stability.
|
|
90
|
+
*/
|
|
91
|
+
signCheckpoint(opts?: {
|
|
92
|
+
timestamp?: number;
|
|
93
|
+
}): Uint8Array;
|
|
94
|
+
/**
|
|
95
|
+
* Parse a signed-note envelope into the structured `SignedTreeHead`
|
|
96
|
+
* form per c2sp.org/signed-note §Format. Surfaces the body's
|
|
97
|
+
* decoded `Checkpoint`, the signature lines that survived the
|
|
98
|
+
* permissive signed-note parse, and the primary log cosignature's
|
|
99
|
+
* POSIX-seconds timestamp (extracted via
|
|
100
|
+
* `parseCosigSignaturePayload` on the line whose keyId matches
|
|
101
|
+
* this log's pubkey-derived keyId).
|
|
102
|
+
*
|
|
103
|
+
* If no signature line matches, `timestamp` is reported as 0. The
|
|
104
|
+
* field is informational at parse time; cryptographic verification
|
|
105
|
+
* lives in `verifyCheckpoint`. Throws `RangeError` on whole-envelope
|
|
106
|
+
* structural failure (the parseSignedNote / parseCheckpointBody
|
|
107
|
+
* contract); does not throw on signature line content issues.
|
|
108
|
+
*/
|
|
109
|
+
parseCheckpoint(bytes: Uint8Array): SignedTreeHead;
|
|
110
|
+
/**
|
|
111
|
+
* Verify a signed-note envelope against this SignedLog's origin,
|
|
112
|
+
* pubkey, suite, and tree hasher. Returns `true` iff the envelope
|
|
113
|
+
* parses, carries a signature line whose keyId matches this log's
|
|
114
|
+
* pubkey-derived keyId, the `timestamped_signature` payload on
|
|
115
|
+
* that line decodes cleanly, and the signature verifies under
|
|
116
|
+
* `suite.verify` over the cosignature signed message reconstructed
|
|
117
|
+
* with the parsed timestamp.
|
|
118
|
+
*
|
|
119
|
+
* Returns `false` on every soft-fail mode: wrong origin, wrong
|
|
120
|
+
* root-hash length, no matching keyId line, malformed payload,
|
|
121
|
+
* signature failure. Throws only on this log's own disposed
|
|
122
|
+
* state; never on envelope content (envelope content is public,
|
|
123
|
+
* so timing distinctions on its content are not security-sensitive).
|
|
124
|
+
*
|
|
125
|
+
* The keyId comparison uses `constantTimeEqual` for hygiene around
|
|
126
|
+
* key-material-adjacent state; the origin and root-hash-length
|
|
127
|
+
* early returns are intentional non-constant-time exits since
|
|
128
|
+
* both fields are public per the spec.
|
|
129
|
+
*/
|
|
130
|
+
verifyCheckpoint(bytes: Uint8Array): boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Zero the stored signing-key copy. Idempotent. Subsequent calls
|
|
133
|
+
* to any public method throw.
|
|
134
|
+
*/
|
|
135
|
+
dispose(): void;
|
|
136
|
+
private _assertNotDisposed;
|
|
137
|
+
/**
|
|
138
|
+
* Dispatch the cosignature signed-message construction on the
|
|
139
|
+
* algorithm-byte registry entry's `messageConstruction`. The
|
|
140
|
+
* `body` argument is the canonical checkpoint body from
|
|
141
|
+
* `serializeCheckpointBody`, ending in 0x0A.
|
|
142
|
+
*
|
|
143
|
+
* 'cosig' c2sp.org/tlog-cosignature §"Ed25519 signed
|
|
144
|
+
* message". The full envelope body is
|
|
145
|
+
* embedded verbatim after the
|
|
146
|
+
* cosignature/v1 + time prefix.
|
|
147
|
+
*
|
|
148
|
+
* 'cosigned-message' c2sp.org/tlog-cosignature §"ML-DSA-44
|
|
149
|
+
* signed message". The body is decomposed
|
|
150
|
+
* into origin, tree size, and root hash;
|
|
151
|
+
* cosigner_name == origin (Phase 7 logs sign
|
|
152
|
+
* their own checkpoints); start == 0; end ==
|
|
153
|
+
* tree size; hash == root hash.
|
|
154
|
+
*/
|
|
155
|
+
private _buildSignedMessage;
|
|
156
|
+
}
|