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.
- package/CHANGELOG.md +20 -0
- package/Ed25519/package.json +6 -0
- package/X25519/package.json +6 -0
- package/_cjs/core/Bls.js +10 -0
- package/_cjs/core/Bls.js.map +1 -1
- package/_cjs/core/Ed25519.js +53 -0
- package/_cjs/core/Ed25519.js.map +1 -0
- package/_cjs/core/Keystore.js +65 -5
- package/_cjs/core/Keystore.js.map +1 -1
- package/_cjs/core/P256.js +23 -0
- package/_cjs/core/P256.js.map +1 -1
- package/_cjs/core/Secp256k1.js +20 -0
- package/_cjs/core/Secp256k1.js.map +1 -1
- package/_cjs/core/WebCryptoP256.js +31 -0
- package/_cjs/core/WebCryptoP256.js.map +1 -1
- package/_cjs/core/X25519.js +45 -0
- package/_cjs/core/X25519.js.map +1 -0
- package/_cjs/index.js +4 -2
- package/_cjs/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/core/Bls.js +109 -0
- package/_esm/core/Bls.js.map +1 -1
- package/_esm/core/Ed25519.js +121 -0
- package/_esm/core/Ed25519.js.map +1 -0
- package/_esm/core/Keystore.js +107 -9
- package/_esm/core/Keystore.js.map +1 -1
- package/_esm/core/P256.js +54 -2
- package/_esm/core/P256.js.map +1 -1
- package/_esm/core/Secp256k1.js +50 -0
- package/_esm/core/Secp256k1.js.map +1 -1
- package/_esm/core/WebCryptoP256.js +72 -0
- package/_esm/core/WebCryptoP256.js.map +1 -1
- package/_esm/core/X25519.js +97 -0
- package/_esm/core/X25519.js.map +1 -0
- package/_esm/index.js +85 -4
- package/_esm/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/core/Bls.d.ts +124 -0
- package/_types/core/Bls.d.ts.map +1 -1
- package/_types/core/Ed25519.d.ts +156 -0
- package/_types/core/Ed25519.d.ts.map +1 -0
- package/_types/core/Keystore.d.ts +66 -8
- package/_types/core/Keystore.d.ts.map +1 -1
- package/_types/core/P256.d.ts +68 -2
- package/_types/core/P256.d.ts.map +1 -1
- package/_types/core/Secp256k1.d.ts +67 -0
- package/_types/core/Secp256k1.d.ts.map +1 -1
- package/_types/core/WebCryptoP256.d.ts +76 -1
- package/_types/core/WebCryptoP256.d.ts.map +1 -1
- package/_types/core/X25519.d.ts +127 -0
- package/_types/core/X25519.d.ts.map +1 -0
- package/_types/index.d.ts +85 -4
- package/_types/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/core/Bls.ts +150 -0
- package/core/Ed25519.ts +237 -0
- package/core/Keystore.ts +141 -12
- package/core/P256.ts +114 -2
- package/core/Secp256k1.ts +110 -0
- package/core/WebCryptoP256.ts +141 -1
- package/core/X25519.ts +202 -0
- package/index.ts +87 -4
- package/package.json +11 -1
- 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
|
-
* //
|
|
86
|
-
* const key =
|
|
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 =
|
|
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
|
|
99
|
+
export function decrypt<as extends 'Hex' | 'Bytes' = 'Hex'>(
|
|
99
100
|
keystore: Keystore,
|
|
100
101
|
key: Key,
|
|
101
102
|
options: decrypt.Options<as> = {},
|
|
102
|
-
):
|
|
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 =
|
|
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
|
|
182
|
+
export function encrypt(
|
|
182
183
|
privateKey: Bytes.Bytes | Hex.Hex,
|
|
183
184
|
key: Key,
|
|
184
185
|
options: encrypt.Options,
|
|
185
|
-
):
|
|
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.
|
|
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
|
*
|
package/core/WebCryptoP256.ts
CHANGED
|
@@ -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
|
|
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
|
*
|