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