@noble/curves 0.5.2 → 0.6.0

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 (59) hide show
  1. package/README.md +49 -5
  2. package/lib/_shortw_utils.d.ts +10 -21
  3. package/lib/abstract/bls.d.ts +39 -32
  4. package/lib/abstract/bls.js +74 -73
  5. package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
  6. package/lib/abstract/{group.js → curve.js} +39 -2
  7. package/lib/abstract/edwards.d.ts +30 -72
  8. package/lib/abstract/edwards.js +197 -375
  9. package/lib/abstract/hash-to-curve.d.ts +25 -6
  10. package/lib/abstract/hash-to-curve.js +40 -12
  11. package/lib/abstract/modular.d.ts +20 -7
  12. package/lib/abstract/modular.js +61 -35
  13. package/lib/abstract/montgomery.js +4 -5
  14. package/lib/abstract/poseidon.d.ts +29 -0
  15. package/lib/abstract/poseidon.js +115 -0
  16. package/lib/abstract/utils.d.ts +5 -36
  17. package/lib/abstract/utils.js +23 -71
  18. package/lib/abstract/weierstrass.d.ts +51 -74
  19. package/lib/abstract/weierstrass.js +455 -628
  20. package/lib/bls12-381.js +63 -58
  21. package/lib/bn.js +1 -1
  22. package/lib/ed25519.d.ts +7 -5
  23. package/lib/ed25519.js +82 -79
  24. package/lib/ed448.d.ts +3 -0
  25. package/lib/ed448.js +86 -83
  26. package/lib/esm/abstract/bls.js +75 -74
  27. package/lib/esm/abstract/{group.js → curve.js} +37 -1
  28. package/lib/esm/abstract/edwards.js +196 -374
  29. package/lib/esm/abstract/hash-to-curve.js +38 -11
  30. package/lib/esm/abstract/modular.js +58 -34
  31. package/lib/esm/abstract/montgomery.js +5 -6
  32. package/lib/esm/abstract/poseidon.js +109 -0
  33. package/lib/esm/abstract/utils.js +21 -66
  34. package/lib/esm/abstract/weierstrass.js +454 -627
  35. package/lib/esm/bls12-381.js +75 -70
  36. package/lib/esm/bn.js +1 -1
  37. package/lib/esm/ed25519.js +80 -78
  38. package/lib/esm/ed448.js +84 -82
  39. package/lib/esm/jubjub.js +1 -1
  40. package/lib/esm/p256.js +11 -9
  41. package/lib/esm/p384.js +11 -9
  42. package/lib/esm/p521.js +13 -12
  43. package/lib/esm/secp256k1.js +115 -151
  44. package/lib/esm/stark.js +104 -40
  45. package/lib/jubjub.d.ts +2 -2
  46. package/lib/jubjub.js +1 -1
  47. package/lib/p192.d.ts +20 -42
  48. package/lib/p224.d.ts +20 -42
  49. package/lib/p256.d.ts +23 -42
  50. package/lib/p256.js +13 -10
  51. package/lib/p384.d.ts +23 -42
  52. package/lib/p384.js +13 -10
  53. package/lib/p521.d.ts +23 -42
  54. package/lib/p521.js +15 -13
  55. package/lib/secp256k1.d.ts +25 -37
  56. package/lib/secp256k1.js +115 -151
  57. package/lib/stark.d.ts +36 -19
  58. package/lib/stark.js +107 -40
  59. package/package.json +13 -8
@@ -1,28 +1,19 @@
1
1
  "use strict";
2
- /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
3
- // Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
4
2
  Object.defineProperty(exports, "__esModule", { value: true });
5
3
  exports.twistedEdwards = void 0;
6
- // Differences from @noble/ed25519 1.7:
7
- // 1. Variable field element lengths between EDDSA/ECDH:
8
- // EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes
9
- // 2. Different addition formula (doubling is same)
10
- // 3. uvRatio differs between curves (half-expected, not only pow fn changes)
11
- // 4. Point decompression code is different (unexpected), now using generalized formula
12
- // 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448
13
- const mod = require("./modular.js");
14
- const ut = require("./utils.js");
4
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
+ // Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y²
6
+ const modular_js_1 = require("./modular.js");
15
7
  const utils_js_1 = require("./utils.js");
16
- const group_js_1 = require("./group.js");
17
- const hash_to_curve_js_1 = require("./hash-to-curve.js");
8
+ const curve_js_1 = require("./curve.js");
18
9
  // Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
19
10
  const _0n = BigInt(0);
20
11
  const _1n = BigInt(1);
21
12
  const _2n = BigInt(2);
22
13
  const _8n = BigInt(8);
23
14
  function validateOpts(curve) {
24
- const opts = ut.validateOpts(curve);
25
- if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen))
15
+ const opts = (0, curve_js_1.validateAbsOpts)(curve);
16
+ if (typeof opts.hash !== 'function')
26
17
  throw new Error('Invalid hash function');
27
18
  for (const i of ['a', 'd']) {
28
19
  const val = opts[i];
@@ -39,25 +30,20 @@ function validateOpts(curve) {
39
30
  if (typeof opts[fn] !== 'function')
40
31
  throw new Error(`Invalid ${fn} function`);
41
32
  }
42
- if (opts.htfDefaults !== undefined)
43
- (0, hash_to_curve_js_1.validateHTFOpts)(opts.htfDefaults);
44
33
  // Set defaults
45
34
  return Object.freeze({ ...opts });
46
35
  }
47
- // NOTE: it is not generic twisted curve for now, but ed25519/ed448 generic implementation
36
+ // It is not generic twisted curve for now, but ed25519/ed448 generic implementation
48
37
  function twistedEdwards(curveDef) {
49
38
  const CURVE = validateOpts(curveDef);
50
- const Fp = CURVE.Fp;
51
- const CURVE_ORDER = CURVE.n;
52
- const maxGroupElement = _2n ** BigInt(CURVE.nByteLength * 8);
53
- // Function overrides
54
- const { randomBytes } = CURVE;
55
- const modP = Fp.create;
39
+ const { Fp, n: CURVE_ORDER, preHash, hash: cHash, randomBytes, nByteLength, h: cofactor } = CURVE;
40
+ const MASK = _2n ** BigInt(nByteLength * 8);
41
+ const modP = Fp.create; // Function overrides
56
42
  // sqrt(u/v)
57
43
  const uvRatio = CURVE.uvRatio ||
58
44
  ((u, v) => {
59
45
  try {
60
- return { isValid: true, value: Fp.sqrt(u * Fp.invert(v)) };
46
+ return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
61
47
  }
62
48
  catch (e) {
63
49
  return { isValid: false, value: _0n };
@@ -70,57 +56,89 @@ function twistedEdwards(curveDef) {
70
56
  throw new Error('Contexts/pre-hash are not supported');
71
57
  return data;
72
58
  }); // NOOP
73
- /**
74
- * Extended Point works in extended coordinates: (x, y, z, t) (x=x/z, y=y/z, t=xy).
75
- * Default Point works in affine coordinates: (x, y)
76
- * https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
77
- */
78
- class ExtendedPoint {
79
- constructor(x, y, z, t) {
80
- this.x = x;
81
- this.y = y;
82
- this.z = z;
83
- this.t = t;
59
+ const inBig = (n) => typeof n === 'bigint' && 0n < n; // n in [1..]
60
+ const inRange = (n, max) => inBig(n) && inBig(max) && n < max; // n in [1..max-1]
61
+ const in0MaskRange = (n) => n === _0n || inRange(n, MASK); // n in [0..MASK-1]
62
+ function assertInRange(n, max) {
63
+ // n in [1..max-1]
64
+ if (inRange(n, max))
65
+ return n;
66
+ throw new Error(`Expected valid scalar < ${max}, got ${typeof n} ${n}`);
67
+ }
68
+ function assertGE0(n) {
69
+ // n in [0..CURVE_ORDER-1]
70
+ return n === _0n ? n : assertInRange(n, CURVE_ORDER); // GE = prime subgroup, not full group
71
+ }
72
+ const pointPrecomputes = new Map();
73
+ function isPoint(other) {
74
+ if (!(other instanceof Point))
75
+ throw new Error('ExtendedPoint expected');
76
+ }
77
+ // Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
78
+ // https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
79
+ class Point {
80
+ constructor(ex, ey, ez, et) {
81
+ this.ex = ex;
82
+ this.ey = ey;
83
+ this.ez = ez;
84
+ this.et = et;
85
+ if (!in0MaskRange(ex))
86
+ throw new Error('x required');
87
+ if (!in0MaskRange(ey))
88
+ throw new Error('y required');
89
+ if (!in0MaskRange(ez))
90
+ throw new Error('z required');
91
+ if (!in0MaskRange(et))
92
+ throw new Error('t required');
93
+ }
94
+ get x() {
95
+ return this.toAffine().x;
96
+ }
97
+ get y() {
98
+ return this.toAffine().y;
84
99
  }
85
100
  static fromAffine(p) {
86
- if (!(p instanceof Point)) {
87
- throw new TypeError('ExtendedPoint#fromAffine: expected Point');
88
- }
89
- if (p.equals(Point.ZERO))
90
- return ExtendedPoint.ZERO;
91
- return new ExtendedPoint(p.x, p.y, _1n, modP(p.x * p.y));
92
- }
93
- // Takes a bunch of Jacobian Points but executes only one
94
- // invert on all of them. invert is very slow operation,
95
- // so this improves performance massively.
96
- static toAffineBatch(points) {
97
- const toInv = Fp.invertBatch(points.map((p) => p.z));
98
- return points.map((p, i) => p.toAffine(toInv[i]));
101
+ if (p instanceof Point)
102
+ throw new Error('extended point not allowed');
103
+ const { x, y } = p || {};
104
+ if (!in0MaskRange(x) || !in0MaskRange(y))
105
+ throw new Error('invalid affine point');
106
+ return new Point(x, y, _1n, modP(x * y));
99
107
  }
100
108
  static normalizeZ(points) {
101
- return this.toAffineBatch(points).map(this.fromAffine);
109
+ const toInv = Fp.invertBatch(points.map((p) => p.ez));
110
+ return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
111
+ }
112
+ // "Private method", don't use it directly
113
+ _setWindowSize(windowSize) {
114
+ this._WINDOW_SIZE = windowSize;
115
+ pointPrecomputes.delete(this);
102
116
  }
117
+ assertValidity() { }
103
118
  // Compare one point to another.
104
119
  equals(other) {
105
- assertExtPoint(other);
106
- const { x: X1, y: Y1, z: Z1 } = this;
107
- const { x: X2, y: Y2, z: Z2 } = other;
120
+ isPoint(other);
121
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
122
+ const { ex: X2, ey: Y2, ez: Z2 } = other;
108
123
  const X1Z2 = modP(X1 * Z2);
109
124
  const X2Z1 = modP(X2 * Z1);
110
125
  const Y1Z2 = modP(Y1 * Z2);
111
126
  const Y2Z1 = modP(Y2 * Z1);
112
127
  return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
113
128
  }
114
- // Inverses point to one corresponding to (x, -y) in Affine coordinates.
129
+ is0() {
130
+ return this.equals(Point.ZERO);
131
+ }
115
132
  negate() {
116
- return new ExtendedPoint(modP(-this.x), this.y, this.z, modP(-this.t));
133
+ // Flips point sign to a negative one (-x, y in affine coords)
134
+ return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
117
135
  }
118
136
  // Fast algo for doubling Extended Point.
119
137
  // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
120
138
  // Cost: 4M + 4S + 1*a + 6add + 1*2.
121
139
  double() {
122
140
  const { a } = CURVE;
123
- const { x: X1, y: Y1, z: Z1 } = this;
141
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
124
142
  const A = modP(X1 * X1); // A = X12
125
143
  const B = modP(Y1 * Y1); // B = Y12
126
144
  const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
@@ -134,16 +152,16 @@ function twistedEdwards(curveDef) {
134
152
  const Y3 = modP(G * H); // Y3 = G*H
135
153
  const T3 = modP(E * H); // T3 = E*H
136
154
  const Z3 = modP(F * G); // Z3 = F*G
137
- return new ExtendedPoint(X3, Y3, Z3, T3);
155
+ return new Point(X3, Y3, Z3, T3);
138
156
  }
139
157
  // Fast algo for adding 2 Extended Points.
140
158
  // https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
141
159
  // Cost: 9M + 1*a + 1*d + 7add.
142
160
  add(other) {
143
- assertExtPoint(other);
161
+ isPoint(other);
144
162
  const { a, d } = CURVE;
145
- const { x: X1, y: Y1, z: Z1, t: T1 } = this;
146
- const { x: X2, y: Y2, z: Z2, t: T2 } = other;
163
+ const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
164
+ const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
147
165
  // Faster algo for adding 2 Extended Points when curve's a=-1.
148
166
  // http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
149
167
  // Cost: 8M + 8add + 2*2.
@@ -163,7 +181,7 @@ function twistedEdwards(curveDef) {
163
181
  const Y3 = modP(G * H);
164
182
  const T3 = modP(E * H);
165
183
  const Z3 = modP(F * G);
166
- return new ExtendedPoint(X3, Y3, Z3, T3);
184
+ return new Point(X3, Y3, Z3, T3);
167
185
  }
168
186
  const A = modP(X1 * X2); // A = X1*X2
169
187
  const B = modP(Y1 * Y2); // B = Y1*Y2
@@ -177,44 +195,30 @@ function twistedEdwards(curveDef) {
177
195
  const Y3 = modP(G * H); // Y3 = G*H
178
196
  const T3 = modP(E * H); // T3 = E*H
179
197
  const Z3 = modP(F * G); // Z3 = F*G
180
- return new ExtendedPoint(X3, Y3, Z3, T3);
198
+ return new Point(X3, Y3, Z3, T3);
181
199
  }
182
200
  subtract(other) {
183
201
  return this.add(other.negate());
184
202
  }
185
- wNAF(n, affinePoint) {
186
- if (!affinePoint && this.equals(ExtendedPoint.BASE))
187
- affinePoint = Point.BASE;
188
- const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
189
- let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
190
- if (!precomputes) {
191
- precomputes = wnaf.precomputeWindow(this, W);
192
- if (affinePoint && W !== 1) {
193
- precomputes = ExtendedPoint.normalizeZ(precomputes);
194
- pointPrecomputes.set(affinePoint, precomputes);
195
- }
196
- }
197
- const { p, f } = wnaf.wNAF(W, precomputes, n);
198
- return ExtendedPoint.normalizeZ([p, f])[0];
203
+ wNAF(n) {
204
+ return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
199
205
  }
200
- // Constant time multiplication.
201
- // Uses wNAF method. Windowed method may be 10% faster,
202
- // but takes 2x longer to generate and consumes 2x memory.
203
- multiply(scalar, affinePoint) {
204
- return this.wNAF(normalizeScalar(scalar, CURVE_ORDER), affinePoint);
206
+ // Constant-time multiplication.
207
+ multiply(scalar) {
208
+ const { p, f } = this.wNAF(assertInRange(scalar, CURVE_ORDER));
209
+ return Point.normalizeZ([p, f])[0];
205
210
  }
206
211
  // Non-constant-time multiplication. Uses double-and-add algorithm.
207
212
  // It's faster, but should only be used when you don't care about
208
213
  // an exposed private key e.g. sig verification.
209
214
  multiplyUnsafe(scalar) {
210
- let n = normalizeScalar(scalar, CURVE_ORDER, false);
211
- const P0 = ExtendedPoint.ZERO;
215
+ let n = assertGE0(scalar);
212
216
  if (n === _0n)
213
- return P0;
214
- if (this.equals(P0) || n === _1n)
217
+ return I;
218
+ if (this.equals(I) || n === _1n)
215
219
  return this;
216
- if (this.equals(ExtendedPoint.BASE))
217
- return this.wNAF(n);
220
+ if (this.equals(G))
221
+ return this.wNAF(n).p;
218
222
  return wnaf.unsafeLadder(this, n);
219
223
  }
220
224
  // Checks if point is of small order.
@@ -222,28 +226,28 @@ function twistedEdwards(curveDef) {
222
226
  // point with torsion component.
223
227
  // Multiplies point by cofactor and checks if the result is 0.
224
228
  isSmallOrder() {
225
- return this.multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO);
229
+ return this.multiplyUnsafe(cofactor).is0();
226
230
  }
227
- // Multiplies point by curve order (very big scalar CURVE.n) and checks if the result is 0.
231
+ // Multiplies point by curve order and checks if the result is 0.
228
232
  // Returns `false` is the point is dirty.
229
233
  isTorsionFree() {
230
- return wnaf.unsafeLadder(this, CURVE_ORDER).equals(ExtendedPoint.ZERO);
234
+ return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
231
235
  }
232
236
  // Converts Extended point to default (x, y) coordinates.
233
237
  // Can accept precomputed Z^-1 - for example, from invertBatch.
234
- toAffine(invZ) {
235
- const { x, y, z } = this;
236
- const is0 = this.equals(ExtendedPoint.ZERO);
237
- if (invZ == null)
238
- invZ = is0 ? _8n : Fp.invert(z); // 8 was chosen arbitrarily
239
- const ax = modP(x * invZ);
240
- const ay = modP(y * invZ);
241
- const zz = modP(z * invZ);
238
+ toAffine(iz) {
239
+ const { ex: x, ey: y, ez: z } = this;
240
+ const is0 = this.is0();
241
+ if (iz == null)
242
+ iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
243
+ const ax = modP(x * iz);
244
+ const ay = modP(y * iz);
245
+ const zz = modP(z * iz);
242
246
  if (is0)
243
- return Point.ZERO;
247
+ return { x: _0n, y: _1n };
244
248
  if (zz !== _1n)
245
249
  throw new Error('invZ was invalid');
246
- return new Point(ax, ay);
250
+ return { x: ax, y: ay };
247
251
  }
248
252
  clearCofactor() {
249
253
  const { h: cofactor } = CURVE;
@@ -251,306 +255,127 @@ function twistedEdwards(curveDef) {
251
255
  return this;
252
256
  return this.multiplyUnsafe(cofactor);
253
257
  }
254
- }
255
- ExtendedPoint.BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
256
- ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n);
257
- const wnaf = (0, group_js_1.wNAF)(ExtendedPoint, CURVE.nByteLength * 8);
258
- function assertExtPoint(other) {
259
- if (!(other instanceof ExtendedPoint))
260
- throw new TypeError('ExtendedPoint expected');
261
- }
262
- // Stores precomputed values for points.
263
- const pointPrecomputes = new WeakMap();
264
- /**
265
- * Default Point works in affine coordinates: (x, y)
266
- */
267
- class Point {
268
- constructor(x, y) {
269
- this.x = x;
270
- this.y = y;
271
- }
272
- // "Private method", don't use it directly.
273
- _setWindowSize(windowSize) {
274
- this._WINDOW_SIZE = windowSize;
275
- pointPrecomputes.delete(this);
276
- }
277
258
  // Converts hash string or Uint8Array to Point.
278
259
  // Uses algo from RFC8032 5.1.3.
279
260
  static fromHex(hex, strict = true) {
280
261
  const { d, a } = CURVE;
281
262
  const len = Fp.BYTES;
282
- hex = (0, utils_js_1.ensureBytes)(hex, len);
283
- // 1. First, interpret the string as an integer in little-endian
284
- // representation. Bit 255 of this number is the least significant
285
- // bit of the x-coordinate and denote this value x_0. The
286
- // y-coordinate is recovered simply by clearing this bit. If the
287
- // resulting value is >= p, decoding fails.
288
- const normed = hex.slice();
289
- const lastByte = hex[len - 1];
290
- normed[len - 1] = lastByte & ~0x80;
291
- const y = ut.bytesToNumberLE(normed);
292
- if (strict && y >= Fp.ORDER)
293
- throw new Error('Expected 0 < hex < P');
294
- if (!strict && y >= maxGroupElement)
295
- throw new Error('Expected 0 < hex < CURVE.n');
296
- // 2. To recover the x-coordinate, the curve equation implies
297
- // Ed25519: x² = (y² - 1) / (d y² + 1) (mod p).
298
- // Ed448: x² = (- 1) / (d y² - 1) (mod p).
299
- // For generic case:
300
- // a*x²+y²=1+d*x²*y²
301
- // -> y²-1 = d*x²*y²-a*x²
302
- // -> y²-1 = x² (d*y²-a)
303
- // -> x² = (y²-1) / (d*y²-a)
304
- // The denominator is always non-zero mod p. Let u = y² - 1 and v = d y² + 1.
305
- const y2 = modP(y * y);
306
- const u = modP(y2 - _1n);
307
- const v = modP(d * y2 - a);
308
- let { isValid, value: x } = uvRatio(u, v);
263
+ hex = (0, utils_js_1.ensureBytes)(hex, len); // copy hex to a new array
264
+ const normed = hex.slice(); // copy again, we'll manipulate it
265
+ const lastByte = hex[len - 1]; // select last byte
266
+ normed[len - 1] = lastByte & ~0x80; // clear last bit
267
+ const y = (0, utils_js_1.bytesToNumberLE)(normed);
268
+ if (y === _0n) {
269
+ // y=0 is allowed
270
+ }
271
+ else {
272
+ // RFC8032 prohibits >= p, but ZIP215 doesn't
273
+ if (strict)
274
+ assertInRange(y, Fp.ORDER); // strict=true [1..P-1] (2^255-19-1 for ed25519)
275
+ else
276
+ assertInRange(y, MASK); // strict=false [1..MASK-1] (2^256-1 for ed25519)
277
+ }
278
+ // Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
279
+ // ax²+y²=1+dx² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
280
+ const y2 = modP(y * y); // denominator is always non-0 mod p.
281
+ const u = modP(y2 - _1n); // u = y² - 1
282
+ const v = modP(d * y2 - a); // v = d + 1.
283
+ let { isValid, value: x } = uvRatio(u, v); // √(u/v)
309
284
  if (!isValid)
310
285
  throw new Error('Point.fromHex: invalid y coordinate');
311
- // 4. Finally, use the x_0 bit to select the right square root. If
312
- // x = 0, and x_0 = 1, decoding fails. Otherwise, if x_0 != x mod
313
- // 2, set x <-- p - x. Return the decoded point (x,y).
314
- const isXOdd = (x & _1n) === _1n;
315
- const isLastByteOdd = (lastByte & 0x80) !== 0;
286
+ const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
287
+ const isLastByteOdd = (lastByte & 0x80) !== 0; // if x=0 and x_0 = 1, fail
316
288
  if (isLastByteOdd !== isXOdd)
317
- x = modP(-x);
318
- return new Point(x, y);
289
+ x = modP(-x); // if x_0 != x mod 2, set x = p-x
290
+ return Point.fromAffine({ x, y });
319
291
  }
320
- static fromPrivateKey(privateKey) {
321
- return getExtendedPublicKey(privateKey).point;
292
+ static fromPrivateKey(privKey) {
293
+ return getExtendedPublicKey(privKey).point;
322
294
  }
323
- // There can always be only two x values (x, -x) for any y
324
- // When compressing point, it's enough to only store its y coordinate
325
- // and use the last byte to encode sign of x.
326
295
  toRawBytes() {
327
- const bytes = ut.numberToBytesLE(this.y, Fp.BYTES);
328
- bytes[Fp.BYTES - 1] |= this.x & _1n ? 0x80 : 0;
329
- return bytes;
296
+ const { x, y } = this.toAffine();
297
+ const bytes = (0, utils_js_1.numberToBytesLE)(y, Fp.BYTES); // each y has 2 x values (x, -y)
298
+ bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
299
+ return bytes; // and use the last byte to encode sign of x
330
300
  }
331
- // Same as toRawBytes, but returns string.
332
301
  toHex() {
333
- return ut.bytesToHex(this.toRawBytes());
334
- }
335
- // Determines if point is in prime-order subgroup.
336
- // Returns `false` is the point is dirty.
337
- isTorsionFree() {
338
- return ExtendedPoint.fromAffine(this).isTorsionFree();
339
- }
340
- equals(other) {
341
- if (!(other instanceof Point))
342
- throw new TypeError('Point#equals: expected Point');
343
- return this.x === other.x && this.y === other.y;
344
- }
345
- negate() {
346
- return new Point(modP(-this.x), this.y);
347
- }
348
- double() {
349
- return ExtendedPoint.fromAffine(this).double().toAffine();
350
- }
351
- add(other) {
352
- return ExtendedPoint.fromAffine(this).add(ExtendedPoint.fromAffine(other)).toAffine();
353
- }
354
- subtract(other) {
355
- return this.add(other.negate());
356
- }
357
- /**
358
- * Constant time multiplication.
359
- * @param scalar Big-Endian number
360
- * @returns new point
361
- */
362
- multiply(scalar) {
363
- return ExtendedPoint.fromAffine(this).multiply(scalar, this).toAffine();
364
- }
365
- clearCofactor() {
366
- return ExtendedPoint.fromAffine(this).clearCofactor().toAffine();
367
- }
368
- // Encodes byte string to elliptic curve
369
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
370
- static hashToCurve(msg, options) {
371
- const { mapToCurve, htfDefaults } = CURVE;
372
- if (!mapToCurve)
373
- throw new Error('No mapToCurve defined for curve');
374
- const u = (0, hash_to_curve_js_1.hash_to_field)((0, utils_js_1.ensureBytes)(msg), 2, { ...htfDefaults, ...options });
375
- const { x: x0, y: y0 } = mapToCurve(u[0]);
376
- const { x: x1, y: y1 } = mapToCurve(u[1]);
377
- const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
378
- return p;
379
- }
380
- // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
381
- static encodeToCurve(msg, options) {
382
- const { mapToCurve, htfDefaults } = CURVE;
383
- if (!mapToCurve)
384
- throw new Error('No mapToCurve defined for curve');
385
- const u = (0, hash_to_curve_js_1.hash_to_field)((0, utils_js_1.ensureBytes)(msg), 1, { ...htfDefaults, ...options });
386
- const { x, y } = mapToCurve(u[0]);
387
- return new Point(x, y).clearCofactor();
302
+ return (0, utils_js_1.bytesToHex)(this.toRawBytes()); // Same as toRawBytes, but returns string.
388
303
  }
389
304
  }
390
- // Base point aka generator
391
- // public_key = Point.BASE * private_key
392
- Point.BASE = new Point(CURVE.Gx, CURVE.Gy);
393
- // Identity point aka point at infinity
394
- // point = point + zero_point
395
- Point.ZERO = new Point(_0n, _1n);
396
- /**
397
- * EDDSA signature.
398
- */
399
- class Signature {
400
- constructor(r, s) {
401
- this.r = r;
402
- this.s = s;
403
- this.assertValidity();
404
- }
405
- static fromHex(hex) {
406
- const len = Fp.BYTES;
407
- const bytes = (0, utils_js_1.ensureBytes)(hex, 2 * len);
408
- const r = Point.fromHex(bytes.slice(0, len), false);
409
- const s = ut.bytesToNumberLE(bytes.slice(len, 2 * len));
410
- return new Signature(r, s);
411
- }
412
- assertValidity() {
413
- const { r, s } = this;
414
- if (!(r instanceof Point))
415
- throw new Error('Expected Point instance');
416
- // 0 <= s < l
417
- normalizeScalar(s, CURVE_ORDER, false);
418
- return this;
419
- }
420
- toRawBytes() {
421
- return ut.concatBytes(this.r.toRawBytes(), ut.numberToBytesLE(this.s, Fp.BYTES));
422
- }
423
- toHex() {
424
- return ut.bytesToHex(this.toRawBytes());
425
- }
305
+ Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
306
+ Point.ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
307
+ const { BASE: G, ZERO: I } = Point;
308
+ const wnaf = (0, curve_js_1.wNAF)(Point, nByteLength * 8);
309
+ function modN(a) {
310
+ return (0, modular_js_1.mod)(a, CURVE_ORDER);
426
311
  }
427
312
  // Little-endian SHA512 with modulo n
428
- function modnLE(hash) {
429
- return mod.mod(ut.bytesToNumberLE(hash), CURVE_ORDER);
313
+ function modN_LE(hash) {
314
+ return modN((0, utils_js_1.bytesToNumberLE)(hash));
430
315
  }
431
- /**
432
- * Checks for num to be in range:
433
- * For strict == true: `0 < num < max`.
434
- * For strict == false: `0 <= num < max`.
435
- * Converts non-float safe numbers to bigints.
436
- */
437
- function normalizeScalar(num, max, strict = true) {
438
- if (!max)
439
- throw new TypeError('Specify max value');
440
- if (ut.isPositiveInt(num))
441
- num = BigInt(num);
442
- if (typeof num === 'bigint' && num < max) {
443
- if (strict) {
444
- if (_0n < num)
445
- return num;
446
- }
447
- else {
448
- if (_0n <= num)
449
- return num;
450
- }
451
- }
452
- throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`);
316
+ function isHex(item, err) {
317
+ if (typeof item !== 'string' && !(item instanceof Uint8Array))
318
+ throw new Error(`${err} must be hex string or Uint8Array`);
453
319
  }
454
320
  /** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
455
321
  function getExtendedPublicKey(key) {
456
- const groupLen = CURVE.nByteLength;
457
- // Normalize bigint / number / string to Uint8Array
458
- const keyb = typeof key === 'bigint' || typeof key === 'number'
459
- ? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen)
460
- : key;
322
+ isHex(key, 'private key');
323
+ const len = nByteLength;
461
324
  // Hash private key with curve's hash function to produce uniformingly random input
462
- // We check byte lengths e.g.: ensureBytes(64, hash(ensureBytes(32, key)))
463
- const hashed = (0, utils_js_1.ensureBytes)(CURVE.hash((0, utils_js_1.ensureBytes)(keyb, groupLen)), 2 * groupLen);
464
- // First half's bits are cleared to produce a random field element.
465
- const head = adjustScalarBytes(hashed.slice(0, groupLen));
466
- // Second half is called key prefix (5.1.6)
467
- const prefix = hashed.slice(groupLen, 2 * groupLen);
468
- // The actual private scalar
469
- const scalar = modnLE(head);
470
- // Point on Edwards curve aka public key
471
- const point = Point.BASE.multiply(scalar);
472
- // Uint8Array representation
473
- const pointBytes = point.toRawBytes();
325
+ // Check byte lengths: ensure(64, h(ensure(32, key)))
326
+ const hashed = (0, utils_js_1.ensureBytes)(cHash((0, utils_js_1.ensureBytes)(key, len)), 2 * len);
327
+ const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
328
+ const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
329
+ const scalar = modN_LE(head); // The actual private scalar
330
+ const point = G.multiply(scalar); // Point on Edwards curve aka public key
331
+ const pointBytes = point.toRawBytes(); // Uint8Array representation
474
332
  return { head, prefix, scalar, point, pointBytes };
475
333
  }
476
- /**
477
- * Calculates ed25519 public key. RFC8032 5.1.5
478
- * 1. private key is hashed with sha512, then first 32 bytes are taken from the hash
479
- * 2. 3 least significant bits of the first byte are cleared
480
- */
481
- function getPublicKey(privateKey) {
482
- return getExtendedPublicKey(privateKey).pointBytes;
334
+ // Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
335
+ function getPublicKey(privKey) {
336
+ return getExtendedPublicKey(privKey).pointBytes;
483
337
  }
484
- const EMPTY = new Uint8Array();
485
- function hashDomainToScalar(message, context = EMPTY) {
486
- context = (0, utils_js_1.ensureBytes)(context);
487
- return modnLE(CURVE.hash(domain(message, context, !!CURVE.preHash)));
338
+ // int('LE', SHA512(dom2(F, C) || msgs)) mod N
339
+ function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
340
+ const msg = (0, utils_js_1.concatBytes)(...msgs);
341
+ return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)(context), !!preHash)));
488
342
  }
489
343
  /** Signs message with privateKey. RFC8032 5.1.6 */
490
- function sign(message, privateKey, context) {
491
- message = (0, utils_js_1.ensureBytes)(message);
492
- if (CURVE.preHash)
493
- message = CURVE.preHash(message);
494
- const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey);
495
- const r = hashDomainToScalar(ut.concatBytes(prefix, message), context);
496
- const R = Point.BASE.multiply(r); // R = rG
497
- const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg)
498
- const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp
499
- return new Signature(R, s).toRawBytes();
344
+ function sign(msg, privKey, context) {
345
+ isHex(msg, 'message');
346
+ msg = (0, utils_js_1.ensureBytes)(msg);
347
+ if (preHash)
348
+ msg = preHash(msg); // for ed25519ph etc.
349
+ const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
350
+ const r = hashDomainToScalar(context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
351
+ const R = G.multiply(r).toRawBytes(); // R = rG
352
+ const k = hashDomainToScalar(context, R, pointBytes, msg); // R || A || PH(M)
353
+ const s = modN(r + k * scalar); // S = (r + k * s) mod L
354
+ assertGE0(s); // 0 <= s < l
355
+ const res = (0, utils_js_1.concatBytes)(R, (0, utils_js_1.numberToBytesLE)(s, Fp.BYTES));
356
+ return (0, utils_js_1.ensureBytes)(res, nByteLength * 2); // 64-byte signature
500
357
  }
501
- /**
502
- * Verifies EdDSA signature against message and public key.
503
- * An extended group equation is checked.
504
- * RFC8032 5.1.7
505
- * Compliant with ZIP215:
506
- * 0 <= sig.R/publicKey < 2**256 (can be >= curve.P)
507
- * 0 <= sig.s < l
508
- * Not compliant with RFC8032: it's not possible to comply to both ZIP & RFC at the same time.
509
- */
510
- function verify(sig, message, publicKey, context) {
511
- message = (0, utils_js_1.ensureBytes)(message);
512
- if (CURVE.preHash)
513
- message = CURVE.preHash(message);
514
- // When hex is passed, we check public key fully.
515
- // When Point instance is passed, we assume it has already been checked, for performance.
516
- // If user passes Point/Sig instance, we assume it has been already verified.
517
- // We don't check its equations for performance. We do check for valid bounds for s though
518
- // We always check for: a) s bounds. b) hex validity
519
- if (publicKey instanceof Point) {
520
- // ignore
521
- }
522
- else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
523
- publicKey = Point.fromHex(publicKey, false);
524
- }
525
- else {
526
- throw new Error(`Invalid publicKey: ${publicKey}`);
527
- }
528
- if (sig instanceof Signature)
529
- sig.assertValidity();
530
- else if (sig instanceof Uint8Array || typeof sig === 'string')
531
- sig = Signature.fromHex(sig);
532
- else
533
- throw new Error(`Wrong signature: ${sig}`);
534
- const { r, s } = sig;
535
- const SB = ExtendedPoint.BASE.multiplyUnsafe(s);
536
- const k = hashDomainToScalar(ut.concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context);
537
- const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k);
538
- const RkA = ExtendedPoint.fromAffine(r).add(kA);
358
+ function verify(sig, msg, publicKey, context) {
359
+ isHex(sig, 'sig');
360
+ isHex(msg, 'message');
361
+ const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
362
+ sig = (0, utils_js_1.ensureBytes)(sig, 2 * len); // An extended group equation is checked.
363
+ msg = (0, utils_js_1.ensureBytes)(msg); // ZIP215 compliant, which means not fully RFC8032 compliant.
364
+ if (preHash)
365
+ msg = preHash(msg); // for ed25519ph, etc
366
+ const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
367
+ const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P
368
+ const s = (0, utils_js_1.bytesToNumberLE)(sig.slice(len, 2 * len)); // 0 <= s < l
369
+ const SB = G.multiplyUnsafe(s);
370
+ const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
371
+ const RkA = R.add(A.multiplyUnsafe(k));
539
372
  // [8][S]B = [8]R + [8][k]A'
540
- return RkA.subtract(SB).clearCofactor().equals(ExtendedPoint.ZERO);
373
+ return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
541
374
  }
542
- // Enable precomputes. Slows down first publicKey computation by 20ms.
543
- Point.BASE._setWindowSize(8);
375
+ G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
544
376
  const utils = {
545
377
  getExtendedPublicKey,
546
- /**
547
- * Not needed for ed25519 private keys. Needed if you use scalars directly (rare).
548
- */
549
- hashToPrivateScalar: (hash) => ut.hashToPrivateScalar(hash, CURVE_ORDER, true),
550
- /**
551
- * ed25519 private keys are uniform 32-bit strings. We do not need to check for
552
- * modulo bias like we do in secp256k1 randomPrivateKey()
553
- */
378
+ // ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
554
379
  randomPrivateKey: () => randomBytes(Fp.BYTES),
555
380
  /**
556
381
  * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
@@ -559,10 +384,9 @@ function twistedEdwards(curveDef) {
559
384
  * @param windowSize 2, 4, 8, 16
560
385
  */
561
386
  precompute(windowSize = 8, point = Point.BASE) {
562
- const cached = point.equals(Point.BASE) ? point : new Point(point.x, point.y);
563
- cached._setWindowSize(windowSize);
564
- cached.multiply(_2n);
565
- return cached;
387
+ point._setWindowSize(windowSize);
388
+ point.multiply(BigInt(3));
389
+ return point;
566
390
  },
567
391
  };
568
392
  return {
@@ -570,9 +394,7 @@ function twistedEdwards(curveDef) {
570
394
  getPublicKey,
571
395
  sign,
572
396
  verify,
573
- ExtendedPoint,
574
- Point,
575
- Signature,
397
+ ExtendedPoint: Point,
576
398
  utils,
577
399
  };
578
400
  }