ox 1.1.0 → 1.2.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 (284) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/core/Engine.d.ts +43 -11
  3. package/dist/core/Engine.d.ts.map +1 -1
  4. package/dist/core/Engine.js +40 -10
  5. package/dist/core/Engine.js.map +1 -1
  6. package/dist/core/Hash.d.ts +36 -0
  7. package/dist/core/Hash.d.ts.map +1 -1
  8. package/dist/core/Hash.js +35 -0
  9. package/dist/core/Hash.js.map +1 -1
  10. package/dist/core/internal/engine.d.ts +8 -8
  11. package/dist/core/internal/engine.d.ts.map +1 -1
  12. package/dist/index.d.ts +5 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +5 -2
  15. package/dist/index.js.map +1 -1
  16. package/dist/node/Ed25519.d.ts +33 -0
  17. package/dist/node/Ed25519.d.ts.map +1 -0
  18. package/dist/node/Ed25519.js +67 -0
  19. package/dist/node/Ed25519.js.map +1 -0
  20. package/dist/node/Engine.d.ts +29 -16
  21. package/dist/node/Engine.d.ts.map +1 -1
  22. package/dist/node/Engine.js +38 -14
  23. package/dist/node/Engine.js.map +1 -1
  24. package/dist/node/Hash.d.ts +17 -19
  25. package/dist/node/Hash.d.ts.map +1 -1
  26. package/dist/node/Hash.js +17 -19
  27. package/dist/node/Hash.js.map +1 -1
  28. package/dist/node/Keystore.d.ts +36 -0
  29. package/dist/node/Keystore.d.ts.map +1 -0
  30. package/dist/node/Keystore.js +72 -0
  31. package/dist/node/Keystore.js.map +1 -0
  32. package/dist/node/Mnemonic.d.ts +35 -0
  33. package/dist/node/Mnemonic.d.ts.map +1 -0
  34. package/dist/node/Mnemonic.js +46 -0
  35. package/dist/node/Mnemonic.js.map +1 -0
  36. package/dist/node/P256.d.ts +32 -0
  37. package/dist/node/P256.d.ts.map +1 -0
  38. package/dist/node/P256.js +34 -0
  39. package/dist/node/P256.js.map +1 -0
  40. package/dist/node/X25519.d.ts +31 -0
  41. package/dist/node/X25519.d.ts.map +1 -0
  42. package/dist/node/X25519.js +71 -0
  43. package/dist/node/X25519.js.map +1 -0
  44. package/dist/node/index.d.ts +95 -8
  45. package/dist/node/index.d.ts.map +1 -1
  46. package/dist/node/index.js +95 -8
  47. package/dist/node/index.js.map +1 -1
  48. package/dist/trusted-setups/Paths.d.ts +1 -0
  49. package/dist/trusted-setups/Paths.d.ts.map +1 -1
  50. package/dist/trusted-setups/Paths.js +1 -0
  51. package/dist/trusted-setups/Paths.js.map +1 -1
  52. package/dist/trusted-setups/Setups.d.ts +35 -0
  53. package/dist/trusted-setups/Setups.d.ts.map +1 -0
  54. package/dist/trusted-setups/Setups.js +29 -0
  55. package/dist/trusted-setups/Setups.js.map +1 -0
  56. package/dist/trusted-setups/index.d.ts +20 -0
  57. package/dist/trusted-setups/index.d.ts.map +1 -1
  58. package/dist/trusted-setups/index.js +20 -0
  59. package/dist/trusted-setups/index.js.map +1 -1
  60. package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
  61. package/dist/trusted-setups/internal/paths.js +1 -1
  62. package/dist/trusted-setups/internal/paths.js.map +1 -1
  63. package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
  64. package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
  65. package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
  66. package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
  67. package/dist/wasm/Ed25519.d.ts +35 -0
  68. package/dist/wasm/Ed25519.d.ts.map +1 -0
  69. package/dist/wasm/Ed25519.js +88 -0
  70. package/dist/wasm/Ed25519.js.map +1 -0
  71. package/dist/wasm/Engine.d.ts +36 -26
  72. package/dist/wasm/Engine.d.ts.map +1 -1
  73. package/dist/wasm/Engine.js +44 -25
  74. package/dist/wasm/Engine.js.map +1 -1
  75. package/dist/wasm/Hash.d.ts +13 -19
  76. package/dist/wasm/Hash.d.ts.map +1 -1
  77. package/dist/wasm/Hash.js +21 -27
  78. package/dist/wasm/Hash.js.map +1 -1
  79. package/dist/wasm/Keystore.d.ts +51 -0
  80. package/dist/wasm/Keystore.d.ts.map +1 -0
  81. package/dist/wasm/Keystore.js +51 -0
  82. package/dist/wasm/Keystore.js.map +1 -0
  83. package/dist/wasm/Kzg.d.ts +112 -0
  84. package/dist/wasm/Kzg.d.ts.map +1 -0
  85. package/dist/wasm/Kzg.js +333 -0
  86. package/dist/wasm/Kzg.js.map +1 -0
  87. package/dist/wasm/Mnemonic.d.ts +33 -0
  88. package/dist/wasm/Mnemonic.d.ts.map +1 -0
  89. package/dist/wasm/Mnemonic.js +30 -0
  90. package/dist/wasm/Mnemonic.js.map +1 -0
  91. package/dist/wasm/Secp256k1.d.ts +55 -0
  92. package/dist/wasm/Secp256k1.d.ts.map +1 -0
  93. package/dist/wasm/Secp256k1.js +181 -0
  94. package/dist/wasm/Secp256k1.js.map +1 -0
  95. package/dist/wasm/X25519.d.ts +33 -0
  96. package/dist/wasm/X25519.d.ts.map +1 -0
  97. package/dist/wasm/X25519.js +53 -0
  98. package/dist/wasm/X25519.js.map +1 -0
  99. package/dist/wasm/index.d.ts +124 -12
  100. package/dist/wasm/index.d.ts.map +1 -1
  101. package/dist/wasm/index.js +124 -12
  102. package/dist/wasm/index.js.map +1 -1
  103. package/dist/wasm/internal/blake3.d.ts +11 -0
  104. package/dist/wasm/internal/blake3.d.ts.map +1 -0
  105. package/dist/wasm/internal/blake3.js +31 -0
  106. package/dist/wasm/internal/blake3.js.map +1 -0
  107. package/dist/wasm/internal/blake3.wasm.d.ts +3 -0
  108. package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -0
  109. package/dist/wasm/internal/blake3.wasm.js +10 -0
  110. package/dist/wasm/internal/blake3.wasm.js.map +1 -0
  111. package/dist/wasm/internal/crypto25519.d.ts +16 -0
  112. package/dist/wasm/internal/crypto25519.d.ts.map +1 -0
  113. package/dist/wasm/internal/crypto25519.js +5 -0
  114. package/dist/wasm/internal/crypto25519.js.map +1 -0
  115. package/dist/wasm/internal/crypto25519.wasm.d.ts +3 -0
  116. package/dist/wasm/internal/crypto25519.wasm.d.ts.map +1 -0
  117. package/dist/wasm/internal/crypto25519.wasm.js +10 -0
  118. package/dist/wasm/internal/crypto25519.wasm.js.map +1 -0
  119. package/dist/wasm/internal/ed25519.d.ts +4 -0
  120. package/dist/wasm/internal/ed25519.d.ts.map +1 -0
  121. package/dist/wasm/internal/ed25519.js +21 -0
  122. package/dist/wasm/internal/ed25519.js.map +1 -0
  123. package/dist/wasm/internal/hashes.d.ts +12 -0
  124. package/dist/wasm/internal/hashes.d.ts.map +1 -0
  125. package/dist/wasm/internal/hashes.js +8 -0
  126. package/dist/wasm/internal/hashes.js.map +1 -0
  127. package/dist/wasm/internal/hashes.wasm.d.ts +4 -2
  128. package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
  129. package/dist/wasm/internal/hashes.wasm.js +4 -2
  130. package/dist/wasm/internal/hashes.wasm.js.map +1 -1
  131. package/dist/wasm/internal/instantiate.d.ts +12 -0
  132. package/dist/wasm/internal/instantiate.d.ts.map +1 -1
  133. package/dist/wasm/internal/instantiate.js +23 -7
  134. package/dist/wasm/internal/instantiate.js.map +1 -1
  135. package/dist/wasm/internal/keystore.d.ts +10 -0
  136. package/dist/wasm/internal/keystore.d.ts.map +1 -0
  137. package/dist/wasm/internal/keystore.js +10 -0
  138. package/dist/wasm/internal/keystore.js.map +1 -0
  139. package/dist/wasm/internal/kzg.d.ts +17 -0
  140. package/dist/wasm/internal/kzg.d.ts.map +1 -0
  141. package/dist/wasm/internal/kzg.js +8 -0
  142. package/dist/wasm/internal/kzg.js.map +1 -0
  143. package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
  144. package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
  145. package/dist/wasm/internal/kzg.wasm.js +10 -0
  146. package/dist/wasm/internal/kzg.wasm.js.map +1 -0
  147. package/dist/wasm/internal/mnemonic.d.ts +4 -0
  148. package/dist/wasm/internal/mnemonic.d.ts.map +1 -0
  149. package/dist/wasm/internal/mnemonic.js +45 -0
  150. package/dist/wasm/internal/mnemonic.js.map +1 -0
  151. package/dist/wasm/internal/pbkdf2.d.ts +12 -0
  152. package/dist/wasm/internal/pbkdf2.d.ts.map +1 -0
  153. package/dist/wasm/internal/pbkdf2.js +51 -0
  154. package/dist/wasm/internal/pbkdf2.js.map +1 -0
  155. package/dist/wasm/internal/scrypt.d.ts +16 -0
  156. package/dist/wasm/internal/scrypt.d.ts.map +1 -0
  157. package/dist/wasm/internal/scrypt.js +78 -0
  158. package/dist/wasm/internal/scrypt.js.map +1 -0
  159. package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
  160. package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
  161. package/dist/wasm/internal/scrypt.wasm.js +14 -0
  162. package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
  163. package/dist/wasm/internal/secp256k1.d.ts +16 -0
  164. package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
  165. package/dist/wasm/internal/secp256k1.js +20 -0
  166. package/dist/wasm/internal/secp256k1.js.map +1 -0
  167. package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
  168. package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
  169. package/dist/wasm/internal/secp256k1.wasm.js +10 -0
  170. package/dist/wasm/internal/secp256k1.wasm.js.map +1 -0
  171. package/dist/wasm/internal/x25519.d.ts +4 -0
  172. package/dist/wasm/internal/x25519.d.ts.map +1 -0
  173. package/dist/wasm/internal/x25519.js +21 -0
  174. package/dist/wasm/internal/x25519.js.map +1 -0
  175. package/package.json +64 -2
  176. package/src/core/Engine.ts +84 -11
  177. package/src/core/Hash.ts +59 -0
  178. package/src/core/_test/Block.test.ts +9 -2
  179. package/src/core/_test/Engine.test-d.ts +56 -0
  180. package/src/core/_test/Engine.test.ts +95 -0
  181. package/src/core/_test/Hash.test-d.ts +18 -0
  182. package/src/core/_test/Hash.test.ts +100 -1
  183. package/src/core/_test/Hash.vectors.test.ts +7 -0
  184. package/src/core/_test/Transaction.test.ts +11 -2
  185. package/src/core/internal/engine.ts +8 -8
  186. package/src/index.ts +5 -2
  187. package/src/node/Ed25519.ts +87 -0
  188. package/src/node/Engine.ts +79 -23
  189. package/src/node/Hash.ts +25 -30
  190. package/src/node/Keystore.ts +103 -0
  191. package/src/node/Mnemonic.ts +68 -0
  192. package/src/node/P256.ts +48 -0
  193. package/src/node/X25519.ts +93 -0
  194. package/src/node/_test/Curves.fuzz.ts +76 -0
  195. package/src/node/_test/Ed25519.test-d.ts +35 -0
  196. package/src/node/_test/Ed25519.test.ts +220 -0
  197. package/src/node/_test/Engine.test-d.ts +28 -0
  198. package/src/node/_test/Engine.test.ts +62 -22
  199. package/src/node/_test/Hash.test-d.ts +16 -9
  200. package/src/node/_test/Hash.test.ts +10 -11
  201. package/src/node/_test/Keystore.fuzz.ts +58 -0
  202. package/src/node/_test/Keystore.test-d.ts +43 -0
  203. package/src/node/_test/Keystore.test.ts +198 -0
  204. package/src/node/_test/Mnemonic.fuzz.ts +31 -0
  205. package/src/node/_test/Mnemonic.test-d.ts +21 -0
  206. package/src/node/_test/Mnemonic.test.ts +98 -0
  207. package/src/node/_test/P256.test-d.ts +29 -0
  208. package/src/node/_test/P256.test.ts +102 -0
  209. package/src/node/_test/X25519.test-d.ts +28 -0
  210. package/src/node/_test/X25519.test.ts +239 -0
  211. package/src/node/index.ts +100 -8
  212. package/src/trusted-setups/Paths.ts +1 -0
  213. package/src/trusted-setups/README.md +39 -0
  214. package/src/trusted-setups/Setups.ts +46 -0
  215. package/src/trusted-setups/_test/Paths.test.ts +16 -0
  216. package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
  217. package/src/trusted-setups/_test/Setups.test.ts +43 -0
  218. package/src/trusted-setups/_test/index.test.ts +1 -0
  219. package/src/trusted-setups/index.ts +21 -0
  220. package/src/trusted-setups/internal/paths.ts +4 -1
  221. package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
  222. package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
  223. package/src/version.ts +1 -1
  224. package/src/wasm/Ed25519.ts +114 -0
  225. package/src/wasm/Engine.ts +78 -31
  226. package/src/wasm/Hash.bench.ts +2 -2
  227. package/src/wasm/Hash.ts +29 -50
  228. package/src/wasm/Keystore.bench.ts +26 -0
  229. package/src/wasm/Keystore.ts +66 -0
  230. package/src/wasm/Kzg.bench.ts +63 -0
  231. package/src/wasm/Kzg.ts +518 -0
  232. package/src/wasm/Mnemonic.ts +44 -0
  233. package/src/wasm/Secp256k1.bench.ts +79 -0
  234. package/src/wasm/Secp256k1.ts +248 -0
  235. package/src/wasm/X25519.ts +70 -0
  236. package/src/wasm/_test/Crypto25519.conformance.ts +57 -0
  237. package/src/wasm/_test/Crypto25519.fuzz.ts +187 -0
  238. package/src/wasm/_test/Ed25519.test-d.ts +28 -0
  239. package/src/wasm/_test/Ed25519.test.ts +168 -0
  240. package/src/wasm/_test/Ed25519.vectors.test.ts +73 -0
  241. package/src/wasm/_test/Engine.test-d.ts +29 -0
  242. package/src/wasm/_test/Engine.test.ts +59 -25
  243. package/src/wasm/_test/Hash.browser.test.ts +14 -7
  244. package/src/wasm/_test/Hash.fuzz.ts +58 -23
  245. package/src/wasm/_test/Hash.test-d.ts +16 -10
  246. package/src/wasm/_test/Hash.test.ts +148 -48
  247. package/src/wasm/_test/Hash.vectors.test.ts +15 -8
  248. package/src/wasm/_test/Keystore.browser.test.ts +73 -0
  249. package/src/wasm/_test/Keystore.fuzz.ts +163 -0
  250. package/src/wasm/_test/Keystore.test-d.ts +42 -0
  251. package/src/wasm/_test/Keystore.test.ts +477 -0
  252. package/src/wasm/_test/Keystore.vectors.test.ts +21 -0
  253. package/src/wasm/_test/Kzg.test-d.ts +19 -0
  254. package/src/wasm/_test/Kzg.test.ts +220 -0
  255. package/src/wasm/_test/Mnemonic.test-d.ts +17 -0
  256. package/src/wasm/_test/Mnemonic.test.ts +110 -0
  257. package/src/wasm/_test/Runtime.test.ts +9 -0
  258. package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
  259. package/src/wasm/_test/Secp256k1.test.ts +354 -0
  260. package/src/wasm/_test/X25519.test-d.ts +21 -0
  261. package/src/wasm/_test/X25519.test.ts +194 -0
  262. package/src/wasm/_test/fixtures.ts +4 -0
  263. package/src/wasm/index.ts +130 -12
  264. package/src/wasm/internal/blake3.ts +45 -0
  265. package/src/wasm/internal/blake3.wasm.ts +11 -0
  266. package/src/wasm/internal/crypto25519.ts +43 -0
  267. package/src/wasm/internal/crypto25519.wasm.ts +11 -0
  268. package/src/wasm/internal/ed25519.ts +31 -0
  269. package/src/wasm/internal/hashes.ts +29 -0
  270. package/src/wasm/internal/hashes.wasm.ts +5 -2
  271. package/src/wasm/internal/instantiate.ts +29 -9
  272. package/src/wasm/internal/keystore.ts +19 -0
  273. package/src/wasm/internal/kzg.ts +50 -0
  274. package/src/wasm/internal/kzg.wasm.ts +11 -0
  275. package/src/wasm/internal/mnemonic.ts +56 -0
  276. package/src/wasm/internal/pbkdf2.ts +86 -0
  277. package/src/wasm/internal/scrypt.ts +131 -0
  278. package/src/wasm/internal/scrypt.wasm.ts +17 -0
  279. package/src/wasm/internal/secp256k1.ts +62 -0
  280. package/src/wasm/internal/secp256k1.wasm.ts +11 -0
  281. package/src/wasm/internal/x25519.ts +29 -0
  282. package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
  283. package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
  284. package/src/trusted-setups/internal/setups/mainnet.txt +0 -4163
@@ -1,3 +1,4 @@
1
+ import { blake3 } from '@noble/hashes/blake3.js'
1
2
  import { hmac } from '@noble/hashes/hmac.js'
2
3
  import { ripemd160 } from '@noble/hashes/legacy.js'
3
4
  import { sha256 } from '@noble/hashes/sha2.js'
@@ -5,25 +6,23 @@ import { keccak_256 } from '@noble/hashes/sha3.js'
5
6
  import { Bytes, Engine, Hash } from 'ox'
6
7
  import { Hash as WasmHash } from 'ox/wasm'
7
8
  import { beforeAll, describe, expect, test } from 'vp/test'
9
+ import { wasmBase64 as blake3WasmBase64 } from '../internal/blake3.wasm.js'
10
+ import * as blake3_internal from '../internal/blake3.js'
8
11
  import { hmacSha256ScratchSize, wasmBase64 } from '../internal/hashes.wasm.js'
9
12
  import * as hash from '../internal/hash.js'
10
13
  import * as internal from '../internal/instantiate.js'
11
14
 
12
- let engine: WasmHash.create.ReturnType
15
+ let engine: WasmHash.engine.ReturnType
13
16
 
14
17
  beforeAll(async () => {
15
- engine = await WasmHash.create()
18
+ engine = await WasmHash.engine()
16
19
  })
17
20
 
18
- describe('create', () => {
21
+ describe('engine', () => {
19
22
  test('default', async () => {
20
- expect(Object.keys(await WasmHash.create())).toMatchInlineSnapshot(`
21
- [
22
- "Hash",
23
- ]
24
- `)
25
- expect(Object.keys((await WasmHash.create()).Hash)).toMatchInlineSnapshot(`
23
+ expect(Object.keys(await WasmHash.engine())).toMatchInlineSnapshot(`
26
24
  [
25
+ "blake3",
27
26
  "hmacSha256",
28
27
  "keccak256",
29
28
  "ripemd160",
@@ -33,28 +32,26 @@ describe('create', () => {
33
32
  })
34
33
 
35
34
  test('behavior: hands every caller its own engine', async () => {
36
- const [a, b] = await Promise.all([WasmHash.create(), WasmHash.create()])
35
+ const [a, b] = await Promise.all([WasmHash.engine(), WasmHash.engine()])
37
36
 
38
- // Deliberately not the same object. A shared slot lets one caller's
39
- // customisation for composition leak into what a later `create` -- or
40
- // `Engine.load` -- installs.
37
+ // Deliberately not the same object. Sharing the slot object would let one
38
+ // caller's customisation leak into a later `engine` or `Engine.install`.
41
39
  expect(a).not.toBe(b)
42
- expect(a.Hash).not.toBe(b.Hash)
43
40
 
44
- a.Hash.keccak256 = () => new Uint8Array(32).fill(9)
45
- const c = await WasmHash.create()
46
- expect(c.Hash.keccak256(Bytes.from('0xdeadbeef'))).toEqual(
47
- b.Hash.keccak256(Bytes.from('0xdeadbeef')),
41
+ a.keccak256 = () => new Uint8Array(32).fill(9)
42
+ const c = await WasmHash.engine()
43
+ expect(c.keccak256(Bytes.from('0xdeadbeef'))).toEqual(
44
+ b.keccak256(Bytes.from('0xdeadbeef')),
48
45
  )
49
46
  })
50
47
  })
51
48
 
52
49
  // Sizes chosen around the block boundaries where padding bugs live: keccak256's
53
50
  // rate is 136 bytes, sha256 and ripemd160 use 64-byte blocks and need a second
54
- // block once the remainder reaches 56.
51
+ // block once the remainder reaches 56, and BLAKE3 uses 1024-byte chunks.
55
52
  const sizes = [
56
53
  0, 1, 2, 31, 32, 55, 56, 63, 64, 65, 71, 72, 111, 135, 136, 137, 199, 200,
57
- 271, 272, 1000,
54
+ 271, 272, 1000, 1023, 1024, 1025, 2048, 2049,
58
55
  ]
59
56
 
60
57
  function input(size: number) {
@@ -62,16 +59,20 @@ function input(size: number) {
62
59
  }
63
60
 
64
61
  describe('Hash', () => {
62
+ test.each(sizes)('blake3 matches the default at %i bytes', (size) => {
63
+ expect(engine.blake3(input(size))).toEqual(blake3(input(size)))
64
+ })
65
+
65
66
  test.each(sizes)('keccak256 matches the default at %i bytes', (size) => {
66
- expect(engine.Hash.keccak256(input(size))).toEqual(keccak_256(input(size)))
67
+ expect(engine.keccak256(input(size))).toEqual(keccak_256(input(size)))
67
68
  })
68
69
 
69
70
  test.each(sizes)('sha256 matches the default at %i bytes', (size) => {
70
- expect(engine.Hash.sha256(input(size))).toEqual(sha256(input(size)))
71
+ expect(engine.sha256(input(size))).toEqual(sha256(input(size)))
71
72
  })
72
73
 
73
74
  test.each(sizes)('ripemd160 matches the default at %i bytes', (size) => {
74
- expect(engine.Hash.ripemd160(input(size))).toEqual(ripemd160(input(size)))
75
+ expect(engine.ripemd160(input(size))).toEqual(ripemd160(input(size)))
75
76
  })
76
77
 
77
78
  test.each([0, 1, 32, 63, 64, 65, 200])(
@@ -79,33 +80,117 @@ describe('Hash', () => {
79
80
  (size) => {
80
81
  const key = new Uint8Array(size).fill(0xa5)
81
82
  const message = input(100)
82
- expect(engine.Hash.hmacSha256(key, message)).toEqual(
83
+ expect(engine.hmacSha256(key, message)).toEqual(
83
84
  hmac(sha256, key, message),
84
85
  )
85
86
  },
86
87
  )
87
88
 
88
89
  test('behavior: known vectors', () => {
89
- expect(Bytes.toHex(engine.Hash.keccak256(new Uint8Array(0)))).toBe(
90
+ expect(Bytes.toHex(engine.blake3(new Uint8Array(0)))).toBe(
91
+ '0xaf1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262',
92
+ )
93
+ expect(Bytes.toHex(engine.keccak256(new Uint8Array(0)))).toBe(
90
94
  '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',
91
95
  )
92
- expect(Bytes.toHex(engine.Hash.sha256(new Uint8Array(0)))).toBe(
96
+ expect(Bytes.toHex(engine.sha256(new Uint8Array(0)))).toBe(
93
97
  '0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
94
98
  )
95
- expect(Bytes.toHex(engine.Hash.ripemd160(new Uint8Array(0)))).toBe(
99
+ expect(Bytes.toHex(engine.ripemd160(new Uint8Array(0)))).toBe(
96
100
  '0x9c1185a5c5e9fc54612808977ee8f548b2258d31',
97
101
  )
98
102
  })
99
103
 
100
104
  test('behavior: grows memory past 64 MiB, then still hashes small inputs', () => {
101
105
  // The input itself is exactly 64 MiB, so the heap base and digest force
102
- // memory past that boundary. Growing detaches the previous `ArrayBuffer`; a
103
- // retained view would silently read zero bytes in the assertion below.
106
+ // memory past that boundary. Growing detaches the previous `ArrayBuffer`;
107
+ // a retained view would silently read zero bytes in the assertion below.
104
108
  const large = new Uint8Array(64 * 1024 * 1024).fill(7)
105
- expect(engine.Hash.sha256(large)).toEqual(sha256(large))
109
+ expect(engine.sha256(large)).toEqual(sha256(large))
110
+ expect(engine.blake3(large)).toEqual(blake3(large))
106
111
 
107
112
  const small = input(32)
108
- expect(engine.Hash.sha256(small)).toEqual(sha256(small))
113
+ expect(engine.sha256(small)).toEqual(sha256(small))
114
+ expect(engine.blake3(small)).toEqual(blake3(small))
115
+ }, 60_000)
116
+
117
+ test('behavior: accepts Uint8Array subviews without mutation', () => {
118
+ const backing = input(1027)
119
+ const subview = backing.subarray(1, 1026)
120
+ const snapshot = backing.slice()
121
+ expect(engine.blake3(subview)).toEqual(blake3(subview))
122
+ expect(backing).toEqual(snapshot)
123
+ })
124
+
125
+ test('behavior: blake3 clears its complete memory region', async () => {
126
+ const module =
127
+ await internal.instantiate<blake3_internal.Exports>(blake3WasmBase64)
128
+ const message = input(1025)
129
+ const end = module.heapBase + message.length + 32
130
+
131
+ expect(blake3_internal.hash(module, message)).toEqual(blake3(message))
132
+ expect(module.view().slice(module.heapBase, end)).toEqual(
133
+ new Uint8Array(end - module.heapBase),
134
+ )
135
+ })
136
+
137
+ test('behavior: blake3 clears its complete region after a late trap', async () => {
138
+ const module =
139
+ await internal.instantiate<blake3_internal.Exports>(blake3WasmBase64)
140
+ const message = input(1025)
141
+ const end = module.heapBase + message.length + 32
142
+ const exports = {
143
+ blake3_hash(inputPtr: number, length: number, outPtr: number) {
144
+ module.exports.blake3_hash(inputPtr, length, outPtr)
145
+ throw new WebAssembly.RuntimeError('forced late trap')
146
+ },
147
+ zero: (ptr: number, length: number) => module.exports.zero(ptr, length),
148
+ }
149
+
150
+ expect(() =>
151
+ blake3_internal.hash({ ...module, exports }, message),
152
+ ).toThrowError('forced late trap')
153
+ expect(module.view().slice(module.heapBase, end)).toEqual(
154
+ new Uint8Array(end - module.heapBase),
155
+ )
156
+ })
157
+
158
+ test('behavior: rejects wasm32 pointer overflow before reserving', () => {
159
+ let reserved = false
160
+ const module: internal.Module<blake3_internal.Exports> = {
161
+ exports: {
162
+ blake3_hash() {},
163
+ zero() {},
164
+ },
165
+ heapBase: 0xffff_fff0,
166
+ reserve() {
167
+ reserved = true
168
+ },
169
+ view: () => new Uint8Array(0),
170
+ }
171
+
172
+ expect(() => blake3_internal.hash(module, input(1))).toThrow(
173
+ internal.MemoryError,
174
+ )
175
+ expect(reserved).toBe(false)
176
+ })
177
+
178
+ test('behavior: accepts a workspace ending exactly at the wasm32 boundary', () => {
179
+ const marker = new Error('reserve reached')
180
+ const module: internal.Module<blake3_internal.Exports> = {
181
+ exports: {
182
+ blake3_hash() {},
183
+ zero() {},
184
+ },
185
+ heapBase: 0x1_0000_0000 - 33,
186
+ reserve(bytes) {
187
+ expect(bytes).toBe(33)
188
+ throw marker
189
+ },
190
+ view: () => new Uint8Array(0),
191
+ }
192
+
193
+ expect(() => blake3_internal.hash(module, input(1))).toThrow(marker)
109
194
  })
110
195
 
111
196
  test('behavior: hmacSha256 clears its complete memory region', async () => {
@@ -182,30 +267,45 @@ describe('Hash', () => {
182
267
  test('behavior: consecutive calls do not corrupt each other', () => {
183
268
  const a = input(200)
184
269
  const b = input(7)
185
- expect(engine.Hash.keccak256(a)).toEqual(keccak_256(a))
186
- expect(engine.Hash.keccak256(b)).toEqual(keccak_256(b))
187
- expect(engine.Hash.keccak256(a)).toEqual(keccak_256(a))
270
+ expect(engine.keccak256(a)).toEqual(keccak_256(a))
271
+ expect(engine.blake3(a)).toEqual(blake3(a))
272
+ expect(engine.blake3(b)).toEqual(blake3(b))
273
+ expect(engine.blake3(a)).toEqual(blake3(a))
274
+ expect(engine.keccak256(b)).toEqual(keccak_256(b))
275
+ expect(engine.keccak256(a)).toEqual(keccak_256(a))
188
276
  })
189
277
 
190
278
  test('behavior: results do not alias WASM memory', () => {
191
- const first = engine.Hash.keccak256(input(32))
279
+ const first = engine.keccak256(input(32))
192
280
  const snapshot = first.slice()
193
- engine.Hash.keccak256(input(64))
281
+ engine.keccak256(input(64))
194
282
  expect(first).toEqual(snapshot)
283
+
284
+ const blake3First = engine.blake3(input(32))
285
+ const blake3Snapshot = blake3First.slice()
286
+ engine.blake3(input(64))
287
+ expect(blake3First).toEqual(blake3Snapshot)
195
288
  })
196
289
  })
197
290
 
198
- describe('Engine.set', () => {
199
- test('behavior: ox uses the WASM implementation once installed', () => {
200
- Engine.set(engine)
201
- expect(Hash.keccak256('0xdeadbeef')).toBe(
202
- '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
203
- )
204
- expect(Hash.sha256('0xdeadbeef')).toBe(
205
- '0x5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953',
206
- )
207
- expect(Hash.ripemd160('0xdeadbeef')).toBe(
208
- '0x226821c2f5423e11fe9af68bd285c249db2e4b5a',
209
- )
291
+ describe('Engine.install', () => {
292
+ test('behavior: ox uses the WASM implementation once installed', async () => {
293
+ await Engine.install({ Hash: engine })
294
+ try {
295
+ expect(Hash.blake3('0xdeadbeef')).toBe(
296
+ '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd',
297
+ )
298
+ expect(Hash.keccak256('0xdeadbeef')).toBe(
299
+ '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
300
+ )
301
+ expect(Hash.sha256('0xdeadbeef')).toBe(
302
+ '0x5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953',
303
+ )
304
+ expect(Hash.ripemd160('0xdeadbeef')).toBe(
305
+ '0x226821c2f5423e11fe9af68bd285c249db2e4b5a',
306
+ )
307
+ } finally {
308
+ Engine.reset()
309
+ }
210
310
  })
211
311
  })
@@ -13,16 +13,23 @@ import { instantiate } from '../internal/instantiate.js'
13
13
  * See `test/vectors/hashes/README.md`.
14
14
  */
15
15
 
16
- let engine: WasmHash.create.ReturnType
16
+ let engine: WasmHash.engine.ReturnType
17
17
 
18
18
  beforeAll(async () => {
19
- engine = await WasmHash.create()
19
+ engine = await WasmHash.engine()
20
+ })
21
+
22
+ describe('blake3', () => {
23
+ test(`matches ${vectors.blake3.length} official BLAKE3 vectors`, () => {
24
+ for (const { digest, message } of vectors.blake3)
25
+ expect(engine.blake3(message)).toEqual(digest)
26
+ })
20
27
  })
21
28
 
22
29
  describe('sha256', () => {
23
30
  test(`matches ${vectors.sha256.length} NIST CAVP vectors`, () => {
24
31
  for (const { digest, message } of vectors.sha256)
25
- expect(engine.Hash.sha256(message)).toEqual(digest)
32
+ expect(engine.sha256(message)).toEqual(digest)
26
33
  })
27
34
  })
28
35
 
@@ -31,26 +38,26 @@ describe('hmacSha256', () => {
31
38
  // Two of these use 131-byte keys, which is the only coverage of the
32
39
  // longer-than-a-block path where the key is hashed before padding.
33
40
  for (const { digest, key, message } of vectors.hmacSha256)
34
- expect(engine.Hash.hmacSha256(key, message)).toEqual(digest)
41
+ expect(engine.hmacSha256(key, message)).toEqual(digest)
35
42
  })
36
43
  })
37
44
 
38
45
  describe('ripemd160', () => {
39
46
  test(`matches ${vectors.ripemd160.length} reference vectors`, () => {
40
47
  for (const { digest, message } of vectors.ripemd160)
41
- expect(engine.Hash.ripemd160(message)).toEqual(digest)
48
+ expect(engine.ripemd160(message)).toEqual(digest)
42
49
  })
43
50
 
44
51
  test('matches the million-`a` reference vector', () => {
45
52
  const { digest, message } = vectors.ripemd160MillionA
46
- expect(engine.Hash.ripemd160(message)).toEqual(digest)
53
+ expect(engine.ripemd160(message)).toEqual(digest)
47
54
  })
48
55
  })
49
56
 
50
57
  describe('keccak256', () => {
51
58
  test(`matches ${vectors.keccak256.length} OpenSSL vectors`, () => {
52
59
  for (const { digest, message } of vectors.keccak256)
53
- expect(engine.Hash.keccak256(message)).toEqual(digest)
60
+ expect(engine.keccak256(message)).toEqual(digest)
54
61
  })
55
62
  })
56
63
 
@@ -60,7 +67,7 @@ describe('keccak_f1600', () => {
60
67
  /** Writes 25 lanes as little-endian 64-bit words, permutes, and reads back. */
61
68
  async function permute(lanes: readonly bigint[]) {
62
69
  // A separate instance, because this export is not part of the engine and so
63
- // is deliberately not reachable through `WasmHash.load`.
70
+ // is deliberately not reachable through `WasmHash.engine`.
64
71
  const module = await instantiate<Exports>(wasmBase64)
65
72
  module.reserve(200)
66
73
  const view = module.view()
@@ -0,0 +1,73 @@
1
+ import { pbkdf2 as pbkdf2_noble } from '@noble/hashes/pbkdf2.js'
2
+ import { scrypt as scrypt_noble } from '@noble/hashes/scrypt.js'
3
+ import { sha256 } from '@noble/hashes/sha2.js'
4
+ import { describe, expect, test } from 'vp/test'
5
+ import * as Bytes from '../../core/Bytes.js'
6
+ import * as Engine from '../../core/Engine.js'
7
+ import * as Keystore from '../../core/Keystore.js'
8
+ import * as WasmKeystore from '../Keystore.js'
9
+
10
+ describe('engine', () => {
11
+ test('behavior: derives arbitrary-length keys in a real browser', async () => {
12
+ const engine = await WasmKeystore.engine()
13
+ const password = Bytes.fromString('testpassword')
14
+ const salt = new Uint8Array(52).fill(0xa5)
15
+
16
+ expect(engine.pbkdf2Sha256(password, salt, { c: 3, dkLen: 65 })).toEqual(
17
+ pbkdf2_noble(sha256, password, salt, { c: 3, dkLen: 65 }),
18
+ )
19
+ expect(
20
+ engine.scrypt(password, salt, { N: 1024, dkLen: 65, p: 2, r: 1 }),
21
+ ).toEqual(scrypt_noble(password, salt, { N: 1024, dkLen: 65, p: 2, r: 1 }))
22
+ })
23
+
24
+ test('behavior: ox uses synchronous WASM PBKDF2 once installed', async () => {
25
+ const engine = await WasmKeystore.engine()
26
+ await Engine.install({ Keystore: engine })
27
+ try {
28
+ const salt = new Uint8Array(32).fill(7)
29
+ const [key] = Keystore.pbkdf2({
30
+ iterations: 1_000,
31
+ password: 'testpassword',
32
+ salt,
33
+ })
34
+ expect(key()).toBe(
35
+ Bytes.toHex(
36
+ pbkdf2_noble(sha256, Bytes.fromString('testpassword'), salt, {
37
+ c: 1_000,
38
+ dkLen: 32,
39
+ }),
40
+ ),
41
+ )
42
+ } finally {
43
+ Engine.reset()
44
+ }
45
+ })
46
+
47
+ test('behavior: ox uses synchronous WASM scrypt once installed', async () => {
48
+ const engine = await WasmKeystore.engine()
49
+ await Engine.install({ Keystore: engine })
50
+ try {
51
+ const salt = new Uint8Array(32).fill(7)
52
+ const [key] = Keystore.scrypt({
53
+ n: 1024,
54
+ p: 2,
55
+ password: 'testpassword',
56
+ r: 1,
57
+ salt,
58
+ })
59
+ expect(key()).toBe(
60
+ Bytes.toHex(
61
+ scrypt_noble(Bytes.fromString('testpassword'), salt, {
62
+ N: 1024,
63
+ dkLen: 32,
64
+ p: 2,
65
+ r: 1,
66
+ }),
67
+ ),
68
+ )
69
+ } finally {
70
+ Engine.reset()
71
+ }
72
+ })
73
+ })
@@ -0,0 +1,163 @@
1
+ import { fc, test } from '@fast-check/vitest'
2
+ import { pbkdf2 as pbkdf2_noble } from '@noble/hashes/pbkdf2.js'
3
+ import { scrypt as scrypt_noble } from '@noble/hashes/scrypt.js'
4
+ import { sha256 } from '@noble/hashes/sha2.js'
5
+ import { beforeAll, describe, expect } from 'vp/test'
6
+ import { numRuns } from '../../../test/fuzz/numRuns.js'
7
+ import * as WasmHash from '../Hash.js'
8
+ import * as WasmKeystore from '../Keystore.js'
9
+
10
+ let hash: WasmHash.engine.ReturnType
11
+ let keystore: WasmKeystore.engine.ReturnType
12
+
13
+ beforeAll(async () => {
14
+ ;[hash, keystore] = await Promise.all([
15
+ WasmHash.engine(),
16
+ WasmKeystore.engine(),
17
+ ])
18
+ })
19
+
20
+ const arbitraryPassword = fc.oneof(
21
+ {
22
+ arbitrary: fc
23
+ .constantFrom(0, 1, 32, 63, 64, 65, 128, 129)
24
+ .chain((size) => fc.uint8Array({ maxLength: size, minLength: size })),
25
+ weight: 3,
26
+ },
27
+ { arbitrary: fc.uint8Array({ maxLength: 200 }), weight: 2 },
28
+ )
29
+
30
+ const arbitrarySalt = fc.oneof(
31
+ {
32
+ arbitrary: fc
33
+ .constantFrom(0, 1, 31, 32, 51, 52, 55, 56, 63, 64, 65, 136)
34
+ .chain((size) => fc.uint8Array({ maxLength: size, minLength: size })),
35
+ weight: 3,
36
+ },
37
+ { arbitrary: fc.uint8Array({ maxLength: 200 }), weight: 2 },
38
+ )
39
+
40
+ const arbitraryDkLen = fc.oneof(
41
+ {
42
+ arbitrary: fc.constantFrom(1, 2, 31, 32, 33, 63, 64, 65, 96, 97),
43
+ weight: 3,
44
+ },
45
+ { arbitrary: fc.integer({ max: 256, min: 1 }), weight: 2 },
46
+ )
47
+
48
+ function asSubview(input: Uint8Array) {
49
+ const backing = new Uint8Array(input.length + 11).fill(0xa5)
50
+ backing.set(input, 7)
51
+ return { backing, view: backing.subarray(7, 7 + input.length) }
52
+ }
53
+
54
+ describe('pbkdf2Sha256', () => {
55
+ test.prop(
56
+ {
57
+ c: fc.integer({ max: 20, min: 1 }),
58
+ dkLen: arbitraryDkLen,
59
+ password: arbitraryPassword,
60
+ salt: arbitrarySalt,
61
+ },
62
+ { numRuns },
63
+ )(
64
+ 'matches @noble/hashes for arbitrary inputs and output lengths',
65
+ ({ c, dkLen, password, salt }) => {
66
+ expect(keystore.pbkdf2Sha256(password, salt, { c, dkLen })).toEqual(
67
+ pbkdf2_noble(sha256, password, salt, { c, dkLen }),
68
+ )
69
+ },
70
+ )
71
+
72
+ test.prop(
73
+ {
74
+ c: fc.integer({ max: 10, min: 1 }),
75
+ dkLen: arbitraryDkLen,
76
+ password: arbitraryPassword,
77
+ salt: arbitrarySalt,
78
+ },
79
+ { numRuns },
80
+ )(
81
+ 'handles subviews without mutating either backing buffer',
82
+ ({ c, dkLen, password, salt }) => {
83
+ const password_ = asSubview(password)
84
+ const salt_ = asSubview(salt)
85
+ const passwordSnapshot = password_.backing.slice()
86
+ const saltSnapshot = salt_.backing.slice()
87
+
88
+ expect(
89
+ keystore.pbkdf2Sha256(password_.view, salt_.view, {
90
+ c,
91
+ dkLen,
92
+ }),
93
+ ).toEqual(pbkdf2_noble(sha256, password_.view, salt_.view, { c, dkLen }))
94
+ expect(password_.backing).toEqual(passwordSnapshot)
95
+ expect(salt_.backing).toEqual(saltSnapshot)
96
+ },
97
+ )
98
+
99
+ test.prop(
100
+ {
101
+ dkLen: arbitraryDkLen,
102
+ input: fc.uint8Array({ maxLength: 256 }),
103
+ },
104
+ { numRuns },
105
+ )('shares memory safely with hash calls', ({ dkLen, input }) => {
106
+ const derived = keystore.pbkdf2Sha256(input, input, {
107
+ c: 2,
108
+ dkLen,
109
+ })
110
+ const snapshot = derived.slice()
111
+ expect(hash.sha256(input)).toEqual(sha256(input))
112
+ expect(derived).toEqual(snapshot)
113
+ })
114
+ })
115
+
116
+ describe('scrypt', () => {
117
+ test.prop(
118
+ {
119
+ dkLen: arbitraryDkLen,
120
+ logN: fc.integer({ max: 7, min: 1 }),
121
+ p: fc.integer({ max: 3, min: 1 }),
122
+ password: arbitraryPassword,
123
+ r: fc.integer({ max: 4, min: 1 }),
124
+ salt: arbitrarySalt,
125
+ },
126
+ { numRuns },
127
+ )(
128
+ 'matches @noble/hashes for arbitrary parameters and inputs',
129
+ ({ dkLen, logN, p, password, r, salt }) => {
130
+ const options = { N: 2 ** logN, dkLen, p, r }
131
+ expect(keystore.scrypt(password, salt, options)).toEqual(
132
+ scrypt_noble(password, salt, options),
133
+ )
134
+ },
135
+ )
136
+
137
+ test.prop(
138
+ {
139
+ dkLen: arbitraryDkLen,
140
+ logN: fc.integer({ max: 7, min: 1 }),
141
+ p: fc.integer({ max: 3, min: 1 }),
142
+ password: arbitraryPassword,
143
+ r: fc.integer({ max: 4, min: 1 }),
144
+ salt: arbitrarySalt,
145
+ },
146
+ { numRuns },
147
+ )(
148
+ 'handles subviews without mutating either backing buffer',
149
+ ({ dkLen, logN, p, password, r, salt }) => {
150
+ const password_ = asSubview(password)
151
+ const salt_ = asSubview(salt)
152
+ const passwordSnapshot = password_.backing.slice()
153
+ const saltSnapshot = salt_.backing.slice()
154
+ const options = { N: 2 ** logN, dkLen, p, r }
155
+
156
+ expect(keystore.scrypt(password_.view, salt_.view, options)).toEqual(
157
+ scrypt_noble(password_.view, salt_.view, options),
158
+ )
159
+ expect(password_.backing).toEqual(passwordSnapshot)
160
+ expect(salt_.backing).toEqual(saltSnapshot)
161
+ },
162
+ )
163
+ })
@@ -0,0 +1,42 @@
1
+ import type { Engine } from 'ox'
2
+ import { expectTypeOf, test } from 'vp/test'
3
+ import * as core_Keystore from '../../core/Keystore.js'
4
+ import * as WasmKeystore from '../Keystore.js'
5
+
6
+ type Slot = Awaited<ReturnType<typeof WasmKeystore.engine>>
7
+
8
+ test('synchronous PBKDF2 is present', () => {
9
+ expectTypeOf<Slot['pbkdf2Sha256']>().toEqualTypeOf<
10
+ (
11
+ password: Uint8Array,
12
+ salt: Uint8Array,
13
+ options: { c: number; dkLen: number },
14
+ ) => Uint8Array
15
+ >()
16
+ })
17
+
18
+ test('synchronous scrypt is present', () => {
19
+ expectTypeOf<Slot['scrypt']>().toEqualTypeOf<
20
+ (
21
+ password: Uint8Array,
22
+ salt: Uint8Array,
23
+ options: { N: number; dkLen: number; p: number; r: number },
24
+ ) => Uint8Array
25
+ >()
26
+ })
27
+
28
+ test('unsupported primitives are absent', () => {
29
+ expectTypeOf<Slot>().not.toHaveProperty('aesCtrDecrypt')
30
+ expectTypeOf<Slot>().not.toHaveProperty('aesCtrEncrypt')
31
+ expectTypeOf<Slot>().not.toHaveProperty('pbkdf2Sha256Async')
32
+ expectTypeOf<Slot>().not.toHaveProperty('scryptAsync')
33
+ })
34
+
35
+ test('the result is the raw slot', () => {
36
+ expectTypeOf<{ Keystore: Slot }>().toExtend<Engine.Engine>()
37
+ })
38
+
39
+ test('the WASM namespace exposes the public Keystore API', () => {
40
+ expectTypeOf(WasmKeystore.pbkdf2).toEqualTypeOf(core_Keystore.pbkdf2)
41
+ expectTypeOf<typeof WasmKeystore>().not.toHaveProperty('create')
42
+ })