ox 1.1.2 → 1.3.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/CHANGELOG.md +61 -0
  2. package/dist/core/AbiFunction.d.ts +1 -1
  3. package/dist/core/AbiFunction.d.ts.map +1 -1
  4. package/dist/core/AbiFunction.js +6 -2
  5. package/dist/core/AbiFunction.js.map +1 -1
  6. package/dist/core/AbiParameters.d.ts +1 -1
  7. package/dist/core/AbiParameters.d.ts.map +1 -1
  8. package/dist/core/AbiParameters.js +1 -13
  9. package/dist/core/AbiParameters.js.map +1 -1
  10. package/dist/core/AesGcm.d.ts +36 -1
  11. package/dist/core/AesGcm.d.ts.map +1 -1
  12. package/dist/core/AesGcm.js +44 -0
  13. package/dist/core/AesGcm.js.map +1 -1
  14. package/dist/core/Ed25519.d.ts +46 -1
  15. package/dist/core/Ed25519.d.ts.map +1 -1
  16. package/dist/core/Ed25519.js +43 -0
  17. package/dist/core/Ed25519.js.map +1 -1
  18. package/dist/core/Engine.d.ts +1 -1
  19. package/dist/core/Engine.d.ts.map +1 -1
  20. package/dist/core/Engine.js.map +1 -1
  21. package/dist/core/Hash.d.ts +200 -1
  22. package/dist/core/Hash.d.ts.map +1 -1
  23. package/dist/core/Hash.js +207 -0
  24. package/dist/core/Hash.js.map +1 -1
  25. package/dist/core/Hex.d.ts.map +1 -1
  26. package/dist/core/Hex.js +1 -4
  27. package/dist/core/Hex.js.map +1 -1
  28. package/dist/core/Rlp.d.ts +32 -0
  29. package/dist/core/Rlp.d.ts.map +1 -1
  30. package/dist/core/Rlp.js +146 -0
  31. package/dist/core/Rlp.js.map +1 -1
  32. package/dist/core/Secp256k1.d.ts +46 -1
  33. package/dist/core/Secp256k1.d.ts.map +1 -1
  34. package/dist/core/Secp256k1.js +48 -0
  35. package/dist/core/Secp256k1.js.map +1 -1
  36. package/dist/core/WebAuthn.d.ts +224 -0
  37. package/dist/core/WebAuthn.d.ts.map +1 -0
  38. package/dist/core/WebAuthn.js +343 -0
  39. package/dist/core/WebAuthn.js.map +1 -0
  40. package/dist/core/WebAuthnP256.d.ts +46 -50
  41. package/dist/core/WebAuthnP256.d.ts.map +1 -1
  42. package/dist/core/WebAuthnP256.js +44 -50
  43. package/dist/core/WebAuthnP256.js.map +1 -1
  44. package/dist/core/internal/abiParameters.d.ts +7 -87
  45. package/dist/core/internal/abiParameters.d.ts.map +1 -1
  46. package/dist/core/internal/abiParameters.js +339 -194
  47. package/dist/core/internal/abiParameters.js.map +1 -1
  48. package/dist/core/internal/engine.d.ts +35 -2
  49. package/dist/core/internal/engine.d.ts.map +1 -1
  50. package/dist/core/internal/engine.js +12 -1
  51. package/dist/core/internal/engine.js.map +1 -1
  52. package/dist/core/internal/hash.d.ts +10 -0
  53. package/dist/core/internal/hash.d.ts.map +1 -1
  54. package/dist/core/internal/hash.js +26 -1
  55. package/dist/core/internal/hash.js.map +1 -1
  56. package/dist/index.d.ts +23 -14
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +23 -14
  59. package/dist/index.js.map +1 -1
  60. package/dist/node/Engine.d.ts +1 -1
  61. package/dist/node/Engine.d.ts.map +1 -1
  62. package/dist/node/Engine.js.map +1 -1
  63. package/dist/node/Hash.d.ts +6 -4
  64. package/dist/node/Hash.d.ts.map +1 -1
  65. package/dist/node/Hash.js +35 -3
  66. package/dist/node/Hash.js.map +1 -1
  67. package/dist/tempo/AuthorizationTempo.d.ts +6 -6
  68. package/dist/tempo/AuthorizationTempo.js +6 -6
  69. package/dist/tempo/KeyAuthorization.d.ts +3 -8
  70. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  71. package/dist/tempo/KeyAuthorization.js +3 -8
  72. package/dist/tempo/KeyAuthorization.js.map +1 -1
  73. package/dist/tempo/SignatureEnvelope.d.ts +8 -8
  74. package/dist/tempo/SignatureEnvelope.js +8 -8
  75. package/dist/trusted-setups/Paths.d.ts +1 -0
  76. package/dist/trusted-setups/Paths.d.ts.map +1 -1
  77. package/dist/trusted-setups/Paths.js +1 -0
  78. package/dist/trusted-setups/Paths.js.map +1 -1
  79. package/dist/trusted-setups/Setups.d.ts +35 -0
  80. package/dist/trusted-setups/Setups.d.ts.map +1 -0
  81. package/dist/trusted-setups/Setups.js +29 -0
  82. package/dist/trusted-setups/Setups.js.map +1 -0
  83. package/dist/trusted-setups/index.d.ts +20 -0
  84. package/dist/trusted-setups/index.d.ts.map +1 -1
  85. package/dist/trusted-setups/index.js +20 -0
  86. package/dist/trusted-setups/index.js.map +1 -1
  87. package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
  88. package/dist/trusted-setups/internal/paths.js +1 -1
  89. package/dist/trusted-setups/internal/paths.js.map +1 -1
  90. package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
  91. package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
  92. package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
  93. package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
  94. package/dist/wasm/Engine.d.ts +4 -3
  95. package/dist/wasm/Engine.d.ts.map +1 -1
  96. package/dist/wasm/Engine.js +5 -5
  97. package/dist/wasm/Engine.js.map +1 -1
  98. package/dist/wasm/Hash.d.ts +1 -1
  99. package/dist/wasm/Hash.d.ts.map +1 -1
  100. package/dist/wasm/Hash.js +5 -0
  101. package/dist/wasm/Hash.js.map +1 -1
  102. package/dist/wasm/Keystore.d.ts +23 -9
  103. package/dist/wasm/Keystore.d.ts.map +1 -1
  104. package/dist/wasm/Keystore.js +30 -13
  105. package/dist/wasm/Keystore.js.map +1 -1
  106. package/dist/wasm/Kzg.d.ts +112 -0
  107. package/dist/wasm/Kzg.d.ts.map +1 -0
  108. package/dist/wasm/Kzg.js +333 -0
  109. package/dist/wasm/Kzg.js.map +1 -0
  110. package/dist/wasm/Secp256k1.d.ts +55 -0
  111. package/dist/wasm/Secp256k1.d.ts.map +1 -0
  112. package/dist/wasm/Secp256k1.js +181 -0
  113. package/dist/wasm/Secp256k1.js.map +1 -0
  114. package/dist/wasm/index.d.ts +44 -1
  115. package/dist/wasm/index.d.ts.map +1 -1
  116. package/dist/wasm/index.js +44 -1
  117. package/dist/wasm/index.js.map +1 -1
  118. package/dist/wasm/internal/blake3.d.ts +10 -1
  119. package/dist/wasm/internal/blake3.d.ts.map +1 -1
  120. package/dist/wasm/internal/blake3.js +12 -1
  121. package/dist/wasm/internal/blake3.js.map +1 -1
  122. package/dist/wasm/internal/blake3.wasm.d.ts +3 -1
  123. package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -1
  124. package/dist/wasm/internal/blake3.wasm.js +3 -1
  125. package/dist/wasm/internal/blake3.wasm.js.map +1 -1
  126. package/dist/wasm/internal/hash.d.ts +9 -0
  127. package/dist/wasm/internal/hash.d.ts.map +1 -1
  128. package/dist/wasm/internal/hash.js +13 -1
  129. package/dist/wasm/internal/hash.js.map +1 -1
  130. package/dist/wasm/internal/hashState.d.ts +25 -0
  131. package/dist/wasm/internal/hashState.d.ts.map +1 -0
  132. package/dist/wasm/internal/hashState.js +105 -0
  133. package/dist/wasm/internal/hashState.js.map +1 -0
  134. package/dist/wasm/internal/hashes.d.ts +12 -1
  135. package/dist/wasm/internal/hashes.d.ts.map +1 -1
  136. package/dist/wasm/internal/hashes.js +18 -1
  137. package/dist/wasm/internal/hashes.js.map +1 -1
  138. package/dist/wasm/internal/hashes.wasm.d.ts +11 -3
  139. package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
  140. package/dist/wasm/internal/hashes.wasm.js +11 -3
  141. package/dist/wasm/internal/hashes.wasm.js.map +1 -1
  142. package/dist/wasm/internal/instantiate.d.ts +12 -0
  143. package/dist/wasm/internal/instantiate.d.ts.map +1 -1
  144. package/dist/wasm/internal/instantiate.js +23 -7
  145. package/dist/wasm/internal/instantiate.js.map +1 -1
  146. package/dist/wasm/internal/keystore.d.ts +10 -0
  147. package/dist/wasm/internal/keystore.d.ts.map +1 -0
  148. package/dist/wasm/internal/keystore.js +10 -0
  149. package/dist/wasm/internal/keystore.js.map +1 -0
  150. package/dist/wasm/internal/kzg.d.ts +17 -0
  151. package/dist/wasm/internal/kzg.d.ts.map +1 -0
  152. package/dist/wasm/internal/kzg.js +8 -0
  153. package/dist/wasm/internal/kzg.js.map +1 -0
  154. package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
  155. package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
  156. package/dist/wasm/internal/kzg.wasm.js +10 -0
  157. package/dist/wasm/internal/kzg.wasm.js.map +1 -0
  158. package/dist/wasm/internal/scrypt.d.ts +16 -0
  159. package/dist/wasm/internal/scrypt.d.ts.map +1 -0
  160. package/dist/wasm/internal/scrypt.js +78 -0
  161. package/dist/wasm/internal/scrypt.js.map +1 -0
  162. package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
  163. package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
  164. package/dist/wasm/internal/scrypt.wasm.js +14 -0
  165. package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
  166. package/dist/wasm/internal/secp256k1.d.ts +16 -0
  167. package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
  168. package/dist/wasm/internal/secp256k1.js +20 -0
  169. package/dist/wasm/internal/secp256k1.js.map +1 -0
  170. package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
  171. package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
  172. package/dist/wasm/internal/secp256k1.wasm.js +10 -0
  173. package/dist/wasm/internal/secp256k1.wasm.js.map +1 -0
  174. package/dist/webauthn/Authentication.d.ts +2 -0
  175. package/dist/webauthn/Authentication.d.ts.map +1 -1
  176. package/dist/webauthn/Authentication.js +2 -1
  177. package/dist/webauthn/Authentication.js.map +1 -1
  178. package/dist/webauthn/Registration.d.ts.map +1 -1
  179. package/dist/webauthn/Registration.js +2 -1
  180. package/dist/webauthn/Registration.js.map +1 -1
  181. package/dist/webauthn/Types.d.ts +27 -6
  182. package/dist/webauthn/Types.d.ts.map +1 -1
  183. package/dist/webauthn/internal/utils.d.ts.map +1 -1
  184. package/dist/webauthn/internal/utils.js +35 -9
  185. package/dist/webauthn/internal/utils.js.map +1 -1
  186. package/dist/zod/TypedData.d.ts +2 -2
  187. package/package.json +27 -2
  188. package/src/benchmarks/Ox.bench.ts +6 -0
  189. package/src/benchmarks/OxNode.bench.ts +8 -0
  190. package/src/benchmarks/OxV0.bench.ts +4 -0
  191. package/src/benchmarks/OxWasm.bench.ts +22 -0
  192. package/src/core/AbiFunction.ts +5 -5
  193. package/src/core/AbiParameters.ts +8 -20
  194. package/src/core/AesGcm.ts +86 -1
  195. package/src/core/Ed25519.ts +87 -1
  196. package/src/core/Engine.ts +1 -0
  197. package/src/core/Hash.ts +321 -1
  198. package/src/core/Hex.ts +1 -4
  199. package/src/core/Rlp.ts +172 -5
  200. package/src/core/Secp256k1.ts +92 -1
  201. package/src/core/WebAuthn.ts +579 -0
  202. package/src/core/WebAuthnP256.ts +47 -50
  203. package/src/core/_test/AbiFunction.test.ts +9 -0
  204. package/src/core/_test/AbiParameters.differential.fuzz.ts +156 -0
  205. package/src/core/_test/AbiParameters.encode.test.ts +22 -0
  206. package/src/core/_test/AbiParameters.fuzz.ts +4 -0
  207. package/src/core/_test/AesGcm.test.ts +76 -0
  208. package/src/core/_test/Block.test.ts +9 -2
  209. package/src/core/_test/Ed25519.test-d.ts +13 -1
  210. package/src/core/_test/Ed25519.test.ts +120 -1
  211. package/src/core/_test/Engine.test-d.ts +9 -0
  212. package/src/core/_test/Engine.test.ts +1 -1
  213. package/src/core/_test/Hash.test-d.ts +41 -0
  214. package/src/core/_test/Hash.test.ts +278 -1
  215. package/src/core/_test/Hash.vectors.test.ts +1 -2
  216. package/src/core/_test/Hex.test.ts +2 -0
  217. package/src/core/_test/Rlp.test-d.ts +18 -0
  218. package/src/core/_test/Rlp.test.ts +109 -0
  219. package/src/core/_test/Secp256k1.test-d.ts +14 -0
  220. package/src/core/_test/Secp256k1.test.ts +91 -1
  221. package/src/core/_test/Transaction.test.ts +11 -2
  222. package/src/core/_test/WebAuthn.test-d.ts +142 -0
  223. package/src/core/_test/WebAuthn.test.ts +766 -0
  224. package/src/core/_test/index.test.ts +1 -0
  225. package/src/core/internal/abiParameters.ts +466 -326
  226. package/src/core/internal/engine.ts +47 -2
  227. package/src/core/internal/hash.ts +53 -1
  228. package/src/index.ts +24 -14
  229. package/src/node/Engine.ts +1 -0
  230. package/src/node/Hash.ts +43 -6
  231. package/src/node/_test/Engine.test-d.ts +1 -0
  232. package/src/node/_test/Engine.test.ts +2 -0
  233. package/src/node/_test/Hash.test-d.ts +11 -0
  234. package/src/node/_test/Hash.test.ts +46 -0
  235. package/src/tempo/AuthorizationTempo.ts +6 -6
  236. package/src/tempo/KeyAuthorization.ts +3 -8
  237. package/src/tempo/SignatureEnvelope.ts +8 -8
  238. package/src/trusted-setups/Paths.ts +1 -0
  239. package/src/trusted-setups/README.md +39 -0
  240. package/src/trusted-setups/Setups.ts +46 -0
  241. package/src/trusted-setups/_test/Paths.test.ts +16 -0
  242. package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
  243. package/src/trusted-setups/_test/Setups.test.ts +43 -0
  244. package/src/trusted-setups/_test/index.test.ts +1 -0
  245. package/src/trusted-setups/index.ts +21 -0
  246. package/src/trusted-setups/internal/paths.ts +4 -1
  247. package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
  248. package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
  249. package/src/version.ts +1 -1
  250. package/src/wasm/Engine.ts +9 -6
  251. package/src/wasm/Hash.ts +10 -0
  252. package/src/wasm/Keystore.bench.ts +26 -0
  253. package/src/wasm/Keystore.ts +32 -14
  254. package/src/wasm/Kzg.bench.ts +63 -0
  255. package/src/wasm/Kzg.ts +518 -0
  256. package/src/wasm/Secp256k1.bench.ts +79 -0
  257. package/src/wasm/Secp256k1.ts +248 -0
  258. package/src/wasm/_test/Engine.test-d.ts +2 -0
  259. package/src/wasm/_test/Engine.test.ts +5 -0
  260. package/src/wasm/_test/Hash.browser.test.ts +31 -0
  261. package/src/wasm/_test/Hash.test-d.ts +17 -0
  262. package/src/wasm/_test/Hash.test.ts +252 -3
  263. package/src/wasm/_test/Keystore.browser.test.ts +31 -0
  264. package/src/wasm/_test/Keystore.fuzz.ts +50 -0
  265. package/src/wasm/_test/Keystore.test-d.ts +10 -1
  266. package/src/wasm/_test/Keystore.test.ts +289 -4
  267. package/src/wasm/_test/Kzg.test-d.ts +19 -0
  268. package/src/wasm/_test/Kzg.test.ts +220 -0
  269. package/src/wasm/_test/Runtime.test.ts +9 -0
  270. package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
  271. package/src/wasm/_test/Secp256k1.test.ts +354 -0
  272. package/src/wasm/index.ts +46 -1
  273. package/src/wasm/internal/blake3.ts +23 -2
  274. package/src/wasm/internal/blake3.wasm.ts +4 -1
  275. package/src/wasm/internal/hash.ts +27 -1
  276. package/src/wasm/internal/hashState.ts +154 -0
  277. package/src/wasm/internal/hashes.ts +56 -15
  278. package/src/wasm/internal/hashes.wasm.ts +15 -3
  279. package/src/wasm/internal/instantiate.ts +29 -9
  280. package/src/wasm/internal/keystore.ts +19 -0
  281. package/src/wasm/internal/kzg.ts +50 -0
  282. package/src/wasm/internal/kzg.wasm.ts +11 -0
  283. package/src/wasm/internal/scrypt.ts +131 -0
  284. package/src/wasm/internal/scrypt.wasm.ts +17 -0
  285. package/src/wasm/internal/secp256k1.ts +62 -0
  286. package/src/wasm/internal/secp256k1.wasm.ts +11 -0
  287. package/src/webauthn/Authentication.ts +4 -0
  288. package/src/webauthn/Registration.ts +2 -0
  289. package/src/webauthn/Types.ts +32 -2
  290. package/src/webauthn/_test/Authentication.test.ts +123 -1
  291. package/src/webauthn/_test/Registration.test.ts +26 -6
  292. package/src/webauthn/_test/WebAuthn.browser.test.ts +28 -0
  293. package/src/webauthn/internal/utils.ts +41 -8
  294. package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
  295. package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
  296. package/src/trusted-setups/internal/setups/mainnet.txt +0 -4163
@@ -0,0 +1,248 @@
1
+ import type * as Engine from '../core/Engine.js'
2
+ import * as Errors from '../core/Errors.js'
3
+ import * as internal from './internal/instantiate.js'
4
+ import * as secp256k1 from './internal/secp256k1.js'
5
+
6
+ export * from '../core/Secp256k1.js'
7
+ export { MemoryError } from './internal/instantiate.js'
8
+
9
+ const privateKeySize = 32
10
+ const publicKeySize = 65
11
+ const sharedSecretSize = 33
12
+ const compactSignatureSize = 64
13
+ const recoveredSignatureSize = 65
14
+ const maxRawMessageSize = 8192
15
+
16
+ /**
17
+ * Compiles WASM implementations of deterministic
18
+ * [`Secp256k1`](/api/Secp256k1) primitives, without installing them.
19
+ *
20
+ * Random key generation remains on Ox's host-backed default implementation.
21
+ *
22
+ * @example
23
+ * ```ts twoslash
24
+ * // @noErrors
25
+ * import { Engine } from 'ox'
26
+ * import { Secp256k1 } from 'ox/wasm'
27
+ *
28
+ * await Engine.install({ Secp256k1: Secp256k1.engine() })
29
+ *
30
+ * Secp256k1.getPublicKey({ privateKey: '0x...' })
31
+ * ```
32
+ *
33
+ * @returns The WASM implementation of the deterministic `Secp256k1` primitives.
34
+ */
35
+ export async function engine(): Promise<engine.ReturnType> {
36
+ const module = await secp256k1.load()
37
+
38
+ return {
39
+ getPublicKey(privateKey) {
40
+ assertLength(privateKey, privateKeySize, 'private key')
41
+ const size = privateKeySize + publicKeySize
42
+ module.reserve(size)
43
+ const privateKeyPtr = module.heapBase
44
+ const publicKeyPtr = privateKeyPtr + privateKeySize
45
+ try {
46
+ module.view().set(privateKey, privateKeyPtr)
47
+ if (
48
+ module.exports.secp256k1_get_public_key(
49
+ privateKeyPtr,
50
+ publicKeyPtr,
51
+ ) !== 1
52
+ )
53
+ throw new InvalidInputError({ operation: 'getPublicKey' })
54
+ return module.view().slice(publicKeyPtr, publicKeyPtr + publicKeySize)
55
+ } finally {
56
+ module.exports.zero(privateKeyPtr, size)
57
+ }
58
+ },
59
+ getSharedSecret(privateKey, publicKey) {
60
+ assertLength(privateKey, privateKeySize, 'private key')
61
+ assertPublicKeyLength(publicKey)
62
+ const size = privateKeySize + publicKey.length + sharedSecretSize
63
+ module.reserve(size)
64
+ const privateKeyPtr = module.heapBase
65
+ const publicKeyPtr = privateKeyPtr + privateKeySize
66
+ const sharedSecretPtr = publicKeyPtr + publicKey.length
67
+ try {
68
+ const memory = module.view()
69
+ memory.set(privateKey, privateKeyPtr)
70
+ memory.set(publicKey, publicKeyPtr)
71
+ if (
72
+ module.exports.secp256k1_get_shared_secret(
73
+ privateKeyPtr,
74
+ publicKeyPtr,
75
+ publicKey.length,
76
+ sharedSecretPtr,
77
+ ) !== 1
78
+ )
79
+ throw new InvalidInputError({ operation: 'getSharedSecret' })
80
+ return module
81
+ .view()
82
+ .slice(sharedSecretPtr, sharedSecretPtr + sharedSecretSize)
83
+ } finally {
84
+ module.exports.zero(privateKeyPtr, size)
85
+ }
86
+ },
87
+ recoverPublicKey(signature, payload) {
88
+ assertLength(signature, recoveredSignatureSize, 'signature')
89
+ assertRawMessageLength(payload)
90
+ const size = recoveredSignatureSize + payload.length + publicKeySize
91
+ module.reserve(size)
92
+ const signaturePtr = module.heapBase
93
+ const payloadPtr = signaturePtr + recoveredSignatureSize
94
+ const publicKeyPtr = payloadPtr + payload.length
95
+ try {
96
+ const memory = module.view()
97
+ memory.set(signature, signaturePtr)
98
+ memory.set(payload, payloadPtr)
99
+ if (
100
+ module.exports.secp256k1_recover_public_key(
101
+ signaturePtr,
102
+ payloadPtr,
103
+ payload.length,
104
+ publicKeyPtr,
105
+ ) !== 1
106
+ )
107
+ throw new InvalidInputError({ operation: 'recoverPublicKey' })
108
+ return module.view().slice(publicKeyPtr, publicKeyPtr + publicKeySize)
109
+ } finally {
110
+ module.exports.zero(signaturePtr, size)
111
+ }
112
+ },
113
+ sign(payload, privateKey, options) {
114
+ assertLength(privateKey, privateKeySize, 'private key')
115
+ if (!options.prehash) assertRawMessageLength(payload)
116
+ const entropy =
117
+ options.extraEntropy === true
118
+ ? crypto.getRandomValues(new Uint8Array(privateKeySize))
119
+ : options.extraEntropy === false
120
+ ? undefined
121
+ : options.extraEntropy
122
+ const entropySize = entropy?.length ?? 0
123
+ const size =
124
+ payload.length + privateKeySize + entropySize + recoveredSignatureSize
125
+ module.reserve(size)
126
+ const payloadPtr = module.heapBase
127
+ const privateKeyPtr = payloadPtr + payload.length
128
+ const entropyPtr = entropy ? privateKeyPtr + privateKeySize : 0
129
+ const signaturePtr = privateKeyPtr + privateKeySize + entropySize
130
+ try {
131
+ const memory = module.view()
132
+ memory.set(payload, payloadPtr)
133
+ memory.set(privateKey, privateKeyPtr)
134
+ if (entropy) memory.set(entropy, entropyPtr)
135
+ if (
136
+ module.exports.secp256k1_sign(
137
+ payloadPtr,
138
+ payload.length,
139
+ privateKeyPtr,
140
+ entropyPtr,
141
+ entropySize,
142
+ Number(options.prehash),
143
+ signaturePtr,
144
+ ) !== 1
145
+ )
146
+ throw new InvalidInputError({ operation: 'sign' })
147
+ return module
148
+ .view()
149
+ .slice(signaturePtr, signaturePtr + recoveredSignatureSize)
150
+ } finally {
151
+ module.exports.zero(payloadPtr, size)
152
+ }
153
+ },
154
+ verify(signature, payload, publicKey, options) {
155
+ assertLength(signature, compactSignatureSize, 'signature')
156
+ assertPublicKeyLength(publicKey)
157
+ if (!options.prehash && payload.length > maxRawMessageSize) return false
158
+ const size = compactSignatureSize + payload.length + publicKey.length
159
+ module.reserve(size)
160
+ const signaturePtr = module.heapBase
161
+ const payloadPtr = signaturePtr + compactSignatureSize
162
+ const publicKeyPtr = payloadPtr + payload.length
163
+ try {
164
+ const memory = module.view()
165
+ memory.set(signature, signaturePtr)
166
+ memory.set(payload, payloadPtr)
167
+ memory.set(publicKey, publicKeyPtr)
168
+ return (
169
+ module.exports.secp256k1_verify(
170
+ signaturePtr,
171
+ payloadPtr,
172
+ payload.length,
173
+ publicKeyPtr,
174
+ publicKey.length,
175
+ Number(options.prehash),
176
+ ) === 1
177
+ )
178
+ } finally {
179
+ module.exports.zero(signaturePtr, size)
180
+ }
181
+ },
182
+ }
183
+ }
184
+
185
+ export declare namespace engine {
186
+ /** Every `Secp256k1` primitive this module implements. */
187
+ type ReturnType = {
188
+ [key in
189
+ | 'getPublicKey'
190
+ | 'getSharedSecret'
191
+ | 'recoverPublicKey'
192
+ | 'sign'
193
+ | 'verify']-?: NonNullable<Engine.Ecdsa[key]>
194
+ }
195
+
196
+ type ErrorType =
197
+ | InvalidInputError
198
+ | internal.MemoryError
199
+ | Errors.GlobalErrorType
200
+ }
201
+
202
+ function assertLength(value: Uint8Array, length: number, name: string): void {
203
+ if (value.length !== length)
204
+ throw new RangeError(
205
+ `Secp256k1 ${name} must be ${length} bytes, got ${value.length}`,
206
+ )
207
+ }
208
+
209
+ function assertPublicKeyLength(publicKey: Uint8Array): void {
210
+ if (
211
+ publicKey.length !== sharedSecretSize &&
212
+ publicKey.length !== publicKeySize
213
+ )
214
+ throw new RangeError(
215
+ `Secp256k1 public key must be 33 or 65 bytes, got ${publicKey.length}`,
216
+ )
217
+ }
218
+
219
+ function assertRawMessageLength(payload: Uint8Array): void {
220
+ if (payload.length > maxRawMessageSize)
221
+ throw new RangeError(
222
+ `Secp256k1 unhashed payload must not exceed ${maxRawMessageSize} bytes, got ${payload.length}`,
223
+ )
224
+ }
225
+
226
+ /**
227
+ * Thrown when libsecp256k1 rejects a key or recovered signature.
228
+ *
229
+ * @example
230
+ * ```ts twoslash
231
+ * import { Secp256k1 } from 'ox/wasm'
232
+ *
233
+ * try {
234
+ * const engine = await Secp256k1.engine()
235
+ * engine.getPublicKey(new Uint8Array(32))
236
+ * } catch (error) {
237
+ * if (error instanceof Secp256k1.InvalidInputError)
238
+ * console.error(error.message)
239
+ * }
240
+ * ```
241
+ */
242
+ export class InvalidInputError extends Errors.BaseError {
243
+ override readonly name = 'Secp256k1.InvalidInputError'
244
+
245
+ constructor({ operation }: { operation: string }) {
246
+ super(`WASM secp256k1 ${operation} received invalid input.`)
247
+ }
248
+ }
@@ -10,9 +10,11 @@ test('install and engine expose the same precise engine', () => {
10
10
  expectTypeOf<Installed['Hash']['sha256']>().toEqualTypeOf<
11
11
  (input: Uint8Array) => Uint8Array
12
12
  >()
13
+ expectTypeOf<Installed['Keystore']>().not.toHaveProperty('scrypt')
13
14
  })
14
15
 
15
16
  test('the WASM Engine namespace exposes synchronous core operations', () => {
17
+ expectTypeOf<Engine.HashState>().toEqualTypeOf<CoreEngine.HashState>()
16
18
  expectTypeOf(Engine.get).toEqualTypeOf(CoreEngine.get)
17
19
  expectTypeOf(Engine.InvalidSlotValueError).toEqualTypeOf(
18
20
  CoreEngine.InvalidSlotValueError,
@@ -75,6 +75,11 @@ describe('install', () => {
75
75
  ],
76
76
  "Hash": [
77
77
  "blake3",
78
+ "createBlake3",
79
+ "createHmacSha256",
80
+ "createKeccak256",
81
+ "createRipemd160",
82
+ "createSha256",
78
83
  "hmacSha256",
79
84
  "keccak256",
80
85
  "ripemd160",
@@ -27,6 +27,28 @@ describe('engine', () => {
27
27
  }
28
28
  })
29
29
 
30
+ test('behavior: incremental states clone and cross block boundaries', async () => {
31
+ const engine = await WasmHash.engine()
32
+ const prefix = new Uint8Array(137).fill(1)
33
+ const left = new Uint8Array(1025).fill(2)
34
+ const right = new Uint8Array(65).fill(3)
35
+ const state = engine.createBlake3()
36
+ state.update(prefix.subarray(0, 1))
37
+ state.update(prefix.subarray(1, 136))
38
+ state.update(prefix.subarray(136))
39
+ const clone = state.clone()
40
+ state.update(left)
41
+ clone.update(right)
42
+
43
+ const output = new Uint8Array(32)
44
+ state.digestInto(output)
45
+ expect(output).toEqual(blake3(new Uint8Array([...prefix, ...left])))
46
+
47
+ const clonedOutput = new Uint8Array(32)
48
+ clone.digestInto(clonedOutput)
49
+ expect(clonedOutput).toEqual(blake3(new Uint8Array([...prefix, ...right])))
50
+ })
51
+
30
52
  test('behavior: grows memory in a real browser, then hashes a small input', async () => {
31
53
  const engine = await WasmHash.engine()
32
54
 
@@ -44,9 +66,18 @@ describe('engine', () => {
44
66
  test('behavior: ox uses the WASM implementation once installed', async () => {
45
67
  await Engine.install({ Hash: WasmHash.engine() })
46
68
  try {
69
+ expect(Hash.blake3('0xdeadbeef')).toBe(
70
+ '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd',
71
+ )
47
72
  expect(Hash.keccak256('0xdeadbeef')).toBe(
48
73
  '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
49
74
  )
75
+ const state = Hash.createKeccak256()
76
+ state.update('0xdead')
77
+ state.update('0xbeef')
78
+ expect(state.digest()).toBe(
79
+ '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
80
+ )
50
81
  } finally {
51
82
  Engine.reset()
52
83
  }
@@ -21,6 +21,21 @@ test('every primitive is present, so callers need no assertion', () => {
21
21
  expectTypeOf<Slot['hmacSha256']>().toEqualTypeOf<
22
22
  (key: Uint8Array, message: Uint8Array) => Uint8Array
23
23
  >()
24
+ expectTypeOf<Slot['createBlake3']>().toEqualTypeOf<
25
+ () => CoreEngine.HashState
26
+ >()
27
+ expectTypeOf<Slot['createHmacSha256']>().toEqualTypeOf<
28
+ (key: Uint8Array) => CoreEngine.HashState
29
+ >()
30
+ expectTypeOf<Slot['createKeccak256']>().toEqualTypeOf<
31
+ () => CoreEngine.HashState
32
+ >()
33
+ expectTypeOf<Slot['createRipemd160']>().toEqualTypeOf<
34
+ () => CoreEngine.HashState
35
+ >()
36
+ expectTypeOf<Slot['createSha256']>().toEqualTypeOf<
37
+ () => CoreEngine.HashState
38
+ >()
24
39
  })
25
40
 
26
41
  test('the result is the raw slot, so `Engine.install` accepts it', () => {
@@ -28,6 +43,8 @@ test('the result is the raw slot, so `Engine.install` accepts it', () => {
28
43
  })
29
44
 
30
45
  test('the WASM namespace exposes the public Hash API', () => {
46
+ expectTypeOf(WasmHash.blake3).toEqualTypeOf(CoreHash.blake3)
47
+ expectTypeOf(WasmHash.createBlake3).toEqualTypeOf(CoreHash.createBlake3)
31
48
  expectTypeOf(WasmHash.sha256).toEqualTypeOf(CoreHash.sha256)
32
49
  expectTypeOf<typeof WasmHash>().not.toHaveProperty('create')
33
50
  })
@@ -8,8 +8,14 @@ import { Hash as WasmHash } from 'ox/wasm'
8
8
  import { beforeAll, describe, expect, test } from 'vp/test'
9
9
  import { wasmBase64 as blake3WasmBase64 } from '../internal/blake3.wasm.js'
10
10
  import * as blake3_internal from '../internal/blake3.js'
11
- import { hmacSha256ScratchSize, wasmBase64 } from '../internal/hashes.wasm.js'
11
+ import { blake3StateSize } from '../internal/blake3.wasm.js'
12
+ import {
13
+ hmacSha256ScratchSize,
14
+ hmacSha256StateSize,
15
+ wasmBase64,
16
+ } from '../internal/hashes.wasm.js'
12
17
  import * as hash from '../internal/hash.js'
18
+ import * as hashes_internal from '../internal/hashes.js'
13
19
  import * as internal from '../internal/instantiate.js'
14
20
 
15
21
  let engine: WasmHash.engine.ReturnType
@@ -23,6 +29,11 @@ describe('engine', () => {
23
29
  expect(Object.keys(await WasmHash.engine())).toMatchInlineSnapshot(`
24
30
  [
25
31
  "blake3",
32
+ "createBlake3",
33
+ "createHmacSha256",
34
+ "createKeccak256",
35
+ "createRipemd160",
36
+ "createSha256",
26
37
  "hmacSha256",
27
38
  "keccak256",
28
39
  "ripemd160",
@@ -58,6 +69,79 @@ function input(size: number) {
58
69
  return new Uint8Array(size).map((_, index) => (index * 37 + 11) & 0xff)
59
70
  }
60
71
 
72
+ type HashState = ReturnType<WasmHash.engine.ReturnType['createSha256']>
73
+
74
+ type IncrementalCase = {
75
+ create: () => HashState
76
+ digestSize: number
77
+ name: string
78
+ oneShot: (input: Uint8Array) => Uint8Array
79
+ wasmOneShot: (input: Uint8Array) => Uint8Array
80
+ }
81
+
82
+ function concat(...values: readonly Uint8Array[]) {
83
+ const output = new Uint8Array(
84
+ values.reduce((length, value) => length + value.length, 0),
85
+ )
86
+ let offset = 0
87
+ for (const value of values) {
88
+ output.set(value, offset)
89
+ offset += value.length
90
+ }
91
+ return output
92
+ }
93
+
94
+ function align8(value: number) {
95
+ return Math.ceil(value / 8) * 8
96
+ }
97
+
98
+ function digest(state: HashState, size: number) {
99
+ const output = new Uint8Array(size)
100
+ state.digestInto(output)
101
+ return output
102
+ }
103
+
104
+ function incrementalCases(): readonly IncrementalCase[] {
105
+ const key = input(100)
106
+ return [
107
+ {
108
+ create: () => engine.createBlake3(),
109
+ digestSize: 32,
110
+ name: 'blake3',
111
+ oneShot: blake3,
112
+ wasmOneShot: (value) => engine.blake3(value),
113
+ },
114
+ {
115
+ create: () => engine.createHmacSha256(key),
116
+ digestSize: 32,
117
+ name: 'hmacSha256',
118
+ oneShot: (value) => hmac(sha256, key, value),
119
+ wasmOneShot: (value) => engine.hmacSha256(key, value),
120
+ },
121
+ {
122
+ create: () => engine.createKeccak256(),
123
+ digestSize: 32,
124
+ name: 'keccak256',
125
+ oneShot: keccak_256,
126
+ wasmOneShot: (value) => engine.keccak256(value),
127
+ },
128
+ {
129
+ create: () => engine.createRipemd160(),
130
+ digestSize: 20,
131
+ name: 'ripemd160',
132
+ oneShot: ripemd160,
133
+ wasmOneShot: (value) => engine.ripemd160(value),
134
+ },
135
+ {
136
+ create: () => engine.createSha256(),
137
+ digestSize: 32,
138
+ name: 'sha256',
139
+ oneShot: sha256,
140
+ wasmOneShot: (value) => engine.sha256(value),
141
+ },
142
+ ]
143
+ }
144
+
61
145
  describe('Hash', () => {
62
146
  test.each(sizes)('blake3 matches the default at %i bytes', (size) => {
63
147
  expect(engine.blake3(input(size))).toEqual(blake3(input(size)))
@@ -86,6 +170,97 @@ describe('Hash', () => {
86
170
  },
87
171
  )
88
172
 
173
+ test.each(incrementalCases())(
174
+ '$name matches arbitrary chunk boundaries',
175
+ ({ create, digestSize, oneShot }) => {
176
+ const message = input(4097)
177
+ const snapshot = message.slice()
178
+ const chunks = [0, 1, 7, 55, 64, 129, 1023, 136, 2]
179
+ const state = create()
180
+ let offset = 0
181
+ let index = 0
182
+ while (offset < message.length) {
183
+ const length = Math.min(
184
+ chunks[index++ % chunks.length]!,
185
+ message.length - offset,
186
+ )
187
+ state.update(message.subarray(offset, offset + length))
188
+ offset += length
189
+ }
190
+
191
+ expect(digest(state, digestSize)).toEqual(oneShot(message))
192
+ expect(message).toEqual(snapshot)
193
+ },
194
+ )
195
+
196
+ test.each(incrementalCases())(
197
+ '$name clones an independent prefix',
198
+ ({ create, digestSize, oneShot }) => {
199
+ const prefix = input(137)
200
+ const left = input(63)
201
+ const right = input(1025)
202
+ const state = create()
203
+ state.update(prefix)
204
+ const clone = state.clone()
205
+ state.update(left)
206
+ clone.update(right)
207
+
208
+ expect(digest(state, digestSize)).toEqual(oneShot(concat(prefix, left)))
209
+ expect(digest(clone, digestSize)).toEqual(oneShot(concat(prefix, right)))
210
+ },
211
+ )
212
+
213
+ test.each(incrementalCases())(
214
+ '$name keeps interleaved states independent',
215
+ ({ create, digestSize, oneShot }) => {
216
+ const a = input(2049)
217
+ const b = input(271)
218
+ const stateA = create()
219
+ const stateB = create()
220
+
221
+ stateA.update(a.subarray(0, 55))
222
+ stateB.update(b.subarray(0, 136))
223
+ stateA.update(a.subarray(55, 1024))
224
+ stateB.update(b.subarray(136))
225
+ stateA.update(a.subarray(1024))
226
+
227
+ expect(digest(stateB, digestSize)).toEqual(oneShot(b))
228
+ expect(digest(stateA, digestSize)).toEqual(oneShot(a))
229
+ },
230
+ )
231
+
232
+ test.each(incrementalCases())(
233
+ '$name survives one-shot calls through the shared module',
234
+ ({ create, digestSize, oneShot, wasmOneShot }) => {
235
+ const message = input(1025)
236
+ const state = create()
237
+ state.update(message.subarray(0, 136))
238
+
239
+ expect(wasmOneShot(input(2049))).toEqual(oneShot(input(2049)))
240
+
241
+ state.update(message.subarray(136))
242
+ expect(digest(state, digestSize)).toEqual(oneShot(message))
243
+ },
244
+ )
245
+
246
+ test.each(incrementalCases())(
247
+ '$name consumes and clears its lifecycle',
248
+ ({ create, digestSize, oneShot }) => {
249
+ const message = input(72)
250
+ const state = create()
251
+ state.update(message)
252
+ const output = new Uint8Array(digestSize + 7).fill(0xa5)
253
+ state.digestInto(output)
254
+
255
+ expect(output.subarray(0, digestSize)).toEqual(oneShot(message))
256
+ expect(output.subarray(digestSize)).toEqual(new Uint8Array(7).fill(0xa5))
257
+ expect(() => state.update(message)).toThrow('destroyed')
258
+ expect(() => state.clone()).toThrow('destroyed')
259
+ expect(() => state.digestInto(output)).toThrow('destroyed')
260
+ expect(() => state.destroy()).not.toThrow()
261
+ },
262
+ )
263
+
89
264
  test('behavior: known vectors', () => {
90
265
  expect(Bytes.toHex(engine.blake3(new Uint8Array(0)))).toBe(
91
266
  '0xaf1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262',
@@ -134,6 +309,34 @@ describe('Hash', () => {
134
309
  )
135
310
  })
136
311
 
312
+ test('behavior: incremental blake3 clears every WASM workspace', async () => {
313
+ const module = await internal.instantiate<
314
+ blake3_internal.Exports & blake3_internal.StateExports
315
+ >(blake3WasmBase64)
316
+ const initEnd = align8(module.heapBase) + blake3StateSize
317
+ module.view().fill(0xa5, module.heapBase, initEnd)
318
+ const state = blake3_internal.create(module)
319
+ expect(module.view().slice(module.heapBase, initEnd)).toEqual(
320
+ new Uint8Array(initEnd - module.heapBase),
321
+ )
322
+
323
+ const message = input(1025)
324
+ const updateEnd = align8(module.heapBase + message.length) + blake3StateSize
325
+ state.update(message)
326
+ expect(module.view().slice(module.heapBase, updateEnd)).toEqual(
327
+ new Uint8Array(updateEnd - module.heapBase),
328
+ )
329
+
330
+ const output = new Uint8Array(32)
331
+ const finalizeEnd =
332
+ align8(module.heapBase) + blake3StateSize + output.length
333
+ state.digestInto(output)
334
+ expect(output).toEqual(blake3(message))
335
+ expect(module.view().slice(module.heapBase, finalizeEnd)).toEqual(
336
+ new Uint8Array(finalizeEnd - module.heapBase),
337
+ )
338
+ })
339
+
137
340
  test('behavior: blake3 clears its complete region after a late trap', async () => {
138
341
  const module =
139
342
  await internal.instantiate<blake3_internal.Exports>(blake3WasmBase64)
@@ -215,6 +418,35 @@ describe('Hash', () => {
215
418
  )
216
419
  })
217
420
 
421
+ test('behavior: incremental hmacSha256 clears every WASM workspace', async () => {
422
+ const module =
423
+ await internal.instantiate<hashes_internal.Exports>(wasmBase64)
424
+ const key = input(100)
425
+ const initEnd = align8(module.heapBase + key.length) + hmacSha256StateSize
426
+ module.view().fill(0xa5, module.heapBase, initEnd)
427
+ const state = hash.createHmacSha256(module, key)
428
+ expect(module.view().slice(module.heapBase, initEnd)).toEqual(
429
+ new Uint8Array(initEnd - module.heapBase),
430
+ )
431
+
432
+ const message = input(137)
433
+ const updateEnd =
434
+ align8(module.heapBase + message.length) + hmacSha256StateSize
435
+ state.update(message)
436
+ expect(module.view().slice(module.heapBase, updateEnd)).toEqual(
437
+ new Uint8Array(updateEnd - module.heapBase),
438
+ )
439
+
440
+ const output = new Uint8Array(32)
441
+ const finalizeEnd =
442
+ align8(module.heapBase) + hmacSha256StateSize + output.length
443
+ state.digestInto(output)
444
+ expect(output).toEqual(hmac(sha256, key, message))
445
+ expect(module.view().slice(module.heapBase, finalizeEnd)).toEqual(
446
+ new Uint8Array(finalizeEnd - module.heapBase),
447
+ )
448
+ })
449
+
218
450
  test('behavior: hmacSha256 clears its complete region after a trap', async () => {
219
451
  const module = await internal.instantiate<hash.Exports>(wasmBase64)
220
452
  const key = input(120)
@@ -264,6 +496,23 @@ describe('Hash', () => {
264
496
  )
265
497
  })
266
498
 
499
+ test('behavior: streams a large total input through bounded memory', async () => {
500
+ const module =
501
+ await internal.instantiate<hashes_internal.Exports>(wasmBase64)
502
+ const state = hashes_internal.create(module, 'sha256')
503
+ const expected = sha256.create()
504
+ const chunk = input(64 * 1024)
505
+ const memorySize = module.view().length
506
+
507
+ for (let index = 0; index < 1024; index++) {
508
+ state.update(chunk)
509
+ expected.update(chunk)
510
+ }
511
+
512
+ expect(module.view().length).toBe(memorySize)
513
+ expect(digest(state, 32)).toEqual(expected.digest())
514
+ }, 60_000)
515
+
267
516
  test('behavior: consecutive calls do not corrupt each other', () => {
268
517
  const a = input(200)
269
518
  const b = input(7)
@@ -292,8 +541,8 @@ describe('Engine.install', () => {
292
541
  test('behavior: ox uses the WASM implementation once installed', async () => {
293
542
  await Engine.install({ Hash: engine })
294
543
  try {
295
- expect(Engine.get().Hash?.blake3?.(Bytes.from('0xdeadbeef'))).toEqual(
296
- blake3(Bytes.from('0xdeadbeef')),
544
+ expect(Hash.blake3('0xdeadbeef')).toBe(
545
+ '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd',
297
546
  )
298
547
  expect(Hash.keccak256('0xdeadbeef')).toBe(
299
548
  '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',