@noble/curves 0.5.1 → 0.5.2

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.
@@ -3,64 +3,61 @@
3
3
  // Short Weierstrass curve. The formula is: y² = x³ + ax + b
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.mapToCurveSimpleSWU = exports.SWUFpSqrtRatio = exports.weierstrass = exports.weierstrassPoints = void 0;
6
- // TODO: sync vs async naming
7
- // TODO: default randomBytes
8
6
  // Differences from @noble/secp256k1 1.7:
9
7
  // 1. Different double() formula (but same addition)
10
8
  // 2. Different sqrt() function
11
9
  // 3. truncateHash() truncateOnly mode
12
10
  // 4. DRBG supports outputLen bigger than outputLen of hmac
11
+ // 5. Support for different hash functions
13
12
  const mod = require("./modular.js");
13
+ const ut = require("./utils.js");
14
14
  const utils_js_1 = require("./utils.js");
15
- const utils = require("./utils.js");
16
15
  const hash_to_curve_js_1 = require("./hash-to-curve.js");
17
16
  const group_js_1 = require("./group.js");
18
- // DER encoding utilities
17
+ // ASN.1 DER encoding utilities
19
18
  class DERError extends Error {
20
19
  constructor(message) {
21
20
  super(message);
22
21
  }
23
22
  }
24
- function sliceDER(s) {
25
- // Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)])
26
- // Padding done by numberToHex
27
- return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s;
28
- }
29
- function parseDERInt(data) {
30
- if (data.length < 2 || data[0] !== 0x02) {
31
- throw new DERError(`Invalid signature integer tag: ${(0, utils_js_1.bytesToHex)(data)}`);
32
- }
33
- const len = data[1];
34
- const res = data.subarray(2, len + 2);
35
- if (!len || res.length !== len) {
36
- throw new DERError(`Invalid signature integer: wrong length`);
37
- }
38
- // Strange condition, its not about length, but about first bytes of number.
39
- if (res[0] === 0x00 && res[1] <= 0x7f) {
40
- throw new DERError('Invalid signature integer: trailing length');
41
- }
42
- return { data: (0, utils_js_1.bytesToNumberBE)(res), left: data.subarray(len + 2) };
43
- }
44
- function parseDERSignature(data) {
45
- if (data.length < 2 || data[0] != 0x30) {
46
- throw new DERError(`Invalid signature tag: ${(0, utils_js_1.bytesToHex)(data)}`);
47
- }
48
- if (data[1] !== data.length - 2) {
49
- throw new DERError('Invalid signature: incorrect length');
50
- }
51
- const { data: r, left: sBytes } = parseDERInt(data.subarray(2));
52
- const { data: s, left: rBytesLeft } = parseDERInt(sBytes);
53
- if (rBytesLeft.length) {
54
- throw new DERError(`Invalid signature: left bytes after parsing: ${(0, utils_js_1.bytesToHex)(rBytesLeft)}`);
55
- }
56
- return { r, s };
57
- }
58
- // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
59
- const _0n = BigInt(0);
60
- const _1n = BigInt(1);
61
- const _3n = BigInt(3);
23
+ const DER = {
24
+ slice(s) {
25
+ // Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)])
26
+ // Padding done by numberToHex
27
+ return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s;
28
+ },
29
+ parseInt(data) {
30
+ if (data.length < 2 || data[0] !== 0x02) {
31
+ throw new DERError(`Invalid signature integer tag: ${(0, utils_js_1.bytesToHex)(data)}`);
32
+ }
33
+ const len = data[1];
34
+ const res = data.subarray(2, len + 2);
35
+ if (!len || res.length !== len) {
36
+ throw new DERError(`Invalid signature integer: wrong length`);
37
+ }
38
+ // Strange condition, its not about length, but about first bytes of number.
39
+ if (res[0] === 0x00 && res[1] <= 0x7f) {
40
+ throw new DERError('Invalid signature integer: trailing length');
41
+ }
42
+ return { data: ut.bytesToNumberBE(res), left: data.subarray(len + 2) };
43
+ },
44
+ parseSig(data) {
45
+ if (data.length < 2 || data[0] != 0x30) {
46
+ throw new DERError(`Invalid signature tag: ${(0, utils_js_1.bytesToHex)(data)}`);
47
+ }
48
+ if (data[1] !== data.length - 2) {
49
+ throw new DERError('Invalid signature: incorrect length');
50
+ }
51
+ const { data: r, left: sBytes } = DER.parseInt(data.subarray(2));
52
+ const { data: s, left: rBytesLeft } = DER.parseInt(sBytes);
53
+ if (rBytesLeft.length) {
54
+ throw new DERError(`Invalid signature: left bytes after parsing: ${(0, utils_js_1.bytesToHex)(rBytesLeft)}`);
55
+ }
56
+ return { r, s };
57
+ },
58
+ };
62
59
  function validatePointOpts(curve) {
63
- const opts = utils.validateOpts(curve);
60
+ const opts = ut.validateOpts(curve);
64
61
  const Fp = opts.Fp;
65
62
  for (const i of ['a', 'b']) {
66
63
  if (!Fp.isValid(curve[i]))
@@ -93,21 +90,13 @@ function validatePointOpts(curve) {
93
90
  // Set defaults
94
91
  return Object.freeze({ ...opts });
95
92
  }
93
+ // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
94
+ const _0n = BigInt(0);
95
+ const _1n = BigInt(1);
96
+ const _3n = BigInt(3);
96
97
  function weierstrassPoints(opts) {
97
98
  const CURVE = validatePointOpts(opts);
98
- const Fp = CURVE.Fp;
99
- // Lengths
100
- // All curves has same field / group length as for now, but it can be different for other curves
101
- const { nByteLength, nBitLength } = CURVE;
102
- const groupLen = nByteLength;
103
- // Not using ** operator with bigints for old engines.
104
- // 2n ** (8n * 32n) == 2n << (8n * 32n - 1n)
105
- //const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n);
106
- // function numToFieldStr(num: bigint): string {
107
- // if (typeof num !== 'bigint') throw new Error('Expected bigint');
108
- // if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`);
109
- // return num.toString(16).padStart(2 * fieldLen, '0');
110
- // }
99
+ const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
111
100
  /**
112
101
  * y² = x³ + ax + b: Short weierstrass curve formula
113
102
  * @returns y²
@@ -118,41 +107,55 @@ function weierstrassPoints(opts) {
118
107
  const x3 = Fp.mul(x2, x); // x2 * x
119
108
  return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
120
109
  }
110
+ // Valid group elements reside in range 1..n-1
121
111
  function isWithinCurveOrder(num) {
122
112
  return _0n < num && num < CURVE.n;
123
113
  }
114
+ /**
115
+ * Validates if a private key is valid and converts it to bigint form.
116
+ * Supports two options, that are passed when CURVE is initialized:
117
+ * - `normalizePrivateKey()` executed before all checks
118
+ * - `wrapPrivateKey` when true, executed after most checks, but before `0 < key < n`
119
+ */
124
120
  function normalizePrivateKey(key) {
125
- if (typeof CURVE.normalizePrivateKey === 'function') {
126
- key = CURVE.normalizePrivateKey(key);
127
- }
121
+ const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n: order } = CURVE;
122
+ if (typeof custom === 'function')
123
+ key = custom(key);
128
124
  let num;
129
125
  if (typeof key === 'bigint') {
126
+ // Curve order check is done below
130
127
  num = key;
131
128
  }
132
- else if (typeof key === 'number' && Number.isSafeInteger(key) && key > 0) {
129
+ else if (ut.isPositiveInt(key)) {
133
130
  num = BigInt(key);
134
131
  }
135
132
  else if (typeof key === 'string') {
136
133
  if (key.length !== 2 * groupLen)
137
134
  throw new Error(`Expected ${groupLen} bytes of private key`);
138
- num = (0, utils_js_1.hexToNumber)(key);
135
+ // Validates individual octets
136
+ num = ut.hexToNumber(key);
139
137
  }
140
138
  else if (key instanceof Uint8Array) {
141
139
  if (key.length !== groupLen)
142
140
  throw new Error(`Expected ${groupLen} bytes of private key`);
143
- num = (0, utils_js_1.bytesToNumberBE)(key);
141
+ num = ut.bytesToNumberBE(key);
144
142
  }
145
143
  else {
146
144
  throw new TypeError('Expected valid private key');
147
145
  }
148
- if (CURVE.wrapPrivateKey)
149
- num = mod.mod(num, CURVE.n);
146
+ // Useful for curves with cofactor != 1
147
+ if (wrapPrivateKey)
148
+ num = mod.mod(num, order);
150
149
  if (!isWithinCurveOrder(num))
151
150
  throw new Error('Expected private key: 0 < key < n');
152
151
  return num;
153
152
  }
153
+ /**
154
+ * Validates if a scalar ("private number") is valid.
155
+ * Scalars are valid only if they are less than curve order.
156
+ */
154
157
  function normalizeScalar(num) {
155
- if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
158
+ if (ut.isPositiveInt(num))
156
159
  return BigInt(num);
157
160
  if (typeof num === 'bigint' && isWithinCurveOrder(num))
158
161
  return num;
@@ -180,13 +183,16 @@ function weierstrassPoints(opts) {
180
183
  }
181
184
  /**
182
185
  * Takes a bunch of Projective Points but executes only one
183
- * invert on all of them. invert is very slow operation,
186
+ * inversion on all of them. Inversion is very slow operation,
184
187
  * so this improves performance massively.
185
188
  */
186
189
  static toAffineBatch(points) {
187
190
  const toInv = Fp.invertBatch(points.map((p) => p.z));
188
191
  return points.map((p, i) => p.toAffine(toInv[i]));
189
192
  }
193
+ /**
194
+ * Optimization: converts a list of projective points to a list of identical points with Z=1.
195
+ */
190
196
  static normalizeZ(points) {
191
197
  return ProjectivePoint.toAffineBatch(points).map(ProjectivePoint.fromAffine);
192
198
  }
@@ -207,9 +213,6 @@ function weierstrassPoints(opts) {
207
213
  negate() {
208
214
  return new ProjectivePoint(this.x, Fp.negate(this.y), this.z);
209
215
  }
210
- doubleAdd() {
211
- return this.add(this);
212
- }
213
216
  // Renes-Costello-Batina exception-free doubling formula.
214
217
  // There is 30% faster Jacobian formula, but it is not complete.
215
218
  // https://eprint.iacr.org/2015/1060, algorithm 3
@@ -414,26 +417,26 @@ function weierstrassPoints(opts) {
414
417
  return new Point(ax, ay);
415
418
  }
416
419
  isTorsionFree() {
417
- if (CURVE.h === _1n)
418
- return true; // No subgroups, always torsion fee
419
- if (CURVE.isTorsionFree)
420
- return CURVE.isTorsionFree(ProjectivePoint, this);
421
- // is multiplyUnsafe(CURVE.n) is always ok, same as for edwards?
422
- throw new Error('Unsupported!');
423
- }
424
- // Clear cofactor of G1
425
- // https://eprint.iacr.org/2019/403
420
+ const { h: cofactor, isTorsionFree } = CURVE;
421
+ if (cofactor === _1n)
422
+ return true; // No subgroups, always torsion-free
423
+ if (isTorsionFree)
424
+ return isTorsionFree(ProjectivePoint, this);
425
+ throw new Error('isTorsionFree() has not been declared for the elliptic curve');
426
+ }
426
427
  clearCofactor() {
427
- if (CURVE.h === _1n)
428
+ const { h: cofactor, clearCofactor } = CURVE;
429
+ if (cofactor === _1n)
428
430
  return this; // Fast-path
429
- if (CURVE.clearCofactor)
430
- return CURVE.clearCofactor(ProjectivePoint, this);
431
+ if (clearCofactor)
432
+ return clearCofactor(ProjectivePoint, this);
431
433
  return this.multiplyUnsafe(CURVE.h);
432
434
  }
433
435
  }
434
436
  ProjectivePoint.BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
435
437
  ProjectivePoint.ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
436
- const wnaf = (0, group_js_1.wNAF)(ProjectivePoint, CURVE.endo ? nBitLength / 2 : nBitLength);
438
+ const _bits = CURVE.nBitLength;
439
+ const wnaf = (0, group_js_1.wNAF)(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
437
440
  function assertPrjPoint(other) {
438
441
  if (!(other instanceof ProjectivePoint))
439
442
  throw new TypeError('ProjectivePoint expected');
@@ -464,7 +467,7 @@ function weierstrassPoints(opts) {
464
467
  * @param hex short/long ECDSA hex
465
468
  */
466
469
  static fromHex(hex) {
467
- const { x, y } = CURVE.fromBytes((0, utils_js_1.ensureBytes)(hex));
470
+ const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex));
468
471
  const point = new Point(x, y);
469
472
  point.assertValidity();
470
473
  return point;
@@ -486,18 +489,18 @@ function weierstrassPoints(opts) {
486
489
  if (this.equals(Point.ZERO)) {
487
490
  if (CURVE.allowInfinityPoint)
488
491
  return;
489
- throw new Error('Point is infinity');
492
+ throw new Error('Point at infinity');
490
493
  }
491
494
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
492
495
  const msg = 'Point is not on elliptic curve';
493
496
  const { x, y } = this;
497
+ // Check if x, y are valid field elements
494
498
  if (!Fp.isValid(x) || !Fp.isValid(y))
495
499
  throw new Error(msg);
496
- const left = Fp.square(y);
497
- const right = weierstrassEquation(x);
500
+ const left = Fp.square(y); // y²
501
+ const right = weierstrassEquation(x); // x³ + ax + b
498
502
  if (!Fp.equals(left, right))
499
503
  throw new Error(msg);
500
- // TODO: flag to disable this?
501
504
  if (!this.isTorsionFree())
502
505
  throw new Error('Point must be of prime-order subgroup');
503
506
  }
@@ -510,29 +513,30 @@ function weierstrassPoints(opts) {
510
513
  negate() {
511
514
  return new Point(this.x, Fp.negate(this.y));
512
515
  }
516
+ toProj() {
517
+ return ProjectivePoint.fromAffine(this);
518
+ }
513
519
  // Adds point to itself
514
520
  double() {
515
- return ProjectivePoint.fromAffine(this).double().toAffine();
521
+ return this.toProj().double().toAffine();
516
522
  }
517
- // Adds point to other point
518
523
  add(other) {
519
- return ProjectivePoint.fromAffine(this).add(ProjectivePoint.fromAffine(other)).toAffine();
524
+ return this.toProj().add(ProjectivePoint.fromAffine(other)).toAffine();
520
525
  }
521
- // Subtracts other point from the point
522
526
  subtract(other) {
523
527
  return this.add(other.negate());
524
528
  }
525
529
  multiply(scalar) {
526
- return ProjectivePoint.fromAffine(this).multiply(scalar, this).toAffine();
530
+ return this.toProj().multiply(scalar, this).toAffine();
527
531
  }
528
532
  multiplyUnsafe(scalar) {
529
- return ProjectivePoint.fromAffine(this).multiplyUnsafe(scalar).toAffine();
533
+ return this.toProj().multiplyUnsafe(scalar).toAffine();
530
534
  }
531
535
  clearCofactor() {
532
- return ProjectivePoint.fromAffine(this).clearCofactor().toAffine();
536
+ return this.toProj().clearCofactor().toAffine();
533
537
  }
534
538
  isTorsionFree() {
535
- return ProjectivePoint.fromAffine(this).isTorsionFree();
539
+ return this.toProj().isTorsionFree();
536
540
  }
537
541
  /**
538
542
  * Efficiently calculate `aP + bQ`.
@@ -541,7 +545,7 @@ function weierstrassPoints(opts) {
541
545
  * @returns non-zero affine point
542
546
  */
543
547
  multiplyAndAddUnsafe(Q, a, b) {
544
- const P = ProjectivePoint.fromAffine(this);
548
+ const P = this.toProj();
545
549
  const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
546
550
  const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
547
551
  const sum = aP.add(bQ);
@@ -550,31 +554,32 @@ function weierstrassPoints(opts) {
550
554
  // Encodes byte string to elliptic curve
551
555
  // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
552
556
  static hashToCurve(msg, options) {
553
- if (!CURVE.mapToCurve)
554
- throw new Error('No mapToCurve defined for curve');
555
- msg = (0, utils_js_1.ensureBytes)(msg);
557
+ const { mapToCurve } = CURVE;
558
+ if (!mapToCurve)
559
+ throw new Error('CURVE.mapToCurve() has not been defined');
560
+ msg = ut.ensureBytes(msg);
556
561
  const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 2, { ...CURVE.htfDefaults, ...options });
557
- const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]);
558
- const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]);
559
- const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
560
- return p;
562
+ const { x: x0, y: y0 } = mapToCurve(u[0]);
563
+ const { x: x1, y: y1 } = mapToCurve(u[1]);
564
+ return new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
561
565
  }
562
566
  // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
563
567
  static encodeToCurve(msg, options) {
564
- if (!CURVE.mapToCurve)
565
- throw new Error('No mapToCurve defined for curve');
566
- msg = (0, utils_js_1.ensureBytes)(msg);
568
+ const { mapToCurve } = CURVE;
569
+ if (!mapToCurve)
570
+ throw new Error('CURVE.mapToCurve() has not been defined');
571
+ msg = ut.ensureBytes(msg);
567
572
  const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 1, { ...CURVE.htfDefaults, ...options });
568
- const { x, y } = CURVE.mapToCurve(u[0]);
573
+ const { x, y } = mapToCurve(u[0]);
569
574
  return new Point(x, y).clearCofactor();
570
575
  }
571
576
  }
572
577
  /**
573
- * Base point aka generator. public_key = Point.BASE * private_key
578
+ * Base point aka generator. Any public_key = Point.BASE * private_key
574
579
  */
575
580
  Point.BASE = new Point(CURVE.Gx, CURVE.Gy);
576
581
  /**
577
- * Identity point aka point at infinity. point = point + zero_point
582
+ * Identity point aka point at infinity. p - p = zero_p; p + zero_p = p
578
583
  */
579
584
  Point.ZERO = new Point(Fp.ZERO, Fp.ZERO);
580
585
  return {
@@ -587,8 +592,8 @@ function weierstrassPoints(opts) {
587
592
  }
588
593
  exports.weierstrassPoints = weierstrassPoints;
589
594
  function validateOpts(curve) {
590
- const opts = utils.validateOpts(curve);
591
- if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
595
+ const opts = ut.validateOpts(curve);
596
+ if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen))
592
597
  throw new Error('Invalid hash function');
593
598
  if (typeof opts.hmac !== 'function')
594
599
  throw new Error('Invalid hmac function');
@@ -644,15 +649,15 @@ class HmacDrbg {
644
649
  out.push(sl);
645
650
  len += this.v.length;
646
651
  }
647
- return (0, utils_js_1.concatBytes)(...out);
652
+ return ut.concatBytes(...out);
648
653
  }
649
654
  }
650
655
  function weierstrass(curveDef) {
651
656
  const CURVE = validateOpts(curveDef);
652
657
  const CURVE_ORDER = CURVE.n;
653
658
  const Fp = CURVE.Fp;
654
- const compressedLen = Fp.BYTES + 1; // 33
655
- const uncompressedLen = 2 * Fp.BYTES + 1; // 65
659
+ const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
660
+ const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
656
661
  function isValidFieldElement(num) {
657
662
  // 0 is disallowed by arbitrary reasons. Probably because infinity point?
658
663
  return _0n < num && num < Fp.ORDER;
@@ -660,11 +665,13 @@ function weierstrass(curveDef) {
660
665
  const { Point, ProjectivePoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({
661
666
  ...CURVE,
662
667
  toBytes(c, point, isCompressed) {
668
+ const x = Fp.toBytes(point.x);
669
+ const cat = ut.concatBytes;
663
670
  if (isCompressed) {
664
- return (0, utils_js_1.concatBytes)(new Uint8Array([point.hasEvenY() ? 0x02 : 0x03]), Fp.toBytes(point.x));
671
+ return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
665
672
  }
666
673
  else {
667
- return (0, utils_js_1.concatBytes)(new Uint8Array([0x04]), Fp.toBytes(point.x), Fp.toBytes(point.y));
674
+ return cat(Uint8Array.from([0x04]), x, Fp.toBytes(point.y));
668
675
  }
669
676
  },
670
677
  fromBytes(bytes) {
@@ -672,7 +679,7 @@ function weierstrass(curveDef) {
672
679
  const header = bytes[0];
673
680
  // this.assertValidity() is done inside of fromHex
674
681
  if (len === compressedLen && (header === 0x02 || header === 0x03)) {
675
- const x = (0, utils_js_1.bytesToNumberBE)(bytes.subarray(1));
682
+ const x = ut.bytesToNumberBE(bytes.subarray(1));
676
683
  if (!isValidFieldElement(x))
677
684
  throw new Error('Point is not on curve');
678
685
  const y2 = weierstrassEquation(x); // y² = x³ + ax + b
@@ -725,17 +732,18 @@ function weierstrass(curveDef) {
725
732
  function normalizeS(s) {
726
733
  return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
727
734
  }
735
+ function bits2int_2(bytes) {
736
+ const delta = bytes.length * 8 - CURVE.nBitLength;
737
+ const num = ut.bytesToNumberBE(bytes);
738
+ return delta > 0 ? num >> BigInt(delta) : num;
739
+ }
728
740
  // Ensures ECDSA message hashes are 32 bytes and < curve order
729
741
  function _truncateHash(hash, truncateOnly = false) {
730
- const { n, nBitLength } = CURVE;
731
- const byteLength = hash.length;
732
- const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
733
- let h = (0, utils_js_1.bytesToNumberBE)(hash);
734
- if (delta > 0)
735
- h = h >> BigInt(delta);
736
- if (!truncateOnly && h >= n)
737
- h -= n;
738
- return h;
742
+ const h = bits2int_2(hash);
743
+ if (truncateOnly)
744
+ return h;
745
+ const { n } = CURVE;
746
+ return h >= n ? h - n : h;
739
747
  }
740
748
  const truncateHash = CURVE.truncateHash || _truncateHash;
741
749
  /**
@@ -748,16 +756,18 @@ function weierstrass(curveDef) {
748
756
  this.recovery = recovery;
749
757
  this.assertValidity();
750
758
  }
751
- // pair (32 bytes of r, 32 bytes of s)
759
+ // pair (bytes of r, bytes of s)
752
760
  static fromCompact(hex) {
753
761
  const arr = hex instanceof Uint8Array;
754
762
  const name = 'Signature.fromCompact';
755
763
  if (typeof hex !== 'string' && !arr)
756
764
  throw new TypeError(`${name}: Expected string or Uint8Array`);
757
765
  const str = arr ? (0, utils_js_1.bytesToHex)(hex) : hex;
758
- if (str.length !== 128)
759
- throw new Error(`${name}: Expected 64-byte hex`);
760
- return new Signature((0, utils_js_1.hexToNumber)(str.slice(0, 64)), (0, utils_js_1.hexToNumber)(str.slice(64, 128)));
766
+ const gl = CURVE.nByteLength * 2; // group length in hex, not ui8a
767
+ if (str.length !== 2 * gl)
768
+ throw new Error(`${name}: Expected ${gl / 2}-byte hex`);
769
+ const slice = (from, to) => ut.hexToNumber(str.slice(from, to));
770
+ return new Signature(slice(0, gl), slice(gl, 2 * gl));
761
771
  }
762
772
  // DER encoded ECDSA signature
763
773
  // https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
@@ -765,7 +775,7 @@ function weierstrass(curveDef) {
765
775
  const arr = hex instanceof Uint8Array;
766
776
  if (typeof hex !== 'string' && !arr)
767
777
  throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`);
768
- const { r, s } = parseDERSignature(arr ? hex : (0, utils_js_1.hexToBytes)(hex));
778
+ const { r, s } = DER.parseSig(arr ? hex : ut.hexToBytes(hex));
769
779
  return new Signature(r, s);
770
780
  }
771
781
  assertValidity() {
@@ -781,6 +791,7 @@ function weierstrass(curveDef) {
781
791
  /**
782
792
  * Recovers public key from signature with recovery bit. Throws on invalid hash.
783
793
  * https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery
794
+ * It's also possible to recover key without bit: try all 4 bit values and check for sig match.
784
795
  *
785
796
  * ```
786
797
  * recover(r, s, h) where
@@ -796,17 +807,20 @@ function weierstrass(curveDef) {
796
807
  const { r, s, recovery } = this;
797
808
  if (recovery == null)
798
809
  throw new Error('Cannot recover: recovery bit is not present');
799
- if (recovery !== 0 && recovery !== 1)
810
+ if (![0, 1, 2, 3].includes(recovery))
800
811
  throw new Error('Cannot recover: invalid recovery bit');
801
- const h = truncateHash((0, utils_js_1.ensureBytes)(msgHash));
812
+ const h = truncateHash(ut.ensureBytes(msgHash));
802
813
  const { n } = CURVE;
803
- const rinv = mod.invert(r, n);
814
+ const radj = recovery === 2 || recovery === 3 ? r + n : r;
815
+ if (radj >= Fp.ORDER)
816
+ throw new Error('Cannot recover: bit 2/3 is invalid with current r');
817
+ const rinv = mod.invert(radj, n);
804
818
  // Q = u1⋅G + u2⋅R
805
819
  const u1 = mod.mod(-h * rinv, n);
806
820
  const u2 = mod.mod(s * rinv, n);
807
821
  const prefix = recovery & 1 ? '03' : '02';
808
- const R = Point.fromHex(prefix + numToFieldStr(r));
809
- const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2);
822
+ const R = Point.fromHex(prefix + numToFieldStr(radj));
823
+ const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked
810
824
  if (!Q)
811
825
  throw new Error('Cannot recover: point at infinify');
812
826
  Q.assertValidity();
@@ -826,30 +840,29 @@ function weierstrass(curveDef) {
826
840
  : this;
827
841
  }
828
842
  // DER-encoded
829
- toDERRawBytes(isCompressed = false) {
830
- return (0, utils_js_1.hexToBytes)(this.toDERHex(isCompressed));
831
- }
832
- toDERHex(isCompressed = false) {
833
- const sHex = sliceDER((0, utils_js_1.numberToHexUnpadded)(this.s));
834
- if (isCompressed)
835
- return sHex;
836
- const rHex = sliceDER((0, utils_js_1.numberToHexUnpadded)(this.r));
837
- const rLen = (0, utils_js_1.numberToHexUnpadded)(rHex.length / 2);
838
- const sLen = (0, utils_js_1.numberToHexUnpadded)(sHex.length / 2);
839
- const length = (0, utils_js_1.numberToHexUnpadded)(rHex.length / 2 + sHex.length / 2 + 4);
843
+ toDERRawBytes() {
844
+ return ut.hexToBytes(this.toDERHex());
845
+ }
846
+ toDERHex() {
847
+ const { numberToHexUnpadded: toHex } = ut;
848
+ const sHex = DER.slice(toHex(this.s));
849
+ const rHex = DER.slice(toHex(this.r));
850
+ const sHexL = sHex.length / 2;
851
+ const rHexL = rHex.length / 2;
852
+ const sLen = toHex(sHexL);
853
+ const rLen = toHex(rHexL);
854
+ const length = toHex(rHexL + sHexL + 4);
840
855
  return `30${length}02${rLen}${rHex}02${sLen}${sHex}`;
841
856
  }
842
- // 32 bytes of r, then 32 bytes of s
857
+ // padded bytes of r, then padded bytes of s
843
858
  toCompactRawBytes() {
844
- return (0, utils_js_1.hexToBytes)(this.toCompactHex());
859
+ return ut.hexToBytes(this.toCompactHex());
845
860
  }
846
861
  toCompactHex() {
847
862
  return numToFieldStr(this.r) + numToFieldStr(this.s);
848
863
  }
849
864
  }
850
865
  const utils = {
851
- mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo),
852
- invert: Fp.invert,
853
866
  isValidPrivateKey(privateKey) {
854
867
  try {
855
868
  normalizePrivateKey(privateKey);
@@ -869,7 +882,7 @@ function weierstrass(curveDef) {
869
882
  /**
870
883
  * Converts some bytes to a valid private key. Needs at least (nBitLength+64) bytes.
871
884
  */
872
- hashToPrivateKey: (hash) => numToField((0, utils_js_1.hashToPrivateScalar)(hash, CURVE_ORDER)),
885
+ hashToPrivateKey: (hash) => numToField(ut.hashToPrivateScalar(hash, CURVE_ORDER)),
873
886
  /**
874
887
  * Produces cryptographically secure private key from random of size (nBitLength+64)
875
888
  * as per FIPS 186 B.4.1 with modulo bias being neglible.
@@ -891,10 +904,10 @@ function weierstrass(curveDef) {
891
904
  },
892
905
  };
893
906
  /**
894
- * Computes public key for a private key.
907
+ * Computes public key for a private key. Checks for validity of the private key.
895
908
  * @param privateKey private key
896
- * @param isCompressed whether to return compact, or full key
897
- * @returns Public key, full by default; short when isCompressed=true
909
+ * @param isCompressed whether to return compact (default), or full key
910
+ * @returns Public key, full when isCompressed=false; short when isCompressed=true
898
911
  */
899
912
  function getPublicKey(privateKey, isCompressed = false) {
900
913
  return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
@@ -915,12 +928,12 @@ function weierstrass(curveDef) {
915
928
  return false;
916
929
  }
917
930
  /**
918
- * ECDH (Elliptic Curve Diffie Hellman) implementation.
919
- * 1. Checks for validity of private key
920
- * 2. Checks for the public key of being on-curve
931
+ * ECDH (Elliptic Curve Diffie Hellman).
932
+ * Computes shared public key from private key and public key.
933
+ * Checks: 1) private key validity 2) shared key is on-curve
921
934
  * @param privateA private key
922
935
  * @param publicB different public key
923
- * @param isCompressed whether to return compact (33-byte), or full (65-byte) key
936
+ * @param isCompressed whether to return compact (default), or full key
924
937
  * @returns shared public key
925
938
  */
926
939
  function getSharedSecret(privateA, publicB, isCompressed = false) {
@@ -934,8 +947,21 @@ function weierstrass(curveDef) {
934
947
  }
935
948
  // RFC6979 methods
936
949
  function bits2int(bytes) {
937
- const slice = bytes.length > Fp.BYTES ? bytes.slice(0, Fp.BYTES) : bytes;
938
- return (0, utils_js_1.bytesToNumberBE)(slice);
950
+ const { nByteLength } = CURVE;
951
+ if (!(bytes instanceof Uint8Array))
952
+ throw new Error('Expected Uint8Array');
953
+ const slice = bytes.length > nByteLength ? bytes.slice(0, nByteLength) : bytes;
954
+ // const slice = bytes; nByteLength; nBitLength;
955
+ let num = ut.bytesToNumberBE(slice);
956
+ // const { nBitLength } = CURVE;
957
+ // const delta = (bytes.length * 8) - nBitLength;
958
+ // if (delta > 0) {
959
+ // // console.log('bits=', bytes.length*8, 'CURVE n=', nBitLength, 'delta=', delta);
960
+ // // console.log(bytes.length, nBitLength, delta);
961
+ // // console.log(bytes, new Error().stack);
962
+ // num >>= BigInt(delta);
963
+ // }
964
+ return num;
939
965
  }
940
966
  function bits2octets(bytes) {
941
967
  const z1 = bits2int(bytes);
@@ -943,7 +969,7 @@ function weierstrass(curveDef) {
943
969
  return int2octets(z2 < _0n ? z1 : z2);
944
970
  }
945
971
  function int2octets(num) {
946
- return numToField(num); // prohibits >32 bytes
972
+ return numToField(num); // prohibits >nByteLength bytes
947
973
  }
948
974
  // Steps A, D of RFC6979 3.2
949
975
  // Creates RFC6979 seed; converts msg/privKey to numbers.
@@ -951,7 +977,7 @@ function weierstrass(curveDef) {
951
977
  if (msgHash == null)
952
978
  throw new Error(`sign: expected valid message hash, not "${msgHash}"`);
953
979
  // Step A is ignored, since we already provide hash instead of msg
954
- const h1 = numToField(truncateHash((0, utils_js_1.ensureBytes)(msgHash)));
980
+ const h1 = numToField(truncateHash(ut.ensureBytes(msgHash)));
955
981
  const d = normalizePrivateKey(privateKey);
956
982
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
957
983
  const seedArgs = [int2octets(d), bits2octets(h1)];
@@ -959,7 +985,7 @@ function weierstrass(curveDef) {
959
985
  if (extraEntropy != null) {
960
986
  if (extraEntropy === true)
961
987
  extraEntropy = CURVE.randomBytes(Fp.BYTES);
962
- const e = (0, utils_js_1.ensureBytes)(extraEntropy);
988
+ const e = ut.ensureBytes(extraEntropy);
963
989
  if (e.length !== Fp.BYTES)
964
990
  throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
965
991
  seedArgs.push(e);
@@ -967,7 +993,7 @@ function weierstrass(curveDef) {
967
993
  // seed is constructed from private key and message
968
994
  // Step D
969
995
  // V, 0x00 are done in HmacDRBG constructor.
970
- const seed = (0, utils_js_1.concatBytes)(...seedArgs);
996
+ const seed = ut.concatBytes(...seedArgs);
971
997
  const m = bits2int(h1);
972
998
  return { seed, m, d };
973
999
  }
@@ -991,10 +1017,11 @@ function weierstrass(curveDef) {
991
1017
  const r = mod.mod(q.x, n);
992
1018
  if (r === _0n)
993
1019
  return;
994
- // s = (1/k * (m + dr) mod n
1020
+ // s = (m + dr)/k mod n where x/k == x*inv(k)
995
1021
  const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n);
996
1022
  if (s === _0n)
997
1023
  return;
1024
+ // recovery bit is usually 0 or 1; rarely it's 2 or 3, when q.x > n
998
1025
  let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n);
999
1026
  let normS = s;
1000
1027
  if (lowS && isBiggerThanHalfOrder(s)) {
@@ -1003,11 +1030,18 @@ function weierstrass(curveDef) {
1003
1030
  }
1004
1031
  return new Signature(r, normS, recovery);
1005
1032
  }
1033
+ const defaultSigOpts = { lowS: CURVE.lowS };
1006
1034
  /**
1007
1035
  * Signs message hash (not message: you need to hash it by yourself).
1036
+ * ```
1037
+ * sign(m, d, k) where
1038
+ * (x, y) = G × k
1039
+ * r = x mod n
1040
+ * s = (m + dr)/k mod n
1041
+ * ```
1008
1042
  * @param opts `lowS, extraEntropy`
1009
1043
  */
1010
- function sign(msgHash, privKey, opts = { lowS: CURVE.lowS }) {
1044
+ function sign(msgHash, privKey, opts = defaultSigOpts) {
1011
1045
  // Steps A, D of RFC6979 3.2.
1012
1046
  const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy);
1013
1047
  // Steps B, C, D, E, F, G
@@ -1019,6 +1053,12 @@ function weierstrass(curveDef) {
1019
1053
  drbg.reseedSync();
1020
1054
  return sig;
1021
1055
  }
1056
+ /**
1057
+ * Signs a message (not message hash).
1058
+ */
1059
+ function signUnhashed(msg, privKey, opts = defaultSigOpts) {
1060
+ return sign(CURVE.hash(ut.ensureBytes(msg)), privKey, opts);
1061
+ }
1022
1062
  // Enable precomputes. Slows down first publicKey computation by 20ms.
1023
1063
  Point.BASE._setWindowSize(8);
1024
1064
  /**
@@ -1051,7 +1091,7 @@ function weierstrass(curveDef) {
1051
1091
  signature = Signature.fromCompact(signature);
1052
1092
  }
1053
1093
  }
1054
- msgHash = (0, utils_js_1.ensureBytes)(msgHash);
1094
+ msgHash = ut.ensureBytes(msgHash);
1055
1095
  }
1056
1096
  catch (error) {
1057
1097
  return false;
@@ -1085,6 +1125,7 @@ function weierstrass(curveDef) {
1085
1125
  getPublicKey,
1086
1126
  getSharedSecret,
1087
1127
  sign,
1128
+ signUnhashed,
1088
1129
  verify,
1089
1130
  Point,
1090
1131
  ProjectivePoint,