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
@@ -19,9 +19,9 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/suite.ts
22
+ // src/ts/mlkem/suite.ts
23
23
  //
24
- // KyberSuite wraps a MlKemBase + inner CipherSuite into a unified CipherSuite.
24
+ // MlKemSuite, wraps a MlKemBase + inner CipherSuite into a unified CipherSuite.
25
25
  // Provides hybrid KEM + symmetric AEAD for use with SealStream / OpenStream / Seal.
26
26
  import { concat } from '../utils.js';
27
27
  import { HKDF_SHA256 } from '../sha2/index.js';
@@ -36,7 +36,7 @@ const KEM_LABEL = {
36
36
  3: 'mlkem768',
37
37
  4: 'mlkem1024',
38
38
  };
39
- export function KyberSuite(kem, inner) {
39
+ export function MlKemSuite(kem, inner) {
40
40
  const p = kem.params;
41
41
  const kemNibble = KEM_NIBBLE[p.k];
42
42
  if (!kemNibble)
@@ -50,23 +50,24 @@ export function KyberSuite(kem, inner) {
50
50
  keySize: p.ekBytes,
51
51
  decKeySize: p.dkBytes,
52
52
  kemCtSize: p.ctBytes,
53
+ commitmentSize: inner.commitmentSize,
53
54
  tagSize: inner.tagSize,
54
55
  padded: inner.padded,
55
56
  wasmChunkSize: inner.wasmChunkSize,
56
- wasmModules: [...inner.wasmModules, 'kyber', 'sha3'],
57
- deriveKeys(key, nonce, kemCt) {
57
+ wasmModules: [...inner.wasmModules, 'mlkem', 'sha3'],
58
+ deriveKeys(key, nonce, kemCt, header) {
58
59
  let sharedSecret;
59
60
  let outKemCt;
60
61
  let ctForInfo;
61
62
  if (kemCt === undefined) {
62
- // encrypt path: key = ek encapsulate to produce shared secret + ct
63
+ // encrypt path: key = ek, encapsulate to produce shared secret + ct
63
64
  const result = kem.encapsulate(key);
64
65
  sharedSecret = result.sharedSecret;
65
66
  outKemCt = result.ciphertext;
66
67
  ctForInfo = outKemCt;
67
68
  }
68
69
  else {
69
- // decrypt path: key = dk decapsulate to recover shared secret
70
+ // decrypt path: key = dk, decapsulate to recover shared secret
70
71
  sharedSecret = kem.decapsulate(key, kemCt);
71
72
  ctForInfo = kemCt;
72
73
  }
@@ -76,12 +77,16 @@ export function KyberSuite(kem, inner) {
76
77
  const derived = hkdf.derive(sharedSecret, nonce, info, inner.keySize);
77
78
  hkdf.dispose();
78
79
  sharedSecret.fill(0);
79
- // Delegate actual per-cipher key derivation to the inner suite
80
- const innerKeys = inner.deriveKeys(derived, nonce);
80
+ // Delegate actual per-cipher key derivation to the inner suite.
81
+ // Header is forwarded so XChaCha20's deriveKeys can bind it into
82
+ // HKDF info; SerpentCipher accepts and ignores it.
83
+ const innerKeys = inner.deriveKeys(derived, nonce, undefined, header);
81
84
  derived.fill(0);
82
- return outKemCt
83
- ? { bytes: innerKeys.bytes, kemCiphertext: outKemCt }
84
- : innerKeys;
85
+ if (!outKemCt)
86
+ return innerKeys;
87
+ return innerKeys.commitment
88
+ ? { bytes: innerKeys.bytes, kemCiphertext: outKemCt, commitment: innerKeys.commitment }
89
+ : { bytes: innerKeys.bytes, kemCiphertext: outKemCt };
85
90
  },
86
91
  sealChunk: inner.sealChunk.bind(inner),
87
92
  openChunk: inner.openChunk.bind(inner),
@@ -1,4 +1,4 @@
1
- export interface KyberExports {
1
+ export interface MlKemExports {
2
2
  memory: WebAssembly.Memory;
3
3
  getModuleId: () => number;
4
4
  getMemoryPages: () => number;
@@ -89,11 +89,11 @@ export interface Sha3Exports {
89
89
  shakeSqueezeBlock: () => void;
90
90
  wipeBuffers: () => void;
91
91
  }
92
- export interface KyberKeyPair {
92
+ export interface MlKemKeyPair {
93
93
  encapsulationKey: Uint8Array;
94
94
  decapsulationKey: Uint8Array;
95
95
  }
96
- export interface KyberEncapsulation {
96
+ export interface MlKemEncapsulation {
97
97
  ciphertext: Uint8Array;
98
98
  sharedSecret: Uint8Array;
99
99
  }
@@ -19,7 +19,7 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/types.ts
22
+ // src/ts/mlkem/types.ts
23
23
  //
24
24
  // ML-KEM type definitions: WASM export interfaces and KEM API types.
25
25
  export {};
@@ -1,23 +1,23 @@
1
- import type { KyberExports, Sha3Exports } from './types.js';
2
- import type { KyberParams } from './params.js';
1
+ import type { MlKemExports, Sha3Exports } from './types.js';
2
+ import type { MlKemParams } from './params.js';
3
3
  /**
4
- * Encapsulation key check FIPS 203 §7.2 (EncapsulationKeyCheck).
4
+ * Encapsulation key check, FIPS 203 §7.2 (EncapsulationKeyCheck).
5
5
  *
6
6
  * 1. Length gate: ek.length must equal params.ekBytes.
7
7
  * 2. Decode the polyvec portion via ByteDecode₁₂ (polyvec_frombytes). The
8
- * decoded coefficients are raw 12-bit values in [0, 4095] frombytes
8
+ * decoded coefficients are raw 12-bit values in [0, 4095], frombytes
9
9
  * does not reduce mod q.
10
10
  * 3. Modulus scan: every coefficient must satisfy c < Q = 3329.
11
11
  *
12
12
  * Returns true iff both gates pass. The seed ρ (final 32 bytes of ek) is
13
13
  * not checked; any 32-byte value is a valid ρ per FIPS 203.
14
14
  */
15
- export declare function checkEncapsulationKey(kx: KyberExports, params: KyberParams, ek: Uint8Array): boolean;
15
+ export declare function checkEncapsulationKey(kx: MlKemExports, params: MlKemParams, ek: Uint8Array): boolean;
16
16
  /**
17
- * Decapsulation key check FIPS 203 §7.3 (DecapsulationKeyCheck).
17
+ * Decapsulation key check, FIPS 203 §7.3 (DecapsulationKeyCheck).
18
18
  *
19
19
  * 1. Length check: dk.length == params.dkBytes
20
20
  * 2. Extract embedded ek and H(ek), verify SHA3-256(ek) matches stored H
21
21
  * 3. Also run checkEncapsulationKey on the embedded ek
22
22
  */
23
- export declare function checkDecapsulationKey(kx: KyberExports, sx: Sha3Exports, params: KyberParams, dk: Uint8Array): boolean;
23
+ export declare function checkDecapsulationKey(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, dk: Uint8Array): boolean;
@@ -19,17 +19,17 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/validate.ts
22
+ // src/ts/mlkem/validate.ts
23
23
  //
24
- // ML-KEM key validation FIPS 203 §7.2 and §7.3.
24
+ // ML-KEM key validation, FIPS 203 §7.2 and §7.3.
25
25
  import { sha3_256Hash } from './indcpa.js';
26
26
  import { constantTimeEqual } from '../utils.js';
27
27
  /**
28
- * Encapsulation key check FIPS 203 §7.2 (EncapsulationKeyCheck).
28
+ * Encapsulation key check, FIPS 203 §7.2 (EncapsulationKeyCheck).
29
29
  *
30
30
  * 1. Length gate: ek.length must equal params.ekBytes.
31
31
  * 2. Decode the polyvec portion via ByteDecode₁₂ (polyvec_frombytes). The
32
- * decoded coefficients are raw 12-bit values in [0, 4095] frombytes
32
+ * decoded coefficients are raw 12-bit values in [0, 4095], frombytes
33
33
  * does not reduce mod q.
34
34
  * 3. Modulus scan: every coefficient must satisfy c < Q = 3329.
35
35
  *
@@ -40,15 +40,15 @@ export function checkEncapsulationKey(kx, params, ek) {
40
40
  if (ek.length !== params.ekBytes)
41
41
  return false;
42
42
  const { k } = params;
43
- const kyberMem = new Uint8Array(kx.memory.buffer);
43
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
44
44
  const pkOff = kx.getPkOffset();
45
45
  const pvecOff = kx.getPolyvecSlot0();
46
- kyberMem.set(ek.subarray(0, k * 384), pkOff);
46
+ mlkemMem.set(ek.subarray(0, k * 384), pkOff);
47
47
  kx.polyvec_frombytes(pvecOff, pkOff, k);
48
48
  return kx.polyvec_modulus_check(pvecOff, k) === 0;
49
49
  }
50
50
  /**
51
- * Decapsulation key check FIPS 203 §7.3 (DecapsulationKeyCheck).
51
+ * Decapsulation key check, FIPS 203 §7.3 (DecapsulationKeyCheck).
52
52
  *
53
53
  * 1. Length check: dk.length == params.dkBytes
54
54
  * 2. Extract embedded ek and H(ek), verify SHA3-256(ek) matches stored H
Binary file
package/dist/p256.wasm ADDED
Binary file
@@ -3,4 +3,6 @@ export { ratchetInit, kemRatchetEncap, kemRatchetDecap } from './root-kdf.js';
3
3
  export { SkippedKeyStore } from './skipped-key-store.js';
4
4
  export { RatchetKeypair } from './ratchet-keypair.js';
5
5
  export type { RatchetInitResult, KemEncapResult, KemDecapResult, MlKemLike, RatchetMessageHeader, ResolveHandle, SkippedKeyStoreOpts, } from './types.js';
6
+ import { isInitialized } from '../init.js';
7
+ export { isInitialized };
6
8
  export declare function ratchetReady(): boolean;
@@ -27,6 +27,7 @@ export { ratchetInit, kemRatchetEncap, kemRatchetDecap } from './root-kdf.js';
27
27
  export { SkippedKeyStore } from './skipped-key-store.js';
28
28
  export { RatchetKeypair } from './ratchet-keypair.js';
29
29
  import { isInitialized } from '../init.js';
30
+ export { isInitialized };
30
31
  export function ratchetReady() {
31
32
  try {
32
33
  return isInitialized('sha2');
@@ -21,12 +21,12 @@
21
21
  //
22
22
  // src/ts/ratchet/kdf-chain.ts
23
23
  //
24
- // KDFChain stateful symmetric ratchet chain step (spec §5.2, KDF_SCKA_CK).
24
+ // KDFChain, stateful symmetric ratchet chain step (spec §5.2, KDF_SCKA_CK).
25
25
  // Each step derives a message key and advances the chain key via HKDF-SHA-256.
26
26
  import { HKDF_SHA256 } from '../sha2/index.js';
27
27
  import { isInitialized } from '../init.js';
28
28
  import { wipe, concat, utf8ToBytes } from '../utils.js';
29
- // Signal Double Ratchet §7.2 chain step info string
29
+ // Signal Double Ratchet §7.2, chain step info string
30
30
  const INFO_CHAIN_BYTES = utf8ToBytes('leviathan-ratchet-v1 Chain Step');
31
31
  const ZERO_SALT = new Uint8Array(32);
32
32
  export class KDFChain {
@@ -48,7 +48,7 @@ export class KDFChain {
48
48
  if (this._n >= Number.MAX_SAFE_INTEGER)
49
49
  throw new RangeError('KDFChain: counter exceeds maximum safe integer');
50
50
  const nextN = this._n + 1;
51
- // Encode counter as big-endian uint64 two u32 calls, no BigInt
51
+ // Encode counter as big-endian uint64, two u32 calls, no BigInt
52
52
  const ctrBuf = new Uint8Array(8);
53
53
  const dv = new DataView(ctrBuf.buffer);
54
54
  dv.setUint32(0, Math.floor(nextN / 0x100000000), false);
@@ -21,7 +21,7 @@
21
21
  //
22
22
  // src/ts/ratchet/ratchet-keypair.ts
23
23
  //
24
- // RatchetKeypair single-use ek/dk lifecycle for one KEM ratchet step.
24
+ // RatchetKeypair, single-use ek/dk lifecycle for one KEM ratchet step.
25
25
  // Enforces the DR spec requirement that both parties rotate encapsulation
26
26
  // keys after each KEM ratchet step.
27
27
  import { wipe } from '../utils.js';
@@ -37,7 +37,7 @@ export class RatchetKeypair {
37
37
  this._used = false;
38
38
  }
39
39
  // Decapsulate using the stored dk. May only be called once per instance.
40
- // Wipes the dk immediately after decap the dk never leaves this class.
40
+ // Wipes the dk immediately after decap, the dk never leaves this class.
41
41
  // The stored ek is passed as `ownEk` so both sides bind the identical
42
42
  // (peerEk, kemCt) pair into the HKDF info string.
43
43
  decap(kem, rk, kemCt, context) {
@@ -26,7 +26,7 @@
26
26
  import { HKDF_SHA256 } from '../sha2/index.js';
27
27
  import { isInitialized } from '../init.js';
28
28
  import { wipe, concat, utf8ToBytes } from '../utils.js';
29
- // Signal Double Ratchet §7.2 info strings
29
+ // Signal Double Ratchet §7.2, info strings
30
30
  const INFO_INIT = utf8ToBytes('leviathan-ratchet-v1 Chain Start');
31
31
  const INFO_ROOT = utf8ToBytes('leviathan-ratchet-v1 Chain Add Epoch');
32
32
  // INFO_CHAIN ('leviathan-ratchet-v1 Chain Step') is used in kdf-chain.ts
@@ -52,7 +52,7 @@ function kdfRoot(secret, salt, info) {
52
52
  h.dispose();
53
53
  }
54
54
  }
55
- // u32 big-endian length prefix same convention as serpent/cipher-suite.ts AAD encoding.
55
+ // u32 big-endian length prefix, same convention as serpent/cipher-suite.ts AAD encoding.
56
56
  function u32be(n) {
57
57
  if (!Number.isInteger(n) || n < 0 || n > 0xFFFFFFFF)
58
58
  throw new RangeError(`u32be: n must be an integer in [0, 0xFFFFFFFF] (got ${n})`);
@@ -60,7 +60,7 @@ function u32be(n) {
60
60
  new DataView(b.buffer).setUint32(0, n, false);
61
61
  return b;
62
62
  }
63
- // KEM ratchet info binds peerEk, kemCt, and context with u32be length prefixes.
63
+ // KEM ratchet info, binds peerEk, kemCt, and context with u32be length prefixes.
64
64
  // Defense-in-depth: the HKDF output is tied to the exact (peerEk, kemCt, context)
65
65
  // tuple used, not just the KEM shared secret. An attacker who substitutes any of
66
66
  // these inputs derives a different chain key trio, regardless of the KEM transcript.
@@ -68,7 +68,7 @@ function buildRootInfo(peerEk, kemCt, context) {
68
68
  const ctxBytes = context ?? new Uint8Array(0);
69
69
  return concat(INFO_ROOT, u32be(peerEk.length), peerEk, u32be(kemCt.length), kemCt, u32be(ctxBytes.length), ctxBytes);
70
70
  }
71
- // KDF_SCKA_INIT spec §7.2
71
+ // KDF_SCKA_INIT, spec §7.2
72
72
  // Derives initial root key, send chain key, and receive chain key from a
73
73
  // shared secret sk. Optional context bytes are appended to the info string.
74
74
  export function ratchetInit(sk, context) {
@@ -80,7 +80,7 @@ export function ratchetInit(sk, context) {
80
80
  const { nextRootKey, sendChainKey, recvChainKey } = kdfRoot(sk, ZERO_SALT, info);
81
81
  return { nextRootKey, sendChainKey, recvChainKey };
82
82
  }
83
- // KDF_SCKA_RK encapsulation side (spec §7.2)
83
+ // KDF_SCKA_RK, encapsulation side (spec §7.2)
84
84
  // Generates a fresh KEM ciphertext, derives next epoch keys from the shared secret.
85
85
  // `peerEk` and `kemCt` are bound into the HKDF info string (defense-in-depth).
86
86
  export function kemRatchetEncap(kem, rk, peerEk, context) {
@@ -98,11 +98,11 @@ export function kemRatchetEncap(kem, rk, peerEk, context) {
98
98
  wipe(sharedSecret);
99
99
  }
100
100
  }
101
- // KDF_SCKA_RK decapsulation side (spec §7.2)
101
+ // KDF_SCKA_RK, decapsulation side (spec §7.2)
102
102
  // Recovers the shared secret from the KEM ciphertext, derives next epoch keys.
103
103
  // The chain key slots are swapped relative to the encap side: what the KDF
104
104
  // labels 'sendChainKey' (A→B direction) becomes the decap side's recvChainKey,
105
- // and vice versa Alice's send IS Bob's receive.
105
+ // and vice versa, Alice's send IS Bob's receive.
106
106
  //
107
107
  // `ownEk` is the local party's encapsulation key (the same public key the
108
108
  // encap side targeted as `peerEk`). It must be passed explicitly so both
@@ -21,7 +21,7 @@
21
21
  //
22
22
  // src/ts/ratchet/skipped-key-store.ts
23
23
  //
24
- // SkippedKeyStore MKSKIPPED cache for a single KDFChain (DR spec §3.2/§3.5).
24
+ // SkippedKeyStore, MKSKIPPED cache for a single KDFChain (DR spec §3.2/§3.5).
25
25
  // Manages out-of-order and skipped message key storage with transactional
26
26
  // resolve via ResolveHandle. Split budgets: maxCacheSize bounds memory;
27
27
  // maxSkipPerResolve bounds per-message HKDF work (DoS mitigation).
@@ -46,7 +46,7 @@ export class SkippedKeyStore {
46
46
  this._maxCacheSize = cache;
47
47
  this._maxSkipPerResolve = skip;
48
48
  }
49
- // O(1) eviction Map iteration is insertion order, and keys are inserted
49
+ // O(1) eviction, Map iteration is insertion order, and keys are inserted
50
50
  // in strictly increasing counter order, so the first key yielded IS the
51
51
  // oldest (lowest counter).
52
52
  _evictOldest() {
@@ -59,8 +59,8 @@ export class SkippedKeyStore {
59
59
  this._store.delete(oldest);
60
60
  }
61
61
  // Resolve a message key for the given counter. Returns a ResolveHandle the
62
- // caller settles via commit() (success key wiped) or rollback()
63
- // (failure key returned to the store so a later legitimate message at
62
+ // caller settles via commit() (success, key wiped) or rollback()
63
+ // (failure, key returned to the store so a later legitimate message at
64
64
  // the same counter can still decrypt).
65
65
  //
66
66
  // Three paths based on counter vs chain.n:
@@ -1,4 +1,4 @@
1
- export type { MlKemLike } from '../kyber/suite.js';
1
+ export type { MlKemLike } from '../mlkem/suite.js';
2
2
  export interface RatchetInitResult {
3
3
  readonly nextRootKey: Uint8Array;
4
4
  readonly sendChainKey: Uint8Array;
@@ -21,16 +21,21 @@
21
21
  //
22
22
  // src/ts/serpent/cipher-suite.ts
23
23
  //
24
- // SerpentCipher CipherSuite implementation for the STREAM construction.
24
+ // SerpentCipher, CipherSuite implementation for the STREAM construction.
25
25
  // 3-key HKDF derivation, HMAC-derived CBC IV, Serpent-CBC + HMAC-SHA-256.
26
26
  // Verify-then-decrypt ordering prevents padding oracle attacks (Vaudenay 2002).
27
+ //
28
+ // Salamander immunity: HMAC-SHA-256 is key-committing under SHA-256 collision
29
+ // resistance, so Serpent suites need no separate commitment in the preamble
30
+ // (commitmentSize: 0). Two distinct master keys cannot produce a tag that
31
+ // validates under both for the same chunk.
27
32
  import { HKDF_SHA256 } from '../sha2/index.js';
28
33
  import { constantTimeEqual, wipe, concat, randomBytes } from '../utils.js';
29
34
  import { AuthenticationError } from '../errors.js';
30
35
  import { getInstance, _assertNotOwned } from '../init.js';
31
36
  import { hmacSha256, cbcEncryptChunk, cbcDecryptChunk, } from './shared-ops.js';
32
37
  import { WORKER_SOURCE } from '../embedded/serpent-pool-worker.js';
33
- const INFO = new TextEncoder().encode('serpent-sealstream-v2');
38
+ const INFO = new TextEncoder().encode('serpent-sealstream-v3');
34
39
  /**
35
40
  * `CipherSuite` implementation for the stream construction using Serpent-256.
36
41
  *
@@ -44,9 +49,10 @@ const INFO = new TextEncoder().encode('serpent-sealstream-v2');
44
49
  export const SerpentCipher = {
45
50
  formatEnum: 0x02,
46
51
  formatName: 'serpent',
47
- hkdfInfo: 'serpent-sealstream-v2',
52
+ hkdfInfo: 'serpent-sealstream-v3',
48
53
  keySize: 32,
49
54
  kemCtSize: 0,
55
+ commitmentSize: 0,
50
56
  tagSize: 32,
51
57
  padded: true,
52
58
  wasmChunkSize: 65552, // src/asm/serpent/buffers.ts CHUNK_SIZE (65536 + 16 PKCS7 max overhead)
@@ -62,10 +68,15 @@ export const SerpentCipher = {
62
68
  * @param nonce Stream nonce (16 bytes minimum)
63
69
  * @returns `DerivedKeys` holding the 96-byte material
64
70
  */
65
- deriveKeys(masterKey, nonce, _kemCt) {
71
+ deriveKeys(masterKey, nonce, _kemCt, _header) {
66
72
  const hkdf = new HKDF_SHA256();
67
- const derived = hkdf.derive(masterKey, nonce, INFO, 96);
68
- hkdf.dispose();
73
+ let derived;
74
+ try {
75
+ derived = hkdf.derive(masterKey, nonce, INFO, 96);
76
+ }
77
+ finally {
78
+ hkdf.dispose();
79
+ }
69
80
  // bytes[0:32]=enc_key, bytes[32:64]=mac_key, bytes[64:96]=iv_key
70
81
  return { bytes: derived };
71
82
  },
@@ -113,7 +124,7 @@ export const SerpentCipher = {
113
124
  wipe(iv);
114
125
  if (tagInput)
115
126
  wipe(tagInput);
116
- // No hmac/cbc instance to dispose shared-ops functions are instance-free.
127
+ // No hmac/cbc instance to dispose, shared-ops functions are instance-free.
117
128
  }
118
129
  },
119
130
  /**
@@ -121,7 +132,7 @@ export const SerpentCipher = {
121
132
  * to prevent padding oracle attacks (Vaudenay 2002). Throws
122
133
  * `AuthenticationError` on tag mismatch.
123
134
  * @param keys Derived keys from `deriveKeys`
124
- * @param counterNonce Per-chunk nonce must match the value used by `sealChunk`
135
+ * @param counterNonce Per-chunk nonce, must match the value used by `sealChunk`
125
136
  * @param chunk Ciphertext || 32-byte HMAC tag
126
137
  * @param aad Optional additional authenticated data
127
138
  * @returns Plaintext with PKCS7 padding removed
@@ -189,18 +200,10 @@ export const SerpentCipher = {
189
200
  * @returns Newly constructed `Worker` instance
190
201
  */
191
202
  createPoolWorker() {
192
- // IIFE source is bundled at lib build time (scripts/embed-workers.ts).
193
- // Avoids the syntactic `new Worker(new URL(..., import.meta.url))`
194
- // pattern that triggers eager worker-chunk emission in Vite's
195
- // transform hook (issue.md). Classic worker via blob URL —
196
- // module workers fail on file:// in Chromium (issue2.md).
203
+ // See docs/architecture.md#pool-worker-spawn-pattern.
197
204
  const blob = new Blob([WORKER_SOURCE], { type: 'application/javascript' });
198
205
  const url = URL.createObjectURL(blob);
199
206
  const w = new Worker(url);
200
- // Worker spec fetches the URL synchronously at construction. Revoke
201
- // in a macrotask so the spawn completes first; releases the Blob
202
- // (~5 KB per spawn × N workers) instead of leaking it for the
203
- // document's lifetime.
204
207
  setTimeout(() => URL.revokeObjectURL(url), 0);
205
208
  return w;
206
209
  },
@@ -6,7 +6,7 @@ import type { Generator } from '../types.js';
6
6
  * one block of pseudorandom output. Practical Cryptography (Ferguson &
7
7
  * Schneier, 2003) §9.4.
8
8
  *
9
- * Pass to `Fortuna.create({ generator: SerpentGenerator, ... })` do not
9
+ * Pass to `Fortuna.create({ generator: SerpentGenerator, ... })`, do not
10
10
  * call `generate()` directly outside of Fortuna.
11
11
  */
12
12
  export declare const SerpentGenerator: Generator;
@@ -21,7 +21,7 @@
21
21
  //
22
22
  // src/ts/serpent/generator.ts
23
23
  //
24
- // Practical Cryptography (Ferguson & Schneier, 2003) §9.4 generator
24
+ // Practical Cryptography (Ferguson & Schneier, 2003) §9.4, generator
25
25
  // Serpent-256 ECB counter-mode PRF for Fortuna's generator slot.
26
26
  import { _assertNotOwned, getInstance } from '../init.js';
27
27
  import { wipe } from '../utils.js';
@@ -32,7 +32,7 @@ import { wipe } from '../utils.js';
32
32
  * one block of pseudorandom output. Practical Cryptography (Ferguson &
33
33
  * Schneier, 2003) §9.4.
34
34
  *
35
- * Pass to `Fortuna.create({ generator: SerpentGenerator, ... })` do not
35
+ * Pass to `Fortuna.create({ generator: SerpentGenerator, ... })`, do not
36
36
  * call `generate()` directly outside of Fortuna.
37
37
  */
38
38
  export const SerpentGenerator = {
@@ -2,13 +2,14 @@ import type { WasmSource } from '../wasm-source.js';
2
2
  /**
3
3
  * Load and initialise the Serpent WASM module from `source`.
4
4
  * Must be called before constructing any Serpent class.
5
- * @param source WASM binary gzip+base64 string, URL, ArrayBuffer, Uint8Array,
5
+ * @param source WASM binary, gzip+base64 string, URL, ArrayBuffer, Uint8Array,
6
6
  * pre-compiled WebAssembly.Module, Response, or Promise<Response>
7
7
  */
8
8
  export declare function serpentInit(source: WasmSource): Promise<void>;
9
9
  export type { WasmSource };
10
+ export { isInitialized } from '../init.js';
10
11
  /**
11
- * Low-level Serpent-256 block cipher raw ECB encrypt/decrypt.
12
+ * Low-level Serpent-256 block cipher, raw ECB encrypt/decrypt.
12
13
  *
13
14
  * Atomic (stateless) class: each method call is independent.
14
15
  * Does not hold exclusive module access; cannot be used while a stateful
@@ -63,23 +64,23 @@ export declare class SerpentCtr {
63
64
  * Load key and nonce into WASM state and reset the block counter to 0.
64
65
  * Must be called before each message.
65
66
  * @param key 16, 24, or 32 bytes
66
- * @param nonce 16 bytes must be unique per (key, message)
67
+ * @param nonce 16 bytes, must be unique per (key, message)
67
68
  */
68
69
  beginEncrypt(key: Uint8Array, nonce: Uint8Array): void;
69
70
  /**
70
71
  * XOR `chunk` with the next keystream block(s). Counter advances automatically.
71
- * @param chunk Plaintext chunk must not exceed WASM CHUNK_SIZE
72
+ * @param chunk Plaintext chunk, must not exceed WASM CHUNK_SIZE
72
73
  * @returns Ciphertext of the same length
73
74
  */
74
75
  encryptChunk(chunk: Uint8Array): Uint8Array;
75
76
  /**
76
- * Alias for `beginEncrypt` CTR mode is symmetric.
77
+ * Alias for `beginEncrypt`, CTR mode is symmetric.
77
78
  * @param key 16, 24, or 32 bytes
78
- * @param nonce 16 bytes must match the value used to encrypt
79
+ * @param nonce 16 bytes, must match the value used to encrypt
79
80
  */
80
81
  beginDecrypt(key: Uint8Array, nonce: Uint8Array): void;
81
82
  /**
82
- * Alias for `encryptChunk` CTR mode is symmetric.
83
+ * Alias for `encryptChunk`, CTR mode is symmetric.
83
84
  * @param chunk Ciphertext chunk
84
85
  * @returns Plaintext of the same length
85
86
  */
@@ -22,24 +22,25 @@
22
22
  // src/ts/serpent/index.ts
23
23
  //
24
24
  // Public API classes for the Serpent-256 WASM module.
25
- // Uses the init() module cache call serpentInit(source) before constructing.
25
+ // Uses the init() module cache, call serpentInit(source) before constructing.
26
26
  import { getInstance, initModule, _acquireModule, _releaseModule, _assertNotOwned } from '../init.js';
27
27
  /**
28
28
  * Load and initialise the Serpent WASM module from `source`.
29
29
  * Must be called before constructing any Serpent class.
30
- * @param source WASM binary gzip+base64 string, URL, ArrayBuffer, Uint8Array,
30
+ * @param source WASM binary, gzip+base64 string, URL, ArrayBuffer, Uint8Array,
31
31
  * pre-compiled WebAssembly.Module, Response, or Promise<Response>
32
32
  */
33
33
  export async function serpentInit(source) {
34
34
  return initModule('serpent', source);
35
35
  }
36
+ export { isInitialized } from '../init.js';
36
37
  /** Returns the raw serpent WASM export object. @internal */
37
38
  function getExports() {
38
39
  return getInstance('serpent').exports;
39
40
  }
40
41
  // ── Serpent ─────────────────────────────────────────────────────────────────
41
42
  /**
42
- * Low-level Serpent-256 block cipher raw ECB encrypt/decrypt.
43
+ * Low-level Serpent-256 block cipher, raw ECB encrypt/decrypt.
43
44
  *
44
45
  * Atomic (stateless) class: each method call is independent.
45
46
  * Does not hold exclusive module access; cannot be used while a stateful
@@ -62,8 +63,10 @@ export class Serpent {
62
63
  throw new RangeError(`key must be 16, 24, or 32 bytes (got ${key.length})`);
63
64
  const mem = new Uint8Array(this.x.memory.buffer);
64
65
  mem.set(key, this.x.getKeyOffset());
65
- if (this.x.loadKey(key.length) !== 0)
66
+ if (this.x.loadKey(key.length) !== 0) {
67
+ this.x.wipeBuffers();
66
68
  throw new Error('loadKey failed');
69
+ }
67
70
  }
68
71
  /**
69
72
  * Encrypt one 128-bit block with the previously loaded key schedule.
@@ -123,7 +126,7 @@ export class SerpentCtr {
123
126
  _tok;
124
127
  constructor(opts) {
125
128
  if (!opts?.dangerUnauthenticated) {
126
- throw new Error('leviathan-crypto: SerpentCtr is unauthenticated use Seal with SerpentCipher instead. ' +
129
+ throw new Error('leviathan-crypto: SerpentCtr is unauthenticated, use Seal with SerpentCipher instead. ' +
127
130
  'To use SerpentCtr directly, pass { dangerUnauthenticated: true }.');
128
131
  }
129
132
  this.x = getExports();
@@ -133,7 +136,7 @@ export class SerpentCtr {
133
136
  * Load key and nonce into WASM state and reset the block counter to 0.
134
137
  * Must be called before each message.
135
138
  * @param key 16, 24, or 32 bytes
136
- * @param nonce 16 bytes must be unique per (key, message)
139
+ * @param nonce 16 bytes, must be unique per (key, message)
137
140
  */
138
141
  beginEncrypt(key, nonce) {
139
142
  if (this._tok === undefined)
@@ -145,12 +148,15 @@ export class SerpentCtr {
145
148
  const mem = new Uint8Array(this.x.memory.buffer);
146
149
  mem.set(key, this.x.getKeyOffset());
147
150
  mem.set(nonce, this.x.getNonceOffset());
148
- this.x.loadKey(key.length);
151
+ if (this.x.loadKey(key.length) !== 0) {
152
+ this.x.wipeBuffers();
153
+ throw new Error('SerpentCtr: loadKey failed');
154
+ }
149
155
  this.x.resetCounter();
150
156
  }
151
157
  /**
152
158
  * XOR `chunk` with the next keystream block(s). Counter advances automatically.
153
- * @param chunk Plaintext chunk must not exceed WASM CHUNK_SIZE
159
+ * @param chunk Plaintext chunk, must not exceed WASM CHUNK_SIZE
154
160
  * @returns Ciphertext of the same length
155
161
  */
156
162
  encryptChunk(chunk) {
@@ -158,7 +164,7 @@ export class SerpentCtr {
158
164
  throw new Error('SerpentCtr: instance has been disposed');
159
165
  const maxChunk = this.x.getChunkSize();
160
166
  if (chunk.length > maxChunk)
161
- throw new RangeError(`chunk exceeds maximum size of ${maxChunk} bytes split into smaller chunks`);
167
+ throw new RangeError(`chunk exceeds maximum size of ${maxChunk} bytes, split into smaller chunks`);
162
168
  const mem = new Uint8Array(this.x.memory.buffer);
163
169
  const ptOff = this.x.getChunkPtOffset();
164
170
  const ctOff = this.x.getChunkCtOffset();
@@ -167,15 +173,15 @@ export class SerpentCtr {
167
173
  return mem.slice(ctOff, ctOff + chunk.length);
168
174
  }
169
175
  /**
170
- * Alias for `beginEncrypt` CTR mode is symmetric.
176
+ * Alias for `beginEncrypt`, CTR mode is symmetric.
171
177
  * @param key 16, 24, or 32 bytes
172
- * @param nonce 16 bytes must match the value used to encrypt
178
+ * @param nonce 16 bytes, must match the value used to encrypt
173
179
  */
174
180
  beginDecrypt(key, nonce) {
175
181
  this.beginEncrypt(key, nonce);
176
182
  }
177
183
  /**
178
- * Alias for `encryptChunk` CTR mode is symmetric.
184
+ * Alias for `encryptChunk`, CTR mode is symmetric.
179
185
  * @param chunk Ciphertext chunk
180
186
  * @returns Plaintext of the same length
181
187
  */
@@ -202,18 +208,3 @@ export { AuthenticationError } from '../errors.js';
202
208
  export { SerpentCipher } from './cipher-suite.js';
203
209
  // ── SerpentGenerator ────────────────────────────────────────────────────────
204
210
  export { SerpentGenerator } from './generator.js';
205
- // ── Ready check ─────────────────────────────────────────────────────────────
206
- /**
207
- * Returns `true` if the serpent WASM module has been initialised.
208
- * Used by tests and internal guards; not part of the public API.
209
- * @internal
210
- */
211
- export function _serpentReady() {
212
- try {
213
- getInstance('serpent');
214
- return true;
215
- }
216
- catch {
217
- return false;
218
- }
219
- }