@did-btcr2/keypair 0.7.1 → 0.8.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/dist/cjs/pair.js +34 -32
- package/dist/cjs/pair.js.map +1 -1
- package/dist/cjs/public.js +109 -71
- package/dist/cjs/public.js.map +1 -1
- package/dist/cjs/secret.js +39 -18
- package/dist/cjs/secret.js.map +1 -1
- package/dist/esm/pair.js +34 -32
- package/dist/esm/pair.js.map +1 -1
- package/dist/esm/public.js +109 -71
- package/dist/esm/public.js.map +1 -1
- package/dist/esm/secret.js +39 -18
- package/dist/esm/secret.js.map +1 -1
- package/dist/types/pair.d.ts +9 -20
- package/dist/types/pair.d.ts.map +1 -1
- package/dist/types/public.d.ts +46 -28
- package/dist/types/public.d.ts.map +1 -1
- package/dist/types/secret.d.ts +12 -7
- package/dist/types/secret.d.ts.map +1 -1
- package/dist/types/types.d.ts +3 -0
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/pair.ts +43 -46
- package/src/public.ts +139 -92
- package/src/secret.ts +46 -21
- package/src/types.ts +2 -0
package/src/pair.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Hex,
|
|
3
|
+
HexString,
|
|
3
4
|
KeyBytes,
|
|
4
5
|
KeyPairError,
|
|
5
6
|
SchnorrKeyPairObject
|
|
6
7
|
} from '@did-btcr2/common';
|
|
7
|
-
import { CompressedSecp256k1PublicKey } from './public.js';
|
|
8
|
-
import { Secp256k1SecretKey } from './secret.js';
|
|
8
|
+
import { CompressedSecp256k1PublicKey, PublicKey } from './public.js';
|
|
9
|
+
import { Secp256k1SecretKey, SecretKey } from './secret.js';
|
|
9
10
|
import { HexSchnorrKeyPair, MultibaseKeys, RawSchnorrKeyPair, SchnorrKeyPairParams } from './types.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -15,21 +16,15 @@ import { HexSchnorrKeyPair, MultibaseKeys, RawSchnorrKeyPair, SchnorrKeyPairPara
|
|
|
15
16
|
*/
|
|
16
17
|
export interface KeyPair {
|
|
17
18
|
/**
|
|
18
|
-
* @type {
|
|
19
|
+
* @type {PublicKey} The public key associated with the SchnorrKeyPair (required).
|
|
19
20
|
*/
|
|
20
|
-
readonly publicKey:
|
|
21
|
+
readonly publicKey: PublicKey;
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
|
-
* @type {
|
|
24
|
+
* @type {SecretKey} The secret key associated with the SchnorrKeyPair (optional).
|
|
24
25
|
* @throws {KeyPairError} If the secret key is not available.
|
|
25
26
|
*/
|
|
26
|
-
readonly secretKey?:
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* JSON representation of the SchnorrKeyPair object.
|
|
30
|
-
* @returns {SchnorrKeyPairObject} The SchnorrKeyPair as a JSON object.
|
|
31
|
-
*/
|
|
32
|
-
json(): SchnorrKeyPairObject;
|
|
27
|
+
readonly secretKey?: SecretKey;
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
/**
|
|
@@ -39,16 +34,16 @@ export interface KeyPair {
|
|
|
39
34
|
*/
|
|
40
35
|
export class SchnorrKeyPair implements KeyPair {
|
|
41
36
|
/** @type {Secp256k1SecretKey} The secret key object */
|
|
42
|
-
|
|
37
|
+
#secretKey?: Secp256k1SecretKey;
|
|
43
38
|
|
|
44
39
|
/** @type {CompressedSecp256k1PublicKey} The public key object */;
|
|
45
|
-
|
|
40
|
+
#publicKey: CompressedSecp256k1PublicKey;
|
|
46
41
|
|
|
47
42
|
/** @type {string} The public key in multibase format */
|
|
48
|
-
|
|
43
|
+
#publicKeyMultibase: string;
|
|
49
44
|
|
|
50
45
|
/** @type {string} The secret key in multibase format */
|
|
51
|
-
|
|
46
|
+
#secretKeyMultibase: string;
|
|
52
47
|
|
|
53
48
|
/**
|
|
54
49
|
* Creates an instance of Keys. Must provide a at least a secret key.
|
|
@@ -67,22 +62,22 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
67
62
|
|
|
68
63
|
// Set the secretKey
|
|
69
64
|
if(params.secretKey instanceof Uint8Array) {
|
|
70
|
-
this
|
|
65
|
+
this.#secretKey = new Secp256k1SecretKey(params.secretKey);
|
|
71
66
|
} else if (params.secretKey instanceof Secp256k1SecretKey) {
|
|
72
|
-
this
|
|
67
|
+
this.#secretKey = params.secretKey;
|
|
73
68
|
}
|
|
74
69
|
|
|
75
70
|
// Set the publicKey
|
|
76
71
|
if(params.publicKey instanceof CompressedSecp256k1PublicKey) {
|
|
77
|
-
this
|
|
72
|
+
this.#publicKey = params.publicKey;
|
|
78
73
|
} else if (params.publicKey instanceof Uint8Array) {
|
|
79
|
-
this
|
|
74
|
+
this.#publicKey = new CompressedSecp256k1PublicKey(params.publicKey);
|
|
80
75
|
} else {
|
|
81
|
-
this
|
|
76
|
+
this.#publicKey = this.#secretKey!.computePublicKey();
|
|
82
77
|
}
|
|
83
78
|
|
|
84
|
-
this
|
|
85
|
-
this
|
|
79
|
+
this.#publicKeyMultibase = this.#publicKey.multibase.encoded;
|
|
80
|
+
this.#secretKeyMultibase = this.#secretKey ? this.#secretKey.multibase : '';
|
|
86
81
|
}
|
|
87
82
|
|
|
88
83
|
/**
|
|
@@ -92,15 +87,15 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
92
87
|
*/
|
|
93
88
|
get secretKey(): Secp256k1SecretKey {
|
|
94
89
|
// If the secret key is not available, throw an error
|
|
95
|
-
if(!this
|
|
90
|
+
if(!this.#secretKey) {
|
|
96
91
|
throw new KeyPairError('Secret key not available', 'SECRET_KEY_ERROR');
|
|
97
92
|
}
|
|
98
93
|
// If the secret key is not valid, throw an error
|
|
99
|
-
if(!this.
|
|
94
|
+
if(!this.#secretKey.isValid()) {
|
|
100
95
|
throw new KeyPairError('Secret key is not valid', 'SECRET_KEY_ERROR');
|
|
101
96
|
}
|
|
102
97
|
// Return a copy of the secret key
|
|
103
|
-
const secret = this
|
|
98
|
+
const secret = this.#secretKey;
|
|
104
99
|
return secret;
|
|
105
100
|
}
|
|
106
101
|
|
|
@@ -119,9 +114,9 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
119
114
|
if(!publicKey.equals(cPk))
|
|
120
115
|
throw new KeyPairError('Public key is not a valid pair with the secret key', 'PUBLIC_KEY_ERROR');
|
|
121
116
|
}
|
|
122
|
-
this
|
|
123
|
-
this
|
|
124
|
-
this
|
|
117
|
+
this.#publicKey = publicKey;
|
|
118
|
+
this.#publicKeyMultibase = publicKey.multibase.encoded;
|
|
119
|
+
this.#secretKeyMultibase = this.#secretKey ? this.#secretKey.multibase : '';
|
|
125
120
|
}
|
|
126
121
|
|
|
127
122
|
/**
|
|
@@ -129,7 +124,7 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
129
124
|
* @returns {CompressedSecp256k1PublicKey} The CompressedSecp256k1PublicKey object
|
|
130
125
|
*/
|
|
131
126
|
get publicKey(): CompressedSecp256k1PublicKey {
|
|
132
|
-
const publicKey = this
|
|
127
|
+
const publicKey = this.#publicKey;
|
|
133
128
|
return publicKey;
|
|
134
129
|
}
|
|
135
130
|
|
|
@@ -151,7 +146,7 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
151
146
|
get hex(): HexSchnorrKeyPair {
|
|
152
147
|
return {
|
|
153
148
|
public : this.publicKey.hex,
|
|
154
|
-
secret : this
|
|
149
|
+
secret : this.#secretKey ? this.secretKey.hex : undefined
|
|
155
150
|
};
|
|
156
151
|
}
|
|
157
152
|
|
|
@@ -161,8 +156,8 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
161
156
|
*/
|
|
162
157
|
get multibase(): MultibaseKeys {
|
|
163
158
|
return {
|
|
164
|
-
publicKeyMultibase
|
|
165
|
-
secretKeyMultibase : this
|
|
159
|
+
publicKeyMultibase : this.#publicKeyMultibase,
|
|
160
|
+
secretKeyMultibase : this.#secretKeyMultibase,
|
|
166
161
|
};
|
|
167
162
|
}
|
|
168
163
|
|
|
@@ -194,24 +189,26 @@ export class SchnorrKeyPair implements KeyPair {
|
|
|
194
189
|
* @param {Secp256k1SecretKey | KeyBytes} data The secret key bytes
|
|
195
190
|
* @returns {SchnorrKeyPair} A new SchnorrKeyPair object
|
|
196
191
|
*/
|
|
197
|
-
public static
|
|
198
|
-
|
|
199
|
-
// If the
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
192
|
+
public static fromSecret(data: KeyBytes | HexString): SchnorrKeyPair {
|
|
193
|
+
|
|
194
|
+
// If the data is Secp256k1SecretKey object, get the raw bytes
|
|
195
|
+
// Else if data is string, convert to byte array
|
|
196
|
+
// Else must be bytes, use them
|
|
197
|
+
const secret = typeof data === 'string'
|
|
198
|
+
? Buffer.from(data, 'hex')
|
|
199
|
+
: data;
|
|
200
|
+
|
|
201
|
+
// Check the lenth
|
|
202
|
+
if(secret.length !== 32) {
|
|
203
|
+
throw new KeyPairError('Invalid arg: must be 32 byte secret key', 'FROM_SECRET_KEY_ERROR');
|
|
205
204
|
}
|
|
206
205
|
|
|
207
206
|
// If pk Uint8Array, construct Secp256k1SecretKey object else use the object
|
|
208
|
-
const
|
|
207
|
+
const secretKey = new Secp256k1SecretKey(secret);
|
|
208
|
+
const publicKey = secretKey.computePublicKey();
|
|
209
209
|
|
|
210
210
|
// Return a new Keys object
|
|
211
|
-
return new SchnorrKeyPair({
|
|
212
|
-
secretKey : data instanceof Uint8Array ? new Secp256k1SecretKey(data) : data,
|
|
213
|
-
publicKey : secret.computePublicKey()
|
|
214
|
-
});
|
|
211
|
+
return new SchnorrKeyPair({ secretKey, publicKey });
|
|
215
212
|
}
|
|
216
213
|
|
|
217
214
|
/**
|
package/src/public.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BIP340_PUBLIC_KEY_MULTIBASE_PREFIX,
|
|
3
3
|
BIP340_PUBLIC_KEY_MULTIBASE_PREFIX_HASH,
|
|
4
|
+
Bytes,
|
|
4
5
|
CURVE,
|
|
5
6
|
Hex,
|
|
6
7
|
KeyBytes,
|
|
@@ -12,7 +13,13 @@ import { sha256 } from '@noble/hashes/sha2';
|
|
|
12
13
|
import { base58btc } from 'multiformats/bases/base58';
|
|
13
14
|
import * as tinysecp from 'tiny-secp256k1';
|
|
14
15
|
import { Secp256k1SecretKey } from './secret.js';
|
|
16
|
+
import { CryptoOptions } from './types.js';
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Point Interface representing an (x, y) coordinate on the secp256k1 curve.
|
|
20
|
+
* @interface Point
|
|
21
|
+
* @type {Point}
|
|
22
|
+
*/
|
|
16
23
|
export interface Point {
|
|
17
24
|
x: KeyBytes;
|
|
18
25
|
y: KeyBytes;
|
|
@@ -119,10 +126,10 @@ export interface PublicKey {
|
|
|
119
126
|
*/
|
|
120
127
|
export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
121
128
|
/** @type {KeyBytes} The public key bytes */
|
|
122
|
-
|
|
129
|
+
readonly #bytes: KeyBytes;
|
|
123
130
|
|
|
124
131
|
/** @type {MultibaseObject} The public key as a MultibaseObject */
|
|
125
|
-
|
|
132
|
+
readonly #multibase: MultibaseObject = {
|
|
126
133
|
prefix : BIP340_PUBLIC_KEY_MULTIBASE_PREFIX,
|
|
127
134
|
key : [],
|
|
128
135
|
encoded : ''
|
|
@@ -130,31 +137,36 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
130
137
|
|
|
131
138
|
/**
|
|
132
139
|
* Creates a CompressedSecp256k1PublicKey instance.
|
|
133
|
-
* @param {
|
|
140
|
+
* @param {Hex} initialBytes The public key byte array.
|
|
134
141
|
* @throws {PublicKeyError} if the byte length is not 32 (x-only) or 33 (compressed)
|
|
135
142
|
*/
|
|
136
|
-
constructor(
|
|
143
|
+
constructor(initialBytes: Hex) {
|
|
144
|
+
// Convert hex string to Uint8Array if necessary
|
|
145
|
+
const keyBytes = initialBytes instanceof Uint8Array
|
|
146
|
+
? initialBytes
|
|
147
|
+
: Uint8Array.from(Buffer.from(initialBytes, 'hex'));
|
|
148
|
+
|
|
137
149
|
// If the byte length is not 33, throw an error
|
|
138
|
-
if(
|
|
150
|
+
if(!keyBytes || keyBytes.length !== 33) {
|
|
139
151
|
throw new PublicKeyError(
|
|
140
152
|
'Invalid argument: byte length must be 33 (compressed)',
|
|
141
|
-
'CONSTRUCTOR_ERROR', {
|
|
153
|
+
'CONSTRUCTOR_ERROR', { keyBytes }
|
|
142
154
|
);
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
// Validate the point is on curve and in compressed form
|
|
146
|
-
if (!tinysecp.isPoint(
|
|
158
|
+
if (!tinysecp.isPoint(keyBytes)) {
|
|
147
159
|
throw new PublicKeyError(
|
|
148
|
-
'Invalid argument:
|
|
149
|
-
'CONSTRUCTOR_ERROR', {
|
|
160
|
+
'Invalid argument: not a valid secp256k1 compressed point',
|
|
161
|
+
'CONSTRUCTOR_ERROR', { keyBytes }
|
|
150
162
|
);
|
|
151
163
|
}
|
|
152
164
|
// Set the bytes
|
|
153
|
-
this
|
|
165
|
+
this.#bytes = keyBytes;
|
|
154
166
|
|
|
155
167
|
// Set multibase
|
|
156
|
-
this.
|
|
157
|
-
this.
|
|
168
|
+
this.#multibase.encoded = this.encode();
|
|
169
|
+
this.#multibase.key = [...this.#multibase.prefix, ...this.compressed];
|
|
158
170
|
}
|
|
159
171
|
|
|
160
172
|
/**
|
|
@@ -162,7 +174,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
162
174
|
* @returns {KeyBytes} The 33-byte compressed public key (0x02 or 0x03, x).
|
|
163
175
|
*/
|
|
164
176
|
get compressed(): KeyBytes {
|
|
165
|
-
const bytes = new Uint8Array(this
|
|
177
|
+
const bytes = new Uint8Array(this.#bytes);
|
|
166
178
|
return bytes;
|
|
167
179
|
};
|
|
168
180
|
|
|
@@ -179,7 +191,8 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
179
191
|
* X-only (32-byte) view of the public key per BIP-340.
|
|
180
192
|
*/
|
|
181
193
|
get xOnly(): KeyBytes {
|
|
182
|
-
|
|
194
|
+
const xOnly = this.compressed.slice(1);
|
|
195
|
+
return xOnly;
|
|
183
196
|
}
|
|
184
197
|
|
|
185
198
|
/**
|
|
@@ -188,7 +201,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
188
201
|
* @throws {PublicKeyError} If the parity byte is not 0x02 or 0x03.
|
|
189
202
|
*/
|
|
190
203
|
get parity(): 0x02 | 0x03 {
|
|
191
|
-
const parity = this.
|
|
204
|
+
const parity = this.compressed[0];
|
|
192
205
|
if(![0x02, 0x03].includes(parity)) {
|
|
193
206
|
throw new PublicKeyError(
|
|
194
207
|
'Invalid state: parity byte must be 2 or 3',
|
|
@@ -203,7 +216,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
203
216
|
* @returns {boolean} True if the public key has even Y.
|
|
204
217
|
*/
|
|
205
218
|
get isEven(): boolean {
|
|
206
|
-
return this.
|
|
219
|
+
return this.parity === 0x02;
|
|
207
220
|
}
|
|
208
221
|
|
|
209
222
|
/**
|
|
@@ -229,15 +242,15 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
229
242
|
* @returns {MultibaseObject} An object containing the multibase bytes, address and prefix.
|
|
230
243
|
*/
|
|
231
244
|
get multibase(): MultibaseObject {
|
|
232
|
-
const multibase = this
|
|
245
|
+
const multibase = this.#multibase;
|
|
233
246
|
return multibase;
|
|
234
247
|
}
|
|
235
248
|
|
|
236
249
|
/**
|
|
237
250
|
* Returns the raw public key as a hex string.
|
|
238
|
-
* @returns {
|
|
251
|
+
* @returns {string} The public key as a hex string.
|
|
239
252
|
*/
|
|
240
|
-
get hex():
|
|
253
|
+
get hex(): string {
|
|
241
254
|
const hex = Buffer.from(this.compressed).toString('hex');
|
|
242
255
|
return hex;
|
|
243
256
|
}
|
|
@@ -257,41 +270,15 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
257
270
|
* Returns the BIP-340 (x-only) representation of this key.
|
|
258
271
|
* @returns {KeyBytes} The BIP-340 (x-only) representation of the public key.
|
|
259
272
|
*/
|
|
260
|
-
|
|
273
|
+
bip340(): KeyBytes {
|
|
261
274
|
return this.xOnly;
|
|
262
275
|
}
|
|
263
276
|
|
|
264
|
-
/**
|
|
265
|
-
* Returns the point of the public key.
|
|
266
|
-
* @param {Hex} pk The public key in hex (Uint8Array or string) format.
|
|
267
|
-
* @returns {Point} The point of the public key.
|
|
268
|
-
* @throws {PublicKeyError} If the public key is not a valid hex string or byte array.
|
|
269
|
-
*/
|
|
270
|
-
static point(pk: Hex): Point {
|
|
271
|
-
// If the public key is a hex string, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
272
|
-
if(typeof pk === 'string' && /^[0-9a-fA-F]+$/.test(pk)) {
|
|
273
|
-
const publicKey = new CompressedSecp256k1PublicKey(Buffer.fromHex(pk));
|
|
274
|
-
return publicKey.point;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// If the public key is a byte array or ArrayBuffer, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
278
|
-
if(pk instanceof Uint8Array || ArrayBuffer.isView(pk)) {
|
|
279
|
-
const publicKey = new CompressedSecp256k1PublicKey(pk as KeyBytes);
|
|
280
|
-
return publicKey.point;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// If the public key is neither a hex string nor a byte array, throw an error
|
|
284
|
-
throw new PublicKeyError(
|
|
285
|
-
'Invalid publicKey: must be a hex string or byte array',
|
|
286
|
-
'POINT_ERROR', { publicKey: pk }
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
277
|
/**
|
|
291
278
|
* Decodes the multibase string to the 35-byte corresponding public key (2 byte prefix + 32 byte public key).
|
|
292
279
|
* @returns {KeyBytes} The decoded public key: prefix and public key bytes
|
|
293
280
|
*/
|
|
294
|
-
|
|
281
|
+
decode(): KeyBytes {
|
|
295
282
|
// Decode the public key multibase string
|
|
296
283
|
const decoded = base58btc.decode(this.multibase.encoded);
|
|
297
284
|
|
|
@@ -325,9 +312,9 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
325
312
|
* Encodes compressed secp256k1 public key from bytes to BIP340 multibase format.
|
|
326
313
|
* @returns {string} The public key encoded in base-58-btc multibase format.
|
|
327
314
|
*/
|
|
328
|
-
|
|
315
|
+
encode(): string {
|
|
329
316
|
// Convert public key bytes to an array
|
|
330
|
-
const pk = this.compressed
|
|
317
|
+
const pk = Array.from(this.compressed);
|
|
331
318
|
|
|
332
319
|
// Ensure the public key is 33-byte secp256k1 compressed public key
|
|
333
320
|
if (pk.length !== 33) {
|
|
@@ -338,13 +325,33 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
338
325
|
}
|
|
339
326
|
|
|
340
327
|
// Convert prefix to an array
|
|
341
|
-
const publicKeyMultibase =
|
|
328
|
+
const publicKeyMultibase = Array.from(BIP340_PUBLIC_KEY_MULTIBASE_PREFIX);
|
|
342
329
|
|
|
343
330
|
// Push the public key bytes at the end of the prefix
|
|
344
331
|
publicKeyMultibase.push(...pk);
|
|
345
332
|
|
|
346
333
|
// Encode the bytes in base58btc format and return
|
|
347
|
-
return base58btc.encode(
|
|
334
|
+
return base58btc.encode(Uint8Array.from(publicKeyMultibase));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Verify a signature using schnorr or ecdsa.
|
|
339
|
+
* @param {SignatureBytes} signature Signature for verification.
|
|
340
|
+
* @param {string} data Data for verification.
|
|
341
|
+
* @param {CryptoOptions} opts Options for signing.
|
|
342
|
+
* @param {('ecdsa' | 'schnorr')} opts.scheme The signature scheme to use. Default is 'schnorr'.
|
|
343
|
+
* @returns {boolean} If the signature is valid against the public key.
|
|
344
|
+
*/
|
|
345
|
+
verify(signature: Bytes, data: Bytes, opts?: CryptoOptions): boolean {
|
|
346
|
+
opts ??= { scheme: 'schnorr' };
|
|
347
|
+
// Verify the signature depending on the scheme and return the result
|
|
348
|
+
if(opts.scheme === 'ecdsa') {
|
|
349
|
+
return tinysecp.verify(data, this.compressed, signature); }
|
|
350
|
+
else if(opts.scheme === 'schnorr') {
|
|
351
|
+
return tinysecp.verifySchnorr(data, this.x, signature);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
throw new PublicKeyError(`Invalid scheme: ${opts.scheme}.`, 'VERIFY_SIGNATURE_ERROR', opts);
|
|
348
355
|
}
|
|
349
356
|
|
|
350
357
|
/**
|
|
@@ -352,7 +359,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
352
359
|
* @param {CompressedSecp256k1PublicKey} other The other public key to compare
|
|
353
360
|
* @returns {boolean} True if the public keys are equal, false otherwise.
|
|
354
361
|
*/
|
|
355
|
-
|
|
362
|
+
equals(other: CompressedSecp256k1PublicKey): boolean {
|
|
356
363
|
return this.hex === other.hex;
|
|
357
364
|
}
|
|
358
365
|
|
|
@@ -360,51 +367,18 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
360
367
|
* JSON representation of a CompressedSecp256k1PublicKey object.
|
|
361
368
|
* @returns {PublicKeyObject} The CompressedSecp256k1PublicKey as a JSON object.
|
|
362
369
|
*/
|
|
363
|
-
|
|
370
|
+
json(): PublicKeyObject {
|
|
364
371
|
return {
|
|
365
372
|
hex : this.hex,
|
|
366
373
|
multibase : this.multibase,
|
|
367
374
|
point : {
|
|
368
|
-
x : this.x
|
|
369
|
-
y : this.y
|
|
375
|
+
x : Array.from(this.x),
|
|
376
|
+
y : Array.from(this.y),
|
|
370
377
|
parity : this.parity,
|
|
371
378
|
},
|
|
372
379
|
};
|
|
373
380
|
}
|
|
374
381
|
|
|
375
|
-
/**
|
|
376
|
-
* Creates a CompressedSecp256k1PublicKey object from a JSON representation.
|
|
377
|
-
* @param {PublicKeyObject} json The JSON object to initialize the CompressedSecp256k1PublicKey.
|
|
378
|
-
* @returns {CompressedSecp256k1PublicKey} The initialized CompressedSecp256k1PublicKey object.
|
|
379
|
-
*/
|
|
380
|
-
public static fromJSON(json: PublicKeyObject): CompressedSecp256k1PublicKey {
|
|
381
|
-
json.point.x.unshift(json.point.parity);
|
|
382
|
-
return new CompressedSecp256k1PublicKey(json.point.x.toUint8Array());
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Computes the deterministic public key for a given secret key.
|
|
387
|
-
* @param {Secp256k1SecretKey | KeyBytes} sk The Secp256k1SecretKey object or the secret key bytes
|
|
388
|
-
* @returns {CompressedSecp256k1PublicKey} A new CompressedSecp256k1PublicKey object
|
|
389
|
-
*/
|
|
390
|
-
public static fromSecretKey(sk: Secp256k1SecretKey | KeyBytes): CompressedSecp256k1PublicKey {
|
|
391
|
-
// If the secret key is a Secp256k1SecretKey object, get the raw bytes else use the bytes
|
|
392
|
-
const bytes = sk instanceof Secp256k1SecretKey ? sk.bytes : sk;
|
|
393
|
-
|
|
394
|
-
// Throw error if the secret key is not 32 bytes
|
|
395
|
-
if(bytes.length !== 32) {
|
|
396
|
-
throw new PublicKeyError('Invalid arg: must be 32 byte secret key', 'FROM_SECRET_KEY_ERROR');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Compute the public key from the secret key
|
|
400
|
-
const secret = sk instanceof Secp256k1SecretKey
|
|
401
|
-
? sk
|
|
402
|
-
: new Secp256k1SecretKey(sk);
|
|
403
|
-
|
|
404
|
-
// Return a new CompressedSecp256k1PublicKey object
|
|
405
|
-
return secret.computePublicKey();
|
|
406
|
-
}
|
|
407
|
-
|
|
408
382
|
/**
|
|
409
383
|
* Computes modular exponentiation: (base^exp) % mod.
|
|
410
384
|
* Used for computing modular square roots.
|
|
@@ -413,7 +387,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
413
387
|
* @param {bigint} mod The modulus value
|
|
414
388
|
* @returns {bigint} The result of the modular exponentiation
|
|
415
389
|
*/
|
|
416
|
-
|
|
390
|
+
modPow(base: bigint, exp: bigint, mod: bigint): bigint {
|
|
417
391
|
let result = 1n;
|
|
418
392
|
while (exp > 0n) {
|
|
419
393
|
if (exp & 1n) result = (result * base) % mod;
|
|
@@ -430,7 +404,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
430
404
|
* @param {bigint} p The prime modulus
|
|
431
405
|
* @returns {bigint} The square root of `a` mod `p`
|
|
432
406
|
*/
|
|
433
|
-
|
|
407
|
+
sqrtMod(a: bigint, p: bigint): bigint {
|
|
434
408
|
return this.modPow(a, (p + 1n) >> 2n, p);
|
|
435
409
|
};
|
|
436
410
|
|
|
@@ -439,7 +413,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
439
413
|
* @param xBytes 32-byte x-coordinate
|
|
440
414
|
* @returns {Uint8Array} 65-byte uncompressed public key (starts with `0x04`)
|
|
441
415
|
*/
|
|
442
|
-
|
|
416
|
+
liftX(): Uint8Array {
|
|
443
417
|
// Ensure x-coordinate is 32 bytes
|
|
444
418
|
if (this.x.length !== 32) {
|
|
445
419
|
throw new PublicKeyError('Invalid argument: x-coordinate length must be 32 bytes', 'LIFT_X_ERROR');
|
|
@@ -458,9 +432,82 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
|
|
|
458
432
|
const y = this.sqrtMod(ySquared, CURVE.p);
|
|
459
433
|
|
|
460
434
|
// Convert x and y to Uint8Array
|
|
461
|
-
const yBytes = Buffer.
|
|
435
|
+
const yBytes = Buffer.from(y.toString(16).padStart(64, '0'), 'hex');
|
|
462
436
|
|
|
463
437
|
// Return 65-byte uncompressed public key: `0x04 || x || y`
|
|
464
438
|
return new Uint8Array(Buffer.concat([Buffer.from([0x04]), Buffer.from(this.x), yBytes]));
|
|
465
439
|
};
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Static method to validate a public key.
|
|
443
|
+
* @param {Hex} pk The public key in hex (Uint8Array or string) format.
|
|
444
|
+
* @returns {boolean} True if the public key is valid, false otherwise.
|
|
445
|
+
*/
|
|
446
|
+
static isValid(pk: Hex): boolean {
|
|
447
|
+
try {
|
|
448
|
+
new CompressedSecp256k1PublicKey(pk);
|
|
449
|
+
return true;
|
|
450
|
+
} catch {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Returns the point of the public key.
|
|
457
|
+
* @param {Hex} pk The public key in hex (Uint8Array or string) format.
|
|
458
|
+
* @returns {Point} The point of the public key.
|
|
459
|
+
* @throws {PublicKeyError} If the public key is not a valid hex string or byte array.
|
|
460
|
+
*/
|
|
461
|
+
static point(pk: Hex): Point {
|
|
462
|
+
// If the public key is a hex string, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
463
|
+
if(typeof pk === 'string' && /^[0-9a-fA-F]+$/.test(pk)) {
|
|
464
|
+
const publicKey = new CompressedSecp256k1PublicKey(Buffer.from(pk, 'hex'));
|
|
465
|
+
return publicKey.point;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// If the public key is a byte array or ArrayBuffer, convert it to a CompressedSecp256k1PublicKey object and return the point
|
|
469
|
+
if(pk instanceof Uint8Array || ArrayBuffer.isView(pk)) {
|
|
470
|
+
const publicKey = new CompressedSecp256k1PublicKey(pk as KeyBytes);
|
|
471
|
+
return publicKey.point;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// If the public key is neither a hex string nor a byte array, throw an error
|
|
475
|
+
throw new PublicKeyError(
|
|
476
|
+
'Invalid publicKey: must be a hex string or byte array',
|
|
477
|
+
'POINT_ERROR', { publicKey: pk }
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Creates a CompressedSecp256k1PublicKey object from a JSON representation.
|
|
483
|
+
* @param {PublicKeyObject} json The JSON object to initialize the CompressedSecp256k1PublicKey.
|
|
484
|
+
* @returns {CompressedSecp256k1PublicKey} The initialized CompressedSecp256k1PublicKey object.
|
|
485
|
+
*/
|
|
486
|
+
static fromJSON(json: PublicKeyObject): CompressedSecp256k1PublicKey {
|
|
487
|
+
json.point.x.unshift(json.point.parity);
|
|
488
|
+
return new CompressedSecp256k1PublicKey(Uint8Array.from(json.point.x));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Computes the deterministic public key for a given secret key.
|
|
493
|
+
* @param {Secp256k1SecretKey | KeyBytes} sk The Secp256k1SecretKey object or the secret key bytes
|
|
494
|
+
* @returns {CompressedSecp256k1PublicKey} A new CompressedSecp256k1PublicKey object
|
|
495
|
+
*/
|
|
496
|
+
static fromSecretKey(sk: Secp256k1SecretKey | KeyBytes): CompressedSecp256k1PublicKey {
|
|
497
|
+
// If the secret key is a Secp256k1SecretKey object, get the raw bytes else use the bytes
|
|
498
|
+
const bytes = sk instanceof Secp256k1SecretKey ? sk.bytes : sk;
|
|
499
|
+
|
|
500
|
+
// Throw error if the secret key is not 32 bytes
|
|
501
|
+
if(bytes.length !== 32) {
|
|
502
|
+
throw new PublicKeyError('Invalid arg: must be 32 byte secret key', 'FROM_SECRET_KEY_ERROR');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Compute the public key from the secret key
|
|
506
|
+
const secret = sk instanceof Secp256k1SecretKey
|
|
507
|
+
? sk
|
|
508
|
+
: new Secp256k1SecretKey(sk);
|
|
509
|
+
|
|
510
|
+
// Return a new CompressedSecp256k1PublicKey object
|
|
511
|
+
return secret.computePublicKey();
|
|
512
|
+
}
|
|
466
513
|
}
|