@noble/curves 1.9.1 → 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 (189) hide show
  1. package/README.md +56 -25
  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 +86 -7
  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 +138 -102
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/fft.d.ts +12 -10
  19. package/abstract/fft.d.ts.map +1 -1
  20. package/abstract/fft.js +12 -13
  21. package/abstract/fft.js.map +1 -1
  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 +24 -11
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +49 -20
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +1 -1
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +5 -4
  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 +9 -3
  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 +132 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +462 -398
  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 -466
  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 +8 -5
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +67 -54
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +10 -6
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +80 -57
  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 +83 -8
  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 +138 -104
  81. package/esm/abstract/edwards.js.map +1 -1
  82. package/esm/abstract/fft.d.ts +12 -10
  83. package/esm/abstract/fft.d.ts.map +1 -1
  84. package/esm/abstract/fft.js +10 -11
  85. package/esm/abstract/fft.js.map +1 -1
  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 +24 -11
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +48 -19
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +1 -1
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +5 -4
  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 +9 -3
  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 +132 -76
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +460 -400
  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 -465
  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 +8 -5
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +62 -49
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +10 -6
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +74 -51
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/misc.d.ts.map +1 -1
  131. package/esm/misc.js +31 -26
  132. package/esm/misc.js.map +1 -1
  133. package/esm/nist.d.ts +7 -16
  134. package/esm/nist.d.ts.map +1 -1
  135. package/esm/nist.js +86 -97
  136. package/esm/nist.js.map +1 -1
  137. package/esm/p256.d.ts +3 -3
  138. package/esm/p384.d.ts +3 -3
  139. package/esm/p521.d.ts +3 -3
  140. package/esm/secp256k1.d.ts +6 -6
  141. package/esm/secp256k1.d.ts.map +1 -1
  142. package/esm/secp256k1.js +43 -40
  143. package/esm/secp256k1.js.map +1 -1
  144. package/esm/utils.d.ts +96 -0
  145. package/esm/utils.d.ts.map +1 -0
  146. package/esm/utils.js +279 -0
  147. package/esm/utils.js.map +1 -0
  148. package/misc.d.ts.map +1 -1
  149. package/misc.js +35 -30
  150. package/misc.js.map +1 -1
  151. package/nist.d.ts +7 -16
  152. package/nist.d.ts.map +1 -1
  153. package/nist.js +86 -97
  154. package/nist.js.map +1 -1
  155. package/p256.d.ts +3 -3
  156. package/p384.d.ts +3 -3
  157. package/p521.d.ts +3 -3
  158. package/package.json +14 -5
  159. package/secp256k1.d.ts +6 -6
  160. package/secp256k1.d.ts.map +1 -1
  161. package/secp256k1.js +46 -43
  162. package/secp256k1.js.map +1 -1
  163. package/src/_shortw_utils.ts +5 -15
  164. package/src/abstract/bls.ts +260 -145
  165. package/src/abstract/curve.ts +115 -13
  166. package/src/abstract/edwards.ts +279 -138
  167. package/src/abstract/fft.ts +30 -19
  168. package/src/abstract/hash-to-curve.ts +51 -27
  169. package/src/abstract/modular.ts +49 -28
  170. package/src/abstract/montgomery.ts +9 -7
  171. package/src/abstract/poseidon.ts +22 -18
  172. package/src/abstract/tower.ts +36 -67
  173. package/src/abstract/utils.ts +3 -378
  174. package/src/abstract/weierstrass.ts +700 -453
  175. package/src/bls12-381.ts +540 -489
  176. package/src/bn254.ts +47 -35
  177. package/src/ed25519.ts +80 -64
  178. package/src/ed448.ts +129 -92
  179. package/src/misc.ts +39 -34
  180. package/src/nist.ts +138 -127
  181. package/src/p256.ts +3 -3
  182. package/src/p384.ts +3 -3
  183. package/src/p521.ts +3 -3
  184. package/src/secp256k1.ts +58 -46
  185. package/src/utils.ts +328 -0
  186. package/utils.d.ts +96 -0
  187. package/utils.d.ts.map +1 -0
  188. package/utils.js +313 -0
  189. package/utils.js.map +1 -0
@@ -76,8 +76,15 @@ function findGenerator(field: IField<bigint>) {
76
76
  return G;
77
77
  }
78
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
+ };
79
86
  /** We limit roots up to 2**31, which is a lot: 2-billion polynomimal should be rare. */
80
- export function rootsOfUnity(field: IField<bigint>, generator?: bigint) {
87
+ export function rootsOfUnity(field: IField<bigint>, generator?: bigint): RootsOfUnity {
81
88
  // Factor field.ORDER-1 as oddFactor * 2^powerOfTwo
82
89
  let oddFactor = field.ORDER - _1n;
83
90
  let powerOfTwo = 0;
@@ -186,8 +193,7 @@ export type FFTCoreLoop<T> = <P extends Polynomial<T>>(values: P) => P;
186
193
  * Cyclic NTT: Rq = Zq[x]/(x^n-1). butterfly_DIT+loop_DIT OR butterfly_DIF+loop_DIT, roots are omega
187
194
  * Negacyclic NTT: Rq = Zq[x]/(x^n+1). butterfly_DIT+loop_DIF, at least for mlkem / mldsa
188
195
  */
189
- export const FFTCore = <T, R>(opts: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FFTCoreLoop<T> => {
190
- const { add, sub, mul } = opts; // inline to butteflies
196
+ export const FFTCore = <T, R>(F: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FFTCoreLoop<T> => {
191
197
  const { N, roots, dit, invertButterflies = false, skipStages = 0, brp = true } = coreOpts;
192
198
  const bits = log2(N);
193
199
  if (!isPowerOfTwo(N)) throw new Error('FFT: Polynomial size should be power of two');
@@ -214,15 +220,15 @@ export const FFTCore = <T, R>(opts: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FF
214
220
  const a = values[i0];
215
221
  // Inlining gives us 10% perf in kyber vs functions
216
222
  if (isDit) {
217
- const t = mul(b, omega); // Standard DIT butterfly
218
- values[i0] = add(a, t);
219
- values[i1] = sub(a, t);
223
+ const t = F.mul(b, omega); // Standard DIT butterfly
224
+ values[i0] = F.add(a, t);
225
+ values[i1] = F.sub(a, t);
220
226
  } else if (invertButterflies) {
221
- values[i0] = add(b, a); // DIT loop + inverted butterflies (Kyber decode)
222
- values[i1] = mul(sub(b, a), omega);
227
+ values[i0] = F.add(b, a); // DIT loop + inverted butterflies (Kyber decode)
228
+ values[i1] = F.mul(F.sub(b, a), omega);
223
229
  } else {
224
- values[i0] = add(a, b); // Standard DIF butterfly
225
- values[i1] = mul(sub(a, b), omega);
230
+ values[i0] = F.add(a, b); // Standard DIF butterfly
231
+ values[i1] = F.mul(F.sub(a, b), omega);
226
232
  }
227
233
  }
228
234
  }
@@ -232,11 +238,16 @@ export const FFTCore = <T, R>(opts: FFTOpts<T, R>, coreOpts: FFTCoreOpts<R>): FF
232
238
  };
233
239
  };
234
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
+
235
246
  /**
236
247
  * NTT aka FFT over finite field (NOT over complex numbers).
237
248
  * Naming mirrors other libraries.
238
249
  */
239
- export const FFT = <T>(roots: ReturnType<typeof rootsOfUnity>, opts: FFTOpts<T, bigint>) => {
250
+ export function FFT<T>(roots: RootsOfUnity, opts: FFTOpts<T, bigint>): FFTMethods<T> {
240
251
  const getLoop = (
241
252
  N: number,
242
253
  roots: Polynomial<bigint>,
@@ -272,12 +283,12 @@ export const FFT = <T>(roots: ReturnType<typeof rootsOfUnity>, opts: FFTOpts<T,
272
283
  return res;
273
284
  },
274
285
  };
275
- };
286
+ }
276
287
 
277
288
  export type CreatePolyFn<P extends Polynomial<T>, T> = (len: number, elm?: T) => P;
278
289
 
279
290
  export type PolyFn<P extends Polynomial<T>, T> = {
280
- roots: ReturnType<typeof rootsOfUnity>;
291
+ roots: RootsOfUnity;
281
292
  create: CreatePolyFn<P, T>;
282
293
  length?: number; // optional enforced size
283
294
 
@@ -318,23 +329,23 @@ export type PolyFn<P extends Polynomial<T>, T> = {
318
329
  */
319
330
  export function poly<T>(
320
331
  field: IField<T>,
321
- roots: ReturnType<typeof rootsOfUnity>,
332
+ roots: RootsOfUnity,
322
333
  create?: undefined,
323
- fft?: ReturnType<typeof FFT<T>>,
334
+ fft?: FFTMethods<T>,
324
335
  length?: number
325
336
  ): PolyFn<T[], T>;
326
337
  export function poly<T, P extends Polynomial<T>>(
327
338
  field: IField<T>,
328
- roots: ReturnType<typeof rootsOfUnity>,
339
+ roots: RootsOfUnity,
329
340
  create: CreatePolyFn<P, T>,
330
- fft?: ReturnType<typeof FFT<T>>,
341
+ fft?: FFTMethods<T>,
331
342
  length?: number
332
343
  ): PolyFn<P, T>;
333
344
  export function poly<T, P extends Polynomial<T>>(
334
345
  field: IField<T>,
335
- roots: ReturnType<typeof rootsOfUnity>,
346
+ roots: RootsOfUnity,
336
347
  create?: CreatePolyFn<P, T>,
337
- fft?: ReturnType<typeof FFT<T>>,
348
+ fft?: FFTMethods<T>,
338
349
  length?: number
339
350
  ): PolyFn<any, T> {
340
351
  const F = field;
@@ -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)
@@ -1,25 +1,26 @@
1
1
  /**
2
- * Utils for modular division and finite fields.
3
- * A finite field over 11 is integer number operations `mod 11`.
2
+ * Utils for modular division and fields.
3
+ * Field over 11 is a finite (Galois) field is integer number operations `mod 11`.
4
4
  * There is no division: it is replaced by modular multiplicative inverse.
5
5
  * @module
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
- import { anumber } from '@noble/hashes/utils';
9
8
  import {
9
+ _validateObject,
10
+ anumber,
10
11
  bitMask,
11
12
  bytesToNumberBE,
12
13
  bytesToNumberLE,
13
14
  ensureBytes,
14
15
  numberToBytesBE,
15
16
  numberToBytesLE,
16
- validateObject,
17
- } from './utils.ts';
17
+ } from '../utils.ts';
18
18
 
19
19
  // prettier-ignore
20
20
  const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
21
21
  // prettier-ignore
22
- const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _8n = /* @__PURE__ */ BigInt(8);
22
+ const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5);
23
+ const _8n = /* @__PURE__ */ BigInt(8);
23
24
 
24
25
  // Calculates a modulo b
25
26
  export function mod(a: bigint, b: bigint): bigint {
@@ -29,7 +30,6 @@ export function mod(a: bigint, b: bigint): bigint {
29
30
  /**
30
31
  * Efficiently raise num to power and do modular division.
31
32
  * Unsafe in some contexts: uses ladder, so can expose bigint bits.
32
- * TODO: remove.
33
33
  * @example
34
34
  * pow(2n, 6n, 11n) // 64n % 11n == 9n
35
35
  */
@@ -128,6 +128,7 @@ function sqrt5mod8<T>(Fp: IField<T>, n: T) {
128
128
  */
129
129
  export function tonelliShanks(P: bigint): <T>(Fp: IField<T>, n: T) => T {
130
130
  // Initialization (precomputation).
131
+ // Caching initialization could boost perf by 7%.
131
132
  if (P < BigInt(3)) throw new Error('sqrt is not defined for small field');
132
133
  // Factor P - 1 = Q * 2^S, where Q is odd
133
134
  let Q = P - _1n;
@@ -228,6 +229,7 @@ export interface IField<T> {
228
229
  create: (num: T) => T;
229
230
  isValid: (num: T) => boolean;
230
231
  is0: (num: T) => boolean;
232
+ isValidNot0: (num: T) => boolean;
231
233
  neg(num: T): T;
232
234
  inv(num: T): T;
233
235
  sqrt(num: T): T;
@@ -267,14 +269,18 @@ export function validateField<T>(field: IField<T>): IField<T> {
267
269
  const initial = {
268
270
  ORDER: 'bigint',
269
271
  MASK: 'bigint',
270
- BYTES: 'isSafeInteger',
271
- BITS: 'isSafeInteger',
272
+ BYTES: 'number',
273
+ BITS: 'number',
272
274
  } as Record<string, string>;
273
275
  const opts = FIELD_FIELDS.reduce((map, val: string) => {
274
276
  map[val] = 'function';
275
277
  return map;
276
278
  }, initial);
277
- return validateObject(field, opts);
279
+ _validateObject(field, opts);
280
+ // const max = 16384;
281
+ // if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
282
+ // if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
283
+ return field;
278
284
  }
279
285
 
280
286
  // Generic field functions
@@ -353,14 +359,9 @@ export function FpIsSquare<T>(Fp: IField<T>, n: T): boolean {
353
359
  return l === 1;
354
360
  }
355
361
 
362
+ export type NLength = { nByteLength: number; nBitLength: number };
356
363
  // CURVE.n lengths
357
- export function nLength(
358
- n: bigint,
359
- nBitLength?: number
360
- ): {
361
- nBitLength: number;
362
- nByteLength: number;
363
- } {
364
+ export function nLength(n: bigint, nBitLength?: number): NLength {
364
365
  // Bit size, byte size of CURVE.n
365
366
  if (nBitLength !== undefined) anumber(nBitLength);
366
367
  const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
@@ -369,29 +370,47 @@ export function nLength(
369
370
  }
370
371
 
371
372
  type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
373
+ type SqrtFn = (n: bigint) => bigint;
374
+ type FieldOpts = Partial<{ sqrt: SqrtFn; isLE: boolean; BITS: number }>;
372
375
  /**
373
- * Initializes a finite field over prime.
374
- * Major performance optimizations:
375
- * * a) denormalized operations like mulN instead of mul
376
- * * b) same object shape: never add or remove keys
377
- * * c) Object.freeze
376
+ * Creates a finite field. Major performance optimizations:
377
+ * * 1. Denormalized operations like mulN instead of mul.
378
+ * * 2. Identical object shape: never add or remove keys.
379
+ * * 3. `Object.freeze`.
378
380
  * Fragile: always run a benchmark on a change.
379
381
  * Security note: operations don't check 'isValid' for all elements for performance reasons,
380
382
  * it is caller responsibility to check this.
381
383
  * This is low-level code, please make sure you know what you're doing.
382
- * @param ORDER prime positive bigint
384
+ *
385
+ * Note about field properties:
386
+ * * CHARACTERISTIC p = prime number, number of elements in main subgroup.
387
+ * * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
388
+ *
389
+ * @param ORDER field order, probably prime, or could be composite
383
390
  * @param bitLen how many bits the field consumes
384
- * @param isLE (def: false) if encoding / decoding should be in little-endian
391
+ * @param isLE (default: false) if encoding / decoding should be in little-endian
385
392
  * @param redef optional faster redefinitions of sqrt and other methods
386
393
  */
387
394
  export function Field(
388
395
  ORDER: bigint,
389
- bitLen?: number,
396
+ bitLenOrOpts?: number | FieldOpts,
390
397
  isLE = false,
391
- redef: Partial<IField<bigint>> = {}
398
+ opts: { sqrt?: SqrtFn } = {}
392
399
  ): Readonly<FpField> {
393
400
  if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
394
- const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, bitLen);
401
+ let _nbitLength: number | undefined = undefined;
402
+ let _sqrt: SqrtFn | undefined = undefined;
403
+ if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
404
+ if (opts.sqrt || isLE) throw new Error('cannot specify opts in two arguments');
405
+ const _opts = bitLenOrOpts;
406
+ if (_opts.BITS) _nbitLength = _opts.BITS;
407
+ if (_opts.sqrt) _sqrt = _opts.sqrt;
408
+ if (typeof _opts.isLE === 'boolean') isLE = _opts.isLE;
409
+ } else {
410
+ if (typeof bitLenOrOpts === 'number') _nbitLength = bitLenOrOpts;
411
+ if (opts.sqrt) _sqrt = opts.sqrt;
412
+ }
413
+ const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
395
414
  if (BYTES > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes');
396
415
  let sqrtP: ReturnType<typeof FpSqrt>; // cached sqrtP
397
416
  const f: Readonly<FpField> = Object.freeze({
@@ -409,6 +428,8 @@ export function Field(
409
428
  return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
410
429
  },
411
430
  is0: (num) => num === _0n,
431
+ // is valid and invertible
432
+ isValidNot0: (num: bigint) => !f.is0(num) && f.isValid(num),
412
433
  isOdd: (num) => (num & _1n) === _1n,
413
434
  neg: (num) => mod(-num, ORDER),
414
435
  eql: (lhs, rhs) => lhs === rhs,
@@ -428,7 +449,7 @@ export function Field(
428
449
 
429
450
  inv: (num) => invert(num, ORDER),
430
451
  sqrt:
431
- redef.sqrt ||
452
+ _sqrt ||
432
453
  ((n) => {
433
454
  if (!sqrtP) sqrtP = FpSqrt(ORDER);
434
455
  return sqrtP(f, n);
@@ -5,14 +5,15 @@
5
5
  * @module
6
6
  */
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
- import { mod } from './modular.ts';
9
8
  import {
9
+ _validateObject,
10
10
  aInRange,
11
11
  bytesToNumberLE,
12
12
  ensureBytes,
13
13
  numberToBytesLE,
14
- validateObject,
15
- } from './utils.ts';
14
+ randomBytes,
15
+ } from '../utils.ts';
16
+ import { mod } from './modular.ts';
16
17
 
17
18
  const _0n = BigInt(0);
18
19
  const _1n = BigInt(1);
@@ -24,7 +25,7 @@ export type CurveType = {
24
25
  type: 'x25519' | 'x448';
25
26
  adjustScalarBytes: (bytes: Uint8Array) => Uint8Array;
26
27
  powPminus2: (x: bigint) => bigint;
27
- randomBytes: (bytesLength?: number) => Uint8Array;
28
+ randomBytes?: (bytesLength?: number) => Uint8Array;
28
29
  };
29
30
 
30
31
  export type CurveFn = {
@@ -37,7 +38,7 @@ export type CurveFn = {
37
38
  };
38
39
 
39
40
  function validateOpts(curve: CurveType) {
40
- validateObject(curve, {
41
+ _validateObject(curve, {
41
42
  adjustScalarBytes: 'function',
42
43
  powPminus2: 'function',
43
44
  });
@@ -46,9 +47,10 @@ function validateOpts(curve: CurveType) {
46
47
 
47
48
  export function montgomery(curveDef: CurveType): CurveFn {
48
49
  const CURVE = validateOpts(curveDef);
49
- const { P, type, adjustScalarBytes, powPminus2 } = CURVE;
50
+ const { P, type, adjustScalarBytes, powPminus2, randomBytes: rand } = CURVE;
50
51
  const is25519 = type === 'x25519';
51
52
  if (!is25519 && type !== 'x448') throw new Error('invalid type');
53
+ const randomBytes_ = rand || randomBytes;
52
54
 
53
55
  const montgomeryBits = is25519 ? 255 : 448;
54
56
  const fieldLen = is25519 ? 32 : 56;
@@ -159,7 +161,7 @@ export function montgomery(curveDef: CurveType): CurveFn {
159
161
  scalarMultBase,
160
162
  getSharedSecret: (privateKey: Hex, publicKey: Hex) => scalarMult(privateKey, publicKey),
161
163
  getPublicKey: (privateKey: Hex): Uint8Array => scalarMultBase(privateKey),
162
- utils: { randomPrivateKey: () => CURVE.randomBytes!(fieldLen) },
164
+ utils: { randomPrivateKey: () => randomBytes_(fieldLen) },
163
165
  GuBytes: GuBytes.slice(),
164
166
  };
165
167
  }
@@ -7,8 +7,8 @@
7
7
  * @module
8
8
  */
9
9
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
10
+ import { _validateObject, bitGet } from '../utils.ts';
10
11
  import { FpInvertBatch, FpPow, type IField, validateField } from './modular.ts';
11
- import { bitGet } from './utils.ts';
12
12
 
13
13
  // Grain LFSR (Linear-Feedback Shift Register): https://eprint.iacr.org/2009/109.pdf
14
14
  function grainLFSR(state: number[]): () => boolean {
@@ -41,20 +41,28 @@ export type PoseidonBasicOpts = {
41
41
  isSboxInverse?: boolean;
42
42
  };
43
43
 
44
- function validateBasicOpts(opts: PoseidonBasicOpts) {
44
+ function assertValidPosOpts(opts: PoseidonBasicOpts) {
45
45
  const { Fp, roundsFull } = opts;
46
46
  validateField(Fp);
47
+ _validateObject(
48
+ opts,
49
+ {
50
+ t: 'number',
51
+ roundsFull: 'number',
52
+ roundsPartial: 'number',
53
+ },
54
+ {
55
+ isSboxInverse: 'boolean',
56
+ }
57
+ );
47
58
  for (const i of ['t', 'roundsFull', 'roundsPartial'] as const) {
48
- if (typeof opts[i] !== 'number' || !Number.isSafeInteger(opts[i]))
49
- throw new Error('invalid number ' + i);
59
+ if (!Number.isSafeInteger(opts[i]) || opts[i] < 1) throw new Error('invalid number ' + i);
50
60
  }
51
- if (opts.isSboxInverse !== undefined && typeof opts.isSboxInverse !== 'boolean')
52
- throw new Error(`Poseidon: invalid param isSboxInverse=${opts.isSboxInverse}`);
53
61
  if (roundsFull & 1) throw new Error('roundsFull is not even' + roundsFull);
54
62
  }
55
63
 
56
64
  function poseidonGrain(opts: PoseidonBasicOpts) {
57
- validateBasicOpts(opts);
65
+ assertValidPosOpts(opts);
58
66
  const { Fp } = opts;
59
67
  const state = Array(80).fill(1);
60
68
  let pos = 0;
@@ -140,7 +148,7 @@ export function validateOpts(opts: PoseidonOpts): Readonly<{
140
148
  sboxPower?: number;
141
149
  reversePartialPowIdx?: boolean; // Hack for stark
142
150
  }> {
143
- validateBasicOpts(opts);
151
+ assertValidPosOpts(opts);
144
152
  const { Fp, mds, reversePartialPowIdx: rev, roundConstants: rc } = opts;
145
153
  const { roundsFull, roundsPartial, sboxPower, t } = opts;
146
154
 
@@ -196,12 +204,13 @@ export function splitConstants(rc: bigint[], t: number): bigint[][] {
196
204
  return res;
197
205
  }
198
206
 
199
- /** Poseidon NTT-friendly hash. */
200
- export function poseidon(opts: PoseidonOpts): {
207
+ export type PoseidonFn = {
201
208
  (values: bigint[]): bigint[];
202
209
  // For verification in tests
203
210
  roundConstants: bigint[][];
204
- } {
211
+ };
212
+ /** Poseidon NTT-friendly hash. */
213
+ export function poseidon(opts: PoseidonOpts): PoseidonFn {
205
214
  const _opts = validateOpts(opts);
206
215
  const { Fp, mds, roundConstants, rounds: totalRounds, roundsPartial, sboxFn, t } = _opts;
207
216
  const halfRoundsFull = _opts.roundsFull / 2;
@@ -242,17 +251,12 @@ export class PoseidonSponge {
242
251
  private Fp: IField<bigint>;
243
252
  readonly rate: number;
244
253
  readonly capacity: number;
245
- readonly hash: ReturnType<typeof poseidon>;
254
+ readonly hash: PoseidonFn;
246
255
  private state: bigint[]; // [...capacity, ...rate]
247
256
  private pos = 0;
248
257
  private isAbsorbing = true;
249
258
 
250
- constructor(
251
- Fp: IField<bigint>,
252
- rate: number,
253
- capacity: number,
254
- hash: ReturnType<typeof poseidon>
255
- ) {
259
+ constructor(Fp: IField<bigint>, rate: number, capacity: number, hash: PoseidonFn) {
256
260
  this.Fp = Fp;
257
261
  this.hash = hash;
258
262
  this.rate = rate;