leviathan-crypto 1.4.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CLAUDE.md +129 -94
  2. package/README.md +166 -223
  3. package/SECURITY.md +90 -45
  4. package/dist/chacha20/cipher-suite.d.ts +4 -0
  5. package/dist/chacha20/cipher-suite.js +79 -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 +323 -0
  17. package/dist/docs/architecture.md +427 -292
  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 +94 -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/embedded/serpent.d.ts +1 -1
  32. package/dist/embedded/serpent.js +1 -1
  33. package/dist/errors.d.ts +10 -0
  34. package/dist/errors.js +38 -0
  35. package/dist/fortuna.d.ts +0 -6
  36. package/dist/fortuna.js +5 -5
  37. package/dist/index.d.ts +25 -9
  38. package/dist/index.js +36 -7
  39. package/dist/init.d.ts +3 -7
  40. package/dist/init.js +18 -35
  41. package/dist/keccak/embedded.d.ts +1 -0
  42. package/dist/keccak/embedded.js +27 -0
  43. package/dist/keccak/index.d.ts +4 -0
  44. package/dist/keccak/index.js +31 -0
  45. package/dist/kyber/embedded.d.ts +1 -0
  46. package/dist/kyber/embedded.js +27 -0
  47. package/dist/kyber/indcpa.d.ts +49 -0
  48. package/dist/kyber/indcpa.js +352 -0
  49. package/dist/kyber/index.d.ts +38 -0
  50. package/dist/kyber/index.js +150 -0
  51. package/dist/kyber/kem.d.ts +21 -0
  52. package/dist/kyber/kem.js +160 -0
  53. package/dist/kyber/params.d.ts +14 -0
  54. package/dist/kyber/params.js +37 -0
  55. package/dist/kyber/suite.d.ts +13 -0
  56. package/dist/kyber/suite.js +94 -0
  57. package/dist/kyber/types.d.ts +98 -0
  58. package/dist/kyber/types.js +25 -0
  59. package/dist/kyber/validate.d.ts +19 -0
  60. package/dist/kyber/validate.js +68 -0
  61. package/dist/kyber.wasm +0 -0
  62. package/dist/loader.d.ts +15 -6
  63. package/dist/loader.js +65 -21
  64. package/dist/serpent/cipher-suite.d.ts +4 -0
  65. package/dist/serpent/cipher-suite.js +122 -0
  66. package/dist/serpent/embedded.d.ts +1 -0
  67. package/dist/serpent/embedded.js +27 -0
  68. package/dist/serpent/index.d.ts +6 -37
  69. package/dist/serpent/index.js +9 -118
  70. package/dist/serpent/pool-worker.d.ts +1 -0
  71. package/dist/serpent/pool-worker.js +208 -0
  72. package/dist/serpent/serpent-cbc.d.ts +30 -0
  73. package/dist/serpent/serpent-cbc.js +142 -0
  74. package/dist/serpent.wasm +0 -0
  75. package/dist/sha2/embedded.d.ts +1 -0
  76. package/dist/sha2/embedded.js +27 -0
  77. package/dist/sha2/hkdf.js +6 -2
  78. package/dist/sha2/index.d.ts +3 -2
  79. package/dist/sha2/index.js +3 -4
  80. package/dist/sha3/embedded.d.ts +1 -0
  81. package/dist/sha3/embedded.js +27 -0
  82. package/dist/sha3/index.d.ts +3 -2
  83. package/dist/sha3/index.js +3 -4
  84. package/dist/stream/constants.d.ts +6 -0
  85. package/dist/stream/constants.js +30 -0
  86. package/dist/stream/header.d.ts +9 -0
  87. package/dist/stream/header.js +77 -0
  88. package/dist/stream/index.d.ts +7 -0
  89. package/dist/stream/index.js +27 -0
  90. package/dist/stream/open-stream.d.ts +21 -0
  91. package/dist/stream/open-stream.js +146 -0
  92. package/dist/stream/seal-stream-pool.d.ts +38 -0
  93. package/dist/stream/seal-stream-pool.js +400 -0
  94. package/dist/stream/seal-stream.d.ts +20 -0
  95. package/dist/stream/seal-stream.js +142 -0
  96. package/dist/stream/seal.d.ts +9 -0
  97. package/dist/stream/seal.js +75 -0
  98. package/dist/stream/types.d.ts +25 -0
  99. package/dist/stream/types.js +26 -0
  100. package/dist/utils.d.ts +7 -2
  101. package/dist/utils.js +49 -3
  102. package/dist/wasm-source.d.ts +12 -0
  103. package/dist/wasm-source.js +26 -0
  104. package/package.json +13 -5
  105. package/dist/chacha20/pool.d.ts +0 -52
  106. package/dist/chacha20/pool.js +0 -178
  107. package/dist/chacha20/pool.worker.js +0 -37
  108. package/dist/chacha20/stream-sealer.d.ts +0 -49
  109. package/dist/chacha20/stream-sealer.js +0 -327
  110. package/dist/docs/chacha20_pool.md +0 -309
  111. package/dist/docs/wasm.md +0 -194
  112. package/dist/serpent/seal.d.ts +0 -8
  113. package/dist/serpent/seal.js +0 -72
  114. package/dist/serpent/stream-pool.d.ts +0 -48
  115. package/dist/serpent/stream-pool.js +0 -275
  116. package/dist/serpent/stream-sealer.d.ts +0 -55
  117. package/dist/serpent/stream-sealer.js +0 -342
  118. package/dist/serpent/stream.d.ts +0 -28
  119. package/dist/serpent/stream.js +0 -205
  120. package/dist/serpent/stream.worker.d.ts +0 -32
  121. package/dist/serpent/stream.worker.js +0 -117
  122. /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
@@ -5,19 +5,29 @@
5
5
  > [`argon2id`](https://www.npmjs.com/package/argon2id) npm package directly and
6
6
  > how to pair it with leviathan primitives for passphrase-based encryption.
7
7
 
8
+ > ### Table of Contents
9
+ > - [Why Argon2id](#why-argon2id)
10
+ > - [Installation](#installation)
11
+ > - [Basic usage](#basic-usage)
12
+ > - [Parameter presets](#parameter-presets)
13
+ > - [Password hashing and verification](#password-hashing-and-verification)
14
+ > - [Passphrase-based encryption with leviathan-crypto](#passphrase-based-encryption-with-leviathan-crypto)
15
+ > - [Memory note](#memory-note)
16
+
17
+ ---
18
+
8
19
  ## Why Argon2id
9
20
 
10
21
  Password hashing is the last line of defense when a database is breached. If an
11
22
  attacker obtains hashed passwords, the hash function determines how expensive it
12
- is to recover each plaintext. Traditional hash functions even iterated ones
13
- like PBKDF2 — are fast on GPUs and custom hardware. An attacker with a few
23
+ is to recover each plaintext. Traditional hash functions, even iterated ones like PBKDF2, are fast on GPUs and custom hardware. An attacker with a few
14
24
  thousand dollars of GPU hardware can test billions of PBKDF2-SHA256 candidates
15
25
  per second. bcrypt improves on this with a 4 KiB memory requirement that limits
16
26
  GPU parallelism, but 4 KiB is trivial by modern standards.
17
27
 
18
28
  Argon2 was the winner of the Password Hashing Competition (PHC, 2013–2015),
19
29
  selected from 24 submissions after two years of public analysis. It was designed
20
- specifically to be **memory-hard**, computing the hash requires not just CPU
30
+ specifically to be **memory-hard**: computing the hash requires not just CPU
21
31
  time but a large block of RAM that cannot be traded away. An attacker who tries
22
32
  to use less memory must perform exponentially more computation, making GPU and
23
33
  ASIC attacks economically impractical.
@@ -145,21 +155,23 @@ const valid = constantTimeEqual(candidate, stored.hash);
145
155
  ## Passphrase-based encryption with leviathan-crypto
146
156
 
147
157
  Argon2id produces a root key from a passphrase. HKDF-SHA256 from leviathan then
148
- expands that root key into the separate encryption and MAC keys that
149
- `SerpentSeal` and `XChaCha20Poly1305` expect. Keeping the two steps separate
150
- means the expensive Argon2id call happens once per passphrase, and HKDF handles
151
- any further key material needed.
158
+ expands that root key into the key material `Seal` and `XChaCha20Poly1305` expect.
159
+ Keeping the two steps separate means the expensive Argon2id call happens once
160
+ per passphrase, and HKDF handles any further key material needed.
152
161
 
153
- ### With SerpentSeal
162
+ ### With Seal + SerpentCipher
154
163
 
155
- `SerpentSeal` takes a 64-byte key (32-byte enc key + 32-byte MAC key). HKDF
156
- expands the 32-byte Argon2id output to 64 bytes:
164
+ `SerpentCipher` takes a 32-byte key. HKDF expands the 32-byte Argon2id output
165
+ to 32 bytes with a domain-separation info string:
157
166
 
158
167
  ```typescript
159
168
  import loadArgon2idWasm from 'argon2id';
160
- import { init, SerpentSeal, HKDF_SHA256 } from 'leviathan-crypto';
169
+ import { init, Seal, SerpentCipher, HKDF_SHA256 } from 'leviathan-crypto';
170
+
171
+ import { serpentWasm } from 'leviathan-crypto/serpent/embedded';
172
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded';
161
173
 
162
- await init(['serpent', 'sha2']);
174
+ await init({ serpent: serpentWasm, sha2: sha2Wasm });
163
175
  const argon2id = await loadArgon2idWasm();
164
176
 
165
177
  // ── Encrypt ──────────────────────────────────────────────────────────────────
@@ -174,16 +186,14 @@ const rootKey = argon2id({
174
186
  tagLength: 32,
175
187
  });
176
188
 
177
- const hkdf = new HKDF_SHA256();
178
- const fullKey = hkdf.derive(rootKey, argonSalt, new TextEncoder().encode('serpent-seal-v1'), 64);
189
+ const hkdf = new HKDF_SHA256();
190
+ const key = hkdf.derive(rootKey, argonSalt, new TextEncoder().encode('myapp-serpent-key'), 32);
179
191
  hkdf.dispose();
180
192
 
181
- const serpent = new SerpentSeal();
182
- const ciphertext = serpent.encrypt(fullKey, plaintext);
183
- serpent.dispose();
193
+ const blob = Seal.encrypt(SerpentCipher, key, plaintext);
184
194
 
185
- // Store with ciphertext — all required for decryption
186
- const envelope = { ciphertext, argonSalt };
195
+ // Store with blob — all required for decryption
196
+ const envelope = { blob, argonSalt };
187
197
 
188
198
  // ── Decrypt ──────────────────────────────────────────────────────────────────
189
199
 
@@ -196,13 +206,11 @@ const rootKey2 = argon2id({
196
206
  tagLength: 32,
197
207
  });
198
208
 
199
- const hkdf2 = new HKDF_SHA256();
200
- const fullKey2 = hkdf2.derive(rootKey2, envelope.argonSalt, new TextEncoder().encode('serpent-seal-v1'), 64);
209
+ const hkdf2 = new HKDF_SHA256();
210
+ const key2 = hkdf2.derive(rootKey2, envelope.argonSalt, new TextEncoder().encode('myapp-serpent-key'), 32);
201
211
  hkdf2.dispose();
202
212
 
203
- const serpent2 = new SerpentSeal();
204
- const decrypted = serpent2.decrypt(fullKey2, envelope.ciphertext);
205
- serpent2.dispose();
213
+ const decrypted = Seal.decrypt(SerpentCipher, key2, envelope.blob);
206
214
  ```
207
215
 
208
216
  ### With XChaCha20Poly1305
@@ -214,7 +222,10 @@ generated fresh per encryption; only the Argon2id salt needs to be stored:
214
222
  import loadArgon2idWasm from 'argon2id';
215
223
  import { init, XChaCha20Poly1305, HKDF_SHA256 } from 'leviathan-crypto';
216
224
 
217
- await init(['chacha20', 'sha2']);
225
+ import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded';
226
+ import { sha2Wasm } from 'leviathan-crypto/sha2/embedded';
227
+
228
+ await init({ chacha20: chacha20Wasm, sha2: sha2Wasm });
218
229
  const argon2id = await loadArgon2idWasm();
219
230
 
220
231
  // ── Encrypt ──────────────────────────────────────────────────────────────────
@@ -232,7 +243,7 @@ const rootKey = argon2id({
232
243
  // tagLength: 32 already matches XChaCha20Poly1305's expected key size
233
244
  // HKDF is optional here but included for domain separation.
234
245
  const hkdf = new HKDF_SHA256();
235
- const key = hkdf.derive(rootKey, argonSalt, new TextEncoder().encode('xchacha-v1'), 32);
246
+ const key = hkdf.derive(rootKey, argonSalt, new TextEncoder().encode('myapp-xchacha20-key'), 32);
236
247
  hkdf.dispose();
237
248
 
238
249
  const nonce = crypto.getRandomValues(new Uint8Array(24));
@@ -254,7 +265,7 @@ const rootKey2 = argon2id({
254
265
  });
255
266
 
256
267
  const hkdf2 = new HKDF_SHA256();
257
- const key2 = hkdf2.derive(rootKey2, envelope.argonSalt, new TextEncoder().encode('xchacha-v1'), 32);
268
+ const key2 = hkdf2.derive(rootKey2, envelope.argonSalt, new TextEncoder().encode('myapp-xchacha20-key'), 32);
258
269
  hkdf2.dispose();
259
270
 
260
271
  const xc2 = new XChaCha20Poly1305();
@@ -265,13 +276,13 @@ xc2.dispose();
265
276
  > [!CAUTION]
266
277
  > Never reuse an Argon2id salt across different passphrases or key derivation
267
278
  > contexts. Generate a fresh random salt for each new encryption envelope. The
268
- > salt is not secret store it in plaintext alongside the ciphertext.
279
+ > salt is not secret. Store it in plaintext alongside the ciphertext.
269
280
 
270
281
  ---
271
282
 
272
283
  ## Memory note
273
284
 
274
- >[!IMPORTANT]
285
+ > [!IMPORTANT]
275
286
  > Each call to `loadArgon2idWasm()` instantiates a separate WASM instance. The
276
287
  > package's own documentation recommends reloading the module between hashes when
277
288
  > the `memorySize` varies significantly, since WASM linear memory is not
@@ -284,7 +295,8 @@ xc2.dispose();
284
295
  >
285
296
  > - [index](./README.md) — Project Documentation index
286
297
  > - [sha2](./sha2.md) — HKDF-SHA256 for key expansion from Argon2id root keys
287
- > - [serpent](./serpent.md) — SerpentSeal: Serpent-256 authenticated encryption (pairs with Argon2id-derived 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`
288
300
  > - [chacha20](./chacha20.md) — XChaCha20Poly1305: ChaCha20 authenticated encryption (pairs with Argon2id-derived keys)
289
301
  > - [utils](./utils.md) — `randomBytes` for generating salts, `constantTimeEqual` for hash verification
290
302
  > - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline