@noble/curves 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +62 -14
  2. package/lib/_shortw_utils.d.ts +2 -6
  3. package/lib/abstract/bls.d.ts +17 -8
  4. package/lib/abstract/bls.js +15 -78
  5. package/lib/abstract/edwards.d.ts +7 -16
  6. package/lib/abstract/edwards.js +89 -106
  7. package/lib/abstract/hash-to-curve.d.ts +10 -1
  8. package/lib/abstract/hash-to-curve.js +32 -10
  9. package/lib/abstract/modular.d.ts +8 -17
  10. package/lib/abstract/modular.js +134 -152
  11. package/lib/abstract/montgomery.js +1 -1
  12. package/lib/abstract/utils.d.ts +8 -4
  13. package/lib/abstract/utils.js +22 -14
  14. package/lib/abstract/weierstrass.d.ts +8 -8
  15. package/lib/abstract/weierstrass.js +209 -168
  16. package/lib/bls12-381.d.ts +2 -1
  17. package/lib/bls12-381.js +14 -9
  18. package/lib/ed25519.js +75 -12
  19. package/lib/ed448.js +86 -2
  20. package/lib/esm/abstract/bls.js +19 -82
  21. package/lib/esm/abstract/edwards.js +90 -107
  22. package/lib/esm/abstract/hash-to-curve.js +30 -9
  23. package/lib/esm/abstract/modular.js +128 -148
  24. package/lib/esm/abstract/montgomery.js +2 -4
  25. package/lib/esm/abstract/utils.js +20 -13
  26. package/lib/esm/abstract/weierstrass.js +210 -169
  27. package/lib/esm/bls12-381.js +13 -8
  28. package/lib/esm/ed25519.js +76 -13
  29. package/lib/esm/ed448.js +87 -3
  30. package/lib/esm/jubjub.js +5 -4
  31. package/lib/esm/p256.js +1 -1
  32. package/lib/esm/p384.js +1 -1
  33. package/lib/esm/p521.js +1 -1
  34. package/lib/esm/secp256k1.js +27 -27
  35. package/lib/esm/stark.js +5 -2
  36. package/lib/jubjub.d.ts +1 -0
  37. package/lib/jubjub.js +5 -4
  38. package/lib/p192.d.ts +4 -12
  39. package/lib/p224.d.ts +4 -12
  40. package/lib/p256.d.ts +4 -12
  41. package/lib/p256.js +1 -1
  42. package/lib/p384.d.ts +4 -12
  43. package/lib/p384.js +1 -1
  44. package/lib/p521.d.ts +4 -12
  45. package/lib/p521.js +1 -1
  46. package/lib/secp256k1.d.ts +2 -6
  47. package/lib/secp256k1.js +27 -27
  48. package/lib/stark.d.ts +1 -3
  49. package/lib/stark.js +5 -2
  50. package/package.json +2 -2
@@ -1,10 +1,11 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // TODO: remove circular imports
2
3
  import * as utils from './utils.js';
3
4
  // Utilities for modular arithmetics and finite fields
4
5
  // prettier-ignore
5
6
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
6
7
  // prettier-ignore
7
- const _4n = BigInt(4), _5n = BigInt(5), _7n = BigInt(7), _8n = BigInt(8);
8
+ const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
8
9
  // prettier-ignore
9
10
  const _9n = BigInt(9), _16n = BigInt(16);
10
11
  // Calculates a modulo b
@@ -54,6 +55,7 @@ export function invert(number, modulo) {
54
55
  // prettier-ignore
55
56
  let x = _0n, y = _1n, u = _1n, v = _0n;
56
57
  while (a !== _0n) {
58
+ // JIT applies optimization if those two lines follow each other
57
59
  const q = b / a;
58
60
  const r = b % a;
59
61
  const m = x - u * q;
@@ -66,25 +68,68 @@ export function invert(number, modulo) {
66
68
  throw new Error('invert: does not exist');
67
69
  return mod(x, modulo);
68
70
  }
69
- /**
70
- * Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p).
71
- * * (a | p) 1 if a is a square (mod p)
72
- * * (a | p) ≡ -1 if a is not a square (mod p)
73
- * * (a | p) 0 if a 0 (mod p)
74
- */
75
- export function legendre(num, fieldPrime) {
76
- return pow(num, (fieldPrime - _1n) / _2n, fieldPrime);
71
+ // Tonelli-Shanks algorithm
72
+ // Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
73
+ // Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
74
+ export function tonelliShanks(P) {
75
+ // Legendre constant: used to calculate Legendre symbol (a | p),
76
+ // which denotes the value of a^((p-1)/2) (mod p).
77
+ // (a | p) ≡ 1 if a is a square (mod p)
78
+ // (a | p) ≡ -1 if a is not a square (mod p)
79
+ // (a | p) ≡ 0 if a ≡ 0 (mod p)
80
+ const legendreC = (P - _1n) / _2n;
81
+ let Q, S, Z;
82
+ // Step 1: By factoring out powers of 2 from p - 1,
83
+ // find q and s such that p - 1 = q*(2^s) with q odd
84
+ for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++)
85
+ ;
86
+ // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
87
+ for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++)
88
+ ;
89
+ // Fast-path
90
+ if (S === 1) {
91
+ const p1div4 = (P + _1n) / _4n;
92
+ return function tonelliFast(Fp, n) {
93
+ const root = Fp.pow(n, p1div4);
94
+ if (!Fp.equals(Fp.square(root), n))
95
+ throw new Error('Cannot find square root');
96
+ return root;
97
+ };
98
+ }
99
+ // Slow-path
100
+ const Q1div2 = (Q + _1n) / _2n;
101
+ return function tonelliSlow(Fp, n) {
102
+ // Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
103
+ if (Fp.pow(n, legendreC) === Fp.negate(Fp.ONE))
104
+ throw new Error('Cannot find square root');
105
+ let r = S;
106
+ // TODO: will fail at Fp2/etc
107
+ let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
108
+ let x = Fp.pow(n, Q1div2); // first guess at the square root
109
+ let b = Fp.pow(n, Q); // first guess at the fudge factor
110
+ while (!Fp.equals(b, Fp.ONE)) {
111
+ if (Fp.equals(b, Fp.ZERO))
112
+ return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
113
+ // Find m such b^(2^m)==1
114
+ let m = 1;
115
+ for (let t2 = Fp.square(b); m < r; m++) {
116
+ if (Fp.equals(t2, Fp.ONE))
117
+ break;
118
+ t2 = Fp.square(t2); // t2 *= t2
119
+ }
120
+ // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
121
+ const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
122
+ g = Fp.square(ge); // g = ge * ge
123
+ x = Fp.mul(x, ge); // x *= ge
124
+ b = Fp.mul(b, g); // b *= g
125
+ r = m;
126
+ }
127
+ return x;
128
+ };
77
129
  }
78
- /**
79
- * Calculates square root of a number in a finite field.
80
- * √a mod P
81
- */
82
- // TODO: rewrite as generic Fp function && remove bls versions
83
- export function sqrt(number, modulo) {
84
- // prettier-ignore
85
- const n = number;
86
- const P = modulo;
87
- const p1div4 = (P + _1n) / _4n;
130
+ export function FpSqrt(P) {
131
+ // NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
132
+ // For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
88
133
  // P ≡ 3 (mod 4)
89
134
  // √n = n^((P+1)/4)
90
135
  if (P % _4n === _3n) {
@@ -92,50 +137,53 @@ export function sqrt(number, modulo) {
92
137
  // const ORDER =
93
138
  // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
94
139
  // const NUM = 72057594037927816n;
95
- // TODO: fix sqrtMod in secp256k1
96
- const root = pow(n, p1div4, P);
97
- if (mod(root * root, modulo) !== number)
98
- throw new Error('Cannot find square root');
99
- return root;
140
+ const p1div4 = (P + _1n) / _4n;
141
+ return function sqrt3mod4(Fp, n) {
142
+ const root = Fp.pow(n, p1div4);
143
+ // Throw if root**2 != n
144
+ if (!Fp.equals(Fp.square(root), n))
145
+ throw new Error('Cannot find square root');
146
+ return root;
147
+ };
100
148
  }
101
- // P ≡ 5 (mod 8)
149
+ // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
102
150
  if (P % _8n === _5n) {
103
- const n2 = mod(n * _2n, P);
104
- const v = pow(n2, (P - _5n) / _8n, P);
105
- const nv = mod(n * v, P);
106
- const i = mod(_2n * nv * v, P);
107
- const r = mod(nv * (i - _1n), P);
108
- return r;
151
+ const c1 = (P - _5n) / _8n;
152
+ return function sqrt5mod8(Fp, n) {
153
+ const n2 = Fp.mul(n, _2n);
154
+ const v = Fp.pow(n2, c1);
155
+ const nv = Fp.mul(n, v);
156
+ const i = Fp.mul(Fp.mul(nv, _2n), v);
157
+ const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
158
+ if (!Fp.equals(Fp.square(root), n))
159
+ throw new Error('Cannot find square root');
160
+ return root;
161
+ };
109
162
  }
110
- // Other cases: Tonelli-Shanks algorithm
111
- if (legendre(n, P) !== _1n)
112
- throw new Error('Cannot find square root');
113
- let q, s, z;
114
- for (q = P - _1n, s = 0; q % _2n === _0n; q /= _2n, s++)
115
- ;
116
- if (s === 1)
117
- return pow(n, p1div4, P);
118
- for (z = _2n; z < P && legendre(z, P) !== P - _1n; z++)
119
- ;
120
- let c = pow(z, q, P);
121
- let r = pow(n, (q + _1n) / _2n, P);
122
- let t = pow(n, q, P);
123
- let t2 = _0n;
124
- while (mod(t - _1n, P) !== _0n) {
125
- t2 = mod(t * t, P);
126
- let i;
127
- for (i = 1; i < s; i++) {
128
- if (mod(t2 - _1n, P) === _0n)
129
- break;
130
- t2 = mod(t2 * t2, P);
131
- }
132
- let b = pow(c, BigInt(1 << (s - i - 1)), P);
133
- r = mod(r * b, P);
134
- c = mod(b * b, P);
135
- t = mod(t * c, P);
136
- s = i;
163
+ // P 9 (mod 16)
164
+ if (P % _16n === _9n) {
165
+ // NOTE: tonelli is too slow for bls-Fp2 calculations even on start
166
+ // Means we cannot use sqrt for constants at all!
167
+ //
168
+ // const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
169
+ // const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
170
+ // const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
171
+ // const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
172
+ // sqrt = (x) => {
173
+ // let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
174
+ // let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
175
+ // const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
176
+ // let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
177
+ // const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
178
+ // const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
179
+ // tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
180
+ // tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
181
+ // const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
182
+ // return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
183
+ // }
137
184
  }
138
- return r;
185
+ // Other cases: Tonelli-Shanks algorithm
186
+ return tonelliShanks(P);
139
187
  }
140
188
  // Little-endian check for first LE bit (last BE bit);
141
189
  export const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
@@ -202,17 +250,21 @@ export function FpInvertBatch(f, nums) {
202
250
  export function FpDiv(f, lhs, rhs) {
203
251
  return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
204
252
  }
205
- // NOTE: very fragile, always bench. Major performance points:
206
- // - NonNormalized ops
207
- // - Object.freeze
208
- // - same shape of object (don't add/remove keys)
253
+ // This function returns True whenever the value x is a square in the field F.
254
+ export function FpIsSquare(f) {
255
+ const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
256
+ return (x) => {
257
+ const p = f.pow(x, legendreConst);
258
+ return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
259
+ };
260
+ }
209
261
  export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
210
262
  if (ORDER <= _0n)
211
263
  throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
212
264
  const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
213
265
  if (BYTES > 2048)
214
266
  throw new Error('Field lengths over 2048 bytes are not supported');
215
- const sqrtP = (num) => sqrt(num, ORDER);
267
+ const sqrtP = FpSqrt(ORDER);
216
268
  const f = Object.freeze({
217
269
  ORDER,
218
270
  BITS,
@@ -242,7 +294,7 @@ export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
242
294
  subN: (lhs, rhs) => lhs - rhs,
243
295
  mulN: (lhs, rhs) => lhs * rhs,
244
296
  invert: (num) => invert(num, ORDER),
245
- sqrt: redef.sqrt || sqrtP,
297
+ sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
246
298
  invertBatch: (lst) => FpInvertBatch(f, lst),
247
299
  // TODO: do we really need constant cmov?
248
300
  // We don't have const-time bigints anyway, so probably will be not very useful
@@ -256,87 +308,15 @@ export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
256
308
  });
257
309
  return Object.freeze(f);
258
310
  }
259
- // TODO: re-use in bls/generic sqrt for field/etc?
260
- // Something like sqrtUnsafe which always returns value, but sqrt throws exception if non-square
261
- // From draft-irtf-cfrg-hash-to-curve-16
262
- export function FpSqrt(Fp) {
263
- // NOTE: it requires another sqrt for constant precomputes, but no need for roots of unity,
264
- // probably we can simply bls code using it
265
- const q = Fp.ORDER;
266
- const squareConst = (q - _1n) / _2n;
267
- // is_square(x) := { True, if x^((q - 1) / 2) is 0 or 1 in F;
268
- // { False, otherwise.
269
- let isSquare = (x) => {
270
- const p = Fp.pow(x, squareConst);
271
- return Fp.equals(p, Fp.ZERO) || Fp.equals(p, Fp.ONE);
272
- };
273
- // Constant-time Tonelli-Shanks algorithm
274
- let l = _0n;
275
- for (let o = q - _1n; o % _2n === _0n; o /= _2n)
276
- l += _1n;
277
- const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
278
- const c2 = (q - _1n) / _2n ** c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
279
- const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
280
- // 4. c4, a non-square value in F
281
- // 5. c5 = c4^c2 in F
282
- let c4 = Fp.ONE;
283
- while (isSquare(c4))
284
- c4 = Fp.add(c4, Fp.ONE);
285
- const c5 = Fp.pow(c4, c2);
286
- let sqrt = (x) => {
287
- let z = Fp.pow(x, c3); // 1. z = x^c3
288
- let t = Fp.square(z); // 2. t = z * z
289
- t = Fp.mul(t, x); // 3. t = t * x
290
- z = Fp.mul(z, x); // 4. z = z * x
291
- let b = t; // 5. b = t
292
- let c = c5; // 6. c = c5
293
- // 7. for i in (c1, c1 - 1, ..., 2):
294
- for (let i = c1; i > 1; i--) {
295
- // 8. for j in (1, 2, ..., i - 2):
296
- // 9. b = b * b
297
- for (let j = _1n; j < i - _1n; i++)
298
- b = Fp.square(b);
299
- const e = Fp.equals(b, Fp.ONE); // 10. e = b == 1
300
- const zt = Fp.mul(z, c); // 11. zt = z * c
301
- z = Fp.cmov(zt, z, e); // 12. z = CMOV(zt, z, e)
302
- c = Fp.square(c); // 13. c = c * c
303
- let tt = Fp.mul(t, c); // 14. tt = t * c
304
- t = Fp.cmov(tt, t, e); // 15. t = CMOV(tt, t, e)
305
- b = t; // 16. b = t
306
- }
307
- return z; // 17. return z
308
- };
309
- if (q % _4n === _3n) {
310
- const c1 = (q + _1n) / _4n; // 1. c1 = (q + 1) / 4 # Integer arithmetic
311
- sqrt = (x) => Fp.pow(x, c1);
312
- }
313
- else if (q % _8n === _5n) {
314
- const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
315
- const c2 = (q + _3n) / _8n; // 2. c2 = (q + 3) / 8 # Integer arithmetic
316
- sqrt = (x) => {
317
- let tv1 = Fp.pow(x, c2); // 1. tv1 = x^c2
318
- let tv2 = Fp.mul(tv1, c1); // 2. tv2 = tv1 * c1
319
- let e = Fp.equals(Fp.square(tv1), x); // 3. e = (tv1^2) == x
320
- return Fp.cmov(tv2, tv1, e); // 4. z = CMOV(tv2, tv1, e)
321
- };
322
- }
323
- else if (Fp.ORDER % _16n === _9n) {
324
- const c1 = Fp.sqrt(Fp.negate(Fp.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
325
- const c2 = Fp.sqrt(c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
326
- const c3 = Fp.sqrt(Fp.negate(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
327
- const c4 = (Fp.ORDER + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
328
- sqrt = (x) => {
329
- let tv1 = Fp.pow(x, c4); // 1. tv1 = x^c4
330
- let tv2 = Fp.mul(c1, tv1); // 2. tv2 = c1 * tv1
331
- const tv3 = Fp.mul(c2, tv1); // 3. tv3 = c2 * tv1
332
- let tv4 = Fp.mul(c3, tv1); // 4. tv4 = c3 * tv1
333
- const e1 = Fp.equals(Fp.square(tv2), x); // 5. e1 = (tv2^2) == x
334
- const e2 = Fp.equals(Fp.square(tv3), x); // 6. e2 = (tv3^2) == x
335
- tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
336
- tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
337
- const e3 = Fp.equals(Fp.square(tv2), x); // 9. e3 = (tv2^2) == x
338
- return Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select the sqrt from tv1 and tv2
339
- };
340
- }
341
- return { sqrt, isSquare };
311
+ export function FpSqrtOdd(Fp, elm) {
312
+ if (!Fp.isOdd)
313
+ throw new Error(`Field doesn't have isOdd`);
314
+ const root = Fp.sqrt(elm);
315
+ return Fp.isOdd(root) ? root : Fp.negate(root);
316
+ }
317
+ export function FpSqrtEven(Fp, elm) {
318
+ if (!Fp.isOdd)
319
+ throw new Error(`Field doesn't have isOdd`);
320
+ const root = Fp.sqrt(elm);
321
+ return Fp.isOdd(root) ? Fp.negate(root) : root;
342
322
  }
@@ -1,8 +1,6 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
2
  import * as mod from './modular.js';
3
- import { ensureBytes, numberToBytesLE, bytesToNumberLE,
4
- // nLength,
5
- } from './utils.js';
3
+ import { ensureBytes, numberToBytesLE, bytesToNumberLE, isPositiveInt } from './utils.js';
6
4
  const _0n = BigInt(0);
7
5
  const _1n = BigInt(1);
8
6
  function validateOpts(curve) {
@@ -13,7 +11,7 @@ function validateOpts(curve) {
13
11
  for (const i of ['montgomeryBits', 'nByteLength']) {
14
12
  if (curve[i] === undefined)
15
13
  continue; // Optional
16
- if (!Number.isSafeInteger(curve[i]))
14
+ if (!isPositiveInt(curve[i]))
17
15
  throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
18
16
  }
19
17
  for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
@@ -3,21 +3,27 @@ import * as mod from './modular.js';
3
3
  const _0n = BigInt(0);
4
4
  const _1n = BigInt(1);
5
5
  const _2n = BigInt(2);
6
+ // Bans floats and integers above 2^53-1
7
+ export function isPositiveInt(num) {
8
+ return typeof num === 'number' && Number.isSafeInteger(num) && num > 0;
9
+ }
6
10
  export function validateOpts(curve) {
7
11
  mod.validateField(curve.Fp);
8
12
  for (const i of ['n', 'h']) {
9
- if (typeof curve[i] !== 'bigint')
10
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
13
+ const val = curve[i];
14
+ if (typeof val !== 'bigint')
15
+ throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
11
16
  }
12
17
  if (!curve.Fp.isValid(curve.Gx))
13
18
  throw new Error('Invalid generator X coordinate Fp element');
14
19
  if (!curve.Fp.isValid(curve.Gy))
15
20
  throw new Error('Invalid generator Y coordinate Fp element');
16
21
  for (const i of ['nBitLength', 'nByteLength']) {
17
- if (curve[i] === undefined)
22
+ const val = curve[i];
23
+ if (val === undefined)
18
24
  continue; // Optional
19
- if (!Number.isSafeInteger(curve[i]))
20
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
25
+ if (!isPositiveInt(val))
26
+ throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
21
27
  }
22
28
  // Set defaults
23
29
  return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve });
@@ -104,21 +110,22 @@ export function nLength(n, nBitLength) {
104
110
  return { nBitLength: _nBitLength, nByteLength };
105
111
  }
106
112
  /**
113
+ * FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
107
114
  * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
108
115
  * and convert them into private scalar, with the modulo bias being neglible.
109
- * As per FIPS 186 B.4.1.
116
+ * Needs at least 40 bytes of input for 32-byte private key.
110
117
  * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
111
- * @param hash hash output from sha512, or a similar function
118
+ * @param hash hash output from SHA3 or a similar function
112
119
  * @returns valid private scalar
113
120
  */
114
- export function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) {
121
+ export function hashToPrivateScalar(hash, groupOrder, isLE = false) {
115
122
  hash = ensureBytes(hash);
116
- const orderLen = nLength(CURVE_ORDER).nByteLength;
117
- const minLen = orderLen + 8;
118
- if (orderLen < 16 || hash.length < minLen || hash.length > 1024)
119
- throw new Error('Expected valid bytes of private key as per FIPS 186');
123
+ const hashLen = hash.length;
124
+ const minLen = nLength(groupOrder).nByteLength + 8;
125
+ if (minLen < 24 || hashLen < minLen || hashLen > 1024)
126
+ throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
120
127
  const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
121
- return mod.mod(num, CURVE_ORDER - _1n) + _1n;
128
+ return mod.mod(num, groupOrder - _1n) + _1n;
122
129
  }
123
130
  export function equalBytes(b1, b2) {
124
131
  // We don't care about timing attacks here