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