leviathan-crypto 2.1.0 → 3.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 (296) hide show
  1. package/CLAUDE.md +86 -443
  2. package/README.md +198 -65
  3. package/dist/aes/aes-cbc.d.ts +40 -0
  4. package/dist/aes/aes-cbc.js +158 -0
  5. package/dist/aes/aes-ctr.d.ts +50 -0
  6. package/dist/aes/aes-ctr.js +141 -0
  7. package/dist/aes/aes-gcm-siv.d.ts +67 -0
  8. package/dist/aes/aes-gcm-siv.js +217 -0
  9. package/dist/aes/aes-gcm.d.ts +61 -0
  10. package/dist/aes/aes-gcm.js +226 -0
  11. package/dist/aes/cipher-suite.d.ts +21 -0
  12. package/dist/aes/cipher-suite.js +179 -0
  13. package/dist/aes/embedded.d.ts +1 -0
  14. package/dist/aes/embedded.js +26 -0
  15. package/dist/aes/generator.d.ts +14 -0
  16. package/dist/aes/generator.js +103 -0
  17. package/dist/aes/index.d.ts +58 -0
  18. package/dist/aes/index.js +125 -0
  19. package/dist/aes/ops.d.ts +60 -0
  20. package/dist/aes/ops.js +164 -0
  21. package/dist/aes/pool-worker.d.ts +1 -0
  22. package/dist/aes/pool-worker.js +92 -0
  23. package/dist/aes/types.d.ts +1 -0
  24. package/dist/aes/types.js +23 -0
  25. package/dist/aes.wasm +0 -0
  26. package/dist/blake3/embedded.d.ts +1 -0
  27. package/dist/blake3/embedded.js +26 -0
  28. package/dist/blake3/index.d.ts +143 -0
  29. package/dist/blake3/index.js +620 -0
  30. package/dist/blake3/types.d.ts +102 -0
  31. package/dist/blake3/types.js +31 -0
  32. package/dist/blake3/validate.d.ts +29 -0
  33. package/dist/blake3/validate.js +80 -0
  34. package/dist/blake3.wasm +0 -0
  35. package/dist/chacha20/cipher-suite.js +47 -25
  36. package/dist/chacha20/generator.d.ts +2 -2
  37. package/dist/chacha20/generator.js +4 -4
  38. package/dist/chacha20/index.d.ts +16 -15
  39. package/dist/chacha20/index.js +52 -46
  40. package/dist/chacha20/ops.d.ts +7 -7
  41. package/dist/chacha20/ops.js +34 -34
  42. package/dist/chacha20/pool-worker.js +5 -3
  43. package/dist/cte-wasm.d.ts +1 -0
  44. package/dist/cte-wasm.js +3 -0
  45. package/dist/curve25519.wasm +0 -0
  46. package/dist/ecdsa/der.d.ts +23 -0
  47. package/dist/ecdsa/der.js +192 -0
  48. package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
  49. package/dist/ecdsa/ecprivatekey-der.js +230 -0
  50. package/dist/ecdsa/embedded.d.ts +1 -0
  51. package/dist/ecdsa/embedded.js +25 -0
  52. package/dist/ecdsa/index.d.ts +124 -0
  53. package/dist/ecdsa/index.js +366 -0
  54. package/dist/ecdsa/types.d.ts +31 -0
  55. package/dist/ecdsa/types.js +28 -0
  56. package/dist/ecdsa/validate.d.ts +18 -0
  57. package/dist/ecdsa/validate.js +92 -0
  58. package/dist/ed25519/embedded.d.ts +1 -0
  59. package/dist/ed25519/embedded.js +31 -0
  60. package/dist/ed25519/index.d.ts +70 -0
  61. package/dist/ed25519/index.js +308 -0
  62. package/dist/ed25519/types.d.ts +27 -0
  63. package/dist/ed25519/types.js +27 -0
  64. package/dist/ed25519/validate.d.ts +7 -0
  65. package/dist/ed25519/validate.js +77 -0
  66. package/dist/embedded/aes-pool-worker.d.ts +1 -0
  67. package/dist/embedded/aes-pool-worker.js +5 -0
  68. package/dist/embedded/aes.d.ts +1 -0
  69. package/dist/embedded/aes.js +3 -0
  70. package/dist/embedded/blake3.d.ts +1 -0
  71. package/dist/embedded/blake3.js +3 -0
  72. package/dist/embedded/chacha20-pool-worker.d.ts +1 -1
  73. package/dist/embedded/chacha20-pool-worker.js +2 -2
  74. package/dist/embedded/chacha20.d.ts +1 -1
  75. package/dist/embedded/chacha20.js +2 -2
  76. package/dist/embedded/curve25519.d.ts +1 -0
  77. package/dist/embedded/curve25519.js +3 -0
  78. package/dist/embedded/mldsa.d.ts +1 -0
  79. package/dist/embedded/mldsa.js +3 -0
  80. package/dist/embedded/mlkem.d.ts +1 -0
  81. package/dist/embedded/mlkem.js +3 -0
  82. package/dist/embedded/p256.d.ts +1 -0
  83. package/dist/embedded/p256.js +3 -0
  84. package/dist/embedded/serpent-pool-worker.d.ts +1 -1
  85. package/dist/embedded/serpent-pool-worker.js +2 -2
  86. package/dist/embedded/serpent.d.ts +1 -1
  87. package/dist/embedded/serpent.js +2 -2
  88. package/dist/embedded/sha2.d.ts +1 -1
  89. package/dist/embedded/sha2.js +2 -2
  90. package/dist/embedded/sha3.d.ts +1 -1
  91. package/dist/embedded/sha3.js +2 -2
  92. package/dist/embedded/slhdsa.d.ts +1 -0
  93. package/dist/embedded/slhdsa.js +3 -0
  94. package/dist/errors.d.ts +92 -1
  95. package/dist/errors.js +111 -1
  96. package/dist/fortuna.d.ts +5 -5
  97. package/dist/fortuna.js +37 -64
  98. package/dist/index.d.ts +38 -9
  99. package/dist/index.js +63 -19
  100. package/dist/init.d.ts +1 -1
  101. package/dist/init.js +11 -25
  102. package/dist/keccak/embedded.js +1 -1
  103. package/dist/keccak/index.d.ts +2 -0
  104. package/dist/keccak/index.js +4 -2
  105. package/dist/loader.d.ts +1 -24
  106. package/dist/loader.js +13 -16
  107. package/dist/merkle/blake3-tree.d.ts +35 -0
  108. package/dist/merkle/blake3-tree.js +187 -0
  109. package/dist/merkle/checkpoint.d.ts +58 -0
  110. package/dist/merkle/checkpoint.js +217 -0
  111. package/dist/merkle/index.d.ts +19 -0
  112. package/dist/merkle/index.js +37 -0
  113. package/dist/merkle/merkle-log.d.ts +130 -0
  114. package/dist/merkle/merkle-log.js +207 -0
  115. package/dist/merkle/merkle-verifier.d.ts +126 -0
  116. package/dist/merkle/merkle-verifier.js +296 -0
  117. package/dist/merkle/proof.d.ts +70 -0
  118. package/dist/merkle/proof.js +300 -0
  119. package/dist/merkle/sha256-tree.d.ts +33 -0
  120. package/dist/merkle/sha256-tree.js +145 -0
  121. package/dist/merkle/signed-log.d.ts +156 -0
  122. package/dist/merkle/signed-log.js +356 -0
  123. package/dist/merkle/signed-note.d.ts +309 -0
  124. package/dist/merkle/signed-note.js +648 -0
  125. package/dist/merkle/sth.d.ts +31 -0
  126. package/dist/merkle/sth.js +31 -0
  127. package/dist/merkle/storage.d.ts +40 -0
  128. package/dist/merkle/storage.js +71 -0
  129. package/dist/merkle/tree.d.ts +68 -0
  130. package/dist/merkle/tree.js +94 -0
  131. package/dist/mldsa/embedded.d.ts +1 -0
  132. package/dist/{kyber → mldsa}/embedded.js +5 -5
  133. package/dist/mldsa/expand.d.ts +53 -0
  134. package/dist/mldsa/expand.js +188 -0
  135. package/dist/mldsa/format.d.ts +16 -0
  136. package/dist/mldsa/format.js +68 -0
  137. package/dist/mldsa/hashvariant.d.ts +32 -0
  138. package/dist/mldsa/hashvariant.js +248 -0
  139. package/dist/mldsa/index.d.ts +142 -0
  140. package/dist/mldsa/index.js +463 -0
  141. package/dist/mldsa/keygen.d.ts +16 -0
  142. package/dist/mldsa/keygen.js +232 -0
  143. package/dist/mldsa/params.d.ts +21 -0
  144. package/dist/mldsa/params.js +55 -0
  145. package/dist/mldsa/sha3-helpers.d.ts +30 -0
  146. package/dist/mldsa/sha3-helpers.js +124 -0
  147. package/dist/mldsa/sign.d.ts +36 -0
  148. package/dist/mldsa/sign.js +380 -0
  149. package/dist/mldsa/types.d.ts +91 -0
  150. package/dist/mldsa/types.js +25 -0
  151. package/dist/mldsa/validate.d.ts +55 -0
  152. package/dist/mldsa/validate.js +125 -0
  153. package/dist/mldsa/verify.d.ts +29 -0
  154. package/dist/mldsa/verify.js +269 -0
  155. package/dist/mldsa.wasm +0 -0
  156. package/dist/mlkem/embedded.d.ts +1 -0
  157. package/dist/mlkem/embedded.js +27 -0
  158. package/dist/mlkem/indcpa.d.ts +49 -0
  159. package/dist/{kyber → mlkem}/indcpa.js +44 -44
  160. package/dist/mlkem/index.d.ts +37 -0
  161. package/dist/{kyber → mlkem}/index.js +24 -34
  162. package/dist/mlkem/kem.d.ts +21 -0
  163. package/dist/{kyber → mlkem}/kem.js +44 -64
  164. package/dist/{kyber → mlkem}/params.d.ts +4 -4
  165. package/dist/{kyber → mlkem}/params.js +2 -2
  166. package/dist/mlkem/suite.d.ts +12 -0
  167. package/dist/{kyber → mlkem}/suite.js +17 -12
  168. package/dist/{kyber → mlkem}/types.d.ts +3 -3
  169. package/dist/{kyber → mlkem}/types.js +1 -1
  170. package/dist/{kyber → mlkem}/validate.d.ts +7 -7
  171. package/dist/{kyber → mlkem}/validate.js +7 -7
  172. package/dist/{kyber.wasm → mlkem.wasm} +0 -0
  173. package/dist/p256.wasm +0 -0
  174. package/dist/ratchet/index.d.ts +2 -0
  175. package/dist/ratchet/index.js +1 -0
  176. package/dist/ratchet/kdf-chain.js +3 -3
  177. package/dist/ratchet/ratchet-keypair.js +2 -2
  178. package/dist/ratchet/root-kdf.js +7 -7
  179. package/dist/ratchet/skipped-key-store.js +4 -4
  180. package/dist/ratchet/types.d.ts +1 -1
  181. package/dist/serpent/cipher-suite.js +20 -17
  182. package/dist/serpent/generator.d.ts +1 -1
  183. package/dist/serpent/generator.js +2 -2
  184. package/dist/serpent/index.d.ts +8 -7
  185. package/dist/serpent/index.js +18 -27
  186. package/dist/serpent/pool-worker.js +7 -5
  187. package/dist/serpent/serpent-cbc.d.ts +4 -4
  188. package/dist/serpent/serpent-cbc.js +11 -8
  189. package/dist/serpent/shared-ops.d.ts +3 -23
  190. package/dist/serpent/shared-ops.js +50 -85
  191. package/dist/serpent.wasm +0 -0
  192. package/dist/sha2/hkdf.js +5 -5
  193. package/dist/sha2/index.d.ts +21 -1
  194. package/dist/sha2/index.js +65 -10
  195. package/dist/sha2/types.d.ts +41 -2
  196. package/dist/sha2.wasm +0 -0
  197. package/dist/sha3/index.d.ts +72 -3
  198. package/dist/sha3/index.js +240 -14
  199. package/dist/sha3/kmac.d.ts +121 -0
  200. package/dist/sha3/kmac.js +800 -0
  201. package/dist/sha3.wasm +0 -0
  202. package/dist/shared/pkcs7.d.ts +22 -0
  203. package/dist/shared/pkcs7.js +84 -0
  204. package/dist/sign/ctx.d.ts +41 -0
  205. package/dist/sign/ctx.js +102 -0
  206. package/dist/sign/envelope.d.ts +45 -0
  207. package/dist/sign/envelope.js +152 -0
  208. package/dist/sign/hasher.d.ts +9 -0
  209. package/dist/sign/hasher.js +132 -0
  210. package/dist/sign/index.d.ts +11 -0
  211. package/dist/sign/index.js +34 -0
  212. package/dist/sign/sign-stream.d.ts +25 -0
  213. package/dist/sign/sign-stream.js +112 -0
  214. package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
  215. package/dist/sign/suites/ecdsa-p256.js +120 -0
  216. package/dist/sign/suites/ed25519.d.ts +3 -0
  217. package/dist/sign/suites/ed25519.js +165 -0
  218. package/dist/sign/suites/hybrid-classical.d.ts +23 -0
  219. package/dist/sign/suites/hybrid-classical.js +526 -0
  220. package/dist/sign/suites/hybrid-pq.d.ts +4 -0
  221. package/dist/sign/suites/hybrid-pq.js +234 -0
  222. package/dist/sign/suites/mldsa.d.ts +7 -0
  223. package/dist/sign/suites/mldsa.js +161 -0
  224. package/dist/sign/suites/slhdsa.d.ts +7 -0
  225. package/dist/sign/suites/slhdsa.js +176 -0
  226. package/dist/sign/types.d.ts +106 -0
  227. package/dist/sign/types.js +28 -0
  228. package/dist/sign/verify-stream.d.ts +30 -0
  229. package/dist/sign/verify-stream.js +227 -0
  230. package/dist/slhdsa/embedded.d.ts +1 -0
  231. package/dist/slhdsa/embedded.js +26 -0
  232. package/dist/slhdsa/index.d.ts +149 -0
  233. package/dist/slhdsa/index.js +493 -0
  234. package/dist/slhdsa/params.d.ts +26 -0
  235. package/dist/slhdsa/params.js +70 -0
  236. package/dist/slhdsa/prehash.d.ts +68 -0
  237. package/dist/slhdsa/prehash.js +307 -0
  238. package/dist/slhdsa/sign.d.ts +39 -0
  239. package/dist/slhdsa/sign.js +116 -0
  240. package/dist/slhdsa/types.d.ts +129 -0
  241. package/dist/slhdsa/types.js +27 -0
  242. package/dist/slhdsa/validate.d.ts +60 -0
  243. package/dist/slhdsa/validate.js +127 -0
  244. package/dist/slhdsa/verify.d.ts +32 -0
  245. package/dist/slhdsa/verify.js +107 -0
  246. package/dist/slhdsa.wasm +0 -0
  247. package/dist/stream/header.js +3 -3
  248. package/dist/stream/index.d.ts +1 -0
  249. package/dist/stream/index.js +1 -0
  250. package/dist/stream/open-stream.js +31 -10
  251. package/dist/stream/seal-stream-pool.d.ts +1 -0
  252. package/dist/stream/seal-stream-pool.js +63 -26
  253. package/dist/stream/seal-stream.d.ts +1 -1
  254. package/dist/stream/seal-stream.js +20 -9
  255. package/dist/stream/seal.js +6 -6
  256. package/dist/stream/types.d.ts +3 -1
  257. package/dist/stream/types.js +1 -1
  258. package/dist/types.d.ts +1 -1
  259. package/dist/types.js +1 -1
  260. package/dist/utils.d.ts +3 -3
  261. package/dist/utils.js +46 -54
  262. package/dist/wasm-source.d.ts +7 -7
  263. package/dist/wasm-source.js +1 -1
  264. package/dist/x25519/embedded.d.ts +1 -0
  265. package/dist/x25519/embedded.js +31 -0
  266. package/dist/x25519/index.d.ts +43 -0
  267. package/dist/x25519/index.js +159 -0
  268. package/dist/x25519/types.d.ts +25 -0
  269. package/dist/x25519/types.js +27 -0
  270. package/dist/x25519/validate.d.ts +2 -0
  271. package/dist/x25519/validate.js +39 -0
  272. package/package.json +70 -26
  273. package/SECURITY.md +0 -163
  274. package/dist/ct-wasm.d.ts +0 -1
  275. package/dist/ct-wasm.js +0 -3
  276. package/dist/docs/aead.md +0 -363
  277. package/dist/docs/architecture.md +0 -1011
  278. package/dist/docs/argon2id.md +0 -305
  279. package/dist/docs/chacha20.md +0 -781
  280. package/dist/docs/exports.md +0 -277
  281. package/dist/docs/fortuna.md +0 -530
  282. package/dist/docs/init.md +0 -301
  283. package/dist/docs/loader.md +0 -256
  284. package/dist/docs/serpent.md +0 -617
  285. package/dist/docs/sha2.md +0 -671
  286. package/dist/docs/sha3.md +0 -612
  287. package/dist/docs/types.md +0 -416
  288. package/dist/docs/utils.md +0 -457
  289. package/dist/embedded/kyber.d.ts +0 -1
  290. package/dist/embedded/kyber.js +0 -3
  291. package/dist/kyber/embedded.d.ts +0 -1
  292. package/dist/kyber/indcpa.d.ts +0 -49
  293. package/dist/kyber/index.d.ts +0 -38
  294. package/dist/kyber/kem.d.ts +0 -21
  295. package/dist/kyber/suite.d.ts +0 -12
  296. /package/dist/{ct.wasm → cte.wasm} +0 -0
@@ -1,305 +0,0 @@
1
- <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="logo" width="120" align="left" margin="10">
2
-
3
- ### Argon2id: Memory-Hardened Password Hashing and Key Derivation
4
-
5
- leviathan-crypto does not wrap Argon2id. This document covers how to use the [`argon2id`](https://www.npmjs.com/package/argon2id) npm package directly and how to pair it with leviathan primitives for passphrase-based encryption.
6
-
7
- > ### Table of Contents
8
- > - [Why Argon2id](#why-argon2id)
9
- > - [Installation](#installation)
10
- > - [Basic usage](#basic-usage)
11
- > - [Parameter presets](#parameter-presets)
12
- > - [Password hashing and verification](#password-hashing-and-verification)
13
- > - [Passphrase-based encryption with leviathan-crypto](#passphrase-based-encryption-with-leviathan-crypto)
14
- > - [Memory note](#memory-note)
15
-
16
- ---
17
-
18
- ## Why Argon2id
19
-
20
- Password hashing is the last line of defense when a database is breached. If an
21
- attacker obtains hashed passwords, the hash function determines how expensive it
22
- 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
23
- thousand dollars of GPU hardware can test billions of PBKDF2-SHA256 candidates
24
- per second. bcrypt improves on this with a 4 KiB memory requirement that limits
25
- GPU parallelism, but 4 KiB is trivial by modern standards.
26
-
27
- Argon2 was the winner of the Password Hashing Competition (PHC, 2013–2015),
28
- selected from 24 submissions after two years of public analysis. It was designed
29
- specifically to be **memory-hard**: computing the hash requires not just CPU
30
- time but a large block of RAM that cannot be traded away. An attacker who tries
31
- to use less memory must perform exponentially more computation, making GPU and
32
- ASIC attacks economically impractical.
33
-
34
- Argon2id is the recommended variant (RFC 9106): it uses Argon2i for the first
35
- pass (resisting side-channel attacks) and Argon2d for subsequent passes
36
- (resisting GPU attacks). It is the only Argon2 variant you should use for new
37
- applications.
38
-
39
- ---
40
-
41
- ## Installation
42
-
43
- ```sh
44
- npm i argon2id
45
- ```
46
-
47
- The compiled WASM binaries are included. SIMD acceleration is used automatically
48
- where available (all modern browsers and Node ≥ 18), with a scalar fallback for
49
- environments that do not support it. Both produce identical output.
50
-
51
- ---
52
-
53
- ## Basic usage
54
-
55
- With a bundler (Rollup, Webpack, Vite):
56
-
57
- ```typescript
58
- import loadArgon2idWasm from 'argon2id';
59
-
60
- const argon2id = await loadArgon2idWasm();
61
-
62
- const hash = argon2id({
63
- password: new TextEncoder().encode('hunter2'),
64
- salt: crypto.getRandomValues(new Uint8Array(16)),
65
- passes: 2,
66
- memorySize: 19456, // KiB
67
- parallelism: 1,
68
- tagLength: 32,
69
- });
70
- // hash is a Uint8Array
71
- ```
72
-
73
- Without a bundler (Node, or browsers using `setupWasm` directly):
74
-
75
- ```typescript
76
- import setupWasm from 'argon2id/lib/setup.js';
77
- import { readFileSync } from 'fs';
78
-
79
- const argon2id = await setupWasm(
80
- importObj => WebAssembly.instantiate(readFileSync('node_modules/argon2id/dist/simd.wasm'), importObj),
81
- importObj => WebAssembly.instantiate(readFileSync('node_modules/argon2id/dist/no-simd.wasm'), importObj),
82
- );
83
- ```
84
-
85
- The hash function signature is the same either way.
86
-
87
- ---
88
-
89
- ## Parameter presets
90
-
91
- These align with OWASP and RFC 9106 recommendations:
92
-
93
- | Name | `memorySize` | `passes` | `parallelism` | `tagLength` | Use case |
94
- |------|-------------|---------|---------------|-------------|----------|
95
- | INTERACTIVE | 19456 KiB | 2 | 1 | 32 | Login forms, session tokens |
96
- | SENSITIVE | 65536 KiB | 3 | 4 | 32 | Master passwords, high-value secrets |
97
-
98
- **INTERACTIVE** (~200–500 ms on modern hardware) is the right default for user
99
- login. **SENSITIVE** (~1–2 s, 64 MiB) is for situations where latency is
100
- acceptable in exchange for significantly stronger resistance: master passwords,
101
- recovery keys, encryption keys derived from a passphrase.
102
-
103
- A function that requires 64 MiB of RAM per evaluation means a GPU with 8 GiB
104
- of VRAM can only run ~128 evaluations in parallel regardless of shader core
105
- count. PBKDF2 at any practical iteration count cannot approach this resistance.
106
-
107
- ---
108
-
109
- ## Password hashing and verification
110
-
111
- ```typescript
112
- import loadArgon2idWasm from 'argon2id';
113
- import { constantTimeEqual } from 'leviathan-crypto';
114
-
115
- const argon2id = await loadArgon2idWasm();
116
-
117
- // Registration — hash and store
118
- const salt = crypto.getRandomValues(new Uint8Array(16));
119
- const hash = argon2id({
120
- password: new TextEncoder().encode(password),
121
- salt,
122
- passes: 2,
123
- memorySize: 19456,
124
- parallelism: 1,
125
- tagLength: 32,
126
- });
127
-
128
- // Store hash, salt, and params together. Salt is not secret.
129
- db.store(userId, { hash, salt, passes: 2, memorySize: 19456, parallelism: 1 });
130
-
131
- // Verification — recompute and compare
132
- const stored = db.load(userId);
133
- const candidate = argon2id({
134
- password: new TextEncoder().encode(candidatePassword),
135
- salt: stored.salt,
136
- passes: stored.passes,
137
- memorySize: stored.memorySize,
138
- parallelism: stored.parallelism,
139
- tagLength: 32,
140
- });
141
-
142
- // constantTimeEqual from leviathan-crypto prevents timing side-channels
143
- const valid = constantTimeEqual(candidate, stored.hash);
144
- ```
145
-
146
- > [!IMPORTANT]
147
- > The `salt` **must** be stored alongside the hash. It is not secret, but without
148
- > the original salt the hash cannot be recomputed and verification will always
149
- > fail. Store `salt`, `passes`, `memorySize`, and `parallelism` together as a
150
- > unit.
151
-
152
- ---
153
-
154
- ## Passphrase-based encryption with leviathan-crypto
155
-
156
- Argon2id produces a root key from a passphrase. HKDF-SHA256 from leviathan then
157
- expands that root key into the key material `Seal` and `XChaCha20Poly1305` expect.
158
- Keeping the two steps separate means the expensive Argon2id call happens once
159
- per passphrase, and HKDF handles any further key material needed.
160
-
161
- ### With Seal + SerpentCipher
162
-
163
- `SerpentCipher` takes a 32-byte key. HKDF expands the 32-byte Argon2id output
164
- to 32 bytes with a domain-separation info string:
165
-
166
- ```typescript
167
- import loadArgon2idWasm from 'argon2id';
168
- import { init, Seal, SerpentCipher, HKDF_SHA256 } from 'leviathan-crypto';
169
-
170
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded';
171
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded';
172
-
173
- await init({ serpent: serpentWasm, sha2: sha2Wasm });
174
- const argon2id = await loadArgon2idWasm();
175
-
176
- // ── Encrypt ──────────────────────────────────────────────────────────────────
177
-
178
- const argonSalt = crypto.getRandomValues(new Uint8Array(16));
179
- const rootKey = argon2id({
180
- password: new TextEncoder().encode(passphrase),
181
- salt: argonSalt,
182
- passes: 2,
183
- memorySize: 19456,
184
- parallelism: 1,
185
- tagLength: 32,
186
- });
187
-
188
- const hkdf = new HKDF_SHA256();
189
- const key = hkdf.derive(rootKey, argonSalt, new TextEncoder().encode('myapp-serpent-key'), 32);
190
- hkdf.dispose();
191
-
192
- const blob = Seal.encrypt(SerpentCipher, key, plaintext);
193
-
194
- // Store with blob — all required for decryption
195
- const envelope = { blob, argonSalt };
196
-
197
- // ── Decrypt ──────────────────────────────────────────────────────────────────
198
-
199
- const rootKey2 = argon2id({
200
- password: new TextEncoder().encode(passphrase),
201
- salt: envelope.argonSalt,
202
- passes: 2,
203
- memorySize: 19456,
204
- parallelism: 1,
205
- tagLength: 32,
206
- });
207
-
208
- const hkdf2 = new HKDF_SHA256();
209
- const key2 = hkdf2.derive(rootKey2, envelope.argonSalt, new TextEncoder().encode('myapp-serpent-key'), 32);
210
- hkdf2.dispose();
211
-
212
- const decrypted = Seal.decrypt(SerpentCipher, key2, envelope.blob);
213
- ```
214
-
215
- ### With XChaCha20Poly1305
216
-
217
- `XChaCha20Poly1305` takes a 32-byte key and a 24-byte nonce. The nonce is
218
- generated fresh per encryption; only the Argon2id salt needs to be stored:
219
-
220
- ```typescript
221
- import loadArgon2idWasm from 'argon2id';
222
- import { init, XChaCha20Poly1305, HKDF_SHA256 } from 'leviathan-crypto';
223
-
224
- import { chacha20Wasm } from 'leviathan-crypto/chacha20/embedded';
225
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded';
226
-
227
- await init({ chacha20: chacha20Wasm, sha2: sha2Wasm });
228
- const argon2id = await loadArgon2idWasm();
229
-
230
- // ── Encrypt ──────────────────────────────────────────────────────────────────
231
-
232
- const argonSalt = crypto.getRandomValues(new Uint8Array(16));
233
- const rootKey = argon2id({
234
- password: new TextEncoder().encode(passphrase),
235
- salt: argonSalt,
236
- passes: 2,
237
- memorySize: 19456,
238
- parallelism: 1,
239
- tagLength: 32,
240
- });
241
-
242
- // tagLength: 32 already matches XChaCha20Poly1305's expected key size
243
- // HKDF is optional here but included for domain separation.
244
- const hkdf = new HKDF_SHA256();
245
- const key = hkdf.derive(rootKey, argonSalt, new TextEncoder().encode('myapp-xchacha20-key'), 32);
246
- hkdf.dispose();
247
-
248
- const nonce = crypto.getRandomValues(new Uint8Array(24));
249
- const xc = new XChaCha20Poly1305();
250
- const ct = xc.encrypt(key, nonce, plaintext);
251
- xc.dispose();
252
-
253
- const envelope = { ct, nonce, argonSalt };
254
-
255
- // ── Decrypt ──────────────────────────────────────────────────────────────────
256
-
257
- const rootKey2 = argon2id({
258
- password: new TextEncoder().encode(passphrase),
259
- salt: envelope.argonSalt,
260
- passes: 2,
261
- memorySize: 19456,
262
- parallelism: 1,
263
- tagLength: 32,
264
- });
265
-
266
- const hkdf2 = new HKDF_SHA256();
267
- const key2 = hkdf2.derive(rootKey2, envelope.argonSalt, new TextEncoder().encode('myapp-xchacha20-key'), 32);
268
- hkdf2.dispose();
269
-
270
- const xc2 = new XChaCha20Poly1305();
271
- const decrypted = xc2.decrypt(key2, envelope.nonce, envelope.ct);
272
- xc2.dispose();
273
- ```
274
-
275
- > [!CAUTION]
276
- > Never reuse an Argon2id salt across different passphrases or key derivation
277
- > contexts. Generate a fresh random salt for each new encryption envelope. The
278
- > salt is not secret. Store it in plaintext alongside the ciphertext.
279
-
280
- ---
281
-
282
- ## Memory note
283
-
284
- > [!IMPORTANT]
285
- > Each call to `loadArgon2idWasm()` instantiates a separate WASM instance. The
286
- > package's own documentation recommends reloading the module between hashes when
287
- > the `memorySize` varies significantly, since WASM linear memory is not
288
- > deallocated between calls. For a single `memorySize` used consistently (the
289
- > common case), one `await loadArgon2idWasm()` call at startup is correct.
290
-
291
- ---
292
-
293
-
294
- ## Cross-References
295
-
296
- | Document | Description |
297
- | -------- | ----------- |
298
- | [index](./README.md) | Project Documentation index |
299
- | [sha2](./sha2.md) | HKDF-SHA256 for key expansion from Argon2id root keys |
300
- | [serpent](./serpent.md) | `SerpentCipher`: Serpent-256 cipher suite (use with `Seal` and Argon2id-derived keys) |
301
- | [authenticated encryption](./aead.md) | `Seal`, `SealStream`, `OpenStream`: cipher-agnostic AEAD APIs using a `CipherSuite` such as `SerpentCipher` or `XChaCha20Cipher` |
302
- | [chacha20](./chacha20.md) | XChaCha20Poly1305: ChaCha20 authenticated encryption (pairs with Argon2id-derived keys) |
303
- | [utils](./utils.md) | `randomBytes` for generating salts, `constantTimeEqual` for hash verification |
304
- | [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
305
-