@noble/curves 1.6.0 → 1.8.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 (194) hide show
  1. package/README.md +78 -30
  2. package/_shortw_utils.d.ts +8 -54
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +6 -2
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +15 -11
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +26 -10
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +42 -24
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +203 -53
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +14 -23
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +25 -10
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/hash-to-curve.d.ts +28 -8
  19. package/abstract/hash-to-curve.d.ts.map +1 -1
  20. package/abstract/hash-to-curve.js +18 -14
  21. package/abstract/hash-to-curve.js.map +1 -1
  22. package/abstract/modular.d.ts +30 -5
  23. package/abstract/modular.d.ts.map +1 -1
  24. package/abstract/modular.js +68 -37
  25. package/abstract/modular.js.map +1 -1
  26. package/abstract/montgomery.d.ts.map +1 -1
  27. package/abstract/montgomery.js +11 -4
  28. package/abstract/montgomery.js.map +1 -1
  29. package/abstract/poseidon.d.ts +9 -0
  30. package/abstract/poseidon.d.ts.map +1 -1
  31. package/abstract/poseidon.js +31 -23
  32. package/abstract/poseidon.js.map +1 -1
  33. package/abstract/tower.d.ts +13 -1
  34. package/abstract/tower.d.ts.map +1 -1
  35. package/abstract/tower.js +20 -17
  36. package/abstract/tower.js.map +1 -1
  37. package/abstract/utils.d.ts +10 -5
  38. package/abstract/utils.d.ts.map +1 -1
  39. package/abstract/utils.js +26 -24
  40. package/abstract/utils.js.map +1 -1
  41. package/abstract/weierstrass.d.ts +47 -80
  42. package/abstract/weierstrass.d.ts.map +1 -1
  43. package/abstract/weierstrass.js +88 -43
  44. package/abstract/weierstrass.js.map +1 -1
  45. package/bls12-381.d.ts +11 -0
  46. package/bls12-381.d.ts.map +1 -1
  47. package/bls12-381.js +80 -67
  48. package/bls12-381.js.map +1 -1
  49. package/bn254.d.ts +5 -3
  50. package/bn254.d.ts.map +1 -1
  51. package/bn254.js +33 -27
  52. package/bn254.js.map +1 -1
  53. package/ed25519.d.ts +24 -4
  54. package/ed25519.d.ts.map +1 -1
  55. package/ed25519.js +30 -6
  56. package/ed25519.js.map +1 -1
  57. package/ed448.d.ts +23 -8
  58. package/ed448.d.ts.map +1 -1
  59. package/ed448.js +31 -9
  60. package/ed448.js.map +1 -1
  61. package/esm/_shortw_utils.d.ts +8 -54
  62. package/esm/_shortw_utils.d.ts.map +1 -1
  63. package/esm/_shortw_utils.js +6 -2
  64. package/esm/_shortw_utils.js.map +1 -1
  65. package/esm/abstract/bls.d.ts +15 -11
  66. package/esm/abstract/bls.d.ts.map +1 -1
  67. package/esm/abstract/bls.js +26 -10
  68. package/esm/abstract/bls.js.map +1 -1
  69. package/esm/abstract/curve.d.ts +42 -24
  70. package/esm/abstract/curve.d.ts.map +1 -1
  71. package/esm/abstract/curve.js +202 -53
  72. package/esm/abstract/curve.js.map +1 -1
  73. package/esm/abstract/edwards.d.ts +14 -23
  74. package/esm/abstract/edwards.d.ts.map +1 -1
  75. package/esm/abstract/edwards.js +25 -10
  76. package/esm/abstract/edwards.js.map +1 -1
  77. package/esm/abstract/hash-to-curve.d.ts +28 -8
  78. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  79. package/esm/abstract/hash-to-curve.js +18 -14
  80. package/esm/abstract/hash-to-curve.js.map +1 -1
  81. package/esm/abstract/modular.d.ts +30 -5
  82. package/esm/abstract/modular.d.ts.map +1 -1
  83. package/esm/abstract/modular.js +68 -37
  84. package/esm/abstract/modular.js.map +1 -1
  85. package/esm/abstract/montgomery.d.ts.map +1 -1
  86. package/esm/abstract/montgomery.js +11 -4
  87. package/esm/abstract/montgomery.js.map +1 -1
  88. package/esm/abstract/poseidon.d.ts +9 -0
  89. package/esm/abstract/poseidon.d.ts.map +1 -1
  90. package/esm/abstract/poseidon.js +31 -23
  91. package/esm/abstract/poseidon.js.map +1 -1
  92. package/esm/abstract/tower.d.ts +13 -1
  93. package/esm/abstract/tower.d.ts.map +1 -1
  94. package/esm/abstract/tower.js +20 -17
  95. package/esm/abstract/tower.js.map +1 -1
  96. package/esm/abstract/utils.d.ts +10 -5
  97. package/esm/abstract/utils.d.ts.map +1 -1
  98. package/esm/abstract/utils.js +25 -23
  99. package/esm/abstract/utils.js.map +1 -1
  100. package/esm/abstract/weierstrass.d.ts +47 -80
  101. package/esm/abstract/weierstrass.d.ts.map +1 -1
  102. package/esm/abstract/weierstrass.js +86 -42
  103. package/esm/abstract/weierstrass.js.map +1 -1
  104. package/esm/bls12-381.d.ts +11 -0
  105. package/esm/bls12-381.d.ts.map +1 -1
  106. package/esm/bls12-381.js +80 -67
  107. package/esm/bls12-381.js.map +1 -1
  108. package/esm/bn254.d.ts +5 -3
  109. package/esm/bn254.d.ts.map +1 -1
  110. package/esm/bn254.js +31 -26
  111. package/esm/bn254.js.map +1 -1
  112. package/esm/ed25519.d.ts +24 -4
  113. package/esm/ed25519.d.ts.map +1 -1
  114. package/esm/ed25519.js +31 -7
  115. package/esm/ed25519.js.map +1 -1
  116. package/esm/ed448.d.ts +23 -8
  117. package/esm/ed448.d.ts.map +1 -1
  118. package/esm/ed448.js +32 -10
  119. package/esm/ed448.js.map +1 -1
  120. package/esm/index.js +4 -0
  121. package/esm/index.js.map +1 -1
  122. package/esm/jubjub.d.ts +4 -8
  123. package/esm/jubjub.d.ts.map +1 -1
  124. package/esm/jubjub.js +6 -5
  125. package/esm/jubjub.js.map +1 -1
  126. package/esm/p256.d.ts +10 -104
  127. package/esm/p256.d.ts.map +1 -1
  128. package/esm/p256.js +15 -8
  129. package/esm/p256.js.map +1 -1
  130. package/esm/p384.d.ts +10 -104
  131. package/esm/p384.d.ts.map +1 -1
  132. package/esm/p384.js +15 -8
  133. package/esm/p384.js.map +1 -1
  134. package/esm/p521.d.ts +11 -104
  135. package/esm/p521.d.ts.map +1 -1
  136. package/esm/p521.js +18 -10
  137. package/esm/p521.js.map +1 -1
  138. package/esm/pasta.d.ts +5 -2
  139. package/esm/pasta.d.ts.map +1 -1
  140. package/esm/pasta.js +6 -2
  141. package/esm/pasta.js.map +1 -1
  142. package/esm/secp256k1.d.ts +29 -57
  143. package/esm/secp256k1.d.ts.map +1 -1
  144. package/esm/secp256k1.js +41 -15
  145. package/esm/secp256k1.js.map +1 -1
  146. package/index.js +4 -0
  147. package/index.js.map +1 -1
  148. package/jubjub.d.ts +4 -8
  149. package/jubjub.d.ts.map +1 -1
  150. package/jubjub.js +6 -5
  151. package/jubjub.js.map +1 -1
  152. package/p256.d.ts +10 -104
  153. package/p256.d.ts.map +1 -1
  154. package/p256.js +15 -8
  155. package/p256.js.map +1 -1
  156. package/p384.d.ts +10 -104
  157. package/p384.d.ts.map +1 -1
  158. package/p384.js +15 -8
  159. package/p384.js.map +1 -1
  160. package/p521.d.ts +11 -104
  161. package/p521.d.ts.map +1 -1
  162. package/p521.js +18 -10
  163. package/p521.js.map +1 -1
  164. package/package.json +11 -8
  165. package/pasta.d.ts +5 -2
  166. package/pasta.d.ts.map +1 -1
  167. package/pasta.js +6 -2
  168. package/pasta.js.map +1 -1
  169. package/secp256k1.d.ts +29 -57
  170. package/secp256k1.d.ts.map +1 -1
  171. package/secp256k1.js +41 -15
  172. package/secp256k1.js.map +1 -1
  173. package/src/_shortw_utils.ts +18 -8
  174. package/src/abstract/bls.ts +42 -30
  175. package/src/abstract/curve.ts +237 -55
  176. package/src/abstract/edwards.ts +36 -15
  177. package/src/abstract/hash-to-curve.ts +43 -21
  178. package/src/abstract/modular.ts +84 -46
  179. package/src/abstract/montgomery.ts +12 -4
  180. package/src/abstract/poseidon.ts +48 -30
  181. package/src/abstract/tower.ts +66 -20
  182. package/src/abstract/utils.ts +44 -43
  183. package/src/abstract/weierstrass.ts +125 -70
  184. package/src/bls12-381.ts +80 -68
  185. package/src/bn254.ts +47 -30
  186. package/src/ed25519.ts +50 -20
  187. package/src/ed448.ts +49 -22
  188. package/src/index.ts +4 -0
  189. package/src/jubjub.ts +10 -10
  190. package/src/p256.ts +21 -15
  191. package/src/p384.ts +21 -15
  192. package/src/p521.ts +24 -17
  193. package/src/pasta.ts +15 -7
  194. package/src/secp256k1.ts +63 -21
@@ -1,9 +1,16 @@
1
+ /**
2
+ * hash-to-curve from [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
3
+ * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
4
+ * @module
5
+ */
1
6
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
7
  import type { AffinePoint, Group, GroupConstructor } from './curve.js';
3
8
  import { IField, mod } from './modular.js';
4
9
  import type { CHash } from './utils.js';
5
10
  import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } from './utils.js';
6
11
 
12
+ export type UnicodeOrBytes = string | Uint8Array;
13
+
7
14
  /**
8
15
  * * `DST` is a domain separation tag, defined in section 2.2.5
9
16
  * * `p` characteristic of F, where F is a finite field of characteristic p and order q = p^m
@@ -12,7 +19,6 @@ import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } fro
12
19
  * * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
13
20
  * * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
14
21
  */
15
- type UnicodeOrBytes = string | Uint8Array;
16
22
  export type Opts = {
17
23
  DST: UnicodeOrBytes;
18
24
  p: bigint;
@@ -29,9 +35,7 @@ const os2ip = bytesToNumberBE;
29
35
  function i2osp(value: number, length: number): Uint8Array {
30
36
  anum(value);
31
37
  anum(length);
32
- if (value < 0 || value >= 1 << (8 * length)) {
33
- throw new Error(`bad I2OSP call: value=${value} length=${length}`);
34
- }
38
+ if (value < 0 || value >= 1 << (8 * length)) throw new Error('invalid I2OSP input: ' + value);
35
39
  const res = Array.from({ length }).fill(0) as number[];
36
40
  for (let i = length - 1; i >= 0; i--) {
37
41
  res[i] = value & 0xff;
@@ -52,8 +56,10 @@ function anum(item: unknown): void {
52
56
  if (!Number.isSafeInteger(item)) throw new Error('number expected');
53
57
  }
54
58
 
55
- // Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits
56
- // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1
59
+ /**
60
+ * Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
61
+ * [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
62
+ */
57
63
  export function expand_message_xmd(
58
64
  msg: Uint8Array,
59
65
  DST: Uint8Array,
@@ -82,11 +88,13 @@ export function expand_message_xmd(
82
88
  return pseudo_random_bytes.slice(0, lenInBytes);
83
89
  }
84
90
 
85
- // Produces a uniformly random byte string using an extendable-output function (XOF) H.
86
- // 1. The collision resistance of H MUST be at least k bits.
87
- // 2. H MUST be an XOF that has been proved indifferentiable from
88
- // a random oracle under a reasonable cryptographic assumption.
89
- // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2
91
+ /**
92
+ * Produces a uniformly random byte string using an extendable-output function (XOF) H.
93
+ * 1. The collision resistance of H MUST be at least k bits.
94
+ * 2. H MUST be an XOF that has been proved indifferentiable from
95
+ * a random oracle under a reasonable cryptographic assumption.
96
+ * [RFC 9380 5.3.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2).
97
+ */
90
98
  export function expand_message_xof(
91
99
  msg: Uint8Array,
92
100
  DST: Uint8Array,
@@ -117,8 +125,8 @@ export function expand_message_xof(
117
125
  }
118
126
 
119
127
  /**
120
- * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F
121
- * https://www.rfc-editor.org/rfc/rfc9380#section-5.2
128
+ * Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
129
+ * [RFC 9380 5.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.2).
122
130
  * @param msg a byte string containing the message to hash
123
131
  * @param count the number of elements of F to output
124
132
  * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
@@ -163,7 +171,14 @@ export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bi
163
171
  return u;
164
172
  }
165
173
 
166
- export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]) {
174
+ export type XY<T> = (
175
+ x: T,
176
+ y: T
177
+ ) => {
178
+ x: T;
179
+ y: T;
180
+ };
181
+ export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[], T[]]): XY<T> {
167
182
  // Make same order as in spec
168
183
  const COEFF = map.map((i) => Array.from(i).reverse());
169
184
  return (x: T, y: T) => {
@@ -172,10 +187,11 @@ export function isogenyMap<T, F extends IField<T>>(field: F, map: [T[], T[], T[]
172
187
  );
173
188
  x = field.div(xNum, xDen); // xNum / xDen
174
189
  y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
175
- return { x, y };
190
+ return { x: x, y: y };
176
191
  };
177
192
  }
178
193
 
194
+ /** Point interface, which curves must implement to work correctly with the module. */
179
195
  export interface H2CPoint<T> extends Group<H2CPoint<T>> {
180
196
  add(rhs: H2CPoint<T>): H2CPoint<T>;
181
197
  toAffine(iz?: bigint): AffinePoint<T>;
@@ -192,17 +208,24 @@ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
192
208
  // Separated from initialization opts, so users won't accidentally change per-curve parameters
193
209
  // (changing DST is ok!)
194
210
  export type htfBasicOpts = { DST: UnicodeOrBytes };
211
+ export type HTFMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
212
+ export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
195
213
 
214
+ /** Creates hash-to-curve methods from EC Point and mapToCurve function. */
196
215
  export function createHasher<T>(
197
216
  Point: H2CPointConstructor<T>,
198
217
  mapToCurve: MapToCurve<T>,
199
218
  def: Opts & { encodeDST?: UnicodeOrBytes }
200
- ) {
219
+ ): {
220
+ hashToCurve: HTFMethod<T>;
221
+ encodeToCurve: HTFMethod<T>;
222
+ mapToCurve: MapMethod<T>;
223
+ } {
201
224
  if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
202
225
  return {
203
226
  // Encodes byte string to elliptic curve.
204
227
  // hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
205
- hashToCurve(msg: Uint8Array, options?: htfBasicOpts) {
228
+ hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
206
229
  const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options } as Opts);
207
230
  const u0 = Point.fromAffine(mapToCurve(u[0]));
208
231
  const u1 = Point.fromAffine(mapToCurve(u[1]));
@@ -213,18 +236,17 @@ export function createHasher<T>(
213
236
 
214
237
  // Encodes byte string to elliptic curve.
215
238
  // encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
216
- encodeToCurve(msg: Uint8Array, options?: htfBasicOpts) {
239
+ encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
217
240
  const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options } as Opts);
218
241
  const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
219
242
  P.assertValidity();
220
243
  return P;
221
244
  },
222
245
  // Same as encodeToCurve, but without hash
223
- mapToCurve(scalars: bigint[]) {
246
+ mapToCurve(scalars: bigint[]): H2CPoint<T> {
224
247
  if (!Array.isArray(scalars)) throw new Error('mapToCurve: expected array of bigints');
225
248
  for (const i of scalars)
226
- if (typeof i !== 'bigint')
227
- throw new Error(`mapToCurve: expected array of bigints, got ${i} in array`);
249
+ if (typeof i !== 'bigint') throw new Error('mapToCurve: expected array of bigints');
228
250
  const P = Point.fromAffine(mapToCurve(scalars)).clearCofactor();
229
251
  P.assertValidity();
230
252
  return P;
@@ -1,5 +1,10 @@
1
+ /**
2
+ * Utils for modular division and finite fields.
3
+ * A finite field over 11 is integer number operations `mod 11`.
4
+ * There is no division: it is replaced by modular multiplicative inverse.
5
+ * @module
6
+ */
1
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
- // Utilities for modular arithmetics and finite fields
3
8
  import {
4
9
  bitMask,
5
10
  bytesToNumberBE,
@@ -9,12 +14,13 @@ import {
9
14
  numberToBytesLE,
10
15
  validateObject,
11
16
  } from './utils.js';
17
+
12
18
  // prettier-ignore
13
- const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3);
19
+ const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
14
20
  // prettier-ignore
15
- const _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8);
21
+ const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _8n = /* @__PURE__ */ BigInt(8);
16
22
  // prettier-ignore
17
- const _9n = BigInt(9), _16n = BigInt(16);
23
+ const _9n =/* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
18
24
 
19
25
  // Calculates a modulo b
20
26
  export function mod(a: bigint, b: bigint): bigint {
@@ -24,12 +30,13 @@ export function mod(a: bigint, b: bigint): bigint {
24
30
  /**
25
31
  * Efficiently raise num to power and do modular division.
26
32
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
33
+ * @todo use field version && remove
27
34
  * @example
28
35
  * pow(2n, 6n, 11n) // 64n % 11n == 9n
29
36
  */
30
- // TODO: use field version && remove
31
37
  export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
32
- if (modulo <= _0n || power < _0n) throw new Error('Expected power/modulo > 0');
38
+ if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
39
+ if (modulo <= _0n) throw new Error('invalid modulus');
33
40
  if (modulo === _1n) return _0n;
34
41
  let res = _1n;
35
42
  while (power > _0n) {
@@ -40,7 +47,7 @@ export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
40
47
  return res;
41
48
  }
42
49
 
43
- // Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4)
50
+ /** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
44
51
  export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
45
52
  let res = x;
46
53
  while (power-- > _0n) {
@@ -50,12 +57,13 @@ export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
50
57
  return res;
51
58
  }
52
59
 
53
- // Inverses number over modulo
60
+ /**
61
+ * Inverses number over modulo.
62
+ * Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
63
+ */
54
64
  export function invert(number: bigint, modulo: bigint): bigint {
55
- if (number === _0n || modulo <= _0n) {
56
- throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`);
57
- }
58
- // Euclidean GCD https://brilliant.org/wiki/extended-euclidean-algorithm/
65
+ if (number === _0n) throw new Error('invert: expected non-zero number');
66
+ if (modulo <= _0n) throw new Error('invert: expected positive modulus, got ' + modulo);
59
67
  // Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
60
68
  let a = mod(number, modulo);
61
69
  let b = modulo;
@@ -83,7 +91,7 @@ export function invert(number: bigint, modulo: bigint): bigint {
83
91
  * @param P field order
84
92
  * @returns function that takes field Fp (created from P) and number n
85
93
  */
86
- export function tonelliShanks(P: bigint) {
94
+ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
87
95
  // Legendre constant: used to calculate Legendre symbol (a | p),
88
96
  // which denotes the value of a^((p-1)/2) (mod p).
89
97
  // (a | p) ≡ 1 if a is a square (mod p)
@@ -97,7 +105,10 @@ export function tonelliShanks(P: bigint) {
97
105
  for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++);
98
106
 
99
107
  // Step 2: Select a non-square z such that (z | p) ≡ -1 and set c ≡ zq
100
- for (Z = _2n; Z < P && pow(Z, legendreC, P) !== P - _1n; Z++);
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');
111
+ }
101
112
 
102
113
  // Fast-path
103
114
  if (S === 1) {
@@ -139,10 +150,18 @@ export function tonelliShanks(P: bigint) {
139
150
  };
140
151
  }
141
152
 
142
- export function FpSqrt(P: bigint) {
143
- // NOTE: different algorithms can give different roots, it is up to user to decide which one they want.
144
- // For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
145
-
153
+ /**
154
+ * Square root for a finite field. It will try to check if optimizations are applicable and fall back to 4:
155
+ *
156
+ * 1. P ≡ 3 (mod 4)
157
+ * 2. P ≡ 5 (mod 8)
158
+ * 3. P ≡ 9 (mod 16)
159
+ * 4. Tonelli-Shanks algorithm
160
+ *
161
+ * Different algorithms can give different roots, it is up to user to decide which one they want.
162
+ * For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
163
+ */
164
+ export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
146
165
  // P ≡ 3 (mod 4)
147
166
  // √n = n^((P+1)/4)
148
167
  if (P % _4n === _3n) {
@@ -200,11 +219,13 @@ export function FpSqrt(P: bigint) {
200
219
  }
201
220
 
202
221
  // Little-endian check for first LE bit (last BE bit);
203
- export const isNegativeLE = (num: bigint, modulo: bigint) => (mod(num, modulo) & _1n) === _1n;
222
+ export const isNegativeLE = (num: bigint, modulo: bigint): boolean =>
223
+ (mod(num, modulo) & _1n) === _1n;
204
224
 
205
- // Field is not always over prime: for example, Fp2 has ORDER(q)=p^m
225
+ /** Field is not always over prime: for example, Fp2 has ORDER(q)=p^m. */
206
226
  export interface IField<T> {
207
227
  ORDER: bigint;
228
+ isLE: boolean;
208
229
  BYTES: number;
209
230
  BITS: number;
210
231
  MASK: bigint;
@@ -250,7 +271,7 @@ const FIELD_FIELDS = [
250
271
  'eql', 'add', 'sub', 'mul', 'pow', 'div',
251
272
  'addN', 'subN', 'mulN', 'sqrN'
252
273
  ] as const;
253
- export function validateField<T>(field: IField<T>) {
274
+ export function validateField<T>(field: IField<T>): IField<T> {
254
275
  const initial = {
255
276
  ORDER: 'bigint',
256
277
  MASK: 'bigint',
@@ -273,7 +294,7 @@ export function validateField<T>(field: IField<T>) {
273
294
  export function FpPow<T>(f: IField<T>, num: T, power: bigint): T {
274
295
  // Should have same speed as pow for bigints
275
296
  // TODO: benchmark!
276
- if (power < _0n) throw new Error('Expected power > 0');
297
+ if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
277
298
  if (power === _0n) return f.ONE;
278
299
  if (power === _1n) return num;
279
300
  let p = f.ONE;
@@ -313,16 +334,19 @@ export function FpDiv<T>(f: IField<T>, lhs: T, rhs: T | bigint): T {
313
334
  return f.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.inv(rhs));
314
335
  }
315
336
 
316
- export function FpLegendre(order: bigint) {
317
- // (a | p) ≡ 1 if a is a square (mod p), quadratic residue
318
- // (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
319
- // (a | p) ≡ 0 if a 0 (mod p)
337
+ /**
338
+ * Legendre symbol.
339
+ * * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
340
+ * * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
341
+ * * (a | p) ≡ 0 if a ≡ 0 (mod p)
342
+ */
343
+ export function FpLegendre(order: bigint): <T>(f: IField<T>, x: T) => T {
320
344
  const legendreConst = (order - _1n) / _2n; // Integer arithmetic
321
345
  return <T>(f: IField<T>, x: T): T => f.pow(x, legendreConst);
322
346
  }
323
347
 
324
348
  // This function returns True whenever the value x is a square in the field F.
325
- export function FpIsSquare<T>(f: IField<T>) {
349
+ export function FpIsSquare<T>(f: IField<T>): (x: T) => boolean {
326
350
  const legendre = FpLegendre(f.ORDER);
327
351
  return (x: T): boolean => {
328
352
  const p = legendre(f, x);
@@ -331,7 +355,13 @@ export function FpIsSquare<T>(f: IField<T>) {
331
355
  }
332
356
 
333
357
  // CURVE.n lengths
334
- export function nLength(n: bigint, nBitLength?: number) {
358
+ export function nLength(
359
+ n: bigint,
360
+ nBitLength?: number
361
+ ): {
362
+ nBitLength: number;
363
+ nByteLength: number;
364
+ } {
335
365
  // Bit size, byte size of CURVE.n
336
366
  const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
337
367
  const nByteLength = Math.ceil(_nBitLength / 8);
@@ -340,15 +370,15 @@ export function nLength(n: bigint, nBitLength?: number) {
340
370
 
341
371
  type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
342
372
  /**
343
- * Initializes a finite field over prime. **Non-primes are not supported.**
344
- * Do not init in loop: slow. Very fragile: always run a benchmark on a change.
373
+ * Initializes a finite field over prime.
345
374
  * Major performance optimizations:
346
375
  * * a) denormalized operations like mulN instead of mul
347
376
  * * b) same object shape: never add or remove keys
348
377
  * * c) Object.freeze
349
- * NOTE: operations don't check 'isValid' for all elements for performance reasons,
378
+ * Fragile: always run a benchmark on a change.
379
+ * Security note: operations don't check 'isValid' for all elements for performance reasons,
350
380
  * it is caller responsibility to check this.
351
- * This is low-level code, please make sure you know what you doing.
381
+ * This is low-level code, please make sure you know what you're doing.
352
382
  * @param ORDER prime positive bigint
353
383
  * @param bitLen how many bits the field consumes
354
384
  * @param isLE (def: false) if encoding / decoding should be in little-endian
@@ -360,12 +390,13 @@ export function Field(
360
390
  isLE = false,
361
391
  redef: Partial<IField<bigint>> = {}
362
392
  ): Readonly<FpField> {
363
- if (ORDER <= _0n) throw new Error(`Expected Field ORDER > 0, got ${ORDER}`);
393
+ if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
364
394
  const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
365
- if (BYTES > 2048) throw new Error('Field lengths over 2048 bytes are not supported');
366
- const sqrtP = FpSqrt(ORDER);
395
+ if (BYTES > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes');
396
+ let sqrtP: ReturnType<typeof FpSqrt>; // cached sqrtP
367
397
  const f: Readonly<FpField> = Object.freeze({
368
398
  ORDER,
399
+ isLE,
369
400
  BITS,
370
401
  BYTES,
371
402
  MASK: bitMask(BITS),
@@ -374,7 +405,7 @@ export function Field(
374
405
  create: (num) => mod(num, ORDER),
375
406
  isValid: (num) => {
376
407
  if (typeof num !== 'bigint')
377
- throw new Error(`Invalid field element: expected bigint, got ${typeof num}`);
408
+ throw new Error('invalid field element: expected bigint, got ' + typeof num);
378
409
  return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
379
410
  },
380
411
  is0: (num) => num === _0n,
@@ -396,7 +427,12 @@ export function Field(
396
427
  mulN: (lhs, rhs) => lhs * rhs,
397
428
 
398
429
  inv: (num) => invert(num, ORDER),
399
- sqrt: redef.sqrt || ((n) => sqrtP(f, n)),
430
+ sqrt:
431
+ redef.sqrt ||
432
+ ((n) => {
433
+ if (!sqrtP) sqrtP = FpSqrt(ORDER);
434
+ return sqrtP(f, n);
435
+ }),
400
436
  invertBatch: (lst) => FpInvertBatch(f, lst),
401
437
  // TODO: do we really need constant cmov?
402
438
  // We don't have const-time bigints anyway, so probably will be not very useful
@@ -404,21 +440,21 @@ export function Field(
404
440
  toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
405
441
  fromBytes: (bytes) => {
406
442
  if (bytes.length !== BYTES)
407
- throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`);
443
+ throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
408
444
  return isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
409
445
  },
410
446
  } as FpField);
411
447
  return Object.freeze(f);
412
448
  }
413
449
 
414
- export function FpSqrtOdd<T>(Fp: IField<T>, elm: T) {
415
- if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
450
+ export function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T {
451
+ if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
416
452
  const root = Fp.sqrt(elm);
417
453
  return Fp.isOdd(root) ? root : Fp.neg(root);
418
454
  }
419
455
 
420
- export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
421
- if (!Fp.isOdd) throw new Error(`Field doesn't have isOdd`);
456
+ export function FpSqrtEven<T>(Fp: IField<T>, elm: T): T {
457
+ if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
422
458
  const root = Fp.sqrt(elm);
423
459
  return Fp.isOdd(root) ? Fp.neg(root) : root;
424
460
  }
@@ -427,7 +463,7 @@ export function FpSqrtEven<T>(Fp: IField<T>, elm: T) {
427
463
  * "Constant-time" private key generation utility.
428
464
  * Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
429
465
  * Which makes it slightly more biased, less secure.
430
- * @deprecated use mapKeyToField instead
466
+ * @deprecated use `mapKeyToField` instead
431
467
  */
432
468
  export function hashToPrivateScalar(
433
469
  hash: string | Uint8Array,
@@ -438,7 +474,9 @@ export function hashToPrivateScalar(
438
474
  const hashLen = hash.length;
439
475
  const minLen = nLength(groupOrder).nByteLength + 8;
440
476
  if (minLen < 24 || hashLen < minLen || hashLen > 1024)
441
- throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`);
477
+ throw new Error(
478
+ 'hashToPrivateScalar: expected ' + minLen + '-1024 bytes of input, got ' + hashLen
479
+ );
442
480
  const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
443
481
  return mod(num, groupOrder - _1n) + _1n;
444
482
  }
@@ -486,8 +524,8 @@ export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false
486
524
  const minLen = getMinHashLength(fieldOrder);
487
525
  // No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
488
526
  if (len < 16 || len < minLen || len > 1024)
489
- throw new Error(`expected ${minLen}-1024 bytes of input, got ${len}`);
490
- const num = isLE ? bytesToNumberBE(key) : bytesToNumberLE(key);
527
+ throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
528
+ const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
491
529
  // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
492
530
  const reduced = mod(num, fieldOrder - _1n) + _1n;
493
531
  return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Montgomery curve methods. It's not really whole montgomery curve,
3
+ * just bunch of very specific methods for X25519 / X448 from
4
+ * [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748)
5
+ * @module
6
+ */
1
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
2
8
  import { mod, pow } from './modular.js';
3
9
  import {
@@ -24,6 +30,7 @@ export type CurveType = {
24
30
  Gu: bigint;
25
31
  randomBytes?: (bytesLength?: number) => Uint8Array;
26
32
  };
33
+
27
34
  export type CurveFn = {
28
35
  scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
29
36
  scalarMultBase: (scalar: Hex) => Uint8Array;
@@ -52,7 +59,6 @@ function validateOpts(curve: CurveType) {
52
59
  return Object.freeze({ ...curve } as const);
53
60
  }
54
61
 
55
- // NOTE: not really montgomery curve, just bunch of very specific methods for X25519/X448 (RFC 7748, https://www.rfc-editor.org/rfc/rfc7748)
56
62
  // Uses only one coordinate instead of two
57
63
  export function montgomery(curveDef: CurveType): CurveFn {
58
64
  const CURVE = validateOpts(curveDef);
@@ -158,8 +164,10 @@ export function montgomery(curveDef: CurveType): CurveFn {
158
164
  function decodeScalar(n: Hex): bigint {
159
165
  const bytes = ensureBytes('scalar', n);
160
166
  const len = bytes.length;
161
- if (len !== montgomeryBytes && len !== fieldLen)
162
- throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${len}`);
167
+ if (len !== montgomeryBytes && len !== fieldLen) {
168
+ let valid = '' + montgomeryBytes + ' or ' + fieldLen;
169
+ throw new Error('invalid scalar, expected ' + valid + ' bytes, got ' + len);
170
+ }
163
171
  return bytesToNumberLE(adjustScalarBytes(bytes));
164
172
  }
165
173
  function scalarMult(scalar: Hex, u: Hex): Uint8Array {
@@ -168,7 +176,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
168
176
  const pu = montgomeryLadder(pointU, _scalar);
169
177
  // The result was not contributory
170
178
  // https://cr.yp.to/ecdh.html#validate
171
- if (pu === _0n) throw new Error('Invalid private or public key received');
179
+ if (pu === _0n) throw new Error('invalid private or public key received');
172
180
  return encodeUCoordinate(pu);
173
181
  }
174
182
  // Computes public key from private. By doing scalar multiplication of base point.
@@ -1,8 +1,14 @@
1
+ /**
2
+ * Implements [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash.
3
+ *
4
+ * There are many poseidon variants with different constants.
5
+ * We don't provide them: you should construct them manually.
6
+ * Check out [micro-starknet](https://github.com/paulmillr/micro-starknet) package for a proper example.
7
+ * @module
8
+ */
1
9
  /*! 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
10
  import { FpPow, IField, validateField } from './modular.js';
4
- // We don't provide any constants, since different implementations use different constants.
5
- // For reference constants see './test/poseidon.test.js'.
11
+
6
12
  export type PoseidonOpts = {
7
13
  Fp: IField<bigint>;
8
14
  t: number;
@@ -14,47 +20,55 @@ export type PoseidonOpts = {
14
20
  roundConstants: bigint[][];
15
21
  };
16
22
 
17
- export function validateOpts(opts: PoseidonOpts) {
23
+ export function validateOpts(opts: PoseidonOpts): Readonly<{
24
+ rounds: number;
25
+ sboxFn: (n: bigint) => bigint;
26
+ roundConstants: bigint[][];
27
+ mds: bigint[][];
28
+ Fp: IField<bigint>;
29
+ t: number;
30
+ roundsFull: number;
31
+ roundsPartial: number;
32
+ sboxPower?: number;
33
+ reversePartialPowIdx?: boolean; // Hack for stark
34
+ }> {
18
35
  const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
19
36
  const { roundsFull, roundsPartial, sboxPower, t } = opts;
20
37
 
21
38
  validateField(Fp);
22
39
  for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
23
40
  if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
24
- throw new Error(`Poseidon: invalid param ${i}=${opts[i]} (${typeof opts[i]})`);
41
+ throw new Error('invalid number ' + i);
25
42
  }
26
43
 
27
44
  // MDS is TxT matrix
28
- if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: wrong MDS matrix');
45
+ if (!Array.isArray(mds) || mds.length !== t) throw new Error('Poseidon: invalid MDS matrix');
29
46
  const _mds = mds.map((mdsRow) => {
30
47
  if (!Array.isArray(mdsRow) || mdsRow.length !== t)
31
- throw new Error(`Poseidon MDS matrix row: ${mdsRow}`);
48
+ throw new Error('invalid MDS matrix row: ' + mdsRow);
32
49
  return mdsRow.map((i) => {
33
- if (typeof i !== 'bigint') throw new Error(`Poseidon MDS matrix value=${i}`);
50
+ if (typeof i !== 'bigint') throw new Error('invalid MDS matrix bigint: ' + i);
34
51
  return Fp.create(i);
35
52
  });
36
53
  });
37
54
 
38
55
  if (rev !== undefined && typeof rev !== 'boolean')
39
- throw new Error(`Poseidon: invalid param reversePartialPowIdx=${rev}`);
56
+ throw new Error('invalid param reversePartialPowIdx=' + rev);
40
57
 
41
- if (roundsFull % 2 !== 0) throw new Error(`Poseidon roundsFull is not even: ${roundsFull}`);
58
+ if (roundsFull & 1) throw new Error('roundsFull is not even' + roundsFull);
42
59
  const rounds = roundsFull + roundsPartial;
43
60
 
44
61
  if (!Array.isArray(rc) || rc.length !== rounds)
45
- throw new Error('Poseidon: wrong round constants');
62
+ throw new Error('Poseidon: invalid round constants');
46
63
  const roundConstants = rc.map((rc) => {
47
- if (!Array.isArray(rc) || rc.length !== t)
48
- throw new Error(`Poseidon wrong round constants: ${rc}`);
64
+ if (!Array.isArray(rc) || rc.length !== t) throw new Error('invalid round constants');
49
65
  return rc.map((i) => {
50
- if (typeof i !== 'bigint' || !Fp.isValid(i))
51
- throw new Error(`Poseidon wrong round constant=${i}`);
66
+ if (typeof i !== 'bigint' || !Fp.isValid(i)) throw new Error('invalid round constant');
52
67
  return Fp.create(i);
53
68
  });
54
69
  });
55
70
 
56
- if (!sboxPower || ![3, 5, 7].includes(sboxPower))
57
- throw new Error(`Poseidon wrong sboxPower=${sboxPower}`);
71
+ if (!sboxPower || ![3, 5, 7].includes(sboxPower)) throw new Error('invalid sboxPower');
58
72
  const _sboxPower = BigInt(sboxPower);
59
73
  let sboxFn = (n: bigint) => FpPow(Fp, n, _sboxPower);
60
74
  // Unwrapped sbox power for common cases (195->142μs)
@@ -64,9 +78,9 @@ export function validateOpts(opts: PoseidonOpts) {
64
78
  return Object.freeze({ ...opts, rounds, sboxFn, roundConstants, mds: _mds });
65
79
  }
66
80
 
67
- export function splitConstants(rc: bigint[], t: number) {
68
- if (typeof t !== 'number') throw new Error('poseidonSplitConstants: wrong t');
69
- if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants: wrong rc');
81
+ export function splitConstants(rc: bigint[], t: number): bigint[][] {
82
+ if (typeof t !== 'number') throw new Error('poseidonSplitConstants: invalid t');
83
+ if (!Array.isArray(rc) || rc.length % t) throw new Error('poseidonSplitConstants: invalid rc');
70
84
  const res = [];
71
85
  let tmp = [];
72
86
  for (let i = 0; i < rc.length; i++) {
@@ -79,9 +93,14 @@ export function splitConstants(rc: bigint[], t: number) {
79
93
  return res;
80
94
  }
81
95
 
82
- export function poseidon(opts: PoseidonOpts) {
96
+ /** Poseidon NTT-friendly hash. */
97
+ export function poseidon(opts: PoseidonOpts): {
98
+ (values: bigint[]): bigint[];
99
+ // For verification in tests
100
+ roundConstants: bigint[][];
101
+ } {
83
102
  const _opts = validateOpts(opts);
84
- const { Fp, mds, roundConstants, rounds, roundsPartial, sboxFn, t } = _opts;
103
+ const { Fp, mds, roundConstants, rounds: totalRounds, roundsPartial, sboxFn, t } = _opts;
85
104
  const halfRoundsFull = _opts.roundsFull / 2;
86
105
  const partialIdx = _opts.reversePartialPowIdx ? t - 1 : 0;
87
106
  const poseidonRound = (values: bigint[], isFull: boolean, idx: number) => {
@@ -95,21 +114,20 @@ export function poseidon(opts: PoseidonOpts) {
95
114
  };
96
115
  const poseidonHash = function poseidonHash(values: bigint[]) {
97
116
  if (!Array.isArray(values) || values.length !== t)
98
- throw new Error(`Poseidon: wrong values (expected array of bigints with length ${t})`);
117
+ throw new Error('invalid values, expected array of bigints with length ' + t);
99
118
  values = values.map((i) => {
100
- if (typeof i !== 'bigint') throw new Error(`Poseidon: wrong value=${i} (${typeof i})`);
119
+ if (typeof i !== 'bigint') throw new Error('invalid bigint=' + i);
101
120
  return Fp.create(i);
102
121
  });
103
- let round = 0;
122
+ let lastRound = 0;
104
123
  // Apply r_f/2 full rounds.
105
- for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
124
+ for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, lastRound++);
106
125
  // Apply r_p partial rounds.
107
- for (let i = 0; i < roundsPartial; i++) values = poseidonRound(values, false, round++);
126
+ for (let i = 0; i < roundsPartial; i++) values = poseidonRound(values, false, lastRound++);
108
127
  // Apply r_f/2 full rounds.
109
- for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, round++);
128
+ for (let i = 0; i < halfRoundsFull; i++) values = poseidonRound(values, true, lastRound++);
110
129
 
111
- if (round !== rounds)
112
- throw new Error(`Poseidon: wrong number of rounds: last round=${round}, total=${rounds}`);
130
+ if (lastRound !== totalRounds) throw new Error('invalid number of rounds');
113
131
  return values;
114
132
  };
115
133
  // For verification in tests