@noble/curves 2.0.1 → 2.2.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 (110) hide show
  1. package/README.md +214 -122
  2. package/abstract/bls.d.ts +299 -16
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js +82 -22
  5. package/abstract/bls.js.map +1 -1
  6. package/abstract/curve.d.ts +274 -27
  7. package/abstract/curve.d.ts.map +1 -1
  8. package/abstract/curve.js +177 -23
  9. package/abstract/curve.js.map +1 -1
  10. package/abstract/edwards.d.ts +166 -30
  11. package/abstract/edwards.d.ts.map +1 -1
  12. package/abstract/edwards.js +221 -86
  13. package/abstract/edwards.js.map +1 -1
  14. package/abstract/fft.d.ts +322 -10
  15. package/abstract/fft.d.ts.map +1 -1
  16. package/abstract/fft.js +154 -12
  17. package/abstract/fft.js.map +1 -1
  18. package/abstract/frost.d.ts +293 -0
  19. package/abstract/frost.d.ts.map +1 -0
  20. package/abstract/frost.js +704 -0
  21. package/abstract/frost.js.map +1 -0
  22. package/abstract/hash-to-curve.d.ts +173 -24
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +170 -31
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +429 -37
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +414 -119
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +83 -12
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +32 -7
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/oprf.d.ts +164 -91
  35. package/abstract/oprf.d.ts.map +1 -1
  36. package/abstract/oprf.js +88 -29
  37. package/abstract/oprf.js.map +1 -1
  38. package/abstract/poseidon.d.ts +138 -7
  39. package/abstract/poseidon.d.ts.map +1 -1
  40. package/abstract/poseidon.js +178 -15
  41. package/abstract/poseidon.js.map +1 -1
  42. package/abstract/tower.d.ts +122 -3
  43. package/abstract/tower.d.ts.map +1 -1
  44. package/abstract/tower.js +323 -139
  45. package/abstract/tower.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +339 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +395 -205
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +16 -2
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +199 -209
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +11 -2
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +93 -38
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +125 -14
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +202 -40
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +108 -14
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +194 -42
  65. package/ed448.js.map +1 -1
  66. package/index.js +7 -1
  67. package/index.js.map +1 -1
  68. package/misc.d.ts +106 -7
  69. package/misc.d.ts.map +1 -1
  70. package/misc.js +141 -32
  71. package/misc.js.map +1 -1
  72. package/nist.d.ts +112 -11
  73. package/nist.d.ts.map +1 -1
  74. package/nist.js +139 -17
  75. package/nist.js.map +1 -1
  76. package/package.json +11 -6
  77. package/secp256k1.d.ts +92 -15
  78. package/secp256k1.d.ts.map +1 -1
  79. package/secp256k1.js +211 -28
  80. package/secp256k1.js.map +1 -1
  81. package/src/abstract/bls.ts +350 -67
  82. package/src/abstract/curve.ts +327 -44
  83. package/src/abstract/edwards.ts +367 -143
  84. package/src/abstract/fft.ts +369 -36
  85. package/src/abstract/frost.ts +1092 -0
  86. package/src/abstract/hash-to-curve.ts +255 -56
  87. package/src/abstract/modular.ts +591 -144
  88. package/src/abstract/montgomery.ts +114 -30
  89. package/src/abstract/oprf.ts +383 -194
  90. package/src/abstract/poseidon.ts +235 -35
  91. package/src/abstract/tower.ts +428 -159
  92. package/src/abstract/weierstrass.ts +710 -312
  93. package/src/bls12-381.ts +239 -236
  94. package/src/bn254.ts +107 -46
  95. package/src/ed25519.ts +227 -55
  96. package/src/ed448.ts +227 -57
  97. package/src/index.ts +7 -1
  98. package/src/misc.ts +154 -35
  99. package/src/nist.ts +143 -20
  100. package/src/secp256k1.ts +284 -41
  101. package/src/utils.ts +583 -81
  102. package/src/webcrypto.ts +302 -73
  103. package/utils.d.ts +457 -24
  104. package/utils.d.ts.map +1 -1
  105. package/utils.js +410 -53
  106. package/utils.js.map +1 -1
  107. package/webcrypto.d.ts +167 -25
  108. package/webcrypto.d.ts.map +1 -1
  109. package/webcrypto.js +165 -58
  110. package/webcrypto.js.map +1 -1
@@ -5,7 +5,7 @@
5
5
  * @module
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
- import { abytes, anumber, bytesToNumberBE, bytesToNumberLE, numberToBytesBE, numberToBytesLE, validateObject, } from "../utils.js";
8
+ import { abool, abytes, anumber, asafenumber, bitLen, bytesToNumberBE, bytesToNumberLE, numberToBytesBE, numberToBytesLE, validateObject, } from "../utils.js";
9
9
  // Numbers aren't used in x25519 / x448 builds
10
10
  // prettier-ignore
11
11
  const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2);
@@ -14,22 +14,63 @@ const _3n = /* @__PURE__ */ BigInt(3), _4n = /* @__PURE__ */ BigInt(4), _5n = /*
14
14
  // prettier-ignore
15
15
  const _7n = /* @__PURE__ */ BigInt(7), _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9);
16
16
  const _16n = /* @__PURE__ */ BigInt(16);
17
- // Calculates a modulo b
17
+ /**
18
+ * @param a - Dividend value.
19
+ * @param b - Positive modulus.
20
+ * @returns Reduced value in `[0, b)` only when `b` is positive.
21
+ * @throws If the modulus is not positive. {@link Error}
22
+ * @example
23
+ * Normalize a bigint into one field residue.
24
+ *
25
+ * ```ts
26
+ * mod(-1n, 5n);
27
+ * ```
28
+ */
18
29
  export function mod(a, b) {
30
+ if (b <= _0n)
31
+ throw new Error('mod: expected positive modulus, got ' + b);
19
32
  const result = a % b;
20
33
  return result >= _0n ? result : b + result;
21
34
  }
22
35
  /**
23
- * Efficiently raise num to power and do modular division.
36
+ * Efficiently raise num to a power with modular reduction.
24
37
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
38
+ * Low-level helper: callers that need canonical residues must pass a valid `num` for the chosen
39
+ * modulus instead of relying on the `power===0/1` fast paths to normalize it.
40
+ * @param num - Base value.
41
+ * @param power - Exponent value.
42
+ * @param modulo - Reduction modulus.
43
+ * @returns Modular exponentiation result.
44
+ * @throws If the modulus or exponent is invalid. {@link Error}
25
45
  * @example
46
+ * Raise one bigint to a modular power.
47
+ *
48
+ * ```ts
26
49
  * pow(2n, 6n, 11n) // 64n % 11n == 9n
50
+ * ```
27
51
  */
28
52
  export function pow(num, power, modulo) {
29
53
  return FpPow(Field(modulo), num, power);
30
54
  }
31
- /** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
55
+ /**
56
+ * Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)`.
57
+ * Low-level helper: callers that need canonical residues must pass a valid `x` for the chosen
58
+ * modulus; the `power===0` fast path intentionally returns the input unchanged.
59
+ * @param x - Base value.
60
+ * @param power - Number of squarings.
61
+ * @param modulo - Reduction modulus.
62
+ * @returns Repeated-squaring result.
63
+ * @throws If the exponent is negative. {@link Error}
64
+ * @example
65
+ * Apply repeated squaring inside one field.
66
+ *
67
+ * ```ts
68
+ * pow2(3n, 2n, 11n);
69
+ * ```
70
+ */
32
71
  export function pow2(x, power, modulo) {
72
+ if (power < _0n)
73
+ throw new Error('pow2: expected non-negative exponent, got ' + power);
33
74
  let res = x;
34
75
  while (power-- > _0n) {
35
76
  res *= res;
@@ -39,7 +80,17 @@ export function pow2(x, power, modulo) {
39
80
  }
40
81
  /**
41
82
  * Inverses number over modulo.
42
- * Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
83
+ * Implemented using the {@link https://brilliant.org/wiki/extended-euclidean-algorithm/ | extended Euclidean algorithm}.
84
+ * @param number - Value to invert.
85
+ * @param modulo - Positive modulus.
86
+ * @returns Multiplicative inverse.
87
+ * @throws If the modulus is invalid or the inverse does not exist. {@link Error}
88
+ * @example
89
+ * Compute one modular inverse with the extended Euclidean algorithm.
90
+ *
91
+ * ```ts
92
+ * invert(3n, 11n);
93
+ * ```
43
94
  */
44
95
  export function invert(number, modulo) {
45
96
  if (number === _0n)
@@ -52,9 +103,8 @@ export function invert(number, modulo) {
52
103
  // prettier-ignore
53
104
  let x = _0n, y = _1n, u = _1n, v = _0n;
54
105
  while (a !== _0n) {
55
- // JIT applies optimization if those two lines follow each other
56
106
  const q = b / a;
57
- const r = b % a;
107
+ const r = b - a * q;
58
108
  const m = x - u * q;
59
109
  const n = y - v * q;
60
110
  // prettier-ignore
@@ -66,7 +116,8 @@ export function invert(number, modulo) {
66
116
  return mod(x, modulo);
67
117
  }
68
118
  function assertIsSquare(Fp, root, n) {
69
- if (!Fp.eql(Fp.sqr(root), n))
119
+ const F = Fp;
120
+ if (!F.eql(F.sqr(root), n))
70
121
  throw new Error('Cannot find square root');
71
122
  }
72
123
  // Not all roots are possible! Example which will throw:
@@ -74,19 +125,23 @@ function assertIsSquare(Fp, root, n) {
74
125
  // n = 72057594037927816n;
75
126
  // Fp = Field(BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'));
76
127
  function sqrt3mod4(Fp, n) {
77
- const p1div4 = (Fp.ORDER + _1n) / _4n;
78
- const root = Fp.pow(n, p1div4);
79
- assertIsSquare(Fp, root, n);
128
+ const F = Fp;
129
+ const p1div4 = (F.ORDER + _1n) / _4n;
130
+ const root = F.pow(n, p1div4);
131
+ assertIsSquare(F, root, n);
80
132
  return root;
81
133
  }
134
+ // Equivalent `q = 5 (mod 8)` square-root formula (Atkin-style), not the RFC Appendix I.2 CMOV
135
+ // pseudocode verbatim.
82
136
  function sqrt5mod8(Fp, n) {
83
- const p5div8 = (Fp.ORDER - _5n) / _8n;
84
- const n2 = Fp.mul(n, _2n);
85
- const v = Fp.pow(n2, p5div8);
86
- const nv = Fp.mul(n, v);
87
- const i = Fp.mul(Fp.mul(nv, _2n), v);
88
- const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
89
- assertIsSquare(Fp, root, n);
137
+ const F = Fp;
138
+ const p5div8 = (F.ORDER - _5n) / _8n;
139
+ const n2 = F.mul(n, _2n);
140
+ const v = F.pow(n2, p5div8);
141
+ const nv = F.mul(n, v);
142
+ const i = F.mul(F.mul(nv, _2n), v);
143
+ const root = F.mul(nv, F.sub(i, F.ONE));
144
+ assertIsSquare(F, root, n);
90
145
  return root;
91
146
  }
92
147
  // Based on RFC9380, Kong algorithm
@@ -98,27 +153,39 @@ function sqrt9mod16(P) {
98
153
  const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
99
154
  const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
100
155
  const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
101
- return (Fp, n) => {
102
- let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
103
- let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
104
- const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
105
- const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
106
- const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
107
- const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
108
- tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
109
- tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
110
- const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
111
- const root = Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
112
- assertIsSquare(Fp, root, n);
156
+ return ((Fp, n) => {
157
+ const F = Fp;
158
+ let tv1 = F.pow(n, c4); // 1. tv1 = x^c4
159
+ let tv2 = F.mul(tv1, c1); // 2. tv2 = c1 * tv1
160
+ const tv3 = F.mul(tv1, c2); // 3. tv3 = c2 * tv1
161
+ const tv4 = F.mul(tv1, c3); // 4. tv4 = c3 * tv1
162
+ const e1 = F.eql(F.sqr(tv2), n); // 5. e1 = (tv2^2) == x
163
+ const e2 = F.eql(F.sqr(tv3), n); // 6. e2 = (tv3^2) == x
164
+ tv1 = F.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
165
+ tv2 = F.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
166
+ const e3 = F.eql(F.sqr(tv2), n); // 9. e3 = (tv2^2) == x
167
+ const root = F.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
168
+ assertIsSquare(F, root, n);
113
169
  return root;
114
- };
170
+ });
115
171
  }
116
172
  /**
117
173
  * Tonelli-Shanks square root search algorithm.
118
- * 1. https://eprint.iacr.org/2012/685.pdf (page 12)
174
+ * This implementation is variable-time: it searches data-dependently for the first non-residue `Z`
175
+ * and for the smallest `i` in the main loop, unlike RFC 9380 Appendix I.4's constant-time shape.
176
+ * 1. {@link https://eprint.iacr.org/2012/685.pdf | eprint 2012/685}, page 12
119
177
  * 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
120
- * @param P field order
178
+ * @param P - field order
121
179
  * @returns function that takes field Fp (created from P) and number n
180
+ * @throws If the field is too small, non-prime, or the square root does not exist. {@link Error}
181
+ * @example
182
+ * Construct a square-root helper for primes that need Tonelli-Shanks.
183
+ *
184
+ * ```ts
185
+ * import { Field, tonelliShanks } from '@noble/curves/abstract/modular.js';
186
+ * const Fp = Field(17n);
187
+ * const sqrt = tonelliShanks(17n)(Fp, 4n);
188
+ * ```
122
189
  */
123
190
  export function tonelliShanks(P) {
124
191
  // Initialization (precomputation).
@@ -149,38 +216,39 @@ export function tonelliShanks(P) {
149
216
  let cc = _Fp.pow(Z, Q); // c = z^Q
150
217
  const Q1div2 = (Q + _1n) / _2n;
151
218
  return function tonelliSlow(Fp, n) {
152
- if (Fp.is0(n))
219
+ const F = Fp;
220
+ if (F.is0(n))
153
221
  return n;
154
222
  // Check if n is a quadratic residue using Legendre symbol
155
- if (FpLegendre(Fp, n) !== 1)
223
+ if (FpLegendre(F, n) !== 1)
156
224
  throw new Error('Cannot find square root');
157
225
  // Initialize variables for the main loop
158
226
  let M = S;
159
- let c = Fp.mul(Fp.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp
160
- let t = Fp.pow(n, Q); // t = n^Q, first guess at the fudge factor
161
- let R = Fp.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root
227
+ let c = F.mul(F.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp
228
+ let t = F.pow(n, Q); // t = n^Q, first guess at the fudge factor
229
+ let R = F.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root
162
230
  // Main loop
163
231
  // while t != 1
164
- while (!Fp.eql(t, Fp.ONE)) {
165
- if (Fp.is0(t))
166
- return Fp.ZERO; // if t=0 return R=0
232
+ while (!F.eql(t, F.ONE)) {
233
+ if (F.is0(t))
234
+ return F.ZERO; // if t=0 return R=0
167
235
  let i = 1;
168
236
  // Find the smallest i >= 1 such that t^(2^i) ≡ 1 (mod P)
169
- let t_tmp = Fp.sqr(t); // t^(2^1)
170
- while (!Fp.eql(t_tmp, Fp.ONE)) {
237
+ let t_tmp = F.sqr(t); // t^(2^1)
238
+ while (!F.eql(t_tmp, F.ONE)) {
171
239
  i++;
172
- t_tmp = Fp.sqr(t_tmp); // t^(2^2)...
240
+ t_tmp = F.sqr(t_tmp); // t^(2^2)...
173
241
  if (i === M)
174
242
  throw new Error('Cannot find square root');
175
243
  }
176
244
  // Calculate the exponent for b: 2^(M - i - 1)
177
245
  const exponent = _1n << BigInt(M - i - 1); // bigint is important
178
- const b = Fp.pow(c, exponent); // b = 2^(M - i - 1)
246
+ const b = F.pow(c, exponent); // b = 2^(M - i - 1)
179
247
  // Update variables
180
248
  M = i;
181
- c = Fp.sqr(b); // c = b^2
182
- t = Fp.mul(t, c); // t = (t * b^2)
183
- R = Fp.mul(R, b); // R = R*b
249
+ c = F.sqr(b); // c = b^2
250
+ t = F.mul(t, c); // t = (t * b^2)
251
+ R = F.mul(R, b); // R = R*b
184
252
  }
185
253
  return R;
186
254
  };
@@ -194,7 +262,20 @@ export function tonelliShanks(P) {
194
262
  * 4. Tonelli-Shanks algorithm
195
263
  *
196
264
  * Different algorithms can give different roots, it is up to user to decide which one they want.
197
- * For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
265
+ * For example there is FpSqrtOdd/FpSqrtEven to choose a root by oddness
266
+ * (used for hash-to-curve).
267
+ * @param P - Field order.
268
+ * @returns Square-root helper. The generic fallback inherits Tonelli-Shanks' variable-time
269
+ * behavior and this selector assumes prime-field-style integer moduli.
270
+ * @throws If the field is unsupported or the square root does not exist. {@link Error}
271
+ * @example
272
+ * Choose the square-root helper appropriate for one field modulus.
273
+ *
274
+ * ```ts
275
+ * import { Field, FpSqrt } from '@noble/curves/abstract/modular.js';
276
+ * const Fp = Field(17n);
277
+ * const sqrt = FpSqrt(17n)(Fp, 4n);
278
+ * ```
198
279
  */
199
280
  export function FpSqrt(P) {
200
281
  // P ≡ 3 (mod 4) => √n = n^((P+1)/4)
@@ -209,14 +290,42 @@ export function FpSqrt(P) {
209
290
  // Tonelli-Shanks algorithm
210
291
  return tonelliShanks(P);
211
292
  }
212
- // Little-endian check for first LE bit (last BE bit);
293
+ /**
294
+ * @param num - Value to inspect.
295
+ * @param modulo - Field modulus.
296
+ * @returns `true` when the least-significant little-endian bit is set.
297
+ * @throws If the modulus is invalid for `mod(...)`. {@link Error}
298
+ * @example
299
+ * Inspect the low bit used by little-endian sign conventions.
300
+ *
301
+ * ```ts
302
+ * isNegativeLE(3n, 11n);
303
+ * ```
304
+ */
213
305
  export const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n;
214
306
  // prettier-ignore
307
+ // Arithmetic-only subset checked by validateField(). This is intentionally not the full runtime
308
+ // IField contract: helpers like `isValidNot0`, `invertBatch`, `toBytes`, `fromBytes`, `cmov`, and
309
+ // field-specific extras like `isOdd` are left to the callers that actually need them.
215
310
  const FIELD_FIELDS = [
216
311
  'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
217
312
  'eql', 'add', 'sub', 'mul', 'pow', 'div',
218
313
  'addN', 'subN', 'mulN', 'sqrN'
219
314
  ];
315
+ /**
316
+ * @param field - Field implementation.
317
+ * @returns Validated field. This only checks the arithmetic subset needed by generic helpers; it
318
+ * does not guarantee full runtime-method coverage for serialization, batching, `cmov`, or
319
+ * field-specific extras beyond positive `BYTES` / `BITS`.
320
+ * @throws If the field shape or numeric metadata are invalid. {@link Error}
321
+ * @example
322
+ * Check that a field implementation exposes the operations curve code expects.
323
+ *
324
+ * ```ts
325
+ * import { Field, validateField } from '@noble/curves/abstract/modular.js';
326
+ * const Fp = validateField(Field(17n));
327
+ * ```
328
+ */
220
329
  export function validateField(field) {
221
330
  const initial = {
222
331
  ORDER: 'bigint',
@@ -228,61 +337,109 @@ export function validateField(field) {
228
337
  return map;
229
338
  }, initial);
230
339
  validateObject(field, opts);
231
- // const max = 16384;
232
- // if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
233
- // if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
340
+ // Runtime field implementations must expose real integer byte/bit sizes; fractional / NaN /
341
+ // infinite metadata leaks through validateObject(type='number') but breaks encoders and caches.
342
+ asafenumber(field.BYTES, 'BYTES');
343
+ asafenumber(field.BITS, 'BITS');
344
+ // Runtime field implementations must expose positive byte/bit sizes; zero leaks through the
345
+ // numeric shape checks above but still breaks encoding helpers and cached-length assumptions.
346
+ if (field.BYTES < 1 || field.BITS < 1)
347
+ throw new Error('invalid field: expected BYTES/BITS > 0');
348
+ if (field.ORDER <= _1n)
349
+ throw new Error('invalid field: expected ORDER > 1, got ' + field.ORDER);
234
350
  return field;
235
351
  }
236
352
  // Generic field functions
237
353
  /**
238
354
  * Same as `pow` but for Fp: non-constant-time.
239
355
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
356
+ * @param Fp - Field implementation.
357
+ * @param num - Base value.
358
+ * @param power - Exponent value.
359
+ * @returns Powered field element.
360
+ * @throws If the exponent is negative. {@link Error}
361
+ * @example
362
+ * Raise one field element to a public exponent.
363
+ *
364
+ * ```ts
365
+ * import { Field, FpPow } from '@noble/curves/abstract/modular.js';
366
+ * const Fp = Field(17n);
367
+ * const x = FpPow(Fp, 3n, 5n);
368
+ * ```
240
369
  */
241
370
  export function FpPow(Fp, num, power) {
371
+ const F = Fp;
242
372
  if (power < _0n)
243
373
  throw new Error('invalid exponent, negatives unsupported');
244
374
  if (power === _0n)
245
- return Fp.ONE;
375
+ return F.ONE;
246
376
  if (power === _1n)
247
377
  return num;
248
- let p = Fp.ONE;
378
+ let p = F.ONE;
249
379
  let d = num;
250
380
  while (power > _0n) {
251
381
  if (power & _1n)
252
- p = Fp.mul(p, d);
253
- d = Fp.sqr(d);
382
+ p = F.mul(p, d);
383
+ d = F.sqr(d);
254
384
  power >>= _1n;
255
385
  }
256
386
  return p;
257
387
  }
258
388
  /**
259
389
  * Efficiently invert an array of Field elements.
260
- * Exception-free. Will return `undefined` for 0 elements.
261
- * @param passZero map 0 to 0 (instead of undefined)
390
+ * Exception-free. Zero-valued field elements stay `undefined` unless `passZero` is enabled.
391
+ * @param Fp - Field implementation.
392
+ * @param nums - Values to invert.
393
+ * @param passZero - map 0 to 0 (instead of undefined)
394
+ * @returns Inverted values.
395
+ * @example
396
+ * Invert several field elements with one shared inversion.
397
+ *
398
+ * ```ts
399
+ * import { Field, FpInvertBatch } from '@noble/curves/abstract/modular.js';
400
+ * const Fp = Field(17n);
401
+ * const inv = FpInvertBatch(Fp, [1n, 2n, 4n]);
402
+ * ```
262
403
  */
263
404
  export function FpInvertBatch(Fp, nums, passZero = false) {
264
- const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined);
405
+ const F = Fp;
406
+ const inverted = new Array(nums.length).fill(passZero ? F.ZERO : undefined);
265
407
  // Walk from first to last, multiply them by each other MOD p
266
408
  const multipliedAcc = nums.reduce((acc, num, i) => {
267
- if (Fp.is0(num))
409
+ if (F.is0(num))
268
410
  return acc;
269
411
  inverted[i] = acc;
270
- return Fp.mul(acc, num);
271
- }, Fp.ONE);
412
+ return F.mul(acc, num);
413
+ }, F.ONE);
272
414
  // Invert last element
273
- const invertedAcc = Fp.inv(multipliedAcc);
415
+ const invertedAcc = F.inv(multipliedAcc);
274
416
  // Walk from last to first, multiply them by inverted each other MOD p
275
417
  nums.reduceRight((acc, num, i) => {
276
- if (Fp.is0(num))
418
+ if (F.is0(num))
277
419
  return acc;
278
- inverted[i] = Fp.mul(acc, inverted[i]);
279
- return Fp.mul(acc, num);
420
+ inverted[i] = F.mul(acc, inverted[i]);
421
+ return F.mul(acc, num);
280
422
  }, invertedAcc);
281
423
  return inverted;
282
424
  }
283
- // TODO: remove
425
+ /**
426
+ * @param Fp - Field implementation.
427
+ * @param lhs - Dividend value.
428
+ * @param rhs - Divisor value.
429
+ * @returns Division result.
430
+ * @throws If the divisor is non-invertible. {@link Error}
431
+ * @example
432
+ * Divide one field element by another.
433
+ *
434
+ * ```ts
435
+ * import { Field, FpDiv } from '@noble/curves/abstract/modular.js';
436
+ * const Fp = Field(17n);
437
+ * const x = FpDiv(Fp, 6n, 3n);
438
+ * ```
439
+ */
284
440
  export function FpDiv(Fp, lhs, rhs) {
285
- return Fp.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, Fp.ORDER) : Fp.inv(rhs));
441
+ const F = Fp;
442
+ return F.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, F.ORDER) : F.inv(rhs));
286
443
  }
287
444
  /**
288
445
  * Legendre symbol.
@@ -292,33 +449,85 @@ export function FpDiv(Fp, lhs, rhs) {
292
449
  * * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
293
450
  * * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
294
451
  * * (a | p) ≡ 0 if a ≡ 0 (mod p)
452
+ * @param Fp - Field implementation.
453
+ * @param n - Value to inspect.
454
+ * @returns Legendre symbol.
455
+ * @throws If the field returns an invalid Legendre symbol value. {@link Error}
456
+ * @example
457
+ * Compute the Legendre symbol of one field element.
458
+ *
459
+ * ```ts
460
+ * import { Field, FpLegendre } from '@noble/curves/abstract/modular.js';
461
+ * const Fp = Field(17n);
462
+ * const symbol = FpLegendre(Fp, 4n);
463
+ * ```
295
464
  */
296
465
  export function FpLegendre(Fp, n) {
466
+ const F = Fp;
297
467
  // We can use 3rd argument as optional cache of this value
298
468
  // but seems unneeded for now. The operation is very fast.
299
- const p1mod2 = (Fp.ORDER - _1n) / _2n;
300
- const powered = Fp.pow(n, p1mod2);
301
- const yes = Fp.eql(powered, Fp.ONE);
302
- const zero = Fp.eql(powered, Fp.ZERO);
303
- const no = Fp.eql(powered, Fp.neg(Fp.ONE));
469
+ const p1mod2 = (F.ORDER - _1n) / _2n;
470
+ const powered = F.pow(n, p1mod2);
471
+ const yes = F.eql(powered, F.ONE);
472
+ const zero = F.eql(powered, F.ZERO);
473
+ const no = F.eql(powered, F.neg(F.ONE));
304
474
  if (!yes && !zero && !no)
305
475
  throw new Error('invalid Legendre symbol result');
306
476
  return yes ? 1 : zero ? 0 : -1;
307
477
  }
308
- // This function returns True whenever the value x is a square in the field F.
478
+ /**
479
+ * @param Fp - Field implementation.
480
+ * @param n - Value to inspect.
481
+ * @returns `true` when `Fp.sqrt(n)` exists. This includes `0`, even though strict "quadratic
482
+ * residue" terminology often reserves that name for the non-zero square class.
483
+ * @throws If the field returns an invalid Legendre symbol value. {@link Error}
484
+ * @example
485
+ * Check whether one field element has a square root in the field.
486
+ *
487
+ * ```ts
488
+ * import { Field, FpIsSquare } from '@noble/curves/abstract/modular.js';
489
+ * const Fp = Field(17n);
490
+ * const isSquare = FpIsSquare(Fp, 4n);
491
+ * ```
492
+ */
309
493
  export function FpIsSquare(Fp, n) {
310
494
  const l = FpLegendre(Fp, n);
311
- return l === 1;
495
+ // Zero is a square too: 0 = 0^2, and Fp.sqrt(0) already returns 0.
496
+ return l !== -1;
312
497
  }
313
- // CURVE.n lengths
498
+ /**
499
+ * @param n - Curve order. Callers are expected to pass a positive order.
500
+ * @param nBitLength - Optional cached bit length. Callers are expected to pass a positive cached
501
+ * value when overriding the derived bit length.
502
+ * @returns Byte and bit lengths.
503
+ * @throws If the order or cached bit length is invalid. {@link Error}
504
+ * @example
505
+ * Measure the encoding sizes needed for one modulus.
506
+ *
507
+ * ```ts
508
+ * nLength(255n);
509
+ * ```
510
+ */
314
511
  export function nLength(n, nBitLength) {
315
512
  // Bit size, byte size of CURVE.n
316
513
  if (nBitLength !== undefined)
317
514
  anumber(nBitLength);
318
- const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
515
+ if (n <= _0n)
516
+ throw new Error('invalid n length: expected positive n, got ' + n);
517
+ if (nBitLength !== undefined && nBitLength < 1)
518
+ throw new Error('invalid n length: expected positive bit length, got ' + nBitLength);
519
+ const bits = bitLen(n);
520
+ // Cached bit lengths smaller than ORDER would truncate serialized scalars/elements and poison
521
+ // any math that relies on the derived field metadata.
522
+ if (nBitLength !== undefined && nBitLength < bits)
523
+ throw new Error(`invalid n length: expected bit length (${bits}) >= n.length (${nBitLength})`);
524
+ const _nBitLength = nBitLength !== undefined ? nBitLength : bits;
319
525
  const nByteLength = Math.ceil(_nBitLength / 8);
320
526
  return { nBitLength: _nBitLength, nByteLength };
321
527
  }
528
+ // Keep the lazy sqrt cache off-instance so Field(...) can return a frozen object. Otherwise the
529
+ // cached helper write would keep the field surface externally mutable.
530
+ const FIELD_SQRT = new WeakMap();
322
531
  class _Field {
323
532
  ORDER;
324
533
  BITS;
@@ -327,22 +536,26 @@ class _Field {
327
536
  ZERO = _0n;
328
537
  ONE = _1n;
329
538
  _lengths;
330
- _sqrt; // cached sqrt
331
539
  _mod;
332
540
  constructor(ORDER, opts = {}) {
333
- if (ORDER <= _0n)
334
- throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
541
+ // ORDER <= 1 is degenerate: ONE would not be a valid field element and helpers like pow/inv
542
+ // would stop modeling field arithmetic.
543
+ if (ORDER <= _1n)
544
+ throw new Error('invalid field: expected ORDER > 1, got ' + ORDER);
335
545
  let _nbitLength = undefined;
336
546
  this.isLE = false;
337
547
  if (opts != null && typeof opts === 'object') {
548
+ // Cached bit lengths are trusted here and should already be positive / consistent with ORDER.
338
549
  if (typeof opts.BITS === 'number')
339
550
  _nbitLength = opts.BITS;
340
551
  if (typeof opts.sqrt === 'function')
341
- this.sqrt = opts.sqrt;
552
+ // `_Field.prototype` is frozen below, so custom sqrt hooks must become own properties
553
+ // explicitly instead of relying on writable prototype shadowing via assignment.
554
+ Object.defineProperty(this, 'sqrt', { value: opts.sqrt, enumerable: true });
342
555
  if (typeof opts.isLE === 'boolean')
343
556
  this.isLE = opts.isLE;
344
557
  if (opts.allowedLengths)
345
- this._lengths = opts.allowedLengths?.slice();
558
+ this._lengths = Object.freeze(opts.allowedLengths.slice());
346
559
  if (typeof opts.modFromBytes === 'boolean')
347
560
  this._mod = opts.modFromBytes;
348
561
  }
@@ -352,15 +565,14 @@ class _Field {
352
565
  this.ORDER = ORDER;
353
566
  this.BITS = nBitLength;
354
567
  this.BYTES = nByteLength;
355
- this._sqrt = undefined;
356
- Object.preventExtensions(this);
568
+ Object.freeze(this);
357
569
  }
358
570
  create(num) {
359
571
  return mod(num, this.ORDER);
360
572
  }
361
573
  isValid(num) {
362
574
  if (typeof num !== 'bigint')
363
- throw new Error('invalid field element: expected bigint, got ' + typeof num);
575
+ throw new TypeError('invalid field element: expected bigint, got ' + typeof num);
364
576
  return _0n <= num && num < this.ORDER; // 0 is valid element, but it's not invertible
365
577
  }
366
578
  is0(num) {
@@ -414,19 +626,26 @@ class _Field {
414
626
  return invert(num, this.ORDER);
415
627
  }
416
628
  sqrt(num) {
417
- // Caching _sqrt speeds up sqrt9mod16 by 5x and tonneli-shanks by 10%
418
- if (!this._sqrt)
419
- this._sqrt = FpSqrt(this.ORDER);
420
- return this._sqrt(this, num);
629
+ // Caching sqrt helpers speeds up sqrt9mod16 by 5x and Tonelli-Shanks by about 10% without keeping
630
+ // the field instance itself mutable.
631
+ let sqrt = FIELD_SQRT.get(this);
632
+ if (!sqrt)
633
+ FIELD_SQRT.set(this, (sqrt = FpSqrt(this.ORDER)));
634
+ return sqrt(this, num);
421
635
  }
422
636
  toBytes(num) {
637
+ // Serialize fixed-width limbs without re-validating the field range. Callers that need a
638
+ // canonical encoding must pass a valid element; some protocols intentionally serialize raw
639
+ // residues here and reduce or validate them elsewhere.
423
640
  return this.isLE ? numberToBytesLE(num, this.BYTES) : numberToBytesBE(num, this.BYTES);
424
641
  }
425
642
  fromBytes(bytes, skipValidation = false) {
426
643
  abytes(bytes);
427
644
  const { _lengths: allowedLengths, BYTES, isLE, ORDER, _mod: modFromBytes } = this;
428
645
  if (allowedLengths) {
429
- if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
646
+ // `allowedLengths` must list real positive byte lengths; otherwise empty input would get
647
+ // padded into zero and silently decode as a field element.
648
+ if (bytes.length < 1 || !allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
430
649
  throw new Error('Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length);
431
650
  }
432
651
  const padded = new Uint8Array(BYTES);
@@ -442,8 +661,8 @@ class _Field {
442
661
  if (!skipValidation)
443
662
  if (!this.isValid(scalar))
444
663
  throw new Error('invalid field element: outside of range 0..ORDER');
445
- // NOTE: we don't validate scalar here, please use isValid. This done such way because some
446
- // protocol may allow non-reduced scalar that reduced later or changed some other way.
664
+ // Range validation is optional here because some protocols intentionally decode raw residues
665
+ // and reduce or validate them elsewhere.
447
666
  return scalar;
448
667
  }
449
668
  // TODO: we don't need it here, move out to separate fn
@@ -453,27 +672,40 @@ class _Field {
453
672
  // We can't move this out because Fp6, Fp12 implement it
454
673
  // and it's unclear what to return in there.
455
674
  cmov(a, b, condition) {
675
+ // Field elements have `isValid(...)`; the CMOV branch bit is a direct runtime input, so reject
676
+ // non-boolean selectors here instead of letting JS truthiness silently change arithmetic.
677
+ abool(condition, 'condition');
456
678
  return condition ? b : a;
457
679
  }
458
680
  }
681
+ // Freeze the shared method surface too; otherwise callers can still poison every Field instance by
682
+ // monkey-patching `_Field.prototype` even if each instance is frozen.
683
+ Object.freeze(_Field.prototype);
459
684
  /**
460
685
  * Creates a finite field. Major performance optimizations:
461
686
  * * 1. Denormalized operations like mulN instead of mul.
462
687
  * * 2. Identical object shape: never add or remove keys.
463
- * * 3. `Object.freeze`.
688
+ * * 3. Frozen stable object shape; the lazy sqrt cache lives in a module-level `WeakMap`.
464
689
  * Fragile: always run a benchmark on a change.
465
- * Security note: operations don't check 'isValid' for all elements for performance reasons,
466
- * it is caller responsibility to check this.
690
+ * Security note: operations and low-level serializers like `toBytes` don't check `isValid` for
691
+ * all elements for performance and protocol-flexibility reasons; callers are responsible for
692
+ * supplying valid elements when they need canonical field behavior.
467
693
  * This is low-level code, please make sure you know what you're doing.
468
694
  *
469
695
  * Note about field properties:
470
696
  * * CHARACTERISTIC p = prime number, number of elements in main subgroup.
471
697
  * * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
472
698
  *
473
- * @param ORDER field order, probably prime, or could be composite
474
- * @param bitLen how many bits the field consumes
475
- * @param isLE (default: false) if encoding / decoding should be in little-endian
476
- * @param redef optional faster redefinitions of sqrt and other methods
699
+ * @param ORDER - field order, probably prime, or could be composite
700
+ * @param opts - Field options such as bit length or endianness. See {@link FieldOpts}.
701
+ * @returns Frozen field instance with a stable object shape. This wrapper forwards `opts` straight
702
+ * into `_Field`, so it inherits `_Field`'s assumptions about cached sizes and `allowedLengths`.
703
+ * @example
704
+ * Construct one prime field with optional overrides.
705
+ *
706
+ * ```ts
707
+ * Field(11n);
708
+ * ```
477
709
  */
478
710
  export function Field(ORDER, opts = {}) {
479
711
  return new _Field(ORDER, opts);
@@ -491,36 +723,88 @@ export function Field(ORDER, opts = {}) {
491
723
  // const reduced = unsafeAllowZero ? mod(num, ORDER) : mod(num, ORDER - _1n) + _1n;
492
724
  // return reduced;
493
725
  // },
726
+ /**
727
+ * @param Fp - Field implementation.
728
+ * @param elm - Value to square-root.
729
+ * @returns Odd square root when two roots exist. The special case `elm = 0` still returns `0`,
730
+ * which is the only square root but is not odd.
731
+ * @throws If the field lacks oddness checks or the square root does not exist. {@link Error}
732
+ * @example
733
+ * Select the odd square root when two roots exist.
734
+ *
735
+ * ```ts
736
+ * import { Field, FpSqrtOdd } from '@noble/curves/abstract/modular.js';
737
+ * const Fp = Field(17n);
738
+ * const root = FpSqrtOdd(Fp, 4n);
739
+ * ```
740
+ */
494
741
  export function FpSqrtOdd(Fp, elm) {
495
- if (!Fp.isOdd)
742
+ const F = Fp;
743
+ if (!F.isOdd)
496
744
  throw new Error("Field doesn't have isOdd");
497
- const root = Fp.sqrt(elm);
498
- return Fp.isOdd(root) ? root : Fp.neg(root);
745
+ const root = F.sqrt(elm);
746
+ return F.isOdd(root) ? root : F.neg(root);
499
747
  }
748
+ /**
749
+ * @param Fp - Field implementation.
750
+ * @param elm - Value to square-root.
751
+ * @returns Even square root.
752
+ * @throws If the field lacks oddness checks or the square root does not exist. {@link Error}
753
+ * @example
754
+ * Select the even square root when two roots exist.
755
+ *
756
+ * ```ts
757
+ * import { Field, FpSqrtEven } from '@noble/curves/abstract/modular.js';
758
+ * const Fp = Field(17n);
759
+ * const root = FpSqrtEven(Fp, 4n);
760
+ * ```
761
+ */
500
762
  export function FpSqrtEven(Fp, elm) {
501
- if (!Fp.isOdd)
763
+ const F = Fp;
764
+ if (!F.isOdd)
502
765
  throw new Error("Field doesn't have isOdd");
503
- const root = Fp.sqrt(elm);
504
- return Fp.isOdd(root) ? Fp.neg(root) : root;
766
+ const root = F.sqrt(elm);
767
+ return F.isOdd(root) ? F.neg(root) : root;
505
768
  }
506
769
  /**
507
770
  * Returns total number of bytes consumed by the field element.
508
771
  * For example, 32 bytes for usual 256-bit weierstrass curve.
509
- * @param fieldOrder number of field elements, usually CURVE.n
772
+ * @param fieldOrder - number of field elements, usually CURVE.n. Callers are expected to pass an
773
+ * order greater than 1.
510
774
  * @returns byte length of field
775
+ * @throws If the field order is not a bigint. {@link Error}
776
+ * @example
777
+ * Read the fixed-width byte length of one field.
778
+ *
779
+ * ```ts
780
+ * getFieldBytesLength(255n);
781
+ * ```
511
782
  */
512
783
  export function getFieldBytesLength(fieldOrder) {
513
784
  if (typeof fieldOrder !== 'bigint')
514
785
  throw new Error('field order must be bigint');
515
- const bitLength = fieldOrder.toString(2).length;
786
+ // Valid field elements are in 0..ORDER-1, so ORDER <= 1 would make the encoded range degenerate.
787
+ if (fieldOrder <= _1n)
788
+ throw new Error('field order must be greater than 1');
789
+ // Valid field elements are < ORDER, so the maximal encoded element is ORDER - 1.
790
+ const bitLength = bitLen(fieldOrder - _1n);
516
791
  return Math.ceil(bitLength / 8);
517
792
  }
518
793
  /**
519
794
  * Returns minimal amount of bytes that can be safely reduced
520
795
  * by field order.
521
796
  * Should be 2^-128 for 128-bit curve such as P256.
522
- * @param fieldOrder number of field elements, usually CURVE.n
797
+ * This is the reduction / modulo-bias lower bound; higher-level helpers may still impose a larger
798
+ * absolute floor for policy reasons.
799
+ * @param fieldOrder - number of field elements greater than 1, usually CURVE.n.
523
800
  * @returns byte length of target hash
801
+ * @throws If the field order is invalid. {@link Error}
802
+ * @example
803
+ * Compute the minimum hash length needed for field reduction.
804
+ *
805
+ * ```ts
806
+ * getMinHashLength(255n);
807
+ * ```
524
808
  */
525
809
  export function getMinHashLength(fieldOrder) {
526
810
  const length = getFieldBytesLength(fieldOrder);
@@ -530,22 +814,33 @@ export function getMinHashLength(fieldOrder) {
530
814
  * "Constant-time" private key generation utility.
531
815
  * Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
532
816
  * and convert them into private scalar, with the modulo bias being negligible.
533
- * Needs at least 48 bytes of input for 32-byte private key.
534
- * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
535
- * FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
536
- * RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
537
- * @param hash hash output from SHA3 or a similar function
538
- * @param groupOrder size of subgroup - (e.g. secp256k1.Point.Fn.ORDER)
539
- * @param isLE interpret hash bytes as LE num
817
+ * Needs at least 48 bytes of input for 32-byte private key. The implementation also keeps a hard
818
+ * 16-byte minimum even when `getMinHashLength(...)` is smaller, so toy-small inputs do not look
819
+ * accidentally acceptable for real scalar derivation.
820
+ * See {@link https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | Kudelski's modulo-bias guide},
821
+ * {@link https://csrc.nist.gov/publications/detail/fips/186/5/final | FIPS 186-5 appendix A.2}, and
822
+ * {@link https://www.rfc-editor.org/rfc/rfc9380#section-5 | RFC 9380 section 5}. Unlike RFC 9380
823
+ * `hash_to_field`, this helper intentionally maps into the non-zero private-scalar range `1..n-1`.
824
+ * @param key - Uniform input bytes.
825
+ * @param fieldOrder - Size of subgroup.
826
+ * @param isLE - interpret hash bytes as LE num
540
827
  * @returns valid private scalar
828
+ * @throws If the hash length or field order is invalid for scalar reduction. {@link Error}
829
+ * @example
830
+ * Map hash output into a private scalar range.
831
+ *
832
+ * ```ts
833
+ * mapHashToField(new Uint8Array(48).fill(1), 255n);
834
+ * ```
541
835
  */
542
836
  export function mapHashToField(key, fieldOrder, isLE = false) {
543
837
  abytes(key);
544
838
  const len = key.length;
545
839
  const fieldLen = getFieldBytesLength(fieldOrder);
546
- const minLen = getMinHashLength(fieldOrder);
547
- // No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
548
- if (len < 16 || len < minLen || len > 1024)
840
+ const minLen = Math.max(getMinHashLength(fieldOrder), 16);
841
+ // No toy-small inputs: the helper is for real scalar derivation, not tiny test curves. No huge
842
+ // inputs: easier to reason about JS timing / allocation behavior.
843
+ if (len < minLen || len > 1024)
549
844
  throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
550
845
  const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
551
846
  // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0