leviathan-crypto 1.3.1 → 2.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 +129 -76
- package/README.md +166 -221
- package/SECURITY.md +89 -37
- package/dist/chacha20/cipher-suite.d.ts +4 -0
- package/dist/chacha20/cipher-suite.js +78 -0
- package/dist/chacha20/embedded.d.ts +1 -0
- package/dist/chacha20/embedded.js +27 -0
- package/dist/chacha20/index.d.ts +20 -7
- package/dist/chacha20/index.js +41 -14
- package/dist/chacha20/ops.d.ts +1 -1
- package/dist/chacha20/ops.js +19 -18
- package/dist/chacha20/pool-worker.js +77 -0
- package/dist/ct-wasm.d.ts +1 -0
- package/dist/ct-wasm.js +3 -0
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +320 -0
- package/dist/docs/architecture.md +419 -285
- package/dist/docs/argon2id.md +42 -30
- package/dist/docs/chacha20.md +218 -150
- package/dist/docs/exports.md +241 -0
- package/dist/docs/fortuna.md +65 -74
- package/dist/docs/init.md +172 -178
- package/dist/docs/loader.md +87 -132
- package/dist/docs/serpent.md +134 -565
- package/dist/docs/sha2.md +91 -103
- package/dist/docs/sha3.md +70 -36
- package/dist/docs/types.md +93 -16
- package/dist/docs/utils.md +114 -41
- package/dist/embedded/chacha20.d.ts +1 -1
- package/dist/embedded/chacha20.js +2 -1
- package/dist/embedded/kyber.d.ts +1 -0
- package/dist/embedded/kyber.js +3 -0
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +2 -1
- package/dist/embedded/sha2.d.ts +1 -1
- package/dist/embedded/sha2.js +2 -1
- package/dist/embedded/sha3.d.ts +1 -1
- package/dist/embedded/sha3.js +2 -1
- package/dist/errors.d.ts +10 -0
- package/dist/{serpent/seal.js → errors.js} +14 -46
- package/dist/fortuna.d.ts +2 -8
- package/dist/fortuna.js +11 -9
- package/dist/index.d.ts +25 -9
- package/dist/index.js +36 -7
- package/dist/init.d.ts +3 -7
- package/dist/init.js +18 -35
- package/dist/keccak/embedded.d.ts +1 -0
- package/dist/keccak/embedded.js +27 -0
- package/dist/keccak/index.d.ts +4 -0
- package/dist/keccak/index.js +31 -0
- package/dist/kyber/embedded.d.ts +1 -0
- package/dist/kyber/embedded.js +27 -0
- package/dist/kyber/indcpa.d.ts +49 -0
- package/dist/kyber/indcpa.js +352 -0
- package/dist/kyber/index.d.ts +38 -0
- package/dist/kyber/index.js +150 -0
- package/dist/kyber/kem.d.ts +21 -0
- package/dist/kyber/kem.js +160 -0
- package/dist/kyber/params.d.ts +14 -0
- package/dist/kyber/params.js +37 -0
- package/dist/kyber/suite.d.ts +13 -0
- package/dist/kyber/suite.js +93 -0
- package/dist/kyber/types.d.ts +98 -0
- package/dist/kyber/types.js +25 -0
- package/dist/kyber/validate.d.ts +19 -0
- package/dist/kyber/validate.js +68 -0
- package/dist/kyber.wasm +0 -0
- package/dist/loader.d.ts +19 -4
- package/dist/loader.js +91 -25
- package/dist/serpent/cipher-suite.d.ts +4 -0
- package/dist/serpent/cipher-suite.js +121 -0
- package/dist/serpent/embedded.d.ts +1 -0
- package/dist/serpent/embedded.js +27 -0
- package/dist/serpent/index.d.ts +6 -37
- package/dist/serpent/index.js +9 -118
- package/dist/serpent/pool-worker.d.ts +1 -0
- package/dist/serpent/pool-worker.js +202 -0
- package/dist/serpent/serpent-cbc.d.ts +30 -0
- package/dist/serpent/serpent-cbc.js +136 -0
- package/dist/sha2/embedded.d.ts +1 -0
- package/dist/sha2/embedded.js +27 -0
- package/dist/sha2/hkdf.js +6 -2
- package/dist/sha2/index.d.ts +3 -2
- package/dist/sha2/index.js +3 -4
- package/dist/sha3/embedded.d.ts +1 -0
- package/dist/sha3/embedded.js +27 -0
- package/dist/sha3/index.d.ts +3 -2
- package/dist/sha3/index.js +3 -4
- package/dist/stream/constants.d.ts +6 -0
- package/dist/stream/constants.js +30 -0
- package/dist/stream/header.d.ts +9 -0
- package/dist/stream/header.js +77 -0
- package/dist/stream/index.d.ts +7 -0
- package/dist/stream/index.js +27 -0
- package/dist/stream/open-stream.d.ts +21 -0
- package/dist/stream/open-stream.js +146 -0
- package/dist/stream/seal-stream-pool.d.ts +38 -0
- package/dist/stream/seal-stream-pool.js +391 -0
- package/dist/stream/seal-stream.d.ts +20 -0
- package/dist/stream/seal-stream.js +142 -0
- package/dist/stream/seal.d.ts +9 -0
- package/dist/stream/seal.js +75 -0
- package/dist/stream/types.d.ts +24 -0
- package/dist/stream/types.js +26 -0
- package/dist/utils.d.ts +12 -7
- package/dist/utils.js +75 -19
- package/dist/wasm-source.d.ts +12 -0
- package/dist/wasm-source.js +26 -0
- package/package.json +13 -5
- package/dist/chacha20/pool.d.ts +0 -52
- package/dist/chacha20/pool.js +0 -188
- package/dist/chacha20/pool.worker.js +0 -37
- package/dist/docs/chacha20_pool.md +0 -309
- package/dist/docs/wasm.md +0 -194
- package/dist/serpent/seal.d.ts +0 -8
- package/dist/serpent/stream-pool.d.ts +0 -48
- package/dist/serpent/stream-pool.js +0 -285
- package/dist/serpent/stream-sealer.d.ts +0 -50
- package/dist/serpent/stream-sealer.js +0 -341
- package/dist/serpent/stream.d.ts +0 -28
- package/dist/serpent/stream.js +0 -205
- package/dist/serpent/stream.worker.d.ts +0 -32
- package/dist/serpent/stream.worker.js +0 -117
- /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# All Exports
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> Complete reference for every public export in leviathan-crypto, grouped by module. Follow the module links for deeper documentation on each class.
|
|
5
|
+
|
|
6
|
+
> ### Table of Contents
|
|
7
|
+
> - [Initialization](#initialization)
|
|
8
|
+
> - [Serpent-256](#serpent-256)
|
|
9
|
+
> - [Stream](#stream)
|
|
10
|
+
> - [Errors](#errors)
|
|
11
|
+
> - [XChaCha20 / Poly1305](#xchacha20--poly1305)
|
|
12
|
+
> - [SHA-2](#sha-2)
|
|
13
|
+
> - [SHA-3](#sha-3)
|
|
14
|
+
> - [Keccak (alias for SHA-3)](#keccak-alias-for-sha-3)
|
|
15
|
+
> - [ML-KEM (Post-quantum KEM)](#ml-kem-post-quantum-kem)
|
|
16
|
+
> - [Fortuna CSPRNG](#fortuna-csprng)
|
|
17
|
+
> - [Types](#types)
|
|
18
|
+
> - [Utilities](#utilities)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Initialization
|
|
23
|
+
|
|
24
|
+
Root barrel `leviathan-crypto`. No module required.
|
|
25
|
+
|
|
26
|
+
| Export | Kind | Description |
|
|
27
|
+
|--------|------|-------------|
|
|
28
|
+
| `init` | function | Load and cache WASM modules. `init(sources: Partial<Record<Module, WasmSource>>)`. |
|
|
29
|
+
| `isInitialized` | function | `isInitialized(mod: Module): boolean`. Returns `true` if the given module has been loaded. Useful for diagnostic checks. |
|
|
30
|
+
| `Module` | type | `'serpent' \| 'chacha20' \| 'sha2' \| 'sha3' \| 'keccak' \| 'kyber'` |
|
|
31
|
+
| `WasmSource` | type | Union of all accepted WASM loading strategies. See below. |
|
|
32
|
+
|
|
33
|
+
**`WasmSource`** accepted by every init function:
|
|
34
|
+
|
|
35
|
+
| Value | Strategy |
|
|
36
|
+
|-------|----------|
|
|
37
|
+
| `string` | Decode gzip+base64 embedded blob |
|
|
38
|
+
| `URL` | `fetch` + `instantiateStreaming` |
|
|
39
|
+
| `ArrayBuffer` | Compile from raw WASM bytes |
|
|
40
|
+
| `Uint8Array` | Compile from raw WASM bytes |
|
|
41
|
+
| `WebAssembly.Module` | Instantiate pre-compiled module |
|
|
42
|
+
| `Response` | `instantiateStreaming` from fetch response |
|
|
43
|
+
| `Promise<Response>` | `instantiateStreaming` from deferred fetch |
|
|
44
|
+
|
|
45
|
+
See [init.md](./init.md) for full loading documentation.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Serpent-256
|
|
50
|
+
|
|
51
|
+
Requires `init({ serpent: serpentWasm, sha2: sha2Wasm })` for authenticated classes, `init({ serpent: serpentWasm })` for raw modes.
|
|
52
|
+
Subpath: `leviathan-crypto/serpent`. See [serpent.md](./serpent.md).
|
|
53
|
+
|
|
54
|
+
| Export | Kind | Description |
|
|
55
|
+
|--------|------|-------------|
|
|
56
|
+
| `serpentInit` | function | Module-scoped init. `serpentInit(source: WasmSource)` loads only serpent. |
|
|
57
|
+
| `SerpentCipher` | const | `CipherSuite` for Serpent-256 CBC+HMAC-SHA-256. `keygen()` → 32-byte key. `formatEnum: 0x02`, `keySize: 32`, `tagSize: 32`, `padded: true`. Used with `Seal`, `SealStream`, `OpenStream`. |
|
|
58
|
+
| `Serpent` | class | Serpent-256 ECB block cipher. `loadKey()`, `encryptBlock()`, `decryptBlock()`. Unauthenticated. |
|
|
59
|
+
| `SerpentCtr` | class | Serpent-256 CTR mode. `beginEncrypt()`, `encryptChunk()`, `beginDecrypt()`, `decryptChunk()`. Unauthenticated. |
|
|
60
|
+
| `SerpentCbc` | class | Serpent-256 CBC mode with PKCS7 padding. `encrypt(key, iv, plaintext)`, `decrypt(key, iv, ciphertext)`. Unauthenticated. |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Stream
|
|
65
|
+
|
|
66
|
+
Cipher-agnostic streaming encryption using the STREAM construction.
|
|
67
|
+
Subpath: `leviathan-crypto/stream`. See [aead.md](./aead.md).
|
|
68
|
+
|
|
69
|
+
| Export | Kind | Description |
|
|
70
|
+
|--------|------|-------------|
|
|
71
|
+
| `Seal` | class (static) | One-shot AEAD. `Seal.encrypt(suite, key, plaintext)` / `Seal.decrypt(suite, key, blob)`. Works with any `CipherSuite` including `KyberSuite`. Never instantiated. |
|
|
72
|
+
| `SealStream` | class | Cipher-agnostic streaming encryption (STREAM construction). `push(chunk)`, `finalize(chunk)`, `toTransformStream()`. |
|
|
73
|
+
| `OpenStream` | class | Cipher-agnostic streaming decryption. `pull(chunk)`, `finalize(chunk)`, `seek(index)`, `toTransformStream()`. |
|
|
74
|
+
| `SealStreamPool` | class | Parallel batch seal/open via Web Workers. `SealStreamPool.create(cipher, key, opts)` static factory. |
|
|
75
|
+
| `CipherSuite` | interface | Cipher-specific logic injected into SealStream/OpenStream. Implementations: `XChaCha20Cipher`, `SerpentCipher`, `KyberSuite`. See [ciphersuite.md](./ciphersuite.md). |
|
|
76
|
+
| `DerivedKeys` | interface | Opaque key material returned by `CipherSuite.deriveKeys()`. |
|
|
77
|
+
| `SealStreamOpts` | type | Options for SealStream: `chunkSize?`, `framed?`. |
|
|
78
|
+
| `PoolOpts` | type | Options for SealStreamPool: `wasm`, `workers?`, `chunkSize?`, `framed?`, `jobTimeout?`. |
|
|
79
|
+
| `HEADER_SIZE` | const | Stream header size in bytes (20). |
|
|
80
|
+
| `CHUNK_MIN` | const | Minimum chunk size (1024). |
|
|
81
|
+
| `CHUNK_MAX` | const | Maximum chunk size (16777215, u24 max). |
|
|
82
|
+
| `FLAG_FRAMED` | const | Header byte 0 framed flag (0x80). |
|
|
83
|
+
| `TAG_DATA` | const | Counter nonce final flag for data chunks (0x00). |
|
|
84
|
+
| `TAG_FINAL` | const | Counter nonce final flag for final chunk (0x01). |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Errors
|
|
89
|
+
|
|
90
|
+
| Export | Kind | Description |
|
|
91
|
+
|--------|------|-------------|
|
|
92
|
+
| `AuthenticationError` | class | Thrown on AEAD auth failure. Extends `Error`. Constructor takes cipher name string. |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## XChaCha20 / Poly1305
|
|
97
|
+
|
|
98
|
+
Requires `init({ chacha20: chacha20Wasm })` or subpath `chacha20Init()`.
|
|
99
|
+
Subpath: `leviathan-crypto/chacha20`. See [chacha20.md](./chacha20.md).
|
|
100
|
+
|
|
101
|
+
| Export | Kind | Description |
|
|
102
|
+
|--------|------|-------------|
|
|
103
|
+
| `chacha20Init` | function | Module-scoped init. `chacha20Init(source: WasmSource)` loads only chacha20. |
|
|
104
|
+
| `XChaCha20Poly1305` | class | XChaCha20-Poly1305 AEAD. 24-byte nonce. `encrypt()` returns single `Uint8Array` (ct‖tag), `decrypt()` accepts same format. Single-use encrypt guard. |
|
|
105
|
+
| `XChaCha20Cipher` | const | `CipherSuite` for XChaCha20-Poly1305. `keygen()` → 32-byte key. `formatEnum: 0x01`, `keySize: 32`, `tagSize: 16`, `padded: false`. Used with `Seal`, `SealStream`, `OpenStream`. |
|
|
106
|
+
| `ChaCha20Poly1305` | class | ChaCha20-Poly1305 AEAD (RFC 8439). 12-byte nonce. `encrypt()` returns single `Uint8Array` (ct‖tag), `decrypt()` accepts same format. Single-use encrypt guard. |
|
|
107
|
+
| `ChaCha20` | class | ChaCha20 stream cipher (RFC 8439). `beginEncrypt()`, `encryptChunk()`. Unauthenticated. |
|
|
108
|
+
| `Poly1305` | class | Poly1305 one-time MAC (RFC 8439). `mac(key, msg)`. |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## SHA-2
|
|
113
|
+
|
|
114
|
+
Requires `init({ sha2: sha2Wasm })` or subpath `sha2Init(source)`.
|
|
115
|
+
Subpath: `leviathan-crypto/sha2`. See [sha2.md](./sha2.md).
|
|
116
|
+
|
|
117
|
+
| Export | Kind | Description |
|
|
118
|
+
|--------|------|-------------|
|
|
119
|
+
| `sha2Init` | function | Module-scoped init. `sha2Init(source: WasmSource)` loads only sha2. |
|
|
120
|
+
| `SHA256` | class | SHA-256 hash (FIPS 180-4). `hash(msg)` returns 32 bytes. |
|
|
121
|
+
| `SHA384` | class | SHA-384 hash (FIPS 180-4). `hash(msg)` returns 48 bytes. |
|
|
122
|
+
| `SHA512` | class | SHA-512 hash (FIPS 180-4). `hash(msg)` returns 64 bytes. |
|
|
123
|
+
| `HMAC_SHA256` | class | HMAC-SHA256 (RFC 2104). `hash(key, msg)` returns 32 bytes. |
|
|
124
|
+
| `HMAC_SHA384` | class | HMAC-SHA384 (RFC 2104). `hash(key, msg)` returns 48 bytes. |
|
|
125
|
+
| `HMAC_SHA512` | class | HMAC-SHA512 (RFC 2104). `hash(key, msg)` returns 64 bytes. |
|
|
126
|
+
| `HKDF_SHA256` | class | HKDF with HMAC-SHA256 (RFC 5869). `derive(ikm, salt, info, length)`. |
|
|
127
|
+
| `HKDF_SHA512` | class | HKDF with HMAC-SHA512 (RFC 5869). `derive(ikm, salt, info, length)`. |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## SHA-3
|
|
132
|
+
|
|
133
|
+
Requires `init({ sha3: sha3Wasm })` or subpath `sha3Init(source)`.
|
|
134
|
+
Subpath: `leviathan-crypto/sha3`. See [sha3.md](./sha3.md).
|
|
135
|
+
|
|
136
|
+
| Export | Kind | Description |
|
|
137
|
+
|--------|------|-------------|
|
|
138
|
+
| `sha3Init` | function | Module-scoped init. `sha3Init(source: WasmSource)` loads only sha3. |
|
|
139
|
+
| `SHA3_224` | class | SHA3-224 hash (FIPS 202). `hash(msg)` returns 28 bytes. |
|
|
140
|
+
| `SHA3_256` | class | SHA3-256 hash (FIPS 202). `hash(msg)` returns 32 bytes. |
|
|
141
|
+
| `SHA3_384` | class | SHA3-384 hash (FIPS 202). `hash(msg)` returns 48 bytes. |
|
|
142
|
+
| `SHA3_512` | class | SHA3-512 hash (FIPS 202). `hash(msg)` returns 64 bytes. |
|
|
143
|
+
| `SHAKE128` | class | SHAKE128 XOF (FIPS 202). Unbounded output. `hash(msg, outputLength)`, `absorb(msg)`, `squeeze(n)`, `reset()`. |
|
|
144
|
+
| `SHAKE256` | class | SHAKE256 XOF (FIPS 202). Unbounded output. `hash(msg, outputLength)`, `absorb(msg)`, `squeeze(n)`, `reset()`. |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Keccak (alias for SHA-3)
|
|
149
|
+
|
|
150
|
+
`'keccak'` is an alias for `'sha3'`. Same WASM binary, same instance slot.
|
|
151
|
+
Both `init({ sha3: sha3Wasm })` and `init({ keccak: keccakWasm })` load the same module.
|
|
152
|
+
Provided so Kyber/ML-KEM consumers can use the semantically correct primitive name.
|
|
153
|
+
Subpath: `leviathan-crypto/keccak`.
|
|
154
|
+
|
|
155
|
+
| Export | Kind | Description |
|
|
156
|
+
|--------|------|-------------|
|
|
157
|
+
| `keccakInit` | function | Alias init. `keccakInit(source: WasmSource)` loads the sha3 WASM slot via the keccak alias. |
|
|
158
|
+
| `SHA3_224` | class | Re-exported from `leviathan-crypto/sha3`. |
|
|
159
|
+
| `SHA3_256` | class | Re-exported from `leviathan-crypto/sha3`. |
|
|
160
|
+
| `SHA3_384` | class | Re-exported from `leviathan-crypto/sha3`. |
|
|
161
|
+
| `SHA3_512` | class | Re-exported from `leviathan-crypto/sha3`. |
|
|
162
|
+
| `SHAKE128` | class | Re-exported from `leviathan-crypto/sha3`. |
|
|
163
|
+
| `SHAKE256` | class | Re-exported from `leviathan-crypto/sha3`. |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## ML-KEM (Post-quantum KEM)
|
|
168
|
+
|
|
169
|
+
Requires `init({ kyber: kyberWasm, sha3: sha3Wasm })`.
|
|
170
|
+
Subpath: `leviathan-crypto/kyber`. See [kyber.md](./kyber.md).
|
|
171
|
+
|
|
172
|
+
| Export | Kind | Description |
|
|
173
|
+
|--------|------|-------------|
|
|
174
|
+
| `kyberInit` | function | Module-scoped init. `kyberInit(source: WasmSource)` loads only kyber WASM. |
|
|
175
|
+
| `MlKemBase` | class | Abstract base class for all ML-KEM variants. Holds `params: KyberParams`. Not normally instantiated directly. Use `MlKem512`, `MlKem768`, or `MlKem1024`. |
|
|
176
|
+
| `MlKem512` | class | ML-KEM-512. k=2, η₁=3. `keygen()`, `encapsulate(ek)`, `decapsulate(dk, c)`, `checkEncapsulationKey(ek)`, `checkDecapsulationKey(dk)`. |
|
|
177
|
+
| `MlKem768` | class | ML-KEM-768. k=3, η₁=2. Recommended default. Same API as MlKem512. |
|
|
178
|
+
| `MlKem1024` | class | ML-KEM-1024. k=4, η₁=2. Same API as MlKem512. |
|
|
179
|
+
| `KyberSuite` | function | Factory. `KyberSuite(kem, innerCipher)` → `CipherSuite & { keygen(): KyberKeyPair }`. Wraps `MlKemBase` + `CipherSuite` into a hybrid KEM+AEAD suite for use with `Seal`, `SealStream`, `OpenStream`. |
|
|
180
|
+
| `KyberKeyPair` | type | `{ encapsulationKey: Uint8Array, decapsulationKey: Uint8Array }` |
|
|
181
|
+
| `KyberEncapsulation` | type | `{ ciphertext: Uint8Array, sharedSecret: Uint8Array }` |
|
|
182
|
+
| `KyberParams` | type | Parameter set configuration (k, η₁, η₂, dᵤ, dᵥ, byte sizes). |
|
|
183
|
+
| `MLKEM512` | const | Parameter set for ML-KEM-512. |
|
|
184
|
+
| `MLKEM768` | const | Parameter set for ML-KEM-768. |
|
|
185
|
+
| `MLKEM1024` | const | Parameter set for ML-KEM-1024. |
|
|
186
|
+
|
|
187
|
+
> [!NOTE]
|
|
188
|
+
> `ntt_scalar` and `invntt_scalar` are scalar NTT references exported for SIMD gate tests. They are not part of the public API.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Fortuna CSPRNG
|
|
193
|
+
|
|
194
|
+
Requires `init({ serpent: serpentWasm, sha2: sha2Wasm })`. See [fortuna.md](./fortuna.md).
|
|
195
|
+
|
|
196
|
+
| Export | Kind | Description |
|
|
197
|
+
|--------|------|-------------|
|
|
198
|
+
| `Fortuna` | class | Fortuna CSPRNG (Ferguson & Schneier). `Fortuna.create()` static factory, `get(n)`, `addEntropy()`, `stop()`. |
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Types
|
|
203
|
+
|
|
204
|
+
No `init()` required. See [types.md](./types.md).
|
|
205
|
+
|
|
206
|
+
| Export | Kind | Description |
|
|
207
|
+
|--------|------|-------------|
|
|
208
|
+
| `Hash` | interface | `hash(msg): Uint8Array`, `dispose()` |
|
|
209
|
+
| `KeyedHash` | interface | `hash(key, msg): Uint8Array`, `dispose()` |
|
|
210
|
+
| `Blockcipher` | interface | `encrypt(block): Uint8Array`, `decrypt(block): Uint8Array`, `dispose()` |
|
|
211
|
+
| `Streamcipher` | interface | `encrypt(msg): Uint8Array`, `decrypt(msg): Uint8Array`, `dispose()` |
|
|
212
|
+
| `AEAD` | interface | `encrypt(msg, aad?): Uint8Array`, `decrypt(ciphertext, aad?): Uint8Array`, `dispose()` |
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Utilities
|
|
217
|
+
|
|
218
|
+
No `init()` required. See [utils.md](./utils.md).
|
|
219
|
+
|
|
220
|
+
| Export | Kind | Description |
|
|
221
|
+
|--------|------|-------------|
|
|
222
|
+
| `hexToBytes` | function | Hex string to `Uint8Array`. Accepts `0x` prefix, uppercase/lowercase. Throws `RangeError` on odd-length input. |
|
|
223
|
+
| `bytesToHex` | function | `Uint8Array` to lowercase hex string. |
|
|
224
|
+
| `utf8ToBytes` | function | UTF-8 string to `Uint8Array`. |
|
|
225
|
+
| `bytesToUtf8` | function | `Uint8Array` to UTF-8 string. |
|
|
226
|
+
| `base64ToBytes` | function | Base64/base64url string to `Uint8Array`. Returns `undefined` on invalid input. |
|
|
227
|
+
| `bytesToBase64` | function | `Uint8Array` to base64 string. Pass `url=true` for base64url. |
|
|
228
|
+
| `constantTimeEqual` | function | Best-available constant-time byte-array equality. Uses WASM SIMD when available to eliminate JIT timing leaks; falls back to XOR-accumulate in JS. Returns `false` immediately on length mismatch. Throws `RangeError` if either input exceeds `CT_MAX_BYTES`. |
|
|
229
|
+
| `CT_MAX_BYTES` | const | Maximum input size for `constantTimeEqual` per side (32768 bytes, one 64 KiB WASM page split between two buffers). |
|
|
230
|
+
| `wipe` | function | Zero a typed array in place. |
|
|
231
|
+
| `xor` | function | XOR two equal-length `Uint8Array`s, returns new array. |
|
|
232
|
+
| `concat` | function | Concatenate one or more `Uint8Array`s into a new array. Variadic. |
|
|
233
|
+
| `randomBytes` | function | Cryptographically secure random bytes via Web Crypto API. |
|
|
234
|
+
| `hasSIMD` | function | Returns `true` if the runtime supports WebAssembly SIMD. Cached after first call. Used internally for CTR/CBC-decrypt and ChaCha20 dispatch. Exported for informational use. |
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
> ## Cross-References
|
|
239
|
+
>
|
|
240
|
+
> - [index](./README.md) — Project Documentation index
|
|
241
|
+
> - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
|
package/dist/docs/fortuna.md
CHANGED
|
@@ -1,30 +1,37 @@
|
|
|
1
|
-
# Fortuna
|
|
1
|
+
# Fortuna CSPRNG
|
|
2
2
|
|
|
3
3
|
> [!NOTE]
|
|
4
4
|
> A CSPRNG that continuously collects entropy from the environment and generates
|
|
5
5
|
> cryptographically secure random bytes, backed by WASM Serpent-256 and SHA-256.
|
|
6
6
|
|
|
7
|
+
> ### Table of Contents
|
|
8
|
+
> - [Overview](#overview)
|
|
9
|
+
> - [Security Notes](#security-notes)
|
|
10
|
+
> - [API Reference](#api-reference)
|
|
11
|
+
> - [Usage Examples](#usage-examples)
|
|
12
|
+
> - [Error Conditions](#error-conditions)
|
|
13
|
+
> - [How It Works (Simplified)](#how-it-works-simplified)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
7
17
|
## Overview
|
|
8
18
|
|
|
9
19
|
A cryptographically secure pseudorandom number generator (CSPRNG) produces random
|
|
10
20
|
bytes that are indistinguishable from true randomness to any observer, even one
|
|
11
21
|
with significant computational resources. This matters because many security
|
|
12
|
-
operations
|
|
13
|
-
-- require randomness that an attacker cannot predict. If an attacker can predict
|
|
22
|
+
operations require unpredictable randomness: generating encryption keys, initialization vectors, nonces, and tokens. If an attacker can predict
|
|
14
23
|
the output of your random number generator, they can predict your keys, and your
|
|
15
24
|
encryption provides no protection.
|
|
16
25
|
|
|
17
26
|
Fortuna is a CSPRNG designed by Bruce Schneier and Niels Ferguson, published in
|
|
18
27
|
*Practical Cryptography* (2003). It continuously collects entropy from multiple
|
|
19
|
-
sources
|
|
28
|
+
sources (mouse movements, keyboard events, system timers, OS randomness) and
|
|
20
29
|
feeds that entropy into 32 independent pools. When you request random bytes,
|
|
21
30
|
Fortuna combines pool contents and uses them to reseed an internal generator
|
|
22
31
|
built on Serpent-256 (block cipher) and SHA-256 (hash function). Both primitives
|
|
23
32
|
run entirely in WebAssembly.
|
|
24
33
|
|
|
25
|
-
|
|
26
|
-
good, and Fortuna seeds itself from it on creation. But Fortuna adds two
|
|
27
|
-
properties on top. First, **forward secrecy**: after every call to `get()`, the
|
|
34
|
+
Fortuna adds two properties on top of `crypto.getRandomValues()`. First, **forward secrecy**: after every call to `get()`, the
|
|
28
35
|
internal generation key is replaced, so compromising the current state does not
|
|
29
36
|
reveal any past outputs. Second, **defense-in-depth entropy pooling**: Fortuna
|
|
30
37
|
collects entropy from many independent sources and distributes it across 32 pools
|
|
@@ -39,41 +46,19 @@ you must use the `Fortuna.create()` static factory rather than `new Fortuna()`.
|
|
|
39
46
|
|
|
40
47
|
## Security Notes
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
immediately usable. You do not need to wait for entropy to accumulate before
|
|
56
|
-
calling `get()`.
|
|
57
|
-
|
|
58
|
-
- **Browser entropy sources** -- mouse movements, keyboard events, click events,
|
|
59
|
-
scroll position, touch events, device motion and orientation,
|
|
60
|
-
`performance.now()` timing, DOM content hash, and periodic
|
|
61
|
-
`crypto.getRandomValues()`.
|
|
62
|
-
|
|
63
|
-
- **Node.js entropy sources** -- `crypto.randomBytes()`, `process.hrtime` (nanosecond
|
|
64
|
-
timing jitter), `process.cpuUsage()`, `process.memoryUsage()`, `os.loadavg()`,
|
|
65
|
-
`os.freemem()`.
|
|
66
|
-
|
|
67
|
-
- **Wipe state when done** -- Call `stop()` when you are finished with the
|
|
68
|
-
instance. This wipes the generation key and counter from memory and stops all
|
|
69
|
-
background entropy collectors. Key material should not persist longer than
|
|
70
|
-
necessary.
|
|
71
|
-
|
|
72
|
-
- **Output quality depends on entropy** -- The initial seed from the OS random
|
|
73
|
-
source is strong. Over time, the additional entropy collectors improve the
|
|
74
|
-
state further. In environments with limited user interaction (headless servers,
|
|
75
|
-
automated tests), fewer entropy sources contribute, but the OS random seed
|
|
76
|
-
still provides a solid baseline.
|
|
49
|
+
**Forward secrecy.** The generation key is replaced after every call to `get()`. If an attacker compromises the internal state at time T, they cannot reconstruct any output produced before time T.
|
|
50
|
+
|
|
51
|
+
**32 entropy pools.** Entropy is distributed across 32 independent pools using round-robin assignment. Pool 0 is used on every reseed, pool 1 on every second reseed, pool 2 on every fourth, and so on. This exponential schedule means that even if an attacker can observe or influence some entropy sources, higher-numbered pools accumulate enough entropy over time to produce a strong reseed eventually.
|
|
52
|
+
|
|
53
|
+
**Immediate usability.** Fortuna seeds itself from `crypto.getRandomValues()` (browser) or `crypto.randomBytes()` (Node.js) during creation. You do not need to wait for entropy to accumulate before calling `get()`.
|
|
54
|
+
|
|
55
|
+
**Browser entropy sources.** Mouse movements, keyboard events, click events, scroll position, touch events, device motion and orientation, `performance.now()` timing, DOM content hash, and periodic `crypto.getRandomValues()`.
|
|
56
|
+
|
|
57
|
+
**Node.js entropy sources.** `crypto.randomBytes()`, `process.hrtime` (nanosecond timing jitter), `process.cpuUsage()`, `process.memoryUsage()`, `os.loadavg()`, `os.freemem()`.
|
|
58
|
+
|
|
59
|
+
**Wipe state when done.** Call `stop()` when you are finished with the instance. This wipes the generation key and counter from memory and stops all background entropy collectors. Key material should not persist longer than necessary.
|
|
60
|
+
|
|
61
|
+
**Output quality depends on entropy.** The initial seed from the OS random source is strong. Over time the additional entropy collectors improve the state further. In environments with limited user interaction (headless servers, automated tests), fewer entropy sources contribute, but the OS random seed still provides a solid baseline.
|
|
77
62
|
|
|
78
63
|
---
|
|
79
64
|
|
|
@@ -81,7 +66,9 @@ you must use the `Fortuna.create()` static factory rather than `new Fortuna()`.
|
|
|
81
66
|
|
|
82
67
|
### `Fortuna.create(opts?)`
|
|
83
68
|
|
|
84
|
-
Static async factory. Returns a `Promise<Fortuna>`.
|
|
69
|
+
Static async factory. Returns a `Promise<Fortuna>`. The returned instance is
|
|
70
|
+
guaranteed to be seeded. `create()` forces an initial reseed before resolving,
|
|
71
|
+
so `get()` is immediately usable.
|
|
85
72
|
|
|
86
73
|
```typescript
|
|
87
74
|
static async create(opts?: {
|
|
@@ -93,12 +80,11 @@ static async create(opts?: {
|
|
|
93
80
|
| Parameter | Type | Default | Description |
|
|
94
81
|
|-----------|------|---------|-------------|
|
|
95
82
|
| `opts.msPerReseed` | `number` | `100` | Minimum milliseconds between reseeds. |
|
|
96
|
-
| `opts.entropy` | `Uint8Array` |
|
|
83
|
+
| `opts.entropy` | `Uint8Array` | | Optional extra entropy to mix in during creation. |
|
|
97
84
|
|
|
98
|
-
Throws if `init(
|
|
85
|
+
Throws if `init({ serpent: serpentWasm, sha2: sha2Wasm })` has not been called.
|
|
99
86
|
|
|
100
|
-
Direct construction with `new Fortuna()` is not possible
|
|
101
|
-
private. Always use `Fortuna.create()`.
|
|
87
|
+
Direct construction with `new Fortuna()` is not possible. The constructor is private. Always use `Fortuna.create()`.
|
|
102
88
|
|
|
103
89
|
---
|
|
104
90
|
|
|
@@ -107,15 +93,14 @@ private. Always use `Fortuna.create()`.
|
|
|
107
93
|
Generate `length` random bytes.
|
|
108
94
|
|
|
109
95
|
```typescript
|
|
110
|
-
get(length: number): Uint8Array
|
|
96
|
+
get(length: number): Uint8Array
|
|
111
97
|
```
|
|
112
98
|
|
|
113
|
-
Returns a `Uint8Array` of the requested length
|
|
114
|
-
|
|
115
|
-
`create()` seeds the generator immediately).
|
|
99
|
+
Returns a `Uint8Array` of the requested length. The instance is always seeded
|
|
100
|
+
after `create()` resolves, so this method is guaranteed to return data.
|
|
116
101
|
|
|
117
102
|
After producing the output, the generation key is replaced with fresh
|
|
118
|
-
pseudorandom material. This is the forward secrecy mechanism
|
|
103
|
+
pseudorandom material. This is the forward secrecy mechanism. The key used to
|
|
119
104
|
produce this output no longer exists.
|
|
120
105
|
|
|
121
106
|
---
|
|
@@ -143,8 +128,7 @@ getEntropy(): number
|
|
|
143
128
|
```
|
|
144
129
|
|
|
145
130
|
Returns the estimated total entropy accumulated across all pools, in bytes. This
|
|
146
|
-
is an estimate, not a guarantee
|
|
147
|
-
by each collector.
|
|
131
|
+
is an estimate, not a guarantee. It reflects the sum of entropy credits assigned by each collector.
|
|
148
132
|
|
|
149
133
|
---
|
|
150
134
|
|
|
@@ -179,13 +163,15 @@ There is no `start()` or restart capability.
|
|
|
179
163
|
|
|
180
164
|
## Usage Examples
|
|
181
165
|
|
|
182
|
-
### Basic usage
|
|
166
|
+
### Basic usage
|
|
183
167
|
|
|
184
168
|
```typescript
|
|
185
169
|
import { init, Fortuna } from 'leviathan-crypto'
|
|
170
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
171
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
186
172
|
|
|
187
173
|
// Initialize both WASM modules that Fortuna depends on
|
|
188
|
-
await init(
|
|
174
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
189
175
|
|
|
190
176
|
// Create the CSPRNG
|
|
191
177
|
const rng = await Fortuna.create()
|
|
@@ -196,7 +182,7 @@ const key = rng.get(32)
|
|
|
196
182
|
// Generate 12 random bytes (e.g., for a nonce)
|
|
197
183
|
const nonce = rng.get(12)
|
|
198
184
|
|
|
199
|
-
// Clean up when done
|
|
185
|
+
// Clean up when done, wipes key material from memory
|
|
200
186
|
rng.stop()
|
|
201
187
|
```
|
|
202
188
|
|
|
@@ -204,8 +190,10 @@ rng.stop()
|
|
|
204
190
|
|
|
205
191
|
```typescript
|
|
206
192
|
import { init, Fortuna, utf8ToBytes } from 'leviathan-crypto'
|
|
193
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
194
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
207
195
|
|
|
208
|
-
await init(
|
|
196
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
209
197
|
const rng = await Fortuna.create()
|
|
210
198
|
|
|
211
199
|
// Feed application-specific data as additional entropy.
|
|
@@ -226,8 +214,10 @@ rng.stop()
|
|
|
226
214
|
|
|
227
215
|
```typescript
|
|
228
216
|
import { init, Fortuna } from 'leviathan-crypto'
|
|
217
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
218
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
229
219
|
|
|
230
|
-
await init(
|
|
220
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
231
221
|
|
|
232
222
|
// Fortuna automatically registers browser event listeners on creation:
|
|
233
223
|
// - mousemove (throttled to 50ms)
|
|
@@ -238,7 +228,7 @@ await init(['serpent', 'sha2'])
|
|
|
238
228
|
// - devicemotion, deviceorientation, orientationchange
|
|
239
229
|
//
|
|
240
230
|
// Every user interaction feeds entropy into the pools.
|
|
241
|
-
// No manual setup is needed
|
|
231
|
+
// No manual setup is needed, it starts collecting immediately.
|
|
242
232
|
|
|
243
233
|
const rng = await Fortuna.create()
|
|
244
234
|
|
|
@@ -258,8 +248,10 @@ window.addEventListener('beforeunload', () => rng.stop())
|
|
|
258
248
|
|
|
259
249
|
```typescript
|
|
260
250
|
import { init, Fortuna } from 'leviathan-crypto'
|
|
251
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
252
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
261
253
|
|
|
262
|
-
await init(
|
|
254
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
263
255
|
|
|
264
256
|
// You can pass extra entropy at creation time.
|
|
265
257
|
// This is mixed into the pools during initialization, before the
|
|
@@ -278,10 +270,9 @@ rng.stop()
|
|
|
278
270
|
|
|
279
271
|
| Condition | What happens |
|
|
280
272
|
|-----------|-------------|
|
|
281
|
-
| `init()` not called | `Fortuna.create()` throws: `leviathan-crypto: call init(
|
|
282
|
-
| Only one module initialized | Same error
|
|
283
|
-
| `new Fortuna()` | Compile-time error
|
|
284
|
-
| `get()` before first reseed | Returns `undefined`. Under normal usage this does not happen because `create()` seeds the generator during initialization. |
|
|
273
|
+
| `init()` not called | `Fortuna.create()` throws: `leviathan-crypto: call init({ serpent: ..., sha2: ... }) before using Fortuna` |
|
|
274
|
+
| Only one module initialized | Same error. Both `serpent` and `sha2` must be initialized. |
|
|
275
|
+
| `new Fortuna()` | Compile-time error. The constructor is private. TypeScript will not allow it. |
|
|
285
276
|
| Any method after `stop()` | Throws: `Fortuna instance has been disposed`. The instance is permanently disposed. |
|
|
286
277
|
|
|
287
278
|
---
|
|
@@ -291,21 +282,21 @@ rng.stop()
|
|
|
291
282
|
For readers who want to understand what Fortuna does internally, without needing
|
|
292
283
|
to read the spec:
|
|
293
284
|
|
|
294
|
-
1. **Entropy collection
|
|
285
|
+
1. **Entropy collection.** Background listeners and timers capture small,
|
|
295
286
|
unpredictable measurements (mouse coordinates, nanosecond timings, memory
|
|
296
287
|
usage) and feed them into 32 separate pools via SHA-256 hash chaining.
|
|
297
288
|
|
|
298
|
-
2. **Reseed
|
|
289
|
+
2. **Reseed.** When pool 0 has accumulated enough entropy and enough time has
|
|
299
290
|
passed since the last reseed, Fortuna combines the contents of eligible pools
|
|
300
291
|
(determined by the reseed counter) into a seed, and derives a new generation
|
|
301
292
|
key: `genKey = SHA-256(genKey || seed)`.
|
|
302
293
|
|
|
303
|
-
3. **Generation
|
|
294
|
+
3. **Generation.** To produce output, the generator encrypts an incrementing
|
|
304
295
|
counter with Serpent-256 in ECB mode using the current generation key. The
|
|
305
296
|
output is the concatenation of encrypted counter blocks, truncated to the
|
|
306
297
|
requested length.
|
|
307
298
|
|
|
308
|
-
4. **Key replacement
|
|
299
|
+
4. **Key replacement.** Immediately after producing output, the generation key
|
|
309
300
|
is replaced with fresh pseudorandom blocks. The old key is gone. This is what
|
|
310
301
|
provides forward secrecy.
|
|
311
302
|
|
|
@@ -315,8 +306,8 @@ to read the spec:
|
|
|
315
306
|
>
|
|
316
307
|
> - [index](./README.md) — Project Documentation index
|
|
317
308
|
> - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
|
|
318
|
-
> - [serpent](./serpent.md)
|
|
319
|
-
> - [sha2](./sha2.md)
|
|
320
|
-
> - [asm_serpent](./asm_serpent.md)
|
|
321
|
-
> - [asm_sha2](./asm_sha2.md)
|
|
322
|
-
> - [utils](./utils.md)
|
|
309
|
+
> - [serpent](./serpent.md) — Serpent-256 TypeScript API (Fortuna uses Serpent ECB internally)
|
|
310
|
+
> - [sha2](./sha2.md) — SHA-256 TypeScript API (Fortuna uses SHA-256 for entropy accumulation)
|
|
311
|
+
> - [asm_serpent](./asm_serpent.md) — Serpent-256 WASM implementation details
|
|
312
|
+
> - [asm_sha2](./asm_sha2.md) — SHA-256 WASM implementation details
|
|
313
|
+
> - [utils](./utils.md) — `randomBytes()` for simpler random generation needs
|