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.
- package/CLAUDE.md +129 -94
- package/README.md +166 -223
- package/SECURITY.md +90 -45
- package/dist/chacha20/cipher-suite.d.ts +4 -0
- package/dist/chacha20/cipher-suite.js +79 -0
- package/dist/chacha20/embedded.d.ts +1 -0
- package/dist/chacha20/embedded.js +27 -0
- package/dist/chacha20/index.d.ts +20 -27
- package/dist/chacha20/index.js +40 -59
- package/dist/chacha20/ops.d.ts +1 -1
- package/dist/chacha20/ops.js +19 -18
- package/dist/chacha20/pool-worker.js +77 -0
- package/dist/ct-wasm.d.ts +1 -0
- package/dist/ct-wasm.js +3 -0
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +323 -0
- package/dist/docs/architecture.md +427 -292
- package/dist/docs/argon2id.md +42 -30
- package/dist/docs/chacha20.md +192 -266
- package/dist/docs/exports.md +241 -0
- package/dist/docs/fortuna.md +60 -69
- package/dist/docs/init.md +172 -178
- package/dist/docs/loader.md +87 -142
- package/dist/docs/serpent.md +134 -583
- package/dist/docs/sha2.md +91 -103
- package/dist/docs/sha3.md +70 -36
- package/dist/docs/types.md +94 -16
- package/dist/docs/utils.md +109 -32
- package/dist/embedded/kyber.d.ts +1 -0
- package/dist/embedded/kyber.js +3 -0
- package/dist/embedded/serpent.d.ts +1 -1
- package/dist/embedded/serpent.js +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +38 -0
- package/dist/fortuna.d.ts +0 -6
- package/dist/fortuna.js +5 -5
- package/dist/index.d.ts +25 -9
- package/dist/index.js +36 -7
- package/dist/init.d.ts +3 -7
- package/dist/init.js +18 -35
- package/dist/keccak/embedded.d.ts +1 -0
- package/dist/keccak/embedded.js +27 -0
- package/dist/keccak/index.d.ts +4 -0
- package/dist/keccak/index.js +31 -0
- package/dist/kyber/embedded.d.ts +1 -0
- package/dist/kyber/embedded.js +27 -0
- package/dist/kyber/indcpa.d.ts +49 -0
- package/dist/kyber/indcpa.js +352 -0
- package/dist/kyber/index.d.ts +38 -0
- package/dist/kyber/index.js +150 -0
- package/dist/kyber/kem.d.ts +21 -0
- package/dist/kyber/kem.js +160 -0
- package/dist/kyber/params.d.ts +14 -0
- package/dist/kyber/params.js +37 -0
- package/dist/kyber/suite.d.ts +13 -0
- package/dist/kyber/suite.js +94 -0
- package/dist/kyber/types.d.ts +98 -0
- package/dist/kyber/types.js +25 -0
- package/dist/kyber/validate.d.ts +19 -0
- package/dist/kyber/validate.js +68 -0
- package/dist/kyber.wasm +0 -0
- package/dist/loader.d.ts +15 -6
- package/dist/loader.js +65 -21
- package/dist/serpent/cipher-suite.d.ts +4 -0
- package/dist/serpent/cipher-suite.js +122 -0
- package/dist/serpent/embedded.d.ts +1 -0
- package/dist/serpent/embedded.js +27 -0
- package/dist/serpent/index.d.ts +6 -37
- package/dist/serpent/index.js +9 -118
- package/dist/serpent/pool-worker.d.ts +1 -0
- package/dist/serpent/pool-worker.js +208 -0
- package/dist/serpent/serpent-cbc.d.ts +30 -0
- package/dist/serpent/serpent-cbc.js +142 -0
- package/dist/serpent.wasm +0 -0
- package/dist/sha2/embedded.d.ts +1 -0
- package/dist/sha2/embedded.js +27 -0
- package/dist/sha2/hkdf.js +6 -2
- package/dist/sha2/index.d.ts +3 -2
- package/dist/sha2/index.js +3 -4
- package/dist/sha3/embedded.d.ts +1 -0
- package/dist/sha3/embedded.js +27 -0
- package/dist/sha3/index.d.ts +3 -2
- package/dist/sha3/index.js +3 -4
- package/dist/stream/constants.d.ts +6 -0
- package/dist/stream/constants.js +30 -0
- package/dist/stream/header.d.ts +9 -0
- package/dist/stream/header.js +77 -0
- package/dist/stream/index.d.ts +7 -0
- package/dist/stream/index.js +27 -0
- package/dist/stream/open-stream.d.ts +21 -0
- package/dist/stream/open-stream.js +146 -0
- package/dist/stream/seal-stream-pool.d.ts +38 -0
- package/dist/stream/seal-stream-pool.js +400 -0
- package/dist/stream/seal-stream.d.ts +20 -0
- package/dist/stream/seal-stream.js +142 -0
- package/dist/stream/seal.d.ts +9 -0
- package/dist/stream/seal.js +75 -0
- package/dist/stream/types.d.ts +25 -0
- package/dist/stream/types.js +26 -0
- package/dist/utils.d.ts +7 -2
- package/dist/utils.js +49 -3
- package/dist/wasm-source.d.ts +12 -0
- package/dist/wasm-source.js +26 -0
- package/package.json +13 -5
- package/dist/chacha20/pool.d.ts +0 -52
- package/dist/chacha20/pool.js +0 -178
- package/dist/chacha20/pool.worker.js +0 -37
- package/dist/chacha20/stream-sealer.d.ts +0 -49
- package/dist/chacha20/stream-sealer.js +0 -327
- package/dist/docs/chacha20_pool.md +0 -309
- package/dist/docs/wasm.md +0 -194
- package/dist/serpent/seal.d.ts +0 -8
- package/dist/serpent/seal.js +0 -72
- package/dist/serpent/stream-pool.d.ts +0 -48
- package/dist/serpent/stream-pool.js +0 -275
- package/dist/serpent/stream-sealer.d.ts +0 -55
- package/dist/serpent/stream-sealer.js +0 -342
- package/dist/serpent/stream.d.ts +0 -28
- package/dist/serpent/stream.js +0 -205
- package/dist/serpent/stream.worker.d.ts +0 -32
- package/dist/serpent/stream.worker.js +0 -117
- /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
package/dist/docs/argon2id.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
162
|
+
### With Seal + SerpentCipher
|
|
154
163
|
|
|
155
|
-
`
|
|
156
|
-
|
|
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,
|
|
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(
|
|
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
|
|
178
|
-
const
|
|
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
|
|
182
|
-
const ciphertext = serpent.encrypt(fullKey, plaintext);
|
|
183
|
-
serpent.dispose();
|
|
193
|
+
const blob = Seal.encrypt(SerpentCipher, key, plaintext);
|
|
184
194
|
|
|
185
|
-
// Store with
|
|
186
|
-
const envelope = {
|
|
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
|
|
200
|
-
const
|
|
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
|
|
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
|
-
|
|
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('
|
|
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('
|
|
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
|
|
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) —
|
|
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
|