leviathan-crypto 2.0.1 → 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 (312) hide show
  1. package/CLAUDE.md +88 -281
  2. package/LICENSE +4 -0
  3. package/README.md +275 -87
  4. package/dist/aes/aes-cbc.d.ts +40 -0
  5. package/dist/aes/aes-cbc.js +158 -0
  6. package/dist/aes/aes-ctr.d.ts +50 -0
  7. package/dist/aes/aes-ctr.js +141 -0
  8. package/dist/aes/aes-gcm-siv.d.ts +67 -0
  9. package/dist/aes/aes-gcm-siv.js +217 -0
  10. package/dist/aes/aes-gcm.d.ts +61 -0
  11. package/dist/aes/aes-gcm.js +226 -0
  12. package/dist/aes/cipher-suite.d.ts +21 -0
  13. package/dist/aes/cipher-suite.js +179 -0
  14. package/dist/aes/embedded.d.ts +1 -0
  15. package/dist/aes/embedded.js +26 -0
  16. package/dist/aes/generator.d.ts +14 -0
  17. package/dist/aes/generator.js +103 -0
  18. package/dist/aes/index.d.ts +58 -0
  19. package/dist/aes/index.js +125 -0
  20. package/dist/aes/ops.d.ts +60 -0
  21. package/dist/aes/ops.js +164 -0
  22. package/dist/aes/pool-worker.d.ts +1 -0
  23. package/dist/aes/pool-worker.js +92 -0
  24. package/dist/aes/types.d.ts +1 -0
  25. package/dist/aes/types.js +23 -0
  26. package/dist/aes.wasm +0 -0
  27. package/dist/blake3/embedded.d.ts +1 -0
  28. package/dist/blake3/embedded.js +26 -0
  29. package/dist/blake3/index.d.ts +143 -0
  30. package/dist/blake3/index.js +620 -0
  31. package/dist/blake3/types.d.ts +102 -0
  32. package/dist/blake3/types.js +31 -0
  33. package/dist/blake3/validate.d.ts +29 -0
  34. package/dist/blake3/validate.js +80 -0
  35. package/dist/blake3.wasm +0 -0
  36. package/dist/chacha20/cipher-suite.d.ts +10 -0
  37. package/dist/chacha20/cipher-suite.js +98 -13
  38. package/dist/chacha20/generator.d.ts +12 -0
  39. package/dist/chacha20/generator.js +91 -0
  40. package/dist/chacha20/index.d.ts +100 -3
  41. package/dist/chacha20/index.js +169 -35
  42. package/dist/chacha20/ops.d.ts +57 -6
  43. package/dist/chacha20/ops.js +107 -27
  44. package/dist/chacha20/pool-worker.js +14 -0
  45. package/dist/chacha20/types.d.ts +1 -32
  46. package/dist/cte-wasm.d.ts +1 -0
  47. package/dist/cte-wasm.js +3 -0
  48. package/dist/cte.wasm +0 -0
  49. package/dist/curve25519.wasm +0 -0
  50. package/dist/ecdsa/der.d.ts +23 -0
  51. package/dist/ecdsa/der.js +192 -0
  52. package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
  53. package/dist/ecdsa/ecprivatekey-der.js +230 -0
  54. package/dist/ecdsa/embedded.d.ts +1 -0
  55. package/dist/ecdsa/embedded.js +25 -0
  56. package/dist/ecdsa/index.d.ts +124 -0
  57. package/dist/ecdsa/index.js +366 -0
  58. package/dist/ecdsa/types.d.ts +31 -0
  59. package/dist/ecdsa/types.js +28 -0
  60. package/dist/ecdsa/validate.d.ts +18 -0
  61. package/dist/ecdsa/validate.js +92 -0
  62. package/dist/ed25519/embedded.d.ts +1 -0
  63. package/dist/ed25519/embedded.js +31 -0
  64. package/dist/ed25519/index.d.ts +70 -0
  65. package/dist/ed25519/index.js +308 -0
  66. package/dist/ed25519/types.d.ts +27 -0
  67. package/dist/ed25519/types.js +27 -0
  68. package/dist/ed25519/validate.d.ts +7 -0
  69. package/dist/ed25519/validate.js +77 -0
  70. package/dist/embedded/aes-pool-worker.d.ts +1 -0
  71. package/dist/embedded/aes-pool-worker.js +5 -0
  72. package/dist/embedded/aes.d.ts +1 -0
  73. package/dist/embedded/aes.js +3 -0
  74. package/dist/embedded/blake3.d.ts +1 -0
  75. package/dist/embedded/blake3.js +3 -0
  76. package/dist/embedded/chacha20-pool-worker.d.ts +1 -0
  77. package/dist/embedded/chacha20-pool-worker.js +5 -0
  78. package/dist/embedded/chacha20.d.ts +1 -1
  79. package/dist/embedded/chacha20.js +2 -2
  80. package/dist/embedded/curve25519.d.ts +1 -0
  81. package/dist/embedded/curve25519.js +3 -0
  82. package/dist/embedded/mldsa.d.ts +1 -0
  83. package/dist/embedded/mldsa.js +3 -0
  84. package/dist/embedded/mlkem.d.ts +1 -0
  85. package/dist/embedded/mlkem.js +3 -0
  86. package/dist/embedded/p256.d.ts +1 -0
  87. package/dist/embedded/p256.js +3 -0
  88. package/dist/embedded/serpent-pool-worker.d.ts +1 -0
  89. package/dist/embedded/serpent-pool-worker.js +5 -0
  90. package/dist/embedded/serpent.d.ts +1 -1
  91. package/dist/embedded/serpent.js +2 -2
  92. package/dist/embedded/sha2.d.ts +1 -1
  93. package/dist/embedded/sha2.js +2 -2
  94. package/dist/embedded/sha3.d.ts +1 -1
  95. package/dist/embedded/sha3.js +2 -2
  96. package/dist/embedded/slhdsa.d.ts +1 -0
  97. package/dist/embedded/slhdsa.js +3 -0
  98. package/dist/errors.d.ts +92 -1
  99. package/dist/errors.js +111 -1
  100. package/dist/fortuna.d.ts +18 -12
  101. package/dist/fortuna.js +166 -99
  102. package/dist/index.d.ts +42 -11
  103. package/dist/index.js +65 -20
  104. package/dist/init.d.ts +1 -3
  105. package/dist/init.js +73 -7
  106. package/dist/keccak/embedded.js +1 -1
  107. package/dist/keccak/index.d.ts +2 -0
  108. package/dist/keccak/index.js +4 -2
  109. package/dist/loader.d.ts +1 -19
  110. package/dist/loader.js +26 -32
  111. package/dist/merkle/blake3-tree.d.ts +35 -0
  112. package/dist/merkle/blake3-tree.js +187 -0
  113. package/dist/merkle/checkpoint.d.ts +58 -0
  114. package/dist/merkle/checkpoint.js +217 -0
  115. package/dist/merkle/index.d.ts +19 -0
  116. package/dist/merkle/index.js +37 -0
  117. package/dist/merkle/merkle-log.d.ts +130 -0
  118. package/dist/merkle/merkle-log.js +207 -0
  119. package/dist/merkle/merkle-verifier.d.ts +126 -0
  120. package/dist/merkle/merkle-verifier.js +296 -0
  121. package/dist/merkle/proof.d.ts +70 -0
  122. package/dist/merkle/proof.js +300 -0
  123. package/dist/merkle/sha256-tree.d.ts +33 -0
  124. package/dist/merkle/sha256-tree.js +145 -0
  125. package/dist/merkle/signed-log.d.ts +156 -0
  126. package/dist/merkle/signed-log.js +356 -0
  127. package/dist/merkle/signed-note.d.ts +309 -0
  128. package/dist/merkle/signed-note.js +648 -0
  129. package/dist/merkle/sth.d.ts +31 -0
  130. package/dist/merkle/sth.js +31 -0
  131. package/dist/merkle/storage.d.ts +40 -0
  132. package/dist/merkle/storage.js +71 -0
  133. package/dist/merkle/tree.d.ts +68 -0
  134. package/dist/merkle/tree.js +94 -0
  135. package/dist/mldsa/embedded.d.ts +1 -0
  136. package/dist/{kyber → mldsa}/embedded.js +5 -5
  137. package/dist/mldsa/expand.d.ts +53 -0
  138. package/dist/mldsa/expand.js +188 -0
  139. package/dist/mldsa/format.d.ts +16 -0
  140. package/dist/mldsa/format.js +68 -0
  141. package/dist/mldsa/hashvariant.d.ts +32 -0
  142. package/dist/mldsa/hashvariant.js +248 -0
  143. package/dist/mldsa/index.d.ts +142 -0
  144. package/dist/mldsa/index.js +463 -0
  145. package/dist/mldsa/keygen.d.ts +16 -0
  146. package/dist/mldsa/keygen.js +232 -0
  147. package/dist/mldsa/params.d.ts +21 -0
  148. package/dist/mldsa/params.js +55 -0
  149. package/dist/mldsa/sha3-helpers.d.ts +30 -0
  150. package/dist/mldsa/sha3-helpers.js +124 -0
  151. package/dist/mldsa/sign.d.ts +36 -0
  152. package/dist/mldsa/sign.js +380 -0
  153. package/dist/mldsa/types.d.ts +91 -0
  154. package/dist/mldsa/types.js +25 -0
  155. package/dist/mldsa/validate.d.ts +55 -0
  156. package/dist/mldsa/validate.js +125 -0
  157. package/dist/mldsa/verify.d.ts +29 -0
  158. package/dist/mldsa/verify.js +269 -0
  159. package/dist/mldsa.wasm +0 -0
  160. package/dist/mlkem/embedded.d.ts +1 -0
  161. package/dist/mlkem/embedded.js +27 -0
  162. package/dist/mlkem/indcpa.d.ts +49 -0
  163. package/dist/{kyber → mlkem}/indcpa.js +48 -48
  164. package/dist/mlkem/index.d.ts +37 -0
  165. package/dist/{kyber → mlkem}/index.js +41 -31
  166. package/dist/mlkem/kem.d.ts +21 -0
  167. package/dist/{kyber → mlkem}/kem.js +48 -13
  168. package/dist/{kyber → mlkem}/params.d.ts +4 -4
  169. package/dist/{kyber → mlkem}/params.js +2 -2
  170. package/dist/mlkem/suite.d.ts +12 -0
  171. package/dist/{kyber → mlkem}/suite.js +17 -12
  172. package/dist/{kyber → mlkem}/types.d.ts +4 -3
  173. package/dist/{kyber → mlkem}/types.js +1 -1
  174. package/dist/mlkem/validate.d.ts +23 -0
  175. package/dist/{kyber → mlkem}/validate.js +24 -20
  176. package/dist/{kyber.wasm → mlkem.wasm} +0 -0
  177. package/dist/p256.wasm +0 -0
  178. package/dist/ratchet/index.d.ts +8 -0
  179. package/dist/ratchet/index.js +38 -0
  180. package/dist/ratchet/kdf-chain.d.ts +13 -0
  181. package/dist/ratchet/kdf-chain.js +85 -0
  182. package/dist/ratchet/ratchet-keypair.d.ts +9 -0
  183. package/dist/ratchet/ratchet-keypair.js +61 -0
  184. package/dist/ratchet/root-kdf.d.ts +4 -0
  185. package/dist/ratchet/root-kdf.js +124 -0
  186. package/dist/ratchet/skipped-key-store.d.ts +14 -0
  187. package/dist/ratchet/skipped-key-store.js +154 -0
  188. package/dist/ratchet/types.d.ts +36 -0
  189. package/dist/ratchet/types.js +26 -0
  190. package/dist/serpent/cipher-suite.d.ts +10 -0
  191. package/dist/serpent/cipher-suite.js +144 -56
  192. package/dist/serpent/generator.d.ts +12 -0
  193. package/dist/serpent/generator.js +97 -0
  194. package/dist/serpent/index.d.ts +62 -1
  195. package/dist/serpent/index.js +97 -21
  196. package/dist/serpent/pool-worker.js +28 -102
  197. package/dist/serpent/serpent-cbc.d.ts +16 -6
  198. package/dist/serpent/serpent-cbc.js +58 -37
  199. package/dist/serpent/shared-ops.d.ts +63 -0
  200. package/dist/serpent/shared-ops.js +178 -0
  201. package/dist/serpent/types.d.ts +1 -5
  202. package/dist/serpent.wasm +0 -0
  203. package/dist/sha2/hash.d.ts +2 -0
  204. package/dist/sha2/hash.js +53 -0
  205. package/dist/sha2/hkdf.js +5 -5
  206. package/dist/sha2/index.d.ts +22 -1
  207. package/dist/sha2/index.js +80 -11
  208. package/dist/sha2/types.d.ts +41 -2
  209. package/dist/sha2.wasm +0 -0
  210. package/dist/sha3/hash.d.ts +2 -0
  211. package/dist/sha3/hash.js +53 -0
  212. package/dist/sha3/index.d.ts +87 -3
  213. package/dist/sha3/index.js +317 -19
  214. package/dist/sha3/kmac.d.ts +121 -0
  215. package/dist/sha3/kmac.js +800 -0
  216. package/dist/sha3.wasm +0 -0
  217. package/dist/shared/pkcs7.d.ts +22 -0
  218. package/dist/shared/pkcs7.js +84 -0
  219. package/dist/sign/ctx.d.ts +41 -0
  220. package/dist/sign/ctx.js +102 -0
  221. package/dist/sign/envelope.d.ts +45 -0
  222. package/dist/sign/envelope.js +152 -0
  223. package/dist/sign/hasher.d.ts +9 -0
  224. package/dist/sign/hasher.js +132 -0
  225. package/dist/sign/index.d.ts +11 -0
  226. package/dist/sign/index.js +34 -0
  227. package/dist/sign/sign-stream.d.ts +25 -0
  228. package/dist/sign/sign-stream.js +112 -0
  229. package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
  230. package/dist/sign/suites/ecdsa-p256.js +120 -0
  231. package/dist/sign/suites/ed25519.d.ts +3 -0
  232. package/dist/sign/suites/ed25519.js +165 -0
  233. package/dist/sign/suites/hybrid-classical.d.ts +23 -0
  234. package/dist/sign/suites/hybrid-classical.js +526 -0
  235. package/dist/sign/suites/hybrid-pq.d.ts +4 -0
  236. package/dist/sign/suites/hybrid-pq.js +234 -0
  237. package/dist/sign/suites/mldsa.d.ts +7 -0
  238. package/dist/sign/suites/mldsa.js +161 -0
  239. package/dist/sign/suites/slhdsa.d.ts +7 -0
  240. package/dist/sign/suites/slhdsa.js +176 -0
  241. package/dist/sign/types.d.ts +106 -0
  242. package/dist/sign/types.js +28 -0
  243. package/dist/sign/verify-stream.d.ts +30 -0
  244. package/dist/sign/verify-stream.js +227 -0
  245. package/dist/slhdsa/embedded.d.ts +1 -0
  246. package/dist/slhdsa/embedded.js +26 -0
  247. package/dist/slhdsa/index.d.ts +149 -0
  248. package/dist/slhdsa/index.js +493 -0
  249. package/dist/slhdsa/params.d.ts +26 -0
  250. package/dist/slhdsa/params.js +70 -0
  251. package/dist/slhdsa/prehash.d.ts +68 -0
  252. package/dist/slhdsa/prehash.js +307 -0
  253. package/dist/slhdsa/sign.d.ts +39 -0
  254. package/dist/slhdsa/sign.js +116 -0
  255. package/dist/slhdsa/types.d.ts +129 -0
  256. package/dist/slhdsa/types.js +27 -0
  257. package/dist/slhdsa/validate.d.ts +60 -0
  258. package/dist/slhdsa/validate.js +127 -0
  259. package/dist/slhdsa/verify.d.ts +32 -0
  260. package/dist/slhdsa/verify.js +107 -0
  261. package/dist/slhdsa.wasm +0 -0
  262. package/dist/stream/header.js +8 -8
  263. package/dist/stream/index.d.ts +1 -0
  264. package/dist/stream/index.js +1 -0
  265. package/dist/stream/open-stream.js +65 -22
  266. package/dist/stream/seal-stream-pool.d.ts +2 -0
  267. package/dist/stream/seal-stream-pool.js +100 -33
  268. package/dist/stream/seal-stream.d.ts +1 -1
  269. package/dist/stream/seal-stream.js +48 -19
  270. package/dist/stream/seal.js +6 -6
  271. package/dist/stream/types.d.ts +3 -1
  272. package/dist/stream/types.js +1 -1
  273. package/dist/types.d.ts +22 -1
  274. package/dist/types.js +1 -1
  275. package/dist/utils.d.ts +9 -10
  276. package/dist/utils.js +84 -59
  277. package/dist/wasm-source.d.ts +9 -8
  278. package/dist/wasm-source.js +1 -1
  279. package/dist/x25519/embedded.d.ts +1 -0
  280. package/dist/x25519/embedded.js +31 -0
  281. package/dist/x25519/index.d.ts +43 -0
  282. package/dist/x25519/index.js +159 -0
  283. package/dist/x25519/types.d.ts +25 -0
  284. package/dist/x25519/types.js +27 -0
  285. package/dist/x25519/validate.d.ts +2 -0
  286. package/dist/x25519/validate.js +39 -0
  287. package/package.json +123 -64
  288. package/SECURITY.md +0 -276
  289. package/dist/ct-wasm.d.ts +0 -1
  290. package/dist/ct-wasm.js +0 -3
  291. package/dist/ct.wasm +0 -0
  292. package/dist/docs/aead.md +0 -323
  293. package/dist/docs/architecture.md +0 -932
  294. package/dist/docs/argon2id.md +0 -302
  295. package/dist/docs/chacha20.md +0 -674
  296. package/dist/docs/exports.md +0 -241
  297. package/dist/docs/fortuna.md +0 -313
  298. package/dist/docs/init.md +0 -302
  299. package/dist/docs/loader.md +0 -161
  300. package/dist/docs/serpent.md +0 -519
  301. package/dist/docs/sha2.md +0 -613
  302. package/dist/docs/sha3.md +0 -546
  303. package/dist/docs/types.md +0 -276
  304. package/dist/docs/utils.md +0 -367
  305. package/dist/embedded/kyber.d.ts +0 -1
  306. package/dist/embedded/kyber.js +0 -3
  307. package/dist/kyber/embedded.d.ts +0 -1
  308. package/dist/kyber/indcpa.d.ts +0 -49
  309. package/dist/kyber/index.d.ts +0 -38
  310. package/dist/kyber/kem.d.ts +0 -21
  311. package/dist/kyber/suite.d.ts +0 -13
  312. package/dist/kyber/validate.d.ts +0 -19
@@ -1,519 +0,0 @@
1
- # Serpent-256 TypeScript API
2
-
3
- > [!NOTE]
4
- > See [Serpent implementation audit](./serpent_audit.md) for algorithm correctness verifications.
5
-
6
- > ### Table of Contents
7
- > - [Overview](#overview)
8
- > - [Security Notes](#security-notes)
9
- > - [Module Init](#module-init)
10
- > - [API Reference](#api-reference)
11
- > - [Usage Examples](#usage-examples)
12
- > - [Error Conditions](#error-conditions)
13
-
14
- ---
15
-
16
- ## Overview
17
-
18
- `SerpentCipher` is the primary API for authenticated Serpent-256 encryption. Pass it
19
- to `Seal` for one-shot AEAD, or to `SealStream`/`OpenStream` for streaming. There is
20
- no manual IV generation, no separate MAC step, and no room for misuse. Internally it
21
- uses Encrypt-then-MAC (Serpent-CBC + HMAC-SHA-256) with HKDF key derivation.
22
-
23
- For advanced use cases, three lower-level classes are available: `Serpent` (raw
24
- 16-byte block operations), `SerpentCtr` (counter mode streaming), and `SerpentCbc`
25
- (cipher block chaining with PKCS7 padding). These are unauthenticated and require
26
- explicit opt-in.
27
-
28
- Serpent was an AES finalist. It uses 32 rounds versus AES's 10 to 14, yielding a
29
- larger security margin at comparable speed in WASM.
30
-
31
- ---
32
-
33
- ## Security Notes
34
-
35
- > [!IMPORTANT]
36
- > Read this section carefully before using any Serpent class. These are not
37
- > theoretical concerns. Ignoring them will render encryption useless.
38
-
39
- ### SerpentCbc and SerpentCtr are unauthenticated
40
-
41
- This is the most dangerous mistake you can make with this module. An attacker who
42
- can modify ciphertext encrypted with `SerpentCbc` or `SerpentCtr` will produce
43
- corrupted plaintext on decryption. Decryption succeeds without any indication of
44
- tampering. There is no integrity check. Your caller receives garbage and has no way
45
- to distinguish it from the original message.
46
-
47
- `Seal` with `SerpentCipher` eliminates this problem. It computes an HMAC tag over
48
- the ciphertext and verifies it before decryption. If anything has been modified,
49
- `Seal.decrypt()` throws instead of returning corrupted data.
50
-
51
- `Seal` with `XChaCha20Cipher` is an alternative using a different cipher.
52
- See [chacha20.md](./chacha20.md).
53
-
54
- ### Never reuse a nonce or IV with the same key
55
-
56
- In CTR mode, reusing a nonce with the same key is catastrophic. It produces the
57
- same keystream, which means an attacker can XOR two ciphertexts together and
58
- recover both plaintexts. Always generate a fresh random nonce for each message.
59
- In CBC mode, the IV must be random and unpredictable for each encryption. A predictable IV enables chosen-plaintext attacks.
60
-
61
- Use `randomBytes(16)` to generate nonces and IVs. `Seal` with `SerpentCipher` handles IV generation internally.
62
-
63
- ### Always use 256-bit keys
64
-
65
- Unless you have a specific reason to use a shorter key, pass a 32-byte key to
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.
68
-
69
- ### Call dispose() when done
70
-
71
- Every Serpent class holds key material in WebAssembly memory. When you are finished
72
- with an instance, call `dispose()` to zero out all key material, intermediate
73
- state, and buffers. Failing to call `dispose()` means sensitive data may persist
74
- in memory longer than necessary.
75
-
76
- ---
77
-
78
- ## Module Init
79
-
80
- Each module subpath exports its own init function for consumers who want
81
- tree-shakeable imports.
82
-
83
- ### `serpentInit(source)`
84
-
85
- Initializes only the serpent WASM binary. Equivalent to calling the
86
- root `init({ serpent: serpentWasm })` but without pulling the other three
87
- modules into the bundle.
88
-
89
- **Signature:**
90
-
91
- ```typescript
92
- async function serpentInit(source: WasmSource): Promise<void>
93
- ```
94
-
95
- **Usage:**
96
-
97
- ```typescript
98
- import { serpentInit, Serpent } from 'leviathan-crypto/serpent'
99
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
100
-
101
- await serpentInit(serpentWasm)
102
- const cipher = new Serpent()
103
- ```
104
-
105
- ---
106
-
107
- ## API Reference
108
-
109
- All classes require their WASM modules to be initialized before construction.
110
- `SerpentCipher` (and therefore `Seal`, `SealStream`, `OpenStream`) requires both
111
- `serpent` and `sha2`. `Serpent`, `SerpentCtr`, and `SerpentCbc` require `serpent` only.
112
-
113
- ### SerpentCipher
114
-
115
- `CipherSuite` implementation for Serpent-256 CBC+HMAC-SHA-256. Pass to `Seal`,
116
- `SealStream`, or `OpenStream`. Never instantiated directly.
117
-
118
- Requires `init({ serpent: serpentWasm, sha2: sha2Wasm })`.
119
-
120
- | Property | Value |
121
- |----------|-------|
122
- | `formatEnum` | `0x02` |
123
- | `keySize` | `32` |
124
- | `tagSize` | `32` (HMAC-SHA-256) |
125
- | `padded` | `true` (PKCS7) |
126
- | `wasmModules` | `['serpent', 'sha2']` |
127
-
128
- #### `SerpentCipher.keygen(): Uint8Array`
129
-
130
- Returns `randomBytes(32)`. Convenience method. Not on the `CipherSuite` interface.
131
-
132
- #### Usage with `Seal`
133
-
134
- ```typescript
135
- import { init, Seal, SerpentCipher } from 'leviathan-crypto'
136
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
137
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
138
-
139
- await init({ serpent: serpentWasm, sha2: sha2Wasm })
140
-
141
- const key = SerpentCipher.keygen()
142
- const blob = Seal.encrypt(SerpentCipher, key, plaintext)
143
- const pt = Seal.decrypt(SerpentCipher, key, blob) // throws on tamper
144
- ```
145
-
146
- #### Usage with `SealStream` / `OpenStream`
147
-
148
- ```typescript
149
- import { SealStream, OpenStream } from 'leviathan-crypto/stream'
150
- import { SerpentCipher } from 'leviathan-crypto/serpent'
151
-
152
- const sealer = new SealStream(SerpentCipher, key)
153
- const preamble = sealer.preamble // 20 bytes, send before first chunk
154
- const ct0 = sealer.push(chunk0)
155
- const ctLast = sealer.finalize(lastChunk)
156
-
157
- const opener = new OpenStream(SerpentCipher, key, preamble)
158
- const pt0 = opener.pull(ct0)
159
- const ptLast = opener.finalize(ctLast)
160
- ```
161
-
162
- See [aead.md](./aead.md) for the full `Seal`, `SealStream`, and `OpenStream` API.
163
-
164
- ---
165
-
166
- ### Serpent
167
-
168
- Raw Serpent block encryption and decryption. Operates on exactly 16-byte blocks.
169
- This class is a low-level building block; use `Seal` with `SerpentCipher` for most purposes.
170
-
171
- ```typescript
172
- class Serpent {
173
- constructor()
174
- loadKey(key: Uint8Array): void
175
- encryptBlock(plaintext: Uint8Array): Uint8Array
176
- decryptBlock(ciphertext: Uint8Array): Uint8Array
177
- dispose(): void
178
- }
179
- ```
180
-
181
- #### `constructor()`
182
-
183
- Creates a new Serpent instance. Throws if `init({ serpent: serpentWasm })` has not been called.
184
-
185
- ---
186
-
187
- #### `loadKey(key: Uint8Array): void`
188
-
189
- Loads and expands a key for subsequent block operations. Must be called before
190
- `encryptBlock()` or `decryptBlock()`.
191
-
192
- - **key**: 16, 24, or 32 bytes. Throws `RangeError` if the length is invalid.
193
-
194
- ---
195
-
196
- #### `encryptBlock(plaintext: Uint8Array): Uint8Array`
197
-
198
- Encrypts a single 16-byte block and returns the 16-byte ciphertext.
199
-
200
- - **plaintext**: exactly 16 bytes. Throws `RangeError` if the length is not 16.
201
-
202
- ---
203
-
204
- #### `decryptBlock(ciphertext: Uint8Array): Uint8Array`
205
-
206
- Decrypts a single 16-byte block and returns the 16-byte plaintext.
207
-
208
- - **ciphertext**: exactly 16 bytes. Throws `RangeError` if the length is not 16.
209
-
210
- ---
211
-
212
- #### `dispose(): void`
213
-
214
- Wipes all key material and intermediate state from WASM memory. Always call this
215
- when you are done with the instance.
216
-
217
- ---
218
-
219
- ### SerpentCtr
220
-
221
- Serpent in Counter (CTR) mode. Encrypts and decrypts data of any length as a
222
- stream of chunks.
223
-
224
- > [!WARNING]
225
- > CTR mode is unauthenticated. An attacker can modify ciphertext
226
- > without detection. Use `Seal` with `SerpentCipher` for authenticated encryption, or pair
227
- > with HMAC-SHA256 (Encrypt-then-MAC).
228
-
229
- ```typescript
230
- class SerpentCtr {
231
- constructor(opts: { dangerUnauthenticated: true })
232
- beginEncrypt(key: Uint8Array, nonce: Uint8Array): void
233
- encryptChunk(chunk: Uint8Array): Uint8Array
234
- beginDecrypt(key: Uint8Array, nonce: Uint8Array): void
235
- decryptChunk(chunk: Uint8Array): Uint8Array
236
- dispose(): void
237
- }
238
- ```
239
-
240
- #### `constructor(opts: { dangerUnauthenticated: true })`
241
-
242
- Creates a new SerpentCtr instance. Throws if `init({ serpent: serpentWasm })` has not been
243
- called. Throws if `{ dangerUnauthenticated: true }` is not passed:
244
-
245
- ```
246
- leviathan-crypto: SerpentCtr is unauthenticated — use Seal with SerpentCipher instead.
247
- To use SerpentCtr directly, pass { dangerUnauthenticated: true }.
248
- ```
249
-
250
- ---
251
-
252
- #### `beginEncrypt(key: Uint8Array, nonce: Uint8Array): void`
253
-
254
- Initializes the CTR state for encryption. Loads the key, sets the nonce, and
255
- resets the internal counter to zero.
256
-
257
- - **key**: 16, 24, or 32 bytes. Throws `RangeError` if the length is invalid.
258
- - **nonce**: exactly 16 bytes. Throws `RangeError` if the length is not 16.
259
-
260
- ---
261
-
262
- #### `encryptChunk(chunk: Uint8Array): Uint8Array`
263
-
264
- Encrypts a chunk of plaintext and returns the same-length ciphertext. Call this
265
- one or more times after `beginEncrypt()`. The internal counter advances
266
- automatically.
267
-
268
- - **chunk**: any length up to the module's internal chunk buffer size. Throws `RangeError` if the chunk exceeds the maximum size.
269
-
270
- > [!NOTE]
271
- > Automatically dispatches to the 4-wide SIMD path (`encryptChunk_simd`) when the runtime supports WebAssembly SIMD (`hasSIMD()` returns `true`), otherwise falls back to the scalar unrolled path. The dispatch is transparent with no API change required.
272
-
273
- ---
274
-
275
- #### `beginDecrypt(key: Uint8Array, nonce: Uint8Array): void`
276
-
277
- Initializes the CTR state for decryption. Functionally identical to `beginEncrypt()`. CTR mode uses the same operation in both directions.
278
-
279
- - **key**: 16, 24, or 32 bytes. Throws `RangeError` if the length is invalid.
280
- - **nonce**: exactly 16 bytes. Throws `RangeError` if the length is not 16.
281
-
282
- ---
283
-
284
- #### `decryptChunk(chunk: Uint8Array): Uint8Array`
285
-
286
- Decrypts a chunk of ciphertext and returns the same-length plaintext.
287
- Functionally identical to `encryptChunk()`.
288
-
289
- - **chunk**: any length up to the module's internal chunk buffer size. Throws `RangeError` if the chunk exceeds the maximum size.
290
-
291
- ---
292
-
293
- #### `dispose(): void`
294
-
295
- Wipes all key material and intermediate state from WASM memory.
296
-
297
- ---
298
-
299
- ### SerpentCbc
300
-
301
- Serpent in Cipher Block Chaining (CBC) mode with automatic PKCS7 padding.
302
- Encrypts and decrypts entire messages in a single call.
303
-
304
- > [!WARNING]
305
- > CBC mode is unauthenticated. Always authenticate the output with
306
- > HMAC-SHA256 (Encrypt-then-MAC) or use `Seal` with `SerpentCipher` instead.
307
-
308
- ```typescript
309
- class SerpentCbc {
310
- constructor(opts: { dangerUnauthenticated: true })
311
- encrypt(key: Uint8Array, iv: Uint8Array, plaintext: Uint8Array): Uint8Array
312
- decrypt(key: Uint8Array, iv: Uint8Array, ciphertext: Uint8Array): Uint8Array
313
- dispose(): void
314
- }
315
- ```
316
-
317
- #### `constructor(opts: { dangerUnauthenticated: true })`
318
-
319
- Creates a new SerpentCbc instance. Throws if `init({ serpent: serpentWasm })` has not been
320
- called. Throws if `{ dangerUnauthenticated: true }` is not passed:
321
-
322
- ```
323
- leviathan-crypto: SerpentCbc is unauthenticated — use Seal with SerpentCipher instead.
324
- To use SerpentCbc directly, pass { dangerUnauthenticated: true }.
325
- ```
326
-
327
- ---
328
-
329
- #### `encrypt(key: Uint8Array, iv: Uint8Array, plaintext: Uint8Array): Uint8Array`
330
-
331
- Encrypts plaintext with Serpent CBC and PKCS7 padding. The returned ciphertext is
332
- always a multiple of 16 bytes and is at least 16 bytes longer than the input (due
333
- to padding).
334
-
335
- - **key**: 16, 24, or 32 bytes. Throws `RangeError` if the length is invalid.
336
- - **iv**: exactly 16 bytes. Must be random and unique per (key, message) pair. Throws `RangeError` if the length is not 16.
337
- - **plaintext**: any length including zero. PKCS7 padding is applied automatically.
338
-
339
- Returns the ciphertext as a new `Uint8Array`.
340
-
341
- ---
342
-
343
- #### `decrypt(key: Uint8Array, iv: Uint8Array, ciphertext: Uint8Array): Uint8Array`
344
-
345
- Decrypts Serpent CBC ciphertext and strips PKCS7 padding.
346
-
347
- - **key**: 16, 24, or 32 bytes. Throws `RangeError` if the length is invalid.
348
- - **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` if the length is zero or not a multiple of 16. Also throws if PKCS7 padding is invalid, which typically indicates the wrong key, wrong IV, or corrupted ciphertext.
350
-
351
- Returns the decrypted plaintext as a new `Uint8Array`.
352
-
353
- > [!NOTE]
354
- > Automatically dispatches to the 4-wide SIMD path (`cbcDecryptChunk_simd`) when the runtime supports WebAssembly SIMD (`hasSIMD()` returns `true`), otherwise falls back to the scalar unrolled path. CBC encryption has no SIMD variant since each ciphertext block depends on the previous one.
355
-
356
- ---
357
-
358
- #### `dispose(): void`
359
-
360
- Wipes all key material and intermediate state from WASM memory.
361
-
362
- ---
363
-
364
- ## Usage Examples
365
-
366
- ### Example 1: Seal with SerpentCipher (authenticated encryption)
367
-
368
- ```typescript
369
- import { init, Seal, SerpentCipher } from 'leviathan-crypto'
370
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
371
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
372
-
373
- await init({ serpent: serpentWasm, sha2: sha2Wasm })
374
-
375
- const key = SerpentCipher.keygen()
376
- const plaintext = new TextEncoder().encode('Authenticated secret message.')
377
- const blob = Seal.encrypt(SerpentCipher, key, plaintext)
378
- const decrypted = Seal.decrypt(SerpentCipher, key, blob)
379
-
380
- console.log(new TextDecoder().decode(decrypted))
381
- // "Authenticated secret message."
382
- ```
383
-
384
- ### Example 2: CTR mode (advanced)
385
-
386
- For authenticated encryption, use `Seal` with `SerpentCipher`. Use `SerpentCtr` to
387
- encrypt data of any length. CTR mode produces ciphertext the same length as the
388
- plaintext with no padding overhead.
389
-
390
- ```typescript
391
- import { init, SerpentCtr, randomBytes } from 'leviathan-crypto';
392
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded';
393
-
394
- await init({ serpent: serpentWasm });
395
-
396
- const key = randomBytes(32); // 256-bit key
397
- const nonce = randomBytes(16); // 16-byte nonce, NEVER reuse with the same key
398
-
399
- const ctr = new SerpentCtr({ dangerUnauthenticated: true });
400
-
401
- // Encrypt
402
- ctr.beginEncrypt(key, nonce);
403
- const ciphertext1 = ctr.encryptChunk(new TextEncoder().encode('Hello, '));
404
- const ciphertext2 = ctr.encryptChunk(new TextEncoder().encode('world!'));
405
-
406
- // Decrypt (same key and nonce)
407
- ctr.beginDecrypt(key, nonce);
408
- const plain1 = ctr.decryptChunk(ciphertext1);
409
- const plain2 = ctr.decryptChunk(ciphertext2);
410
-
411
- console.log(new TextDecoder().decode(plain1)); // "Hello, "
412
- console.log(new TextDecoder().decode(plain2)); // "world!"
413
-
414
- // Wipe key material
415
- ctr.dispose();
416
- ```
417
-
418
- > [!IMPORTANT]
419
- > CTR mode is unauthenticated. An attacker can tamper with the
420
- > ciphertext without detection. Use `Seal` with `SerpentCipher` for authenticated encryption.
421
-
422
- ### Example 3: CBC mode (advanced)
423
-
424
- For authenticated encryption, use `Seal` with `SerpentCipher`. Use `SerpentCbc` for
425
- message-level encryption with automatic PKCS7 padding.
426
-
427
- ```typescript
428
- import { init, SerpentCbc, randomBytes } from 'leviathan-crypto';
429
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded';
430
-
431
- await init({ serpent: serpentWasm });
432
-
433
- const key = randomBytes(32); // 256-bit key
434
- const iv = randomBytes(16); // Random IV, must be unique per message
435
-
436
- const cbc = new SerpentCbc({ dangerUnauthenticated: true });
437
-
438
- // Encrypt
439
- const plaintext = new TextEncoder().encode('This is a secret message.');
440
- const ciphertext = cbc.encrypt(key, iv, plaintext);
441
-
442
- // Decrypt
443
- const decrypted = cbc.decrypt(key, iv, ciphertext);
444
- console.log(new TextDecoder().decode(decrypted)); // "This is a secret message."
445
-
446
- // Wipe key material
447
- cbc.dispose();
448
- ```
449
-
450
- > [!IMPORTANT]
451
- > CBC mode is unauthenticated. Use `Seal` with `SerpentCipher` for authenticated encryption.
452
-
453
- ### Example 4: Raw block operations (low-level)
454
-
455
- 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
-
458
- ```typescript
459
- import { init, Serpent } from 'leviathan-crypto';
460
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded';
461
-
462
- await init({ serpent: serpentWasm });
463
-
464
- const cipher = new Serpent();
465
-
466
- // Load a 256-bit key (32 bytes)
467
- const key = new Uint8Array(32);
468
- crypto.getRandomValues(key);
469
- cipher.loadKey(key);
470
-
471
- // Encrypt a 16-byte block
472
- const plaintext = new Uint8Array([
473
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
474
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
475
- ]);
476
- const ciphertext = cipher.encryptBlock(plaintext);
477
-
478
- // Decrypt it back
479
- const decrypted = cipher.decryptBlock(ciphertext);
480
- // decrypted is identical to plaintext
481
-
482
- // Wipe key material from memory when done
483
- cipher.dispose();
484
- ```
485
-
486
- ---
487
-
488
- ## Error Conditions
489
-
490
- | Condition | Error type | Message |
491
- |-----------|-----------|---------|
492
- | `init({ serpent: ... })` not called before constructing `Serpent` | `Error` | `leviathan-crypto: call init({ serpent: ... }) before using this class` |
493
- | `SerpentCbc` constructed without `{ dangerUnauthenticated: true }` | `Error` | `leviathan-crypto: SerpentCbc is unauthenticated — use Seal with SerpentCipher instead. To use SerpentCbc directly, pass { dangerUnauthenticated: true }.` |
494
- | `SerpentCtr` constructed without `{ dangerUnauthenticated: true }` | `Error` | `leviathan-crypto: SerpentCtr is unauthenticated — use Seal with SerpentCipher instead. To use SerpentCtr directly, pass { dangerUnauthenticated: true }.` |
495
- | Key is not 16, 24, or 32 bytes (`Serpent.loadKey`) | `RangeError` | `key must be 16, 24, or 32 bytes (got N)` |
496
- | Key is not 16, 24, or 32 bytes (`SerpentCbc`) | `RangeError` | `Serpent key must be 16, 24, or 32 bytes (got N)` |
497
- | Key is not 16, 24, or 32 bytes (`SerpentCtr`) | `RangeError` | `key must be 16, 24, or 32 bytes` |
498
- | Block is not 16 bytes (`Serpent`) | `RangeError` | `block must be 16 bytes (got N)` |
499
- | Nonce is not 16 bytes (`SerpentCtr`) | `RangeError` | `nonce must be 16 bytes (got N)` |
500
- | Chunk exceeds buffer size (`SerpentCtr`) | `RangeError` | `chunk exceeds maximum size of N bytes — split into smaller chunks` |
501
- | IV is not 16 bytes (`SerpentCbc`) | `RangeError` | `CBC IV must be 16 bytes (got N)` |
502
- | Ciphertext length is zero or not a multiple of 16 (`SerpentCbc.decrypt`) | `RangeError` | `ciphertext length must be a non-zero multiple of 16` |
503
- | Invalid PKCS7 padding on decrypt (`SerpentCbc.decrypt`) | `RangeError` | `invalid PKCS7 padding` |
504
-
505
- ---
506
-
507
- > ## Cross-References
508
- >
509
- > - [index](./README.md) — Project Documentation index
510
- > - [lexicon](./lexicon.md) — Glossary of cryptographic terms
511
- > - [architecture](./architecture.md) — architecture overview, module relationships, buffer layouts, and build pipeline
512
- > - [asm_serpent](./asm_serpent.md) — WASM implementation details and buffer layout
513
- > - [serpent_reference](./serpent_reference.md) — algorithm specification, S-boxes, linear transform, and known attacks
514
- > - [serpent_audit](./serpent_audit.md) — security audit findings (correctness, side-channel analysis)
515
- > - [authenticated encryption](./aead.md) — `Seal`, `SealStream`, `OpenStream`: use `SerpentCipher` as the suite argument
516
- > - [chacha20](./chacha20.md) — `XChaCha20Cipher`: alternative `CipherSuite` for `Seal` and streaming
517
- > - [sha2](./sha2.md) — HMAC-SHA256 and HKDF used internally by `SerpentCipher`
518
- > - [types](./types.md) — `Blockcipher`, `Streamcipher`, and `AEAD` interfaces implemented by Serpent classes
519
- > - [utils](./utils.md) — `constantTimeEqual`, `wipe`, `randomBytes` used by Serpent wrappers