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,620 @@
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/index.ts
23
+ //
24
+ // BLAKE3 public API. One-shot and streaming flavours of hash, keyed_hash,
25
+ // derive_key (BLAKE3 §2.3 Modes), and the XOF reader (§2.6). Call
26
+ // `blake3Init(source)` before constructing any class.
27
+ import { getInstance, initModule, isInitialized, _acquireModule, _releaseModule, _assertNotOwned, } from '../init.js';
28
+ import { validateKey, validateContext, validateOutputLen } from './validate.js';
29
+ export { isInitialized };
30
+ export async function blake3Init(source) {
31
+ return initModule('blake3', source);
32
+ }
33
+ function getExports() {
34
+ return getInstance('blake3').exports;
35
+ }
36
+ // Input scratch sits past BUFFER_END from src/asm/blake3/buffers.ts;
37
+ // usable region is 131072 - INPUT_SCRATCH_OFF bytes.
38
+ const INPUT_SCRATCH_OFF = 27648;
39
+ const INPUT_SCRATCH_MAX = 131072 - INPUT_SCRATCH_OFF;
40
+ // One-shot output staging cap; larger XOF reads go through OutputReader.
41
+ const OUTPUT_STAGING_SIZE = 1024;
42
+ // BLAKE3 §2.6: root compress emits 64 bytes per squeeze.
43
+ const ROOT_BLOCK_SIZE = 64;
44
+ function tooBigForScratchError(len) {
45
+ return new RangeError(`leviathan-crypto: blake3 input length ${len} exceeds the per-call `
46
+ + `WASM input scratch (${INPUT_SCRATCH_MAX} bytes). Split the input or `
47
+ + 'use the streaming surface (BLAKE3Stream / BLAKE3KeyedHashStream / '
48
+ + 'BLAKE3DeriveKeyStream).');
49
+ }
50
+ function tooBigForOneShotError(len) {
51
+ return new RangeError(`leviathan-crypto: blake3 outLen ${len} exceeds the per-call output `
52
+ + `staging size (${OUTPUT_STAGING_SIZE} bytes). For larger XOF reads `
53
+ + 'use finalizeXof() and BLAKE3OutputReader.read(n).');
54
+ }
55
+ // Stage `input` into INPUT_SCRATCH_OFF and return (offset, length) for
56
+ // the WASM hash() call. Caller is responsible for wiping the scratch
57
+ // region after the WASM call returns (we do that in a finally in each
58
+ // public method so secret-derived inputs do not linger).
59
+ function stageInput(x, input) {
60
+ if (input.length > INPUT_SCRATCH_MAX)
61
+ throw tooBigForScratchError(input.length);
62
+ const mem = new Uint8Array(x.memory.buffer);
63
+ mem.set(input, INPUT_SCRATCH_OFF);
64
+ }
65
+ function wipeInput(x, len) {
66
+ const mem = new Uint8Array(x.memory.buffer);
67
+ mem.fill(0, INPUT_SCRATCH_OFF, INPUT_SCRATCH_OFF + len);
68
+ }
69
+ // One-shot hash entry, no key / no context. Writes the requested prefix
70
+ // of the BLAKE3 hash output to a fresh Uint8Array and returns it. Wipes
71
+ // the input scratch on the way out.
72
+ function oneShotHash(x, input, outLen) {
73
+ const outOff = x.getOutputStagingOffset();
74
+ stageInput(x, input);
75
+ try {
76
+ x.hash(INPUT_SCRATCH_OFF, input.length, outOff, outLen);
77
+ const mem = new Uint8Array(x.memory.buffer);
78
+ return mem.slice(outOff, outOff + outLen);
79
+ }
80
+ finally {
81
+ wipeInput(x, input.length);
82
+ x.wipeBuffers();
83
+ }
84
+ }
85
+ function oneShotKeyedHash(x, key, input, outLen) {
86
+ const mem = new Uint8Array(x.memory.buffer);
87
+ const keyOff = x.getKeyedKeyOffset();
88
+ const outOff = x.getOutputStagingOffset();
89
+ mem.set(key, keyOff);
90
+ stageInput(x, input);
91
+ try {
92
+ x.hashKeyed(keyOff, INPUT_SCRATCH_OFF, input.length, outOff, outLen);
93
+ return mem.slice(outOff, outOff + outLen);
94
+ }
95
+ finally {
96
+ mem.fill(0, keyOff, keyOff + 32);
97
+ wipeInput(x, input.length);
98
+ x.wipeBuffers();
99
+ }
100
+ }
101
+ function oneShotDeriveKey(x, contextBytes, material, outLen) {
102
+ if (contextBytes.length + material.length > INPUT_SCRATCH_MAX)
103
+ throw tooBigForScratchError(contextBytes.length + material.length);
104
+ const mem = new Uint8Array(x.memory.buffer);
105
+ const ctxOff = INPUT_SCRATCH_OFF;
106
+ const matOff = INPUT_SCRATCH_OFF + contextBytes.length;
107
+ const outOff = x.getOutputStagingOffset();
108
+ mem.set(contextBytes, ctxOff);
109
+ mem.set(material, matOff);
110
+ try {
111
+ x.deriveKey(ctxOff, contextBytes.length, matOff, material.length, outOff, outLen);
112
+ return mem.slice(outOff, outOff + outLen);
113
+ }
114
+ finally {
115
+ mem.fill(0, ctxOff, matOff + material.length);
116
+ x.wipeBuffers();
117
+ }
118
+ }
119
+ // ── BLAKE3 ──────────────────────────────────────────────────────────────────
120
+ /**
121
+ * BLAKE3 default-mode hash (BLAKE3 §2.3 Modes — `hash`).
122
+ *
123
+ * One-shot: `hash(msg, outLen?)` runs the full chunk / tree / root
124
+ * pipeline and returns `outLen` (default 32) bytes of XOF output.
125
+ * Module exclusivity is acquired and released per call.
126
+ */
127
+ export class BLAKE3 {
128
+ x;
129
+ constructor() {
130
+ this.x = getExports();
131
+ }
132
+ hash(msg, outLen = 32) {
133
+ _assertNotOwned('blake3');
134
+ if (!(msg instanceof Uint8Array))
135
+ throw new TypeError('leviathan-crypto: blake3 message must be a Uint8Array');
136
+ validateOutputLen(outLen);
137
+ if (outLen > OUTPUT_STAGING_SIZE)
138
+ throw tooBigForOneShotError(outLen);
139
+ return oneShotHash(this.x, msg, outLen);
140
+ }
141
+ dispose() {
142
+ _assertNotOwned('blake3');
143
+ try {
144
+ this.x.wipeBuffers();
145
+ }
146
+ catch { /* idempotent */ }
147
+ }
148
+ }
149
+ /**
150
+ * BLAKE3 keyed_hash (BLAKE3 §2.3 Modes — `keyed_hash`).
151
+ *
152
+ * The 32-byte key seeds the chunk machine in place of the BLAKE3 IV and
153
+ * every compress carries the KEYED_HASH flag. Use cases include MACs and
154
+ * keyed-pseudorandom generation; the construction is a PRF when the key
155
+ * is uniform and secret.
156
+ */
157
+ export class BLAKE3KeyedHash {
158
+ x;
159
+ constructor() {
160
+ this.x = getExports();
161
+ }
162
+ hash(key, msg, outLen = 32) {
163
+ _assertNotOwned('blake3');
164
+ validateKey(key);
165
+ if (!(msg instanceof Uint8Array))
166
+ throw new TypeError('leviathan-crypto: blake3 message must be a Uint8Array');
167
+ validateOutputLen(outLen);
168
+ if (outLen > OUTPUT_STAGING_SIZE)
169
+ throw tooBigForOneShotError(outLen);
170
+ return oneShotKeyedHash(this.x, key, msg, outLen);
171
+ }
172
+ dispose() {
173
+ _assertNotOwned('blake3');
174
+ try {
175
+ this.x.wipeBuffers();
176
+ }
177
+ catch { /* idempotent */ }
178
+ }
179
+ }
180
+ /**
181
+ * BLAKE3 derive_key (BLAKE3 §2.3 Modes — `derive_key`).
182
+ *
183
+ * Two-pass KDF: pass 1 hashes the context string with the
184
+ * DERIVE_KEY_CONTEXT flag, pass 2 hashes the key material with the
185
+ * DERIVE_KEY_MATERIAL flag using the pass-1 output as its starting CV.
186
+ * Context strings are conventionally hardcoded UTF-8 application
187
+ * constants; empty contexts are rejected by `validateContext`.
188
+ */
189
+ export class BLAKE3DeriveKey {
190
+ x;
191
+ constructor() {
192
+ this.x = getExports();
193
+ }
194
+ derive(context, material, outLen = 32) {
195
+ _assertNotOwned('blake3');
196
+ const ctxBytes = validateContext(context);
197
+ if (!(material instanceof Uint8Array))
198
+ throw new TypeError('leviathan-crypto: blake3 derive_key material must be a Uint8Array');
199
+ validateOutputLen(outLen);
200
+ if (outLen > OUTPUT_STAGING_SIZE)
201
+ throw tooBigForOneShotError(outLen);
202
+ return oneShotDeriveKey(this.x, ctxBytes, material, outLen);
203
+ }
204
+ dispose() {
205
+ _assertNotOwned('blake3');
206
+ try {
207
+ this.x.wipeBuffers();
208
+ }
209
+ catch { /* idempotent */ }
210
+ }
211
+ }
212
+ // ── Streaming base ─────────────────────────────────────────────────────────
213
+ //
214
+ // Buffer input, run one-shot WASM hash at finalize. Lifecycle mirrors
215
+ // SHA3_256Stream. See docs/architecture.md.
216
+ class StreamState {
217
+ chunks = [];
218
+ totalLen = 0;
219
+ consumed = false;
220
+ pushChunk(chunk) {
221
+ if (this.consumed)
222
+ throw new Error('BLAKE3 stream: update() after finalize/finalizeXof');
223
+ if (!(chunk instanceof Uint8Array))
224
+ throw new TypeError('BLAKE3 stream: chunk must be a Uint8Array');
225
+ if (this.totalLen + chunk.length > INPUT_SCRATCH_MAX)
226
+ throw tooBigForScratchError(this.totalLen + chunk.length);
227
+ this.chunks.push(chunk);
228
+ this.totalLen += chunk.length;
229
+ }
230
+ concat() {
231
+ if (this.chunks.length === 1)
232
+ return this.chunks[0];
233
+ const out = new Uint8Array(this.totalLen);
234
+ let pos = 0;
235
+ for (const c of this.chunks) {
236
+ out.set(c, pos);
237
+ pos += c.length;
238
+ }
239
+ return out;
240
+ }
241
+ wipe() {
242
+ // Drop references; we don't own caller buffers. concat() buffers
243
+ // are wiped by the calling class.
244
+ this.chunks = [];
245
+ this.totalLen = 0;
246
+ }
247
+ }
248
+ // ── BLAKE3Stream ───────────────────────────────────────────────────────────
249
+ /**
250
+ * Streaming BLAKE3 default-mode hash (BLAKE3 §2.3 Modes — `hash`).
251
+ *
252
+ * `update()` accepts chunks of any size; `finalize()` returns the
253
+ * `outLen`-byte (default 32) digest and disposes the instance. Holds
254
+ * exclusive access to the `blake3` WASM module from construction until
255
+ * `dispose()` or `finalize()` / `finalizeXof()`.
256
+ */
257
+ export class BLAKE3Stream {
258
+ x;
259
+ _tok;
260
+ _state = new StreamState();
261
+ constructor() {
262
+ this.x = getExports();
263
+ this._tok = _acquireModule('blake3');
264
+ }
265
+ update(chunk) {
266
+ this._checkLive();
267
+ this._state.pushChunk(chunk);
268
+ return this;
269
+ }
270
+ finalize(outLen = 32) {
271
+ this._checkLive();
272
+ validateOutputLen(outLen);
273
+ if (outLen > OUTPUT_STAGING_SIZE)
274
+ throw tooBigForOneShotError(outLen);
275
+ this._state.consumed = true;
276
+ const full = this._state.concat();
277
+ try {
278
+ return oneShotHash(this.x, full, outLen);
279
+ }
280
+ finally {
281
+ this.dispose();
282
+ }
283
+ }
284
+ finalizeXof() {
285
+ this._checkLive();
286
+ // _checkLive guarantees _tok is set; the local capture lets the type
287
+ // system see this beyond the assignment to undefined on the next line.
288
+ const tok = this._tok;
289
+ this._state.consumed = true;
290
+ // The reader holds the module token for its own lifetime; transfer
291
+ // ownership rather than releasing-then-reacquiring (which would race
292
+ // against any other consumer trying to acquire the module in between).
293
+ this._tok = undefined;
294
+ const full = this._state.concat();
295
+ return new BLAKE3OutputReader('hash', this.x, tok, full);
296
+ }
297
+ dispose() {
298
+ if (this._tok === undefined)
299
+ return;
300
+ this._state.wipe();
301
+ try {
302
+ this.x.wipeBuffers();
303
+ }
304
+ finally {
305
+ _releaseModule('blake3', this._tok);
306
+ this._tok = undefined;
307
+ }
308
+ }
309
+ _checkLive() {
310
+ if (this._tok === undefined)
311
+ throw new Error('BLAKE3Stream: instance has been disposed');
312
+ if (this._state.consumed)
313
+ throw new Error('BLAKE3Stream: stream has been finalized');
314
+ }
315
+ }
316
+ // ── BLAKE3KeyedHashStream ──────────────────────────────────────────────────
317
+ /**
318
+ * Streaming BLAKE3 keyed_hash (BLAKE3 §2.3 Modes). Key is bound at construction
319
+ * time. Same lifecycle as `BLAKE3Stream`.
320
+ */
321
+ export class BLAKE3KeyedHashStream {
322
+ x;
323
+ _tok;
324
+ _state = new StreamState();
325
+ _key;
326
+ constructor(key) {
327
+ validateKey(key);
328
+ this.x = getExports();
329
+ this._tok = _acquireModule('blake3');
330
+ // Defensive copy: we own this buffer for the lifetime of the stream
331
+ // and wipe it on dispose / finalize.
332
+ this._key = new Uint8Array(32);
333
+ this._key.set(key);
334
+ }
335
+ update(chunk) {
336
+ this._checkLive();
337
+ this._state.pushChunk(chunk);
338
+ return this;
339
+ }
340
+ finalize(outLen = 32) {
341
+ this._checkLive();
342
+ validateOutputLen(outLen);
343
+ if (outLen > OUTPUT_STAGING_SIZE)
344
+ throw tooBigForOneShotError(outLen);
345
+ this._state.consumed = true;
346
+ const full = this._state.concat();
347
+ try {
348
+ return oneShotKeyedHash(this.x, this._key, full, outLen);
349
+ }
350
+ finally {
351
+ this.dispose();
352
+ }
353
+ }
354
+ finalizeXof() {
355
+ this._checkLive();
356
+ const tok = this._tok;
357
+ this._state.consumed = true;
358
+ this._tok = undefined;
359
+ const full = this._state.concat();
360
+ const reader = new BLAKE3OutputReader('keyed', this.x, tok, full, this._key);
361
+ // Reader owns its own copy of the key; wipe the stream's.
362
+ this._key.fill(0);
363
+ return reader;
364
+ }
365
+ dispose() {
366
+ if (this._tok === undefined)
367
+ return;
368
+ this._key.fill(0);
369
+ this._state.wipe();
370
+ try {
371
+ this.x.wipeBuffers();
372
+ }
373
+ finally {
374
+ _releaseModule('blake3', this._tok);
375
+ this._tok = undefined;
376
+ }
377
+ }
378
+ _checkLive() {
379
+ if (this._tok === undefined)
380
+ throw new Error('BLAKE3KeyedHashStream: instance has been disposed');
381
+ if (this._state.consumed)
382
+ throw new Error('BLAKE3KeyedHashStream: stream has been finalized');
383
+ }
384
+ }
385
+ // ── BLAKE3DeriveKeyStream ──────────────────────────────────────────────────
386
+ /**
387
+ * Streaming BLAKE3 derive_key (BLAKE3 §2.3 Modes). Context is bound at
388
+ * construction time; updates stream the material; finalize derives.
389
+ * Same lifecycle as `BLAKE3Stream`.
390
+ */
391
+ export class BLAKE3DeriveKeyStream {
392
+ x;
393
+ _tok;
394
+ _state = new StreamState();
395
+ _ctxBytes;
396
+ constructor(context) {
397
+ this._ctxBytes = validateContext(context);
398
+ this.x = getExports();
399
+ this._tok = _acquireModule('blake3');
400
+ }
401
+ update(chunk) {
402
+ this._checkLive();
403
+ this._state.pushChunk(chunk);
404
+ return this;
405
+ }
406
+ finalize(outLen = 32) {
407
+ this._checkLive();
408
+ validateOutputLen(outLen);
409
+ if (outLen > OUTPUT_STAGING_SIZE)
410
+ throw tooBigForOneShotError(outLen);
411
+ this._state.consumed = true;
412
+ const full = this._state.concat();
413
+ try {
414
+ return oneShotDeriveKey(this.x, this._ctxBytes, full, outLen);
415
+ }
416
+ finally {
417
+ this.dispose();
418
+ }
419
+ }
420
+ finalizeXof() {
421
+ this._checkLive();
422
+ const tok = this._tok;
423
+ this._state.consumed = true;
424
+ this._tok = undefined;
425
+ const full = this._state.concat();
426
+ return new BLAKE3OutputReader('derive', this.x, tok, full, undefined, this._ctxBytes);
427
+ }
428
+ dispose() {
429
+ if (this._tok === undefined)
430
+ return;
431
+ this._state.wipe();
432
+ try {
433
+ this.x.wipeBuffers();
434
+ }
435
+ finally {
436
+ _releaseModule('blake3', this._tok);
437
+ this._tok = undefined;
438
+ }
439
+ }
440
+ _checkLive() {
441
+ if (this._tok === undefined)
442
+ throw new Error('BLAKE3DeriveKeyStream: instance has been disposed');
443
+ if (this._state.consumed)
444
+ throw new Error('BLAKE3DeriveKeyStream: stream has been finalized');
445
+ }
446
+ }
447
+ /**
448
+ * BLAKE3 XOF reader (BLAKE3 §2.6 Extendable Output).
449
+ *
450
+ * Sequential `read(nBytes)` calls squeeze the next bytes of XOF output.
451
+ * Constructed via `finalizeXof()` on a streaming class. Holds module
452
+ * exclusivity until `dispose()`.
453
+ *
454
+ * Implementation: the first `read()` runs the underlying hash entry once
455
+ * to populate the WASM-side root-compress snapshot (ROOT_STATE_*), then
456
+ * caches the first 64-byte XOF block. Subsequent reads pump
457
+ * `squeezeXofBlock` on the WASM module with an incrementing counter to
458
+ * lift additional 64-byte blocks off the snapshot. The reader's lifetime
459
+ * coincides with its hold on the module token, so `ROOT_STATE_*` stays
460
+ * intact between read calls (no other consumer can fire a hash that
461
+ * would clobber it).
462
+ */
463
+ export class BLAKE3OutputReader {
464
+ x;
465
+ _tok;
466
+ _mode;
467
+ _input;
468
+ _key;
469
+ _ctx;
470
+ _blockBuf = new Uint8Array(ROOT_BLOCK_SIZE);
471
+ _blockPos = ROOT_BLOCK_SIZE; // forces refill on first read
472
+ _nextCounter = 0n;
473
+ _populated = false;
474
+ /** @internal Constructed by `finalizeXof()` on a streaming class. */
475
+ constructor(mode, x, tok, input, key, ctx) {
476
+ this._mode = mode;
477
+ this.x = x;
478
+ this._tok = tok;
479
+ this._input = input;
480
+ // Defensive copy of key, the reader outlives the parent stream's
481
+ // _key buffer (which is wiped on transfer).
482
+ if (key) {
483
+ this._key = new Uint8Array(32);
484
+ this._key.set(key);
485
+ }
486
+ else {
487
+ this._key = undefined;
488
+ }
489
+ this._ctx = ctx;
490
+ }
491
+ read(nBytes) {
492
+ if (this._tok === undefined)
493
+ throw new Error('BLAKE3OutputReader: instance has been disposed');
494
+ validateOutputLen(nBytes);
495
+ if (!this._populated) {
496
+ this._populate();
497
+ }
498
+ const out = new Uint8Array(nBytes);
499
+ let pos = 0;
500
+ while (pos < nBytes) {
501
+ if (this._blockPos >= ROOT_BLOCK_SIZE) {
502
+ this._squeezeNextBlock();
503
+ }
504
+ const available = ROOT_BLOCK_SIZE - this._blockPos;
505
+ const take = Math.min(nBytes - pos, available);
506
+ out.set(this._blockBuf.subarray(this._blockPos, this._blockPos + take), pos);
507
+ this._blockPos += take;
508
+ pos += take;
509
+ }
510
+ return out;
511
+ }
512
+ dispose() {
513
+ if (this._tok === undefined)
514
+ return;
515
+ this._blockBuf.fill(0);
516
+ this._blockPos = ROOT_BLOCK_SIZE;
517
+ this._nextCounter = 0n;
518
+ if (this._key)
519
+ this._key.fill(0);
520
+ try {
521
+ this.x.wipeBuffers();
522
+ }
523
+ finally {
524
+ _releaseModule('blake3', this._tok);
525
+ this._tok = undefined;
526
+ }
527
+ }
528
+ // Captures ROOT_STATE_* inside WASM, copies block 0 to _blockBuf.
529
+ // Does NOT call wipeBuffers; that would clear ROOT_STATE_* and break
530
+ // subsequent squeezeXofBlock calls. The reader wipes on dispose.
531
+ _populate() {
532
+ const x = this.x;
533
+ const outOff = x.getOutputStagingOffset();
534
+ const mem = new Uint8Array(x.memory.buffer);
535
+ if (this._input.length > INPUT_SCRATCH_MAX)
536
+ throw tooBigForScratchError(this._input.length);
537
+ mem.set(this._input, INPUT_SCRATCH_OFF);
538
+ try {
539
+ switch (this._mode) {
540
+ case 'hash':
541
+ x.hash(INPUT_SCRATCH_OFF, this._input.length, outOff, ROOT_BLOCK_SIZE);
542
+ break;
543
+ case 'keyed': {
544
+ if (!this._key)
545
+ throw new Error('BLAKE3OutputReader: keyed mode without key');
546
+ const keyOff = x.getKeyedKeyOffset();
547
+ mem.set(this._key, keyOff);
548
+ try {
549
+ x.hashKeyed(keyOff, INPUT_SCRATCH_OFF, this._input.length, outOff, ROOT_BLOCK_SIZE);
550
+ }
551
+ finally {
552
+ mem.fill(0, keyOff, keyOff + 32);
553
+ }
554
+ break;
555
+ }
556
+ case 'derive': {
557
+ if (!this._ctx)
558
+ throw new Error('BLAKE3OutputReader: derive mode without context');
559
+ const ctx = this._ctx;
560
+ if (ctx.length + this._input.length > INPUT_SCRATCH_MAX)
561
+ throw tooBigForScratchError(ctx.length + this._input.length);
562
+ // Re-stage with context prefixed in front of material.
563
+ mem.set(ctx, INPUT_SCRATCH_OFF);
564
+ mem.set(this._input, INPUT_SCRATCH_OFF + ctx.length);
565
+ try {
566
+ x.deriveKey(INPUT_SCRATCH_OFF, ctx.length, INPUT_SCRATCH_OFF + ctx.length, this._input.length, outOff, ROOT_BLOCK_SIZE);
567
+ }
568
+ finally {
569
+ mem.fill(0, INPUT_SCRATCH_OFF, INPUT_SCRATCH_OFF + ctx.length);
570
+ }
571
+ break;
572
+ }
573
+ }
574
+ this._blockBuf.set(mem.subarray(outOff, outOff + ROOT_BLOCK_SIZE));
575
+ this._blockPos = 0;
576
+ this._nextCounter = 1n;
577
+ this._populated = true;
578
+ }
579
+ finally {
580
+ const inLen = this._mode === 'derive' && this._ctx
581
+ ? this._ctx.length + this._input.length
582
+ : this._input.length;
583
+ mem.fill(0, INPUT_SCRATCH_OFF, INPUT_SCRATCH_OFF + inLen);
584
+ mem.fill(0, outOff, outOff + ROOT_BLOCK_SIZE);
585
+ }
586
+ }
587
+ _squeezeNextBlock() {
588
+ const x = this.x;
589
+ const outOff = x.getOutputStagingOffset();
590
+ const ctr = this._nextCounter;
591
+ const ctrLo = Number(ctr & 0xffffffffn);
592
+ const ctrHi = Number((ctr >> 32n) & 0xffffffffn);
593
+ x.squeezeXofBlock(ctrLo, ctrHi, outOff);
594
+ const mem = new Uint8Array(x.memory.buffer);
595
+ this._blockBuf.set(mem.subarray(outOff, outOff + ROOT_BLOCK_SIZE));
596
+ this._blockPos = 0;
597
+ this._nextCounter = ctr + 1n;
598
+ // Scrub the staging slot now that the bytes are in _blockBuf; the
599
+ // reader's wipe-on-dispose covers _blockBuf itself.
600
+ mem.fill(0, outOff, outOff + ROOT_BLOCK_SIZE);
601
+ }
602
+ }
603
+ // ── BLAKE3Hash, Fortuna HashFn const ───────────────────────────────────────
604
+ /**
605
+ * Stateless BLAKE3-256 HashFn. Shape mirrors `SHA256Hash` in
606
+ * `src/ts/sha2/hash.ts`: 32-byte output, single WASM module dependency,
607
+ * `digest(msg)` runs a one-shot hash with default `outLen`.
608
+ *
609
+ * Usable as a Fortuna accumulator / reseed hash when paired with a
610
+ * matching 32-byte-key Generator.
611
+ */
612
+ export const BLAKE3Hash = {
613
+ outputSize: 32,
614
+ wasmModules: ['blake3'],
615
+ digest(msg) {
616
+ _assertNotOwned('blake3');
617
+ const x = getExports();
618
+ return oneShotHash(x, msg, 32);
619
+ },
620
+ };