leviathan-crypto 2.0.1 → 2.1.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.
Files changed (93) hide show
  1. package/CLAUDE.md +171 -7
  2. package/LICENSE +4 -0
  3. package/README.md +109 -54
  4. package/SECURITY.md +125 -238
  5. package/dist/chacha20/cipher-suite.d.ts +10 -0
  6. package/dist/chacha20/cipher-suite.js +65 -2
  7. package/dist/chacha20/generator.d.ts +12 -0
  8. package/dist/chacha20/generator.js +91 -0
  9. package/dist/chacha20/index.d.ts +97 -1
  10. package/dist/chacha20/index.js +139 -11
  11. package/dist/chacha20/ops.d.ts +57 -6
  12. package/dist/chacha20/ops.js +93 -13
  13. package/dist/chacha20/pool-worker.js +12 -0
  14. package/dist/chacha20/types.d.ts +1 -32
  15. package/dist/ct-wasm.js +1 -1
  16. package/dist/ct.wasm +0 -0
  17. package/dist/docs/aead.md +66 -26
  18. package/dist/docs/architecture.md +600 -521
  19. package/dist/docs/argon2id.md +17 -14
  20. package/dist/docs/chacha20.md +146 -39
  21. package/dist/docs/exports.md +46 -10
  22. package/dist/docs/fortuna.md +339 -122
  23. package/dist/docs/init.md +24 -25
  24. package/dist/docs/loader.md +142 -47
  25. package/dist/docs/serpent.md +139 -41
  26. package/dist/docs/sha2.md +77 -19
  27. package/dist/docs/sha3.md +81 -15
  28. package/dist/docs/types.md +155 -15
  29. package/dist/docs/utils.md +171 -81
  30. package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
  31. package/dist/embedded/chacha20-pool-worker.js +5 -0
  32. package/dist/embedded/kyber.d.ts +1 -1
  33. package/dist/embedded/kyber.js +1 -1
  34. package/dist/embedded/serpent-pool-worker.d.ts +1 -0
  35. package/dist/embedded/serpent-pool-worker.js +5 -0
  36. package/dist/fortuna.d.ts +14 -8
  37. package/dist/fortuna.js +144 -50
  38. package/dist/index.d.ts +8 -6
  39. package/dist/index.js +6 -5
  40. package/dist/init.d.ts +0 -2
  41. package/dist/init.js +83 -3
  42. package/dist/kyber/indcpa.js +4 -4
  43. package/dist/kyber/index.js +25 -5
  44. package/dist/kyber/kem.js +56 -1
  45. package/dist/kyber/suite.d.ts +1 -2
  46. package/dist/kyber/types.d.ts +1 -0
  47. package/dist/kyber/validate.d.ts +8 -4
  48. package/dist/kyber/validate.js +18 -14
  49. package/dist/kyber.wasm +0 -0
  50. package/dist/loader.d.ts +7 -2
  51. package/dist/loader.js +25 -28
  52. package/dist/ratchet/index.d.ts +6 -0
  53. package/dist/ratchet/index.js +37 -0
  54. package/dist/ratchet/kdf-chain.d.ts +13 -0
  55. package/dist/ratchet/kdf-chain.js +85 -0
  56. package/dist/ratchet/ratchet-keypair.d.ts +9 -0
  57. package/dist/ratchet/ratchet-keypair.js +61 -0
  58. package/dist/ratchet/root-kdf.d.ts +4 -0
  59. package/dist/ratchet/root-kdf.js +124 -0
  60. package/dist/ratchet/skipped-key-store.d.ts +14 -0
  61. package/dist/ratchet/skipped-key-store.js +154 -0
  62. package/dist/ratchet/types.d.ts +36 -0
  63. package/dist/ratchet/types.js +26 -0
  64. package/dist/serpent/cipher-suite.d.ts +10 -0
  65. package/dist/serpent/cipher-suite.js +135 -50
  66. package/dist/serpent/generator.d.ts +12 -0
  67. package/dist/serpent/generator.js +97 -0
  68. package/dist/serpent/index.d.ts +61 -1
  69. package/dist/serpent/index.js +92 -7
  70. package/dist/serpent/pool-worker.js +25 -101
  71. package/dist/serpent/serpent-cbc.d.ts +14 -4
  72. package/dist/serpent/serpent-cbc.js +50 -32
  73. package/dist/serpent/shared-ops.d.ts +83 -0
  74. package/dist/serpent/shared-ops.js +213 -0
  75. package/dist/serpent/types.d.ts +1 -5
  76. package/dist/sha2/hash.d.ts +2 -0
  77. package/dist/sha2/hash.js +53 -0
  78. package/dist/sha2/index.d.ts +1 -0
  79. package/dist/sha2/index.js +15 -1
  80. package/dist/sha3/hash.d.ts +2 -0
  81. package/dist/sha3/hash.js +53 -0
  82. package/dist/sha3/index.d.ts +17 -2
  83. package/dist/sha3/index.js +79 -7
  84. package/dist/stream/header.js +5 -5
  85. package/dist/stream/open-stream.js +36 -14
  86. package/dist/stream/seal-stream-pool.d.ts +1 -0
  87. package/dist/stream/seal-stream-pool.js +38 -8
  88. package/dist/stream/seal-stream.js +29 -11
  89. package/dist/types.d.ts +21 -0
  90. package/dist/utils.d.ts +7 -8
  91. package/dist/utils.js +73 -40
  92. package/dist/wasm-source.d.ts +9 -8
  93. package/package.json +79 -64
@@ -1,9 +1,8 @@
1
- # Argon2id: Memory-Hardened Password Hashing and Key Derivation
1
+ <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="logo" width="120" align="left" margin="10">
2
2
 
3
- > [!NOTE]
4
- > leviathan-crypto does not wrap Argon2id. This document covers how to use the
5
- > [`argon2id`](https://www.npmjs.com/package/argon2id) npm package directly and
6
- > how to pair it with leviathan primitives for passphrase-based encryption.
3
+ ### Argon2id: Memory-Hardened Password Hashing and Key Derivation
4
+
5
+ leviathan-crypto does not wrap Argon2id. This document covers how to use the [`argon2id`](https://www.npmjs.com/package/argon2id) npm package directly and how to pair it with leviathan primitives for passphrase-based encryption.
7
6
 
8
7
  > ### Table of Contents
9
8
  > - [Why Argon2id](#why-argon2id)
@@ -291,12 +290,16 @@ xc2.dispose();
291
290
 
292
291
  ---
293
292
 
294
- > ## Cross-References
295
- >
296
- > - [index](./README.md) — Project Documentation index
297
- > - [sha2](./sha2.md) HKDF-SHA256 for key expansion from Argon2id root keys
298
- > - [serpent](./serpent.md) `SerpentCipher`: Serpent-256 cipher suite (use with `Seal` and Argon2id-derived keys)
299
- > - [authenticated encryption](./aead.md) `Seal`: one-shot AEAD over any `CipherSuite`
300
- > - [chacha20](./chacha20.md) XChaCha20Poly1305: ChaCha20 authenticated encryption (pairs with Argon2id-derived keys)
301
- > - [utils](./utils.md) `randomBytes` for generating salts, `constantTimeEqual` for hash verification
302
- > - [architecture](./architecture.md) architecture overview, module relationships, buffer layouts, and build pipeline
293
+
294
+ ## Cross-References
295
+
296
+ | Document | Description |
297
+ | -------- | ----------- |
298
+ | [index](./README.md) | Project Documentation index |
299
+ | [sha2](./sha2.md) | HKDF-SHA256 for key expansion from Argon2id root keys |
300
+ | [serpent](./serpent.md) | `SerpentCipher`: Serpent-256 cipher suite (use with `Seal` and Argon2id-derived keys) |
301
+ | [authenticated encryption](./aead.md) | `Seal`, `SealStream`, `OpenStream`: cipher-agnostic AEAD APIs using a `CipherSuite` such as `SerpentCipher` or `XChaCha20Cipher` |
302
+ | [chacha20](./chacha20.md) | XChaCha20Poly1305: ChaCha20 authenticated encryption (pairs with Argon2id-derived keys) |
303
+ | [utils](./utils.md) | `randomBytes` for generating salts, `constantTimeEqual` for hash verification |
304
+ | [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
305
+
@@ -1,7 +1,8 @@
1
- # ChaCha20 TypeScript API
1
+ <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="logo" width="120" align="left" margin="10">
2
2
 
3
- > [!NOTE]
4
- > API reference for `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, and `XChaCha20Cipher`. Covers initialization, all class methods, usage examples, and error conditions.
3
+ ### ChaCha20 TypeScript API
4
+
5
+ API reference for `ChaCha20`, `Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, and `XChaCha20Cipher`. Covers initialization, all class methods, usage examples, and error conditions.
5
6
 
6
7
  > ### Table of Contents
7
8
  > - [Overview](#overview)
@@ -12,6 +13,7 @@
12
13
  > - [`Poly1305`](#poly1305)
13
14
  > - [`ChaCha20Poly1305`](#chacha20poly1305)
14
15
  > - [`XChaCha20Poly1305`](#xchacha20poly1305)
16
+ > - [`ChaCha20Generator`](#chacha20generator)
15
17
  > - [XChaCha20Cipher](#xchacha20cipher)
16
18
  > - [Usage Examples](#usage-examples)
17
19
  > - [Error Conditions](#error-conditions)
@@ -20,11 +22,13 @@
20
22
 
21
23
  ## Overview
22
24
 
23
- **ChaCha20** is a modern stream cipher designed by Daniel J. Bernstein. It is fast
24
- on all platforms (including those without hardware AES), resistant to timing attacks
25
- by design, and widely deployed in TLS, SSH, and WireGuard. ChaCha20 encrypts data
26
- by generating a pseudorandom keystream from a 256-bit key and a nonce, then XORing
27
- it with the plaintext. It does **not** provide authentication on its own. A modified message will decrypt to garbage with no warning.
25
+ **ChaCha20** is a modern stream cipher designed by Daniel J. Bernstein. It is
26
+ fast on all platforms (including those without hardware AES), resistant to
27
+ timing attacks by design, and widely deployed in TLS, SSH, and WireGuard.
28
+ ChaCha20 encrypts data by generating a pseudorandom keystream from a 256-bit
29
+ key and a nonce, then XORing it with the plaintext. It does **not** provide
30
+ authentication on its own. A modified message will decrypt to garbage with no
31
+ warning.
28
32
 
29
33
  **Poly1305** is a one-time message authentication code (MAC). Given a unique 256-bit
30
34
  key and a message, it produces a 16-byte tag that proves the message has not been
@@ -41,10 +45,10 @@ you get an error instead of corrupted data. The nonce is 96 bits (12 bytes).
41
45
  **XChaCha20-Poly1305** extends the nonce to 192 bits (24 bytes) using the HChaCha20
42
46
  subkey derivation step. This makes random nonce generation completely safe. With a
43
47
  24-byte nonce, the probability of a collision is negligible even after billions of
44
- messages. **For most users, `Seal` with `XChaCha20Cipher` is the recommended choice.** It
45
- produces a self-contained authenticated blob with no nonce management, no
46
- instantiation, and no `dispose()`. For protocol interop requiring explicit nonce
47
- control, use `XChaCha20Poly1305` directly.
48
+ messages. **For most users, [`Seal`](./aead.md#seal) with [`XChaCha20Cipher`](./ciphersuite.md#xchacha20cipher)
49
+ is the recommended choice.** It produces a self-contained authenticated blob with
50
+ no nonce management, no instantiation, and no `dispose()`. For protocol interop
51
+ requiring explicit nonce control, use `XChaCha20Poly1305` directly.
48
52
 
49
53
  ---
50
54
 
@@ -54,10 +58,10 @@ control, use `XChaCha20Poly1305` directly.
54
58
  > Read this section before writing any code. These are not theoretical concerns.
55
59
  > They are the mistakes that cause real-world breaches.
56
60
 
57
- - **Use `Seal` with `XChaCha20Cipher` unless you need explicit nonce control.**
58
- It is the safest default: authenticated encryption in a single static call.
59
- If you are unsure which class to pick, pick this one. Use `XChaCha20Poly1305`
60
- when protocol interop requires you to manage nonces yourself.
61
+ - **Use [`Seal`](./aead.md#seal) with [`XChaCha20Cipher`](./ciphersuite.md#xchacha20cipher)
62
+ unless you need explicit nonce control.** It is the safest default: authenticated
63
+ encryption in a single static call. If you are unsure which class to pick, pick this
64
+ one. Use `XChaCha20Poly1305` when protocol interop requires you to manage nonces yourself.
61
65
 
62
66
  - **Never reuse a nonce with the same key.** This is the single most important
63
67
  rule. If you encrypt two different messages with the same key and the same nonce,
@@ -138,13 +142,21 @@ Error: leviathan-crypto: call init({ chacha20: ... }) before using this class
138
142
  Raw ChaCha20 stream cipher. **No authentication.** Use `XChaCha20Poly1305` instead
139
143
  unless you are building a custom protocol and understand the risks.
140
144
 
145
+ > [!CAUTION]
146
+ > `ChaCha20` is stateful and holds exclusive access to the `chacha20` WASM
147
+ > module for its entire lifetime. Constructing a second `ChaCha20`, or any
148
+ > atomic chacha20 class (`Poly1305`, `ChaCha20Poly1305`, `XChaCha20Poly1305`,
149
+ > or a `XChaCha20Cipher`-backed `Seal`/`SealStream`), while this instance is
150
+ > live throws. Call `dispose()` when done. Pool workers are unaffected.
151
+
141
152
  #### Constructor
142
153
 
143
154
  ```typescript
144
155
  new ChaCha20()
145
156
  ```
146
157
 
147
- Throws if `init({ chacha20: chacha20Wasm })` has not been called.
158
+ Throws if `init({ chacha20: chacha20Wasm })` has not been called, or if
159
+ another stateful chacha20 instance is still live.
148
160
 
149
161
  ---
150
162
 
@@ -159,6 +171,13 @@ Prepares the cipher for encryption with the given key and nonce.
159
171
 
160
172
  **Throws** `RangeError` if `key` is not 32 bytes or `nonce` is not 12 bytes.
161
173
 
174
+ > [!NOTE]
175
+ > **Block counter convention.** `beginEncrypt()` starts the ChaCha20 keystream
176
+ > at block counter 1, not 0. Block 0 is reserved for Poly1305 one-time key
177
+ > generation per RFC 8439 §2.6. The class is designed for use inside an AEAD
178
+ > construction; if you need to match RFC 8439 §2.4.2 verbatim (keystream
179
+ > starting at counter 0), use the lower-level primitives directly.
180
+
162
181
  ---
163
182
 
164
183
  #### `encryptChunk(chunk: Uint8Array): Uint8Array`
@@ -176,7 +195,8 @@ a new `Uint8Array` containing the ciphertext (same length as input).
176
195
 
177
196
  #### `beginDecrypt(key: Uint8Array, nonce: Uint8Array): void`
178
197
 
179
- Prepares the cipher for decryption. Identical to `beginEncrypt`. ChaCha20 is symmetric; encryption and decryption are the same XOR operation.
198
+ Prepares the cipher for decryption. Identical to `beginEncrypt`. ChaCha20 is
199
+ symmetric; encryption and decryption are the same XOR operation.
180
200
 
181
201
  | Parameter | Type | Description |
182
202
  |-----------|------|-------------|
@@ -205,6 +225,11 @@ plaintext (same length as input).
205
225
  Wipes all key material and intermediate state from WASM memory. Always call this
206
226
  when you are done with the instance.
207
227
 
228
+ After `dispose()`, all instance methods (`beginEncrypt`, `encryptChunk`,
229
+ `beginDecrypt`, `decryptChunk`) throw `Error: ChaCha20: instance has been
230
+ disposed`. Disposal is permanent; construct a new instance if you need to
231
+ continue.
232
+
208
233
  ---
209
234
 
210
235
  ### `Poly1305`
@@ -272,6 +297,13 @@ appended.
272
297
  > Each `ChaCha20Poly1305` instance allows only **one** `encrypt()` call. A second
273
298
  > call throws to prevent accidental nonce reuse. Create a new instance for each
274
299
  > encryption, or use `Seal` with `XChaCha20Cipher` for automatic nonce management.
300
+ >
301
+ > **Strict single-use on any throw.** Any throw from `encrypt()`, including
302
+ > `RangeError` from wrong-length `key` or `nonce`, exclusivity errors, or any
303
+ > failure inside the WASM call, is *terminal*. The guard is set before
304
+ > argument validation, so a subsequent `encrypt()` on the same instance throws
305
+ > the single-use error rather than retrying. Always allocate a new AEAD
306
+ > instance per message.
275
307
 
276
308
  | Parameter | Type | Default | Description |
277
309
  |-----------|------|---------|-------------|
@@ -283,9 +315,9 @@ appended.
283
315
  **Returns** `Uint8Array`: ciphertext + 16-byte tag (length = plaintext.length + 16).
284
316
 
285
317
  **Throws:**
286
- - `RangeError` if `key` is not 32 bytes
287
- - `RangeError` if `nonce` is not 12 bytes
288
- - `RangeError` if `plaintext` exceeds the maximum chunk size
318
+ - `RangeError` if `key` is not 32 bytes *(terminal: instance locked)*
319
+ - `RangeError` if `nonce` is not 12 bytes *(terminal: instance locked)*
320
+ - `RangeError` if `plaintext` exceeds the maximum chunk size *(terminal: instance locked)*
289
321
  - `Error` if `encrypt()` has already been called on this instance
290
322
 
291
323
  ---
@@ -297,7 +329,8 @@ parameter must include the appended 16-byte tag (i.e., the exact output of
297
329
  `encrypt()`). If authentication fails, an error is thrown and no plaintext is
298
330
  returned.
299
331
 
300
- Tag comparison uses a constant-time XOR-accumulate pattern; no timing side channel leaks whether the tag was "close" to correct.
332
+ Tag comparison uses a constant-time XOR-accumulate pattern; no timing side
333
+ channel leaks whether the tag was "close" to correct.
301
334
 
302
335
  | Parameter | Type | Default | Description |
303
336
  |-----------|------|---------|-------------|
@@ -326,8 +359,8 @@ Wipes all key material and intermediate state from WASM memory.
326
359
 
327
360
  XChaCha20-Poly1305 AEAD (draft-irtf-cfrg-xchacha). RFC-faithful stateless
328
361
  primitive. Key and nonce are passed per-call. Use when protocol interop
329
- requires explicit nonce control. For most use cases, prefer `Seal` with
330
- `XChaCha20Cipher` (automatic nonce management, no instantiation).
362
+ requires explicit nonce control. For most use cases, prefer [`Seal`](./aead.md#seal)
363
+ with [`XChaCha20Cipher`](./ciphersuite.md#xchacha20cipher)(automatic nonce management, no instantiation).
331
364
 
332
365
  It uses a 24-byte (192-bit) nonce, which is large enough that randomly generated
333
366
  nonces will never collide in practice. Internally, it derives a subkey via
@@ -351,6 +384,19 @@ Throws if `init({ chacha20: chacha20Wasm })` has not been called.
351
384
 
352
385
  Encrypts plaintext and returns the ciphertext with the 16-byte tag appended.
353
386
 
387
+ > [!WARNING]
388
+ > Same single-use contract as `ChaCha20Poly1305.encrypt()`. Each instance
389
+ > allows only **one** `encrypt()` call. A second call throws to prevent
390
+ > accidental nonce reuse. Create a new instance for each encryption, or use
391
+ > `Seal` with `XChaCha20Cipher` for automatic nonce management.
392
+ >
393
+ > **Strict single-use on any throw.** Any throw from `encrypt()` is *terminal*,
394
+ > whether the cause is a `RangeError` from wrong-length `key` or `nonce`, an
395
+ > exclusivity error, or a failure inside the WASM call. The guard is set
396
+ > before argument validation, so a subsequent `encrypt()` on the same instance
397
+ > throws the single-use error rather than retrying with a reused nonce. Always
398
+ > allocate a new AEAD instance per message.
399
+
354
400
  | Parameter | Type | Default | Description |
355
401
  |-----------|------|---------|-------------|
356
402
  | `key` | `Uint8Array` | | 32 bytes (256-bit key) |
@@ -361,8 +407,10 @@ Encrypts plaintext and returns the ciphertext with the 16-byte tag appended.
361
407
  **Returns** `Uint8Array`: ciphertext + 16-byte tag (length = plaintext.length + 16).
362
408
 
363
409
  **Throws:**
364
- - `RangeError` if `key` is not 32 bytes
365
- - `RangeError` if `nonce` is not 24 bytes
410
+ - `RangeError` if `key` is not 32 bytes *(terminal: instance locked)*
411
+ - `RangeError` if `nonce` is not 24 bytes *(terminal: instance locked)*
412
+ - `RangeError` if `plaintext` exceeds the maximum chunk size *(terminal: instance locked)*
413
+ - `Error` if `encrypt()` has already been called on this instance
366
414
 
367
415
  ---
368
416
 
@@ -395,6 +443,59 @@ Wipes all key material and intermediate state from WASM memory.
395
443
 
396
444
  ---
397
445
 
446
+ ### ChaCha20Generator
447
+
448
+ ChaCha20 block-function PRF for Fortuna's generator slot (RFC 8439 §2.3).
449
+ Uses a fixed zero nonce; Fortuna's counter is the only varying input.
450
+ Implements the `Generator` interface. Plain `const` object — no instantiation,
451
+ no `dispose()`.
452
+
453
+ Requires `init({ chacha20: chacha20Wasm })`. See [fortuna.md](./fortuna.md)
454
+ for full usage with `Fortuna.create()`.
455
+
456
+ | Property | Value |
457
+ |----------|-------|
458
+ | `keySize` | `32` |
459
+ | `blockSize` | `64` |
460
+ | `counterSize` | `4` |
461
+ | `wasmModules` | `['chacha20']` |
462
+
463
+ #### `ChaCha20Generator.generate(key, counter, n): Uint8Array`
464
+
465
+ Produces `n` bytes from `(key, counter)`. Neither input is mutated. Wipes WASM
466
+ key/state/keystream scratch before returning.
467
+
468
+ | Parameter | Type | Description |
469
+ |-----------|------|-------------|
470
+ | `key` | `Uint8Array` | 32 bytes |
471
+ | `counter` | `Uint8Array` | 4 bytes, read as a little-endian u32 |
472
+ | `n` | `number` | Output byte count: 0 ≤ n ≤ 2³⁰ |
473
+
474
+ **Returns** a new `Uint8Array` of length `n`.
475
+
476
+ **Throws:**
477
+ - `RangeError('ChaCha20Generator: key must be 32 bytes (got N)')` if key length ≠ 32
478
+ - `RangeError('ChaCha20Generator: counter must be 4 bytes (got N)')` if counter length ≠ 4
479
+ - `RangeError('ChaCha20Generator: n must be a non-negative safe integer <= 2^30 (got N)')` if n is out of range
480
+ - `Error` if another stateful instance currently owns the `chacha20` WASM module
481
+
482
+ #### Usage with `Fortuna`
483
+
484
+ ```typescript
485
+ import { init, Fortuna } from 'leviathan-crypto'
486
+ import { ChaCha20Generator } from 'leviathan-crypto/chacha20'
487
+ import { SHA256Hash } from 'leviathan-crypto/sha2'
488
+ import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
489
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
490
+
491
+ await init({ chacha20: chacha20Wasm, sha2: sha2Wasm })
492
+ const rng = await Fortuna.create({ generator: ChaCha20Generator, hash: SHA256Hash })
493
+ const bytes = rng.get(32)
494
+ rng.stop()
495
+ ```
496
+
497
+ ---
498
+
398
499
  ## XChaCha20Cipher
399
500
 
400
501
  `CipherSuite` implementation for XChaCha20-Poly1305. Pass to `Seal`,
@@ -412,7 +513,7 @@ Requires `init({ chacha20: chacha20Wasm, sha2: sha2Wasm })`.
412
513
  | `keySize` | `32` |
413
514
  | `tagSize` | `16` (Poly1305) |
414
515
  | `padded` | `false` |
415
- | `wasmModules` | `['chacha20', 'sha2']` |
516
+ | `wasmModules` | `['chacha20']` |
416
517
 
417
518
  #### `XChaCha20Cipher.keygen(): Uint8Array`
418
519
 
@@ -638,7 +739,7 @@ const pt2 = cipher.decryptChunk(ct2)
638
739
 
639
740
  // WARNING: Without authentication, an attacker can flip bits in ciphertext
640
741
  // and the corresponding plaintext bits will flip with no error.
641
- // Pair with HMAC (Encrypt-then-MAC) or use XChaCha20Poly1305 instead.
742
+ // Use the seal api or XChaCha20Poly1305 instead.
642
743
 
643
744
  cipher.dispose()
644
745
  ```
@@ -660,15 +761,21 @@ cipher.dispose()
660
761
  | Chunk or plaintext exceeds WASM buffer size | `RangeError` | `plaintext exceeds N bytes — split into smaller chunks` / `chunk exceeds maximum size of N bytes — split into smaller chunks` |
661
762
  | Authentication tag does not match on decrypt | `Error` | `ChaCha20Poly1305: authentication failed` |
662
763
  | Empty plaintext | | Allowed. Encrypting zero bytes produces just a 16-byte tag (AEAD) or zero bytes (raw ChaCha20). |
764
+ | `ChaCha20Generator.generate()` key ≠ 32 bytes | `RangeError` | `ChaCha20Generator: key must be 32 bytes (got N)` |
765
+ | `ChaCha20Generator.generate()` counter ≠ 4 bytes | `RangeError` | `ChaCha20Generator: counter must be 4 bytes (got N)` |
766
+ | `ChaCha20Generator.generate()` n out of range | `RangeError` | `ChaCha20Generator: n must be a non-negative safe integer <= 2^30 (got N)` |
767
+
768
+ ## Cross-References
769
+
770
+ | Document | Description |
771
+ | -------- | ----------- |
772
+ | [index](./README.md) | Project Documentation index |
773
+ | [lexicon](./lexicon.md) | Glossary of cryptographic terms |
774
+ | [asm_chacha](./asm_chacha.md) | WASM (AssemblyScript) implementation details for the chacha20 module |
775
+ | [authenticated encryption](./aead.md) | `Seal`, `SealStream`, `OpenStream`: use `XChaCha20Cipher` as the suite argument |
776
+ | [serpent](./serpent.md) | `SerpentCipher`: alternative `CipherSuite` for `Seal` and streaming |
777
+ | [sha2](./sha2.md) | SHA-2 hashes and HMAC. Needed for Encrypt-then-MAC if using raw ChaCha20 |
778
+ | [types](./types.md) | `AEAD` and `Streamcipher` interfaces implemented by ChaCha20 classes |
779
+ | [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
780
+ | [chacha_audit](./chacha_audit.md) | XChaCha20-Poly1305 implementation audit |
663
781
 
664
- > ## Cross-References
665
- >
666
- > - [index](./README.md) — Project Documentation index
667
- > - [lexicon](./lexicon.md) — Glossary of cryptographic terms
668
- > - [asm_chacha](./asm_chacha.md) — WASM (AssemblyScript) implementation details for the chacha20 module
669
- > - [authenticated encryption](./aead.md) — `Seal`, `SealStream`, `OpenStream`: use `XChaCha20Cipher` as the suite argument
670
- > - [serpent](./serpent.md) — `SerpentCipher`: alternative `CipherSuite` for `Seal` and streaming
671
- > - [sha2](./sha2.md) — SHA-2 hashes and HMAC. Needed for Encrypt-then-MAC if using raw ChaCha20
672
- > - [types](./types.md) — `AEAD` and `Streamcipher` interfaces implemented by ChaCha20 classes
673
- > - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
674
- > - [chacha_audit](./chacha_audit.md) — XChaCha20-Poly1305 implementation audit
@@ -1,7 +1,8 @@
1
- # All Exports
1
+ <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="logo" width="120" align="left" margin="10">
2
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.
3
+ ### All Exports
4
+
5
+ Complete reference for every public export in leviathan-crypto, grouped by module. Follow the module links for deeper documentation on each class.
5
6
 
6
7
  > ### Table of Contents
7
8
  > - [Initialization](#initialization)
@@ -14,6 +15,7 @@
14
15
  > - [Keccak (alias for SHA-3)](#keccak-alias-for-sha-3)
15
16
  > - [ML-KEM (Post-quantum KEM)](#ml-kem-post-quantum-kem)
16
17
  > - [Fortuna CSPRNG](#fortuna-csprng)
18
+ > - [Ratchet (Sparse Post-Quantum Ratchet KDF)](#ratchet-sparse-post-quantum-ratchet-kdf)
17
19
  > - [Types](#types)
18
20
  > - [Utilities](#utilities)
19
21
 
@@ -191,11 +193,41 @@ Subpath: `leviathan-crypto/kyber`. See [kyber.md](./kyber.md).
191
193
 
192
194
  ## Fortuna CSPRNG
193
195
 
194
- Requires `init({ serpent: serpentWasm, sha2: sha2Wasm })`. See [fortuna.md](./fortuna.md).
196
+ Takes a `Generator` and a `HashFn` at create time. Required `init()` modules depend on which pair you pass; valid combinations are listed in [fortuna.md](./fortuna.md).
197
+
198
+ | Export | Kind | Description |
199
+ |--------|------|-------------|
200
+ | `Fortuna` | class | Fortuna CSPRNG (Ferguson & Schneier). `Fortuna.create({ generator, hash })` static factory; `get(n)`, `addEntropy()`, `stop()`. |
201
+ | `SerpentGenerator` | const | `Generator` const for `Fortuna`. Serpent-256 PRF in counter mode. Requires `init({ serpent })`. Re-exported from `'leviathan-crypto/serpent'`. |
202
+ | `ChaCha20Generator` | const | `Generator` const for `Fortuna`. ChaCha20 PRF with fixed zero nonce. Requires `init({ chacha20 })`. Re-exported from `'leviathan-crypto/chacha20'`. |
203
+ | `SHA256Hash` | const | `HashFn` const for `Fortuna`. Stateless SHA-256. Requires `init({ sha2 })`. Re-exported from `'leviathan-crypto/sha2'`. |
204
+ | `SHA3_256Hash` | const | `HashFn` const for `Fortuna`. Stateless SHA3-256. Requires `init({ sha3 })`. Re-exported from `'leviathan-crypto/sha3'`. |
205
+ | `Generator` | type | Interface implemented by `SerpentGenerator` and `ChaCha20Generator`. |
206
+ | `HashFn` | type | Interface implemented by `SHA256Hash` and `SHA3_256Hash`. |
207
+
208
+ ---
209
+
210
+ ## Ratchet (Sparse Post-Quantum Ratchet KDF)
211
+
212
+ `ratchetInit`, `KDFChain`, `ratchetReady` require `init({ sha2: sha2Wasm })`.
213
+ `kemRatchetEncap`, `kemRatchetDecap` additionally require `init({ kyber: kyberWasm, sha3: sha3Wasm })`.
214
+ Subpath: `leviathan-crypto/ratchet`. See [ratchet.md](./ratchet.md).
195
215
 
196
216
  | Export | Kind | Description |
197
217
  |--------|------|-------------|
198
- | `Fortuna` | class | Fortuna CSPRNG (Ferguson & Schneier). `Fortuna.create()` static factory, `get(n)`, `addEntropy()`, `stop()`. |
218
+ | `ratchetInit` | function | `ratchetInit(sk, context?)` derives initial root key, send chain key, and receive chain key from a 32-byte shared secret (`KDF_SCKA_INIT`). Returns `RatchetInitResult`. |
219
+ | `KDFChain` | class | Stateful symmetric ratchet chain (`KDF_SCKA_CK`). `new KDFChain(ck)`, `step()` → 32-byte message key, `stepWithCounter()` → `{ key, counter }`, `dispose()`. |
220
+ | `SkippedKeyStore` | class | MKSKIPPED cache for a single `KDFChain` (DR spec §3.2/§3.5). `new SkippedKeyStore({ maxCacheSize?, maxSkipPerResolve? })`. `resolve(chain, counter)` → `ResolveHandle` — call `handle.commit()` on successful decrypt, `handle.rollback()` on auth failure. `advanceToBoundary(chain, pn)`, `size`, `wipeAll()`. Requires `sha2`. |
221
+ | `RatchetKeypair` | class | Single-use ek/dk lifecycle for one KEM ratchet step. `new RatchetKeypair(kem)`, `readonly ek`, `decap(kem, rk, kemCt, context?)`, `dispose()`. Requires `sha2`, `kyber`, `sha3`. |
222
+ | `kemRatchetEncap` | function | `kemRatchetEncap(kem, rk, peerEk, context?)` — encapsulation side of a KEM ratchet step (`KDF_SCKA_RK`). Returns `KemEncapResult` including `kemCt` to transmit to peer. |
223
+ | `kemRatchetDecap` | function | `kemRatchetDecap(kem, rk, dk, kemCt, ownEk, context?)` — decapsulation side of a KEM ratchet step. `ownEk` is the local party's encapsulation key, bound into the HKDF info string alongside `peerEk` and `kemCt` as defense-in-depth on top of the KEM FO transform. Returns `KemDecapResult` with chain key slots swapped to match Bob's perspective. |
224
+ | `ratchetReady` | function | `ratchetReady(): boolean` — returns `true` if `sha2` has been initialized. |
225
+ | `RatchetInitResult` | type | `{ nextRootKey, sendChainKey, recvChainKey }` — all 32-byte `Uint8Array` fields. |
226
+ | `KemEncapResult` | type | `{ nextRootKey, sendChainKey, recvChainKey, kemCt }` — three 32-byte keys plus the ML-KEM ciphertext. |
227
+ | `KemDecapResult` | type | `{ nextRootKey, sendChainKey, recvChainKey }` — all 32-byte `Uint8Array` fields. Slots are swapped relative to the encap side. |
228
+ | `RatchetMessageHeader` | interface | `{ epoch, counter, pn?, kemCt? }` — canonical message header shape. `pn` and `kemCt` present only on the first message of a new epoch. |
229
+ | `MlKemLike` | interface | Structural interface satisfied by `MlKem512`, `MlKem768`, `MlKem1024`. Used as the `kem` parameter type for `kemRatchetEncap`/`kemRatchetDecap`/`RatchetKeypair`. |
230
+ | `ResolveHandle` | interface | Return type of `SkippedKeyStore.resolve()`. `readonly key` — 32-byte message key (throws after settlement). `commit()` — wipes key, marks settled (call on successful decrypt). `rollback()` — returns key to store, marks settled (call on auth failure). Double-settle throws. |
199
231
 
200
232
  ---
201
233
 
@@ -225,7 +257,7 @@ No `init()` required. See [utils.md](./utils.md).
225
257
  | `bytesToUtf8` | function | `Uint8Array` to UTF-8 string. |
226
258
  | `base64ToBytes` | function | Base64/base64url string to `Uint8Array`. Returns `undefined` on invalid input. |
227
259
  | `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`. |
260
+ | `constantTimeEqual` | function | Constant-time byte-array equality. Runs entirely inside a dedicated WASM SIMD module (v128 XOR-accumulate with branch-free reduction) to eliminate JIT timing leaks. Throws a branded error on runtimes without WebAssembly SIMD; no JS fallback. Returns `false` immediately on length mismatch. Throws `RangeError` if either input exceeds `CT_MAX_BYTES`. |
229
261
  | `CT_MAX_BYTES` | const | Maximum input size for `constantTimeEqual` per side (32768 bytes, one 64 KiB WASM page split between two buffers). |
230
262
  | `wipe` | function | Zero a typed array in place. |
231
263
  | `xor` | function | XOR two equal-length `Uint8Array`s, returns new array. |
@@ -235,7 +267,11 @@ No `init()` required. See [utils.md](./utils.md).
235
267
 
236
268
  ---
237
269
 
238
- > ## Cross-References
239
- >
240
- > - [index](./README.md) — Project Documentation index
241
- > - [architecture](./architecture.md) architecture overview, module relationships, buffer layouts, and build pipeline
270
+
271
+ ## Cross-References
272
+
273
+ | Document | Description |
274
+ | -------- | ----------- |
275
+ | [index](./README.md) | Project Documentation index |
276
+ | [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
277
+