@noble/curves 0.5.2 → 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 (59) hide show
  1. package/README.md +49 -5
  2. package/lib/_shortw_utils.d.ts +10 -21
  3. package/lib/abstract/bls.d.ts +39 -32
  4. package/lib/abstract/bls.js +74 -73
  5. package/lib/abstract/{group.d.ts → curve.d.ts} +31 -1
  6. package/lib/abstract/{group.js → curve.js} +39 -2
  7. package/lib/abstract/edwards.d.ts +30 -72
  8. package/lib/abstract/edwards.js +197 -375
  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 +61 -35
  13. package/lib/abstract/montgomery.js +4 -5
  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 -36
  17. package/lib/abstract/utils.js +23 -71
  18. package/lib/abstract/weierstrass.d.ts +51 -74
  19. package/lib/abstract/weierstrass.js +455 -628
  20. package/lib/bls12-381.js +63 -58
  21. package/lib/bn.js +1 -1
  22. package/lib/ed25519.d.ts +7 -5
  23. package/lib/ed25519.js +82 -79
  24. package/lib/ed448.d.ts +3 -0
  25. package/lib/ed448.js +86 -83
  26. package/lib/esm/abstract/bls.js +75 -74
  27. package/lib/esm/abstract/{group.js → curve.js} +37 -1
  28. package/lib/esm/abstract/edwards.js +196 -374
  29. package/lib/esm/abstract/hash-to-curve.js +38 -11
  30. package/lib/esm/abstract/modular.js +58 -34
  31. package/lib/esm/abstract/montgomery.js +5 -6
  32. package/lib/esm/abstract/poseidon.js +109 -0
  33. package/lib/esm/abstract/utils.js +21 -66
  34. package/lib/esm/abstract/weierstrass.js +454 -627
  35. package/lib/esm/bls12-381.js +75 -70
  36. package/lib/esm/bn.js +1 -1
  37. package/lib/esm/ed25519.js +80 -78
  38. package/lib/esm/ed448.js +84 -82
  39. package/lib/esm/jubjub.js +1 -1
  40. package/lib/esm/p256.js +11 -9
  41. package/lib/esm/p384.js +11 -9
  42. package/lib/esm/p521.js +13 -12
  43. package/lib/esm/secp256k1.js +115 -151
  44. package/lib/esm/stark.js +104 -40
  45. package/lib/jubjub.d.ts +2 -2
  46. package/lib/jubjub.js +1 -1
  47. package/lib/p192.d.ts +20 -42
  48. package/lib/p224.d.ts +20 -42
  49. package/lib/p256.d.ts +23 -42
  50. package/lib/p256.js +13 -10
  51. package/lib/p384.d.ts +23 -42
  52. package/lib/p384.js +13 -10
  53. package/lib/p521.d.ts +23 -42
  54. package/lib/p521.js +15 -13
  55. package/lib/secp256k1.d.ts +25 -37
  56. package/lib/secp256k1.js +115 -151
  57. package/lib/stark.d.ts +36 -19
  58. package/lib/stark.js +107 -40
  59. package/package.json +13 -8
@@ -1,7 +1,6 @@
1
- /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- import { concatBytes } from './utils.js';
3
- import * as mod from './modular.js';
4
- export function validateHTFOpts(opts) {
1
+ import { mod } from './modular.js';
2
+ import { concatBytes, ensureBytes } from './utils.js';
3
+ export function validateOpts(opts) {
5
4
  if (typeof opts.DST !== 'string')
6
5
  throw new Error('Invalid htf/DST');
7
6
  if (typeof opts.p !== 'bigint')
@@ -15,13 +14,11 @@ export function validateHTFOpts(opts) {
15
14
  if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
16
15
  throw new Error('Invalid htf/hash function');
17
16
  }
18
- // UTF8 to ui8a
19
- // TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
20
17
  export function stringToBytes(str) {
21
- const bytes = new Uint8Array(str.length);
22
- for (let i = 0; i < str.length; i++)
23
- bytes[i] = str.charCodeAt(i);
24
- return bytes;
18
+ if (typeof str !== 'string') {
19
+ throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
20
+ }
21
+ return new TextEncoder().encode(str);
25
22
  }
26
23
  // Octet Stream to Integer (bytesToNumberBE)
27
24
  function os2ip(bytes) {
@@ -120,7 +117,7 @@ export function hash_to_field(msg, count, options) {
120
117
  for (let j = 0; j < options.m; j++) {
121
118
  const elm_offset = L * (j + i * options.m);
122
119
  const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
123
- e[j] = mod.mod(os2ip(tv), options.p);
120
+ e[j] = mod(os2ip(tv), options.p);
124
121
  }
125
122
  u[i] = e;
126
123
  }
@@ -136,3 +133,33 @@ export function isogenyMap(field, map) {
136
133
  return { x, y };
137
134
  };
138
135
  }
136
+ export function hashToCurve(Point, mapToCurve, def) {
137
+ validateOpts(def);
138
+ if (typeof mapToCurve !== 'function')
139
+ throw new Error('hashToCurve: mapToCurve() has not been defined');
140
+ return {
141
+ // Encodes byte string to elliptic curve
142
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
143
+ hashToCurve(msg, options) {
144
+ if (!mapToCurve)
145
+ throw new Error('CURVE.mapToCurve() has not been defined');
146
+ msg = ensureBytes(msg);
147
+ const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options });
148
+ const P = Point.fromAffine(mapToCurve(u[0]))
149
+ .add(Point.fromAffine(mapToCurve(u[1])))
150
+ .clearCofactor();
151
+ P.assertValidity();
152
+ return P;
153
+ },
154
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
155
+ encodeToCurve(msg, options) {
156
+ if (!mapToCurve)
157
+ throw new Error('CURVE.mapToCurve() has not been defined');
158
+ msg = ensureBytes(msg);
159
+ const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options });
160
+ const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
161
+ P.assertValidity();
162
+ return P;
163
+ },
164
+ };
165
+ }
@@ -1,7 +1,6 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- // TODO: remove circular imports
3
- import * as utils from './utils.js';
4
2
  // Utilities for modular arithmetics and finite fields
3
+ import { bitMask, numberToBytesBE, numberToBytesLE, bytesToNumberBE, bytesToNumberLE, ensureBytes, } from './utils.js';
5
4
  // prettier-ignore
6
5
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
7
6
  // prettier-ignore
@@ -91,7 +90,7 @@ export function tonelliShanks(P) {
91
90
  const p1div4 = (P + _1n) / _4n;
92
91
  return function tonelliFast(Fp, n) {
93
92
  const root = Fp.pow(n, p1div4);
94
- if (!Fp.equals(Fp.square(root), n))
93
+ if (!Fp.eql(Fp.sqr(root), n))
95
94
  throw new Error('Cannot find square root');
96
95
  return root;
97
96
  };
@@ -100,26 +99,26 @@ export function tonelliShanks(P) {
100
99
  const Q1div2 = (Q + _1n) / _2n;
101
100
  return function tonelliSlow(Fp, n) {
102
101
  // 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
+ if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE))
104
103
  throw new Error('Cannot find square root');
105
104
  let r = S;
106
105
  // TODO: will fail at Fp2/etc
107
106
  let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
108
107
  let x = Fp.pow(n, Q1div2); // first guess at the square root
109
108
  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))
109
+ while (!Fp.eql(b, Fp.ONE)) {
110
+ if (Fp.eql(b, Fp.ZERO))
112
111
  return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
113
112
  // Find m such b^(2^m)==1
114
113
  let m = 1;
115
- for (let t2 = Fp.square(b); m < r; m++) {
116
- if (Fp.equals(t2, Fp.ONE))
114
+ for (let t2 = Fp.sqr(b); m < r; m++) {
115
+ if (Fp.eql(t2, Fp.ONE))
117
116
  break;
118
- t2 = Fp.square(t2); // t2 *= t2
117
+ t2 = Fp.sqr(t2); // t2 *= t2
119
118
  }
120
119
  // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
121
120
  const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
122
- g = Fp.square(ge); // g = ge * ge
121
+ g = Fp.sqr(ge); // g = ge * ge
123
122
  x = Fp.mul(x, ge); // x *= ge
124
123
  b = Fp.mul(b, g); // b *= g
125
124
  r = m;
@@ -141,7 +140,7 @@ export function FpSqrt(P) {
141
140
  return function sqrt3mod4(Fp, n) {
142
141
  const root = Fp.pow(n, p1div4);
143
142
  // Throw if root**2 != n
144
- if (!Fp.equals(Fp.square(root), n))
143
+ if (!Fp.eql(Fp.sqr(root), n))
145
144
  throw new Error('Cannot find square root');
146
145
  return root;
147
146
  };
@@ -155,7 +154,7 @@ export function FpSqrt(P) {
155
154
  const nv = Fp.mul(n, v);
156
155
  const i = Fp.mul(Fp.mul(nv, _2n), v);
157
156
  const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
158
- if (!Fp.equals(Fp.square(root), n))
157
+ if (!Fp.eql(Fp.sqr(root), n))
159
158
  throw new Error('Cannot find square root');
160
159
  return root;
161
160
  };
@@ -189,9 +188,9 @@ export function FpSqrt(P) {
189
188
  export const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
190
189
  // prettier-ignore
191
190
  const FIELD_FIELDS = [
192
- 'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square',
193
- 'equals', 'add', 'sub', 'mul', 'pow', 'div',
194
- 'addN', 'subN', 'mulN', 'squareN'
191
+ 'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
192
+ 'eql', 'add', 'sub', 'mul', 'pow', 'div',
193
+ 'addN', 'subN', 'mulN', 'sqrN'
195
194
  ];
196
195
  export function validateField(field) {
197
196
  for (const i of ['ORDER', 'MASK']) {
@@ -222,7 +221,7 @@ export function FpPow(f, num, power) {
222
221
  while (power > _0n) {
223
222
  if (power & _1n)
224
223
  p = f.mul(p, d);
225
- d = f.square(d);
224
+ d = f.sqr(d);
226
225
  power >>= 1n;
227
226
  }
228
227
  return p;
@@ -231,16 +230,16 @@ export function FpInvertBatch(f, nums) {
231
230
  const tmp = new Array(nums.length);
232
231
  // Walk from first to last, multiply them by each other MOD p
233
232
  const lastMultiplied = nums.reduce((acc, num, i) => {
234
- if (f.isZero(num))
233
+ if (f.is0(num))
235
234
  return acc;
236
235
  tmp[i] = acc;
237
236
  return f.mul(acc, num);
238
237
  }, f.ONE);
239
238
  // Invert last element
240
- const inverted = f.invert(lastMultiplied);
239
+ const inverted = f.inv(lastMultiplied);
241
240
  // Walk from last to first, multiply them by inverted each other MOD p
242
241
  nums.reduceRight((acc, num, i) => {
243
- if (f.isZero(num))
242
+ if (f.is0(num))
244
243
  return acc;
245
244
  tmp[i] = f.mul(acc, tmp[i]);
246
245
  return f.mul(acc, num);
@@ -248,20 +247,27 @@ export function FpInvertBatch(f, nums) {
248
247
  return tmp;
249
248
  }
250
249
  export function FpDiv(f, lhs, rhs) {
251
- return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
250
+ return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
252
251
  }
253
252
  // This function returns True whenever the value x is a square in the field F.
254
253
  export function FpIsSquare(f) {
255
254
  const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
256
255
  return (x) => {
257
256
  const p = f.pow(x, legendreConst);
258
- return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
257
+ return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
259
258
  };
260
259
  }
260
+ // CURVE.n lengths
261
+ export function nLength(n, nBitLength) {
262
+ // Bit size, byte size of CURVE.n
263
+ const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
264
+ const nByteLength = Math.ceil(_nBitLength / 8);
265
+ return { nBitLength: _nBitLength, nByteLength };
266
+ }
261
267
  export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
262
268
  if (ORDER <= _0n)
263
269
  throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
264
- const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
270
+ const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
265
271
  if (BYTES > 2048)
266
272
  throw new Error('Field lengths over 2048 bytes are not supported');
267
273
  const sqrtP = FpSqrt(ORDER);
@@ -269,41 +275,41 @@ export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
269
275
  ORDER,
270
276
  BITS,
271
277
  BYTES,
272
- MASK: utils.bitMask(BITS),
278
+ MASK: bitMask(BITS),
273
279
  ZERO: _0n,
274
280
  ONE: _1n,
275
281
  create: (num) => mod(num, ORDER),
276
282
  isValid: (num) => {
277
283
  if (typeof num !== 'bigint')
278
284
  throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
279
- return _0n <= num && num < ORDER;
285
+ return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
280
286
  },
281
- isZero: (num) => num === _0n,
287
+ is0: (num) => num === _0n,
282
288
  isOdd: (num) => (num & _1n) === _1n,
283
- negate: (num) => mod(-num, ORDER),
284
- equals: (lhs, rhs) => lhs === rhs,
285
- square: (num) => mod(num * num, ORDER),
289
+ neg: (num) => mod(-num, ORDER),
290
+ eql: (lhs, rhs) => lhs === rhs,
291
+ sqr: (num) => mod(num * num, ORDER),
286
292
  add: (lhs, rhs) => mod(lhs + rhs, ORDER),
287
293
  sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
288
294
  mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
289
295
  pow: (num, power) => FpPow(f, num, power),
290
296
  div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
291
297
  // Same as above, but doesn't normalize
292
- squareN: (num) => num * num,
298
+ sqrN: (num) => num * num,
293
299
  addN: (lhs, rhs) => lhs + rhs,
294
300
  subN: (lhs, rhs) => lhs - rhs,
295
301
  mulN: (lhs, rhs) => lhs * rhs,
296
- invert: (num) => invert(num, ORDER),
302
+ inv: (num) => invert(num, ORDER),
297
303
  sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
298
304
  invertBatch: (lst) => FpInvertBatch(f, lst),
299
305
  // TODO: do we really need constant cmov?
300
306
  // We don't have const-time bigints anyway, so probably will be not very useful
301
307
  cmov: (a, b, c) => (c ? b : a),
302
- toBytes: (num) => isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
308
+ toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
303
309
  fromBytes: (bytes) => {
304
310
  if (bytes.length !== BYTES)
305
311
  throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
306
- return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
312
+ return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
307
313
  },
308
314
  });
309
315
  return Object.freeze(f);
@@ -312,11 +318,29 @@ export function FpSqrtOdd(Fp, elm) {
312
318
  if (!Fp.isOdd)
313
319
  throw new Error(`Field doesn't have isOdd`);
314
320
  const root = Fp.sqrt(elm);
315
- return Fp.isOdd(root) ? root : Fp.negate(root);
321
+ return Fp.isOdd(root) ? root : Fp.neg(root);
316
322
  }
317
323
  export function FpSqrtEven(Fp, elm) {
318
324
  if (!Fp.isOdd)
319
325
  throw new Error(`Field doesn't have isOdd`);
320
326
  const root = Fp.sqrt(elm);
321
- return Fp.isOdd(root) ? Fp.negate(root) : root;
327
+ return Fp.isOdd(root) ? Fp.neg(root) : root;
328
+ }
329
+ /**
330
+ * FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
331
+ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
332
+ * and convert them into private scalar, with the modulo bias being neglible.
333
+ * Needs at least 40 bytes of input for 32-byte private key.
334
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
335
+ * @param hash hash output from SHA3 or a similar function
336
+ * @returns valid private scalar
337
+ */
338
+ export function hashToPrivateScalar(hash, groupOrder, isLE = false) {
339
+ hash = ensureBytes(hash);
340
+ const hashLen = hash.length;
341
+ const minLen = nLength(groupOrder).nByteLength + 8;
342
+ if (minLen < 24 || hashLen < minLen || hashLen > 1024)
343
+ throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
344
+ const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
345
+ return mod(num, groupOrder - _1n) + _1n;
322
346
  }
@@ -1,6 +1,6 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- import * as mod from './modular.js';
3
- import { ensureBytes, numberToBytesLE, bytesToNumberLE, isPositiveInt } from './utils.js';
2
+ import { mod, pow } from './modular.js';
3
+ import { ensureBytes, numberToBytesLE, bytesToNumberLE } from './utils.js';
4
4
  const _0n = BigInt(0);
5
5
  const _1n = BigInt(1);
6
6
  function validateOpts(curve) {
@@ -11,7 +11,7 @@ function validateOpts(curve) {
11
11
  for (const i of ['montgomeryBits', 'nByteLength']) {
12
12
  if (curve[i] === undefined)
13
13
  continue; // Optional
14
- if (!isPositiveInt(curve[i]))
14
+ if (!Number.isSafeInteger(curve[i]))
15
15
  throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
16
16
  }
17
17
  for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
@@ -27,7 +27,6 @@ function validateOpts(curve) {
27
27
  throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
28
28
  }
29
29
  // Set defaults
30
- // ...nLength(curve.n, curve.nBitLength),
31
30
  return Object.freeze({ ...curve });
32
31
  }
33
32
  // NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
@@ -35,12 +34,12 @@ function validateOpts(curve) {
35
34
  export function montgomery(curveDef) {
36
35
  const CURVE = validateOpts(curveDef);
37
36
  const { P } = CURVE;
38
- const modP = (a) => mod.mod(a, P);
37
+ const modP = (a) => mod(a, P);
39
38
  const montgomeryBits = CURVE.montgomeryBits;
40
39
  const montgomeryBytes = Math.ceil(montgomeryBits / 8);
41
40
  const fieldLen = CURVE.nByteLength;
42
41
  const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
43
- const powPminus2 = CURVE.powPminus2 || ((x) => mod.pow(x, P - BigInt(2), P));
42
+ const powPminus2 = CURVE.powPminus2 || ((x) => pow(x, P - BigInt(2), P));
44
43
  /**
45
44
  * Checks for num to be in range:
46
45
  * For strict == true: `0 < num < max`.
@@ -0,0 +1,109 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ // Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
3
+ import { validateField, FpPow } from './modular.js';
4
+ export function validateOpts(opts) {
5
+ const { Fp } = opts;
6
+ validateField(Fp);
7
+ for (const i of ['t', 'roundsFull', 'roundsPartial']) {
8
+ if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
9
+ throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
10
+ }
11
+ if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
12
+ throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
13
+ // Default is 5, but by some reasons stark uses 3
14
+ let sboxPower = opts.sboxPower;
15
+ if (sboxPower === undefined)
16
+ sboxPower = 5;
17
+ if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
18
+ throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
19
+ const _sboxPower = BigInt(sboxPower);
20
+ let sboxFn = (n) => FpPow(Fp, n, _sboxPower);
21
+ // Unwrapped sbox power for common cases (195->142μs)
22
+ if (sboxPower === 3)
23
+ sboxFn = (n) => Fp.mul(Fp.sqrN(n), n);
24
+ else if (sboxPower === 5)
25
+ sboxFn = (n) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
26
+ if (opts.roundsFull % 2 !== 0)
27
+ throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
28
+ const rounds = opts.roundsFull + opts.roundsPartial;
29
+ if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
30
+ throw new Error('Poseidon: wrong round constants');
31
+ const roundConstants = opts.roundConstants.map((rc) => {
32
+ if (!Array.isArray(rc) || rc.length !== opts.t)
33
+ throw new Error(`Poseidon wrong round constants: ${rc}`);
34
+ return rc.map((i) => {
35
+ if (typeof i !== 'bigint' || !Fp.isValid(i))
36
+ throw new Error(`Poseidon wrong round constant=${i}`);
37
+ return Fp.create(i);
38
+ });
39
+ });
40
+ // MDS is TxT matrix
41
+ if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
42
+ throw new Error('Poseidon: wrong MDS matrix');
43
+ const mds = opts.mds.map((mdsRow) => {
44
+ if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
45
+ throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
46
+ return mdsRow.map((i) => {
47
+ if (typeof i !== 'bigint')
48
+ throw new Error(`Poseidon MDS matrix value=${i}`);
49
+ return Fp.create(i);
50
+ });
51
+ });
52
+ return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
53
+ }
54
+ export function splitConstants(rc, t) {
55
+ if (typeof t !== 'number')
56
+ throw new Error('poseidonSplitConstants: wrong t');
57
+ if (!Array.isArray(rc) || rc.length % t)
58
+ throw new Error('poseidonSplitConstants: wrong rc');
59
+ const res = [];
60
+ let tmp = [];
61
+ for (let i = 0; i < rc.length; i++) {
62
+ tmp.push(rc[i]);
63
+ if (tmp.length === t) {
64
+ res.push(tmp);
65
+ tmp = [];
66
+ }
67
+ }
68
+ return res;
69
+ }
70
+ export function poseidon(opts) {
71
+ const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
72
+ const halfRoundsFull = Math.floor(opts.roundsFull / 2);
73
+ const partialIdx = reversePartialPowIdx ? t - 1 : 0;
74
+ const poseidonRound = (values, isFull, idx) => {
75
+ values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
76
+ if (isFull)
77
+ values = values.map((i) => sboxFn(i));
78
+ else
79
+ values[partialIdx] = sboxFn(values[partialIdx]);
80
+ // Matrix multiplication
81
+ values = opts.mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
82
+ return values;
83
+ };
84
+ const poseidonHash = function poseidonHash(values) {
85
+ if (!Array.isArray(values) || values.length !== t)
86
+ throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
87
+ values = values.map((i) => {
88
+ if (typeof i !== 'bigint')
89
+ throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
90
+ return Fp.create(i);
91
+ });
92
+ let round = 0;
93
+ // Apply r_f/2 full rounds.
94
+ for (let i = 0; i < halfRoundsFull; i++)
95
+ values = poseidonRound(values, true, round++);
96
+ // Apply r_p partial rounds.
97
+ for (let i = 0; i < opts.roundsPartial; i++)
98
+ values = poseidonRound(values, false, round++);
99
+ // Apply r_f/2 full rounds.
100
+ for (let i = 0; i < halfRoundsFull; i++)
101
+ values = poseidonRound(values, true, round++);
102
+ if (round !== rounds)
103
+ throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
104
+ return values;
105
+ };
106
+ // For verification in tests
107
+ poseidonHash.roundConstants = opts.roundConstants;
108
+ return poseidonHash;
109
+ }
@@ -1,41 +1,16 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- import * as mod from './modular.js';
3
2
  const _0n = BigInt(0);
4
3
  const _1n = BigInt(1);
5
4
  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
- }
10
- export function validateOpts(curve) {
11
- mod.validateField(curve.Fp);
12
- for (const i of ['n', 'h']) {
13
- const val = curve[i];
14
- if (typeof val !== 'bigint')
15
- throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
16
- }
17
- if (!curve.Fp.isValid(curve.Gx))
18
- throw new Error('Invalid generator X coordinate Fp element');
19
- if (!curve.Fp.isValid(curve.Gy))
20
- throw new Error('Invalid generator Y coordinate Fp element');
21
- for (const i of ['nBitLength', 'nByteLength']) {
22
- const val = curve[i];
23
- if (val === undefined)
24
- continue; // Optional
25
- if (!isPositiveInt(val))
26
- throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`);
27
- }
28
- // Set defaults
29
- return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve });
30
- }
5
+ const u8a = (a) => a instanceof Uint8Array;
31
6
  const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0'));
32
- export function bytesToHex(uint8a) {
33
- if (!(uint8a instanceof Uint8Array))
7
+ export function bytesToHex(bytes) {
8
+ if (!u8a(bytes))
34
9
  throw new Error('Expected Uint8Array');
35
10
  // pre-caching improves the speed 6x
36
11
  let hex = '';
37
- for (let i = 0; i < uint8a.length; i++) {
38
- hex += hexes[uint8a[i]];
12
+ for (let i = 0; i < bytes.length; i++) {
13
+ hex += hexes[bytes[i]];
39
14
  }
40
15
  return hex;
41
16
  }
@@ -44,17 +19,15 @@ export function numberToHexUnpadded(num) {
44
19
  return hex.length & 1 ? `0${hex}` : hex;
45
20
  }
46
21
  export function hexToNumber(hex) {
47
- if (typeof hex !== 'string') {
48
- throw new TypeError('hexToNumber: expected string, got ' + typeof hex);
49
- }
22
+ if (typeof hex !== 'string')
23
+ throw new Error('hexToNumber: expected string, got ' + typeof hex);
50
24
  // Big Endian
51
25
  return BigInt(`0x${hex}`);
52
26
  }
53
27
  // Caching slows it down 2-3x
54
28
  export function hexToBytes(hex) {
55
- if (typeof hex !== 'string') {
56
- throw new TypeError('hexToBytes: expected string, got ' + typeof hex);
57
- }
29
+ if (typeof hex !== 'string')
30
+ throw new Error('hexToBytes: expected string, got ' + typeof hex);
58
31
  if (hex.length % 2)
59
32
  throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length);
60
33
  const array = new Uint8Array(hex.length / 2);
@@ -72,24 +45,31 @@ export function hexToBytes(hex) {
72
45
  export function bytesToNumberBE(bytes) {
73
46
  return hexToNumber(bytesToHex(bytes));
74
47
  }
75
- export function bytesToNumberLE(uint8a) {
76
- if (!(uint8a instanceof Uint8Array))
48
+ export function bytesToNumberLE(bytes) {
49
+ if (!u8a(bytes))
77
50
  throw new Error('Expected Uint8Array');
78
- return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse()));
51
+ return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
79
52
  }
80
53
  export const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0'));
81
54
  export const numberToBytesLE = (n, len) => numberToBytesBE(n, len).reverse();
55
+ // Returns variable number bytes (minimal bigint encoding?)
56
+ export const numberToVarBytesBE = (n) => {
57
+ let hex = n.toString(16);
58
+ if (hex.length & 1)
59
+ hex = '0' + hex;
60
+ return hexToBytes(hex);
61
+ };
82
62
  export function ensureBytes(hex, expectedLength) {
83
63
  // Uint8Array.from() instead of hash.slice() because node.js Buffer
84
64
  // is instance of Uint8Array, and its slice() creates **mutable** copy
85
- const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex);
65
+ const bytes = u8a(hex) ? Uint8Array.from(hex) : hexToBytes(hex);
86
66
  if (typeof expectedLength === 'number' && bytes.length !== expectedLength)
87
67
  throw new Error(`Expected ${expectedLength} bytes`);
88
68
  return bytes;
89
69
  }
90
70
  // Copies several Uint8Arrays into one.
91
71
  export function concatBytes(...arrays) {
92
- if (!arrays.every((b) => b instanceof Uint8Array))
72
+ if (!arrays.every((b) => u8a(b)))
93
73
  throw new Error('Uint8Array list expected');
94
74
  if (arrays.length === 1)
95
75
  return arrays[0];
@@ -102,31 +82,6 @@ export function concatBytes(...arrays) {
102
82
  }
103
83
  return result;
104
84
  }
105
- // CURVE.n lengths
106
- export function nLength(n, nBitLength) {
107
- // Bit size, byte size of CURVE.n
108
- const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
109
- const nByteLength = Math.ceil(_nBitLength / 8);
110
- return { nBitLength: _nBitLength, nByteLength };
111
- }
112
- /**
113
- * FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
114
- * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
115
- * and convert them into private scalar, with the modulo bias being neglible.
116
- * Needs at least 40 bytes of input for 32-byte private key.
117
- * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
118
- * @param hash hash output from SHA3 or a similar function
119
- * @returns valid private scalar
120
- */
121
- export function hashToPrivateScalar(hash, groupOrder, isLE = false) {
122
- hash = ensureBytes(hash);
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}`);
127
- const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
128
- return mod.mod(num, groupOrder - _1n) + _1n;
129
- }
130
85
  export function equalBytes(b1, b2) {
131
86
  // We don't care about timing attacks here
132
87
  if (b1.length !== b2.length)