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/fortuna.js
CHANGED
|
@@ -21,31 +21,29 @@
|
|
|
21
21
|
//
|
|
22
22
|
// src/ts/fortuna.ts
|
|
23
23
|
//
|
|
24
|
-
// Fortuna CSPRNG
|
|
25
|
-
// Backed by
|
|
26
|
-
// Requires init(
|
|
27
|
-
import { isInitialized } from './init.js';
|
|
28
|
-
import { Serpent } from './serpent/index.js';
|
|
29
|
-
import { SHA256 } from './sha2/index.js';
|
|
24
|
+
// Fortuna CSPRNG, Ferguson & Schneier, Practical Cryptography (2003), Chapter 9.
|
|
25
|
+
// Backed by a pluggable Generator (cipher PRF) and HashFn (accumulator hash).
|
|
26
|
+
// Requires init() for the modules used by the chosen generator and hash pair.
|
|
27
|
+
import { isInitialized, getInstance } from './init.js';
|
|
30
28
|
import { wipe, utf8ToBytes, concat } from './utils.js';
|
|
31
29
|
const isBrowser = typeof window !== 'undefined';
|
|
32
30
|
const isNode = typeof process !== 'undefined' && typeof process.pid === 'number';
|
|
33
31
|
/**
|
|
34
|
-
* Fortuna CSPRNG
|
|
32
|
+
* Fortuna CSPRNG, spec §9.3-§9.5
|
|
35
33
|
*
|
|
36
|
-
* Use `Fortuna.create()` to instantiate. Direct construction is not allowed.
|
|
34
|
+
* Use `Fortuna.create({ generator, hash })` to instantiate. Direct construction is not allowed.
|
|
37
35
|
*/
|
|
38
36
|
export class Fortuna {
|
|
39
37
|
// ── Constants ──────────────────────────────────────────────────────────
|
|
40
38
|
static NUM_POOLS = 32;
|
|
41
|
-
static RESEED_LIMIT = 64; // bits
|
|
42
|
-
static MS_PER_RESEED = 100; // ms
|
|
43
|
-
static NODE_STATS_INTERVAL = 1000; // ms
|
|
44
|
-
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
|
|
45
43
|
// ── State ─────────────────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
poolHash; // 32 running
|
|
44
|
+
gen;
|
|
45
|
+
hash;
|
|
46
|
+
poolHash; // 32 running hash chain values
|
|
49
47
|
poolEntropy;
|
|
50
48
|
genKey;
|
|
51
49
|
genCnt;
|
|
@@ -62,25 +60,33 @@ export class Fortuna {
|
|
|
62
60
|
timers = [];
|
|
63
61
|
// ── Static factory ────────────────────────────────────────────────────
|
|
64
62
|
static async create(opts) {
|
|
65
|
-
if (!
|
|
66
|
-
throw new
|
|
67
|
-
if (
|
|
68
|
-
throw new
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
if (!opts || !opts.generator || !opts.hash)
|
|
64
|
+
throw new TypeError('leviathan-crypto: Fortuna.create() requires { generator, hash }');
|
|
65
|
+
if (opts.hash.outputSize !== opts.generator.keySize)
|
|
66
|
+
throw new RangeError(`leviathan-crypto: Fortuna requires hash.outputSize (${opts.hash.outputSize}) `
|
|
67
|
+
+ `to match generator.keySize (${opts.generator.keySize})`);
|
|
68
|
+
const required = new Set([...opts.generator.wasmModules, ...opts.hash.wasmModules]);
|
|
69
|
+
for (const mod of required) {
|
|
70
|
+
if (!isInitialized(mod)) {
|
|
71
|
+
const args = [...required].map(m => `${m}: ...`).join(', ');
|
|
72
|
+
throw new Error(`leviathan-crypto: call init({ ${args} }) before using Fortuna`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const f = new Fortuna(opts.generator, opts.hash, opts.msPerReseed ?? Fortuna.MS_PER_RESEED);
|
|
76
|
+
f.initialize(opts.entropy);
|
|
77
|
+
// Force the first reseed, pool[0] is saturated by initialize(),
|
|
72
78
|
// so this call triggers an immediate reseed and guarantees get() never
|
|
73
79
|
// returns undefined. The byte is discarded.
|
|
74
80
|
f.get(1);
|
|
75
81
|
return f;
|
|
76
82
|
}
|
|
77
|
-
constructor(msPerReseed) {
|
|
78
|
-
this.
|
|
79
|
-
this.
|
|
83
|
+
constructor(gen, hash, msPerReseed) {
|
|
84
|
+
this.gen = gen;
|
|
85
|
+
this.hash = hash;
|
|
80
86
|
this.poolHash = [];
|
|
81
87
|
this.poolEntropy = [];
|
|
82
|
-
this.genKey = new Uint8Array(
|
|
83
|
-
this.genCnt = new Uint8Array(
|
|
88
|
+
this.genKey = new Uint8Array(gen.keySize);
|
|
89
|
+
this.genCnt = new Uint8Array(gen.counterSize);
|
|
84
90
|
this.reseedCnt = 0;
|
|
85
91
|
this.lastReseed = 0;
|
|
86
92
|
this.entropyLevel = 0;
|
|
@@ -90,36 +96,42 @@ export class Fortuna {
|
|
|
90
96
|
this.msPerReseed = msPerReseed;
|
|
91
97
|
this.robin = { kbd: 0, mouse: 0, scroll: 0, touch: 0, motion: 0, time: 0, rnd: 0, dom: 0 };
|
|
92
98
|
for (let i = 0; i < Fortuna.NUM_POOLS; i++) {
|
|
93
|
-
this.poolHash.push(new Uint8Array(
|
|
99
|
+
this.poolHash.push(new Uint8Array(hash.outputSize)); // zero-initialized chain value
|
|
94
100
|
this.poolEntropy.push(0);
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
// ── Public API ────────────────────────────────────────────────────────
|
|
98
|
-
/** Get n random bytes. Always returns Uint8Array
|
|
104
|
+
/** Get n random bytes. Always returns Uint8Array, instance is guaranteed seeded after create(). */
|
|
99
105
|
get(length) {
|
|
100
106
|
if (this.disposed)
|
|
101
107
|
throw new Error('Fortuna instance has been disposed');
|
|
102
|
-
// Capture hrtime jitter at call time (Node.js)
|
|
108
|
+
// Capture hrtime jitter at call time (Node.js), spec §9.5
|
|
103
109
|
if (isNode)
|
|
104
110
|
this.captureHrtime();
|
|
105
|
-
// Check reseed trigger
|
|
111
|
+
// Check reseed trigger, spec §9.5
|
|
106
112
|
if (this.poolEntropy[0] >= Fortuna.RESEED_LIMIT &&
|
|
107
113
|
Date.now() >= this.lastReseed + this.msPerReseed) {
|
|
108
114
|
this.reseedCnt = (this.reseedCnt + 1) >>> 0; // u32 wrap
|
|
109
115
|
let seed = new Uint8Array(0);
|
|
110
116
|
let strength = 0;
|
|
111
117
|
for (let i = 0; i < Fortuna.NUM_POOLS; i++) {
|
|
112
|
-
|
|
118
|
+
// Practical Cryptography (Ferguson & Schneier, 2003) §9.5.5:
|
|
119
|
+
// pool P_i is used in reseed r iff 2^i divides r.
|
|
120
|
+
if ((this.reseedCnt & ((1 << i) - 1)) === 0) {
|
|
113
121
|
// Pool digest = current chain hash
|
|
114
122
|
seed = concat(seed, this.poolHash[i]);
|
|
115
123
|
strength += this.poolEntropy[i];
|
|
116
|
-
// Reset pool
|
|
117
|
-
this.poolHash[i]
|
|
124
|
+
// Reset pool, wipe old chain hash before dropping the reference.
|
|
125
|
+
const old = this.poolHash[i];
|
|
126
|
+
this.poolHash[i] = new Uint8Array(this.hash.outputSize);
|
|
127
|
+
wipe(old);
|
|
118
128
|
this.poolEntropy[i] = 0;
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
131
|
this.entropyLevel -= strength;
|
|
122
132
|
this.reseed(seed);
|
|
133
|
+
// seed is built from concatenated pool-hash copies; wipe the temp.
|
|
134
|
+
wipe(seed);
|
|
123
135
|
}
|
|
124
136
|
return this.pseudoRandomData(length);
|
|
125
137
|
}
|
|
@@ -140,64 +152,130 @@ export class Fortuna {
|
|
|
140
152
|
stop() {
|
|
141
153
|
if (this.disposed)
|
|
142
154
|
throw new Error('Fortuna instance has been disposed');
|
|
155
|
+
// Mark disposed first so partially-disposed reentry throws cleanly.
|
|
156
|
+
this.disposed = true;
|
|
143
157
|
this.stopCollectors();
|
|
144
158
|
wipe(this.genKey);
|
|
145
159
|
wipe(this.genCnt);
|
|
160
|
+
// Wipe the 32 pool-hash chain values so residual entropy-bearing
|
|
161
|
+
// bytes do not outlive the instance.
|
|
162
|
+
for (const p of this.poolHash)
|
|
163
|
+
wipe(p);
|
|
146
164
|
this.reseedCnt = 0;
|
|
147
|
-
|
|
165
|
+
// Best-effort wipe of WASM scratch; surface the first error.
|
|
166
|
+
const required = new Set([...this.gen.wasmModules, ...this.hash.wasmModules]);
|
|
167
|
+
let err;
|
|
168
|
+
for (const mod of required) {
|
|
169
|
+
try {
|
|
170
|
+
getInstance(mod).exports.wipeBuffers();
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
err ??= e;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (err)
|
|
177
|
+
throw err;
|
|
148
178
|
}
|
|
149
179
|
// ── Test-only accessors ───────────────────────────────────────────────
|
|
150
|
-
/** @internal
|
|
180
|
+
/** @internal, exposed for testing key replacement */
|
|
151
181
|
_getGenKey() {
|
|
152
182
|
return this.genKey;
|
|
153
183
|
}
|
|
154
|
-
/** @internal
|
|
184
|
+
/** @internal, exposed for testing pool state */
|
|
155
185
|
_getPoolEntropy() {
|
|
156
186
|
return this.poolEntropy;
|
|
157
187
|
}
|
|
158
|
-
/** @internal
|
|
188
|
+
/** @internal, exposed for testing reseed count */
|
|
159
189
|
_getReseedCnt() {
|
|
160
190
|
return this.reseedCnt;
|
|
161
191
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
192
|
+
/** @internal, exposed for testing pool-hash backing arrays */
|
|
193
|
+
_getPoolHash() {
|
|
194
|
+
return this.poolHash;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* @internal, test-only deterministic factory. Seeds pool[0] with the provided
|
|
198
|
+
* entropy and triggers one reseed directly, bypassing all OS entropy collection
|
|
199
|
+
* and the hrtime jitter capture in get(). This makes KAT vectors reproducible
|
|
200
|
+
* across runs. Not suitable for production use.
|
|
201
|
+
*/
|
|
202
|
+
static async _createDeterministicForTesting(opts) {
|
|
203
|
+
if (!opts || !opts.generator || !opts.hash)
|
|
204
|
+
throw new TypeError('Fortuna._createDeterministicForTesting() requires { generator, hash, entropy }');
|
|
205
|
+
if (opts.hash.outputSize !== opts.generator.keySize)
|
|
206
|
+
throw new RangeError(`leviathan-crypto: Fortuna requires hash.outputSize (${opts.hash.outputSize}) `
|
|
207
|
+
+ `to match generator.keySize (${opts.generator.keySize})`);
|
|
208
|
+
const required = new Set([...opts.generator.wasmModules, ...opts.hash.wasmModules]);
|
|
209
|
+
for (const mod of required) {
|
|
210
|
+
if (!isInitialized(mod)) {
|
|
211
|
+
const args = [...required].map(m => `${m}: ...`).join(', ');
|
|
212
|
+
throw new Error(`leviathan-crypto: call init({ ${args} }) before using Fortuna`);
|
|
213
|
+
}
|
|
171
214
|
}
|
|
172
|
-
|
|
215
|
+
const f = new Fortuna(opts.generator, opts.hash, 0);
|
|
216
|
+
// Seed pool[0] with the provided entropy, no OS collection.
|
|
217
|
+
f.addRandomEvent(opts.entropy, 0, opts.entropy.length * 8);
|
|
218
|
+
// Manually trigger reseed #1 without calling get(), get() calls captureHrtime()
|
|
219
|
+
// in Node.js which adds non-deterministic data before the reseed fires.
|
|
220
|
+
f.reseedFromPool0();
|
|
221
|
+
return f;
|
|
173
222
|
}
|
|
174
|
-
|
|
223
|
+
// ── Generator (spec §9.4) ─────────────────────────────────────────────
|
|
224
|
+
/** Get length pseudo-random bytes., spec §9.4 */
|
|
175
225
|
pseudoRandomData(length) {
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
226
|
+
const blocks = Math.ceil(length / this.gen.blockSize);
|
|
227
|
+
const out = this.gen.generate(this.genKey, this.genCnt, length);
|
|
228
|
+
// External counter advance, generator is stateless and does not mutate caller's counter
|
|
229
|
+
for (let i = 0; i < blocks; i++)
|
|
230
|
+
this.incrementCounter();
|
|
231
|
+
// Key replacement, mandatory forward secrecy (spec §9.4).
|
|
232
|
+
// Wipe the prior key BEFORE dropping its reference so no key bytes are
|
|
233
|
+
// reachable after key replacement; anyone holding a Uint8Array view to
|
|
234
|
+
// the old key now observes zero.
|
|
235
|
+
const newKey = this.gen.generate(this.genKey, this.genCnt, this.gen.keySize);
|
|
236
|
+
for (let i = 0; i < Math.ceil(this.gen.keySize / this.gen.blockSize); i++)
|
|
237
|
+
this.incrementCounter();
|
|
238
|
+
wipe(this.genKey);
|
|
239
|
+
this.genKey = newKey;
|
|
240
|
+
return out;
|
|
183
241
|
}
|
|
184
|
-
/** Reseed the generator
|
|
242
|
+
/** Reseed the generator, spec §9.4 */
|
|
185
243
|
reseed(seed) {
|
|
186
|
-
// genKey =
|
|
187
|
-
|
|
188
|
-
|
|
244
|
+
// genKey = hash(genKey ‖ seed). Wipe both the hash input and the
|
|
245
|
+
// prior key before dropping references.
|
|
246
|
+
const combined = concat(this.genKey, seed);
|
|
247
|
+
const newKey = this.hash.digest(combined);
|
|
248
|
+
wipe(combined);
|
|
249
|
+
wipe(this.genKey);
|
|
250
|
+
this.genKey = newKey;
|
|
251
|
+
// Increment counter, makes it nonzero on first reseed, marking generator as seeded
|
|
189
252
|
this.incrementCounter();
|
|
190
253
|
this.lastReseed = Date.now();
|
|
191
254
|
}
|
|
192
|
-
/**
|
|
255
|
+
/** Drain pool 0 into a fresh seed and reseed. Used by the deterministic
|
|
256
|
+
* test factory; production reseeds in get() walk the §9.5.5 schedule
|
|
257
|
+
* across all pools, not just pool 0. Caller is responsible for any
|
|
258
|
+
* entropy-threshold check. */
|
|
259
|
+
reseedFromPool0() {
|
|
260
|
+
this.reseedCnt = (this.reseedCnt + 1) >>> 0;
|
|
261
|
+
const seed = this.poolHash[0].slice();
|
|
262
|
+
const old = this.poolHash[0];
|
|
263
|
+
this.poolHash[0] = new Uint8Array(this.hash.outputSize);
|
|
264
|
+
wipe(old);
|
|
265
|
+
this.entropyLevel -= this.poolEntropy[0];
|
|
266
|
+
this.poolEntropy[0] = 0;
|
|
267
|
+
this.reseed(seed);
|
|
268
|
+
wipe(seed);
|
|
269
|
+
}
|
|
270
|
+
/** Increment little-endian counter., spec §9.4 */
|
|
193
271
|
incrementCounter() {
|
|
194
|
-
for (let i = 0; i <
|
|
272
|
+
for (let i = 0; i < this.genCnt.length; i++) {
|
|
195
273
|
if (++this.genCnt[i] !== 0)
|
|
196
274
|
break;
|
|
197
275
|
}
|
|
198
276
|
}
|
|
199
277
|
// ── Accumulator (spec §9.5) ───────────────────────────────────────────
|
|
200
|
-
/** Add an event to a pool via hash chaining: poolHash[i] =
|
|
278
|
+
/** Add an event to a pool via hash chaining: poolHash[i] = hash(poolHash[i] ‖ eventId ‖ data). */
|
|
201
279
|
addRandomEvent(data, poolIdx, entropyBits) {
|
|
202
280
|
// Encode eventId as 4 bytes little-endian
|
|
203
281
|
const id = new Uint8Array(4);
|
|
@@ -206,14 +284,19 @@ export class Fortuna {
|
|
|
206
284
|
id[2] = (this.eventId >>> 16) & 0xff;
|
|
207
285
|
id[3] = (this.eventId >>> 24) & 0xff;
|
|
208
286
|
this.eventId = (this.eventId + 1) >>> 0; // u32 wrap
|
|
209
|
-
// Chain: poolHash[i] =
|
|
210
|
-
|
|
287
|
+
// Chain: poolHash[i] = hash(poolHash[i] ‖ id ‖ data).
|
|
288
|
+
// Wipe the chain input and the prior chain value before dropping refs.
|
|
289
|
+
const combined = concat(this.poolHash[poolIdx], id, data);
|
|
290
|
+
const newChain = this.hash.digest(combined);
|
|
291
|
+
wipe(combined);
|
|
292
|
+
wipe(this.poolHash[poolIdx]);
|
|
293
|
+
this.poolHash[poolIdx] = newChain;
|
|
211
294
|
this.poolEntropy[poolIdx] += entropyBits;
|
|
212
295
|
this.entropyLevel += entropyBits;
|
|
213
296
|
}
|
|
214
297
|
// ── Initialization ────────────────────────────────────────────────────
|
|
215
298
|
initialize(entropy) {
|
|
216
|
-
// Initial seeding
|
|
299
|
+
// Initial seeding, crypto random per pool (spec §9.5)
|
|
217
300
|
for (let i = 0; i < Fortuna.NUM_POOLS * 4; i++) {
|
|
218
301
|
this.collectorCryptoRandom();
|
|
219
302
|
}
|
|
@@ -226,6 +309,13 @@ export class Fortuna {
|
|
|
226
309
|
this.addRandomEvent(entropy, this.robin.rnd, entropy.length * 8);
|
|
227
310
|
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
228
311
|
}
|
|
312
|
+
// F-2 invariant: fail loudly if no OS entropy source delivered anything.
|
|
313
|
+
// The try/catch in collectorCryptoRandom is preserved to protect against
|
|
314
|
+
// platforms where crypto.getRandomValues itself throws (non-standard
|
|
315
|
+
// runtimes). This post-init check covers all silent-failure paths uniformly.
|
|
316
|
+
if (this.poolEntropy[0] < Fortuna.RESEED_LIMIT)
|
|
317
|
+
throw new Error('leviathan-crypto: Fortuna initialization could not gather sufficient entropy. '
|
|
318
|
+
+ 'No working crypto.getRandomValues in this environment.');
|
|
229
319
|
this.startCollectors();
|
|
230
320
|
}
|
|
231
321
|
// ── Collectors ────────────────────────────────────────────────────────
|
|
@@ -255,7 +345,7 @@ export class Fortuna {
|
|
|
255
345
|
// OS stats timer
|
|
256
346
|
this.timers.push(setInterval(() => this.collectNodeStats(), Fortuna.NODE_STATS_INTERVAL));
|
|
257
347
|
}
|
|
258
|
-
// Crypto timer
|
|
348
|
+
// Crypto timer, both environments
|
|
259
349
|
this.timers.push(setInterval(() => this.collectorCryptoRandom(), Fortuna.CRYPTO_INTERVAL));
|
|
260
350
|
this.active = true;
|
|
261
351
|
}
|
|
@@ -358,25 +448,18 @@ export class Fortuna {
|
|
|
358
448
|
}
|
|
359
449
|
collectorDom() {
|
|
360
450
|
if (typeof document !== 'undefined' && document.documentElement) {
|
|
361
|
-
this.addRandomEvent(this.
|
|
451
|
+
this.addRandomEvent(this.hash.digest(utf8ToBytes(document.documentElement.innerHTML)), this.robin.dom, 2);
|
|
362
452
|
this.robin.dom = (this.robin.dom + 1) % Fortuna.NUM_POOLS;
|
|
363
453
|
}
|
|
364
454
|
}
|
|
365
455
|
collectorCryptoRandom() {
|
|
366
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;
|
|
367
461
|
const rnd = new Uint8Array(128);
|
|
368
|
-
|
|
369
|
-
globalThis.crypto.getRandomValues(rnd);
|
|
370
|
-
}
|
|
371
|
-
else if (isNode) {
|
|
372
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
373
|
-
const nodeCrypto = require('node:crypto');
|
|
374
|
-
const buf = nodeCrypto.randomBytes(128);
|
|
375
|
-
rnd.set(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
return; // no crypto source available
|
|
379
|
-
}
|
|
462
|
+
globalThis.crypto.getRandomValues(rnd);
|
|
380
463
|
this.addRandomEvent(rnd, this.robin.rnd, 1024);
|
|
381
464
|
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
382
465
|
}
|
|
@@ -395,14 +478,14 @@ export class Fortuna {
|
|
|
395
478
|
}
|
|
396
479
|
collectNodeStats() {
|
|
397
480
|
try {
|
|
398
|
-
// hrtime
|
|
481
|
+
// hrtime, nanosecond scheduling jitter
|
|
399
482
|
const hr = process.hrtime.bigint();
|
|
400
483
|
const hrBytes = new Uint8Array(8);
|
|
401
484
|
for (let i = 0; i < 8; i++)
|
|
402
485
|
hrBytes[i] = Number((hr >> BigInt(i * 8)) & 0xffn);
|
|
403
486
|
this.addRandomEvent(hrBytes, this.robin.time, 8);
|
|
404
487
|
this.robin.time = (this.robin.time + 1) % Fortuna.NUM_POOLS;
|
|
405
|
-
// cpuUsage
|
|
488
|
+
// cpuUsage, user + system CPU microseconds
|
|
406
489
|
const cpu = process.cpuUsage();
|
|
407
490
|
const cpuBytes = new Uint8Array(8);
|
|
408
491
|
cpuBytes[0] = cpu.user & 0xff;
|
|
@@ -415,7 +498,7 @@ export class Fortuna {
|
|
|
415
498
|
cpuBytes[7] = (cpu.system >>> 24) & 0xff;
|
|
416
499
|
this.addRandomEvent(cpuBytes, this.robin.rnd, 2);
|
|
417
500
|
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
418
|
-
// memoryUsage
|
|
501
|
+
// memoryUsage, heapUsed changes constantly
|
|
419
502
|
const mem = process.memoryUsage();
|
|
420
503
|
const memVal = mem.heapUsed;
|
|
421
504
|
const memBytes = new Uint8Array(4);
|
|
@@ -425,22 +508,6 @@ export class Fortuna {
|
|
|
425
508
|
memBytes[3] = (memVal >>> 24) & 0xff;
|
|
426
509
|
this.addRandomEvent(memBytes, this.robin.rnd, 1);
|
|
427
510
|
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
428
|
-
// loadavg — slow-changing but real system state
|
|
429
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
430
|
-
const os = require('node:os');
|
|
431
|
-
const la = os.loadavg();
|
|
432
|
-
const laStr = la.map((n) => Math.round(n * 1000).toString()).join('');
|
|
433
|
-
this.addRandomEvent(utf8ToBytes(laStr), this.robin.time, 1);
|
|
434
|
-
this.robin.time = (this.robin.time + 1) % Fortuna.NUM_POOLS;
|
|
435
|
-
// freemem — changes with allocation activity
|
|
436
|
-
const fm = os.freemem();
|
|
437
|
-
const fmBytes = new Uint8Array(4);
|
|
438
|
-
fmBytes[0] = fm & 0xff;
|
|
439
|
-
fmBytes[1] = (fm >>> 8) & 0xff;
|
|
440
|
-
fmBytes[2] = (fm >>> 16) & 0xff;
|
|
441
|
-
fmBytes[3] = (fm >>> 24) & 0xff;
|
|
442
|
-
this.addRandomEvent(fmBytes, this.robin.rnd, 1);
|
|
443
|
-
this.robin.rnd = (this.robin.rnd + 1) % Fortuna.NUM_POOLS;
|
|
444
511
|
}
|
|
445
512
|
catch { /* Node APIs may not be available */ }
|
|
446
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,20 +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
|
-
export { isInitialized
|
|
17
|
-
export { AuthenticationError } from './errors.js';
|
|
18
|
-
export { serpentInit, Serpent, SerpentCtr, SerpentCbc, SerpentCipher,
|
|
19
|
-
export { chacha20Init, ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Cipher,
|
|
20
|
-
export { sha2Init, SHA256,
|
|
21
|
-
export { sha3Init, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256,
|
|
27
|
+
export { isInitialized } from './init.js';
|
|
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
|
-
export type { Hash, KeyedHash, Blockcipher, Streamcipher, AEAD } from './types.js';
|
|
29
|
-
export {
|
|
57
|
+
export type { Hash, KeyedHash, Blockcipher, Streamcipher, AEAD, Generator, HashFn } from './types.js';
|
|
58
|
+
export { KDFChain, ratchetInit, kemRatchetEncap, kemRatchetDecap, ratchetReady, SkippedKeyStore, RatchetKeypair, } from './ratchet/index.js';
|
|
59
|
+
export type { RatchetInitResult, KemEncapResult, KemDecapResult, MlKemLike, RatchetMessageHeader, ResolveHandle, SkippedKeyStoreOpts, } from './ratchet/index.js';
|
|
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,29 +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
|
-
export { isInitialized
|
|
64
|
-
export { AuthenticationError } from './errors.js';
|
|
65
|
-
export { serpentInit, Serpent, SerpentCtr, SerpentCbc, SerpentCipher,
|
|
66
|
-
export { chacha20Init, ChaCha20, Poly1305, ChaCha20Poly1305, XChaCha20Poly1305, XChaCha20Cipher,
|
|
67
|
-
export { sha2Init, SHA256,
|
|
68
|
-
export { sha3Init, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256,
|
|
96
|
+
export { isInitialized } from './init.js';
|
|
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
|
-
export {
|
|
117
|
+
export { KDFChain, ratchetInit, kemRatchetEncap, kemRatchetDecap, ratchetReady, SkippedKeyStore, RatchetKeypair, } from './ratchet/index.js';
|
|
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,7 +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;
|
|
6
|
-
/** Reset all cached instances — for testing only */
|
|
7
|
-
export declare function _resetForTesting(): void;
|