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
package/dist/utils.js CHANGED
@@ -21,15 +21,18 @@
21
21
  //
22
22
  // src/ts/utils.ts
23
23
  //
24
- // Pure TypeScript utilities no init() dependency.
25
- // Ported from leviathan/src/base.ts (Convert namespace, Util namespace, constantTimeEqual).
26
- // ── Encoding ─────────────────────────────────────────────────────────────────
27
- /** Hex string to Uint8Array. Accepts lowercase/uppercase, optional 0x prefix. Throws RangeError on odd-length input. */
24
+ // Pure TypeScript utilities, no init() dependency.
25
+ // ── Encoding ────────────────────────────────────────────────────────────────
26
+ /** Hex string to Uint8Array. Accepts lowercase/uppercase, optional 0x prefix. Throws RangeError on odd-length or non-hex input. */
28
27
  export const hexToBytes = (hex) => {
29
28
  if (hex.startsWith('0x') || hex.startsWith('0X'))
30
29
  hex = hex.slice(2);
31
30
  if (hex.length % 2)
32
- throw new RangeError(`hexToBytes: odd-length string (${hex.length} chars) input must be an even-length hex string`);
31
+ throw new RangeError(`hexToBytes: odd-length string (${hex.length} chars), input must be an even-length hex string`);
32
+ // parseInt('0g', 16) returns 0 (not NaN) because it stops at the first
33
+ // invalid char, silent wrong-answer. Reject non-hex chars up front.
34
+ if (hex.length > 0 && !/^[0-9a-fA-F]*$/.test(hex))
35
+ throw new RangeError('hexToBytes: input contains non-hex characters');
33
36
  const bin = new Uint8Array(hex.length >>> 1);
34
37
  for (let i = 0, len = hex.length >>> 1; i < len; i++)
35
38
  bin[i] = parseInt(hex.slice(i << 1, (i << 1) + 2), 16);
@@ -51,20 +54,20 @@ export const utf8ToBytes = (str) => {
51
54
  export const bytesToUtf8 = (bytes) => {
52
55
  return new TextDecoder().decode(bytes);
53
56
  };
54
- /** Base64 or base64url string to Uint8Array. Handles padded, unpadded, and legacy %3d padding. Returns undefined on invalid input. */
57
+ /** Base64 or base64url string to Uint8Array. Handles padded, unpadded, and legacy %3d padding. Throws RangeError on invalid input. */
55
58
  export const base64ToBytes = (b64) => {
56
59
  // Normalise base64url → base64
57
60
  b64 = b64.replace(/-/g, '+').replace(/_/g, '/').replace(/%3d/gi, '=');
58
61
  // Re-pad if unpadded (RFC 4648 §5 base64url omits '=')
59
62
  const rem = b64.length % 4;
60
63
  if (rem === 1)
61
- return undefined; // always invalid no valid b64 produces this
64
+ throw new RangeError('base64ToBytes: invalid base64 input'); // no valid b64 produces this
62
65
  if (rem === 2)
63
66
  b64 += '==';
64
67
  if (rem === 3)
65
68
  b64 += '=';
66
69
  if (!/^[A-Za-z0-9+/]*={0,2}$/.test(b64))
67
- return undefined;
70
+ throw new RangeError('base64ToBytes: invalid base64 input');
68
71
  let strlen = b64.length / 4 * 3;
69
72
  if (b64.charAt(b64.length - 1) === '=')
70
73
  strlen--;
@@ -75,7 +78,7 @@ export const base64ToBytes = (b64) => {
75
78
  return new Uint8Array(atob(b64).split('').map(c => c.charCodeAt(0)));
76
79
  }
77
80
  catch {
78
- return undefined;
81
+ throw new RangeError('base64ToBytes: invalid base64 input');
79
82
  }
80
83
  }
81
84
  // Fallback: manual decode
@@ -110,7 +113,7 @@ export const base64ToBytes = (b64) => {
110
113
  }
111
114
  return bin;
112
115
  };
113
- /** Uint8Array to base64 string. Pass url=true for base64url (RFC 4648 §5 no padding characters). */
116
+ /** Uint8Array to base64 string. Pass url=true for base64url (RFC 4648 §5, no padding characters). */
114
117
  export const bytesToBase64 = (bytes, url = false) => {
115
118
  if (typeof btoa !== 'undefined') {
116
119
  const raw = btoa(String.fromCharCode.apply(null, Array.from(bytes)));
@@ -136,65 +139,83 @@ export const bytesToBase64 = (bytes, url = false) => {
136
139
  }
137
140
  return base64;
138
141
  };
139
- // ── Constant-time comparison ─────────────────────────────────────────────────
140
- import { CT_WASM } from './ct-wasm.js';
141
- let _ctCompare = null;
142
- let _ctMem = null;
143
- let _ctInit = false;
144
- // CT WASM module uses 1 page (64KB) of linear memory with both buffers
145
- // laid out side-by-side: a at offset 0, b at offset a.length.
146
- // Max per-side = _ctMem.buffer.byteLength >>> 1 = 32768 bytes.
147
- // In practice the largest comparison is a 32-byte HMAC-SHA-256 tag.
148
- export const CT_MAX_BYTES = 32768;
149
- /** Try to compile the SIMD WASM ct module. Returns false if unavailable. */
150
- function _initCt() {
151
- if (_ctInit)
152
- return _ctCompare !== null;
153
- _ctInit = true;
142
+ // ── Constant-time comparison ────────────────────────────────────────────────
143
+ import { CTE_WASM } from './cte-wasm.js';
144
+ let _cteCompare = null;
145
+ let _cteMem = null;
146
+ let _cteMemView = null;
147
+ let _cteInit = false;
148
+ let _cteInitError = null;
149
+ export const CTE_MAX_BYTES = 32768;
150
+ function _initCte() {
151
+ if (_cteInit) {
152
+ if (_cteInitError)
153
+ throw _cteInitError;
154
+ return;
155
+ }
156
+ _cteInit = true;
157
+ if (!hasSIMD()) {
158
+ _cteInitError = new Error('leviathan-crypto: constantTimeEqual requires WebAssembly SIMD, '
159
+ + 'this runtime does not support it');
160
+ throw _cteInitError;
161
+ }
154
162
  try {
155
- if (!hasSIMD())
156
- return false;
157
- _ctMem = new WebAssembly.Memory({ initial: 1, maximum: 1 });
158
- const buf = CT_WASM.buffer.slice(CT_WASM.byteOffset, CT_WASM.byteOffset + CT_WASM.byteLength);
163
+ const buf = CTE_WASM.buffer.slice(CTE_WASM.byteOffset, CTE_WASM.byteOffset + CTE_WASM.byteLength);
159
164
  const mod = new WebAssembly.Module(buf);
160
- const inst = new WebAssembly.Instance(mod, { env: { memory: _ctMem } });
161
- _ctCompare = inst.exports.compare;
162
- return true;
165
+ const inst = new WebAssembly.Instance(mod);
166
+ const exports = inst.exports;
167
+ _cteMem = exports.memory;
168
+ _cteMemView = new Uint8Array(_cteMem.buffer);
169
+ _cteCompare = exports.compare;
163
170
  }
164
- catch {
165
- return false;
171
+ catch (cause) {
172
+ _cteInitError = new Error(`leviathan-crypto: cte WASM module failed to instantiate: ${cause.message}`);
173
+ throw _cteInitError;
166
174
  }
167
175
  }
168
176
  /**
169
177
  * Constant-time byte-array equality.
170
- * Uses WASM SIMD when available (no JIT short-circuiting, no speculative
171
- * optimization). Falls back to a JS XOR-accumulate loop on runtimes
172
- * without SIMD support.
173
- * Length check is not constant-time (length is non-secret in all protocols).
174
- * Max input size: 32768 bytes per side (enforced regardless of code path).
178
+ * Runs entirely inside a WASM SIMD module (v128 XOR accumulate with
179
+ * branch-free reduction). Throws on runtimes without SIMD support,
180
+ * no JS fallback. Length check is not constant-time (length is
181
+ * non-secret in all protocols). Max input size: 32768 bytes per side.
175
182
  */
176
183
  export const constantTimeEqual = (a, b) => {
177
184
  if (a.length !== b.length)
178
185
  return false;
179
- if (a.length > CT_MAX_BYTES)
180
- throw new RangeError(`constantTimeEqual: max ${CT_MAX_BYTES} bytes (got ${a.length})`);
181
- if (_initCt() && _ctMem && _ctCompare) {
182
- const mem = new Uint8Array(_ctMem.buffer);
183
- mem.set(a, 0);
184
- mem.set(b, a.length);
185
- try {
186
- return _ctCompare(0, a.length, a.length) === 1;
187
- }
188
- finally {
189
- mem.fill(0, 0, a.length * 2);
190
- }
186
+ if (a.length > CTE_MAX_BYTES)
187
+ throw new RangeError(`constantTimeEqual: max ${CTE_MAX_BYTES} bytes (got ${a.length})`);
188
+ _initCte();
189
+ const mem = _cteMemView;
190
+ const compare = _cteCompare;
191
+ if (!mem || !compare)
192
+ throw new Error('leviathan-crypto: cte init invariant violated');
193
+ mem.set(a, 0);
194
+ mem.set(b, a.length);
195
+ try {
196
+ return compare(0, a.length, a.length) === 1;
197
+ }
198
+ finally {
199
+ // Wipe the full cte memory region, not just the bytes we wrote.
200
+ // Defense in depth against stale residue from longer prior calls.
201
+ // The module-private WASM memory is never read from outside this
202
+ // function, but we keep the surface clean regardless.
203
+ mem.fill(0, 0, CTE_MAX_BYTES * 2);
191
204
  }
192
- // JS fallback — best-effort constant-time via XOR accumulate
193
- let diff = 0;
194
- for (let i = 0; i < a.length; i++)
195
- diff |= a[i] ^ b[i];
196
- return diff === 0;
197
205
  };
206
+ /**
207
+ * Reset the internal CTE WASM cache, including any cached initialization
208
+ * error. Exists so the test suite can force re-instantiation across
209
+ * describe blocks.
210
+ * @internal
211
+ */
212
+ export function _cteResetForTesting() {
213
+ _cteInit = false;
214
+ _cteCompare = null;
215
+ _cteMem = null;
216
+ _cteMemView = null;
217
+ _cteInitError = null;
218
+ }
198
219
  /** Zero a typed array in place. */
199
220
  export const wipe = (data) => {
200
221
  data.fill(0);
@@ -218,11 +239,15 @@ export const concat = (...arrays) => {
218
239
  };
219
240
  /** Cryptographically secure random bytes via Web Crypto API. */
220
241
  export const randomBytes = (n) => {
242
+ if (typeof globalThis.crypto === 'undefined'
243
+ || typeof globalThis.crypto.getRandomValues !== 'function')
244
+ throw new Error('leviathan-crypto: crypto.getRandomValues is required, '
245
+ + 'this runtime does not expose the Web Crypto API');
221
246
  const buf = new Uint8Array(n);
222
- crypto.getRandomValues(buf);
247
+ globalThis.crypto.getRandomValues(buf);
223
248
  return buf;
224
249
  };
225
- // ── SIMD detection ───────────────────────────────────────────────────────────
250
+ // ── SIMD detection ──────────────────────────────────────────────────────────
226
251
  let _simd = null;
227
252
  /**
228
253
  * Detects WASM SIMD support once and caches the result.
@@ -236,7 +261,7 @@ export function hasSIMD() {
236
261
  _simd = false;
237
262
  return _simd;
238
263
  }
239
- // Minimal WASM module using v128 validates iff SIMD is supported
264
+ // Minimal WASM module using v128, validates iff SIMD is supported
240
265
  try {
241
266
  _simd = WebAssembly.validate(new Uint8Array([
242
267
  0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123,
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * All accepted forms of WASM input for init functions.
3
3
  *
4
- * - `string` gzip+base64 embedded blob (from `/embedded` subpath)
5
- * - `URL` fetched via `WebAssembly.instantiateStreaming`
6
- * - `ArrayBuffer` raw WASM bytes, compiled inline
7
- * - `Uint8Array` raw WASM bytes, compiled inline
8
- * - `WebAssembly.Module` pre-compiled module (Cloudflare Workers, edge runtimes)
9
- * - `Response` streaming instantiation from an in-flight fetch response
10
- * - `Promise<Response>` streaming instantiation from a deferred fetch
4
+ * - `string` , gzip+base64 embedded blob (from `/embedded` subpath)
5
+ * - `URL` , streaming-compiled from `fetch(url)`
6
+ * - `ArrayBuffer` , raw WASM bytes, compiled inline
7
+ * - `Uint8Array` , raw WASM bytes, compiled inline
8
+ * - `WebAssembly.Module` , pre-compiled module (Cloudflare Workers, edge runtimes)
9
+ * - `Response` , streaming-compiled from an in-flight fetch
10
+ * - `PromiseLike<WasmSource>` , any thenable resolving to another `WasmSource`; nesting
11
+ * is resolved recursively (max depth 3).
11
12
  */
12
- export type WasmSource = string | URL | ArrayBuffer | Uint8Array | WebAssembly.Module | Response | Promise<Response>;
13
+ export type WasmSource = string | URL | ArrayBuffer | Uint8Array | WebAssembly.Module | Response | PromiseLike<WasmSource>;
@@ -22,5 +22,5 @@
22
22
  // src/ts/wasm-source.ts
23
23
  //
24
24
  // Union type for all accepted WASM loading strategies.
25
- // The argument type determines the loading path no mode string required.
25
+ // The argument type determines the loading path, no mode string required.
26
26
  export {};
@@ -0,0 +1 @@
1
+ export { WASM_GZ_BASE64 as curve25519Wasm, WASM_GZ_BASE64 as x25519Wasm, } from '../embedded/curve25519.js';
@@ -0,0 +1,31 @@
1
+ // ▄▄▄▄▄▄▄▄▄▄
2
+ // ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
3
+ // ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
4
+ // ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
5
+ // ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
6
+ // ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
7
+ // ███████▌ ▀██▀ ███
8
+ // ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
9
+ // ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
10
+ // ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
11
+ // ▀████▄ ▄██▄
12
+ // ▐████ ▐███ Author: xero (https://x-e.ro)
13
+ // ▄▄██████████ ▐███ ▄▄ License: MIT
14
+ // ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
15
+ // ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
16
+ // ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
17
+ // ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
18
+ // █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
19
+ // ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
20
+ // ▀█████▀▀
21
+ //
22
+ // src/ts/x25519/embedded.ts
23
+ //
24
+ // Exports the gzip+base64 curve25519 WASM blob for use as a WasmSource.
25
+ // Ed25519 and X25519 share the same curve25519 WASM binary; both the
26
+ // `leviathan-crypto/ed25519/embedded` and `leviathan-crypto/x25519/embedded`
27
+ // subpaths re-export the same blob under three names: `curve25519Wasm`
28
+ // (canonical), `ed25519Wasm`, and `x25519Wasm` (aliases that read more
29
+ // naturally in the matching subpath context). All three resolve to the
30
+ // identical underlying string; tree-shaking is unaffected.
31
+ export { WASM_GZ_BASE64 as curve25519Wasm, WASM_GZ_BASE64 as x25519Wasm, } from '../embedded/curve25519.js';
@@ -0,0 +1,43 @@
1
+ import { isInitialized } from '../init.js';
2
+ import type { WasmSource } from '../wasm-source.js';
3
+ import type { X25519KeyPair } from './types.js';
4
+ /**
5
+ * Initialise the curve25519 WASM module under the `x25519` alias.
6
+ * Equivalent to `ed25519Init(source)`; both target the same WASM module
7
+ * and the init layer de-dupes when given identical sources.
8
+ */
9
+ export declare function x25519Init(source: WasmSource): Promise<void>;
10
+ export type { WasmSource };
11
+ export type { X25519KeyPair, X25519Exports } from './types.js';
12
+ export { isInitialized };
13
+ export declare class X25519 {
14
+ constructor();
15
+ private get mx();
16
+ /**
17
+ * Deterministic X25519 key generation from a 32-byte secret per
18
+ * RFC 7748 §6. Clamping is applied internally on every WASM call;
19
+ * the returned secretKey is a fresh copy of the supplied sk bytes
20
+ * (NOT pre-clamped).
21
+ */
22
+ keygenDerand(sk: Uint8Array): X25519KeyPair;
23
+ /** Random X25519 key generation, wraps `keygenDerand` with `randomBytes(32)`. */
24
+ keygen(): X25519KeyPair;
25
+ /**
26
+ * X25519 Diffie-Hellman, RFC 7748 §6.
27
+ *
28
+ * Computes the shared u-coordinate from the local secret and the
29
+ * peer's public key. If the resulting shared secret is all-zero
30
+ * (peer public key is a small-order point per RFC 7748 §7), throws
31
+ * `KeyAgreementError` rather than returning the degenerate value.
32
+ * The all-zero scan accumulates via OR across all 32 output bytes
33
+ * before comparing to zero, so the success path is constant-time;
34
+ * the throw branches off only after a known-bad outcome is observed.
35
+ *
36
+ * @param sk 32-byte local secret (NOT pre-clamped, clamped internally)
37
+ * @param peerPk 32-byte peer public key (u-coordinate)
38
+ * @returns 32-byte shared u-coordinate
39
+ * @throws KeyAgreementError on all-zero shared secret
40
+ */
41
+ dh(sk: Uint8Array, peerPk: Uint8Array): Uint8Array;
42
+ dispose(): void;
43
+ }
@@ -0,0 +1,159 @@
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/x25519/index.ts
23
+ //
24
+ // X25519 public API. RFC 7748 §5 (algorithm), §6 (keygen + DH), §7
25
+ // (security considerations and the small-order peer-pk rejection that
26
+ // motivates the TS-layer all-zero shared-secret check).
27
+ //
28
+ // Both Ed25519 and X25519 share the curve25519 WASM module;
29
+ // `ed25519Init(source)` and `x25519Init(source)` both target it and the
30
+ // init() layer de-dupes when given identical sources.
31
+ //
32
+ // Per-call lifecycle mirrors the Ed25519 wrapper: stage caller inputs
33
+ // at fixed offsets above the WASM mutable buffer region, call the
34
+ // underlying export, copy outputs to fresh `Uint8Array`s, wipe both
35
+ // the WASM-internal scratch and the TS-side I/O staging region.
36
+ import { getInstance, initModule, isInitialized, _assertNotOwned } from '../init.js';
37
+ import { randomBytes, wipe } from '../utils.js';
38
+ import { KeyAgreementError } from '../errors.js';
39
+ import { validateSecretKey, validatePublicKey } from './validate.js';
40
+ /**
41
+ * Initialise the curve25519 WASM module under the `x25519` alias.
42
+ * Equivalent to `ed25519Init(source)`; both target the same WASM module
43
+ * and the init layer de-dupes when given identical sources.
44
+ */
45
+ export async function x25519Init(source) {
46
+ return initModule('curve25519', source);
47
+ }
48
+ export { isInitialized };
49
+ // ── I/O staging layout ─────────────────────────────────────────────────────
50
+ //
51
+ // Same base as the ed25519 wrapper, fixed offsets above the WASM mutable
52
+ // buffer region (BUFFER_END = 7836). Two 32-byte input slots plus one
53
+ // 32-byte output slot, no large variable region.
54
+ const IO_BASE = 8192;
55
+ const SK_STAGE = IO_BASE; // 32 bytes
56
+ const PK_STAGE = IO_BASE + 32; // 32 bytes
57
+ const PEER_STAGE = IO_BASE + 64; // 32 bytes
58
+ const SHARED_STAGE = IO_BASE + 96; // 32 bytes
59
+ function ioWipe(mx) {
60
+ // Zero the entire TS-managed staging region. wipeBuffers covers
61
+ // MUTABLE_START..BUFFER_END only; the I/O slots above must be
62
+ // scrubbed at the wrapper layer.
63
+ new Uint8Array(mx.memory.buffer).fill(0, IO_BASE, mx.memory.buffer.byteLength);
64
+ }
65
+ export class X25519 {
66
+ constructor() {
67
+ if (!isInitialized('curve25519'))
68
+ throw new Error('leviathan-crypto: call init({ x25519: ... }) before using X25519');
69
+ }
70
+ get mx() {
71
+ return getInstance('curve25519').exports;
72
+ }
73
+ /**
74
+ * Deterministic X25519 key generation from a 32-byte secret per
75
+ * RFC 7748 §6. Clamping is applied internally on every WASM call;
76
+ * the returned secretKey is a fresh copy of the supplied sk bytes
77
+ * (NOT pre-clamped).
78
+ */
79
+ keygenDerand(sk) {
80
+ _assertNotOwned('curve25519');
81
+ validateSecretKey(sk);
82
+ const mx = this.mx;
83
+ const mem = new Uint8Array(mx.memory.buffer);
84
+ mem.set(sk, SK_STAGE);
85
+ try {
86
+ mx.x25519Keygen(SK_STAGE, PK_STAGE);
87
+ const publicKey = mem.slice(PK_STAGE, PK_STAGE + 32);
88
+ const secretKey = new Uint8Array(32);
89
+ secretKey.set(sk);
90
+ return { publicKey, secretKey };
91
+ }
92
+ finally {
93
+ ioWipe(mx);
94
+ mx.wipeBuffers();
95
+ }
96
+ }
97
+ /** Random X25519 key generation, wraps `keygenDerand` with `randomBytes(32)`. */
98
+ keygen() {
99
+ const sk = randomBytes(32);
100
+ try {
101
+ return this.keygenDerand(sk);
102
+ }
103
+ finally {
104
+ wipe(sk);
105
+ }
106
+ }
107
+ /**
108
+ * X25519 Diffie-Hellman, RFC 7748 §6.
109
+ *
110
+ * Computes the shared u-coordinate from the local secret and the
111
+ * peer's public key. If the resulting shared secret is all-zero
112
+ * (peer public key is a small-order point per RFC 7748 §7), throws
113
+ * `KeyAgreementError` rather than returning the degenerate value.
114
+ * The all-zero scan accumulates via OR across all 32 output bytes
115
+ * before comparing to zero, so the success path is constant-time;
116
+ * the throw branches off only after a known-bad outcome is observed.
117
+ *
118
+ * @param sk 32-byte local secret (NOT pre-clamped, clamped internally)
119
+ * @param peerPk 32-byte peer public key (u-coordinate)
120
+ * @returns 32-byte shared u-coordinate
121
+ * @throws KeyAgreementError on all-zero shared secret
122
+ */
123
+ dh(sk, peerPk) {
124
+ _assertNotOwned('curve25519');
125
+ validateSecretKey(sk);
126
+ validatePublicKey(peerPk);
127
+ const mx = this.mx;
128
+ const mem = new Uint8Array(mx.memory.buffer);
129
+ mem.set(sk, SK_STAGE);
130
+ mem.set(peerPk, PEER_STAGE);
131
+ try {
132
+ mx.x25519DH(SK_STAGE, PEER_STAGE, SHARED_STAGE);
133
+ const shared = mem.slice(SHARED_STAGE, SHARED_STAGE + 32);
134
+ // Constant-time OR-accumulate scan, RFC 7748 §6.1
135
+ // contributory-behaviour requirement. No early exit on the
136
+ // first non-zero byte.
137
+ let acc = 0;
138
+ for (let i = 0; i < 32; i++)
139
+ acc |= shared[i];
140
+ if (acc === 0) {
141
+ wipe(shared);
142
+ throw new KeyAgreementError('leviathan-crypto: X25519 shared secret is all-zero '
143
+ + '(peer public key is a small-order point)');
144
+ }
145
+ return shared;
146
+ }
147
+ finally {
148
+ ioWipe(mx);
149
+ mx.wipeBuffers();
150
+ }
151
+ }
152
+ dispose() {
153
+ try {
154
+ this.mx.wipeBuffers();
155
+ ioWipe(this.mx);
156
+ }
157
+ catch { /* idempotent */ }
158
+ }
159
+ }
@@ -0,0 +1,25 @@
1
+ export interface X25519KeyPair {
2
+ /** 32-byte public u-coordinate. */
3
+ publicKey: Uint8Array;
4
+ /**
5
+ * 32-byte secret. Per RFC 7748 §5 / §6 this is "any 32 random bytes";
6
+ * clamping is applied internally on every WASM call. The stored
7
+ * secretKey is the unclamped form so that round-tripping through
8
+ * keygen / external storage preserves byte-equality.
9
+ */
10
+ secretKey: Uint8Array;
11
+ }
12
+ /**
13
+ * The X25519-relevant subset of the curve25519 WASM exports. The
14
+ * curve25519 module is shared between Ed25519 and X25519; this interface
15
+ * deliberately surfaces only the X25519 high-level entry points plus the
16
+ * layout / wipe primitives.
17
+ */
18
+ export interface X25519Exports {
19
+ memory: WebAssembly.Memory;
20
+ getModuleId: () => number;
21
+ getMemoryPages: () => number;
22
+ x25519Keygen: (skOff: number, pkOff: number) => void;
23
+ x25519DH: (skOff: number, peerPkOff: number, sharedOff: number) => void;
24
+ wipeBuffers: () => void;
25
+ }
@@ -0,0 +1,27 @@
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/x25519/types.ts
23
+ //
24
+ // X25519 type surface: the WASM export interface for the curve25519
25
+ // module (X25519-relevant subset) and the public key-pair shape returned
26
+ // by keygen / keygenDerand. RFC 7748 §5 / §6.
27
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare function validateSecretKey(sk: Uint8Array): void;
2
+ export declare function validatePublicKey(pk: Uint8Array): void;
@@ -0,0 +1,39 @@
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/x25519/validate.ts
23
+ //
24
+ // X25519 caller-side input validation. Pure length / type checks. Peer
25
+ // public-key masking happens inside the WASM (feFromBytes masks bit 255
26
+ // per RFC 7748 §5); the all-zero shared-secret rejection happens in the
27
+ // TS X25519.dh method after the WASM returns.
28
+ export function validateSecretKey(sk) {
29
+ if (!(sk instanceof Uint8Array))
30
+ throw new TypeError('leviathan-crypto: x25519 secret key must be a Uint8Array');
31
+ if (sk.length !== 32)
32
+ throw new RangeError(`leviathan-crypto: x25519 secret key must be 32 bytes (got ${sk.length})`);
33
+ }
34
+ export function validatePublicKey(pk) {
35
+ if (!(pk instanceof Uint8Array))
36
+ throw new TypeError('leviathan-crypto: x25519 public key must be a Uint8Array');
37
+ if (pk.length !== 32)
38
+ throw new RangeError(`leviathan-crypto: x25519 public key must be 32 bytes (got ${pk.length})`);
39
+ }