@noble/curves 0.5.2 → 0.6.1

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