leviathan-crypto 1.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 (78) hide show
  1. package/CLAUDE.md +265 -0
  2. package/LICENSE +21 -0
  3. package/README.md +322 -0
  4. package/SECURITY.md +174 -0
  5. package/dist/chacha.wasm +0 -0
  6. package/dist/chacha20/index.d.ts +49 -0
  7. package/dist/chacha20/index.js +177 -0
  8. package/dist/chacha20/ops.d.ts +16 -0
  9. package/dist/chacha20/ops.js +146 -0
  10. package/dist/chacha20/pool.d.ts +52 -0
  11. package/dist/chacha20/pool.js +188 -0
  12. package/dist/chacha20/pool.worker.d.ts +1 -0
  13. package/dist/chacha20/pool.worker.js +37 -0
  14. package/dist/chacha20/types.d.ts +30 -0
  15. package/dist/chacha20/types.js +1 -0
  16. package/dist/docs/architecture.md +795 -0
  17. package/dist/docs/argon2id.md +290 -0
  18. package/dist/docs/chacha20.md +602 -0
  19. package/dist/docs/chacha20_pool.md +306 -0
  20. package/dist/docs/fortuna.md +322 -0
  21. package/dist/docs/init.md +308 -0
  22. package/dist/docs/loader.md +206 -0
  23. package/dist/docs/serpent.md +914 -0
  24. package/dist/docs/sha2.md +620 -0
  25. package/dist/docs/sha3.md +509 -0
  26. package/dist/docs/types.md +198 -0
  27. package/dist/docs/utils.md +273 -0
  28. package/dist/docs/wasm.md +193 -0
  29. package/dist/embedded/chacha.d.ts +1 -0
  30. package/dist/embedded/chacha.js +2 -0
  31. package/dist/embedded/serpent.d.ts +1 -0
  32. package/dist/embedded/serpent.js +2 -0
  33. package/dist/embedded/sha2.d.ts +1 -0
  34. package/dist/embedded/sha2.js +2 -0
  35. package/dist/embedded/sha3.d.ts +1 -0
  36. package/dist/embedded/sha3.js +2 -0
  37. package/dist/fortuna.d.ts +72 -0
  38. package/dist/fortuna.js +445 -0
  39. package/dist/index.d.ts +13 -0
  40. package/dist/index.js +44 -0
  41. package/dist/init.d.ts +11 -0
  42. package/dist/init.js +49 -0
  43. package/dist/loader.d.ts +4 -0
  44. package/dist/loader.js +30 -0
  45. package/dist/serpent/index.d.ts +65 -0
  46. package/dist/serpent/index.js +242 -0
  47. package/dist/serpent/seal.d.ts +8 -0
  48. package/dist/serpent/seal.js +70 -0
  49. package/dist/serpent/stream-encoder.d.ts +20 -0
  50. package/dist/serpent/stream-encoder.js +167 -0
  51. package/dist/serpent/stream-pool.d.ts +48 -0
  52. package/dist/serpent/stream-pool.js +285 -0
  53. package/dist/serpent/stream-sealer.d.ts +34 -0
  54. package/dist/serpent/stream-sealer.js +223 -0
  55. package/dist/serpent/stream.d.ts +28 -0
  56. package/dist/serpent/stream.js +205 -0
  57. package/dist/serpent/stream.worker.d.ts +32 -0
  58. package/dist/serpent/stream.worker.js +117 -0
  59. package/dist/serpent/types.d.ts +5 -0
  60. package/dist/serpent/types.js +1 -0
  61. package/dist/serpent.wasm +0 -0
  62. package/dist/sha2/hkdf.d.ts +16 -0
  63. package/dist/sha2/hkdf.js +108 -0
  64. package/dist/sha2/index.d.ts +40 -0
  65. package/dist/sha2/index.js +190 -0
  66. package/dist/sha2/types.d.ts +5 -0
  67. package/dist/sha2/types.js +1 -0
  68. package/dist/sha2.wasm +0 -0
  69. package/dist/sha3/index.d.ts +55 -0
  70. package/dist/sha3/index.js +246 -0
  71. package/dist/sha3/types.d.ts +5 -0
  72. package/dist/sha3/types.js +1 -0
  73. package/dist/sha3.wasm +0 -0
  74. package/dist/types.d.ts +24 -0
  75. package/dist/types.js +26 -0
  76. package/dist/utils.d.ts +26 -0
  77. package/dist/utils.js +169 -0
  78. package/package.json +90 -0
@@ -0,0 +1,509 @@
1
+ # SHA3 TypeScript API Reference
2
+
3
+ > [!NOTE]
4
+ > SHA-3 hash functions and SHAKE XOFs (TypeScript API)
5
+
6
+ ## Overview
7
+
8
+ The SHA-3 family provides six hash functions standardized in **FIPS 202**: four
9
+ fixed-output hash functions (SHA3-224, SHA3-256, SHA3-384, SHA3-512) and two
10
+ extendable-output functions, or XOFs (SHAKE128, SHAKE256). All six are built on
11
+ the **Keccak sponge construction** -- a fundamentally different design from the
12
+ Merkle-Damgard structure used by SHA-2.
13
+
14
+ SHA-3 is **not** a replacement for SHA-2. Both are considered secure, and both are
15
+ standardized by NIST. SHA-3 exists to provide **defense-in-depth**: if a flaw is
16
+ ever discovered in SHA-2, SHA-3 is completely unaffected because it uses a different
17
+ mathematical foundation. Think of it as insurance -- you may never need it, but if
18
+ you do, you will be very glad it is there.
19
+
20
+ The SHAKE XOFs are particularly flexible. Unlike SHA3-256, which always produces
21
+ exactly 32 bytes, SHAKE128 and SHAKE256 can produce variable-length output -- you
22
+ tell them how many bytes you want. This is useful for key derivation, generating
23
+ nonces, or any situation where you need more (or fewer) bytes than a standard hash
24
+ provides.
25
+
26
+ One key advantage of SHA-3 over SHA-2: **SHA-3 is immune to length extension
27
+ attacks.** With SHA-2, if you know `SHA256(secret + message)` but not the secret,
28
+ you can compute `SHA256(secret + message + padding + extra)` without knowing the
29
+ secret. SHA-3's sponge construction makes this impossible.
30
+
31
+ ---
32
+
33
+ ## Security Notes
34
+
35
+ > [!IMPORTANT]
36
+ > Read these before using the API. Misusing hash functions is one of the most
37
+ > common sources of security vulnerabilities.
38
+
39
+ - **Length extension immunity.** Unlike SHA-2, the SHA-3 sponge construction does
40
+ not leak enough internal state for length extension attacks. Computing
41
+ `SHA3(secret + message)` does not let an attacker forge `SHA3(secret + message + extra)`.
42
+ That said, **HMAC is still the correct way to build a MAC** -- do not use raw
43
+ `SHA3(key + message)` as a MAC construction, even though it is not vulnerable to
44
+ length extension. HMAC provides a formally proven security reduction.
45
+
46
+ - **SHAKE output is unbounded.** SHAKE128 and SHAKE256 are full XOFs — output
47
+ length is unbounded. Request any number of bytes via `hash()`, or drive the
48
+ sponge directly with `absorb()` / `squeeze()`. The only constraint is
49
+ `outputLength >= 1`.
50
+
51
+ - **Not for password hashing.** SHA-3 is a fast hash — that is the opposite of
52
+ what you want for password storage. Passwords must be hashed with a slow,
53
+ memory-hardened algorithm like **Argon2id**. See [argon2id.md](./argon2id.md) for
54
+ usage patterns including passphrase-based encryption with leviathan primitives.
55
+
56
+ - **Call `dispose()` when finished.** Every SHA-3 class wraps a WASM module that
57
+ stores Keccak state in linear memory. Calling `dispose()` zeroes all internal
58
+ state (the 200-byte lane matrix, input buffer, output buffer, and metadata).
59
+ If you skip `dispose()`, key material or intermediate hash state may persist
60
+ in memory.
61
+
62
+ ---
63
+
64
+ ## Module Init
65
+
66
+ Each module subpath exports its own init function for consumers who want
67
+ tree-shakeable imports.
68
+
69
+ ### `sha3Init(mode?, opts?)`
70
+
71
+ Initializes only the sha3 WASM binary. Equivalent to calling the
72
+ root `init(['sha3'], mode, opts)` but without pulling the other three
73
+ modules into the bundle.
74
+
75
+ **Signature:**
76
+
77
+ ```typescript
78
+ async function sha3Init(mode?: Mode, opts?: InitOpts): Promise<void>
79
+ ```
80
+
81
+ **Usage:**
82
+
83
+ ```typescript
84
+ import { sha3Init, SHA3_256 } from 'leviathan-crypto/sha3'
85
+
86
+ await sha3Init()
87
+ const sha3 = new SHA3_256()
88
+ ```
89
+
90
+ ---
91
+
92
+ ## API Reference
93
+
94
+ All SHA-3 classes require initialization before use. Either the root `init()`:
95
+
96
+ ```typescript
97
+ import { init } from 'leviathan-crypto'
98
+
99
+ await init('sha3')
100
+ ```
101
+
102
+ Both `init('sha3')` and `init(['sha3'])` are valid — the root `init()` accepts
103
+ a single `Module` string or an array.
104
+
105
+ Or the subpath `sha3Init()`:
106
+
107
+ ```typescript
108
+ import { sha3Init } from 'leviathan-crypto/sha3'
109
+
110
+ await sha3Init()
111
+ ```
112
+
113
+ If you use SHA-3 classes without calling `init()` first, the constructor
114
+ will throw an error.
115
+
116
+ ---
117
+
118
+ ### SHA3_224
119
+
120
+ Fixed-output hash function. Produces a **28-byte** (224-bit) digest.
121
+
122
+ ```typescript
123
+ class SHA3_224 {
124
+ constructor()
125
+ hash(msg: Uint8Array): Uint8Array // returns 28 bytes
126
+ dispose(): void
127
+ }
128
+ ```
129
+
130
+ ---
131
+
132
+ ### SHA3_256
133
+
134
+ Fixed-output hash function. Produces a **32-byte** (256-bit) digest. This is the
135
+ most commonly used SHA-3 variant -- 256-bit security is suitable for most
136
+ applications.
137
+
138
+ ```typescript
139
+ class SHA3_256 {
140
+ constructor()
141
+ hash(msg: Uint8Array): Uint8Array // returns 32 bytes
142
+ dispose(): void
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ### SHA3_384
149
+
150
+ Fixed-output hash function. Produces a **48-byte** (384-bit) digest.
151
+
152
+ ```typescript
153
+ class SHA3_384 {
154
+ constructor()
155
+ hash(msg: Uint8Array): Uint8Array // returns 48 bytes
156
+ dispose(): void
157
+ }
158
+ ```
159
+
160
+ ---
161
+
162
+ ### SHA3_512
163
+
164
+ Fixed-output hash function. Produces a **64-byte** (512-bit) digest. Use this when
165
+ you need the highest security margin.
166
+
167
+ ```typescript
168
+ class SHA3_512 {
169
+ constructor()
170
+ hash(msg: Uint8Array): Uint8Array // returns 64 bytes
171
+ dispose(): void
172
+ }
173
+ ```
174
+
175
+ ---
176
+
177
+ ### SHAKE128
178
+
179
+ Extendable-output function (XOF). Produces **variable-length** output — any
180
+ number of bytes you request. 128-bit security level.
181
+
182
+ ```typescript
183
+ class SHAKE128 {
184
+ constructor()
185
+ hash(msg: Uint8Array, outputLength: number): Uint8Array
186
+ absorb(msg: Uint8Array): this
187
+ squeeze(n: number): Uint8Array
188
+ reset(): this
189
+ dispose(): void
190
+ }
191
+ ```
192
+
193
+ | Method | Description |
194
+ |--------|-------------|
195
+ | `hash(msg, outputLength)` | One-shot: reset, absorb, squeeze. Safe on a dirty instance. |
196
+ | `absorb(msg)` | Feed data into the sponge. Chainable. Throws if called after `squeeze()`. |
197
+ | `squeeze(n)` | Pull `n` bytes of XOF output. Output is contiguous — `squeeze(a)` followed by `squeeze(b)` yields bytes `[0, a)` and `[a, a+b)` of the XOF stream. |
198
+ | `reset()` | Return to a fresh, zeroed state. Chainable. Safe at any point. |
199
+ | `dispose()` | Zero all WASM state and the TS-side block buffer. |
200
+
201
+ **`outputLength`** / **`n`** must be `>= 1`. Values below 1 throw a `RangeError`.
202
+
203
+ ---
204
+
205
+ ### SHAKE256
206
+
207
+ Extendable-output function (XOF). Produces **variable-length** output — any
208
+ number of bytes you request. 256-bit security level.
209
+
210
+ ```typescript
211
+ class SHAKE256 {
212
+ constructor()
213
+ hash(msg: Uint8Array, outputLength: number): Uint8Array
214
+ absorb(msg: Uint8Array): this
215
+ squeeze(n: number): Uint8Array
216
+ reset(): this
217
+ dispose(): void
218
+ }
219
+ ```
220
+
221
+ | Method | Description |
222
+ |--------|-------------|
223
+ | `hash(msg, outputLength)` | One-shot: reset, absorb, squeeze. Safe on a dirty instance. |
224
+ | `absorb(msg)` | Feed data into the sponge. Chainable. Throws if called after `squeeze()`. |
225
+ | `squeeze(n)` | Pull `n` bytes of XOF output. Output is contiguous — `squeeze(a)` followed by `squeeze(b)` yields bytes `[0, a)` and `[a, a+b)` of the XOF stream. |
226
+ | `reset()` | Return to a fresh, zeroed state. Chainable. Safe at any point. |
227
+ | `dispose()` | Zero all WASM state and the TS-side block buffer. |
228
+
229
+ **`outputLength`** / **`n`** must be `>= 1`. Values below 1 throw a `RangeError`.
230
+
231
+ ---
232
+
233
+ ## Incremental XOF API (`absorb` / `squeeze` / `reset`)
234
+
235
+ For use cases where you need to pull output in multiple steps — key derivation,
236
+ mask generation, protocol-specific domain separation — the SHAKE classes expose
237
+ a streaming interface alongside the one-shot `hash()`.
238
+
239
+ ### State machine
240
+
241
+ | State | Valid calls |
242
+ |-------------|---------------------------------|
243
+ | fresh | `absorb()`, `hash()`, `reset()` |
244
+ | absorbing | `absorb()`, `squeeze()`, `hash()`, `reset()` |
245
+ | squeezing | `squeeze()`, `hash()`, `reset()` |
246
+
247
+ Calling `absorb()` while squeezing throws:
248
+ `"SHAKE128: cannot absorb after squeeze — call reset() first"`
249
+
250
+ `hash()` always resets before running — safe to call on a dirty instance.
251
+
252
+ ### Example
253
+
254
+ ```typescript
255
+ import { init, SHAKE256 } from 'leviathan-crypto'
256
+
257
+ await init('sha3')
258
+
259
+ const xof = new SHAKE256()
260
+ xof.absorb(ikm) // input key material
261
+ xof.absorb(salt) // additional context
262
+
263
+ const encKey = xof.squeeze(32) // 256-bit encryption key
264
+ const macKey = xof.squeeze(32) // 256-bit MAC key
265
+ const nonce = xof.squeeze(12) // 96-bit nonce
266
+
267
+ xof.dispose()
268
+ ```
269
+
270
+ ---
271
+
272
+ ## Usage Examples
273
+
274
+ ### Example 1: Hash a string with SHA3-256
275
+
276
+ The most common use case -- hash some data and get a hex digest.
277
+
278
+ ```typescript
279
+ import { init, SHA3_256, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
280
+
281
+ // Initialize the SHA-3 WASM module (once, at startup)
282
+ await init('sha3')
283
+
284
+ // Create a hasher
285
+ const sha3 = new SHA3_256()
286
+
287
+ // Hash a UTF-8 string
288
+ const message = utf8ToBytes('Hello, world!')
289
+ const digest = sha3.hash(message)
290
+
291
+ console.log(bytesToHex(digest))
292
+ // 32 bytes (64 hex characters) of SHA3-256 output
293
+
294
+ // Clean up -- zeroes all WASM state
295
+ sha3.dispose()
296
+ ```
297
+
298
+ ---
299
+
300
+ ### Example 2: Hash binary data with SHA3-512
301
+
302
+ ```typescript
303
+ import { init, SHA3_512, bytesToHex } from 'leviathan-crypto'
304
+
305
+ await init('sha3')
306
+
307
+ const sha3 = new SHA3_512()
308
+
309
+ // Hash raw bytes (e.g., a file, a key, a nonce)
310
+ const data = new Uint8Array([0x01, 0x02, 0x03, 0x04])
311
+ const digest = sha3.hash(data)
312
+
313
+ console.log(bytesToHex(digest))
314
+ // 64 bytes (128 hex characters) of SHA3-512 output
315
+
316
+ sha3.dispose()
317
+ ```
318
+
319
+ ---
320
+
321
+ ### Example 3: Hash multiple messages
322
+
323
+ Each call to `hash()` is independent -- the internal state is reset automatically.
324
+ You can reuse the same class instance for multiple hashes.
325
+
326
+ ```typescript
327
+ import { init, SHA3_256, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
328
+
329
+ await init('sha3')
330
+
331
+ const sha3 = new SHA3_256()
332
+
333
+ const hash1 = sha3.hash(utf8ToBytes('first message'))
334
+ const hash2 = sha3.hash(utf8ToBytes('second message'))
335
+ const hash3 = sha3.hash(utf8ToBytes('first message'))
336
+
337
+ // hash1 and hash3 are identical -- same input, same output
338
+ console.log(bytesToHex(hash1) === bytesToHex(hash3)) // true
339
+
340
+ // hash2 is different -- different input
341
+ console.log(bytesToHex(hash1) === bytesToHex(hash2)) // false
342
+
343
+ sha3.dispose()
344
+ ```
345
+
346
+ ---
347
+
348
+ ### Example 4: SHAKE128 variable-length output
349
+
350
+ SHAKE lets you choose exactly how many bytes of output you need. This is useful
351
+ for key derivation or generating fixed-size tokens.
352
+
353
+ ```typescript
354
+ import { init, SHAKE128, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
355
+
356
+ await init('sha3')
357
+
358
+ const shake = new SHAKE128()
359
+
360
+ const seed = utf8ToBytes('my-application-seed')
361
+
362
+ // Derive a 16-byte key (128 bits)
363
+ const key128 = shake.hash(seed, 16)
364
+ console.log('16-byte key:', bytesToHex(key128))
365
+
366
+ // Derive a 32-byte key (256 bits) from the same seed
367
+ const key256 = shake.hash(seed, 32)
368
+ console.log('32-byte key:', bytesToHex(key256))
369
+
370
+ // The 16-byte output is NOT a prefix of the 32-byte output --
371
+ // each call resets state, re-absorbs, and squeezes independently.
372
+ // However, for SHAKE, the first 16 bytes of the 32-byte output
373
+ // ARE identical to the 16-byte output (this is how XOFs work).
374
+
375
+ shake.dispose()
376
+ ```
377
+
378
+ ---
379
+
380
+ ### Example 5: SHAKE256 for key derivation
381
+
382
+ ```typescript
383
+ import { init, SHAKE256, bytesToHex } from 'leviathan-crypto'
384
+
385
+ await init('sha3')
386
+
387
+ const shake = new SHAKE256()
388
+
389
+ // Derive a 48-byte key from raw entropy
390
+ const entropy = crypto.getRandomValues(new Uint8Array(32))
391
+ const derivedKey = shake.hash(entropy, 48)
392
+
393
+ console.log('Derived key:', bytesToHex(derivedKey))
394
+ // 48 bytes (96 hex characters) of SHAKE256 output
395
+
396
+ shake.dispose()
397
+ ```
398
+
399
+ ---
400
+
401
+ ### Example 6: SHA-256 vs SHA3-256 -- different algorithms, different output
402
+
403
+ SHA-256 (from the SHA-2 family) and SHA3-256 are completely different algorithms.
404
+ They produce different output for the same input. Neither is "better" -- both
405
+ are secure. SHA3-256 adds defense-in-depth.
406
+
407
+ ```typescript
408
+ import { init, SHA256, SHA3_256, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
409
+
410
+ // Initialize both modules
411
+ await init(['sha2', 'sha3'])
412
+
413
+ const sha2 = new SHA256()
414
+ const sha3 = new SHA3_256()
415
+
416
+ const message = utf8ToBytes('abc')
417
+
418
+ const sha2Digest = sha2.hash(message)
419
+ const sha3Digest = sha3.hash(message)
420
+
421
+ console.log('SHA-256: ', bytesToHex(sha2Digest))
422
+ console.log('SHA3-256: ', bytesToHex(sha3Digest))
423
+ // These are completely different values -- different algorithms
424
+
425
+ sha2.dispose()
426
+ sha3.dispose()
427
+ ```
428
+
429
+ ---
430
+
431
+ ### Example 7: Hashing empty input
432
+
433
+ All hash functions accept empty input. This is well-defined and produces a
434
+ deterministic output.
435
+
436
+ ```typescript
437
+ import { init, SHA3_256, bytesToHex } from 'leviathan-crypto'
438
+
439
+ await init('sha3')
440
+
441
+ const sha3 = new SHA3_256()
442
+ const digest = sha3.hash(new Uint8Array(0))
443
+
444
+ console.log(bytesToHex(digest))
445
+ // The SHA3-256 hash of empty input -- a fixed, known value
446
+
447
+ sha3.dispose()
448
+ ```
449
+
450
+ ---
451
+
452
+ ## Error Conditions
453
+
454
+ ### `init('sha3')` not called
455
+
456
+ If you construct a SHA-3 class before initializing the module, the constructor
457
+ throws immediately:
458
+
459
+ ```
460
+ Error: leviathan-crypto: call init(['sha3']) before using this class
461
+ ```
462
+
463
+ **Fix:** Call `await init('sha3')` once at application startup, before creating
464
+ any SHA-3 class instances.
465
+
466
+ ---
467
+
468
+ ### SHAKE output length out of range
469
+
470
+ SHAKE128 and SHAKE256 require `outputLength >= 1`. Passing 0 or a negative number
471
+ throws a `RangeError`:
472
+
473
+ ```
474
+ RangeError: outputLength must be >= 1 (got 0)
475
+ ```
476
+
477
+ **Fix:** Request at least 1 byte.
478
+
479
+ ---
480
+
481
+ ### SHAKE absorb after squeeze
482
+
483
+ Calling `absorb()` after `squeeze()` has been called throws an `Error`. The sponge
484
+ has been padded and finalized — further absorption is not meaningful.
485
+
486
+ ```
487
+ Error: SHAKE128: cannot absorb after squeeze — call reset() first
488
+ ```
489
+
490
+ **Fix:** Call `reset()` to return the instance to a fresh state before absorbing
491
+ new data.
492
+
493
+ ---
494
+
495
+ ### Empty input
496
+
497
+ Passing an empty `Uint8Array` (length 0) is **not** an error. All SHA-3 and SHAKE
498
+ functions produce valid, deterministic output for empty input. The sponge simply
499
+ absorbs zero bytes and then squeezes.
500
+
501
+ ---
502
+
503
+ ## Cross-References
504
+
505
+ - [README.md](./README.md): Project overview and quick-start guide
506
+ - [asm_sha3.md](./asm_sha3.md): WASM implementation details (buffer layout, Keccak internals, variant parameters)
507
+ - [sha2.md](./sha2.md): Alternative: SHA-2 family (SHA-256, SHA-384, SHA-512) and HMAC
508
+ - [utils.md](./utils.md): Encoding utilities: `bytesToHex`, `hexToBytes`, `utf8ToBytes`
509
+ - [architecture.md](./architecture.md): Library architecture and `init()` API
@@ -0,0 +1,198 @@
1
+ # Public TypeScript interfaces for cryptographic primitives
2
+
3
+ ## Overview
4
+
5
+ This module defines the abstract interfaces that all leviathan-crypto cryptographic classes implement. These are **type-only exports** -- they contain no runtime code and generate no JavaScript output.
6
+
7
+ Use these interfaces when you need to write generic code that works with any hash function, any cipher, or any AEAD scheme without depending on a specific implementation. They are available immediately on import with no `init()` call required.
8
+
9
+ ---
10
+
11
+ ## Security Notes
12
+
13
+ This module contains type definitions only. There are no security-sensitive operations.
14
+
15
+ ---
16
+
17
+ ## API Reference
18
+
19
+ ### Hash
20
+
21
+ ```typescript
22
+ interface Hash {
23
+ hash(msg: Uint8Array): Uint8Array;
24
+ dispose(): void;
25
+ }
26
+ ```
27
+
28
+ Interface for unkeyed hash functions (e.g., SHA-256, SHA-512, SHA-3).
29
+
30
+ | Method | Description |
31
+ |---|---|
32
+ | `hash(msg)` | Hashes the entire message and returns the digest as a new `Uint8Array`. |
33
+ | `dispose()` | Releases WASM resources and wipes internal buffers. Call when done. |
34
+
35
+ ---
36
+
37
+ ### KeyedHash
38
+
39
+ ```typescript
40
+ interface KeyedHash {
41
+ hash(key: Uint8Array, msg: Uint8Array): Uint8Array;
42
+ dispose(): void;
43
+ }
44
+ ```
45
+
46
+ Interface for keyed hash functions / MACs (e.g., HMAC-SHA256, HMAC-SHA512).
47
+
48
+ Note: `KeyedHash` does **not** extend `Hash`. Its `hash` method takes a `key` parameter in addition to the message.
49
+
50
+ | Method | Description |
51
+ |---|---|
52
+ | `hash(key, msg)` | Computes the keyed hash / MAC over `msg` using `key`. Returns the tag as a new `Uint8Array`. |
53
+ | `dispose()` | Releases WASM resources and wipes internal buffers. Call when done. |
54
+
55
+ ---
56
+
57
+ ### Blockcipher
58
+
59
+ ```typescript
60
+ interface Blockcipher {
61
+ encrypt(block: Uint8Array): Uint8Array;
62
+ decrypt(block: Uint8Array): Uint8Array;
63
+ dispose(): void;
64
+ }
65
+ ```
66
+
67
+ Interface for raw block ciphers (e.g., Serpent in ECB mode). Operates on single blocks.
68
+
69
+ | Method | Description |
70
+ |---|---|
71
+ | `encrypt(block)` | Encrypts a single block and returns the ciphertext. |
72
+ | `decrypt(block)` | Decrypts a single block and returns the plaintext. |
73
+ | `dispose()` | Releases WASM resources and wipes internal buffers (including expanded key schedule). |
74
+
75
+ ---
76
+
77
+ ### Streamcipher
78
+
79
+ ```typescript
80
+ interface Streamcipher {
81
+ encrypt(msg: Uint8Array): Uint8Array;
82
+ decrypt(msg: Uint8Array): Uint8Array;
83
+ dispose(): void;
84
+ }
85
+ ```
86
+
87
+ Interface for stream ciphers and block cipher streaming modes (e.g., Serpent-CTR, ChaCha20). Handles arbitrary-length messages.
88
+
89
+ | Method | Description |
90
+ |---|---|
91
+ | `encrypt(msg)` | Encrypts an arbitrary-length message. Returns the ciphertext (same length as input). |
92
+ | `decrypt(msg)` | Decrypts an arbitrary-length ciphertext. Returns the plaintext (same length as input). |
93
+ | `dispose()` | Releases WASM resources and wipes internal buffers. |
94
+
95
+ ---
96
+
97
+ ### AEAD
98
+
99
+ ```typescript
100
+ interface AEAD {
101
+ encrypt(msg: Uint8Array, aad?: Uint8Array): Uint8Array;
102
+ decrypt(ciphertext: Uint8Array, aad?: Uint8Array): Uint8Array;
103
+ dispose(): void;
104
+ }
105
+ ```
106
+
107
+ Interface for authenticated encryption with associated data (e.g., XChaCha20-Poly1305). Provides both confidentiality and integrity.
108
+
109
+ | Method | Description |
110
+ |---|---|
111
+ | `encrypt(msg, aad?)` | Encrypts `msg` and authenticates both `msg` and optional `aad`. Returns ciphertext with appended authentication tag. |
112
+ | `decrypt(ciphertext, aad?)` | Decrypts and verifies the authentication tag. Returns plaintext on success. Throws `Error` on authentication failure — never returns null. |
113
+ | `dispose()` | Releases WASM resources and wipes internal buffers. |
114
+
115
+ ---
116
+
117
+ ## Usage Examples
118
+
119
+ ### Type-constraining a function parameter
120
+
121
+ ```typescript
122
+ import type { Hash } from 'leviathan-crypto'
123
+
124
+ function digestAndLog(hasher: Hash, data: Uint8Array): Uint8Array {
125
+ const digest = hasher.hash(data)
126
+ console.log('digest length:', digest.length)
127
+ return digest
128
+ }
129
+ ```
130
+
131
+ This function accepts any `Hash` implementation -- `SHA256`, `SHA512`, `SHA3_256`, etc. -- without importing any of them directly.
132
+
133
+ ---
134
+
135
+ ### Accepting any AEAD scheme
136
+
137
+ ```typescript
138
+ import type { AEAD } from 'leviathan-crypto'
139
+
140
+ function sealMessage(aead: AEAD, plaintext: Uint8Array, metadata: Uint8Array): Uint8Array {
141
+ return aead.encrypt(plaintext, metadata)
142
+ }
143
+
144
+ function openMessage(aead: AEAD, ciphertext: Uint8Array, metadata: Uint8Array): Uint8Array {
145
+ // decrypt() throws on auth failure — no null check needed
146
+ return aead.decrypt(ciphertext, metadata)
147
+ }
148
+ ```
149
+
150
+ ---
151
+
152
+ ### Generic keyed-hash wrapper
153
+
154
+ ```typescript
155
+ import type { KeyedHash } from 'leviathan-crypto'
156
+
157
+ function authenticate(mac: KeyedHash, key: Uint8Array, ...parts: Uint8Array[]): Uint8Array {
158
+ // Concatenate all message parts, then compute the tag
159
+ const total = parts.reduce((sum, p) => sum + p.length, 0)
160
+ const msg = new Uint8Array(total)
161
+ let offset = 0
162
+ for (const part of parts) {
163
+ msg.set(part, offset)
164
+ offset += part.length
165
+ }
166
+ return mac.hash(key, msg)
167
+ }
168
+ ```
169
+
170
+ ---
171
+
172
+ ### Storing a cipher with its interface type
173
+
174
+ ```typescript
175
+ import type { Streamcipher, Blockcipher } from 'leviathan-crypto'
176
+
177
+ interface EncryptionContext {
178
+ cipher: Streamcipher | Blockcipher
179
+ mode: 'stream' | 'block'
180
+ }
181
+
182
+ function cleanup(ctx: EncryptionContext): void {
183
+ ctx.cipher.dispose()
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Cross-References
190
+
191
+ - [README.md](./README.md) — library documentation index and exports table
192
+ - [architecture.md](./architecture.md) — module structure and correctness contracts
193
+ - [utils.md](./utils.md) — encoding utilities and `constantTimeEqual` for verifying MACs from `KeyedHash`
194
+ - [serpent.md](./serpent.md) — Serpent classes implement `Blockcipher`, `Streamcipher`, and `AEAD`
195
+ - [chacha20.md](./chacha20.md) — ChaCha20/Poly1305 classes implement `Streamcipher` and `AEAD`
196
+ - [sha2.md](./sha2.md) — SHA-2 classes implement `Hash`; HMAC classes implement `KeyedHash`
197
+ - [sha3.md](./sha3.md) — SHA-3 classes implement `Hash`; SHAKE classes extend with XOF API
198
+ - [test-suite.md](./test-suite.md) — test suite structure and vector corpus