@noble/curves 0.5.2 → 0.6.1

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 (61) hide show
  1. package/README.md +115 -41
  2. package/lib/_shortw_utils.d.ts +13 -24
  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} +30 -1
  6. package/lib/abstract/{group.js → curve.js} +33 -2
  7. package/lib/abstract/edwards.d.ts +30 -72
  8. package/lib/abstract/edwards.js +206 -389
  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 +21 -8
  12. package/lib/abstract/modular.js +72 -48
  13. package/lib/abstract/montgomery.js +23 -68
  14. package/lib/abstract/poseidon.d.ts +29 -0
  15. package/lib/abstract/poseidon.js +115 -0
  16. package/lib/abstract/utils.d.ts +9 -37
  17. package/lib/abstract/utils.js +61 -87
  18. package/lib/abstract/weierstrass.d.ts +58 -81
  19. package/lib/abstract/weierstrass.js +485 -679
  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} +31 -1
  28. package/lib/esm/abstract/edwards.js +204 -387
  29. package/lib/esm/abstract/hash-to-curve.js +38 -11
  30. package/lib/esm/abstract/modular.js +69 -47
  31. package/lib/esm/abstract/montgomery.js +24 -69
  32. package/lib/esm/abstract/poseidon.js +109 -0
  33. package/lib/esm/abstract/utils.js +58 -82
  34. package/lib/esm/abstract/weierstrass.js +484 -678
  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/p224.js +1 -1
  41. package/lib/esm/p256.js +11 -9
  42. package/lib/esm/p384.js +11 -9
  43. package/lib/esm/p521.js +12 -23
  44. package/lib/esm/secp256k1.js +124 -162
  45. package/lib/esm/stark.js +105 -41
  46. package/lib/jubjub.d.ts +2 -2
  47. package/lib/jubjub.js +1 -1
  48. package/lib/p192.d.ts +26 -48
  49. package/lib/p224.d.ts +26 -48
  50. package/lib/p224.js +1 -1
  51. package/lib/p256.d.ts +29 -48
  52. package/lib/p256.js +13 -10
  53. package/lib/p384.d.ts +29 -48
  54. package/lib/p384.js +13 -10
  55. package/lib/p521.d.ts +37 -57
  56. package/lib/p521.js +14 -24
  57. package/lib/secp256k1.d.ts +37 -46
  58. package/lib/secp256k1.js +124 -162
  59. package/lib/stark.d.ts +39 -22
  60. package/lib/stark.js +108 -41
  61. package/package.json +15 -10
@@ -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 Error(`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, validateObject, } 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
@@ -35,7 +34,6 @@ export function pow(num, power, modulo) {
35
34
  return res;
36
35
  }
37
36
  // Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
38
- // TODO: Fp version?
39
37
  export function pow2(x, power, modulo) {
40
38
  let res = x;
41
39
  while (power-- > _0n) {
@@ -91,7 +89,7 @@ export function tonelliShanks(P) {
91
89
  const p1div4 = (P + _1n) / _4n;
92
90
  return function tonelliFast(Fp, n) {
93
91
  const root = Fp.pow(n, p1div4);
94
- if (!Fp.equals(Fp.square(root), n))
92
+ if (!Fp.eql(Fp.sqr(root), n))
95
93
  throw new Error('Cannot find square root');
96
94
  return root;
97
95
  };
@@ -100,26 +98,26 @@ export function tonelliShanks(P) {
100
98
  const Q1div2 = (Q + _1n) / _2n;
101
99
  return function tonelliSlow(Fp, n) {
102
100
  // 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))
101
+ if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE))
104
102
  throw new Error('Cannot find square root');
105
103
  let r = S;
106
104
  // TODO: will fail at Fp2/etc
107
105
  let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
108
106
  let x = Fp.pow(n, Q1div2); // first guess at the square root
109
107
  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))
108
+ while (!Fp.eql(b, Fp.ONE)) {
109
+ if (Fp.eql(b, Fp.ZERO))
112
110
  return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
113
111
  // Find m such b^(2^m)==1
114
112
  let m = 1;
115
- for (let t2 = Fp.square(b); m < r; m++) {
116
- if (Fp.equals(t2, Fp.ONE))
113
+ for (let t2 = Fp.sqr(b); m < r; m++) {
114
+ if (Fp.eql(t2, Fp.ONE))
117
115
  break;
118
- t2 = Fp.square(t2); // t2 *= t2
116
+ t2 = Fp.sqr(t2); // t2 *= t2
119
117
  }
120
118
  // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
121
119
  const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
122
- g = Fp.square(ge); // g = ge * ge
120
+ g = Fp.sqr(ge); // g = ge * ge
123
121
  x = Fp.mul(x, ge); // x *= ge
124
122
  b = Fp.mul(b, g); // b *= g
125
123
  r = m;
@@ -141,7 +139,7 @@ export function FpSqrt(P) {
141
139
  return function sqrt3mod4(Fp, n) {
142
140
  const root = Fp.pow(n, p1div4);
143
141
  // Throw if root**2 != n
144
- if (!Fp.equals(Fp.square(root), n))
142
+ if (!Fp.eql(Fp.sqr(root), n))
145
143
  throw new Error('Cannot find square root');
146
144
  return root;
147
145
  };
@@ -155,7 +153,7 @@ export function FpSqrt(P) {
155
153
  const nv = Fp.mul(n, v);
156
154
  const i = Fp.mul(Fp.mul(nv, _2n), v);
157
155
  const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
158
- if (!Fp.equals(Fp.square(root), n))
156
+ if (!Fp.eql(Fp.sqr(root), n))
159
157
  throw new Error('Cannot find square root');
160
158
  return root;
161
159
  };
@@ -189,23 +187,22 @@ export function FpSqrt(P) {
189
187
  export const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
190
188
  // prettier-ignore
191
189
  const FIELD_FIELDS = [
192
- 'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square',
193
- 'equals', 'add', 'sub', 'mul', 'pow', 'div',
194
- 'addN', 'subN', 'mulN', 'squareN'
190
+ 'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
191
+ 'eql', 'add', 'sub', 'mul', 'pow', 'div',
192
+ 'addN', 'subN', 'mulN', 'sqrN'
195
193
  ];
196
194
  export function validateField(field) {
197
- for (const i of ['ORDER', 'MASK']) {
198
- if (typeof field[i] !== 'bigint')
199
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
200
- }
201
- for (const i of ['BYTES', 'BITS']) {
202
- if (typeof field[i] !== 'number')
203
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
204
- }
205
- for (const i of FIELD_FIELDS) {
206
- if (typeof field[i] !== 'function')
207
- throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`);
208
- }
195
+ const initial = {
196
+ ORDER: 'bigint',
197
+ MASK: 'bigint',
198
+ BYTES: 'isSafeInteger',
199
+ BITS: 'isSafeInteger',
200
+ };
201
+ const opts = FIELD_FIELDS.reduce((map, val) => {
202
+ map[val] = 'function';
203
+ return map;
204
+ }, initial);
205
+ return validateObject(field, opts);
209
206
  }
210
207
  // Generic field functions
211
208
  export function FpPow(f, num, power) {
@@ -222,7 +219,7 @@ export function FpPow(f, num, power) {
222
219
  while (power > _0n) {
223
220
  if (power & _1n)
224
221
  p = f.mul(p, d);
225
- d = f.square(d);
222
+ d = f.sqr(d);
226
223
  power >>= 1n;
227
224
  }
228
225
  return p;
@@ -231,16 +228,16 @@ export function FpInvertBatch(f, nums) {
231
228
  const tmp = new Array(nums.length);
232
229
  // Walk from first to last, multiply them by each other MOD p
233
230
  const lastMultiplied = nums.reduce((acc, num, i) => {
234
- if (f.isZero(num))
231
+ if (f.is0(num))
235
232
  return acc;
236
233
  tmp[i] = acc;
237
234
  return f.mul(acc, num);
238
235
  }, f.ONE);
239
236
  // Invert last element
240
- const inverted = f.invert(lastMultiplied);
237
+ const inverted = f.inv(lastMultiplied);
241
238
  // Walk from last to first, multiply them by inverted each other MOD p
242
239
  nums.reduceRight((acc, num, i) => {
243
- if (f.isZero(num))
240
+ if (f.is0(num))
244
241
  return acc;
245
242
  tmp[i] = f.mul(acc, tmp[i]);
246
243
  return f.mul(acc, num);
@@ -248,20 +245,27 @@ export function FpInvertBatch(f, nums) {
248
245
  return tmp;
249
246
  }
250
247
  export function FpDiv(f, lhs, rhs) {
251
- return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
248
+ return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
252
249
  }
253
250
  // This function returns True whenever the value x is a square in the field F.
254
251
  export function FpIsSquare(f) {
255
252
  const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
256
253
  return (x) => {
257
254
  const p = f.pow(x, legendreConst);
258
- return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
255
+ return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
259
256
  };
260
257
  }
258
+ // CURVE.n lengths
259
+ export function nLength(n, nBitLength) {
260
+ // Bit size, byte size of CURVE.n
261
+ const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
262
+ const nByteLength = Math.ceil(_nBitLength / 8);
263
+ return { nBitLength: _nBitLength, nByteLength };
264
+ }
261
265
  export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
262
266
  if (ORDER <= _0n)
263
267
  throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
264
- const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
268
+ const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
265
269
  if (BYTES > 2048)
266
270
  throw new Error('Field lengths over 2048 bytes are not supported');
267
271
  const sqrtP = FpSqrt(ORDER);
@@ -269,41 +273,41 @@ export function Fp(ORDER, bitLen, isLE = false, redef = {}) {
269
273
  ORDER,
270
274
  BITS,
271
275
  BYTES,
272
- MASK: utils.bitMask(BITS),
276
+ MASK: bitMask(BITS),
273
277
  ZERO: _0n,
274
278
  ONE: _1n,
275
279
  create: (num) => mod(num, ORDER),
276
280
  isValid: (num) => {
277
281
  if (typeof num !== 'bigint')
278
282
  throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
279
- return _0n <= num && num < ORDER;
283
+ return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
280
284
  },
281
- isZero: (num) => num === _0n,
285
+ is0: (num) => num === _0n,
282
286
  isOdd: (num) => (num & _1n) === _1n,
283
- negate: (num) => mod(-num, ORDER),
284
- equals: (lhs, rhs) => lhs === rhs,
285
- square: (num) => mod(num * num, ORDER),
287
+ neg: (num) => mod(-num, ORDER),
288
+ eql: (lhs, rhs) => lhs === rhs,
289
+ sqr: (num) => mod(num * num, ORDER),
286
290
  add: (lhs, rhs) => mod(lhs + rhs, ORDER),
287
291
  sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
288
292
  mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
289
293
  pow: (num, power) => FpPow(f, num, power),
290
294
  div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
291
295
  // Same as above, but doesn't normalize
292
- squareN: (num) => num * num,
296
+ sqrN: (num) => num * num,
293
297
  addN: (lhs, rhs) => lhs + rhs,
294
298
  subN: (lhs, rhs) => lhs - rhs,
295
299
  mulN: (lhs, rhs) => lhs * rhs,
296
- invert: (num) => invert(num, ORDER),
300
+ inv: (num) => invert(num, ORDER),
297
301
  sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
298
302
  invertBatch: (lst) => FpInvertBatch(f, lst),
299
303
  // TODO: do we really need constant cmov?
300
304
  // We don't have const-time bigints anyway, so probably will be not very useful
301
305
  cmov: (a, b, c) => (c ? b : a),
302
- toBytes: (num) => isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
306
+ toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
303
307
  fromBytes: (bytes) => {
304
308
  if (bytes.length !== BYTES)
305
309
  throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
306
- return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
310
+ return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
307
311
  },
308
312
  });
309
313
  return Object.freeze(f);
@@ -312,11 +316,29 @@ export function FpSqrtOdd(Fp, elm) {
312
316
  if (!Fp.isOdd)
313
317
  throw new Error(`Field doesn't have isOdd`);
314
318
  const root = Fp.sqrt(elm);
315
- return Fp.isOdd(root) ? root : Fp.negate(root);
319
+ return Fp.isOdd(root) ? root : Fp.neg(root);
316
320
  }
317
321
  export function FpSqrtEven(Fp, elm) {
318
322
  if (!Fp.isOdd)
319
323
  throw new Error(`Field doesn't have isOdd`);
320
324
  const root = Fp.sqrt(elm);
321
- return Fp.isOdd(root) ? Fp.negate(root) : root;
325
+ return Fp.isOdd(root) ? Fp.neg(root) : root;
326
+ }
327
+ /**
328
+ * FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
329
+ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
330
+ * and convert them into private scalar, with the modulo bias being neglible.
331
+ * Needs at least 40 bytes of input for 32-byte private key.
332
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
333
+ * @param hash hash output from SHA3 or a similar function
334
+ * @returns valid private scalar
335
+ */
336
+ export function hashToPrivateScalar(hash, groupOrder, isLE = false) {
337
+ hash = ensureBytes(hash);
338
+ const hashLen = hash.length;
339
+ const minLen = nLength(groupOrder).nByteLength + 8;
340
+ if (minLen < 24 || hashLen < minLen || hashLen > 1024)
341
+ throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
342
+ const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
343
+ return mod(num, groupOrder - _1n) + _1n;
322
344
  }
@@ -1,33 +1,20 @@
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 { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js';
4
4
  const _0n = BigInt(0);
5
5
  const _1n = BigInt(1);
6
6
  function validateOpts(curve) {
7
- for (const i of ['a24']) {
8
- if (typeof curve[i] !== 'bigint')
9
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
10
- }
11
- for (const i of ['montgomeryBits', 'nByteLength']) {
12
- if (curve[i] === undefined)
13
- continue; // Optional
14
- if (!isPositiveInt(curve[i]))
15
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
16
- }
17
- for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) {
18
- if (curve[fn] === undefined)
19
- continue; // Optional
20
- if (typeof curve[fn] !== 'function')
21
- throw new Error(`Invalid ${fn} function`);
22
- }
23
- for (const i of ['Gu']) {
24
- if (curve[i] === undefined)
25
- continue; // Optional
26
- if (typeof curve[i] !== 'string')
27
- throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
28
- }
7
+ validateObject(curve, {
8
+ a24: 'bigint',
9
+ }, {
10
+ montgomeryBits: 'isSafeInteger',
11
+ nByteLength: 'isSafeInteger',
12
+ adjustScalarBytes: 'function',
13
+ domain: 'function',
14
+ powPminus2: 'function',
15
+ Gu: 'string',
16
+ });
29
17
  // Set defaults
30
- // ...nLength(curve.n, curve.nBitLength),
31
18
  return Object.freeze({ ...curve });
32
19
  }
33
20
  // NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
@@ -35,37 +22,13 @@ function validateOpts(curve) {
35
22
  export function montgomery(curveDef) {
36
23
  const CURVE = validateOpts(curveDef);
37
24
  const { P } = CURVE;
38
- const modP = (a) => mod.mod(a, P);
25
+ const modP = (a) => mod(a, P);
39
26
  const montgomeryBits = CURVE.montgomeryBits;
40
27
  const montgomeryBytes = Math.ceil(montgomeryBits / 8);
41
28
  const fieldLen = CURVE.nByteLength;
42
29
  const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
43
- const powPminus2 = CURVE.powPminus2 || ((x) => mod.pow(x, P - BigInt(2), P));
44
- /**
45
- * Checks for num to be in range:
46
- * For strict == true: `0 < num < max`.
47
- * For strict == false: `0 <= num < max`.
48
- * Converts non-float safe numbers to bigints.
49
- */
50
- function normalizeScalar(num, max, strict = true) {
51
- if (!max)
52
- throw new TypeError('Specify max value');
53
- if (typeof num === 'number' && Number.isSafeInteger(num))
54
- num = BigInt(num);
55
- if (typeof num === 'bigint' && num < max) {
56
- if (strict) {
57
- if (_0n < num)
58
- return num;
59
- }
60
- else {
61
- if (_0n <= num)
62
- return num;
63
- }
64
- }
65
- throw new TypeError('Expected valid scalar: 0 < scalar < max');
66
- }
67
- // cswap from RFC7748
68
- // NOTE: cswap is not from RFC7748!
30
+ const powPminus2 = CURVE.powPminus2 || ((x) => pow(x, P - BigInt(2), P));
31
+ // cswap from RFC7748. But it is not from RFC7748!
69
32
  /*
70
33
  cswap(swap, x_2, x_3):
71
34
  dummy = mask(swap) AND (x_2 XOR x_3)
@@ -81,6 +44,11 @@ export function montgomery(curveDef) {
81
44
  x_3 = modP(x_3 + dummy);
82
45
  return [x_2, x_3];
83
46
  }
47
+ function assertFieldElement(n) {
48
+ if (typeof n === 'bigint' && _0n <= n && n < P)
49
+ return n;
50
+ throw new Error('Expected valid scalar 0 < scalar < CURVE.P');
51
+ }
84
52
  // x25519 from 4
85
53
  /**
86
54
  *
@@ -89,11 +57,10 @@ export function montgomery(curveDef) {
89
57
  * @returns new Point on Montgomery curve
90
58
  */
91
59
  function montgomeryLadder(pointU, scalar) {
92
- const { P } = CURVE;
93
- const u = normalizeScalar(pointU, P);
60
+ const u = assertFieldElement(pointU);
94
61
  // Section 5: Implementations MUST accept non-canonical values and process them as
95
62
  // if they had been reduced modulo the field prime.
96
- const k = normalizeScalar(scalar, P);
63
+ const k = assertFieldElement(scalar);
97
64
  // The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519
98
65
  const a24 = CURVE.a24;
99
66
  const x_1 = u;
@@ -146,11 +113,11 @@ export function montgomery(curveDef) {
146
113
  return numberToBytesLE(modP(u), montgomeryBytes);
147
114
  }
148
115
  function decodeUCoordinate(uEnc) {
149
- const u = ensureBytes(uEnc, montgomeryBytes);
150
116
  // Section 5: When receiving such an array, implementations of X25519
151
117
  // MUST mask the most significant bit in the final byte.
152
118
  // This is very ugly way, but it works because fieldLen-1 is outside of bounds for X448, so this becomes NOOP
153
119
  // fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519
120
+ const u = ensureBytes(uEnc, montgomeryBytes);
154
121
  u[fieldLen - 1] &= 127; // 0b0111_1111
155
122
  return bytesToNumberLE(u);
156
123
  }
@@ -160,13 +127,6 @@ export function montgomery(curveDef) {
160
127
  throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`);
161
128
  return bytesToNumberLE(adjustScalarBytes(bytes));
162
129
  }
163
- /**
164
- * Computes shared secret between private key "scalar" and public key's "u" (x) coordinate.
165
- * We can get 'y' coordinate from 'u',
166
- * but Point.fromHex also wants 'x' coordinate oddity flag,
167
- * and we cannot get 'x' without knowing 'v'.
168
- * Need to add generic conversion between twisted edwards and complimentary curve for JubJub.
169
- */
170
130
  function scalarMult(scalar, u) {
171
131
  const pointU = decodeUCoordinate(u);
172
132
  const _scalar = decodeScalar(scalar);
@@ -177,12 +137,7 @@ export function montgomery(curveDef) {
177
137
  throw new Error('Invalid private or public key received');
178
138
  return encodeUCoordinate(pu);
179
139
  }
180
- /**
181
- * Computes public key from private.
182
- * Executes scalar multiplication of curve's base point by scalar.
183
- * @param scalar private key
184
- * @returns new public key
185
- */
140
+ // Computes public key from private. By doing scalar multiplication of base point.
186
141
  function scalarMultBase(scalar) {
187
142
  return scalarMult(scalar, CURVE.Gu);
188
143
  }
@@ -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 { FpPow, validateField } 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
+ }