@did-btcr2/keypair 0.5.1 → 0.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/README.md +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/pair.js +76 -65
- package/dist/cjs/pair.js.map +1 -1
- package/dist/cjs/public.js +64 -47
- package/dist/cjs/public.js.map +1 -1
- package/dist/cjs/secret.js +35 -33
- package/dist/cjs/secret.js.map +1 -1
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pair.js +76 -65
- package/dist/esm/pair.js.map +1 -1
- package/dist/esm/public.js +64 -47
- package/dist/esm/public.js.map +1 -1
- package/dist/esm/secret.js +35 -33
- package/dist/esm/secret.js.map +1 -1
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/pair.d.ts +42 -51
- package/dist/types/pair.d.ts.map +1 -1
- package/dist/types/public.d.ts +68 -44
- package/dist/types/public.d.ts.map +1 -1
- package/dist/types/secret.d.ts +35 -36
- package/dist/types/secret.d.ts.map +1 -1
- package/dist/types/types.d.ts +16 -0
- package/dist/types/types.d.ts.map +1 -0
- package/package.json +3 -4
- package/src/index.ts +2 -1
- package/src/pair.ts +89 -93
- package/src/public.ts +115 -72
- package/src/secret.ts +51 -49
- package/src/types.ts +19 -0
package/src/pair.ts
CHANGED
|
@@ -4,25 +4,26 @@ import {
|
|
|
4
4
|
KeyPairError,
|
|
5
5
|
SchnorrKeyPairObject
|
|
6
6
|
} from '@did-btcr2/common';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { CompressedSecp256k1PublicKey } from './public.js';
|
|
8
|
+
import { Secp256k1SecretKey } from './secret.js';
|
|
9
|
+
import { MultibaseKeys, RawSchnorrKeyPair, SchnorrKeyPairParams } from './types.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* General KeyPair interface used by SchnorrKeyPair class.
|
|
12
13
|
* @interface KeyPair
|
|
13
14
|
* @type {KeyPair}
|
|
14
15
|
*/
|
|
15
16
|
export interface KeyPair {
|
|
16
17
|
/**
|
|
17
|
-
* @type {
|
|
18
|
+
* @type {CompressedSecp256k1PublicKey} The public key associated with the SchnorrKeyPair (required).
|
|
18
19
|
*/
|
|
19
|
-
readonly publicKey:
|
|
20
|
+
readonly publicKey: CompressedSecp256k1PublicKey;
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* @type {
|
|
23
|
+
* @type {Secp256k1SecretKey} The secret key associated with the SchnorrKeyPair (optional).
|
|
23
24
|
* @throws {KeyPairError} If the secret key is not available.
|
|
24
25
|
*/
|
|
25
|
-
readonly secretKey?:
|
|
26
|
+
readonly secretKey?: Secp256k1SecretKey;
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* JSON representation of the SchnorrKeyPair object.
|
|
@@ -31,32 +32,17 @@ export interface KeyPair {
|
|
|
31
32
|
json(): SchnorrKeyPairObject;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
type RawKeyPair = {
|
|
35
|
-
public: KeyBytes;
|
|
36
|
-
secret?: KeyBytes
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Params for the {@link SchnorrKeyPair} constructor */
|
|
40
|
-
interface KeyParams {
|
|
41
|
-
secretKey?: SecretKey | KeyBytes;
|
|
42
|
-
publicKey?: PublicKey | KeyBytes;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface MultibaseKeys {
|
|
46
|
-
publicKeyMultibase: string;
|
|
47
|
-
secretKeyMultibase: string
|
|
48
|
-
}
|
|
49
35
|
/**
|
|
50
|
-
* Encapsulates a
|
|
36
|
+
* Encapsulates a CompressedSecp256k1PublicKey and a Secp256k1SecretKey object as a single SchnorrKeyPair object.
|
|
51
37
|
* @class SchnorrKeyPair
|
|
52
38
|
* @type {SchnorrKeyPair}
|
|
53
39
|
*/
|
|
54
40
|
export class SchnorrKeyPair implements KeyPair {
|
|
55
|
-
/** @type {
|
|
56
|
-
private _secretKey?:
|
|
41
|
+
/** @type {Secp256k1SecretKey} The secret key object */
|
|
42
|
+
private _secretKey?: Secp256k1SecretKey;
|
|
57
43
|
|
|
58
|
-
/** @type {
|
|
59
|
-
private _publicKey:
|
|
44
|
+
/** @type {CompressedSecp256k1PublicKey} The public key object */;
|
|
45
|
+
private _publicKey: CompressedSecp256k1PublicKey;
|
|
60
46
|
|
|
61
47
|
/** @type {string} The public key in multibase format */
|
|
62
48
|
private _publicKeyMultibase: string;
|
|
@@ -66,41 +52,45 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
66
52
|
|
|
67
53
|
/**
|
|
68
54
|
* Creates an instance of Keys. Must provide a at least a secret key.
|
|
69
|
-
* Can optionally provide both a
|
|
70
|
-
* @param {
|
|
55
|
+
* Can optionally provide both a secret and public key, but must be a valid pair.
|
|
56
|
+
* @param {SchnorrKeyPairParams} params The parameters to initialize the Keys object.
|
|
57
|
+
* @param {CompressedSecp256k1PublicKey | KeyBytes} params.publicKey The public key object or bytes
|
|
58
|
+
* @param {Secp256k1SecretKey | KeyBytes} [params.secret] The secret key object or bytes
|
|
59
|
+
* @throws {KeyPairError} If neither a public key or secret key is provided.
|
|
60
|
+
* @throws {KeyPairError} If the public key is not a valid pair with the secret key.
|
|
71
61
|
*/
|
|
72
|
-
constructor(
|
|
62
|
+
constructor(params: SchnorrKeyPairParams = {}) {
|
|
73
63
|
// If no secret key or public key, throw an error
|
|
74
|
-
if (!publicKey && !secretKey) {
|
|
64
|
+
if (!params.publicKey && !params.secretKey) {
|
|
75
65
|
throw new KeyPairError('Argument missing: must at least provide a publicKey', 'CONSTRUCTOR_ERROR');
|
|
76
66
|
}
|
|
77
67
|
|
|
78
|
-
// Set the
|
|
79
|
-
if(secretKey instanceof Uint8Array) {
|
|
80
|
-
this._secretKey = new
|
|
81
|
-
} else if (secretKey instanceof
|
|
82
|
-
this._secretKey = secretKey;
|
|
68
|
+
// Set the secretKey
|
|
69
|
+
if(params.secretKey instanceof Uint8Array) {
|
|
70
|
+
this._secretKey = new Secp256k1SecretKey(params.secretKey);
|
|
71
|
+
} else if (params.secretKey instanceof Secp256k1SecretKey) {
|
|
72
|
+
this._secretKey = params.secretKey;
|
|
83
73
|
}
|
|
84
74
|
|
|
85
|
-
// Set the
|
|
86
|
-
if(publicKey instanceof
|
|
87
|
-
this._publicKey = publicKey;
|
|
88
|
-
} else if (publicKey instanceof Uint8Array) {
|
|
89
|
-
this._publicKey = new
|
|
75
|
+
// Set the publicKey
|
|
76
|
+
if(params.publicKey instanceof CompressedSecp256k1PublicKey) {
|
|
77
|
+
this._publicKey = params.publicKey;
|
|
78
|
+
} else if (params.publicKey instanceof Uint8Array) {
|
|
79
|
+
this._publicKey = new CompressedSecp256k1PublicKey(params.publicKey);
|
|
90
80
|
} else {
|
|
91
|
-
this._publicKey =
|
|
81
|
+
this._publicKey = this._secretKey!.computePublicKey();
|
|
92
82
|
}
|
|
93
83
|
|
|
94
|
-
this._publicKeyMultibase = this._publicKey.multibase.
|
|
84
|
+
this._publicKeyMultibase = this._publicKey.multibase.encoded;
|
|
95
85
|
this._secretKeyMultibase = this._secretKey ? this._secretKey.multibase : '';
|
|
96
86
|
}
|
|
97
87
|
|
|
98
88
|
/**
|
|
99
|
-
* Get the
|
|
100
|
-
* @returns {
|
|
89
|
+
* Get the Secp256k1SecretKey.
|
|
90
|
+
* @returns {Secp256k1SecretKey} The Secp256k1SecretKey object
|
|
101
91
|
* @throws {KeyPairError} If the secret key is not available
|
|
102
92
|
*/
|
|
103
|
-
get secretKey():
|
|
93
|
+
get secretKey(): Secp256k1SecretKey {
|
|
104
94
|
// If the secret key is not available, throw an error
|
|
105
95
|
if(!this._secretKey) {
|
|
106
96
|
throw new KeyPairError('Secret key not available', 'SECRET_KEY_ERROR');
|
|
@@ -110,39 +100,44 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
110
100
|
throw new KeyPairError('Secret key is not valid', 'SECRET_KEY_ERROR');
|
|
111
101
|
}
|
|
112
102
|
// Return a copy of the secret key
|
|
113
|
-
const
|
|
114
|
-
return
|
|
103
|
+
const secret = this._secretKey;
|
|
104
|
+
return secret;
|
|
115
105
|
}
|
|
116
106
|
|
|
117
107
|
/**
|
|
118
|
-
* Set the
|
|
119
|
-
* @param {
|
|
108
|
+
* Set the CompressedSecp256k1PublicKey.
|
|
109
|
+
* @param {CompressedSecp256k1PublicKey} publicKey The CompressedSecp256k1PublicKey object
|
|
120
110
|
* @throws {KeyPairError} If the public key is not a valid pair with the secret key.
|
|
121
111
|
*/
|
|
122
|
-
set publicKey(publicKey:
|
|
112
|
+
set publicKey(publicKey: CompressedSecp256k1PublicKey) {
|
|
123
113
|
// If the public key is not a valid pair with the secret key, throw an error
|
|
124
|
-
if(this.secretKey
|
|
125
|
-
|
|
114
|
+
if(this.secretKey) {
|
|
115
|
+
if(!this.secretKey.hasValidPublicKey()) {
|
|
116
|
+
throw new KeyPairError('Secret key is not valid', 'SECRET_KEY_ERROR');
|
|
117
|
+
}
|
|
118
|
+
const cPk = this.secretKey.computePublicKey();
|
|
119
|
+
if(!publicKey.equals(cPk))
|
|
120
|
+
throw new KeyPairError('Public key is not a valid pair with the secret key', 'PUBLIC_KEY_ERROR');
|
|
126
121
|
}
|
|
127
122
|
this._publicKey = publicKey;
|
|
128
|
-
this._publicKeyMultibase = publicKey.multibase.
|
|
123
|
+
this._publicKeyMultibase = publicKey.multibase.encoded;
|
|
129
124
|
this._secretKeyMultibase = this._secretKey ? this._secretKey.multibase : '';
|
|
130
125
|
}
|
|
131
126
|
|
|
132
127
|
/**
|
|
133
|
-
* Get the
|
|
134
|
-
* @returns {
|
|
128
|
+
* Get the CompressedSecp256k1PublicKey.
|
|
129
|
+
* @returns {CompressedSecp256k1PublicKey} The CompressedSecp256k1PublicKey object
|
|
135
130
|
*/
|
|
136
|
-
get publicKey():
|
|
131
|
+
get publicKey(): CompressedSecp256k1PublicKey {
|
|
137
132
|
const publicKey = this._publicKey;
|
|
138
133
|
return publicKey;
|
|
139
134
|
}
|
|
140
135
|
|
|
141
136
|
/**
|
|
142
|
-
* Get the
|
|
143
|
-
* @returns {
|
|
137
|
+
* Get the raw bytes of each key in the SchnorrKeyPair.
|
|
138
|
+
* @returns {RawSchnorrKeyPair} JSON object with the SchnorrKeyPair raw bytes.
|
|
144
139
|
*/
|
|
145
|
-
get raw():
|
|
140
|
+
get raw(): RawSchnorrKeyPair {
|
|
146
141
|
return {
|
|
147
142
|
public : this.publicKey.x,
|
|
148
143
|
secret : this.secretKey ? this.secretKey.bytes : undefined
|
|
@@ -151,7 +146,7 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
151
146
|
|
|
152
147
|
/**
|
|
153
148
|
* Get the Keys in multibase format.
|
|
154
|
-
* @returns {MultibaseKeys} The
|
|
149
|
+
* @returns {MultibaseKeys} The Secp256k1SecretKey in multibase format
|
|
155
150
|
*/
|
|
156
151
|
get multibase(): MultibaseKeys {
|
|
157
152
|
return {
|
|
@@ -177,50 +172,51 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
177
172
|
* @returns {SchnorrKeyPair} The initialized Keys object.
|
|
178
173
|
*/
|
|
179
174
|
public static fromJSON(keys: SchnorrKeyPairObject): SchnorrKeyPair {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
return new SchnorrKeyPair({
|
|
176
|
+
secretKey : Secp256k1SecretKey.fromJSON(keys.secretKey),
|
|
177
|
+
publicKey : CompressedSecp256k1PublicKey.fromJSON(keys.publicKey)
|
|
178
|
+
});
|
|
183
179
|
}
|
|
184
180
|
|
|
185
181
|
/**
|
|
186
|
-
* Static method creates a new SchnorrKeyPair from a
|
|
187
|
-
* @param {
|
|
182
|
+
* Static method creates a new SchnorrKeyPair from a Secp256k1SecretKey object or secret key bytes.
|
|
183
|
+
* @param {Secp256k1SecretKey | KeyBytes} data The secret key bytes
|
|
188
184
|
* @returns {SchnorrKeyPair} A new SchnorrKeyPair object
|
|
189
185
|
*/
|
|
190
|
-
public static fromPrivateKey(data:
|
|
186
|
+
public static fromPrivateKey(data: Secp256k1SecretKey | KeyBytes): SchnorrKeyPair {
|
|
191
187
|
|
|
192
|
-
// If the secret key is a
|
|
193
|
-
const bytes = data instanceof
|
|
188
|
+
// If the secret key is a Secp256k1SecretKey object, get the raw bytes else use the bytes
|
|
189
|
+
const bytes = data instanceof Secp256k1SecretKey ? data.bytes : data;
|
|
194
190
|
|
|
195
191
|
// Throw error if the secret key is not 32 bytes
|
|
196
192
|
if(bytes.length !== 32) {
|
|
197
193
|
throw new KeyPairError('Invalid arg: must be 32 byte secret key', 'FROM_PRIVATE_KEY_ERROR');
|
|
198
194
|
}
|
|
199
195
|
|
|
200
|
-
// If pk Uint8Array, construct
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
// Compute the public key from the secret key
|
|
204
|
-
const publicKey = secretKey.computePublicKey();
|
|
196
|
+
// If pk Uint8Array, construct Secp256k1SecretKey object else use the object
|
|
197
|
+
const secret = data instanceof Uint8Array ? new Secp256k1SecretKey(data) : data;
|
|
205
198
|
|
|
206
199
|
// Return a new Keys object
|
|
207
|
-
return new SchnorrKeyPair({
|
|
200
|
+
return new SchnorrKeyPair({
|
|
201
|
+
secretKey : data instanceof Uint8Array ? new Secp256k1SecretKey(data) : data,
|
|
202
|
+
publicKey : secret.computePublicKey()
|
|
203
|
+
});
|
|
208
204
|
}
|
|
209
205
|
|
|
210
206
|
/**
|
|
211
|
-
* Static method creates a new Keys (
|
|
212
|
-
* @param {bigint}
|
|
213
|
-
* @returns {
|
|
207
|
+
* Static method creates a new Keys (Secp256k1SecretKey/CompressedSecp256k1PublicKey) from bigint entropy.
|
|
208
|
+
* @param {bigint} entropy The entropy in bigint form
|
|
209
|
+
* @returns {SchnorrKeyPair} A new SchnorrKeyPair object
|
|
214
210
|
*/
|
|
215
|
-
public static
|
|
216
|
-
const secretKey =
|
|
211
|
+
public static fromEntropy(entropy: bigint): SchnorrKeyPair {
|
|
212
|
+
const secretKey = Secp256k1SecretKey.fromEntropy(entropy);
|
|
217
213
|
const publicKey = secretKey.computePublicKey();
|
|
218
214
|
return new SchnorrKeyPair({ secretKey, publicKey });
|
|
219
215
|
}
|
|
220
216
|
|
|
221
217
|
/**
|
|
222
218
|
* Converts key bytes to a hex string.
|
|
223
|
-
* @param {KeyBytes} keyBytes The key bytes (
|
|
219
|
+
* @param {KeyBytes} keyBytes The key bytes (secret or public).
|
|
224
220
|
* @returns {Hex} The key bytes as a hex string.
|
|
225
221
|
*/
|
|
226
222
|
public static toHex(keyBytes: KeyBytes): Hex {
|
|
@@ -229,23 +225,23 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
229
225
|
|
|
230
226
|
/**
|
|
231
227
|
* Compares two Keys objects for equality.
|
|
232
|
-
* @param {SchnorrKeyPair}
|
|
233
|
-
* @param {SchnorrKeyPair}
|
|
228
|
+
* @param {SchnorrKeyPair} kp The main keys.
|
|
229
|
+
* @param {SchnorrKeyPair} otherKp The other keys to compare.
|
|
234
230
|
* @returns {boolean} True if the public key and secret key are equal, false otherwise.
|
|
235
231
|
*/
|
|
236
|
-
public static equals(
|
|
232
|
+
public static equals(kp: SchnorrKeyPair, otherKp: SchnorrKeyPair): boolean {
|
|
237
233
|
// Deconstruct the public keys from the key pairs
|
|
238
|
-
const pk =
|
|
239
|
-
const otherPk =
|
|
234
|
+
const pk = kp.publicKey;
|
|
235
|
+
const otherPk = otherKp.publicKey;
|
|
240
236
|
|
|
241
237
|
// If publicKeys present, use to compare as hex strings.
|
|
242
238
|
if(pk && otherPk) {
|
|
243
239
|
return pk.hex === otherPk.hex;
|
|
244
240
|
}
|
|
245
241
|
|
|
246
|
-
// Deconstruct the
|
|
247
|
-
const sk =
|
|
248
|
-
const otherSk =
|
|
242
|
+
// Deconstruct the secret keys from the key pairs
|
|
243
|
+
const sk = kp.secretKey;
|
|
244
|
+
const otherSk = otherKp.secretKey;
|
|
249
245
|
if(sk && otherSk) {
|
|
250
246
|
// Get the public key hex strings for both key pair publicKeys
|
|
251
247
|
return sk.hex === otherSk.hex;
|
|
@@ -256,14 +252,14 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
256
252
|
|
|
257
253
|
/**
|
|
258
254
|
* Static method to generate a new random SchnorrKeyPair instance.
|
|
259
|
-
* @returns {SchnorrKeyPair} A new
|
|
255
|
+
* @returns {SchnorrKeyPair} A new Secp256k1SecretKey object.
|
|
260
256
|
*/
|
|
261
257
|
public static generate(): SchnorrKeyPair {
|
|
262
258
|
// Generate random secret key bytes
|
|
263
|
-
const
|
|
259
|
+
const sk = Secp256k1SecretKey.random();
|
|
264
260
|
|
|
265
|
-
// Construct a new
|
|
266
|
-
const secretKey = new
|
|
261
|
+
// Construct a new Secp256k1SecretKey object
|
|
262
|
+
const secretKey = new Secp256k1SecretKey(sk);
|
|
267
263
|
|
|
268
264
|
// Compute the public key from the secret key
|
|
269
265
|
const publicKey = secretKey.computePublicKey();
|
package/src/public.ts
CHANGED
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
} from '@did-btcr2/common';
|
|
12
12
|
import { sha256 } from '@noble/hashes/sha2';
|
|
13
13
|
import { base58btc } from 'multiformats/bases/base58';
|
|
14
|
-
import
|
|
14
|
+
import * as tinysecp from 'tiny-secp256k1';
|
|
15
|
+
import { Secp256k1SecretKey } from './secret.js';
|
|
15
16
|
|
|
16
17
|
export interface Point {
|
|
17
18
|
x: KeyBytes;
|
|
@@ -19,11 +20,17 @@ export interface Point {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* Interface
|
|
23
|
-
* @interface
|
|
24
|
-
* @type {
|
|
23
|
+
* General PublicKey Interface used by CompressedSecp256k1PublicKey.
|
|
24
|
+
* @interface PublicKey
|
|
25
|
+
* @type {PublicKey}
|
|
25
26
|
*/
|
|
26
|
-
export interface
|
|
27
|
+
export interface PublicKey {
|
|
28
|
+
/**
|
|
29
|
+
* Compressed public key getter.
|
|
30
|
+
* @readonly @type {KeyBytes} The 33 byte compressed public key [parity, x-coord].
|
|
31
|
+
*/
|
|
32
|
+
compressed: KeyBytes;
|
|
33
|
+
|
|
27
34
|
/**
|
|
28
35
|
* Uncompressed public key getter.
|
|
29
36
|
* @readonly @type {KeyBytes} The 65 byte uncompressed public key [0x04, x-coord, y-coord].
|
|
@@ -31,41 +38,53 @@ export interface IPublicKey {
|
|
|
31
38
|
uncompressed: KeyBytes;
|
|
32
39
|
|
|
33
40
|
/**
|
|
34
|
-
*
|
|
35
|
-
* @readonly @type {KeyBytes} The
|
|
41
|
+
* X-only public key getter.
|
|
42
|
+
* @readonly @type {KeyBytes} The 32 byte x-only public key [x-coord].
|
|
36
43
|
*/
|
|
37
|
-
|
|
44
|
+
xOnly: KeyBytes;
|
|
38
45
|
|
|
39
46
|
/**
|
|
40
|
-
*
|
|
47
|
+
* CompressedSecp256k1PublicKey parity getter.
|
|
41
48
|
* @readonly @type {number} The 1 byte parity (0x02 if even, 0x03 if odd).
|
|
42
49
|
*/
|
|
43
50
|
parity: number;
|
|
44
51
|
|
|
45
52
|
/**
|
|
46
|
-
*
|
|
53
|
+
* CompressedSecp256k1PublicKey isEven getter.
|
|
54
|
+
* @readonly @type {boolean} True if the public key is even, false if odd.
|
|
55
|
+
*/
|
|
56
|
+
isEven: boolean;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* CompressedSecp256k1PublicKey x-coordinate getter.
|
|
47
60
|
* @readonly @type {KeyBytes} The 32 byte x-coordinate of the public key.
|
|
48
61
|
*/
|
|
49
62
|
x: KeyBytes;
|
|
50
63
|
|
|
51
64
|
/**
|
|
52
|
-
*
|
|
65
|
+
* CompressedSecp256k1PublicKey y-coordinate getter.
|
|
53
66
|
* @readonly @type {KeyBytes} The 32 byte y-coordinate of the public key.
|
|
54
67
|
*/
|
|
55
68
|
y: KeyBytes;
|
|
56
69
|
|
|
57
70
|
/**
|
|
58
|
-
*
|
|
71
|
+
* CompressedSecp256k1PublicKey multibase getter.
|
|
59
72
|
* @readonly @returns {MultibaseObject} The public key as MultibaseObject as a address string, key and prefix bytes.
|
|
60
73
|
*/
|
|
61
74
|
multibase: MultibaseObject;
|
|
62
75
|
|
|
63
76
|
/**
|
|
64
|
-
*
|
|
77
|
+
* CompressedSecp256k1PublicKey hex string getter.
|
|
65
78
|
* @readonly @type {Hex} The public key as a hex string.
|
|
66
79
|
*/
|
|
67
80
|
hex: Hex;
|
|
68
81
|
|
|
82
|
+
/**
|
|
83
|
+
* CompressedSecp256k1PublicKey point getter.
|
|
84
|
+
* @readonly @type {Point} The public key as a point (x, y).
|
|
85
|
+
*/
|
|
86
|
+
point: Point;
|
|
87
|
+
|
|
69
88
|
/**
|
|
70
89
|
* Decode the base58btc multibase string to the compressed public key prefixed with 0x02.
|
|
71
90
|
* @returns {KeyBytes} The public key as a 33-byte compressed public key with header.
|
|
@@ -73,21 +92,21 @@ export interface IPublicKey {
|
|
|
73
92
|
decode(): KeyBytes;
|
|
74
93
|
|
|
75
94
|
/**
|
|
76
|
-
* Encode the
|
|
95
|
+
* Encode the CompressedSecp256k1PublicKey as an x-only base58btc multibase public key.
|
|
77
96
|
* @returns {string} The public key formatted a base58btc multibase string.
|
|
78
97
|
*/
|
|
79
98
|
encode(): string;
|
|
80
99
|
|
|
81
100
|
/**
|
|
82
|
-
*
|
|
83
|
-
* @param {
|
|
101
|
+
* CompressedSecp256k1PublicKey key equality check. Checks if `this` public key is equal to `other` public key.
|
|
102
|
+
* @param {CompressedSecp256k1PublicKey} other The public key to compare.
|
|
84
103
|
* @returns {boolean} True if the public keys are equal.
|
|
85
104
|
*/
|
|
86
|
-
equals(other:
|
|
105
|
+
equals(other: CompressedSecp256k1PublicKey): boolean;
|
|
87
106
|
|
|
88
107
|
/**
|
|
89
|
-
* JSON representation of a
|
|
90
|
-
* @returns {PublicKeyObject} The
|
|
108
|
+
* JSON representation of a CompressedSecp256k1PublicKey object.
|
|
109
|
+
* @returns {PublicKeyObject} The CompressedSecp256k1PublicKey as a JSON object.
|
|
91
110
|
*/
|
|
92
111
|
json(): PublicKeyObject;
|
|
93
112
|
}
|
|
@@ -96,10 +115,10 @@ export interface IPublicKey {
|
|
|
96
115
|
* Encapsulates a secp256k1 public key compliant to BIP-340 BIP schnorr signature scheme.
|
|
97
116
|
* Provides get methods for different formats (compressed, x-only, multibase).
|
|
98
117
|
* Provides helpers methods for comparison and serialization.
|
|
99
|
-
* @class
|
|
100
|
-
* @type {
|
|
118
|
+
* @class CompressedSecp256k1PublicKey
|
|
119
|
+
* @type {CompressedSecp256k1PublicKey}
|
|
101
120
|
*/
|
|
102
|
-
export class
|
|
121
|
+
export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
103
122
|
/** @type {KeyBytes} The public key bytes */
|
|
104
123
|
private readonly _bytes: KeyBytes;
|
|
105
124
|
|
|
@@ -107,11 +126,11 @@ export class PublicKey implements PublicKey {
|
|
|
107
126
|
private _multibase: MultibaseObject = {
|
|
108
127
|
prefix : BIP340_PUBLIC_KEY_MULTIBASE_PREFIX,
|
|
109
128
|
key : [],
|
|
110
|
-
|
|
129
|
+
encoded : ''
|
|
111
130
|
};
|
|
112
131
|
|
|
113
132
|
/**
|
|
114
|
-
* Creates a
|
|
133
|
+
* Creates a CompressedSecp256k1PublicKey instance.
|
|
115
134
|
* @param {KeyBytes} bytes The public key byte array.
|
|
116
135
|
* @throws {PublicKeyError} if the byte length is not 32 (x-only) or 33 (compressed)
|
|
117
136
|
*/
|
|
@@ -123,11 +142,19 @@ export class PublicKey implements PublicKey {
|
|
|
123
142
|
'CONSTRUCTOR_ERROR', { bytes }
|
|
124
143
|
);
|
|
125
144
|
}
|
|
145
|
+
|
|
146
|
+
// Validate the point is on curve and in compressed form
|
|
147
|
+
if (!tinysecp.isPoint(bytes)) {
|
|
148
|
+
throw new PublicKeyError(
|
|
149
|
+
'Invalid argument: bytes are not a valid secp256k1 compressed point',
|
|
150
|
+
'CONSTRUCTOR_ERROR', { bytes }
|
|
151
|
+
);
|
|
152
|
+
}
|
|
126
153
|
// Set the bytes
|
|
127
154
|
this._bytes = bytes;
|
|
128
155
|
|
|
129
156
|
// Set multibase
|
|
130
|
-
this._multibase.
|
|
157
|
+
this._multibase.encoded = this.encode();
|
|
131
158
|
this._multibase.key = [...this._multibase.prefix, ...this.compressed];
|
|
132
159
|
}
|
|
133
160
|
|
|
@@ -150,12 +177,34 @@ export class PublicKey implements PublicKey {
|
|
|
150
177
|
}
|
|
151
178
|
|
|
152
179
|
/**
|
|
153
|
-
*
|
|
154
|
-
|
|
180
|
+
* X-only (32-byte) view of the public key per BIP-340.
|
|
181
|
+
*/
|
|
182
|
+
get xOnly(): KeyBytes {
|
|
183
|
+
return this._bytes.slice(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Parity of the SEC compressed public key.
|
|
188
|
+
* @returns {0x02 | 0x03} The parity byte (0x02 if even, 0x03 if odd).
|
|
189
|
+
* @throws {PublicKeyError} If the parity byte is not 0x02 or 0x03.
|
|
190
|
+
*/
|
|
191
|
+
get parity(): 0x02 | 0x03 {
|
|
192
|
+
const parity = this._bytes[0];
|
|
193
|
+
if(![0x02, 0x03].includes(parity)) {
|
|
194
|
+
throw new PublicKeyError(
|
|
195
|
+
'Invalid state: parity byte must be 2 or 3',
|
|
196
|
+
'PARITY_ERROR', { parity }
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return parity as 0x02 | 0x03;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Whether the SEC compressed public key has even Y.
|
|
204
|
+
* @returns {boolean} True if the public key has even Y.
|
|
155
205
|
*/
|
|
156
|
-
get
|
|
157
|
-
|
|
158
|
-
return parity;
|
|
206
|
+
get isEven(): boolean {
|
|
207
|
+
return this._bytes[0] === 0x02;
|
|
159
208
|
}
|
|
160
209
|
|
|
161
210
|
/**
|
|
@@ -205,6 +254,14 @@ export class PublicKey implements PublicKey {
|
|
|
205
254
|
};
|
|
206
255
|
}
|
|
207
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Returns the BIP-340 (x-only) representation of this key.
|
|
259
|
+
* @returns {KeyBytes} The BIP-340 (x-only) representation of the public key.
|
|
260
|
+
*/
|
|
261
|
+
public bip340(): KeyBytes {
|
|
262
|
+
return this.xOnly;
|
|
263
|
+
}
|
|
264
|
+
|
|
208
265
|
/**
|
|
209
266
|
* Returns the point of the public key.
|
|
210
267
|
* @param {Hex} pk The public key in hex (Uint8Array or string) format.
|
|
@@ -212,15 +269,15 @@ export class PublicKey implements PublicKey {
|
|
|
212
269
|
* @throws {PublicKeyError} If the public key is not a valid hex string or byte array.
|
|
213
270
|
*/
|
|
214
271
|
static point(pk: Hex): Point {
|
|
215
|
-
// If the public key is a hex string, convert it to a
|
|
272
|
+
// If the public key is a hex string, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
216
273
|
if(typeof pk === 'string' && /^[0-9a-fA-F]+$/.test(pk)) {
|
|
217
|
-
const publicKey = new
|
|
274
|
+
const publicKey = new CompressedSecp256k1PublicKey(Buffer.fromHex(pk));
|
|
218
275
|
return publicKey.point;
|
|
219
276
|
}
|
|
220
277
|
|
|
221
|
-
// If the public key is a byte array or ArrayBuffer, convert it to a
|
|
278
|
+
// If the public key is a byte array or ArrayBuffer, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
222
279
|
if(pk instanceof Uint8Array || ArrayBuffer.isView(pk)) {
|
|
223
|
-
const publicKey = new
|
|
280
|
+
const publicKey = new CompressedSecp256k1PublicKey(pk as KeyBytes);
|
|
224
281
|
return publicKey.point;
|
|
225
282
|
}
|
|
226
283
|
|
|
@@ -237,7 +294,7 @@ export class PublicKey implements PublicKey {
|
|
|
237
294
|
*/
|
|
238
295
|
public decode(): KeyBytes {
|
|
239
296
|
// Decode the public key multibase string
|
|
240
|
-
const decoded = base58btc.decode(this.multibase.
|
|
297
|
+
const decoded = base58btc.decode(this.multibase.encoded);
|
|
241
298
|
|
|
242
299
|
// If the public key bytes are not 35 bytes, throw an error
|
|
243
300
|
if(decoded.length !== 35) {
|
|
@@ -293,16 +350,16 @@ export class PublicKey implements PublicKey {
|
|
|
293
350
|
|
|
294
351
|
/**
|
|
295
352
|
* Compares this public key to another public key.
|
|
296
|
-
* @param {
|
|
353
|
+
* @param {CompressedSecp256k1PublicKey} other The other public key to compare
|
|
297
354
|
* @returns {boolean} True if the public keys are equal, false otherwise.
|
|
298
355
|
*/
|
|
299
|
-
public equals(other:
|
|
356
|
+
public equals(other: CompressedSecp256k1PublicKey): boolean {
|
|
300
357
|
return this.hex === other.hex;
|
|
301
358
|
}
|
|
302
359
|
|
|
303
360
|
/**
|
|
304
|
-
* JSON representation of a
|
|
305
|
-
* @returns {PublicKeyObject} The
|
|
361
|
+
* JSON representation of a CompressedSecp256k1PublicKey object.
|
|
362
|
+
* @returns {PublicKeyObject} The CompressedSecp256k1PublicKey as a JSON object.
|
|
306
363
|
*/
|
|
307
364
|
public json(): PublicKeyObject {
|
|
308
365
|
return {
|
|
@@ -317,34 +374,36 @@ export class PublicKey implements PublicKey {
|
|
|
317
374
|
}
|
|
318
375
|
|
|
319
376
|
/**
|
|
320
|
-
* Creates a
|
|
321
|
-
* @param {PublicKeyObject} json The JSON object to initialize the
|
|
322
|
-
* @returns {
|
|
377
|
+
* Creates a CompressedSecp256k1PublicKey object from a JSON representation.
|
|
378
|
+
* @param {PublicKeyObject} json The JSON object to initialize the CompressedSecp256k1PublicKey.
|
|
379
|
+
* @returns {CompressedSecp256k1PublicKey} The initialized CompressedSecp256k1PublicKey object.
|
|
323
380
|
*/
|
|
324
|
-
public static fromJSON(json: Maybe<PublicKeyObject>):
|
|
381
|
+
public static fromJSON(json: Maybe<PublicKeyObject>): CompressedSecp256k1PublicKey {
|
|
325
382
|
json.x.unshift(json.parity);
|
|
326
|
-
return new
|
|
383
|
+
return new CompressedSecp256k1PublicKey(json.x.toUint8Array());
|
|
327
384
|
}
|
|
328
385
|
|
|
329
386
|
/**
|
|
330
|
-
* Computes the deterministic public key for a given
|
|
331
|
-
* @param {
|
|
332
|
-
* @returns {
|
|
387
|
+
* Computes the deterministic public key for a given secret key.
|
|
388
|
+
* @param {Secp256k1SecretKey | KeyBytes} sk The Secp256k1SecretKey object or the secret key bytes
|
|
389
|
+
* @returns {CompressedSecp256k1PublicKey} A new CompressedSecp256k1PublicKey object
|
|
333
390
|
*/
|
|
334
|
-
public static fromSecretKey(sk:
|
|
335
|
-
// If the
|
|
336
|
-
const bytes = sk instanceof
|
|
391
|
+
public static fromSecretKey(sk: Secp256k1SecretKey | KeyBytes): CompressedSecp256k1PublicKey {
|
|
392
|
+
// If the secret key is a Secp256k1SecretKey object, get the raw bytes else use the bytes
|
|
393
|
+
const bytes = sk instanceof Secp256k1SecretKey ? sk.bytes : sk;
|
|
337
394
|
|
|
338
|
-
// Throw error if the
|
|
395
|
+
// Throw error if the secret key is not 32 bytes
|
|
339
396
|
if(bytes.length !== 32) {
|
|
340
|
-
throw new PublicKeyError('Invalid arg: must be 32 byte
|
|
397
|
+
throw new PublicKeyError('Invalid arg: must be 32 byte secret key', 'FROM_SECRET_KEY_ERROR');
|
|
341
398
|
}
|
|
342
399
|
|
|
343
|
-
// Compute the public key from the
|
|
344
|
-
const
|
|
400
|
+
// Compute the public key from the secret key
|
|
401
|
+
const secret = sk instanceof Secp256k1SecretKey
|
|
402
|
+
? sk
|
|
403
|
+
: new Secp256k1SecretKey(sk);
|
|
345
404
|
|
|
346
|
-
// Return a new
|
|
347
|
-
return
|
|
405
|
+
// Return a new CompressedSecp256k1PublicKey object
|
|
406
|
+
return secret.computePublicKey();
|
|
348
407
|
}
|
|
349
408
|
|
|
350
409
|
/**
|
|
@@ -405,20 +464,4 @@ export class PublicKey implements PublicKey {
|
|
|
405
464
|
// Return 65-byte uncompressed public key: `0x04 || x || y`
|
|
406
465
|
return new Uint8Array(Buffer.concat([Buffer.from([0x04]), Buffer.from(this.x), yBytes]));
|
|
407
466
|
};
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Static version of liftX method.
|
|
411
|
-
* @param {KeyBytes} x The 32-byte x-coordinate to lift.
|
|
412
|
-
* @returns {Uint8Array} The 65-byte uncompressed public key (0x04, x, y).
|
|
413
|
-
*/
|
|
414
|
-
public static xOnly(x: KeyBytes): Uint8Array {
|
|
415
|
-
// Ensure x-coordinate is 32 bytes
|
|
416
|
-
if (x.length !== 32) {
|
|
417
|
-
throw new PublicKeyError('Invalid argument: x-coordinate length must be 32 bytes', 'LIFT_X_ERROR');
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Create a PublicKey instance and lift the x-coordinate
|
|
421
|
-
const publicKey = new PublicKey(x);
|
|
422
|
-
return publicKey.x;
|
|
423
|
-
}
|
|
424
467
|
}
|