leviathan-crypto 2.1.0 → 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 (296) hide show
  1. package/CLAUDE.md +86 -443
  2. package/README.md +198 -65
  3. package/dist/aes/aes-cbc.d.ts +40 -0
  4. package/dist/aes/aes-cbc.js +158 -0
  5. package/dist/aes/aes-ctr.d.ts +50 -0
  6. package/dist/aes/aes-ctr.js +141 -0
  7. package/dist/aes/aes-gcm-siv.d.ts +67 -0
  8. package/dist/aes/aes-gcm-siv.js +217 -0
  9. package/dist/aes/aes-gcm.d.ts +61 -0
  10. package/dist/aes/aes-gcm.js +226 -0
  11. package/dist/aes/cipher-suite.d.ts +21 -0
  12. package/dist/aes/cipher-suite.js +179 -0
  13. package/dist/aes/embedded.d.ts +1 -0
  14. package/dist/aes/embedded.js +26 -0
  15. package/dist/aes/generator.d.ts +14 -0
  16. package/dist/aes/generator.js +103 -0
  17. package/dist/aes/index.d.ts +58 -0
  18. package/dist/aes/index.js +125 -0
  19. package/dist/aes/ops.d.ts +60 -0
  20. package/dist/aes/ops.js +164 -0
  21. package/dist/aes/pool-worker.d.ts +1 -0
  22. package/dist/aes/pool-worker.js +92 -0
  23. package/dist/aes/types.d.ts +1 -0
  24. package/dist/aes/types.js +23 -0
  25. package/dist/aes.wasm +0 -0
  26. package/dist/blake3/embedded.d.ts +1 -0
  27. package/dist/blake3/embedded.js +26 -0
  28. package/dist/blake3/index.d.ts +143 -0
  29. package/dist/blake3/index.js +620 -0
  30. package/dist/blake3/types.d.ts +102 -0
  31. package/dist/blake3/types.js +31 -0
  32. package/dist/blake3/validate.d.ts +29 -0
  33. package/dist/blake3/validate.js +80 -0
  34. package/dist/blake3.wasm +0 -0
  35. package/dist/chacha20/cipher-suite.js +47 -25
  36. package/dist/chacha20/generator.d.ts +2 -2
  37. package/dist/chacha20/generator.js +4 -4
  38. package/dist/chacha20/index.d.ts +16 -15
  39. package/dist/chacha20/index.js +52 -46
  40. package/dist/chacha20/ops.d.ts +7 -7
  41. package/dist/chacha20/ops.js +34 -34
  42. package/dist/chacha20/pool-worker.js +5 -3
  43. package/dist/cte-wasm.d.ts +1 -0
  44. package/dist/cte-wasm.js +3 -0
  45. package/dist/curve25519.wasm +0 -0
  46. package/dist/ecdsa/der.d.ts +23 -0
  47. package/dist/ecdsa/der.js +192 -0
  48. package/dist/ecdsa/ecprivatekey-der.d.ts +32 -0
  49. package/dist/ecdsa/ecprivatekey-der.js +230 -0
  50. package/dist/ecdsa/embedded.d.ts +1 -0
  51. package/dist/ecdsa/embedded.js +25 -0
  52. package/dist/ecdsa/index.d.ts +124 -0
  53. package/dist/ecdsa/index.js +366 -0
  54. package/dist/ecdsa/types.d.ts +31 -0
  55. package/dist/ecdsa/types.js +28 -0
  56. package/dist/ecdsa/validate.d.ts +18 -0
  57. package/dist/ecdsa/validate.js +92 -0
  58. package/dist/ed25519/embedded.d.ts +1 -0
  59. package/dist/ed25519/embedded.js +31 -0
  60. package/dist/ed25519/index.d.ts +70 -0
  61. package/dist/ed25519/index.js +308 -0
  62. package/dist/ed25519/types.d.ts +27 -0
  63. package/dist/ed25519/types.js +27 -0
  64. package/dist/ed25519/validate.d.ts +7 -0
  65. package/dist/ed25519/validate.js +77 -0
  66. package/dist/embedded/aes-pool-worker.d.ts +1 -0
  67. package/dist/embedded/aes-pool-worker.js +5 -0
  68. package/dist/embedded/aes.d.ts +1 -0
  69. package/dist/embedded/aes.js +3 -0
  70. package/dist/embedded/blake3.d.ts +1 -0
  71. package/dist/embedded/blake3.js +3 -0
  72. package/dist/embedded/chacha20-pool-worker.d.ts +1 -1
  73. package/dist/embedded/chacha20-pool-worker.js +2 -2
  74. package/dist/embedded/chacha20.d.ts +1 -1
  75. package/dist/embedded/chacha20.js +2 -2
  76. package/dist/embedded/curve25519.d.ts +1 -0
  77. package/dist/embedded/curve25519.js +3 -0
  78. package/dist/embedded/mldsa.d.ts +1 -0
  79. package/dist/embedded/mldsa.js +3 -0
  80. package/dist/embedded/mlkem.d.ts +1 -0
  81. package/dist/embedded/mlkem.js +3 -0
  82. package/dist/embedded/p256.d.ts +1 -0
  83. package/dist/embedded/p256.js +3 -0
  84. package/dist/embedded/serpent-pool-worker.d.ts +1 -1
  85. package/dist/embedded/serpent-pool-worker.js +2 -2
  86. package/dist/embedded/serpent.d.ts +1 -1
  87. package/dist/embedded/serpent.js +2 -2
  88. package/dist/embedded/sha2.d.ts +1 -1
  89. package/dist/embedded/sha2.js +2 -2
  90. package/dist/embedded/sha3.d.ts +1 -1
  91. package/dist/embedded/sha3.js +2 -2
  92. package/dist/embedded/slhdsa.d.ts +1 -0
  93. package/dist/embedded/slhdsa.js +3 -0
  94. package/dist/errors.d.ts +92 -1
  95. package/dist/errors.js +111 -1
  96. package/dist/fortuna.d.ts +5 -5
  97. package/dist/fortuna.js +37 -64
  98. package/dist/index.d.ts +38 -9
  99. package/dist/index.js +63 -19
  100. package/dist/init.d.ts +1 -1
  101. package/dist/init.js +11 -25
  102. package/dist/keccak/embedded.js +1 -1
  103. package/dist/keccak/index.d.ts +2 -0
  104. package/dist/keccak/index.js +4 -2
  105. package/dist/loader.d.ts +1 -24
  106. package/dist/loader.js +13 -16
  107. package/dist/merkle/blake3-tree.d.ts +35 -0
  108. package/dist/merkle/blake3-tree.js +187 -0
  109. package/dist/merkle/checkpoint.d.ts +58 -0
  110. package/dist/merkle/checkpoint.js +217 -0
  111. package/dist/merkle/index.d.ts +19 -0
  112. package/dist/merkle/index.js +37 -0
  113. package/dist/merkle/merkle-log.d.ts +130 -0
  114. package/dist/merkle/merkle-log.js +207 -0
  115. package/dist/merkle/merkle-verifier.d.ts +126 -0
  116. package/dist/merkle/merkle-verifier.js +296 -0
  117. package/dist/merkle/proof.d.ts +70 -0
  118. package/dist/merkle/proof.js +300 -0
  119. package/dist/merkle/sha256-tree.d.ts +33 -0
  120. package/dist/merkle/sha256-tree.js +145 -0
  121. package/dist/merkle/signed-log.d.ts +156 -0
  122. package/dist/merkle/signed-log.js +356 -0
  123. package/dist/merkle/signed-note.d.ts +309 -0
  124. package/dist/merkle/signed-note.js +648 -0
  125. package/dist/merkle/sth.d.ts +31 -0
  126. package/dist/merkle/sth.js +31 -0
  127. package/dist/merkle/storage.d.ts +40 -0
  128. package/dist/merkle/storage.js +71 -0
  129. package/dist/merkle/tree.d.ts +68 -0
  130. package/dist/merkle/tree.js +94 -0
  131. package/dist/mldsa/embedded.d.ts +1 -0
  132. package/dist/{kyber → mldsa}/embedded.js +5 -5
  133. package/dist/mldsa/expand.d.ts +53 -0
  134. package/dist/mldsa/expand.js +188 -0
  135. package/dist/mldsa/format.d.ts +16 -0
  136. package/dist/mldsa/format.js +68 -0
  137. package/dist/mldsa/hashvariant.d.ts +32 -0
  138. package/dist/mldsa/hashvariant.js +248 -0
  139. package/dist/mldsa/index.d.ts +142 -0
  140. package/dist/mldsa/index.js +463 -0
  141. package/dist/mldsa/keygen.d.ts +16 -0
  142. package/dist/mldsa/keygen.js +232 -0
  143. package/dist/mldsa/params.d.ts +21 -0
  144. package/dist/mldsa/params.js +55 -0
  145. package/dist/mldsa/sha3-helpers.d.ts +30 -0
  146. package/dist/mldsa/sha3-helpers.js +124 -0
  147. package/dist/mldsa/sign.d.ts +36 -0
  148. package/dist/mldsa/sign.js +380 -0
  149. package/dist/mldsa/types.d.ts +91 -0
  150. package/dist/mldsa/types.js +25 -0
  151. package/dist/mldsa/validate.d.ts +55 -0
  152. package/dist/mldsa/validate.js +125 -0
  153. package/dist/mldsa/verify.d.ts +29 -0
  154. package/dist/mldsa/verify.js +269 -0
  155. package/dist/mldsa.wasm +0 -0
  156. package/dist/mlkem/embedded.d.ts +1 -0
  157. package/dist/mlkem/embedded.js +27 -0
  158. package/dist/mlkem/indcpa.d.ts +49 -0
  159. package/dist/{kyber → mlkem}/indcpa.js +44 -44
  160. package/dist/mlkem/index.d.ts +37 -0
  161. package/dist/{kyber → mlkem}/index.js +24 -34
  162. package/dist/mlkem/kem.d.ts +21 -0
  163. package/dist/{kyber → mlkem}/kem.js +44 -64
  164. package/dist/{kyber → mlkem}/params.d.ts +4 -4
  165. package/dist/{kyber → mlkem}/params.js +2 -2
  166. package/dist/mlkem/suite.d.ts +12 -0
  167. package/dist/{kyber → mlkem}/suite.js +17 -12
  168. package/dist/{kyber → mlkem}/types.d.ts +3 -3
  169. package/dist/{kyber → mlkem}/types.js +1 -1
  170. package/dist/{kyber → mlkem}/validate.d.ts +7 -7
  171. package/dist/{kyber → mlkem}/validate.js +7 -7
  172. package/dist/{kyber.wasm → mlkem.wasm} +0 -0
  173. package/dist/p256.wasm +0 -0
  174. package/dist/ratchet/index.d.ts +2 -0
  175. package/dist/ratchet/index.js +1 -0
  176. package/dist/ratchet/kdf-chain.js +3 -3
  177. package/dist/ratchet/ratchet-keypair.js +2 -2
  178. package/dist/ratchet/root-kdf.js +7 -7
  179. package/dist/ratchet/skipped-key-store.js +4 -4
  180. package/dist/ratchet/types.d.ts +1 -1
  181. package/dist/serpent/cipher-suite.js +20 -17
  182. package/dist/serpent/generator.d.ts +1 -1
  183. package/dist/serpent/generator.js +2 -2
  184. package/dist/serpent/index.d.ts +8 -7
  185. package/dist/serpent/index.js +18 -27
  186. package/dist/serpent/pool-worker.js +7 -5
  187. package/dist/serpent/serpent-cbc.d.ts +4 -4
  188. package/dist/serpent/serpent-cbc.js +11 -8
  189. package/dist/serpent/shared-ops.d.ts +3 -23
  190. package/dist/serpent/shared-ops.js +50 -85
  191. package/dist/serpent.wasm +0 -0
  192. package/dist/sha2/hkdf.js +5 -5
  193. package/dist/sha2/index.d.ts +21 -1
  194. package/dist/sha2/index.js +65 -10
  195. package/dist/sha2/types.d.ts +41 -2
  196. package/dist/sha2.wasm +0 -0
  197. package/dist/sha3/index.d.ts +72 -3
  198. package/dist/sha3/index.js +240 -14
  199. package/dist/sha3/kmac.d.ts +121 -0
  200. package/dist/sha3/kmac.js +800 -0
  201. package/dist/sha3.wasm +0 -0
  202. package/dist/shared/pkcs7.d.ts +22 -0
  203. package/dist/shared/pkcs7.js +84 -0
  204. package/dist/sign/ctx.d.ts +41 -0
  205. package/dist/sign/ctx.js +102 -0
  206. package/dist/sign/envelope.d.ts +45 -0
  207. package/dist/sign/envelope.js +152 -0
  208. package/dist/sign/hasher.d.ts +9 -0
  209. package/dist/sign/hasher.js +132 -0
  210. package/dist/sign/index.d.ts +11 -0
  211. package/dist/sign/index.js +34 -0
  212. package/dist/sign/sign-stream.d.ts +25 -0
  213. package/dist/sign/sign-stream.js +112 -0
  214. package/dist/sign/suites/ecdsa-p256.d.ts +2 -0
  215. package/dist/sign/suites/ecdsa-p256.js +120 -0
  216. package/dist/sign/suites/ed25519.d.ts +3 -0
  217. package/dist/sign/suites/ed25519.js +165 -0
  218. package/dist/sign/suites/hybrid-classical.d.ts +23 -0
  219. package/dist/sign/suites/hybrid-classical.js +526 -0
  220. package/dist/sign/suites/hybrid-pq.d.ts +4 -0
  221. package/dist/sign/suites/hybrid-pq.js +234 -0
  222. package/dist/sign/suites/mldsa.d.ts +7 -0
  223. package/dist/sign/suites/mldsa.js +161 -0
  224. package/dist/sign/suites/slhdsa.d.ts +7 -0
  225. package/dist/sign/suites/slhdsa.js +176 -0
  226. package/dist/sign/types.d.ts +106 -0
  227. package/dist/sign/types.js +28 -0
  228. package/dist/sign/verify-stream.d.ts +30 -0
  229. package/dist/sign/verify-stream.js +227 -0
  230. package/dist/slhdsa/embedded.d.ts +1 -0
  231. package/dist/slhdsa/embedded.js +26 -0
  232. package/dist/slhdsa/index.d.ts +149 -0
  233. package/dist/slhdsa/index.js +493 -0
  234. package/dist/slhdsa/params.d.ts +26 -0
  235. package/dist/slhdsa/params.js +70 -0
  236. package/dist/slhdsa/prehash.d.ts +68 -0
  237. package/dist/slhdsa/prehash.js +307 -0
  238. package/dist/slhdsa/sign.d.ts +39 -0
  239. package/dist/slhdsa/sign.js +116 -0
  240. package/dist/slhdsa/types.d.ts +129 -0
  241. package/dist/slhdsa/types.js +27 -0
  242. package/dist/slhdsa/validate.d.ts +60 -0
  243. package/dist/slhdsa/validate.js +127 -0
  244. package/dist/slhdsa/verify.d.ts +32 -0
  245. package/dist/slhdsa/verify.js +107 -0
  246. package/dist/slhdsa.wasm +0 -0
  247. package/dist/stream/header.js +3 -3
  248. package/dist/stream/index.d.ts +1 -0
  249. package/dist/stream/index.js +1 -0
  250. package/dist/stream/open-stream.js +31 -10
  251. package/dist/stream/seal-stream-pool.d.ts +1 -0
  252. package/dist/stream/seal-stream-pool.js +63 -26
  253. package/dist/stream/seal-stream.d.ts +1 -1
  254. package/dist/stream/seal-stream.js +20 -9
  255. package/dist/stream/seal.js +6 -6
  256. package/dist/stream/types.d.ts +3 -1
  257. package/dist/stream/types.js +1 -1
  258. package/dist/types.d.ts +1 -1
  259. package/dist/types.js +1 -1
  260. package/dist/utils.d.ts +3 -3
  261. package/dist/utils.js +46 -54
  262. package/dist/wasm-source.d.ts +7 -7
  263. package/dist/wasm-source.js +1 -1
  264. package/dist/x25519/embedded.d.ts +1 -0
  265. package/dist/x25519/embedded.js +31 -0
  266. package/dist/x25519/index.d.ts +43 -0
  267. package/dist/x25519/index.js +159 -0
  268. package/dist/x25519/types.d.ts +25 -0
  269. package/dist/x25519/types.js +27 -0
  270. package/dist/x25519/validate.d.ts +2 -0
  271. package/dist/x25519/validate.js +39 -0
  272. package/package.json +70 -26
  273. package/SECURITY.md +0 -163
  274. package/dist/ct-wasm.d.ts +0 -1
  275. package/dist/ct-wasm.js +0 -3
  276. package/dist/docs/aead.md +0 -363
  277. package/dist/docs/architecture.md +0 -1011
  278. package/dist/docs/argon2id.md +0 -305
  279. package/dist/docs/chacha20.md +0 -781
  280. package/dist/docs/exports.md +0 -277
  281. package/dist/docs/fortuna.md +0 -530
  282. package/dist/docs/init.md +0 -301
  283. package/dist/docs/loader.md +0 -256
  284. package/dist/docs/serpent.md +0 -617
  285. package/dist/docs/sha2.md +0 -671
  286. package/dist/docs/sha3.md +0 -612
  287. package/dist/docs/types.md +0 -416
  288. package/dist/docs/utils.md +0 -457
  289. package/dist/embedded/kyber.d.ts +0 -1
  290. package/dist/embedded/kyber.js +0 -3
  291. package/dist/kyber/embedded.d.ts +0 -1
  292. package/dist/kyber/indcpa.d.ts +0 -49
  293. package/dist/kyber/index.d.ts +0 -38
  294. package/dist/kyber/kem.d.ts +0 -21
  295. package/dist/kyber/suite.d.ts +0 -12
  296. /package/dist/{ct.wasm → cte.wasm} +0 -0
package/dist/docs/sha2.md DELETED
@@ -1,671 +0,0 @@
1
- <img src="https://github.com/xero/leviathan-crypto/raw/main/docs/logo.svg" alt="logo" width="120" align="left" margin="10">
2
-
3
- ### SHA-2 TypeScript API
4
-
5
- Cryptographic hashing and message authentication using SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512.
6
-
7
- > ### Table of Contents
8
- > - [Overview](#overview)
9
- > - [Security Notes](#security-notes)
10
- > - [Module Init](#module-init)
11
- > - [API Reference](#api-reference)
12
- > - [SHA256Hash](#sha256hash)
13
- > - [Usage Examples](#usage-examples)
14
- > - [Error Conditions](#error-conditions)
15
-
16
- ---
17
-
18
- ## Overview
19
-
20
- SHA-2 is a family of cryptographic hash functions standardized in
21
- [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final).
22
- A hash function takes an input of any size (a password, a file, a single byte) and produces a fixed-size output called a **digest** (sometimes called
23
- a "fingerprint" or "hash"). Even the smallest change to the input produces a
24
- completely different digest. This makes hash functions useful for verifying that
25
- data has not been tampered with.
26
-
27
- leviathan-crypto provides three SHA-2 variants:
28
-
29
- **SHA-256.** 32-byte (256-bit) digest. The most widely used variant. Use this unless you have a specific reason to choose another.
30
-
31
- **SHA-512.** 64-byte (512-bit) digest. Higher security margin. Faster than SHA-256 on 64-bit platforms.
32
-
33
- **SHA-384.** 48-byte (384-bit) digest. A truncated variant of SHA-512. Useful when you need a digest longer than 256 bits but shorter than 512 bits, or when a protocol specifies it (e.g. TLS cipher suites).
34
-
35
- **HMAC** (Hash-based Message Authentication Code, [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104))
36
- combines a secret key with a hash function to produce a **tag** that proves both
37
- the integrity and the authenticity of a message. Anyone can compute a plain SHA-256
38
- hash of a message. But only someone who holds the secret key can compute the correct HMAC tag. The recipient can then verify that the message was sent by
39
- someone who knows the key and that it was not modified in transit.
40
-
41
- leviathan-crypto provides three HMAC variants corresponding to each hash:
42
-
43
- **HMAC_SHA256.** 32-byte tag.
44
-
45
- **HMAC_SHA512.** 64-byte tag.
46
-
47
- **HMAC_SHA384.** 48-byte tag.
48
-
49
- All computation runs in WebAssembly. The TypeScript classes handle input
50
- validation and the JS/WASM boundary. They never implement cryptographic algorithms directly.
51
-
52
- ---
53
-
54
- ## Security Notes
55
-
56
- > [!IMPORTANT]
57
- > Read these before using the API. Misusing hash functions is one of the most
58
- > common sources of security vulnerabilities.
59
-
60
- ### Hashing is NOT encryption
61
-
62
- A hash is a one-way function. You **cannot** recover the original input from a
63
- hash digest. If you need to protect data so that it can be read later, you need
64
- encryption (see [serpent.md](./serpent.md) or use `XChaCha20Poly1305`).
65
-
66
- ### Do NOT use plain SHA-2 for passwords
67
-
68
- SHA-2 is extremely fast by design. An attacker with a GPU can compute billions
69
- of SHA-256 hashes per second, making brute-force attacks on passwords trivial.
70
- For password hashing, use a memory-hardened function like **Argon2id**. See
71
- [argon2id.md](./argon2id.md) for usage patterns including passphrase-based
72
- encryption with leviathan primitives.
73
-
74
- ### SHA-2 is vulnerable to length extension attacks
75
-
76
- Never construct a MAC by concatenating a secret and a message and hashing them:
77
-
78
- ```typescript
79
- // DANGEROUS: DO NOT DO THIS
80
- const bad = sha256.hash(concat(secret, message))
81
- ```
82
-
83
- An attacker who sees `SHA256(secret || message)` can compute
84
- `SHA256(secret || message || padding || attacker_data)` without knowing the
85
- secret. This is called a **length extension attack**.
86
-
87
- **Always use HMAC** when you need to authenticate a message with a secret key.
88
- HMAC is specifically designed to be immune to this attack.
89
-
90
- ### HMAC key length
91
-
92
- HMAC keys should be **at least as long as the hash output**:
93
-
94
- | HMAC variant | Minimum recommended key length |
95
- |---------------|-------------------------------|
96
- | HMAC_SHA256 | 32 bytes (256 bits) |
97
- | HMAC_SHA384 | 48 bytes (384 bits) |
98
- | HMAC_SHA512 | 64 bytes (512 bits) |
99
-
100
- Keys shorter than this are technically valid (they will be zero-padded
101
- internally) but provide less security than the hash function offers. Keys
102
- longer than the hash block size (64 bytes for SHA-256, 128 bytes for
103
- SHA-384/SHA-512) are pre-hashed automatically per RFC 2104 section 3. There is no benefit to using very long keys.
104
-
105
- ### Always use constant-time comparison for HMAC verification
106
-
107
- When verifying an HMAC tag, **never** use `===` or any other comparison that
108
- can return early on the first mismatched byte. An attacker can measure how long
109
- the comparison takes and use that information to forge a valid tag one byte at a
110
- time (this is called a **timing attack**).
111
-
112
- Use `constantTimeEqual()` from leviathan-crypto instead. It always compares
113
- every byte regardless of where the first difference is.
114
-
115
- Note: `constantTimeEqual` requires WebAssembly SIMD; on non-SIMD runtimes it
116
- throws at first call. See [utils.md](./utils.md#constanttimeequal).
117
-
118
- ### Call dispose() when you are done
119
-
120
- `dispose()` calls `wipeBuffers()` in the WASM module, which zeroes out all
121
- internal buffers including hash state and key material. This prevents sensitive
122
- data from lingering in memory. Always call `dispose()` when you are finished
123
- with a hash or HMAC instance.
124
-
125
- > [!NOTE]
126
- > See [SHA-2 implementation audit](./sha2_audit.md), [HMAC audit](./hmac_audit.md), and [HKDF audit](./hkdf_audit.md) for algorithm correctness verifications.
127
-
128
- ---
129
-
130
- ## Module Init
131
-
132
- Each module subpath exports its own init function for consumers who want
133
- tree-shakeable imports.
134
-
135
- ### `sha2Init(source)`
136
-
137
- Initializes only the sha2 WASM binary. Equivalent to calling the
138
- root `init({ sha2: source })` but without pulling the other three
139
- modules into the bundle.
140
-
141
- **Signature:**
142
-
143
- ```typescript
144
- async function sha2Init(source: WasmSource): Promise<void>
145
- ```
146
-
147
- **Usage:**
148
-
149
- ```typescript
150
- import { sha2Init, SHA256 } from 'leviathan-crypto/sha2'
151
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
152
-
153
- await sha2Init(sha2Wasm)
154
- const sha = new SHA256()
155
- ```
156
-
157
- ---
158
-
159
- ## API Reference
160
-
161
- All classes require `init({ sha2: sha2Wasm })` or the subpath `sha2Init(sha2Wasm)` to be called first.
162
- Constructing any SHA-2 class before initialization throws an error.
163
-
164
- ### SHA256
165
-
166
- Computes a SHA-256 hash (32-byte digest).
167
-
168
- ```typescript
169
- class SHA256 {
170
- constructor()
171
- hash(msg: Uint8Array): Uint8Array
172
- dispose(): void
173
- }
174
- ```
175
-
176
- **`constructor()`** Creates a new SHA256 instance. Throws if `init({ sha2: sha2Wasm })` has not been called.
177
-
178
- **`hash(msg: Uint8Array): Uint8Array`** Hashes the entire message and returns a 32-byte `Uint8Array` digest. The message can be any length including empty. Large messages are internally chunked and streamed through the WASM hash function, so memory usage stays constant regardless of input size.
179
-
180
- **`dispose(): void`** Wipes all internal WASM buffers (hash state, input buffer, output buffer). Call this when you are done with the instance.
181
-
182
- ---
183
-
184
- ### SHA512
185
-
186
- Computes a SHA-512 hash (64-byte digest).
187
-
188
- ```typescript
189
- class SHA512 {
190
- constructor()
191
- hash(msg: Uint8Array): Uint8Array
192
- dispose(): void
193
- }
194
- ```
195
-
196
- **`constructor()`** Creates a new SHA512 instance. Throws if not initialized.
197
-
198
- **`hash(msg: Uint8Array): Uint8Array`** Returns a 64-byte digest.
199
-
200
- **`dispose(): void`** Wipes all internal WASM buffers.
201
-
202
- ---
203
-
204
- ### SHA384
205
-
206
- Computes a SHA-384 hash (48-byte digest). SHA-384 is a truncated variant of
207
- SHA-512 with different initial values.
208
-
209
- ```typescript
210
- class SHA384 {
211
- constructor()
212
- hash(msg: Uint8Array): Uint8Array
213
- dispose(): void
214
- }
215
- ```
216
-
217
- **`constructor()`** Creates a new SHA384 instance. Throws if not initialized.
218
-
219
- **`hash(msg: Uint8Array): Uint8Array`** Returns a 48-byte digest.
220
-
221
- **`dispose(): void`** Wipes all internal WASM buffers.
222
-
223
- ---
224
-
225
- ### HMAC_SHA256
226
-
227
- Computes an HMAC-SHA256 authentication tag (32-byte output).
228
-
229
- ```typescript
230
- class HMAC_SHA256 {
231
- constructor()
232
- hash(key: Uint8Array, msg: Uint8Array): Uint8Array
233
- dispose(): void
234
- }
235
- ```
236
-
237
- **`constructor()`** Creates a new HMAC_SHA256 instance. Throws if not initialized.
238
-
239
- **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** Computes the HMAC-SHA256 tag for the given message using the given key. Returns a 32-byte `Uint8Array`. Keys longer than 64 bytes are automatically pre-hashed with SHA-256 per RFC 2104 section 3.
240
-
241
- **`dispose(): void`** Wipes all internal WASM buffers, including key material.
242
-
243
- ---
244
-
245
- ### HMAC_SHA512
246
-
247
- Computes an HMAC-SHA512 authentication tag (64-byte output).
248
-
249
- ```typescript
250
- class HMAC_SHA512 {
251
- constructor()
252
- hash(key: Uint8Array, msg: Uint8Array): Uint8Array
253
- dispose(): void
254
- }
255
- ```
256
-
257
- **`constructor()`** Creates a new HMAC_SHA512 instance. Throws if not initialized.
258
-
259
- **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** Returns a 64-byte HMAC tag. Keys longer than 128 bytes are pre-hashed with SHA-512.
260
-
261
- **`dispose(): void`** Wipes all internal WASM buffers.
262
-
263
- ---
264
-
265
- ### HMAC_SHA384
266
-
267
- Computes an HMAC-SHA384 authentication tag (48-byte output).
268
-
269
- ```typescript
270
- class HMAC_SHA384 {
271
- constructor()
272
- hash(key: Uint8Array, msg: Uint8Array): Uint8Array
273
- dispose(): void
274
- }
275
- ```
276
-
277
- **`constructor()`** Creates a new HMAC_SHA384 instance. Throws if not initialized.
278
-
279
- **`hash(key: Uint8Array, msg: Uint8Array): Uint8Array`** Returns a 48-byte HMAC tag. Keys longer than 128 bytes are pre-hashed with SHA-384.
280
-
281
- **`dispose(): void`** Wipes all internal WASM buffers.
282
-
283
- ---
284
-
285
- ### HKDF_SHA256
286
-
287
- RFC 5869 HMAC-based Extract-and-Expand Key Derivation Function over
288
- HMAC-SHA256. Use HKDF when you need to derive one or more keys from a shared
289
- secret (e.g. after a Diffie-Hellman exchange) or to separate keys for different
290
- purposes from a single source of keying material.
291
-
292
- `HKDF_SHA256` should be the default choice. `HKDF_SHA384` does not exist.
293
-
294
- ```typescript
295
- class HKDF_SHA256 {
296
- constructor()
297
- extract(salt: Uint8Array | null, ikm: Uint8Array): Uint8Array
298
- expand(prk: Uint8Array, info: Uint8Array, length: number): Uint8Array
299
- derive(ikm: Uint8Array, salt: Uint8Array | null, info: Uint8Array, length: number): Uint8Array
300
- dispose(): void
301
- }
302
- ```
303
-
304
- **`constructor()`** Creates a new HKDF_SHA256 instance. Throws if `init({ sha2: sha2Wasm })` has not been called.
305
-
306
- **`extract(salt, ikm): Uint8Array`** RFC 5869 section 2.2. Computes `PRK = HMAC-SHA256(salt, IKM)`. Returns a 32-byte pseudorandom key. If `salt` is `null` or empty, defaults to 32 zero bytes per RFC section 2.2.
307
-
308
- **`expand(prk, info, length): Uint8Array`** RFC 5869 section 2.3. Derives `length` bytes of output keying material from a 32-byte PRK. `info` provides application-specific context (can be empty). `length` must be between 1 and 8160 (255 x 32). Throws `RangeError` if `prk` is not exactly 32 bytes or if `length` is out of range.
309
-
310
- **`derive(ikm, salt, info, length): Uint8Array`** One-shot: calls `extract(salt, ikm)` then `expand(prk, info, length)`. This is the correct path for most callers. `extract()` and `expand()` are exposed separately for advanced use cases such as key separation and ratchets. Callers who reach for them should know why.
311
-
312
- **`dispose(): void`** Releases the internal HMAC instance.
313
-
314
- ---
315
-
316
- ### HKDF_SHA512
317
-
318
- Identical to `HKDF_SHA256` but uses HMAC-SHA512 internally. HashLen is 64, so
319
- PRK must be exactly 64 bytes and maximum output length is 16320 (255 x 64).
320
-
321
- ```typescript
322
- class HKDF_SHA512 {
323
- constructor()
324
- extract(salt: Uint8Array | null, ikm: Uint8Array): Uint8Array
325
- expand(prk: Uint8Array, info: Uint8Array, length: number): Uint8Array
326
- derive(ikm: Uint8Array, salt: Uint8Array | null, info: Uint8Array, length: number): Uint8Array
327
- dispose(): void
328
- }
329
- ```
330
-
331
- **`constructor()`** Creates a new `HKDF_SHA512` instance. Throws if `init({ sha2: sha2Wasm })` has not been called.
332
-
333
- **`extract(salt, ikm)`** If `salt` is `null` or empty, defaults to 64 zero bytes.
334
-
335
- **`expand(prk, info, length)`** PRK must be exactly 64 bytes. `length` must be between 1 and 16320. Throws `RangeError` otherwise.
336
-
337
- **`derive(ikm, salt, info, length): Uint8Array`** One-shot: calls `extract(salt, ikm)` then `expand(prk, info, length)`. This is the correct path for most callers.
338
-
339
- **`dispose(): void`** Releases the internal HMAC instance.
340
-
341
- > [!NOTE]
342
- > HKDF is a pure TypeScript composition over the WASM-backed HMAC classes.
343
- > It does not introduce new WASM code or new `init()` modules. Initializing
344
- > `sha2` is sufficient.
345
-
346
- **Usage example:**
347
-
348
- ```typescript
349
- import { init, HKDF_SHA256, bytesToHex } from 'leviathan-crypto'
350
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
351
-
352
- await init({ sha2: sha2Wasm })
353
-
354
- const hkdf = new HKDF_SHA256()
355
- const ikm = new Uint8Array(32) // your input keying material
356
- const salt = crypto.getRandomValues(new Uint8Array(32))
357
- const info = new TextEncoder().encode('my-app-v1-encryption-key')
358
-
359
- const key = hkdf.derive(ikm, salt, info, 32)
360
- console.log('Derived key:', bytesToHex(key))
361
-
362
- hkdf.dispose()
363
- ```
364
-
365
- ---
366
-
367
- ## SHA256Hash
368
-
369
- Stateless SHA-256 `HashFn` for Fortuna's accumulator and reseed slots. Plain
370
- `const` object — no instantiation, no `dispose()`.
371
-
372
- Requires `init({ sha2: sha2Wasm })`. See [fortuna.md](./fortuna.md) for full
373
- usage with `Fortuna.create()`.
374
-
375
- | Property | Value |
376
- |----------|-------|
377
- | `outputSize` | `32` |
378
- | `wasmModules` | `['sha2']` |
379
-
380
- ### `SHA256Hash.digest(msg): Uint8Array`
381
-
382
- Hashes `msg` and returns a 32-byte SHA-256 digest. Wipes WASM input/output/state
383
- scratch before returning.
384
-
385
- | Parameter | Type | Description |
386
- |-----------|------|-------------|
387
- | `msg` | `Uint8Array` | Message to hash (any length) |
388
-
389
- **Returns** a new `Uint8Array` of 32 bytes.
390
-
391
- **Throws** `Error` if another stateful instance currently owns the `sha2` WASM module.
392
-
393
- ### Usage with `Fortuna`
394
-
395
- ```typescript
396
- import { init, Fortuna } from 'leviathan-crypto'
397
- import { SHA256Hash } from 'leviathan-crypto/sha2'
398
- import { SerpentGenerator } from 'leviathan-crypto/serpent'
399
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
400
- import { serpentWasm } from 'leviathan-crypto/serpent/embedded'
401
-
402
- await init({ sha2: sha2Wasm, serpent: serpentWasm })
403
- const rng = await Fortuna.create({ generator: SerpentGenerator, hash: SHA256Hash })
404
- const bytes = rng.get(32)
405
- rng.stop()
406
- ```
407
-
408
- ---
409
-
410
- ## Usage Examples
411
-
412
- ### Example 1: Hash a message with SHA-256
413
-
414
- Hash a string and get a hex-encoded digest.
415
-
416
- ```typescript
417
- import { init, SHA256, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
418
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
419
-
420
- // Step 1: Initialize the SHA-2 WASM module (do this once at app startup)
421
- await init({ sha2: sha2Wasm })
422
-
423
- // Step 2: Create a SHA256 instance
424
- const sha = new SHA256()
425
-
426
- // Step 3: Hash a message
427
- // utf8ToBytes converts a string to a Uint8Array
428
- const message = utf8ToBytes('Hello, world!')
429
- const digest = sha.hash(message)
430
-
431
- // Step 4: Convert the digest to a hex string for display or storage
432
- console.log(bytesToHex(digest))
433
- // => "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"
434
-
435
- // Step 5: Clean up, wipes hash state from WASM memory
436
- sha.dispose()
437
- ```
438
-
439
- ### Example 2: Hash binary data (e.g., a file)
440
-
441
- SHA-256 works on raw bytes, not just strings. You can hash any `Uint8Array`,
442
- including file contents read from a `<input type="file">` element or a fetch
443
- response.
444
-
445
- ```typescript
446
- import { init, SHA256, bytesToHex } from 'leviathan-crypto'
447
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
448
-
449
- await init({ sha2: sha2Wasm })
450
-
451
- // Suppose you have file contents as an ArrayBuffer (from FileReader, fetch, etc.)
452
- const response = await fetch('https://example.com/file.bin')
453
- const buffer = new Uint8Array(await response.arrayBuffer())
454
-
455
- const sha = new SHA256()
456
- const digest = sha.hash(buffer)
457
- console.log('SHA-256:', bytesToHex(digest))
458
-
459
- sha.dispose()
460
- ```
461
-
462
- The library handles large inputs automatically. It streams the data through the WASM hash function in chunks, so you do not need to worry about memory.
463
-
464
- ### Example 3: Using SHA-512 or SHA-384
465
-
466
- The API is identical for all three hash variants. Only the output size differs.
467
-
468
- ```typescript
469
- import { init, SHA256, SHA384, SHA512, bytesToHex, utf8ToBytes } from 'leviathan-crypto'
470
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
471
-
472
- await init({ sha2: sha2Wasm })
473
-
474
- const msg = utf8ToBytes('Same message, different hashes')
475
-
476
- const sha256 = new SHA256()
477
- const sha384 = new SHA384()
478
- const sha512 = new SHA512()
479
-
480
- console.log('SHA-256 (32 bytes):', bytesToHex(sha256.hash(msg)))
481
- console.log('SHA-384 (48 bytes):', bytesToHex(sha384.hash(msg)))
482
- console.log('SHA-512 (64 bytes):', bytesToHex(sha512.hash(msg)))
483
-
484
- sha256.dispose()
485
- sha384.dispose()
486
- sha512.dispose()
487
- ```
488
-
489
- ### Example 4: Generate and verify an HMAC
490
-
491
- Use HMAC when you need to prove that a message was created by someone who holds
492
- a secret key. One side generates a tag, the other side recomputes the tag with
493
- the same key and checks that they match.
494
-
495
- ```typescript
496
- import {
497
- init, HMAC_SHA256, constantTimeEqual, randomBytes,
498
- bytesToHex, utf8ToBytes
499
- } from 'leviathan-crypto'
500
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
501
-
502
- await init({ sha2: sha2Wasm })
503
-
504
- // Generate a random 32-byte key (do this once, store it securely)
505
- const key = randomBytes(32)
506
-
507
- const hmac = new HMAC_SHA256()
508
- const message = utf8ToBytes('Transfer $100 to Alice')
509
-
510
- // --- Sender side: generate the tag ---
511
- const tag = hmac.hash(key, message)
512
- console.log('HMAC tag:', bytesToHex(tag))
513
-
514
- // Send both `message` and `tag` to the recipient...
515
-
516
- // --- Recipient side: verify the tag ---
517
- // The recipient has the same key and recomputes the tag
518
- const recomputed = hmac.hash(key, message)
519
-
520
- // Use constant-time comparison to check the tags
521
- if (constantTimeEqual(tag, recomputed)) {
522
- console.log('Message is authentic. It was not tampered with.')
523
- } else {
524
- console.log('WARNING: message has been modified or key is wrong')
525
- }
526
-
527
- hmac.dispose()
528
- ```
529
-
530
- ### Example 5: HMAC verification — the right way
531
-
532
- The difference between these two approaches is the difference between a secure
533
- system and a broken one.
534
-
535
- ```typescript
536
- import { init, HMAC_SHA256, constantTimeEqual, bytesToHex } from 'leviathan-crypto'
537
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
538
-
539
- await init({ sha2: sha2Wasm })
540
- const hmac = new HMAC_SHA256()
541
-
542
- // Suppose you received a message with a tag and you recomputed the expected tag:
543
- const receivedTag = hmac.hash(key, message)
544
- const expectedTag = hmac.hash(key, message)
545
-
546
- // WRONG: timing attack vulnerable
547
- // JavaScript's === operator compares byte-by-byte and returns false as soon as
548
- // it finds a mismatch. An attacker can measure the response time to figure out
549
- // how many leading bytes of the tag are correct, then forge a valid tag one
550
- // byte at a time.
551
- if (bytesToHex(receivedTag) === bytesToHex(expectedTag)) {
552
- // This works but is insecure
553
- }
554
-
555
- // RIGHT: constant-time comparison
556
- // constantTimeEqual always examines every byte, regardless of where the first
557
- // difference is. The comparison takes the same amount of time whether zero
558
- // bytes match or all bytes match.
559
- if (constantTimeEqual(receivedTag, expectedTag)) {
560
- // This is secure
561
- }
562
-
563
- hmac.dispose()
564
- ```
565
-
566
- ### Example 6: Using HMAC-SHA512 for higher security
567
-
568
- The pattern is identical to HMAC-SHA256. Use a 64-byte key for full security.
569
-
570
- ```typescript
571
- import { init, HMAC_SHA512, constantTimeEqual, randomBytes, utf8ToBytes } from 'leviathan-crypto'
572
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
573
-
574
- await init({ sha2: sha2Wasm })
575
-
576
- // 64-byte key for HMAC-SHA512
577
- const key = randomBytes(64)
578
- const hmac = new HMAC_SHA512()
579
-
580
- const tag = hmac.hash(key, utf8ToBytes('Important message'))
581
- // tag is a 64-byte Uint8Array
582
-
583
- // Verify
584
- const recomputed = hmac.hash(key, utf8ToBytes('Important message'))
585
- console.log('Valid:', constantTimeEqual(tag, recomputed)) // true
586
-
587
- hmac.dispose()
588
- ```
589
-
590
- ### Example 7: Hashing an empty input
591
-
592
- SHA-2 is well-defined for empty inputs. This can be useful as a sanity check.
593
-
594
- ```typescript
595
- import { init, SHA256, bytesToHex } from 'leviathan-crypto'
596
- import { sha2Wasm } from 'leviathan-crypto/sha2/embedded'
597
-
598
- await init({ sha2: sha2Wasm })
599
-
600
- const sha = new SHA256()
601
- const digest = sha.hash(new Uint8Array(0))
602
- console.log(bytesToHex(digest))
603
- // => "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
604
- // (This is the well-known SHA-256 hash of the empty string)
605
-
606
- sha.dispose()
607
- ```
608
-
609
- ---
610
-
611
- ## Error Conditions
612
-
613
- ### Module not initialized
614
-
615
- If you construct any SHA-2 class before calling `init({ sha2: sha2Wasm })`, the
616
- constructor throws immediately:
617
-
618
- ```
619
- Error: leviathan-crypto: call init({ sha2: ... }) before using this class
620
- ```
621
-
622
- **Fix:** Call `await init({ sha2: sha2Wasm })` at application startup, before creating any
623
- SHA-2 instances.
624
-
625
- ```typescript
626
- // This will throw:
627
- const sha = new SHA256() // Error!
628
-
629
- // Do this instead:
630
- await init({ sha2: sha2Wasm })
631
- const sha = new SHA256() // OK
632
- ```
633
-
634
- ### HMAC key length
635
-
636
- HMAC accepts keys of any length, including zero-length keys. However:
637
-
638
- - **Keys shorter than the recommended minimum** (see the table in Security
639
- Notes) are zero-padded internally. They will produce valid HMAC tags, but the
640
- security of the MAC is limited by the key length, not the hash output size.
641
- A 16-byte key with HMAC-SHA256 provides at most 128 bits of security, not 256.
642
- - **Keys longer than the hash block size** (64 bytes for HMAC-SHA256, 128 bytes
643
- for HMAC-SHA384 and HMAC-SHA512) are automatically pre-hashed to fit. This is
644
- standard behavior defined in RFC 2104 section 3 and is handled transparently.
645
- - **Zero-length keys** are technically valid per the HMAC spec, but provide no
646
- authentication. Do not use a zero-length key in production.
647
-
648
- ### Empty message input
649
-
650
- All hash and HMAC functions accept empty `Uint8Array` inputs (`new Uint8Array(0)`).
651
- SHA-2 is well-defined for zero-length messages and will return the correct digest.
652
-
653
- ---
654
-
655
-
656
- ## Cross-References
657
-
658
- | Document | Description |
659
- | -------- | ----------- |
660
- | [index](./README.md) | Project Documentation index |
661
- | [architecture](./architecture.md) | architecture overview, module relationships, buffer layouts, and build pipeline |
662
- | [asm_sha2](./asm_sha2.md) | WASM implementation details (AssemblyScript buffer layout, compression functions) |
663
- | [sha3](./sha3.md) | alternative: SHA-3 family (immune to length extension attacks) |
664
- | [serpent](./serpent.md) | `SerpentCipher` uses HMAC-SHA256 and HKDF internally via `Seal` and `SealStream` |
665
- | [argon2id](./argon2id.md) | Argon2id password hashing; HKDF expands Argon2id root keys |
666
- | [fortuna](./fortuna.md) | Fortuna CSPRNG uses SHA-256 for entropy accumulation |
667
- | [utils](./utils.md) | `constantTimeEqual`, `bytesToHex`, `utf8ToBytes`, `randomBytes` |
668
- | [sha2_audit.md](./sha2_audit.md) | SHA-2 implementation audit |
669
- | [hmac_audit.md](./hmac_audit.md) | HMAC implementation audit |
670
- | [hkdf_audit.md](./hkdf_audit.md) | HKDF implementation audit |
671
-