leviathan-crypto 1.4.0 → 2.0.1
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 -94
- package/README.md +166 -223
- package/SECURITY.md +90 -45
- package/dist/chacha20/cipher-suite.d.ts +4 -0
- package/dist/chacha20/cipher-suite.js +79 -0
- package/dist/chacha20/embedded.d.ts +1 -0
- package/dist/chacha20/embedded.js +27 -0
- package/dist/chacha20/index.d.ts +20 -27
- package/dist/chacha20/index.js +40 -59
- 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 +323 -0
- package/dist/docs/architecture.md +427 -292
- package/dist/docs/argon2id.md +42 -30
- package/dist/docs/chacha20.md +192 -266
- package/dist/docs/exports.md +241 -0
- package/dist/docs/fortuna.md +60 -69
- package/dist/docs/init.md +172 -178
- package/dist/docs/loader.md +87 -142
- package/dist/docs/serpent.md +134 -583
- package/dist/docs/sha2.md +91 -103
- package/dist/docs/sha3.md +70 -36
- package/dist/docs/types.md +94 -16
- package/dist/docs/utils.md +109 -32
- 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 +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +38 -0
- package/dist/fortuna.d.ts +0 -6
- package/dist/fortuna.js +5 -5
- 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 +94 -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 +15 -6
- package/dist/loader.js +65 -21
- package/dist/serpent/cipher-suite.d.ts +4 -0
- package/dist/serpent/cipher-suite.js +122 -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 +208 -0
- package/dist/serpent/serpent-cbc.d.ts +30 -0
- package/dist/serpent/serpent-cbc.js +142 -0
- package/dist/serpent.wasm +0 -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 +400 -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 +25 -0
- package/dist/stream/types.js +26 -0
- package/dist/utils.d.ts +7 -2
- package/dist/utils.js +49 -3
- 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 -178
- package/dist/chacha20/pool.worker.js +0 -37
- package/dist/chacha20/stream-sealer.d.ts +0 -49
- package/dist/chacha20/stream-sealer.js +0 -327
- 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/seal.js +0 -72
- package/dist/serpent/stream-pool.d.ts +0 -48
- package/dist/serpent/stream-pool.js +0 -275
- package/dist/serpent/stream-sealer.d.ts +0 -55
- package/dist/serpent/stream-sealer.js +0 -342
- 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
|
|
|
@@ -82,7 +67,7 @@ you must use the `Fortuna.create()` static factory rather than `new Fortuna()`.
|
|
|
82
67
|
### `Fortuna.create(opts?)`
|
|
83
68
|
|
|
84
69
|
Static async factory. Returns a `Promise<Fortuna>`. The returned instance is
|
|
85
|
-
guaranteed to be seeded
|
|
70
|
+
guaranteed to be seeded. `create()` forces an initial reseed before resolving,
|
|
86
71
|
so `get()` is immediately usable.
|
|
87
72
|
|
|
88
73
|
```typescript
|
|
@@ -95,12 +80,11 @@ static async create(opts?: {
|
|
|
95
80
|
| Parameter | Type | Default | Description |
|
|
96
81
|
|-----------|------|---------|-------------|
|
|
97
82
|
| `opts.msPerReseed` | `number` | `100` | Minimum milliseconds between reseeds. |
|
|
98
|
-
| `opts.entropy` | `Uint8Array` |
|
|
83
|
+
| `opts.entropy` | `Uint8Array` | | Optional extra entropy to mix in during creation. |
|
|
99
84
|
|
|
100
|
-
Throws if `init(
|
|
85
|
+
Throws if `init({ serpent: serpentWasm, sha2: sha2Wasm })` has not been called.
|
|
101
86
|
|
|
102
|
-
Direct construction with `new Fortuna()` is not possible
|
|
103
|
-
private. Always use `Fortuna.create()`.
|
|
87
|
+
Direct construction with `new Fortuna()` is not possible. The constructor is private. Always use `Fortuna.create()`.
|
|
104
88
|
|
|
105
89
|
---
|
|
106
90
|
|
|
@@ -116,7 +100,7 @@ Returns a `Uint8Array` of the requested length. The instance is always seeded
|
|
|
116
100
|
after `create()` resolves, so this method is guaranteed to return data.
|
|
117
101
|
|
|
118
102
|
After producing the output, the generation key is replaced with fresh
|
|
119
|
-
pseudorandom material. This is the forward secrecy mechanism
|
|
103
|
+
pseudorandom material. This is the forward secrecy mechanism. The key used to
|
|
120
104
|
produce this output no longer exists.
|
|
121
105
|
|
|
122
106
|
---
|
|
@@ -144,8 +128,7 @@ getEntropy(): number
|
|
|
144
128
|
```
|
|
145
129
|
|
|
146
130
|
Returns the estimated total entropy accumulated across all pools, in bytes. This
|
|
147
|
-
is an estimate, not a guarantee
|
|
148
|
-
by each collector.
|
|
131
|
+
is an estimate, not a guarantee. It reflects the sum of entropy credits assigned by each collector.
|
|
149
132
|
|
|
150
133
|
---
|
|
151
134
|
|
|
@@ -180,13 +163,15 @@ There is no `start()` or restart capability.
|
|
|
180
163
|
|
|
181
164
|
## Usage Examples
|
|
182
165
|
|
|
183
|
-
### Basic usage
|
|
166
|
+
### Basic usage
|
|
184
167
|
|
|
185
168
|
```typescript
|
|
186
169
|
import { init, Fortuna } from 'leviathan-crypto'
|
|
170
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
171
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
187
172
|
|
|
188
173
|
// Initialize both WASM modules that Fortuna depends on
|
|
189
|
-
await init(
|
|
174
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
190
175
|
|
|
191
176
|
// Create the CSPRNG
|
|
192
177
|
const rng = await Fortuna.create()
|
|
@@ -197,7 +182,7 @@ const key = rng.get(32)
|
|
|
197
182
|
// Generate 12 random bytes (e.g., for a nonce)
|
|
198
183
|
const nonce = rng.get(12)
|
|
199
184
|
|
|
200
|
-
// Clean up when done
|
|
185
|
+
// Clean up when done, wipes key material from memory
|
|
201
186
|
rng.stop()
|
|
202
187
|
```
|
|
203
188
|
|
|
@@ -205,8 +190,10 @@ rng.stop()
|
|
|
205
190
|
|
|
206
191
|
```typescript
|
|
207
192
|
import { init, Fortuna, utf8ToBytes } from 'leviathan-crypto'
|
|
193
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
194
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
208
195
|
|
|
209
|
-
await init(
|
|
196
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
210
197
|
const rng = await Fortuna.create()
|
|
211
198
|
|
|
212
199
|
// Feed application-specific data as additional entropy.
|
|
@@ -227,8 +214,10 @@ rng.stop()
|
|
|
227
214
|
|
|
228
215
|
```typescript
|
|
229
216
|
import { init, Fortuna } from 'leviathan-crypto'
|
|
217
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
218
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
230
219
|
|
|
231
|
-
await init(
|
|
220
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
232
221
|
|
|
233
222
|
// Fortuna automatically registers browser event listeners on creation:
|
|
234
223
|
// - mousemove (throttled to 50ms)
|
|
@@ -239,7 +228,7 @@ await init(['serpent', 'sha2'])
|
|
|
239
228
|
// - devicemotion, deviceorientation, orientationchange
|
|
240
229
|
//
|
|
241
230
|
// Every user interaction feeds entropy into the pools.
|
|
242
|
-
// No manual setup is needed
|
|
231
|
+
// No manual setup is needed, it starts collecting immediately.
|
|
243
232
|
|
|
244
233
|
const rng = await Fortuna.create()
|
|
245
234
|
|
|
@@ -259,8 +248,10 @@ window.addEventListener('beforeunload', () => rng.stop())
|
|
|
259
248
|
|
|
260
249
|
```typescript
|
|
261
250
|
import { init, Fortuna } from 'leviathan-crypto'
|
|
251
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
252
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
262
253
|
|
|
263
|
-
await init(
|
|
254
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
264
255
|
|
|
265
256
|
// You can pass extra entropy at creation time.
|
|
266
257
|
// This is mixed into the pools during initialization, before the
|
|
@@ -279,9 +270,9 @@ rng.stop()
|
|
|
279
270
|
|
|
280
271
|
| Condition | What happens |
|
|
281
272
|
|-----------|-------------|
|
|
282
|
-
| `init()` not called | `Fortuna.create()` throws: `leviathan-crypto: call init(
|
|
283
|
-
| Only one module initialized | Same error
|
|
284
|
-
| `new Fortuna()` | Compile-time error
|
|
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
|