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,300 @@
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/merkle/proof.ts
23
+ //
24
+ // Hash-agnostic, free-function proof verifiers and builders for the
25
+ // RFC 9162 (Certificate Transparency Version 2.0) §2.1.3 / §2.1.4
26
+ // proof formats. Every entry point takes a `Hasher`; SHA-256 and
27
+ // BLAKE3 trees share the same wire format and the same algorithmic
28
+ // core.
29
+ //
30
+ // Verifiers return boolean. Malformed-proof conditions (wrong inner /
31
+ // border length, mismatched root) return false. Contract violations
32
+ // (wrong-sized root for the hasher, leafIndex out of range, oldSize >
33
+ // newSize) throw RangeError; the caller is responsible for staying
34
+ // within the public contract.
35
+ //
36
+ // Builders accept a `getNode(level, index)` callback that abstracts
37
+ // the storage layer. Memory, file, and database backends drive the
38
+ // same builder without bringing storage details into the proof
39
+ // algorithms.
40
+ import { bitLen, popcount, splitPoint, trailingZeros } from './tree.js';
41
+ import { constantTimeEqual } from '../utils.js';
42
+ // ── Internal chaining primitives (RFC 9162 §2.1.3 / §2.1.4) ─────────────────
43
+ /**
44
+ * Decompose an inclusion proof into its inner (path-up-the-tree) and
45
+ * border (left siblings completing the right edge) segments. The sum
46
+ * inner + border is the required proof length.
47
+ *
48
+ * RFC 9162 §2.1.3, Inclusion Proof Verification: the path from a leaf
49
+ * at index to the root of a size-n tree has bitLen(index XOR (size-1))
50
+ * inner levels and popcount(index >> inner) border levels.
51
+ */
52
+ function decompInclProof(index, size) {
53
+ const inner = bitLen(index ^ (size - 1));
54
+ const border = popcount(Math.floor(index / 2 ** inner));
55
+ return { inner, border };
56
+ }
57
+ /**
58
+ * Chain `inner` proof entries up from `seed`. At level i, the bit
59
+ * (index >> i) & 1 selects whether the sibling is on the left or the
60
+ * right of `seed`. RFC 9162 §2.1.3.
61
+ */
62
+ function chainInner(hasher, seed, proof, index) {
63
+ let acc = seed;
64
+ for (let i = 0; i < proof.length; i++) {
65
+ const bit = Math.floor(index / 2 ** i) & 1;
66
+ acc = bit === 0
67
+ ? hasher.hashInternal(acc, proof[i])
68
+ : hasher.hashInternal(proof[i], acc);
69
+ }
70
+ return acc;
71
+ }
72
+ /**
73
+ * Chain `inner` entries but only fold in left siblings (skip the right
74
+ * ones). Used by the consistency verifier to reconstruct the OLD root
75
+ * from the suffix shared with the inclusion proof. RFC 9162 §2.1.4.
76
+ */
77
+ function chainInnerRight(hasher, seed, proof, index) {
78
+ let acc = seed;
79
+ for (let i = 0; i < proof.length; i++) {
80
+ const bit = Math.floor(index / 2 ** i) & 1;
81
+ if (bit === 1)
82
+ acc = hasher.hashInternal(proof[i], acc);
83
+ }
84
+ return acc;
85
+ }
86
+ /**
87
+ * Chain border entries: every remaining sibling is a left sibling
88
+ * along the size-1 path back to the root. RFC 9162 §2.1.3.
89
+ */
90
+ function chainBorderRight(hasher, seed, proof) {
91
+ let acc = seed;
92
+ for (const h of proof)
93
+ acc = hasher.hashInternal(h, acc);
94
+ return acc;
95
+ }
96
+ function assertHashLen(hasher, label, h) {
97
+ if (h.length !== hasher.outputSize)
98
+ throw new RangeError(`${label}: wrong length ${h.length}, expected ${hasher.outputSize} for ${hasher.name}`);
99
+ }
100
+ /**
101
+ * RFC 9162 §2.1.3, Inclusion Proof Verification. Returns true if the
102
+ * proof reconstructs `rootHash` from `leafHash` at position
103
+ * (leafIndex, treeSize). Wrong proof length, wrong leaf-hash size, or
104
+ * a reconstructed root that differs from `rootHash` all return false.
105
+ * Contract violations (negative or out-of-range index, treeSize <= 0,
106
+ * wrong-sized rootHash) throw RangeError.
107
+ *
108
+ * `leafHash` is the leaf's MTH ({d_m} hashed under the leaf prefix), not
109
+ * the raw leaf bytes. Thin verifiers receiving a leaf over the wire
110
+ * should compute `hasher.hashLeaf(bytes)` before calling.
111
+ */
112
+ export function verifyInclusionProof(input) {
113
+ const { hasher, leafHash, leafIndex, treeSize, proof, rootHash } = input;
114
+ if (!Number.isInteger(leafIndex) || leafIndex < 0)
115
+ throw new RangeError(`verifyInclusionProof: leafIndex must be a non-negative integer, got ${leafIndex}`);
116
+ if (!Number.isInteger(treeSize) || treeSize < 1)
117
+ throw new RangeError(`verifyInclusionProof: treeSize must be a positive integer, got ${treeSize}`);
118
+ if (leafIndex >= treeSize)
119
+ throw new RangeError(`verifyInclusionProof: leafIndex ${leafIndex} >= treeSize ${treeSize}`);
120
+ assertHashLen(hasher, 'verifyInclusionProof: rootHash', rootHash);
121
+ if (leafHash.length !== hasher.outputSize)
122
+ return false;
123
+ const { inner, border } = decompInclProof(leafIndex, treeSize);
124
+ if (proof.length !== inner + border)
125
+ return false;
126
+ for (const h of proof) {
127
+ if (h.length !== hasher.outputSize)
128
+ return false;
129
+ }
130
+ const innerProof = proof.slice(0, inner);
131
+ const borderProof = proof.slice(inner);
132
+ let res = chainInner(hasher, leafHash, innerProof, leafIndex);
133
+ res = chainBorderRight(hasher, res, borderProof);
134
+ return constantTimeEqual(res, rootHash);
135
+ }
136
+ /**
137
+ * RFC 9162 §2.1.4, Consistency Proof Verification. Returns true if
138
+ * `proof` proves that the size-`oldSize` tree with root `oldRoot` is a
139
+ * prefix of the size-`newSize` tree with root `newRoot`.
140
+ *
141
+ * Malformed-proof conditions (wrong proof length, non-empty proof when
142
+ * one is forbidden, mismatched old/new root reconstruction) return
143
+ * false. Contract violations (`oldSize > newSize`, wrong-sized root)
144
+ * throw RangeError; the special "consistency from empty tree" form is
145
+ * not part of the wire format and returns false.
146
+ */
147
+ export function verifyConsistencyProof(input) {
148
+ const { hasher, oldSize, newSize, oldRoot, newRoot, proof } = input;
149
+ if (!Number.isInteger(oldSize) || oldSize < 0)
150
+ throw new RangeError(`verifyConsistencyProof: oldSize must be a non-negative integer, got ${oldSize}`);
151
+ if (!Number.isInteger(newSize) || newSize < 0)
152
+ throw new RangeError(`verifyConsistencyProof: newSize must be a non-negative integer, got ${newSize}`);
153
+ if (oldSize > newSize)
154
+ throw new RangeError(`verifyConsistencyProof: oldSize ${oldSize} > newSize ${newSize}`);
155
+ // Equal-size shortcut: RFC says the proof is empty and roots match.
156
+ // Byte-for-byte comparison; root hashes flow through unchanged because
157
+ // no reconstruction runs, so hash-length validation does not apply.
158
+ if (oldSize === newSize) {
159
+ if (proof.length > 0)
160
+ return false;
161
+ return oldRoot.length === newRoot.length && constantTimeEqual(oldRoot, newRoot);
162
+ }
163
+ // "Consistency from empty tree" is undefined: the verifier cannot
164
+ // recover oldRoot from no proof, so reject as malformed.
165
+ if (oldSize === 0)
166
+ return false;
167
+ if (proof.length === 0)
168
+ return false;
169
+ assertHashLen(hasher, 'verifyConsistencyProof: oldRoot', oldRoot);
170
+ assertHashLen(hasher, 'verifyConsistencyProof: newRoot', newRoot);
171
+ for (const h of proof) {
172
+ if (h.length !== hasher.outputSize)
173
+ return false;
174
+ }
175
+ const { inner: innerFull, border } = decompInclProof(oldSize - 1, newSize);
176
+ const shift = trailingZeros(oldSize);
177
+ const inner = innerFull - shift;
178
+ // If oldSize is a power of two, the verifier already knows the
179
+ // subtree's root (== oldRoot) and the proof omits it. Otherwise the
180
+ // proof's first element is the seed for both chains.
181
+ const oldIsPow2 = oldSize === 2 ** shift;
182
+ let seed;
183
+ let start;
184
+ if (oldIsPow2) {
185
+ seed = oldRoot;
186
+ start = 0;
187
+ }
188
+ else {
189
+ seed = proof[0];
190
+ start = 1;
191
+ }
192
+ const expectedLen = start + inner + border;
193
+ if (proof.length !== expectedLen)
194
+ return false;
195
+ const tail = proof.slice(start);
196
+ const innerProof = tail.slice(0, inner);
197
+ const borderProof = tail.slice(inner);
198
+ // Bit pattern for chainInnerRight: we re-derive the oldRoot from
199
+ // the proof. `mask` is (oldSize - 1) >> shift, the path bits above
200
+ // the size-`oldSize` subtree's root level.
201
+ const mask = Math.floor((oldSize - 1) / 2 ** shift);
202
+ let hash1 = chainInnerRight(hasher, seed, innerProof, mask);
203
+ hash1 = chainBorderRight(hasher, hash1, borderProof);
204
+ if (!constantTimeEqual(hash1, oldRoot))
205
+ return false;
206
+ let hash2 = chainInner(hasher, seed, innerProof, mask);
207
+ hash2 = chainBorderRight(hasher, hash2, borderProof);
208
+ return constantTimeEqual(hash2, newRoot);
209
+ }
210
+ /**
211
+ * RFC 9162 §2.1.3: build the inclusion proof for leaf `leafIndex` in
212
+ * a tree of size `treeSize`. The returned bytes are ordered from the
213
+ * lowest level upward (leaf sibling first, root-adjacent last), the
214
+ * order `verifyInclusionProof` consumes.
215
+ */
216
+ export function buildInclusionProof(input) {
217
+ const { hasher, leafIndex, treeSize, getNode } = input;
218
+ if (!Number.isInteger(leafIndex) || leafIndex < 0)
219
+ throw new RangeError(`buildInclusionProof: leafIndex must be a non-negative integer, got ${leafIndex}`);
220
+ if (!Number.isInteger(treeSize) || treeSize < 1)
221
+ throw new RangeError(`buildInclusionProof: treeSize must be a positive integer, got ${treeSize}`);
222
+ if (leafIndex >= treeSize)
223
+ throw new RangeError(`buildInclusionProof: leafIndex ${leafIndex} >= treeSize ${treeSize}`);
224
+ return pathBuild(hasher, leafIndex, 0, treeSize, getNode);
225
+ }
226
+ /**
227
+ * RFC 9162 §2.1.4: build the consistency proof between two tree
228
+ * sizes. Returns an empty array when oldSize equals newSize or
229
+ * oldSize is zero (the verifier rejects the latter, but the builder
230
+ * is symmetric for inspection-time use).
231
+ */
232
+ export function buildConsistencyProof(input) {
233
+ const { hasher, oldSize, newSize, getNode } = input;
234
+ if (!Number.isInteger(oldSize) || oldSize < 0)
235
+ throw new RangeError(`buildConsistencyProof: oldSize must be a non-negative integer, got ${oldSize}`);
236
+ if (!Number.isInteger(newSize) || newSize < 0)
237
+ throw new RangeError(`buildConsistencyProof: newSize must be a non-negative integer, got ${newSize}`);
238
+ if (oldSize > newSize)
239
+ throw new RangeError(`buildConsistencyProof: oldSize ${oldSize} > newSize ${newSize}`);
240
+ if (oldSize === newSize || oldSize === 0)
241
+ return [];
242
+ return subProof(hasher, oldSize, 0, newSize, true, getNode);
243
+ }
244
+ // RFC 9162 §2.1.4 SUBPROOF(m, D[n], b). `lo` and `hi` parameterise
245
+ // the [lo, hi) range covered by the current subtree; `m` is the size
246
+ // of the older subtree being witnessed.
247
+ function subProof(hasher, m, lo, hi, b, getNode) {
248
+ const n = hi - lo;
249
+ if (m === n) {
250
+ // Whole subtree: emit its root only if b == false.
251
+ return b ? [] : [subtreeHash(hasher, lo, hi, getNode)];
252
+ }
253
+ const k = splitPoint(n);
254
+ if (m <= k) {
255
+ const sub = subProof(hasher, m, lo, lo + k, b, getNode);
256
+ sub.push(subtreeHash(hasher, lo + k, hi, getNode));
257
+ return sub;
258
+ }
259
+ const sub = subProof(hasher, m - k, lo + k, hi, false, getNode);
260
+ sub.push(subtreeHash(hasher, lo, lo + k, getNode));
261
+ return sub;
262
+ }
263
+ // Inclusion-proof path build: yields siblings ordered from the lowest
264
+ // level (leaf sibling) up. Sibling = root of the other half of the
265
+ // current subtree.
266
+ function pathBuild(hasher, leafIndex, lo, hi, getNode) {
267
+ if (hi - lo <= 1)
268
+ return [];
269
+ const k = splitPoint(hi - lo);
270
+ if (leafIndex - lo < k) {
271
+ const sub = pathBuild(hasher, leafIndex, lo, lo + k, getNode);
272
+ sub.push(subtreeHash(hasher, lo + k, hi, getNode));
273
+ return sub;
274
+ }
275
+ const sub = pathBuild(hasher, leafIndex, lo + k, hi, getNode);
276
+ sub.push(subtreeHash(hasher, lo, lo + k, getNode));
277
+ return sub;
278
+ }
279
+ /**
280
+ * RFC 9162 §2.1.1 MTH(D[lo:hi]). For a perfect aligned subtree the
281
+ * value is stored at (level, index); otherwise the value is the
282
+ * internal hash of the perfect left half and the recursive right
283
+ * half. Visible to the tree class so `rootHash()` can share the
284
+ * recursion with the builders.
285
+ *
286
+ * @internal
287
+ */
288
+ export function subtreeHash(hasher, lo, hi, getNode) {
289
+ const n = hi - lo;
290
+ if (n === 1)
291
+ return getNode(0, lo);
292
+ const k = splitPoint(n);
293
+ if (k === n / 2 && (lo % n) === 0) {
294
+ // Perfect aligned subtree: a stored internal node.
295
+ return getNode(bitLen(n) - 1, Math.floor(lo / n));
296
+ }
297
+ const left = subtreeHash(hasher, lo, lo + k, getNode);
298
+ const right = subtreeHash(hasher, lo + k, hi, getNode);
299
+ return hasher.hashInternal(left, right);
300
+ }
@@ -0,0 +1,33 @@
1
+ import type { Hasher, MerkleTree } from './tree.js';
2
+ import type { MerkleStorage } from './storage.js';
3
+ /**
4
+ * RFC 9162 §2.1.1, Merkle Hash Trees. The CT-flavoured SHA-256 hash
5
+ * function: empty-tree value `MTH({}) = SHA-256()`, leaf prefix `0x00`,
6
+ * internal-node prefix `0x01`.
7
+ *
8
+ * Stateless and reentrant: each method takes the sha2 module fresh,
9
+ * runs SHA-256 once, and releases. No `dispose()` is needed.
10
+ */
11
+ export declare const Sha256Hasher: Hasher;
12
+ /**
13
+ * Stateful SHA-256 Merkle log. Stores leaf hashes and every perfect
14
+ * aligned internal subtree's hash via the injected `MerkleStorage`;
15
+ * partial right-edge subtrees are recomputed on demand from the
16
+ * stored perfect subtrees.
17
+ *
18
+ * Constructed empty. `append` is the only mutator and is the leaf-hash
19
+ * factory; consumers feed leaf bytes, not pre-computed leaf hashes.
20
+ */
21
+ export declare class Sha256Tree implements MerkleTree {
22
+ readonly hasher: Hasher;
23
+ private readonly storage;
24
+ constructor(storage: MerkleStorage);
25
+ size(): number;
26
+ rootHash(): Uint8Array;
27
+ append(leafBytes: Uint8Array): {
28
+ leafIndex: number;
29
+ leafHash: Uint8Array;
30
+ };
31
+ getInclusionProof(leafIndex: number, treeSize?: number): Uint8Array[];
32
+ getConsistencyProof(oldSize: number, newSize: number): Uint8Array[];
33
+ }
@@ -0,0 +1,145 @@
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/merkle/sha256-tree.ts
23
+ //
24
+ // SHA-256 specialisation of the Hasher / MerkleTree interfaces. Wraps
25
+ // the existing `SHA256` class from the sha2 module under the RFC 9162
26
+ // (Certificate Transparency Version 2.0) §2.1.1 leaf and internal-node
27
+ // domain separators.
28
+ //
29
+ // Per-call WASM lifecycle: every Sha256Hasher method instantiates a
30
+ // fresh SHA256 inside a try / finally + dispose pattern. There is no
31
+ // long-lived module ownership; concurrent users are serialised by the
32
+ // per-module exclusivity guard in the sha2 substrate. This mirrors
33
+ // the SignatureSuite factories under src/ts/sign/suites/.
34
+ import { SHA256 } from '../sha2/index.js';
35
+ import { buildConsistencyProof, buildInclusionProof, subtreeHash, } from './proof.js';
36
+ // ── Sha256Hasher const ──────────────────────────────────────────────────────
37
+ const SHA256_OUTPUT = 32;
38
+ const LEAF_PREFIX = new Uint8Array([0x00]);
39
+ const INTERNAL_PREFIX = new Uint8Array([0x01]);
40
+ const SHA256_WASM_MODULES = Object.freeze(['sha2']);
41
+ function sha256Hash(input) {
42
+ const h = new SHA256();
43
+ try {
44
+ return h.hash(input);
45
+ }
46
+ finally {
47
+ h.dispose();
48
+ }
49
+ }
50
+ /**
51
+ * RFC 9162 §2.1.1, Merkle Hash Trees. The CT-flavoured SHA-256 hash
52
+ * function: empty-tree value `MTH({}) = SHA-256()`, leaf prefix `0x00`,
53
+ * internal-node prefix `0x01`.
54
+ *
55
+ * Stateless and reentrant: each method takes the sha2 module fresh,
56
+ * runs SHA-256 once, and releases. No `dispose()` is needed.
57
+ */
58
+ export const Sha256Hasher = Object.freeze({
59
+ name: 'sha256',
60
+ outputSize: SHA256_OUTPUT,
61
+ wasmModules: SHA256_WASM_MODULES,
62
+ hashEmpty() {
63
+ // RFC 9162 §2.1.1: MTH({}) is the hash of an empty bit-string.
64
+ return sha256Hash(new Uint8Array(0));
65
+ },
66
+ hashLeaf(leaf) {
67
+ // RFC 9162 §2.1.1: MTH({d}) = HASH(0x00 || d). The 0x00 prefix
68
+ // is the domain separator that prevents an internal-node hash
69
+ // from being mistaken for a leaf hash.
70
+ const buf = new Uint8Array(1 + leaf.length);
71
+ buf.set(LEAF_PREFIX, 0);
72
+ buf.set(leaf, 1);
73
+ return sha256Hash(buf);
74
+ },
75
+ hashInternal(left, right) {
76
+ // RFC 9162 §2.1.1: MTH(D[n]) = HASH(0x01 || MTH(D[0:k]) ||
77
+ // MTH(D[k:n])). The 0x01 prefix is the other half of the
78
+ // second-preimage-resistance domain separator.
79
+ const buf = new Uint8Array(1 + left.length + right.length);
80
+ buf.set(INTERNAL_PREFIX, 0);
81
+ buf.set(left, 1);
82
+ buf.set(right, 1 + left.length);
83
+ return sha256Hash(buf);
84
+ },
85
+ });
86
+ // ── Sha256Tree class ────────────────────────────────────────────────────────
87
+ /**
88
+ * Stateful SHA-256 Merkle log. Stores leaf hashes and every perfect
89
+ * aligned internal subtree's hash via the injected `MerkleStorage`;
90
+ * partial right-edge subtrees are recomputed on demand from the
91
+ * stored perfect subtrees.
92
+ *
93
+ * Constructed empty. `append` is the only mutator and is the leaf-hash
94
+ * factory; consumers feed leaf bytes, not pre-computed leaf hashes.
95
+ */
96
+ export class Sha256Tree {
97
+ hasher = Sha256Hasher;
98
+ storage;
99
+ constructor(storage) {
100
+ this.storage = storage;
101
+ }
102
+ size() {
103
+ return this.storage.size();
104
+ }
105
+ rootHash() {
106
+ const n = this.storage.size();
107
+ if (n === 0)
108
+ return this.hasher.hashEmpty();
109
+ const getNode = (level, index) => this.storage.getNode(level, index);
110
+ return subtreeHash(this.hasher, 0, n, getNode);
111
+ }
112
+ append(leafBytes) {
113
+ const leafIndex = this.storage.size();
114
+ const leafHash = this.hasher.hashLeaf(leafBytes);
115
+ this.storage.appendLeaf(leafIndex, leafHash);
116
+ // Propagate completed internal nodes up the right edge. RFC 9162
117
+ // §2.1.1 makes the tree fill left-to-right; whenever a node lands
118
+ // at an odd index its left sibling already exists, so the parent
119
+ // becomes computable for free.
120
+ let level = 0;
121
+ let idx = leafIndex;
122
+ while ((idx & 1) === 1) {
123
+ const left = this.storage.getNode(level, idx - 1);
124
+ const right = this.storage.getNode(level, idx);
125
+ const parent = this.hasher.hashInternal(left, right);
126
+ this.storage.putNode(level + 1, idx >>> 1, parent);
127
+ idx = idx >>> 1;
128
+ level++;
129
+ }
130
+ return { leafIndex, leafHash };
131
+ }
132
+ getInclusionProof(leafIndex, treeSize) {
133
+ const ts = treeSize ?? this.storage.size();
134
+ if (!Number.isInteger(ts) || ts < 1 || ts > this.storage.size())
135
+ throw new RangeError(`Sha256Tree.getInclusionProof: treeSize ${ts} out of range [1, ${this.storage.size()}]`);
136
+ const getNode = (level, index) => this.storage.getNode(level, index);
137
+ return buildInclusionProof({ hasher: this.hasher, leafIndex, treeSize: ts, getNode });
138
+ }
139
+ getConsistencyProof(oldSize, newSize) {
140
+ if (!Number.isInteger(newSize) || newSize < 0 || newSize > this.storage.size())
141
+ throw new RangeError(`Sha256Tree.getConsistencyProof: newSize ${newSize} out of range [0, ${this.storage.size()}]`);
142
+ const getNode = (level, index) => this.storage.getNode(level, index);
143
+ return buildConsistencyProof({ hasher: this.hasher, oldSize, newSize, getNode });
144
+ }
145
+ }
@@ -0,0 +1,156 @@
1
+ import type { SignedTreeHead } from './sth.js';
2
+ import type { MerkleTree } from './tree.js';
3
+ import type { SignatureSuite } from '../sign/types.js';
4
+ /**
5
+ * Constructor options for `SignedLog`.
6
+ */
7
+ export interface SignedLogOpts<S extends SignatureSuite> {
8
+ /** Underlying Merkle tree, holds the stateful append + proof surface. */
9
+ tree: MerkleTree;
10
+ /**
11
+ * Signature suite. Must have an entry in the C2SP cosignature
12
+ * algorithm-byte registry (currently `Ed25519Suite` and
13
+ * `MlDsa44Suite`); other suites throw `SigningError`.
14
+ */
15
+ suite: S;
16
+ /**
17
+ * Log identity, the first line of every checkpoint body. Validated
18
+ * at construction (non-empty, no whitespace, no plus characters)
19
+ * per c2sp.org/tlog-checkpoint §Note text.
20
+ */
21
+ origin: string;
22
+ /**
23
+ * Signing key, exactly `suite.skSize` bytes. The SignedLog stores
24
+ * a private copy; `dispose()` zeroes that copy. The caller's view
25
+ * of the buffer is left untouched.
26
+ */
27
+ signingKey: Uint8Array;
28
+ /**
29
+ * Public key, exactly `suite.pkSize` bytes. Used to derive the
30
+ * 4-byte keyId stamped on every emitted signature line and to
31
+ * match incoming signature lines during verify.
32
+ */
33
+ pubkey: Uint8Array;
34
+ }
35
+ /**
36
+ * Signed transparency log substrate. Combines a `MerkleTree` with a
37
+ * registered cosignature `SignatureSuite` and an origin string;
38
+ * exposes append, proof, and cosignature sign / verify operations.
39
+ *
40
+ * Per-call WASM lifecycle is enforced by the suite itself (see the
41
+ * SignatureSuite factories under `src/ts/sign/suites/`). `SignedLog`
42
+ * does not wrap additional try/finally around `suite.sign` /
43
+ * `suite.verify` because the suite already does. Internally the
44
+ * SignedLog owns a private copy of the signing key wiped by
45
+ * `dispose()`.
46
+ */
47
+ export declare class SignedLog<S extends SignatureSuite> {
48
+ readonly tree: MerkleTree;
49
+ readonly suite: S;
50
+ readonly origin: string;
51
+ readonly pubkey: Uint8Array;
52
+ readonly wasmModules: readonly string[];
53
+ private readonly _algoEntry;
54
+ private readonly _keyId;
55
+ private _signingKey;
56
+ private _disposed;
57
+ constructor(opts: SignedLogOpts<S>);
58
+ /**
59
+ * Append a leaf to the underlying tree and return the new leaf's
60
+ * index, hash, and inclusion proof against the post-append tree size.
61
+ */
62
+ append(leafBytes: Uint8Array): {
63
+ leafIndex: number;
64
+ leafHash: Uint8Array;
65
+ inclusionProof: Uint8Array[];
66
+ };
67
+ size(): number;
68
+ rootHash(): Uint8Array;
69
+ getInclusionProof(leafIndex: number, treeSize?: number): Uint8Array[];
70
+ getConsistencyProof(oldSize: number, newSize: number): Uint8Array[];
71
+ /**
72
+ * Issue a cosignature over the current checkpoint and emit the
73
+ * signed-note envelope per c2sp.org/signed-note §Format. The
74
+ * signature line carries the `timestamped_signature` payload
75
+ * from c2sp.org/tlog-cosignature §Format; the bytes the suite
76
+ * signs are dispatched on the algorithm's
77
+ * `messageConstruction`:
78
+ *
79
+ * - `'cosig'` → `buildCosigSignedMessage(body, ts)`
80
+ * (Ed25519, §"Ed25519 signed message")
81
+ * - `'cosigned-message'` → `buildCosignedMessage(...)`
82
+ * (ML-DSA-44, §"ML-DSA-44 signed message")
83
+ *
84
+ * `timestamp` defaults to current wall-clock POSIX seconds. The
85
+ * c2sp.org/tlog-witness `add-checkpoint` rule mandates a non-zero
86
+ * timestamp on production cosignatures; `0` is accepted by this
87
+ * function for test reproducibility but witness verifiers will
88
+ * reject envelopes that carry it. Tests and vector generators
89
+ * pass an explicit value to lock byte stability.
90
+ */
91
+ signCheckpoint(opts?: {
92
+ timestamp?: number;
93
+ }): Uint8Array;
94
+ /**
95
+ * Parse a signed-note envelope into the structured `SignedTreeHead`
96
+ * form per c2sp.org/signed-note §Format. Surfaces the body's
97
+ * decoded `Checkpoint`, the signature lines that survived the
98
+ * permissive signed-note parse, and the primary log cosignature's
99
+ * POSIX-seconds timestamp (extracted via
100
+ * `parseCosigSignaturePayload` on the line whose keyId matches
101
+ * this log's pubkey-derived keyId).
102
+ *
103
+ * If no signature line matches, `timestamp` is reported as 0. The
104
+ * field is informational at parse time; cryptographic verification
105
+ * lives in `verifyCheckpoint`. Throws `RangeError` on whole-envelope
106
+ * structural failure (the parseSignedNote / parseCheckpointBody
107
+ * contract); does not throw on signature line content issues.
108
+ */
109
+ parseCheckpoint(bytes: Uint8Array): SignedTreeHead;
110
+ /**
111
+ * Verify a signed-note envelope against this SignedLog's origin,
112
+ * pubkey, suite, and tree hasher. Returns `true` iff the envelope
113
+ * parses, carries a signature line whose keyId matches this log's
114
+ * pubkey-derived keyId, the `timestamped_signature` payload on
115
+ * that line decodes cleanly, and the signature verifies under
116
+ * `suite.verify` over the cosignature signed message reconstructed
117
+ * with the parsed timestamp.
118
+ *
119
+ * Returns `false` on every soft-fail mode: wrong origin, wrong
120
+ * root-hash length, no matching keyId line, malformed payload,
121
+ * signature failure. Throws only on this log's own disposed
122
+ * state; never on envelope content (envelope content is public,
123
+ * so timing distinctions on its content are not security-sensitive).
124
+ *
125
+ * The keyId comparison uses `constantTimeEqual` for hygiene around
126
+ * key-material-adjacent state; the origin and root-hash-length
127
+ * early returns are intentional non-constant-time exits since
128
+ * both fields are public per the spec.
129
+ */
130
+ verifyCheckpoint(bytes: Uint8Array): boolean;
131
+ /**
132
+ * Zero the stored signing-key copy. Idempotent. Subsequent calls
133
+ * to any public method throw.
134
+ */
135
+ dispose(): void;
136
+ private _assertNotDisposed;
137
+ /**
138
+ * Dispatch the cosignature signed-message construction on the
139
+ * algorithm-byte registry entry's `messageConstruction`. The
140
+ * `body` argument is the canonical checkpoint body from
141
+ * `serializeCheckpointBody`, ending in 0x0A.
142
+ *
143
+ * 'cosig' c2sp.org/tlog-cosignature §"Ed25519 signed
144
+ * message". The full envelope body is
145
+ * embedded verbatim after the
146
+ * cosignature/v1 + time prefix.
147
+ *
148
+ * 'cosigned-message' c2sp.org/tlog-cosignature §"ML-DSA-44
149
+ * signed message". The body is decomposed
150
+ * into origin, tree size, and root hash;
151
+ * cosigner_name == origin (Phase 7 logs sign
152
+ * their own checkpoints); start == 0; end ==
153
+ * tree size; hash == root hash.
154
+ */
155
+ private _buildSignedMessage;
156
+ }