@did-btcr2/keypair 0.10.0 → 0.11.1

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/public.ts CHANGED
@@ -1,20 +1,18 @@
1
1
  import {
2
- BIP340_PUBLIC_KEY_MULTIBASE_PREFIX,
3
- BIP340_PUBLIC_KEY_MULTIBASE_PREFIX_HASH,
4
2
  Bytes,
5
- CURVE,
6
3
  Hex,
7
4
  KeyBytes,
8
5
  MultibaseObject,
9
6
  PublicKeyError,
10
7
  PublicKeyObject
11
8
  } from '@did-btcr2/common';
12
- import { sha256 } from '@noble/hashes/sha2';
13
- import { base58btc } from 'multiformats/bases/base58';
14
- import * as tinysecp from 'tiny-secp256k1';
15
- import { Secp256k1SecretKey } from './secret.js';
9
+ import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
10
+ import { equalBytes } from '@noble/curves/utils.js';
11
+ import { base58 } from '@scure/base';
16
12
  import { CryptoOptions } from './types.js';
17
13
 
14
+ export const BIP340_PUBLIC_KEY_MULTIBASE_PREFIX: Bytes = new Uint8Array([0xe7, 0x01]);
15
+
18
16
  /**
19
17
  * Point Interface representing an (x, y) coordinate on the secp256k1 curve.
20
18
  * @interface Point
@@ -104,11 +102,11 @@ export interface PublicKey {
104
102
  encode(): string;
105
103
 
106
104
  /**
107
- * CompressedSecp256k1PublicKey key equality check. Checks if `this` public key is equal to `other` public key.
108
- * @param {CompressedSecp256k1PublicKey} other The public key to compare.
105
+ * Public key equality check.
106
+ * @param {PublicKey} other The public key to compare.
109
107
  * @returns {boolean} True if the public keys are equal.
110
108
  */
111
- equals(other: CompressedSecp256k1PublicKey): boolean;
109
+ equals(other: PublicKey): boolean;
112
110
  }
113
111
 
114
112
  /**
@@ -153,14 +151,14 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
153
151
  }
154
152
 
155
153
  // Validate the point is on curve and in compressed form
156
- if (!tinysecp.isPoint(keyBytes)) {
154
+ if (!secp256k1.utils.isValidPublicKey(keyBytes)) {
157
155
  throw new PublicKeyError(
158
156
  'Invalid argument: not a valid secp256k1 compressed point',
159
157
  'CONSTRUCTOR_ERROR', { keyBytes }
160
158
  );
161
159
  }
162
- // Set the bytes
163
- this.#bytes = keyBytes;
160
+ // Defensive copy — caller cannot mutate internal state
161
+ this.#bytes = new Uint8Array(keyBytes);
164
162
 
165
163
  // Set multibase
166
164
  this.#multibase.encoded = this.encode();
@@ -181,8 +179,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
181
179
  * @returns {Uint8Array} The 65-byte uncompressed public key (0x04, x, y).
182
180
  */
183
181
  get uncompressed(): KeyBytes {
184
- const uncompressed = this.liftX();
185
- return uncompressed;
182
+ return secp256k1.Point.fromBytes(this.compressed).toBytes(false);
186
183
  }
187
184
 
188
185
  /**
@@ -240,8 +237,11 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
240
237
  * @returns {MultibaseObject} An object containing the multibase bytes, address and prefix.
241
238
  */
242
239
  get multibase(): MultibaseObject {
243
- const multibase = this.#multibase;
244
- return multibase;
240
+ return {
241
+ prefix : new Uint8Array(this.#multibase.prefix),
242
+ key : [...this.#multibase.key],
243
+ encoded : this.#multibase.encoded
244
+ };
245
245
  }
246
246
 
247
247
  /**
@@ -277,33 +277,7 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
277
277
  * @returns {KeyBytes} The decoded public key: prefix and public key bytes
278
278
  */
279
279
  decode(): KeyBytes {
280
- // Decode the public key multibase string
281
- const decoded = base58btc.decode(this.multibase.encoded);
282
-
283
- // If the public key bytes are not 35 bytes, throw an error
284
- if(decoded.length !== 35) {
285
- throw new PublicKeyError(
286
- 'Invalid argument: must be 35 byte publicKeyMultibase',
287
- 'DECODE_MULTIBASE_ERROR'
288
- );
289
- }
290
-
291
- // Grab the prefix bytes
292
- const prefix = decoded.slice(0, 2);
293
-
294
- // Compute the prefix hash
295
- const prefixHash = Buffer.from(sha256(prefix)).toString('hex');
296
-
297
- // If the prefix hash does not equal the BIP340 prefix hash, throw an error
298
- if (prefixHash !== BIP340_PUBLIC_KEY_MULTIBASE_PREFIX_HASH) {
299
- throw new PublicKeyError(
300
- `Invalid prefix: malformed multibase prefix ${prefix}`,
301
- 'DECODE_MULTIBASE_ERROR'
302
- );
303
- }
304
-
305
- // Return the decoded public key bytes
306
- return decoded;
280
+ return base58.decode(this.multibase.encoded.slice(1));
307
281
  }
308
282
 
309
283
  /**
@@ -311,25 +285,10 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
311
285
  * @returns {string} The public key encoded in base-58-btc multibase format.
312
286
  */
313
287
  encode(): string {
314
- // Convert public key bytes to an array
315
288
  const pk = Array.from(this.compressed);
316
-
317
- // Ensure the public key is 33-byte secp256k1 compressed public key
318
- if (pk.length !== 33) {
319
- throw new PublicKeyError(
320
- 'Invalid argument: must be 33-byte (compressed) public key',
321
- 'ENCODE_MULTIBASE_ERROR'
322
- );
323
- }
324
-
325
- // Convert prefix to an array
326
289
  const publicKeyMultibase = Array.from(BIP340_PUBLIC_KEY_MULTIBASE_PREFIX);
327
-
328
- // Push the public key bytes at the end of the prefix
329
290
  publicKeyMultibase.push(...pk);
330
-
331
- // Encode the bytes in base58btc format and return
332
- return base58btc.encode(Uint8Array.from(publicKeyMultibase));
291
+ return 'z' + base58.encode(Uint8Array.from(publicKeyMultibase));
333
292
  }
334
293
 
335
294
  /**
@@ -344,13 +303,11 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
344
303
  // Default to schnorr scheme
345
304
  opts ??= { scheme: 'schnorr' };
346
305
 
347
- // If scheme is ecdsa, verify using ecdsa
348
306
  if(opts.scheme === 'ecdsa') {
349
- return tinysecp.verify(data, this.compressed, signature);
307
+ return secp256k1.verify(signature, data, this.compressed);
350
308
  }
351
- // If scheme is schnorr, verify using schnorr
352
309
  else if(opts.scheme === 'schnorr') {
353
- return tinysecp.verifySchnorr(data, this.x, signature);
310
+ return schnorr.verify(signature, data, this.x);
354
311
  }
355
312
 
356
313
  // If scheme is neither ecdsa nor schnorr, throw an error
@@ -359,11 +316,11 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
359
316
 
360
317
  /**
361
318
  * Compares this public key to another public key.
362
- * @param {CompressedSecp256k1PublicKey} other The other public key to compare
319
+ * @param {PublicKey} other The other public key to compare
363
320
  * @returns {boolean} True if the public keys are equal, false otherwise.
364
321
  */
365
- equals(other: CompressedSecp256k1PublicKey): boolean {
366
- return this.hex === other.hex;
322
+ equals(other: PublicKey): boolean {
323
+ return equalBytes(this.compressed, other.compressed);
367
324
  }
368
325
 
369
326
  /**
@@ -382,65 +339,6 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
382
339
  };
383
340
  }
384
341
 
385
- /**
386
- * Computes modular exponentiation: (base^exp) % mod.
387
- * Used for computing modular square roots.
388
- * @param {bigint} base The base value
389
- * @param {bigint} exp The exponent value
390
- * @param {bigint} mod The modulus value
391
- * @returns {bigint} The result of the modular exponentiation
392
- */
393
- modPow(base: bigint, exp: bigint, mod: bigint): bigint {
394
- let result = 1n;
395
- while (exp > 0n) {
396
- if (exp & 1n) result = (result * base) % mod;
397
- base = (base * base) % mod;
398
- exp >>= 1n;
399
- }
400
- return result;
401
- };
402
-
403
- /**
404
- * Computes `sqrt(a) mod p` using Tonelli-Shanks algorithm.
405
- * This finds `y` such that `y^2 ≡ a mod p`.
406
- * @param {bigint} a The value to find the square root of
407
- * @param {bigint} p The prime modulus
408
- * @returns {bigint} The square root of `a` mod `p`
409
- */
410
- sqrtMod(a: bigint, p: bigint): bigint {
411
- return this.modPow(a, (p + 1n) >> 2n, p);
412
- };
413
-
414
- /**
415
- * Lifts a 32-byte x-only coordinate into a full secp256k1 point (x, y).
416
- * @param xBytes 32-byte x-coordinate
417
- * @returns {Uint8Array} 65-byte uncompressed public key (starts with `0x04`)
418
- */
419
- liftX(): Uint8Array {
420
- // Ensure x-coordinate is 32 bytes
421
- if (this.x.length !== 32) {
422
- throw new PublicKeyError('Invalid argument: x-coordinate length must be 32 bytes', 'LIFT_X_ERROR');
423
- }
424
-
425
- // Convert x from Uint8Array → BigInt
426
- const x = BigInt('0x' + Buffer.from(this.x).toString('hex'));
427
- if (x <= 0n || x >= CURVE.p) {
428
- throw new PublicKeyError('Invalid conversion: x out of range as BigInt', 'LIFT_X_ERROR');
429
- }
430
-
431
- // Compute y² = x³ + 7 mod p
432
- const ySquared = BigInt((x ** 3n + CURVE.b) % CURVE.p);
433
-
434
- // Compute y (do not enforce parity)
435
- const y = this.sqrtMod(ySquared, CURVE.p);
436
-
437
- // Convert x and y to Uint8Array
438
- const yBytes = Buffer.from(y.toString(16).padStart(64, '0'), 'hex');
439
-
440
- // Return 65-byte uncompressed public key: `0x04 || x || y`
441
- return new Uint8Array(Buffer.concat([Buffer.from([0x04]), Buffer.from(this.x), yBytes]));
442
- };
443
-
444
342
  /**
445
343
  * Static method to validate a public key.
446
344
  * @param {Hex} pk The public key in hex (Uint8Array or string) format.
@@ -455,62 +353,13 @@ export class CompressedSecp256k1PublicKey implements PublicKey {
455
353
  }
456
354
  }
457
355
 
458
- /**
459
- * Returns the point of the public key.
460
- * @param {Hex} pk The public key in hex (Uint8Array or string) format.
461
- * @returns {Point} The point of the public key.
462
- * @throws {PublicKeyError} If the public key is not a valid hex string or byte array.
463
- */
464
- static point(pk: Hex): Point {
465
- // If the public key is a hex string, convert it to a CompressedSecp256k1PublicKey object and return the point
466
- if(typeof pk === 'string' && /^[0-9a-fA-F]+$/.test(pk)) {
467
- const publicKey = new CompressedSecp256k1PublicKey(Buffer.from(pk, 'hex'));
468
- return publicKey.point;
469
- }
470
-
471
- // If the public key is a byte array or ArrayBuffer, convert it to a CompressedSecp256k1PublicKey object and return the point
472
- if(pk instanceof Uint8Array || ArrayBuffer.isView(pk)) {
473
- const publicKey = new CompressedSecp256k1PublicKey(pk as KeyBytes);
474
- return publicKey.point;
475
- }
476
-
477
- // If the public key is neither a hex string nor a byte array, throw an error
478
- throw new PublicKeyError(
479
- 'Invalid publicKey: must be a hex string or byte array',
480
- 'POINT_ERROR', { publicKey: pk }
481
- );
482
- }
483
-
484
356
  /**
485
357
  * Creates a CompressedSecp256k1PublicKey object from a JSON representation.
486
358
  * @param {PublicKeyObject} json The JSON object to initialize the CompressedSecp256k1PublicKey.
487
359
  * @returns {CompressedSecp256k1PublicKey} The initialized CompressedSecp256k1PublicKey object.
488
360
  */
489
361
  static fromJSON(json: PublicKeyObject): CompressedSecp256k1PublicKey {
490
- json.point.x.unshift(json.point.parity);
491
- return new CompressedSecp256k1PublicKey(Uint8Array.from(json.point.x));
362
+ return new CompressedSecp256k1PublicKey(Uint8Array.from([json.point.parity, ...json.point.x]));
492
363
  }
493
364
 
494
- /**
495
- * Computes the deterministic public key for a given secret key.
496
- * @param {Secp256k1SecretKey | KeyBytes} sk The Secp256k1SecretKey object or the secret key bytes
497
- * @returns {CompressedSecp256k1PublicKey} A new CompressedSecp256k1PublicKey object
498
- */
499
- static fromSecretKey(sk: Secp256k1SecretKey | KeyBytes): CompressedSecp256k1PublicKey {
500
- // If the secret key is a Secp256k1SecretKey object, get the raw bytes else use the bytes
501
- const bytes = sk instanceof Secp256k1SecretKey ? sk.bytes : sk;
502
-
503
- // Throw error if the secret key is not 32 bytes
504
- if(bytes.length !== 32) {
505
- throw new PublicKeyError('Invalid arg: must be 32 byte secret key', 'FROM_SECRET_KEY_ERROR');
506
- }
507
-
508
- // Compute the public key from the secret key
509
- const secret = sk instanceof Secp256k1SecretKey
510
- ? sk
511
- : new Secp256k1SecretKey(sk);
512
-
513
- // Return a new CompressedSecp256k1PublicKey object
514
- return secret.computePublicKey();
515
- }
516
365
  }
package/src/secret.ts CHANGED
@@ -1,8 +1,5 @@
1
1
  import {
2
- BIP340_SECRET_KEY_MULTIBASE_PREFIX,
3
- BIP340_SECRET_KEY_MULTIBASE_PREFIX_HASH,
4
2
  Bytes,
5
- CURVE,
6
3
  Hex,
7
4
  HexString,
8
5
  KeyBytes,
@@ -11,12 +8,18 @@ import {
11
8
  SignatureBytes
12
9
  } from '@did-btcr2/common';
13
10
  import { sha256 } from '@noble/hashes/sha2';
14
- import { getRandomValues, randomBytes } from 'crypto';
15
- import { base58btc } from 'multiformats/bases/base58';
16
- import * as tinysecp from 'tiny-secp256k1';
17
- import { SchnorrKeyPair } from './pair.js';
11
+ import { bytesToHex } from '@noble/hashes/utils';
12
+ import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
13
+ import { randomBytes } from '@noble/hashes/utils';
14
+ import { base58 } from '@scure/base';
18
15
  import { CompressedSecp256k1PublicKey } from './public.js';
19
16
  import { CryptoOptions } from './types.js';
17
+ import { equalBytes } from '@noble/curves/utils.js';
18
+
19
+ /** Fixed secret key header bytes per the Data Integrity BIP340 Cryptosuite spec: [0x81, 0x26] */
20
+ const BIP340_SECRET_KEY_MULTIBASE_PREFIX: Bytes = new Uint8Array([0x81, 0x26]);
21
+ /** Hash of the BIP-340 Multikey secret key prefix */
22
+ const BIP340_SECRET_KEY_MULTIBASE_PREFIX_HASH: string = bytesToHex(sha256(BIP340_SECRET_KEY_MULTIBASE_PREFIX));
20
23
 
21
24
  /**
22
25
  * General SecretKey interface for the Secp256k1SecretKey class.
@@ -45,10 +48,10 @@ export interface SecretKey {
45
48
 
46
49
  /**
47
50
  * Checks if this secret key is equal to another secret key.
48
- * @param {Secp256k1SecretKey} other The other secret key to compare.
51
+ * @param {SecretKey} other The other secret key to compare.
49
52
  * @returns {boolean} True if the private keys are equal.
50
53
  */
51
- equals(other: Secp256k1SecretKey): boolean;
54
+ equals(other: SecretKey): boolean;
52
55
 
53
56
  /**
54
57
  * Uses the secret key to compute the corresponding public key.
@@ -73,13 +76,13 @@ export interface SecretKey {
73
76
  */
74
77
  export class Secp256k1SecretKey implements SecretKey {
75
78
  /** @type {KeyBytes} The entropy for the secret key as a byte array */
76
- readonly #bytes?: KeyBytes;
79
+ #bytes?: KeyBytes;
77
80
 
78
81
  /** @type {bigint} The entropy for the secret key as a bigint */
79
- readonly #seed?: bigint;
82
+ #seed?: bigint;
80
83
 
81
84
  /** @type {string} The secret key as a secretKeyMultibase */
82
- readonly #multibase: string;
85
+ #multibase: string;
83
86
 
84
87
  /**
85
88
  * Instantiates an instance of Secp256k1SecretKey.
@@ -97,14 +100,14 @@ export class Secp256k1SecretKey implements SecretKey {
97
100
  );
98
101
  }
99
102
 
100
- // If bytes and bytes are not length 32
103
+ // If bytes and length is 32, defensive-copy and derive seed
101
104
  if (isBytes && entropy.length === 32) {
102
- this.#bytes = entropy;
103
- this.#seed = Secp256k1SecretKey.toSecret(entropy);
105
+ this.#bytes = new Uint8Array(entropy);
106
+ this.#seed = Secp256k1SecretKey.toSecret(this.#bytes);
104
107
  }
105
108
 
106
- // If secret and secret is not a valid bigint, throw error
107
- if (isSecret && !(entropy < 1n || entropy >= CURVE.n)) {
109
+ // If bigint in valid range [1, n), convert to bytes
110
+ if (isSecret && entropy >= 1n && entropy < secp256k1.Point.Fn.ORDER) {
108
111
  this.#bytes = Secp256k1SecretKey.toBytes(entropy);
109
112
  this.#seed = entropy;
110
113
  }
@@ -116,9 +119,9 @@ export class Secp256k1SecretKey implements SecretKey {
116
119
  );
117
120
  }
118
121
 
119
- if(!this.#seed || (this.#seed < 1n || this.#seed >= CURVE.n)) {
122
+ if(!this.#seed || this.#seed < 1n || this.#seed >= secp256k1.Point.Fn.ORDER) {
120
123
  throw new SecretKeyError(
121
- 'Invalid seed: must must be valid bigint',
124
+ 'Invalid seed: must be valid bigint',
122
125
  'CONSTRUCTOR_ERROR'
123
126
  );
124
127
  }
@@ -127,6 +130,16 @@ export class Secp256k1SecretKey implements SecretKey {
127
130
  this.#multibase = this.encode();
128
131
  }
129
132
 
133
+ /**
134
+ * Zeros out secret key material from memory.
135
+ * The instance should not be used after calling this method.
136
+ */
137
+ public destroy(): void {
138
+ if (this.#bytes) this.#bytes.fill(0);
139
+ this.#seed = undefined;
140
+ this.#multibase = '';
141
+ }
142
+
130
143
  /**
131
144
  * Get the secret key entropy as a byte array.
132
145
  * @returns {KeyBytes} The secret key bytes as a Uint8Array
@@ -171,33 +184,19 @@ export class Secp256k1SecretKey implements SecretKey {
171
184
  * @returns {string} The secret key in BIP340 multibase format.
172
185
  */
173
186
  public encode(): string {
174
- // Convert Uint8Array bytes to an Array
175
187
  const secretKeyBytes = Array.from(this.bytes);
176
-
177
- if(secretKeyBytes.length !== 32) {
178
- throw new SecretKeyError(
179
- 'Invalid secret key: must be a valid 32-byte secret key',
180
- 'ENCODE_MULTIBASE_ERROR'
181
- );
182
- }
183
- // Convert prefix to an array
184
188
  const mbaseBytes = Array.from(BIP340_SECRET_KEY_MULTIBASE_PREFIX);
185
-
186
- // Push the secret key bytes at the end of the prefix
187
189
  mbaseBytes.push(...secretKeyBytes);
188
-
189
- // Encode the bytes in base58btc format and return
190
- return base58btc.encode(Uint8Array.from(mbaseBytes));
190
+ return 'z' + base58.encode(Uint8Array.from(mbaseBytes));
191
191
  }
192
192
 
193
193
  /**
194
194
  * Checks if this secret key is equal to another.
195
- * @param {Secp256k1SecretKey} other The other secret key
195
+ * @param {SecretKey} other The other secret key
196
196
  * @returns {boolean} True if the private keys are equal, false otherwise
197
197
  */
198
- public equals(other: Secp256k1SecretKey): boolean {
199
- // Compare the hex strings of the private keys
200
- return this.hex === other.hex;
198
+ public equals(other: SecretKey): boolean {
199
+ return equalBytes(this.bytes, other.bytes);
201
200
  }
202
201
 
203
202
  /**
@@ -205,33 +204,22 @@ export class Secp256k1SecretKey implements SecretKey {
205
204
  * @returns {CompressedSecp256k1PublicKey} The computed public key
206
205
  */
207
206
  public computePublicKey(): CompressedSecp256k1PublicKey {
208
- // Derive the public key from the secret key
209
- const publicKeyBytes = tinysecp.pointFromScalar(this.bytes, true);
210
-
211
- // If no public key, throw error
212
- if (!publicKeyBytes) {
213
- throw new SecretKeyError(
214
- 'Invalid compute: failed to derive public key',
215
- 'COMPUTE_PUBLIC_KEY_ERROR'
216
- );
217
- }
218
-
219
- // If public key is not compressed, throw error
220
- if(publicKeyBytes.length !== 33) {
221
- throw new SecretKeyError(
222
- 'Invalid compute: public key not compressed format',
223
- 'COMPUTE_PUBLIC_KEY_ERROR'
224
- );
225
- }
207
+ return new CompressedSecp256k1PublicKey(secp256k1.getPublicKey(this.bytes));
208
+ }
226
209
 
227
- return new CompressedSecp256k1PublicKey(publicKeyBytes);
210
+ /**
211
+ * Safe JSON representation. Does not expose secret material.
212
+ * Called implicitly by JSON.stringify(). Use exportJSON() for full serialization.
213
+ */
214
+ public toJSON(): { type: string } {
215
+ return { type: 'Secp256k1SecretKey' };
228
216
  }
229
217
 
230
218
  /**
231
- * Converts the secret key to a JSON object.
219
+ * Exports the secret key as a JSON object. Contains sensitive material.
232
220
  * @returns {SecretKeyObject} The secret key as a JSON object
233
221
  */
234
- public toJSON(): SecretKeyObject {
222
+ public exportJSON(): SecretKeyObject {
235
223
  return {
236
224
  bytes : Array.from(this.bytes),
237
225
  seed : this.seed.toString(),
@@ -239,12 +227,22 @@ export class Secp256k1SecretKey implements SecretKey {
239
227
  };
240
228
  }
241
229
 
230
+ /** @override Prevents secret material from appearing in console.log */
231
+ public toString(): string {
232
+ return '[Secp256k1SecretKey]';
233
+ }
234
+
235
+ /** @override Prevents secret material from appearing in Node.js inspect */
236
+ [Symbol.for('nodejs.util.inspect.custom')](): string {
237
+ return '[Secp256k1SecretKey]';
238
+ }
239
+
242
240
  /**
243
241
  * Checks if the secret key is valid.
244
242
  * @returns {boolean} True if the secret key is valid, false otherwise
245
243
  */
246
244
  public isValid(): boolean {
247
- return tinysecp.isPrivate(this.bytes);
245
+ return secp256k1.utils.isValidSecretKey(this.bytes);
248
246
  }
249
247
 
250
248
  /**
@@ -252,16 +250,12 @@ export class Secp256k1SecretKey implements SecretKey {
252
250
  * @returns {boolean} True if the public key is valid, false otherwise
253
251
  */
254
252
  public hasValidPublicKey(): boolean {
255
- // Compute the public key from the secret key and compress it
256
- const pk = this.computePublicKey();
257
-
258
- // If the public key is not valid, return false
259
- if (!tinysecp.isPoint(pk.compressed)) {
253
+ try {
254
+ this.computePublicKey();
255
+ return true;
256
+ } catch {
260
257
  return false;
261
258
  }
262
-
263
- // Return true if the computed public key equals the provided public key
264
- return true;
265
259
  }
266
260
 
267
261
  /**
@@ -276,14 +270,12 @@ export class Secp256k1SecretKey implements SecretKey {
276
270
  // Set default options if not provided
277
271
  opts ??= { scheme: 'schnorr' };
278
272
 
279
- // Sign ecdsa and return
280
273
  if(opts.scheme === 'ecdsa') {
281
- return tinysecp.sign(data, this.bytes);
274
+ return secp256k1.sign(data, this.bytes);
282
275
  }
283
276
 
284
- // Sign schnorr and return
285
277
  if(opts.scheme === 'schnorr') {
286
- return tinysecp.signSchnorr(data, this.bytes, randomBytes(32));
278
+ return schnorr.sign(data, this.bytes);
287
279
  }
288
280
 
289
281
  throw new SecretKeyError(`Invalid scheme: ${opts.scheme}.`, 'SIGN_ERROR', opts);
@@ -296,7 +288,7 @@ export class Secp256k1SecretKey implements SecretKey {
296
288
  */
297
289
  public static decode(multibase: string): Bytes {
298
290
  // Decode the public key multibase string
299
- const decoded = base58btc.decode(multibase);
291
+ const decoded = base58.decode(multibase.slice(1));
300
292
 
301
293
  // If the public key bytes are not 35 bytes, throw an error
302
294
  if(decoded.length !== 34) {
@@ -333,23 +325,6 @@ export class Secp256k1SecretKey implements SecretKey {
333
325
  return new Secp256k1SecretKey(new Uint8Array(json.bytes));
334
326
  }
335
327
 
336
- /**
337
- * Converts a Secp256k1SecretKey or KeyBytes to a SchnorrKeyPair.
338
- * @param {KeyBytes} bytes The secret key bytes
339
- * @returns {SchnorrKeyPair} The SchnorrKeyPair object containing the public and private keys
340
- * @throws {SecretKeyError} If the secret key is not valid
341
- */
342
- public static toKeyPair(bytes: KeyBytes): SchnorrKeyPair {
343
- // Create a new Secp256k1SecretKey from the bytes
344
- const secretKey = new Secp256k1SecretKey(bytes);
345
-
346
- // Compute the public key from the secret key
347
- const publicKey = secretKey.computePublicKey();
348
-
349
- // Create a new Pair from the public key and secret key
350
- return new SchnorrKeyPair({ publicKey, secretKey });
351
- }
352
-
353
328
  /**
354
329
  * Convert a bigint secret to secret key bytes.
355
330
  * @param {KeyBytes} bytes The secret key bytes
@@ -372,7 +347,7 @@ export class Secp256k1SecretKey implements SecretKey {
372
347
  );
373
348
 
374
349
  // If bytes are not a valid secp256k1 secret key, throw error
375
- if (!tinysecp.isPrivate(bytes)) {
350
+ if (!secp256k1.utils.isValidSecretKey(bytes)) {
376
351
  throw new SecretKeyError(
377
352
  'Invalid secret key: secret out of valid range',
378
353
  'SET_PRIVATE_KEY_ERROR'
@@ -381,28 +356,13 @@ export class Secp256k1SecretKey implements SecretKey {
381
356
  return new Uint8Array(bytes);
382
357
  }
383
358
 
384
- /**
385
- * Creates a new Secp256k1SecretKey object from random bytes.
386
- * @param {KeyBytes} bytes The secret key bytes
387
- * @returns {Secp256k1SecretKey} A new Secp256k1SecretKey object
388
- */
389
- public static fromBytes(bytes: KeyBytes): Secp256k1SecretKey {
390
- // Return a new Secp256k1SecretKey object
391
- return new Secp256k1SecretKey(bytes);
392
- }
393
-
394
359
  /**
395
360
  * Creates a new Secp256k1SecretKey object from a bigint secret.
396
361
  * @param {bigint} bint The secret bigint
397
362
  * @returns {Secp256k1SecretKey} A new Secp256k1SecretKey object
398
363
  */
399
364
  public static fromBigInt(bint: bigint): Secp256k1SecretKey {
400
- // Convert the secret bigint to a hex string
401
- const hexsecret = bint.toString(16).padStart(64, '0');
402
- // Convert the hex string to a Uint8Array
403
- const bytes = new Uint8Array(hexsecret.match(/.{2}/g)!.map(byte => parseInt(byte, 16)));
404
- // Return a new Secp256k1SecretKey object
405
- return new Secp256k1SecretKey(bytes);
365
+ return new Secp256k1SecretKey(Secp256k1SecretKey.toBytes(bint));
406
366
  }
407
367
 
408
368
  /**
@@ -410,11 +370,12 @@ export class Secp256k1SecretKey implements SecretKey {
410
370
  * @returns {KeyBytes} Uint8Array of 32 random bytes.
411
371
  */
412
372
  public static random(): KeyBytes {
413
- // Generate empty 32-byte array
414
- const byteArray = new Uint8Array(32);
415
-
416
- // Use the getRandomValues function to fill the byteArray with random values
417
- return getRandomValues(byteArray);
373
+ let byteArray: Uint8Array;
374
+ // Retry until bytes fall in valid scalar range [1, n)
375
+ do {
376
+ byteArray = randomBytes(32);
377
+ } while (!secp256k1.utils.isValidSecretKey(byteArray));
378
+ return byteArray;
418
379
  }
419
380
 
420
381
  /**
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Hex, KeyBytes } from '@did-btcr2/common';
2
- import { CompressedSecp256k1PublicKey } from './public.js';
3
- import { Secp256k1SecretKey } from './secret.js';
2
+ import type { CompressedSecp256k1PublicKey } from './public.js';
3
+ import type { Secp256k1SecretKey } from './secret.js';
4
4
 
5
5
  export type CryptoOptions = { scheme: 'ecdsa' | 'schnorr' }
6
6