@noble/curves 0.3.1 → 0.4.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 (70) hide show
  1. package/README.md +2 -1
  2. package/lib/bls.d.ts +79 -0
  3. package/lib/bls.js +304 -0
  4. package/lib/edwards.d.ts +10 -6
  5. package/lib/edwards.js +16 -11
  6. package/lib/esm/bls.js +300 -0
  7. package/lib/esm/edwards.js +17 -12
  8. package/lib/esm/group.js +2 -2
  9. package/lib/esm/hashToCurve.js +105 -0
  10. package/lib/esm/modular.js +131 -50
  11. package/lib/esm/utils.js +25 -19
  12. package/lib/esm/weierstrass.js +351 -272
  13. package/lib/group.js +2 -2
  14. package/lib/hashToCurve.d.ts +13 -0
  15. package/lib/hashToCurve.js +112 -0
  16. package/lib/modular.d.ts +37 -17
  17. package/lib/modular.js +138 -54
  18. package/lib/utils.d.ts +28 -10
  19. package/lib/utils.js +31 -22
  20. package/lib/weierstrass.d.ts +106 -69
  21. package/lib/weierstrass.js +352 -272
  22. package/package.json +23 -44
  23. package/lib/crypto.d.ts +0 -4
  24. package/lib/crypto.js +0 -8
  25. package/lib/cryptoBrowser.d.ts +0 -4
  26. package/lib/cryptoBrowser.js +0 -7
  27. package/lib/definitions/_shortw_utils.d.ts +0 -63
  28. package/lib/definitions/_shortw_utils.js +0 -18
  29. package/lib/definitions/bn.d.ts +0 -7
  30. package/lib/definitions/bn.js +0 -23
  31. package/lib/definitions/ed25519.d.ts +0 -49
  32. package/lib/definitions/ed25519.js +0 -308
  33. package/lib/definitions/ed448.d.ts +0 -3
  34. package/lib/definitions/ed448.js +0 -127
  35. package/lib/definitions/index.d.ts +0 -0
  36. package/lib/definitions/index.js +0 -2
  37. package/lib/definitions/jubjub.d.ts +0 -7
  38. package/lib/definitions/jubjub.js +0 -55
  39. package/lib/definitions/p192.d.ts +0 -112
  40. package/lib/definitions/p192.js +0 -23
  41. package/lib/definitions/p224.d.ts +0 -112
  42. package/lib/definitions/p224.js +0 -24
  43. package/lib/definitions/p256.d.ts +0 -112
  44. package/lib/definitions/p256.js +0 -23
  45. package/lib/definitions/p384.d.ts +0 -112
  46. package/lib/definitions/p384.js +0 -24
  47. package/lib/definitions/p521.d.ts +0 -113
  48. package/lib/definitions/p521.js +0 -36
  49. package/lib/definitions/pasta.d.ts +0 -2
  50. package/lib/definitions/pasta.js +0 -32
  51. package/lib/definitions/secp256k1.d.ts +0 -87
  52. package/lib/definitions/secp256k1.js +0 -245
  53. package/lib/definitions/stark.d.ts +0 -62
  54. package/lib/definitions/stark.js +0 -248
  55. package/lib/esm/crypto.js +0 -5
  56. package/lib/esm/cryptoBrowser.js +0 -4
  57. package/lib/esm/definitions/_shortw_utils.js +0 -13
  58. package/lib/esm/definitions/bn.js +0 -20
  59. package/lib/esm/definitions/ed25519.js +0 -304
  60. package/lib/esm/definitions/ed448.js +0 -124
  61. package/lib/esm/definitions/index.js +0 -2
  62. package/lib/esm/definitions/jubjub.js +0 -50
  63. package/lib/esm/definitions/p192.js +0 -20
  64. package/lib/esm/definitions/p224.js +0 -21
  65. package/lib/esm/definitions/p256.js +0 -20
  66. package/lib/esm/definitions/p384.js +0 -21
  67. package/lib/esm/definitions/p521.js +0 -33
  68. package/lib/esm/definitions/pasta.js +0 -29
  69. package/lib/esm/definitions/secp256k1.js +0 -241
  70. package/lib/esm/definitions/stark.js +0 -227
@@ -8,42 +8,10 @@
8
8
  // 3. truncateHash() truncateOnly mode
9
9
  // 4. DRBG supports outputLen bigger than outputLen of hmac
10
10
  import * as mod from './modular.js';
11
- import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, validateOpts as utilOpts, randomBytes as utilRandomBytes, } from './utils.js';
11
+ import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, } from './utils.js';
12
+ import * as utils from './utils.js';
13
+ import { hash_to_field, validateHTFOpts } from './hashToCurve.js';
12
14
  import { wNAF } from './group.js';
13
- // Should be separate from overrides, since overrides can use information about curve (for example nBits)
14
- function validateOpts(curve) {
15
- const opts = utilOpts(curve);
16
- for (const i of ['a', 'b']) {
17
- if (typeof opts[i] !== 'bigint')
18
- throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
19
- }
20
- for (const fn of ['hash', 'hmac']) {
21
- if (typeof opts[fn] !== 'function')
22
- throw new Error(`Invalid ${fn} function`);
23
- }
24
- for (const fn of ['randomBytes']) {
25
- if (opts[fn] === undefined)
26
- continue; // Optional
27
- if (typeof opts[fn] !== 'function')
28
- throw new Error(`Invalid ${fn} function`);
29
- }
30
- if (!Number.isSafeInteger(opts.hash.outputLen))
31
- throw new Error('Invalid hash function');
32
- const endo = opts.endo;
33
- if (endo) {
34
- if (opts.a !== _0n) {
35
- throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
36
- }
37
- if (typeof endo !== 'object' ||
38
- typeof endo.beta !== 'bigint' ||
39
- typeof endo.splitScalar !== 'function') {
40
- throw new Error('Expected endomorphism with beta: bigint and splitScalar: function');
41
- }
42
- }
43
- // Set defaults
44
- return Object.freeze({ lowS: true, randomBytes: utilRandomBytes, ...opts });
45
- }
46
- // TODO: convert bits to bytes aligned to 32 bits? (224 for example)
47
15
  // DER encoding utilities
48
16
  class DERError extends Error {
49
17
  constructor(message) {
@@ -90,108 +58,73 @@ const _1n = BigInt(1);
90
58
  const _2n = BigInt(2);
91
59
  const _3n = BigInt(3);
92
60
  const _8n = BigInt(8);
93
- /**
94
- * Minimal HMAC-DRBG (NIST 800-90) for signatures.
95
- * Used only for RFC6979, does not fully implement DRBG spec.
96
- */
97
- class HmacDrbg {
98
- constructor(hashLen, qByteLen, hmacFn) {
99
- this.hashLen = hashLen;
100
- this.qByteLen = qByteLen;
101
- this.hmacFn = hmacFn;
102
- if (typeof hashLen !== 'number' || hashLen < 2)
103
- throw new Error('hashLen must be a number');
104
- if (typeof qByteLen !== 'number' || qByteLen < 2)
105
- throw new Error('qByteLen must be a number');
106
- if (typeof hmacFn !== 'function')
107
- throw new Error('hmacFn must be a function');
108
- // Step B, Step C: set hashLen to 8*ceil(hlen/8)
109
- this.v = new Uint8Array(hashLen).fill(1);
110
- this.k = new Uint8Array(hashLen).fill(0);
111
- this.counter = 0;
112
- }
113
- hmacSync(...values) {
114
- return this.hmacFn(this.k, ...values);
115
- }
116
- incr() {
117
- if (this.counter >= 1000)
118
- throw new Error('Tried 1,000 k values for sign(), all were invalid');
119
- this.counter += 1;
61
+ function validatePointOpts(curve) {
62
+ const opts = utils.validateOpts(curve);
63
+ const Fp = opts.Fp;
64
+ for (const i of ['a', 'b']) {
65
+ if (!Fp.isValid(curve[i]))
66
+ throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`);
120
67
  }
121
- reseedSync(seed = new Uint8Array()) {
122
- this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed);
123
- this.v = this.hmacSync(this.v);
124
- if (seed.length === 0)
125
- return;
126
- this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed);
127
- this.v = this.hmacSync(this.v);
68
+ for (const i of ['isTorsionFree', 'clearCofactor', 'mapToCurve']) {
69
+ if (curve[i] === undefined)
70
+ continue; // Optional
71
+ if (typeof curve[i] !== 'function')
72
+ throw new Error(`Invalid ${i} function`);
128
73
  }
129
- // TODO: review
130
- generateSync() {
131
- this.incr();
132
- let len = 0;
133
- const out = [];
134
- while (len < this.qByteLen) {
135
- this.v = this.hmacSync(this.v);
136
- const sl = this.v.slice();
137
- out.push(sl);
138
- len += this.v.length;
74
+ const endo = opts.endo;
75
+ if (endo) {
76
+ if (!Fp.equals(opts.a, Fp.ZERO)) {
77
+ throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0');
78
+ }
79
+ if (typeof endo !== 'object' ||
80
+ typeof endo.beta !== 'bigint' ||
81
+ typeof endo.splitScalar !== 'function') {
82
+ throw new Error('Expected endomorphism with beta: bigint and splitScalar: function');
139
83
  }
140
- return concatBytes(...out);
141
84
  }
85
+ if (typeof opts.fromBytes !== 'function')
86
+ throw new Error('Invalid fromBytes function');
87
+ if (typeof opts.toBytes !== 'function')
88
+ throw new Error('Invalid fromBytes function');
89
+ // Requires including hashToCurve file
90
+ if (opts.htfDefaults !== undefined)
91
+ validateHTFOpts(opts.htfDefaults);
92
+ // Set defaults
93
+ return Object.freeze({ ...opts });
142
94
  }
143
- // Use only input from curveOpts!
144
- export function weierstrass(curveDef) {
145
- const CURVE = validateOpts(curveDef);
146
- const CURVE_ORDER = CURVE.n;
95
+ export function weierstrassPoints(opts) {
96
+ const CURVE = validatePointOpts(opts);
97
+ const Fp = CURVE.Fp;
147
98
  // Lengths
148
99
  // All curves has same field / group length as for now, but it can be different for other curves
149
- const groupLen = CURVE.nByteLength;
150
- const fieldLen = CURVE.nByteLength; // 32 (length of one field element)
151
- if (fieldLen > 2048)
152
- throw new Error('Field lengths over 2048 are not supported');
153
- const compressedLen = fieldLen + 1; // 33
154
- const uncompressedLen = 2 * fieldLen + 1; // 65
100
+ const { nByteLength, nBitLength } = CURVE;
101
+ const groupLen = nByteLength;
155
102
  // Not using ** operator with bigints for old engines.
156
103
  // 2n ** (8n * 32n) == 2n << (8n * 32n - 1n)
157
- const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n);
158
- function numToFieldStr(num) {
159
- if (typeof num !== 'bigint')
160
- throw new Error('Expected bigint');
161
- if (!(_0n <= num && num < FIELD_MASK))
162
- throw new Error(`Expected number < 2^${fieldLen * 8}`);
163
- return num.toString(16).padStart(2 * fieldLen, '0');
164
- }
165
- function numToField(num) {
166
- const b = hexToBytes(numToFieldStr(num));
167
- if (b.length !== fieldLen)
168
- throw new Error(`Error: expected ${fieldLen} bytes`);
169
- return b;
170
- }
171
- function modP(n, m = CURVE.P) {
172
- return mod.mod(n, m);
173
- }
104
+ //const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n);
105
+ // function numToFieldStr(num: bigint): string {
106
+ // if (typeof num !== 'bigint') throw new Error('Expected bigint');
107
+ // if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`);
108
+ // return num.toString(16).padStart(2 * fieldLen, '0');
109
+ // }
174
110
  /**
175
111
  * y² = x³ + ax + b: Short weierstrass curve formula
176
112
  * @returns y²
177
113
  */
178
114
  function weierstrassEquation(x) {
179
115
  const { a, b } = CURVE;
180
- const x2 = modP(x * x);
181
- const x3 = modP(x2 * x);
182
- return modP(x3 + a * x + b);
116
+ const x2 = Fp.square(x); // x * x
117
+ const x3 = Fp.multiply(x2, x); // x2 * x
118
+ return Fp.add(Fp.add(x3, Fp.multiply(x, a)), b); // x3 + a * x + b
183
119
  }
184
120
  function isWithinCurveOrder(num) {
185
121
  return _0n < num && num < CURVE.n;
186
122
  }
187
- function isValidFieldElement(num) {
188
- return _0n < num && num < CURVE.P;
189
- }
190
123
  function normalizePrivateKey(key) {
191
- let num;
192
124
  if (typeof CURVE.normalizePrivateKey === 'function') {
193
125
  key = CURVE.normalizePrivateKey(key);
194
126
  }
127
+ let num;
195
128
  if (typeof key === 'bigint') {
196
129
  num = key;
197
130
  }
@@ -211,32 +144,12 @@ export function weierstrass(curveDef) {
211
144
  else {
212
145
  throw new TypeError('Expected valid private key');
213
146
  }
147
+ if (CURVE.wrapPrivateKey)
148
+ num = mod.mod(num, CURVE.n);
214
149
  if (!isWithinCurveOrder(num))
215
150
  throw new Error('Expected private key: 0 < key < n');
216
151
  return num;
217
152
  }
218
- /**
219
- * Normalizes hex, bytes, Point to Point. Checks for curve equation.
220
- */
221
- function normalizePublicKey(publicKey) {
222
- if (publicKey instanceof Point) {
223
- publicKey.assertValidity();
224
- return publicKey;
225
- }
226
- else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
227
- return Point.fromHex(publicKey);
228
- // This can happen because PointType can be instance of different class
229
- }
230
- else
231
- throw new Error(`Unknown type of public key: ${publicKey}`);
232
- }
233
- function isBiggerThanHalfOrder(number) {
234
- const HALF = CURVE_ORDER >> _1n;
235
- return number > HALF;
236
- }
237
- function normalizeS(s) {
238
- return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
239
- }
240
153
  function normalizeScalar(num) {
241
154
  if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0)
242
155
  return BigInt(num);
@@ -244,20 +157,6 @@ export function weierstrass(curveDef) {
244
157
  return num;
245
158
  throw new TypeError('Expected valid private scalar: 0 < scalar < curve.n');
246
159
  }
247
- const sqrtModCurve = CURVE.sqrtMod || mod.sqrt;
248
- // Ensures ECDSA message hashes are 32 bytes and < curve order
249
- function _truncateHash(hash, truncateOnly = false) {
250
- const { n, nBitLength } = CURVE;
251
- const byteLength = hash.length;
252
- const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
253
- let h = bytesToNumberBE(hash);
254
- if (delta > 0)
255
- h = h >> BigInt(delta);
256
- if (!truncateOnly && h >= n)
257
- h -= n;
258
- return h;
259
- }
260
- const truncateHash = CURVE.truncateHash || _truncateHash;
261
160
  /**
262
161
  * Jacobian Point works in 3d / jacobi coordinates: (x, y, z) ∋ (x=x/z², y=y/z³)
263
162
  * Default Point works in 2d / affine coordinates: (x, y)
@@ -276,7 +175,7 @@ export function weierstrass(curveDef) {
276
175
  // fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
277
176
  if (p.equals(Point.ZERO))
278
177
  return JacobianPoint.ZERO;
279
- return new JacobianPoint(p.x, p.y, _1n);
178
+ return new JacobianPoint(p.x, p.y, Fp.ONE);
280
179
  }
281
180
  /**
282
181
  * Takes a bunch of Jacobian Points but executes only one
@@ -284,7 +183,7 @@ export function weierstrass(curveDef) {
284
183
  * so this improves performance massively.
285
184
  */
286
185
  static toAffineBatch(points) {
287
- const toInv = mod.invertBatch(points.map((p) => p.z), CURVE.P);
186
+ const toInv = Fp.invertBatch(points.map((p) => p.z));
288
187
  return points.map((p, i) => p.toAffine(toInv[i]));
289
188
  }
290
189
  static normalizeZ(points) {
@@ -298,19 +197,19 @@ export function weierstrass(curveDef) {
298
197
  throw new TypeError('JacobianPoint expected');
299
198
  const { x: X1, y: Y1, z: Z1 } = this;
300
199
  const { x: X2, y: Y2, z: Z2 } = other;
301
- const Z1Z1 = modP(Z1 * Z1);
302
- const Z2Z2 = modP(Z2 * Z2);
303
- const U1 = modP(X1 * Z2Z2);
304
- const U2 = modP(X2 * Z1Z1);
305
- const S1 = modP(modP(Y1 * Z2) * Z2Z2);
306
- const S2 = modP(modP(Y2 * Z1) * Z1Z1);
307
- return U1 === U2 && S1 === S2;
200
+ const Z1Z1 = Fp.square(Z1); // Z1 * Z1
201
+ const Z2Z2 = Fp.square(Z2); // Z2 * Z2
202
+ const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2
203
+ const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1
204
+ const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2
205
+ const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1
206
+ return Fp.equals(U1, U2) && Fp.equals(S1, S2);
308
207
  }
309
208
  /**
310
209
  * Flips point to one corresponding to (x, -y) in Affine coordinates.
311
210
  */
312
211
  negate() {
313
- return new JacobianPoint(this.x, modP(-this.y), this.z);
212
+ return new JacobianPoint(this.x, Fp.negate(this.y), this.z);
314
213
  }
315
214
  // Fast algo for doubling 2 Jacobian Points.
316
215
  // From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
@@ -321,31 +220,31 @@ export function weierstrass(curveDef) {
321
220
  // Faster algorithm: when a=0
322
221
  // From: https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
323
222
  // Cost: 2M + 5S + 6add + 3*2 + 1*3 + 1*8.
324
- if (a === _0n) {
325
- const A = modP(X1 * X1);
326
- const B = modP(Y1 * Y1);
327
- const C = modP(B * B);
328
- const x1b = X1 + B;
329
- const D = modP(_2n * (modP(x1b * x1b) - A - C));
330
- const E = modP(_3n * A);
331
- const F = modP(E * E);
332
- const X3 = modP(F - _2n * D);
333
- const Y3 = modP(E * (D - X3) - _8n * C);
334
- const Z3 = modP(_2n * Y1 * Z1);
223
+ if (Fp.isZero(a)) {
224
+ const A = Fp.square(X1); // X1 * X1
225
+ const B = Fp.square(Y1); // Y1 * Y1
226
+ const C = Fp.square(B); // B * B
227
+ const x1b = Fp.addN(X1, B); // X1 + B
228
+ const D = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(x1b), A), C), _2n); // ((x1b * x1b) - A - C) * 2
229
+ const E = Fp.multiply(A, _3n); // A * 3
230
+ const F = Fp.square(E); // E * E
231
+ const X3 = Fp.subtract(F, Fp.multiplyN(D, _2n)); // F - 2 * D
232
+ const Y3 = Fp.subtract(Fp.multiplyN(E, Fp.subtractN(D, X3)), Fp.multiplyN(C, _8n)); // E * (D - X3) - 8 * C;
233
+ const Z3 = Fp.multiply(Fp.multiplyN(Y1, _2n), Z1); // 2 * Y1 * Z1
335
234
  return new JacobianPoint(X3, Y3, Z3);
336
235
  }
337
- const XX = modP(X1 * X1);
338
- const YY = modP(Y1 * Y1);
339
- const YYYY = modP(YY * YY);
340
- const ZZ = modP(Z1 * Z1);
341
- const tmp1 = modP(X1 + YY);
342
- const S = modP(_2n * (modP(tmp1 * tmp1) - XX - YYYY)); // 2*((X1+YY)^2-XX-YYYY)
343
- const M = modP(_3n * XX + a * modP(ZZ * ZZ));
344
- const T = modP(modP(M * M) - _2n * S); // M^2-2*S
236
+ const XX = Fp.square(X1); // X1 * X1
237
+ const YY = Fp.square(Y1); // Y1 * Y1
238
+ const YYYY = Fp.square(YY); // YY * YY
239
+ const ZZ = Fp.square(Z1); // Z1 * Z1
240
+ const tmp1 = Fp.add(X1, YY); // X1 + YY
241
+ const S = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(tmp1), XX), YYYY), _2n); // 2*((X1+YY)^2-XX-YYYY)
242
+ const M = Fp.add(Fp.multiplyN(XX, _3n), Fp.multiplyN(Fp.square(ZZ), a)); // 3 * XX + a * ZZ^2
243
+ const T = Fp.subtract(Fp.square(M), Fp.multiplyN(S, _2n)); // M^2-2*S
345
244
  const X3 = T;
346
- const Y3 = modP(M * (S - T) - _8n * YYYY); // M*(S-T)-8*YYYY
347
- const y1az1 = modP(Y1 + Z1); // (Y1+Z1)
348
- const Z3 = modP(modP(y1az1 * y1az1) - YY - ZZ); // (Y1+Z1)^2-YY-ZZ
245
+ const Y3 = Fp.subtract(Fp.multiplyN(M, Fp.subtractN(S, T)), Fp.multiplyN(YYYY, _8n)); // M*(S-T)-8*YYYY
246
+ const y1az1 = Fp.add(Y1, Z1); // (Y1+Z1)
247
+ const Z3 = Fp.subtract(Fp.subtractN(Fp.square(y1az1), YY), ZZ); // (Y1+Z1)^2-YY-ZZ
349
248
  return new JacobianPoint(X3, Y3, Z3);
350
249
  }
351
250
  // Fast algo for adding 2 Jacobian Points.
@@ -357,28 +256,28 @@ export function weierstrass(curveDef) {
357
256
  throw new TypeError('JacobianPoint expected');
358
257
  const { x: X1, y: Y1, z: Z1 } = this;
359
258
  const { x: X2, y: Y2, z: Z2 } = other;
360
- if (X2 === _0n || Y2 === _0n)
259
+ if (Fp.isZero(X2) || Fp.isZero(Y2))
361
260
  return this;
362
- if (X1 === _0n || Y1 === _0n)
261
+ if (Fp.isZero(X1) || Fp.isZero(Y1))
363
262
  return other;
364
263
  // We're using same code in equals()
365
- const Z1Z1 = modP(Z1 * Z1); // Z1Z1 = Z1^2
366
- const Z2Z2 = modP(Z2 * Z2); // Z2Z2 = Z2^2;
367
- const U1 = modP(X1 * Z2Z2); // X1 * Z2Z2
368
- const U2 = modP(X2 * Z1Z1); // X2 * Z1Z1
369
- const S1 = modP(modP(Y1 * Z2) * Z2Z2); // Y1 * Z2 * Z2Z2
370
- const S2 = modP(modP(Y2 * Z1) * Z1Z1); // Y2 * Z1 * Z1Z1
371
- const H = modP(U2 - U1); // H = U2 - U1
372
- const r = modP(S2 - S1); // S2 - S1
264
+ const Z1Z1 = Fp.square(Z1); // Z1Z1 = Z1^2
265
+ const Z2Z2 = Fp.square(Z2); // Z2Z2 = Z2^2;
266
+ const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2
267
+ const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1
268
+ const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2
269
+ const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1
270
+ const H = Fp.subtractN(U2, U1); // H = U2 - U1
271
+ const r = Fp.subtractN(S2, S1); // S2 - S1
373
272
  // H = 0 meaning it's the same point.
374
- if (H === _0n)
375
- return r === _0n ? this.double() : JacobianPoint.ZERO;
376
- const HH = modP(H * H); // HH = H2
377
- const HHH = modP(H * HH); // HHH = H * HH
378
- const V = modP(U1 * HH); // V = U1 * HH
379
- const X3 = modP(r * r - HHH - _2n * V); // X3 = r^2 - HHH - 2 * V;
380
- const Y3 = modP(r * (V - X3) - S1 * HHH); // Y3 = r * (V - X3) - S1 * HHH;
381
- const Z3 = modP(Z1 * Z2 * H); // Z3 = Z1 * Z2 * H;
273
+ if (Fp.isZero(H))
274
+ return Fp.isZero(r) ? this.double() : JacobianPoint.ZERO;
275
+ const HH = Fp.square(H); // HH = H2
276
+ const HHH = Fp.multiply(H, HH); // HHH = H * HH
277
+ const V = Fp.multiply(U1, HH); // V = U1 * HH
278
+ const X3 = Fp.subtract(Fp.subtractN(Fp.squareN(r), HHH), Fp.multiplyN(V, _2n)); // X3 = r^2 - HHH - 2 * V;
279
+ const Y3 = Fp.subtract(Fp.multiplyN(r, Fp.subtractN(V, X3)), Fp.multiplyN(S1, HHH)); // Y3 = r * (V - X3) - S1 * HHH;
280
+ const Z3 = Fp.multiply(Fp.multiply(Z1, Z2), H); // Z3 = Z1 * Z2 * H;
382
281
  return new JacobianPoint(X3, Y3, Z3);
383
282
  }
384
283
  subtract(other) {
@@ -417,7 +316,7 @@ export function weierstrass(curveDef) {
417
316
  k1p = k1p.negate();
418
317
  if (k2neg)
419
318
  k2p = k2p.negate();
420
- k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z);
319
+ k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
421
320
  return k1p.add(k2p);
422
321
  }
423
322
  /**
@@ -458,7 +357,7 @@ export function weierstrass(curveDef) {
458
357
  let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
459
358
  k1p = wnaf.constTimeNegate(k1neg, k1p);
460
359
  k2p = wnaf.constTimeNegate(k2neg, k2p);
461
- k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z);
360
+ k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
462
361
  point = k1p.add(k2p);
463
362
  fake = f1p.add(f2p);
464
363
  }
@@ -478,25 +377,42 @@ export function weierstrass(curveDef) {
478
377
  const { x, y, z } = this;
479
378
  const is0 = this.equals(JacobianPoint.ZERO);
480
379
  // If invZ was 0, we return zero point. However we still want to execute
481
- // all operations, so we replace invZ with a random number, 8.
380
+ // all operations, so we replace invZ with a random number, 1.
482
381
  if (invZ == null)
483
- invZ = is0 ? _8n : mod.invert(z, CURVE.P);
382
+ invZ = is0 ? Fp.ONE : Fp.invert(z);
484
383
  const iz1 = invZ;
485
- const iz2 = modP(iz1 * iz1);
486
- const iz3 = modP(iz2 * iz1);
487
- const ax = modP(x * iz2);
488
- const ay = modP(y * iz3);
489
- const zz = modP(z * iz1);
384
+ const iz2 = Fp.square(iz1); // iz1 * iz1
385
+ const iz3 = Fp.multiply(iz2, iz1); // iz2 * iz1
386
+ const ax = Fp.multiply(x, iz2); // x * iz2
387
+ const ay = Fp.multiply(y, iz3); // y * iz3
388
+ const zz = Fp.multiply(z, iz1); // z * iz1
490
389
  if (is0)
491
390
  return Point.ZERO;
492
- if (zz !== _1n)
391
+ if (!Fp.equals(zz, Fp.ONE))
493
392
  throw new Error('invZ was invalid');
494
393
  return new Point(ax, ay);
495
394
  }
395
+ isTorsionFree() {
396
+ if (CURVE.h === _1n)
397
+ return true; // No subgroups, always torsion fee
398
+ if (CURVE.isTorsionFree)
399
+ return CURVE.isTorsionFree(JacobianPoint, this);
400
+ // is multiplyUnsafe(CURVE.n) is always ok, same as for edwards?
401
+ throw new Error('Unsupported!');
402
+ }
403
+ // Clear cofactor of G1
404
+ // https://eprint.iacr.org/2019/403
405
+ clearCofactor() {
406
+ if (CURVE.h === _1n)
407
+ return this; // Fast-path
408
+ if (CURVE.clearCofactor)
409
+ return CURVE.clearCofactor(JacobianPoint, this);
410
+ return this.multiplyUnsafe(CURVE.h);
411
+ }
496
412
  }
497
- JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n);
498
- JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n);
499
- const wnaf = wNAF(JacobianPoint, CURVE.endo ? CURVE.nBitLength / 2 : CURVE.nBitLength);
413
+ JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
414
+ JacobianPoint.ZERO = new JacobianPoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
415
+ const wnaf = wNAF(JacobianPoint, CURVE.endo ? nBitLength / 2 : nBitLength);
500
416
  // Stores precomputed values for points.
501
417
  const pointPrecomputes = new WeakMap();
502
418
  /**
@@ -514,87 +430,60 @@ export function weierstrass(curveDef) {
514
430
  }
515
431
  // Checks for y % 2 == 0
516
432
  hasEvenY() {
517
- return this.y % _2n === _0n;
518
- }
519
- /**
520
- * Supports compressed ECDSA points
521
- * @returns Point instance
522
- */
523
- static fromCompressedHex(bytes) {
524
- const P = CURVE.P;
525
- const x = bytesToNumberBE(bytes.subarray(1));
526
- if (!isValidFieldElement(x))
527
- throw new Error('Point is not on curve');
528
- const y2 = weierstrassEquation(x); // y² = x³ + ax + b
529
- let y = sqrtModCurve(y2, P); // y = y² ^ (p+1)/4
530
- const isYOdd = (y & _1n) === _1n;
531
- // ECDSA
532
- const isFirstByteOdd = (bytes[0] & 1) === 1;
533
- if (isFirstByteOdd !== isYOdd)
534
- y = modP(-y);
535
- const point = new Point(x, y);
536
- point.assertValidity();
537
- return point;
538
- }
539
- static fromUncompressedHex(bytes) {
540
- const x = bytesToNumberBE(bytes.subarray(1, fieldLen + 1));
541
- const y = bytesToNumberBE(bytes.subarray(fieldLen + 1, 2 * fieldLen + 1));
542
- const point = new Point(x, y);
543
- point.assertValidity();
544
- return point;
433
+ if (Fp.isOdd)
434
+ return !Fp.isOdd(this.y);
435
+ throw new Error("Field doesn't support isOdd");
545
436
  }
546
437
  /**
547
438
  * Converts hash string or Uint8Array to Point.
548
439
  * @param hex short/long ECDSA hex
549
440
  */
550
441
  static fromHex(hex) {
551
- const bytes = ensureBytes(hex);
552
- const len = bytes.length;
553
- const header = bytes[0];
554
- // this.assertValidity() is done inside of those two functions
555
- if (len === compressedLen && (header === 0x02 || header === 0x03))
556
- return this.fromCompressedHex(bytes);
557
- if (len === uncompressedLen && header === 0x04)
558
- return this.fromUncompressedHex(bytes);
559
- throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`);
442
+ const { x, y } = CURVE.fromBytes(ensureBytes(hex));
443
+ const point = new Point(x, y);
444
+ point.assertValidity();
445
+ return point;
560
446
  }
561
447
  // Multiplies generator point by privateKey.
562
448
  static fromPrivateKey(privateKey) {
563
449
  return Point.BASE.multiply(normalizePrivateKey(privateKey));
564
450
  }
565
451
  toRawBytes(isCompressed = false) {
566
- return hexToBytes(this.toHex(isCompressed));
452
+ this.assertValidity();
453
+ return CURVE.toBytes(Point, this, isCompressed);
567
454
  }
568
455
  toHex(isCompressed = false) {
569
- const x = numToFieldStr(this.x);
570
- if (isCompressed) {
571
- const prefix = this.hasEvenY() ? '02' : '03';
572
- return `${prefix}${x}`;
573
- }
574
- else {
575
- return `04${x}${numToFieldStr(this.y)}`;
576
- }
456
+ return bytesToHex(this.toRawBytes(isCompressed));
577
457
  }
578
458
  // A point on curve is valid if it conforms to equation.
579
459
  assertValidity() {
460
+ // Zero is valid point too!
461
+ if (this.equals(Point.ZERO)) {
462
+ if (CURVE.allowInfinityPoint)
463
+ return;
464
+ throw new Error('Point is infinity');
465
+ }
580
466
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
581
467
  const msg = 'Point is not on elliptic curve';
582
468
  const { x, y } = this;
583
- if (!isValidFieldElement(x) || !isValidFieldElement(y))
469
+ if (!Fp.isValid(x) || !Fp.isValid(y))
584
470
  throw new Error(msg);
585
- const left = modP(y * y);
471
+ const left = Fp.square(y);
586
472
  const right = weierstrassEquation(x);
587
- if (modP(left - right) !== _0n)
473
+ if (!Fp.equals(left, right))
588
474
  throw new Error(msg);
475
+ // TODO: flag to disable this?
476
+ if (!this.isTorsionFree())
477
+ throw new Error('Point must be of prime-order subgroup');
589
478
  }
590
479
  equals(other) {
591
480
  if (!(other instanceof Point))
592
481
  throw new TypeError('Point#equals: expected Point');
593
- return this.x === other.x && this.y === other.y;
482
+ return Fp.equals(this.x, other.x) && Fp.equals(this.y, other.y);
594
483
  }
595
484
  // Returns the same point with inverted `y`
596
485
  negate() {
597
- return new Point(this.x, modP(-this.y));
486
+ return new Point(this.x, Fp.negate(this.y));
598
487
  }
599
488
  // Adds point to itself
600
489
  double() {
@@ -611,6 +500,15 @@ export function weierstrass(curveDef) {
611
500
  multiply(scalar) {
612
501
  return JacobianPoint.fromAffine(this).multiply(scalar, this).toAffine();
613
502
  }
503
+ multiplyUnsafe(scalar) {
504
+ return JacobianPoint.fromAffine(this).multiplyUnsafe(scalar).toAffine();
505
+ }
506
+ clearCofactor() {
507
+ return JacobianPoint.fromAffine(this).clearCofactor().toAffine();
508
+ }
509
+ isTorsionFree() {
510
+ return JacobianPoint.fromAffine(this).isTorsionFree();
511
+ }
614
512
  /**
615
513
  * Efficiently calculate `aP + bQ`.
616
514
  * Unsafe, can expose private key, if used incorrectly.
@@ -624,6 +522,27 @@ export function weierstrass(curveDef) {
624
522
  const sum = aP.add(bQ);
625
523
  return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine();
626
524
  }
525
+ // Encodes byte string to elliptic curve
526
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
527
+ static hashToCurve(msg, options) {
528
+ if (!CURVE.mapToCurve)
529
+ throw new Error('No mapToCurve defined for curve');
530
+ msg = ensureBytes(msg);
531
+ const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options });
532
+ const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]);
533
+ const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]);
534
+ const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor();
535
+ return p;
536
+ }
537
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
538
+ static encodeToCurve(msg, options) {
539
+ if (!CURVE.mapToCurve)
540
+ throw new Error('No mapToCurve defined for curve');
541
+ msg = ensureBytes(msg);
542
+ const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options });
543
+ const { x, y } = CURVE.mapToCurve(u[0]);
544
+ return new Point(x, y).clearCofactor();
545
+ }
627
546
  }
628
547
  /**
629
548
  * Base point aka generator. public_key = Point.BASE * private_key
@@ -632,7 +551,167 @@ export function weierstrass(curveDef) {
632
551
  /**
633
552
  * Identity point aka point at infinity. point = point + zero_point
634
553
  */
635
- Point.ZERO = new Point(_0n, _0n);
554
+ Point.ZERO = new Point(Fp.ZERO, Fp.ZERO);
555
+ return {
556
+ Point: Point,
557
+ JacobianPoint: JacobianPoint,
558
+ normalizePrivateKey,
559
+ weierstrassEquation,
560
+ isWithinCurveOrder,
561
+ };
562
+ }
563
+ function validateOpts(curve) {
564
+ const opts = utils.validateOpts(curve);
565
+ if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
566
+ throw new Error('Invalid hash function');
567
+ if (typeof opts.hmac !== 'function')
568
+ throw new Error('Invalid hmac function');
569
+ if (typeof opts.randomBytes !== 'function')
570
+ throw new Error('Invalid randomBytes function');
571
+ // Set defaults
572
+ return Object.freeze({ lowS: true, ...opts });
573
+ }
574
+ /**
575
+ * Minimal HMAC-DRBG (NIST 800-90) for signatures.
576
+ * Used only for RFC6979, does not fully implement DRBG spec.
577
+ */
578
+ class HmacDrbg {
579
+ constructor(hashLen, qByteLen, hmacFn) {
580
+ this.hashLen = hashLen;
581
+ this.qByteLen = qByteLen;
582
+ this.hmacFn = hmacFn;
583
+ if (typeof hashLen !== 'number' || hashLen < 2)
584
+ throw new Error('hashLen must be a number');
585
+ if (typeof qByteLen !== 'number' || qByteLen < 2)
586
+ throw new Error('qByteLen must be a number');
587
+ if (typeof hmacFn !== 'function')
588
+ throw new Error('hmacFn must be a function');
589
+ // Step B, Step C: set hashLen to 8*ceil(hlen/8)
590
+ this.v = new Uint8Array(hashLen).fill(1);
591
+ this.k = new Uint8Array(hashLen).fill(0);
592
+ this.counter = 0;
593
+ }
594
+ hmacSync(...values) {
595
+ return this.hmacFn(this.k, ...values);
596
+ }
597
+ incr() {
598
+ if (this.counter >= 1000)
599
+ throw new Error('Tried 1,000 k values for sign(), all were invalid');
600
+ this.counter += 1;
601
+ }
602
+ reseedSync(seed = new Uint8Array()) {
603
+ this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed);
604
+ this.v = this.hmacSync(this.v);
605
+ if (seed.length === 0)
606
+ return;
607
+ this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed);
608
+ this.v = this.hmacSync(this.v);
609
+ }
610
+ // TODO: review
611
+ generateSync() {
612
+ this.incr();
613
+ let len = 0;
614
+ const out = [];
615
+ while (len < this.qByteLen) {
616
+ this.v = this.hmacSync(this.v);
617
+ const sl = this.v.slice();
618
+ out.push(sl);
619
+ len += this.v.length;
620
+ }
621
+ return concatBytes(...out);
622
+ }
623
+ }
624
+ export function weierstrass(curveDef) {
625
+ const CURVE = validateOpts(curveDef);
626
+ const CURVE_ORDER = CURVE.n;
627
+ const Fp = CURVE.Fp;
628
+ const compressedLen = Fp.BYTES + 1; // 33
629
+ const uncompressedLen = 2 * Fp.BYTES + 1; // 65
630
+ function isValidFieldElement(num) {
631
+ // 0 is disallowed by arbitrary reasons. Probably because infinity point?
632
+ return _0n < num && num < Fp.ORDER;
633
+ }
634
+ const { Point, JacobianPoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({
635
+ ...CURVE,
636
+ toBytes(c, point, isCompressed) {
637
+ if (isCompressed) {
638
+ return concatBytes(new Uint8Array([point.hasEvenY() ? 0x02 : 0x03]), Fp.toBytes(point.x));
639
+ }
640
+ else {
641
+ return concatBytes(new Uint8Array([0x04]), Fp.toBytes(point.x), Fp.toBytes(point.y));
642
+ }
643
+ },
644
+ fromBytes(bytes) {
645
+ const len = bytes.length;
646
+ const header = bytes[0];
647
+ // this.assertValidity() is done inside of fromHex
648
+ if (len === compressedLen && (header === 0x02 || header === 0x03)) {
649
+ const x = bytesToNumberBE(bytes.subarray(1));
650
+ if (!isValidFieldElement(x))
651
+ throw new Error('Point is not on curve');
652
+ const y2 = weierstrassEquation(x); // y² = x³ + ax + b
653
+ let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
654
+ const isYOdd = (y & _1n) === _1n;
655
+ // ECDSA
656
+ const isFirstByteOdd = (bytes[0] & 1) === 1;
657
+ if (isFirstByteOdd !== isYOdd)
658
+ y = Fp.negate(y);
659
+ return { x, y };
660
+ }
661
+ else if (len === uncompressedLen && header === 0x04) {
662
+ const x = Fp.fromBytes(bytes.subarray(1, Fp.BYTES + 1));
663
+ const y = Fp.fromBytes(bytes.subarray(Fp.BYTES + 1, 2 * Fp.BYTES + 1));
664
+ return { x, y };
665
+ }
666
+ else {
667
+ throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`);
668
+ }
669
+ },
670
+ });
671
+ // Do we need these functions at all?
672
+ function numToField(num) {
673
+ if (typeof num !== 'bigint')
674
+ throw new Error('Expected bigint');
675
+ if (!(_0n <= num && num < Fp.MASK))
676
+ throw new Error(`Expected number < 2^${Fp.BYTES * 8}`);
677
+ return Fp.toBytes(num);
678
+ }
679
+ const numToFieldStr = (num) => bytesToHex(numToField(num));
680
+ /**
681
+ * Normalizes hex, bytes, Point to Point. Checks for curve equation.
682
+ */
683
+ function normalizePublicKey(publicKey) {
684
+ if (publicKey instanceof Point) {
685
+ publicKey.assertValidity();
686
+ return publicKey;
687
+ }
688
+ else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
689
+ return Point.fromHex(publicKey);
690
+ // This can happen because PointType can be instance of different class
691
+ }
692
+ else
693
+ throw new Error(`Unknown type of public key: ${publicKey}`);
694
+ }
695
+ function isBiggerThanHalfOrder(number) {
696
+ const HALF = CURVE_ORDER >> _1n;
697
+ return number > HALF;
698
+ }
699
+ function normalizeS(s) {
700
+ return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s;
701
+ }
702
+ // Ensures ECDSA message hashes are 32 bytes and < curve order
703
+ function _truncateHash(hash, truncateOnly = false) {
704
+ const { n, nBitLength } = CURVE;
705
+ const byteLength = hash.length;
706
+ const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits)
707
+ let h = bytesToNumberBE(hash);
708
+ if (delta > 0)
709
+ h = h >> BigInt(delta);
710
+ if (!truncateOnly && h >= n)
711
+ h -= n;
712
+ return h;
713
+ }
714
+ const truncateHash = CURVE.truncateHash || _truncateHash;
636
715
  /**
637
716
  * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
638
717
  */
@@ -743,8 +822,8 @@ export function weierstrass(curveDef) {
743
822
  }
744
823
  }
745
824
  const utils = {
746
- mod: modP,
747
- invert: (n, m = CURVE.P) => mod.invert(n, m),
825
+ mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo),
826
+ invert: Fp.invert,
748
827
  isValidPrivateKey(privateKey) {
749
828
  try {
750
829
  normalizePrivateKey(privateKey);
@@ -769,7 +848,7 @@ export function weierstrass(curveDef) {
769
848
  * Produces cryptographically secure private key from random of size (nBitLength+64)
770
849
  * as per FIPS 186 B.4.1 with modulo bias being neglible.
771
850
  */
772
- randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(fieldLen + 8)),
851
+ randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(Fp.BYTES + 8)),
773
852
  /**
774
853
  * 1. Returns cached point which you can use to pass to `getSharedSecret` or `#multiply` by it.
775
854
  * 2. Precomputes point multiplication table. Is done by default on first `getPublicKey()` call.
@@ -829,7 +908,7 @@ export function weierstrass(curveDef) {
829
908
  }
830
909
  // RFC6979 methods
831
910
  function bits2int(bytes) {
832
- const slice = bytes.length > fieldLen ? bytes.slice(0, fieldLen) : bytes;
911
+ const slice = bytes.length > Fp.BYTES ? bytes.slice(0, Fp.BYTES) : bytes;
833
912
  return bytesToNumberBE(slice);
834
913
  }
835
914
  function bits2octets(bytes) {
@@ -853,10 +932,10 @@ export function weierstrass(curveDef) {
853
932
  // RFC6979 3.6: additional k' could be provided
854
933
  if (extraEntropy != null) {
855
934
  if (extraEntropy === true)
856
- extraEntropy = CURVE.randomBytes(fieldLen);
935
+ extraEntropy = CURVE.randomBytes(Fp.BYTES);
857
936
  const e = ensureBytes(extraEntropy);
858
- if (e.length !== fieldLen)
859
- throw new Error(`sign: Expected ${fieldLen} bytes of extra data`);
937
+ if (e.length !== Fp.BYTES)
938
+ throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`);
860
939
  seedArgs.push(e);
861
940
  }
862
941
  // seed is constructed from private key and message