ox 1.6.3 → 1.7.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 +6 -0
- package/dist/core/AesGcm.d.ts +67 -2
- package/dist/core/AesGcm.d.ts.map +1 -1
- package/dist/core/AesGcm.js +79 -10
- package/dist/core/AesGcm.js.map +1 -1
- package/dist/core/Ed25519.d.ts +83 -2
- package/dist/core/Ed25519.d.ts.map +1 -1
- package/dist/core/Ed25519.js +79 -4
- package/dist/core/Ed25519.js.map +1 -1
- package/dist/core/MlDsa44.d.ts +84 -5
- package/dist/core/MlDsa44.d.ts.map +1 -1
- package/dist/core/MlDsa44.js +79 -4
- package/dist/core/MlDsa44.js.map +1 -1
- package/dist/core/P256.d.ts +82 -1
- package/dist/core/P256.d.ts.map +1 -1
- package/dist/core/P256.js +78 -0
- package/dist/core/P256.js.map +1 -1
- package/dist/core/Secp256k1.d.ts +85 -1
- package/dist/core/Secp256k1.d.ts.map +1 -1
- package/dist/core/Secp256k1.js +74 -3
- package/dist/core/Secp256k1.js.map +1 -1
- package/dist/core/internal/keyDerivation.d.ts +13 -0
- package/dist/core/internal/keyDerivation.d.ts.map +1 -0
- package/dist/core/internal/keyDerivation.js +13 -0
- package/dist/core/internal/keyDerivation.js.map +1 -0
- package/package.json +1 -1
- package/src/core/AesGcm.ts +137 -30
- package/src/core/Ed25519.ts +135 -4
- package/src/core/MlDsa44.ts +135 -4
- package/src/core/P256.ts +135 -1
- package/src/core/Secp256k1.ts +134 -3
- package/src/core/_test/AesGcm.test-d.ts +19 -0
- package/src/core/_test/AesGcm.test.ts +90 -1
- package/src/core/_test/Ed25519.test-d.ts +24 -0
- package/src/core/_test/Ed25519.test.ts +87 -1
- package/src/core/_test/MlDsa44.test-d.ts +24 -0
- package/src/core/_test/MlDsa44.test.ts +69 -1
- package/src/core/_test/P256.test-d.ts +26 -0
- package/src/core/_test/P256.test.ts +98 -1
- package/src/core/_test/Secp256k1.test-d.ts +27 -0
- package/src/core/_test/Secp256k1.test.ts +112 -1
- package/src/core/internal/keyDerivation.ts +34 -0
- package/src/version.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Bytes, Ed25519, Engine, Hex, X25519 } from 'ox'
|
|
1
|
+
import { Bytes, Ed25519, Engine, Hex, Mnemonic, X25519 } from 'ox'
|
|
2
2
|
import { describe, expect, test } from 'vp/test'
|
|
3
3
|
|
|
4
4
|
describe('createKeyPair', () => {
|
|
@@ -146,6 +146,89 @@ describe('fromPrf', () => {
|
|
|
146
146
|
})
|
|
147
147
|
})
|
|
148
148
|
|
|
149
|
+
describe('fromMnemonic', () => {
|
|
150
|
+
const mnemonic = 'test test test test test test test test test test test junk'
|
|
151
|
+
|
|
152
|
+
test('default', () => {
|
|
153
|
+
const privateKey = Ed25519.fromMnemonic(mnemonic)
|
|
154
|
+
|
|
155
|
+
expect(privateKey).toBe(Ed25519.fromSeed(Mnemonic.toSeed(mnemonic)))
|
|
156
|
+
expect(privateKey).toMatchInlineSnapshot(
|
|
157
|
+
`"0x23fb07a427d2bc36141d1e6c7e56c72679739eff374a8e8def282f1048674567"`,
|
|
158
|
+
)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('options: passphrase', () => {
|
|
162
|
+
expect(
|
|
163
|
+
Ed25519.fromMnemonic(mnemonic, { passphrase: 'qwerty' }),
|
|
164
|
+
).toMatchInlineSnapshot(
|
|
165
|
+
`"0xe888791c9d783e772d92b0bf2aa326b1cda8214a03c3c07933615b1a98fc8651"`,
|
|
166
|
+
)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test('options: as', () => {
|
|
170
|
+
const privateKey = Ed25519.fromMnemonic(mnemonic, { as: 'Bytes' })
|
|
171
|
+
|
|
172
|
+
expect(privateKey).toBeInstanceOf(Uint8Array)
|
|
173
|
+
expect(privateKey).toHaveLength(32)
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
describe('fromSeed', () => {
|
|
178
|
+
const seed =
|
|
179
|
+
'0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
180
|
+
|
|
181
|
+
test('vector', () => {
|
|
182
|
+
expect(Ed25519.fromSeed(seed)).toMatchInlineSnapshot(
|
|
183
|
+
`"0xfd09c7b2acda46034df7e428377fdc37200094c9b51690fa1b733ee3c16a2d07"`,
|
|
184
|
+
)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('value: Bytes', () => {
|
|
188
|
+
expect(Ed25519.fromSeed(Bytes.fromHex(seed))).toMatchInlineSnapshot(
|
|
189
|
+
`"0xfd09c7b2acda46034df7e428377fdc37200094c9b51690fa1b733ee3c16a2d07"`,
|
|
190
|
+
)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
test('options: as', () => {
|
|
194
|
+
expect(Ed25519.fromSeed(seed, { as: 'Bytes' })).toEqual(
|
|
195
|
+
Bytes.fromHex(
|
|
196
|
+
'0xfd09c7b2acda46034df7e428377fdc37200094c9b51690fa1b733ee3c16a2d07',
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('behavior: output signs and verifies', () => {
|
|
202
|
+
const privateKey = Ed25519.fromSeed(new Uint8Array(64))
|
|
203
|
+
const publicKey = Ed25519.getPublicKey({ privateKey })
|
|
204
|
+
const payload = '0xdeadbeef'
|
|
205
|
+
const signature = Ed25519.sign({ payload, privateKey })
|
|
206
|
+
|
|
207
|
+
expect(Ed25519.verify({ payload, publicKey, signature })).toBe(true)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test('behavior: zeroes intermediate key for Hex output', () => {
|
|
211
|
+
const candidate = new Uint8Array(32).fill(1)
|
|
212
|
+
Engine.with(
|
|
213
|
+
{
|
|
214
|
+
Hash: {
|
|
215
|
+
hmacSha256: () => candidate,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
() => Ed25519.fromSeed(new Uint8Array(32)),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
expect(candidate).toEqual(new Uint8Array(32))
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
test('error: seed is too short', () => {
|
|
225
|
+
expect(() => Ed25519.fromSeed(new Uint8Array(31)))
|
|
226
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
227
|
+
[Ed25519.InvalidSeedSizeError: Seed must contain at least 32 bytes. Received 31 bytes.]
|
|
228
|
+
`)
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
|
|
149
232
|
describe('getPublicKey', () => {
|
|
150
233
|
const privateKey =
|
|
151
234
|
'0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'
|
|
@@ -545,6 +628,8 @@ test('exports', () => {
|
|
|
545
628
|
"noble",
|
|
546
629
|
"createKeyPair",
|
|
547
630
|
"fromPrf",
|
|
631
|
+
"fromMnemonic",
|
|
632
|
+
"fromSeed",
|
|
548
633
|
"getPublicKey",
|
|
549
634
|
"randomPrivateKey",
|
|
550
635
|
"sign",
|
|
@@ -552,6 +637,7 @@ test('exports', () => {
|
|
|
552
637
|
"toX25519PublicKey",
|
|
553
638
|
"toX25519PrivateKey",
|
|
554
639
|
"InvalidPrfSizeError",
|
|
640
|
+
"InvalidSeedSizeError",
|
|
555
641
|
]
|
|
556
642
|
`)
|
|
557
643
|
})
|
|
@@ -24,6 +24,30 @@ test('fromPrf', () => {
|
|
|
24
24
|
).toEqualTypeOf<Bytes.Bytes>()
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
+
test('fromMnemonic', () => {
|
|
28
|
+
const mnemonic = 'test test test test test test test test test test test junk'
|
|
29
|
+
|
|
30
|
+
expectTypeOf(MlDsa44.fromMnemonic(mnemonic)).toEqualTypeOf<Hex.Hex>()
|
|
31
|
+
expectTypeOf(
|
|
32
|
+
MlDsa44.fromMnemonic(mnemonic, { passphrase: 'qwerty' }),
|
|
33
|
+
).toEqualTypeOf<Hex.Hex>()
|
|
34
|
+
expectTypeOf(
|
|
35
|
+
MlDsa44.fromMnemonic(mnemonic, { as: 'Bytes' }),
|
|
36
|
+
).toEqualTypeOf<Bytes.Bytes>()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('fromSeed', () => {
|
|
40
|
+
const seed =
|
|
41
|
+
'0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
42
|
+
|
|
43
|
+
expectTypeOf(MlDsa44.fromSeed(seed)).toEqualTypeOf<Hex.Hex>()
|
|
44
|
+
expectTypeOf(MlDsa44.fromSeed(new Uint8Array(32))).toEqualTypeOf<Hex.Hex>()
|
|
45
|
+
expectTypeOf(MlDsa44.fromSeed(seed, { as: 'Hex' })).toEqualTypeOf<Hex.Hex>()
|
|
46
|
+
expectTypeOf(
|
|
47
|
+
MlDsa44.fromSeed(seed, { as: 'Bytes' }),
|
|
48
|
+
).toEqualTypeOf<Bytes.Bytes>()
|
|
49
|
+
})
|
|
50
|
+
|
|
27
51
|
test('getPublicKey', () => {
|
|
28
52
|
expectTypeOf(
|
|
29
53
|
MlDsa44.getPublicKey({ privateKey: '0x' }),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Bytes, Engine, Hash, Hex, MlDsa44 } from 'ox'
|
|
1
|
+
import { Bytes, Engine, Hash, Hex, MlDsa44, Mnemonic } from 'ox'
|
|
2
2
|
import { describe, expect, test } from 'vp/test'
|
|
3
3
|
|
|
4
4
|
const privateKey = Hex.fromBytes(new Uint8Array(32).fill(7))
|
|
@@ -139,6 +139,74 @@ describe('fromPrf', () => {
|
|
|
139
139
|
})
|
|
140
140
|
})
|
|
141
141
|
|
|
142
|
+
describe('fromMnemonic', () => {
|
|
143
|
+
const mnemonic = 'test test test test test test test test test test test junk'
|
|
144
|
+
|
|
145
|
+
test('default', () => {
|
|
146
|
+
const privateKey = MlDsa44.fromMnemonic(mnemonic)
|
|
147
|
+
|
|
148
|
+
expect(privateKey).toBe(MlDsa44.fromSeed(Mnemonic.toSeed(mnemonic)))
|
|
149
|
+
expect(privateKey).toMatchInlineSnapshot(
|
|
150
|
+
`"0x787f747b571a3511deac4d045a7876ae2349f45b9f9f4e3a6ee40342313a963f"`,
|
|
151
|
+
)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('options: passphrase', () => {
|
|
155
|
+
expect(
|
|
156
|
+
MlDsa44.fromMnemonic(mnemonic, { passphrase: 'qwerty' }),
|
|
157
|
+
).toMatchInlineSnapshot(
|
|
158
|
+
`"0x51202c4aaf8b4090f94c43ae43234b133a2646642fbc4ca39d8883a0b39d2b44"`,
|
|
159
|
+
)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('options: as', () => {
|
|
163
|
+
const privateKey = MlDsa44.fromMnemonic(mnemonic, { as: 'Bytes' })
|
|
164
|
+
|
|
165
|
+
expect(privateKey).toBeInstanceOf(Uint8Array)
|
|
166
|
+
expect(privateKey).toHaveLength(32)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe('fromSeed', () => {
|
|
171
|
+
const seed =
|
|
172
|
+
'0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
173
|
+
|
|
174
|
+
test('vector', () => {
|
|
175
|
+
expect(MlDsa44.fromSeed(seed)).toMatchInlineSnapshot(
|
|
176
|
+
`"0xbee74160aef452ec0e90a4c347bbb0ac37e93fb94855f7a98dfe9afabebbdad6"`,
|
|
177
|
+
)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('value: Bytes', () => {
|
|
181
|
+
expect(MlDsa44.fromSeed(Bytes.fromHex(seed))).toMatchInlineSnapshot(
|
|
182
|
+
`"0xbee74160aef452ec0e90a4c347bbb0ac37e93fb94855f7a98dfe9afabebbdad6"`,
|
|
183
|
+
)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('options: as', () => {
|
|
187
|
+
expect(MlDsa44.fromSeed(seed, { as: 'Bytes' })).toEqual(
|
|
188
|
+
Bytes.fromHex(
|
|
189
|
+
'0xbee74160aef452ec0e90a4c347bbb0ac37e93fb94855f7a98dfe9afabebbdad6',
|
|
190
|
+
),
|
|
191
|
+
)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('behavior: output signs and verifies', () => {
|
|
195
|
+
const privateKey = MlDsa44.fromSeed(new Uint8Array(64))
|
|
196
|
+
const publicKey = MlDsa44.getPublicKey({ privateKey })
|
|
197
|
+
const signature = MlDsa44.sign({ payload, privateKey })
|
|
198
|
+
|
|
199
|
+
expect(MlDsa44.verify({ payload, publicKey, signature })).toBe(true)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test('error: seed is too short', () => {
|
|
203
|
+
expect(() => MlDsa44.fromSeed(new Uint8Array(31)))
|
|
204
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
205
|
+
[MlDsa44.InvalidSeedSizeError: Seed must contain at least 32 bytes. Received 31 bytes.]
|
|
206
|
+
`)
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
142
210
|
describe('getPublicKey', () => {
|
|
143
211
|
test('default', () => {
|
|
144
212
|
const publicKey = MlDsa44.getPublicKey({ privateKey })
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Bytes, type Hex, P256 } from 'ox'
|
|
2
|
+
import { expectTypeOf, test } from 'vp/test'
|
|
3
|
+
|
|
4
|
+
test('fromMnemonic', () => {
|
|
5
|
+
const mnemonic = 'test test test test test test test test test test test junk'
|
|
6
|
+
|
|
7
|
+
expectTypeOf(P256.fromMnemonic(mnemonic)).toEqualTypeOf<Hex.Hex>()
|
|
8
|
+
expectTypeOf(
|
|
9
|
+
P256.fromMnemonic(mnemonic, { passphrase: 'qwerty' }),
|
|
10
|
+
).toEqualTypeOf<Hex.Hex>()
|
|
11
|
+
expectTypeOf(
|
|
12
|
+
P256.fromMnemonic(mnemonic, { as: 'Bytes' }),
|
|
13
|
+
).toEqualTypeOf<Bytes.Bytes>()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('fromSeed', () => {
|
|
17
|
+
const seed =
|
|
18
|
+
'0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
19
|
+
|
|
20
|
+
expectTypeOf(P256.fromSeed(seed)).toEqualTypeOf<Hex.Hex>()
|
|
21
|
+
expectTypeOf(P256.fromSeed(new Uint8Array(32))).toEqualTypeOf<Hex.Hex>()
|
|
22
|
+
expectTypeOf(P256.fromSeed(seed, { as: 'Hex' })).toEqualTypeOf<Hex.Hex>()
|
|
23
|
+
expectTypeOf(
|
|
24
|
+
P256.fromSeed(seed, { as: 'Bytes' }),
|
|
25
|
+
).toEqualTypeOf<Bytes.Bytes>()
|
|
26
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Bytes, Hex, P256 } from 'ox'
|
|
1
|
+
import { Bytes, Engine, Hex, Mnemonic, P256 } from 'ox'
|
|
2
2
|
import { describe, expect, test } from 'vp/test'
|
|
3
3
|
import { accounts } from '../../../test/constants/accounts.js'
|
|
4
4
|
|
|
@@ -145,6 +145,100 @@ describe('createKeyPair', () => {
|
|
|
145
145
|
})
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
+
describe('fromMnemonic', () => {
|
|
149
|
+
const mnemonic = 'test test test test test test test test test test test junk'
|
|
150
|
+
|
|
151
|
+
test('default', () => {
|
|
152
|
+
const privateKey = P256.fromMnemonic(mnemonic)
|
|
153
|
+
|
|
154
|
+
expect(privateKey).toBe(P256.fromSeed(Mnemonic.toSeed(mnemonic)))
|
|
155
|
+
expect(privateKey).toMatchInlineSnapshot(
|
|
156
|
+
`"0x882f5b02a84c96bceeebf9c868bec73041d51b041ae05380e66a41508648ebc3"`,
|
|
157
|
+
)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('options: passphrase', () => {
|
|
161
|
+
expect(
|
|
162
|
+
P256.fromMnemonic(mnemonic, { passphrase: 'qwerty' }),
|
|
163
|
+
).toMatchInlineSnapshot(
|
|
164
|
+
`"0x364ff539f37ce477d49a35476c9ad32a2a3fed1d1237eaee7abdfd07655ad9f5"`,
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test('options: as', () => {
|
|
169
|
+
const privateKey = P256.fromMnemonic(mnemonic, { as: 'Bytes' })
|
|
170
|
+
|
|
171
|
+
expect(privateKey).toBeInstanceOf(Uint8Array)
|
|
172
|
+
expect(privateKey).toHaveLength(32)
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('fromSeed', () => {
|
|
177
|
+
const seed =
|
|
178
|
+
'0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
179
|
+
|
|
180
|
+
test('vector', () => {
|
|
181
|
+
expect(P256.fromSeed(seed)).toMatchInlineSnapshot(
|
|
182
|
+
`"0x724009e76eb27e34eb77b93db354b9015ecd4296deb57561f5214bab68b94a6f"`,
|
|
183
|
+
)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('value: Bytes', () => {
|
|
187
|
+
expect(P256.fromSeed(Bytes.fromHex(seed))).toMatchInlineSnapshot(
|
|
188
|
+
`"0x724009e76eb27e34eb77b93db354b9015ecd4296deb57561f5214bab68b94a6f"`,
|
|
189
|
+
)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('options: as', () => {
|
|
193
|
+
expect(P256.fromSeed(seed, { as: 'Bytes' })).toEqual(
|
|
194
|
+
Bytes.fromHex(
|
|
195
|
+
'0x724009e76eb27e34eb77b93db354b9015ecd4296deb57561f5214bab68b94a6f',
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('behavior: output is a valid P256 private key', () => {
|
|
201
|
+
const privateKey = P256.fromSeed(new Uint8Array(64), { as: 'Bytes' })
|
|
202
|
+
|
|
203
|
+
expect(P256.noble.utils.isValidSecretKey(privateKey)).toBe(true)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
test('behavior: skips invalid scalar candidates', () => {
|
|
207
|
+
const messages: Hex.Hex[] = []
|
|
208
|
+
const privateKey = Engine.with(
|
|
209
|
+
{
|
|
210
|
+
Hash: {
|
|
211
|
+
hmacSha256: (_key, message) => {
|
|
212
|
+
messages.push(Hex.fromBytes(message))
|
|
213
|
+
if (messages.length === 1) return new Uint8Array(32)
|
|
214
|
+
return Bytes.fromHex(
|
|
215
|
+
'0xef3f982137910a4c362017e69ba173fbb342325802ec827767aa6942027af35b',
|
|
216
|
+
)
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
() => P256.fromSeed(new Uint8Array(32)),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
expect(privateKey).toBe(
|
|
224
|
+
'0xef3f982137910a4c362017e69ba173fbb342325802ec827767aa6942027af35b',
|
|
225
|
+
)
|
|
226
|
+
expect(messages).toMatchInlineSnapshot(`
|
|
227
|
+
[
|
|
228
|
+
"0x6f782e703235362e66726f6d536565642e763100000000",
|
|
229
|
+
"0x6f782e703235362e66726f6d536565642e763100000001",
|
|
230
|
+
]
|
|
231
|
+
`)
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
test('error: seed is too short', () => {
|
|
235
|
+
expect(() => P256.fromSeed(new Uint8Array(31)))
|
|
236
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
237
|
+
[P256.InvalidSeedSizeError: Seed must contain at least 32 bytes. Received 31 bytes.]
|
|
238
|
+
`)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
148
242
|
describe('getSharedSecret', () => {
|
|
149
243
|
test('default', () => {
|
|
150
244
|
const { privateKey: privateKeyA, publicKey: publicKeyA } =
|
|
@@ -546,12 +640,15 @@ test('exports', () => {
|
|
|
546
640
|
[
|
|
547
641
|
"noble",
|
|
548
642
|
"createKeyPair",
|
|
643
|
+
"fromMnemonic",
|
|
644
|
+
"fromSeed",
|
|
549
645
|
"getPublicKey",
|
|
550
646
|
"getSharedSecret",
|
|
551
647
|
"randomPrivateKey",
|
|
552
648
|
"recoverPublicKey",
|
|
553
649
|
"sign",
|
|
554
650
|
"verify",
|
|
651
|
+
"InvalidSeedSizeError",
|
|
555
652
|
]
|
|
556
653
|
`)
|
|
557
654
|
})
|
|
@@ -12,3 +12,30 @@ test('fromPrf', () => {
|
|
|
12
12
|
Secp256k1.fromPrf(prf, { as: 'Bytes' }),
|
|
13
13
|
).toEqualTypeOf<Bytes.Bytes>()
|
|
14
14
|
})
|
|
15
|
+
|
|
16
|
+
test('fromMnemonic', () => {
|
|
17
|
+
const mnemonic = 'test test test test test test test test test test test junk'
|
|
18
|
+
|
|
19
|
+
expectTypeOf(Secp256k1.fromMnemonic(mnemonic)).toEqualTypeOf<Hex.Hex>()
|
|
20
|
+
expectTypeOf(
|
|
21
|
+
Secp256k1.fromMnemonic(mnemonic, { passphrase: 'qwerty' }),
|
|
22
|
+
).toEqualTypeOf<Hex.Hex>()
|
|
23
|
+
expectTypeOf(
|
|
24
|
+
Secp256k1.fromMnemonic(mnemonic, { as: 'Bytes' }),
|
|
25
|
+
).toEqualTypeOf<Bytes.Bytes>()
|
|
26
|
+
expectTypeOf(
|
|
27
|
+
Secp256k1.fromMnemonic(mnemonic, { path: "m/44'/60'/0'/0/1" }),
|
|
28
|
+
).toEqualTypeOf<Hex.Hex>()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('fromSeed', () => {
|
|
32
|
+
const seed =
|
|
33
|
+
'0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
34
|
+
|
|
35
|
+
expectTypeOf(Secp256k1.fromSeed(seed)).toEqualTypeOf<Hex.Hex>()
|
|
36
|
+
expectTypeOf(Secp256k1.fromSeed(new Uint8Array(32))).toEqualTypeOf<Hex.Hex>()
|
|
37
|
+
expectTypeOf(Secp256k1.fromSeed(seed, { as: 'Hex' })).toEqualTypeOf<Hex.Hex>()
|
|
38
|
+
expectTypeOf(
|
|
39
|
+
Secp256k1.fromSeed(seed, { as: 'Bytes' }),
|
|
40
|
+
).toEqualTypeOf<Bytes.Bytes>()
|
|
41
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Address, Bytes, Engine, Hex, PublicKey, Secp256k1 } from 'ox'
|
|
1
|
+
import { Address, Bytes, Engine, Hex, Mnemonic, PublicKey, Secp256k1 } from 'ox'
|
|
2
2
|
import { describe, expect, test } from 'vp/test'
|
|
3
3
|
import { accounts } from '../../../test/constants/accounts.js'
|
|
4
4
|
|
|
@@ -211,6 +211,114 @@ describe('fromPrf', () => {
|
|
|
211
211
|
})
|
|
212
212
|
})
|
|
213
213
|
|
|
214
|
+
describe('fromMnemonic', () => {
|
|
215
|
+
const mnemonic = 'test test test test test test test test test test test junk'
|
|
216
|
+
|
|
217
|
+
test('default', () => {
|
|
218
|
+
const privateKey = Secp256k1.fromMnemonic(mnemonic)
|
|
219
|
+
|
|
220
|
+
expect(privateKey).toBe(Mnemonic.toPrivateKey(mnemonic, { as: 'Hex' }))
|
|
221
|
+
expect(privateKey).toMatchInlineSnapshot(
|
|
222
|
+
`"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"`,
|
|
223
|
+
)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('options: passphrase', () => {
|
|
227
|
+
expect(
|
|
228
|
+
Secp256k1.fromMnemonic(mnemonic, { passphrase: 'qwerty' }),
|
|
229
|
+
).toMatchInlineSnapshot(
|
|
230
|
+
`"0x0bef893b1cc27e9ce726d5f12f75d61a07d4df87c02106083463cd712ac5c478"`,
|
|
231
|
+
)
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
test('options: as', () => {
|
|
235
|
+
const privateKey = Secp256k1.fromMnemonic(mnemonic, { as: 'Bytes' })
|
|
236
|
+
|
|
237
|
+
expect(privateKey).toBeInstanceOf(Uint8Array)
|
|
238
|
+
expect(privateKey).toHaveLength(32)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
test('options: path', () => {
|
|
242
|
+
const path = "m/44'/60'/0'/0/1"
|
|
243
|
+
|
|
244
|
+
expect(Secp256k1.fromMnemonic(mnemonic, { path })).toBe(
|
|
245
|
+
Mnemonic.toPrivateKey(mnemonic, { as: 'Hex', path }),
|
|
246
|
+
)
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
describe('fromSeed', () => {
|
|
251
|
+
const seed =
|
|
252
|
+
'0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
|
253
|
+
|
|
254
|
+
test('vector', () => {
|
|
255
|
+
expect(Secp256k1.fromSeed(seed)).toMatchInlineSnapshot(
|
|
256
|
+
`"0x5f7dcbba41adaafa378861d78b144f7b2827e79fa994a341628f5470d5bfbdd4"`,
|
|
257
|
+
)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
test('value: Bytes', () => {
|
|
261
|
+
expect(Secp256k1.fromSeed(Bytes.fromHex(seed))).toMatchInlineSnapshot(
|
|
262
|
+
`"0x5f7dcbba41adaafa378861d78b144f7b2827e79fa994a341628f5470d5bfbdd4"`,
|
|
263
|
+
)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
test('options: as', () => {
|
|
267
|
+
expect(Secp256k1.fromSeed(seed, { as: 'Bytes' })).toEqual(
|
|
268
|
+
Bytes.fromHex(
|
|
269
|
+
'0x5f7dcbba41adaafa378861d78b144f7b2827e79fa994a341628f5470d5bfbdd4',
|
|
270
|
+
),
|
|
271
|
+
)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
test('behavior: accepts seeds longer than 32 bytes', () => {
|
|
275
|
+
const privateKey = Secp256k1.fromSeed(new Uint8Array(64), { as: 'Bytes' })
|
|
276
|
+
|
|
277
|
+
expect(Secp256k1.noble.utils.isValidSecretKey(privateKey)).toBe(true)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test('behavior: domain is distinct from fromPrf', () => {
|
|
281
|
+
expect(Secp256k1.fromSeed(seed)).not.toBe(Secp256k1.fromPrf(seed))
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
test('behavior: skips invalid scalar candidates', () => {
|
|
285
|
+
const invalid = new Uint8Array(32).fill(0xff)
|
|
286
|
+
const messages: Hex.Hex[] = []
|
|
287
|
+
const privateKey = Engine.with(
|
|
288
|
+
{
|
|
289
|
+
Hash: {
|
|
290
|
+
hmacSha256: (_key, message) => {
|
|
291
|
+
messages.push(Hex.fromBytes(message))
|
|
292
|
+
if (messages.length === 1) return invalid
|
|
293
|
+
return Bytes.fromHex(
|
|
294
|
+
'0xef3f982137910a4c362017e69ba173fbb342325802ec827767aa6942027af35b',
|
|
295
|
+
)
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
() => Secp256k1.fromSeed(new Uint8Array(32)),
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
expect(privateKey).toBe(
|
|
303
|
+
'0xef3f982137910a4c362017e69ba173fbb342325802ec827767aa6942027af35b',
|
|
304
|
+
)
|
|
305
|
+
expect(invalid).toEqual(new Uint8Array(32))
|
|
306
|
+
expect(messages).toMatchInlineSnapshot(`
|
|
307
|
+
[
|
|
308
|
+
"0x6f782e736563703235366b312e66726f6d536565642e763100000000",
|
|
309
|
+
"0x6f782e736563703235366b312e66726f6d536565642e763100000001",
|
|
310
|
+
]
|
|
311
|
+
`)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('error: seed is too short', () => {
|
|
315
|
+
expect(() => Secp256k1.fromSeed(new Uint8Array(31)))
|
|
316
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
317
|
+
[Secp256k1.InvalidSeedSizeError: Seed must contain at least 32 bytes. Received 31 bytes.]
|
|
318
|
+
`)
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
214
322
|
describe('getSharedSecret', () => {
|
|
215
323
|
test('default', () => {
|
|
216
324
|
const privateKeyA = accounts[0].privateKey
|
|
@@ -572,6 +680,8 @@ test('exports', () => {
|
|
|
572
680
|
"noble",
|
|
573
681
|
"createKeyPair",
|
|
574
682
|
"fromPrf",
|
|
683
|
+
"fromMnemonic",
|
|
684
|
+
"fromSeed",
|
|
575
685
|
"getPublicKey",
|
|
576
686
|
"getSharedSecret",
|
|
577
687
|
"randomPrivateKey",
|
|
@@ -580,6 +690,7 @@ test('exports', () => {
|
|
|
580
690
|
"sign",
|
|
581
691
|
"verify",
|
|
582
692
|
"InvalidPrfSizeError",
|
|
693
|
+
"InvalidSeedSizeError",
|
|
583
694
|
]
|
|
584
695
|
`)
|
|
585
696
|
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as Bytes from '../Bytes.js'
|
|
2
|
+
import type * as Errors from '../Errors.js'
|
|
3
|
+
import * as Hash from '../Hash.js'
|
|
4
|
+
|
|
5
|
+
/** @internal */
|
|
6
|
+
export function derive(
|
|
7
|
+
seed: Bytes.Bytes,
|
|
8
|
+
domain: Bytes.Bytes,
|
|
9
|
+
options: derive.Options = {},
|
|
10
|
+
): Bytes.Bytes {
|
|
11
|
+
const { validate } = options
|
|
12
|
+
for (let counter = 0; ; counter++) {
|
|
13
|
+
const key = Hash.hmac256(
|
|
14
|
+
seed,
|
|
15
|
+
Bytes.concat(domain, Bytes.fromNumber(counter, { size: 4 })),
|
|
16
|
+
{ as: 'Bytes' },
|
|
17
|
+
)
|
|
18
|
+
if (!validate || validate(key)) return key
|
|
19
|
+
key.fill(0)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** @internal */
|
|
24
|
+
export declare namespace derive {
|
|
25
|
+
type Options = {
|
|
26
|
+
validate?: ((key: Bytes.Bytes) => boolean) | undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type ErrorType =
|
|
30
|
+
| Bytes.concat.ErrorType
|
|
31
|
+
| Bytes.fromNumber.ErrorType
|
|
32
|
+
| Hash.hmac256.ErrorType
|
|
33
|
+
| Errors.GlobalErrorType
|
|
34
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
-
export const version = '1.
|
|
2
|
+
export const version = '1.7.0'
|