leviathan-crypto 1.4.0 → 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.
Files changed (119) hide show
  1. package/CLAUDE.md +129 -94
  2. package/README.md +166 -223
  3. package/SECURITY.md +85 -45
  4. package/dist/chacha20/cipher-suite.d.ts +4 -0
  5. package/dist/chacha20/cipher-suite.js +78 -0
  6. package/dist/chacha20/embedded.d.ts +1 -0
  7. package/dist/chacha20/embedded.js +27 -0
  8. package/dist/chacha20/index.d.ts +20 -27
  9. package/dist/chacha20/index.js +40 -59
  10. package/dist/chacha20/ops.d.ts +1 -1
  11. package/dist/chacha20/ops.js +19 -18
  12. package/dist/chacha20/pool-worker.js +77 -0
  13. package/dist/ct-wasm.d.ts +1 -0
  14. package/dist/ct-wasm.js +3 -0
  15. package/dist/ct.wasm +0 -0
  16. package/dist/docs/aead.md +320 -0
  17. package/dist/docs/architecture.md +419 -285
  18. package/dist/docs/argon2id.md +42 -30
  19. package/dist/docs/chacha20.md +192 -266
  20. package/dist/docs/exports.md +241 -0
  21. package/dist/docs/fortuna.md +60 -69
  22. package/dist/docs/init.md +172 -178
  23. package/dist/docs/loader.md +87 -142
  24. package/dist/docs/serpent.md +134 -583
  25. package/dist/docs/sha2.md +91 -103
  26. package/dist/docs/sha3.md +70 -36
  27. package/dist/docs/types.md +93 -16
  28. package/dist/docs/utils.md +109 -32
  29. package/dist/embedded/kyber.d.ts +1 -0
  30. package/dist/embedded/kyber.js +3 -0
  31. package/dist/errors.d.ts +10 -0
  32. package/dist/errors.js +38 -0
  33. package/dist/fortuna.d.ts +0 -6
  34. package/dist/fortuna.js +5 -5
  35. package/dist/index.d.ts +25 -9
  36. package/dist/index.js +36 -7
  37. package/dist/init.d.ts +3 -7
  38. package/dist/init.js +18 -35
  39. package/dist/keccak/embedded.d.ts +1 -0
  40. package/dist/keccak/embedded.js +27 -0
  41. package/dist/keccak/index.d.ts +4 -0
  42. package/dist/keccak/index.js +31 -0
  43. package/dist/kyber/embedded.d.ts +1 -0
  44. package/dist/kyber/embedded.js +27 -0
  45. package/dist/kyber/indcpa.d.ts +49 -0
  46. package/dist/kyber/indcpa.js +352 -0
  47. package/dist/kyber/index.d.ts +38 -0
  48. package/dist/kyber/index.js +150 -0
  49. package/dist/kyber/kem.d.ts +21 -0
  50. package/dist/kyber/kem.js +160 -0
  51. package/dist/kyber/params.d.ts +14 -0
  52. package/dist/kyber/params.js +37 -0
  53. package/dist/kyber/suite.d.ts +13 -0
  54. package/dist/kyber/suite.js +93 -0
  55. package/dist/kyber/types.d.ts +98 -0
  56. package/dist/kyber/types.js +25 -0
  57. package/dist/kyber/validate.d.ts +19 -0
  58. package/dist/kyber/validate.js +68 -0
  59. package/dist/kyber.wasm +0 -0
  60. package/dist/loader.d.ts +15 -6
  61. package/dist/loader.js +65 -21
  62. package/dist/serpent/cipher-suite.d.ts +4 -0
  63. package/dist/serpent/cipher-suite.js +121 -0
  64. package/dist/serpent/embedded.d.ts +1 -0
  65. package/dist/serpent/embedded.js +27 -0
  66. package/dist/serpent/index.d.ts +6 -37
  67. package/dist/serpent/index.js +9 -118
  68. package/dist/serpent/pool-worker.d.ts +1 -0
  69. package/dist/serpent/pool-worker.js +202 -0
  70. package/dist/serpent/serpent-cbc.d.ts +30 -0
  71. package/dist/serpent/serpent-cbc.js +136 -0
  72. package/dist/sha2/embedded.d.ts +1 -0
  73. package/dist/sha2/embedded.js +27 -0
  74. package/dist/sha2/hkdf.js +6 -2
  75. package/dist/sha2/index.d.ts +3 -2
  76. package/dist/sha2/index.js +3 -4
  77. package/dist/sha3/embedded.d.ts +1 -0
  78. package/dist/sha3/embedded.js +27 -0
  79. package/dist/sha3/index.d.ts +3 -2
  80. package/dist/sha3/index.js +3 -4
  81. package/dist/stream/constants.d.ts +6 -0
  82. package/dist/stream/constants.js +30 -0
  83. package/dist/stream/header.d.ts +9 -0
  84. package/dist/stream/header.js +77 -0
  85. package/dist/stream/index.d.ts +7 -0
  86. package/dist/stream/index.js +27 -0
  87. package/dist/stream/open-stream.d.ts +21 -0
  88. package/dist/stream/open-stream.js +146 -0
  89. package/dist/stream/seal-stream-pool.d.ts +38 -0
  90. package/dist/stream/seal-stream-pool.js +391 -0
  91. package/dist/stream/seal-stream.d.ts +20 -0
  92. package/dist/stream/seal-stream.js +142 -0
  93. package/dist/stream/seal.d.ts +9 -0
  94. package/dist/stream/seal.js +75 -0
  95. package/dist/stream/types.d.ts +24 -0
  96. package/dist/stream/types.js +26 -0
  97. package/dist/utils.d.ts +7 -2
  98. package/dist/utils.js +49 -3
  99. package/dist/wasm-source.d.ts +12 -0
  100. package/dist/wasm-source.js +26 -0
  101. package/package.json +13 -5
  102. package/dist/chacha20/pool.d.ts +0 -52
  103. package/dist/chacha20/pool.js +0 -178
  104. package/dist/chacha20/pool.worker.js +0 -37
  105. package/dist/chacha20/stream-sealer.d.ts +0 -49
  106. package/dist/chacha20/stream-sealer.js +0 -327
  107. package/dist/docs/chacha20_pool.md +0 -309
  108. package/dist/docs/wasm.md +0 -194
  109. package/dist/serpent/seal.d.ts +0 -8
  110. package/dist/serpent/seal.js +0 -72
  111. package/dist/serpent/stream-pool.d.ts +0 -48
  112. package/dist/serpent/stream-pool.js +0 -275
  113. package/dist/serpent/stream-sealer.d.ts +0 -55
  114. package/dist/serpent/stream-sealer.js +0 -342
  115. package/dist/serpent/stream.d.ts +0 -28
  116. package/dist/serpent/stream.js +0 -205
  117. package/dist/serpent/stream.worker.d.ts +0 -32
  118. package/dist/serpent/stream.worker.js +0 -117
  119. /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
package/dist/docs/sha2.md CHANGED
@@ -1,4 +1,4 @@
1
- # SHA-2 hash functions and HMAC TypeScript API
1
+ # SHA-2 TypeScript API
2
2
 
3
3
  > [!NOTE]
4
4
  > Cryptographic hashing and message authentication using SHA-256, SHA-384,
@@ -6,42 +6,49 @@
6
6
  >
7
7
  > See [SHA-2 implementation audit](./sha2_audit.md), [HMAC audit](./hmac_audit.md), and [HKDF audit](./hkdf_audit.md) for algorithm correctness verifications.
8
8
 
9
+ > ### Table of Contents
10
+ > - [Overview](#overview)
11
+ > - [Security Notes](#security-notes)
12
+ > - [Module Init](#module-init)
13
+ > - [API Reference](#api-reference)
14
+ > - [Usage Examples](#usage-examples)
15
+ > - [Error Conditions](#error-conditions)
16
+
17
+ ---
18
+
9
19
  ## Overview
10
20
 
11
21
  SHA-2 is a family of cryptographic hash functions standardized in
12
22
  [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final).
13
- A hash function takes an input of any size -- a password, a file, a single
14
- byte -- and produces a fixed-size output called a **digest** (sometimes called
23
+ A hash function takes an input of any size (a password, a file, a single byte) and produces a fixed-size output called a **digest** (sometimes called
15
24
  a "fingerprint" or "hash"). Even the smallest change to the input produces a
16
25
  completely different digest. This makes hash functions useful for verifying that
17
26
  data has not been tampered with.
18
27
 
19
28
  leviathan-crypto provides three SHA-2 variants:
20
29
 
21
- - **SHA-256** -- 32-byte (256-bit) digest. The most widely used variant. Use
22
- this unless you have a specific reason to choose another.
23
- - **SHA-512** -- 64-byte (512-bit) digest. Higher security margin. Faster than
24
- SHA-256 on 64-bit platforms.
25
- - **SHA-384** -- 48-byte (384-bit) digest. A truncated variant of SHA-512.
26
- Useful when you need a digest longer than 256 bits but shorter than 512 bits,
27
- or when a protocol specifies it (e.g. TLS cipher suites).
30
+ **SHA-256.** 32-byte (256-bit) digest. The most widely used variant. Use this unless you have a specific reason to choose another.
31
+
32
+ **SHA-512.** 64-byte (512-bit) digest. Higher security margin. Faster than SHA-256 on 64-bit platforms.
33
+
34
+ **SHA-384.** 48-byte (384-bit) digest. A truncated variant of SHA-512. Useful when you need a digest longer than 256 bits but shorter than 512 bits, or when a protocol specifies it (e.g. TLS cipher suites).
28
35
 
29
36
  **HMAC** (Hash-based Message Authentication Code, [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104))
30
37
  combines a secret key with a hash function to produce a **tag** that proves both
31
38
  the integrity and the authenticity of a message. Anyone can compute a plain SHA-256
32
- hash of a message -- but only someone who holds the secret key can compute the
33
- correct HMAC tag. This means the recipient can verify that the message was sent by
34
- someone who knows the key, and that it was not modified in transit.
39
+ hash of a message. But only someone who holds the secret key can compute the correct HMAC tag. The recipient can then verify that the message was sent by
40
+ someone who knows the key and that it was not modified in transit.
35
41
 
36
42
  leviathan-crypto provides three HMAC variants corresponding to each hash:
37
43
 
38
- - **HMAC_SHA256** -- 32-byte tag, using SHA-256
39
- - **HMAC_SHA512** -- 64-byte tag, using SHA-512
40
- - **HMAC_SHA384** -- 48-byte tag, using SHA-384
44
+ **HMAC_SHA256.** 32-byte tag.
45
+
46
+ **HMAC_SHA512.** 64-byte tag.
47
+
48
+ **HMAC_SHA384.** 48-byte tag.
41
49
 
42
50
  All computation runs in WebAssembly. The TypeScript classes handle input
43
- validation and the JS/WASM boundary -- they never implement cryptographic
44
- algorithms directly.
51
+ validation and the JS/WASM boundary. They never implement cryptographic algorithms directly.
45
52
 
46
53
  ---
47
54
 
@@ -70,7 +77,7 @@ encryption with leviathan primitives.
70
77
  Never construct a MAC by concatenating a secret and a message and hashing them:
71
78
 
72
79
  ```typescript
73
- // DANGEROUS -- DO NOT DO THIS
80
+ // DANGEROUS: DO NOT DO THIS
74
81
  const bad = sha256.hash(concat(secret, message))
75
82
  ```
76
83
 
@@ -94,8 +101,7 @@ HMAC keys should be **at least as long as the hash output**:
94
101
  Keys shorter than this are technically valid (they will be zero-padded
95
102
  internally) but provide less security than the hash function offers. Keys
96
103
  longer than the hash block size (64 bytes for SHA-256, 128 bytes for
97
- SHA-384/SHA-512) are pre-hashed automatically per RFC 2104 section 3 -- this is
98
- handled for you, but there is no benefit to using very long keys.
104
+ SHA-384/SHA-512) are pre-hashed automatically per RFC 2104 section 3. There is no benefit to using very long keys.
99
105
 
100
106
  ### Always use constant-time comparison for HMAC verification
101
107
 
@@ -121,24 +127,25 @@ with a hash or HMAC instance.
121
127
  Each module subpath exports its own init function for consumers who want
122
128
  tree-shakeable imports.
123
129
 
124
- ### `sha2Init(mode?, opts?)`
130
+ ### `sha2Init(source)`
125
131
 
126
132
  Initializes only the sha2 WASM binary. Equivalent to calling the
127
- root `init(['sha2'], mode, opts)` but without pulling the other three
133
+ root `init({ sha2: source })` but without pulling the other three
128
134
  modules into the bundle.
129
135
 
130
136
  **Signature:**
131
137
 
132
138
  ```typescript
133
- async function sha2Init(mode?: Mode, opts?: InitOpts): Promise<void>
139
+ async function sha2Init(source: WasmSource): Promise<void>
134
140
  ```
135
141
 
136
142
  **Usage:**
137
143
 
138
144
  ```typescript
139
145
  import { sha2Init, SHA256 } from 'leviathan-crypto/sha2'
146
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
140
147
 
141
- await sha2Init()
148
+ await sha2Init(sha2Wasm)
142
149
  const sha = new SHA256()
143
150
  ```
144
151
 
@@ -146,7 +153,7 @@ const sha = new SHA256()
146
153
 
147
154
  ## API Reference
148
155
 
149
- All classes require `init(['sha2'])` or the subpath `sha2Init()` to be called first.
156
+ All classes require `init({ sha2: sha2Wasm })` or the subpath `sha2Init(sha2Wasm)` to be called first.
150
157
  Constructing any SHA-2 class before initialization throws an error.
151
158
 
152
159
  ### SHA256
@@ -161,16 +168,11 @@ class SHA256 {
161
168
  }
162
169
  ```
163
170
 
164
- **`constructor()`** -- Creates a new SHA256 instance. Throws if `init(['sha2'])`
165
- has not been called.
171
+ **`constructor()`** Creates a new SHA256 instance. Throws if `init({ sha2: sha2Wasm })` has not been called.
166
172
 
167
- **`hash(msg: Uint8Array): Uint8Array`** -- Hashes the entire message and returns
168
- a 32-byte `Uint8Array` digest. The message can be any length (including empty).
169
- Large messages are internally chunked and streamed through the WASM hash function,
170
- so memory usage stays constant regardless of input size.
173
+ **`hash(msg: Uint8Array): Uint8Array`** Hashes the entire message and returns a 32-byte `Uint8Array` digest. The message can be any length including empty. Large messages are internally chunked and streamed through the WASM hash function, so memory usage stays constant regardless of input size.
171
174
 
172
- **`dispose(): void`** -- Wipes all internal WASM buffers (hash state, input
173
- buffer, output buffer). Call this when you are done with the instance.
175
+ **`dispose(): void`** Wipes all internal WASM buffers (hash state, input buffer, output buffer). Call this when you are done with the instance.
174
176
 
175
177
  ---
176
178
 
@@ -186,11 +188,11 @@ class SHA512 {
186
188
  }
187
189
  ```
188
190
 
189
- **`constructor()`** -- Creates a new SHA512 instance. Throws if not initialized.
191
+ **`constructor()`** Creates a new SHA512 instance. Throws if not initialized.
190
192
 
191
- **`hash(msg: Uint8Array): Uint8Array`** -- Returns a 64-byte digest.
193
+ **`hash(msg: Uint8Array): Uint8Array`** Returns a 64-byte digest.
192
194
 
193
- **`dispose(): void`** -- Wipes all internal WASM buffers.
195
+ **`dispose(): void`** Wipes all internal WASM buffers.
194
196
 
195
197
  ---
196
198
 
@@ -207,11 +209,11 @@ class SHA384 {
207
209
  }
208
210
  ```
209
211
 
210
- **`constructor()`** -- Creates a new SHA384 instance. Throws if not initialized.
212
+ **`constructor()`** Creates a new SHA384 instance. Throws if not initialized.
211
213
 
212
- **`hash(msg: Uint8Array): Uint8Array`** -- Returns a 48-byte digest.
214
+ **`hash(msg: Uint8Array): Uint8Array`** Returns a 48-byte digest.
213
215
 
214
- **`dispose(): void`** -- Wipes all internal WASM buffers.
216
+ **`dispose(): void`** Wipes all internal WASM buffers.
215
217
 
216
218
  ---
217
219
 
@@ -227,15 +229,11 @@ class HMAC_SHA256 {
227
229
  }
228
230
  ```
229
231
 
230
- **`constructor()`** -- Creates a new HMAC_SHA256 instance. Throws if not
231
- initialized.
232
+ **`constructor()`** Creates a new HMAC_SHA256 instance. Throws if not initialized.
232
233
 
233
- **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** -- Computes the
234
- HMAC-SHA256 tag for the given message using the given key. Returns a 32-byte
235
- `Uint8Array`. Keys longer than 64 bytes are automatically pre-hashed with
236
- SHA-256 per RFC 2104 section 3.
234
+ **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** Computes the HMAC-SHA256 tag for the given message using the given key. Returns a 32-byte `Uint8Array`. Keys longer than 64 bytes are automatically pre-hashed with SHA-256 per RFC 2104 section 3.
237
235
 
238
- **`dispose(): void`** -- Wipes all internal WASM buffers, including key material.
236
+ **`dispose(): void`** Wipes all internal WASM buffers, including key material.
239
237
 
240
238
  ---
241
239
 
@@ -251,13 +249,11 @@ class HMAC_SHA512 {
251
249
  }
252
250
  ```
253
251
 
254
- **`constructor()`** -- Creates a new HMAC_SHA512 instance. Throws if not
255
- initialized.
252
+ **`constructor()`** Creates a new HMAC_SHA512 instance. Throws if not initialized.
256
253
 
257
- **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** -- Returns a 64-byte
258
- HMAC tag. Keys longer than 128 bytes are pre-hashed with SHA-512.
254
+ **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** Returns a 64-byte HMAC tag. Keys longer than 128 bytes are pre-hashed with SHA-512.
259
255
 
260
- **`dispose(): void`** -- Wipes all internal WASM buffers.
256
+ **`dispose(): void`** Wipes all internal WASM buffers.
261
257
 
262
258
  ---
263
259
 
@@ -273,13 +269,11 @@ class HMAC_SHA384 {
273
269
  }
274
270
  ```
275
271
 
276
- **`constructor()`** -- Creates a new HMAC_SHA384 instance. Throws if not
277
- initialized.
272
+ **`constructor()`** Creates a new HMAC_SHA384 instance. Throws if not initialized.
278
273
 
279
- **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** -- Returns a 48-byte
280
- HMAC tag. Keys longer than 128 bytes are pre-hashed with SHA-384.
274
+ **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** Returns a 48-byte HMAC tag. Keys longer than 128 bytes are pre-hashed with SHA-384.
281
275
 
282
- **`dispose(): void`** -- Wipes all internal WASM buffers.
276
+ **`dispose(): void`** Wipes all internal WASM buffers.
283
277
 
284
278
  ---
285
279
 
@@ -302,26 +296,15 @@ class HKDF_SHA256 {
302
296
  }
303
297
  ```
304
298
 
305
- **`constructor()`** -- Creates a new HKDF_SHA256 instance. Throws if
306
- `init(['sha2'])` has not been called.
299
+ **`constructor()`** Creates a new HKDF_SHA256 instance. Throws if `init({ sha2: sha2Wasm })` has not been called.
307
300
 
308
- **`extract(salt, ikm): Uint8Array`** -- RFC 5869 section 2.2. Computes
309
- `PRK = HMAC-SHA256(salt, IKM)`. Returns a 32-byte pseudorandom key. If `salt`
310
- is `null` or empty, defaults to 32 zero bytes per RFC section 2.2.
301
+ **`extract(salt, ikm): Uint8Array`** RFC 5869 section 2.2. Computes `PRK = HMAC-SHA256(salt, IKM)`. Returns a 32-byte pseudorandom key. If `salt` is `null` or empty, defaults to 32 zero bytes per RFC section 2.2.
311
302
 
312
- **`expand(prk, info, length): Uint8Array`** -- RFC 5869 section 2.3. Derives
313
- `length` bytes of output keying material from a 32-byte PRK. `info` provides
314
- application-specific context (can be empty). `length` must be between 1 and
315
- 8160 (255 x 32). Throws `RangeError` if `prk` is not exactly 32 bytes or if
316
- `length` is out of range.
303
+ **`expand(prk, info, length): Uint8Array`** RFC 5869 section 2.3. Derives `length` bytes of output keying material from a 32-byte PRK. `info` provides application-specific context (can be empty). `length` must be between 1 and 8160 (255 x 32). Throws `RangeError` if `prk` is not exactly 32 bytes or if `length` is out of range.
317
304
 
318
- **`derive(ikm, salt, info, length): Uint8Array`** -- One-shot: calls
319
- `extract(salt, ikm)` then `expand(prk, info, length)`. This is the correct
320
- path for most callers. `extract()` and `expand()` are exposed separately for
321
- advanced use cases such as key separation and ratchets -- callers who reach for
322
- them should know why.
305
+ **`derive(ikm, salt, info, length): Uint8Array`** One-shot: calls `extract(salt, ikm)` then `expand(prk, info, length)`. This is the correct path for most callers. `extract()` and `expand()` are exposed separately for advanced use cases such as key separation and ratchets. Callers who reach for them should know why.
323
306
 
324
- **`dispose(): void`** -- Releases the internal HMAC instance.
307
+ **`dispose(): void`** Releases the internal HMAC instance.
325
308
 
326
309
  ---
327
310
 
@@ -340,11 +323,9 @@ class HKDF_SHA512 {
340
323
  }
341
324
  ```
342
325
 
343
- **`extract(salt, ikm)`** -- If `salt` is `null` or empty, defaults to 64 zero
344
- bytes.
326
+ **`extract(salt, ikm)`** If `salt` is `null` or empty, defaults to 64 zero bytes.
345
327
 
346
- **`expand(prk, info, length)`** -- PRK must be exactly 64 bytes. `length` must
347
- be between 1 and 16320. Throws `RangeError` otherwise.
328
+ **`expand(prk, info, length)`** PRK must be exactly 64 bytes. `length` must be between 1 and 16320. Throws `RangeError` otherwise.
348
329
 
349
330
  > [!NOTE]
350
331
  > HKDF is a pure TypeScript composition over the WASM-backed HMAC classes.
@@ -355,8 +336,9 @@ be between 1 and 16320. Throws `RangeError` otherwise.
355
336
 
356
337
  ```typescript
357
338
  import { init, HKDF_SHA256, bytesToHex } from 'leviathan-crypto'
339
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
358
340
 
359
- await init(['sha2'])
341
+ await init({ sha2: sha2Wasm })
360
342
 
361
343
  const hkdf = new HKDF_SHA256()
362
344
  const ikm = new Uint8Array(32) // your input keying material
@@ -375,13 +357,14 @@ hkdf.dispose()
375
357
 
376
358
  ### Example 1: Hash a message with SHA-256
377
359
 
378
- The most common operation: hash a string and get a hex-encoded digest.
360
+ Hash a string and get a hex-encoded digest.
379
361
 
380
362
  ```typescript
381
363
  import { init, SHA256, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
364
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
382
365
 
383
366
  // Step 1: Initialize the SHA-2 WASM module (do this once at app startup)
384
- await init(['sha2'])
367
+ await init({ sha2: sha2Wasm })
385
368
 
386
369
  // Step 2: Create a SHA256 instance
387
370
  const sha = new SHA256()
@@ -395,7 +378,7 @@ const digest = sha.hash(message)
395
378
  console.log(bytesToHex(digest))
396
379
  // => "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"
397
380
 
398
- // Step 5: Clean up -- wipes hash state from WASM memory
381
+ // Step 5: Clean up, wipes hash state from WASM memory
399
382
  sha.dispose()
400
383
  ```
401
384
 
@@ -407,8 +390,9 @@ response.
407
390
 
408
391
  ```typescript
409
392
  import { init, SHA256, bytesToHex } from 'leviathan-crypto'
393
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
410
394
 
411
- await init(['sha2'])
395
+ await init({ sha2: sha2Wasm })
412
396
 
413
397
  // Suppose you have file contents as an ArrayBuffer (from FileReader, fetch, etc.)
414
398
  const response = await fetch('https://example.com/file.bin')
@@ -421,8 +405,7 @@ console.log('SHA-256:', bytesToHex(digest))
421
405
  sha.dispose()
422
406
  ```
423
407
 
424
- The library handles large inputs automatically -- it streams the data through
425
- the WASM hash function in chunks, so you do not need to worry about memory.
408
+ The library handles large inputs automatically. It streams the data through the WASM hash function in chunks, so you do not need to worry about memory.
426
409
 
427
410
  ### Example 3: Using SHA-512 or SHA-384
428
411
 
@@ -430,8 +413,9 @@ The API is identical for all three hash variants. Only the output size differs.
430
413
 
431
414
  ```typescript
432
415
  import { init, SHA256, SHA384, SHA512, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
416
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
433
417
 
434
- await init(['sha2'])
418
+ await init({ sha2: sha2Wasm })
435
419
 
436
420
  const msg = utf8ToBytes('Same message, different hashes')
437
421
 
@@ -451,16 +435,17 @@ sha512.dispose()
451
435
  ### Example 4: Generate and verify an HMAC
452
436
 
453
437
  Use HMAC when you need to prove that a message was created by someone who holds
454
- a secret key. A typical pattern: one side generates a tag, the other side
455
- recomputes the tag with the same key and checks that they match.
438
+ a secret key. One side generates a tag, the other side recomputes the tag with
439
+ the same key and checks that they match.
456
440
 
457
441
  ```typescript
458
442
  import {
459
443
  init, HMAC_SHA256, constantTimeEqual, randomBytes,
460
444
  bytesToHex, utf8ToBytes
461
445
  } from 'leviathan-crypto'
446
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
462
447
 
463
- await init(['sha2'])
448
+ await init({ sha2: sha2Wasm })
464
449
 
465
450
  // Generate a random 32-byte key (do this once, store it securely)
466
451
  const key = randomBytes(32)
@@ -480,7 +465,7 @@ const recomputed = hmac.hash(key, message)
480
465
 
481
466
  // Use constant-time comparison to check the tags
482
467
  if (constantTimeEqual(tag, recomputed)) {
483
- console.log('Message is authentic -- it was not tampered with')
468
+ console.log('Message is authentic. It was not tampered with.')
484
469
  } else {
485
470
  console.log('WARNING: message has been modified or key is wrong')
486
471
  }
@@ -488,31 +473,32 @@ if (constantTimeEqual(tag, recomputed)) {
488
473
  hmac.dispose()
489
474
  ```
490
475
 
491
- ### Example 5: HMAC verification -- the wrong way vs. the right way
476
+ ### Example 5: HMAC verification the right way
492
477
 
493
- This is important enough to call out separately. The difference between these
494
- two approaches is the difference between a secure system and a broken one.
478
+ The difference between these two approaches is the difference between a secure
479
+ system and a broken one.
495
480
 
496
481
  ```typescript
497
482
  import { init, HMAC_SHA256, constantTimeEqual, bytesToHex } from 'leviathan-crypto'
483
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
498
484
 
499
- await init(['sha2'])
485
+ await init({ sha2: sha2Wasm })
500
486
  const hmac = new HMAC_SHA256()
501
487
 
502
488
  // Suppose you received a message with a tag and you recomputed the expected tag:
503
489
  const receivedTag = hmac.hash(key, message)
504
490
  const expectedTag = hmac.hash(key, message)
505
491
 
506
- // WRONG -- timing attack vulnerable!
492
+ // WRONG: timing attack vulnerable
507
493
  // JavaScript's === operator compares byte-by-byte and returns false as soon as
508
494
  // it finds a mismatch. An attacker can measure the response time to figure out
509
495
  // how many leading bytes of the tag are correct, then forge a valid tag one
510
496
  // byte at a time.
511
497
  if (bytesToHex(receivedTag) === bytesToHex(expectedTag)) {
512
- // This "works" but is insecure
498
+ // This works but is insecure
513
499
  }
514
500
 
515
- // RIGHT -- constant-time comparison
501
+ // RIGHT: constant-time comparison
516
502
  // constantTimeEqual always examines every byte, regardless of where the first
517
503
  // difference is. The comparison takes the same amount of time whether zero
518
504
  // bytes match or all bytes match.
@@ -529,8 +515,9 @@ The pattern is identical to HMAC-SHA256. Use a 64-byte key for full security.
529
515
 
530
516
  ```typescript
531
517
  import { init, HMAC_SHA512, constantTimeEqual, randomBytes, utf8ToBytes } from 'leviathan-crypto'
518
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
532
519
 
533
- await init(['sha2'])
520
+ await init({ sha2: sha2Wasm })
534
521
 
535
522
  // 64-byte key for HMAC-SHA512
536
523
  const key = randomBytes(64)
@@ -552,8 +539,9 @@ SHA-2 is well-defined for empty inputs. This can be useful as a sanity check.
552
539
 
553
540
  ```typescript
554
541
  import { init, SHA256, bytesToHex } from 'leviathan-crypto'
542
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
555
543
 
556
- await init(['sha2'])
544
+ await init({ sha2: sha2Wasm })
557
545
 
558
546
  const sha = new SHA256()
559
547
  const digest = sha.hash(new Uint8Array(0))
@@ -570,14 +558,14 @@ sha.dispose()
570
558
 
571
559
  ### Module not initialized
572
560
 
573
- If you construct any SHA-2 class before calling `init(['sha2'])`, the
561
+ If you construct any SHA-2 class before calling `init({ sha2: sha2Wasm })`, the
574
562
  constructor throws immediately:
575
563
 
576
564
  ```
577
- Error: leviathan-crypto: call init(['sha2']) before using this class
565
+ Error: leviathan-crypto: call init({ sha2: ... }) before using this class
578
566
  ```
579
567
 
580
- **Fix:** Call `await init(['sha2'])` at application startup, before creating any
568
+ **Fix:** Call `await init({ sha2: sha2Wasm })` at application startup, before creating any
581
569
  SHA-2 instances.
582
570
 
583
571
  ```typescript
@@ -585,7 +573,7 @@ SHA-2 instances.
585
573
  const sha = new SHA256() // Error!
586
574
 
587
575
  // Do this instead:
588
- await init(['sha2'])
576
+ await init({ sha2: sha2Wasm })
589
577
  const sha = new SHA256() // OK
590
578
  ```
591
579
 
@@ -616,7 +604,7 @@ SHA-2 is well-defined for zero-length messages and will return the correct diges
616
604
  > - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
617
605
  > - [asm_sha2](./asm_sha2.md) — WASM implementation details (AssemblyScript buffer layout, compression functions)
618
606
  > - [sha3](./sha3.md) — alternative: SHA-3 family (immune to length extension attacks)
619
- > - [serpent](./serpent.md) — SerpentSeal and SerpentStream use HMAC-SHA256 and HKDF internally
607
+ > - [serpent](./serpent.md) — `SerpentCipher` uses HMAC-SHA256 and HKDF internally via `Seal` and `SealStream`
620
608
  > - [argon2id](./argon2id.md) — Argon2id password hashing; HKDF expands Argon2id root keys
621
609
  > - [fortuna](./fortuna.md) — Fortuna CSPRNG uses SHA-256 for entropy accumulation
622
610
  > - [utils](./utils.md) — `constantTimeEqual`, `bytesToHex`, `utf8ToBytes`, `randomBytes`