ox 1.1.0 → 1.1.2
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/Engine.d.ts +43 -11
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +40 -10
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/internal/engine.d.ts +8 -8
- package/dist/core/internal/engine.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/node/Ed25519.d.ts +33 -0
- package/dist/node/Ed25519.d.ts.map +1 -0
- package/dist/node/Ed25519.js +67 -0
- package/dist/node/Ed25519.js.map +1 -0
- package/dist/node/Engine.d.ts +29 -16
- package/dist/node/Engine.d.ts.map +1 -1
- package/dist/node/Engine.js +38 -14
- package/dist/node/Engine.js.map +1 -1
- package/dist/node/Hash.d.ts +14 -16
- package/dist/node/Hash.d.ts.map +1 -1
- package/dist/node/Hash.js +14 -16
- package/dist/node/Hash.js.map +1 -1
- package/dist/node/Keystore.d.ts +36 -0
- package/dist/node/Keystore.d.ts.map +1 -0
- package/dist/node/Keystore.js +72 -0
- package/dist/node/Keystore.js.map +1 -0
- package/dist/node/Mnemonic.d.ts +35 -0
- package/dist/node/Mnemonic.d.ts.map +1 -0
- package/dist/node/Mnemonic.js +46 -0
- package/dist/node/Mnemonic.js.map +1 -0
- package/dist/node/P256.d.ts +32 -0
- package/dist/node/P256.d.ts.map +1 -0
- package/dist/node/P256.js +34 -0
- package/dist/node/P256.js.map +1 -0
- package/dist/node/X25519.d.ts +31 -0
- package/dist/node/X25519.d.ts.map +1 -0
- package/dist/node/X25519.js +71 -0
- package/dist/node/X25519.js.map +1 -0
- package/dist/node/index.d.ts +95 -8
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +95 -8
- package/dist/node/index.js.map +1 -1
- package/dist/wasm/Ed25519.d.ts +35 -0
- package/dist/wasm/Ed25519.d.ts.map +1 -0
- package/dist/wasm/Ed25519.js +88 -0
- package/dist/wasm/Ed25519.js.map +1 -0
- package/dist/wasm/Engine.d.ts +35 -26
- package/dist/wasm/Engine.d.ts.map +1 -1
- package/dist/wasm/Engine.js +44 -25
- package/dist/wasm/Engine.js.map +1 -1
- package/dist/wasm/Hash.d.ts +13 -19
- package/dist/wasm/Hash.d.ts.map +1 -1
- package/dist/wasm/Hash.js +21 -27
- package/dist/wasm/Hash.js.map +1 -1
- package/dist/wasm/Keystore.d.ts +37 -0
- package/dist/wasm/Keystore.d.ts.map +1 -0
- package/dist/wasm/Keystore.js +34 -0
- package/dist/wasm/Keystore.js.map +1 -0
- package/dist/wasm/Mnemonic.d.ts +33 -0
- package/dist/wasm/Mnemonic.d.ts.map +1 -0
- package/dist/wasm/Mnemonic.js +30 -0
- package/dist/wasm/Mnemonic.js.map +1 -0
- package/dist/wasm/X25519.d.ts +33 -0
- package/dist/wasm/X25519.d.ts.map +1 -0
- package/dist/wasm/X25519.js +53 -0
- package/dist/wasm/X25519.js.map +1 -0
- package/dist/wasm/index.d.ts +81 -12
- package/dist/wasm/index.d.ts.map +1 -1
- package/dist/wasm/index.js +81 -12
- package/dist/wasm/index.js.map +1 -1
- package/dist/wasm/internal/blake3.d.ts +11 -0
- package/dist/wasm/internal/blake3.d.ts.map +1 -0
- package/dist/wasm/internal/blake3.js +31 -0
- package/dist/wasm/internal/blake3.js.map +1 -0
- package/dist/wasm/internal/blake3.wasm.d.ts +3 -0
- package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/blake3.wasm.js +10 -0
- package/dist/wasm/internal/blake3.wasm.js.map +1 -0
- package/dist/wasm/internal/crypto25519.d.ts +16 -0
- package/dist/wasm/internal/crypto25519.d.ts.map +1 -0
- package/dist/wasm/internal/crypto25519.js +5 -0
- package/dist/wasm/internal/crypto25519.js.map +1 -0
- package/dist/wasm/internal/crypto25519.wasm.d.ts +3 -0
- package/dist/wasm/internal/crypto25519.wasm.d.ts.map +1 -0
- package/dist/wasm/internal/crypto25519.wasm.js +10 -0
- package/dist/wasm/internal/crypto25519.wasm.js.map +1 -0
- package/dist/wasm/internal/ed25519.d.ts +4 -0
- package/dist/wasm/internal/ed25519.d.ts.map +1 -0
- package/dist/wasm/internal/ed25519.js +21 -0
- package/dist/wasm/internal/ed25519.js.map +1 -0
- package/dist/wasm/internal/hashes.d.ts +12 -0
- package/dist/wasm/internal/hashes.d.ts.map +1 -0
- package/dist/wasm/internal/hashes.js +8 -0
- package/dist/wasm/internal/hashes.js.map +1 -0
- package/dist/wasm/internal/hashes.wasm.d.ts +4 -2
- package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
- package/dist/wasm/internal/hashes.wasm.js +4 -2
- package/dist/wasm/internal/hashes.wasm.js.map +1 -1
- package/dist/wasm/internal/mnemonic.d.ts +4 -0
- package/dist/wasm/internal/mnemonic.d.ts.map +1 -0
- package/dist/wasm/internal/mnemonic.js +45 -0
- package/dist/wasm/internal/mnemonic.js.map +1 -0
- package/dist/wasm/internal/pbkdf2.d.ts +12 -0
- package/dist/wasm/internal/pbkdf2.d.ts.map +1 -0
- package/dist/wasm/internal/pbkdf2.js +51 -0
- package/dist/wasm/internal/pbkdf2.js.map +1 -0
- package/dist/wasm/internal/x25519.d.ts +4 -0
- package/dist/wasm/internal/x25519.d.ts.map +1 -0
- package/dist/wasm/internal/x25519.js +21 -0
- package/dist/wasm/internal/x25519.js.map +1 -0
- package/package.json +46 -1
- package/src/core/Engine.ts +84 -11
- package/src/core/_test/Engine.test-d.ts +56 -0
- package/src/core/_test/Engine.test.ts +95 -0
- package/src/core/_test/Hash.vectors.test.ts +8 -0
- package/src/core/internal/engine.ts +8 -8
- package/src/index.ts +5 -2
- package/src/node/Ed25519.ts +87 -0
- package/src/node/Engine.ts +79 -23
- package/src/node/Hash.ts +22 -27
- package/src/node/Keystore.ts +103 -0
- package/src/node/Mnemonic.ts +68 -0
- package/src/node/P256.ts +48 -0
- package/src/node/X25519.ts +93 -0
- package/src/node/_test/Curves.fuzz.ts +76 -0
- package/src/node/_test/Ed25519.test-d.ts +35 -0
- package/src/node/_test/Ed25519.test.ts +220 -0
- package/src/node/_test/Engine.test-d.ts +28 -0
- package/src/node/_test/Engine.test.ts +62 -22
- package/src/node/_test/Hash.test-d.ts +15 -9
- package/src/node/_test/Hash.test.ts +10 -11
- package/src/node/_test/Keystore.fuzz.ts +58 -0
- package/src/node/_test/Keystore.test-d.ts +43 -0
- package/src/node/_test/Keystore.test.ts +198 -0
- package/src/node/_test/Mnemonic.fuzz.ts +31 -0
- package/src/node/_test/Mnemonic.test-d.ts +21 -0
- package/src/node/_test/Mnemonic.test.ts +98 -0
- package/src/node/_test/P256.test-d.ts +29 -0
- package/src/node/_test/P256.test.ts +102 -0
- package/src/node/_test/X25519.test-d.ts +28 -0
- package/src/node/_test/X25519.test.ts +239 -0
- package/src/node/index.ts +100 -8
- package/src/version.ts +1 -1
- package/src/wasm/Ed25519.ts +114 -0
- package/src/wasm/Engine.ts +76 -31
- package/src/wasm/Hash.bench.ts +2 -2
- package/src/wasm/Hash.ts +29 -50
- package/src/wasm/Keystore.ts +48 -0
- package/src/wasm/Mnemonic.ts +44 -0
- package/src/wasm/X25519.ts +70 -0
- package/src/wasm/_test/Crypto25519.conformance.ts +57 -0
- package/src/wasm/_test/Crypto25519.fuzz.ts +187 -0
- package/src/wasm/_test/Ed25519.test-d.ts +28 -0
- package/src/wasm/_test/Ed25519.test.ts +168 -0
- package/src/wasm/_test/Ed25519.vectors.test.ts +73 -0
- package/src/wasm/_test/Engine.test-d.ts +28 -0
- package/src/wasm/_test/Engine.test.ts +59 -25
- package/src/wasm/_test/Hash.browser.test.ts +11 -7
- package/src/wasm/_test/Hash.fuzz.ts +58 -23
- package/src/wasm/_test/Hash.test-d.ts +15 -10
- package/src/wasm/_test/Hash.test.ts +148 -48
- package/src/wasm/_test/Hash.vectors.test.ts +15 -8
- package/src/wasm/_test/Keystore.browser.test.ts +42 -0
- package/src/wasm/_test/Keystore.fuzz.ts +113 -0
- package/src/wasm/_test/Keystore.test-d.ts +33 -0
- package/src/wasm/_test/Keystore.test.ts +192 -0
- package/src/wasm/_test/Keystore.vectors.test.ts +21 -0
- package/src/wasm/_test/Mnemonic.test-d.ts +17 -0
- package/src/wasm/_test/Mnemonic.test.ts +110 -0
- package/src/wasm/_test/X25519.test-d.ts +21 -0
- package/src/wasm/_test/X25519.test.ts +194 -0
- package/src/wasm/_test/fixtures.ts +4 -0
- package/src/wasm/index.ts +85 -12
- package/src/wasm/internal/blake3.ts +45 -0
- package/src/wasm/internal/blake3.wasm.ts +11 -0
- package/src/wasm/internal/crypto25519.ts +43 -0
- package/src/wasm/internal/crypto25519.wasm.ts +11 -0
- package/src/wasm/internal/ed25519.ts +31 -0
- package/src/wasm/internal/hashes.ts +29 -0
- package/src/wasm/internal/hashes.wasm.ts +5 -2
- package/src/wasm/internal/mnemonic.ts +56 -0
- package/src/wasm/internal/pbkdf2.ts +86 -0
- package/src/wasm/internal/x25519.ts +29 -0
|
@@ -17,6 +17,62 @@ type Unlisted = {
|
|
|
17
17
|
>
|
|
18
18
|
}[keyof Engine.Engine]
|
|
19
19
|
|
|
20
|
+
describe('install', () => {
|
|
21
|
+
test('accepts raw and promised slots and preserves their resolved shape', () => {
|
|
22
|
+
const keccak256 = (input: Uint8Array) => input
|
|
23
|
+
const toSeed = (_mnemonic: string) => new Uint8Array(64)
|
|
24
|
+
|
|
25
|
+
expectTypeOf(
|
|
26
|
+
Engine.install({
|
|
27
|
+
Hash: Promise.resolve({ keccak256 }),
|
|
28
|
+
Mnemonic: { toSeed },
|
|
29
|
+
}),
|
|
30
|
+
).toEqualTypeOf<
|
|
31
|
+
Promise<{
|
|
32
|
+
Hash: { keccak256: typeof keccak256 }
|
|
33
|
+
Mnemonic: { toSeed: typeof toSeed }
|
|
34
|
+
}>
|
|
35
|
+
>()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('rejects an unknown slot', () => {
|
|
39
|
+
// @ts-expect-error
|
|
40
|
+
void Engine.install({ Keccak: Promise.resolve({}) })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('rejects an unknown slot alongside a valid slot', () => {
|
|
44
|
+
void Engine.install({
|
|
45
|
+
Hash: { keccak256: (input) => input },
|
|
46
|
+
// @ts-expect-error
|
|
47
|
+
Keccak: {},
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('rejects a misspelled function', () => {
|
|
52
|
+
void Engine.install({
|
|
53
|
+
// @ts-expect-error
|
|
54
|
+
Hash: Promise.resolve({ keccak_256: (input: Uint8Array) => input }),
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('rejects a misspelled function alongside a valid function', () => {
|
|
59
|
+
void Engine.install({
|
|
60
|
+
// @ts-expect-error
|
|
61
|
+
Hash: {
|
|
62
|
+
keccak256: (input: Uint8Array) => input,
|
|
63
|
+
keccak_256: (input: Uint8Array) => input,
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('rejects a mistyped promised function', () => {
|
|
69
|
+
void Engine.install({
|
|
70
|
+
// @ts-expect-error
|
|
71
|
+
Hash: Promise.resolve({ keccak256: (input: string) => input }),
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
20
76
|
describe('set', () => {
|
|
21
77
|
test('accepts a partial engine', () => {
|
|
22
78
|
expectTypeOf(Engine.set).parameter(0).toEqualTypeOf<Engine.Engine>()
|
|
@@ -38,6 +38,101 @@ function merkelize() {
|
|
|
38
38
|
return BinaryStateTree.merkelize(tree)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
describe('install', () => {
|
|
42
|
+
test('behavior: resolves slots in parallel and installs them atomically', async () => {
|
|
43
|
+
const keccak256 = () => new Uint8Array(32).fill(1)
|
|
44
|
+
const sign = () => new Uint8Array(65).fill(3)
|
|
45
|
+
const toSeed = () => new Uint8Array(64).fill(2)
|
|
46
|
+
let releaseHash: (() => void) | undefined
|
|
47
|
+
|
|
48
|
+
Engine.set({ Secp256k1: { sign } })
|
|
49
|
+
|
|
50
|
+
const hash = {
|
|
51
|
+
// oxlint-disable-next-line unicorn/no-thenable -- Distinguishes parallel resolution from sequential awaits.
|
|
52
|
+
then(
|
|
53
|
+
resolve: (value: Engine.Hash) => void,
|
|
54
|
+
reject: (reason: unknown) => void,
|
|
55
|
+
) {
|
|
56
|
+
const timeout = setTimeout(
|
|
57
|
+
() => reject(new Error('slots resolved serially')),
|
|
58
|
+
0,
|
|
59
|
+
)
|
|
60
|
+
releaseHash = () => {
|
|
61
|
+
clearTimeout(timeout)
|
|
62
|
+
resolve({ keccak256 })
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
} as unknown as PromiseLike<Engine.Hash>
|
|
66
|
+
const mnemonic = {
|
|
67
|
+
// oxlint-disable-next-line unicorn/no-thenable -- Distinguishes parallel resolution from sequential awaits.
|
|
68
|
+
then(resolve: (value: Engine.Mnemonic) => void) {
|
|
69
|
+
resolve({ toSeed })
|
|
70
|
+
releaseHash?.()
|
|
71
|
+
},
|
|
72
|
+
} as unknown as PromiseLike<Engine.Mnemonic>
|
|
73
|
+
|
|
74
|
+
const installed = await Engine.install({ Hash: hash, Mnemonic: mnemonic })
|
|
75
|
+
|
|
76
|
+
expect(installed).toEqual({
|
|
77
|
+
Hash: { keccak256 },
|
|
78
|
+
Mnemonic: { toSeed },
|
|
79
|
+
})
|
|
80
|
+
expect(Engine.get()).toEqual({
|
|
81
|
+
...installed,
|
|
82
|
+
Secp256k1: { sign },
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('behavior: a rejected slot leaves the previous engine installed', async () => {
|
|
87
|
+
const keccak256 = () => new Uint8Array(32).fill(1)
|
|
88
|
+
Engine.set({ Hash: { keccak256 } })
|
|
89
|
+
|
|
90
|
+
await expect(
|
|
91
|
+
Engine.install({
|
|
92
|
+
Hash: Promise.resolve({
|
|
93
|
+
sha256: () => new Uint8Array(32).fill(2),
|
|
94
|
+
}),
|
|
95
|
+
Mnemonic: Promise.reject(new Error('could not load engine')),
|
|
96
|
+
}),
|
|
97
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
98
|
+
`[Error: could not load engine]`,
|
|
99
|
+
)
|
|
100
|
+
expect(Engine.get()).toEqual({ Hash: { keccak256 } })
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('behavior: an invalid resolved engine leaves the previous engine installed', async () => {
|
|
104
|
+
const keccak256 = () => new Uint8Array(32).fill(1)
|
|
105
|
+
Engine.set({ Hash: { keccak256 } })
|
|
106
|
+
|
|
107
|
+
await expect(
|
|
108
|
+
Engine.install({
|
|
109
|
+
Hash: Promise.resolve({
|
|
110
|
+
sha256: () => new Uint8Array(32).fill(2),
|
|
111
|
+
}),
|
|
112
|
+
Mnemonic: Promise.resolve({
|
|
113
|
+
to_seed: () => new Uint8Array(64),
|
|
114
|
+
}),
|
|
115
|
+
} as never),
|
|
116
|
+
).rejects.toThrowError(Engine.UnknownPrimitiveError)
|
|
117
|
+
expect(Engine.get()).toEqual({ Hash: { keccak256 } })
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('behavior: a null resolved slot leaves the previous engine installed', async () => {
|
|
121
|
+
const keccak256 = () => new Uint8Array(32).fill(1)
|
|
122
|
+
Engine.set({ Hash: { keccak256 } })
|
|
123
|
+
|
|
124
|
+
await expect(
|
|
125
|
+
Engine.install({
|
|
126
|
+
Hash: Promise.resolve({
|
|
127
|
+
sha256: () => new Uint8Array(32).fill(2),
|
|
128
|
+
}),
|
|
129
|
+
Mnemonic: Promise.resolve(null),
|
|
130
|
+
} as never),
|
|
131
|
+
).rejects.toThrowError(Engine.InvalidSlotValueError)
|
|
132
|
+
expect(Engine.get()).toEqual({ Hash: { keccak256 } })
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
41
136
|
describe('set', () => {
|
|
42
137
|
test('behavior: empty engine is a no-op', () => {
|
|
43
138
|
const before = Hash.keccak256(payload)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Hash } from 'ox'
|
|
2
2
|
import { describe, expect, test } from 'vp/test'
|
|
3
3
|
import * as vectors from '../../../test/vectors/hashes/index.js'
|
|
4
|
+
import * as hash from '../internal/hash.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Published vectors against ox's default (`@noble/hashes`) implementations.
|
|
@@ -11,6 +12,13 @@ import * as vectors from '../../../test/vectors/hashes/index.js'
|
|
|
11
12
|
* See `test/vectors/hashes/README.md`.
|
|
12
13
|
*/
|
|
13
14
|
|
|
15
|
+
describe('blake3', () => {
|
|
16
|
+
test(`matches ${vectors.blake3.length} official BLAKE3 vectors`, () => {
|
|
17
|
+
for (const { digest, message } of vectors.blake3)
|
|
18
|
+
expect(hash.blake3(message)).toEqual(digest)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
14
22
|
describe('sha256', () => {
|
|
15
23
|
test(`matches ${vectors.sha256.length} NIST CAVP vectors`, () => {
|
|
16
24
|
for (const { digest, message } of vectors.sha256)
|
|
@@ -263,21 +263,21 @@ export type Bls = {
|
|
|
263
263
|
* @internal
|
|
264
264
|
*/
|
|
265
265
|
export type Engine = {
|
|
266
|
-
/** Implementation for
|
|
266
|
+
/** Implementation for [`Bls`](/api/Bls). */
|
|
267
267
|
Bls?: Bls | undefined
|
|
268
|
-
/** Implementation for
|
|
268
|
+
/** Implementation for [`Ed25519`](/api/Ed25519). */
|
|
269
269
|
Ed25519?: Eddsa | undefined
|
|
270
|
-
/** Implementation for
|
|
270
|
+
/** Implementation for [`Hash`](/api/Hash). */
|
|
271
271
|
Hash?: Hash | undefined
|
|
272
|
-
/** Implementation for
|
|
272
|
+
/** Implementation for [`Keystore`](/api/Keystore). */
|
|
273
273
|
Keystore?: Keystore | undefined
|
|
274
|
-
/** Implementation for
|
|
274
|
+
/** Implementation for [`Mnemonic`](/api/Mnemonic). */
|
|
275
275
|
Mnemonic?: Mnemonic | undefined
|
|
276
|
-
/** Implementation for
|
|
276
|
+
/** Implementation for [`P256`](/api/P256). */
|
|
277
277
|
P256?: Ecdsa | undefined
|
|
278
|
-
/** Implementation for
|
|
278
|
+
/** Implementation for [`Secp256k1`](/api/Secp256k1). */
|
|
279
279
|
Secp256k1?: Ecdsa | undefined
|
|
280
|
-
/** Implementation for
|
|
280
|
+
/** Implementation for [`X25519`](/api/X25519). */
|
|
281
281
|
X25519?: Ecdh | undefined
|
|
282
282
|
}
|
|
283
283
|
|
package/src/index.ts
CHANGED
|
@@ -1536,9 +1536,12 @@ export * as Ed25519 from './core/Ed25519.js'
|
|
|
1536
1536
|
*
|
|
1537
1537
|
* ```ts twoslash
|
|
1538
1538
|
* // @noErrors
|
|
1539
|
-
* import { Engine } from 'ox
|
|
1539
|
+
* import { Engine } from 'ox'
|
|
1540
|
+
* import { Hash } from 'ox/wasm'
|
|
1540
1541
|
*
|
|
1541
|
-
* await Engine.
|
|
1542
|
+
* await Engine.install({
|
|
1543
|
+
* Hash: Hash.engine()
|
|
1544
|
+
* })
|
|
1542
1545
|
* ```
|
|
1543
1546
|
*
|
|
1544
1547
|
* @example
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto'
|
|
2
|
+
import type * as Engine from '../core/Engine.js'
|
|
3
|
+
import type * as Errors from '../core/Errors.js'
|
|
4
|
+
|
|
5
|
+
export * from '../core/Ed25519.js'
|
|
6
|
+
|
|
7
|
+
const privateKeyPrefix = Buffer.from('302e020100300506032b657004220420', 'hex')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a Node.js implementation of the [`Ed25519`](/api/Ed25519) engine
|
|
11
|
+
* slot, without installing it.
|
|
12
|
+
*
|
|
13
|
+
* Node's verifier does not implement Ox's ZIP-215 verification semantics, so
|
|
14
|
+
* this engine deliberately omits `verify`. Public-key conversion and random
|
|
15
|
+
* key generation also remain on Ox's default implementation.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts twoslash
|
|
19
|
+
* // @noErrors
|
|
20
|
+
* import { Engine } from 'ox'
|
|
21
|
+
* import { Ed25519 } from 'ox/node'
|
|
22
|
+
*
|
|
23
|
+
* await Engine.install({ Ed25519: Ed25519.engine() })
|
|
24
|
+
*
|
|
25
|
+
* Ed25519.getPublicKey({ privateKey: '0x...' })
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @returns The raw `Ed25519` engine slot.
|
|
29
|
+
*/
|
|
30
|
+
export function engine(): Promise<engine.ReturnType> {
|
|
31
|
+
return Promise.resolve({
|
|
32
|
+
getPublicKey: (privateKey) =>
|
|
33
|
+
rawPublicKey(crypto.createPublicKey(toPrivateKey(privateKey))),
|
|
34
|
+
sign: (payload, privateKey) =>
|
|
35
|
+
new Uint8Array(crypto.sign(null, payload, toPrivateKey(privateKey))),
|
|
36
|
+
toMontgomerySecret: (privateKey) => {
|
|
37
|
+
assertLength(privateKey)
|
|
38
|
+
const digest = crypto.hash('sha512', privateKey, 'buffer')
|
|
39
|
+
try {
|
|
40
|
+
const secretKey = Uint8Array.from(digest.subarray(0, 32))
|
|
41
|
+
secretKey[0]! &= 248
|
|
42
|
+
secretKey[31]! &= 127
|
|
43
|
+
secretKey[31]! |= 64
|
|
44
|
+
return secretKey
|
|
45
|
+
} finally {
|
|
46
|
+
digest.fill(0)
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export declare namespace engine {
|
|
53
|
+
/** Every `Ed25519` primitive this module implements. */
|
|
54
|
+
type ReturnType = {
|
|
55
|
+
[key in 'getPublicKey' | 'sign' | 'toMontgomerySecret']-?: NonNullable<
|
|
56
|
+
Engine.Eddsa[key]
|
|
57
|
+
>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type ErrorType = Errors.GlobalErrorType
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function assertLength(key: Uint8Array): void {
|
|
64
|
+
if (key.length !== 32)
|
|
65
|
+
throw new RangeError(
|
|
66
|
+
`Ed25519 private key must be 32 bytes, got ${key.length}`,
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function rawPublicKey(publicKey: crypto.KeyObject): Uint8Array {
|
|
71
|
+
const der = publicKey.export({ format: 'der', type: 'spki' })
|
|
72
|
+
return Uint8Array.from(der.subarray(-32))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function toPrivateKey(privateKey: Uint8Array): crypto.KeyObject {
|
|
76
|
+
assertLength(privateKey)
|
|
77
|
+
const der = Buffer.concat([privateKeyPrefix, privateKey])
|
|
78
|
+
try {
|
|
79
|
+
return crypto.createPrivateKey({
|
|
80
|
+
format: 'der',
|
|
81
|
+
key: der,
|
|
82
|
+
type: 'pkcs8',
|
|
83
|
+
})
|
|
84
|
+
} finally {
|
|
85
|
+
der.fill(0)
|
|
86
|
+
}
|
|
87
|
+
}
|
package/src/node/Engine.ts
CHANGED
|
@@ -1,40 +1,67 @@
|
|
|
1
1
|
import * as CoreEngine from '../core/Engine.js'
|
|
2
2
|
import type * as Errors from '../core/Errors.js'
|
|
3
|
+
import * as Ed25519 from './Ed25519.js'
|
|
3
4
|
import * as Hash from './Hash.js'
|
|
5
|
+
import * as Keystore from './Keystore.js'
|
|
6
|
+
import * as Mnemonic from './Mnemonic.js'
|
|
7
|
+
import * as P256 from './P256.js'
|
|
8
|
+
import * as X25519 from './X25519.js'
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
AsyncScopeError,
|
|
12
|
+
get,
|
|
13
|
+
InvalidSlotValueError,
|
|
14
|
+
reset,
|
|
15
|
+
set,
|
|
16
|
+
UnknownPrimitiveError,
|
|
17
|
+
UnknownSlotError,
|
|
18
|
+
with,
|
|
19
|
+
} from '../core/Engine.js'
|
|
20
|
+
export type {
|
|
21
|
+
Bls,
|
|
22
|
+
Ecdh,
|
|
23
|
+
Ecdsa,
|
|
24
|
+
Eddsa,
|
|
25
|
+
Engine,
|
|
26
|
+
Hash,
|
|
27
|
+
Keystore,
|
|
28
|
+
Mnemonic,
|
|
29
|
+
} from '../core/Engine.js'
|
|
4
30
|
|
|
5
31
|
/**
|
|
6
|
-
*
|
|
32
|
+
* Installs every Node.js implementation this entrypoint provides.
|
|
7
33
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* startup, before any cryptographic operation.
|
|
34
|
+
* Slots initialize in parallel and are installed atomically. Call this once
|
|
35
|
+
* during startup, before any cryptographic operation.
|
|
11
36
|
*
|
|
12
37
|
* @example
|
|
13
38
|
* ```ts twoslash
|
|
14
39
|
* // @noErrors
|
|
15
|
-
* import { Hash } from 'ox'
|
|
16
|
-
* import { Engine } from 'ox/node'
|
|
40
|
+
* import { Engine, Hash } from 'ox/node'
|
|
17
41
|
*
|
|
18
|
-
* await Engine.
|
|
42
|
+
* await Engine.install()
|
|
19
43
|
*
|
|
20
44
|
* Hash.sha256('0xdeadbeef')
|
|
21
45
|
* ```
|
|
22
46
|
*
|
|
23
47
|
* @returns The engine that was installed.
|
|
24
48
|
*/
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
49
|
+
export function install(): Promise<install.ReturnType> {
|
|
50
|
+
return CoreEngine.install({
|
|
51
|
+
Ed25519: Ed25519.engine(),
|
|
52
|
+
Hash: Hash.engine(),
|
|
53
|
+
Keystore: Keystore.engine(),
|
|
54
|
+
Mnemonic: Mnemonic.engine(),
|
|
55
|
+
P256: P256.engine(),
|
|
56
|
+
X25519: X25519.engine(),
|
|
57
|
+
})
|
|
29
58
|
}
|
|
30
59
|
|
|
31
|
-
export declare namespace
|
|
32
|
-
|
|
60
|
+
export declare namespace install {
|
|
61
|
+
/** Every slot this entrypoint installs, each with its primitives present. */
|
|
62
|
+
type ReturnType = engine.ReturnType
|
|
33
63
|
|
|
34
|
-
type ErrorType =
|
|
35
|
-
| create.ErrorType
|
|
36
|
-
| CoreEngine.set.ErrorType
|
|
37
|
-
| Errors.GlobalErrorType
|
|
64
|
+
type ErrorType = engine.ErrorType | CoreEngine.install.ErrorType
|
|
38
65
|
}
|
|
39
66
|
|
|
40
67
|
/**
|
|
@@ -50,20 +77,49 @@ export declare namespace load {
|
|
|
50
77
|
* import { Engine, Hash } from 'ox'
|
|
51
78
|
* import { Engine as NodeEngine } from 'ox/node'
|
|
52
79
|
*
|
|
53
|
-
* const node = await NodeEngine.
|
|
80
|
+
* const node = await NodeEngine.engine()
|
|
54
81
|
*
|
|
55
82
|
* Engine.with(node, () => Hash.sha256('0xdeadbeef'))
|
|
56
83
|
* ```
|
|
57
84
|
*
|
|
58
85
|
* @returns An engine, ready to install.
|
|
59
86
|
*/
|
|
60
|
-
export async function
|
|
61
|
-
|
|
87
|
+
export async function engine(): Promise<engine.ReturnType> {
|
|
88
|
+
const [ed25519, hash, keystore, mnemonic, p256, x25519] = await Promise.all([
|
|
89
|
+
Ed25519.engine(),
|
|
90
|
+
Hash.engine(),
|
|
91
|
+
Keystore.engine(),
|
|
92
|
+
Mnemonic.engine(),
|
|
93
|
+
P256.engine(),
|
|
94
|
+
X25519.engine(),
|
|
95
|
+
])
|
|
96
|
+
return {
|
|
97
|
+
Ed25519: ed25519,
|
|
98
|
+
Hash: hash,
|
|
99
|
+
Keystore: keystore,
|
|
100
|
+
Mnemonic: mnemonic,
|
|
101
|
+
P256: p256,
|
|
102
|
+
X25519: x25519,
|
|
103
|
+
}
|
|
62
104
|
}
|
|
63
105
|
|
|
64
|
-
export declare namespace
|
|
106
|
+
export declare namespace engine {
|
|
65
107
|
/** Every slot this entrypoint supplies, each with its primitives present. */
|
|
66
|
-
type ReturnType =
|
|
108
|
+
type ReturnType = {
|
|
109
|
+
Ed25519: Ed25519.engine.ReturnType
|
|
110
|
+
Hash: Hash.engine.ReturnType
|
|
111
|
+
Keystore: Keystore.engine.ReturnType
|
|
112
|
+
Mnemonic: Mnemonic.engine.ReturnType
|
|
113
|
+
P256: P256.engine.ReturnType
|
|
114
|
+
X25519: X25519.engine.ReturnType
|
|
115
|
+
}
|
|
67
116
|
|
|
68
|
-
type ErrorType =
|
|
117
|
+
type ErrorType =
|
|
118
|
+
| Ed25519.engine.ErrorType
|
|
119
|
+
| Hash.engine.ErrorType
|
|
120
|
+
| Keystore.engine.ErrorType
|
|
121
|
+
| Mnemonic.engine.ErrorType
|
|
122
|
+
| P256.engine.ErrorType
|
|
123
|
+
| X25519.engine.ErrorType
|
|
124
|
+
| Errors.GlobalErrorType
|
|
69
125
|
}
|
package/src/node/Hash.ts
CHANGED
|
@@ -2,14 +2,15 @@ import * as crypto from 'node:crypto'
|
|
|
2
2
|
import type * as Engine from '../core/Engine.js'
|
|
3
3
|
import type * as Errors from '../core/Errors.js'
|
|
4
4
|
|
|
5
|
+
export * from '../core/Hash.js'
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
|
-
* Creates Node.js
|
|
7
|
-
* installing
|
|
8
|
+
* Creates a Node.js implementation of the [`Hash`](/api/Hash) engine slot,
|
|
9
|
+
* without installing it.
|
|
8
10
|
*
|
|
9
|
-
* Most callers want
|
|
10
|
-
* implementation this entrypoint provides. Reach for this
|
|
11
|
-
*
|
|
12
|
-
* installed engine.
|
|
11
|
+
* Most callers want [`Engine.install`](/node/crypto/Engine/install) instead,
|
|
12
|
+
* which installs every implementation this entrypoint provides. Reach for this
|
|
13
|
+
* to install or hold the `Hash` slot on its own.
|
|
13
14
|
*
|
|
14
15
|
* Node's `sha3-256` is not Ethereum Keccak256, so this engine deliberately
|
|
15
16
|
* omits `keccak256`. Any earlier override remains installed; otherwise Ox uses
|
|
@@ -18,38 +19,32 @@ import type * as Errors from '../core/Errors.js'
|
|
|
18
19
|
* @example
|
|
19
20
|
* ```ts twoslash
|
|
20
21
|
* // @noErrors
|
|
21
|
-
* import { Engine
|
|
22
|
-
* import
|
|
22
|
+
* import { Engine } from 'ox'
|
|
23
|
+
* import { Hash } from 'ox/node'
|
|
23
24
|
*
|
|
24
|
-
* Engine.
|
|
25
|
+
* await Engine.install({ Hash: Hash.engine() })
|
|
25
26
|
*
|
|
26
27
|
* Hash.sha256('0xdeadbeef')
|
|
27
28
|
* ```
|
|
28
29
|
*
|
|
29
|
-
* @returns
|
|
30
|
+
* @returns The raw `Hash` engine slot.
|
|
30
31
|
*/
|
|
31
|
-
export function
|
|
32
|
+
export function engine(): Promise<engine.ReturnType> {
|
|
32
33
|
return Promise.resolve({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
ripemd160: (input) =>
|
|
39
|
-
new Uint8Array(crypto.hash('ripemd160', input, 'buffer')),
|
|
40
|
-
sha256: (input) => new Uint8Array(crypto.hash('sha256', input, 'buffer')),
|
|
41
|
-
},
|
|
34
|
+
hmacSha256: (key, message) =>
|
|
35
|
+
new Uint8Array(crypto.createHmac('sha256', key).update(message).digest()),
|
|
36
|
+
ripemd160: (input) =>
|
|
37
|
+
new Uint8Array(crypto.hash('ripemd160', input, 'buffer')),
|
|
38
|
+
sha256: (input) => new Uint8Array(crypto.hash('sha256', input, 'buffer')),
|
|
42
39
|
})
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
export declare namespace
|
|
46
|
-
/**
|
|
42
|
+
export declare namespace engine {
|
|
43
|
+
/** Every `Hash` primitive this module implements. */
|
|
47
44
|
type ReturnType = {
|
|
48
|
-
|
|
49
|
-
[key
|
|
50
|
-
|
|
51
|
-
>
|
|
52
|
-
}
|
|
45
|
+
[key in 'hmacSha256' | 'ripemd160' | 'sha256']-?: NonNullable<
|
|
46
|
+
Engine.Hash[key]
|
|
47
|
+
>
|
|
53
48
|
}
|
|
54
49
|
|
|
55
50
|
type ErrorType = Errors.GlobalErrorType
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto'
|
|
2
|
+
import type * as Engine from '../core/Engine.js'
|
|
3
|
+
import type * as Errors from '../core/Errors.js'
|
|
4
|
+
|
|
5
|
+
export * from '../core/Keystore.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a Node.js implementation of the [`Keystore`](/api/Keystore) engine
|
|
9
|
+
* slot, without installing it.
|
|
10
|
+
*
|
|
11
|
+
* Most callers want [`Engine.install`](/node/crypto/Engine/install) instead,
|
|
12
|
+
* which installs every implementation this entrypoint provides. Reach for this
|
|
13
|
+
* to install or hold the `Keystore` slot on its own.
|
|
14
|
+
*
|
|
15
|
+
* This engine deliberately omits scrypt. OpenSSL rejects Ox's default scrypt
|
|
16
|
+
* parameter shape, so installing it would make valid existing calls fail.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts twoslash
|
|
20
|
+
* // @noErrors
|
|
21
|
+
* import { Engine } from 'ox'
|
|
22
|
+
* import { Keystore } from 'ox/node'
|
|
23
|
+
*
|
|
24
|
+
* await Engine.install({ Keystore: Keystore.engine() })
|
|
25
|
+
*
|
|
26
|
+
* Keystore.pbkdf2({ password: 'testpassword' })
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @returns The raw `Keystore` engine slot.
|
|
30
|
+
*/
|
|
31
|
+
export function engine(): Promise<engine.ReturnType> {
|
|
32
|
+
return Promise.resolve({
|
|
33
|
+
aesCtrDecrypt: (key, iv, data) => {
|
|
34
|
+
const cipher = crypto.createDecipheriv(
|
|
35
|
+
`aes-${key.length * 8}-ctr`,
|
|
36
|
+
key,
|
|
37
|
+
iv,
|
|
38
|
+
)
|
|
39
|
+
return cipherData(cipher, data)
|
|
40
|
+
},
|
|
41
|
+
aesCtrEncrypt: (key, iv, data) => {
|
|
42
|
+
const cipher = crypto.createCipheriv(`aes-${key.length * 8}-ctr`, key, iv)
|
|
43
|
+
return cipherData(cipher, data)
|
|
44
|
+
},
|
|
45
|
+
pbkdf2Sha256: (password, salt, options) =>
|
|
46
|
+
copyAndClear(
|
|
47
|
+
crypto.pbkdf2Sync(password, salt, options.c, options.dkLen, 'sha256'),
|
|
48
|
+
),
|
|
49
|
+
pbkdf2Sha256Async: (password, salt, options) =>
|
|
50
|
+
new Promise((resolve, reject) => {
|
|
51
|
+
crypto.pbkdf2(
|
|
52
|
+
password,
|
|
53
|
+
salt,
|
|
54
|
+
options.c,
|
|
55
|
+
options.dkLen,
|
|
56
|
+
'sha256',
|
|
57
|
+
(error, key) => {
|
|
58
|
+
if (error) return reject(error)
|
|
59
|
+
try {
|
|
60
|
+
resolve(copyAndClear(key))
|
|
61
|
+
} catch (error) {
|
|
62
|
+
reject(error)
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
}),
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export declare namespace engine {
|
|
71
|
+
/** Every `Keystore` primitive this module implements. */
|
|
72
|
+
type ReturnType = {
|
|
73
|
+
[key in
|
|
74
|
+
| 'aesCtrDecrypt'
|
|
75
|
+
| 'aesCtrEncrypt'
|
|
76
|
+
| 'pbkdf2Sha256'
|
|
77
|
+
| 'pbkdf2Sha256Async']-?: NonNullable<Engine.Keystore[key]>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type ErrorType = Errors.GlobalErrorType
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function cipherData(
|
|
84
|
+
cipher: crypto.Cipheriv | crypto.Decipheriv,
|
|
85
|
+
data: Uint8Array,
|
|
86
|
+
): Uint8Array {
|
|
87
|
+
const chunks: Buffer[] = []
|
|
88
|
+
try {
|
|
89
|
+
chunks.push(cipher.update(data))
|
|
90
|
+
chunks.push(cipher.final())
|
|
91
|
+
return copyAndClear(Buffer.concat(chunks))
|
|
92
|
+
} finally {
|
|
93
|
+
for (const chunk of chunks) chunk.fill(0)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function copyAndClear(value: Buffer): Uint8Array {
|
|
98
|
+
try {
|
|
99
|
+
return Uint8Array.from(value)
|
|
100
|
+
} finally {
|
|
101
|
+
value.fill(0)
|
|
102
|
+
}
|
|
103
|
+
}
|