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
@@ -19,9 +19,9 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/suite.ts
22
+ // src/ts/mlkem/suite.ts
23
23
  //
24
- // KyberSuite wraps a MlKemBase + inner CipherSuite into a unified CipherSuite.
24
+ // MlKemSuite, wraps a MlKemBase + inner CipherSuite into a unified CipherSuite.
25
25
  // Provides hybrid KEM + symmetric AEAD for use with SealStream / OpenStream / Seal.
26
26
  import { concat } from '../utils.js';
27
27
  import { HKDF_SHA256 } from '../sha2/index.js';
@@ -36,7 +36,7 @@ const KEM_LABEL = {
36
36
  3: 'mlkem768',
37
37
  4: 'mlkem1024',
38
38
  };
39
- export function KyberSuite(kem, inner) {
39
+ export function MlKemSuite(kem, inner) {
40
40
  const p = kem.params;
41
41
  const kemNibble = KEM_NIBBLE[p.k];
42
42
  if (!kemNibble)
@@ -50,23 +50,24 @@ export function KyberSuite(kem, inner) {
50
50
  keySize: p.ekBytes,
51
51
  decKeySize: p.dkBytes,
52
52
  kemCtSize: p.ctBytes,
53
+ commitmentSize: inner.commitmentSize,
53
54
  tagSize: inner.tagSize,
54
55
  padded: inner.padded,
55
56
  wasmChunkSize: inner.wasmChunkSize,
56
- wasmModules: [...inner.wasmModules, 'kyber', 'sha3'],
57
- deriveKeys(key, nonce, kemCt) {
57
+ wasmModules: [...inner.wasmModules, 'mlkem', 'sha3'],
58
+ deriveKeys(key, nonce, kemCt, header) {
58
59
  let sharedSecret;
59
60
  let outKemCt;
60
61
  let ctForInfo;
61
62
  if (kemCt === undefined) {
62
- // encrypt path: key = ek encapsulate to produce shared secret + ct
63
+ // encrypt path: key = ek, encapsulate to produce shared secret + ct
63
64
  const result = kem.encapsulate(key);
64
65
  sharedSecret = result.sharedSecret;
65
66
  outKemCt = result.ciphertext;
66
67
  ctForInfo = outKemCt;
67
68
  }
68
69
  else {
69
- // decrypt path: key = dk decapsulate to recover shared secret
70
+ // decrypt path: key = dk, decapsulate to recover shared secret
70
71
  sharedSecret = kem.decapsulate(key, kemCt);
71
72
  ctForInfo = kemCt;
72
73
  }
@@ -76,12 +77,16 @@ export function KyberSuite(kem, inner) {
76
77
  const derived = hkdf.derive(sharedSecret, nonce, info, inner.keySize);
77
78
  hkdf.dispose();
78
79
  sharedSecret.fill(0);
79
- // Delegate actual per-cipher key derivation to the inner suite
80
- const innerKeys = inner.deriveKeys(derived, nonce);
80
+ // Delegate actual per-cipher key derivation to the inner suite.
81
+ // Header is forwarded so XChaCha20's deriveKeys can bind it into
82
+ // HKDF info; SerpentCipher accepts and ignores it.
83
+ const innerKeys = inner.deriveKeys(derived, nonce, undefined, header);
81
84
  derived.fill(0);
82
- return outKemCt
83
- ? { bytes: innerKeys.bytes, kemCiphertext: outKemCt }
84
- : innerKeys;
85
+ if (!outKemCt)
86
+ return innerKeys;
87
+ return innerKeys.commitment
88
+ ? { bytes: innerKeys.bytes, kemCiphertext: outKemCt, commitment: innerKeys.commitment }
89
+ : { bytes: innerKeys.bytes, kemCiphertext: outKemCt };
85
90
  },
86
91
  sealChunk: inner.sealChunk.bind(inner),
87
92
  openChunk: inner.openChunk.bind(inner),
@@ -1,4 +1,4 @@
1
- export interface KyberExports {
1
+ export interface MlKemExports {
2
2
  memory: WebAssembly.Memory;
3
3
  getModuleId: () => number;
4
4
  getMemoryPages: () => number;
@@ -63,6 +63,7 @@ export interface KyberExports {
63
63
  polyvec_reduce: (pvOffset: number, k: number) => void;
64
64
  polyvec_add: (rOffset: number, aOffset: number, bOffset: number, k: number) => void;
65
65
  polyvec_basemul_acc_montgomery: (rOffset: number, aOffset: number, bOffset: number, k: number) => void;
66
+ polyvec_modulus_check: (pvOffset: number, k: number) => number;
66
67
  rej_uniform: (polyOffset: number, ctrStart: number, bufOffset: number, buflen: number) => number;
67
68
  ct_verify: (aOffset: number, bOffset: number, len: number) => number;
68
69
  ct_cmov: (rOffset: number, xOffset: number, len: number, b: number) => void;
@@ -88,11 +89,11 @@ export interface Sha3Exports {
88
89
  shakeSqueezeBlock: () => void;
89
90
  wipeBuffers: () => void;
90
91
  }
91
- export interface KyberKeyPair {
92
+ export interface MlKemKeyPair {
92
93
  encapsulationKey: Uint8Array;
93
94
  decapsulationKey: Uint8Array;
94
95
  }
95
- export interface KyberEncapsulation {
96
+ export interface MlKemEncapsulation {
96
97
  ciphertext: Uint8Array;
97
98
  sharedSecret: Uint8Array;
98
99
  }
@@ -19,7 +19,7 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/types.ts
22
+ // src/ts/mlkem/types.ts
23
23
  //
24
24
  // ML-KEM type definitions: WASM export interfaces and KEM API types.
25
25
  export {};
@@ -0,0 +1,23 @@
1
+ import type { MlKemExports, Sha3Exports } from './types.js';
2
+ import type { MlKemParams } from './params.js';
3
+ /**
4
+ * Encapsulation key check, FIPS 203 §7.2 (EncapsulationKeyCheck).
5
+ *
6
+ * 1. Length gate: ek.length must equal params.ekBytes.
7
+ * 2. Decode the polyvec portion via ByteDecode₁₂ (polyvec_frombytes). The
8
+ * decoded coefficients are raw 12-bit values in [0, 4095], frombytes
9
+ * does not reduce mod q.
10
+ * 3. Modulus scan: every coefficient must satisfy c < Q = 3329.
11
+ *
12
+ * Returns true iff both gates pass. The seed ρ (final 32 bytes of ek) is
13
+ * not checked; any 32-byte value is a valid ρ per FIPS 203.
14
+ */
15
+ export declare function checkEncapsulationKey(kx: MlKemExports, params: MlKemParams, ek: Uint8Array): boolean;
16
+ /**
17
+ * Decapsulation key check, FIPS 203 §7.3 (DecapsulationKeyCheck).
18
+ *
19
+ * 1. Length check: dk.length == params.dkBytes
20
+ * 2. Extract embedded ek and H(ek), verify SHA3-256(ek) matches stored H
21
+ * 3. Also run checkEncapsulationKey on the embedded ek
22
+ */
23
+ export declare function checkDecapsulationKey(kx: MlKemExports, sx: Sha3Exports, params: MlKemParams, dk: Uint8Array): boolean;
@@ -19,37 +19,36 @@
19
19
  // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
20
  // ▀█████▀▀
21
21
  //
22
- // src/ts/kyber/validate.ts
22
+ // src/ts/mlkem/validate.ts
23
23
  //
24
- // ML-KEM key validation FIPS 203 §7.2 and §7.3.
24
+ // ML-KEM key validation, FIPS 203 §7.2 and §7.3.
25
25
  import { sha3_256Hash } from './indcpa.js';
26
26
  import { constantTimeEqual } from '../utils.js';
27
27
  /**
28
- * Encapsulation key check FIPS 203 §7.2 (EncapsulationKeyCheck).
28
+ * Encapsulation key check, FIPS 203 §7.2 (EncapsulationKeyCheck).
29
29
  *
30
- * 1. Length check: ek.length == params.ekBytes
31
- * 2. ByteDecode₁₂ ByteEncode₁₂ round-trip check on the polyvec portion.
32
- * Any coefficient q stored modulo 2^12 survives frombytes, but tobytes
33
- * re-encodes it differently so the round-trip fails iff any coeff was ≥ q.
30
+ * 1. Length gate: ek.length must equal params.ekBytes.
31
+ * 2. Decode the polyvec portion via ByteDecode₁₂ (polyvec_frombytes). The
32
+ * decoded coefficients are raw 12-bit values in [0, 4095], frombytes
33
+ * does not reduce mod q.
34
+ * 3. Modulus scan: every coefficient must satisfy c < Q = 3329.
35
+ *
36
+ * Returns true iff both gates pass. The seed ρ (final 32 bytes of ek) is
37
+ * not checked; any 32-byte value is a valid ρ per FIPS 203.
34
38
  */
35
39
  export function checkEncapsulationKey(kx, params, ek) {
36
40
  if (ek.length !== params.ekBytes)
37
41
  return false;
38
42
  const { k } = params;
39
- const kyberMem = new Uint8Array(kx.memory.buffer);
43
+ const mlkemMem = new Uint8Array(kx.memory.buffer);
40
44
  const pkOff = kx.getPkOffset();
41
- const skOff = kx.getSkOffset();
42
45
  const pvecOff = kx.getPolyvecSlot0();
43
- // Write the polyvec portion of ek into PK buffer, decode, re-encode
44
- kyberMem.set(ek.subarray(0, k * 384), pkOff);
46
+ mlkemMem.set(ek.subarray(0, k * 384), pkOff);
45
47
  kx.polyvec_frombytes(pvecOff, pkOff, k);
46
- kx.polyvec_tobytes(skOff, pvecOff, k);
47
- // orig is at pkOff (written above); reEnc is at skOff (polyvec_tobytes output)
48
- const mismatch = kx.ct_verify(pkOff, skOff, k * 384);
49
- return mismatch === 0;
48
+ return kx.polyvec_modulus_check(pvecOff, k) === 0;
50
49
  }
51
50
  /**
52
- * Decapsulation key check FIPS 203 §7.3 (DecapsulationKeyCheck).
51
+ * Decapsulation key check, FIPS 203 §7.3 (DecapsulationKeyCheck).
53
52
  *
54
53
  * 1. Length check: dk.length == params.dkBytes
55
54
  * 2. Extract embedded ek and H(ek), verify SHA3-256(ek) matches stored H
@@ -61,8 +60,13 @@ export function checkDecapsulationKey(kx, sx, params, dk) {
61
60
  const { skCpaBytes, ekBytes } = params;
62
61
  const ek = dk.slice(skCpaBytes, skCpaBytes + ekBytes);
63
62
  const h = dk.slice(skCpaBytes + ekBytes, skCpaBytes + ekBytes + 32);
64
- const hComputed = sha3_256Hash(sx, ek);
65
- if (!constantTimeEqual(hComputed, h))
66
- return false;
67
- return checkEncapsulationKey(kx, params, ek);
63
+ try {
64
+ const hComputed = sha3_256Hash(sx, ek);
65
+ if (!constantTimeEqual(hComputed, h))
66
+ return false;
67
+ return checkEncapsulationKey(kx, params, ek);
68
+ }
69
+ finally {
70
+ sx.wipeBuffers();
71
+ }
68
72
  }
Binary file
package/dist/p256.wasm ADDED
Binary file
@@ -0,0 +1,8 @@
1
+ export { KDFChain } from './kdf-chain.js';
2
+ export { ratchetInit, kemRatchetEncap, kemRatchetDecap } from './root-kdf.js';
3
+ export { SkippedKeyStore } from './skipped-key-store.js';
4
+ export { RatchetKeypair } from './ratchet-keypair.js';
5
+ export type { RatchetInitResult, KemEncapResult, KemDecapResult, MlKemLike, RatchetMessageHeader, ResolveHandle, SkippedKeyStoreOpts, } from './types.js';
6
+ import { isInitialized } from '../init.js';
7
+ export { isInitialized };
8
+ export declare function ratchetReady(): boolean;
@@ -0,0 +1,38 @@
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/ratchet/index.ts
23
+ //
24
+ // Public barrel for the ratchet module.
25
+ export { KDFChain } from './kdf-chain.js';
26
+ export { ratchetInit, kemRatchetEncap, kemRatchetDecap } from './root-kdf.js';
27
+ export { SkippedKeyStore } from './skipped-key-store.js';
28
+ export { RatchetKeypair } from './ratchet-keypair.js';
29
+ import { isInitialized } from '../init.js';
30
+ export { isInitialized };
31
+ export function ratchetReady() {
32
+ try {
33
+ return isInitialized('sha2');
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
@@ -0,0 +1,13 @@
1
+ export declare class KDFChain {
2
+ private _ck;
3
+ private _n;
4
+ private _disposed;
5
+ constructor(ck: Uint8Array);
6
+ step(): Uint8Array;
7
+ stepWithCounter(): {
8
+ key: Uint8Array;
9
+ counter: number;
10
+ };
11
+ get n(): number;
12
+ dispose(): void;
13
+ }
@@ -0,0 +1,85 @@
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/ratchet/kdf-chain.ts
23
+ //
24
+ // KDFChain, stateful symmetric ratchet chain step (spec §5.2, KDF_SCKA_CK).
25
+ // Each step derives a message key and advances the chain key via HKDF-SHA-256.
26
+ import { HKDF_SHA256 } from '../sha2/index.js';
27
+ import { isInitialized } from '../init.js';
28
+ import { wipe, concat, utf8ToBytes } from '../utils.js';
29
+ // Signal Double Ratchet §7.2, chain step info string
30
+ const INFO_CHAIN_BYTES = utf8ToBytes('leviathan-ratchet-v1 Chain Step');
31
+ const ZERO_SALT = new Uint8Array(32);
32
+ export class KDFChain {
33
+ _ck;
34
+ _n;
35
+ _disposed;
36
+ constructor(ck) {
37
+ if (!isInitialized('sha2'))
38
+ throw new Error('leviathan-crypto: call init({ sha2: ... }) before using KDFChain');
39
+ if (ck.length !== 32)
40
+ throw new RangeError('KDFChain: ck must be 32 bytes');
41
+ this._ck = ck.slice();
42
+ this._n = 0;
43
+ this._disposed = false;
44
+ }
45
+ step() {
46
+ if (this._disposed)
47
+ throw new Error('KDFChain: instance has been disposed');
48
+ if (this._n >= Number.MAX_SAFE_INTEGER)
49
+ throw new RangeError('KDFChain: counter exceeds maximum safe integer');
50
+ const nextN = this._n + 1;
51
+ // Encode counter as big-endian uint64, two u32 calls, no BigInt
52
+ const ctrBuf = new Uint8Array(8);
53
+ const dv = new DataView(ctrBuf.buffer);
54
+ dv.setUint32(0, Math.floor(nextN / 0x100000000), false);
55
+ dv.setUint32(4, nextN >>> 0, false);
56
+ const info = concat(INFO_CHAIN_BYTES, ctrBuf);
57
+ const h = new HKDF_SHA256();
58
+ try {
59
+ const okm = h.derive(this._ck, ZERO_SALT, info, 64);
60
+ const nextCk = okm.slice(0, 32);
61
+ const msgKey = okm.slice(32, 64);
62
+ wipe(this._ck);
63
+ this._ck = nextCk;
64
+ this._n = nextN;
65
+ wipe(okm);
66
+ return msgKey;
67
+ }
68
+ finally {
69
+ h.dispose();
70
+ }
71
+ }
72
+ // Returns both the message key and the post-step counter atomically.
73
+ // Eliminates the two-step step() + .n read pattern and the off-by-one risk.
74
+ stepWithCounter() {
75
+ const key = this.step();
76
+ return { key, counter: this._n };
77
+ }
78
+ get n() {
79
+ return this._n;
80
+ }
81
+ dispose() {
82
+ wipe(this._ck);
83
+ this._disposed = true;
84
+ }
85
+ }
@@ -0,0 +1,9 @@
1
+ import type { MlKemLike, KemDecapResult } from './types.js';
2
+ export declare class RatchetKeypair {
3
+ readonly ek: Uint8Array;
4
+ private _dk;
5
+ private _used;
6
+ constructor(kem: MlKemLike);
7
+ decap(kem: MlKemLike, rk: Uint8Array, kemCt: Uint8Array, context?: Uint8Array): KemDecapResult;
8
+ dispose(): void;
9
+ }
@@ -0,0 +1,61 @@
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/ratchet/ratchet-keypair.ts
23
+ //
24
+ // RatchetKeypair, single-use ek/dk lifecycle for one KEM ratchet step.
25
+ // Enforces the DR spec requirement that both parties rotate encapsulation
26
+ // keys after each KEM ratchet step.
27
+ import { wipe } from '../utils.js';
28
+ import { kemRatchetDecap } from './root-kdf.js';
29
+ export class RatchetKeypair {
30
+ ek;
31
+ _dk;
32
+ _used;
33
+ constructor(kem) {
34
+ const { encapsulationKey, decapsulationKey } = kem.keygen();
35
+ this.ek = encapsulationKey;
36
+ this._dk = decapsulationKey;
37
+ this._used = false;
38
+ }
39
+ // Decapsulate using the stored dk. May only be called once per instance.
40
+ // Wipes the dk immediately after decap, the dk never leaves this class.
41
+ // The stored ek is passed as `ownEk` so both sides bind the identical
42
+ // (peerEk, kemCt) pair into the HKDF info string.
43
+ decap(kem, rk, kemCt, context) {
44
+ if (this._used)
45
+ throw new Error('RatchetKeypair: already consumed or disposed. generate a new keypair for the next ratchet step');
46
+ this._used = true;
47
+ try {
48
+ return kemRatchetDecap(kem, rk, this._dk, kemCt, this.ek, context);
49
+ }
50
+ finally {
51
+ wipe(this._dk);
52
+ }
53
+ }
54
+ // Wipe the dk if not already wiped by decap. Idempotent.
55
+ dispose() {
56
+ if (!this._used) {
57
+ wipe(this._dk);
58
+ this._used = true;
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,4 @@
1
+ import type { MlKemLike, RatchetInitResult, KemEncapResult, KemDecapResult } from './types.js';
2
+ export declare function ratchetInit(sk: Uint8Array, context?: Uint8Array): RatchetInitResult;
3
+ export declare function kemRatchetEncap(kem: MlKemLike, rk: Uint8Array, peerEk: Uint8Array, context?: Uint8Array): KemEncapResult;
4
+ export declare function kemRatchetDecap(kem: MlKemLike, rk: Uint8Array, dk: Uint8Array, kemCt: Uint8Array, ownEk: Uint8Array, context?: Uint8Array): KemDecapResult;
@@ -0,0 +1,124 @@
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/ratchet/root-kdf.ts
23
+ //
24
+ // Root KDF functions for the Sparse Post-Quantum Ratchet (spec §7.2).
25
+ // Implements KDF_SCKA_INIT (ratchetInit) and KDF_SCKA_RK (kemRatchetEncap/Decap).
26
+ import { HKDF_SHA256 } from '../sha2/index.js';
27
+ import { isInitialized } from '../init.js';
28
+ import { wipe, concat, utf8ToBytes } from '../utils.js';
29
+ // Signal Double Ratchet §7.2, info strings
30
+ const INFO_INIT = utf8ToBytes('leviathan-ratchet-v1 Chain Start');
31
+ const INFO_ROOT = utf8ToBytes('leviathan-ratchet-v1 Chain Add Epoch');
32
+ // INFO_CHAIN ('leviathan-ratchet-v1 Chain Step') is used in kdf-chain.ts
33
+ const ZERO_SALT = new Uint8Array(32);
34
+ // HKDF-SHA-256 root step (KDF_SCKA_INIT and KDF_SCKA_RK share this shape).
35
+ // ikm = 32-byte secret (shared secret or sk)
36
+ // salt = 32-byte salt (zero bytes for init, rk for KEM ratchet)
37
+ // info = protocol info bytes
38
+ // length = 96
39
+ // → { nextRootKey: [0:32], sendChainKey: [32:64], recvChainKey: [64:96] }
40
+ // Wipes okm after slicing.
41
+ function kdfRoot(secret, salt, info) {
42
+ const h = new HKDF_SHA256();
43
+ try {
44
+ const okm = h.derive(secret, salt, info, 96);
45
+ const nextRootKey = okm.slice(0, 32);
46
+ const sendChainKey = okm.slice(32, 64);
47
+ const recvChainKey = okm.slice(64, 96);
48
+ wipe(okm);
49
+ return { nextRootKey, sendChainKey, recvChainKey };
50
+ }
51
+ finally {
52
+ h.dispose();
53
+ }
54
+ }
55
+ // u32 big-endian length prefix, same convention as serpent/cipher-suite.ts AAD encoding.
56
+ function u32be(n) {
57
+ if (!Number.isInteger(n) || n < 0 || n > 0xFFFFFFFF)
58
+ throw new RangeError(`u32be: n must be an integer in [0, 0xFFFFFFFF] (got ${n})`);
59
+ const b = new Uint8Array(4);
60
+ new DataView(b.buffer).setUint32(0, n, false);
61
+ return b;
62
+ }
63
+ // KEM ratchet info, binds peerEk, kemCt, and context with u32be length prefixes.
64
+ // Defense-in-depth: the HKDF output is tied to the exact (peerEk, kemCt, context)
65
+ // tuple used, not just the KEM shared secret. An attacker who substitutes any of
66
+ // these inputs derives a different chain key trio, regardless of the KEM transcript.
67
+ function buildRootInfo(peerEk, kemCt, context) {
68
+ const ctxBytes = context ?? new Uint8Array(0);
69
+ return concat(INFO_ROOT, u32be(peerEk.length), peerEk, u32be(kemCt.length), kemCt, u32be(ctxBytes.length), ctxBytes);
70
+ }
71
+ // KDF_SCKA_INIT, spec §7.2
72
+ // Derives initial root key, send chain key, and receive chain key from a
73
+ // shared secret sk. Optional context bytes are appended to the info string.
74
+ export function ratchetInit(sk, context) {
75
+ if (!isInitialized('sha2'))
76
+ throw new Error('leviathan-crypto: call init({ sha2: ... }) before using ratchetInit');
77
+ if (sk.length !== 32)
78
+ throw new RangeError('ratchetInit: sk must be 32 bytes');
79
+ const info = context != null && context.length > 0 ? concat(INFO_INIT, context) : INFO_INIT;
80
+ const { nextRootKey, sendChainKey, recvChainKey } = kdfRoot(sk, ZERO_SALT, info);
81
+ return { nextRootKey, sendChainKey, recvChainKey };
82
+ }
83
+ // KDF_SCKA_RK, encapsulation side (spec §7.2)
84
+ // Generates a fresh KEM ciphertext, derives next epoch keys from the shared secret.
85
+ // `peerEk` and `kemCt` are bound into the HKDF info string (defense-in-depth).
86
+ export function kemRatchetEncap(kem, rk, peerEk, context) {
87
+ if (!isInitialized('sha2'))
88
+ throw new Error('leviathan-crypto: call init({ sha2: ... }) before using kemRatchetEncap');
89
+ if (rk.length !== 32)
90
+ throw new RangeError('kemRatchetEncap: rk must be 32 bytes');
91
+ const { ciphertext: kemCt, sharedSecret } = kem.encapsulate(peerEk);
92
+ const info = buildRootInfo(peerEk, kemCt, context);
93
+ try {
94
+ const { nextRootKey, sendChainKey, recvChainKey } = kdfRoot(sharedSecret, rk, info);
95
+ return { nextRootKey, sendChainKey, recvChainKey, kemCt };
96
+ }
97
+ finally {
98
+ wipe(sharedSecret);
99
+ }
100
+ }
101
+ // KDF_SCKA_RK, decapsulation side (spec §7.2)
102
+ // Recovers the shared secret from the KEM ciphertext, derives next epoch keys.
103
+ // The chain key slots are swapped relative to the encap side: what the KDF
104
+ // labels 'sendChainKey' (A→B direction) becomes the decap side's recvChainKey,
105
+ // and vice versa, Alice's send IS Bob's receive.
106
+ //
107
+ // `ownEk` is the local party's encapsulation key (the same public key the
108
+ // encap side targeted as `peerEk`). It must be passed explicitly so both
109
+ // sides bind the identical (peerEk, kemCt) pair into the HKDF info string.
110
+ export function kemRatchetDecap(kem, rk, dk, kemCt, ownEk, context) {
111
+ if (!isInitialized('sha2'))
112
+ throw new Error('leviathan-crypto: call init({ sha2: ... }) before using kemRatchetDecap');
113
+ if (rk.length !== 32)
114
+ throw new RangeError('kemRatchetDecap: rk must be 32 bytes');
115
+ const sharedSecret = kem.decapsulate(dk, kemCt);
116
+ const info = buildRootInfo(ownEk, kemCt, context);
117
+ try {
118
+ const { nextRootKey, sendChainKey: recvChainKey, recvChainKey: sendChainKey } = kdfRoot(sharedSecret, rk, info);
119
+ return { nextRootKey, sendChainKey, recvChainKey };
120
+ }
121
+ finally {
122
+ wipe(sharedSecret);
123
+ }
124
+ }
@@ -0,0 +1,14 @@
1
+ import { KDFChain } from './kdf-chain.js';
2
+ import type { ResolveHandle, SkippedKeyStoreOpts } from './types.js';
3
+ export declare class SkippedKeyStore {
4
+ private _store;
5
+ private _maxCacheSize;
6
+ private _maxSkipPerResolve;
7
+ constructor(opts?: SkippedKeyStoreOpts);
8
+ private _evictOldest;
9
+ resolve(chain: KDFChain, counter: number): ResolveHandle;
10
+ private _makeHandle;
11
+ advanceToBoundary(chain: KDFChain, pn: number): void;
12
+ get size(): number;
13
+ wipeAll(): void;
14
+ }