@noble/curves 1.9.0 → 1.9.2

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 (207) hide show
  1. package/README.md +78 -34
  2. package/_shortw_utils.d.ts +7 -5
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +2 -8
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +60 -24
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +158 -109
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +44 -9
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +99 -11
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +112 -25
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +141 -92
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/fft.d.ts +122 -0
  19. package/abstract/fft.d.ts.map +1 -0
  20. package/abstract/fft.js +438 -0
  21. package/abstract/fft.js.map +1 -0
  22. package/abstract/hash-to-curve.d.ts +25 -11
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +17 -14
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +28 -17
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +156 -139
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +3 -8
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +73 -93
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/poseidon.d.ts +5 -13
  35. package/abstract/poseidon.d.ts.map +1 -1
  36. package/abstract/poseidon.js +12 -7
  37. package/abstract/poseidon.js.map +1 -1
  38. package/abstract/tower.d.ts +20 -46
  39. package/abstract/tower.d.ts.map +1 -1
  40. package/abstract/tower.js +10 -4
  41. package/abstract/tower.js.map +1 -1
  42. package/abstract/utils.d.ts +1 -115
  43. package/abstract/utils.d.ts.map +1 -1
  44. package/abstract/utils.js +17 -371
  45. package/abstract/utils.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +152 -73
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +487 -404
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +2 -0
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +504 -480
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +2 -0
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +44 -32
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +25 -9
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +89 -65
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +29 -10
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +116 -81
  65. package/ed448.js.map +1 -1
  66. package/esm/_shortw_utils.d.ts +7 -5
  67. package/esm/_shortw_utils.d.ts.map +1 -1
  68. package/esm/_shortw_utils.js +2 -8
  69. package/esm/_shortw_utils.js.map +1 -1
  70. package/esm/abstract/bls.d.ts +60 -24
  71. package/esm/abstract/bls.d.ts.map +1 -1
  72. package/esm/abstract/bls.js +158 -109
  73. package/esm/abstract/bls.js.map +1 -1
  74. package/esm/abstract/curve.d.ts +44 -9
  75. package/esm/abstract/curve.d.ts.map +1 -1
  76. package/esm/abstract/curve.js +96 -12
  77. package/esm/abstract/curve.js.map +1 -1
  78. package/esm/abstract/edwards.d.ts +112 -25
  79. package/esm/abstract/edwards.d.ts.map +1 -1
  80. package/esm/abstract/edwards.js +141 -94
  81. package/esm/abstract/edwards.js.map +1 -1
  82. package/esm/abstract/fft.d.ts +122 -0
  83. package/esm/abstract/fft.d.ts.map +1 -0
  84. package/esm/abstract/fft.js +425 -0
  85. package/esm/abstract/fft.js.map +1 -0
  86. package/esm/abstract/hash-to-curve.d.ts +25 -11
  87. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  88. package/esm/abstract/hash-to-curve.js +17 -14
  89. package/esm/abstract/hash-to-curve.js.map +1 -1
  90. package/esm/abstract/modular.d.ts +28 -17
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +155 -138
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +3 -8
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +74 -94
  97. package/esm/abstract/montgomery.js.map +1 -1
  98. package/esm/abstract/poseidon.d.ts +5 -13
  99. package/esm/abstract/poseidon.d.ts.map +1 -1
  100. package/esm/abstract/poseidon.js +12 -7
  101. package/esm/abstract/poseidon.js.map +1 -1
  102. package/esm/abstract/tower.d.ts +20 -46
  103. package/esm/abstract/tower.d.ts.map +1 -1
  104. package/esm/abstract/tower.js +10 -4
  105. package/esm/abstract/tower.js.map +1 -1
  106. package/esm/abstract/utils.d.ts +1 -115
  107. package/esm/abstract/utils.d.ts.map +1 -1
  108. package/esm/abstract/utils.js +3 -344
  109. package/esm/abstract/utils.js.map +1 -1
  110. package/esm/abstract/weierstrass.d.ts +152 -73
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +485 -406
  113. package/esm/abstract/weierstrass.js.map +1 -1
  114. package/esm/bls12-381.d.ts +2 -0
  115. package/esm/bls12-381.d.ts.map +1 -1
  116. package/esm/bls12-381.js +503 -479
  117. package/esm/bls12-381.js.map +1 -1
  118. package/esm/bn254.d.ts +2 -0
  119. package/esm/bn254.d.ts.map +1 -1
  120. package/esm/bn254.js +41 -29
  121. package/esm/bn254.js.map +1 -1
  122. package/esm/ed25519.d.ts +25 -9
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +84 -60
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +29 -10
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +113 -78
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/jubjub.d.ts +4 -0
  131. package/esm/jubjub.d.ts.map +1 -1
  132. package/esm/jubjub.js +4 -0
  133. package/esm/jubjub.js.map +1 -1
  134. package/esm/misc.d.ts.map +1 -1
  135. package/esm/misc.js +31 -26
  136. package/esm/misc.js.map +1 -1
  137. package/esm/nist.d.ts +8 -16
  138. package/esm/nist.d.ts.map +1 -1
  139. package/esm/nist.js +87 -97
  140. package/esm/nist.js.map +1 -1
  141. package/esm/p256.d.ts +3 -3
  142. package/esm/p384.d.ts +3 -3
  143. package/esm/p521.d.ts +3 -3
  144. package/esm/pasta.d.ts +4 -0
  145. package/esm/pasta.d.ts.map +1 -1
  146. package/esm/pasta.js +4 -0
  147. package/esm/pasta.js.map +1 -1
  148. package/esm/secp256k1.d.ts +6 -6
  149. package/esm/secp256k1.d.ts.map +1 -1
  150. package/esm/secp256k1.js +44 -41
  151. package/esm/secp256k1.js.map +1 -1
  152. package/esm/utils.d.ts +96 -0
  153. package/esm/utils.d.ts.map +1 -0
  154. package/esm/utils.js +279 -0
  155. package/esm/utils.js.map +1 -0
  156. package/jubjub.d.ts +4 -0
  157. package/jubjub.d.ts.map +1 -1
  158. package/jubjub.js +4 -0
  159. package/jubjub.js.map +1 -1
  160. package/misc.d.ts.map +1 -1
  161. package/misc.js +35 -30
  162. package/misc.js.map +1 -1
  163. package/nist.d.ts +8 -16
  164. package/nist.d.ts.map +1 -1
  165. package/nist.js +87 -97
  166. package/nist.js.map +1 -1
  167. package/p256.d.ts +3 -3
  168. package/p384.d.ts +3 -3
  169. package/p521.d.ts +3 -3
  170. package/package.json +26 -8
  171. package/pasta.d.ts +4 -0
  172. package/pasta.d.ts.map +1 -1
  173. package/pasta.js +4 -0
  174. package/pasta.js.map +1 -1
  175. package/secp256k1.d.ts +6 -6
  176. package/secp256k1.d.ts.map +1 -1
  177. package/secp256k1.js +47 -44
  178. package/secp256k1.js.map +1 -1
  179. package/src/_shortw_utils.ts +5 -15
  180. package/src/abstract/bls.ts +260 -145
  181. package/src/abstract/curve.ts +125 -18
  182. package/src/abstract/edwards.ts +282 -127
  183. package/src/abstract/fft.ts +519 -0
  184. package/src/abstract/hash-to-curve.ts +51 -27
  185. package/src/abstract/modular.ts +156 -143
  186. package/src/abstract/montgomery.ts +81 -111
  187. package/src/abstract/poseidon.ts +22 -18
  188. package/src/abstract/tower.ts +37 -68
  189. package/src/abstract/utils.ts +3 -378
  190. package/src/abstract/weierstrass.ts +752 -461
  191. package/src/bls12-381.ts +542 -507
  192. package/src/bn254.ts +47 -35
  193. package/src/ed25519.ts +104 -76
  194. package/src/ed448.ts +156 -105
  195. package/src/jubjub.ts +4 -0
  196. package/src/misc.ts +39 -34
  197. package/src/nist.ts +138 -126
  198. package/src/p256.ts +3 -3
  199. package/src/p384.ts +3 -3
  200. package/src/p521.ts +3 -3
  201. package/src/pasta.ts +5 -1
  202. package/src/secp256k1.ts +59 -47
  203. package/src/utils.ts +328 -0
  204. package/utils.d.ts +96 -0
  205. package/utils.d.ts.map +1 -0
  206. package/utils.js +313 -0
  207. package/utils.js.map +1 -0
@@ -0,0 +1,519 @@
1
+ /**
2
+ * Experimental implementation of NTT / FFT (Fast Fourier Transform) over finite fields.
3
+ * API may change at any time. The code has not been audited. Feature requests are welcome.
4
+ * @module
5
+ */
6
+ import type { IField } from './modular.ts';
7
+
8
+ export interface MutableArrayLike<T> {
9
+ [index: number]: T;
10
+ length: number;
11
+ slice(start?: number, end?: number): this;
12
+ [Symbol.iterator](): Iterator<T>;
13
+ }
14
+
15
+ function checkU32(n: number) {
16
+ // 0xff_ff_ff_ff
17
+ if (!Number.isSafeInteger(n) || n < 0 || n > 0xffffffff)
18
+ throw new Error('wrong u32 integer:' + n);
19
+ return n;
20
+ }
21
+
22
+ /** Checks if integer is in form of `1 << X` */
23
+ export function isPowerOfTwo(x: number): boolean {
24
+ checkU32(x);
25
+ return (x & (x - 1)) === 0 && x !== 0;
26
+ }
27
+
28
+ export function nextPowerOfTwo(n: number): number {
29
+ checkU32(n);
30
+ if (n <= 1) return 1;
31
+ return (1 << (log2(n - 1) + 1)) >>> 0;
32
+ }
33
+
34
+ export function reverseBits(n: number, bits: number): number {
35
+ checkU32(n);
36
+ let reversed = 0;
37
+ for (let i = 0; i < bits; i++, n >>>= 1) reversed = (reversed << 1) | (n & 1);
38
+ return reversed;
39
+ }
40
+
41
+ /** Similar to `bitLen(x)-1` but much faster for small integers, like indices */
42
+ export function log2(n: number): number {
43
+ checkU32(n);
44
+ return 31 - Math.clz32(n);
45
+ }
46
+
47
+ /**
48
+ * Moves lowest bit to highest position, which at first step splits
49
+ * array on even and odd indices, then it applied again to each part,
50
+ * which is core of fft
51
+ */
52
+ export function bitReversalInplace<T extends MutableArrayLike<any>>(values: T): T {
53
+ const n = values.length;
54
+ if (n < 2 || !isPowerOfTwo(n))
55
+ throw new Error('n must be a power of 2 and greater than 1. Got ' + n);
56
+ const bits = log2(n);
57
+ for (let i = 0; i < n; i++) {
58
+ const j = reverseBits(i, bits);
59
+ if (i < j) {
60
+ const tmp = values[i];
61
+ values[i] = values[j];
62
+ values[j] = tmp;
63
+ }
64
+ }
65
+ return values;
66
+ }
67
+
68
+ export function bitReversalPermutation<T>(values: T[]): T[] {
69
+ return bitReversalInplace(values.slice()) as T[];
70
+ }
71
+
72
+ const _1n = /** @__PURE__ */ BigInt(1);
73
+ function findGenerator(field: IField<bigint>) {
74
+ let G = BigInt(2);
75
+ for (; field.eql(field.pow(G, field.ORDER >> _1n), field.ONE); G++);
76
+ return G;
77
+ }
78
+
79
+ export type RootsOfUnity = {
80
+ roots: (bits: number) => bigint[];
81
+ brp(bits: number): bigint[];
82
+ inverse(bits: number): bigint[];
83
+ omega: (bits: number) => bigint;
84
+ clear: () => void;
85
+ };
86
+ /** We limit roots up to 2**31, which is a lot: 2-billion polynomimal should be rare. */
87
+ export function rootsOfUnity(field: IField<bigint>, generator?: bigint): RootsOfUnity {
88
+ // Factor field.ORDER-1 as oddFactor * 2^powerOfTwo
89
+ let oddFactor = field.ORDER - _1n;
90
+ let powerOfTwo = 0;
91
+ for (; (oddFactor & _1n) !== _1n; powerOfTwo++, oddFactor >>= _1n);
92
+
93
+ // Find non quadratic residue
94
+ let G = generator !== undefined ? BigInt(generator) : findGenerator(field);
95
+ // Powers of generator
96
+ const omegas: bigint[] = new Array(powerOfTwo + 1);
97
+ omegas[powerOfTwo] = field.pow(G, oddFactor);
98
+ for (let i = powerOfTwo; i > 0; i--) omegas[i - 1] = field.sqr(omegas[i]);
99
+ // Compute all roots of unity for powers up to maxPower
100
+ const rootsCache: bigint[][] = [];
101
+ const checkBits = (bits: number) => {
102
+ checkU32(bits);
103
+ if (bits > 31 || bits > powerOfTwo)
104
+ throw new Error('rootsOfUnity: wrong bits ' + bits + ' powerOfTwo=' + powerOfTwo);
105
+ return bits;
106
+ };
107
+ const precomputeRoots = (maxPower: number) => {
108
+ checkBits(maxPower);
109
+ for (let power = maxPower; power >= 0; power--) {
110
+ if (rootsCache[power]) continue; // Skip if we've already computed roots for this power
111
+ const rootsAtPower: bigint[] = [];
112
+ for (let j = 0, cur = field.ONE; j < 2 ** power; j++, cur = field.mul(cur, omegas[power]))
113
+ rootsAtPower.push(cur);
114
+ rootsCache[power] = rootsAtPower;
115
+ }
116
+ return rootsCache[maxPower];
117
+ };
118
+ const brpCache = new Map<number, bigint[]>();
119
+ const inverseCache = new Map<number, bigint[]>();
120
+
121
+ // NOTE: we use bits instead of power, because power = 2**bits,
122
+ // but power is not neccesary isPowerOfTwo(power)!
123
+ return {
124
+ roots: (bits: number): bigint[] => {
125
+ const b = checkBits(bits);
126
+ return precomputeRoots(b);
127
+ },
128
+ brp(bits: number): bigint[] {
129
+ const b = checkBits(bits);
130
+ if (brpCache.has(b)) return brpCache.get(b)!;
131
+ else {
132
+ const res = bitReversalPermutation(this.roots(b));
133
+ brpCache.set(b, res);
134
+ return res;
135
+ }
136
+ },
137
+ inverse(bits: number): bigint[] {
138
+ const b = checkBits(bits);
139
+ if (inverseCache.has(b)) return inverseCache.get(b)!;
140
+ else {
141
+ const res = field.invertBatch(this.roots(b));
142
+ inverseCache.set(b, res);
143
+ return res;
144
+ }
145
+ },
146
+ omega: (bits: number): bigint => omegas[checkBits(bits)],
147
+ clear: (): void => {
148
+ rootsCache.splice(0, rootsCache.length);
149
+ brpCache.clear();
150
+ },
151
+ };
152
+ }
153
+
154
+ export type Polynomial<T> = MutableArrayLike<T>;
155
+
156
+ /**
157
+ * Maps great to Field<bigint>, but not to Group (EC points):
158
+ * - inv from scalar field
159
+ * - we need multiplyUnsafe here, instead of multiply for speed
160
+ * - multiplyUnsafe is safe in the context: we do mul(rootsOfUnity), which are public and sparse
161
+ */
162
+ export type FFTOpts<T, R> = {
163
+ add: (a: T, b: T) => T;
164
+ sub: (a: T, b: T) => T;
165
+ mul: (a: T, scalar: R) => T;
166
+ inv: (a: R) => R;
167
+ };
168
+
169
+ export type FFTCoreOpts<R> = {
170
+ N: number;
171
+ roots: Polynomial<R>;
172
+ dit: boolean;
173
+ invertButterflies?: boolean;
174
+ skipStages?: number;
175
+ brp?: boolean;
176
+ };
177
+
178
+ export type FFTCoreLoop<T> = <P extends Polynomial<T>>(values: P) => P;
179
+
180
+ /**
181
+ * Constructs different flavors of FFT. radix2 implementation of low level mutating API. Flavors:
182
+ *
183
+ * - DIT (Decimation-in-Time): Bottom-Up (leaves -> root), Cool-Turkey
184
+ * - DIF (Decimation-in-Frequency): Top-Down (root -> leaves), Gentleman–Sande
185
+ *
186
+ * DIT takes brp input, returns natural output.
187
+ * DIF takes natural input, returns brp output.
188
+ *
189
+ * The output is actually identical. Time / frequence distinction is not meaningful
190
+ * for Polynomial multiplication in fields.
191
+ * Which means if protocol supports/needs brp output/inputs, then we can skip this step.
192
+ *
193
+ * Cyclic NTT: Rq = Zq[x]/(x^n-1). butterfly_DIT+loop_DIT OR butterfly_DIF+loop_DIT, roots are omega
194
+ * Negacyclic NTT: Rq = Zq[x]/(x^n+1). butterfly_DIT+loop_DIF, at least for mlkem / mldsa
195
+ */
196
+ export const FFTCore = <T, R>(F: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FFTCoreLoop<T> => {
197
+ const { N, roots, dit, invertButterflies = false, skipStages = 0, brp = true } = coreOpts;
198
+ const bits = log2(N);
199
+ if (!isPowerOfTwo(N)) throw new Error('FFT: Polynomial size should be power of two');
200
+ const isDit = dit !== invertButterflies;
201
+ isDit;
202
+ return <P extends Polynomial<T>>(values: P): P => {
203
+ if (values.length !== N) throw new Error('FFT: wrong Polynomial length');
204
+ if (dit && brp) bitReversalInplace(values);
205
+ for (let i = 0, g = 1; i < bits - skipStages; i++) {
206
+ // For each stage s (sub-FFT length m = 2^s)
207
+ const s = dit ? i + 1 + skipStages : bits - i;
208
+ const m = 1 << s;
209
+ const m2 = m >> 1;
210
+ const stride = N >> s;
211
+ // Loop over each subarray of length m
212
+ for (let k = 0; k < N; k += m) {
213
+ // Loop over each butterfly within the subarray
214
+ for (let j = 0, grp = g++; j < m2; j++) {
215
+ const rootPos = invertButterflies ? (dit ? N - grp : grp) : j * stride;
216
+ const i0 = k + j;
217
+ const i1 = k + j + m2;
218
+ const omega = roots[rootPos];
219
+ const b = values[i1];
220
+ const a = values[i0];
221
+ // Inlining gives us 10% perf in kyber vs functions
222
+ if (isDit) {
223
+ const t = F.mul(b, omega); // Standard DIT butterfly
224
+ values[i0] = F.add(a, t);
225
+ values[i1] = F.sub(a, t);
226
+ } else if (invertButterflies) {
227
+ values[i0] = F.add(b, a); // DIT loop + inverted butterflies (Kyber decode)
228
+ values[i1] = F.mul(F.sub(b, a), omega);
229
+ } else {
230
+ values[i0] = F.add(a, b); // Standard DIF butterfly
231
+ values[i1] = F.mul(F.sub(a, b), omega);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ if (!dit && brp) bitReversalInplace(values);
237
+ return values;
238
+ };
239
+ };
240
+
241
+ export type FFTMethods<T> = {
242
+ direct<P extends Polynomial<T>>(values: P, brpInput?: boolean, brpOutput?: boolean): P;
243
+ inverse<P extends Polynomial<T>>(values: P, brpInput?: boolean, brpOutput?: boolean): P;
244
+ };
245
+
246
+ /**
247
+ * NTT aka FFT over finite field (NOT over complex numbers).
248
+ * Naming mirrors other libraries.
249
+ */
250
+ export function FFT<T>(roots: RootsOfUnity, opts: FFTOpts<T, bigint>): FFTMethods<T> {
251
+ const getLoop = (
252
+ N: number,
253
+ roots: Polynomial<bigint>,
254
+ brpInput = false,
255
+ brpOutput = false
256
+ ): (<P extends Polynomial<T>>(values: P) => P) => {
257
+ if (brpInput && brpOutput) {
258
+ // we cannot optimize this case, but lets support it anyway
259
+ return (values) =>
260
+ FFTCore(opts, { N, roots, dit: false, brp: false })(bitReversalInplace(values));
261
+ }
262
+ if (brpInput) return FFTCore(opts, { N, roots, dit: true, brp: false });
263
+ if (brpOutput) return FFTCore(opts, { N, roots, dit: false, brp: false });
264
+ return FFTCore(opts, { N, roots, dit: true, brp: true }); // all natural
265
+ };
266
+ return {
267
+ direct<P extends Polynomial<T>>(values: P, brpInput = false, brpOutput = false): P {
268
+ const N = values.length;
269
+ if (!isPowerOfTwo(N)) throw new Error('FFT: Polynomial size should be power of two');
270
+ const bits = log2(N);
271
+ return getLoop(N, roots.roots(bits), brpInput, brpOutput)<P>(values.slice());
272
+ },
273
+ inverse<P extends Polynomial<T>>(values: P, brpInput = false, brpOutput = false): P {
274
+ const N = values.length;
275
+ const bits = log2(N);
276
+ const res = getLoop(N, roots.inverse(bits), brpInput, brpOutput)(values.slice());
277
+ const ivm = opts.inv(BigInt(values.length)); // scale
278
+ // we can get brp output if we use dif instead of dit!
279
+ for (let i = 0; i < res.length; i++) res[i] = opts.mul(res[i], ivm);
280
+ // Allows to re-use non-inverted roots, but is VERY fragile
281
+ // return [res[0]].concat(res.slice(1).reverse());
282
+ // inverse calculated as pow(-1), which transforms into ω^{-kn} (-> reverses indices)
283
+ return res;
284
+ },
285
+ };
286
+ }
287
+
288
+ export type CreatePolyFn<P extends Polynomial<T>, T> = (len: number, elm?: T) => P;
289
+
290
+ export type PolyFn<P extends Polynomial<T>, T> = {
291
+ roots: RootsOfUnity;
292
+ create: CreatePolyFn<P, T>;
293
+ length?: number; // optional enforced size
294
+
295
+ degree: (a: P) => number;
296
+ extend: (a: P, len: number) => P;
297
+ add: (a: P, b: P) => P; // fc(x) = fa(x) + fb(x)
298
+ sub: (a: P, b: P) => P; // fc(x) = fa(x) - fb(x)
299
+ mul: (a: P, b: P | T) => P; // fc(x) = fa(x) * fb(x) OR fc(x) = fa(x) * scalar (same as field)
300
+ dot: (a: P, b: P) => P; // point-wise coeff multiplication
301
+ convolve: (a: P, b: P) => P;
302
+ shift: (p: P, factor: bigint) => P; // point-wise coeffcient shift
303
+ clone: (a: P) => P;
304
+ // Eval
305
+ eval: (a: P, basis: P) => T; // y = fc(x)
306
+ monomial: {
307
+ basis: (x: T, n: number) => P;
308
+ eval: (a: P, x: T) => T;
309
+ };
310
+ lagrange: {
311
+ basis: (x: T, n: number, brp?: boolean) => P;
312
+ eval: (a: P, x: T, brp?: boolean) => T;
313
+ };
314
+ // Complex
315
+ vanishing: (roots: P) => P; // f(x) = 0 for every x in roots
316
+ };
317
+
318
+ /**
319
+ * Poly wants a cracker.
320
+ *
321
+ * Polynomials are functions like `y=f(x)`, which means when we multiply two polynomials, result is
322
+ * function `f3(x) = f1(x) * f2(x)`, we don't multiply values. Key takeaways:
323
+ *
324
+ * - **Polynomial** is an array of coefficients: `f(x) = sum(coeff[i] * basis[i](x))`
325
+ * - **Basis** is array of functions
326
+ * - **Monominal** is Polynomial where `basis[i](x) == x**i` (powers)
327
+ * - **Array size** is domain size
328
+ * - **Lattice** is matrix (Polynomial of Polynomials)
329
+ */
330
+ export function poly<T>(
331
+ field: IField<T>,
332
+ roots: RootsOfUnity,
333
+ create?: undefined,
334
+ fft?: FFTMethods<T>,
335
+ length?: number
336
+ ): PolyFn<T[], T>;
337
+ export function poly<T, P extends Polynomial<T>>(
338
+ field: IField<T>,
339
+ roots: RootsOfUnity,
340
+ create: CreatePolyFn<P, T>,
341
+ fft?: FFTMethods<T>,
342
+ length?: number
343
+ ): PolyFn<P, T>;
344
+ export function poly<T, P extends Polynomial<T>>(
345
+ field: IField<T>,
346
+ roots: RootsOfUnity,
347
+ create?: CreatePolyFn<P, T>,
348
+ fft?: FFTMethods<T>,
349
+ length?: number
350
+ ): PolyFn<any, T> {
351
+ const F = field;
352
+ const _create =
353
+ create ||
354
+ (((len: number, elm?: T): Polynomial<T> => new Array(len).fill(elm ?? F.ZERO)) as CreatePolyFn<
355
+ P,
356
+ T
357
+ >);
358
+
359
+ const isPoly = (x: any): x is P => Array.isArray(x) || ArrayBuffer.isView(x);
360
+ const checkLength = (...lst: P[]): number => {
361
+ if (!lst.length) return 0;
362
+ for (const i of lst) if (!isPoly(i)) throw new Error('poly: not polynomial: ' + i);
363
+ const L = lst[0].length;
364
+ for (let i = 1; i < lst.length; i++)
365
+ if (lst[i].length !== L) throw new Error(`poly: mismatched lengths ${L} vs ${lst[i].length}`);
366
+ if (length !== undefined && L !== length)
367
+ throw new Error(`poly: expected fixed length ${length}, got ${L}`);
368
+ return L;
369
+ };
370
+ function findOmegaIndex(x: T, n: number, brp = false): number {
371
+ const bits = log2(n);
372
+ const omega = brp ? roots.brp(bits) : roots.roots(bits);
373
+ for (let i = 0; i < n; i++) if (F.eql(x, omega[i] as T)) return i;
374
+ return -1;
375
+ }
376
+ // TODO: mutating versions for mlkem/mldsa
377
+ return {
378
+ roots,
379
+ create: _create,
380
+ length,
381
+ extend: (a: P, len: number): P => {
382
+ checkLength(a);
383
+ const out = _create(len, F.ZERO);
384
+ for (let i = 0; i < a.length; i++) out[i] = a[i];
385
+ return out;
386
+ },
387
+ degree: (a: P): number => {
388
+ checkLength(a);
389
+ for (let i = a.length - 1; i >= 0; i--) if (!F.is0(a[i])) return i;
390
+ return -1;
391
+ },
392
+ add: (a: P, b: P): P => {
393
+ const len = checkLength(a, b);
394
+ const out = _create(len);
395
+ for (let i = 0; i < len; i++) out[i] = F.add(a[i], b[i]);
396
+ return out;
397
+ },
398
+ sub: (a: P, b: P): P => {
399
+ const len = checkLength(a, b);
400
+ const out = _create(len);
401
+ for (let i = 0; i < len; i++) out[i] = F.sub(a[i], b[i]);
402
+ return out;
403
+ },
404
+ dot: (a: P, b: P): P => {
405
+ const len = checkLength(a, b);
406
+ const out = _create(len);
407
+ for (let i = 0; i < len; i++) out[i] = F.mul(a[i], b[i]);
408
+ return out;
409
+ },
410
+ mul: (a: P, b: P | T): P => {
411
+ if (isPoly(b)) {
412
+ const len = checkLength(a, b);
413
+ if (fft) {
414
+ const A = fft.direct(a, false, true);
415
+ const B = fft.direct(b, false, true);
416
+ for (let i = 0; i < A.length; i++) A[i] = F.mul(A[i], B[i]);
417
+ return fft.inverse(A, true, false) as P;
418
+ } else {
419
+ // NOTE: this is quadratic and mostly for compat tests with FFT
420
+ const res = _create(len);
421
+ for (let i = 0; i < len; i++) {
422
+ for (let j = 0; j < len; j++) {
423
+ const k = (i + j) % len; // wrap mod length
424
+ res[k] = F.add(res[k], F.mul(a[i], b[j]));
425
+ }
426
+ }
427
+ return res;
428
+ }
429
+ } else {
430
+ const out = _create(checkLength(a));
431
+ for (let i = 0; i < out.length; i++) out[i] = F.mul(a[i], b);
432
+ return out;
433
+ }
434
+ },
435
+ convolve(a: P, b: P): P {
436
+ const len = nextPowerOfTwo(a.length + b.length - 1);
437
+ return this.mul(this.extend(a, len), this.extend(b, len));
438
+ },
439
+ shift(p: P, factor: bigint): P {
440
+ const out = _create(checkLength(p));
441
+ out[0] = p[0];
442
+ for (let i = 1, power = F.ONE; i < p.length; i++) {
443
+ power = F.mul(power, factor);
444
+ out[i] = F.mul(p[i], power);
445
+ }
446
+ return out;
447
+ },
448
+ clone: (a: P): P => {
449
+ checkLength(a);
450
+ const out = _create(a.length);
451
+ for (let i = 0; i < a.length; i++) out[i] = a[i];
452
+ return out;
453
+ },
454
+ eval: (a: P, basis: P): T => {
455
+ checkLength(a);
456
+ let acc = F.ZERO;
457
+ for (let i = 0; i < a.length; i++) acc = F.add(acc, F.mul(a[i], basis[i]));
458
+ return acc;
459
+ },
460
+ monomial: {
461
+ basis: (x: T, n: number): P => {
462
+ const out = _create(n);
463
+ let pow = F.ONE;
464
+ for (let i = 0; i < n; i++) {
465
+ out[i] = pow;
466
+ pow = F.mul(pow, x);
467
+ }
468
+ return out;
469
+ },
470
+ eval: (a: P, x: T): T => {
471
+ checkLength(a);
472
+ // Same as eval(a, monomialBasis(x, a.length)), but it is faster this way
473
+ let acc = F.ZERO;
474
+ for (let i = a.length - 1; i >= 0; i--) acc = F.add(F.mul(acc, x), a[i]);
475
+ return acc;
476
+ },
477
+ },
478
+ lagrange: {
479
+ basis: (x: T, n: number, brp = false, weights?: P): P => {
480
+ const bits = log2(n);
481
+ const cache = weights || brp ? roots.brp(bits) : roots.roots(bits); // [ω⁰, ω¹, ..., ωⁿ⁻¹]
482
+ const out = _create(n);
483
+ // Fast Kronecker-δ shortcut
484
+ const idx = findOmegaIndex(x, n, brp);
485
+ if (idx !== -1) {
486
+ out[idx] = F.ONE;
487
+ return out;
488
+ }
489
+ const tm = F.pow(x, BigInt(n));
490
+ const c = F.mul(F.sub(tm, F.ONE), F.inv(BigInt(n) as T)); // c = (xⁿ - 1)/n
491
+ const denom = _create(n);
492
+ for (let i = 0; i < n; i++) denom[i] = F.sub(x, cache[i] as T);
493
+ const inv = F.invertBatch(denom as any as T[]);
494
+ for (let i = 0; i < n; i++) out[i] = F.mul(c, F.mul(cache[i] as T, inv[i]));
495
+ return out;
496
+ },
497
+ eval(a: P, x: T, brp = false): T {
498
+ checkLength(a);
499
+ const idx = findOmegaIndex(x, a.length, brp);
500
+ if (idx !== -1) return a[idx]; // fast path
501
+ const L = this.basis(x, a.length, brp); // Lᵢ(x)
502
+ let acc = F.ZERO;
503
+ for (let i = 0; i < a.length; i++) if (!F.is0(a[i])) acc = F.add(acc, F.mul(a[i], L[i]));
504
+ return acc;
505
+ },
506
+ },
507
+ vanishing(roots: P): P {
508
+ checkLength(roots);
509
+ const out = _create(roots.length + 1, F.ZERO);
510
+ out[0] = F.ONE;
511
+ for (const r of roots) {
512
+ const neg = F.neg(r);
513
+ for (let j = out.length - 1; j > 0; j--) out[j] = F.add(F.mul(out[j], neg), out[j - 1]);
514
+ out[0] = F.mul(out[0], neg);
515
+ }
516
+ return out;
517
+ },
518
+ };
519
+ }
@@ -5,10 +5,18 @@
5
5
  * @module
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
+ import type { CHash } from '../utils.ts';
9
+ import {
10
+ _validateObject,
11
+ abytes,
12
+ bytesToNumberBE,
13
+ concatBytes,
14
+ isBytes,
15
+ isHash,
16
+ utf8ToBytes,
17
+ } from '../utils.ts';
8
18
  import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
9
19
  import { FpInvertBatch, type IField, mod } from './modular.ts';
10
- import type { CHash } from './utils.ts';
11
- import { abytes, bytesToNumberBE, concatBytes, utf8ToBytes, validateObject } from './utils.ts';
12
20
 
13
21
  export type UnicodeOrBytes = string | Uint8Array;
14
22
 
@@ -20,14 +28,20 @@ export type UnicodeOrBytes = string | Uint8Array;
20
28
  * * `expand` is `xmd` (SHA2, SHA3, BLAKE) or `xof` (SHAKE, BLAKE-XOF)
21
29
  * * `hash` conforming to `utils.CHash` interface, with `outputLen` / `blockLen` props
22
30
  */
23
- export type Opts = {
31
+ export type H2COpts = {
24
32
  DST: UnicodeOrBytes;
33
+ expand: 'xmd' | 'xof';
34
+ hash: CHash;
25
35
  p: bigint;
26
36
  m: number;
27
37
  k: number;
38
+ };
39
+ export type H2CHashOpts = {
28
40
  expand: 'xmd' | 'xof';
29
41
  hash: CHash;
30
42
  };
43
+ // todo: remove
44
+ export type Opts = H2COpts;
31
45
 
32
46
  // Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
33
47
  const os2ip = bytesToNumberBE;
@@ -133,15 +147,17 @@ export function expand_message_xof(
133
147
  * @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
134
148
  * @returns [u_0, ..., u_(count - 1)], a list of field elements.
135
149
  */
136
- export function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][] {
137
- validateObject(options, {
138
- DST: 'stringOrUint8Array',
150
+ export function hash_to_field(msg: Uint8Array, count: number, options: H2COpts): bigint[][] {
151
+ _validateObject(options, {
139
152
  p: 'bigint',
140
- m: 'isSafeInteger',
141
- k: 'isSafeInteger',
142
- hash: 'hash',
153
+ m: 'number',
154
+ k: 'number',
155
+ hash: 'function',
143
156
  });
144
157
  const { p, k, m, hash, expand, DST: _DST } = options;
158
+ if (!isBytes(_DST) && typeof _DST !== 'string')
159
+ throw new Error('DST must be string or uint8array');
160
+ if (!isHash(options.hash)) throw new Error('expected valid hash');
145
161
  abytes(msg);
146
162
  anum(count);
147
163
  const DST = typeof _DST === 'string' ? utf8ToBytes(_DST) : _DST;
@@ -209,21 +225,32 @@ export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
209
225
  // Separated from initialization opts, so users won't accidentally change per-curve parameters
210
226
  // (changing DST is ok!)
211
227
  export type htfBasicOpts = { DST: UnicodeOrBytes };
212
- export type HTFMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
228
+ export type H2CMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
229
+ // TODO: remove
230
+ export type HTFMethod<T> = H2CMethod<T>;
213
231
  export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
214
- export type Hasher<T> = {
215
- hashToCurve: HTFMethod<T>;
216
- encodeToCurve: HTFMethod<T>;
232
+ /**
233
+ * RFC 9380 methods, with cofactor clearing. See https://www.rfc-editor.org/rfc/rfc9380#section-3.
234
+ *
235
+ * * hashToCurve: `map(hash(input))`, encodes RANDOM bytes to curve (WITH hashing)
236
+ * * encodeToCurve: `map(hash(input))`, encodes NON-UNIFORM bytes to curve (WITH hashing)
237
+ * * mapToCurve: `map(scalars)`, encodes NON-UNIFORM scalars to curve (NO hashing)
238
+ */
239
+ export type H2CHasher<T> = {
240
+ hashToCurve: H2CMethod<T>;
241
+ encodeToCurve: H2CMethod<T>;
217
242
  mapToCurve: MapMethod<T>;
218
- defaults: Opts & { encodeDST?: UnicodeOrBytes };
243
+ defaults: H2COpts & { encodeDST?: UnicodeOrBytes };
219
244
  };
245
+ // TODO: remove
246
+ export type Hasher<T> = H2CHasher<T>;
220
247
 
221
- /** Creates hash-to-curve methods from EC Point and mapToCurve function. */
248
+ /** Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}. */
222
249
  export function createHasher<T>(
223
250
  Point: H2CPointConstructor<T>,
224
251
  mapToCurve: MapToCurve<T>,
225
- defaults: Opts & { encodeDST?: UnicodeOrBytes }
226
- ): Hasher<T> {
252
+ defaults: H2COpts & { encodeDST?: UnicodeOrBytes }
253
+ ): H2CHasher<T> {
227
254
  if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
228
255
  function map(num: bigint[]) {
229
256
  return Point.fromAffine(mapToCurve(num));
@@ -237,24 +264,21 @@ export function createHasher<T>(
237
264
 
238
265
  return {
239
266
  defaults,
240
-
241
- // Encodes byte string to elliptic curve.
242
- // hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
243
267
  hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
244
- const u = hash_to_field(msg, 2, { ...defaults, DST: defaults.DST, ...options } as Opts);
268
+ const dst = defaults.DST ? defaults.DST : {};
269
+ const opts = Object.assign({}, defaults, dst, options);
270
+ const u = hash_to_field(msg, 2, opts);
245
271
  const u0 = map(u[0]);
246
272
  const u1 = map(u[1]);
247
273
  return clear(u0.add(u1));
248
274
  },
249
-
250
- // Encodes byte string to elliptic curve.
251
- // encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
252
275
  encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
253
- const u = hash_to_field(msg, 1, { ...defaults, DST: defaults.encodeDST, ...options } as Opts);
276
+ const dst = defaults.encodeDST ? defaults.encodeDST : {};
277
+ const opts = Object.assign({}, defaults, dst, options);
278
+ const u = hash_to_field(msg, 1, opts);
254
279
  return clear(map(u[0]));
255
280
  },
256
-
257
- // Same as encodeToCurve, but without hash
281
+ /** See {@link H2CHasher} */
258
282
  mapToCurve(scalars: bigint[]): H2CPoint<T> {
259
283
  if (!Array.isArray(scalars)) throw new Error('expected array of bigints');
260
284
  for (const i of scalars)