ox 1.6.3 → 1.7.1
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 +12 -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/Siwe.d.ts.map +1 -1
- package/dist/core/Siwe.js +45 -6
- package/dist/core/Siwe.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/Siwe.ts +45 -5
- 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/_test/Siwe.test.ts +98 -0
- package/src/core/internal/keyDerivation.ts +34 -0
- package/src/version.ts +1 -1
|
@@ -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
|
})
|
|
@@ -546,6 +546,81 @@ Expiration Time: 2022-02-04T00:00:00.000Z`
|
|
|
546
546
|
)
|
|
547
547
|
})
|
|
548
548
|
|
|
549
|
+
test.each([
|
|
550
|
+
['never', '2023-02-01T00:00:00Z'],
|
|
551
|
+
['2023-02-29T00:00:00Z', '2023-02-28T12:00:00Z'],
|
|
552
|
+
['2023-04-31T00:00:00Z', '2023-04-30T12:00:00Z'],
|
|
553
|
+
['2023-02-01T24:00:00Z', '2023-02-01T12:00:00Z'],
|
|
554
|
+
])(
|
|
555
|
+
'behavior: invalid RFC 3339 expirationTime `%s`',
|
|
556
|
+
(expirationTime, time) => {
|
|
557
|
+
const message = `https://example.com wants you to sign in with your Ethereum account:
|
|
558
|
+
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
|
|
559
|
+
|
|
560
|
+
URI: https://example.com/path
|
|
561
|
+
Version: 1
|
|
562
|
+
Chain ID: 1
|
|
563
|
+
Nonce: foobarbaz
|
|
564
|
+
Issued At: 2023-02-01T00:00:00.000Z
|
|
565
|
+
Expiration Time: ${expirationTime}`
|
|
566
|
+
const parsed = Siwe.parseMessage(message)
|
|
567
|
+
expect({
|
|
568
|
+
invalid: Number.isNaN(parsed.expirationTime?.getTime()),
|
|
569
|
+
valid: Siwe.validateMessage({
|
|
570
|
+
message: parsed,
|
|
571
|
+
time: new Date(time),
|
|
572
|
+
}),
|
|
573
|
+
}).toMatchInlineSnapshot(`
|
|
574
|
+
{
|
|
575
|
+
"invalid": true,
|
|
576
|
+
"valid": false,
|
|
577
|
+
}
|
|
578
|
+
`)
|
|
579
|
+
},
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
test.each([
|
|
583
|
+
'2030-02-04t00:00:00Z',
|
|
584
|
+
'2030-02-04T00:00:00z',
|
|
585
|
+
'2030-02-04t00:00:00z',
|
|
586
|
+
])('behavior: lowercase RFC 3339 expirationTime `%s`', (expirationTime) => {
|
|
587
|
+
const message = `https://example.com wants you to sign in with your Ethereum account:
|
|
588
|
+
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
|
|
589
|
+
|
|
590
|
+
URI: https://example.com/path
|
|
591
|
+
Version: 1
|
|
592
|
+
Chain ID: 1
|
|
593
|
+
Nonce: foobarbaz
|
|
594
|
+
Issued At: 2023-02-01T00:00:00.000Z
|
|
595
|
+
Expiration Time: ${expirationTime}`
|
|
596
|
+
const parsed = Siwe.parseMessage(message)
|
|
597
|
+
expect(parsed.expirationTime).toMatchInlineSnapshot(
|
|
598
|
+
'2030-02-04T00:00:00.000Z',
|
|
599
|
+
)
|
|
600
|
+
expect(
|
|
601
|
+
Siwe.validateMessage({
|
|
602
|
+
message: parsed,
|
|
603
|
+
time: new Date('2029-02-04T00:00:00Z'),
|
|
604
|
+
}),
|
|
605
|
+
).toBeTruthy()
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
test('behavior: leap-year expirationTime', () => {
|
|
609
|
+
const message = `https://example.com wants you to sign in with your Ethereum account:
|
|
610
|
+
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
|
|
611
|
+
|
|
612
|
+
URI: https://example.com/path
|
|
613
|
+
Version: 1
|
|
614
|
+
Chain ID: 1
|
|
615
|
+
Nonce: foobarbaz
|
|
616
|
+
Issued At: 2023-02-01T00:00:00.000Z
|
|
617
|
+
Expiration Time: 2032-02-29T00:00:00Z`
|
|
618
|
+
const parsed = Siwe.parseMessage(message)
|
|
619
|
+
expect(parsed.expirationTime).toMatchInlineSnapshot(
|
|
620
|
+
'2032-02-29T00:00:00.000Z',
|
|
621
|
+
)
|
|
622
|
+
})
|
|
623
|
+
|
|
549
624
|
test('behavior: with notBefore', () => {
|
|
550
625
|
const message = `https://example.com wants you to sign in with your Ethereum account:
|
|
551
626
|
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
|
|
@@ -730,6 +805,29 @@ describe('validateMessage', () => {
|
|
|
730
805
|
).toBeFalsy()
|
|
731
806
|
})
|
|
732
807
|
|
|
808
|
+
test.each(['expirationTime', 'notBefore'] as const)(
|
|
809
|
+
'behavior: invalid %s',
|
|
810
|
+
(field) => {
|
|
811
|
+
expect(
|
|
812
|
+
Siwe.validateMessage({
|
|
813
|
+
message: {
|
|
814
|
+
...message,
|
|
815
|
+
[field]: new Date('never'),
|
|
816
|
+
},
|
|
817
|
+
}),
|
|
818
|
+
).toBeFalsy()
|
|
819
|
+
},
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
test('behavior: invalid time', () => {
|
|
823
|
+
expect(
|
|
824
|
+
Siwe.validateMessage({
|
|
825
|
+
message,
|
|
826
|
+
time: new Date('never'),
|
|
827
|
+
}),
|
|
828
|
+
).toBeFalsy()
|
|
829
|
+
})
|
|
830
|
+
|
|
733
831
|
test('behavior: time is before notBefore', () => {
|
|
734
832
|
vi.useFakeTimers()
|
|
735
833
|
vi.setSystemTime(new Date(Date.UTC(2023, 1, 1)))
|
|
@@ -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.1'
|