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
package/dist/init.js
CHANGED
|
@@ -1,32 +1,98 @@
|
|
|
1
1
|
import { loadWasm } from './loader.js';
|
|
2
2
|
import { hasSIMD } from './utils.js';
|
|
3
|
-
// 'keccak' is an alias for 'sha3'
|
|
3
|
+
// 'keccak' is an alias for 'sha3', same WASM binary, same instance slot
|
|
4
4
|
const ALIASES = { keccak: 'sha3' };
|
|
5
5
|
function resolve(mod) {
|
|
6
6
|
return ALIASES[mod] ?? mod;
|
|
7
7
|
}
|
|
8
|
-
//
|
|
8
|
+
// Per-canonical-module instance cache.
|
|
9
9
|
const instances = new Map();
|
|
10
|
+
// Coalesces concurrent initModule calls.
|
|
11
|
+
const pending = new Map();
|
|
12
|
+
// Per-module exclusivity token held by a stateful wrapper.
|
|
13
|
+
const owners = new Map();
|
|
10
14
|
export async function initModule(mod, source) {
|
|
11
15
|
const resolved = resolve(mod);
|
|
12
16
|
if (instances.has(resolved))
|
|
13
17
|
return;
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
const inflight = pending.get(resolved);
|
|
19
|
+
if (inflight) {
|
|
20
|
+
await inflight;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if ((resolved === 'serpent' || resolved === 'chacha20' || resolved === 'mlkem' || resolved === 'aes' || resolved === 'mldsa' || resolved === 'blake3') && !hasSIMD())
|
|
24
|
+
throw new Error('leviathan-crypto: serpent, chacha20, mlkem, aes, mldsa, and blake3 require WebAssembly SIMD, '
|
|
16
25
|
+ 'this runtime does not support it');
|
|
17
|
-
|
|
26
|
+
const p = loadWasm(source);
|
|
27
|
+
pending.set(resolved, p);
|
|
28
|
+
try {
|
|
29
|
+
instances.set(resolved, await p);
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
pending.delete(resolved);
|
|
33
|
+
}
|
|
18
34
|
}
|
|
19
35
|
export function getInstance(mod) {
|
|
20
|
-
const
|
|
36
|
+
const r = resolve(mod);
|
|
37
|
+
const inst = instances.get(r);
|
|
21
38
|
if (!inst) {
|
|
22
39
|
throw new Error(`leviathan-crypto: call init({ ${mod}: ... }) before using this class`);
|
|
23
40
|
}
|
|
41
|
+
if (owners.has(r)) {
|
|
42
|
+
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module, `
|
|
43
|
+
+ 'call dispose() on it before constructing a new one');
|
|
44
|
+
}
|
|
24
45
|
return inst;
|
|
25
46
|
}
|
|
26
47
|
export function isInitialized(mod) {
|
|
27
48
|
return instances.has(resolve(mod));
|
|
28
49
|
}
|
|
29
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Acquire exclusive access to `mod`. Throws if another stateful instance
|
|
52
|
+
* currently holds it. Returned token must be passed to `_releaseModule`.
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export function _acquireModule(mod) {
|
|
56
|
+
const r = resolve(mod);
|
|
57
|
+
if (owners.has(r))
|
|
58
|
+
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module, `
|
|
59
|
+
+ 'call dispose() on it before constructing a new one');
|
|
60
|
+
const tok = Symbol(r);
|
|
61
|
+
owners.set(r, tok);
|
|
62
|
+
return tok;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Release exclusive access. No-op if the token doesn't match the current
|
|
66
|
+
* owner (makes dispose idempotent).
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export function _releaseModule(mod, tok) {
|
|
70
|
+
const r = resolve(mod);
|
|
71
|
+
if (owners.get(r) === tok)
|
|
72
|
+
owners.delete(r);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* True if a stateful instance currently holds the module.
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
export function _isModuleBusy(mod) {
|
|
79
|
+
return owners.has(resolve(mod));
|
|
80
|
+
}
|
|
81
|
+
/** @internal */
|
|
82
|
+
export function _assertNotOwned(mod) {
|
|
83
|
+
const r = resolve(mod);
|
|
84
|
+
if (owners.has(r))
|
|
85
|
+
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module, `
|
|
86
|
+
+ 'call dispose() on it before constructing a new one');
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Reset all cached instances, for testing only. Clears `instances`, `pending`,
|
|
90
|
+
* and `owners` so tests can re-exercise module lifecycle (init, exclusivity,
|
|
91
|
+
* race) from a known-empty state.
|
|
92
|
+
* @internal
|
|
93
|
+
*/
|
|
30
94
|
export function _resetForTesting() {
|
|
31
95
|
instances.clear();
|
|
96
|
+
pending.clear();
|
|
97
|
+
owners.clear();
|
|
32
98
|
}
|
package/dist/keccak/embedded.js
CHANGED
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
// src/ts/keccak/embedded.ts
|
|
23
23
|
//
|
|
24
24
|
// Re-exports the sha3 WASM blob under the keccak name.
|
|
25
|
-
// The keccak alias shares the sha3 binary
|
|
25
|
+
// The keccak alias shares the sha3 binary, no separate keccak.wasm exists.
|
|
26
26
|
// Import via `leviathan-crypto/keccak/embedded`.
|
|
27
27
|
export { WASM_GZ_BASE64 as keccakWasm } from '../embedded/sha3.js';
|
package/dist/keccak/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { WasmSource } from '../wasm-source.js';
|
|
2
2
|
export declare function keccakInit(source: WasmSource): Promise<void>;
|
|
3
3
|
export type { WasmSource };
|
|
4
|
+
export { isInitialized } from '../init.js';
|
|
4
5
|
export { SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256 } from '../sha3/index.js';
|
|
6
|
+
export { CSHAKE128, CSHAKE256, KMAC128, KMAC256, KMACXOF128, KMACXOF256 } from '../sha3/index.js';
|
package/dist/keccak/index.js
CHANGED
|
@@ -21,11 +21,13 @@
|
|
|
21
21
|
//
|
|
22
22
|
// src/ts/keccak/index.ts
|
|
23
23
|
//
|
|
24
|
-
// Keccak alias subpath
|
|
25
|
-
// Consumers who prefer the Keccak name (e.g.
|
|
24
|
+
// Keccak alias subpath, resolves to the sha3 WASM module slot.
|
|
25
|
+
// Consumers who prefer the Keccak name (e.g. ML-KEM implementations)
|
|
26
26
|
// can import from `leviathan-crypto/keccak` instead of `leviathan-crypto/sha3`.
|
|
27
27
|
import { initModule } from '../init.js';
|
|
28
28
|
export async function keccakInit(source) {
|
|
29
29
|
return initModule('keccak', source);
|
|
30
30
|
}
|
|
31
|
+
export { isInitialized } from '../init.js';
|
|
31
32
|
export { SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256 } from '../sha3/index.js';
|
|
33
|
+
export { CSHAKE128, CSHAKE256, KMAC128, KMAC256, KMACXOF128, KMACXOF256 } from '../sha3/index.js';
|
package/dist/loader.d.ts
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Decode a gzip+base64 embedded WASM string to raw bytes.
|
|
4
|
-
* Guards against missing DecompressionStream (Node <18, non-browser runtimes).
|
|
5
|
-
* Exported for pool worker launchers that decode blobs before spawning threads.
|
|
6
|
-
*/
|
|
7
|
-
export declare function decodeWasm(b64: string): Promise<Uint8Array>;
|
|
8
|
-
/**
|
|
9
|
-
* Compile a WASM source to a Module without instantiating.
|
|
10
|
-
* Used by pool infrastructure to send compiled modules to workers.
|
|
11
|
-
*/
|
|
12
|
-
export declare function compileWasm(source: WasmSource): Promise<WebAssembly.Module>;
|
|
13
|
-
/**
|
|
14
|
-
* Load a WASM module from any accepted source type.
|
|
15
|
-
* The loading strategy is inferred from the argument type — no mode string.
|
|
16
|
-
*
|
|
17
|
-
* Throws `TypeError` for null, numeric, or unrecognised inputs.
|
|
18
|
-
*/
|
|
19
|
-
export declare function loadWasm(source: WasmSource): Promise<WebAssembly.Instance>;
|
|
1
|
+
export {};
|
package/dist/loader.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { base64ToBytes as _b64 } from './utils.js';
|
|
2
|
-
// Each WASM module gets its own fresh Memory — never shared between instances.
|
|
3
|
-
function makeImports() {
|
|
4
|
-
return { env: { memory: new WebAssembly.Memory({ initial: 3, maximum: 3 }) } };
|
|
5
|
-
}
|
|
6
2
|
// TS 5.9 generified Uint8Array<TArrayBuffer> with default ArrayBufferLike, which
|
|
7
3
|
// no longer satisfies BufferSource = ArrayBufferView<ArrayBuffer> | ArrayBuffer.
|
|
8
4
|
// Convert Uint8Array to a proper ArrayBuffer before calling WebAssembly APIs.
|
|
@@ -17,14 +13,14 @@ function toArrayBuffer(bytes) {
|
|
|
17
13
|
* Decode a gzip+base64 embedded WASM string to raw bytes.
|
|
18
14
|
* Guards against missing DecompressionStream (Node <18, non-browser runtimes).
|
|
19
15
|
* Exported for pool worker launchers that decode blobs before spawning threads.
|
|
16
|
+
* @internal
|
|
20
17
|
*/
|
|
21
18
|
export async function decodeWasm(b64) {
|
|
22
19
|
if (typeof DecompressionStream === 'undefined')
|
|
23
|
-
throw new Error('leviathan-crypto: DecompressionStream not available
|
|
20
|
+
throw new Error('leviathan-crypto: DecompressionStream not available, '
|
|
24
21
|
+ 'use a URL, ArrayBuffer, or WebAssembly.Module source in this runtime');
|
|
22
|
+
// _b64 throws RangeError on invalid base64, no nullish check required.
|
|
25
23
|
const compressed = _b64(b64);
|
|
26
|
-
if (!compressed)
|
|
27
|
-
throw new Error('leviathan-crypto: corrupt embedded WASM — base64 decode failed');
|
|
28
24
|
const ds = new DecompressionStream('gzip');
|
|
29
25
|
const writer = ds.writable.getWriter();
|
|
30
26
|
const reader = ds.readable.getReader();
|
|
@@ -44,14 +40,23 @@ export async function decodeWasm(b64) {
|
|
|
44
40
|
}
|
|
45
41
|
return out;
|
|
46
42
|
}
|
|
43
|
+
// Cap thenable-source nesting at 3 to prevent runaway recursion.
|
|
44
|
+
const MAX_THENABLE_DEPTH = 3;
|
|
47
45
|
/**
|
|
48
46
|
* Compile a WASM source to a Module without instantiating.
|
|
49
47
|
* Used by pool infrastructure to send compiled modules to workers.
|
|
48
|
+
*
|
|
49
|
+
* Thenable sources (Promise<Response>, Promise<ArrayBuffer>, etc.) are
|
|
50
|
+
* resolved and then re-dispatched by the runtime type of the resolved value.
|
|
51
|
+
* Depth is capped at `MAX_THENABLE_DEPTH` to prevent runaway recursion.
|
|
52
|
+
* @internal
|
|
50
53
|
*/
|
|
51
|
-
export async function compileWasm(source) {
|
|
54
|
+
export async function compileWasm(source, depth = 0) {
|
|
55
|
+
if (depth > MAX_THENABLE_DEPTH)
|
|
56
|
+
throw new TypeError(`leviathan-crypto: thenable nesting too deep (max ${MAX_THENABLE_DEPTH})`);
|
|
52
57
|
if (typeof source === 'string') {
|
|
53
58
|
if (source.length === 0)
|
|
54
|
-
throw new TypeError('leviathan-crypto: invalid WasmSource
|
|
59
|
+
throw new TypeError('leviathan-crypto: invalid WasmSource, empty string');
|
|
55
60
|
return WebAssembly.compile(toArrayBuffer(await decodeWasm(source)));
|
|
56
61
|
}
|
|
57
62
|
if (source instanceof URL)
|
|
@@ -64,33 +69,22 @@ export async function compileWasm(source) {
|
|
|
64
69
|
return source;
|
|
65
70
|
if (typeof Response !== 'undefined' && source instanceof Response)
|
|
66
71
|
return WebAssembly.compileStreaming(source);
|
|
67
|
-
if (source != null && typeof source.then === 'function')
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
if (source != null && typeof source.then === 'function') {
|
|
73
|
+
const resolved = await source;
|
|
74
|
+
return compileWasm(resolved, depth + 1);
|
|
75
|
+
}
|
|
76
|
+
throw new TypeError(`leviathan-crypto: invalid WasmSource, got ${source === null ? 'null' : typeof source}`);
|
|
70
77
|
}
|
|
71
78
|
/**
|
|
72
79
|
* Load a WASM module from any accepted source type.
|
|
73
|
-
* The loading strategy is inferred from the argument type
|
|
80
|
+
* The loading strategy is inferred from the argument type, no mode string.
|
|
74
81
|
*
|
|
75
|
-
* Throws `TypeError` for null, numeric, or unrecognised inputs
|
|
82
|
+
* Throws `TypeError` for null, numeric, or unrecognised inputs, or if a
|
|
83
|
+
* thenable source nests deeper than `MAX_THENABLE_DEPTH`.
|
|
84
|
+
* @internal
|
|
76
85
|
*/
|
|
77
86
|
export async function loadWasm(source) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return (await WebAssembly.instantiate(toArrayBuffer(await decodeWasm(source)), makeImports())).instance;
|
|
82
|
-
}
|
|
83
|
-
if (source instanceof URL)
|
|
84
|
-
return (await WebAssembly.instantiateStreaming(fetch(source.href), makeImports())).instance;
|
|
85
|
-
if (source instanceof ArrayBuffer)
|
|
86
|
-
return (await WebAssembly.instantiate(source, makeImports())).instance;
|
|
87
|
-
if (source instanceof Uint8Array)
|
|
88
|
-
return (await WebAssembly.instantiate(toArrayBuffer(source), makeImports())).instance;
|
|
89
|
-
if (source instanceof WebAssembly.Module)
|
|
90
|
-
return WebAssembly.instantiate(source, makeImports());
|
|
91
|
-
if (typeof Response !== 'undefined' && source instanceof Response)
|
|
92
|
-
return (await WebAssembly.instantiateStreaming(source, makeImports())).instance;
|
|
93
|
-
if (source != null && typeof source.then === 'function')
|
|
94
|
-
return (await WebAssembly.instantiateStreaming(source, makeImports())).instance;
|
|
95
|
-
throw new TypeError(`leviathan-crypto: invalid WasmSource — got ${source === null ? 'null' : typeof source}`);
|
|
87
|
+
// All modules export their own memory; no host imports today.
|
|
88
|
+
const mod = await compileWasm(source);
|
|
89
|
+
return WebAssembly.instantiate(mod);
|
|
96
90
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Hasher, MerkleTree } from './tree.js';
|
|
2
|
+
import type { MerkleStorage } from './storage.js';
|
|
3
|
+
/**
|
|
4
|
+
* BLAKE3-native `Hasher` (BLAKE3 §2.3, §2.4, §2.5).
|
|
5
|
+
*
|
|
6
|
+
* Empty-tree value is `BLAKE3()`; leaves are `BLAKE3(leaf)`; internal
|
|
7
|
+
* nodes are the §2.5 parent compress over the two child CVs with the
|
|
8
|
+
* BLAKE3 IV as the starting CV and `modeFlags = 0`, `isRoot = 0`.
|
|
9
|
+
*
|
|
10
|
+
* Stateless and reentrant: each method acquires the blake3 module
|
|
11
|
+
* fresh, runs the operation, and releases. No `dispose()` is needed.
|
|
12
|
+
*/
|
|
13
|
+
export declare const Blake3Hasher: Hasher;
|
|
14
|
+
/**
|
|
15
|
+
* Stateful BLAKE3 Merkle log. Same surface and storage discipline as
|
|
16
|
+
* `Sha256Tree`: leaf hashes and every perfect aligned internal subtree
|
|
17
|
+
* hash live in the injected `MerkleStorage`; partial right-edge subtrees
|
|
18
|
+
* are recomputed on demand.
|
|
19
|
+
*
|
|
20
|
+
* `append` is the only mutator and is the leaf-hash factory; consumers
|
|
21
|
+
* feed leaf bytes, not pre-computed leaf hashes.
|
|
22
|
+
*/
|
|
23
|
+
export declare class Blake3Tree implements MerkleTree {
|
|
24
|
+
readonly hasher: Hasher;
|
|
25
|
+
private readonly storage;
|
|
26
|
+
constructor(storage: MerkleStorage);
|
|
27
|
+
size(): number;
|
|
28
|
+
rootHash(): Uint8Array;
|
|
29
|
+
append(leafBytes: Uint8Array): {
|
|
30
|
+
leafIndex: number;
|
|
31
|
+
leafHash: Uint8Array;
|
|
32
|
+
};
|
|
33
|
+
getInclusionProof(leafIndex: number, treeSize?: number): Uint8Array[];
|
|
34
|
+
getConsistencyProof(oldSize: number, newSize: number): Uint8Array[];
|
|
35
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
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/blake3-tree.ts
|
|
23
|
+
//
|
|
24
|
+
// BLAKE3 Hasher / MerkleTree. Domain separation comes from BLAKE3's own
|
|
25
|
+
// §2.4 CHUNK_START / CHUNK_END / ROOT and §2.5 PARENT flags, not from
|
|
26
|
+
// RFC 6962-style 0x00 / 0x01 prefix bytes (those would be redundant and
|
|
27
|
+
// discard `compress4` parallelism at the internal-node layer).
|
|
28
|
+
//
|
|
29
|
+
// Composition:
|
|
30
|
+
// hashEmpty() = BLAKE3() §2.5
|
|
31
|
+
// hashLeaf(leaf) = BLAKE3(leaf) §2.4
|
|
32
|
+
// hashInternal(left, right) = _testParentCV(left, right, IV, 0, 0) §2.5
|
|
33
|
+
//
|
|
34
|
+
// Parent compress runs with modeFlags = 0 and isRoot = 0 at every level.
|
|
35
|
+
// The root flag is the SignedLog layer's concern; the tree's top hash
|
|
36
|
+
// exits as a plain CV, keeping `hashInternal` symmetric.
|
|
37
|
+
import { BLAKE3 } from '../blake3/index.js';
|
|
38
|
+
import { getInstance } from '../init.js';
|
|
39
|
+
import { buildConsistencyProof, buildInclusionProof, subtreeHash, } from './proof.js';
|
|
40
|
+
function getBlake3Exports() {
|
|
41
|
+
return getInstance('blake3').exports;
|
|
42
|
+
}
|
|
43
|
+
// ── Scratch layout for `_testParentCV` ──────────────────────────────────────
|
|
44
|
+
//
|
|
45
|
+
// Second-page offsets (past BUFFER_END = 26328 from
|
|
46
|
+
// `src/asm/blake3/buffers.ts`) are untouched by the §2.4 chunk pipeline
|
|
47
|
+
// and §2.5 tree-assembly queues; safe for caller-supplied scratch.
|
|
48
|
+
const PARENT_LEFT_OFF = 65536;
|
|
49
|
+
const PARENT_RIGHT_OFF = PARENT_LEFT_OFF + 32;
|
|
50
|
+
const PARENT_START_OFF = PARENT_RIGHT_OFF + 32;
|
|
51
|
+
const PARENT_OUT_OFF = PARENT_START_OFF + 32;
|
|
52
|
+
const PARENT_SCRATCH_LEN = 128;
|
|
53
|
+
// BLAKE3 §2.2 Table 1: the BLAKE3 IV equals the FIPS 180-4 SHA-256 IV,
|
|
54
|
+
// packed as eight u32 little-endian words. The IV is the starting CV
|
|
55
|
+
// for default-mode parent compresses (BLAKE3 §2.5, Tree Mode).
|
|
56
|
+
const BLAKE3_IV_BYTES = (() => {
|
|
57
|
+
const iv32 = new Uint32Array([
|
|
58
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
|
59
|
+
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
60
|
+
]);
|
|
61
|
+
return new Uint8Array(iv32.buffer);
|
|
62
|
+
})();
|
|
63
|
+
const BLAKE3_OUTPUT = 32;
|
|
64
|
+
const BLAKE3_WASM_MODULES = Object.freeze(['blake3']);
|
|
65
|
+
// ── Blake3Hasher const ──────────────────────────────────────────────────────
|
|
66
|
+
/**
|
|
67
|
+
* BLAKE3-native `Hasher` (BLAKE3 §2.3, §2.4, §2.5).
|
|
68
|
+
*
|
|
69
|
+
* Empty-tree value is `BLAKE3()`; leaves are `BLAKE3(leaf)`; internal
|
|
70
|
+
* nodes are the §2.5 parent compress over the two child CVs with the
|
|
71
|
+
* BLAKE3 IV as the starting CV and `modeFlags = 0`, `isRoot = 0`.
|
|
72
|
+
*
|
|
73
|
+
* Stateless and reentrant: each method acquires the blake3 module
|
|
74
|
+
* fresh, runs the operation, and releases. No `dispose()` is needed.
|
|
75
|
+
*/
|
|
76
|
+
export const Blake3Hasher = Object.freeze({
|
|
77
|
+
name: 'blake3',
|
|
78
|
+
outputSize: BLAKE3_OUTPUT,
|
|
79
|
+
wasmModules: BLAKE3_WASM_MODULES,
|
|
80
|
+
hashEmpty() {
|
|
81
|
+
// BLAKE3 §2.5, Tree Mode: the natural tree-mode root for an
|
|
82
|
+
// empty input is `BLAKE3()` itself. The chunk machine handles
|
|
83
|
+
// the single empty chunk (CHUNK_START | CHUNK_END | ROOT)
|
|
84
|
+
// internally and returns the 32-byte XOF prefix.
|
|
85
|
+
const h = new BLAKE3();
|
|
86
|
+
try {
|
|
87
|
+
return h.hash(new Uint8Array(0));
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
h.dispose();
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
hashLeaf(leaf) {
|
|
94
|
+
// BLAKE3 §2.4, Chunks: the chunk pipeline applies CHUNK_START,
|
|
95
|
+
// CHUNK_END, and ROOT flags internally. The caller sees a plain
|
|
96
|
+
// 32-byte hash; leaf-vs-internal domain separation is BLAKE3's
|
|
97
|
+
// job through these flag bytes, not the caller's job through
|
|
98
|
+
// prefix bytes.
|
|
99
|
+
const h = new BLAKE3();
|
|
100
|
+
try {
|
|
101
|
+
return h.hash(leaf);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
h.dispose();
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
hashInternal(left, right) {
|
|
108
|
+
if (left.length !== BLAKE3_OUTPUT)
|
|
109
|
+
throw new RangeError(`Blake3Hasher.hashInternal: left must be ${BLAKE3_OUTPUT} bytes, got ${left.length}`);
|
|
110
|
+
if (right.length !== BLAKE3_OUTPUT)
|
|
111
|
+
throw new RangeError(`Blake3Hasher.hashInternal: right must be ${BLAKE3_OUTPUT} bytes, got ${right.length}`);
|
|
112
|
+
// BLAKE3 §2.5, Tree Mode: parent compress over (left || right)
|
|
113
|
+
// with IV as the starting CV and PARENT as the only flag bit
|
|
114
|
+
// (modeFlags = 0 selects default mode; isRoot = 0 keeps the
|
|
115
|
+
// node generic so callers can stack identical compresses up
|
|
116
|
+
// the tree).
|
|
117
|
+
const x = getBlake3Exports();
|
|
118
|
+
const mem = new Uint8Array(x.memory.buffer);
|
|
119
|
+
mem.set(left, PARENT_LEFT_OFF);
|
|
120
|
+
mem.set(right, PARENT_RIGHT_OFF);
|
|
121
|
+
mem.set(BLAKE3_IV_BYTES, PARENT_START_OFF);
|
|
122
|
+
try {
|
|
123
|
+
x._testParentCV(PARENT_LEFT_OFF, PARENT_RIGHT_OFF, PARENT_START_OFF, 0, 0, PARENT_OUT_OFF);
|
|
124
|
+
return mem.slice(PARENT_OUT_OFF, PARENT_OUT_OFF + BLAKE3_OUTPUT);
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
mem.fill(0, PARENT_LEFT_OFF, PARENT_LEFT_OFF + PARENT_SCRATCH_LEN);
|
|
128
|
+
x.wipeBuffers();
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
// ── Blake3Tree class ────────────────────────────────────────────────────────
|
|
133
|
+
/**
|
|
134
|
+
* Stateful BLAKE3 Merkle log. Same surface and storage discipline as
|
|
135
|
+
* `Sha256Tree`: leaf hashes and every perfect aligned internal subtree
|
|
136
|
+
* hash live in the injected `MerkleStorage`; partial right-edge subtrees
|
|
137
|
+
* are recomputed on demand.
|
|
138
|
+
*
|
|
139
|
+
* `append` is the only mutator and is the leaf-hash factory; consumers
|
|
140
|
+
* feed leaf bytes, not pre-computed leaf hashes.
|
|
141
|
+
*/
|
|
142
|
+
export class Blake3Tree {
|
|
143
|
+
hasher = Blake3Hasher;
|
|
144
|
+
storage;
|
|
145
|
+
constructor(storage) {
|
|
146
|
+
this.storage = storage;
|
|
147
|
+
}
|
|
148
|
+
size() {
|
|
149
|
+
return this.storage.size();
|
|
150
|
+
}
|
|
151
|
+
rootHash() {
|
|
152
|
+
const n = this.storage.size();
|
|
153
|
+
if (n === 0)
|
|
154
|
+
return this.hasher.hashEmpty();
|
|
155
|
+
const getNode = (level, index) => this.storage.getNode(level, index);
|
|
156
|
+
return subtreeHash(this.hasher, 0, n, getNode);
|
|
157
|
+
}
|
|
158
|
+
append(leafBytes) {
|
|
159
|
+
const leafIndex = this.storage.size();
|
|
160
|
+
const leafHash = this.hasher.hashLeaf(leafBytes);
|
|
161
|
+
this.storage.appendLeaf(leafIndex, leafHash);
|
|
162
|
+
let level = 0;
|
|
163
|
+
let idx = leafIndex;
|
|
164
|
+
while ((idx & 1) === 1) {
|
|
165
|
+
const left = this.storage.getNode(level, idx - 1);
|
|
166
|
+
const right = this.storage.getNode(level, idx);
|
|
167
|
+
const parent = this.hasher.hashInternal(left, right);
|
|
168
|
+
this.storage.putNode(level + 1, idx >>> 1, parent);
|
|
169
|
+
idx = idx >>> 1;
|
|
170
|
+
level++;
|
|
171
|
+
}
|
|
172
|
+
return { leafIndex, leafHash };
|
|
173
|
+
}
|
|
174
|
+
getInclusionProof(leafIndex, treeSize) {
|
|
175
|
+
const ts = treeSize ?? this.storage.size();
|
|
176
|
+
if (!Number.isInteger(ts) || ts < 1 || ts > this.storage.size())
|
|
177
|
+
throw new RangeError(`Blake3Tree.getInclusionProof: treeSize ${ts} out of range [1, ${this.storage.size()}]`);
|
|
178
|
+
const getNode = (level, index) => this.storage.getNode(level, index);
|
|
179
|
+
return buildInclusionProof({ hasher: this.hasher, leafIndex, treeSize: ts, getNode });
|
|
180
|
+
}
|
|
181
|
+
getConsistencyProof(oldSize, newSize) {
|
|
182
|
+
if (!Number.isInteger(newSize) || newSize < 0 || newSize > this.storage.size())
|
|
183
|
+
throw new RangeError(`Blake3Tree.getConsistencyProof: newSize ${newSize} out of range [0, ${this.storage.size()}]`);
|
|
184
|
+
const getNode = (level, index) => this.storage.getNode(level, index);
|
|
185
|
+
return buildConsistencyProof({ hasher: this.hasher, oldSize, newSize, getNode });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decoded form of a c2sp.org/tlog-checkpoint body. The body shape is
|
|
3
|
+
* hash-and-algo-agnostic: `rootHash` is 32 bytes for both the SHA-256 and
|
|
4
|
+
* BLAKE3 trees Phase 7 ships, but the codec only enforces a caller-supplied
|
|
5
|
+
* length in `parseCheckpointBody`. The signed-note envelope that wraps a
|
|
6
|
+
* checkpoint is handled in `signed-note.ts`.
|
|
7
|
+
*/
|
|
8
|
+
export interface Checkpoint {
|
|
9
|
+
/**
|
|
10
|
+
* Log identity, non-empty UTF-8 with no Unicode spaces, plus signs, or
|
|
11
|
+
* embedded newlines. Per c2sp.org/tlog-checkpoint §Note text the origin
|
|
12
|
+
* SHOULD be a schemeless URL such as `example.com/log42`, but the codec
|
|
13
|
+
* only enforces the MUST-level structural constraints; broader URL
|
|
14
|
+
* shape policy is a caller concern.
|
|
15
|
+
*/
|
|
16
|
+
readonly origin: string;
|
|
17
|
+
/**
|
|
18
|
+
* Number of leaves in the tree at signing time. Must be a non-negative
|
|
19
|
+
* safe integer; ASCII decimal serialization carries no leading zeroes
|
|
20
|
+
* (the literal `0` is the only valid string starting with `0`).
|
|
21
|
+
*/
|
|
22
|
+
readonly treeSize: number;
|
|
23
|
+
/**
|
|
24
|
+
* Merkle root hash. 32 bytes for both Sha256Tree and Blake3Tree; the
|
|
25
|
+
* caller-supplied `expectedHashLen` parameter on `parseCheckpointBody`
|
|
26
|
+
* pins the exact length for a given hasher.
|
|
27
|
+
*/
|
|
28
|
+
readonly rootHash: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Serialize a Checkpoint into its canonical body bytes per
|
|
32
|
+
* c2sp.org/tlog-checkpoint §Note text. Layout:
|
|
33
|
+
*
|
|
34
|
+
* utf8(origin) || 0x0A || utf8(decimal(treeSize)) || 0x0A
|
|
35
|
+
* || base64(rootHash) || 0x0A
|
|
36
|
+
*
|
|
37
|
+
* Base64 uses the RFC 4648 §4 standard alphabet with `=` padding (NOT the
|
|
38
|
+
* URL-safe variant from §5 and NOT padding-stripped). The body has no
|
|
39
|
+
* leading or trailing whitespace beyond the final 0x0A; byte stability
|
|
40
|
+
* is the entire purpose of the codec, since the body bytes are what the
|
|
41
|
+
* STH signature is computed over.
|
|
42
|
+
*/
|
|
43
|
+
export declare function serializeCheckpointBody(c: Checkpoint): Uint8Array;
|
|
44
|
+
/**
|
|
45
|
+
* Parse a canonical checkpoint body. Inverse of `serializeCheckpointBody`;
|
|
46
|
+
* round-trips byte-for-byte. Rejects extension lines, leading or trailing
|
|
47
|
+
* whitespace beyond the mandatory final 0x0A, non-newline ASCII control
|
|
48
|
+
* characters, malformed base64, and root hashes whose decoded length does
|
|
49
|
+
* not match `expectedHashLen` (default 32, the size for both Sha256Tree
|
|
50
|
+
* and Blake3Tree).
|
|
51
|
+
*
|
|
52
|
+
* The caller pins `expectedHashLen` to its hasher's `outputSize`; a future
|
|
53
|
+
* SignedLog (TASK-4) will bind this to the tree's hasher automatically.
|
|
54
|
+
*
|
|
55
|
+
* Per c2sp.org/tlog-checkpoint §Note text and c2sp.org/signed-note
|
|
56
|
+
* §Format.
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseCheckpointBody(bytes: Uint8Array, expectedHashLen?: number): Checkpoint;
|