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
package/dist/fortuna.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
//
|
|
22
22
|
// src/ts/fortuna.ts
|
|
23
23
|
//
|
|
24
|
-
// Fortuna CSPRNG
|
|
24
|
+
// Fortuna CSPRNG, Ferguson & Schneier, Practical Cryptography (2003), Chapter 9.
|
|
25
25
|
// Backed by a pluggable Generator (cipher PRF) and HashFn (accumulator hash).
|
|
26
26
|
// Requires init() for the modules used by the chosen generator and hash pair.
|
|
27
27
|
import { isInitialized, getInstance } from './init.js';
|
|
@@ -29,17 +29,17 @@ import { wipe, utf8ToBytes, concat } from './utils.js';
|
|
|
29
29
|
const isBrowser = typeof window !== 'undefined';
|
|
30
30
|
const isNode = typeof process !== 'undefined' && typeof process.pid === 'number';
|
|
31
31
|
/**
|
|
32
|
-
* Fortuna CSPRNG
|
|
32
|
+
* Fortuna CSPRNG, spec §9.3-§9.5
|
|
33
33
|
*
|
|
34
34
|
* Use `Fortuna.create({ generator, hash })` to instantiate. Direct construction is not allowed.
|
|
35
35
|
*/
|
|
36
36
|
export class Fortuna {
|
|
37
37
|
// ── Constants ──────────────────────────────────────────────────────────
|
|
38
38
|
static NUM_POOLS = 32;
|
|
39
|
-
static RESEED_LIMIT = 64; // bits
|
|
40
|
-
static MS_PER_RESEED = 100; // ms
|
|
41
|
-
static NODE_STATS_INTERVAL = 1000; // ms
|
|
42
|
-
static CRYPTO_INTERVAL = 3000; // ms
|
|
39
|
+
static RESEED_LIMIT = 64; // bits, pool 0 threshold (spec §9.5)
|
|
40
|
+
static MS_PER_RESEED = 100; // ms, minimum reseed interval (spec §9.5)
|
|
41
|
+
static NODE_STATS_INTERVAL = 1000; // ms, OS stats collector interval
|
|
42
|
+
static CRYPTO_INTERVAL = 3000; // ms, crypto.randomBytes interval
|
|
43
43
|
// ── State ─────────────────────────────────────────────────────────────
|
|
44
44
|
gen;
|
|
45
45
|
hash;
|
|
@@ -74,7 +74,7 @@ export class Fortuna {
|
|
|
74
74
|
}
|
|
75
75
|
const f = new Fortuna(opts.generator, opts.hash, opts.msPerReseed ?? Fortuna.MS_PER_RESEED);
|
|
76
76
|
f.initialize(opts.entropy);
|
|
77
|
-
// Force the first reseed
|
|
77
|
+
// Force the first reseed, pool[0] is saturated by initialize(),
|
|
78
78
|
// so this call triggers an immediate reseed and guarantees get() never
|
|
79
79
|
// returns undefined. The byte is discarded.
|
|
80
80
|
f.get(1);
|
|
@@ -101,14 +101,14 @@ export class Fortuna {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
// ── Public API ────────────────────────────────────────────────────────
|
|
104
|
-
/** Get n random bytes. Always returns Uint8Array
|
|
104
|
+
/** Get n random bytes. Always returns Uint8Array, instance is guaranteed seeded after create(). */
|
|
105
105
|
get(length) {
|
|
106
106
|
if (this.disposed)
|
|
107
107
|
throw new Error('Fortuna instance has been disposed');
|
|
108
|
-
// Capture hrtime jitter at call time (Node.js)
|
|
108
|
+
// Capture hrtime jitter at call time (Node.js), spec §9.5
|
|
109
109
|
if (isNode)
|
|
110
110
|
this.captureHrtime();
|
|
111
|
-
// Check reseed trigger
|
|
111
|
+
// Check reseed trigger, spec §9.5
|
|
112
112
|
if (this.poolEntropy[0] >= Fortuna.RESEED_LIMIT &&
|
|
113
113
|
Date.now() >= this.lastReseed + this.msPerReseed) {
|
|
114
114
|
this.reseedCnt = (this.reseedCnt + 1) >>> 0; // u32 wrap
|
|
@@ -121,7 +121,7 @@ export class Fortuna {
|
|
|
121
121
|
// Pool digest = current chain hash
|
|
122
122
|
seed = concat(seed, this.poolHash[i]);
|
|
123
123
|
strength += this.poolEntropy[i];
|
|
124
|
-
// Reset pool
|
|
124
|
+
// Reset pool, wipe old chain hash before dropping the reference.
|
|
125
125
|
const old = this.poolHash[i];
|
|
126
126
|
this.poolHash[i] = new Uint8Array(this.hash.outputSize);
|
|
127
127
|
wipe(old);
|
|
@@ -152,21 +152,17 @@ export class Fortuna {
|
|
|
152
152
|
stop() {
|
|
153
153
|
if (this.disposed)
|
|
154
154
|
throw new Error('Fortuna instance has been disposed');
|
|
155
|
-
// Mark disposed
|
|
156
|
-
// holds the module; we must not allow get()/addEntropy()/getEntropy() to
|
|
157
|
-
// run on a partially-disposed instance.
|
|
155
|
+
// Mark disposed first so partially-disposed reentry throws cleanly.
|
|
158
156
|
this.disposed = true;
|
|
159
157
|
this.stopCollectors();
|
|
160
158
|
wipe(this.genKey);
|
|
161
159
|
wipe(this.genCnt);
|
|
162
|
-
// Wipe
|
|
160
|
+
// Wipe the 32 pool-hash chain values so residual entropy-bearing
|
|
163
161
|
// bytes do not outlive the instance.
|
|
164
162
|
for (const p of this.poolHash)
|
|
165
163
|
wipe(p);
|
|
166
164
|
this.reseedCnt = 0;
|
|
167
|
-
// Best-effort wipe of WASM scratch
|
|
168
|
-
// generator and hash touched. Surface the first error so the caller
|
|
169
|
-
// knows the WASM scratch leak occurred.
|
|
165
|
+
// Best-effort wipe of WASM scratch; surface the first error.
|
|
170
166
|
const required = new Set([...this.gen.wasmModules, ...this.hash.wasmModules]);
|
|
171
167
|
let err;
|
|
172
168
|
for (const mod of required) {
|
|
@@ -181,24 +177,24 @@ export class Fortuna {
|
|
|
181
177
|
throw err;
|
|
182
178
|
}
|
|
183
179
|
// ── Test-only accessors ───────────────────────────────────────────────
|
|
184
|
-
/** @internal
|
|
180
|
+
/** @internal, exposed for testing key replacement */
|
|
185
181
|
_getGenKey() {
|
|
186
182
|
return this.genKey;
|
|
187
183
|
}
|
|
188
|
-
/** @internal
|
|
184
|
+
/** @internal, exposed for testing pool state */
|
|
189
185
|
_getPoolEntropy() {
|
|
190
186
|
return this.poolEntropy;
|
|
191
187
|
}
|
|
192
|
-
/** @internal
|
|
188
|
+
/** @internal, exposed for testing reseed count */
|
|
193
189
|
_getReseedCnt() {
|
|
194
190
|
return this.reseedCnt;
|
|
195
191
|
}
|
|
196
|
-
/** @internal
|
|
192
|
+
/** @internal, exposed for testing pool-hash backing arrays */
|
|
197
193
|
_getPoolHash() {
|
|
198
194
|
return this.poolHash;
|
|
199
195
|
}
|
|
200
196
|
/**
|
|
201
|
-
* @internal
|
|
197
|
+
* @internal, test-only deterministic factory. Seeds pool[0] with the provided
|
|
202
198
|
* entropy and triggers one reseed directly, bypassing all OS entropy collection
|
|
203
199
|
* and the hrtime jitter capture in get(). This makes KAT vectors reproducible
|
|
204
200
|
* across runs. Not suitable for production use.
|
|
@@ -219,20 +215,20 @@ export class Fortuna {
|
|
|
219
215
|
const f = new Fortuna(opts.generator, opts.hash, 0);
|
|
220
216
|
// Seed pool[0] with the provided entropy, no OS collection.
|
|
221
217
|
f.addRandomEvent(opts.entropy, 0, opts.entropy.length * 8);
|
|
222
|
-
// Manually trigger reseed #1 without calling get()
|
|
218
|
+
// Manually trigger reseed #1 without calling get(), get() calls captureHrtime()
|
|
223
219
|
// in Node.js which adds non-deterministic data before the reseed fires.
|
|
224
220
|
f.reseedFromPool0();
|
|
225
221
|
return f;
|
|
226
222
|
}
|
|
227
223
|
// ── Generator (spec §9.4) ─────────────────────────────────────────────
|
|
228
|
-
/** Get length pseudo-random bytes
|
|
224
|
+
/** Get length pseudo-random bytes., spec §9.4 */
|
|
229
225
|
pseudoRandomData(length) {
|
|
230
226
|
const blocks = Math.ceil(length / this.gen.blockSize);
|
|
231
227
|
const out = this.gen.generate(this.genKey, this.genCnt, length);
|
|
232
|
-
// External counter advance
|
|
228
|
+
// External counter advance, generator is stateless and does not mutate caller's counter
|
|
233
229
|
for (let i = 0; i < blocks; i++)
|
|
234
230
|
this.incrementCounter();
|
|
235
|
-
// Key replacement
|
|
231
|
+
// Key replacement, mandatory forward secrecy (spec §9.4).
|
|
236
232
|
// Wipe the prior key BEFORE dropping its reference so no key bytes are
|
|
237
233
|
// reachable after key replacement; anyone holding a Uint8Array view to
|
|
238
234
|
// the old key now observes zero.
|
|
@@ -243,7 +239,7 @@ export class Fortuna {
|
|
|
243
239
|
this.genKey = newKey;
|
|
244
240
|
return out;
|
|
245
241
|
}
|
|
246
|
-
/** Reseed the generator
|
|
242
|
+
/** Reseed the generator, spec §9.4 */
|
|
247
243
|
reseed(seed) {
|
|
248
244
|
// genKey = hash(genKey ‖ seed). Wipe both the hash input and the
|
|
249
245
|
// prior key before dropping references.
|
|
@@ -252,7 +248,7 @@ export class Fortuna {
|
|
|
252
248
|
wipe(combined);
|
|
253
249
|
wipe(this.genKey);
|
|
254
250
|
this.genKey = newKey;
|
|
255
|
-
// Increment counter
|
|
251
|
+
// Increment counter, makes it nonzero on first reseed, marking generator as seeded
|
|
256
252
|
this.incrementCounter();
|
|
257
253
|
this.lastReseed = Date.now();
|
|
258
254
|
}
|
|
@@ -271,7 +267,7 @@ export class Fortuna {
|
|
|
271
267
|
this.reseed(seed);
|
|
272
268
|
wipe(seed);
|
|
273
269
|
}
|
|
274
|
-
/** Increment little-endian counter
|
|
270
|
+
/** Increment little-endian counter., spec §9.4 */
|
|
275
271
|
incrementCounter() {
|
|
276
272
|
for (let i = 0; i < this.genCnt.length; i++) {
|
|
277
273
|
if (++this.genCnt[i] !== 0)
|
|
@@ -300,7 +296,7 @@ export class Fortuna {
|
|
|
300
296
|
}
|
|
301
297
|
// ── Initialization ────────────────────────────────────────────────────
|
|
302
298
|
initialize(entropy) {
|
|
303
|
-
// Initial seeding
|
|
299
|
+
// Initial seeding, crypto random per pool (spec §9.5)
|
|
304
300
|
for (let i = 0; i < Fortuna.NUM_POOLS * 4; i++) {
|
|
305
301
|
this.collectorCryptoRandom();
|
|
306
302
|
}
|
|
@@ -319,7 +315,7 @@ export class Fortuna {
|
|
|
319
315
|
// runtimes). This post-init check covers all silent-failure paths uniformly.
|
|
320
316
|
if (this.poolEntropy[0] < Fortuna.RESEED_LIMIT)
|
|
321
317
|
throw new Error('leviathan-crypto: Fortuna initialization could not gather sufficient entropy. '
|
|
322
|
-
+ 'No working crypto.getRandomValues
|
|
318
|
+
+ 'No working crypto.getRandomValues in this environment.');
|
|
323
319
|
this.startCollectors();
|
|
324
320
|
}
|
|
325
321
|
// ── Collectors ────────────────────────────────────────────────────────
|
|
@@ -349,7 +345,7 @@ export class Fortuna {
|
|
|
349
345
|
// OS stats timer
|
|
350
346
|
this.timers.push(setInterval(() => this.collectNodeStats(), Fortuna.NODE_STATS_INTERVAL));
|
|
351
347
|
}
|
|
352
|
-
// Crypto timer
|
|
348
|
+
// Crypto timer, both environments
|
|
353
349
|
this.timers.push(setInterval(() => this.collectorCryptoRandom(), Fortuna.CRYPTO_INTERVAL));
|
|
354
350
|
this.active = true;
|
|
355
351
|
}
|
|
@@ -458,19 +454,12 @@ export class Fortuna {
|
|
|
458
454
|
}
|
|
459
455
|
collectorCryptoRandom() {
|
|
460
456
|
try {
|
|
457
|
+
// Web Crypto is the only secure source. No node:crypto fallback: an absent
|
|
458
|
+
// source must fail loud via the init invariant below, never be polyfilled.
|
|
459
|
+
if (typeof globalThis.crypto === 'undefined' || typeof globalThis.crypto.getRandomValues !== 'function')
|
|
460
|
+
return;
|
|
461
461
|
const rnd = new Uint8Array(128);
|
|
462
|
-
|
|
463
|
-
globalThis.crypto.getRandomValues(rnd);
|
|
464
|
-
}
|
|
465
|
-
else if (isNode) {
|
|
466
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
467
|
-
const nodeCrypto = require('node:crypto');
|
|
468
|
-
const buf = nodeCrypto.randomBytes(128);
|
|
469
|
-
rnd.set(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
470
|
-
}
|
|
471
|
-
else {
|
|
472
|
-
return; // no crypto source available
|
|
473
|
-
}
|
|
462
|
+
globalThis.crypto.getRandomValues(rnd);
|
|
474
463
|
this.addRandomEvent(rnd, this.robin.rnd, 1024);
|
|
475
464
|
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
476
465
|
}
|
|
@@ -489,14 +478,14 @@ export class Fortuna {
|
|
|
489
478
|
}
|
|
490
479
|
collectNodeStats() {
|
|
491
480
|
try {
|
|
492
|
-
// hrtime
|
|
481
|
+
// hrtime, nanosecond scheduling jitter
|
|
493
482
|
const hr = process.hrtime.bigint();
|
|
494
483
|
const hrBytes = new Uint8Array(8);
|
|
495
484
|
for (let i = 0; i < 8; i++)
|
|
496
485
|
hrBytes[i] = Number((hr >> BigInt(i * 8)) & 0xffn);
|
|
497
486
|
this.addRandomEvent(hrBytes, this.robin.time, 8);
|
|
498
487
|
this.robin.time = (this.robin.time + 1) % Fortuna.NUM_POOLS;
|
|
499
|
-
// cpuUsage
|
|
488
|
+
// cpuUsage, user + system CPU microseconds
|
|
500
489
|
const cpu = process.cpuUsage();
|
|
501
490
|
const cpuBytes = new Uint8Array(8);
|
|
502
491
|
cpuBytes[0] = cpu.user & 0xff;
|
|
@@ -509,7 +498,7 @@ export class Fortuna {
|
|
|
509
498
|
cpuBytes[7] = (cpu.system >>> 24) & 0xff;
|
|
510
499
|
this.addRandomEvent(cpuBytes, this.robin.rnd, 2);
|
|
511
500
|
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
512
|
-
// memoryUsage
|
|
501
|
+
// memoryUsage, heapUsed changes constantly
|
|
513
502
|
const mem = process.memoryUsage();
|
|
514
503
|
const memVal = mem.heapUsed;
|
|
515
504
|
const memBytes = new Uint8Array(4);
|
|
@@ -519,22 +508,6 @@ export class Fortuna {
|
|
|
519
508
|
memBytes[3] = (memVal >>> 24) & 0xff;
|
|
520
509
|
this.addRandomEvent(memBytes, this.robin.rnd, 1);
|
|
521
510
|
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
522
|
-
// loadavg — slow-changing but real system state
|
|
523
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
524
|
-
const os = require('node:os');
|
|
525
|
-
const la = os.loadavg();
|
|
526
|
-
const laStr = la.map((n) => Math.round(n * 1000).toString()).join('');
|
|
527
|
-
this.addRandomEvent(utf8ToBytes(laStr), this.robin.time, 1);
|
|
528
|
-
this.robin.time = (this.robin.time + 1) % Fortuna.NUM_POOLS;
|
|
529
|
-
// freemem — changes with allocation activity
|
|
530
|
-
const fm = os.freemem();
|
|
531
|
-
const fmBytes = new Uint8Array(4);
|
|
532
|
-
fmBytes[0] = fm & 0xff;
|
|
533
|
-
fmBytes[1] = (fm >>> 8) & 0xff;
|
|
534
|
-
fmBytes[2] = (fm >>> 16) & 0xff;
|
|
535
|
-
fmBytes[3] = (fm >>> 24) & 0xff;
|
|
536
|
-
this.addRandomEvent(fmBytes, this.robin.rnd, 1);
|
|
537
|
-
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
538
511
|
}
|
|
539
512
|
catch { /* Node APIs may not be available */ }
|
|
540
513
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { Module } from './init.js';
|
|
2
2
|
import type { WasmSource } from './wasm-source.js';
|
|
3
|
+
/**
|
|
4
|
+
* Top-level init() input. Accepts the canonical module keys plus the
|
|
5
|
+
* `ed25519` and `x25519` aliases; both aliases resolve to the underlying
|
|
6
|
+
* `curve25519` WASM module and are de-duped if given identical sources.
|
|
7
|
+
*/
|
|
8
|
+
export type InitInput = Partial<Record<Module | 'ed25519' | 'x25519', WasmSource>>;
|
|
3
9
|
/**
|
|
4
10
|
* Load one or more WASM modules. Each key is a module name; the value is the
|
|
5
11
|
* WasmSource to load it from (embedded blob, URL, ArrayBuffer, etc.).
|
|
@@ -10,22 +16,45 @@ import type { WasmSource } from './wasm-source.js';
|
|
|
10
16
|
* import { sha2Wasm } from 'leviathan-crypto/sha2/embedded';
|
|
11
17
|
* await init({ serpent: serpentWasm, sha2: sha2Wasm });
|
|
12
18
|
* ```
|
|
19
|
+
*
|
|
20
|
+
* `ed25519` and `x25519` are aliases for the underlying `curve25519`
|
|
21
|
+
* module. `init({ ed25519: src })` and `init({ x25519: src })` both work,
|
|
22
|
+
* and `init({ ed25519: src, x25519: src })` is accepted (single underlying
|
|
23
|
+
* init). Two different sources for the same underlying module is rejected.
|
|
13
24
|
*/
|
|
14
|
-
export declare function init(sources:
|
|
25
|
+
export declare function init(sources: InitInput): Promise<void>;
|
|
15
26
|
export type { Module, WasmSource };
|
|
16
27
|
export { isInitialized } from './init.js';
|
|
17
|
-
export { AuthenticationError } from './errors.js';
|
|
18
|
-
export { serpentInit, Serpent, SerpentCtr, SerpentCbc, SerpentCipher, SerpentGenerator
|
|
19
|
-
export { chacha20Init, ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Cipher, ChaCha20Generator
|
|
20
|
-
export { sha2Init, SHA256,
|
|
21
|
-
export { sha3Init, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256, SHA3_256Hash,
|
|
28
|
+
export { AuthenticationError, SigningError, KeyAgreementError, MerkleCodecError, MerkleLogError } from './errors.js';
|
|
29
|
+
export { serpentInit, Serpent, SerpentCtr, SerpentCbc, SerpentCipher, SerpentGenerator } from './serpent/index.js';
|
|
30
|
+
export { chacha20Init, ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Cipher, ChaCha20Generator } from './chacha20/index.js';
|
|
31
|
+
export { sha2Init, SHA256, SHA224, SHA384, SHA512, SHA512_224, SHA512_256, HMAC_SHA256, HMAC_SHA512, HMAC_SHA384, HKDF_SHA256, HKDF_SHA512, SHA256Hash } from './sha2/index.js';
|
|
32
|
+
export { sha3Init, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHA3_256Stream, SHA3_512Stream, SHAKE128, SHAKE256, SHAKE128Stream, SHAKE256Stream, SHA3_256Hash, CSHAKE128, CSHAKE256, KMAC128, KMAC256, KMACXOF128, KMACXOF256 } from './sha3/index.js';
|
|
22
33
|
export { keccakInit } from './keccak/index.js';
|
|
23
|
-
export {
|
|
24
|
-
export
|
|
34
|
+
export { mlkemInit, MlKem512, MlKem768, MlKem1024, MlKemBase, MlKemSuite } from './mlkem/index.js';
|
|
35
|
+
export { aesInit, AES, AESCbc, AESCtr, AESGCM, AESGCMSIV, AESGenerator, AESGCMSIVCipher } from './aes/index.js';
|
|
36
|
+
export { mldsaInit, MlDsa44, MlDsa65, MlDsa87, MlDsaBase, MLDSA44, MLDSA65, MLDSA87 } from './mldsa/index.js';
|
|
37
|
+
export { slhdsaInit, SlhDsaBase, SlhDsa128f, SlhDsa192f, SlhDsa256f, SLHDSA128F, SLHDSA192F, SLHDSA256F, } from './slhdsa/index.js';
|
|
38
|
+
export { blake3Init, BLAKE3, BLAKE3KeyedHash, BLAKE3DeriveKey, BLAKE3Stream, BLAKE3KeyedHashStream, BLAKE3DeriveKeyStream, BLAKE3OutputReader, BLAKE3Hash, } from './blake3/index.js';
|
|
39
|
+
export { ecdsaP256Init, EcdsaP256, pointDecompress, encodeEcPrivateKey, decodeEcPrivateKey, } from './ecdsa/index.js';
|
|
40
|
+
export type { EcdsaP256KeyPair } from './ecdsa/index.js';
|
|
41
|
+
export { ecdsaSignatureToDer, ecdsaSignatureFromDer } from './ecdsa/der.js';
|
|
42
|
+
export { ed25519Init, Ed25519 } from './ed25519/index.js';
|
|
43
|
+
export type { Ed25519KeyPair } from './ed25519/index.js';
|
|
44
|
+
export { x25519Init, X25519 } from './x25519/index.js';
|
|
45
|
+
export type { X25519KeyPair } from './x25519/index.js';
|
|
46
|
+
export type { MlKemKeyPair, MlKemEncapsulation, MlKemParams } from './mlkem/index.js';
|
|
47
|
+
export type { MlDsaKeyPair, MlDsaParams, PreHashAlgorithm } from './mldsa/index.js';
|
|
48
|
+
export type { SlhDsaKeyPair, SlhDsaParams } from './slhdsa/index.js';
|
|
25
49
|
export { SealStream, OpenStream, Seal, SealStreamPool, FLAG_FRAMED, TAG_DATA, TAG_FINAL, HEADER_SIZE, CHUNK_MIN, CHUNK_MAX } from './stream/index.js';
|
|
26
50
|
export type { CipherSuite, DerivedKeys, SealStreamOpts, PoolOpts } from './stream/index.js';
|
|
51
|
+
export { MemoryStorage, Sha256Hasher, Sha256Tree, Blake3Hasher, Blake3Tree, splitPoint, verifyInclusionProof, verifyConsistencyProof, buildInclusionProof, buildConsistencyProof, serializeCheckpointBody, parseCheckpointBody, emitSignedNote, parseSignedNote, deriveKeyId, suiteFormatEnumToAlgoByte, lookupAlgoEntryByFormatEnum, lookupAlgoEntryByByte, buildCosigSignedMessage, buildCosignedMessage, emitCosigSignaturePayload, parseCosigSignaturePayload, ALGO_BYTE_ED25519_NOTE, ALGO_BYTE_ED25519_COSIG, ALGO_BYTE_MLDSA44_COSIG, SignedLog, MerkleVerifier, MerkleLog, } from './merkle/index.js';
|
|
52
|
+
export type { Hasher, MerkleTree, MerkleStorage, VerifyInclusionInput, VerifyConsistencyInput, BuildInclusionInput, BuildConsistencyInput, GetNode, Checkpoint, SignatureLine, SignedNote, SignedTreeHead, AlgoEntry, MessageConstruction, SignaturePayload, CosignedMessageInput, SignedLogOpts, MerkleVerifierOpts, MerkleLogCreateOpts, MerkleLogGenerateOpts, } from './merkle/index.js';
|
|
53
|
+
export { Sign, SignStream, VerifyStream } from './sign/index.js';
|
|
54
|
+
export type { SignatureSuite, StreamableSignatureSuite, PrehashAlgorithm, } from './sign/index.js';
|
|
55
|
+
export { Ed25519Suite, Ed25519PreHashSuite, EcdsaP256Suite, MlDsa44Suite, MlDsa65Suite, MlDsa87Suite, MlDsa44PreHashSuite, MlDsa65PreHashSuite, MlDsa87PreHashSuite, SlhDsa128fSuite, SlhDsa192fSuite, SlhDsa256fSuite, SlhDsa128fPreHashSuite, SlhDsa192fPreHashSuite, SlhDsa256fPreHashSuite, MlDsa44SlhDsa128fSuite, MlDsa65SlhDsa192fSuite, MlDsa87SlhDsa256fSuite, MlDsa44Ed25519Suite, MlDsa65Ed25519Suite, MlDsa44EcdsaP256Suite, MlDsa65EcdsaP256Suite, } from './sign/index.js';
|
|
27
56
|
export { Fortuna } from './fortuna.js';
|
|
28
57
|
export type { Hash, KeyedHash, Blockcipher, Streamcipher, AEAD, Generator, HashFn } from './types.js';
|
|
29
58
|
export { KDFChain, ratchetInit, kemRatchetEncap, kemRatchetDecap, ratchetReady, SkippedKeyStore, RatchetKeypair, } from './ratchet/index.js';
|
|
30
59
|
export type { RatchetInitResult, KemEncapResult, KemDecapResult, MlKemLike, RatchetMessageHeader, ResolveHandle, SkippedKeyStoreOpts, } from './ratchet/index.js';
|
|
31
|
-
export { hexToBytes, bytesToHex, utf8ToBytes, bytesToUtf8, base64ToBytes, bytesToBase64, constantTimeEqual,
|
|
60
|
+
export { hexToBytes, bytesToHex, utf8ToBytes, bytesToUtf8, base64ToBytes, bytesToBase64, constantTimeEqual, CTE_MAX_BYTES, wipe, xor, concat, randomBytes, hasSIMD, } from './utils.js';
|
package/dist/index.js
CHANGED
|
@@ -19,21 +19,35 @@
|
|
|
19
19
|
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
20
|
// ▀█████▀▀
|
|
21
21
|
//
|
|
22
|
-
// Root barrel
|
|
22
|
+
// Root barrel, re-exports everything
|
|
23
23
|
import { serpentInit } from './serpent/index.js';
|
|
24
24
|
import { chacha20Init } from './chacha20/index.js';
|
|
25
25
|
import { sha2Init } from './sha2/index.js';
|
|
26
26
|
import { sha3Init } from './sha3/index.js';
|
|
27
27
|
import { keccakInit } from './keccak/index.js';
|
|
28
|
-
import {
|
|
28
|
+
import { mlkemInit } from './mlkem/index.js';
|
|
29
|
+
import { aesInit } from './aes/index.js';
|
|
30
|
+
import { mldsaInit } from './mldsa/index.js';
|
|
31
|
+
import { slhdsaInit } from './slhdsa/index.js';
|
|
32
|
+
import { blake3Init } from './blake3/index.js';
|
|
33
|
+
import { ecdsaP256Init } from './ecdsa/index.js';
|
|
34
|
+
import { initModule } from './init.js';
|
|
29
35
|
import { hasSIMD } from './utils.js';
|
|
36
|
+
// curve25519 backs ed25519 + x25519. Public surface is the
|
|
37
|
+
// per-primitive alias; 'curve25519' key accepted for Module symmetry.
|
|
30
38
|
const _dispatchers = {
|
|
31
39
|
serpent: serpentInit,
|
|
32
40
|
chacha20: chacha20Init,
|
|
33
41
|
sha2: sha2Init,
|
|
34
42
|
sha3: sha3Init,
|
|
35
43
|
keccak: keccakInit,
|
|
36
|
-
|
|
44
|
+
mlkem: mlkemInit,
|
|
45
|
+
aes: aesInit,
|
|
46
|
+
mldsa: mldsaInit,
|
|
47
|
+
slhdsa: slhdsaInit,
|
|
48
|
+
blake3: blake3Init,
|
|
49
|
+
curve25519: (source) => initModule('curve25519', source),
|
|
50
|
+
p256: ecdsaP256Init,
|
|
37
51
|
};
|
|
38
52
|
/**
|
|
39
53
|
* Load one or more WASM modules. Each key is a module name; the value is the
|
|
@@ -45,30 +59,60 @@ const _dispatchers = {
|
|
|
45
59
|
* import { sha2Wasm } from 'leviathan-crypto/sha2/embedded';
|
|
46
60
|
* await init({ serpent: serpentWasm, sha2: sha2Wasm });
|
|
47
61
|
* ```
|
|
62
|
+
*
|
|
63
|
+
* `ed25519` and `x25519` are aliases for the underlying `curve25519`
|
|
64
|
+
* module. `init({ ed25519: src })` and `init({ x25519: src })` both work,
|
|
65
|
+
* and `init({ ed25519: src, x25519: src })` is accepted (single underlying
|
|
66
|
+
* init). Two different sources for the same underlying module is rejected.
|
|
48
67
|
*/
|
|
49
68
|
export async function init(sources) {
|
|
50
69
|
const entries = Object.entries(sources);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
throw new Error('leviathan-crypto: serpent, chacha20, and kyber require WebAssembly SIMD — '
|
|
54
|
-
+ 'this runtime does not support it');
|
|
55
|
-
for (const [mod, src] of entries) {
|
|
56
|
-
if (!Object.hasOwn(_dispatchers, mod))
|
|
57
|
-
throw new Error(`leviathan-crypto: unknown module "${mod}" — expected one of: ${Object.keys(_dispatchers).join(', ')}`);
|
|
70
|
+
const resolved = new Map();
|
|
71
|
+
for (const [key, src] of entries) {
|
|
58
72
|
if (src == null)
|
|
59
|
-
throw new TypeError(`leviathan-crypto: source for "${
|
|
73
|
+
throw new TypeError(`leviathan-crypto: source for "${key}" is null or undefined`);
|
|
74
|
+
const target = (key === 'ed25519' || key === 'x25519') ? 'curve25519' : key;
|
|
75
|
+
if (!Object.hasOwn(_dispatchers, target))
|
|
76
|
+
throw new Error(`leviathan-crypto: unknown module "${key}", expected one of: `
|
|
77
|
+
+ `${Object.keys(_dispatchers).join(', ')}, ed25519, x25519`);
|
|
78
|
+
const prior = resolved.get(target);
|
|
79
|
+
if (prior !== undefined) {
|
|
80
|
+
if (prior !== src)
|
|
81
|
+
throw new Error('leviathan-crypto: init() called with different sources for "ed25519" and "x25519" '
|
|
82
|
+
+ '(both alias to curve25519, sources must be identical)');
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
resolved.set(target, src);
|
|
60
86
|
}
|
|
61
|
-
|
|
87
|
+
// SIMD preflight for modules that contain v128 instructions
|
|
88
|
+
// (see scripts/lib/modules.ts).
|
|
89
|
+
if ((resolved.has('serpent') || resolved.has('chacha20') || resolved.has('mlkem')
|
|
90
|
+
|| resolved.has('aes') || resolved.has('mldsa') || resolved.has('blake3'))
|
|
91
|
+
&& !hasSIMD())
|
|
92
|
+
throw new Error('leviathan-crypto: serpent, chacha20, mlkem, aes, mldsa, and blake3 require WebAssembly SIMD, '
|
|
93
|
+
+ 'this runtime does not support it');
|
|
94
|
+
await Promise.all(Array.from(resolved.entries()).map(([mod, src]) => _dispatchers[mod](src)));
|
|
62
95
|
}
|
|
63
96
|
export { isInitialized } from './init.js';
|
|
64
|
-
export { AuthenticationError } from './errors.js';
|
|
65
|
-
export { serpentInit, Serpent, SerpentCtr, SerpentCbc, SerpentCipher, SerpentGenerator
|
|
66
|
-
export { chacha20Init, ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Cipher, ChaCha20Generator
|
|
67
|
-
export { sha2Init, SHA256,
|
|
68
|
-
export { sha3Init, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256, SHA3_256Hash,
|
|
97
|
+
export { AuthenticationError, SigningError, KeyAgreementError, MerkleCodecError, MerkleLogError } from './errors.js';
|
|
98
|
+
export { serpentInit, Serpent, SerpentCtr, SerpentCbc, SerpentCipher, SerpentGenerator } from './serpent/index.js';
|
|
99
|
+
export { chacha20Init, ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Cipher, ChaCha20Generator } from './chacha20/index.js';
|
|
100
|
+
export { sha2Init, SHA256, SHA224, SHA384, SHA512, SHA512_224, SHA512_256, HMAC_SHA256, HMAC_SHA512, HMAC_SHA384, HKDF_SHA256, HKDF_SHA512, SHA256Hash } from './sha2/index.js';
|
|
101
|
+
export { sha3Init, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHA3_256Stream, SHA3_512Stream, SHAKE128, SHAKE256, SHAKE128Stream, SHAKE256Stream, SHA3_256Hash, CSHAKE128, CSHAKE256, KMAC128, KMAC256, KMACXOF128, KMACXOF256 } from './sha3/index.js';
|
|
69
102
|
export { keccakInit } from './keccak/index.js';
|
|
70
|
-
export {
|
|
103
|
+
export { mlkemInit, MlKem512, MlKem768, MlKem1024, MlKemBase, MlKemSuite } from './mlkem/index.js';
|
|
104
|
+
export { aesInit, AES, AESCbc, AESCtr, AESGCM, AESGCMSIV, AESGenerator, AESGCMSIVCipher } from './aes/index.js';
|
|
105
|
+
export { mldsaInit, MlDsa44, MlDsa65, MlDsa87, MlDsaBase, MLDSA44, MLDSA65, MLDSA87 } from './mldsa/index.js';
|
|
106
|
+
export { slhdsaInit, SlhDsaBase, SlhDsa128f, SlhDsa192f, SlhDsa256f, SLHDSA128F, SLHDSA192F, SLHDSA256F, } from './slhdsa/index.js';
|
|
107
|
+
export { blake3Init, BLAKE3, BLAKE3KeyedHash, BLAKE3DeriveKey, BLAKE3Stream, BLAKE3KeyedHashStream, BLAKE3DeriveKeyStream, BLAKE3OutputReader, BLAKE3Hash, } from './blake3/index.js';
|
|
108
|
+
export { ecdsaP256Init, EcdsaP256, pointDecompress, encodeEcPrivateKey, decodeEcPrivateKey, } from './ecdsa/index.js';
|
|
109
|
+
export { ecdsaSignatureToDer, ecdsaSignatureFromDer } from './ecdsa/der.js';
|
|
110
|
+
export { ed25519Init, Ed25519 } from './ed25519/index.js';
|
|
111
|
+
export { x25519Init, X25519 } from './x25519/index.js';
|
|
71
112
|
export { SealStream, OpenStream, Seal, SealStreamPool, FLAG_FRAMED, TAG_DATA, TAG_FINAL, HEADER_SIZE, CHUNK_MIN, CHUNK_MAX } from './stream/index.js';
|
|
113
|
+
export { MemoryStorage, Sha256Hasher, Sha256Tree, Blake3Hasher, Blake3Tree, splitPoint, verifyInclusionProof, verifyConsistencyProof, buildInclusionProof, buildConsistencyProof, serializeCheckpointBody, parseCheckpointBody, emitSignedNote, parseSignedNote, deriveKeyId, suiteFormatEnumToAlgoByte, lookupAlgoEntryByFormatEnum, lookupAlgoEntryByByte, buildCosigSignedMessage, buildCosignedMessage, emitCosigSignaturePayload, parseCosigSignaturePayload, ALGO_BYTE_ED25519_NOTE, ALGO_BYTE_ED25519_COSIG, ALGO_BYTE_MLDSA44_COSIG, SignedLog, MerkleVerifier, MerkleLog, } from './merkle/index.js';
|
|
114
|
+
export { Sign, SignStream, VerifyStream } from './sign/index.js';
|
|
115
|
+
export { Ed25519Suite, Ed25519PreHashSuite, EcdsaP256Suite, MlDsa44Suite, MlDsa65Suite, MlDsa87Suite, MlDsa44PreHashSuite, MlDsa65PreHashSuite, MlDsa87PreHashSuite, SlhDsa128fSuite, SlhDsa192fSuite, SlhDsa256fSuite, SlhDsa128fPreHashSuite, SlhDsa192fPreHashSuite, SlhDsa256fPreHashSuite, MlDsa44SlhDsa128fSuite, MlDsa65SlhDsa192fSuite, MlDsa87SlhDsa256fSuite, MlDsa44Ed25519Suite, MlDsa65Ed25519Suite, MlDsa44EcdsaP256Suite, MlDsa65EcdsaP256Suite, } from './sign/index.js';
|
|
72
116
|
export { Fortuna } from './fortuna.js';
|
|
73
117
|
export { KDFChain, ratchetInit, kemRatchetEncap, kemRatchetDecap, ratchetReady, SkippedKeyStore, RatchetKeypair, } from './ratchet/index.js';
|
|
74
|
-
export { hexToBytes, bytesToHex, utf8ToBytes, bytesToUtf8, base64ToBytes, bytesToBase64, constantTimeEqual,
|
|
118
|
+
export { hexToBytes, bytesToHex, utf8ToBytes, bytesToUtf8, base64ToBytes, bytesToBase64, constantTimeEqual, CTE_MAX_BYTES, wipe, xor, concat, randomBytes, hasSIMD, } from './utils.js';
|
package/dist/init.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WasmSource } from './wasm-source.js';
|
|
2
|
-
export type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3' | 'keccak' | '
|
|
2
|
+
export type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3' | 'keccak' | 'mlkem' | 'aes' | 'mldsa' | 'slhdsa' | 'blake3' | 'curve25519' | 'p256';
|
|
3
3
|
export declare function initModule(mod: Module, source: WasmSource): Promise<void>;
|
|
4
4
|
export declare function getInstance(mod: Module): WebAssembly.Instance;
|
|
5
5
|
export declare function isInitialized(mod: Module): boolean;
|
package/dist/init.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
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
|
-
//
|
|
10
|
+
// Coalesces concurrent initModule calls.
|
|
11
11
|
const pending = new Map();
|
|
12
|
-
//
|
|
13
|
-
// for its entire lifetime. Prevents shared-WASM-state clobber when two
|
|
14
|
-
// instances from the same module would otherwise trample each other's memory.
|
|
12
|
+
// Per-module exclusivity token held by a stateful wrapper.
|
|
15
13
|
const owners = new Map();
|
|
16
14
|
export async function initModule(mod, source) {
|
|
17
15
|
const resolved = resolve(mod);
|
|
@@ -22,8 +20,8 @@ export async function initModule(mod, source) {
|
|
|
22
20
|
await inflight;
|
|
23
21
|
return;
|
|
24
22
|
}
|
|
25
|
-
if ((resolved === 'serpent' || resolved === 'chacha20' || resolved === '
|
|
26
|
-
throw new Error('leviathan-crypto: serpent, chacha20, and
|
|
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, '
|
|
27
25
|
+ 'this runtime does not support it');
|
|
28
26
|
const p = loadWasm(source);
|
|
29
27
|
pending.set(resolved, p);
|
|
@@ -41,7 +39,7 @@ export function getInstance(mod) {
|
|
|
41
39
|
throw new Error(`leviathan-crypto: call init({ ${mod}: ... }) before using this class`);
|
|
42
40
|
}
|
|
43
41
|
if (owners.has(r)) {
|
|
44
|
-
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module
|
|
42
|
+
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module, `
|
|
45
43
|
+ 'call dispose() on it before constructing a new one');
|
|
46
44
|
}
|
|
47
45
|
return inst;
|
|
@@ -57,7 +55,7 @@ export function isInitialized(mod) {
|
|
|
57
55
|
export function _acquireModule(mod) {
|
|
58
56
|
const r = resolve(mod);
|
|
59
57
|
if (owners.has(r))
|
|
60
|
-
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module
|
|
58
|
+
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module, `
|
|
61
59
|
+ 'call dispose() on it before constructing a new one');
|
|
62
60
|
const tok = Symbol(r);
|
|
63
61
|
owners.set(r, tok);
|
|
@@ -80,27 +78,15 @@ export function _releaseModule(mod, tok) {
|
|
|
80
78
|
export function _isModuleBusy(mod) {
|
|
81
79
|
return owners.has(resolve(mod));
|
|
82
80
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Throw if `mod` is currently held by a stateful instance. Called at the top
|
|
85
|
-
* of every atomic WASM-touching method so that cached-exports access paths
|
|
86
|
-
* cannot silently clobber a live stateful instance's WASM state.
|
|
87
|
-
*
|
|
88
|
-
* The error message is intentionally identical to `_acquireModule`'s so that
|
|
89
|
-
* error handlers matching on text work uniformly across construction-time and
|
|
90
|
-
* method-time ownership failures.
|
|
91
|
-
* @internal
|
|
92
|
-
*/
|
|
81
|
+
/** @internal */
|
|
93
82
|
export function _assertNotOwned(mod) {
|
|
94
|
-
// Deliberately unoptimized. Do not add caching or epoch tracking: the
|
|
95
|
-
// check must read current ownership on every call so an atomic op cannot
|
|
96
|
-
// race ahead of a stateful acquirer.
|
|
97
83
|
const r = resolve(mod);
|
|
98
84
|
if (owners.has(r))
|
|
99
|
-
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module
|
|
85
|
+
throw new Error(`leviathan-crypto: another stateful instance is using the '${r}' WASM module, `
|
|
100
86
|
+ 'call dispose() on it before constructing a new one');
|
|
101
87
|
}
|
|
102
88
|
/**
|
|
103
|
-
* Reset all cached instances
|
|
89
|
+
* Reset all cached instances, for testing only. Clears `instances`, `pending`,
|
|
104
90
|
* and `owners` so tests can re-exercise module lifecycle (init, exclusivity,
|
|
105
91
|
* race) from a known-empty state.
|
|
106
92
|
* @internal
|
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,24 +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
|
-
* Thenable sources (Promise<Response>, Promise<ArrayBuffer>, etc.) are
|
|
13
|
-
* resolved and then re-dispatched by the runtime type of the resolved value.
|
|
14
|
-
* Depth is capped at `MAX_THENABLE_DEPTH` to prevent runaway recursion.
|
|
15
|
-
*/
|
|
16
|
-
export declare function compileWasm(source: WasmSource, _depth?: number): Promise<WebAssembly.Module>;
|
|
17
|
-
/**
|
|
18
|
-
* Load a WASM module from any accepted source type.
|
|
19
|
-
* The loading strategy is inferred from the argument type — no mode string.
|
|
20
|
-
*
|
|
21
|
-
* Throws `TypeError` for null, numeric, or unrecognised inputs, or if a
|
|
22
|
-
* thenable source nests deeper than `MAX_THENABLE_DEPTH`.
|
|
23
|
-
*/
|
|
24
|
-
export declare function loadWasm(source: WasmSource): Promise<WebAssembly.Instance>;
|
|
1
|
+
export {};
|