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
@@ -0,0 +1,102 @@
1
+ /** BLAKE3 WASM exports. */
2
+ export interface Blake3Exports {
3
+ memory: WebAssembly.Memory;
4
+ getModuleId: () => number;
5
+ getMemoryPages: () => number;
6
+ getInputStagingOffset: () => number;
7
+ getOutputStagingOffset: () => number;
8
+ getCvOffset: () => number;
9
+ getMsgOffset: () => number;
10
+ getCounterOffset: () => number;
11
+ getBlockLenOffset: () => number;
12
+ getFlagsOffset: () => number;
13
+ getCompressOutOffset: () => number;
14
+ getKeyedKeyOffset: () => number;
15
+ getDeriveCvOffset: () => number;
16
+ getCompress4CvInOffset: () => number;
17
+ getCompress4MsgInOffset: () => number;
18
+ getCompress4CtrInOffset: () => number;
19
+ getCompress4OutOffset: () => number;
20
+ getCompress4BlenInOffset: () => number;
21
+ getCompress4FlagsInOffset: () => number;
22
+ getModeCvOffset: () => number;
23
+ FLAG_CHUNK_START: {
24
+ value: number;
25
+ };
26
+ FLAG_CHUNK_END: {
27
+ value: number;
28
+ };
29
+ FLAG_PARENT: {
30
+ value: number;
31
+ };
32
+ FLAG_ROOT: {
33
+ value: number;
34
+ };
35
+ FLAG_KEYED_HASH: {
36
+ value: number;
37
+ };
38
+ FLAG_DERIVE_KEY_CONTEXT: {
39
+ value: number;
40
+ };
41
+ FLAG_DERIVE_KEY_MATERIAL: {
42
+ value: number;
43
+ };
44
+ BLAKE3_IV0: {
45
+ value: number;
46
+ };
47
+ BLAKE3_IV1: {
48
+ value: number;
49
+ };
50
+ BLAKE3_IV2: {
51
+ value: number;
52
+ };
53
+ BLAKE3_IV3: {
54
+ value: number;
55
+ };
56
+ BLAKE3_IV4: {
57
+ value: number;
58
+ };
59
+ BLAKE3_IV5: {
60
+ value: number;
61
+ };
62
+ BLAKE3_IV6: {
63
+ value: number;
64
+ };
65
+ BLAKE3_IV7: {
66
+ value: number;
67
+ };
68
+ compress: (cvOff: number, blockOff: number, counterLo: number, counterHi: number, blockLen: number, flags: number, outOff: number) => void;
69
+ compress4: () => void;
70
+ chunkInit: (chunkIndex: bigint) => void;
71
+ chunkUpdate: (blockOff: number, blockLen: number) => void;
72
+ chunkFinalize: (outCvOff: number, isRootSoloChunk: number) => void;
73
+ treeInit: () => void;
74
+ treePushChunk: (chunkCvOff: number) => void;
75
+ treeFinalizeRoot: (outOff: number) => void;
76
+ hash: (inputOff: number, inputLen: number, outOff: number, outLen: number) => void;
77
+ hashKeyed: (keyOff: number, inputOff: number, inputLen: number, outOff: number, outLen: number) => void;
78
+ deriveKey: (contextOff: number, contextLen: number, materialOff: number, materialLen: number, outOff: number, outLen: number) => void;
79
+ squeezeXofBlock: (counterLo: number, counterHi: number, outOff: number) => void;
80
+ wipeBuffers: () => void;
81
+ }
82
+ /**
83
+ * BLAKE3 WASM internal test exports. NOT part of the consumer surface,
84
+ * NOT re-exported from `src/ts/blake3/index.ts`. Wired exclusively for
85
+ * the tree-internals test suite (`test/unit/blake3/blake3-tree-internals
86
+ * .test.ts`) and the Merkle-tree substrate
87
+ * (`src/ts/merkle/blake3-tree.ts`) which casts
88
+ * `Blake3Exports & Blake3TestExports` inside the merkle module.
89
+ *
90
+ * Tests obtain these via `test/unit/blake3/helpers.ts`, which casts the
91
+ * public `Blake3Exports` to `Blake3Exports & Blake3TestExports`. Consumer
92
+ * code never sees the `_test*` surface.
93
+ */
94
+ export interface Blake3TestExports {
95
+ _testChunkCV: (inputOff: number, inputLen: number, chunkIndex: bigint, startCvOff: number, modeFlags: number, outCvOff: number) => void;
96
+ _testParentCV: (leftCvOff: number, rightCvOff: number, startCvOff: number, modeFlags: number, isRoot: number, outCvOff: number) => void;
97
+ _testDeriveContextCV: (contextOff: number, contextLen: number, outCcvOff: number) => void;
98
+ _getBatch4CallCount: () => number;
99
+ _resetBatch4CallCount: () => void;
100
+ _getParentBatch4CallCount: () => number;
101
+ _resetParentBatch4CallCount: () => void;
102
+ }
@@ -0,0 +1,31 @@
1
+ // ▄▄▄▄▄▄▄▄▄▄
2
+ // ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
3
+ // ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
4
+ // ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
5
+ // ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
6
+ // ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
7
+ // ███████▌ ▀██▀ ███
8
+ // ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
9
+ // ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
10
+ // ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
11
+ // ▀████▄ ▄██▄
12
+ // ▐████ ▐███ Author: xero (https://x-e.ro)
13
+ // ▄▄██████████ ▐███ ▄▄ License: MIT
14
+ // ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
15
+ // ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
16
+ // ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
17
+ // ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
18
+ // █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
19
+ // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
+ // ▀█████▀▀
21
+ //
22
+ // src/ts/blake3/types.ts
23
+ //
24
+ // BLAKE3 WASM exports interface. Mirrors the AssemblyScript surface in
25
+ // `src/asm/blake3/index.ts`. The consumer-facing TS surface (BLAKE3,
26
+ // BLAKE3Stream and the keyed_hash / derive_key variants) calls the
27
+ // top-level `hash` / `hashKeyed` / `deriveKey` entry points. Lower-level
28
+ // primitives (`compress`, flag constants, buffer accessors) are typed
29
+ // here for completeness and so test code can drive each layer in
30
+ // isolation if needed.
31
+ export {};
@@ -0,0 +1,29 @@
1
+ /**
2
+ * BLAKE3 §2.3 Modes: keyed_hash takes a 32-byte key. The key seeds the chunk
3
+ * machine in place of the BLAKE3 IV and every compress carries the
4
+ * KEYED_HASH flag. A key of any other length is a contract violation.
5
+ */
6
+ export declare function validateKey(key: Uint8Array): void;
7
+ /**
8
+ * BLAKE3 §2.3 Modes: derive_key takes a context string and produces a
9
+ * derived key. The context string is a domain separator and is
10
+ * conventionally a UTF-8 hardcoded application constant. An empty context
11
+ * defeats the domain separation §2.3 is designed to provide; reject it.
12
+ *
13
+ * Accepts a JS string (UTF-8 encoded here) or a Uint8Array (passed
14
+ * through). No upper cap on length.
15
+ */
16
+ export declare function validateContext(context: string | Uint8Array): Uint8Array;
17
+ /**
18
+ * BLAKE3 §2.6 XOF: default-length output is 32 bytes; the XOF can in principle
19
+ * produce up to 2^64 - 1 bytes. The one-shot path (BLAKE3.hash /
20
+ * BLAKE3KeyedHash.hash / BLAKE3DeriveKey.derive and the streaming
21
+ * finalize(outLen) counterparts) writes outLen bytes through a single
22
+ * WASM call sized by the OUTPUT_STAGING region, so the practical
23
+ * upper bound is `OUTPUT_STAGING_SIZE` (1024 bytes); larger consumers
24
+ * use `finalizeXof()` and stream from `BLAKE3OutputReader.read(n)`
25
+ * which squeezes 64 bytes at a time off the WASM-side root snapshot.
26
+ * This validator rejects nonsense (zero, negative, non-finite,
27
+ * non-integer); the one-shot wrappers enforce the per-call ceiling.
28
+ */
29
+ export declare function validateOutputLen(outLen: number): void;
@@ -0,0 +1,80 @@
1
+ // ▄▄▄▄▄▄▄▄▄▄
2
+ // ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
3
+ // ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
4
+ // ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
5
+ // ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
6
+ // ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
7
+ // ███████▌ ▀██▀ ███
8
+ // ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
9
+ // ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
10
+ // ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
11
+ // ▀████▄ ▄██▄
12
+ // ▐████ ▐███ Author: xero (https://x-e.ro)
13
+ // ▄▄██████████ ▐███ ▄▄ License: MIT
14
+ // ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
15
+ // ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
16
+ // ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
17
+ // ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
18
+ // █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
19
+ // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
+ // ▀█████▀▀
21
+ //
22
+ // src/ts/blake3/validate.ts
23
+ //
24
+ // BLAKE3 caller-side input validation. Pure length / type checks, no
25
+ // crypto. BLAKE3 is a hash family, not a signature scheme; rejected
26
+ // inputs throw `RangeError` / `TypeError` (no `SigningError`).
27
+ /**
28
+ * BLAKE3 §2.3 Modes: keyed_hash takes a 32-byte key. The key seeds the chunk
29
+ * machine in place of the BLAKE3 IV and every compress carries the
30
+ * KEYED_HASH flag. A key of any other length is a contract violation.
31
+ */
32
+ export function validateKey(key) {
33
+ if (!(key instanceof Uint8Array))
34
+ throw new TypeError('leviathan-crypto: blake3 key must be a Uint8Array');
35
+ if (key.length !== 32)
36
+ throw new RangeError(`leviathan-crypto: blake3 key must be 32 bytes (got ${key.length})`);
37
+ }
38
+ /**
39
+ * BLAKE3 §2.3 Modes: derive_key takes a context string and produces a
40
+ * derived key. The context string is a domain separator and is
41
+ * conventionally a UTF-8 hardcoded application constant. An empty context
42
+ * defeats the domain separation §2.3 is designed to provide; reject it.
43
+ *
44
+ * Accepts a JS string (UTF-8 encoded here) or a Uint8Array (passed
45
+ * through). No upper cap on length.
46
+ */
47
+ export function validateContext(context) {
48
+ let bytes;
49
+ if (typeof context === 'string') {
50
+ bytes = new TextEncoder().encode(context);
51
+ }
52
+ else if (context instanceof Uint8Array) {
53
+ bytes = context;
54
+ }
55
+ else {
56
+ throw new TypeError('leviathan-crypto: blake3 derive_key context must be a string or Uint8Array');
57
+ }
58
+ if (bytes.length === 0)
59
+ throw new RangeError('leviathan-crypto: blake3 derive_key context must be non-empty '
60
+ + '(empty context defeats §2.3 domain separation)');
61
+ return bytes;
62
+ }
63
+ /**
64
+ * BLAKE3 §2.6 XOF: default-length output is 32 bytes; the XOF can in principle
65
+ * produce up to 2^64 - 1 bytes. The one-shot path (BLAKE3.hash /
66
+ * BLAKE3KeyedHash.hash / BLAKE3DeriveKey.derive and the streaming
67
+ * finalize(outLen) counterparts) writes outLen bytes through a single
68
+ * WASM call sized by the OUTPUT_STAGING region, so the practical
69
+ * upper bound is `OUTPUT_STAGING_SIZE` (1024 bytes); larger consumers
70
+ * use `finalizeXof()` and stream from `BLAKE3OutputReader.read(n)`
71
+ * which squeezes 64 bytes at a time off the WASM-side root snapshot.
72
+ * This validator rejects nonsense (zero, negative, non-finite,
73
+ * non-integer); the one-shot wrappers enforce the per-call ceiling.
74
+ */
75
+ export function validateOutputLen(outLen) {
76
+ if (typeof outLen !== 'number' || !Number.isFinite(outLen) || !Number.isInteger(outLen))
77
+ throw new RangeError(`leviathan-crypto: blake3 outLen must be a finite integer (got ${String(outLen)})`);
78
+ if (outLen < 1)
79
+ throw new RangeError(`leviathan-crypto: blake3 outLen must be >= 1 (got ${outLen})`);
80
+ }
Binary file
@@ -1,4 +1,14 @@
1
1
  import type { CipherSuite } from '../stream/types.js';
2
+ /**
3
+ * `CipherSuite` implementation for the stream construction using XChaCha20-Poly1305.
4
+ *
5
+ * Each chunk is encrypted with ChaCha20-Poly1305 using a HChaCha20 subkey
6
+ * derived via HKDF-SHA-256 + HChaCha20. This is the recommended cipher suite
7
+ * for `SealStream` / `OpenStream` / `SealStreamPool`.
8
+ *
9
+ * Pass to `SealStream` / `OpenStream` / `SealStreamPool` instead of constructing
10
+ * this object directly. Use `XChaCha20Cipher.keygen()` to generate a 32-byte key.
11
+ */
2
12
  export declare const XChaCha20Cipher: CipherSuite & {
3
13
  keygen(): Uint8Array;
4
14
  };
@@ -21,40 +21,101 @@
21
21
  //
22
22
  // src/ts/chacha20/cipher-suite.ts
23
23
  //
24
- // XChaCha20Cipher CipherSuite implementation for the STREAM construction.
24
+ // XChaCha20Cipher, CipherSuite implementation for the STREAM construction.
25
25
  // HKDF-SHA-256 key derivation → HChaCha20 subkey → ChaCha20-Poly1305 per chunk.
26
- import { getInstance } from '../init.js';
26
+ import { getInstance, _assertNotOwned } from '../init.js';
27
27
  import { HKDF_SHA256 } from '../sha2/index.js';
28
28
  import { aeadEncrypt, aeadDecrypt, deriveSubkey } from './ops.js';
29
- import { wipe, randomBytes } from '../utils.js';
30
- const INFO = new TextEncoder().encode('xchacha20-sealstream-v2');
29
+ import { wipe, randomBytes, concat } from '../utils.js';
30
+ import { HEADER_SIZE } from '../stream/constants.js';
31
+ import { WORKER_SOURCE } from '../embedded/chacha20-pool-worker.js';
32
+ const INFO = new TextEncoder().encode('xchacha20-sealstream-v3');
33
+ /** Returns the raw chacha20 WASM export object. @internal */
31
34
  function getExports() {
32
35
  return getInstance('chacha20').exports;
33
36
  }
37
+ /**
38
+ * `CipherSuite` implementation for the stream construction using XChaCha20-Poly1305.
39
+ *
40
+ * Each chunk is encrypted with ChaCha20-Poly1305 using a HChaCha20 subkey
41
+ * derived via HKDF-SHA-256 + HChaCha20. This is the recommended cipher suite
42
+ * for `SealStream` / `OpenStream` / `SealStreamPool`.
43
+ *
44
+ * Pass to `SealStream` / `OpenStream` / `SealStreamPool` instead of constructing
45
+ * this object directly. Use `XChaCha20Cipher.keygen()` to generate a 32-byte key.
46
+ */
34
47
  export const XChaCha20Cipher = {
35
- formatEnum: 0x01,
48
+ formatEnum: 0x03,
36
49
  formatName: 'xchacha20',
37
- hkdfInfo: 'xchacha20-sealstream-v2',
50
+ hkdfInfo: 'xchacha20-sealstream-v3',
38
51
  keySize: 32,
39
52
  kemCtSize: 0,
53
+ commitmentSize: 32,
40
54
  tagSize: 16,
41
55
  padded: false,
42
56
  wasmChunkSize: 65536, // src/asm/chacha20/buffers.ts CHUNK_SIZE
43
57
  wasmModules: ['chacha20'],
58
+ /** Generate a random 32-byte master key suitable for use with `XChaCha20Cipher`. @returns 32 cryptographically random bytes */
44
59
  keygen() {
45
60
  return randomBytes(32);
46
61
  },
47
- deriveKeys(masterKey, nonce, _kemCt) {
62
+ /**
63
+ * Derive a 32-byte HChaCha20 subkey and a 32-byte key commitment from
64
+ * `masterKey` and `nonce` via HKDF-SHA-256 followed by HChaCha20 subkey
65
+ * derivation. The full 20-byte preamble header is appended to the HKDF
66
+ * info string, binding `formatEnum`, framed flag, nonce, and chunkSize
67
+ * into the derived material, header tampering causes derived keys to
68
+ * differ and AEAD fails on the first chunk.
69
+ *
70
+ * The 64-byte HKDF output is split: bytes 0..32 feed HChaCha20 subkey
71
+ * derivation, bytes 32..64 are the key commitment that ends up in the
72
+ * preamble. Verifying the commitment before any chunk is processed
73
+ * closes the Invisible Salamanders attack surface, Poly1305 alone is
74
+ * not key-committing, so without this an adversary with control over
75
+ * two master keys could craft a single ciphertext + tag that decrypts
76
+ * validly under both.
77
+ *
78
+ * @param masterKey 32-byte master key
79
+ * @param nonce Stream nonce (16 bytes, also used as HChaCha20 input)
80
+ * @param _kemCt Unused for symmetric XChaCha20; KEM wrappers pass it through
81
+ * @param header 20-byte preamble header, required (throws otherwise)
82
+ * @returns `DerivedKeys` holding the 32-byte HChaCha20 subkey and 32-byte commitment
83
+ */
84
+ deriveKeys(masterKey, nonce, _kemCt, header) {
85
+ if (!header || header.length !== HEADER_SIZE)
86
+ throw new Error(`XChaCha20Cipher.deriveKeys: header binding required (got ${header?.length ?? 'undefined'} bytes)`);
87
+ _assertNotOwned('chacha20');
48
88
  const hkdf = new HKDF_SHA256();
49
- const streamKey = hkdf.derive(masterKey, nonce, INFO, 32);
50
- hkdf.dispose();
51
- // HChaCha20 subkey derivation nonce[0:16] as XChaCha input
89
+ let okm;
90
+ try {
91
+ // INFO || header, binds formatEnum, framed flag, nonce, chunkSize into the KDF.
92
+ // Any header tampering produces different keys, AEAD fails on the first chunk.
93
+ const info = concat(INFO, header);
94
+ okm = hkdf.derive(masterKey, nonce, info, 64);
95
+ }
96
+ finally {
97
+ hkdf.dispose();
98
+ }
99
+ // Bytes 0..32: streamKey for HChaCha20 subkey derivation.
100
+ // Bytes 32..64: key commitment for the seal preamble.
101
+ const streamKey = okm.subarray(0, 32);
102
+ const commitment = okm.slice(32, 64); // independent backing, survives okm wipe
52
103
  const x = getExports();
53
104
  const subkey = deriveSubkey(x, streamKey, nonce);
54
- wipe(streamKey);
55
- return { bytes: subkey };
105
+ wipe(okm); // wipe both halves of okm; commitment is safe (independent backing)
106
+ return { bytes: subkey, commitment };
56
107
  },
108
+ /**
109
+ * Encrypt and authenticate one stream chunk with ChaCha20-Poly1305.
110
+ * Output: ciphertext || 16-byte Poly1305 tag.
111
+ * @param keys Derived keys from `deriveKeys`
112
+ * @param counterNonce 12-byte per-chunk nonce (unique per chunk in the stream)
113
+ * @param chunk Plaintext chunk
114
+ * @param aad Optional additional authenticated data
115
+ * @returns Authenticated ciphertext
116
+ */
57
117
  sealChunk(keys, counterNonce, chunk, aad) {
118
+ _assertNotOwned('chacha20');
58
119
  const x = getExports();
59
120
  const { ciphertext, tag } = aeadEncrypt(x, keys.bytes, counterNonce, chunk, aad ?? new Uint8Array(0));
60
121
  const out = new Uint8Array(ciphertext.length + 16);
@@ -62,7 +123,16 @@ export const XChaCha20Cipher = {
62
123
  out.set(tag, ciphertext.length);
63
124
  return out;
64
125
  },
126
+ /**
127
+ * Verify and decrypt one stream chunk. Throws `AuthenticationError` on tag mismatch.
128
+ * @param keys Derived keys from `deriveKeys`
129
+ * @param counterNonce 12-byte per-chunk nonce, must match the value used by `sealChunk`
130
+ * @param chunk Ciphertext || 16-byte Poly1305 tag
131
+ * @param aad Optional additional authenticated data
132
+ * @returns Plaintext
133
+ */
65
134
  openChunk(keys, counterNonce, chunk, aad) {
135
+ _assertNotOwned('chacha20');
66
136
  if (chunk.length < 16)
67
137
  throw new RangeError(`chunk too short for 16-byte tag (got ${chunk.length})`);
68
138
  const x = getExports();
@@ -70,10 +140,25 @@ export const XChaCha20Cipher = {
70
140
  const tag = chunk.subarray(chunk.length - 16);
71
141
  return aeadDecrypt(x, keys.bytes, counterNonce, ct, tag, aad ?? new Uint8Array(0), 'xchacha20-poly1305');
72
142
  },
143
+ /**
144
+ * Zero all derived key material in `keys`. Called by the stream layer on
145
+ * teardown and after auth failure.
146
+ * @param keys Derived keys to wipe
147
+ */
73
148
  wipeKeys(keys) {
74
149
  wipe(keys.bytes);
75
150
  },
151
+ /**
152
+ * Spawn an XChaCha20 pool worker from the embedded IIFE bundle.
153
+ * The worker holds its own chacha20 WASM instance and derived subkey.
154
+ * @returns Newly constructed `Worker` instance
155
+ */
76
156
  createPoolWorker() {
77
- return new Worker(new URL('./pool-worker.js', import.meta.url), { type: 'module' });
157
+ // See docs/architecture.md#pool-worker-spawn-pattern.
158
+ const blob = new Blob([WORKER_SOURCE], { type: 'application/javascript' });
159
+ const url = URL.createObjectURL(blob);
160
+ const w = new Worker(url);
161
+ setTimeout(() => URL.revokeObjectURL(url), 0);
162
+ return w;
78
163
  },
79
164
  };
@@ -0,0 +1,12 @@
1
+ import type { Generator } from '../types.js';
2
+ /**
3
+ * ChaCha20 counter-mode PRF for Fortuna's generator slot.
4
+ *
5
+ * The 32-bit counter is read from `counter` as a little-endian u32. Each call
6
+ * to `generate()` encrypts zero blocks to produce keystream output (RFC 8439 §2.3).
7
+ * The nonce is fixed to zero, Fortuna's counter is the only varying input.
8
+ *
9
+ * Pass to `Fortuna.create({ generator: ChaCha20Generator, ... })`, do not
10
+ * call `generate()` directly outside of Fortuna.
11
+ */
12
+ export declare const ChaCha20Generator: Generator;
@@ -0,0 +1,91 @@
1
+ // ▄▄▄▄▄▄▄▄▄▄
2
+ // ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
3
+ // ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
4
+ // ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
5
+ // ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
6
+ // ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
7
+ // ███████▌ ▀██▀ ███
8
+ // ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
9
+ // ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
10
+ // ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
11
+ // ▀████▄ ▄██▄
12
+ // ▐████ ▐███ Author: xero (https://x-e.ro)
13
+ // ▄▄██████████ ▐███ ▄▄ License: MIT
14
+ // ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
15
+ // ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
16
+ // ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
17
+ // ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
18
+ // █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
19
+ // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
+ // ▀█████▀▀
21
+ //
22
+ // src/ts/chacha20/generator.ts
23
+ //
24
+ // RFC 8439 §2.3, ChaCha20 block function as PRF
25
+ // ChaCha20 counter-mode PRF for Fortuna's generator slot.
26
+ //
27
+ // Counter is treated as a 32-bit LE integer. Fortuna's 4-byte genCnt provides
28
+ // 2^32 blocks × 64 bytes = 256 GiB of output between reseeds.
29
+ import { _assertNotOwned, getInstance } from '../init.js';
30
+ /**
31
+ * ChaCha20 counter-mode PRF for Fortuna's generator slot.
32
+ *
33
+ * The 32-bit counter is read from `counter` as a little-endian u32. Each call
34
+ * to `generate()` encrypts zero blocks to produce keystream output (RFC 8439 §2.3).
35
+ * The nonce is fixed to zero, Fortuna's counter is the only varying input.
36
+ *
37
+ * Pass to `Fortuna.create({ generator: ChaCha20Generator, ... })`, do not
38
+ * call `generate()` directly outside of Fortuna.
39
+ */
40
+ export const ChaCha20Generator = {
41
+ keySize: 32,
42
+ blockSize: 64,
43
+ counterSize: 4,
44
+ wasmModules: ['chacha20'],
45
+ /**
46
+ * Generate `n` pseudorandom bytes using ChaCha20 with a zero nonce.
47
+ * @param key 32-byte ChaCha20 key
48
+ * @param counter 4-byte counter (little-endian u32)
49
+ * @param n Number of bytes to generate (0 ≤ n ≤ 2^30)
50
+ * @returns `n` pseudorandom bytes
51
+ */
52
+ generate(key, counter, n) {
53
+ _assertNotOwned('chacha20');
54
+ if (key.length !== 32)
55
+ throw new RangeError(`ChaCha20Generator: key must be 32 bytes (got ${key.length})`);
56
+ if (counter.length !== 4)
57
+ throw new RangeError(`ChaCha20Generator: counter must be 4 bytes (got ${counter.length})`);
58
+ if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 30)
59
+ throw new RangeError(`ChaCha20Generator: n must be a non-negative safe integer <= 2^30 (got ${n})`);
60
+ const x = getInstance('chacha20').exports;
61
+ const c = new DataView(counter.buffer, counter.byteOffset, 4).getUint32(0, true);
62
+ const mem = new Uint8Array(x.memory.buffer);
63
+ try {
64
+ mem.set(key, x.getKeyOffset());
65
+ // Fixed zero nonce, Fortuna's counter is the only varying input
66
+ mem.fill(0, x.getChachaNonceOffset(), x.getChachaNonceOffset() + 12);
67
+ x.chachaSetCounter(c);
68
+ x.chachaLoadKey();
69
+ const output = new Uint8Array(n);
70
+ let remaining = n;
71
+ let outPos = 0;
72
+ const maxChunk = x.getChunkSize();
73
+ const ptOff = x.getChunkPtOffset();
74
+ const ctOff = x.getChunkCtOffset();
75
+ while (remaining > 0) {
76
+ const chunkLen = Math.min(remaining, maxChunk);
77
+ mem.fill(0, ptOff, ptOff + chunkLen);
78
+ x.chachaEncryptChunk_simd(chunkLen);
79
+ output.set(mem.subarray(ctOff, ctOff + chunkLen), outPos);
80
+ outPos += chunkLen;
81
+ remaining -= chunkLen;
82
+ }
83
+ return output;
84
+ }
85
+ finally {
86
+ // Wipe the WASM key/state/keystream scratch so secret-derived bytes
87
+ // do not outlive this call in the shared chacha20 linear memory.
88
+ x.wipeBuffers();
89
+ }
90
+ },
91
+ };