leviathan-crypto 2.0.0 → 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.
- package/CLAUDE.md +171 -7
- package/LICENSE +4 -0
- package/README.md +109 -54
- package/SECURITY.md +125 -233
- package/dist/chacha20/cipher-suite.d.ts +10 -0
- package/dist/chacha20/cipher-suite.js +66 -2
- package/dist/chacha20/generator.d.ts +12 -0
- package/dist/chacha20/generator.js +91 -0
- package/dist/chacha20/index.d.ts +97 -1
- package/dist/chacha20/index.js +139 -11
- package/dist/chacha20/ops.d.ts +57 -6
- package/dist/chacha20/ops.js +93 -13
- package/dist/chacha20/pool-worker.js +12 -0
- package/dist/chacha20/types.d.ts +1 -32
- package/dist/ct-wasm.js +1 -1
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +69 -26
- package/dist/docs/architecture.md +600 -520
- package/dist/docs/argon2id.md +17 -14
- package/dist/docs/chacha20.md +146 -39
- package/dist/docs/exports.md +46 -10
- package/dist/docs/fortuna.md +339 -122
- package/dist/docs/init.md +24 -25
- package/dist/docs/loader.md +142 -47
- package/dist/docs/serpent.md +139 -41
- package/dist/docs/sha2.md +77 -19
- package/dist/docs/sha3.md +81 -15
- package/dist/docs/types.md +156 -15
- package/dist/docs/utils.md +171 -81
- package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
- package/dist/embedded/chacha20-pool-worker.js +5 -0
- package/dist/embedded/kyber.d.ts +1 -1
- package/dist/embedded/kyber.js +1 -1
- package/dist/embedded/serpent-pool-worker.d.ts +1 -0
- package/dist/embedded/serpent-pool-worker.js +5 -0
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +1 -1
- package/dist/fortuna.d.ts +14 -8
- package/dist/fortuna.js +144 -50
- package/dist/index.d.ts +8 -6
- package/dist/index.js +6 -5
- package/dist/init.d.ts +0 -2
- package/dist/init.js +83 -3
- package/dist/kyber/indcpa.js +4 -4
- package/dist/kyber/index.js +25 -5
- package/dist/kyber/kem.js +56 -1
- package/dist/kyber/suite.d.ts +1 -2
- package/dist/kyber/suite.js +1 -0
- package/dist/kyber/types.d.ts +1 -0
- package/dist/kyber/validate.d.ts +8 -4
- package/dist/kyber/validate.js +18 -14
- package/dist/kyber.wasm +0 -0
- package/dist/loader.d.ts +7 -2
- package/dist/loader.js +25 -28
- package/dist/ratchet/index.d.ts +6 -0
- package/dist/ratchet/index.js +37 -0
- package/dist/ratchet/kdf-chain.d.ts +13 -0
- package/dist/ratchet/kdf-chain.js +85 -0
- package/dist/ratchet/ratchet-keypair.d.ts +9 -0
- package/dist/ratchet/ratchet-keypair.js +61 -0
- package/dist/ratchet/root-kdf.d.ts +4 -0
- package/dist/ratchet/root-kdf.js +124 -0
- package/dist/ratchet/skipped-key-store.d.ts +14 -0
- package/dist/ratchet/skipped-key-store.js +154 -0
- package/dist/ratchet/types.d.ts +36 -0
- package/dist/ratchet/types.js +26 -0
- package/dist/serpent/cipher-suite.d.ts +10 -0
- package/dist/serpent/cipher-suite.js +136 -50
- package/dist/serpent/generator.d.ts +12 -0
- package/dist/serpent/generator.js +97 -0
- package/dist/serpent/index.d.ts +61 -1
- package/dist/serpent/index.js +92 -7
- package/dist/serpent/pool-worker.js +25 -95
- package/dist/serpent/serpent-cbc.d.ts +14 -4
- package/dist/serpent/serpent-cbc.js +58 -34
- package/dist/serpent/shared-ops.d.ts +83 -0
- package/dist/serpent/shared-ops.js +213 -0
- package/dist/serpent/types.d.ts +1 -5
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/hash.d.ts +2 -0
- package/dist/sha2/hash.js +53 -0
- package/dist/sha2/index.d.ts +1 -0
- package/dist/sha2/index.js +15 -1
- package/dist/sha3/hash.d.ts +2 -0
- package/dist/sha3/hash.js +53 -0
- package/dist/sha3/index.d.ts +17 -2
- package/dist/sha3/index.js +79 -7
- package/dist/stream/header.js +5 -5
- package/dist/stream/open-stream.js +36 -14
- package/dist/stream/seal-stream-pool.d.ts +1 -0
- package/dist/stream/seal-stream-pool.js +47 -8
- package/dist/stream/seal-stream.js +29 -11
- package/dist/stream/types.d.ts +1 -0
- package/dist/types.d.ts +21 -0
- package/dist/utils.d.ts +7 -8
- package/dist/utils.js +73 -40
- package/dist/wasm-source.d.ts +9 -8
- package/package.json +79 -64
package/dist/docs/serpent.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
4
|
-
|
|
3
|
+
### Serpent-256 TypeScript API
|
|
4
|
+
|
|
5
|
+
See [Serpent implementation audit](./serpent_audit.md) for algorithm correctness verifications.
|
|
5
6
|
|
|
6
7
|
> ### Table of Contents
|
|
7
8
|
> - [Overview](#overview)
|
|
8
9
|
> - [Security Notes](#security-notes)
|
|
9
10
|
> - [Module Init](#module-init)
|
|
10
11
|
> - [API Reference](#api-reference)
|
|
12
|
+
> - [SerpentGenerator](#serpentgenerator)
|
|
11
13
|
> - [Usage Examples](#usage-examples)
|
|
12
14
|
> - [Error Conditions](#error-conditions)
|
|
13
15
|
|
|
@@ -44,12 +46,10 @@ corrupted plaintext on decryption. Decryption succeeds without any indication of
|
|
|
44
46
|
tampering. There is no integrity check. Your caller receives garbage and has no way
|
|
45
47
|
to distinguish it from the original message.
|
|
46
48
|
|
|
47
|
-
`Seal` with `SerpentCipher`
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`Seal` with `XChaCha20Cipher` is an alternative using a different cipher.
|
|
52
|
-
See [chacha20.md](./chacha20.md).
|
|
49
|
+
[`Seal`](./aead.md#seal) with [`SerpentCipher`](./ciphersuite.md#serpentcipher)
|
|
50
|
+
eliminates this problem. It computes an HMAC tag over the ciphertext and
|
|
51
|
+
verifies it before decryption. If anything has been modified, `Seal.decrypt()`
|
|
52
|
+
throws instead of returning corrupted data.
|
|
53
53
|
|
|
54
54
|
### Never reuse a nonce or IV with the same key
|
|
55
55
|
|
|
@@ -58,13 +58,13 @@ same keystream, which means an attacker can XOR two ciphertexts together and
|
|
|
58
58
|
recover both plaintexts. Always generate a fresh random nonce for each message.
|
|
59
59
|
In CBC mode, the IV must be random and unpredictable for each encryption. A predictable IV enables chosen-plaintext attacks.
|
|
60
60
|
|
|
61
|
-
Use `randomBytes(16)` to generate nonces and IVs. `Seal` with `SerpentCipher` handles IV generation internally.
|
|
61
|
+
Use `randomBytes(16)` to generate nonces and IVs. [`Seal`](./aead.md#seal) with [`SerpentCipher`](./ciphersuite.md#serpentcipher) handles IV generation internally.
|
|
62
62
|
|
|
63
63
|
### Always use 256-bit keys
|
|
64
64
|
|
|
65
65
|
Unless you have a specific reason to use a shorter key, pass a 32-byte key to
|
|
66
66
|
every Serpent operation. Shorter keys provide less security margin and there is no
|
|
67
|
-
meaningful performance benefit to using them. `SerpentCipher` requires a 32-byte key; HKDF derives enc/mac/iv keys internally.
|
|
67
|
+
meaningful performance benefit to using them. [`SerpentCipher`](./ciphersuite.md#serpentcipher) requires a 32-byte key; HKDF derives enc/mac/iv keys internally.
|
|
68
68
|
|
|
69
69
|
### Call dispose() when done
|
|
70
70
|
|
|
@@ -127,7 +127,7 @@ Requires `init({ serpent: serpentWasm, sha2: sha2Wasm })`.
|
|
|
127
127
|
|
|
128
128
|
#### `SerpentCipher.keygen(): Uint8Array`
|
|
129
129
|
|
|
130
|
-
Returns `randomBytes(32)`. Convenience method. Not on the `CipherSuite` interface.
|
|
130
|
+
Returns `randomBytes(32)`. Convenience method. Not on the [`CipherSuite`](./ciphersuite.md) interface.
|
|
131
131
|
|
|
132
132
|
#### Usage with `Seal`
|
|
133
133
|
|
|
@@ -166,7 +166,7 @@ See [aead.md](./aead.md) for the full `Seal`, `SealStream`, and `OpenStream` API
|
|
|
166
166
|
### Serpent
|
|
167
167
|
|
|
168
168
|
Raw Serpent block encryption and decryption. Operates on exactly 16-byte blocks.
|
|
169
|
-
This class is a low-level building block
|
|
169
|
+
This class is a low-level building block
|
|
170
170
|
|
|
171
171
|
```typescript
|
|
172
172
|
class Serpent {
|
|
@@ -223,9 +223,17 @@ stream of chunks.
|
|
|
223
223
|
|
|
224
224
|
> [!WARNING]
|
|
225
225
|
> CTR mode is unauthenticated. An attacker can modify ciphertext
|
|
226
|
-
> without detection. Use `Seal` with `SerpentCipher` for authenticated encryption, or pair
|
|
226
|
+
> without detection. Use [`Seal`](./aead.md#seal) with [`SerpentCipher`](./ciphersuite.md#serpentcipher) for authenticated encryption, or pair
|
|
227
227
|
> with HMAC-SHA256 (Encrypt-then-MAC).
|
|
228
228
|
|
|
229
|
+
> [!CAUTION]
|
|
230
|
+
> `SerpentCtr` is stateful and holds exclusive access to the `serpent` WASM
|
|
231
|
+
> module for its entire lifetime. Constructing a second `SerpentCtr`/
|
|
232
|
+
> `SerpentCbc`, `SerpentCipher` usage (`Seal.encrypt(SerpentCipher, ...)`,
|
|
233
|
+
> `SealStream` with `SerpentCipher`), or any atomic serpent class
|
|
234
|
+
> (`Serpent` block) while this instance is live throws. Call `dispose()`
|
|
235
|
+
> when done. Pool workers are unaffected.
|
|
236
|
+
|
|
229
237
|
```typescript
|
|
230
238
|
class SerpentCtr {
|
|
231
239
|
constructor(opts: { dangerUnauthenticated: true })
|
|
@@ -268,7 +276,7 @@ automatically.
|
|
|
268
276
|
- **chunk**: any length up to the module's internal chunk buffer size. Throws `RangeError` if the chunk exceeds the maximum size.
|
|
269
277
|
|
|
270
278
|
> [!NOTE]
|
|
271
|
-
>
|
|
279
|
+
> Always uses the 4-wide SIMD path (`encryptChunk_simd`). SIMD is required by the serpent module; `init()` throws on runtimes without WebAssembly SIMD support.
|
|
272
280
|
|
|
273
281
|
---
|
|
274
282
|
|
|
@@ -294,6 +302,11 @@ Functionally identical to `encryptChunk()`.
|
|
|
294
302
|
|
|
295
303
|
Wipes all key material and intermediate state from WASM memory.
|
|
296
304
|
|
|
305
|
+
After `dispose()`, all instance methods (`beginEncrypt`, `encryptChunk`,
|
|
306
|
+
`beginDecrypt`, `decryptChunk`) throw `Error: SerpentCtr: instance has been
|
|
307
|
+
disposed`. Disposal is permanent; construct a new instance if you need to
|
|
308
|
+
continue.
|
|
309
|
+
|
|
297
310
|
---
|
|
298
311
|
|
|
299
312
|
### SerpentCbc
|
|
@@ -303,7 +316,14 @@ Encrypts and decrypts entire messages in a single call.
|
|
|
303
316
|
|
|
304
317
|
> [!WARNING]
|
|
305
318
|
> CBC mode is unauthenticated. Always authenticate the output with
|
|
306
|
-
> HMAC-SHA256 (Encrypt-then-MAC) or use `Seal` with `SerpentCipher` instead.
|
|
319
|
+
> HMAC-SHA256 (Encrypt-then-MAC) or use [`Seal`](./aead.md#seal) with [`SerpentCipher`](./ciphersuite.md#serpentcipher) instead.
|
|
320
|
+
|
|
321
|
+
> [!CAUTION]
|
|
322
|
+
> `SerpentCbc` is stateful and holds exclusive access to the `serpent` WASM
|
|
323
|
+
> module for its entire lifetime. Constructing a second `SerpentCbc`/
|
|
324
|
+
> `SerpentCtr`, `SerpentCipher` usage (which internally constructs a
|
|
325
|
+
> `SerpentCbc`), or any atomic serpent class (`Serpent` block) while this
|
|
326
|
+
> instance is live throws. Call `dispose()` when done.
|
|
307
327
|
|
|
308
328
|
```typescript
|
|
309
329
|
class SerpentCbc {
|
|
@@ -346,12 +366,12 @@ Decrypts Serpent CBC ciphertext and strips PKCS7 padding.
|
|
|
346
366
|
|
|
347
367
|
- **key**: 16, 24, or 32 bytes. Throws `RangeError` if the length is invalid.
|
|
348
368
|
- **iv**: exactly 16 bytes. Must match the IV used for encryption. Throws `RangeError` if the length is not 16.
|
|
349
|
-
- **ciphertext**: must be a non-zero multiple of 16 bytes. Throws `RangeError`
|
|
369
|
+
- **ciphertext**: must be a non-zero multiple of 16 bytes. Throws `RangeError` with the generic message `'invalid ciphertext'` on any failure — zero length, non-multiple-of-16 length, or invalid PKCS7 padding. The single message and branch-free padding check close the Vaudenay 2002 padding-oracle surface; a caller cannot distinguish failure modes by message or by timing.
|
|
350
370
|
|
|
351
371
|
Returns the decrypted plaintext as a new `Uint8Array`.
|
|
352
372
|
|
|
353
373
|
> [!NOTE]
|
|
354
|
-
>
|
|
374
|
+
> Decryption always uses the 4-wide SIMD path (`cbcDecryptChunk_simd`). SIMD is required by the serpent module; `init()` throws on runtimes without it. CBC encryption has no SIMD variant because each ciphertext block depends on the previous one.
|
|
355
375
|
|
|
356
376
|
---
|
|
357
377
|
|
|
@@ -359,6 +379,81 @@ Returns the decrypted plaintext as a new `Uint8Array`.
|
|
|
359
379
|
|
|
360
380
|
Wipes all key material and intermediate state from WASM memory.
|
|
361
381
|
|
|
382
|
+
After `dispose()`, `encrypt` and `decrypt` throw `Error: SerpentCbc: instance
|
|
383
|
+
has been disposed`. Disposal is permanent; construct a new instance if you
|
|
384
|
+
need to continue.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
### Security — direct use of `SerpentCbc`
|
|
389
|
+
|
|
390
|
+
`SerpentCbc` is unauthenticated. If you use it directly via
|
|
391
|
+
`{ dangerUnauthenticated: true }`, you are responsible for:
|
|
392
|
+
|
|
393
|
+
1. Authenticating the ciphertext (HMAC-SHA256 in Encrypt-then-MAC order)
|
|
394
|
+
2. Verifying the HMAC **before** calling `decrypt()`
|
|
395
|
+
3. Using a unique, random IV per (key, message)
|
|
396
|
+
|
|
397
|
+
`SerpentCbc.decrypt()` throws a single generic `'invalid ciphertext'`
|
|
398
|
+
error for all padding failures and runs its validation in constant time over the
|
|
399
|
+
final 16 bytes. This mitigates padding-oracle attacks (Vaudenay 2002) on callers
|
|
400
|
+
that surface errors to remote parties. The authenticated composition
|
|
401
|
+
`SerpentCipher` always verifies HMAC before any PKCS7 processing and is the
|
|
402
|
+
recommended path.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## SerpentGenerator
|
|
407
|
+
|
|
408
|
+
Serpent-256 ECB counter-mode PRF for Fortuna's generator slot. Implements the
|
|
409
|
+
`Generator` interface (Practical Cryptography, Ferguson & Schneier 2003 §9.4).
|
|
410
|
+
This is a plain `const` object, not a class — no instantiation, no `dispose()`.
|
|
411
|
+
|
|
412
|
+
Requires `init({ serpent: serpentWasm })`. See [fortuna.md](./fortuna.md) for
|
|
413
|
+
full usage with `Fortuna.create()`.
|
|
414
|
+
|
|
415
|
+
| Property | Value |
|
|
416
|
+
|----------|-------|
|
|
417
|
+
| `keySize` | `32` |
|
|
418
|
+
| `blockSize` | `16` |
|
|
419
|
+
| `counterSize` | `16` |
|
|
420
|
+
| `wasmModules` | `['serpent']` |
|
|
421
|
+
|
|
422
|
+
### `SerpentGenerator.generate(key, counter, n): Uint8Array`
|
|
423
|
+
|
|
424
|
+
Produces `n` bytes of pseudorandom output from `(key, counter)`. Neither input
|
|
425
|
+
is mutated. Wipes WASM key/key-schedule/scratch and the JS-heap counter copy
|
|
426
|
+
before returning.
|
|
427
|
+
|
|
428
|
+
| Parameter | Type | Description |
|
|
429
|
+
|-----------|------|-------------|
|
|
430
|
+
| `key` | `Uint8Array` | 32 bytes (256-bit Serpent key) |
|
|
431
|
+
| `counter` | `Uint8Array` | 16 bytes, treated as a little-endian integer |
|
|
432
|
+
| `n` | `number` | Output byte count: 0 ≤ n ≤ 2³⁰ |
|
|
433
|
+
|
|
434
|
+
**Returns** a new `Uint8Array` of length `n`.
|
|
435
|
+
|
|
436
|
+
**Throws:**
|
|
437
|
+
- `RangeError('SerpentGenerator: key must be 32 bytes (got N)')` if key length ≠ 32
|
|
438
|
+
- `RangeError('SerpentGenerator: counter must be 16 bytes (got N)')` if counter length ≠ 16
|
|
439
|
+
- `RangeError('SerpentGenerator: n must be a non-negative safe integer <= 2^30 (got N)')` if n is out of range
|
|
440
|
+
- `Error` if another stateful instance currently owns the `serpent` WASM module
|
|
441
|
+
|
|
442
|
+
### Usage with `Fortuna`
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { init, Fortuna } from 'leviathan-crypto'
|
|
446
|
+
import { SerpentGenerator } from 'leviathan-crypto/serpent'
|
|
447
|
+
import { SHA256Hash } from 'leviathan-crypto/sha2'
|
|
448
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
449
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
450
|
+
|
|
451
|
+
await init({ serpent: serpentWasm, sha2: sha2Wasm })
|
|
452
|
+
const rng = await Fortuna.create({ generator: SerpentGenerator, hash: SHA256Hash })
|
|
453
|
+
const bytes = rng.get(32)
|
|
454
|
+
rng.stop()
|
|
455
|
+
```
|
|
456
|
+
|
|
362
457
|
---
|
|
363
458
|
|
|
364
459
|
## Usage Examples
|
|
@@ -383,9 +478,8 @@ console.log(new TextDecoder().decode(decrypted))
|
|
|
383
478
|
|
|
384
479
|
### Example 2: CTR mode (advanced)
|
|
385
480
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
plaintext with no padding overhead.
|
|
481
|
+
Use `SerpentCtr` to encrypt data of any length. CTR mode produces ciphertext
|
|
482
|
+
the same length as the plaintext with no padding overhead.
|
|
389
483
|
|
|
390
484
|
```typescript
|
|
391
485
|
import { init, SerpentCtr, randomBytes } from 'leviathan-crypto';
|
|
@@ -417,12 +511,11 @@ ctr.dispose();
|
|
|
417
511
|
|
|
418
512
|
> [!IMPORTANT]
|
|
419
513
|
> CTR mode is unauthenticated. An attacker can tamper with the
|
|
420
|
-
> ciphertext without detection. Use
|
|
514
|
+
> ciphertext without detection. Use [`Seal`](./aead.md#seal) with [`SerpentCipher`](./ciphersuite.md#serpentcipher) for authenticated encryption.
|
|
421
515
|
|
|
422
516
|
### Example 3: CBC mode (advanced)
|
|
423
517
|
|
|
424
|
-
|
|
425
|
-
message-level encryption with automatic PKCS7 padding.
|
|
518
|
+
Use `SerpentCbc` for message-level encryption with automatic PKCS7 padding.
|
|
426
519
|
|
|
427
520
|
```typescript
|
|
428
521
|
import { init, SerpentCbc, randomBytes } from 'leviathan-crypto';
|
|
@@ -448,12 +541,11 @@ cbc.dispose();
|
|
|
448
541
|
```
|
|
449
542
|
|
|
450
543
|
> [!IMPORTANT]
|
|
451
|
-
> CBC mode is unauthenticated. Use `Seal` with `SerpentCipher` for authenticated encryption.
|
|
544
|
+
> CBC mode is unauthenticated. Use [`Seal`](./aead.md#seal) with [`SerpentCipher`](./ciphersuite.md#serpentcipher) for authenticated encryption.
|
|
452
545
|
|
|
453
546
|
### Example 4: Raw block operations (low-level)
|
|
454
547
|
|
|
455
548
|
Use the `Serpent` class for single 16-byte block operations. This is the lowest-level
|
|
456
|
-
API; use `Seal` with `SerpentCipher` for most purposes.
|
|
457
549
|
|
|
458
550
|
```typescript
|
|
459
551
|
import { init, Serpent } from 'leviathan-crypto';
|
|
@@ -499,21 +591,27 @@ cipher.dispose();
|
|
|
499
591
|
| Nonce is not 16 bytes (`SerpentCtr`) | `RangeError` | `nonce must be 16 bytes (got N)` |
|
|
500
592
|
| Chunk exceeds buffer size (`SerpentCtr`) | `RangeError` | `chunk exceeds maximum size of N bytes — split into smaller chunks` |
|
|
501
593
|
| IV is not 16 bytes (`SerpentCbc`) | `RangeError` | `CBC IV must be 16 bytes (got N)` |
|
|
502
|
-
| Ciphertext length
|
|
503
|
-
|
|
|
594
|
+
| Ciphertext length zero, not a multiple of 16, or PKCS7 padding invalid (`SerpentCbc.decrypt`) | `RangeError` | `invalid ciphertext` (same message for every failure mode — no numeric leak) |
|
|
595
|
+
| `SerpentGenerator.generate()` key ≠ 32 bytes | `RangeError` | `SerpentGenerator: key must be 32 bytes (got N)` |
|
|
596
|
+
| `SerpentGenerator.generate()` counter ≠ 16 bytes | `RangeError` | `SerpentGenerator: counter must be 16 bytes (got N)` |
|
|
597
|
+
| `SerpentGenerator.generate()` n out of range | `RangeError` | `SerpentGenerator: n must be a non-negative safe integer <= 2^30 (got N)` |
|
|
504
598
|
|
|
505
599
|
---
|
|
506
600
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
601
|
+
|
|
602
|
+
## Cross-References
|
|
603
|
+
|
|
604
|
+
| Document | Description |
|
|
605
|
+
| -------- | ----------- |
|
|
606
|
+
| [index](./README.md) | Project Documentation index |
|
|
607
|
+
| [lexicon](./lexicon.md) | Glossary of cryptographic terms |
|
|
608
|
+
| [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
|
|
609
|
+
| [asm_serpent](./asm_serpent.md) | WASM implementation details and buffer layout |
|
|
610
|
+
| [serpent_reference](./serpent_reference.md) | algorithm specification, S-boxes, linear transform, and known attacks |
|
|
611
|
+
| [serpent_audit](./serpent_audit.md) | security audit findings (correctness, side-channel analysis) |
|
|
612
|
+
| [authenticated encryption](./aead.md) | `Seal`, `SealStream`, `OpenStream`: use `SerpentCipher` as the suite argument |
|
|
613
|
+
| [chacha20](./chacha20.md) | `XChaCha20Cipher`: alternative `CipherSuite` for `Seal` and streaming |
|
|
614
|
+
| [sha2](./sha2.md) | HMAC-SHA256 and HKDF used internally by `SerpentCipher` |
|
|
615
|
+
| [types](./types.md) | `Blockcipher`, `Streamcipher`, and `AEAD` interfaces implemented by Serpent classes |
|
|
616
|
+
| [utils](./utils.md) | `constantTimeEqual`, `wipe`, `randomBytes` used by Serpent wrappers |
|
|
617
|
+
|
package/dist/docs/sha2.md
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
>
|
|
7
|
-
> See [SHA-2 implementation audit](./sha2_audit.md), [HMAC audit](./hmac_audit.md), and [HKDF audit](./hkdf_audit.md) for algorithm correctness verifications.
|
|
3
|
+
### SHA-2 TypeScript API
|
|
4
|
+
|
|
5
|
+
Cryptographic hashing and message authentication using SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512.
|
|
8
6
|
|
|
9
7
|
> ### Table of Contents
|
|
10
8
|
> - [Overview](#overview)
|
|
11
9
|
> - [Security Notes](#security-notes)
|
|
12
10
|
> - [Module Init](#module-init)
|
|
13
11
|
> - [API Reference](#api-reference)
|
|
12
|
+
> - [SHA256Hash](#sha256hash)
|
|
14
13
|
> - [Usage Examples](#usage-examples)
|
|
15
14
|
> - [Error Conditions](#error-conditions)
|
|
16
15
|
|
|
@@ -113,6 +112,9 @@ time (this is called a **timing attack**).
|
|
|
113
112
|
Use `constantTimeEqual()` from leviathan-crypto instead. It always compares
|
|
114
113
|
every byte regardless of where the first difference is.
|
|
115
114
|
|
|
115
|
+
Note: `constantTimeEqual` requires WebAssembly SIMD; on non-SIMD runtimes it
|
|
116
|
+
throws at first call. See [utils.md](./utils.md#constanttimeequal).
|
|
117
|
+
|
|
116
118
|
### Call dispose() when you are done
|
|
117
119
|
|
|
118
120
|
`dispose()` calls `wipeBuffers()` in the WASM module, which zeroes out all
|
|
@@ -120,6 +122,9 @@ internal buffers including hash state and key material. This prevents sensitive
|
|
|
120
122
|
data from lingering in memory. Always call `dispose()` when you are finished
|
|
121
123
|
with a hash or HMAC instance.
|
|
122
124
|
|
|
125
|
+
> [!NOTE]
|
|
126
|
+
> See [SHA-2 implementation audit](./sha2_audit.md), [HMAC audit](./hmac_audit.md), and [HKDF audit](./hkdf_audit.md) for algorithm correctness verifications.
|
|
127
|
+
|
|
123
128
|
---
|
|
124
129
|
|
|
125
130
|
## Module Init
|
|
@@ -323,10 +328,16 @@ class HKDF_SHA512 {
|
|
|
323
328
|
}
|
|
324
329
|
```
|
|
325
330
|
|
|
331
|
+
**`constructor()`** Creates a new `HKDF_SHA512` instance. Throws if `init({ sha2: sha2Wasm })` has not been called.
|
|
332
|
+
|
|
326
333
|
**`extract(salt, ikm)`** If `salt` is `null` or empty, defaults to 64 zero bytes.
|
|
327
334
|
|
|
328
335
|
**`expand(prk, info, length)`** PRK must be exactly 64 bytes. `length` must be between 1 and 16320. Throws `RangeError` otherwise.
|
|
329
336
|
|
|
337
|
+
**`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.
|
|
338
|
+
|
|
339
|
+
**`dispose(): void`** Releases the internal HMAC instance.
|
|
340
|
+
|
|
330
341
|
> [!NOTE]
|
|
331
342
|
> HKDF is a pure TypeScript composition over the WASM-backed HMAC classes.
|
|
332
343
|
> It does not introduce new WASM code or new `init()` modules. Initializing
|
|
@@ -353,6 +364,49 @@ hkdf.dispose()
|
|
|
353
364
|
|
|
354
365
|
---
|
|
355
366
|
|
|
367
|
+
## SHA256Hash
|
|
368
|
+
|
|
369
|
+
Stateless SHA-256 `HashFn` for Fortuna's accumulator and reseed slots. Plain
|
|
370
|
+
`const` object — no instantiation, no `dispose()`.
|
|
371
|
+
|
|
372
|
+
Requires `init({ sha2: sha2Wasm })`. See [fortuna.md](./fortuna.md) for full
|
|
373
|
+
usage with `Fortuna.create()`.
|
|
374
|
+
|
|
375
|
+
| Property | Value |
|
|
376
|
+
|----------|-------|
|
|
377
|
+
| `outputSize` | `32` |
|
|
378
|
+
| `wasmModules` | `['sha2']` |
|
|
379
|
+
|
|
380
|
+
### `SHA256Hash.digest(msg): Uint8Array`
|
|
381
|
+
|
|
382
|
+
Hashes `msg` and returns a 32-byte SHA-256 digest. Wipes WASM input/output/state
|
|
383
|
+
scratch before returning.
|
|
384
|
+
|
|
385
|
+
| Parameter | Type | Description |
|
|
386
|
+
|-----------|------|-------------|
|
|
387
|
+
| `msg` | `Uint8Array` | Message to hash (any length) |
|
|
388
|
+
|
|
389
|
+
**Returns** a new `Uint8Array` of 32 bytes.
|
|
390
|
+
|
|
391
|
+
**Throws** `Error` if another stateful instance currently owns the `sha2` WASM module.
|
|
392
|
+
|
|
393
|
+
### Usage with `Fortuna`
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
import { init, Fortuna } from 'leviathan-crypto'
|
|
397
|
+
import { SHA256Hash } from 'leviathan-crypto/sha2'
|
|
398
|
+
import { SerpentGenerator } from 'leviathan-crypto/serpent'
|
|
399
|
+
import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
|
|
400
|
+
import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
|
|
401
|
+
|
|
402
|
+
await init({ sha2: sha2Wasm, serpent: serpentWasm })
|
|
403
|
+
const rng = await Fortuna.create({ generator: SerpentGenerator, hash: SHA256Hash })
|
|
404
|
+
const bytes = rng.get(32)
|
|
405
|
+
rng.stop()
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
356
410
|
## Usage Examples
|
|
357
411
|
|
|
358
412
|
### Example 1: Hash a message with SHA-256
|
|
@@ -598,16 +652,20 @@ SHA-2 is well-defined for zero-length messages and will return the correct diges
|
|
|
598
652
|
|
|
599
653
|
---
|
|
600
654
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
655
|
+
|
|
656
|
+
## Cross-References
|
|
657
|
+
|
|
658
|
+
| Document | Description |
|
|
659
|
+
| -------- | ----------- |
|
|
660
|
+
| [index](./README.md) | Project Documentation index |
|
|
661
|
+
| [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
|
|
662
|
+
| [asm_sha2](./asm_sha2.md) | WASM implementation details (AssemblyScript buffer layout, compression functions) |
|
|
663
|
+
| [sha3](./sha3.md) | alternative: SHA-3 family (immune to length extension attacks) |
|
|
664
|
+
| [serpent](./serpent.md) | `SerpentCipher` uses HMAC-SHA256 and HKDF internally via `Seal` and `SealStream` |
|
|
665
|
+
| [argon2id](./argon2id.md) | Argon2id password hashing; HKDF expands Argon2id root keys |
|
|
666
|
+
| [fortuna](./fortuna.md) | Fortuna CSPRNG uses SHA-256 for entropy accumulation |
|
|
667
|
+
| [utils](./utils.md) | `constantTimeEqual`, `bytesToHex`, `utf8ToBytes`, `randomBytes` |
|
|
668
|
+
| [sha2_audit.md](./sha2_audit.md) | SHA-2 implementation audit |
|
|
669
|
+
| [hmac_audit.md](./hmac_audit.md) | HMAC implementation audit |
|
|
670
|
+
| [hkdf_audit.md](./hkdf_audit.md) | HKDF implementation audit |
|
|
671
|
+
|
package/dist/docs/sha3.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
4
|
-
|
|
3
|
+
### SHA3 TypeScript API Reference
|
|
4
|
+
|
|
5
|
+
Covers the SHA-3 hash functions (SHA3-224 through SHA3-512) and the SHAKE extendable-output functions (SHAKE128, SHAKE256). See [SHA-3 implementation audit](./sha3_audit.md) for algorithm correctness verifications.
|
|
5
6
|
|
|
6
7
|
> ### Table of Contents
|
|
7
8
|
> - [Overview](#overview)
|
|
@@ -9,6 +10,7 @@
|
|
|
9
10
|
> - [Module Init](#module-init)
|
|
10
11
|
> - [API Reference](#api-reference)
|
|
11
12
|
> - [Incremental XOF API](#incremental-xof-api-absorb--squeeze--reset)
|
|
13
|
+
> - [SHA3_256Hash](#sha3_256hash)
|
|
12
14
|
> - [Usage Examples](#usage-examples)
|
|
13
15
|
> - [Error Conditions](#error-conditions)
|
|
14
16
|
|
|
@@ -207,6 +209,12 @@ class SHA3_512 {
|
|
|
207
209
|
Extendable-output function (XOF). Produces **variable-length** output — any
|
|
208
210
|
number of bytes you request. 128-bit security level.
|
|
209
211
|
|
|
212
|
+
> [!CAUTION]
|
|
213
|
+
> `SHAKE128` is stateful and holds exclusive access to the `sha3` WASM module
|
|
214
|
+
> for its entire lifetime. Constructing a second SHAKE128/SHAKE256 or any
|
|
215
|
+
> other sha3 class (`SHA3_256`, etc.) while this instance is live throws.
|
|
216
|
+
> Call `dispose()` when done. Pool workers are unaffected.
|
|
217
|
+
|
|
210
218
|
```typescript
|
|
211
219
|
class SHAKE128 {
|
|
212
220
|
constructor()
|
|
@@ -223,11 +231,15 @@ class SHAKE128 {
|
|
|
223
231
|
| `hash(msg, outputLength)` | One-shot: reset, absorb, squeeze. Safe on a dirty instance. |
|
|
224
232
|
| `absorb(msg)` | Feed data into the sponge. Chainable. Throws if called after `squeeze()`. |
|
|
225
233
|
| `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. |
|
|
234
|
+
| `reset()` | Return to a fresh, zeroed state. Chainable. Safe at any point. Does not release the sha3 exclusivity token. |
|
|
235
|
+
| `dispose()` | Zero all WASM state and the TS-side block buffer, release the sha3 exclusivity token. Idempotent. |
|
|
228
236
|
|
|
229
237
|
**`outputLength`** / **`n`** must be `>= 1`. Values below 1 throw a `RangeError`.
|
|
230
238
|
|
|
239
|
+
After `dispose()`, all instance methods (`reset`, `absorb`, `squeeze`, `hash`)
|
|
240
|
+
throw `Error: SHAKE128: instance has been disposed`. Disposal is permanent;
|
|
241
|
+
construct a new instance if you need to continue.
|
|
242
|
+
|
|
231
243
|
---
|
|
232
244
|
|
|
233
245
|
### SHAKE256
|
|
@@ -235,6 +247,12 @@ class SHAKE128 {
|
|
|
235
247
|
Extendable-output function (XOF). Produces **variable-length** output — any
|
|
236
248
|
number of bytes you request. 256-bit security level.
|
|
237
249
|
|
|
250
|
+
> [!CAUTION]
|
|
251
|
+
> `SHAKE256` is stateful and holds exclusive access to the `sha3` WASM module
|
|
252
|
+
> for its entire lifetime. Constructing a second SHAKE128/SHAKE256 or any
|
|
253
|
+
> other sha3 class while this instance is live throws. Call `dispose()` when
|
|
254
|
+
> done.
|
|
255
|
+
|
|
238
256
|
```typescript
|
|
239
257
|
class SHAKE256 {
|
|
240
258
|
constructor()
|
|
@@ -251,11 +269,15 @@ class SHAKE256 {
|
|
|
251
269
|
| `hash(msg, outputLength)` | One-shot: reset, absorb, squeeze. Safe on a dirty instance. |
|
|
252
270
|
| `absorb(msg)` | Feed data into the sponge. Chainable. Throws if called after `squeeze()`. |
|
|
253
271
|
| `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. |
|
|
254
|
-
| `reset()` | Return to a fresh, zeroed state. Chainable. Safe at any point. |
|
|
255
|
-
| `dispose()` | Zero all WASM state and the TS-side block buffer. |
|
|
272
|
+
| `reset()` | Return to a fresh, zeroed state. Chainable. Safe at any point. Does not release the sha3 exclusivity token. |
|
|
273
|
+
| `dispose()` | Zero all WASM state and the TS-side block buffer, release the sha3 exclusivity token. Idempotent. |
|
|
256
274
|
|
|
257
275
|
**`outputLength`** / **`n`** must be `>= 1`. Values below 1 throw a `RangeError`.
|
|
258
276
|
|
|
277
|
+
After `dispose()`, all instance methods (`reset`, `absorb`, `squeeze`, `hash`)
|
|
278
|
+
throw `Error: SHAKE256: instance has been disposed`. Disposal is permanent;
|
|
279
|
+
construct a new instance if you need to continue.
|
|
280
|
+
|
|
259
281
|
---
|
|
260
282
|
|
|
261
283
|
## Incremental XOF API (`absorb` / `squeeze` / `reset`)
|
|
@@ -298,6 +320,49 @@ xof.dispose()
|
|
|
298
320
|
|
|
299
321
|
---
|
|
300
322
|
|
|
323
|
+
## SHA3_256Hash
|
|
324
|
+
|
|
325
|
+
Stateless SHA3-256 `HashFn` for Fortuna's accumulator and reseed slots. Plain
|
|
326
|
+
`const` object — no instantiation, no `dispose()`.
|
|
327
|
+
|
|
328
|
+
Requires `init({ sha3: sha3Wasm })` (or the `keccak` alias). See
|
|
329
|
+
[fortuna.md](./fortuna.md) for full usage with `Fortuna.create()`.
|
|
330
|
+
|
|
331
|
+
| Property | Value |
|
|
332
|
+
|----------|-------|
|
|
333
|
+
| `outputSize` | `32` |
|
|
334
|
+
| `wasmModules` | `['sha3']` |
|
|
335
|
+
|
|
336
|
+
### `SHA3_256Hash.digest(msg): Uint8Array`
|
|
337
|
+
|
|
338
|
+
Hashes `msg` and returns a 32-byte SHA3-256 digest. Wipes WASM input/output/sponge
|
|
339
|
+
state scratch before returning.
|
|
340
|
+
|
|
341
|
+
| Parameter | Type | Description |
|
|
342
|
+
|-----------|------|-------------|
|
|
343
|
+
| `msg` | `Uint8Array` | Message to hash (any length) |
|
|
344
|
+
|
|
345
|
+
**Returns** a new `Uint8Array` of 32 bytes.
|
|
346
|
+
|
|
347
|
+
**Throws** `Error` if another stateful instance currently owns the `sha3` WASM module.
|
|
348
|
+
|
|
349
|
+
### Usage with `Fortuna`
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { init, Fortuna } from 'leviathan-crypto'
|
|
353
|
+
import { SHA3_256Hash } from 'leviathan-crypto/sha3'
|
|
354
|
+
import { ChaCha20Generator } from 'leviathan-crypto/chacha20'
|
|
355
|
+
import { sha3Wasm } from 'leviathan-crypto/sha3/embedded'
|
|
356
|
+
import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded'
|
|
357
|
+
|
|
358
|
+
await init({ sha3: sha3Wasm, chacha20: chacha20Wasm })
|
|
359
|
+
const rng = await Fortuna.create({ generator: ChaCha20Generator, hash: SHA3_256Hash })
|
|
360
|
+
const bytes = rng.get(32)
|
|
361
|
+
rng.stop()
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
301
366
|
## Usage Examples
|
|
302
367
|
|
|
303
368
|
### Example 1: Hash a string with SHA3-256
|
|
@@ -536,11 +601,12 @@ absorbs zero bytes and then squeezes.
|
|
|
536
601
|
|
|
537
602
|
---
|
|
538
603
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
604
|
+
|
|
605
|
+
## Cross-References
|
|
606
|
+
|
|
607
|
+
| Document | Description |
|
|
608
|
+
| -------- | ----------- |
|
|
609
|
+
| [index](./README.md) | Project Documentation index |
|
|
610
|
+
| [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
|
|
611
|
+
| [sha3_audit.md](./sha3_audit.md) | SHA-3 / Keccak implementation audit |
|
|
612
|
+
|