@noble/curves 0.5.1 → 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 (60) hide show
  1. package/README.md +49 -8
  2. package/lib/_shortw_utils.d.ts +11 -26
  3. package/lib/abstract/bls.d.ts +51 -35
  4. package/lib/abstract/bls.js +77 -139
  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 -81
  8. package/lib/abstract/edwards.js +225 -420
  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 +80 -51
  13. package/lib/abstract/montgomery.js +3 -4
  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 -34
  17. package/lib/abstract/utils.js +23 -63
  18. package/lib/abstract/weierstrass.d.ts +56 -79
  19. package/lib/abstract/weierstrass.js +509 -641
  20. package/lib/bls12-381.d.ts +1 -0
  21. package/lib/bls12-381.js +75 -65
  22. package/lib/bn.js +1 -1
  23. package/lib/ed25519.d.ts +7 -5
  24. package/lib/ed25519.js +87 -84
  25. package/lib/ed448.d.ts +3 -0
  26. package/lib/ed448.js +88 -84
  27. package/lib/esm/abstract/bls.js +77 -139
  28. package/lib/esm/abstract/{group.js → curve.js} +37 -1
  29. package/lib/esm/abstract/edwards.js +223 -418
  30. package/lib/esm/abstract/hash-to-curve.js +38 -11
  31. package/lib/esm/abstract/modular.js +77 -50
  32. package/lib/esm/abstract/montgomery.js +4 -7
  33. package/lib/esm/abstract/poseidon.js +109 -0
  34. package/lib/esm/abstract/utils.js +21 -59
  35. package/lib/esm/abstract/weierstrass.js +508 -640
  36. package/lib/esm/bls12-381.js +86 -76
  37. package/lib/esm/bn.js +1 -1
  38. package/lib/esm/ed25519.js +85 -83
  39. package/lib/esm/ed448.js +86 -83
  40. package/lib/esm/jubjub.js +6 -5
  41. package/lib/esm/p256.js +11 -9
  42. package/lib/esm/p384.js +11 -9
  43. package/lib/esm/p521.js +13 -12
  44. package/lib/esm/secp256k1.js +118 -157
  45. package/lib/esm/stark.js +104 -39
  46. package/lib/jubjub.d.ts +3 -2
  47. package/lib/jubjub.js +6 -5
  48. package/lib/p192.d.ts +22 -52
  49. package/lib/p224.d.ts +22 -52
  50. package/lib/p256.d.ts +25 -52
  51. package/lib/p256.js +13 -10
  52. package/lib/p384.d.ts +25 -52
  53. package/lib/p384.js +13 -10
  54. package/lib/p521.d.ts +25 -52
  55. package/lib/p521.js +15 -13
  56. package/lib/secp256k1.d.ts +26 -42
  57. package/lib/secp256k1.js +118 -157
  58. package/lib/stark.d.ts +36 -21
  59. package/lib/stark.js +107 -39
  60. package/package.json +14 -9
@@ -1,15 +1,17 @@
1
1
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- import { CHash } from './utils.js';
3
- import * as mod from './modular.js';
4
- export declare type htfOpts = {
2
+ import type { Group, GroupConstructor, AffinePoint } from './curve.js';
3
+ import { Field } from './modular.js';
4
+ import { CHash, Hex } from './utils.js';
5
+ export declare type Opts = {
5
6
  DST: string;
7
+ encodeDST: string;
6
8
  p: bigint;
7
9
  m: number;
8
10
  k: number;
9
11
  expand?: 'xmd' | 'xof';
10
12
  hash: CHash;
11
13
  };
12
- export declare function validateHTFOpts(opts: htfOpts): void;
14
+ export declare function validateOpts(opts: Opts): void;
13
15
  export declare function stringToBytes(str: string): Uint8Array;
14
16
  export declare function expand_message_xmd(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, H: CHash): Uint8Array;
15
17
  export declare function expand_message_xof(msg: Uint8Array, DST: Uint8Array, lenInBytes: number, k: number, H: CHash): Uint8Array;
@@ -21,8 +23,25 @@ export declare function expand_message_xof(msg: Uint8Array, DST: Uint8Array, len
21
23
  * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`
22
24
  * @returns [u_0, ..., u_(count - 1)], a list of field elements.
23
25
  */
24
- export declare function hash_to_field(msg: Uint8Array, count: number, options: htfOpts): bigint[][];
25
- export declare function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[], T[], T[]]): (x: T, y: T) => {
26
+ export declare function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
27
+ export declare function isogenyMap<T, F extends Field<T>>(field: F, map: [T[], T[], T[], T[]]): (x: T, y: T) => {
26
28
  x: T;
27
29
  y: T;
28
30
  };
31
+ export interface H2CPoint<T> extends Group<H2CPoint<T>> {
32
+ add(rhs: H2CPoint<T>): H2CPoint<T>;
33
+ toAffine(iz?: bigint): AffinePoint<T>;
34
+ clearCofactor(): H2CPoint<T>;
35
+ assertValidity(): void;
36
+ }
37
+ export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
38
+ fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
39
+ }
40
+ export declare type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
41
+ export declare type htfBasicOpts = {
42
+ DST: string;
43
+ };
44
+ export declare function hashToCurve<T>(Point: H2CPointConstructor<T>, mapToCurve: MapToCurve<T>, def: Opts): {
45
+ hashToCurve(msg: Hex, options?: htfBasicOpts): H2CPoint<T>;
46
+ encodeToCurve(msg: Hex, options?: htfBasicOpts): H2CPoint<T>;
47
+ };
@@ -1,10 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isogenyMap = exports.hash_to_field = exports.expand_message_xof = exports.expand_message_xmd = exports.stringToBytes = exports.validateHTFOpts = void 0;
4
- /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
3
+ exports.hashToCurve = exports.isogenyMap = exports.hash_to_field = exports.expand_message_xof = exports.expand_message_xmd = exports.stringToBytes = exports.validateOpts = void 0;
4
+ const modular_js_1 = require("./modular.js");
5
5
  const utils_js_1 = require("./utils.js");
6
- const mod = require("./modular.js");
7
- function validateHTFOpts(opts) {
6
+ function validateOpts(opts) {
8
7
  if (typeof opts.DST !== 'string')
9
8
  throw new Error('Invalid htf/DST');
10
9
  if (typeof opts.p !== 'bigint')
@@ -18,14 +17,12 @@ function validateHTFOpts(opts) {
18
17
  if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen))
19
18
  throw new Error('Invalid htf/hash function');
20
19
  }
21
- exports.validateHTFOpts = validateHTFOpts;
22
- // UTF8 to ui8a
23
- // TODO: looks broken, ASCII only, why not TextEncoder/TextDecoder? it is in hashes anyway
20
+ exports.validateOpts = validateOpts;
24
21
  function stringToBytes(str) {
25
- const bytes = new Uint8Array(str.length);
26
- for (let i = 0; i < str.length; i++)
27
- bytes[i] = str.charCodeAt(i);
28
- return bytes;
22
+ if (typeof str !== 'string') {
23
+ throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`);
24
+ }
25
+ return new TextEncoder().encode(str);
29
26
  }
30
27
  exports.stringToBytes = stringToBytes;
31
28
  // Octet Stream to Integer (bytesToNumberBE)
@@ -127,7 +124,7 @@ function hash_to_field(msg, count, options) {
127
124
  for (let j = 0; j < options.m; j++) {
128
125
  const elm_offset = L * (j + i * options.m);
129
126
  const tv = pseudo_random_bytes.subarray(elm_offset, elm_offset + L);
130
- e[j] = mod.mod(os2ip(tv), options.p);
127
+ e[j] = (0, modular_js_1.mod)(os2ip(tv), options.p);
131
128
  }
132
129
  u[i] = e;
133
130
  }
@@ -145,3 +142,34 @@ function isogenyMap(field, map) {
145
142
  };
146
143
  }
147
144
  exports.isogenyMap = isogenyMap;
145
+ function hashToCurve(Point, mapToCurve, def) {
146
+ validateOpts(def);
147
+ if (typeof mapToCurve !== 'function')
148
+ throw new Error('hashToCurve: mapToCurve() has not been defined');
149
+ return {
150
+ // Encodes byte string to elliptic curve
151
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
152
+ hashToCurve(msg, options) {
153
+ if (!mapToCurve)
154
+ throw new Error('CURVE.mapToCurve() has not been defined');
155
+ msg = (0, utils_js_1.ensureBytes)(msg);
156
+ const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options });
157
+ const P = Point.fromAffine(mapToCurve(u[0]))
158
+ .add(Point.fromAffine(mapToCurve(u[1])))
159
+ .clearCofactor();
160
+ P.assertValidity();
161
+ return P;
162
+ },
163
+ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3
164
+ encodeToCurve(msg, options) {
165
+ if (!mapToCurve)
166
+ throw new Error('CURVE.mapToCurve() has not been defined');
167
+ msg = (0, utils_js_1.ensureBytes)(msg);
168
+ const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options });
169
+ const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
170
+ P.assertValidity();
171
+ return P;
172
+ },
173
+ };
174
+ }
175
+ exports.hashToCurve = hashToCurve;
@@ -20,12 +20,12 @@ export interface Field<T> {
20
20
  ONE: T;
21
21
  create: (num: T) => T;
22
22
  isValid: (num: T) => boolean;
23
- isZero: (num: T) => boolean;
24
- negate(num: T): T;
25
- invert(num: T): T;
23
+ is0: (num: T) => boolean;
24
+ neg(num: T): T;
25
+ inv(num: T): T;
26
26
  sqrt(num: T): T;
27
- square(num: T): T;
28
- equals(lhs: T, rhs: T): boolean;
27
+ sqr(num: T): T;
28
+ eql(lhs: T, rhs: T): boolean;
29
29
  add(lhs: T, rhs: T): T;
30
30
  sub(lhs: T, rhs: T): T;
31
31
  mul(lhs: T, rhs: T | bigint): T;
@@ -34,9 +34,8 @@ export interface Field<T> {
34
34
  addN(lhs: T, rhs: T): T;
35
35
  subN(lhs: T, rhs: T): T;
36
36
  mulN(lhs: T, rhs: T | bigint): T;
37
- squareN(num: T): T;
37
+ sqrN(num: T): T;
38
38
  isOdd?(num: T): boolean;
39
- legendre?(num: T): T;
40
39
  pow(lhs: T, power: bigint): T;
41
40
  invertBatch: (lst: T[]) => T[];
42
41
  toBytes(num: T): Uint8Array;
@@ -48,8 +47,22 @@ export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T;
48
47
  export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[];
49
48
  export declare function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T;
50
49
  export declare function FpIsSquare<T>(f: Field<T>): (x: T) => boolean;
50
+ export declare function nLength(n: bigint, nBitLength?: number): {
51
+ nBitLength: number;
52
+ nByteLength: number;
53
+ };
51
54
  declare type FpField = Field<bigint> & Required<Pick<Field<bigint>, 'isOdd'>>;
52
55
  export declare function Fp(ORDER: bigint, bitLen?: number, isLE?: boolean, redef?: Partial<Field<bigint>>): Readonly<FpField>;
53
56
  export declare function FpSqrtOdd<T>(Fp: Field<T>, elm: T): T;
54
57
  export declare function FpSqrtEven<T>(Fp: Field<T>, elm: T): T;
58
+ /**
59
+ * FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
60
+ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
61
+ * and convert them into private scalar, with the modulo bias being neglible.
62
+ * Needs at least 40 bytes of input for 32-byte private key.
63
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
64
+ * @param hash hash output from SHA3 or a similar function
65
+ * @returns valid private scalar
66
+ */
67
+ export declare function hashToPrivateScalar(hash: string | Uint8Array, groupOrder: bigint, isLE?: boolean): bigint;
55
68
  export {};
@@ -1,10 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FpSqrtEven = exports.FpSqrtOdd = exports.Fp = exports.FpIsSquare = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.FpSqrt = exports.tonelliShanks = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
3
+ exports.hashToPrivateScalar = exports.FpSqrtEven = exports.FpSqrtOdd = exports.Fp = exports.nLength = exports.FpIsSquare = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.FpSqrt = exports.tonelliShanks = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0;
4
4
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
- // TODO: remove circular imports
6
- const utils = require("./utils.js");
7
5
  // Utilities for modular arithmetics and finite fields
6
+ const utils_js_1 = require("./utils.js");
8
7
  // prettier-ignore
9
8
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
10
9
  // prettier-ignore
@@ -61,6 +60,7 @@ function invert(number, modulo) {
61
60
  // prettier-ignore
62
61
  let x = _0n, y = _1n, u = _1n, v = _0n;
63
62
  while (a !== _0n) {
63
+ // JIT applies optimization if those two lines follow each other
64
64
  const q = b / a;
65
65
  const r = b % a;
66
66
  const m = x - u * q;
@@ -75,7 +75,8 @@ function invert(number, modulo) {
75
75
  }
76
76
  exports.invert = invert;
77
77
  // Tonelli-Shanks algorithm
78
- // https://eprint.iacr.org/2012/685.pdf (page 12)
78
+ // Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12)
79
+ // Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks
79
80
  function tonelliShanks(P) {
80
81
  // Legendre constant: used to calculate Legendre symbol (a | p),
81
82
  // which denotes the value of a^((p-1)/2) (mod p).
@@ -85,7 +86,7 @@ function tonelliShanks(P) {
85
86
  const legendreC = (P - _1n) / _2n;
86
87
  let Q, S, Z;
87
88
  // Step 1: By factoring out powers of 2 from p - 1,
88
- // find q and s such that p - 1 = q2s with q odd
89
+ // find q and s such that p - 1 = q*(2^s) with q odd
89
90
  for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++)
90
91
  ;
91
92
  // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
@@ -96,7 +97,7 @@ function tonelliShanks(P) {
96
97
  const p1div4 = (P + _1n) / _4n;
97
98
  return function tonelliFast(Fp, n) {
98
99
  const root = Fp.pow(n, p1div4);
99
- if (!Fp.equals(Fp.square(root), n))
100
+ if (!Fp.eql(Fp.sqr(root), n))
100
101
  throw new Error('Cannot find square root');
101
102
  return root;
102
103
  };
@@ -104,31 +105,32 @@ function tonelliShanks(P) {
104
105
  // Slow-path
105
106
  const Q1div2 = (Q + _1n) / _2n;
106
107
  return function tonelliSlow(Fp, n) {
107
- // Step 0: Check that n is indeed a square: (n | p) must be ≡ 1
108
- if (Fp.pow(n, legendreC) !== Fp.ONE)
108
+ // Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
109
+ if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE))
109
110
  throw new Error('Cannot find square root');
110
- let s = S;
111
- let c = pow(Z, Q, P);
112
- let r = Fp.pow(n, Q1div2);
113
- let t = Fp.pow(n, Q);
114
- let t2 = Fp.ZERO;
115
- while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) {
116
- t2 = Fp.square(t);
117
- let i;
118
- for (i = 1; i < s; i++) {
119
- // stop if t2-1 == 0
120
- if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO))
111
+ let r = S;
112
+ // TODO: will fail at Fp2/etc
113
+ let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
114
+ let x = Fp.pow(n, Q1div2); // first guess at the square root
115
+ let b = Fp.pow(n, Q); // first guess at the fudge factor
116
+ while (!Fp.eql(b, Fp.ONE)) {
117
+ if (Fp.eql(b, Fp.ZERO))
118
+ return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
119
+ // Find m such b^(2^m)==1
120
+ let m = 1;
121
+ for (let t2 = Fp.sqr(b); m < r; m++) {
122
+ if (Fp.eql(t2, Fp.ONE))
121
123
  break;
122
- // t2 *= t2
123
- t2 = Fp.square(t2);
124
+ t2 = Fp.sqr(t2); // t2 *= t2
124
125
  }
125
- let b = pow(c, BigInt(1 << (s - i - 1)), P);
126
- r = Fp.mul(r, b);
127
- c = mod(b * b, P);
128
- t = Fp.mul(t, c);
129
- s = i;
126
+ // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
127
+ const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
128
+ g = Fp.sqr(ge); // g = ge * ge
129
+ x = Fp.mul(x, ge); // x *= ge
130
+ b = Fp.mul(b, g); // b *= g
131
+ r = m;
130
132
  }
131
- return r;
133
+ return x;
132
134
  };
133
135
  }
134
136
  exports.tonelliShanks = tonelliShanks;
@@ -146,7 +148,7 @@ function FpSqrt(P) {
146
148
  return function sqrt3mod4(Fp, n) {
147
149
  const root = Fp.pow(n, p1div4);
148
150
  // Throw if root**2 != n
149
- if (!Fp.equals(Fp.square(root), n))
151
+ if (!Fp.eql(Fp.sqr(root), n))
150
152
  throw new Error('Cannot find square root');
151
153
  return root;
152
154
  };
@@ -160,7 +162,7 @@ function FpSqrt(P) {
160
162
  const nv = Fp.mul(n, v);
161
163
  const i = Fp.mul(Fp.mul(nv, _2n), v);
162
164
  const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
163
- if (!Fp.equals(Fp.square(root), n))
165
+ if (!Fp.eql(Fp.sqr(root), n))
164
166
  throw new Error('Cannot find square root');
165
167
  return root;
166
168
  };
@@ -196,9 +198,9 @@ const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
196
198
  exports.isNegativeLE = isNegativeLE;
197
199
  // prettier-ignore
198
200
  const FIELD_FIELDS = [
199
- 'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square',
200
- 'equals', 'add', 'sub', 'mul', 'pow', 'div',
201
- 'addN', 'subN', 'mulN', 'squareN'
201
+ 'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
202
+ 'eql', 'add', 'sub', 'mul', 'pow', 'div',
203
+ 'addN', 'subN', 'mulN', 'sqrN'
202
204
  ];
203
205
  function validateField(field) {
204
206
  for (const i of ['ORDER', 'MASK']) {
@@ -230,7 +232,7 @@ function FpPow(f, num, power) {
230
232
  while (power > _0n) {
231
233
  if (power & _1n)
232
234
  p = f.mul(p, d);
233
- d = f.square(d);
235
+ d = f.sqr(d);
234
236
  power >>= 1n;
235
237
  }
236
238
  return p;
@@ -240,16 +242,16 @@ function FpInvertBatch(f, nums) {
240
242
  const tmp = new Array(nums.length);
241
243
  // Walk from first to last, multiply them by each other MOD p
242
244
  const lastMultiplied = nums.reduce((acc, num, i) => {
243
- if (f.isZero(num))
245
+ if (f.is0(num))
244
246
  return acc;
245
247
  tmp[i] = acc;
246
248
  return f.mul(acc, num);
247
249
  }, f.ONE);
248
250
  // Invert last element
249
- const inverted = f.invert(lastMultiplied);
251
+ const inverted = f.inv(lastMultiplied);
250
252
  // Walk from last to first, multiply them by inverted each other MOD p
251
253
  nums.reduceRight((acc, num, i) => {
252
- if (f.isZero(num))
254
+ if (f.is0(num))
253
255
  return acc;
254
256
  tmp[i] = f.mul(acc, tmp[i]);
255
257
  return f.mul(acc, num);
@@ -258,7 +260,7 @@ function FpInvertBatch(f, nums) {
258
260
  }
259
261
  exports.FpInvertBatch = FpInvertBatch;
260
262
  function FpDiv(f, lhs, rhs) {
261
- return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs));
263
+ return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
262
264
  }
263
265
  exports.FpDiv = FpDiv;
264
266
  // This function returns True whenever the value x is a square in the field F.
@@ -266,14 +268,22 @@ function FpIsSquare(f) {
266
268
  const legendreConst = (f.ORDER - _1n) / _2n; // Integer arithmetic
267
269
  return (x) => {
268
270
  const p = f.pow(x, legendreConst);
269
- return f.equals(p, f.ZERO) || f.equals(p, f.ONE);
271
+ return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
270
272
  };
271
273
  }
272
274
  exports.FpIsSquare = FpIsSquare;
275
+ // CURVE.n lengths
276
+ function nLength(n, nBitLength) {
277
+ // Bit size, byte size of CURVE.n
278
+ const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
279
+ const nByteLength = Math.ceil(_nBitLength / 8);
280
+ return { nBitLength: _nBitLength, nByteLength };
281
+ }
282
+ exports.nLength = nLength;
273
283
  function Fp(ORDER, bitLen, isLE = false, redef = {}) {
274
284
  if (ORDER <= _0n)
275
285
  throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`);
276
- const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen);
286
+ const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
277
287
  if (BYTES > 2048)
278
288
  throw new Error('Field lengths over 2048 bytes are not supported');
279
289
  const sqrtP = FpSqrt(ORDER);
@@ -281,41 +291,41 @@ function Fp(ORDER, bitLen, isLE = false, redef = {}) {
281
291
  ORDER,
282
292
  BITS,
283
293
  BYTES,
284
- MASK: utils.bitMask(BITS),
294
+ MASK: (0, utils_js_1.bitMask)(BITS),
285
295
  ZERO: _0n,
286
296
  ONE: _1n,
287
297
  create: (num) => mod(num, ORDER),
288
298
  isValid: (num) => {
289
299
  if (typeof num !== 'bigint')
290
300
  throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
291
- return _0n <= num && num < ORDER;
301
+ return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
292
302
  },
293
- isZero: (num) => num === _0n,
303
+ is0: (num) => num === _0n,
294
304
  isOdd: (num) => (num & _1n) === _1n,
295
- negate: (num) => mod(-num, ORDER),
296
- equals: (lhs, rhs) => lhs === rhs,
297
- square: (num) => mod(num * num, ORDER),
305
+ neg: (num) => mod(-num, ORDER),
306
+ eql: (lhs, rhs) => lhs === rhs,
307
+ sqr: (num) => mod(num * num, ORDER),
298
308
  add: (lhs, rhs) => mod(lhs + rhs, ORDER),
299
309
  sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
300
310
  mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
301
311
  pow: (num, power) => FpPow(f, num, power),
302
312
  div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
303
313
  // Same as above, but doesn't normalize
304
- squareN: (num) => num * num,
314
+ sqrN: (num) => num * num,
305
315
  addN: (lhs, rhs) => lhs + rhs,
306
316
  subN: (lhs, rhs) => lhs - rhs,
307
317
  mulN: (lhs, rhs) => lhs * rhs,
308
- invert: (num) => invert(num, ORDER),
318
+ inv: (num) => invert(num, ORDER),
309
319
  sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
310
320
  invertBatch: (lst) => FpInvertBatch(f, lst),
311
321
  // TODO: do we really need constant cmov?
312
322
  // We don't have const-time bigints anyway, so probably will be not very useful
313
323
  cmov: (a, b, c) => (c ? b : a),
314
- toBytes: (num) => isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES),
324
+ toBytes: (num) => (isLE ? (0, utils_js_1.numberToBytesLE)(num, BYTES) : (0, utils_js_1.numberToBytesBE)(num, BYTES)),
315
325
  fromBytes: (bytes) => {
316
326
  if (bytes.length !== BYTES)
317
327
  throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
318
- return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes);
328
+ return isLE ? (0, utils_js_1.bytesToNumberLE)(bytes) : (0, utils_js_1.bytesToNumberBE)(bytes);
319
329
  },
320
330
  });
321
331
  return Object.freeze(f);
@@ -325,13 +335,32 @@ function FpSqrtOdd(Fp, elm) {
325
335
  if (!Fp.isOdd)
326
336
  throw new Error(`Field doesn't have isOdd`);
327
337
  const root = Fp.sqrt(elm);
328
- return Fp.isOdd(root) ? root : Fp.negate(root);
338
+ return Fp.isOdd(root) ? root : Fp.neg(root);
329
339
  }
330
340
  exports.FpSqrtOdd = FpSqrtOdd;
331
341
  function FpSqrtEven(Fp, elm) {
332
342
  if (!Fp.isOdd)
333
343
  throw new Error(`Field doesn't have isOdd`);
334
344
  const root = Fp.sqrt(elm);
335
- return Fp.isOdd(root) ? Fp.negate(root) : root;
345
+ return Fp.isOdd(root) ? Fp.neg(root) : root;
336
346
  }
337
347
  exports.FpSqrtEven = FpSqrtEven;
348
+ /**
349
+ * FIPS 186 B.4.1-compliant "constant-time" private key generation utility.
350
+ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF
351
+ * and convert them into private scalar, with the modulo bias being neglible.
352
+ * Needs at least 40 bytes of input for 32-byte private key.
353
+ * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
354
+ * @param hash hash output from SHA3 or a similar function
355
+ * @returns valid private scalar
356
+ */
357
+ function hashToPrivateScalar(hash, groupOrder, isLE = false) {
358
+ hash = (0, utils_js_1.ensureBytes)(hash);
359
+ const hashLen = hash.length;
360
+ const minLen = nLength(groupOrder).nByteLength + 8;
361
+ if (minLen < 24 || hashLen < minLen || hashLen > 1024)
362
+ throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
363
+ const num = isLE ? (0, utils_js_1.bytesToNumberLE)(hash) : (0, utils_js_1.bytesToNumberBE)(hash);
364
+ return mod(num, groupOrder - _1n) + _1n;
365
+ }
366
+ exports.hashToPrivateScalar = hashToPrivateScalar;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.montgomery = void 0;
4
4
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
- const mod = require("./modular.js");
5
+ const modular_js_1 = require("./modular.js");
6
6
  const utils_js_1 = require("./utils.js");
7
7
  const _0n = BigInt(0);
8
8
  const _1n = BigInt(1);
@@ -30,7 +30,6 @@ function validateOpts(curve) {
30
30
  throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`);
31
31
  }
32
32
  // Set defaults
33
- // ...nLength(curve.n, curve.nBitLength),
34
33
  return Object.freeze({ ...curve });
35
34
  }
36
35
  // NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
@@ -38,12 +37,12 @@ function validateOpts(curve) {
38
37
  function montgomery(curveDef) {
39
38
  const CURVE = validateOpts(curveDef);
40
39
  const { P } = CURVE;
41
- const modP = (a) => mod.mod(a, P);
40
+ const modP = (a) => (0, modular_js_1.mod)(a, P);
42
41
  const montgomeryBits = CURVE.montgomeryBits;
43
42
  const montgomeryBytes = Math.ceil(montgomeryBits / 8);
44
43
  const fieldLen = CURVE.nByteLength;
45
44
  const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes);
46
- const powPminus2 = CURVE.powPminus2 || ((x) => mod.pow(x, P - BigInt(2), P));
45
+ const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P));
47
46
  /**
48
47
  * Checks for num to be in range:
49
48
  * For strict == true: `0 < num < max`.
@@ -0,0 +1,29 @@
1
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
+ import { Field } from './modular.js';
3
+ export declare type PoseidonOpts = {
4
+ Fp: Field<bigint>;
5
+ t: number;
6
+ roundsFull: number;
7
+ roundsPartial: number;
8
+ sboxPower?: number;
9
+ reversePartialPowIdx?: boolean;
10
+ mds: bigint[][];
11
+ roundConstants: bigint[][];
12
+ };
13
+ export declare function validateOpts(opts: PoseidonOpts): Readonly<{
14
+ rounds: number;
15
+ sboxFn: (n: bigint) => bigint;
16
+ roundConstants: bigint[][];
17
+ mds: bigint[][];
18
+ Fp: Field<bigint>;
19
+ t: number;
20
+ roundsFull: number;
21
+ roundsPartial: number;
22
+ sboxPower?: number | undefined;
23
+ reversePartialPowIdx?: boolean | undefined;
24
+ }>;
25
+ export declare function splitConstants(rc: bigint[], t: number): bigint[][];
26
+ export declare function poseidon(opts: PoseidonOpts): {
27
+ (values: bigint[]): bigint[];
28
+ roundConstants: bigint[][];
29
+ };
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.poseidon = exports.splitConstants = exports.validateOpts = void 0;
4
+ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
5
+ // Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info
6
+ const modular_js_1 = require("./modular.js");
7
+ function validateOpts(opts) {
8
+ const { Fp } = opts;
9
+ (0, modular_js_1.validateField)(Fp);
10
+ for (const i of ['t', 'roundsFull', 'roundsPartial']) {
11
+ if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
12
+ throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
13
+ }
14
+ if (opts.reversePartialPowIdx !== undefined && typeof opts.reversePartialPowIdx !== 'boolean')
15
+ throw new Error(`Poseidon: invalid param reversePartialPowIdx=${opts.reversePartialPowIdx}`);
16
+ // Default is 5, but by some reasons stark uses 3
17
+ let sboxPower = opts.sboxPower;
18
+ if (sboxPower === undefined)
19
+ sboxPower = 5;
20
+ if (typeof sboxPower !== 'number' || !Number.isSafeInteger(sboxPower))
21
+ throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
22
+ const _sboxPower = BigInt(sboxPower);
23
+ let sboxFn = (n) => (0, modular_js_1.FpPow)(Fp, n, _sboxPower);
24
+ // Unwrapped sbox power for common cases (195->142μs)
25
+ if (sboxPower === 3)
26
+ sboxFn = (n) => Fp.mul(Fp.sqrN(n), n);
27
+ else if (sboxPower === 5)
28
+ sboxFn = (n) => Fp.mul(Fp.sqrN(Fp.sqrN(n)), n);
29
+ if (opts.roundsFull % 2 !== 0)
30
+ throw new Error(`Poseidon roundsFull is not even: ${opts.roundsFull}`);
31
+ const rounds = opts.roundsFull + opts.roundsPartial;
32
+ if (!Array.isArray(opts.roundConstants) || opts.roundConstants.length !== rounds)
33
+ throw new Error('Poseidon: wrong round constants');
34
+ const roundConstants = opts.roundConstants.map((rc) => {
35
+ if (!Array.isArray(rc) || rc.length !== opts.t)
36
+ throw new Error(`Poseidon wrong round constants: ${rc}`);
37
+ return rc.map((i) => {
38
+ if (typeof i !== 'bigint' || !Fp.isValid(i))
39
+ throw new Error(`Poseidon wrong round constant=${i}`);
40
+ return Fp.create(i);
41
+ });
42
+ });
43
+ // MDS is TxT matrix
44
+ if (!Array.isArray(opts.mds) || opts.mds.length !== opts.t)
45
+ throw new Error('Poseidon: wrong MDS matrix');
46
+ const mds = opts.mds.map((mdsRow) => {
47
+ if (!Array.isArray(mdsRow) || mdsRow.length !== opts.t)
48
+ throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
49
+ return mdsRow.map((i) => {
50
+ if (typeof i !== 'bigint')
51
+ throw new Error(`Poseidon MDS matrix value=${i}`);
52
+ return Fp.create(i);
53
+ });
54
+ });
55
+ return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds });
56
+ }
57
+ exports.validateOpts = validateOpts;
58
+ function splitConstants(rc, t) {
59
+ if (typeof t !== 'number')
60
+ throw new Error('poseidonSplitConstants: wrong t');
61
+ if (!Array.isArray(rc) || rc.length % t)
62
+ throw new Error('poseidonSplitConstants: wrong rc');
63
+ const res = [];
64
+ let tmp = [];
65
+ for (let i = 0; i < rc.length; i++) {
66
+ tmp.push(rc[i]);
67
+ if (tmp.length === t) {
68
+ res.push(tmp);
69
+ tmp = [];
70
+ }
71
+ }
72
+ return res;
73
+ }
74
+ exports.splitConstants = splitConstants;
75
+ function poseidon(opts) {
76
+ const { t, Fp, rounds, sboxFn, reversePartialPowIdx } = validateOpts(opts);
77
+ const halfRoundsFull = Math.floor(opts.roundsFull / 2);
78
+ const partialIdx = reversePartialPowIdx ? t - 1 : 0;
79
+ const poseidonRound = (values, isFull, idx) => {
80
+ values = values.map((i, j) => Fp.add(i, opts.roundConstants[idx][j]));
81
+ if (isFull)
82
+ values = values.map((i) => sboxFn(i));
83
+ else
84
+ values[partialIdx] = sboxFn(values[partialIdx]);
85
+ // Matrix multiplication
86
+ values = opts.mds.map((i) => i.reduce((acc, i, j) => Fp.add(acc, Fp.mulN(i, values[j])), Fp.ZERO));
87
+ return values;
88
+ };
89
+ const poseidonHash = function poseidonHash(values) {
90
+ if (!Array.isArray(values) || values.length !== t)
91
+ throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
92
+ values = values.map((i) => {
93
+ if (typeof i !== 'bigint')
94
+ throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
95
+ return Fp.create(i);
96
+ });
97
+ let round = 0;
98
+ // Apply r_f/2 full rounds.
99
+ for (let i = 0; i < halfRoundsFull; i++)
100
+ values = poseidonRound(values, true, round++);
101
+ // Apply r_p partial rounds.
102
+ for (let i = 0; i < opts.roundsPartial; i++)
103
+ values = poseidonRound(values, false, round++);
104
+ // Apply r_f/2 full rounds.
105
+ for (let i = 0; i < halfRoundsFull; i++)
106
+ values = poseidonRound(values, true, round++);
107
+ if (round !== rounds)
108
+ throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
109
+ return values;
110
+ };
111
+ // For verification in tests
112
+ poseidonHash.roundConstants = opts.roundConstants;
113
+ return poseidonHash;
114
+ }
115
+ exports.poseidon = poseidon;