@noble/curves 1.9.4 → 1.9.5

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.
Files changed (103) hide show
  1. package/abstract/curve.d.ts +47 -46
  2. package/abstract/curve.d.ts.map +1 -1
  3. package/abstract/curve.js +9 -6
  4. package/abstract/curve.js.map +1 -1
  5. package/abstract/edwards.d.ts +33 -31
  6. package/abstract/edwards.d.ts.map +1 -1
  7. package/abstract/edwards.js +123 -122
  8. package/abstract/edwards.js.map +1 -1
  9. package/abstract/modular.d.ts +1 -1
  10. package/abstract/modular.d.ts.map +1 -1
  11. package/abstract/modular.js +4 -4
  12. package/abstract/modular.js.map +1 -1
  13. package/abstract/montgomery.d.ts +2 -6
  14. package/abstract/montgomery.d.ts.map +1 -1
  15. package/abstract/montgomery.js +13 -10
  16. package/abstract/montgomery.js.map +1 -1
  17. package/abstract/weierstrass.d.ts +161 -92
  18. package/abstract/weierstrass.d.ts.map +1 -1
  19. package/abstract/weierstrass.js +340 -267
  20. package/abstract/weierstrass.js.map +1 -1
  21. package/bls12-381.d.ts.map +1 -1
  22. package/bls12-381.js +4 -4
  23. package/bls12-381.js.map +1 -1
  24. package/ed25519.d.ts +15 -15
  25. package/ed25519.d.ts.map +1 -1
  26. package/ed25519.js +41 -38
  27. package/ed25519.js.map +1 -1
  28. package/ed448.d.ts +13 -13
  29. package/ed448.d.ts.map +1 -1
  30. package/ed448.js +43 -35
  31. package/ed448.js.map +1 -1
  32. package/esm/abstract/curve.d.ts +47 -46
  33. package/esm/abstract/curve.d.ts.map +1 -1
  34. package/esm/abstract/curve.js +9 -6
  35. package/esm/abstract/curve.js.map +1 -1
  36. package/esm/abstract/edwards.d.ts +33 -31
  37. package/esm/abstract/edwards.d.ts.map +1 -1
  38. package/esm/abstract/edwards.js +124 -123
  39. package/esm/abstract/edwards.js.map +1 -1
  40. package/esm/abstract/modular.d.ts +1 -1
  41. package/esm/abstract/modular.d.ts.map +1 -1
  42. package/esm/abstract/modular.js +4 -4
  43. package/esm/abstract/modular.js.map +1 -1
  44. package/esm/abstract/montgomery.d.ts +2 -6
  45. package/esm/abstract/montgomery.d.ts.map +1 -1
  46. package/esm/abstract/montgomery.js +14 -11
  47. package/esm/abstract/montgomery.js.map +1 -1
  48. package/esm/abstract/weierstrass.d.ts +161 -92
  49. package/esm/abstract/weierstrass.d.ts.map +1 -1
  50. package/esm/abstract/weierstrass.js +342 -270
  51. package/esm/abstract/weierstrass.js.map +1 -1
  52. package/esm/bls12-381.d.ts.map +1 -1
  53. package/esm/bls12-381.js +5 -5
  54. package/esm/bls12-381.js.map +1 -1
  55. package/esm/ed25519.d.ts +15 -15
  56. package/esm/ed25519.d.ts.map +1 -1
  57. package/esm/ed25519.js +40 -37
  58. package/esm/ed25519.js.map +1 -1
  59. package/esm/ed448.d.ts +13 -13
  60. package/esm/ed448.d.ts.map +1 -1
  61. package/esm/ed448.js +42 -34
  62. package/esm/ed448.js.map +1 -1
  63. package/esm/misc.js +2 -2
  64. package/esm/misc.js.map +1 -1
  65. package/esm/nist.d.ts +6 -0
  66. package/esm/nist.d.ts.map +1 -1
  67. package/esm/nist.js +6 -0
  68. package/esm/nist.js.map +1 -1
  69. package/esm/secp256k1.d.ts +2 -6
  70. package/esm/secp256k1.d.ts.map +1 -1
  71. package/esm/secp256k1.js +8 -11
  72. package/esm/secp256k1.js.map +1 -1
  73. package/esm/utils.d.ts +14 -0
  74. package/esm/utils.d.ts.map +1 -1
  75. package/esm/utils.js +43 -0
  76. package/esm/utils.js.map +1 -1
  77. package/misc.js +2 -2
  78. package/misc.js.map +1 -1
  79. package/nist.d.ts +6 -0
  80. package/nist.d.ts.map +1 -1
  81. package/nist.js +7 -1
  82. package/nist.js.map +1 -1
  83. package/package.json +1 -1
  84. package/secp256k1.d.ts +2 -6
  85. package/secp256k1.d.ts.map +1 -1
  86. package/secp256k1.js +7 -10
  87. package/secp256k1.js.map +1 -1
  88. package/src/abstract/curve.ts +131 -68
  89. package/src/abstract/edwards.ts +162 -166
  90. package/src/abstract/modular.ts +4 -4
  91. package/src/abstract/montgomery.ts +16 -16
  92. package/src/abstract/weierstrass.ts +510 -395
  93. package/src/bls12-381.ts +5 -4
  94. package/src/ed25519.ts +51 -46
  95. package/src/ed448.ts +46 -44
  96. package/src/misc.ts +2 -2
  97. package/src/nist.ts +7 -0
  98. package/src/secp256k1.ts +10 -12
  99. package/src/utils.ts +48 -0
  100. package/utils.d.ts +14 -0
  101. package/utils.d.ts.map +1 -1
  102. package/utils.js +47 -0
  103. package/utils.js.map +1 -1
@@ -2,13 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DER = exports.DERErr = void 0;
4
4
  exports._splitEndoScalar = _splitEndoScalar;
5
- exports._legacyHelperEquat = _legacyHelperEquat;
6
5
  exports._normFnElement = _normFnElement;
7
6
  exports.weierstrassN = weierstrassN;
8
- exports.weierstrassPoints = weierstrassPoints;
9
7
  exports.SWUFpSqrtRatio = SWUFpSqrtRatio;
10
8
  exports.mapToCurveSimpleSWU = mapToCurveSimpleSWU;
9
+ exports.ecdh = ecdh;
11
10
  exports.ecdsa = ecdsa;
11
+ exports.weierstrassPoints = weierstrassPoints;
12
+ exports._legacyHelperEquat = _legacyHelperEquat;
12
13
  exports.weierstrass = weierstrass;
13
14
  /**
14
15
  * Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
@@ -72,11 +73,22 @@ function _splitEndoScalar(k, basis, n) {
72
73
  }
73
74
  return { k1neg, k1, k2neg, k2 };
74
75
  }
75
- function validateSigVerOpts(opts) {
76
- if (opts.lowS !== undefined)
77
- (0, utils_ts_1.abool)('lowS', opts.lowS);
78
- if (opts.prehash !== undefined)
79
- (0, utils_ts_1.abool)('prehash', opts.prehash);
76
+ function validateSigFormat(format) {
77
+ if (!['compact', 'recovered', 'der'].includes(format))
78
+ throw new Error('Signature format must be "compact", "recovered", or "der"');
79
+ return format;
80
+ }
81
+ function validateSigOpts(opts, def) {
82
+ const optsn = {};
83
+ for (let optName of Object.keys(def)) {
84
+ // @ts-ignore
85
+ optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
86
+ }
87
+ (0, utils_ts_1._abool2)(optsn.lowS, 'lowS');
88
+ (0, utils_ts_1._abool2)(optsn.prehash, 'prehash');
89
+ if (optsn.format !== undefined)
90
+ validateSigFormat(optsn.format);
91
+ return optsn;
80
92
  }
81
93
  class DERErr extends Error {
82
94
  constructor(m = '') {
@@ -198,19 +210,6 @@ exports.DER = {
198
210
  // Be friendly to bad ECMAScript parsers by not using bigint literals
199
211
  // prettier-ignore
200
212
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
201
- // TODO: remove
202
- function _legacyHelperEquat(Fp, a, b) {
203
- /**
204
- * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
205
- * @returns y²
206
- */
207
- function weierstrassEquation(x) {
208
- const x2 = Fp.sqr(x); // x * x
209
- const x3 = Fp.mul(x2, x); // x² * x
210
- return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
211
- }
212
- return weierstrassEquation;
213
- }
214
213
  function _normFnElement(Fn, key) {
215
214
  const { BYTES: expected } = Fn;
216
215
  let num;
@@ -230,10 +229,29 @@ function _normFnElement(Fn, key) {
230
229
  throw new Error('invalid private key: out of range [1..N-1]');
231
230
  return num;
232
231
  }
233
- function weierstrassN(CURVE, curveOpts = {}) {
234
- const { Fp, Fn } = (0, curve_ts_1._createCurveFields)('weierstrass', CURVE, curveOpts);
232
+ /**
233
+ * Creates weierstrass Point constructor, based on specified curve options.
234
+ *
235
+ * @example
236
+ ```js
237
+ const opts = {
238
+ p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
239
+ n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
240
+ h: BigInt(1),
241
+ a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
242
+ b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
243
+ Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
244
+ Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
245
+ };
246
+ const p256_Point = weierstrass(opts);
247
+ ```
248
+ */
249
+ function weierstrassN(params, extraOpts = {}) {
250
+ const validated = (0, curve_ts_1._createCurveFields)('weierstrass', params, extraOpts);
251
+ const { Fp, Fn } = validated;
252
+ let CURVE = validated.CURVE;
235
253
  const { h: cofactor, n: CURVE_ORDER } = CURVE;
236
- (0, utils_ts_1._validateObject)(curveOpts, {}, {
254
+ (0, utils_ts_1._validateObject)(extraOpts, {}, {
237
255
  allowInfinityPoint: 'boolean',
238
256
  clearCofactor: 'function',
239
257
  isTorsionFree: 'function',
@@ -242,13 +260,14 @@ function weierstrassN(CURVE, curveOpts = {}) {
242
260
  endo: 'object',
243
261
  wrapPrivateKey: 'boolean',
244
262
  });
245
- const { endo } = curveOpts;
263
+ const { endo } = extraOpts;
246
264
  if (endo) {
247
265
  // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
248
266
  if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
249
267
  throw new Error('invalid endo: expected "beta": bigint and "basises": array');
250
268
  }
251
269
  }
270
+ const lengths = getWLengths(Fp, Fn);
252
271
  function assertCompressionIsSupported() {
253
272
  if (!Fp.isOdd)
254
273
  throw new Error('compression is not supported: Field does not have .isOdd()');
@@ -257,7 +276,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
257
276
  function pointToBytes(_c, point, isCompressed) {
258
277
  const { x, y } = point.toAffine();
259
278
  const bx = Fp.toBytes(x);
260
- (0, utils_ts_1.abool)('isCompressed', isCompressed);
279
+ (0, utils_ts_1._abool2)(isCompressed, 'isCompressed');
261
280
  if (isCompressed) {
262
281
  assertCompressionIsSupported();
263
282
  const hasEvenY = !Fp.isOdd(y);
@@ -268,15 +287,13 @@ function weierstrassN(CURVE, curveOpts = {}) {
268
287
  }
269
288
  }
270
289
  function pointFromBytes(bytes) {
271
- (0, utils_ts_1.abytes)(bytes);
272
- const L = Fp.BYTES;
273
- const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
274
- const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
290
+ (0, utils_ts_1._abytes2)(bytes, undefined, 'Point');
291
+ const { public: comp, publicUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
275
292
  const length = bytes.length;
276
293
  const head = bytes[0];
277
294
  const tail = bytes.subarray(1);
278
295
  // No actual validation is done here: use .assertValidity()
279
- if (length === LC && (head === 0x02 || head === 0x03)) {
296
+ if (length === comp && (head === 0x02 || head === 0x03)) {
280
297
  const x = Fp.fromBytes(tail);
281
298
  if (!Fp.isValid(x))
282
299
  throw new Error('bad point: is not on curve, wrong x');
@@ -296,21 +313,26 @@ function weierstrassN(CURVE, curveOpts = {}) {
296
313
  y = Fp.neg(y);
297
314
  return { x, y };
298
315
  }
299
- else if (length === LU && head === 0x04) {
316
+ else if (length === uncomp && head === 0x04) {
300
317
  // TODO: more checks
301
- const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
302
- const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
318
+ const L = Fp.BYTES;
319
+ const x = Fp.fromBytes(tail.subarray(0, L));
320
+ const y = Fp.fromBytes(tail.subarray(L, L * 2));
303
321
  if (!isValidXY(x, y))
304
322
  throw new Error('bad point: is not on curve');
305
323
  return { x, y };
306
324
  }
307
325
  else {
308
- throw new Error(`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`);
326
+ throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);
309
327
  }
310
328
  }
311
- const toBytes = curveOpts.toBytes || pointToBytes;
312
- const fromBytes = curveOpts.fromBytes || pointFromBytes;
313
- const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
329
+ const encodePoint = extraOpts.toBytes || pointToBytes;
330
+ const decodePoint = extraOpts.fromBytes || pointFromBytes;
331
+ function weierstrassEquation(x) {
332
+ const x2 = Fp.sqr(x); // x * x
333
+ const x3 = Fp.mul(x2, x); // x² * x
334
+ return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); // x³ + a * x + b
335
+ }
314
336
  // TODO: move top-level
315
337
  /** Checks whether equation holds for given x, y: y² == x³ + ax + b */
316
338
  function isValidXY(x, y) {
@@ -373,7 +395,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
373
395
  // (0, 1, 0) aka ZERO is invalid in most contexts.
374
396
  // In BLS, ZERO can be serialized, so we allow it.
375
397
  // (0, 0, 0) is invalid representation of ZERO.
376
- if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y))
398
+ if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y))
377
399
  return;
378
400
  throw new Error('bad point: ZERO');
379
401
  }
@@ -406,6 +428,9 @@ function weierstrassN(CURVE, curveOpts = {}) {
406
428
  this.Z = acoord('z', Z);
407
429
  Object.freeze(this);
408
430
  }
431
+ static CURVE() {
432
+ return CURVE;
433
+ }
409
434
  /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
410
435
  static fromAffine(p) {
411
436
  const { x, y } = p || {};
@@ -418,45 +443,19 @@ function weierstrassN(CURVE, curveOpts = {}) {
418
443
  return Point.ZERO;
419
444
  return new Point(x, y, Fp.ONE);
420
445
  }
421
- get x() {
422
- return this.toAffine().x;
423
- }
424
- get y() {
425
- return this.toAffine().y;
426
- }
427
- // TODO: remove
428
- get px() {
429
- return this.X;
430
- }
431
- get py() {
432
- return this.X;
433
- }
434
- get pz() {
435
- return this.Z;
436
- }
437
- static normalizeZ(points) {
438
- return (0, curve_ts_1.normalizeZ)(Point, points);
439
- }
440
446
  static fromBytes(bytes) {
441
- (0, utils_ts_1.abytes)(bytes);
442
- return Point.fromHex(bytes);
443
- }
444
- /** Converts hash string or Uint8Array to Point. */
445
- static fromHex(hex) {
446
- const P = Point.fromAffine(fromBytes((0, utils_ts_1.ensureBytes)('pointHex', hex)));
447
+ const P = Point.fromAffine(decodePoint((0, utils_ts_1._abytes2)(bytes, undefined, 'point')));
447
448
  P.assertValidity();
448
449
  return P;
449
450
  }
450
- /** Multiplies generator point by privateKey. */
451
- static fromPrivateKey(privateKey) {
452
- return Point.BASE.multiply(_normFnElement(Fn, privateKey));
451
+ static fromHex(hex) {
452
+ return Point.fromBytes((0, utils_ts_1.ensureBytes)('pointHex', hex));
453
453
  }
454
- // TODO: remove
455
- static msm(points, scalars) {
456
- return (0, curve_ts_1.pippenger)(Point, Fn, points, scalars);
454
+ get x() {
455
+ return this.toAffine().x;
457
456
  }
458
- _setWindowSize(windowSize) {
459
- this.precompute(windowSize);
457
+ get y() {
458
+ return this.toAffine().y;
460
459
  }
461
460
  /**
462
461
  *
@@ -605,7 +604,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
605
604
  * @returns New point
606
605
  */
607
606
  multiply(scalar) {
608
- const { endo } = curveOpts;
607
+ const { endo } = extraOpts;
609
608
  if (!Fn.isValidNot0(scalar))
610
609
  throw new Error('invalid scalar: out of range'); // 0 is invalid
611
610
  let point, fake; // Fake point is used to const-time mult
@@ -632,7 +631,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
632
631
  * an exposed secret key e.g. sig verification, which works over *public* keys.
633
632
  */
634
633
  multiplyUnsafe(sc) {
635
- const { endo } = curveOpts;
634
+ const { endo } = extraOpts;
636
635
  const p = this;
637
636
  if (!Fn.isValid(sc))
638
637
  throw new Error('invalid scalar: out of range'); // 0 is valid
@@ -667,7 +666,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
667
666
  * Always torsion-free for cofactor=1 curves.
668
667
  */
669
668
  isTorsionFree() {
670
- const { isTorsionFree } = curveOpts;
669
+ const { isTorsionFree } = extraOpts;
671
670
  if (cofactor === _1n)
672
671
  return true;
673
672
  if (isTorsionFree)
@@ -675,7 +674,7 @@ function weierstrassN(CURVE, curveOpts = {}) {
675
674
  return wnaf.unsafe(this, CURVE_ORDER).is0();
676
675
  }
677
676
  clearCofactor() {
678
- const { clearCofactor } = curveOpts;
677
+ const { clearCofactor } = extraOpts;
679
678
  if (cofactor === _1n)
680
679
  return this; // Fast-path
681
680
  if (clearCofactor)
@@ -687,13 +686,9 @@ function weierstrassN(CURVE, curveOpts = {}) {
687
686
  return this.multiplyUnsafe(cofactor).is0();
688
687
  }
689
688
  toBytes(isCompressed = true) {
690
- (0, utils_ts_1.abool)('isCompressed', isCompressed);
689
+ (0, utils_ts_1._abool2)(isCompressed, 'isCompressed');
691
690
  this.assertValidity();
692
- return toBytes(Point, this, isCompressed);
693
- }
694
- /** @deprecated use `toBytes` */
695
- toRawBytes(isCompressed = true) {
696
- return this.toBytes(isCompressed);
691
+ return encodePoint(Point, this, isCompressed);
697
692
  }
698
693
  toHex(isCompressed = true) {
699
694
  return (0, utils_ts_1.bytesToHex)(this.toBytes(isCompressed));
@@ -701,26 +696,44 @@ function weierstrassN(CURVE, curveOpts = {}) {
701
696
  toString() {
702
697
  return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
703
698
  }
699
+ // TODO: remove
700
+ get px() {
701
+ return this.X;
702
+ }
703
+ get py() {
704
+ return this.X;
705
+ }
706
+ get pz() {
707
+ return this.Z;
708
+ }
709
+ toRawBytes(isCompressed = true) {
710
+ return this.toBytes(isCompressed);
711
+ }
712
+ _setWindowSize(windowSize) {
713
+ this.precompute(windowSize);
714
+ }
715
+ static normalizeZ(points) {
716
+ return (0, curve_ts_1.normalizeZ)(Point, points);
717
+ }
718
+ static msm(points, scalars) {
719
+ return (0, curve_ts_1.pippenger)(Point, Fn, points, scalars);
720
+ }
721
+ static fromPrivateKey(privateKey) {
722
+ return Point.BASE.multiply(_normFnElement(Fn, privateKey));
723
+ }
704
724
  }
705
725
  // base / generator point
706
726
  Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
707
727
  // zero / infinity / identity point
708
728
  Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
709
- // fields
729
+ // math field
710
730
  Point.Fp = Fp;
731
+ // scalar field
711
732
  Point.Fn = Fn;
712
733
  const bits = Fn.BITS;
713
- const wnaf = new curve_ts_1.wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
734
+ const wnaf = new curve_ts_1.wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
714
735
  return Point;
715
736
  }
716
- // _legacyWeierstrass
717
- // TODO: remove
718
- /** @deprecated use `weierstrass` in newer releases */
719
- function weierstrassPoints(c) {
720
- const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
721
- const Point = weierstrassN(CURVE, curveOpts);
722
- return _weierstrass_new_output_to_legacy(c, Point);
723
- }
724
737
  // Points start with byte 0x02 when y is even; otherwise 0x03
725
738
  function pprefix(hasEvenY) {
726
739
  return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
@@ -849,8 +862,122 @@ function mapToCurveSimpleSWU(Fp, opts) {
849
862
  return { x, y };
850
863
  };
851
864
  }
865
+ function getWLengths(Fp, Fn) {
866
+ return {
867
+ secret: Fn.BYTES,
868
+ public: 1 + Fp.BYTES,
869
+ publicUncompressed: 1 + 2 * Fp.BYTES,
870
+ publicKeyHasPrefix: true,
871
+ signature: 2 * Fn.BYTES,
872
+ };
873
+ }
874
+ /**
875
+ * Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
876
+ * This helper ensures no signature functionality is present. Less code, smaller bundle size.
877
+ */
878
+ function ecdh(Point, ecdhOpts = {}) {
879
+ const { Fn } = Point;
880
+ const randomBytes_ = ecdhOpts.randomBytes || utils_ts_1.randomBytes;
881
+ const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: (0, modular_ts_1.getMinHashLength)(Fn.ORDER) });
882
+ function isValidSecretKey(secretKey) {
883
+ try {
884
+ return !!_normFnElement(Fn, secretKey);
885
+ }
886
+ catch (error) {
887
+ return false;
888
+ }
889
+ }
890
+ function isValidPublicKey(publicKey, isCompressed) {
891
+ const { public: comp, publicUncompressed } = lengths;
892
+ try {
893
+ const l = publicKey.length;
894
+ if (isCompressed === true && l !== comp)
895
+ return false;
896
+ if (isCompressed === false && l !== publicUncompressed)
897
+ return false;
898
+ return !!Point.fromBytes(publicKey);
899
+ }
900
+ catch (error) {
901
+ return false;
902
+ }
903
+ }
904
+ /**
905
+ * Produces cryptographically secure secret key from random of size
906
+ * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
907
+ */
908
+ function randomSecretKey(seed = randomBytes_(lengths.seed)) {
909
+ return (0, modular_ts_1.mapHashToField)((0, utils_ts_1._abytes2)(seed, lengths.seed, 'seed'), Fn.ORDER);
910
+ }
911
+ /**
912
+ * Computes public key for a secret key. Checks for validity of the secret key.
913
+ * @param isCompressed whether to return compact (default), or full key
914
+ * @returns Public key, full when isCompressed=false; short when isCompressed=true
915
+ */
916
+ function getPublicKey(secretKey, isCompressed = true) {
917
+ return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
918
+ }
919
+ function keygen(seed) {
920
+ const secretKey = randomSecretKey(seed);
921
+ return { secretKey, publicKey: getPublicKey(secretKey) };
922
+ }
923
+ /**
924
+ * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
925
+ */
926
+ function isProbPub(item) {
927
+ if (typeof item === 'bigint')
928
+ return false;
929
+ if (item instanceof Point)
930
+ return true;
931
+ if (Fn.allowedLengths || lengths.secret === lengths.public)
932
+ return undefined;
933
+ const l = (0, utils_ts_1.ensureBytes)('key', item).length;
934
+ return l === lengths.public || l === lengths.publicUncompressed;
935
+ }
936
+ /**
937
+ * ECDH (Elliptic Curve Diffie Hellman).
938
+ * Computes shared public key from secret key A and public key B.
939
+ * Checks: 1) secret key validity 2) shared key is on-curve.
940
+ * Does NOT hash the result.
941
+ * @param isCompressed whether to return compact (default), or full key
942
+ * @returns shared public key
943
+ */
944
+ function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
945
+ if (isProbPub(secretKeyA) === true)
946
+ throw new Error('first arg must be private key');
947
+ if (isProbPub(publicKeyB) === false)
948
+ throw new Error('second arg must be public key');
949
+ const s = _normFnElement(Fn, secretKeyA);
950
+ const b = Point.fromHex(publicKeyB); // checks for being on-curve
951
+ return b.multiply(s).toBytes(isCompressed);
952
+ }
953
+ const utils = {
954
+ isValidSecretKey,
955
+ isValidPublicKey,
956
+ randomSecretKey,
957
+ // TODO: remove
958
+ isValidPrivateKey: isValidSecretKey,
959
+ randomPrivateKey: randomSecretKey,
960
+ normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
961
+ precompute(windowSize = 8, point = Point.BASE) {
962
+ return point.precompute(windowSize, false);
963
+ },
964
+ };
965
+ return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
966
+ }
852
967
  /**
853
- * Creates ECDSA for given elliptic curve Point and hash function.
968
+ * Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
969
+ * We need `hash` for 2 features:
970
+ * 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
971
+ * 2. k generation in `sign`, using HMAC-drbg(hash)
972
+ *
973
+ * ECDSAOpts are only rarely needed.
974
+ *
975
+ * @example
976
+ * ```js
977
+ * const p256_Point = weierstrass(...);
978
+ * const p256_sha256 = ecdsa(p256_Point, sha256);
979
+ * const p256_sha224 = ecdsa(p256_Point, sha224);
980
+ * ```
854
981
  */
855
982
  function ecdsa(Point, hash, ecdsaOpts = {}) {
856
983
  (0, utils_1.ahash)(hash);
@@ -866,14 +993,14 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
866
993
  ((key, ...msgs) => (0, hmac_js_1.hmac)(hash, key, (0, utils_ts_1.concatBytes)(...msgs)));
867
994
  const { Fp, Fn } = Point;
868
995
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
869
- const seedLen = (0, modular_ts_1.getMinHashLength)(CURVE_ORDER);
870
- const lengths = {
871
- secret: Fn.BYTES,
872
- public: 1 + Fp.BYTES,
873
- publicUncompressed: 1 + 2 * Fp.BYTES,
874
- signature: 2 * Fn.BYTES,
875
- seed: seedLen,
996
+ const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
997
+ const defaultSigOpts = {
998
+ prehash: false,
999
+ lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : false,
1000
+ format: undefined, //'compact' as ECDSASigFormat,
1001
+ extraEntropy: false,
876
1002
  };
1003
+ const defaultSigOpts_format = 'compact';
877
1004
  function isBiggerThanHalfOrder(number) {
878
1005
  const HALF = CURVE_ORDER >> _1n;
879
1006
  return number > HALF;
@@ -881,37 +1008,41 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
881
1008
  function normalizeS(s) {
882
1009
  return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
883
1010
  }
884
- function aValidRS(title, num) {
1011
+ function validateRS(title, num) {
885
1012
  if (!Fn.isValidNot0(num))
886
- throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
1013
+ throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
1014
+ return num;
887
1015
  }
888
1016
  /**
889
- * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
1017
+ * ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
890
1018
  */
891
1019
  class Signature {
892
1020
  constructor(r, s, recovery) {
893
- aValidRS('r', r); // r in [1..N-1]
894
- aValidRS('s', s); // s in [1..N-1]
895
- this.r = r;
896
- this.s = s;
1021
+ this.r = validateRS('r', r); // r in [1..N-1];
1022
+ this.s = validateRS('s', s); // s in [1..N-1];
897
1023
  if (recovery != null)
898
1024
  this.recovery = recovery;
899
1025
  Object.freeze(this);
900
1026
  }
901
- static fromBytes(bytes, format = 'compact') {
902
- if (format === 'compact') {
903
- const L = Fn.BYTES;
904
- (0, utils_ts_1.abytes)(bytes, L * 2);
905
- const r = bytes.subarray(0, L);
906
- const s = bytes.subarray(L, L * 2);
907
- return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
908
- }
1027
+ static fromBytes(bytes, format = defaultSigOpts_format) {
1028
+ validateSigFormat(format);
1029
+ const size = lengths.signature;
1030
+ let recid;
909
1031
  if (format === 'der') {
910
- (0, utils_ts_1.abytes)(bytes);
911
- const { r, s } = exports.DER.toSig(bytes);
1032
+ const { r, s } = exports.DER.toSig((0, utils_ts_1._abytes2)(bytes));
912
1033
  return new Signature(r, s);
913
1034
  }
914
- throw new Error('invalid format');
1035
+ if (format === 'recovered') {
1036
+ (0, utils_ts_1._abytes2)(bytes, size + 1);
1037
+ recid = bytes[0];
1038
+ format = 'compact';
1039
+ bytes = bytes.subarray(1);
1040
+ }
1041
+ (0, utils_ts_1._abytes2)(bytes, size);
1042
+ const L = size / 2;
1043
+ const r = bytes.subarray(0, L);
1044
+ const s = bytes.subarray(L, L * 2);
1045
+ return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
915
1046
  }
916
1047
  static fromHex(hex, format) {
917
1048
  return this.fromBytes((0, utils_ts_1.hexToBytes)(hex), format);
@@ -919,7 +1050,6 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
919
1050
  addRecoveryBit(recovery) {
920
1051
  return new Signature(this.r, this.s, recovery);
921
1052
  }
922
- // ProjPointType<bigint>
923
1053
  recoverPublicKey(msgHash) {
924
1054
  const FIELD_ORDER = Fp.ORDER;
925
1055
  const { r, s, recovery: rec } = this;
@@ -940,7 +1070,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
940
1070
  if (!Fp.isValid(radj))
941
1071
  throw new Error('recovery id 2 or 3 invalid');
942
1072
  const x = Fp.toBytes(radj);
943
- const R = Point.fromHex((0, utils_ts_1.concatBytes)(pprefix((rec & 1) === 0), x));
1073
+ const R = Point.fromBytes((0, utils_ts_1.concatBytes)(pprefix((rec & 1) === 0), x));
944
1074
  const ir = Fn.inv(radj); // r^-1
945
1075
  const h = bits2int_modN((0, utils_ts_1.ensureBytes)('msgHash', msgHash)); // Truncate hash
946
1076
  const u1 = Fn.create(-h * ir); // -hr^-1
@@ -956,15 +1086,18 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
956
1086
  hasHighS() {
957
1087
  return isBiggerThanHalfOrder(this.s);
958
1088
  }
959
- normalizeS() {
960
- return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
961
- }
962
- toBytes(format = 'compact') {
963
- if (format === 'compact')
964
- return (0, utils_ts_1.concatBytes)(Fn.toBytes(this.r), Fn.toBytes(this.s));
1089
+ toBytes(format = defaultSigOpts_format) {
1090
+ validateSigFormat(format);
965
1091
  if (format === 'der')
966
1092
  return (0, utils_ts_1.hexToBytes)(exports.DER.hexFromSig(this));
967
- throw new Error('invalid format');
1093
+ const r = Fn.toBytes(this.r);
1094
+ const s = Fn.toBytes(this.s);
1095
+ if (format === 'recovered') {
1096
+ if (this.recovery == null)
1097
+ throw new Error('recovery bit must be present');
1098
+ return (0, utils_ts_1.concatBytes)(Uint8Array.of(this.recovery), r, s);
1099
+ }
1100
+ return (0, utils_ts_1.concatBytes)(r, s);
968
1101
  }
969
1102
  toHex(format) {
970
1103
  return (0, utils_ts_1.bytesToHex)(this.toBytes(format));
@@ -977,6 +1110,9 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
977
1110
  static fromDER(hex) {
978
1111
  return Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', hex), 'der');
979
1112
  }
1113
+ normalizeS() {
1114
+ return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1115
+ }
980
1116
  toDERRawBytes() {
981
1117
  return this.toBytes('der');
982
1118
  }
@@ -990,86 +1126,6 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
990
1126
  return (0, utils_ts_1.bytesToHex)(this.toBytes('compact'));
991
1127
  }
992
1128
  }
993
- function isValidSecretKey(privateKey) {
994
- try {
995
- return !!_normFnElement(Fn, privateKey);
996
- }
997
- catch (error) {
998
- return false;
999
- }
1000
- }
1001
- function isValidPublicKey(publicKey, isCompressed) {
1002
- try {
1003
- const l = publicKey.length;
1004
- if (isCompressed === true && l !== lengths.public)
1005
- return false;
1006
- if (isCompressed === false && l !== lengths.publicUncompressed)
1007
- return false;
1008
- return !!Point.fromBytes(publicKey);
1009
- }
1010
- catch (error) {
1011
- return false;
1012
- }
1013
- }
1014
- /**
1015
- * Produces cryptographically secure secret key from random of size
1016
- * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1017
- */
1018
- function randomSecretKey(seed = randomBytes_(seedLen)) {
1019
- return (0, modular_ts_1.mapHashToField)(seed, CURVE_ORDER);
1020
- }
1021
- const utils = {
1022
- isValidSecretKey,
1023
- isValidPublicKey,
1024
- randomSecretKey,
1025
- // TODO: remove
1026
- isValidPrivateKey: isValidSecretKey,
1027
- randomPrivateKey: randomSecretKey,
1028
- normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
1029
- precompute(windowSize = 8, point = Point.BASE) {
1030
- return point.precompute(windowSize, false);
1031
- },
1032
- };
1033
- /**
1034
- * Computes public key for a secret key. Checks for validity of the secret key.
1035
- * @param isCompressed whether to return compact (default), or full key
1036
- * @returns Public key, full when isCompressed=false; short when isCompressed=true
1037
- */
1038
- function getPublicKey(secretKey, isCompressed = true) {
1039
- return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
1040
- }
1041
- /**
1042
- * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1043
- */
1044
- function isProbPub(item) {
1045
- // TODO: remove
1046
- if (typeof item === 'bigint')
1047
- return false;
1048
- // TODO: remove
1049
- if (item instanceof Point)
1050
- return true;
1051
- if (Fn.allowedLengths || lengths.secret === lengths.public)
1052
- return undefined;
1053
- const l = (0, utils_ts_1.ensureBytes)('key', item).length;
1054
- return l === lengths.public || l === lengths.publicUncompressed;
1055
- }
1056
- /**
1057
- * ECDH (Elliptic Curve Diffie Hellman).
1058
- * Computes shared public key from secret key A and public key B.
1059
- * Checks: 1) secret key validity 2) shared key is on-curve.
1060
- * Does NOT hash the result.
1061
- * @param isCompressed whether to return compact (default), or full key
1062
- * @returns shared public key
1063
- */
1064
- function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
1065
- if (isProbPub(secretKeyA) === true)
1066
- throw new Error('first arg must be private key');
1067
- if (isProbPub(publicKeyB) === false)
1068
- throw new Error('second arg must be public key');
1069
- const s = _normFnElement(Fn, secretKeyA);
1070
- const b = Point.fromHex(publicKeyB); // checks for being on-curve
1071
- return b.multiply(s).toBytes(isCompressed);
1072
- }
1073
1129
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
1074
1130
  // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
1075
1131
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
@@ -1099,31 +1155,33 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
1099
1155
  (0, utils_ts_1.aInRange)('num < 2^' + fnBits, num, _0n, ORDER_MASK);
1100
1156
  return Fn.toBytes(num);
1101
1157
  }
1102
- // Steps A, D of RFC6979 3.2
1103
- // Creates RFC6979 seed; converts msg/privKey to numbers.
1104
- // Used only in sign, not in verify.
1105
- // NOTE: we cannot assume here that msgHash has same amount of bytes as curve order,
1106
- // this will be invalid at least for P521. Also it can be bigger for P224 + SHA256
1107
- function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
1158
+ /**
1159
+ * Steps A, D of RFC6979 3.2.
1160
+ * Creates RFC6979 seed; converts msg/privKey to numbers.
1161
+ * Used only in sign, not in verify.
1162
+ *
1163
+ * Warning: we cannot assume here that message has same amount of bytes as curve order,
1164
+ * this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
1165
+ */
1166
+ function prepSig(message, privateKey, opts) {
1108
1167
  if (['recovered', 'canonical'].some((k) => k in opts))
1109
1168
  throw new Error('sign() legacy options not supported');
1110
- let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
1111
- if (lowS == null)
1112
- lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1113
- msgHash = (0, utils_ts_1.ensureBytes)('msgHash', msgHash);
1114
- validateSigVerOpts(opts);
1169
+ const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
1170
+ // RFC6979 3.2: we skip step A, because we already provide hash
1171
+ message = (0, utils_ts_1._abytes2)(message, undefined, 'message');
1115
1172
  if (prehash)
1116
- msgHash = (0, utils_ts_1.ensureBytes)('prehashed msgHash', hash(msgHash));
1173
+ message = (0, utils_ts_1._abytes2)(hash(message), undefined, 'prehashed message');
1117
1174
  // We can't later call bits2octets, since nested bits2int is broken for curves
1118
1175
  // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1119
1176
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1120
- const h1int = bits2int_modN(msgHash);
1177
+ const h1int = bits2int_modN(message);
1121
1178
  const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
1122
1179
  const seedArgs = [int2octets(d), int2octets(h1int)];
1123
1180
  // extraEntropy. RFC6979 3.6: additional k' (optional).
1124
- if (ent != null && ent !== false) {
1181
+ if (extraEntropy != null && extraEntropy !== false) {
1125
1182
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
1126
- const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
1183
+ // gen random bytes OR pass as-is
1184
+ const e = extraEntropy === true ? randomBytes_(lengths.secret) : extraEntropy;
1127
1185
  seedArgs.push((0, utils_ts_1.ensureBytes)('extraEntropy', e)); // check for being bytes
1128
1186
  }
1129
1187
  const seed = (0, utils_ts_1.concatBytes)(...seedArgs); // Step D of RFC6979 3.2
@@ -1139,7 +1197,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
1139
1197
  function k2sig(kBytes) {
1140
1198
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
1141
1199
  // Important: all mod() calls here must be done over N
1142
- const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
1200
+ const k = bits2int(kBytes); // mod n, not mod p
1143
1201
  if (!Fn.isValidNot0(k))
1144
1202
  return; // Valid scalars (including k) must be in 1..N-1
1145
1203
  const ik = Fn.inv(k); // k^-1 mod n
@@ -1160,49 +1218,48 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
1160
1218
  }
1161
1219
  return { seed, k2sig };
1162
1220
  }
1163
- const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1164
- const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1165
1221
  /**
1166
1222
  * Signs message hash with a secret key.
1223
+ *
1167
1224
  * ```
1168
- * sign(m, d, k) where
1225
+ * sign(m, d) where
1226
+ * k = rfc6979_hmac_drbg(m, d)
1169
1227
  * (x, y) = G × k
1170
1228
  * r = x mod n
1171
- * s = (m + dr)/k mod n
1229
+ * s = (m + dr) / k mod n
1172
1230
  * ```
1173
1231
  */
1174
- function sign(msgHash, secretKey, opts = defaultSigOpts) {
1175
- const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
1232
+ function sign(message, secretKey, opts = {}) {
1233
+ message = (0, utils_ts_1.ensureBytes)('message', message);
1234
+ const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
1176
1235
  const drbg = (0, utils_ts_1.createHmacDrbg)(hash.outputLen, Fn.BYTES, hmac_);
1177
- return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1236
+ const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
1237
+ return sig;
1178
1238
  }
1179
1239
  // Enable precomputes. Slows down first publicKey computation by 20ms.
1180
1240
  Point.BASE.precompute(8);
1181
1241
  /**
1182
- * Verifies a signature against message hash and public key.
1183
- * Rejects lowS signatures by default: to override,
1184
- * specify option `{lowS: false}`. Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
1242
+ * Verifies a signature against message and public key.
1243
+ * Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.
1244
+ * Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
1185
1245
  *
1186
1246
  * ```
1187
1247
  * verify(r, s, h, P) where
1188
- * U1 = hs^-1 mod n
1189
- * U2 = rs^-1 mod n
1190
- * R = U1⋅G - U2⋅P
1248
+ * u1 = hs^-1 mod n
1249
+ * u2 = rs^-1 mod n
1250
+ * R = u1⋅G + u2⋅P
1191
1251
  * mod(R.x, n) == r
1192
1252
  * ```
1193
1253
  */
1194
- function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
1254
+ function verify(signature, message, publicKey, opts = {}) {
1195
1255
  const sg = signature;
1196
- msgHash = (0, utils_ts_1.ensureBytes)('msgHash', msgHash);
1256
+ message = (0, utils_ts_1.ensureBytes)('msgHash', message);
1197
1257
  publicKey = (0, utils_ts_1.ensureBytes)('publicKey', publicKey);
1198
- // Verify opts
1199
- validateSigVerOpts(opts);
1200
- const { lowS, prehash, format } = opts;
1201
- // TODO: remove
1202
- if ('strict' in opts)
1203
- throw new Error('options.strict was renamed to lowS');
1258
+ const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
1204
1259
  let _sig = undefined;
1205
1260
  let P;
1261
+ if ('strict' in opts)
1262
+ throw new Error('options.strict was renamed to lowS');
1206
1263
  if (format === undefined) {
1207
1264
  // Try to deduce format
1208
1265
  const isHex = typeof sg === 'string' || (0, utils_ts_1.isBytes)(sg);
@@ -1243,11 +1300,6 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
1243
1300
  throw new Error('"der" / "compact" format expects Uint8Array signature');
1244
1301
  _sig = Signature.fromBytes((0, utils_ts_1.ensureBytes)('sig', sg), format);
1245
1302
  }
1246
- else if (format === 'js') {
1247
- if (!(sg instanceof Signature))
1248
- throw new Error('"js" format expects Signature instance');
1249
- _sig = sg;
1250
- }
1251
1303
  else {
1252
1304
  throw new Error('format must be "compact", "der" or "js"');
1253
1305
  }
@@ -1260,13 +1312,13 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
1260
1312
  return false;
1261
1313
  // todo: optional.hash => hash
1262
1314
  if (prehash)
1263
- msgHash = hash(msgHash);
1315
+ message = hash(message);
1264
1316
  const { r, s } = _sig;
1265
- const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1266
- const is = Fn.inv(s); // s^-1
1317
+ const h = bits2int_modN(message); // mod n, not mod p
1318
+ const is = Fn.inv(s); // s^-1 mod n
1267
1319
  const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1268
1320
  const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1269
- const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1321
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); // u1⋅G + u2⋅P
1270
1322
  if (R.is0())
1271
1323
  return false;
1272
1324
  const v = Fn.create(R.x); // v = r.x mod n
@@ -1276,23 +1328,34 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
1276
1328
  return false;
1277
1329
  }
1278
1330
  }
1279
- function keygen(seed) {
1280
- const secretKey = utils.randomSecretKey(seed);
1281
- return { secretKey, publicKey: getPublicKey(secretKey) };
1331
+ function recoverPublicKey(signature, message, opts = {}) {
1332
+ const prehash = opts.prehash !== undefined ? opts.prehash : defaultSigOpts.prehash;
1333
+ (0, utils_ts_1._abool2)(prehash, 'prehash');
1334
+ message = (0, utils_ts_1._abytes2)(message, undefined, 'message');
1335
+ if (prehash)
1336
+ message = (0, utils_ts_1._abytes2)(hash(message), undefined, 'prehashed message');
1337
+ return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
1282
1338
  }
1283
1339
  return Object.freeze({
1284
1340
  keygen,
1285
1341
  getPublicKey,
1286
- sign,
1287
- verify,
1288
1342
  getSharedSecret,
1289
1343
  utils,
1344
+ lengths,
1290
1345
  Point,
1346
+ sign,
1347
+ verify,
1348
+ recoverPublicKey,
1291
1349
  Signature,
1292
- info: { type: 'weierstrass', lengths, publicKeyHasPrefix: true },
1350
+ hash,
1293
1351
  });
1294
1352
  }
1295
- // TODO: remove
1353
+ /** @deprecated use `weierstrass` in newer releases */
1354
+ function weierstrassPoints(c) {
1355
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1356
+ const Point = weierstrassN(CURVE, curveOpts);
1357
+ return _weierstrass_new_output_to_legacy(c, Point);
1358
+ }
1296
1359
  function _weierstrass_legacy_opts_to_new(c) {
1297
1360
  const CURVE = {
1298
1361
  a: c.a,
@@ -1310,7 +1373,7 @@ function _weierstrass_legacy_opts_to_new(c) {
1310
1373
  const Fn = (0, modular_ts_1.Field)(CURVE.n, {
1311
1374
  BITS: c.nBitLength,
1312
1375
  allowedLengths: allowedLengths,
1313
- modOnDecode: c.wrapPrivateKey,
1376
+ modFromBytes: c.wrapPrivateKey,
1314
1377
  });
1315
1378
  const curveOpts = {
1316
1379
  Fp,
@@ -1335,10 +1398,20 @@ function _ecdsa_legacy_opts_to_new(c) {
1335
1398
  };
1336
1399
  return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
1337
1400
  }
1338
- // TODO: remove
1401
+ function _legacyHelperEquat(Fp, a, b) {
1402
+ /**
1403
+ * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
1404
+ * @returns y²
1405
+ */
1406
+ function weierstrassEquation(x) {
1407
+ const x2 = Fp.sqr(x); // x * x
1408
+ const x3 = Fp.mul(x2, x); // x² * x
1409
+ return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
1410
+ }
1411
+ return weierstrassEquation;
1412
+ }
1339
1413
  function _weierstrass_new_output_to_legacy(c, Point) {
1340
1414
  const { Fp, Fn } = Point;
1341
- // TODO: remove
1342
1415
  function isWithinCurveOrder(num) {
1343
1416
  return (0, utils_ts_1.inRange)(num, _1n, Fn.ORDER);
1344
1417
  }
@@ -1352,11 +1425,11 @@ function _weierstrass_new_output_to_legacy(c, Point) {
1352
1425
  isWithinCurveOrder,
1353
1426
  });
1354
1427
  }
1355
- // TODO: remove
1356
- function _ecdsa_new_output_to_legacy(c, ecdsa) {
1357
- return Object.assign({}, ecdsa, {
1358
- ProjectivePoint: ecdsa.Point,
1359
- CURVE: c,
1428
+ function _ecdsa_new_output_to_legacy(c, _ecdsa) {
1429
+ const Point = _ecdsa.Point;
1430
+ return Object.assign({}, _ecdsa, {
1431
+ ProjectivePoint: Point,
1432
+ CURVE: Object.assign({}, c, (0, modular_ts_1.nLength)(Point.Fn.ORDER, Point.Fn.BITS)),
1360
1433
  });
1361
1434
  }
1362
1435
  // _ecdsa_legacy