@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
@@ -6,13 +6,18 @@
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
8
  import {
9
+ abool,
9
10
  abytes,
10
11
  anumber,
12
+ asafenumber,
13
+ bitLen,
11
14
  bytesToNumberBE,
12
15
  bytesToNumberLE,
13
16
  numberToBytesBE,
14
17
  numberToBytesLE,
15
18
  validateObject,
19
+ type TArg,
20
+ type TRet,
16
21
  } from '../utils.ts';
17
22
 
18
23
  // Numbers aren't used in x25519 / x448 builds
@@ -24,23 +29,62 @@ const _3n = /* @__PURE__ */ BigInt(3), _4n = /* @__PURE__ */ BigInt(4), _5n = /*
24
29
  const _7n = /* @__PURE__ */ BigInt(7), _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9);
25
30
  const _16n = /* @__PURE__ */ BigInt(16);
26
31
 
27
- // Calculates a modulo b
32
+ /**
33
+ * @param a - Dividend value.
34
+ * @param b - Positive modulus.
35
+ * @returns Reduced value in `[0, b)` only when `b` is positive.
36
+ * @throws If the modulus is not positive. {@link Error}
37
+ * @example
38
+ * Normalize a bigint into one field residue.
39
+ *
40
+ * ```ts
41
+ * mod(-1n, 5n);
42
+ * ```
43
+ */
28
44
  export function mod(a: bigint, b: bigint): bigint {
45
+ if (b <= _0n) throw new Error('mod: expected positive modulus, got ' + b);
29
46
  const result = a % b;
30
47
  return result >= _0n ? result : b + result;
31
48
  }
32
49
  /**
33
- * Efficiently raise num to power and do modular division.
50
+ * Efficiently raise num to a power with modular reduction.
34
51
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
52
+ * Low-level helper: callers that need canonical residues must pass a valid `num` for the chosen
53
+ * modulus instead of relying on the `power===0/1` fast paths to normalize it.
54
+ * @param num - Base value.
55
+ * @param power - Exponent value.
56
+ * @param modulo - Reduction modulus.
57
+ * @returns Modular exponentiation result.
58
+ * @throws If the modulus or exponent is invalid. {@link Error}
35
59
  * @example
60
+ * Raise one bigint to a modular power.
61
+ *
62
+ * ```ts
36
63
  * pow(2n, 6n, 11n) // 64n % 11n == 9n
64
+ * ```
37
65
  */
38
66
  export function pow(num: bigint, power: bigint, modulo: bigint): bigint {
39
67
  return FpPow(Field(modulo), num, power);
40
68
  }
41
69
 
42
- /** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
70
+ /**
71
+ * Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)`.
72
+ * Low-level helper: callers that need canonical residues must pass a valid `x` for the chosen
73
+ * modulus; the `power===0` fast path intentionally returns the input unchanged.
74
+ * @param x - Base value.
75
+ * @param power - Number of squarings.
76
+ * @param modulo - Reduction modulus.
77
+ * @returns Repeated-squaring result.
78
+ * @throws If the exponent is negative. {@link Error}
79
+ * @example
80
+ * Apply repeated squaring inside one field.
81
+ *
82
+ * ```ts
83
+ * pow2(3n, 2n, 11n);
84
+ * ```
85
+ */
43
86
  export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
87
+ if (power < _0n) throw new Error('pow2: expected non-negative exponent, got ' + power);
44
88
  let res = x;
45
89
  while (power-- > _0n) {
46
90
  res *= res;
@@ -51,7 +95,17 @@ export function pow2(x: bigint, power: bigint, modulo: bigint): bigint {
51
95
 
52
96
  /**
53
97
  * Inverses number over modulo.
54
- * Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
98
+ * Implemented using the {@link https://brilliant.org/wiki/extended-euclidean-algorithm/ | extended Euclidean algorithm}.
99
+ * @param number - Value to invert.
100
+ * @param modulo - Positive modulus.
101
+ * @returns Multiplicative inverse.
102
+ * @throws If the modulus is invalid or the inverse does not exist. {@link Error}
103
+ * @example
104
+ * Compute one modular inverse with the extended Euclidean algorithm.
105
+ *
106
+ * ```ts
107
+ * invert(3n, 11n);
108
+ * ```
55
109
  */
56
110
  export function invert(number: bigint, modulo: bigint): bigint {
57
111
  if (number === _0n) throw new Error('invert: expected non-zero number');
@@ -62,9 +116,8 @@ export function invert(number: bigint, modulo: bigint): bigint {
62
116
  // prettier-ignore
63
117
  let x = _0n, y = _1n, u = _1n, v = _0n;
64
118
  while (a !== _0n) {
65
- // JIT applies optimization if those two lines follow each other
66
119
  const q = b / a;
67
- const r = b % a;
120
+ const r = b - a * q;
68
121
  const m = x - u * q;
69
122
  const n = y - v * q;
70
123
  // prettier-ignore
@@ -75,65 +128,82 @@ export function invert(number: bigint, modulo: bigint): bigint {
75
128
  return mod(x, modulo);
76
129
  }
77
130
 
78
- function assertIsSquare<T>(Fp: IField<T>, root: T, n: T): void {
79
- if (!Fp.eql(Fp.sqr(root), n)) throw new Error('Cannot find square root');
131
+ function assertIsSquare<T>(Fp: TArg<IField<T>>, root: T, n: T): void {
132
+ const F = Fp as IField<T>;
133
+ if (!F.eql(F.sqr(root), n)) throw new Error('Cannot find square root');
80
134
  }
81
135
 
82
136
  // Not all roots are possible! Example which will throw:
83
137
  // const NUM =
84
138
  // n = 72057594037927816n;
85
139
  // Fp = Field(BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'));
86
- function sqrt3mod4<T>(Fp: IField<T>, n: T) {
87
- const p1div4 = (Fp.ORDER + _1n) / _4n;
88
- const root = Fp.pow(n, p1div4);
89
- assertIsSquare(Fp, root, n);
140
+ function sqrt3mod4<T>(Fp: TArg<IField<T>>, n: T) {
141
+ const F = Fp as IField<T>;
142
+ const p1div4 = (F.ORDER + _1n) / _4n;
143
+ const root = F.pow(n, p1div4);
144
+ assertIsSquare(F, root, n);
90
145
  return root;
91
146
  }
92
147
 
93
- function sqrt5mod8<T>(Fp: IField<T>, n: T) {
94
- const p5div8 = (Fp.ORDER - _5n) / _8n;
95
- const n2 = Fp.mul(n, _2n);
96
- const v = Fp.pow(n2, p5div8);
97
- const nv = Fp.mul(n, v);
98
- const i = Fp.mul(Fp.mul(nv, _2n), v);
99
- const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
100
- assertIsSquare(Fp, root, n);
148
+ // Equivalent `q = 5 (mod 8)` square-root formula (Atkin-style), not the RFC Appendix I.2 CMOV
149
+ // pseudocode verbatim.
150
+ function sqrt5mod8<T>(Fp: TArg<IField<T>>, n: T) {
151
+ const F = Fp as IField<T>;
152
+ const p5div8 = (F.ORDER - _5n) / _8n;
153
+ const n2 = F.mul(n, _2n);
154
+ const v = F.pow(n2, p5div8);
155
+ const nv = F.mul(n, v);
156
+ const i = F.mul(F.mul(nv, _2n), v);
157
+ const root = F.mul(nv, F.sub(i, F.ONE));
158
+ assertIsSquare(F, root, n);
101
159
  return root;
102
160
  }
103
161
 
104
162
  // Based on RFC9380, Kong algorithm
105
163
  // prettier-ignore
106
- function sqrt9mod16(P: bigint): <T>(Fp: IField<T>, n: T) => T {
164
+ function sqrt9mod16(P: bigint): TRet<<T>(Fp: IField<T>, n: T) => T> {
107
165
  const Fp_ = Field(P);
108
166
  const tn = tonelliShanks(P);
109
167
  const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));// 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
110
168
  const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
111
169
  const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
112
170
  const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
113
- return <T>(Fp: IField<T>, n: T) => {
114
- let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
115
- let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
116
- const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
117
- const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
118
- const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
119
- const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
120
- tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
121
- tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
122
- const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
123
- const root = Fp.cmov(tv1, tv2, e3);// 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
124
- assertIsSquare(Fp, root, n);
171
+ return (<T>(Fp: TArg<IField<T>>, n: T): T => {
172
+ const F = Fp as IField<T>;
173
+ let tv1 = F.pow(n, c4); // 1. tv1 = x^c4
174
+ let tv2 = F.mul(tv1, c1); // 2. tv2 = c1 * tv1
175
+ const tv3 = F.mul(tv1, c2); // 3. tv3 = c2 * tv1
176
+ const tv4 = F.mul(tv1, c3); // 4. tv4 = c3 * tv1
177
+ const e1 = F.eql(F.sqr(tv2), n); // 5. e1 = (tv2^2) == x
178
+ const e2 = F.eql(F.sqr(tv3), n); // 6. e2 = (tv3^2) == x
179
+ tv1 = F.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
180
+ tv2 = F.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
181
+ const e3 = F.eql(F.sqr(tv2), n); // 9. e3 = (tv2^2) == x
182
+ const root = F.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
183
+ assertIsSquare(F, root, n);
125
184
  return root;
126
- };
185
+ }) as TRet<<T>(Fp: IField<T>, n: T) => T>;
127
186
  }
128
187
 
129
188
  /**
130
189
  * Tonelli-Shanks square root search algorithm.
131
- * 1. https://eprint.iacr.org/2012/685.pdf (page 12)
190
+ * This implementation is variable-time: it searches data-dependently for the first non-residue `Z`
191
+ * and for the smallest `i` in the main loop, unlike RFC 9380 Appendix I.4's constant-time shape.
192
+ * 1. {@link https://eprint.iacr.org/2012/685.pdf | eprint 2012/685}, page 12
132
193
  * 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
133
- * @param P field order
194
+ * @param P - field order
134
195
  * @returns function that takes field Fp (created from P) and number n
196
+ * @throws If the field is too small, non-prime, or the square root does not exist. {@link Error}
197
+ * @example
198
+ * Construct a square-root helper for primes that need Tonelli-Shanks.
199
+ *
200
+ * ```ts
201
+ * import { Field, tonelliShanks } from '@noble/curves/abstract/modular.js';
202
+ * const Fp = Field(17n);
203
+ * const sqrt = tonelliShanks(17n)(Fp, 4n);
204
+ * ```
135
205
  */
136
- export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
206
+ export function tonelliShanks(P: bigint): TRet<<T>(Fp: IField<T>, n: T) => T> {
137
207
  // Initialization (precomputation).
138
208
  // Caching initialization could boost perf by 7%.
139
209
  if (P < _3n) throw new Error('sqrt is not defined for small field');
@@ -154,49 +224,50 @@ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
154
224
  if (Z++ > 1000) throw new Error('Cannot find square root: probably non-prime P');
155
225
  }
156
226
  // Fast-path; usually done before Z, but we do "primality test".
157
- if (S === 1) return sqrt3mod4;
227
+ if (S === 1) return sqrt3mod4 as TRet<<T>(Fp: IField<T>, n: T) => T>;
158
228
 
159
229
  // Slow-path
160
230
  // TODO: test on Fp2 and others
161
231
  let cc = _Fp.pow(Z, Q); // c = z^Q
162
232
  const Q1div2 = (Q + _1n) / _2n;
163
- return function tonelliSlow<T>(Fp: IField<T>, n: T): T {
164
- if (Fp.is0(n)) return n;
233
+ return function tonelliSlow<T>(Fp: TArg<IField<T>>, n: T): T {
234
+ const F = Fp as IField<T>;
235
+ if (F.is0(n)) return n;
165
236
  // Check if n is a quadratic residue using Legendre symbol
166
- if (FpLegendre(Fp, n) !== 1) throw new Error('Cannot find square root');
237
+ if (FpLegendre(F, n) !== 1) throw new Error('Cannot find square root');
167
238
 
168
239
  // Initialize variables for the main loop
169
240
  let M = S;
170
- let c = Fp.mul(Fp.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp
171
- let t = Fp.pow(n, Q); // t = n^Q, first guess at the fudge factor
172
- let R = Fp.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root
241
+ let c = F.mul(F.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp
242
+ let t = F.pow(n, Q); // t = n^Q, first guess at the fudge factor
243
+ let R = F.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root
173
244
 
174
245
  // Main loop
175
246
  // while t != 1
176
- while (!Fp.eql(t, Fp.ONE)) {
177
- if (Fp.is0(t)) return Fp.ZERO; // if t=0 return R=0
247
+ while (!F.eql(t, F.ONE)) {
248
+ if (F.is0(t)) return F.ZERO; // if t=0 return R=0
178
249
  let i = 1;
179
250
 
180
251
  // Find the smallest i >= 1 such that t^(2^i) ≡ 1 (mod P)
181
- let t_tmp = Fp.sqr(t); // t^(2^1)
182
- while (!Fp.eql(t_tmp, Fp.ONE)) {
252
+ let t_tmp = F.sqr(t); // t^(2^1)
253
+ while (!F.eql(t_tmp, F.ONE)) {
183
254
  i++;
184
- t_tmp = Fp.sqr(t_tmp); // t^(2^2)...
255
+ t_tmp = F.sqr(t_tmp); // t^(2^2)...
185
256
  if (i === M) throw new Error('Cannot find square root');
186
257
  }
187
258
 
188
259
  // Calculate the exponent for b: 2^(M - i - 1)
189
260
  const exponent = _1n << BigInt(M - i - 1); // bigint is important
190
- const b = Fp.pow(c, exponent); // b = 2^(M - i - 1)
261
+ const b = F.pow(c, exponent); // b = 2^(M - i - 1)
191
262
 
192
263
  // Update variables
193
264
  M = i;
194
- c = Fp.sqr(b); // c = b^2
195
- t = Fp.mul(t, c); // t = (t * b^2)
196
- R = Fp.mul(R, b); // R = R*b
265
+ c = F.sqr(b); // c = b^2
266
+ t = F.mul(t, c); // t = (t * b^2)
267
+ R = F.mul(R, b); // R = R*b
197
268
  }
198
269
  return R;
199
- };
270
+ } as TRet<<T>(Fp: IField<T>, n: T) => T>;
200
271
  }
201
272
 
202
273
  /**
@@ -208,72 +279,255 @@ export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
208
279
  * 4. Tonelli-Shanks algorithm
209
280
  *
210
281
  * Different algorithms can give different roots, it is up to user to decide which one they want.
211
- * For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
282
+ * For example there is FpSqrtOdd/FpSqrtEven to choose a root by oddness
283
+ * (used for hash-to-curve).
284
+ * @param P - Field order.
285
+ * @returns Square-root helper. The generic fallback inherits Tonelli-Shanks' variable-time
286
+ * behavior and this selector assumes prime-field-style integer moduli.
287
+ * @throws If the field is unsupported or the square root does not exist. {@link Error}
288
+ * @example
289
+ * Choose the square-root helper appropriate for one field modulus.
290
+ *
291
+ * ```ts
292
+ * import { Field, FpSqrt } from '@noble/curves/abstract/modular.js';
293
+ * const Fp = Field(17n);
294
+ * const sqrt = FpSqrt(17n)(Fp, 4n);
295
+ * ```
212
296
  */
213
- export function FpSqrt(P: bigint): <T>(Fp: IField<T>, n: T) => T {
297
+ export function FpSqrt(P: bigint): TRet<<T>(Fp: IField<T>, n: T) => T> {
214
298
  // P ≡ 3 (mod 4) => √n = n^((P+1)/4)
215
- if (P % _4n === _3n) return sqrt3mod4;
299
+ if (P % _4n === _3n) return sqrt3mod4 as TRet<<T>(Fp: IField<T>, n: T) => T>;
216
300
  // P ≡ 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf
217
- if (P % _8n === _5n) return sqrt5mod8;
301
+ if (P % _8n === _5n) return sqrt5mod8 as TRet<<T>(Fp: IField<T>, n: T) => T>;
218
302
  // P ≡ 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)
219
303
  if (P % _16n === _9n) return sqrt9mod16(P);
220
304
  // Tonelli-Shanks algorithm
221
305
  return tonelliShanks(P);
222
306
  }
223
307
 
224
- // Little-endian check for first LE bit (last BE bit);
308
+ /**
309
+ * @param num - Value to inspect.
310
+ * @param modulo - Field modulus.
311
+ * @returns `true` when the least-significant little-endian bit is set.
312
+ * @throws If the modulus is invalid for `mod(...)`. {@link Error}
313
+ * @example
314
+ * Inspect the low bit used by little-endian sign conventions.
315
+ *
316
+ * ```ts
317
+ * isNegativeLE(3n, 11n);
318
+ * ```
319
+ */
225
320
  export const isNegativeLE = (num: bigint, modulo: bigint): boolean =>
226
321
  (mod(num, modulo) & _1n) === _1n;
227
322
 
228
- /** Field is not always over prime: for example, Fp2 has ORDER(q)=p^m. */
323
+ /** Generic field interface used by prime and extension fields alike.
324
+ * Generic helpers treat field operations as pure functions: implementations MUST treat provided
325
+ * values/byte buffers as read-only and return detached results instead of mutating arguments.
326
+ */
229
327
  export interface IField<T> {
328
+ /** Field order `q`, which may be prime or a prime power. */
230
329
  ORDER: bigint;
330
+ /** Canonical encoded byte length. */
231
331
  BYTES: number;
332
+ /** Canonical encoded bit length. */
232
333
  BITS: number;
334
+ /** Whether encoded field elements use little-endian bytes. */
233
335
  isLE: boolean;
336
+ /** Additive identity. */
234
337
  ZERO: T;
338
+ /** Multiplicative identity. */
235
339
  ONE: T;
236
340
  // 1-arg
341
+ /**
342
+ * Normalize one value into the field.
343
+ * @param num - Input value.
344
+ * @returns Normalized field value.
345
+ */
237
346
  create: (num: T) => T;
347
+ /**
348
+ * Check whether one value already belongs to the field.
349
+ * @param num - Input value.
350
+ * Implementations may throw `TypeError` on malformed input types instead of returning `false`.
351
+ * @returns Whether the value already belongs to the field.
352
+ */
238
353
  isValid: (num: T) => boolean;
354
+ /**
355
+ * Check whether one value is zero.
356
+ * @param num - Input value.
357
+ * @returns Whether the value is zero.
358
+ */
239
359
  is0: (num: T) => boolean;
360
+ /**
361
+ * Check whether one value is non-zero and belongs to the field.
362
+ * @param num - Input value.
363
+ * Implementations may throw `TypeError` on malformed input types instead of returning `false`.
364
+ * @returns Whether the value is non-zero and valid.
365
+ */
240
366
  isValidNot0: (num: T) => boolean;
367
+ /**
368
+ * Negate one value.
369
+ * @param num - Input value.
370
+ * @returns Negated value.
371
+ */
241
372
  neg(num: T): T;
373
+ /**
374
+ * Invert one value multiplicatively.
375
+ * @param num - Input value.
376
+ * @returns Multiplicative inverse.
377
+ */
242
378
  inv(num: T): T;
379
+ /**
380
+ * Compute one square root when it exists.
381
+ * @param num - Input value.
382
+ * @returns Square root.
383
+ */
243
384
  sqrt(num: T): T;
385
+ /**
386
+ * Square one value.
387
+ * @param num - Input value.
388
+ * @returns Squared value.
389
+ */
244
390
  sqr(num: T): T;
245
391
  // 2-args
392
+ /**
393
+ * Compare two field values.
394
+ * @param lhs - Left value.
395
+ * @param rhs - Right value.
396
+ * @returns Whether both values are equal.
397
+ */
246
398
  eql(lhs: T, rhs: T): boolean;
399
+ /**
400
+ * Add two normalized field values.
401
+ * @param lhs - Left value.
402
+ * @param rhs - Right value.
403
+ * @returns Sum value.
404
+ */
247
405
  add(lhs: T, rhs: T): T;
406
+ /**
407
+ * Subtract two normalized field values.
408
+ * @param lhs - Left value.
409
+ * @param rhs - Right value.
410
+ * @returns Difference value.
411
+ */
248
412
  sub(lhs: T, rhs: T): T;
413
+ /**
414
+ * Multiply two field values.
415
+ * @param lhs - Left value.
416
+ * @param rhs - Right value or scalar.
417
+ * @returns Product value.
418
+ */
249
419
  mul(lhs: T, rhs: T | bigint): T;
420
+ /**
421
+ * Raise one field value to a power.
422
+ * @param lhs - Base value.
423
+ * @param power - Exponent.
424
+ * @returns Power value.
425
+ */
250
426
  pow(lhs: T, power: bigint): T;
427
+ /**
428
+ * Divide one field value by another.
429
+ * @param lhs - Dividend.
430
+ * @param rhs - Divisor or scalar.
431
+ * @returns Quotient value.
432
+ */
251
433
  div(lhs: T, rhs: T | bigint): T;
252
434
  // N for NonNormalized (for now)
435
+ /**
436
+ * Add two values without re-normalizing the result.
437
+ * @param lhs - Left value.
438
+ * @param rhs - Right value.
439
+ * @returns Non-normalized sum.
440
+ */
253
441
  addN(lhs: T, rhs: T): T;
442
+ /**
443
+ * Subtract two values without re-normalizing the result.
444
+ * @param lhs - Left value.
445
+ * @param rhs - Right value.
446
+ * @returns Non-normalized difference.
447
+ */
254
448
  subN(lhs: T, rhs: T): T;
449
+ /**
450
+ * Multiply two values without re-normalizing the result.
451
+ * @param lhs - Left value.
452
+ * @param rhs - Right value or scalar.
453
+ * @returns Non-normalized product.
454
+ */
255
455
  mulN(lhs: T, rhs: T | bigint): T;
456
+ /**
457
+ * Square one value without re-normalizing the result.
458
+ * @param num - Input value.
459
+ * @returns Non-normalized square.
460
+ */
256
461
  sqrN(num: T): T;
257
462
 
258
463
  // Optional
259
464
  // Should be same as sgn0 function in
260
465
  // [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
261
- // NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
262
- isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
466
+ // NOTE: sgn0 is "negative in LE", which is the same as odd.
467
+ // Negative in LE is a somewhat strange definition anyway.
468
+ /**
469
+ * Return the RFC 9380 `sgn0`-style oddness bit when supported.
470
+ * This uses oddness instead of evenness so extension fields like Fp2 can expose the same hook.
471
+ * Returns whether the value is odd under the field encoding.
472
+ */
473
+ isOdd?(num: T): boolean;
263
474
  // legendre?(num: T): T;
475
+ /**
476
+ * Invert many field elements in one batch.
477
+ * @param lst - Values to invert.
478
+ * @returns Batch of inverses.
479
+ */
264
480
  invertBatch: (lst: T[]) => T[];
481
+ /**
482
+ * Encode one field value into fixed-width bytes.
483
+ * Callers that need canonical encodings MUST supply a valid field element.
484
+ * Low-level protocols may also use this to serialize raw / non-canonical residues.
485
+ * @param num - Input value.
486
+ * @returns Fixed-width byte encoding.
487
+ */
265
488
  toBytes(num: T): Uint8Array;
489
+ /**
490
+ * Decode one field value from fixed-width bytes.
491
+ * @param bytes - Fixed-width byte encoding.
492
+ * @param skipValidation - Whether to skip range validation.
493
+ * Implementations MUST treat `bytes` as read-only.
494
+ * @returns Decoded field value.
495
+ */
266
496
  fromBytes(bytes: Uint8Array, skipValidation?: boolean): T;
267
497
  // If c is False, CMOV returns a, otherwise it returns b.
498
+ /**
499
+ * Constant-time conditional move.
500
+ * @param a - Value used when the condition is false.
501
+ * @param b - Value used when the condition is true.
502
+ * @param c - Selection bit.
503
+ * @returns Selected value.
504
+ */
268
505
  cmov(a: T, b: T, c: boolean): T;
269
506
  }
270
507
  // prettier-ignore
508
+ // Arithmetic-only subset checked by validateField(). This is intentionally not the full runtime
509
+ // IField contract: helpers like `isValidNot0`, `invertBatch`, `toBytes`, `fromBytes`, `cmov`, and
510
+ // field-specific extras like `isOdd` are left to the callers that actually need them.
271
511
  const FIELD_FIELDS = [
272
512
  'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
273
513
  'eql', 'add', 'sub', 'mul', 'pow', 'div',
274
514
  'addN', 'subN', 'mulN', 'sqrN'
275
515
  ] as const;
276
- export function validateField<T>(field: IField<T>): IField<T> {
516
+ /**
517
+ * @param field - Field implementation.
518
+ * @returns Validated field. This only checks the arithmetic subset needed by generic helpers; it
519
+ * does not guarantee full runtime-method coverage for serialization, batching, `cmov`, or
520
+ * field-specific extras beyond positive `BYTES` / `BITS`.
521
+ * @throws If the field shape or numeric metadata are invalid. {@link Error}
522
+ * @example
523
+ * Check that a field implementation exposes the operations curve code expects.
524
+ *
525
+ * ```ts
526
+ * import { Field, validateField } from '@noble/curves/abstract/modular.js';
527
+ * const Fp = validateField(Field(17n));
528
+ * ```
529
+ */
530
+ export function validateField<T>(field: TArg<IField<T>>): TRet<IField<T>> {
277
531
  const initial = {
278
532
  ORDER: 'bigint',
279
533
  BYTES: 'number',
@@ -284,10 +538,15 @@ export function validateField<T>(field: IField<T>): IField<T> {
284
538
  return map;
285
539
  }, initial);
286
540
  validateObject(field, opts);
287
- // const max = 16384;
288
- // if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
289
- // if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
290
- return field;
541
+ // Runtime field implementations must expose real integer byte/bit sizes; fractional / NaN /
542
+ // infinite metadata leaks through validateObject(type='number') but breaks encoders and caches.
543
+ asafenumber(field.BYTES, 'BYTES');
544
+ asafenumber(field.BITS, 'BITS');
545
+ // Runtime field implementations must expose positive byte/bit sizes; zero leaks through the
546
+ // numeric shape checks above but still breaks encoding helpers and cached-length assumptions.
547
+ if (field.BYTES < 1 || field.BITS < 1) throw new Error('invalid field: expected BYTES/BITS > 0');
548
+ if (field.ORDER <= _1n) throw new Error('invalid field: expected ORDER > 1, got ' + field.ORDER);
549
+ return field as TRet<IField<T>>;
291
550
  }
292
551
 
293
552
  // Generic field functions
@@ -295,16 +554,30 @@ export function validateField<T>(field: IField<T>): IField<T> {
295
554
  /**
296
555
  * Same as `pow` but for Fp: non-constant-time.
297
556
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
557
+ * @param Fp - Field implementation.
558
+ * @param num - Base value.
559
+ * @param power - Exponent value.
560
+ * @returns Powered field element.
561
+ * @throws If the exponent is negative. {@link Error}
562
+ * @example
563
+ * Raise one field element to a public exponent.
564
+ *
565
+ * ```ts
566
+ * import { Field, FpPow } from '@noble/curves/abstract/modular.js';
567
+ * const Fp = Field(17n);
568
+ * const x = FpPow(Fp, 3n, 5n);
569
+ * ```
298
570
  */
299
- export function FpPow<T>(Fp: IField<T>, num: T, power: bigint): T {
571
+ export function FpPow<T>(Fp: TArg<IField<T>>, num: T, power: bigint): T {
572
+ const F = Fp as IField<T>;
300
573
  if (power < _0n) throw new Error('invalid exponent, negatives unsupported');
301
- if (power === _0n) return Fp.ONE;
574
+ if (power === _0n) return F.ONE;
302
575
  if (power === _1n) return num;
303
- let p = Fp.ONE;
576
+ let p = F.ONE;
304
577
  let d = num;
305
578
  while (power > _0n) {
306
- if (power & _1n) p = Fp.mul(p, d);
307
- d = Fp.sqr(d);
579
+ if (power & _1n) p = F.mul(p, d);
580
+ d = F.sqr(d);
308
581
  power >>= _1n;
309
582
  }
310
583
  return p;
@@ -312,31 +585,58 @@ export function FpPow<T>(Fp: IField<T>, num: T, power: bigint): T {
312
585
 
313
586
  /**
314
587
  * Efficiently invert an array of Field elements.
315
- * Exception-free. Will return `undefined` for 0 elements.
316
- * @param passZero map 0 to 0 (instead of undefined)
588
+ * Exception-free. Zero-valued field elements stay `undefined` unless `passZero` is enabled.
589
+ * @param Fp - Field implementation.
590
+ * @param nums - Values to invert.
591
+ * @param passZero - map 0 to 0 (instead of undefined)
592
+ * @returns Inverted values.
593
+ * @example
594
+ * Invert several field elements with one shared inversion.
595
+ *
596
+ * ```ts
597
+ * import { Field, FpInvertBatch } from '@noble/curves/abstract/modular.js';
598
+ * const Fp = Field(17n);
599
+ * const inv = FpInvertBatch(Fp, [1n, 2n, 4n]);
600
+ * ```
317
601
  */
318
- export function FpInvertBatch<T>(Fp: IField<T>, nums: T[], passZero = false): T[] {
319
- const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined);
602
+ export function FpInvertBatch<T>(Fp: TArg<IField<T>>, nums: T[], passZero = false): T[] {
603
+ const F = Fp as IField<T>;
604
+ const inverted = new Array(nums.length).fill(passZero ? F.ZERO : undefined) as T[];
320
605
  // Walk from first to last, multiply them by each other MOD p
321
606
  const multipliedAcc = nums.reduce((acc, num, i) => {
322
- if (Fp.is0(num)) return acc;
607
+ if (F.is0(num)) return acc;
323
608
  inverted[i] = acc;
324
- return Fp.mul(acc, num);
325
- }, Fp.ONE);
609
+ return F.mul(acc, num);
610
+ }, F.ONE);
326
611
  // Invert last element
327
- const invertedAcc = Fp.inv(multipliedAcc);
612
+ const invertedAcc = F.inv(multipliedAcc);
328
613
  // Walk from last to first, multiply them by inverted each other MOD p
329
614
  nums.reduceRight((acc, num, i) => {
330
- if (Fp.is0(num)) return acc;
331
- inverted[i] = Fp.mul(acc, inverted[i]);
332
- return Fp.mul(acc, num);
615
+ if (F.is0(num)) return acc;
616
+ inverted[i] = F.mul(acc, inverted[i]);
617
+ return F.mul(acc, num);
333
618
  }, invertedAcc);
334
619
  return inverted;
335
620
  }
336
621
 
337
- // TODO: remove
338
- export function FpDiv<T>(Fp: IField<T>, lhs: T, rhs: T | bigint): T {
339
- return Fp.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, Fp.ORDER) : Fp.inv(rhs));
622
+ /**
623
+ * @param Fp - Field implementation.
624
+ * @param lhs - Dividend value.
625
+ * @param rhs - Divisor value.
626
+ * @returns Division result.
627
+ * @throws If the divisor is non-invertible. {@link Error}
628
+ * @example
629
+ * Divide one field element by another.
630
+ *
631
+ * ```ts
632
+ * import { Field, FpDiv } from '@noble/curves/abstract/modular.js';
633
+ * const Fp = Field(17n);
634
+ * const x = FpDiv(Fp, 6n, 3n);
635
+ * ```
636
+ */
637
+ export function FpDiv<T>(Fp: TArg<IField<T>>, lhs: T, rhs: T | bigint): T {
638
+ const F = Fp as IField<T>;
639
+ return F.mul(lhs, typeof rhs === 'bigint' ? invert(rhs, F.ORDER) : F.inv(rhs));
340
640
  }
341
641
 
342
642
  /**
@@ -347,31 +647,85 @@ export function FpDiv<T>(Fp: IField<T>, lhs: T, rhs: T | bigint): T {
347
647
  * * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
348
648
  * * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
349
649
  * * (a | p) ≡ 0 if a ≡ 0 (mod p)
650
+ * @param Fp - Field implementation.
651
+ * @param n - Value to inspect.
652
+ * @returns Legendre symbol.
653
+ * @throws If the field returns an invalid Legendre symbol value. {@link Error}
654
+ * @example
655
+ * Compute the Legendre symbol of one field element.
656
+ *
657
+ * ```ts
658
+ * import { Field, FpLegendre } from '@noble/curves/abstract/modular.js';
659
+ * const Fp = Field(17n);
660
+ * const symbol = FpLegendre(Fp, 4n);
661
+ * ```
350
662
  */
351
- export function FpLegendre<T>(Fp: IField<T>, n: T): -1 | 0 | 1 {
663
+ export function FpLegendre<T>(Fp: TArg<IField<T>>, n: T): -1 | 0 | 1 {
664
+ const F = Fp as IField<T>;
352
665
  // We can use 3rd argument as optional cache of this value
353
666
  // but seems unneeded for now. The operation is very fast.
354
- const p1mod2 = (Fp.ORDER - _1n) / _2n;
355
- const powered = Fp.pow(n, p1mod2);
356
- const yes = Fp.eql(powered, Fp.ONE);
357
- const zero = Fp.eql(powered, Fp.ZERO);
358
- const no = Fp.eql(powered, Fp.neg(Fp.ONE));
667
+ const p1mod2 = (F.ORDER - _1n) / _2n;
668
+ const powered = F.pow(n, p1mod2);
669
+ const yes = F.eql(powered, F.ONE);
670
+ const zero = F.eql(powered, F.ZERO);
671
+ const no = F.eql(powered, F.neg(F.ONE));
359
672
  if (!yes && !zero && !no) throw new Error('invalid Legendre symbol result');
360
673
  return yes ? 1 : zero ? 0 : -1;
361
674
  }
362
675
 
363
- // This function returns True whenever the value x is a square in the field F.
364
- export function FpIsSquare<T>(Fp: IField<T>, n: T): boolean {
365
- const l = FpLegendre(Fp, n);
366
- return l === 1;
676
+ /**
677
+ * @param Fp - Field implementation.
678
+ * @param n - Value to inspect.
679
+ * @returns `true` when `Fp.sqrt(n)` exists. This includes `0`, even though strict "quadratic
680
+ * residue" terminology often reserves that name for the non-zero square class.
681
+ * @throws If the field returns an invalid Legendre symbol value. {@link Error}
682
+ * @example
683
+ * Check whether one field element has a square root in the field.
684
+ *
685
+ * ```ts
686
+ * import { Field, FpIsSquare } from '@noble/curves/abstract/modular.js';
687
+ * const Fp = Field(17n);
688
+ * const isSquare = FpIsSquare(Fp, 4n);
689
+ * ```
690
+ */
691
+ export function FpIsSquare<T>(Fp: TArg<IField<T>>, n: T): boolean {
692
+ const l = FpLegendre(Fp as IField<T>, n);
693
+ // Zero is a square too: 0 = 0^2, and Fp.sqrt(0) already returns 0.
694
+ return l !== -1;
367
695
  }
368
696
 
369
- export type NLength = { nByteLength: number; nBitLength: number };
370
- // CURVE.n lengths
697
+ /** Byte and bit lengths derived from one scalar order. */
698
+ export type NLength = {
699
+ /** Canonical byte length. */
700
+ nByteLength: number;
701
+ /** Canonical bit length. */
702
+ nBitLength: number;
703
+ };
704
+ /**
705
+ * @param n - Curve order. Callers are expected to pass a positive order.
706
+ * @param nBitLength - Optional cached bit length. Callers are expected to pass a positive cached
707
+ * value when overriding the derived bit length.
708
+ * @returns Byte and bit lengths.
709
+ * @throws If the order or cached bit length is invalid. {@link Error}
710
+ * @example
711
+ * Measure the encoding sizes needed for one modulus.
712
+ *
713
+ * ```ts
714
+ * nLength(255n);
715
+ * ```
716
+ */
371
717
  export function nLength(n: bigint, nBitLength?: number): NLength {
372
718
  // Bit size, byte size of CURVE.n
373
719
  if (nBitLength !== undefined) anumber(nBitLength);
374
- const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
720
+ if (n <= _0n) throw new Error('invalid n length: expected positive n, got ' + n);
721
+ if (nBitLength !== undefined && nBitLength < 1)
722
+ throw new Error('invalid n length: expected positive bit length, got ' + nBitLength);
723
+ const bits = bitLen(n);
724
+ // Cached bit lengths smaller than ORDER would truncate serialized scalars/elements and poison
725
+ // any math that relies on the derived field metadata.
726
+ if (nBitLength !== undefined && nBitLength < bits)
727
+ throw new Error(`invalid n length: expected bit length (${bits}) >= n.length (${nBitLength})`);
728
+ const _nBitLength = nBitLength !== undefined ? nBitLength : bits;
375
729
  const nByteLength = Math.ceil(_nBitLength / 8);
376
730
  return { nBitLength: _nBitLength, nByteLength };
377
731
  }
@@ -382,9 +736,12 @@ type FieldOpts = Partial<{
382
736
  isLE: boolean;
383
737
  BITS: number;
384
738
  sqrt: SqrtFn;
385
- allowedLengths?: readonly number[]; // for P521 (adds padding for smaller sizes)
739
+ allowedLengths?: readonly number[]; // for P521 (adds padding for smaller sizes); must stay > 0
386
740
  modFromBytes: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
387
741
  }>;
742
+ // Keep the lazy sqrt cache off-instance so Field(...) can return a frozen object. Otherwise the
743
+ // cached helper write would keep the field surface externally mutable.
744
+ const FIELD_SQRT = new WeakMap<object, ReturnType<typeof FpSqrt>>();
388
745
  class _Field implements IField<bigint> {
389
746
  readonly ORDER: bigint;
390
747
  readonly BITS: number;
@@ -392,18 +749,23 @@ class _Field implements IField<bigint> {
392
749
  readonly isLE: boolean;
393
750
  readonly ZERO = _0n;
394
751
  readonly ONE = _1n;
395
- readonly _lengths?: number[];
396
- private _sqrt: ReturnType<typeof FpSqrt> | undefined; // cached sqrt
752
+ readonly _lengths?: readonly number[];
397
753
  private readonly _mod?: boolean;
398
754
  constructor(ORDER: bigint, opts: FieldOpts = {}) {
399
- if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
755
+ // ORDER <= 1 is degenerate: ONE would not be a valid field element and helpers like pow/inv
756
+ // would stop modeling field arithmetic.
757
+ if (ORDER <= _1n) throw new Error('invalid field: expected ORDER > 1, got ' + ORDER);
400
758
  let _nbitLength: number | undefined = undefined;
401
759
  this.isLE = false;
402
760
  if (opts != null && typeof opts === 'object') {
761
+ // Cached bit lengths are trusted here and should already be positive / consistent with ORDER.
403
762
  if (typeof opts.BITS === 'number') _nbitLength = opts.BITS;
404
- if (typeof opts.sqrt === 'function') this.sqrt = opts.sqrt;
763
+ if (typeof opts.sqrt === 'function')
764
+ // `_Field.prototype` is frozen below, so custom sqrt hooks must become own properties
765
+ // explicitly instead of relying on writable prototype shadowing via assignment.
766
+ Object.defineProperty(this, 'sqrt', { value: opts.sqrt, enumerable: true });
405
767
  if (typeof opts.isLE === 'boolean') this.isLE = opts.isLE;
406
- if (opts.allowedLengths) this._lengths = opts.allowedLengths?.slice();
768
+ if (opts.allowedLengths) this._lengths = Object.freeze(opts.allowedLengths.slice());
407
769
  if (typeof opts.modFromBytes === 'boolean') this._mod = opts.modFromBytes;
408
770
  }
409
771
  const { nBitLength, nByteLength } = nLength(ORDER, _nbitLength);
@@ -411,8 +773,7 @@ class _Field implements IField<bigint> {
411
773
  this.ORDER = ORDER;
412
774
  this.BITS = nBitLength;
413
775
  this.BYTES = nByteLength;
414
- this._sqrt = undefined;
415
- Object.preventExtensions(this);
776
+ Object.freeze(this);
416
777
  }
417
778
 
418
779
  create(num: bigint) {
@@ -420,7 +781,7 @@ class _Field implements IField<bigint> {
420
781
  }
421
782
  isValid(num: bigint) {
422
783
  if (typeof num !== 'bigint')
423
- throw new Error('invalid field element: expected bigint, got ' + typeof num);
784
+ throw new TypeError('invalid field element: expected bigint, got ' + typeof num);
424
785
  return _0n <= num && num < this.ORDER; // 0 is valid element, but it's not invertible
425
786
  }
426
787
  is0(num: bigint) {
@@ -477,18 +838,25 @@ class _Field implements IField<bigint> {
477
838
  return invert(num, this.ORDER);
478
839
  }
479
840
  sqrt(num: bigint): bigint {
480
- // Caching _sqrt speeds up sqrt9mod16 by 5x and tonneli-shanks by 10%
481
- if (!this._sqrt) this._sqrt = FpSqrt(this.ORDER);
482
- return this._sqrt(this, num);
841
+ // Caching sqrt helpers speeds up sqrt9mod16 by 5x and Tonelli-Shanks by about 10% without keeping
842
+ // the field instance itself mutable.
843
+ let sqrt = FIELD_SQRT.get(this);
844
+ if (!sqrt) FIELD_SQRT.set(this, (sqrt = FpSqrt(this.ORDER)));
845
+ return sqrt(this, num);
483
846
  }
484
847
  toBytes(num: bigint) {
848
+ // Serialize fixed-width limbs without re-validating the field range. Callers that need a
849
+ // canonical encoding must pass a valid element; some protocols intentionally serialize raw
850
+ // residues here and reduce or validate them elsewhere.
485
851
  return this.isLE ? numberToBytesLE(num, this.BYTES) : numberToBytesBE(num, this.BYTES);
486
852
  }
487
853
  fromBytes(bytes: Uint8Array, skipValidation = false) {
488
854
  abytes(bytes);
489
855
  const { _lengths: allowedLengths, BYTES, isLE, ORDER, _mod: modFromBytes } = this;
490
856
  if (allowedLengths) {
491
- if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
857
+ // `allowedLengths` must list real positive byte lengths; otherwise empty input would get
858
+ // padded into zero and silently decode as a field element.
859
+ if (bytes.length < 1 || !allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
492
860
  throw new Error(
493
861
  'Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length
494
862
  );
@@ -505,8 +873,8 @@ class _Field implements IField<bigint> {
505
873
  if (!skipValidation)
506
874
  if (!this.isValid(scalar))
507
875
  throw new Error('invalid field element: outside of range 0..ORDER');
508
- // NOTE: we don't validate scalar here, please use isValid. This done such way because some
509
- // protocol may allow non-reduced scalar that reduced later or changed some other way.
876
+ // Range validation is optional here because some protocols intentionally decode raw residues
877
+ // and reduce or validate them elsewhere.
510
878
  return scalar;
511
879
  }
512
880
  // TODO: we don't need it here, move out to separate fn
@@ -516,30 +884,43 @@ class _Field implements IField<bigint> {
516
884
  // We can't move this out because Fp6, Fp12 implement it
517
885
  // and it's unclear what to return in there.
518
886
  cmov(a: bigint, b: bigint, condition: boolean) {
887
+ // Field elements have `isValid(...)`; the CMOV branch bit is a direct runtime input, so reject
888
+ // non-boolean selectors here instead of letting JS truthiness silently change arithmetic.
889
+ abool(condition, 'condition');
519
890
  return condition ? b : a;
520
891
  }
521
892
  }
893
+ // Freeze the shared method surface too; otherwise callers can still poison every Field instance by
894
+ // monkey-patching `_Field.prototype` even if each instance is frozen.
895
+ Object.freeze(_Field.prototype);
522
896
 
523
897
  /**
524
898
  * Creates a finite field. Major performance optimizations:
525
899
  * * 1. Denormalized operations like mulN instead of mul.
526
900
  * * 2. Identical object shape: never add or remove keys.
527
- * * 3. `Object.freeze`.
901
+ * * 3. Frozen stable object shape; the lazy sqrt cache lives in a module-level `WeakMap`.
528
902
  * Fragile: always run a benchmark on a change.
529
- * Security note: operations don't check 'isValid' for all elements for performance reasons,
530
- * it is caller responsibility to check this.
903
+ * Security note: operations and low-level serializers like `toBytes` don't check `isValid` for
904
+ * all elements for performance and protocol-flexibility reasons; callers are responsible for
905
+ * supplying valid elements when they need canonical field behavior.
531
906
  * This is low-level code, please make sure you know what you're doing.
532
907
  *
533
908
  * Note about field properties:
534
909
  * * CHARACTERISTIC p = prime number, number of elements in main subgroup.
535
910
  * * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
536
911
  *
537
- * @param ORDER field order, probably prime, or could be composite
538
- * @param bitLen how many bits the field consumes
539
- * @param isLE (default: false) if encoding / decoding should be in little-endian
540
- * @param redef optional faster redefinitions of sqrt and other methods
912
+ * @param ORDER - field order, probably prime, or could be composite
913
+ * @param opts - Field options such as bit length or endianness. See {@link FieldOpts}.
914
+ * @returns Frozen field instance with a stable object shape. This wrapper forwards `opts` straight
915
+ * into `_Field`, so it inherits `_Field`'s assumptions about cached sizes and `allowedLengths`.
916
+ * @example
917
+ * Construct one prime field with optional overrides.
918
+ *
919
+ * ```ts
920
+ * Field(11n);
921
+ * ```
541
922
  */
542
- export function Field(ORDER: bigint, opts: FieldOpts = {}): Readonly<FpField> {
923
+ export function Field(ORDER: bigint, opts: FieldOpts = {}): TRet<Readonly<FpField>> {
543
924
  return new _Field(ORDER, opts);
544
925
  }
545
926
 
@@ -557,27 +938,69 @@ export function Field(ORDER: bigint, opts: FieldOpts = {}): Readonly<FpField> {
557
938
  // return reduced;
558
939
  // },
559
940
 
560
- export function FpSqrtOdd<T>(Fp: IField<T>, elm: T): T {
561
- if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
562
- const root = Fp.sqrt(elm);
563
- return Fp.isOdd(root) ? root : Fp.neg(root);
941
+ /**
942
+ * @param Fp - Field implementation.
943
+ * @param elm - Value to square-root.
944
+ * @returns Odd square root when two roots exist. The special case `elm = 0` still returns `0`,
945
+ * which is the only square root but is not odd.
946
+ * @throws If the field lacks oddness checks or the square root does not exist. {@link Error}
947
+ * @example
948
+ * Select the odd square root when two roots exist.
949
+ *
950
+ * ```ts
951
+ * import { Field, FpSqrtOdd } from '@noble/curves/abstract/modular.js';
952
+ * const Fp = Field(17n);
953
+ * const root = FpSqrtOdd(Fp, 4n);
954
+ * ```
955
+ */
956
+ export function FpSqrtOdd<T>(Fp: TArg<IField<T>>, elm: T): T {
957
+ const F = Fp as IField<T>;
958
+ if (!F.isOdd) throw new Error("Field doesn't have isOdd");
959
+ const root = F.sqrt(elm);
960
+ return F.isOdd(root) ? root : F.neg(root);
564
961
  }
565
962
 
566
- export function FpSqrtEven<T>(Fp: IField<T>, elm: T): T {
567
- if (!Fp.isOdd) throw new Error("Field doesn't have isOdd");
568
- const root = Fp.sqrt(elm);
569
- return Fp.isOdd(root) ? Fp.neg(root) : root;
963
+ /**
964
+ * @param Fp - Field implementation.
965
+ * @param elm - Value to square-root.
966
+ * @returns Even square root.
967
+ * @throws If the field lacks oddness checks or the square root does not exist. {@link Error}
968
+ * @example
969
+ * Select the even square root when two roots exist.
970
+ *
971
+ * ```ts
972
+ * import { Field, FpSqrtEven } from '@noble/curves/abstract/modular.js';
973
+ * const Fp = Field(17n);
974
+ * const root = FpSqrtEven(Fp, 4n);
975
+ * ```
976
+ */
977
+ export function FpSqrtEven<T>(Fp: TArg<IField<T>>, elm: T): T {
978
+ const F = Fp as IField<T>;
979
+ if (!F.isOdd) throw new Error("Field doesn't have isOdd");
980
+ const root = F.sqrt(elm);
981
+ return F.isOdd(root) ? F.neg(root) : root;
570
982
  }
571
983
 
572
984
  /**
573
985
  * Returns total number of bytes consumed by the field element.
574
986
  * For example, 32 bytes for usual 256-bit weierstrass curve.
575
- * @param fieldOrder number of field elements, usually CURVE.n
987
+ * @param fieldOrder - number of field elements, usually CURVE.n. Callers are expected to pass an
988
+ * order greater than 1.
576
989
  * @returns byte length of field
990
+ * @throws If the field order is not a bigint. {@link Error}
991
+ * @example
992
+ * Read the fixed-width byte length of one field.
993
+ *
994
+ * ```ts
995
+ * getFieldBytesLength(255n);
996
+ * ```
577
997
  */
578
998
  export function getFieldBytesLength(fieldOrder: bigint): number {
579
999
  if (typeof fieldOrder !== 'bigint') throw new Error('field order must be bigint');
580
- const bitLength = fieldOrder.toString(2).length;
1000
+ // Valid field elements are in 0..ORDER-1, so ORDER <= 1 would make the encoded range degenerate.
1001
+ if (fieldOrder <= _1n) throw new Error('field order must be greater than 1');
1002
+ // Valid field elements are < ORDER, so the maximal encoded element is ORDER - 1.
1003
+ const bitLength = bitLen(fieldOrder - _1n);
581
1004
  return Math.ceil(bitLength / 8);
582
1005
  }
583
1006
 
@@ -585,8 +1008,17 @@ export function getFieldBytesLength(fieldOrder: bigint): number {
585
1008
  * Returns minimal amount of bytes that can be safely reduced
586
1009
  * by field order.
587
1010
  * Should be 2^-128 for 128-bit curve such as P256.
588
- * @param fieldOrder number of field elements, usually CURVE.n
1011
+ * This is the reduction / modulo-bias lower bound; higher-level helpers may still impose a larger
1012
+ * absolute floor for policy reasons.
1013
+ * @param fieldOrder - number of field elements greater than 1, usually CURVE.n.
589
1014
  * @returns byte length of target hash
1015
+ * @throws If the field order is invalid. {@link Error}
1016
+ * @example
1017
+ * Compute the minimum hash length needed for field reduction.
1018
+ *
1019
+ * ```ts
1020
+ * getMinHashLength(255n);
1021
+ * ```
590
1022
  */
591
1023
  export function getMinHashLength(fieldOrder: bigint): number {
592
1024
  const length = getFieldBytesLength(fieldOrder);
@@ -597,22 +1029,37 @@ export function getMinHashLength(fieldOrder: bigint): number {
597
1029
  * "Constant-time" private key generation utility.
598
1030
  * Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
599
1031
  * and convert them into private scalar, with the modulo bias being negligible.
600
- * Needs at least 48 bytes of input for 32-byte private key.
601
- * https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
602
- * FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
603
- * RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
604
- * @param hash hash output from SHA3 or a similar function
605
- * @param groupOrder size of subgroup - (e.g. secp256k1.Point.Fn.ORDER)
606
- * @param isLE interpret hash bytes as LE num
1032
+ * Needs at least 48 bytes of input for 32-byte private key. The implementation also keeps a hard
1033
+ * 16-byte minimum even when `getMinHashLength(...)` is smaller, so toy-small inputs do not look
1034
+ * accidentally acceptable for real scalar derivation.
1035
+ * 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},
1036
+ * {@link https://csrc.nist.gov/publications/detail/fips/186/5/final | FIPS 186-5 appendix A.2}, and
1037
+ * {@link https://www.rfc-editor.org/rfc/rfc9380#section-5 | RFC 9380 section 5}. Unlike RFC 9380
1038
+ * `hash_to_field`, this helper intentionally maps into the non-zero private-scalar range `1..n-1`.
1039
+ * @param key - Uniform input bytes.
1040
+ * @param fieldOrder - Size of subgroup.
1041
+ * @param isLE - interpret hash bytes as LE num
607
1042
  * @returns valid private scalar
1043
+ * @throws If the hash length or field order is invalid for scalar reduction. {@link Error}
1044
+ * @example
1045
+ * Map hash output into a private scalar range.
1046
+ *
1047
+ * ```ts
1048
+ * mapHashToField(new Uint8Array(48).fill(1), 255n);
1049
+ * ```
608
1050
  */
609
- export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false): Uint8Array {
1051
+ export function mapHashToField(
1052
+ key: TArg<Uint8Array>,
1053
+ fieldOrder: bigint,
1054
+ isLE = false
1055
+ ): TRet<Uint8Array> {
610
1056
  abytes(key);
611
1057
  const len = key.length;
612
1058
  const fieldLen = getFieldBytesLength(fieldOrder);
613
- const minLen = getMinHashLength(fieldOrder);
614
- // No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
615
- if (len < 16 || len < minLen || len > 1024)
1059
+ const minLen = Math.max(getMinHashLength(fieldOrder), 16);
1060
+ // No toy-small inputs: the helper is for real scalar derivation, not tiny test curves. No huge
1061
+ // inputs: easier to reason about JS timing / allocation behavior.
1062
+ if (len < minLen || len > 1024)
616
1063
  throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
617
1064
  const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
618
1065
  // `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0