@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/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 {CompressedSecp256k1PublicKey} The public key associated with the SchnorrKeyPair (required).
19
+ * @type {PublicKey} The public key associated with the SchnorrKeyPair (required).
19
20
  */
20
- readonly publicKey: CompressedSecp256k1PublicKey;
21
+ readonly publicKey: PublicKey;
21
22
 
22
23
  /**
23
- * @type {Secp256k1SecretKey} The secret key associated with the SchnorrKeyPair (optional).
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?: Secp256k1SecretKey;
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
- private _secretKey?: Secp256k1SecretKey;
37
+ #secretKey?: Secp256k1SecretKey;
43
38
 
44
39
  /** @type {CompressedSecp256k1PublicKey} The public key object */;
45
- private _publicKey: CompressedSecp256k1PublicKey;
40
+ #publicKey: CompressedSecp256k1PublicKey;
46
41
 
47
42
  /** @type {string} The public key in multibase format */
48
- private _publicKeyMultibase: string;
43
+ #publicKeyMultibase: string;
49
44
 
50
45
  /** @type {string} The secret key in multibase format */
51
- private _secretKeyMultibase: string;
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._secretKey = new Secp256k1SecretKey(params.secretKey);
65
+ this.#secretKey = new Secp256k1SecretKey(params.secretKey);
71
66
  } else if (params.secretKey instanceof Secp256k1SecretKey) {
72
- this._secretKey = params.secretKey;
67
+ this.#secretKey = params.secretKey;
73
68
  }
74
69
 
75
70
  // Set the publicKey
76
71
  if(params.publicKey instanceof CompressedSecp256k1PublicKey) {
77
- this._publicKey = params.publicKey;
72
+ this.#publicKey = params.publicKey;
78
73
  } else if (params.publicKey instanceof Uint8Array) {
79
- this._publicKey = new CompressedSecp256k1PublicKey(params.publicKey);
74
+ this.#publicKey = new CompressedSecp256k1PublicKey(params.publicKey);
80
75
  } else {
81
- this._publicKey = this._secretKey!.computePublicKey();
76
+ this.#publicKey = this.#secretKey!.computePublicKey();
82
77
  }
83
78
 
84
- this._publicKeyMultibase = this._publicKey.multibase.encoded;
85
- this._secretKeyMultibase = this._secretKey ? this._secretKey.multibase : '';
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._secretKey) {
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._secretKey.isValid()) {
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._secretKey;
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._publicKey = publicKey;
123
- this._publicKeyMultibase = publicKey.multibase.encoded;
124
- this._secretKeyMultibase = this._secretKey ? this._secretKey.multibase : '';
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._publicKey;
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._secretKey ? this.secretKey.hex : undefined
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 : this._publicKeyMultibase,
165
- secretKeyMultibase : this._secretKeyMultibase,
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 fromPrivateKey(data: Secp256k1SecretKey | KeyBytes): SchnorrKeyPair {
198
-
199
- // If the secret key is a Secp256k1SecretKey object, get the raw bytes else use the bytes
200
- const bytes = data instanceof Secp256k1SecretKey ? data.bytes : data;
201
-
202
- // Throw error if the secret key is not 32 bytes
203
- if(bytes.length !== 32) {
204
- throw new KeyPairError('Invalid arg: must be 32 byte secret key', 'FROM_PRIVATE_KEY_ERROR');
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 secret = data instanceof Uint8Array ? new Secp256k1SecretKey(data) : data;
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
- private readonly _bytes: KeyBytes;
129
+ readonly #bytes: KeyBytes;
123
130
 
124
131
  /** @type {MultibaseObject} The public key as a MultibaseObject */
125
- private _multibase: MultibaseObject = {
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 {KeyBytes} bytes The public key byte array.
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(bytes: KeyBytes) {
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(bytes.length !== 33) {
150
+ if(!keyBytes || keyBytes.length !== 33) {
139
151
  throw new PublicKeyError(
140
152
  'Invalid argument: byte length must be 33 (compressed)',
141
- 'CONSTRUCTOR_ERROR', { bytes }
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(bytes)) {
158
+ if (!tinysecp.isPoint(keyBytes)) {
147
159
  throw new PublicKeyError(
148
- 'Invalid argument: bytes are not a valid secp256k1 compressed point',
149
- 'CONSTRUCTOR_ERROR', { bytes }
160
+ 'Invalid argument: not a valid secp256k1 compressed point',
161
+ 'CONSTRUCTOR_ERROR', { keyBytes }
150
162
  );
151
163
  }
152
164
  // Set the bytes
153
- this._bytes = bytes;
165
+ this.#bytes = keyBytes;
154
166
 
155
167
  // Set multibase
156
- this._multibase.encoded = this.encode();
157
- this._multibase.key = [...this._multibase.prefix, ...this.compressed];
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._bytes);
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
- return this._bytes.slice(1);
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._bytes[0];
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._bytes[0] === 0x02;
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._multibase;
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 {Hex} The public key as a hex string.
251
+ * @returns {string} The public key as a hex string.
239
252
  */
240
- get hex(): 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
- public bip340(): KeyBytes {
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
- public decode(): KeyBytes {
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
- public encode(): string {
315
+ encode(): string {
329
316
  // Convert public key bytes to an array
330
- const pk = this.compressed.toArray();
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 = BIP340_PUBLIC_KEY_MULTIBASE_PREFIX.toArray();
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(publicKeyMultibase.toUint8Array());
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
- public equals(other: CompressedSecp256k1PublicKey): boolean {
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
- public json(): PublicKeyObject {
370
+ json(): PublicKeyObject {
364
371
  return {
365
372
  hex : this.hex,
366
373
  multibase : this.multibase,
367
374
  point : {
368
- x : this.x.toArray(),
369
- y : this.y.toArray(),
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
- public modPow(base: bigint, exp: bigint, mod: bigint): bigint {
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
- public sqrtMod(a: bigint, p: bigint): bigint {
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
- public liftX(): Uint8Array {
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.fromHex(y.toString(16).padStart(64, '0'));
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
  }