ox 1.1.2 → 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 (131) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/core/Hash.d.ts +36 -0
  3. package/dist/core/Hash.d.ts.map +1 -1
  4. package/dist/core/Hash.js +35 -0
  5. package/dist/core/Hash.js.map +1 -1
  6. package/dist/node/Hash.d.ts +3 -3
  7. package/dist/node/Hash.js +3 -3
  8. package/dist/trusted-setups/Paths.d.ts +1 -0
  9. package/dist/trusted-setups/Paths.d.ts.map +1 -1
  10. package/dist/trusted-setups/Paths.js +1 -0
  11. package/dist/trusted-setups/Paths.js.map +1 -1
  12. package/dist/trusted-setups/Setups.d.ts +35 -0
  13. package/dist/trusted-setups/Setups.d.ts.map +1 -0
  14. package/dist/trusted-setups/Setups.js +29 -0
  15. package/dist/trusted-setups/Setups.js.map +1 -0
  16. package/dist/trusted-setups/index.d.ts +20 -0
  17. package/dist/trusted-setups/index.d.ts.map +1 -1
  18. package/dist/trusted-setups/index.js +20 -0
  19. package/dist/trusted-setups/index.js.map +1 -1
  20. package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
  21. package/dist/trusted-setups/internal/paths.js +1 -1
  22. package/dist/trusted-setups/internal/paths.js.map +1 -1
  23. package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
  24. package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
  25. package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
  26. package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
  27. package/dist/wasm/Engine.d.ts +3 -2
  28. package/dist/wasm/Engine.d.ts.map +1 -1
  29. package/dist/wasm/Engine.js +5 -5
  30. package/dist/wasm/Engine.js.map +1 -1
  31. package/dist/wasm/Keystore.d.ts +23 -9
  32. package/dist/wasm/Keystore.d.ts.map +1 -1
  33. package/dist/wasm/Keystore.js +30 -13
  34. package/dist/wasm/Keystore.js.map +1 -1
  35. package/dist/wasm/Kzg.d.ts +112 -0
  36. package/dist/wasm/Kzg.d.ts.map +1 -0
  37. package/dist/wasm/Kzg.js +333 -0
  38. package/dist/wasm/Kzg.js.map +1 -0
  39. package/dist/wasm/Secp256k1.d.ts +55 -0
  40. package/dist/wasm/Secp256k1.d.ts.map +1 -0
  41. package/dist/wasm/Secp256k1.js +181 -0
  42. package/dist/wasm/Secp256k1.js.map +1 -0
  43. package/dist/wasm/index.d.ts +44 -1
  44. package/dist/wasm/index.d.ts.map +1 -1
  45. package/dist/wasm/index.js +44 -1
  46. package/dist/wasm/index.js.map +1 -1
  47. package/dist/wasm/internal/instantiate.d.ts +12 -0
  48. package/dist/wasm/internal/instantiate.d.ts.map +1 -1
  49. package/dist/wasm/internal/instantiate.js +23 -7
  50. package/dist/wasm/internal/instantiate.js.map +1 -1
  51. package/dist/wasm/internal/keystore.d.ts +10 -0
  52. package/dist/wasm/internal/keystore.d.ts.map +1 -0
  53. package/dist/wasm/internal/keystore.js +10 -0
  54. package/dist/wasm/internal/keystore.js.map +1 -0
  55. package/dist/wasm/internal/kzg.d.ts +17 -0
  56. package/dist/wasm/internal/kzg.d.ts.map +1 -0
  57. package/dist/wasm/internal/kzg.js +8 -0
  58. package/dist/wasm/internal/kzg.js.map +1 -0
  59. package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
  60. package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
  61. package/dist/wasm/internal/kzg.wasm.js +10 -0
  62. package/dist/wasm/internal/kzg.wasm.js.map +1 -0
  63. package/dist/wasm/internal/scrypt.d.ts +16 -0
  64. package/dist/wasm/internal/scrypt.d.ts.map +1 -0
  65. package/dist/wasm/internal/scrypt.js +78 -0
  66. package/dist/wasm/internal/scrypt.js.map +1 -0
  67. package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
  68. package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
  69. package/dist/wasm/internal/scrypt.wasm.js +14 -0
  70. package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
  71. package/dist/wasm/internal/secp256k1.d.ts +16 -0
  72. package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
  73. package/dist/wasm/internal/secp256k1.js +20 -0
  74. package/dist/wasm/internal/secp256k1.js.map +1 -0
  75. package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
  76. package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
  77. package/dist/wasm/internal/secp256k1.wasm.js +10 -0
  78. package/dist/wasm/internal/secp256k1.wasm.js.map +1 -0
  79. package/package.json +19 -2
  80. package/src/core/Hash.ts +59 -0
  81. package/src/core/_test/Block.test.ts +9 -2
  82. package/src/core/_test/Hash.test-d.ts +18 -0
  83. package/src/core/_test/Hash.test.ts +100 -1
  84. package/src/core/_test/Hash.vectors.test.ts +1 -2
  85. package/src/core/_test/Transaction.test.ts +11 -2
  86. package/src/node/Hash.ts +3 -3
  87. package/src/node/_test/Hash.test-d.ts +1 -0
  88. package/src/trusted-setups/Paths.ts +1 -0
  89. package/src/trusted-setups/README.md +39 -0
  90. package/src/trusted-setups/Setups.ts +46 -0
  91. package/src/trusted-setups/_test/Paths.test.ts +16 -0
  92. package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
  93. package/src/trusted-setups/_test/Setups.test.ts +43 -0
  94. package/src/trusted-setups/_test/index.test.ts +1 -0
  95. package/src/trusted-setups/index.ts +21 -0
  96. package/src/trusted-setups/internal/paths.ts +4 -1
  97. package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
  98. package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
  99. package/src/version.ts +1 -1
  100. package/src/wasm/Engine.ts +8 -6
  101. package/src/wasm/Keystore.bench.ts +26 -0
  102. package/src/wasm/Keystore.ts +32 -14
  103. package/src/wasm/Kzg.bench.ts +63 -0
  104. package/src/wasm/Kzg.ts +518 -0
  105. package/src/wasm/Secp256k1.bench.ts +79 -0
  106. package/src/wasm/Secp256k1.ts +248 -0
  107. package/src/wasm/_test/Engine.test-d.ts +1 -0
  108. package/src/wasm/_test/Hash.browser.test.ts +3 -0
  109. package/src/wasm/_test/Hash.test-d.ts +1 -0
  110. package/src/wasm/_test/Hash.test.ts +2 -2
  111. package/src/wasm/_test/Keystore.browser.test.ts +31 -0
  112. package/src/wasm/_test/Keystore.fuzz.ts +50 -0
  113. package/src/wasm/_test/Keystore.test-d.ts +10 -1
  114. package/src/wasm/_test/Keystore.test.ts +289 -4
  115. package/src/wasm/_test/Kzg.test-d.ts +19 -0
  116. package/src/wasm/_test/Kzg.test.ts +220 -0
  117. package/src/wasm/_test/Runtime.test.ts +9 -0
  118. package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
  119. package/src/wasm/_test/Secp256k1.test.ts +354 -0
  120. package/src/wasm/index.ts +46 -1
  121. package/src/wasm/internal/instantiate.ts +29 -9
  122. package/src/wasm/internal/keystore.ts +19 -0
  123. package/src/wasm/internal/kzg.ts +50 -0
  124. package/src/wasm/internal/kzg.wasm.ts +11 -0
  125. package/src/wasm/internal/scrypt.ts +131 -0
  126. package/src/wasm/internal/scrypt.wasm.ts +17 -0
  127. package/src/wasm/internal/secp256k1.ts +62 -0
  128. package/src/wasm/internal/secp256k1.wasm.ts +11 -0
  129. package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
  130. package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
  131. 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,6 +10,7 @@ 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', () => {
@@ -44,6 +44,9 @@ describe('engine', () => {
44
44
  test('behavior: ox uses the WASM implementation once installed', async () => {
45
45
  await Engine.install({ Hash: WasmHash.engine() })
46
46
  try {
47
+ expect(Hash.blake3('0xdeadbeef')).toBe(
48
+ '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd',
49
+ )
47
50
  expect(Hash.keccak256('0xdeadbeef')).toBe(
48
51
  '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
49
52
  )
@@ -28,6 +28,7 @@ test('the result is the raw slot, so `Engine.install` accepts it', () => {
28
28
  })
29
29
 
30
30
  test('the WASM namespace exposes the public Hash API', () => {
31
+ expectTypeOf(WasmHash.blake3).toEqualTypeOf(CoreHash.blake3)
31
32
  expectTypeOf(WasmHash.sha256).toEqualTypeOf(CoreHash.sha256)
32
33
  expectTypeOf<typeof WasmHash>().not.toHaveProperty('create')
33
34
  })
@@ -292,8 +292,8 @@ describe('Engine.install', () => {
292
292
  test('behavior: ox uses the WASM implementation once installed', async () => {
293
293
  await Engine.install({ Hash: engine })
294
294
  try {
295
- expect(Engine.get().Hash?.blake3?.(Bytes.from('0xdeadbeef'))).toEqual(
296
- blake3(Bytes.from('0xdeadbeef')),
295
+ expect(Hash.blake3('0xdeadbeef')).toBe(
296
+ '0x53147f3ce49ed4f60dfa5b9654c36ba6103c11f5737df3dabd4cbd296c4161bd',
297
297
  )
298
298
  expect(Hash.keccak256('0xdeadbeef')).toBe(
299
299
  '0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1',
@@ -1,4 +1,5 @@
1
1
  import { pbkdf2 as pbkdf2_noble } from '@noble/hashes/pbkdf2.js'
2
+ import { scrypt as scrypt_noble } from '@noble/hashes/scrypt.js'
2
3
  import { sha256 } from '@noble/hashes/sha2.js'
3
4
  import { describe, expect, test } from 'vp/test'
4
5
  import * as Bytes from '../../core/Bytes.js'
@@ -15,6 +16,9 @@ describe('engine', () => {
15
16
  expect(engine.pbkdf2Sha256(password, salt, { c: 3, dkLen: 65 })).toEqual(
16
17
  pbkdf2_noble(sha256, password, salt, { c: 3, dkLen: 65 }),
17
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 }))
18
22
  })
19
23
 
20
24
  test('behavior: ox uses synchronous WASM PBKDF2 once installed', async () => {
@@ -39,4 +43,31 @@ describe('engine', () => {
39
43
  Engine.reset()
40
44
  }
41
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
+ })
42
73
  })
@@ -1,5 +1,6 @@
1
1
  import { fc, test } from '@fast-check/vitest'
2
2
  import { pbkdf2 as pbkdf2_noble } from '@noble/hashes/pbkdf2.js'
3
+ import { scrypt as scrypt_noble } from '@noble/hashes/scrypt.js'
3
4
  import { sha256 } from '@noble/hashes/sha2.js'
4
5
  import { beforeAll, describe, expect } from 'vp/test'
5
6
  import { numRuns } from '../../../test/fuzz/numRuns.js'
@@ -111,3 +112,52 @@ describe('pbkdf2Sha256', () => {
111
112
  expect(derived).toEqual(snapshot)
112
113
  })
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
+ })
@@ -15,11 +15,20 @@ test('synchronous PBKDF2 is present', () => {
15
15
  >()
16
16
  })
17
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
+
18
28
  test('unsupported primitives are absent', () => {
19
29
  expectTypeOf<Slot>().not.toHaveProperty('aesCtrDecrypt')
20
30
  expectTypeOf<Slot>().not.toHaveProperty('aesCtrEncrypt')
21
31
  expectTypeOf<Slot>().not.toHaveProperty('pbkdf2Sha256Async')
22
- expectTypeOf<Slot>().not.toHaveProperty('scrypt')
23
32
  expectTypeOf<Slot>().not.toHaveProperty('scryptAsync')
24
33
  })
25
34