@noble/curves 1.9.5 → 2.0.0-beta.1

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 (211) hide show
  1. package/README.md +267 -421
  2. package/abstract/bls.d.ts +49 -111
  3. package/abstract/bls.d.ts.map +1 -1
  4. package/abstract/bls.js +108 -152
  5. package/abstract/bls.js.map +1 -1
  6. package/abstract/curve.d.ts +7 -48
  7. package/abstract/curve.d.ts.map +1 -1
  8. package/abstract/curve.js +22 -47
  9. package/abstract/curve.js.map +1 -1
  10. package/abstract/edwards.d.ts +17 -68
  11. package/abstract/edwards.d.ts.map +1 -1
  12. package/abstract/edwards.js +98 -175
  13. package/abstract/edwards.js.map +1 -1
  14. package/abstract/fft.js +14 -27
  15. package/abstract/fft.js.map +1 -1
  16. package/abstract/hash-to-curve.d.ts +11 -24
  17. package/abstract/hash-to-curve.d.ts.map +1 -1
  18. package/abstract/hash-to-curve.js +30 -35
  19. package/abstract/hash-to-curve.js.map +1 -1
  20. package/abstract/modular.d.ts +5 -17
  21. package/abstract/modular.d.ts.map +1 -1
  22. package/abstract/modular.js +166 -167
  23. package/abstract/modular.js.map +1 -1
  24. package/abstract/montgomery.d.ts +4 -9
  25. package/abstract/montgomery.d.ts.map +1 -1
  26. package/abstract/montgomery.js +17 -20
  27. package/abstract/montgomery.js.map +1 -1
  28. package/abstract/oprf.d.ts +282 -0
  29. package/abstract/oprf.d.ts.map +1 -0
  30. package/abstract/oprf.js +297 -0
  31. package/abstract/oprf.js.map +1 -0
  32. package/abstract/poseidon.js +20 -24
  33. package/abstract/poseidon.js.map +1 -1
  34. package/abstract/tower.d.ts +9 -7
  35. package/abstract/tower.d.ts.map +1 -1
  36. package/abstract/tower.js +600 -364
  37. package/abstract/tower.js.map +1 -1
  38. package/abstract/weierstrass.d.ts +12 -145
  39. package/abstract/weierstrass.d.ts.map +1 -1
  40. package/abstract/weierstrass.js +153 -377
  41. package/abstract/weierstrass.js.map +1 -1
  42. package/bls12-381.d.ts +2 -2
  43. package/bls12-381.d.ts.map +1 -1
  44. package/bls12-381.js +174 -216
  45. package/bls12-381.js.map +1 -1
  46. package/bn254.d.ts +58 -10
  47. package/bn254.d.ts.map +1 -1
  48. package/bn254.js +70 -130
  49. package/bn254.js.map +1 -1
  50. package/ed25519.d.ts +12 -31
  51. package/ed25519.d.ts.map +1 -1
  52. package/ed25519.js +104 -146
  53. package/ed25519.js.map +1 -1
  54. package/ed448.d.ts +14 -33
  55. package/ed448.d.ts.map +1 -1
  56. package/ed448.js +105 -132
  57. package/ed448.js.map +1 -1
  58. package/index.js +1 -1
  59. package/misc.d.ts +10 -14
  60. package/misc.d.ts.map +1 -1
  61. package/misc.js +51 -60
  62. package/misc.js.map +1 -1
  63. package/nist.d.ts +11 -14
  64. package/nist.d.ts.map +1 -1
  65. package/nist.js +46 -55
  66. package/nist.js.map +1 -1
  67. package/package.json +9 -224
  68. package/secp256k1.d.ts +7 -23
  69. package/secp256k1.d.ts.map +1 -1
  70. package/secp256k1.js +72 -83
  71. package/secp256k1.js.map +1 -1
  72. package/src/abstract/bls.ts +197 -344
  73. package/src/abstract/curve.ts +10 -83
  74. package/src/abstract/edwards.ts +96 -223
  75. package/src/abstract/hash-to-curve.ts +32 -45
  76. package/src/abstract/modular.ts +144 -130
  77. package/src/abstract/montgomery.ts +21 -22
  78. package/src/abstract/oprf.ts +600 -0
  79. package/src/abstract/tower.ts +627 -382
  80. package/src/abstract/weierstrass.ts +101 -482
  81. package/src/bls12-381.ts +148 -176
  82. package/src/bn254.ts +67 -122
  83. package/src/ed25519.ts +65 -118
  84. package/src/ed448.ts +63 -113
  85. package/src/index.ts +1 -1
  86. package/src/misc.ts +66 -49
  87. package/src/nist.ts +48 -57
  88. package/src/secp256k1.ts +56 -88
  89. package/src/utils.ts +41 -61
  90. package/src/webcrypto.ts +362 -0
  91. package/utils.d.ts +28 -19
  92. package/utils.d.ts.map +1 -1
  93. package/utils.js +45 -121
  94. package/utils.js.map +1 -1
  95. package/webcrypto.d.ts +47 -0
  96. package/webcrypto.d.ts.map +1 -0
  97. package/webcrypto.js +231 -0
  98. package/webcrypto.js.map +1 -0
  99. package/esm/_shortw_utils.d.ts +0 -19
  100. package/esm/_shortw_utils.d.ts.map +0 -1
  101. package/esm/_shortw_utils.js +0 -16
  102. package/esm/_shortw_utils.js.map +0 -1
  103. package/esm/abstract/bls.d.ts +0 -190
  104. package/esm/abstract/bls.d.ts.map +0 -1
  105. package/esm/abstract/bls.js +0 -408
  106. package/esm/abstract/bls.js.map +0 -1
  107. package/esm/abstract/curve.d.ts +0 -231
  108. package/esm/abstract/curve.d.ts.map +0 -1
  109. package/esm/abstract/curve.js +0 -465
  110. package/esm/abstract/curve.js.map +0 -1
  111. package/esm/abstract/edwards.d.ts +0 -237
  112. package/esm/abstract/edwards.d.ts.map +0 -1
  113. package/esm/abstract/edwards.js +0 -632
  114. package/esm/abstract/edwards.js.map +0 -1
  115. package/esm/abstract/fft.d.ts +0 -122
  116. package/esm/abstract/fft.d.ts.map +0 -1
  117. package/esm/abstract/fft.js +0 -425
  118. package/esm/abstract/fft.js.map +0 -1
  119. package/esm/abstract/hash-to-curve.d.ts +0 -102
  120. package/esm/abstract/hash-to-curve.d.ts.map +0 -1
  121. package/esm/abstract/hash-to-curve.js +0 -203
  122. package/esm/abstract/hash-to-curve.js.map +0 -1
  123. package/esm/abstract/modular.d.ts +0 -171
  124. package/esm/abstract/modular.d.ts.map +0 -1
  125. package/esm/abstract/modular.js +0 -530
  126. package/esm/abstract/modular.js.map +0 -1
  127. package/esm/abstract/montgomery.d.ts +0 -30
  128. package/esm/abstract/montgomery.d.ts.map +0 -1
  129. package/esm/abstract/montgomery.js +0 -157
  130. package/esm/abstract/montgomery.js.map +0 -1
  131. package/esm/abstract/poseidon.d.ts +0 -68
  132. package/esm/abstract/poseidon.d.ts.map +0 -1
  133. package/esm/abstract/poseidon.js +0 -296
  134. package/esm/abstract/poseidon.js.map +0 -1
  135. package/esm/abstract/tower.d.ts +0 -93
  136. package/esm/abstract/tower.d.ts.map +0 -1
  137. package/esm/abstract/tower.js +0 -502
  138. package/esm/abstract/tower.js.map +0 -1
  139. package/esm/abstract/utils.d.ts +0 -5
  140. package/esm/abstract/utils.d.ts.map +0 -1
  141. package/esm/abstract/utils.js +0 -7
  142. package/esm/abstract/utils.js.map +0 -1
  143. package/esm/abstract/weierstrass.d.ts +0 -412
  144. package/esm/abstract/weierstrass.d.ts.map +0 -1
  145. package/esm/abstract/weierstrass.js +0 -1428
  146. package/esm/abstract/weierstrass.js.map +0 -1
  147. package/esm/bls12-381.d.ts +0 -16
  148. package/esm/bls12-381.d.ts.map +0 -1
  149. package/esm/bls12-381.js +0 -738
  150. package/esm/bls12-381.js.map +0 -1
  151. package/esm/bn254.d.ts +0 -18
  152. package/esm/bn254.d.ts.map +0 -1
  153. package/esm/bn254.js +0 -246
  154. package/esm/bn254.js.map +0 -1
  155. package/esm/ed25519.d.ts +0 -106
  156. package/esm/ed25519.d.ts.map +0 -1
  157. package/esm/ed25519.js +0 -467
  158. package/esm/ed25519.js.map +0 -1
  159. package/esm/ed448.d.ts +0 -101
  160. package/esm/ed448.d.ts.map +0 -1
  161. package/esm/ed448.js +0 -448
  162. package/esm/ed448.js.map +0 -1
  163. package/esm/index.d.ts +0 -2
  164. package/esm/index.d.ts.map +0 -1
  165. package/esm/index.js +0 -17
  166. package/esm/index.js.map +0 -1
  167. package/esm/jubjub.d.ts +0 -12
  168. package/esm/jubjub.d.ts.map +0 -1
  169. package/esm/jubjub.js +0 -12
  170. package/esm/jubjub.js.map +0 -1
  171. package/esm/misc.d.ts +0 -19
  172. package/esm/misc.d.ts.map +0 -1
  173. package/esm/misc.js +0 -109
  174. package/esm/misc.js.map +0 -1
  175. package/esm/nist.d.ts +0 -21
  176. package/esm/nist.d.ts.map +0 -1
  177. package/esm/nist.js +0 -132
  178. package/esm/nist.js.map +0 -1
  179. package/esm/p256.d.ts +0 -16
  180. package/esm/p256.d.ts.map +0 -1
  181. package/esm/p256.js +0 -16
  182. package/esm/p256.js.map +0 -1
  183. package/esm/p384.d.ts +0 -16
  184. package/esm/p384.d.ts.map +0 -1
  185. package/esm/p384.js +0 -16
  186. package/esm/p384.js.map +0 -1
  187. package/esm/p521.d.ts +0 -16
  188. package/esm/p521.d.ts.map +0 -1
  189. package/esm/p521.js +0 -16
  190. package/esm/p521.js.map +0 -1
  191. package/esm/package.json +0 -4
  192. package/esm/pasta.d.ts +0 -10
  193. package/esm/pasta.d.ts.map +0 -1
  194. package/esm/pasta.js +0 -10
  195. package/esm/pasta.js.map +0 -1
  196. package/esm/secp256k1.d.ts +0 -89
  197. package/esm/secp256k1.d.ts.map +0 -1
  198. package/esm/secp256k1.js +0 -292
  199. package/esm/secp256k1.js.map +0 -1
  200. package/esm/utils.d.ts +0 -110
  201. package/esm/utils.d.ts.map +0 -1
  202. package/esm/utils.js +0 -322
  203. package/esm/utils.js.map +0 -1
  204. package/src/_shortw_utils.ts +0 -21
  205. package/src/abstract/utils.ts +0 -7
  206. package/src/jubjub.ts +0 -12
  207. package/src/p256.ts +0 -15
  208. package/src/p384.ts +0 -15
  209. package/src/p521.ts +0 -15
  210. package/src/package.json +0 -3
  211. package/src/pasta.ts +0 -9
@@ -9,13 +9,13 @@ import type { CHash } from '../utils.ts';
9
9
  import {
10
10
  _validateObject,
11
11
  abytes,
12
+ asciiToBytes,
12
13
  bytesToNumberBE,
13
14
  concatBytes,
14
15
  isBytes,
15
16
  isHash,
16
- utf8ToBytes,
17
17
  } from '../utils.ts';
18
- import type { AffinePoint, Group, GroupConstructor } from './curve.ts';
18
+ import type { AffinePoint, PC_ANY, PC_F, PC_P } from './curve.ts';
19
19
  import { FpInvertBatch, mod, type IField } from './modular.ts';
20
20
 
21
21
  export type UnicodeOrBytes = string | Uint8Array;
@@ -40,8 +40,6 @@ export type H2CHashOpts = {
40
40
  expand: 'xmd' | 'xof';
41
41
  hash: CHash;
42
42
  };
43
- // todo: remove
44
- export type Opts = H2COpts;
45
43
 
46
44
  // Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
47
45
  const os2ip = bytesToNumberBE;
@@ -71,9 +69,12 @@ function anum(item: unknown): void {
71
69
  if (!Number.isSafeInteger(item)) throw new Error('number expected');
72
70
  }
73
71
 
72
+ // User can always use utf8 if they want, by passing Uint8Array.
73
+ // If string is passed, we treat it as ASCII: other formats are likely a mistake.
74
74
  function normDST(DST: UnicodeOrBytes): Uint8Array {
75
- if (!isBytes(DST) && typeof DST !== 'string') throw new Error('DST must be Uint8Array or string');
76
- return typeof DST === 'string' ? utf8ToBytes(DST) : DST;
75
+ if (!isBytes(DST) && typeof DST !== 'string')
76
+ throw new Error('DST must be Uint8Array or ascii string');
77
+ return typeof DST === 'string' ? asciiToBytes(DST) : DST;
77
78
  }
78
79
 
79
80
  /**
@@ -90,7 +91,7 @@ export function expand_message_xmd(
90
91
  anum(lenInBytes);
91
92
  DST = normDST(DST);
92
93
  // https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
93
- if (DST.length > 255) DST = H(concatBytes(utf8ToBytes('H2C-OVERSIZE-DST-'), DST));
94
+ if (DST.length > 255) DST = H(concatBytes(asciiToBytes('H2C-OVERSIZE-DST-'), DST));
94
95
  const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
95
96
  const ell = Math.ceil(lenInBytes / b_in_bytes);
96
97
  if (lenInBytes > 65535 || ell > 255) throw new Error('expand_message_xmd: invalid lenInBytes');
@@ -129,7 +130,7 @@ export function expand_message_xof(
129
130
  // DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
130
131
  if (DST.length > 255) {
131
132
  const dkLen = Math.ceil((2 * k) / 8);
132
- DST = H.create({ dkLen }).update(utf8ToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
133
+ DST = H.create({ dkLen }).update(asciiToBytes('H2C-OVERSIZE-DST-')).update(DST).digest();
133
134
  }
134
135
  if (lenInBytes > 65535 || DST.length > 255)
135
136
  throw new Error('expand_message_xof: invalid lenInBytes');
@@ -210,30 +211,16 @@ export function isogenyMap<T, F extends IField<T>>(field: F, map: XYRatio<T>): X
210
211
  };
211
212
  }
212
213
 
213
- /** Point interface, which curves must implement to work correctly with the module. */
214
- export interface H2CPoint<T> extends Group<H2CPoint<T>> {
215
- add(rhs: H2CPoint<T>): H2CPoint<T>;
216
- toAffine(iz?: bigint): AffinePoint<T>;
217
- clearCofactor(): H2CPoint<T>;
218
- assertValidity(): void;
219
- }
220
-
221
- export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
222
- fromAffine(ap: AffinePoint<T>): H2CPoint<T>;
223
- }
224
-
225
214
  export type MapToCurve<T> = (scalar: bigint[]) => AffinePoint<T>;
226
215
 
227
216
  // Separated from initialization opts, so users won't accidentally change per-curve parameters
228
217
  // (changing DST is ok!)
229
218
  export type htfBasicOpts = { DST: UnicodeOrBytes };
230
- export type H2CMethod<T> = (msg: Uint8Array, options?: htfBasicOpts) => H2CPoint<T>;
219
+ export type H2CMethod<P> = (msg: Uint8Array, options?: htfBasicOpts) => P;
231
220
  // TODO: remove
232
- export type HTFMethod<T> = H2CMethod<T>;
233
- export type MapMethod<T> = (scalars: bigint[]) => H2CPoint<T>;
234
- export type H2CHasherBase<T> = {
235
- hashToCurve: H2CMethod<T>;
236
- hashToScalar: (msg: Uint8Array, options: htfBasicOpts) => bigint;
221
+ export type H2CHasherBase<P> = {
222
+ hashToCurve: H2CMethod<P>;
223
+ hashToScalar: (msg: Uint8Array, options?: htfBasicOpts) => bigint;
237
224
  };
238
225
  /**
239
226
  * RFC 9380 methods, with cofactor clearing. See https://www.rfc-editor.org/rfc/rfc9380#section-3.
@@ -242,44 +229,44 @@ export type H2CHasherBase<T> = {
242
229
  * * encodeToCurve: `map(hash(input))`, encodes NON-UNIFORM bytes to curve (WITH hashing)
243
230
  * * mapToCurve: `map(scalars)`, encodes NON-UNIFORM scalars to curve (NO hashing)
244
231
  */
245
- export type H2CHasher<T> = H2CHasherBase<T> & {
246
- encodeToCurve: H2CMethod<T>;
247
- mapToCurve: MapMethod<T>;
232
+ export type H2CHasher<PC extends PC_ANY> = H2CHasherBase<PC_P<PC>> & {
233
+ Point: PC;
234
+ encodeToCurve: H2CMethod<PC_P<PC>>;
235
+ mapToCurve: MapToCurve<PC_F<PC>>;
248
236
  defaults: H2COpts & { encodeDST?: UnicodeOrBytes };
249
237
  };
250
- // TODO: remove
251
- export type Hasher<T> = H2CHasher<T>;
252
238
 
253
- export const _DST_scalar: Uint8Array = utf8ToBytes('HashToScalar-');
239
+ export const _DST_scalar: Uint8Array = asciiToBytes('HashToScalar-');
254
240
 
255
241
  /** Creates hash-to-curve methods from EC Point and mapToCurve function. See {@link H2CHasher}. */
256
- export function createHasher<T>(
257
- Point: H2CPointConstructor<T>,
258
- mapToCurve: MapToCurve<T>,
242
+ export function createHasher<PC extends PC_ANY>(
243
+ Point: PC,
244
+ mapToCurve: MapToCurve<PC_F<PC>>,
259
245
  defaults: H2COpts & { encodeDST?: UnicodeOrBytes }
260
- ): H2CHasher<T> {
246
+ ): H2CHasher<PC> {
261
247
  if (typeof mapToCurve !== 'function') throw new Error('mapToCurve() must be defined');
262
- function map(num: bigint[]) {
263
- return Point.fromAffine(mapToCurve(num));
248
+ function map(num: bigint[]): PC_P<PC> {
249
+ return Point.fromAffine(mapToCurve(num)) as PC_P<PC>;
264
250
  }
265
- function clear(initial: H2CPoint<T>) {
251
+ function clear(initial: PC_P<PC>): PC_P<PC> {
266
252
  const P = initial.clearCofactor();
267
- if (P.equals(Point.ZERO)) return Point.ZERO; // zero will throw in assert
253
+ if (P.equals(Point.ZERO)) return Point.ZERO as PC_P<PC>; // zero will throw in assert
268
254
  P.assertValidity();
269
- return P;
255
+ return P as PC_P<PC>;
270
256
  }
271
257
 
272
258
  return {
273
259
  defaults,
260
+ Point,
274
261
 
275
- hashToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
262
+ hashToCurve(msg: Uint8Array, options?: htfBasicOpts): PC_P<PC> {
276
263
  const opts = Object.assign({}, defaults, options);
277
264
  const u = hash_to_field(msg, 2, opts);
278
265
  const u0 = map(u[0]);
279
266
  const u1 = map(u[1]);
280
- return clear(u0.add(u1));
267
+ return clear(u0.add(u1) as PC_P<PC>);
281
268
  },
282
- encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): H2CPoint<T> {
269
+ encodeToCurve(msg: Uint8Array, options?: htfBasicOpts): PC_P<PC> {
283
270
  const optsDst = defaults.encodeDST ? { DST: defaults.encodeDST } : {};
284
271
  const opts = Object.assign({}, defaults, optsDst, options);
285
272
  const u = hash_to_field(msg, 1, opts);
@@ -287,7 +274,7 @@ export function createHasher<T>(
287
274
  return clear(u0);
288
275
  },
289
276
  /** See {@link H2CHasher} */
290
- mapToCurve(scalars: bigint[]): H2CPoint<T> {
277
+ mapToCurve(scalars: bigint[]): PC_P<PC> {
291
278
  if (!Array.isArray(scalars)) throw new Error('expected array of bigints');
292
279
  for (const i of scalars)
293
280
  if (typeof i !== 'bigint') throw new Error('expected array of bigints');
@@ -7,17 +7,16 @@
7
7
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
8
8
  import {
9
9
  _validateObject,
10
+ abytes,
10
11
  anumber,
11
- bitMask,
12
12
  bytesToNumberBE,
13
13
  bytesToNumberLE,
14
- ensureBytes,
15
14
  numberToBytesBE,
16
15
  numberToBytesLE,
17
16
  } from '../utils.ts';
18
17
 
19
18
  // prettier-ignore
20
- const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
19
+ const _0n = BigInt(0), _1n = /* @__PURE__ */ BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
21
20
  // prettier-ignore
22
21
  const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _7n = /* @__PURE__ */ BigInt(7);
23
22
  // prettier-ignore
@@ -227,10 +226,9 @@ export const isNegativeLE = (num: bigint, modulo: bigint): boolean =>
227
226
  /** Field is not always over prime: for example, Fp2 has ORDER(q)=p^m. */
228
227
  export interface IField<T> {
229
228
  ORDER: bigint;
230
- isLE: boolean;
231
229
  BYTES: number;
232
230
  BITS: number;
233
- MASK: bigint;
231
+ isLE: boolean;
234
232
  ZERO: T;
235
233
  ONE: T;
236
234
  // 1-arg
@@ -260,7 +258,6 @@ export interface IField<T> {
260
258
  // [RFC9380](https://www.rfc-editor.org/rfc/rfc9380#section-4.1).
261
259
  // NOTE: sgn0 is 'negative in LE', which is same as odd. And negative in LE is kinda strange definition anyway.
262
260
  isOdd?(num: T): boolean; // Odd instead of even since we have it for Fp2
263
- allowedLengths?: number[];
264
261
  // legendre?(num: T): T;
265
262
  invertBatch: (lst: T[]) => T[];
266
263
  toBytes(num: T): Uint8Array;
@@ -277,7 +274,6 @@ const FIELD_FIELDS = [
277
274
  export function validateField<T>(field: IField<T>): IField<T> {
278
275
  const initial = {
279
276
  ORDER: 'bigint',
280
- MASK: 'bigint',
281
277
  BYTES: 'number',
282
278
  BITS: 'number',
283
279
  } as Record<string, string>;
@@ -381,12 +377,147 @@ export function nLength(n: bigint, nBitLength?: number): NLength {
381
377
  type FpField = IField<bigint> & Required<Pick<IField<bigint>, 'isOdd'>>;
382
378
  type SqrtFn = (n: bigint) => bigint;
383
379
  type FieldOpts = Partial<{
384
- sqrt: SqrtFn;
385
380
  isLE: boolean;
386
381
  BITS: number;
387
- modFromBytes: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
382
+ sqrt: SqrtFn;
388
383
  allowedLengths?: readonly number[]; // for P521 (adds padding for smaller sizes)
384
+ modFromBytes: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
389
385
  }>;
386
+ class _Field implements IField<bigint> {
387
+ readonly ORDER: bigint;
388
+ readonly BITS: number;
389
+ readonly BYTES: number;
390
+ readonly isLE: boolean;
391
+ readonly ZERO = _0n;
392
+ readonly ONE = _1n;
393
+ readonly _lengths?: number[];
394
+ private _sqrt: ReturnType<typeof FpSqrt> | undefined; // cached sqrt
395
+ private readonly _mod?: boolean;
396
+ constructor(ORDER: bigint, opts: FieldOpts = {}) {
397
+ if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
398
+ let _nbitLength: number | undefined = undefined;
399
+ this.isLE = false;
400
+ if (opts != null && typeof opts === 'object') {
401
+ if (typeof opts.BITS === 'number') _nbitLength = opts.BITS;
402
+ if (typeof opts.sqrt === 'function') this.sqrt = opts.sqrt;
403
+ if (typeof opts.isLE === 'boolean') this.isLE = opts.isLE;
404
+ if (opts.allowedLengths) this._lengths = opts.allowedLengths?.slice();
405
+ if (typeof opts.modFromBytes === 'boolean') this._mod = opts.modFromBytes;
406
+ }
407
+ const { nBitLength, nByteLength } = nLength(ORDER, _nbitLength);
408
+ if (nByteLength > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes');
409
+ this.ORDER = ORDER;
410
+ this.BITS = nBitLength;
411
+ this.BYTES = nByteLength;
412
+ this._sqrt = undefined;
413
+ Object.preventExtensions(this);
414
+ }
415
+
416
+ create(num: bigint) {
417
+ return mod(num, this.ORDER);
418
+ }
419
+ isValid(num: bigint) {
420
+ if (typeof num !== 'bigint')
421
+ throw new Error('invalid field element: expected bigint, got ' + typeof num);
422
+ return _0n <= num && num < this.ORDER; // 0 is valid element, but it's not invertible
423
+ }
424
+ is0(num: bigint) {
425
+ return num === _0n;
426
+ }
427
+ // is valid and invertible
428
+ isValidNot0(num: bigint) {
429
+ return !this.is0(num) && this.isValid(num);
430
+ }
431
+ isOdd(num: bigint) {
432
+ return (num & _1n) === _1n;
433
+ }
434
+ neg(num: bigint) {
435
+ return mod(-num, this.ORDER);
436
+ }
437
+ eql(lhs: bigint, rhs: bigint) {
438
+ return lhs === rhs;
439
+ }
440
+
441
+ sqr(num: bigint) {
442
+ return mod(num * num, this.ORDER);
443
+ }
444
+ add(lhs: bigint, rhs: bigint) {
445
+ return mod(lhs + rhs, this.ORDER);
446
+ }
447
+ sub(lhs: bigint, rhs: bigint) {
448
+ return mod(lhs - rhs, this.ORDER);
449
+ }
450
+ mul(lhs: bigint, rhs: bigint) {
451
+ return mod(lhs * rhs, this.ORDER);
452
+ }
453
+ pow(num: bigint, power: bigint): bigint {
454
+ return FpPow(this, num, power);
455
+ }
456
+ div(lhs: bigint, rhs: bigint) {
457
+ return mod(lhs * invert(rhs, this.ORDER), this.ORDER);
458
+ }
459
+
460
+ // Same as above, but doesn't normalize
461
+ sqrN(num: bigint) {
462
+ return num * num;
463
+ }
464
+ addN(lhs: bigint, rhs: bigint) {
465
+ return lhs + rhs;
466
+ }
467
+ subN(lhs: bigint, rhs: bigint) {
468
+ return lhs - rhs;
469
+ }
470
+ mulN(lhs: bigint, rhs: bigint) {
471
+ return lhs * rhs;
472
+ }
473
+
474
+ inv(num: bigint) {
475
+ return invert(num, this.ORDER);
476
+ }
477
+ sqrt(num: bigint): bigint {
478
+ // Caching _sqrt speeds up sqrt9mod16 by 5x and tonneli-shanks by 10%
479
+ if (!this._sqrt) this._sqrt = FpSqrt(this.ORDER);
480
+ return this._sqrt(this, num);
481
+ }
482
+ toBytes(num: bigint) {
483
+ return this.isLE ? numberToBytesLE(num, this.BYTES) : numberToBytesBE(num, this.BYTES);
484
+ }
485
+ fromBytes(bytes: Uint8Array, skipValidation = false) {
486
+ abytes(bytes);
487
+ const { _lengths: allowedLengths, BYTES, isLE, ORDER, _mod: modFromBytes } = this;
488
+ if (allowedLengths) {
489
+ if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
490
+ throw new Error(
491
+ 'Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length
492
+ );
493
+ }
494
+ const padded = new Uint8Array(BYTES);
495
+ // isLE add 0 to right, !isLE to the left.
496
+ padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
497
+ bytes = padded;
498
+ }
499
+ if (bytes.length !== BYTES)
500
+ throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
501
+ let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
502
+ if (modFromBytes) scalar = mod(scalar, ORDER);
503
+ if (!skipValidation)
504
+ if (!this.isValid(scalar))
505
+ throw new Error('invalid field element: outside of range 0..ORDER');
506
+ // NOTE: we don't validate scalar here, please use isValid. This done such way because some
507
+ // protocol may allow non-reduced scalar that reduced later or changed some other way.
508
+ return scalar;
509
+ }
510
+ // TODO: we don't need it here, move out to separate fn
511
+ invertBatch(lst: bigint[]): bigint[] {
512
+ return FpInvertBatch(this, lst);
513
+ }
514
+ // We can't move this out because Fp6, Fp12 implement it
515
+ // and it's unclear what to return in there.
516
+ cmov(a: bigint, b: bigint, condition: boolean) {
517
+ return condition ? b : a;
518
+ }
519
+ }
520
+
390
521
  /**
391
522
  * Creates a finite field. Major performance optimizations:
392
523
  * * 1. Denormalized operations like mulN instead of mul.
@@ -406,104 +537,8 @@ type FieldOpts = Partial<{
406
537
  * @param isLE (default: false) if encoding / decoding should be in little-endian
407
538
  * @param redef optional faster redefinitions of sqrt and other methods
408
539
  */
409
- export function Field(
410
- ORDER: bigint,
411
- bitLenOrOpts?: number | FieldOpts, // TODO: use opts only in v2?
412
- isLE = false,
413
- opts: { sqrt?: SqrtFn } = {}
414
- ): Readonly<FpField> {
415
- if (ORDER <= _0n) throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
416
- let _nbitLength: number | undefined = undefined;
417
- let _sqrt: SqrtFn | undefined = undefined;
418
- let modFromBytes: boolean = false;
419
- let allowedLengths: undefined | readonly number[] = undefined;
420
- if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
421
- if (opts.sqrt || isLE) throw new Error('cannot specify opts in two arguments');
422
- const _opts = bitLenOrOpts;
423
- if (_opts.BITS) _nbitLength = _opts.BITS;
424
- if (_opts.sqrt) _sqrt = _opts.sqrt;
425
- if (typeof _opts.isLE === 'boolean') isLE = _opts.isLE;
426
- if (typeof _opts.modFromBytes === 'boolean') modFromBytes = _opts.modFromBytes;
427
- allowedLengths = _opts.allowedLengths;
428
- } else {
429
- if (typeof bitLenOrOpts === 'number') _nbitLength = bitLenOrOpts;
430
- if (opts.sqrt) _sqrt = opts.sqrt;
431
- }
432
- const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
433
- if (BYTES > 2048) throw new Error('invalid field: expected ORDER of <= 2048 bytes');
434
- let sqrtP: ReturnType<typeof FpSqrt>; // cached sqrtP
435
- const f: Readonly<FpField> = Object.freeze({
436
- ORDER,
437
- isLE,
438
- BITS,
439
- BYTES,
440
- MASK: bitMask(BITS),
441
- ZERO: _0n,
442
- ONE: _1n,
443
- allowedLengths: allowedLengths,
444
- create: (num) => mod(num, ORDER),
445
- isValid: (num) => {
446
- if (typeof num !== 'bigint')
447
- throw new Error('invalid field element: expected bigint, got ' + typeof num);
448
- return _0n <= num && num < ORDER; // 0 is valid element, but it's not invertible
449
- },
450
- is0: (num) => num === _0n,
451
- // is valid and invertible
452
- isValidNot0: (num: bigint) => !f.is0(num) && f.isValid(num),
453
- isOdd: (num) => (num & _1n) === _1n,
454
- neg: (num) => mod(-num, ORDER),
455
- eql: (lhs, rhs) => lhs === rhs,
456
-
457
- sqr: (num) => mod(num * num, ORDER),
458
- add: (lhs, rhs) => mod(lhs + rhs, ORDER),
459
- sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
460
- mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
461
- pow: (num, power) => FpPow(f, num, power),
462
- div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
463
-
464
- // Same as above, but doesn't normalize
465
- sqrN: (num) => num * num,
466
- addN: (lhs, rhs) => lhs + rhs,
467
- subN: (lhs, rhs) => lhs - rhs,
468
- mulN: (lhs, rhs) => lhs * rhs,
469
-
470
- inv: (num) => invert(num, ORDER),
471
- sqrt:
472
- _sqrt ||
473
- ((n) => {
474
- if (!sqrtP) sqrtP = FpSqrt(ORDER);
475
- return sqrtP(f, n);
476
- }),
477
- toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
478
- fromBytes: (bytes, skipValidation = true) => {
479
- if (allowedLengths) {
480
- if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
481
- throw new Error(
482
- 'Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length
483
- );
484
- }
485
- const padded = new Uint8Array(BYTES);
486
- // isLE add 0 to right, !isLE to the left.
487
- padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
488
- bytes = padded;
489
- }
490
- if (bytes.length !== BYTES)
491
- throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
492
- let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
493
- if (modFromBytes) scalar = mod(scalar, ORDER);
494
- if (!skipValidation)
495
- if (!f.isValid(scalar)) throw new Error('invalid field element: outside of range 0..ORDER');
496
- // NOTE: we don't validate scalar here, please use isValid. This done such way because some
497
- // protocol may allow non-reduced scalar that reduced later or changed some other way.
498
- return scalar;
499
- },
500
- // TODO: we don't need it here, move out to separate fn
501
- invertBatch: (lst) => FpInvertBatch(f, lst),
502
- // We can't move this out because Fp6, Fp12 implement it
503
- // and it's unclear what to return in there.
504
- cmov: (a, b, c) => (c ? b : a),
505
- } as FpField);
506
- return Object.freeze(f);
540
+ export function Field(ORDER: bigint, opts: FieldOpts = {}): Readonly<FpField> {
541
+ return new _Field(ORDER, opts);
507
542
  }
508
543
 
509
544
  // Generic random scalar, we can do same for other fields if via Fp2.mul(Fp2.ONE, Fp2.random)?
@@ -532,28 +567,6 @@ export function FpSqrtEven<T>(Fp: IField<T>, elm: T): T {
532
567
  return Fp.isOdd(root) ? Fp.neg(root) : root;
533
568
  }
534
569
 
535
- /**
536
- * "Constant-time" private key generation utility.
537
- * Same as mapKeyToField, but accepts less bytes (40 instead of 48 for 32-byte field).
538
- * Which makes it slightly more biased, less secure.
539
- * @deprecated use `mapKeyToField` instead
540
- */
541
- export function hashToPrivateScalar(
542
- hash: string | Uint8Array,
543
- groupOrder: bigint,
544
- isLE = false
545
- ): bigint {
546
- hash = ensureBytes('privateHash', hash);
547
- const hashLen = hash.length;
548
- const minLen = nLength(groupOrder).nByteLength + 8;
549
- if (minLen < 24 || hashLen < minLen || hashLen > 1024)
550
- throw new Error(
551
- 'hashToPrivateScalar: expected ' + minLen + '-1024 bytes of input, got ' + hashLen
552
- );
553
- const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash);
554
- return mod(num, groupOrder - _1n) + _1n;
555
- }
556
-
557
570
  /**
558
571
  * Returns total number of bytes consumed by the field element.
559
572
  * For example, 32 bytes for usual 256-bit weierstrass curve.
@@ -587,11 +600,12 @@ export function getMinHashLength(fieldOrder: bigint): number {
587
600
  * FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
588
601
  * RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
589
602
  * @param hash hash output from SHA3 or a similar function
590
- * @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
603
+ * @param groupOrder size of subgroup - (e.g. secp256k1.Point.Fn.ORDER)
591
604
  * @param isLE interpret hash bytes as LE num
592
605
  * @returns valid private scalar
593
606
  */
594
607
  export function mapHashToField(key: Uint8Array, fieldOrder: bigint, isLE = false): Uint8Array {
608
+ abytes(key);
595
609
  const len = key.length;
596
610
  const fieldLen = getFieldBytesLength(fieldOrder);
597
611
  const minLen = getMinHashLength(fieldOrder);
@@ -10,9 +10,10 @@ import {
10
10
  abytes,
11
11
  aInRange,
12
12
  bytesToNumberLE,
13
- ensureBytes,
13
+ copyBytes,
14
14
  numberToBytesLE,
15
15
  randomBytes,
16
+ type CryptoKeys,
16
17
  } from '../utils.ts';
17
18
  import type { CurveLengths } from './curve.ts';
18
19
  import { mod } from './modular.ts';
@@ -20,7 +21,6 @@ import { mod } from './modular.ts';
20
21
  const _0n = BigInt(0);
21
22
  const _1n = BigInt(1);
22
23
  const _2n = BigInt(2);
23
- type Hex = string | Uint8Array;
24
24
 
25
25
  export type CurveType = {
26
26
  P: bigint; // finite field prime
@@ -31,20 +31,17 @@ export type CurveType = {
31
31
  };
32
32
 
33
33
  export type MontgomeryECDH = {
34
- scalarMult: (scalar: Hex, u: Hex) => Uint8Array;
35
- scalarMultBase: (scalar: Hex) => Uint8Array;
36
- getSharedSecret: (secretKeyA: Hex, publicKeyB: Hex) => Uint8Array;
37
- getPublicKey: (secretKey: Hex) => Uint8Array;
34
+ scalarMult: (scalar: Uint8Array, u: Uint8Array) => Uint8Array;
35
+ scalarMultBase: (scalar: Uint8Array) => Uint8Array;
36
+ getSharedSecret: (secretKeyA: Uint8Array, publicKeyB: Uint8Array) => Uint8Array;
37
+ getPublicKey: (secretKey: Uint8Array) => Uint8Array;
38
38
  utils: {
39
39
  randomSecretKey: () => Uint8Array;
40
- /** @deprecated use `randomSecretKey` */
41
- randomPrivateKey: () => Uint8Array;
42
40
  };
43
41
  GuBytes: Uint8Array;
44
42
  lengths: CurveLengths;
45
43
  keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
46
44
  };
47
- export type CurveFn = MontgomeryECDH;
48
45
 
49
46
  function validateOpts(curve: CurveType) {
50
47
  _validateObject(curve, {
@@ -82,8 +79,8 @@ export function montgomery(curveDef: CurveType): MontgomeryECDH {
82
79
  function encodeU(u: bigint): Uint8Array {
83
80
  return numberToBytesLE(modP(u), fieldLen);
84
81
  }
85
- function decodeU(u: Hex): bigint {
86
- const _u = ensureBytes('u coordinate', u, fieldLen);
82
+ function decodeU(u: Uint8Array): bigint {
83
+ const _u = copyBytes(abytes(u, fieldLen, 'uCoordinate'));
87
84
  // RFC: When receiving such an array, implementations of X25519
88
85
  // (but not X448) MUST mask the most significant bit in the final byte.
89
86
  if (is25519) _u[31] &= 127; // 0b0111_1111
@@ -93,10 +90,10 @@ export function montgomery(curveDef: CurveType): MontgomeryECDH {
93
90
  // - 1 through 2^448 - 1 for X448.
94
91
  return modP(bytesToNumberLE(_u));
95
92
  }
96
- function decodeScalar(scalar: Hex): bigint {
97
- return bytesToNumberLE(adjustScalarBytes(ensureBytes('scalar', scalar, fieldLen)));
93
+ function decodeScalar(scalar: Uint8Array): bigint {
94
+ return bytesToNumberLE(adjustScalarBytes(copyBytes(abytes(scalar, fieldLen, 'scalar'))));
98
95
  }
99
- function scalarMult(scalar: Hex, u: Hex): Uint8Array {
96
+ function scalarMult(scalar: Uint8Array, u: Uint8Array): Uint8Array {
100
97
  const pu = montgomeryLadder(decodeU(u), decodeScalar(scalar));
101
98
  // Some public keys are useless, of low-order. Curve author doesn't think
102
99
  // it needs to be validated, but we do it nonetheless.
@@ -105,7 +102,7 @@ export function montgomery(curveDef: CurveType): MontgomeryECDH {
105
102
  return encodeU(pu);
106
103
  }
107
104
  // Computes public key from private. By doing scalar multiplication of base point.
108
- function scalarMultBase(scalar: Hex): Uint8Array {
105
+ function scalarMultBase(scalar: Uint8Array): Uint8Array {
109
106
  return scalarMult(scalar, GuBytes);
110
107
  }
111
108
 
@@ -165,12 +162,12 @@ export function montgomery(curveDef: CurveType): MontgomeryECDH {
165
162
  return modP(x_2 * z2); // Return x_2 * (z_2^(p - 2))
166
163
  }
167
164
  const lengths = {
168
- secret: fieldLen,
169
- public: fieldLen,
165
+ secretKey: fieldLen,
166
+ publicKey: fieldLen,
170
167
  seed: fieldLen,
171
168
  };
172
169
  const randomSecretKey = (seed = randomBytes_(fieldLen)) => {
173
- abytes(seed, lengths.seed);
170
+ abytes(seed, lengths.seed, 'seed');
174
171
  return seed;
175
172
  };
176
173
  function keygen(seed?: Uint8Array) {
@@ -181,14 +178,16 @@ export function montgomery(curveDef: CurveType): MontgomeryECDH {
181
178
  randomSecretKey,
182
179
  randomPrivateKey: randomSecretKey,
183
180
  };
184
- return {
181
+
182
+ return Object.freeze({
185
183
  keygen,
186
- getSharedSecret: (secretKey: Hex, publicKey: Hex) => scalarMult(secretKey, publicKey),
187
- getPublicKey: (secretKey: Hex): Uint8Array => scalarMultBase(secretKey),
184
+ getSharedSecret: (secretKey: Uint8Array, publicKey: Uint8Array) =>
185
+ scalarMult(secretKey, publicKey),
186
+ getPublicKey: (secretKey: Uint8Array): Uint8Array => scalarMultBase(secretKey),
188
187
  scalarMult,
189
188
  scalarMultBase,
190
189
  utils,
191
190
  GuBytes: GuBytes.slice(),
192
191
  lengths,
193
- };
192
+ }) satisfies CryptoKeys;
194
193
  }