@noble/curves 0.5.1 → 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.
@@ -55,6 +55,7 @@ export function invert(number, modulo) {
55
55
  // prettier-ignore
56
56
  let x = _0n, y = _1n, u = _1n, v = _0n;
57
57
  while (a !== _0n) {
58
+ // JIT applies optimization if those two lines follow each other
58
59
  const q = b / a;
59
60
  const r = b % a;
60
61
  const m = x - u * q;
@@ -68,7 +69,8 @@ export function invert(number, modulo) {
68
69
  return mod(x, modulo);
69
70
  }
70
71
  // Tonelli-Shanks algorithm
71
- // https://eprint.iacr.org/2012/685.pdf (page 12)
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
72
74
  export function tonelliShanks(P) {
73
75
  // Legendre constant: used to calculate Legendre symbol (a | p),
74
76
  // which denotes the value of a^((p-1)/2) (mod p).
@@ -78,7 +80,7 @@ export function tonelliShanks(P) {
78
80
  const legendreC = (P - _1n) / _2n;
79
81
  let Q, S, Z;
80
82
  // Step 1: By factoring out powers of 2 from p - 1,
81
- // find q and s such that p - 1 = q2s with q odd
83
+ // find q and s such that p - 1 = q*(2^s) with q odd
82
84
  for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++)
83
85
  ;
84
86
  // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
@@ -97,31 +99,32 @@ export function tonelliShanks(P) {
97
99
  // Slow-path
98
100
  const Q1div2 = (Q + _1n) / _2n;
99
101
  return function tonelliSlow(Fp, n) {
100
- // Step 0: Check that n is indeed a square: (n | p) must be ≡ 1
101
- if (Fp.pow(n, legendreC) !== Fp.ONE)
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))
102
104
  throw new Error('Cannot find square root');
103
- let s = S;
104
- let c = pow(Z, Q, P);
105
- let r = Fp.pow(n, Q1div2);
106
- let t = Fp.pow(n, Q);
107
- let t2 = Fp.ZERO;
108
- while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) {
109
- t2 = Fp.square(t);
110
- let i;
111
- for (i = 1; i < s; i++) {
112
- // stop if t2-1 == 0
113
- if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO))
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))
114
117
  break;
115
- // t2 *= t2
116
- t2 = Fp.square(t2);
118
+ t2 = Fp.square(t2); // t2 *= t2
117
119
  }
118
- let b = pow(c, BigInt(1 << (s - i - 1)), P);
119
- r = Fp.mul(r, b);
120
- c = mod(b * b, P);
121
- t = Fp.mul(t, c);
122
- s = i;
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;
123
126
  }
124
- return r;
127
+ return x;
125
128
  };
126
129
  }
127
130
  export function FpSqrt(P) {
@@ -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