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.
- package/CHANGELOG.md +27 -0
- package/dist/core/Hash.d.ts +36 -0
- package/dist/core/Hash.d.ts.map +1 -1
- package/dist/core/Hash.js +35 -0
- package/dist/core/Hash.js.map +1 -1
- package/dist/node/Hash.d.ts +3 -3
- package/dist/node/Hash.js +3 -3
- package/dist/trusted-setups/Paths.d.ts +1 -0
- package/dist/trusted-setups/Paths.d.ts.map +1 -1
- package/dist/trusted-setups/Paths.js +1 -0
- package/dist/trusted-setups/Paths.js.map +1 -1
- package/dist/trusted-setups/Setups.d.ts +35 -0
- package/dist/trusted-setups/Setups.d.ts.map +1 -0
- package/dist/trusted-setups/Setups.js +29 -0
- package/dist/trusted-setups/Setups.js.map +1 -0
- package/dist/trusted-setups/index.d.ts +20 -0
- package/dist/trusted-setups/index.d.ts.map +1 -1
- package/dist/trusted-setups/index.js +20 -0
- package/dist/trusted-setups/index.js.map +1 -1
- package/dist/trusted-setups/internal/paths.d.ts.map +1 -1
- package/dist/trusted-setups/internal/paths.js +1 -1
- package/dist/trusted-setups/internal/paths.js.map +1 -1
- package/dist/trusted-setups/internal/setups/mainnet.d.ts +7 -0
- package/dist/trusted-setups/internal/setups/mainnet.d.ts.map +1 -0
- package/dist/trusted-setups/internal/setups/mainnet.js +11 -0
- package/dist/trusted-setups/internal/setups/mainnet.js.map +1 -0
- package/dist/wasm/Engine.d.ts +3 -2
- package/dist/wasm/Engine.d.ts.map +1 -1
- package/dist/wasm/Engine.js +5 -5
- package/dist/wasm/Engine.js.map +1 -1
- package/dist/wasm/Keystore.d.ts +23 -9
- package/dist/wasm/Keystore.d.ts.map +1 -1
- package/dist/wasm/Keystore.js +30 -13
- package/dist/wasm/Keystore.js.map +1 -1
- package/dist/wasm/Kzg.d.ts +112 -0
- package/dist/wasm/Kzg.d.ts.map +1 -0
- package/dist/wasm/Kzg.js +333 -0
- package/dist/wasm/Kzg.js.map +1 -0
- package/dist/wasm/Secp256k1.d.ts +55 -0
- package/dist/wasm/Secp256k1.d.ts.map +1 -0
- package/dist/wasm/Secp256k1.js +181 -0
- package/dist/wasm/Secp256k1.js.map +1 -0
- package/dist/wasm/index.d.ts +44 -1
- package/dist/wasm/index.d.ts.map +1 -1
- package/dist/wasm/index.js +44 -1
- package/dist/wasm/index.js.map +1 -1
- package/dist/wasm/internal/instantiate.d.ts +12 -0
- package/dist/wasm/internal/instantiate.d.ts.map +1 -1
- package/dist/wasm/internal/instantiate.js +23 -7
- package/dist/wasm/internal/instantiate.js.map +1 -1
- package/dist/wasm/internal/keystore.d.ts +10 -0
- package/dist/wasm/internal/keystore.d.ts.map +1 -0
- package/dist/wasm/internal/keystore.js +10 -0
- package/dist/wasm/internal/keystore.js.map +1 -0
- package/dist/wasm/internal/kzg.d.ts +17 -0
- package/dist/wasm/internal/kzg.d.ts.map +1 -0
- package/dist/wasm/internal/kzg.js +8 -0
- package/dist/wasm/internal/kzg.js.map +1 -0
- package/dist/wasm/internal/kzg.wasm.d.ts +3 -0
- package/dist/wasm/internal/kzg.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/kzg.wasm.js +10 -0
- package/dist/wasm/internal/kzg.wasm.js.map +1 -0
- package/dist/wasm/internal/scrypt.d.ts +16 -0
- package/dist/wasm/internal/scrypt.d.ts.map +1 -0
- package/dist/wasm/internal/scrypt.js +78 -0
- package/dist/wasm/internal/scrypt.js.map +1 -0
- package/dist/wasm/internal/scrypt.wasm.d.ts +7 -0
- package/dist/wasm/internal/scrypt.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/scrypt.wasm.js +14 -0
- package/dist/wasm/internal/scrypt.wasm.js.map +1 -0
- package/dist/wasm/internal/secp256k1.d.ts +16 -0
- package/dist/wasm/internal/secp256k1.d.ts.map +1 -0
- package/dist/wasm/internal/secp256k1.js +20 -0
- package/dist/wasm/internal/secp256k1.js.map +1 -0
- package/dist/wasm/internal/secp256k1.wasm.d.ts +3 -0
- package/dist/wasm/internal/secp256k1.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/secp256k1.wasm.js +10 -0
- package/dist/wasm/internal/secp256k1.wasm.js.map +1 -0
- package/package.json +19 -2
- package/src/core/Hash.ts +59 -0
- package/src/core/_test/Block.test.ts +9 -2
- package/src/core/_test/Hash.test-d.ts +18 -0
- package/src/core/_test/Hash.test.ts +100 -1
- package/src/core/_test/Hash.vectors.test.ts +1 -2
- package/src/core/_test/Transaction.test.ts +11 -2
- package/src/node/Hash.ts +3 -3
- package/src/node/_test/Hash.test-d.ts +1 -0
- package/src/trusted-setups/Paths.ts +1 -0
- package/src/trusted-setups/README.md +39 -0
- package/src/trusted-setups/Setups.ts +46 -0
- package/src/trusted-setups/_test/Paths.test.ts +16 -0
- package/src/trusted-setups/_test/Setups.test-d.ts +7 -0
- package/src/trusted-setups/_test/Setups.test.ts +43 -0
- package/src/trusted-setups/_test/index.test.ts +1 -0
- package/src/trusted-setups/index.ts +21 -0
- package/src/trusted-setups/internal/paths.ts +4 -1
- package/src/trusted-setups/internal/setups/mainnet.source.json +7 -0
- package/src/trusted-setups/internal/setups/mainnet.ts +16 -0
- package/src/version.ts +1 -1
- package/src/wasm/Engine.ts +8 -6
- package/src/wasm/Keystore.bench.ts +26 -0
- package/src/wasm/Keystore.ts +32 -14
- package/src/wasm/Kzg.bench.ts +63 -0
- package/src/wasm/Kzg.ts +518 -0
- package/src/wasm/Secp256k1.bench.ts +79 -0
- package/src/wasm/Secp256k1.ts +248 -0
- package/src/wasm/_test/Engine.test-d.ts +1 -0
- package/src/wasm/_test/Hash.browser.test.ts +3 -0
- package/src/wasm/_test/Hash.test-d.ts +1 -0
- package/src/wasm/_test/Hash.test.ts +2 -2
- package/src/wasm/_test/Keystore.browser.test.ts +31 -0
- package/src/wasm/_test/Keystore.fuzz.ts +50 -0
- package/src/wasm/_test/Keystore.test-d.ts +10 -1
- package/src/wasm/_test/Keystore.test.ts +289 -4
- package/src/wasm/_test/Kzg.test-d.ts +19 -0
- package/src/wasm/_test/Kzg.test.ts +220 -0
- package/src/wasm/_test/Runtime.test.ts +9 -0
- package/src/wasm/_test/Secp256k1.test-d.ts +35 -0
- package/src/wasm/_test/Secp256k1.test.ts +354 -0
- package/src/wasm/index.ts +46 -1
- package/src/wasm/internal/instantiate.ts +29 -9
- package/src/wasm/internal/keystore.ts +19 -0
- package/src/wasm/internal/kzg.ts +50 -0
- package/src/wasm/internal/kzg.wasm.ts +11 -0
- package/src/wasm/internal/scrypt.ts +131 -0
- package/src/wasm/internal/scrypt.wasm.ts +17 -0
- package/src/wasm/internal/secp256k1.ts +62 -0
- package/src/wasm/internal/secp256k1.wasm.ts +11 -0
- package/wasm/vendor/c-kzg-4844/LICENSE +201 -0
- package/wasm/vendor/c-kzg-4844/blst/LICENSE +201 -0
- package/src/trusted-setups/internal/setups/mainnet.txt +0 -4163
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js'
|
|
2
|
+
import { describe, expect, test } from 'vp/test'
|
|
3
|
+
import * as Secp256k1 from '../Secp256k1.js'
|
|
4
|
+
import * as secp256k1_internal from '../internal/secp256k1.js'
|
|
5
|
+
|
|
6
|
+
const privateKey = fromHex(
|
|
7
|
+
'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
|
|
8
|
+
)
|
|
9
|
+
const privateKeyB = fromHex(
|
|
10
|
+
'59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
|
|
11
|
+
)
|
|
12
|
+
const curveOrder = BigInt(
|
|
13
|
+
'0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141',
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
describe('engine', () => {
|
|
17
|
+
test('behavior: exposes only deterministic primitives', async () => {
|
|
18
|
+
const engine = await Secp256k1.engine()
|
|
19
|
+
|
|
20
|
+
expect(Object.keys(engine).sort()).toMatchInlineSnapshot(`
|
|
21
|
+
[
|
|
22
|
+
"getPublicKey",
|
|
23
|
+
"getSharedSecret",
|
|
24
|
+
"recoverPublicKey",
|
|
25
|
+
"sign",
|
|
26
|
+
"verify",
|
|
27
|
+
]
|
|
28
|
+
`)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('behavior: matches public keys and compressed shared points', async () => {
|
|
32
|
+
const engine = await Secp256k1.engine()
|
|
33
|
+
const publicKey = secp256k1.getPublicKey(privateKey, false)
|
|
34
|
+
const publicKeyB = secp256k1.getPublicKey(privateKeyB, false)
|
|
35
|
+
const compressedPublicKeyB = secp256k1.getPublicKey(privateKeyB, true)
|
|
36
|
+
const expected = secp256k1.getSharedSecret(privateKey, publicKeyB, true)
|
|
37
|
+
|
|
38
|
+
expect(engine.getPublicKey(privateKey)).toEqual(publicKey)
|
|
39
|
+
expect(engine.getSharedSecret(privateKey, publicKeyB)).toEqual(expected)
|
|
40
|
+
expect(engine.getSharedSecret(privateKey, compressedPublicKeyB)).toEqual(
|
|
41
|
+
expected,
|
|
42
|
+
)
|
|
43
|
+
expect(engine.getSharedSecret(privateKeyB, publicKey)).toEqual(expected)
|
|
44
|
+
expect(expected).toHaveLength(33)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('behavior: matches signing across payload and entropy boundaries', async () => {
|
|
48
|
+
const engine = await Secp256k1.engine()
|
|
49
|
+
const publicKey = secp256k1.getPublicKey(privateKey, false)
|
|
50
|
+
|
|
51
|
+
for (const size of [0, 1, 31, 32, 33, 63, 64, 65, 127, 1024, 8192])
|
|
52
|
+
for (const prehash of [false, true])
|
|
53
|
+
for (const extraEntropy of [
|
|
54
|
+
false,
|
|
55
|
+
new Uint8Array(),
|
|
56
|
+
Uint8Array.of(1),
|
|
57
|
+
new Uint8Array(32).fill(7),
|
|
58
|
+
new Uint8Array(100).fill(9),
|
|
59
|
+
] as const) {
|
|
60
|
+
const payload = bytes(size)
|
|
61
|
+
const signature = engine.sign(payload, privateKey, {
|
|
62
|
+
extraEntropy,
|
|
63
|
+
prehash,
|
|
64
|
+
})
|
|
65
|
+
const expected = secp256k1.sign(payload, privateKey, {
|
|
66
|
+
extraEntropy,
|
|
67
|
+
format: 'recovered',
|
|
68
|
+
lowS: true,
|
|
69
|
+
prehash,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
expect(signature).toEqual(expected)
|
|
73
|
+
expect(
|
|
74
|
+
engine.verify(signature.slice(1), payload, publicKey, { prehash }),
|
|
75
|
+
).toBe(true)
|
|
76
|
+
expect(signature[0]).toBeOneOf([0, 1])
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('behavior: generates host entropy for hedged signing', async () => {
|
|
81
|
+
const engine = await Secp256k1.engine()
|
|
82
|
+
const payload = bytes(32)
|
|
83
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
84
|
+
const signatureA = engine.sign(payload, privateKey, {
|
|
85
|
+
extraEntropy: true,
|
|
86
|
+
prehash: false,
|
|
87
|
+
})
|
|
88
|
+
const signatureB = engine.sign(payload, privateKey, {
|
|
89
|
+
extraEntropy: true,
|
|
90
|
+
prehash: false,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
expect(signatureA).not.toEqual(signatureB)
|
|
94
|
+
expect(
|
|
95
|
+
engine.verify(signatureA.slice(1), payload, publicKey, {
|
|
96
|
+
prehash: false,
|
|
97
|
+
}),
|
|
98
|
+
).toBe(true)
|
|
99
|
+
expect(
|
|
100
|
+
engine.verify(signatureB.slice(1), payload, publicKey, {
|
|
101
|
+
prehash: false,
|
|
102
|
+
}),
|
|
103
|
+
).toBe(true)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('behavior: recovers the public key with both parities', async () => {
|
|
107
|
+
const engine = await Secp256k1.engine()
|
|
108
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
109
|
+
const signatures = new Map<
|
|
110
|
+
number,
|
|
111
|
+
{ payload: Uint8Array; signature: Uint8Array }
|
|
112
|
+
>()
|
|
113
|
+
|
|
114
|
+
for (let index = 0; signatures.size < 2; index++) {
|
|
115
|
+
const payload = bytes(32, index)
|
|
116
|
+
const signature = engine.sign(payload, privateKey, {
|
|
117
|
+
extraEntropy: false,
|
|
118
|
+
prehash: false,
|
|
119
|
+
})
|
|
120
|
+
signatures.set(signature[0]!, { payload, signature })
|
|
121
|
+
if (index > 100)
|
|
122
|
+
throw new Error('could not produce both recovery parities')
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (const { payload, signature } of signatures.values())
|
|
126
|
+
expect(engine.recoverPublicKey(signature, payload)).toEqual(publicKey)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('behavior: rejects high-S signatures', async () => {
|
|
130
|
+
const engine = await Secp256k1.engine()
|
|
131
|
+
const payload = bytes(32)
|
|
132
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
133
|
+
const signature = engine
|
|
134
|
+
.sign(payload, privateKey, {
|
|
135
|
+
extraEntropy: false,
|
|
136
|
+
prehash: false,
|
|
137
|
+
})
|
|
138
|
+
.slice(1)
|
|
139
|
+
const highS = signature.slice()
|
|
140
|
+
highS.set(toBytes(curveOrder - toBigInt(signature.slice(32))), 32)
|
|
141
|
+
|
|
142
|
+
expect(
|
|
143
|
+
engine.verify(signature, payload, publicKey, { prehash: false }),
|
|
144
|
+
).toBe(true)
|
|
145
|
+
expect(engine.verify(highS, payload, publicKey, { prehash: false })).toBe(
|
|
146
|
+
false,
|
|
147
|
+
)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('behavior: matches large payload boundaries', async () => {
|
|
151
|
+
const engine = await Secp256k1.engine()
|
|
152
|
+
const payload = bytes(8193)
|
|
153
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
154
|
+
const truncatedSignature = secp256k1.sign(
|
|
155
|
+
payload.slice(0, 32),
|
|
156
|
+
privateKey,
|
|
157
|
+
{
|
|
158
|
+
extraEntropy: false,
|
|
159
|
+
format: 'compact',
|
|
160
|
+
lowS: true,
|
|
161
|
+
prehash: false,
|
|
162
|
+
},
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
expect(() =>
|
|
166
|
+
engine.sign(payload, privateKey, {
|
|
167
|
+
extraEntropy: false,
|
|
168
|
+
prehash: false,
|
|
169
|
+
}),
|
|
170
|
+
).toThrowError(
|
|
171
|
+
'Secp256k1 unhashed payload must not exceed 8192 bytes, got 8193',
|
|
172
|
+
)
|
|
173
|
+
expect(
|
|
174
|
+
engine.verify(new Uint8Array(64), payload, publicKey, {
|
|
175
|
+
prehash: false,
|
|
176
|
+
}),
|
|
177
|
+
).toBe(false)
|
|
178
|
+
expect(
|
|
179
|
+
secp256k1.verify(truncatedSignature, payload, publicKey, {
|
|
180
|
+
lowS: true,
|
|
181
|
+
prehash: false,
|
|
182
|
+
}),
|
|
183
|
+
).toBe(false)
|
|
184
|
+
expect(
|
|
185
|
+
engine.verify(truncatedSignature, payload, publicKey, { prehash: false }),
|
|
186
|
+
).toBe(false)
|
|
187
|
+
expect(() =>
|
|
188
|
+
engine.recoverPublicKey(new Uint8Array(65), payload),
|
|
189
|
+
).toThrowError(
|
|
190
|
+
'Secp256k1 unhashed payload must not exceed 8192 bytes, got 8193',
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
const signature = engine.sign(payload, privateKey, {
|
|
194
|
+
extraEntropy: false,
|
|
195
|
+
prehash: true,
|
|
196
|
+
})
|
|
197
|
+
expect(
|
|
198
|
+
engine.verify(signature.slice(1), payload, publicKey, { prehash: true }),
|
|
199
|
+
).toBe(true)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test('behavior: rejects malformed keys and recovered signatures', async () => {
|
|
203
|
+
const engine = await Secp256k1.engine()
|
|
204
|
+
const publicKey = engine.getPublicKey(privateKey)
|
|
205
|
+
const payload = bytes(32)
|
|
206
|
+
|
|
207
|
+
for (const size of [0, 1, 31, 33, 64, 65]) {
|
|
208
|
+
if (size !== 32) {
|
|
209
|
+
expect(() => engine.getPublicKey(new Uint8Array(size))).toThrowError(
|
|
210
|
+
`Secp256k1 private key must be 32 bytes, got ${size}`,
|
|
211
|
+
)
|
|
212
|
+
expect(() =>
|
|
213
|
+
engine.sign(payload, new Uint8Array(size), {
|
|
214
|
+
extraEntropy: false,
|
|
215
|
+
prehash: false,
|
|
216
|
+
}),
|
|
217
|
+
).toThrowError(`Secp256k1 private key must be 32 bytes, got ${size}`)
|
|
218
|
+
}
|
|
219
|
+
if (size !== 64)
|
|
220
|
+
expect(() =>
|
|
221
|
+
engine.verify(new Uint8Array(size), payload, publicKey, {
|
|
222
|
+
prehash: false,
|
|
223
|
+
}),
|
|
224
|
+
).toThrowError(`Secp256k1 signature must be 64 bytes, got ${size}`)
|
|
225
|
+
if (size !== 65)
|
|
226
|
+
expect(() =>
|
|
227
|
+
engine.recoverPublicKey(new Uint8Array(size), payload),
|
|
228
|
+
).toThrowError(`Secp256k1 signature must be 65 bytes, got ${size}`)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (const size of [0, 1, 32, 34, 64, 66])
|
|
232
|
+
expect(() =>
|
|
233
|
+
engine.getSharedSecret(privateKey, new Uint8Array(size)),
|
|
234
|
+
).toThrowError(`Secp256k1 public key must be 33 or 65 bytes, got ${size}`)
|
|
235
|
+
|
|
236
|
+
const invalidPrivateKeys = [
|
|
237
|
+
new Uint8Array(32),
|
|
238
|
+
toBytes(curveOrder),
|
|
239
|
+
toBytes(curveOrder + 1n),
|
|
240
|
+
]
|
|
241
|
+
for (const value of invalidPrivateKeys) {
|
|
242
|
+
expect(() => engine.getPublicKey(value)).toThrow(
|
|
243
|
+
Secp256k1.InvalidInputError,
|
|
244
|
+
)
|
|
245
|
+
expect(() => engine.getSharedSecret(value, publicKey)).toThrow(
|
|
246
|
+
Secp256k1.InvalidInputError,
|
|
247
|
+
)
|
|
248
|
+
expect(() =>
|
|
249
|
+
engine.sign(payload, value, {
|
|
250
|
+
extraEntropy: false,
|
|
251
|
+
prehash: false,
|
|
252
|
+
}),
|
|
253
|
+
).toThrow(Secp256k1.InvalidInputError)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for (const publicKeySize of [33, 65]) {
|
|
257
|
+
const invalidPublicKey = new Uint8Array(publicKeySize)
|
|
258
|
+
expect(() =>
|
|
259
|
+
engine.getSharedSecret(privateKey, invalidPublicKey),
|
|
260
|
+
).toThrow(Secp256k1.InvalidInputError)
|
|
261
|
+
expect(
|
|
262
|
+
engine.verify(new Uint8Array(64), payload, invalidPublicKey, {
|
|
263
|
+
prehash: false,
|
|
264
|
+
}),
|
|
265
|
+
).toBe(false)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const invalidRecovery = new Uint8Array(65)
|
|
269
|
+
invalidRecovery[0] = 2
|
|
270
|
+
expect(() => engine.recoverPublicKey(invalidRecovery, payload)).toThrow(
|
|
271
|
+
Secp256k1.InvalidInputError,
|
|
272
|
+
)
|
|
273
|
+
expect(() => engine.recoverPublicKey(new Uint8Array(65), payload)).toThrow(
|
|
274
|
+
Secp256k1.InvalidInputError,
|
|
275
|
+
)
|
|
276
|
+
expect(
|
|
277
|
+
engine.verify(new Uint8Array(64), payload, publicKey, {
|
|
278
|
+
prehash: false,
|
|
279
|
+
}),
|
|
280
|
+
).toBe(false)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
test('behavior: preserves subviews, input ownership, and output ownership', async () => {
|
|
284
|
+
const engine = await Secp256k1.engine()
|
|
285
|
+
const key = offsetView(privateKey)
|
|
286
|
+
const payload = offsetView(bytes(257))
|
|
287
|
+
const publicKey = offsetView(engine.getPublicKey(key))
|
|
288
|
+
const before = {
|
|
289
|
+
key: key.slice(),
|
|
290
|
+
payload: payload.slice(),
|
|
291
|
+
publicKey: publicKey.slice(),
|
|
292
|
+
}
|
|
293
|
+
const signature = engine.sign(payload, key, {
|
|
294
|
+
extraEntropy: false,
|
|
295
|
+
prehash: false,
|
|
296
|
+
})
|
|
297
|
+
const outputs = [
|
|
298
|
+
engine.getPublicKey(key),
|
|
299
|
+
engine.getSharedSecret(key, publicKey),
|
|
300
|
+
signature,
|
|
301
|
+
engine.recoverPublicKey(signature, payload),
|
|
302
|
+
]
|
|
303
|
+
const snapshots = outputs.map((output) => output.slice())
|
|
304
|
+
|
|
305
|
+
engine.sign(new Uint8Array(1024 * 1024), key, {
|
|
306
|
+
extraEntropy: false,
|
|
307
|
+
prehash: true,
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
expect(outputs).toEqual(snapshots)
|
|
311
|
+
expect({ key, payload, publicKey }).toEqual(before)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('behavior: clears the shared staging region', async () => {
|
|
315
|
+
const engine = await Secp256k1.engine()
|
|
316
|
+
const payload = new Uint8Array(1024).fill(0xa5)
|
|
317
|
+
const entropy = new Uint8Array(17).fill(0x5a)
|
|
318
|
+
engine.sign(payload, privateKey, {
|
|
319
|
+
extraEntropy: entropy,
|
|
320
|
+
prehash: false,
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
const module = await secp256k1_internal.load()
|
|
324
|
+
const size = payload.length + 32 + entropy.length + 65
|
|
325
|
+
expect(
|
|
326
|
+
module
|
|
327
|
+
.view()
|
|
328
|
+
.subarray(module.heapBase, module.heapBase + size)
|
|
329
|
+
.every((byte) => byte === 0),
|
|
330
|
+
).toBe(true)
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
function bytes(length: number, offset = length): Uint8Array {
|
|
335
|
+
return Uint8Array.from({ length }, (_, index) => (index * 29 + offset) % 251)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function fromHex(value: string): Uint8Array {
|
|
339
|
+
return Uint8Array.from(Buffer.from(value, 'hex'))
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function offsetView(value: Uint8Array): Uint8Array {
|
|
343
|
+
const bytes = new Uint8Array(value.length + 4).fill(0xff)
|
|
344
|
+
bytes.set(value, 2)
|
|
345
|
+
return bytes.subarray(2, -2)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function toBigInt(value: Uint8Array): bigint {
|
|
349
|
+
return BigInt(`0x${Buffer.from(value).toString('hex')}`)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function toBytes(value: bigint): Uint8Array {
|
|
353
|
+
return fromHex(value.toString(16).padStart(64, '0'))
|
|
354
|
+
}
|
package/src/wasm/index.ts
CHANGED
|
@@ -64,7 +64,8 @@ export * as Ed25519 from './Ed25519.js'
|
|
|
64
64
|
export * as Hash from './Hash.js'
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* WASM
|
|
67
|
+
* WASM implementations of Ox's synchronous PBKDF2-HMAC-SHA256 and scrypt
|
|
68
|
+
* primitives.
|
|
68
69
|
*
|
|
69
70
|
* @example
|
|
70
71
|
* ```ts twoslash
|
|
@@ -81,6 +82,29 @@ export * as Hash from './Hash.js'
|
|
|
81
82
|
*/
|
|
82
83
|
export * as Keystore from './Keystore.js'
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* WASM implementation of Ox's KZG interface, backed by c-kzg-4844.
|
|
87
|
+
*
|
|
88
|
+
* This module is not installed into [`Engine`](/api/Engine). Create an explicit
|
|
89
|
+
* instance, pass it anywhere that accepts `Kzg.Kzg`, and dispose it when done.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts twoslash
|
|
93
|
+
* // @noErrors
|
|
94
|
+
* import { Setups } from 'ox/trusted-setups'
|
|
95
|
+
* import { Kzg } from 'ox/wasm'
|
|
96
|
+
*
|
|
97
|
+
* const kzg = await Kzg.create({
|
|
98
|
+
* trustedSetup: Setups.mainnet
|
|
99
|
+
* })
|
|
100
|
+
*
|
|
101
|
+
* kzg.dispose()
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @category Crypto
|
|
105
|
+
*/
|
|
106
|
+
export * as Kzg from './Kzg.js'
|
|
107
|
+
|
|
84
108
|
/**
|
|
85
109
|
* WASM implementation of Ox's BIP-39 seed derivation.
|
|
86
110
|
*
|
|
@@ -101,6 +125,27 @@ export * as Keystore from './Keystore.js'
|
|
|
101
125
|
*/
|
|
102
126
|
export * as Mnemonic from './Mnemonic.js'
|
|
103
127
|
|
|
128
|
+
/**
|
|
129
|
+
* WASM implementations of deterministic Secp256k1 primitives.
|
|
130
|
+
*
|
|
131
|
+
* This provider is opt-in because its embedded libsecp256k1 artifact is larger
|
|
132
|
+
* than the modules installed by [`Engine.install`](/api/Engine/install).
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts twoslash
|
|
136
|
+
* // @noErrors
|
|
137
|
+
* import { Engine } from 'ox'
|
|
138
|
+
* import { Secp256k1 } from 'ox/wasm'
|
|
139
|
+
*
|
|
140
|
+
* await Engine.install({ Secp256k1: Secp256k1.engine() })
|
|
141
|
+
*
|
|
142
|
+
* Secp256k1.getPublicKey({ privateKey: '0x...' })
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @category Crypto
|
|
146
|
+
*/
|
|
147
|
+
export * as Secp256k1 from './Secp256k1.js'
|
|
148
|
+
|
|
104
149
|
/**
|
|
105
150
|
* WASM implementations of Ox's supported X25519 primitives.
|
|
106
151
|
*
|
|
@@ -48,18 +48,23 @@ function decode(base64: string): Uint8Array {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* Compiles a base64-encoded WASM module without instantiating it.
|
|
52
52
|
*
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export async function compile(base64: string): Promise<WebAssembly.Module> {
|
|
56
|
+
return WebAssembly.compile(decode(base64))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Instantiates a compiled WASM module.
|
|
56
61
|
*
|
|
57
62
|
* @internal
|
|
58
63
|
*/
|
|
59
|
-
export async function
|
|
60
|
-
|
|
61
|
-
): Promise<Module<exports>> {
|
|
62
|
-
const
|
|
64
|
+
export async function instantiateModule<
|
|
65
|
+
exports extends Record<string, unknown>,
|
|
66
|
+
>(compiled: WebAssembly.Module): Promise<Module<exports>> {
|
|
67
|
+
const instance = await WebAssembly.instantiate(compiled, {})
|
|
63
68
|
const exports = instance.exports as {
|
|
64
69
|
heap_base(): number
|
|
65
70
|
memory: WebAssembly.Memory
|
|
@@ -94,6 +99,21 @@ export async function instantiate<exports extends Record<string, unknown>>(
|
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Instantiates a base64-encoded WASM module.
|
|
104
|
+
*
|
|
105
|
+
* Always asynchronous: browsers refuse to compile modules larger than a few
|
|
106
|
+
* kilobytes synchronously on the main thread, so there is no synchronous path to
|
|
107
|
+
* fall back to. Callers await this once and then call the module synchronously.
|
|
108
|
+
*
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
export async function instantiate<exports extends Record<string, unknown>>(
|
|
112
|
+
base64: string,
|
|
113
|
+
): Promise<Module<exports>> {
|
|
114
|
+
return instantiateModule(await compile(base64))
|
|
115
|
+
}
|
|
116
|
+
|
|
97
117
|
/**
|
|
98
118
|
* Memoizes a loader so concurrent callers compile the module once.
|
|
99
119
|
*
|
|
@@ -128,7 +148,7 @@ export class MemoryError extends Errors.BaseError<Error> {
|
|
|
128
148
|
cause,
|
|
129
149
|
metaMessages: [
|
|
130
150
|
`Required: ${bytes} bytes`,
|
|
131
|
-
'The input may be too large for this runtime. Reset the engine slot with `Engine.reset` to fall back to the default implementation.',
|
|
151
|
+
'The input or requested workspace may be too large for this runtime. Reset the engine slot with `Engine.reset` to fall back to the default implementation.',
|
|
132
152
|
],
|
|
133
153
|
})
|
|
134
154
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type * as Engine from '../../core/Engine.js'
|
|
2
|
+
import * as hashes from './hashes.js'
|
|
3
|
+
import * as pbkdf2 from './pbkdf2.js'
|
|
4
|
+
|
|
5
|
+
/** Compiles the aggregate engine's synchronous PBKDF2 implementation. */
|
|
6
|
+
export async function engine(): Promise<engine.ReturnType> {
|
|
7
|
+
const module = await hashes.load()
|
|
8
|
+
return {
|
|
9
|
+
pbkdf2Sha256: (password, salt, options) =>
|
|
10
|
+
pbkdf2.pbkdf2Sha256(module, password, salt, options),
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export declare namespace engine {
|
|
15
|
+
/** The aggregate WASM engine's `Keystore` primitives. */
|
|
16
|
+
type ReturnType = {
|
|
17
|
+
pbkdf2Sha256: NonNullable<Engine.Keystore['pbkdf2Sha256']>
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compile,
|
|
3
|
+
instantiateModule,
|
|
4
|
+
memoize,
|
|
5
|
+
type Module,
|
|
6
|
+
} from './instantiate.js'
|
|
7
|
+
import { wasmBase64 } from './kzg.wasm.js'
|
|
8
|
+
|
|
9
|
+
/** Exports supplied by the dedicated c-kzg artifact. @internal */
|
|
10
|
+
export type Exports = {
|
|
11
|
+
alloc(size: number): number
|
|
12
|
+
dealloc(ptr: number): void
|
|
13
|
+
kzg_blob_to_kzg_commitment(out: number, blob: number): number
|
|
14
|
+
kzg_compute_cells(cells: number, blob: number): number
|
|
15
|
+
kzg_compute_cells_and_kzg_proofs(
|
|
16
|
+
cells: number,
|
|
17
|
+
proofs: number,
|
|
18
|
+
blob: number,
|
|
19
|
+
): number
|
|
20
|
+
kzg_dispose(): void
|
|
21
|
+
kzg_initialize(
|
|
22
|
+
g1Monomial: number,
|
|
23
|
+
g1Lagrange: number,
|
|
24
|
+
g2Monomial: number,
|
|
25
|
+
precompute: number,
|
|
26
|
+
): number
|
|
27
|
+
kzg_recover_cells_and_kzg_proofs(
|
|
28
|
+
recoveredCells: number,
|
|
29
|
+
recoveredProofs: number,
|
|
30
|
+
cellIndices: number,
|
|
31
|
+
cells: number,
|
|
32
|
+
count: number,
|
|
33
|
+
): number
|
|
34
|
+
kzg_verify_cell_kzg_proof_batch(
|
|
35
|
+
verified: number,
|
|
36
|
+
commitments: number,
|
|
37
|
+
cellIndices: number,
|
|
38
|
+
cells: number,
|
|
39
|
+
proofs: number,
|
|
40
|
+
count: number,
|
|
41
|
+
): number
|
|
42
|
+
zero(ptr: number, length: number): void
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const load = memoize(() => compile(wasmBase64))
|
|
46
|
+
|
|
47
|
+
/** Instantiates an independently owned c-kzg module. @internal */
|
|
48
|
+
export async function instantiate(): Promise<Module<Exports>> {
|
|
49
|
+
return instantiateModule(await load())
|
|
50
|
+
}
|