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.
Files changed (43) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/core/AesGcm.d.ts +67 -2
  3. package/dist/core/AesGcm.d.ts.map +1 -1
  4. package/dist/core/AesGcm.js +79 -10
  5. package/dist/core/AesGcm.js.map +1 -1
  6. package/dist/core/Ed25519.d.ts +83 -2
  7. package/dist/core/Ed25519.d.ts.map +1 -1
  8. package/dist/core/Ed25519.js +79 -4
  9. package/dist/core/Ed25519.js.map +1 -1
  10. package/dist/core/MlDsa44.d.ts +84 -5
  11. package/dist/core/MlDsa44.d.ts.map +1 -1
  12. package/dist/core/MlDsa44.js +79 -4
  13. package/dist/core/MlDsa44.js.map +1 -1
  14. package/dist/core/P256.d.ts +82 -1
  15. package/dist/core/P256.d.ts.map +1 -1
  16. package/dist/core/P256.js +78 -0
  17. package/dist/core/P256.js.map +1 -1
  18. package/dist/core/Secp256k1.d.ts +85 -1
  19. package/dist/core/Secp256k1.d.ts.map +1 -1
  20. package/dist/core/Secp256k1.js +74 -3
  21. package/dist/core/Secp256k1.js.map +1 -1
  22. package/dist/core/internal/keyDerivation.d.ts +13 -0
  23. package/dist/core/internal/keyDerivation.d.ts.map +1 -0
  24. package/dist/core/internal/keyDerivation.js +13 -0
  25. package/dist/core/internal/keyDerivation.js.map +1 -0
  26. package/package.json +1 -1
  27. package/src/core/AesGcm.ts +137 -30
  28. package/src/core/Ed25519.ts +135 -4
  29. package/src/core/MlDsa44.ts +135 -4
  30. package/src/core/P256.ts +135 -1
  31. package/src/core/Secp256k1.ts +134 -3
  32. package/src/core/_test/AesGcm.test-d.ts +19 -0
  33. package/src/core/_test/AesGcm.test.ts +90 -1
  34. package/src/core/_test/Ed25519.test-d.ts +24 -0
  35. package/src/core/_test/Ed25519.test.ts +87 -1
  36. package/src/core/_test/MlDsa44.test-d.ts +24 -0
  37. package/src/core/_test/MlDsa44.test.ts +69 -1
  38. package/src/core/_test/P256.test-d.ts +26 -0
  39. package/src/core/_test/P256.test.ts +98 -1
  40. package/src/core/_test/Secp256k1.test-d.ts +27 -0
  41. package/src/core/_test/Secp256k1.test.ts +112 -1
  42. package/src/core/internal/keyDerivation.ts +34 -0
  43. package/src/version.ts +1 -1
@@ -5,9 +5,11 @@ import * as Errors from './Errors.js';
5
5
  import * as Hash from './Hash.js';
6
6
  import * as Hex from './Hex.js';
7
7
  import { formatPublicKey, formatSignature, normalizePublicKey, normalizeSignature, } from './internal/cryptoIo.js';
8
+ import * as keyDerivation from './internal/keyDerivation.js';
8
9
  import * as engine from './internal/secp256k1.js';
9
10
  import * as Entropy from './internal/entropy.js';
10
11
  import { fromRecoveredBytes, toCompactBytes, toRecoveredBytes, } from './internal/signature.js';
12
+ import * as Mnemonic from './Mnemonic.js';
11
13
  import * as PublicKey from './PublicKey.js';
12
14
  /** Re-export of noble/curves secp256k1 utilities. */
13
15
  export const noble = secp256k1;
@@ -37,7 +39,7 @@ export function createKeyPair(options = {}) {
37
39
  * Derives a valid secp256k1 private key from a 32-byte WebAuthn PRF output.
38
40
  *
39
41
  * The permanent derivation contract uses the PRF output as the HMAC-SHA256
40
- * key. Its message is the UTF-8 bytes of `ox.secp256k1.fromPrf.v1` followed by
42
+ * key. The HMAC message uses the `ox.secp256k1.fromPrf.v1` domain followed by
41
43
  * a 32-bit big-endian counter starting at zero. Invalid scalars are skipped.
42
44
  *
43
45
  * @example
@@ -59,7 +61,7 @@ export function fromPrf(value, options = {}) {
59
61
  if (bytes.length !== 32)
60
62
  throw new InvalidPrfSizeError({ size: bytes.length });
61
63
  for (let counter = 0;; counter++) {
62
- const candidate = Hash.hmac256(bytes, Bytes.concat(fromPrfLabel, Bytes.fromNumber(counter, { size: 4 })), { as: 'Bytes' });
64
+ const candidate = Hash.hmac256(bytes, Bytes.concat(fromPrfDomain, Bytes.fromNumber(counter, { size: 4 })), { as: 'Bytes' });
63
65
  if (noble.utils.isValidSecretKey(candidate)) {
64
66
  if (as === 'Hex') {
65
67
  const value = Hex.fromBytes(candidate);
@@ -71,6 +73,67 @@ export function fromPrf(value, options = {}) {
71
73
  candidate.fill(0);
72
74
  }
73
75
  }
76
+ /**
77
+ * Derives a valid secp256k1 private key from a BIP-39 mnemonic.
78
+ *
79
+ * This is equivalent to {@link ox#Mnemonic.toPrivateKey}, and derives the
80
+ * private key at `m/44'/60'/0'/0/0` by default.
81
+ *
82
+ * @example
83
+ * ```ts twoslash
84
+ * import { Secp256k1 } from 'ox'
85
+ *
86
+ * const privateKey = Secp256k1.fromMnemonic(
87
+ * 'test test test test test test test test test test test junk'
88
+ * )
89
+ * ```
90
+ *
91
+ * @param mnemonic - BIP-39 mnemonic phrase.
92
+ * @param options - Options.
93
+ * @returns A valid secp256k1 private key.
94
+ */
95
+ export function fromMnemonic(mnemonic, options = {}) {
96
+ const { as = 'Hex', passphrase, path } = options;
97
+ return Mnemonic.toPrivateKey(mnemonic, { as, passphrase, path });
98
+ }
99
+ /**
100
+ * Derives a valid secp256k1 private key from a seed.
101
+ *
102
+ * The seed must contain at least 32 bytes of cryptographically strong key
103
+ * material. Do not pass a password directly; use a password KDF first.
104
+ *
105
+ * The permanent derivation contract uses the seed as the HMAC-SHA256
106
+ * key. The HMAC message uses the `ox.secp256k1.fromSeed.v1` domain followed by
107
+ * a 32-bit big-endian counter starting at zero. Invalid scalars are skipped.
108
+ *
109
+ * @example
110
+ * ```ts twoslash
111
+ * import { Secp256k1 } from 'ox'
112
+ *
113
+ * const privateKey = Secp256k1.fromSeed(
114
+ * '0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
115
+ * )
116
+ * ```
117
+ *
118
+ * @param seed - Seed containing at least 32 bytes of cryptographically strong key material.
119
+ * @param options - Options.
120
+ * @returns A valid secp256k1 private key.
121
+ */
122
+ export function fromSeed(seed, options = {}) {
123
+ const { as = 'Hex' } = options;
124
+ const bytes = Bytes.from(seed);
125
+ if (bytes.length < 32)
126
+ throw new InvalidSeedSizeError({ size: bytes.length });
127
+ const privateKey = keyDerivation.derive(bytes, fromSeedDomain, {
128
+ validate: noble.utils.isValidSecretKey,
129
+ });
130
+ if (as === 'Hex') {
131
+ const value = Hex.fromBytes(privateKey);
132
+ privateKey.fill(0);
133
+ return value;
134
+ }
135
+ return privateKey;
136
+ }
74
137
  /**
75
138
  * Computes the secp256k1 ECDSA public key from a provided private key.
76
139
  *
@@ -281,5 +344,13 @@ export class InvalidPrfSizeError extends Errors.BaseError {
281
344
  super(`PRF output must be exactly 32 bytes. Received ${options.size} bytes.`);
282
345
  }
283
346
  }
284
- const fromPrfLabel = Bytes.fromString('ox.secp256k1.fromPrf.v1');
347
+ /** Thrown when a seed contains fewer than 32 bytes. */
348
+ export class InvalidSeedSizeError extends Errors.BaseError {
349
+ name = 'Secp256k1.InvalidSeedSizeError';
350
+ constructor(options) {
351
+ super(`Seed must contain at least 32 bytes. Received ${options.size} bytes.`);
352
+ }
353
+ }
354
+ const fromPrfDomain = Bytes.fromString('ox.secp256k1.fromPrf.v1');
355
+ const fromSeedDomain = Bytes.fromString('ox.secp256k1.fromSeed.v1');
285
356
  //# sourceMappingURL=Secp256k1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Secp256k1.js","sourceRoot":"","sources":["../../src/core/Secp256k1.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Secp256k1.js","sourceRoot":"","sources":["../../src/core/Secp256k1.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import * as Bytes from '../Bytes.js';
2
+ import type * as Errors from '../Errors.js';
3
+ import * as Hash from '../Hash.js';
4
+ /** @internal */
5
+ export declare function derive(seed: Bytes.Bytes, domain: Bytes.Bytes, options?: derive.Options): Bytes.Bytes;
6
+ /** @internal */
7
+ export declare namespace derive {
8
+ type Options = {
9
+ validate?: ((key: Bytes.Bytes) => boolean) | undefined;
10
+ };
11
+ type ErrorType = Bytes.concat.ErrorType | Bytes.fromNumber.ErrorType | Hash.hmac256.ErrorType | Errors.GlobalErrorType;
12
+ }
13
+ //# sourceMappingURL=keyDerivation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyDerivation.d.ts","sourceRoot":"","sources":["../../../src/core/internal/keyDerivation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,aAAa,CAAA;AACpC,OAAO,KAAK,KAAK,MAAM,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,IAAI,MAAM,YAAY,CAAA;AAElC,gBAAgB;AAChB,wBAAgB,MAAM,CACpB,IAAI,EAAE,KAAK,CAAC,KAAK,EACjB,MAAM,EAAE,KAAK,CAAC,KAAK,EACnB,OAAO,GAAE,MAAM,CAAC,OAAY,GAC3B,KAAK,CAAC,KAAK,CAWb;AAED,gBAAgB;AAChB,MAAM,CAAC,OAAO,WAAW,MAAM,CAAC;IAC9B,KAAK,OAAO,GAAG;QACb,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;KACvD,CAAA;IAED,KAAK,SAAS,GACV,KAAK,CAAC,MAAM,CAAC,SAAS,GACtB,KAAK,CAAC,UAAU,CAAC,SAAS,GAC1B,IAAI,CAAC,OAAO,CAAC,SAAS,GACtB,MAAM,CAAC,eAAe,CAAA;CAC3B"}
@@ -0,0 +1,13 @@
1
+ import * as Bytes from '../Bytes.js';
2
+ import * as Hash from '../Hash.js';
3
+ /** @internal */
4
+ export function derive(seed, domain, options = {}) {
5
+ const { validate } = options;
6
+ for (let counter = 0;; counter++) {
7
+ const key = Hash.hmac256(seed, Bytes.concat(domain, Bytes.fromNumber(counter, { size: 4 })), { as: 'Bytes' });
8
+ if (!validate || validate(key))
9
+ return key;
10
+ key.fill(0);
11
+ }
12
+ }
13
+ //# sourceMappingURL=keyDerivation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyDerivation.js","sourceRoot":"","sources":["../../../src/core/internal/keyDerivation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,aAAa,CAAA;AAEpC,OAAO,KAAK,IAAI,MAAM,YAAY,CAAA;AAElC,gBAAgB;AAChB,MAAM,UAAU,MAAM,CACpB,IAAiB,EACjB,MAAmB,EACnB,UAA0B,EAAE;IAE5B,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAA;IAC5B,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CACtB,IAAI,EACJ,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,EAC5D,EAAE,EAAE,EAAE,OAAO,EAAE,CAChB,CAAA;QACD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAA;QAC1C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACb,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "ox",
3
3
  "description": "Ethereum Standard Library",
4
4
  "type": "module",
5
- "version": "1.6.3",
5
+ "version": "1.7.0",
6
6
  "main": "./dist/index.js",
7
7
  "sideEffects": false,
8
8
  "license": "MIT",
@@ -1,6 +1,7 @@
1
1
  import * as Bytes from './Bytes.js'
2
2
  import * as Errors from './Errors.js'
3
3
  import * as Hex from './Hex.js'
4
+ import * as mnemonic_ from './internal/mnemonic.js'
4
5
 
5
6
  export const ivLength = 16
6
7
 
@@ -138,8 +139,8 @@ export declare namespace encrypt {
138
139
  * Derives an AES-256-GCM key from a 32-byte WebAuthn PRF output.
139
140
  *
140
141
  * The permanent derivation contract uses the PRF output as the HMAC-SHA256
141
- * key. Its message is the UTF-8 bytes of `ox.aesGcm.fromPrf.v1` followed by
142
- * a 32-bit big-endian counter set to zero.
142
+ * key. The HMAC message uses the `ox.aesGcm.fromPrf.v1` domain followed by a
143
+ * 32-bit big-endian counter set to zero.
143
144
  *
144
145
  * @example
145
146
  * ```ts twoslash
@@ -158,43 +159,95 @@ export async function fromPrf(
158
159
  ): Promise<CryptoKey> {
159
160
  const bytes = Bytes.from(value)
160
161
  if (bytes.length !== 32) throw new InvalidPrfSizeError({ size: bytes.length })
162
+ return deriveKey(bytes, fromPrfDomain)
163
+ }
161
164
 
162
- const prfKey = await globalThis.crypto.subtle.importKey(
163
- 'raw',
164
- bytes,
165
- {
166
- name: 'HMAC',
167
- hash: 'SHA-256',
168
- },
169
- false,
170
- ['sign'],
171
- )
172
- const key = new Uint8Array(
173
- await globalThis.crypto.subtle.sign(
174
- 'HMAC',
175
- prfKey,
176
- Bytes.concat(fromPrfLabel, Bytes.fromNumber(0, { size: 4 })),
177
- ),
178
- )
165
+ export declare namespace fromPrf {
166
+ type ErrorType =
167
+ | Bytes.concat.ErrorType
168
+ | Bytes.from.ErrorType
169
+ | Bytes.fromNumber.ErrorType
170
+ | InvalidPrfSizeError
171
+ | Errors.GlobalErrorType
172
+ }
173
+
174
+ /**
175
+ * Derives an AES-256-GCM key from a BIP-39 mnemonic.
176
+ *
177
+ * This is equivalent to passing `Mnemonic.toSeed(mnemonic, { passphrase })`
178
+ * to {@link ox#AesGcm.fromSeed}.
179
+ *
180
+ * @example
181
+ * ```ts twoslash
182
+ * import { AesGcm } from 'ox'
183
+ *
184
+ * const key = await AesGcm.fromMnemonic(
185
+ * 'test test test test test test test test test test test junk'
186
+ * )
187
+ * ```
188
+ *
189
+ * @param mnemonic - BIP-39 mnemonic phrase.
190
+ * @param options - Options.
191
+ * @returns A nonextractable AES-256-GCM key for encryption and decryption.
192
+ */
193
+ export async function fromMnemonic(
194
+ mnemonic: string,
195
+ options: fromMnemonic.Options = {},
196
+ ): Promise<CryptoKey> {
197
+ const { passphrase } = options
198
+ const seed = mnemonic_.toSeed(mnemonic, passphrase)
179
199
  try {
180
- return await globalThis.crypto.subtle.importKey(
181
- 'raw',
182
- key,
183
- { name: 'AES-GCM' },
184
- false,
185
- ['encrypt', 'decrypt'],
186
- )
200
+ return await fromSeed(seed)
187
201
  } finally {
188
- key.fill(0)
202
+ seed.fill(0)
189
203
  }
190
204
  }
191
205
 
192
- export declare namespace fromPrf {
206
+ export declare namespace fromMnemonic {
207
+ type Options = {
208
+ /** Optional BIP-39 passphrase. */
209
+ passphrase?: string | undefined
210
+ }
211
+
212
+ type ErrorType = fromSeed.ErrorType
213
+ }
214
+
215
+ /**
216
+ * Derives an AES-256-GCM key from a seed.
217
+ *
218
+ * The seed must contain at least 32 bytes of cryptographically strong key
219
+ * material. Do not pass a password directly; use a password KDF first.
220
+ *
221
+ * The permanent derivation contract uses the seed as the HMAC-SHA256
222
+ * key. The HMAC message uses the `ox.aesGcm.fromSeed.v1` domain followed by a
223
+ * 32-bit big-endian counter set to zero.
224
+ *
225
+ * @example
226
+ * ```ts twoslash
227
+ * import { AesGcm } from 'ox'
228
+ *
229
+ * const key = await AesGcm.fromSeed(
230
+ * '0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
231
+ * )
232
+ * ```
233
+ *
234
+ * @param seed - Seed containing at least 32 bytes of cryptographically strong key material.
235
+ * @returns A nonextractable AES-256-GCM key for encryption and decryption.
236
+ */
237
+ export async function fromSeed(
238
+ seed: Hex.Hex | Bytes.Bytes,
239
+ ): Promise<CryptoKey> {
240
+ const bytes = Bytes.from(seed)
241
+ if (bytes.length < 32) throw new InvalidSeedSizeError({ size: bytes.length })
242
+ return deriveKey(bytes, fromSeedDomain)
243
+ }
244
+
245
+ export declare namespace fromSeed {
193
246
  type ErrorType =
194
247
  | Bytes.concat.ErrorType
195
248
  | Bytes.from.ErrorType
196
249
  | Bytes.fromNumber.ErrorType
197
- | InvalidPrfSizeError
250
+ | InvalidSeedSizeError
198
251
  | Errors.GlobalErrorType
199
252
  }
200
253
 
@@ -290,4 +343,58 @@ export declare namespace InvalidPrfSizeError {
290
343
  }
291
344
  }
292
345
 
293
- const fromPrfLabel = Bytes.fromString('ox.aesGcm.fromPrf.v1')
346
+ /** Thrown when a seed contains fewer than 32 bytes. */
347
+ export class InvalidSeedSizeError extends Errors.BaseError {
348
+ override readonly name = 'AesGcm.InvalidSeedSizeError'
349
+
350
+ constructor(options: InvalidSeedSizeError.Options) {
351
+ super(
352
+ `Seed must contain at least 32 bytes. Received ${options.size} bytes.`,
353
+ )
354
+ }
355
+ }
356
+
357
+ export declare namespace InvalidSeedSizeError {
358
+ /** Options for {@link ox#AesGcm.InvalidSeedSizeError}. */
359
+ type Options = {
360
+ /** Received seed size. */
361
+ size: number
362
+ }
363
+ }
364
+
365
+ async function deriveKey(
366
+ seed: Bytes.Bytes,
367
+ domain: Bytes.Bytes,
368
+ ): Promise<CryptoKey> {
369
+ const baseKey = await globalThis.crypto.subtle.importKey(
370
+ 'raw',
371
+ seed,
372
+ {
373
+ name: 'HMAC',
374
+ hash: 'SHA-256',
375
+ },
376
+ false,
377
+ ['sign'],
378
+ )
379
+ const key = new Uint8Array(
380
+ await globalThis.crypto.subtle.sign(
381
+ 'HMAC',
382
+ baseKey,
383
+ Bytes.concat(domain, Bytes.fromNumber(0, { size: 4 })),
384
+ ),
385
+ )
386
+ try {
387
+ return await globalThis.crypto.subtle.importKey(
388
+ 'raw',
389
+ key,
390
+ { name: 'AES-GCM' },
391
+ false,
392
+ ['encrypt', 'decrypt'],
393
+ )
394
+ } finally {
395
+ key.fill(0)
396
+ }
397
+ }
398
+
399
+ const fromPrfDomain = Bytes.fromString('ox.aesGcm.fromPrf.v1')
400
+ const fromSeedDomain = Bytes.fromString('ox.aesGcm.fromSeed.v1')
@@ -4,6 +4,8 @@ import * as Errors from './Errors.js'
4
4
  import * as Hash from './Hash.js'
5
5
  import * as Hex from './Hex.js'
6
6
  import * as engine from './internal/ed25519.js'
7
+ import * as keyDerivation from './internal/keyDerivation.js'
8
+ import * as mnemonic_ from './internal/mnemonic.js'
7
9
 
8
10
  /** Re-export of noble/curves Ed25519 utilities. */
9
11
  export const noble = ed25519
@@ -63,8 +65,8 @@ export declare namespace createKeyPair {
63
65
  * Derives an Ed25519 private key from a 32-byte WebAuthn PRF output.
64
66
  *
65
67
  * The permanent derivation contract uses the PRF output as the HMAC-SHA256
66
- * key. Its message is the UTF-8 bytes of `ox.ed25519.fromPrf.v1` followed by
67
- * a 32-bit big-endian counter set to zero.
68
+ * key. The HMAC message uses the `ox.ed25519.fromPrf.v1` domain followed by a
69
+ * 32-bit big-endian counter set to zero.
68
70
  *
69
71
  * @example
70
72
  * ```ts twoslash
@@ -89,7 +91,7 @@ export function fromPrf<as extends 'Hex' | 'Bytes' = 'Hex'>(
89
91
 
90
92
  const privateKey = Hash.hmac256(
91
93
  bytes,
92
- Bytes.concat(fromPrfLabel, Bytes.fromNumber(0, { size: 4 })),
94
+ Bytes.concat(fromPrfDomain, Bytes.fromNumber(0, { size: 4 })),
93
95
  { as: 'Bytes' },
94
96
  )
95
97
  if (as === 'Hex') {
@@ -123,6 +125,115 @@ export declare namespace fromPrf {
123
125
  | Errors.GlobalErrorType
124
126
  }
125
127
 
128
+ /**
129
+ * Derives an Ed25519 private key from a BIP-39 mnemonic.
130
+ *
131
+ * This is equivalent to passing `Mnemonic.toSeed(mnemonic, { passphrase })`
132
+ * to {@link ox#Ed25519.fromSeed}.
133
+ *
134
+ * @example
135
+ * ```ts twoslash
136
+ * import { Ed25519 } from 'ox'
137
+ *
138
+ * const privateKey = Ed25519.fromMnemonic(
139
+ * 'test test test test test test test test test test test junk'
140
+ * )
141
+ * ```
142
+ *
143
+ * @param mnemonic - BIP-39 mnemonic phrase.
144
+ * @param options - Options.
145
+ * @returns An Ed25519 private key.
146
+ */
147
+ export function fromMnemonic<as extends 'Hex' | 'Bytes' = 'Hex'>(
148
+ mnemonic: string,
149
+ options: fromMnemonic.Options<as> = {},
150
+ ): fromMnemonic.ReturnType<as> {
151
+ const { passphrase } = options
152
+ const seed = mnemonic_.toSeed(mnemonic, passphrase)
153
+ try {
154
+ return fromSeed(seed, options)
155
+ } finally {
156
+ seed.fill(0)
157
+ }
158
+ }
159
+
160
+ export declare namespace fromMnemonic {
161
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
162
+ /**
163
+ * Format of the returned private key.
164
+ * @default 'Hex'
165
+ */
166
+ as?: as | 'Hex' | 'Bytes' | undefined
167
+ /** Optional BIP-39 passphrase. */
168
+ passphrase?: string | undefined
169
+ }
170
+
171
+ type ReturnType<as extends 'Hex' | 'Bytes'> = fromSeed.ReturnType<as>
172
+
173
+ type ErrorType = fromSeed.ErrorType
174
+ }
175
+
176
+ /**
177
+ * Derives an Ed25519 private key from a seed.
178
+ *
179
+ * The seed must contain at least 32 bytes of cryptographically strong key
180
+ * material. Do not pass a password directly; use a password KDF first.
181
+ *
182
+ * The permanent derivation contract uses the seed as the HMAC-SHA256
183
+ * key. The HMAC message uses the `ox.ed25519.fromSeed.v1` domain followed by a
184
+ * 32-bit big-endian counter set to zero.
185
+ *
186
+ * @example
187
+ * ```ts twoslash
188
+ * import { Ed25519 } from 'ox'
189
+ *
190
+ * const privateKey = Ed25519.fromSeed(
191
+ * '0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
192
+ * )
193
+ * ```
194
+ *
195
+ * @param seed - Seed containing at least 32 bytes of cryptographically strong key material.
196
+ * @param options - Options.
197
+ * @returns An Ed25519 private key.
198
+ */
199
+ export function fromSeed<as extends 'Hex' | 'Bytes' = 'Hex'>(
200
+ seed: Hex.Hex | Bytes.Bytes,
201
+ options: fromSeed.Options<as> = {},
202
+ ): fromSeed.ReturnType<as> {
203
+ const { as = 'Hex' } = options
204
+ const bytes = Bytes.from(seed)
205
+ if (bytes.length < 32) throw new InvalidSeedSizeError({ size: bytes.length })
206
+
207
+ const privateKey = keyDerivation.derive(bytes, fromSeedDomain)
208
+ if (as === 'Hex') {
209
+ const value = Hex.fromBytes(privateKey)
210
+ privateKey.fill(0)
211
+ return value as never
212
+ }
213
+ return privateKey as never
214
+ }
215
+
216
+ export declare namespace fromSeed {
217
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
218
+ /**
219
+ * Format of the returned private key.
220
+ * @default 'Hex'
221
+ */
222
+ as?: as | 'Hex' | 'Bytes' | undefined
223
+ }
224
+
225
+ type ReturnType<as extends 'Hex' | 'Bytes'> =
226
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
227
+ | (as extends 'Hex' ? Hex.Hex : never)
228
+
229
+ type ErrorType =
230
+ | Bytes.from.ErrorType
231
+ | Hex.fromBytes.ErrorType
232
+ | keyDerivation.derive.ErrorType
233
+ | InvalidSeedSizeError
234
+ | Errors.GlobalErrorType
235
+ }
236
+
126
237
  /**
127
238
  * Computes the Ed25519 public key from a provided private key.
128
239
  *
@@ -430,4 +541,24 @@ export declare namespace InvalidPrfSizeError {
430
541
  }
431
542
  }
432
543
 
433
- const fromPrfLabel = Bytes.fromString('ox.ed25519.fromPrf.v1')
544
+ /** Thrown when a seed contains fewer than 32 bytes. */
545
+ export class InvalidSeedSizeError extends Errors.BaseError {
546
+ override readonly name = 'Ed25519.InvalidSeedSizeError'
547
+
548
+ constructor(options: InvalidSeedSizeError.Options) {
549
+ super(
550
+ `Seed must contain at least 32 bytes. Received ${options.size} bytes.`,
551
+ )
552
+ }
553
+ }
554
+
555
+ export declare namespace InvalidSeedSizeError {
556
+ /** Options for {@link ox#Ed25519.InvalidSeedSizeError}. */
557
+ type Options = {
558
+ /** Received seed size. */
559
+ size: number
560
+ }
561
+ }
562
+
563
+ const fromPrfDomain = Bytes.fromString('ox.ed25519.fromPrf.v1')
564
+ const fromSeedDomain = Bytes.fromString('ox.ed25519.fromSeed.v1')