ox 0.8.0 → 0.8.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/Ed25519/package.json +6 -0
  3. package/X25519/package.json +6 -0
  4. package/_cjs/core/Bls.js +10 -0
  5. package/_cjs/core/Bls.js.map +1 -1
  6. package/_cjs/core/Ed25519.js +53 -0
  7. package/_cjs/core/Ed25519.js.map +1 -0
  8. package/_cjs/core/Keystore.js +65 -5
  9. package/_cjs/core/Keystore.js.map +1 -1
  10. package/_cjs/core/P256.js +23 -0
  11. package/_cjs/core/P256.js.map +1 -1
  12. package/_cjs/core/Secp256k1.js +20 -0
  13. package/_cjs/core/Secp256k1.js.map +1 -1
  14. package/_cjs/core/WebCryptoP256.js +31 -0
  15. package/_cjs/core/WebCryptoP256.js.map +1 -1
  16. package/_cjs/core/X25519.js +45 -0
  17. package/_cjs/core/X25519.js.map +1 -0
  18. package/_cjs/index.js +4 -2
  19. package/_cjs/index.js.map +1 -1
  20. package/_cjs/version.js +1 -1
  21. package/_esm/core/Bls.js +109 -0
  22. package/_esm/core/Bls.js.map +1 -1
  23. package/_esm/core/Ed25519.js +121 -0
  24. package/_esm/core/Ed25519.js.map +1 -0
  25. package/_esm/core/Keystore.js +107 -9
  26. package/_esm/core/Keystore.js.map +1 -1
  27. package/_esm/core/P256.js +54 -2
  28. package/_esm/core/P256.js.map +1 -1
  29. package/_esm/core/Secp256k1.js +50 -0
  30. package/_esm/core/Secp256k1.js.map +1 -1
  31. package/_esm/core/WebCryptoP256.js +72 -0
  32. package/_esm/core/WebCryptoP256.js.map +1 -1
  33. package/_esm/core/X25519.js +97 -0
  34. package/_esm/core/X25519.js.map +1 -0
  35. package/_esm/index.js +85 -4
  36. package/_esm/index.js.map +1 -1
  37. package/_esm/version.js +1 -1
  38. package/_types/core/Bls.d.ts +124 -0
  39. package/_types/core/Bls.d.ts.map +1 -1
  40. package/_types/core/Ed25519.d.ts +156 -0
  41. package/_types/core/Ed25519.d.ts.map +1 -0
  42. package/_types/core/Keystore.d.ts +66 -8
  43. package/_types/core/Keystore.d.ts.map +1 -1
  44. package/_types/core/P256.d.ts +68 -2
  45. package/_types/core/P256.d.ts.map +1 -1
  46. package/_types/core/Secp256k1.d.ts +67 -0
  47. package/_types/core/Secp256k1.d.ts.map +1 -1
  48. package/_types/core/WebCryptoP256.d.ts +76 -1
  49. package/_types/core/WebCryptoP256.d.ts.map +1 -1
  50. package/_types/core/X25519.d.ts +127 -0
  51. package/_types/core/X25519.d.ts.map +1 -0
  52. package/_types/index.d.ts +85 -4
  53. package/_types/index.d.ts.map +1 -1
  54. package/_types/version.d.ts +1 -1
  55. package/core/Bls.ts +150 -0
  56. package/core/Ed25519.ts +237 -0
  57. package/core/Keystore.ts +141 -12
  58. package/core/P256.ts +114 -2
  59. package/core/Secp256k1.ts +110 -0
  60. package/core/WebCryptoP256.ts +141 -1
  61. package/core/X25519.ts +202 -0
  62. package/index.ts +87 -4
  63. package/package.json +11 -1
  64. package/version.ts +1 -1
package/core/Keystore.ts CHANGED
@@ -12,6 +12,7 @@ import * as Bytes from './Bytes.js'
12
12
  import type * as Errors from './Errors.js'
13
13
  import * as Hash from './Hash.js'
14
14
  import type * as Hex from './Hex.js'
15
+ import type { OneOf } from './internal/types.js'
15
16
 
16
17
  /** Base Derivation Options. */
17
18
  type BaseDeriveOpts<
@@ -82,11 +83,11 @@ export type ScryptDeriveOpts = BaseDeriveOpts<
82
83
  * // JSON keystore.
83
84
  * const keystore = { crypto: { ... }, id: '...', version: 3 }
84
85
  *
85
- * // Key that was previously derived from `Keystore.scrypt` or `Keystore.pbkdf2`.
86
- * const key = "0x..."
86
+ * // Derive the key using your password.
87
+ * const key = Keystore.toKey(keystore, { password: 'hunter2' })
87
88
  *
88
89
  * // Decrypt the private key.
89
- * const privateKey = await Keystore.decrypt(keystore, key)
90
+ * const privateKey = Keystore.decrypt(keystore, key)
90
91
  * // @log: "0x..."
91
92
  * ```
92
93
  *
@@ -95,11 +96,11 @@ export type ScryptDeriveOpts = BaseDeriveOpts<
95
96
  * @param options - Decryption options.
96
97
  * @returns Decrypted private key.
97
98
  */
98
- export async function decrypt<as extends 'Hex' | 'Bytes' = 'Hex'>(
99
+ export function decrypt<as extends 'Hex' | 'Bytes' = 'Hex'>(
99
100
  keystore: Keystore,
100
101
  key: Key,
101
102
  options: decrypt.Options<as> = {},
102
- ): Promise<decrypt.ReturnType<as>> {
103
+ ): decrypt.ReturnType<as> {
103
104
  const { as = 'Hex' } = options
104
105
  const key_ = Bytes.from(typeof key === 'function' ? key() : key)
105
106
 
@@ -151,7 +152,7 @@ export declare namespace decrypt {
151
152
  * const [key, opts] = Keystore.pbkdf2({ password: 'testpassword' })
152
153
  *
153
154
  * // Encrypt the private key.
154
- * const encrypted = await Keystore.encrypt(privateKey, key, opts)
155
+ * const encrypted = Keystore.encrypt(privateKey, key, opts)
155
156
  * // @log: {
156
157
  * // @log: "crypto": {
157
158
  * // @log: "cipher": "aes-128-ctr",
@@ -178,11 +179,11 @@ export declare namespace decrypt {
178
179
  * @param options - Encryption options.
179
180
  * @returns Encrypted keystore.
180
181
  */
181
- export async function encrypt(
182
+ export function encrypt(
182
183
  privateKey: Bytes.Bytes | Hex.Hex,
183
184
  key: Key,
184
185
  options: encrypt.Options,
185
- ): Promise<Keystore> {
186
+ ): Keystore {
186
187
  const { id = crypto.randomUUID(), kdf, kdfparams, iv } = options
187
188
 
188
189
  const key_ = Bytes.from(typeof key === 'function' ? key() : key)
@@ -315,10 +316,7 @@ export declare namespace pbkdf2Async {
315
316
  * @returns Scrypt key.
316
317
  */
317
318
  export function scrypt(options: scrypt.Options) {
318
- const { iv, n = 262_144, password } = options
319
-
320
- const p = 8
321
- const r = 1
319
+ const { iv, n = 262_144, password, p = 8, r = 1 } = options
322
320
 
323
321
  const salt = options.salt ? Bytes.from(options.salt) : Bytes.random(32)
324
322
  const key = Bytes.toHex(
@@ -344,6 +342,10 @@ export declare namespace scrypt {
344
342
  iv?: Bytes.Bytes | Hex.Hex | undefined
345
343
  /** Cost factor. @default 262_144 */
346
344
  n?: number | undefined
345
+ /** Parallelization factor. @default 8 */
346
+ p?: number | undefined
347
+ /** Block size. @default 1 */
348
+ r?: number | undefined
347
349
  /** Password to derive key from. */
348
350
  password: string
349
351
  /** Salt to use for key derivation. @default `Bytes.random(32)` */
@@ -392,6 +394,133 @@ export declare namespace scryptAsync {
392
394
  type Options = scrypt.Options
393
395
  }
394
396
 
397
+ /**
398
+ * Extracts a Key from a JSON Keystore to use for decryption.
399
+ *
400
+ * @example
401
+ * ```ts twoslash
402
+ * // @noErrors
403
+ * import { Keystore } from 'ox'
404
+ *
405
+ * // JSON keystore.
406
+ * const keystore = { crypto: { ... }, id: '...', version: 3 }
407
+ *
408
+ * const key = Keystore.toKey(keystore, { password: 'hunter2' }) // [!code focus]
409
+ *
410
+ * const decrypted = Keystore.decrypt(keystore, key)
411
+ * ```
412
+ *
413
+ * @param keystore - JSON Keystore
414
+ * @param options - Options
415
+ * @returns Key
416
+ */
417
+ export function toKey(keystore: Keystore, options: toKey.Options): Key {
418
+ const { crypto } = keystore
419
+ const { password } = options
420
+ const { cipherparams, kdf, kdfparams } = crypto
421
+ const { iv } = cipherparams
422
+ const { c, n, p, r, salt } = kdfparams as OneOf<
423
+ Pbkdf2DeriveOpts['kdfparams'] | ScryptDeriveOpts['kdfparams']
424
+ >
425
+
426
+ const [key] = (() => {
427
+ switch (kdf) {
428
+ case 'scrypt':
429
+ return scrypt({
430
+ iv: Bytes.from(`0x${iv}`),
431
+ n,
432
+ p,
433
+ r,
434
+ salt: Bytes.from(`0x${salt}`),
435
+ password,
436
+ })
437
+ case 'pbkdf2':
438
+ return pbkdf2({
439
+ iv: Bytes.from(`0x${iv}`),
440
+ iterations: c,
441
+ password,
442
+ salt: Bytes.from(`0x${salt}`),
443
+ })
444
+ default:
445
+ throw new Error('unsupported kdf')
446
+ }
447
+ })()
448
+
449
+ return key
450
+ }
451
+
452
+ export declare namespace toKey {
453
+ type Options = {
454
+ /** Password to derive key from. */
455
+ password: string
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Extracts a Key asynchronously from a JSON Keystore to use for decryption.
461
+ *
462
+ * @example
463
+ * ```ts twoslash
464
+ * // @noErrors
465
+ * import { Keystore } from 'ox'
466
+ *
467
+ * // JSON keystore.
468
+ * const keystore = { crypto: { ... }, id: '...', version: 3 }
469
+ *
470
+ * const key = await Keystore.toKeyAsync(keystore, { password: 'hunter2' }) // [!code focus]
471
+ *
472
+ * const decrypted = Keystore.decrypt(keystore, key)
473
+ * ```
474
+ *
475
+ * @param keystore - JSON Keystore
476
+ * @param options - Options
477
+ * @returns Key
478
+ */
479
+ export async function toKeyAsync(
480
+ keystore: Keystore,
481
+ options: toKeyAsync.Options,
482
+ ): Promise<Key> {
483
+ const { crypto } = keystore
484
+ const { password } = options
485
+ const { cipherparams, kdf, kdfparams } = crypto
486
+ const { iv } = cipherparams
487
+ const { c, n, p, r, salt } = kdfparams as OneOf<
488
+ Pbkdf2DeriveOpts['kdfparams'] | ScryptDeriveOpts['kdfparams']
489
+ >
490
+
491
+ const [key] = await (async () => {
492
+ switch (kdf) {
493
+ case 'scrypt':
494
+ return await scryptAsync({
495
+ iv: Bytes.from(`0x${iv}`),
496
+ n,
497
+ p,
498
+ r,
499
+ salt: Bytes.from(`0x${salt}`),
500
+ password,
501
+ })
502
+ case 'pbkdf2':
503
+ return await pbkdf2({
504
+ iv: Bytes.from(`0x${iv}`),
505
+ iterations: c,
506
+ password,
507
+ salt: Bytes.from(`0x${salt}`),
508
+ })
509
+ default:
510
+ throw new Error('unsupported kdf')
511
+ }
512
+ })()
513
+
514
+ return key
515
+ }
516
+
517
+ export declare namespace toKeyAsync {
518
+ type Options = {
519
+ /** Password to derive key from. */
520
+ password: string
521
+ }
522
+ }
523
+
395
524
  ///////////////////////////////////////////////////////////////////////////
396
525
 
397
526
  /** @internal */
package/core/P256.ts CHANGED
@@ -9,6 +9,54 @@ import * as Entropy from './internal/entropy.js'
9
9
  /** Re-export of noble/curves P256 utilities. */
10
10
  export const noble = secp256r1
11
11
 
12
+ /**
13
+ * Creates a new P256 ECDSA key pair consisting of a private key and its corresponding public key.
14
+ *
15
+ * @example
16
+ * ```ts twoslash
17
+ * import { P256 } from 'ox'
18
+ *
19
+ * const { privateKey, publicKey } = P256.createKeyPair()
20
+ * ```
21
+ *
22
+ * @param options - The options to generate the key pair.
23
+ * @returns The generated key pair containing both private and public keys.
24
+ */
25
+ export function createKeyPair<as extends 'Hex' | 'Bytes' = 'Hex'>(
26
+ options: createKeyPair.Options<as> = {},
27
+ ): createKeyPair.ReturnType<as> {
28
+ const { as = 'Hex' } = options
29
+ const privateKey = randomPrivateKey({ as })
30
+ const publicKey = getPublicKey({ privateKey })
31
+
32
+ return {
33
+ privateKey: privateKey as never,
34
+ publicKey,
35
+ }
36
+ }
37
+
38
+ export declare namespace createKeyPair {
39
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
40
+ /**
41
+ * Format of the returned private key.
42
+ * @default 'Hex'
43
+ */
44
+ as?: as | 'Hex' | 'Bytes' | undefined
45
+ }
46
+
47
+ type ReturnType<as extends 'Hex' | 'Bytes'> = {
48
+ privateKey:
49
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
50
+ | (as extends 'Hex' ? Hex.Hex : never)
51
+ publicKey: PublicKey.PublicKey
52
+ }
53
+
54
+ type ErrorType =
55
+ | Hex.fromBytes.ErrorType
56
+ | PublicKey.from.ErrorType
57
+ | Errors.GlobalErrorType
58
+ }
59
+
12
60
  /**
13
61
  * Computes the P256 ECDSA public key from a provided private key.
14
62
  *
@@ -45,6 +93,71 @@ export declare namespace getPublicKey {
45
93
  type ErrorType = Errors.GlobalErrorType
46
94
  }
47
95
 
96
+ /**
97
+ * Computes a shared secret using ECDH (Elliptic Curve Diffie-Hellman) between a private key and a public key.
98
+ *
99
+ * @example
100
+ * ```ts twoslash
101
+ * import { P256 } from 'ox'
102
+ *
103
+ * const { privateKey: privateKeyA } = P256.createKeyPair()
104
+ * const { publicKey: publicKeyB } = P256.createKeyPair()
105
+ *
106
+ * const sharedSecret = P256.getSharedSecret({
107
+ * privateKey: privateKeyA,
108
+ * publicKey: publicKeyB
109
+ * })
110
+ * ```
111
+ *
112
+ * @param options - The options to compute the shared secret.
113
+ * @returns The computed shared secret.
114
+ */
115
+ export function getSharedSecret<as extends 'Hex' | 'Bytes' = 'Hex'>(
116
+ options: getSharedSecret.Options<as>,
117
+ ): getSharedSecret.ReturnType<as> {
118
+ const { as = 'Hex', privateKey, publicKey } = options
119
+ const point = secp256r1.ProjectivePoint.fromHex(
120
+ PublicKey.toHex(publicKey).slice(2),
121
+ )
122
+ const privateKeyHex =
123
+ typeof privateKey === 'string'
124
+ ? privateKey.slice(2)
125
+ : Hex.fromBytes(privateKey).slice(2)
126
+ const sharedPoint = point.multiply(
127
+ secp256r1.utils.normPrivateKeyToScalar(privateKeyHex),
128
+ )
129
+ const sharedSecret = sharedPoint.toRawBytes(true) // compressed format
130
+ if (as === 'Hex') return Hex.fromBytes(sharedSecret) as never
131
+ return sharedSecret as never
132
+ }
133
+
134
+ export declare namespace getSharedSecret {
135
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
136
+ /**
137
+ * Format of the returned shared secret.
138
+ * @default 'Hex'
139
+ */
140
+ as?: as | 'Hex' | 'Bytes' | undefined
141
+ /**
142
+ * Private key to use for the shared secret computation.
143
+ */
144
+ privateKey: Hex.Hex | Bytes.Bytes
145
+ /**
146
+ * Public key to use for the shared secret computation.
147
+ */
148
+ publicKey: PublicKey.PublicKey<boolean>
149
+ }
150
+
151
+ type ReturnType<as extends 'Hex' | 'Bytes'> =
152
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
153
+ | (as extends 'Hex' ? Hex.Hex : never)
154
+
155
+ type ErrorType =
156
+ | Hex.fromBytes.ErrorType
157
+ | PublicKey.toHex.ErrorType
158
+ | Errors.GlobalErrorType
159
+ }
160
+
48
161
  /**
49
162
  * Generates a random P256 ECDSA private key.
50
163
  *
@@ -204,8 +317,7 @@ export declare namespace sign {
204
317
  * ```ts twoslash
205
318
  * import { P256 } from 'ox'
206
319
  *
207
- * const privateKey = P256.randomPrivateKey()
208
- * const publicKey = P256.getPublicKey({ privateKey })
320
+ * const { privateKey, publicKey } = P256.createKeyPair()
209
321
  * const signature = P256.sign({ payload: '0xdeadbeef', privateKey })
210
322
  *
211
323
  * const verified = P256.verify({ // [!code focus]
package/core/Secp256k1.ts CHANGED
@@ -11,6 +11,54 @@ import type { OneOf } from './internal/types.js'
11
11
  /** Re-export of noble/curves secp256k1 utilities. */
12
12
  export const noble = secp256k1
13
13
 
14
+ /**
15
+ * Creates a new secp256k1 ECDSA key pair consisting of a private key and its corresponding public key.
16
+ *
17
+ * @example
18
+ * ```ts twoslash
19
+ * import { Secp256k1 } from 'ox'
20
+ *
21
+ * const { privateKey, publicKey } = Secp256k1.createKeyPair()
22
+ * ```
23
+ *
24
+ * @param options - The options to generate the key pair.
25
+ * @returns The generated key pair containing both private and public keys.
26
+ */
27
+ export function createKeyPair<as extends 'Hex' | 'Bytes' = 'Hex'>(
28
+ options: createKeyPair.Options<as> = {},
29
+ ): createKeyPair.ReturnType<as> {
30
+ const { as = 'Hex' } = options
31
+ const privateKey = randomPrivateKey({ as })
32
+ const publicKey = getPublicKey({ privateKey })
33
+
34
+ return {
35
+ privateKey: privateKey as never,
36
+ publicKey,
37
+ }
38
+ }
39
+
40
+ export declare namespace createKeyPair {
41
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
42
+ /**
43
+ * Format of the returned private key.
44
+ * @default 'Hex'
45
+ */
46
+ as?: as | 'Hex' | 'Bytes' | undefined
47
+ }
48
+
49
+ type ReturnType<as extends 'Hex' | 'Bytes'> = {
50
+ privateKey:
51
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
52
+ | (as extends 'Hex' ? Hex.Hex : never)
53
+ publicKey: PublicKey.PublicKey
54
+ }
55
+
56
+ type ErrorType =
57
+ | Hex.fromBytes.ErrorType
58
+ | PublicKey.from.ErrorType
59
+ | Errors.GlobalErrorType
60
+ }
61
+
14
62
  /**
15
63
  * Computes the secp256k1 ECDSA public key from a provided private key.
16
64
  *
@@ -48,6 +96,68 @@ export declare namespace getPublicKey {
48
96
  | Errors.GlobalErrorType
49
97
  }
50
98
 
99
+ /**
100
+ * Computes a shared secret using ECDH (Elliptic Curve Diffie-Hellman) between a private key and a public key.
101
+ *
102
+ * @example
103
+ * ```ts twoslash
104
+ * import { Secp256k1 } from 'ox'
105
+ *
106
+ * const { privateKey: privateKeyA } = Secp256k1.createKeyPair()
107
+ * const { publicKey: publicKeyB } = Secp256k1.createKeyPair()
108
+ *
109
+ * const sharedSecret = Secp256k1.getSharedSecret({
110
+ * privateKey: privateKeyA,
111
+ * publicKey: publicKeyB
112
+ * })
113
+ * ```
114
+ *
115
+ * @param options - The options to compute the shared secret.
116
+ * @returns The computed shared secret.
117
+ */
118
+ export function getSharedSecret<as extends 'Hex' | 'Bytes' = 'Hex'>(
119
+ options: getSharedSecret.Options<as>,
120
+ ): getSharedSecret.ReturnType<as> {
121
+ const { as = 'Hex', privateKey, publicKey } = options
122
+ const point = secp256k1.ProjectivePoint.fromHex(
123
+ PublicKey.toHex(publicKey).slice(2),
124
+ )
125
+ const sharedPoint = point.multiply(
126
+ secp256k1.utils.normPrivateKeyToScalar(Hex.from(privateKey).slice(2)),
127
+ )
128
+ const sharedSecret = sharedPoint.toRawBytes(true) // compressed format
129
+ if (as === 'Hex') return Hex.fromBytes(sharedSecret) as never
130
+ return sharedSecret as never
131
+ }
132
+
133
+ export declare namespace getSharedSecret {
134
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
135
+ /**
136
+ * Format of the returned shared secret.
137
+ * @default 'Hex'
138
+ */
139
+ as?: as | 'Hex' | 'Bytes' | undefined
140
+ /**
141
+ * Private key to use for the shared secret computation.
142
+ */
143
+ privateKey: Hex.Hex | Bytes.Bytes
144
+ /**
145
+ * Public key to use for the shared secret computation.
146
+ */
147
+ publicKey: PublicKey.PublicKey<boolean>
148
+ }
149
+
150
+ type ReturnType<as extends 'Hex' | 'Bytes'> =
151
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
152
+ | (as extends 'Hex' ? Hex.Hex : never)
153
+
154
+ type ErrorType =
155
+ | Hex.from.ErrorType
156
+ | PublicKey.toHex.ErrorType
157
+ | Hex.fromBytes.ErrorType
158
+ | Errors.GlobalErrorType
159
+ }
160
+
51
161
  /**
52
162
  * Generates a random ECDSA private key on the secp256k1 curve.
53
163
  *
@@ -1,7 +1,7 @@
1
1
  import { p256 } from '@noble/curves/p256'
2
2
  import * as Bytes from './Bytes.js'
3
3
  import type * as Errors from './Errors.js'
4
- import type * as Hex from './Hex.js'
4
+ import * as Hex from './Hex.js'
5
5
  import * as PublicKey from './PublicKey.js'
6
6
  import type * as Signature from './Signature.js'
7
7
  import type { Compute } from './internal/types.js'
@@ -68,6 +68,146 @@ export declare namespace createKeyPair {
68
68
  type ErrorType = PublicKey.from.ErrorType | Errors.GlobalErrorType
69
69
  }
70
70
 
71
+ /**
72
+ * Generates an ECDH P256 key pair for key agreement that includes:
73
+ *
74
+ * - a `privateKey` of type [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey)
75
+ * - a `publicKey` of type {@link ox#PublicKey.PublicKey}
76
+ *
77
+ * @example
78
+ * ```ts twoslash
79
+ * import { WebCryptoP256 } from 'ox'
80
+ *
81
+ * const { publicKey, privateKey } = await WebCryptoP256.createKeyPairECDH()
82
+ * // @log: {
83
+ * // @log: privateKey: CryptoKey {},
84
+ * // @log: publicKey: {
85
+ * // @log: x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
86
+ * // @log: y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
87
+ * // @log: prefix: 4,
88
+ * // @log: },
89
+ * // @log: }
90
+ * ```
91
+ *
92
+ * @param options - Options for creating the key pair.
93
+ * @returns The key pair.
94
+ */
95
+ export async function createKeyPairECDH(
96
+ options: createKeyPairECDH.Options = {},
97
+ ): Promise<createKeyPairECDH.ReturnType> {
98
+ const { extractable = false } = options
99
+ const keypair = await globalThis.crypto.subtle.generateKey(
100
+ {
101
+ name: 'ECDH',
102
+ namedCurve: 'P-256',
103
+ },
104
+ extractable,
105
+ ['deriveKey', 'deriveBits'],
106
+ )
107
+ const publicKey_raw = await globalThis.crypto.subtle.exportKey(
108
+ 'raw',
109
+ keypair.publicKey,
110
+ )
111
+ const publicKey = PublicKey.from(new Uint8Array(publicKey_raw))
112
+ return {
113
+ privateKey: keypair.privateKey,
114
+ publicKey,
115
+ }
116
+ }
117
+
118
+ export declare namespace createKeyPairECDH {
119
+ type Options = {
120
+ /** A boolean value indicating whether it will be possible to export the private key using `globalThis.crypto.subtle.exportKey()`. */
121
+ extractable?: boolean | undefined
122
+ }
123
+
124
+ type ReturnType = Compute<{
125
+ privateKey: CryptoKey
126
+ publicKey: PublicKey.PublicKey
127
+ }>
128
+
129
+ type ErrorType = PublicKey.from.ErrorType | Errors.GlobalErrorType
130
+ }
131
+
132
+ /**
133
+ * Computes a shared secret using ECDH (Elliptic Curve Diffie-Hellman) between a private key and a public key using Web Crypto APIs.
134
+ *
135
+ * @example
136
+ * ```ts twoslash
137
+ * import { WebCryptoP256 } from 'ox'
138
+ *
139
+ * const { privateKey: privateKeyA } = await WebCryptoP256.createKeyPairECDH()
140
+ * const { publicKey: publicKeyB } = await WebCryptoP256.createKeyPairECDH()
141
+ *
142
+ * const sharedSecret = await WebCryptoP256.getSharedSecret({
143
+ * privateKey: privateKeyA,
144
+ * publicKey: publicKeyB
145
+ * })
146
+ * ```
147
+ *
148
+ * @param options - The options to compute the shared secret.
149
+ * @returns The computed shared secret.
150
+ */
151
+ export async function getSharedSecret<as extends 'Hex' | 'Bytes' = 'Hex'>(
152
+ options: getSharedSecret.Options<as>,
153
+ ): Promise<getSharedSecret.ReturnType<as>> {
154
+ const { as = 'Hex', privateKey, publicKey } = options
155
+
156
+ if (privateKey.algorithm.name === 'ECDSA') {
157
+ throw new Error(
158
+ 'privateKey is not compatible with ECDH. please use `createKeyPairECDH` to create an ECDH key.',
159
+ )
160
+ }
161
+
162
+ const publicKeyCrypto = await globalThis.crypto.subtle.importKey(
163
+ 'raw',
164
+ PublicKey.toBytes(publicKey),
165
+ { name: 'ECDH', namedCurve: 'P-256' },
166
+ false,
167
+ [],
168
+ )
169
+
170
+ const sharedSecretBuffer = await globalThis.crypto.subtle.deriveBits(
171
+ {
172
+ name: 'ECDH',
173
+ public: publicKeyCrypto,
174
+ },
175
+ privateKey,
176
+ 256, // 32 bytes * 8 bits/byte
177
+ )
178
+
179
+ const sharedSecret = new Uint8Array(sharedSecretBuffer)
180
+ if (as === 'Hex') return Hex.fromBytes(sharedSecret) as never
181
+ return sharedSecret as never
182
+ }
183
+
184
+ export declare namespace getSharedSecret {
185
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex'> = {
186
+ /**
187
+ * Format of the returned shared secret.
188
+ * @default 'Hex'
189
+ */
190
+ as?: as | 'Hex' | 'Bytes' | undefined
191
+ /**
192
+ * Private key to use for the shared secret computation (must be a CryptoKey for ECDH).
193
+ */
194
+ privateKey: CryptoKey
195
+ /**
196
+ * Public key to use for the shared secret computation.
197
+ */
198
+ publicKey: PublicKey.PublicKey<boolean>
199
+ }
200
+
201
+ type ReturnType<as extends 'Hex' | 'Bytes'> =
202
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
203
+ | (as extends 'Hex' ? Hex.Hex : never)
204
+
205
+ type ErrorType =
206
+ | PublicKey.toBytes.ErrorType
207
+ | Hex.fromBytes.ErrorType
208
+ | Errors.GlobalErrorType
209
+ }
210
+
71
211
  /**
72
212
  * Signs a payload with the provided `CryptoKey` private key and returns a P256 signature.
73
213
  *