@noble/curves 1.8.2 → 1.9.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 (177) hide show
  1. package/README.md +27 -15
  2. package/abstract/bls.js +1 -1
  3. package/abstract/bls.js.map +1 -1
  4. package/abstract/curve.d.ts +1 -1
  5. package/abstract/curve.d.ts.map +1 -1
  6. package/abstract/edwards.d.ts.map +1 -1
  7. package/abstract/edwards.js +3 -2
  8. package/abstract/edwards.js.map +1 -1
  9. package/abstract/hash-to-curve.d.ts +10 -5
  10. package/abstract/hash-to-curve.d.ts.map +1 -1
  11. package/abstract/hash-to-curve.js +31 -23
  12. package/abstract/hash-to-curve.js.map +1 -1
  13. package/abstract/modular.d.ts +11 -8
  14. package/abstract/modular.d.ts.map +1 -1
  15. package/abstract/modular.js +70 -58
  16. package/abstract/modular.js.map +1 -1
  17. package/abstract/montgomery.d.ts.map +1 -1
  18. package/abstract/montgomery.js +2 -1
  19. package/abstract/montgomery.js.map +1 -1
  20. package/abstract/poseidon.d.ts +39 -2
  21. package/abstract/poseidon.d.ts.map +1 -1
  22. package/abstract/poseidon.js +183 -4
  23. package/abstract/poseidon.js.map +1 -1
  24. package/abstract/tower.d.ts.map +1 -1
  25. package/abstract/tower.js +3 -4
  26. package/abstract/tower.js.map +1 -1
  27. package/abstract/utils.d.ts +1 -0
  28. package/abstract/utils.d.ts.map +1 -1
  29. package/abstract/utils.js +2 -0
  30. package/abstract/utils.js.map +1 -1
  31. package/abstract/weierstrass.d.ts +4 -5
  32. package/abstract/weierstrass.d.ts.map +1 -1
  33. package/abstract/weierstrass.js +6 -6
  34. package/abstract/weierstrass.js.map +1 -1
  35. package/bn254.d.ts +1 -0
  36. package/bn254.d.ts.map +1 -1
  37. package/bn254.js +10 -0
  38. package/bn254.js.map +1 -1
  39. package/ed25519.d.ts +2 -1
  40. package/ed25519.d.ts.map +1 -1
  41. package/ed25519.js +6 -6
  42. package/ed25519.js.map +1 -1
  43. package/ed448.d.ts +2 -1
  44. package/ed448.d.ts.map +1 -1
  45. package/ed448.js +5 -5
  46. package/ed448.js.map +1 -1
  47. package/esm/abstract/bls.js +1 -1
  48. package/esm/abstract/bls.js.map +1 -1
  49. package/esm/abstract/curve.d.ts +1 -1
  50. package/esm/abstract/curve.d.ts.map +1 -1
  51. package/esm/abstract/edwards.d.ts.map +1 -1
  52. package/esm/abstract/edwards.js +5 -4
  53. package/esm/abstract/edwards.js.map +1 -1
  54. package/esm/abstract/hash-to-curve.d.ts +10 -5
  55. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  56. package/esm/abstract/hash-to-curve.js +32 -24
  57. package/esm/abstract/hash-to-curve.js.map +1 -1
  58. package/esm/abstract/modular.d.ts +11 -8
  59. package/esm/abstract/modular.d.ts.map +1 -1
  60. package/esm/abstract/modular.js +70 -58
  61. package/esm/abstract/modular.js.map +1 -1
  62. package/esm/abstract/montgomery.d.ts.map +1 -1
  63. package/esm/abstract/montgomery.js +3 -2
  64. package/esm/abstract/montgomery.js.map +1 -1
  65. package/esm/abstract/poseidon.d.ts +39 -2
  66. package/esm/abstract/poseidon.d.ts.map +1 -1
  67. package/esm/abstract/poseidon.js +180 -5
  68. package/esm/abstract/poseidon.js.map +1 -1
  69. package/esm/abstract/tower.d.ts.map +1 -1
  70. package/esm/abstract/tower.js +3 -4
  71. package/esm/abstract/tower.js.map +1 -1
  72. package/esm/abstract/utils.d.ts +1 -0
  73. package/esm/abstract/utils.d.ts.map +1 -1
  74. package/esm/abstract/utils.js +2 -0
  75. package/esm/abstract/utils.js.map +1 -1
  76. package/esm/abstract/weierstrass.d.ts +4 -5
  77. package/esm/abstract/weierstrass.d.ts.map +1 -1
  78. package/esm/abstract/weierstrass.js +8 -8
  79. package/esm/abstract/weierstrass.js.map +1 -1
  80. package/esm/bn254.d.ts +1 -0
  81. package/esm/bn254.d.ts.map +1 -1
  82. package/esm/bn254.js +10 -0
  83. package/esm/bn254.js.map +1 -1
  84. package/esm/ed25519.d.ts +2 -1
  85. package/esm/ed25519.d.ts.map +1 -1
  86. package/esm/ed25519.js +6 -6
  87. package/esm/ed25519.js.map +1 -1
  88. package/esm/ed448.d.ts +2 -1
  89. package/esm/ed448.d.ts.map +1 -1
  90. package/esm/ed448.js +5 -5
  91. package/esm/ed448.js.map +1 -1
  92. package/esm/jubjub.d.ts +7 -1
  93. package/esm/jubjub.d.ts.map +1 -1
  94. package/esm/jubjub.js +7 -1
  95. package/esm/jubjub.js.map +1 -1
  96. package/esm/misc.d.ts +8 -2
  97. package/esm/misc.d.ts.map +1 -1
  98. package/esm/misc.js +10 -4
  99. package/esm/misc.js.map +1 -1
  100. package/esm/nist.d.ts +29 -0
  101. package/esm/nist.d.ts.map +1 -0
  102. package/esm/nist.js +120 -0
  103. package/esm/nist.js.map +1 -0
  104. package/esm/p256.d.ts +7 -9
  105. package/esm/p256.d.ts.map +1 -1
  106. package/esm/p256.js +6 -44
  107. package/esm/p256.js.map +1 -1
  108. package/esm/p384.d.ts +9 -10
  109. package/esm/p384.d.ts.map +1 -1
  110. package/esm/p384.js +7 -46
  111. package/esm/p384.js.map +1 -1
  112. package/esm/p521.d.ts +7 -8
  113. package/esm/p521.d.ts.map +1 -1
  114. package/esm/p521.js +6 -46
  115. package/esm/p521.js.map +1 -1
  116. package/esm/pasta.d.ts +5 -1
  117. package/esm/pasta.d.ts.map +1 -1
  118. package/esm/pasta.js +5 -1
  119. package/esm/pasta.js.map +1 -1
  120. package/esm/secp256k1.d.ts +3 -3
  121. package/esm/secp256k1.d.ts.map +1 -1
  122. package/esm/secp256k1.js +5 -6
  123. package/esm/secp256k1.js.map +1 -1
  124. package/jubjub.d.ts +7 -1
  125. package/jubjub.d.ts.map +1 -1
  126. package/jubjub.js +8 -5
  127. package/jubjub.js.map +1 -1
  128. package/misc.d.ts +8 -2
  129. package/misc.d.ts.map +1 -1
  130. package/misc.js +11 -5
  131. package/misc.js.map +1 -1
  132. package/nist.d.ts +29 -0
  133. package/nist.d.ts.map +1 -0
  134. package/nist.js +123 -0
  135. package/nist.js.map +1 -0
  136. package/p256.d.ts +7 -9
  137. package/p256.d.ts.map +1 -1
  138. package/p256.js +5 -49
  139. package/p256.js.map +1 -1
  140. package/p384.d.ts +9 -10
  141. package/p384.d.ts.map +1 -1
  142. package/p384.js +6 -51
  143. package/p384.js.map +1 -1
  144. package/p521.d.ts +7 -8
  145. package/p521.d.ts.map +1 -1
  146. package/p521.js +5 -51
  147. package/p521.js.map +1 -1
  148. package/package.json +106 -6
  149. package/pasta.d.ts +5 -1
  150. package/pasta.d.ts.map +1 -1
  151. package/pasta.js +5 -3
  152. package/pasta.js.map +1 -1
  153. package/secp256k1.d.ts +3 -3
  154. package/secp256k1.d.ts.map +1 -1
  155. package/secp256k1.js +6 -7
  156. package/secp256k1.js.map +1 -1
  157. package/src/abstract/bls.ts +1 -1
  158. package/src/abstract/curve.ts +1 -1
  159. package/src/abstract/edwards.ts +11 -11
  160. package/src/abstract/hash-to-curve.ts +44 -36
  161. package/src/abstract/modular.ts +67 -58
  162. package/src/abstract/montgomery.ts +3 -2
  163. package/src/abstract/poseidon.ts +208 -13
  164. package/src/abstract/tower.ts +3 -4
  165. package/src/abstract/utils.ts +2 -0
  166. package/src/abstract/weierstrass.ts +19 -15
  167. package/src/bn254.ts +10 -0
  168. package/src/ed25519.ts +8 -7
  169. package/src/ed448.ts +7 -5
  170. package/src/jubjub.ts +8 -5
  171. package/src/misc.ts +10 -4
  172. package/src/nist.ts +154 -0
  173. package/src/p256.ts +6 -50
  174. package/src/p384.ts +8 -56
  175. package/src/p521.ts +6 -65
  176. package/src/pasta.ts +5 -1
  177. package/src/secp256k1.ts +9 -8
@@ -6,7 +6,7 @@
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
8
  import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
9
- import { type IField, mod } from './modular.ts';
9
+ import { FpInvertBatch, type IField, mod } from './modular.ts';
10
10
  import type { CHash } from './utils.ts';
11
11
  import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } from './utils.ts';
12
12
 
@@ -172,24 +172,23 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
172
172
  return u;
173
173
  }
174
174
 
175
- export type XY<T> = (
176
- x: T,
177
- y: T
178
- ) => {
179
- x: T;
180
- y: T;
181
- };
182
- export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]): XY<T> {
175
+ export type XY<T> = (x: T, y: T) => { x: T; y: T };
176
+ export type XYRatio<T> = [T[], T[], T[], T[]]; // xn/xd, yn/yd
177
+ export function isogenyMap<T, F extends IField<T>>(field: F, map: XYRatio<T>): XY<T> {
183
178
  // Make same order as in spec
184
- const COEFF = map.map((i) => Array.from(i).reverse());
179
+ const coeff = map.map((i) => Array.from(i).reverse());
185
180
  return (x: T, y: T) => {
186
- const [xNum, xDen, yNum, yDen] = COEFF.map((val) =>
181
+ const [xn, xd, yn, yd] = coeff.map((val) =>
187
182
  val.reduce((acc, i) => field.add(field.mul(acc, x), i))
188
183
  );
189
- if (field.is0(xDen) || field.is0(yDen)) throw new Error('bad point: ZERO');
190
- x = field.div(xNum, xDen); // xNum / xDen
191
- y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
192
- return { x: x, y: y };
184
+ // 6.6.3
185
+ // Exceptional cases of iso_map are inputs that cause the denominator of
186
+ // either rational function to evaluate to zero; such cases MUST return
187
+ // the identity point on E.
188
+ const [xd_inv, yd_inv] = FpInvertBatch(field, [xd, yd], true);
189
+ x = field.mul(xn, xd_inv); // xNum / xDen
190
+ y = field.mul(y, field.mul(yn, yd_inv)); // y * (yNum / yDev)
191
+ return { x, y };
193
192
  };
194
193
  }
195
194
 
@@ -212,46 +211,55 @@ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
212
211
  export type htfBasicOpts = { DST: UnicodeOrBytes };
213
212
  export type HTFMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
214
213
  export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
214
+ export type Hasher<T> = {
215
+ hashToCurve: HTFMethod<T>;
216
+ encodeToCurve: HTFMethod<T>;
217
+ mapToCurve: MapMethod<T>;
218
+ defaults: Opts & { encodeDST?: UnicodeOrBytes };
219
+ };
215
220
 
216
221
  /** Creates hash-to-curve methods from EC Point and mapToCurve function. */
217
222
  export function createHasher<T>(
218
223
  Point: H2CPointConstructor<T>,
219
224
  mapToCurve: MapToCurve<T>,
220
- def: Opts & { encodeDST?: UnicodeOrBytes }
221
- ): {
222
- hashToCurve: HTFMethod<T>;
223
- encodeToCurve: HTFMethod<T>;
224
- mapToCurve: MapMethod<T>;
225
- } {
225
+ defaults: Opts & { encodeDST?: UnicodeOrBytes }
226
+ ): Hasher<T> {
226
227
  if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
228
+ function map(num: bigint[]) {
229
+ return Point.fromAffine(mapToCurve(num));
230
+ }
231
+ function clear(initial: H2CPoint<T>) {
232
+ const P = initial.clearCofactor();
233
+ if (P.equals(Point.ZERO)) return Point.ZERO; // zero will throw in assert
234
+ P.assertValidity();
235
+ return P;
236
+ }
237
+
227
238
  return {
239
+ defaults,
240
+
228
241
  // Encodes byte string to elliptic curve.
229
242
  // hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
230
243
  hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
231
- const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
232
- const u0 = Point.fromAffine(mapToCurve(u[0]));
233
- const u1 = Point.fromAffine(mapToCurve(u[1]));
234
- const P = u0.add(u1).clearCofactor();
235
- P.assertValidity();
236
- return P;
244
+ const u = hash_to_field(msg, 2, { ...defaults, DST: defaults.DST, ...options } as Opts);
245
+ const u0 = map(u[0]);
246
+ const u1 = map(u[1]);
247
+ return clear(u0.add(u1));
237
248
  },
238
249
 
239
250
  // Encodes byte string to elliptic curve.
240
251
  // encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
241
252
  encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
242
- const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
243
- const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
244
- P.assertValidity();
245
- return P;
253
+ const u = hash_to_field(msg, 1, { ...defaults, DST: defaults.encodeDST, ...options } as Opts);
254
+ return clear(map(u[0]));
246
255
  },
256
+
247
257
  // Same as encodeToCurve, but without hash
248
258
  mapToCurve(scalars: bigint[]): H2CPoint<T> {
249
- if (!Array.isArray(scalars)) throw new Error('mapToCurve: expected array of bigints');
259
+ if (!Array.isArray(scalars)) throw new Error('expected array of bigints');
250
260
  for (const i of scalars)
251
- if (typeof i !== 'bigint') throw new Error('mapToCurve: expected array of bigints');
252
- const P = Point.fromAffine(mapToCurve(scalars)).clearCofactor();
253
- P.assertValidity();
254
- return P;
261
+ if (typeof i !== 'bigint') throw new Error('expected array of bigints');
262
+ return clear(map(scalars));
255
263
  },
256
264
  };
257
265
  }
@@ -5,6 +5,7 @@
5
5
  * @module
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
+ import { anumber } from '@noble/hashes/utils';
8
9
  import {
9
10
  bitMask,
10
11
  bytesToNumberBE,
@@ -30,7 +31,7 @@ export function mod(a: bigint, b: bigint): bigint {
30
31
  /**
31
32
  * Efficiently raise num to power and do modular division.
32
33
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
33
- * @todo use field version && remove
34
+ * TODO: remove.
34
35
  * @example
35
36
  * pow(2n, 6n, 11n) // 64n % 11n == 9n
36
37
  */
@@ -87,27 +88,25 @@ export function invert(number: bigint, modulo: bigint): bigint {
87
88
  * Tonelli-Shanks square root search algorithm.
88
89
  * 1. https://eprint.iacr.org/2012/685.pdf (page 12)
89
90
  * 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
90
- * Will start an infinite loop if field order P is not prime.
91
91
  * @param P field order
92
92
  * @returns function that takes field Fp (created from P) and number n
93
93
  */
94
94
  export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
95
- // Legendre constant: used to calculate Legendre symbol (a | p),
96
- // which denotes the value of a^((p-1)/2) (mod p).
97
- // (a | p) ≡ 1 if a is a square (mod p)
98
- // (a | p) ≡ -1 if a is not a square (mod p)
99
- // (a | p) ≡ 0 if a ≡ 0 (mod p)
100
- const legendreC = (P - _1n) / _2n;
101
-
102
- let Q: bigint, S: number, Z: bigint;
95
+ // Do expensive precomputation step
103
96
  // Step 1: By factoring out powers of 2 from p - 1,
104
- // find q and s such that p - 1 = q*(2^s) with q odd
105
- for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
97
+ // find q and s such that p-1 == q*(2^s) with q odd
98
+ let Q = P - _1n;
99
+ let S = 0;
100
+ while (Q % _2n === _0n) {
101
+ Q /= _2n;
102
+ S++;
103
+ }
106
104
 
107
105
  // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
108
- for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++) {
109
- // Crash instead of infinity loop, we cannot reasonable count until P.
110
- if (Z > 1000) throw new Error('Cannot find square root: likely non-prime P');
106
+ let Z = _2n;
107
+ const _Fp = Field(P);
108
+ while (Z < P && FpIsSquare(_Fp, Z)) {
109
+ if (Z++ > 1000) throw new Error('Cannot find square root: probably non-prime P');
111
110
  }
112
111
 
113
112
  // Fast-path
@@ -119,27 +118,29 @@ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
119
118
  return root;
120
119
  };
121
120
  }
122
-
123
121
  // Slow-path
124
122
  const Q1div2 = (Q + _1n) / _2n;
125
123
  return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
126
124
  // Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1
127
- if (Fp.pow(n, legendreC) === Fp.neg(Fp.ONE)) throw new Error('Cannot find square root');
125
+ if (!FpIsSquare(Fp, n)) throw new Error('Cannot find square root');
128
126
  let r = S;
129
- // TODO: will fail at Fp2/etc
127
+ // TODO: test on Fp2 and others
130
128
  let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b
131
129
  let x = Fp.pow(n, Q1div2); // first guess at the square root
132
130
  let b = Fp.pow(n, Q); // first guess at the fudge factor
133
131
 
134
132
  while (!Fp.eql(b, Fp.ONE)) {
135
- if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0)
133
+ // (4. If t = 0, return r = 0)
134
+ // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
135
+ if (Fp.eql(b, Fp.ZERO)) return Fp.ZERO;
136
136
  // Find m such b^(2^m)==1
137
137
  let m = 1;
138
138
  for (let t2 = Fp.sqr(b); m < r; m++) {
139
139
  if (Fp.eql(t2, Fp.ONE)) break;
140
140
  t2 = Fp.sqr(t2); // t2 *= t2
141
141
  }
142
- // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow
142
+ // NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift,
143
+ // otherwise there will be overflow.
143
144
  const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1)
144
145
  g = Fp.sqr(ge); // g = ge * ge
145
146
  x = Fp.mul(x, ge); // x *= ge
@@ -169,8 +170,8 @@ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
169
170
  // const ORDER =
170
171
  // 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn;
171
172
  // const NUM = 72057594037927816n;
172
- const p1div4 = (P + _1n) / _4n;
173
173
  return function sqrt3mod4<T>(Fp: IField<T>, n: T) {
174
+ const p1div4 = (P + _1n) / _4n;
174
175
  const root = Fp.pow(n, p1div4);
175
176
  // Throw if root**2 != n
176
177
  if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
@@ -180,9 +181,9 @@ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
180
181
 
181
182
  // Atkin algorithm for q ≡ 5 (mod 8), https://eprint.iacr.org/2012/685.pdf (page 10)
182
183
  if (P % _8n === _5n) {
183
- const c1 = (P - _5n) / _8n;
184
184
  return function sqrt5mod8<T>(Fp: IField<T>, n: T) {
185
185
  const n2 = Fp.mul(n, _2n);
186
+ const c1 = (P - _5n) / _8n;
186
187
  const v = Fp.pow(n2, c1);
187
188
  const nv = Fp.mul(n, v);
188
189
  const i = Fp.mul(Fp.mul(nv, _2n), v);
@@ -291,17 +292,16 @@ export function validateField<T>(field: IField<T>): IField<T> {
291
292
  * Same as `pow` but for Fp: non-constant-time.
292
293
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
293
294
  */
294
- export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
295
- // Should have same speed as pow for bigints
296
- // TODO: benchmark!
295
+ export function FpPow<T>(Fp: IField<T>, num: T, power: bigint): T {
297
296
  if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
298
- if (power === _0n) return f.ONE;
297
+ if (power === _0n) return Fp.ONE;
299
298
  if (power === _1n) return num;
300
- let p = f.ONE;
299
+ // @ts-ignore
300
+ let p = Fp.ONE;
301
301
  let d = num;
302
302
  while (power > _0n) {
303
- if (power & _1n) p = f.mul(p, d);
304
- d = f.sqr(d);
303
+ if (power & _1n) p = Fp.mul(p, d);
304
+ d = Fp.sqr(d);
305
305
  power >>= _1n;
306
306
  }
307
307
  return p;
@@ -309,49 +309,56 @@ export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
309
309
 
310
310
  /**
311
311
  * Efficiently invert an array of Field elements.
312
- * `inv(0)` will return `undefined` here: make sure to throw an error.
312
+ * Exception-free. Will return `undefined` for 0 elements.
313
+ * @param passZero map 0 to 0 (instead of undefined)
313
314
  */
314
- export function FpInvertBatch<T>(f: IField<T>, nums: T[]): T[] {
315
- const tmp = new Array(nums.length);
315
+ export function FpInvertBatch<T>(Fp: IField<T>, nums: T[], passZero = false): T[] {
316
+ const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined);
316
317
  // Walk from first to last, multiply them by each other MOD p
317
- const lastMultiplied = nums.reduce((acc, num, i) => {
318
- if (f.is0(num)) return acc;
319
- tmp[i] = acc;
320
- return f.mul(acc, num);
321
- }, f.ONE);
318
+ const multipliedAcc = nums.reduce((acc, num, i) => {
319
+ if (Fp.is0(num)) return acc;
320
+ inverted[i] = acc;
321
+ return Fp.mul(acc, num);
322
+ }, Fp.ONE);
322
323
  // Invert last element
323
- const inverted = f.inv(lastMultiplied);
324
+ const invertedAcc = Fp.inv(multipliedAcc);
324
325
  // Walk from last to first, multiply them by inverted each other MOD p
325
326
  nums.reduceRight((acc, num, i) => {
326
- if (f.is0(num)) return acc;
327
- tmp[i] = f.mul(acc, tmp[i]);
328
- return f.mul(acc, num);
329
- }, inverted);
330
- return tmp;
327
+ if (Fp.is0(num)) return acc;
328
+ inverted[i] = Fp.mul(acc, inverted[i]);
329
+ return Fp.mul(acc, num);
330
+ }, invertedAcc);
331
+ return inverted;
331
332
  }
332
333
 
333
- export function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T {
334
- return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
334
+ // TODO: remove
335
+ export function FpDiv<T>(Fp: IField<T>, lhs: T, rhs: T | bigint): T {
336
+ return Fp.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, Fp.ORDER) : Fp.inv(rhs));
335
337
  }
336
338
 
337
339
  /**
338
340
  * Legendre symbol.
341
+ * Legendre constant is used to calculate Legendre symbol (a | p)
342
+ * which denotes the value of a^((p-1)/2) (mod p)..
343
+ *
339
344
  * * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
340
345
  * * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
341
346
  * * (a | p) ≡ 0 if a ≡ 0 (mod p)
342
347
  */
343
- export function FpLegendre(order: bigint): <T>(f: IField<T>, x: T) => T {
344
- const legendreConst = (order - _1n) / _2n; // Integer arithmetic
345
- return <T>(f: IField<T>, x: T): T => f.pow(x, legendreConst);
348
+ export function FpLegendre<T>(Fp: IField<T>, n: T): number {
349
+ const legc = (Fp.ORDER - _1n) / _2n;
350
+ const powered = Fp.pow(n, legc);
351
+ const yes = Fp.eql(powered, Fp.ONE);
352
+ const zero = Fp.eql(powered, Fp.ZERO);
353
+ const no = Fp.eql(powered, Fp.neg(Fp.ONE));
354
+ if (!yes && !zero && !no) throw new Error('Cannot find square root: probably non-prime P');
355
+ return yes ? 1 : zero ? 0 : -1;
346
356
  }
347
357
 
348
358
  // This function returns True whenever the value x is a square in the field F.
349
- export function FpIsSquare<T>(f: IField<T>): (x: T) => boolean {
350
- const legendre = FpLegendre(f.ORDER);
351
- return (x: T): boolean => {
352
- const p = legendre(f, x);
353
- return f.eql(p, f.ZERO) || f.eql(p, f.ONE);
354
- };
359
+ export function FpIsSquare<T>(Fp: IField<T>, n: T): boolean {
360
+ const l = FpLegendre(Fp, n);
361
+ return l === 0 || l === 1;
355
362
  }
356
363
 
357
364
  // CURVE.n lengths
@@ -363,6 +370,7 @@ export function nLength(
363
370
  nByteLength: number;
364
371
  } {
365
372
  // Bit size, byte size of CURVE.n
373
+ if (nBitLength !== undefined) anumber(nBitLength);
366
374
  const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
367
375
  const nByteLength = Math.ceil(_nBitLength / 8);
368
376
  return { nBitLength: _nBitLength, nByteLength };
@@ -433,16 +441,17 @@ export function Field(
433
441
  if (!sqrtP) sqrtP = FpSqrt(ORDER);
434
442
  return sqrtP(f, n);
435
443
  }),
436
- invertBatch: (lst) => FpInvertBatch(f, lst),
437
- // TODO: do we really need constant cmov?
438
- // We don't have const-time bigints anyway, so probably will be not very useful
439
- cmov: (a, b, c) => (c ? b : a),
440
444
  toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
441
445
  fromBytes: (bytes) => {
442
446
  if (bytes.length !== BYTES)
443
447
  throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
444
448
  return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
445
449
  },
450
+ // TODO: we don't need it here, move out to separate fn
451
+ invertBatch: (lst) => FpInvertBatch(f, lst),
452
+ // We can't move this out because Fp6, Fp12 implement it
453
+ // and it's unclear what to return in there.
454
+ cmov: (a, b, c) => (c ? b : a),
446
455
  } as FpField);
447
456
  return Object.freeze(f);
448
457
  }
@@ -5,7 +5,7 @@
5
5
  * @module
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
- import { mod, pow } from './modular.ts';
8
+ import { Field, mod } from './modular.ts';
9
9
  import {
10
10
  aInRange,
11
11
  bytesToNumberLE,
@@ -63,12 +63,13 @@ function validateOpts(curve: CurveType) {
63
63
  export function montgomery(curveDef: CurveType): CurveFn {
64
64
  const CURVE = validateOpts(curveDef);
65
65
  const { P } = CURVE;
66
+ const Fp = Field(P);
66
67
  const modP = (n: bigint) => mod(n, P);
67
68
  const montgomeryBits = CURVE.montgomeryBits;
68
69
  const montgomeryBytes = Math.ceil(montgomeryBits / 8);
69
70
  const fieldLen = CURVE.nByteLength;
70
71
  const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes: Uint8Array) => bytes);
71
- const powPminus2 = CURVE.powPminus2 || ((x: bigint) => pow(x, P - BigInt(2), P));
72
+ const powPminus2 = CURVE.powPminus2 || ((x: bigint) => Fp.pow(x, P - BigInt(2)));
72
73
 
73
74
  // cswap from RFC7748. But it is not from RFC7748!
74
75
  /*
@@ -7,19 +7,127 @@
7
7
  * @module
8
8
  */
9
9
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
10
- import { FpPow, type IField, validateField } from './modular.ts';
10
+ import { FpInvertBatch, FpPow, type IField, validateField } from './modular.ts';
11
+ import { bitGet } from './utils.ts';
11
12
 
12
- export type PoseidonOpts = {
13
+ // Grain LFSR (Linear-Feedback Shift Register): https://eprint.iacr.org/2009/109.pdf
14
+ function grainLFSR(state: number[]): () => boolean {
15
+ let pos = 0;
16
+ if (state.length !== 80) throw new Error('grainLFRS: wrong state length, should be 80 bits');
17
+ const getBit = (): boolean => {
18
+ const r = (offset: number) => state[(pos + offset) % 80];
19
+ const bit = r(62) ^ r(51) ^ r(38) ^ r(23) ^ r(13) ^ r(0);
20
+ state[pos] = bit;
21
+ pos = ++pos % 80;
22
+ return !!bit;
23
+ };
24
+ for (let i = 0; i < 160; i++) getBit();
25
+ return () => {
26
+ // https://en.wikipedia.org/wiki/Shrinking_generator
27
+ while (true) {
28
+ const b1 = getBit();
29
+ const b2 = getBit();
30
+ if (!b1) continue;
31
+ return b2;
32
+ }
33
+ };
34
+ }
35
+
36
+ export type PoseidonBasicOpts = {
13
37
  Fp: IField<bigint>;
14
- t: number;
38
+ t: number; // t = rate + capacity
15
39
  roundsFull: number;
16
40
  roundsPartial: number;
41
+ isSboxInverse?: boolean;
42
+ };
43
+
44
+ function validateBasicOpts(opts: PoseidonBasicOpts) {
45
+ const { Fp, roundsFull } = opts;
46
+ validateField(Fp);
47
+ for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
48
+ if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
49
+ throw new Error('invalid number ' + i);
50
+ }
51
+ if (opts.isSboxInverse !== undefined && typeof opts.isSboxInverse !== 'boolean')
52
+ throw new Error(`Poseidon: invalid param isSboxInverse=${opts.isSboxInverse}`);
53
+ if (roundsFull & 1) throw new Error('roundsFull is not even' + roundsFull);
54
+ }
55
+
56
+ function poseidonGrain(opts: PoseidonBasicOpts) {
57
+ validateBasicOpts(opts);
58
+ const { Fp } = opts;
59
+ const state = Array(80).fill(1);
60
+ let pos = 0;
61
+ const writeBits = (value: bigint, bitCount: number) => {
62
+ for (let i = bitCount - 1; i >= 0; i--) state[pos++] = Number(bitGet(value, i));
63
+ };
64
+ const _0n = BigInt(0);
65
+ const _1n = BigInt(1);
66
+ writeBits(_1n, 2); // prime field
67
+ writeBits(opts.isSboxInverse ? _1n : _0n, 4); // b2..b5
68
+ writeBits(BigInt(Fp.BITS), 12); // b6..b17
69
+ writeBits(BigInt(opts.t), 12); // b18..b29
70
+ writeBits(BigInt(opts.roundsFull), 10); // b30..b39
71
+ writeBits(BigInt(opts.roundsPartial), 10); // b40..b49
72
+
73
+ const getBit = grainLFSR(state);
74
+ return (count: number, reject: boolean): bigint[] => {
75
+ const res: bigint[] = [];
76
+ for (let i = 0; i < count; i++) {
77
+ while (true) {
78
+ let num = _0n;
79
+ for (let i = 0; i < Fp.BITS; i++) {
80
+ num <<= _1n;
81
+ if (getBit()) num |= _1n;
82
+ }
83
+ if (reject && num >= Fp.ORDER) continue; // rejection sampling
84
+ res.push(Fp.create(num));
85
+ break;
86
+ }
87
+ }
88
+ return res;
89
+ };
90
+ }
91
+
92
+ export type PoseidonGrainOpts = PoseidonBasicOpts & {
17
93
  sboxPower?: number;
18
- reversePartialPowIdx?: boolean; // Hack for stark
19
- mds: bigint[][];
20
- roundConstants: bigint[][];
21
94
  };
22
95
 
96
+ type PoseidonConstants = { mds: bigint[][]; roundConstants: bigint[][] };
97
+
98
+ // NOTE: this is not standard but used often for constant generation for poseidon
99
+ // (grain LFRS-like structure)
100
+ export function grainGenConstants(opts: PoseidonGrainOpts, skipMDS: number = 0): PoseidonConstants {
101
+ const { Fp, t, roundsFull, roundsPartial } = opts;
102
+ const rounds = roundsFull + roundsPartial;
103
+ const sample = poseidonGrain(opts);
104
+ const roundConstants: bigint[][] = [];
105
+ for (let r = 0; r < rounds; r++) roundConstants.push(sample(t, true));
106
+ if (skipMDS > 0) for (let i = 0; i < skipMDS; i++) sample(2 * t, false);
107
+ const xs = sample(t, false);
108
+ const ys = sample(t, false);
109
+ // Construct MDS Matrix M[i][j] = 1 / (xs[i] + ys[j])
110
+ const mds: bigint[][] = [];
111
+ for (let i = 0; i < t; i++) {
112
+ const row: bigint[] = [];
113
+ for (let j = 0; j < t; j++) {
114
+ const xy = Fp.add(xs[i], ys[j]);
115
+ if (Fp.is0(xy))
116
+ throw new Error(`Error generating MDS matrix: xs[${i}] + ys[${j}] resulted in zero.`);
117
+ row.push(xy);
118
+ }
119
+ mds.push(FpInvertBatch(Fp, row));
120
+ }
121
+
122
+ return { roundConstants, mds };
123
+ }
124
+
125
+ export type PoseidonOpts = PoseidonBasicOpts &
126
+ PoseidonConstants & {
127
+ sboxPower?: number;
128
+ reversePartialPowIdx?: boolean; // Hack for stark
129
+ };
130
+
23
131
  export function validateOpts(opts: PoseidonOpts): Readonly<{
24
132
  rounds: number;
25
133
  sboxFn: (n: bigint) => bigint;
@@ -32,15 +140,10 @@ export function validateOpts(opts: PoseidonOpts): Readonly<{
32
140
  sboxPower?: number;
33
141
  reversePartialPowIdx?: boolean; // Hack for stark
34
142
  }> {
143
+ validateBasicOpts(opts);
35
144
  const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
36
145
  const { roundsFull, roundsPartial, sboxPower, t } = opts;
37
146
 
38
- validateField(Fp);
39
- for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
40
- if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
41
- throw new Error('invalid number ' + i);
42
- }
43
-
44
147
  // MDS is TxT matrix
45
148
  if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: invalid MDS matrix');
46
149
  const _mds = mds.map((mdsRow) => {
@@ -68,7 +171,7 @@ export function validateOpts(opts: PoseidonOpts): Readonly<{
68
171
  });
69
172
  });
70
173
 
71
- if (!sboxPower || ![3, 5, 7].includes(sboxPower)) throw new Error('invalid sboxPower');
174
+ if (!sboxPower || ![3, 5, 7, 17].includes(sboxPower)) throw new Error('invalid sboxPower');
72
175
  const _sboxPower = BigInt(sboxPower);
73
176
  let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
74
177
  // Unwrapped sbox power for common cases (195->142μs)
@@ -134,3 +237,95 @@ export function poseidon(opts: PoseidonOpts): {
134
237
  poseidonHash.roundConstants = roundConstants;
135
238
  return poseidonHash;
136
239
  }
240
+
241
+ export class PoseidonSponge {
242
+ private Fp: IField<bigint>;
243
+ readonly rate: number;
244
+ readonly capacity: number;
245
+ readonly hash: ReturnType<typeof poseidon>;
246
+ private state: bigint[]; // [...capacity, ...rate]
247
+ private pos = 0;
248
+ private isAbsorbing = true;
249
+
250
+ constructor(
251
+ Fp: IField<bigint>,
252
+ rate: number,
253
+ capacity: number,
254
+ hash: ReturnType<typeof poseidon>
255
+ ) {
256
+ this.Fp = Fp;
257
+ this.hash = hash;
258
+ this.rate = rate;
259
+ this.capacity = capacity;
260
+ this.state = new Array(rate + capacity);
261
+ this.clean();
262
+ }
263
+ private process(): void {
264
+ this.state = this.hash(this.state);
265
+ }
266
+ absorb(input: bigint[]): void {
267
+ for (const i of input)
268
+ if (typeof i !== 'bigint' || !this.Fp.isValid(i)) throw new Error('invalid input: ' + i);
269
+ for (let i = 0; i < input.length; ) {
270
+ if (!this.isAbsorbing || this.pos === this.rate) {
271
+ this.process();
272
+ this.pos = 0;
273
+ this.isAbsorbing = true;
274
+ }
275
+ const chunk = Math.min(this.rate - this.pos, input.length - i);
276
+ for (let j = 0; j < chunk; j++) {
277
+ const idx = this.capacity + this.pos++;
278
+ this.state[idx] = this.Fp.add(this.state[idx], input[i++]);
279
+ }
280
+ }
281
+ }
282
+ squeeze(count: number): bigint[] {
283
+ const res: bigint[] = [];
284
+ while (res.length < count) {
285
+ if (this.isAbsorbing || this.pos === this.rate) {
286
+ this.process();
287
+ this.pos = 0;
288
+ this.isAbsorbing = false;
289
+ }
290
+ const chunk = Math.min(this.rate - this.pos, count - res.length);
291
+ for (let i = 0; i < chunk; i++) res.push(this.state[this.capacity + this.pos++]);
292
+ }
293
+ return res;
294
+ }
295
+ clean(): void {
296
+ this.state.fill(this.Fp.ZERO);
297
+ this.isAbsorbing = true;
298
+ this.pos = 0;
299
+ }
300
+ clone(): PoseidonSponge {
301
+ const c = new PoseidonSponge(this.Fp, this.rate, this.capacity, this.hash);
302
+ c.pos = this.pos;
303
+ c.state = [...this.state];
304
+ return c;
305
+ }
306
+ }
307
+
308
+ export type PoseidonSpongeOpts = Omit<PoseidonOpts, 't'> & {
309
+ rate: number;
310
+ capacity: number;
311
+ };
312
+
313
+ /**
314
+ * The method is not defined in spec, but nevertheless used often.
315
+ * Check carefully for compatibility: there are many edge cases, like absorbing an empty array.
316
+ * We cross-test against:
317
+ * - https://github.com/ProvableHQ/snarkVM/tree/staging/algorithms
318
+ * - https://github.com/arkworks-rs/crypto-primitives/tree/main
319
+ */
320
+ export function poseidonSponge(opts: PoseidonSpongeOpts): () => PoseidonSponge {
321
+ for (const i of ['rate', 'capacity'] as const) {
322
+ if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
323
+ throw new Error('invalid number ' + i);
324
+ }
325
+ const { rate, capacity } = opts;
326
+ const t = opts.rate + opts.capacity;
327
+ // Re-use hash instance between multiple instances
328
+ const hash = poseidon({ ...opts, t });
329
+ const { Fp } = opts;
330
+ return () => new PoseidonSponge(Fp, rate, capacity, hash);
331
+ }
@@ -167,7 +167,6 @@ export function tower12(opts: Tower12Opts): {
167
167
  // Fp
168
168
  const Fp = mod.Field(ORDER);
169
169
  const FpNONRESIDUE = Fp.create(opts.NONRESIDUE || BigInt(-1));
170
- const FpLegendre = mod.FpLegendre(ORDER);
171
170
  const Fpdiv2 = Fp.div(Fp.ONE, _2n); // 1/2
172
171
 
173
172
  // Fp2
@@ -265,14 +264,14 @@ export function tower12(opts: Tower12Opts): {
265
264
  const { c0, c1 } = num;
266
265
  if (Fp.is0(c1)) {
267
266
  // if c0 is quadratic residue
268
- if (Fp.eql(FpLegendre(Fp, c0), Fp.ONE)) return Fp2.create({ c0: Fp.sqrt(c0), c1: Fp.ZERO });
267
+ if (mod.FpLegendre(Fp, c0) === 1) return Fp2.create({ c0: Fp.sqrt(c0), c1: Fp.ZERO });
269
268
  else return Fp2.create({ c0: Fp.ZERO, c1: Fp.sqrt(Fp.div(c0, FpNONRESIDUE)) });
270
269
  }
271
270
  const a = Fp.sqrt(Fp.sub(Fp.sqr(c0), Fp.mul(Fp.sqr(c1), FpNONRESIDUE)));
272
271
  let d = Fp.mul(Fp.add(a, c0), Fpdiv2);
273
- const legendre = FpLegendre(Fp, d);
272
+ const legendre = mod.FpLegendre(Fp, d);
274
273
  // -1, Quadratic non residue
275
- if (!Fp.is0(legendre) && !Fp.eql(legendre, Fp.ONE)) d = Fp.sub(d, a);
274
+ if (legendre === -1) d = Fp.sub(d, a);
276
275
  const a0 = Fp.sqrt(d);
277
276
  const candidateSqrt = Fp2.create({ c0: a0, c1: Fp.div(Fp.mul(c1, Fpdiv2), a0) });
278
277
  if (!Fp2.eql(Fp2.sqr(candidateSqrt), num)) throw new Error('Cannot find square root');