@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
@@ -25,11 +25,11 @@
25
25
  * @module
26
26
  */
27
27
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
28
- import { hmac } from '@noble/hashes/hmac.js';
28
+ import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
29
29
  import { ahash } from '@noble/hashes/utils';
30
- import { _validateObject, abool, abytes, aInRange, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes, inRange, isBytes, memoized, numberToHexUnpadded, randomBytes, } from "../utils.js";
30
+ import { _validateObject, _abool2 as abool, _abytes2 as abytes, aInRange, bitLen, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes, inRange, isBytes, memoized, numberToHexUnpadded, randomBytes as wcRandomBytes, } from "../utils.js";
31
31
  import { _createCurveFields, mulEndoUnsafe, negateCt, normalizeZ, pippenger, wNAF, } from "./curve.js";
32
- import { Field, FpInvertBatch, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
32
+ import { Field, FpInvertBatch, getMinHashLength, mapHashToField, nLength, validateField, } from "./modular.js";
33
33
  // We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
34
34
  const divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n) / den;
35
35
  /**
@@ -60,11 +60,22 @@ export function _splitEndoScalar(k, basis, n) {
60
60
  }
61
61
  return { k1neg, k1, k2neg, k2 };
62
62
  }
63
- function validateSigVerOpts(opts) {
64
- if (opts.lowS !== undefined)
65
- abool('lowS', opts.lowS);
66
- if (opts.prehash !== undefined)
67
- abool('prehash', opts.prehash);
63
+ function validateSigFormat(format) {
64
+ if (!['compact', 'recovered', 'der'].includes(format))
65
+ throw new Error('Signature format must be "compact", "recovered", or "der"');
66
+ return format;
67
+ }
68
+ function validateSigOpts(opts, def) {
69
+ const optsn = {};
70
+ for (let optName of Object.keys(def)) {
71
+ // @ts-ignore
72
+ optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
73
+ }
74
+ abool(optsn.lowS, 'lowS');
75
+ abool(optsn.prehash, 'prehash');
76
+ if (optsn.format !== undefined)
77
+ validateSigFormat(optsn.format);
78
+ return optsn;
68
79
  }
69
80
  export class DERErr extends Error {
70
81
  constructor(m = '') {
@@ -185,19 +196,6 @@ export const DER = {
185
196
  // Be friendly to bad ECMAScript parsers by not using bigint literals
186
197
  // prettier-ignore
187
198
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
188
- // TODO: remove
189
- export function _legacyHelperEquat(Fp, a, b) {
190
- /**
191
- * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
192
- * @returns y²
193
- */
194
- function weierstrassEquation(x) {
195
- const x2 = Fp.sqr(x); // x * x
196
- const x3 = Fp.mul(x2, x); // x² * x
197
- return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
198
- }
199
- return weierstrassEquation;
200
- }
201
199
  export function _normFnElement(Fn, key) {
202
200
  const { BYTES: expected } = Fn;
203
201
  let num;
@@ -217,10 +215,29 @@ export function _normFnElement(Fn, key) {
217
215
  throw new Error('invalid private key: out of range [1..N-1]');
218
216
  return num;
219
217
  }
220
- export function weierstrassN(CURVE, curveOpts = {}) {
221
- const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
218
+ /**
219
+ * Creates weierstrass Point constructor, based on specified curve options.
220
+ *
221
+ * @example
222
+ ```js
223
+ const opts = {
224
+ p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
225
+ n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
226
+ h: BigInt(1),
227
+ a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
228
+ b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
229
+ Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
230
+ Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
231
+ };
232
+ const p256_Point = weierstrass(opts);
233
+ ```
234
+ */
235
+ export function weierstrassN(params, extraOpts = {}) {
236
+ const validated = _createCurveFields('weierstrass', params, extraOpts);
237
+ const { Fp, Fn } = validated;
238
+ let CURVE = validated.CURVE;
222
239
  const { h: cofactor, n: CURVE_ORDER } = CURVE;
223
- _validateObject(curveOpts, {}, {
240
+ _validateObject(extraOpts, {}, {
224
241
  allowInfinityPoint: 'boolean',
225
242
  clearCofactor: 'function',
226
243
  isTorsionFree: 'function',
@@ -229,13 +246,14 @@ export function weierstrassN(CURVE, curveOpts = {}) {
229
246
  endo: 'object',
230
247
  wrapPrivateKey: 'boolean',
231
248
  });
232
- const { endo } = curveOpts;
249
+ const { endo } = extraOpts;
233
250
  if (endo) {
234
251
  // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
235
252
  if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
236
253
  throw new Error('invalid endo: expected "beta": bigint and "basises": array');
237
254
  }
238
255
  }
256
+ const lengths = getWLengths(Fp, Fn);
239
257
  function assertCompressionIsSupported() {
240
258
  if (!Fp.isOdd)
241
259
  throw new Error('compression is not supported: Field does not have .isOdd()');
@@ -244,7 +262,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
244
262
  function pointToBytes(_c, point, isCompressed) {
245
263
  const { x, y } = point.toAffine();
246
264
  const bx = Fp.toBytes(x);
247
- abool('isCompressed', isCompressed);
265
+ abool(isCompressed, 'isCompressed');
248
266
  if (isCompressed) {
249
267
  assertCompressionIsSupported();
250
268
  const hasEvenY = !Fp.isOdd(y);
@@ -255,15 +273,13 @@ export function weierstrassN(CURVE, curveOpts = {}) {
255
273
  }
256
274
  }
257
275
  function pointFromBytes(bytes) {
258
- abytes(bytes);
259
- const L = Fp.BYTES;
260
- const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
261
- const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
276
+ abytes(bytes, undefined, 'Point');
277
+ const { public: comp, publicUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
262
278
  const length = bytes.length;
263
279
  const head = bytes[0];
264
280
  const tail = bytes.subarray(1);
265
281
  // No actual validation is done here: use .assertValidity()
266
- if (length === LC && (head === 0x02 || head === 0x03)) {
282
+ if (length === comp && (head === 0x02 || head === 0x03)) {
267
283
  const x = Fp.fromBytes(tail);
268
284
  if (!Fp.isValid(x))
269
285
  throw new Error('bad point: is not on curve, wrong x');
@@ -283,21 +299,26 @@ export function weierstrassN(CURVE, curveOpts = {}) {
283
299
  y = Fp.neg(y);
284
300
  return { x, y };
285
301
  }
286
- else if (length === LU && head === 0x04) {
302
+ else if (length === uncomp && head === 0x04) {
287
303
  // TODO: more checks
288
- const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
289
- const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
304
+ const L = Fp.BYTES;
305
+ const x = Fp.fromBytes(tail.subarray(0, L));
306
+ const y = Fp.fromBytes(tail.subarray(L, L * 2));
290
307
  if (!isValidXY(x, y))
291
308
  throw new Error('bad point: is not on curve');
292
309
  return { x, y };
293
310
  }
294
311
  else {
295
- throw new Error(`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`);
312
+ throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);
296
313
  }
297
314
  }
298
- const toBytes = curveOpts.toBytes || pointToBytes;
299
- const fromBytes = curveOpts.fromBytes || pointFromBytes;
300
- const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
315
+ const encodePoint = extraOpts.toBytes || pointToBytes;
316
+ const decodePoint = extraOpts.fromBytes || pointFromBytes;
317
+ function weierstrassEquation(x) {
318
+ const x2 = Fp.sqr(x); // x * x
319
+ const x3 = Fp.mul(x2, x); // x² * x
320
+ return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); // x³ + a * x + b
321
+ }
301
322
  // TODO: move top-level
302
323
  /** Checks whether equation holds for given x, y: y² == x³ + ax + b */
303
324
  function isValidXY(x, y) {
@@ -360,7 +381,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
360
381
  // (0, 1, 0) aka ZERO is invalid in most contexts.
361
382
  // In BLS, ZERO can be serialized, so we allow it.
362
383
  // (0, 0, 0) is invalid representation of ZERO.
363
- if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y))
384
+ if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y))
364
385
  return;
365
386
  throw new Error('bad point: ZERO');
366
387
  }
@@ -393,6 +414,9 @@ export function weierstrassN(CURVE, curveOpts = {}) {
393
414
  this.Z = acoord('z', Z);
394
415
  Object.freeze(this);
395
416
  }
417
+ static CURVE() {
418
+ return CURVE;
419
+ }
396
420
  /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
397
421
  static fromAffine(p) {
398
422
  const { x, y } = p || {};
@@ -405,45 +429,19 @@ export function weierstrassN(CURVE, curveOpts = {}) {
405
429
  return Point.ZERO;
406
430
  return new Point(x, y, Fp.ONE);
407
431
  }
408
- get x() {
409
- return this.toAffine().x;
410
- }
411
- get y() {
412
- return this.toAffine().y;
413
- }
414
- // TODO: remove
415
- get px() {
416
- return this.X;
417
- }
418
- get py() {
419
- return this.X;
420
- }
421
- get pz() {
422
- return this.Z;
423
- }
424
- static normalizeZ(points) {
425
- return normalizeZ(Point, points);
426
- }
427
432
  static fromBytes(bytes) {
428
- abytes(bytes);
429
- return Point.fromHex(bytes);
430
- }
431
- /** Converts hash string or Uint8Array to Point. */
432
- static fromHex(hex) {
433
- const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
433
+ const P = Point.fromAffine(decodePoint(abytes(bytes, undefined, 'point')));
434
434
  P.assertValidity();
435
435
  return P;
436
436
  }
437
- /** Multiplies generator point by privateKey. */
438
- static fromPrivateKey(privateKey) {
439
- return Point.BASE.multiply(_normFnElement(Fn, privateKey));
437
+ static fromHex(hex) {
438
+ return Point.fromBytes(ensureBytes('pointHex', hex));
440
439
  }
441
- // TODO: remove
442
- static msm(points, scalars) {
443
- return pippenger(Point, Fn, points, scalars);
440
+ get x() {
441
+ return this.toAffine().x;
444
442
  }
445
- _setWindowSize(windowSize) {
446
- this.precompute(windowSize);
443
+ get y() {
444
+ return this.toAffine().y;
447
445
  }
448
446
  /**
449
447
  *
@@ -592,7 +590,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
592
590
  * @returns New point
593
591
  */
594
592
  multiply(scalar) {
595
- const { endo } = curveOpts;
593
+ const { endo } = extraOpts;
596
594
  if (!Fn.isValidNot0(scalar))
597
595
  throw new Error('invalid scalar: out of range'); // 0 is invalid
598
596
  let point, fake; // Fake point is used to const-time mult
@@ -619,7 +617,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
619
617
  * an exposed secret key e.g. sig verification, which works over *public* keys.
620
618
  */
621
619
  multiplyUnsafe(sc) {
622
- const { endo } = curveOpts;
620
+ const { endo } = extraOpts;
623
621
  const p = this;
624
622
  if (!Fn.isValid(sc))
625
623
  throw new Error('invalid scalar: out of range'); // 0 is valid
@@ -654,7 +652,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
654
652
  * Always torsion-free for cofactor=1 curves.
655
653
  */
656
654
  isTorsionFree() {
657
- const { isTorsionFree } = curveOpts;
655
+ const { isTorsionFree } = extraOpts;
658
656
  if (cofactor === _1n)
659
657
  return true;
660
658
  if (isTorsionFree)
@@ -662,7 +660,7 @@ export function weierstrassN(CURVE, curveOpts = {}) {
662
660
  return wnaf.unsafe(this, CURVE_ORDER).is0();
663
661
  }
664
662
  clearCofactor() {
665
- const { clearCofactor } = curveOpts;
663
+ const { clearCofactor } = extraOpts;
666
664
  if (cofactor === _1n)
667
665
  return this; // Fast-path
668
666
  if (clearCofactor)
@@ -674,13 +672,9 @@ export function weierstrassN(CURVE, curveOpts = {}) {
674
672
  return this.multiplyUnsafe(cofactor).is0();
675
673
  }
676
674
  toBytes(isCompressed = true) {
677
- abool('isCompressed', isCompressed);
675
+ abool(isCompressed, 'isCompressed');
678
676
  this.assertValidity();
679
- return toBytes(Point, this, isCompressed);
680
- }
681
- /** @deprecated use `toBytes` */
682
- toRawBytes(isCompressed = true) {
683
- return this.toBytes(isCompressed);
677
+ return encodePoint(Point, this, isCompressed);
684
678
  }
685
679
  toHex(isCompressed = true) {
686
680
  return bytesToHex(this.toBytes(isCompressed));
@@ -688,26 +682,44 @@ export function weierstrassN(CURVE, curveOpts = {}) {
688
682
  toString() {
689
683
  return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
690
684
  }
685
+ // TODO: remove
686
+ get px() {
687
+ return this.X;
688
+ }
689
+ get py() {
690
+ return this.X;
691
+ }
692
+ get pz() {
693
+ return this.Z;
694
+ }
695
+ toRawBytes(isCompressed = true) {
696
+ return this.toBytes(isCompressed);
697
+ }
698
+ _setWindowSize(windowSize) {
699
+ this.precompute(windowSize);
700
+ }
701
+ static normalizeZ(points) {
702
+ return normalizeZ(Point, points);
703
+ }
704
+ static msm(points, scalars) {
705
+ return pippenger(Point, Fn, points, scalars);
706
+ }
707
+ static fromPrivateKey(privateKey) {
708
+ return Point.BASE.multiply(_normFnElement(Fn, privateKey));
709
+ }
691
710
  }
692
711
  // base / generator point
693
712
  Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
694
713
  // zero / infinity / identity point
695
714
  Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
696
- // fields
715
+ // math field
697
716
  Point.Fp = Fp;
717
+ // scalar field
698
718
  Point.Fn = Fn;
699
719
  const bits = Fn.BITS;
700
- const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
720
+ const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
701
721
  return Point;
702
722
  }
703
- // _legacyWeierstrass
704
- // TODO: remove
705
- /** @deprecated use `weierstrass` in newer releases */
706
- export function weierstrassPoints(c) {
707
- const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
708
- const Point = weierstrassN(CURVE, curveOpts);
709
- return _weierstrass_new_output_to_legacy(c, Point);
710
- }
711
723
  // Points start with byte 0x02 when y is even; otherwise 0x03
712
724
  function pprefix(hasEvenY) {
713
725
  return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
@@ -836,8 +848,122 @@ export function mapToCurveSimpleSWU(Fp, opts) {
836
848
  return { x, y };
837
849
  };
838
850
  }
851
+ function getWLengths(Fp, Fn) {
852
+ return {
853
+ secret: Fn.BYTES,
854
+ public: 1 + Fp.BYTES,
855
+ publicUncompressed: 1 + 2 * Fp.BYTES,
856
+ publicKeyHasPrefix: true,
857
+ signature: 2 * Fn.BYTES,
858
+ };
859
+ }
860
+ /**
861
+ * Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
862
+ * This helper ensures no signature functionality is present. Less code, smaller bundle size.
863
+ */
864
+ export function ecdh(Point, ecdhOpts = {}) {
865
+ const { Fn } = Point;
866
+ const randomBytes_ = ecdhOpts.randomBytes || wcRandomBytes;
867
+ const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
868
+ function isValidSecretKey(secretKey) {
869
+ try {
870
+ return !!_normFnElement(Fn, secretKey);
871
+ }
872
+ catch (error) {
873
+ return false;
874
+ }
875
+ }
876
+ function isValidPublicKey(publicKey, isCompressed) {
877
+ const { public: comp, publicUncompressed } = lengths;
878
+ try {
879
+ const l = publicKey.length;
880
+ if (isCompressed === true && l !== comp)
881
+ return false;
882
+ if (isCompressed === false && l !== publicUncompressed)
883
+ return false;
884
+ return !!Point.fromBytes(publicKey);
885
+ }
886
+ catch (error) {
887
+ return false;
888
+ }
889
+ }
890
+ /**
891
+ * Produces cryptographically secure secret key from random of size
892
+ * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
893
+ */
894
+ function randomSecretKey(seed = randomBytes_(lengths.seed)) {
895
+ return mapHashToField(abytes(seed, lengths.seed, 'seed'), Fn.ORDER);
896
+ }
897
+ /**
898
+ * Computes public key for a secret key. Checks for validity of the secret key.
899
+ * @param isCompressed whether to return compact (default), or full key
900
+ * @returns Public key, full when isCompressed=false; short when isCompressed=true
901
+ */
902
+ function getPublicKey(secretKey, isCompressed = true) {
903
+ return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
904
+ }
905
+ function keygen(seed) {
906
+ const secretKey = randomSecretKey(seed);
907
+ return { secretKey, publicKey: getPublicKey(secretKey) };
908
+ }
909
+ /**
910
+ * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
911
+ */
912
+ function isProbPub(item) {
913
+ if (typeof item === 'bigint')
914
+ return false;
915
+ if (item instanceof Point)
916
+ return true;
917
+ if (Fn.allowedLengths || lengths.secret === lengths.public)
918
+ return undefined;
919
+ const l = ensureBytes('key', item).length;
920
+ return l === lengths.public || l === lengths.publicUncompressed;
921
+ }
922
+ /**
923
+ * ECDH (Elliptic Curve Diffie Hellman).
924
+ * Computes shared public key from secret key A and public key B.
925
+ * Checks: 1) secret key validity 2) shared key is on-curve.
926
+ * Does NOT hash the result.
927
+ * @param isCompressed whether to return compact (default), or full key
928
+ * @returns shared public key
929
+ */
930
+ function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
931
+ if (isProbPub(secretKeyA) === true)
932
+ throw new Error('first arg must be private key');
933
+ if (isProbPub(publicKeyB) === false)
934
+ throw new Error('second arg must be public key');
935
+ const s = _normFnElement(Fn, secretKeyA);
936
+ const b = Point.fromHex(publicKeyB); // checks for being on-curve
937
+ return b.multiply(s).toBytes(isCompressed);
938
+ }
939
+ const utils = {
940
+ isValidSecretKey,
941
+ isValidPublicKey,
942
+ randomSecretKey,
943
+ // TODO: remove
944
+ isValidPrivateKey: isValidSecretKey,
945
+ randomPrivateKey: randomSecretKey,
946
+ normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
947
+ precompute(windowSize = 8, point = Point.BASE) {
948
+ return point.precompute(windowSize, false);
949
+ },
950
+ };
951
+ return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
952
+ }
839
953
  /**
840
- * Creates ECDSA for given elliptic curve Point and hash function.
954
+ * Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
955
+ * We need `hash` for 2 features:
956
+ * 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
957
+ * 2. k generation in `sign`, using HMAC-drbg(hash)
958
+ *
959
+ * ECDSAOpts are only rarely needed.
960
+ *
961
+ * @example
962
+ * ```js
963
+ * const p256_Point = weierstrass(...);
964
+ * const p256_sha256 = ecdsa(p256_Point, sha256);
965
+ * const p256_sha224 = ecdsa(p256_Point, sha224);
966
+ * ```
841
967
  */
842
968
  export function ecdsa(Point, hash, ecdsaOpts = {}) {
843
969
  ahash(hash);
@@ -848,19 +974,19 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
848
974
  bits2int: 'function',
849
975
  bits2int_modN: 'function',
850
976
  });
851
- const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
977
+ const randomBytes_ = ecdsaOpts.randomBytes || wcRandomBytes;
852
978
  const hmac_ = ecdsaOpts.hmac ||
853
- ((key, ...msgs) => hmac(hash, key, concatBytes(...msgs)));
979
+ ((key, ...msgs) => nobleHmac(hash, key, concatBytes(...msgs)));
854
980
  const { Fp, Fn } = Point;
855
981
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
856
- const seedLen = getMinHashLength(CURVE_ORDER);
857
- const lengths = {
858
- secret: Fn.BYTES,
859
- public: 1 + Fp.BYTES,
860
- publicUncompressed: 1 + 2 * Fp.BYTES,
861
- signature: 2 * Fn.BYTES,
862
- seed: seedLen,
982
+ const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
983
+ const defaultSigOpts = {
984
+ prehash: false,
985
+ lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : false,
986
+ format: undefined, //'compact' as ECDSASigFormat,
987
+ extraEntropy: false,
863
988
  };
989
+ const defaultSigOpts_format = 'compact';
864
990
  function isBiggerThanHalfOrder(number) {
865
991
  const HALF = CURVE_ORDER >> _1n;
866
992
  return number > HALF;
@@ -868,37 +994,41 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
868
994
  function normalizeS(s) {
869
995
  return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
870
996
  }
871
- function aValidRS(title, num) {
997
+ function validateRS(title, num) {
872
998
  if (!Fn.isValidNot0(num))
873
- throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
999
+ throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
1000
+ return num;
874
1001
  }
875
1002
  /**
876
- * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
1003
+ * ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
877
1004
  */
878
1005
  class Signature {
879
1006
  constructor(r, s, recovery) {
880
- aValidRS('r', r); // r in [1..N-1]
881
- aValidRS('s', s); // s in [1..N-1]
882
- this.r = r;
883
- this.s = s;
1007
+ this.r = validateRS('r', r); // r in [1..N-1];
1008
+ this.s = validateRS('s', s); // s in [1..N-1];
884
1009
  if (recovery != null)
885
1010
  this.recovery = recovery;
886
1011
  Object.freeze(this);
887
1012
  }
888
- static fromBytes(bytes, format = 'compact') {
889
- if (format === 'compact') {
890
- const L = Fn.BYTES;
891
- abytes(bytes, L * 2);
892
- const r = bytes.subarray(0, L);
893
- const s = bytes.subarray(L, L * 2);
894
- return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
895
- }
1013
+ static fromBytes(bytes, format = defaultSigOpts_format) {
1014
+ validateSigFormat(format);
1015
+ const size = lengths.signature;
1016
+ let recid;
896
1017
  if (format === 'der') {
897
- abytes(bytes);
898
- const { r, s } = DER.toSig(bytes);
1018
+ const { r, s } = DER.toSig(abytes(bytes));
899
1019
  return new Signature(r, s);
900
1020
  }
901
- throw new Error('invalid format');
1021
+ if (format === 'recovered') {
1022
+ abytes(bytes, size + 1);
1023
+ recid = bytes[0];
1024
+ format = 'compact';
1025
+ bytes = bytes.subarray(1);
1026
+ }
1027
+ abytes(bytes, size);
1028
+ const L = size / 2;
1029
+ const r = bytes.subarray(0, L);
1030
+ const s = bytes.subarray(L, L * 2);
1031
+ return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
902
1032
  }
903
1033
  static fromHex(hex, format) {
904
1034
  return this.fromBytes(hexToBytes(hex), format);
@@ -906,7 +1036,6 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
906
1036
  addRecoveryBit(recovery) {
907
1037
  return new Signature(this.r, this.s, recovery);
908
1038
  }
909
- // ProjPointType<bigint>
910
1039
  recoverPublicKey(msgHash) {
911
1040
  const FIELD_ORDER = Fp.ORDER;
912
1041
  const { r, s, recovery: rec } = this;
@@ -927,7 +1056,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
927
1056
  if (!Fp.isValid(radj))
928
1057
  throw new Error('recovery id 2 or 3 invalid');
929
1058
  const x = Fp.toBytes(radj);
930
- const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
1059
+ const R = Point.fromBytes(concatBytes(pprefix((rec & 1) === 0), x));
931
1060
  const ir = Fn.inv(radj); // r^-1
932
1061
  const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
933
1062
  const u1 = Fn.create(-h * ir); // -hr^-1
@@ -943,15 +1072,18 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
943
1072
  hasHighS() {
944
1073
  return isBiggerThanHalfOrder(this.s);
945
1074
  }
946
- normalizeS() {
947
- return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
948
- }
949
- toBytes(format = 'compact') {
950
- if (format === 'compact')
951
- return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
1075
+ toBytes(format = defaultSigOpts_format) {
1076
+ validateSigFormat(format);
952
1077
  if (format === 'der')
953
1078
  return hexToBytes(DER.hexFromSig(this));
954
- throw new Error('invalid format');
1079
+ const r = Fn.toBytes(this.r);
1080
+ const s = Fn.toBytes(this.s);
1081
+ if (format === 'recovered') {
1082
+ if (this.recovery == null)
1083
+ throw new Error('recovery bit must be present');
1084
+ return concatBytes(Uint8Array.of(this.recovery), r, s);
1085
+ }
1086
+ return concatBytes(r, s);
955
1087
  }
956
1088
  toHex(format) {
957
1089
  return bytesToHex(this.toBytes(format));
@@ -964,6 +1096,9 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
964
1096
  static fromDER(hex) {
965
1097
  return Signature.fromBytes(ensureBytes('sig', hex), 'der');
966
1098
  }
1099
+ normalizeS() {
1100
+ return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1101
+ }
967
1102
  toDERRawBytes() {
968
1103
  return this.toBytes('der');
969
1104
  }
@@ -977,86 +1112,6 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
977
1112
  return bytesToHex(this.toBytes('compact'));
978
1113
  }
979
1114
  }
980
- function isValidSecretKey(privateKey) {
981
- try {
982
- return !!_normFnElement(Fn, privateKey);
983
- }
984
- catch (error) {
985
- return false;
986
- }
987
- }
988
- function isValidPublicKey(publicKey, isCompressed) {
989
- try {
990
- const l = publicKey.length;
991
- if (isCompressed === true && l !== lengths.public)
992
- return false;
993
- if (isCompressed === false && l !== lengths.publicUncompressed)
994
- return false;
995
- return !!Point.fromBytes(publicKey);
996
- }
997
- catch (error) {
998
- return false;
999
- }
1000
- }
1001
- /**
1002
- * Produces cryptographically secure secret key from random of size
1003
- * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1004
- */
1005
- function randomSecretKey(seed = randomBytes_(seedLen)) {
1006
- return mapHashToField(seed, CURVE_ORDER);
1007
- }
1008
- const utils = {
1009
- isValidSecretKey,
1010
- isValidPublicKey,
1011
- randomSecretKey,
1012
- // TODO: remove
1013
- isValidPrivateKey: isValidSecretKey,
1014
- randomPrivateKey: randomSecretKey,
1015
- normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
1016
- precompute(windowSize = 8, point = Point.BASE) {
1017
- return point.precompute(windowSize, false);
1018
- },
1019
- };
1020
- /**
1021
- * Computes public key for a secret key. Checks for validity of the secret key.
1022
- * @param isCompressed whether to return compact (default), or full key
1023
- * @returns Public key, full when isCompressed=false; short when isCompressed=true
1024
- */
1025
- function getPublicKey(secretKey, isCompressed = true) {
1026
- return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
1027
- }
1028
- /**
1029
- * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1030
- */
1031
- function isProbPub(item) {
1032
- // TODO: remove
1033
- if (typeof item === 'bigint')
1034
- return false;
1035
- // TODO: remove
1036
- if (item instanceof Point)
1037
- return true;
1038
- if (Fn.allowedLengths || lengths.secret === lengths.public)
1039
- return undefined;
1040
- const l = ensureBytes('key', item).length;
1041
- return l === lengths.public || l === lengths.publicUncompressed;
1042
- }
1043
- /**
1044
- * ECDH (Elliptic Curve Diffie Hellman).
1045
- * Computes shared public key from secret key A and public key B.
1046
- * Checks: 1) secret key validity 2) shared key is on-curve.
1047
- * Does NOT hash the result.
1048
- * @param isCompressed whether to return compact (default), or full key
1049
- * @returns shared public key
1050
- */
1051
- function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
1052
- if (isProbPub(secretKeyA) === true)
1053
- throw new Error('first arg must be private key');
1054
- if (isProbPub(publicKeyB) === false)
1055
- throw new Error('second arg must be public key');
1056
- const s = _normFnElement(Fn, secretKeyA);
1057
- const b = Point.fromHex(publicKeyB); // checks for being on-curve
1058
- return b.multiply(s).toBytes(isCompressed);
1059
- }
1060
1115
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
1061
1116
  // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
1062
1117
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
@@ -1086,31 +1141,33 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1086
1141
  aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
1087
1142
  return Fn.toBytes(num);
1088
1143
  }
1089
- // Steps A, D of RFC6979 3.2
1090
- // Creates RFC6979 seed; converts msg/privKey to numbers.
1091
- // Used only in sign, not in verify.
1092
- // NOTE: we cannot assume here that msgHash has same amount of bytes as curve order,
1093
- // this will be invalid at least for P521. Also it can be bigger for P224 + SHA256
1094
- function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
1144
+ /**
1145
+ * Steps A, D of RFC6979 3.2.
1146
+ * Creates RFC6979 seed; converts msg/privKey to numbers.
1147
+ * Used only in sign, not in verify.
1148
+ *
1149
+ * Warning: we cannot assume here that message has same amount of bytes as curve order,
1150
+ * this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
1151
+ */
1152
+ function prepSig(message, privateKey, opts) {
1095
1153
  if (['recovered', 'canonical'].some((k) => k in opts))
1096
1154
  throw new Error('sign() legacy options not supported');
1097
- let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
1098
- if (lowS == null)
1099
- lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1100
- msgHash = ensureBytes('msgHash', msgHash);
1101
- validateSigVerOpts(opts);
1155
+ const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
1156
+ // RFC6979 3.2: we skip step A, because we already provide hash
1157
+ message = abytes(message, undefined, 'message');
1102
1158
  if (prehash)
1103
- msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
1159
+ message = abytes(hash(message), undefined, 'prehashed message');
1104
1160
  // We can't later call bits2octets, since nested bits2int is broken for curves
1105
1161
  // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1106
1162
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1107
- const h1int = bits2int_modN(msgHash);
1163
+ const h1int = bits2int_modN(message);
1108
1164
  const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
1109
1165
  const seedArgs = [int2octets(d), int2octets(h1int)];
1110
1166
  // extraEntropy. RFC6979 3.6: additional k' (optional).
1111
- if (ent != null && ent !== false) {
1167
+ if (extraEntropy != null && extraEntropy !== false) {
1112
1168
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
1113
- const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
1169
+ // gen random bytes OR pass as-is
1170
+ const e = extraEntropy === true ? randomBytes_(lengths.secret) : extraEntropy;
1114
1171
  seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
1115
1172
  }
1116
1173
  const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
@@ -1126,7 +1183,7 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1126
1183
  function k2sig(kBytes) {
1127
1184
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
1128
1185
  // Important: all mod() calls here must be done over N
1129
- const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
1186
+ const k = bits2int(kBytes); // mod n, not mod p
1130
1187
  if (!Fn.isValidNot0(k))
1131
1188
  return; // Valid scalars (including k) must be in 1..N-1
1132
1189
  const ik = Fn.inv(k); // k^-1 mod n
@@ -1147,49 +1204,48 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1147
1204
  }
1148
1205
  return { seed, k2sig };
1149
1206
  }
1150
- const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1151
- const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1152
1207
  /**
1153
1208
  * Signs message hash with a secret key.
1209
+ *
1154
1210
  * ```
1155
- * sign(m, d, k) where
1211
+ * sign(m, d) where
1212
+ * k = rfc6979_hmac_drbg(m, d)
1156
1213
  * (x, y) = G × k
1157
1214
  * r = x mod n
1158
- * s = (m + dr)/k mod n
1215
+ * s = (m + dr) / k mod n
1159
1216
  * ```
1160
1217
  */
1161
- function sign(msgHash, secretKey, opts = defaultSigOpts) {
1162
- const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
1218
+ function sign(message, secretKey, opts = {}) {
1219
+ message = ensureBytes('message', message);
1220
+ const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
1163
1221
  const drbg = createHmacDrbg(hash.outputLen, Fn.BYTES, hmac_);
1164
- return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1222
+ const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
1223
+ return sig;
1165
1224
  }
1166
1225
  // Enable precomputes. Slows down first publicKey computation by 20ms.
1167
1226
  Point.BASE.precompute(8);
1168
1227
  /**
1169
- * Verifies a signature against message hash and public key.
1170
- * Rejects lowS signatures by default: to override,
1171
- * specify option `{lowS: false}`. Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
1228
+ * Verifies a signature against message and public key.
1229
+ * Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.
1230
+ * Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
1172
1231
  *
1173
1232
  * ```
1174
1233
  * verify(r, s, h, P) where
1175
- * U1 = hs^-1 mod n
1176
- * U2 = rs^-1 mod n
1177
- * R = U1⋅G - U2⋅P
1234
+ * u1 = hs^-1 mod n
1235
+ * u2 = rs^-1 mod n
1236
+ * R = u1⋅G + u2⋅P
1178
1237
  * mod(R.x, n) == r
1179
1238
  * ```
1180
1239
  */
1181
- function verify(signature, msgHash, publicKey, opts = defaultVerOpts) {
1240
+ function verify(signature, message, publicKey, opts = {}) {
1182
1241
  const sg = signature;
1183
- msgHash = ensureBytes('msgHash', msgHash);
1242
+ message = ensureBytes('msgHash', message);
1184
1243
  publicKey = ensureBytes('publicKey', publicKey);
1185
- // Verify opts
1186
- validateSigVerOpts(opts);
1187
- const { lowS, prehash, format } = opts;
1188
- // TODO: remove
1189
- if ('strict' in opts)
1190
- throw new Error('options.strict was renamed to lowS');
1244
+ const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
1191
1245
  let _sig = undefined;
1192
1246
  let P;
1247
+ if ('strict' in opts)
1248
+ throw new Error('options.strict was renamed to lowS');
1193
1249
  if (format === undefined) {
1194
1250
  // Try to deduce format
1195
1251
  const isHex = typeof sg === 'string' || isBytes(sg);
@@ -1230,11 +1286,6 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1230
1286
  throw new Error('"der" / "compact" format expects Uint8Array signature');
1231
1287
  _sig = Signature.fromBytes(ensureBytes('sig', sg), format);
1232
1288
  }
1233
- else if (format === 'js') {
1234
- if (!(sg instanceof Signature))
1235
- throw new Error('"js" format expects Signature instance');
1236
- _sig = sg;
1237
- }
1238
1289
  else {
1239
1290
  throw new Error('format must be "compact", "der" or "js"');
1240
1291
  }
@@ -1247,13 +1298,13 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1247
1298
  return false;
1248
1299
  // todo: optional.hash => hash
1249
1300
  if (prehash)
1250
- msgHash = hash(msgHash);
1301
+ message = hash(message);
1251
1302
  const { r, s } = _sig;
1252
- const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1253
- const is = Fn.inv(s); // s^-1
1303
+ const h = bits2int_modN(message); // mod n, not mod p
1304
+ const is = Fn.inv(s); // s^-1 mod n
1254
1305
  const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1255
1306
  const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1256
- const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1307
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); // u1⋅G + u2⋅P
1257
1308
  if (R.is0())
1258
1309
  return false;
1259
1310
  const v = Fn.create(R.x); // v = r.x mod n
@@ -1263,23 +1314,34 @@ export function ecdsa(Point, hash, ecdsaOpts = {}) {
1263
1314
  return false;
1264
1315
  }
1265
1316
  }
1266
- function keygen(seed) {
1267
- const secretKey = utils.randomSecretKey(seed);
1268
- return { secretKey, publicKey: getPublicKey(secretKey) };
1317
+ function recoverPublicKey(signature, message, opts = {}) {
1318
+ const prehash = opts.prehash !== undefined ? opts.prehash : defaultSigOpts.prehash;
1319
+ abool(prehash, 'prehash');
1320
+ message = abytes(message, undefined, 'message');
1321
+ if (prehash)
1322
+ message = abytes(hash(message), undefined, 'prehashed message');
1323
+ return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
1269
1324
  }
1270
1325
  return Object.freeze({
1271
1326
  keygen,
1272
1327
  getPublicKey,
1273
- sign,
1274
- verify,
1275
1328
  getSharedSecret,
1276
1329
  utils,
1330
+ lengths,
1277
1331
  Point,
1332
+ sign,
1333
+ verify,
1334
+ recoverPublicKey,
1278
1335
  Signature,
1279
- info: { type: 'weierstrass', lengths, publicKeyHasPrefix: true },
1336
+ hash,
1280
1337
  });
1281
1338
  }
1282
- // TODO: remove
1339
+ /** @deprecated use `weierstrass` in newer releases */
1340
+ export function weierstrassPoints(c) {
1341
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1342
+ const Point = weierstrassN(CURVE, curveOpts);
1343
+ return _weierstrass_new_output_to_legacy(c, Point);
1344
+ }
1283
1345
  function _weierstrass_legacy_opts_to_new(c) {
1284
1346
  const CURVE = {
1285
1347
  a: c.a,
@@ -1297,7 +1359,7 @@ function _weierstrass_legacy_opts_to_new(c) {
1297
1359
  const Fn = Field(CURVE.n, {
1298
1360
  BITS: c.nBitLength,
1299
1361
  allowedLengths: allowedLengths,
1300
- modOnDecode: c.wrapPrivateKey,
1362
+ modFromBytes: c.wrapPrivateKey,
1301
1363
  });
1302
1364
  const curveOpts = {
1303
1365
  Fp,
@@ -1322,10 +1384,20 @@ function _ecdsa_legacy_opts_to_new(c) {
1322
1384
  };
1323
1385
  return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
1324
1386
  }
1325
- // TODO: remove
1387
+ export function _legacyHelperEquat(Fp, a, b) {
1388
+ /**
1389
+ * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
1390
+ * @returns y²
1391
+ */
1392
+ function weierstrassEquation(x) {
1393
+ const x2 = Fp.sqr(x); // x * x
1394
+ const x3 = Fp.mul(x2, x); // x² * x
1395
+ return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
1396
+ }
1397
+ return weierstrassEquation;
1398
+ }
1326
1399
  function _weierstrass_new_output_to_legacy(c, Point) {
1327
1400
  const { Fp, Fn } = Point;
1328
- // TODO: remove
1329
1401
  function isWithinCurveOrder(num) {
1330
1402
  return inRange(num, _1n, Fn.ORDER);
1331
1403
  }
@@ -1339,11 +1411,11 @@ function _weierstrass_new_output_to_legacy(c, Point) {
1339
1411
  isWithinCurveOrder,
1340
1412
  });
1341
1413
  }
1342
- // TODO: remove
1343
- function _ecdsa_new_output_to_legacy(c, ecdsa) {
1344
- return Object.assign({}, ecdsa, {
1345
- ProjectivePoint: ecdsa.Point,
1346
- CURVE: c,
1414
+ function _ecdsa_new_output_to_legacy(c, _ecdsa) {
1415
+ const Point = _ecdsa.Point;
1416
+ return Object.assign({}, _ecdsa, {
1417
+ ProjectivePoint: Point,
1418
+ CURVE: Object.assign({}, c, nLength(Point.Fn.ORDER, Point.Fn.BITS)),
1347
1419
  });
1348
1420
  }
1349
1421
  // _ecdsa_legacy