@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
@@ -1,19 +1,6 @@
1
1
  /**
2
2
  * Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
3
3
  *
4
- * ### Parameters
5
- *
6
- * To initialize a weierstrass curve, one needs to pass following params:
7
- *
8
- * * a: formula param
9
- * * b: formula param
10
- * * Fp: finite Field over which we'll do calculations. Can be complex (Fp2, Fp12)
11
- * * n: Curve prime subgroup order, total count of valid points in the field
12
- * * Gx: Base point (x, y) aka generator point x coordinate
13
- * * Gy: ...y coordinate
14
- * * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
15
- * * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
16
- *
17
4
  * ### Design rationale for types
18
5
  *
19
6
  * * Interaction between classes from different curves should fail:
@@ -38,29 +25,76 @@
38
25
  * @module
39
26
  */
40
27
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
41
- // prettier-ignore
28
+ import { hmac } from '@noble/hashes/hmac.js';
42
29
  import {
43
- type AffinePoint, type BasicCurve, type Group, type GroupConstructor,
44
- pippenger, validateBasic, wNAF
30
+ _validateObject,
31
+ abool,
32
+ abytes,
33
+ aInRange,
34
+ bitMask,
35
+ bytesToHex,
36
+ bytesToNumberBE,
37
+ concatBytes,
38
+ createHmacDrbg,
39
+ ensureBytes,
40
+ hexToBytes,
41
+ inRange,
42
+ isBytes,
43
+ memoized,
44
+ numberToHexUnpadded,
45
+ randomBytes,
46
+ type CHash,
47
+ type Hex,
48
+ type PrivKey,
49
+ } from '../utils.ts';
50
+ import {
51
+ _createCurveFields,
52
+ mulEndoUnsafe,
53
+ negateCt,
54
+ normalizeZ,
55
+ pippenger,
56
+ wNAF,
57
+ type AffinePoint,
58
+ type BasicCurve,
59
+ type Group,
60
+ type GroupConstructor,
45
61
  } from './curve.ts';
46
- // prettier-ignore
47
62
  import {
48
63
  Field,
49
64
  FpInvertBatch,
50
- type IField, getMinHashLength, invert, mapHashToField, mod, validateField
65
+ getMinHashLength,
66
+ mapHashToField,
67
+ validateField,
68
+ type IField,
69
+ type NLength,
51
70
  } from './modular.ts';
52
- // prettier-ignore
53
- import {
54
- type CHash, type Hex, type PrivKey,
55
- aInRange, abool,
56
- bitMask,
57
- bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes,
58
- inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject
59
- } from './utils.ts';
60
71
 
61
72
  export type { AffinePoint };
62
- type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
63
- type EndomorphismOpts = {
73
+ export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
74
+ /**
75
+ * When Weierstrass curve has `a=0`, it becomes Koblitz curve.
76
+ * Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
77
+ * Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
78
+ * For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
79
+ *
80
+ * Endomorphism consists of beta, lambda and splitScalar:
81
+ *
82
+ * 1. GLV endomorphism ψ transforms a point: `P = (x, y) ↦ ψ(P) = (β·x mod p, y)`
83
+ * 2. GLV scalar decomposition transforms a scalar: `k ≡ k₁ + k₂·λ (mod n)`
84
+ * 3. Then these are combined: `k·P = k₁·P + k₂·ψ(P)`
85
+ * 4. Two 128-bit point-by-scalar multiplications + one point addition is faster than
86
+ * one 256-bit multiplication.
87
+ *
88
+ * where
89
+ * * beta: β ∈ Fₚ with β³ = 1, β ≠ 1
90
+ * * lambda: λ ∈ Fₙ with λ³ = 1, λ ≠ 1
91
+ * * splitScalar decomposes k ↦ k₁, k₂, by using reduced basis vectors.
92
+ * Gauss lattice reduction calculates them from initial basis vectors `(n, 0), (-λ, 0)`
93
+ *
94
+ * Check out `test/misc/endomorphism.js` and
95
+ * [gist](https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066).
96
+ */
97
+ export type EndomorphismOpts = {
64
98
  beta: bigint;
65
99
  splitScalar: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
66
100
  };
@@ -72,7 +106,7 @@ export type BasicWCurve<T> = BasicCurve<T> & {
72
106
  // Optional params
73
107
  allowedPrivateKeyLengths?: readonly number[]; // for P521
74
108
  wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
75
- endo?: EndomorphismOpts; // Endomorphism options for Koblitz curves
109
+ endo?: EndomorphismOpts;
76
110
  // When a cofactor != 1, there can be an effective methods to:
77
111
  // 1. Determine whether a point is torsion-free
78
112
  isTorsionFree?: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
@@ -82,36 +116,70 @@ export type BasicWCurve<T> = BasicCurve<T> & {
82
116
 
83
117
  export type Entropy = Hex | boolean;
84
118
  export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
85
- export type VerOpts = { lowS?: boolean; prehash?: boolean; format?: 'compact' | 'der' | undefined };
119
+ export type VerOpts = {
120
+ lowS?: boolean;
121
+ prehash?: boolean;
122
+ format?: 'compact' | 'der' | 'js' | undefined;
123
+ };
86
124
 
87
125
  function validateSigVerOpts(opts: SignOpts | VerOpts) {
88
126
  if (opts.lowS !== undefined) abool('lowS', opts.lowS);
89
127
  if (opts.prehash !== undefined) abool('prehash', opts.prehash);
90
128
  }
91
129
 
92
- // Instance for 3d XYZ points
130
+ /** Instance methods for 3D XYZ points. */
93
131
  export interface ProjPointType<T> extends Group<ProjPointType<T>> {
132
+ /** projective x coordinate. Note: different from .x */
94
133
  readonly px: T;
134
+ /** projective y coordinate. Note: different from .y */
95
135
  readonly py: T;
136
+ /** projective z coordinate */
96
137
  readonly pz: T;
138
+ /** affine x coordinate */
97
139
  get x(): T;
140
+ /** affine y coordinate */
98
141
  get y(): T;
99
- toAffine(iz?: T): AffinePoint<T>;
100
- toHex(isCompressed?: boolean): string;
101
- toRawBytes(isCompressed?: boolean): Uint8Array;
102
-
103
142
  assertValidity(): void;
104
- hasEvenY(): boolean;
143
+ clearCofactor(): ProjPointType<T>;
144
+ is0(): boolean;
145
+ isTorsionFree(): boolean;
105
146
  multiplyUnsafe(scalar: bigint): ProjPointType<T>;
147
+ /**
148
+ * Massively speeds up `p.multiply(n)` by using wnaf precompute tables (caching).
149
+ * Table generation takes 30MB of ram and 10ms on high-end CPU, but may take
150
+ * much longer on slow devices.
151
+ * Actual generation will happen on first call of `.multiply()`.
152
+ * By default, BASE point is precomputed.
153
+ * @param windowSize - table window size
154
+ * @param isLazy - (default true) allows to defer generation
155
+ */
156
+ precompute(windowSize?: number, isLazy?: boolean): ProjPointType<T>;
157
+
158
+ /** Converts 3D XYZ projective point to 2D xy affine coordinates */
159
+ toAffine(invertedZ?: T): AffinePoint<T>;
160
+ /** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
161
+ toBytes(isCompressed?: boolean): Uint8Array;
162
+ toHex(isCompressed?: boolean): string;
163
+
164
+ /** @deprecated use `toBytes` */
165
+ toRawBytes(isCompressed?: boolean): Uint8Array;
166
+ /** @deprecated use `multiplyUnsafe` */
106
167
  multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
107
- isTorsionFree(): boolean;
108
- clearCofactor(): ProjPointType<T>;
168
+ /** @deprecated use `p.y % 2n === 0n` */
169
+ hasEvenY(): boolean;
170
+ /** @deprecated use `p.precompute(windowSize)` */
109
171
  _setWindowSize(windowSize: number): void;
110
172
  }
111
- // Static methods for 3d XYZ points
173
+
174
+ /** Static methods for 3D XYZ points. */
112
175
  export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
176
+ Fp: IField<T>;
177
+ Fn: IField<bigint>;
178
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
113
179
  new (x: T, y: T, z: T): ProjPointType<T>;
180
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
114
181
  fromAffine(p: AffinePoint<T>): ProjPointType<T>;
182
+ fromBytes(encodedPoint: Uint8Array): ProjPointType<T>;
115
183
  fromHex(hex: Hex): ProjPointType<T>;
116
184
  fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
117
185
  normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
@@ -119,57 +187,104 @@ export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
119
187
  }
120
188
 
121
189
  export type CurvePointsType<T> = BasicWCurve<T> & {
122
- // Bytes
123
190
  fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
124
191
  toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
125
192
  };
126
193
 
127
- export type CurvePointsTypeWithLength<T> = Readonly<
128
- CurvePointsType<T> & { nByteLength: number; nBitLength: number }
129
- >;
130
-
131
- function validatePointOpts<T>(curve: CurvePointsType<T>): CurvePointsTypeWithLength<T> {
132
- const opts = validateBasic(curve);
133
- validateObject(
134
- opts,
135
- {
136
- a: 'field',
137
- b: 'field',
138
- },
139
- {
140
- allowedPrivateKeyLengths: 'array',
141
- wrapPrivateKey: 'boolean',
142
- isTorsionFree: 'function',
143
- clearCofactor: 'function',
144
- allowInfinityPoint: 'boolean',
145
- fromBytes: 'function',
146
- toBytes: 'function',
147
- }
148
- );
149
- const { endo, Fp, a } = opts;
150
- if (endo) {
151
- if (!Fp.eql(a, Fp.ZERO)) {
152
- throw new Error('invalid endomorphism, can only be defined for Koblitz curves that have a=0');
153
- }
154
- if (
155
- typeof endo !== 'object' ||
156
- typeof endo.beta !== 'bigint' ||
157
- typeof endo.splitScalar !== 'function'
158
- ) {
159
- throw new Error('invalid endomorphism, expected beta: bigint and splitScalar: function');
160
- }
161
- }
162
- return Object.freeze({ ...opts } as const);
163
- }
194
+ // LegacyWeierstrassOpts
195
+ export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
164
196
 
197
+ // LegacyWeierstrass
165
198
  export type CurvePointsRes<T> = {
166
- CURVE: ReturnType<typeof validatePointOpts<T>>;
199
+ /** @deprecated import individual CURVE params */
200
+ CURVE: CurvePointsType<T>;
201
+ Point: ProjConstructor<T>;
202
+ /** @deprecated use `Point` */
167
203
  ProjectivePoint: ProjConstructor<T>;
204
+ /** @deprecated */
168
205
  normPrivateKeyToScalar: (key: PrivKey) => bigint;
206
+ /** @deprecated */
169
207
  weierstrassEquation: (x: T) => T;
208
+ /** @deprecated use `Point.Fn.isValidNot0(num)` */
170
209
  isWithinCurveOrder: (num: bigint) => boolean;
171
210
  };
172
211
 
212
+ // Aliases to legacy types
213
+ // export type CurveType = LegacyECDSAOpts;
214
+ // export type CurveFn = LegacyECDSA;
215
+ // export type CurvePointsRes<T> = LegacyWeierstrass<T>;
216
+ // export type CurvePointsType<T> = LegacyWeierstrassOpts<T>;
217
+ // export type CurvePointsTypeWithLength<T> = LegacyWeierstrassOpts<T>;
218
+ // export type BasicWCurve<T> = LegacyWeierstrassOpts<T>;
219
+
220
+ /**
221
+ * Weierstrass curve options.
222
+ *
223
+ * * p: prime characteristic (order) of finite field, in which arithmetics is done
224
+ * * n: order of prime subgroup a.k.a total amount of valid curve points
225
+ * * h: cofactor, usually 1. h*n is group order; n is subgroup order
226
+ * * a: formula param, must be in field of p
227
+ * * b: formula param, must be in field of p
228
+ * * Gx: x coordinate of generator point a.k.a. base point
229
+ * * Gy: y coordinate of generator point
230
+ */
231
+ export type WeierstrassOpts<T> = Readonly<{
232
+ p: bigint;
233
+ n: bigint;
234
+ h: bigint;
235
+ a: T;
236
+ b: T;
237
+ Gx: T;
238
+ Gy: T;
239
+ }>;
240
+
241
+ // When a cofactor != 1, there can be an effective methods to:
242
+ // 1. Determine whether a point is torsion-free
243
+ // 2. Clear torsion component
244
+ // wrapPrivateKey: bls12-381 requires mod(n) instead of rejecting keys >= n
245
+ export type WeierstrassExtraOpts<T> = Partial<{
246
+ Fp: IField<T>;
247
+ Fn: IField<bigint>;
248
+ // TODO: remove
249
+ allowedPrivateKeyLengths: readonly number[]; // for P521
250
+ allowInfinityPoint: boolean;
251
+ endo: EndomorphismOpts;
252
+ wrapPrivateKey: boolean;
253
+ isTorsionFree: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
254
+ clearCofactor: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
255
+ fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
256
+ toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
257
+ }>;
258
+
259
+ /**
260
+ * Options for ECDSA signatures over a Weierstrass curve.
261
+ */
262
+ export type ECDSAOpts = {
263
+ hash: CHash;
264
+ hmac?: HmacFnSync;
265
+ randomBytes?: (bytesLength?: number) => Uint8Array;
266
+ lowS?: boolean;
267
+ bits2int?: (bytes: Uint8Array) => bigint;
268
+ bits2int_modN?: (bytes: Uint8Array) => bigint;
269
+ };
270
+
271
+ /** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
272
+ export interface ECDSA {
273
+ getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
274
+ getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
275
+ sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
276
+ verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
277
+ Point: ProjConstructor<bigint>;
278
+ Signature: SignatureConstructor;
279
+ utils: {
280
+ isValidPrivateKey(privateKey: PrivKey): boolean;
281
+ randomPrivateKey: () => Uint8Array;
282
+ // TODO: deprecate those two
283
+ normPrivateKeyToScalar: (key: PrivKey) => bigint;
284
+ /** @deprecated */
285
+ precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
286
+ };
287
+ }
173
288
  export class DERErr extends Error {
174
289
  constructor(m = '') {
175
290
  super(m);
@@ -292,74 +407,171 @@ export const DER: IDER = {
292
407
  // prettier-ignore
293
408
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
294
409
 
295
- export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T> {
296
- const CURVE = validatePointOpts(opts);
297
- const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
298
- const Fn = Field(CURVE.n, CURVE.nBitLength);
299
-
300
- const toBytes =
301
- CURVE.toBytes ||
302
- ((_c: ProjConstructor<T>, point: ProjPointType<T>, _isCompressed: boolean) => {
303
- const a = point.toAffine();
304
- return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
305
- });
306
- const fromBytes =
307
- CURVE.fromBytes ||
308
- ((bytes: Uint8Array) => {
309
- // const head = bytes[0];
310
- const tail = bytes.subarray(1);
311
- // if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
312
- const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
313
- const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
314
- return { x, y };
315
- });
316
-
410
+ // TODO: remove
411
+ export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
317
412
  /**
318
413
  * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
319
414
  * @returns y²
320
415
  */
321
416
  function weierstrassEquation(x: T): T {
322
- const { a, b } = CURVE;
323
417
  const x2 = Fp.sqr(x); // x * x
324
- const x3 = Fp.mul(x2, x); // x2 * x
325
- return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x3 + a * x + b
326
- }
327
- // Validate whether the passed curve params are valid.
328
- // We check if curve equation works for generator point.
329
- // `assertValidity()` won't work: `isTorsionFree()` is not available at this point in bls12-381.
330
- // ProjectivePoint class has not been initialized yet.
331
- if (!Fp.eql(Fp.sqr(CURVE.Gy), weierstrassEquation(CURVE.Gx)))
332
- throw new Error('bad generator point: equation left != right');
333
-
334
- // Valid group elements reside in range 1..n-1
335
- function isWithinCurveOrder(num: bigint): boolean {
336
- return inRange(num, _1n, CURVE.n);
418
+ const x3 = Fp.mul(x2, x); // * x
419
+ return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // + a * x + b
337
420
  }
421
+ return weierstrassEquation;
422
+ }
423
+ export function _legacyHelperNormPriv(
424
+ Fn: IField<bigint>,
425
+ allowedPrivateKeyLengths?: readonly number[],
426
+ wrapPrivateKey?: boolean
427
+ ): (key: PrivKey) => bigint {
428
+ const { BYTES: expected } = Fn;
338
429
  // Validates if priv key is valid and converts it to bigint.
339
- // Supports options allowedPrivateKeyLengths and wrapPrivateKey.
340
430
  function normPrivateKeyToScalar(key: PrivKey): bigint {
341
- const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
342
- if (lengths && typeof key !== 'bigint') {
343
- if (isBytes(key)) key = bytesToHex(key);
344
- // Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
345
- if (typeof key !== 'string' || !lengths.includes(key.length))
346
- throw new Error('invalid private key');
347
- key = key.padStart(nByteLength * 2, '0');
348
- }
349
431
  let num: bigint;
350
- try {
351
- num =
352
- typeof key === 'bigint'
353
- ? key
354
- : bytesToNumberBE(ensureBytes('private key', key, nByteLength));
355
- } catch (error) {
432
+ if (typeof key === 'bigint') {
433
+ num = key;
434
+ } else {
435
+ let bytes = ensureBytes('private key', key);
436
+ if (allowedPrivateKeyLengths) {
437
+ if (!allowedPrivateKeyLengths.includes(bytes.length * 2))
438
+ throw new Error('invalid private key');
439
+ const padded = new Uint8Array(expected);
440
+ padded.set(bytes, padded.length - bytes.length);
441
+ bytes = padded;
442
+ }
443
+ try {
444
+ num = Fn.fromBytes(bytes);
445
+ } catch (error) {
446
+ throw new Error(
447
+ `invalid private key: expected ui8a of size ${expected}, got ${typeof key}`
448
+ );
449
+ }
450
+ }
451
+ if (wrapPrivateKey) num = Fn.create(num); // disabled by default, enabled for BLS
452
+ if (!Fn.isValidNot0(num)) throw new Error('invalid private key: out of range [1..N-1]');
453
+ return num;
454
+ }
455
+ return normPrivateKeyToScalar;
456
+ }
457
+
458
+ export function weierstrassN<T>(
459
+ CURVE: WeierstrassOpts<T>,
460
+ curveOpts: WeierstrassExtraOpts<T> = {}
461
+ ): ProjConstructor<T> {
462
+ const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
463
+ const { h: cofactor, n: CURVE_ORDER } = CURVE;
464
+ _validateObject(
465
+ curveOpts,
466
+ {},
467
+ {
468
+ allowInfinityPoint: 'boolean',
469
+ clearCofactor: 'function',
470
+ isTorsionFree: 'function',
471
+ fromBytes: 'function',
472
+ toBytes: 'function',
473
+ endo: 'object',
474
+ wrapPrivateKey: 'boolean',
475
+ }
476
+ );
477
+
478
+ const { endo } = curveOpts;
479
+ if (endo) {
480
+ // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
481
+ if (
482
+ !Fp.is0(CURVE.a) ||
483
+ typeof endo.beta !== 'bigint' ||
484
+ typeof endo.splitScalar !== 'function'
485
+ ) {
486
+ throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
487
+ }
488
+ }
489
+
490
+ function assertCompressionIsSupported() {
491
+ if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
492
+ }
493
+
494
+ // Implements IEEE P1363 point encoding
495
+ function pointToBytes(
496
+ _c: ProjConstructor<T>,
497
+ point: ProjPointType<T>,
498
+ isCompressed: boolean
499
+ ): Uint8Array {
500
+ const { x, y } = point.toAffine();
501
+ const bx = Fp.toBytes(x);
502
+ abool('isCompressed', isCompressed);
503
+ if (isCompressed) {
504
+ assertCompressionIsSupported();
505
+ const hasEvenY = !Fp.isOdd!(y);
506
+ return concatBytes(pprefix(hasEvenY), bx);
507
+ } else {
508
+ return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
509
+ }
510
+ }
511
+ function pointFromBytes(bytes: Uint8Array) {
512
+ abytes(bytes);
513
+ const L = Fp.BYTES;
514
+ const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
515
+ const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
516
+ const length = bytes.length;
517
+ const head = bytes[0];
518
+ const tail = bytes.subarray(1);
519
+ // No actual validation is done here: use .assertValidity()
520
+ if (length === LC && (head === 0x02 || head === 0x03)) {
521
+ const x = Fp.fromBytes(tail);
522
+ if (!Fp.isValid(x)) throw new Error('bad point: is not on curve, wrong x');
523
+ const y2 = weierstrassEquation(x); // y² = x³ + ax + b
524
+ let y: T;
525
+ try {
526
+ y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
527
+ } catch (sqrtError) {
528
+ const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
529
+ throw new Error('bad point: is not on curve, sqrt error' + err);
530
+ }
531
+ assertCompressionIsSupported();
532
+ const isYOdd = Fp.isOdd!(y); // (y & _1n) === _1n;
533
+ const isHeadOdd = (head & 1) === 1; // ECDSA-specific
534
+ if (isHeadOdd !== isYOdd) y = Fp.neg(y);
535
+ return { x, y };
536
+ } else if (length === LU && head === 0x04) {
537
+ // TODO: more checks
538
+ const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
539
+ const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
540
+ if (!isValidXY(x, y)) throw new Error('bad point: is not on curve');
541
+ return { x, y };
542
+ } else {
356
543
  throw new Error(
357
- 'invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key
544
+ `bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`
358
545
  );
359
546
  }
360
- if (wrapPrivateKey) num = mod(num, N); // disabled by default, enabled for BLS
361
- aInRange('private key', num, _1n, N); // num in range [1..N-1]
362
- return num;
547
+ }
548
+
549
+ const toBytes = curveOpts.toBytes || pointToBytes;
550
+ const fromBytes = curveOpts.fromBytes || pointFromBytes;
551
+ const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
552
+
553
+ // TODO: move top-level
554
+ /** Checks whether equation holds for given x, y: y² == x³ + ax + b */
555
+ function isValidXY(x: T, y: T): boolean {
556
+ const left = Fp.sqr(y); // y²
557
+ const right = weierstrassEquation(x); // x³ + ax + b
558
+ return Fp.eql(left, right);
559
+ }
560
+
561
+ // Validate whether the passed curve params are valid.
562
+ // Test 1: equation y² = x³ + ax + b should work for generator point.
563
+ if (!isValidXY(CURVE.Gx, CURVE.Gy)) throw new Error('bad curve params: generator point');
564
+
565
+ // Test 2: discriminant Δ part should be non-zero: 4a³ + 27b² != 0.
566
+ // Guarantees curve is genus-1, smooth (non-singular).
567
+ const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n), _4n);
568
+ const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
569
+ if (Fp.is0(Fp.add(_4a3, _27b2))) throw new Error('bad curve params: a or b');
570
+
571
+ /** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
572
+ function acoord(title: string, n: T, banZero = false) {
573
+ if (!Fp.isValid(n) || (banZero && Fp.is0(n))) throw new Error(`bad point coordinate ${title}`);
574
+ return n;
363
575
  }
364
576
 
365
577
  function aprjpoint(other: unknown) {
@@ -370,7 +582,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
370
582
 
371
583
  // Converts Projective point to affine (x, y) coordinates.
372
584
  // Can accept precomputed Z^-1 - for example, from invertBatch.
373
- // (x, y, z) ∋ (x=x/z, y=y/z)
585
+ // (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
374
586
  const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
375
587
  const { px: x, py: y, pz: z } = p;
376
588
  // Fast-path for normalized points
@@ -393,51 +605,63 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
393
605
  // (0, 1, 0) aka ZERO is invalid in most contexts.
394
606
  // In BLS, ZERO can be serialized, so we allow it.
395
607
  // (0, 0, 0) is invalid representation of ZERO.
396
- if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return;
608
+ if (curveOpts.allowInfinityPoint && !Fp.is0(p.py)) return;
397
609
  throw new Error('bad point: ZERO');
398
610
  }
399
611
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
400
612
  const { x, y } = p.toAffine();
401
- // Check if x, y are valid field elements
402
- if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
403
- const left = Fp.sqr(y); // y²
404
- const right = weierstrassEquation(x); // x³ + ax + b
405
- if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
613
+ if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not field elements');
614
+ if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
406
615
  if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
407
616
  return true;
408
617
  });
409
618
 
619
+ function finishEndo(
620
+ endoBeta: EndomorphismOpts['beta'],
621
+ k1p: Point,
622
+ k2p: Point,
623
+ k1neg: boolean,
624
+ k2neg: boolean
625
+ ) {
626
+ k2p = new Point(Fp.mul(k2p.px, endoBeta), k2p.py, k2p.pz);
627
+ k1p = negateCt(k1neg, k1p);
628
+ k2p = negateCt(k2neg, k2p);
629
+ return k1p.add(k2p);
630
+ }
631
+
410
632
  /**
411
- * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
412
- * Default Point works in 2d / affine coordinates: (x, y)
633
+ * Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
634
+ * Default Point works in 2d / affine coordinates: (x, y).
413
635
  * We're doing calculations in projective, because its operations don't require costly inversion.
414
636
  */
415
637
  class Point implements ProjPointType<T> {
638
+ // base / generator point
416
639
  static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
640
+ // zero / infinity / identity point
417
641
  static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
642
+ // fields
643
+ static readonly Fp = Fp;
644
+ static readonly Fn = Fn;
645
+
418
646
  readonly px: T;
419
647
  readonly py: T;
420
648
  readonly pz: T;
421
649
 
650
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
422
651
  constructor(px: T, py: T, pz: T) {
423
- if (px == null || !Fp.isValid(px)) throw new Error('x required');
424
- if (py == null || !Fp.isValid(py) || Fp.is0(py)) throw new Error('y required');
425
- if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
426
- this.px = px;
427
- this.py = py;
428
- this.pz = pz;
652
+ this.px = acoord('x', px);
653
+ this.py = acoord('y', py, true);
654
+ this.pz = acoord('z', pz);
429
655
  Object.freeze(this);
430
656
  }
431
657
 
432
- // Does not validate if the point is on-curve.
433
- // Use fromHex instead, or call assertValidity() later.
658
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
434
659
  static fromAffine(p: AffinePoint<T>): Point {
435
660
  const { x, y } = p || {};
436
661
  if (!p || !Fp.isValid(x) || !Fp.isValid(y)) throw new Error('invalid affine point');
437
662
  if (p instanceof Point) throw new Error('projective point not allowed');
438
- const is0 = (i: T) => Fp.eql(i, Fp.ZERO);
439
- // fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
440
- if (is0(x) && is0(y)) return Point.ZERO;
663
+ // (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
664
+ if (Fp.is0(x) && Fp.is0(y)) return Point.ZERO;
441
665
  return new Point(x, y, Fp.ONE);
442
666
  }
443
667
 
@@ -448,59 +672,67 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
448
672
  return this.toAffine().y;
449
673
  }
450
674
 
451
- /**
452
- * Takes a bunch of Projective Points but executes only one
453
- * inversion on all of them. Inversion is very slow operation,
454
- * so this improves performance massively.
455
- * Optimization: converts a list of projective points to a list of identical points with Z=1.
456
- */
457
675
  static normalizeZ(points: Point[]): Point[] {
458
- const toInv = FpInvertBatch(
459
- Fp,
460
- points.map((p) => p.pz)
461
- );
462
- return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
676
+ return normalizeZ(Point, 'pz', points);
463
677
  }
464
678
 
465
- /**
466
- * Converts hash string or Uint8Array to Point.
467
- * @param hex short/long ECDSA hex
468
- */
679
+ static fromBytes(bytes: Uint8Array): Point {
680
+ abytes(bytes);
681
+ return Point.fromHex(bytes);
682
+ }
683
+
684
+ /** Converts hash string or Uint8Array to Point. */
469
685
  static fromHex(hex: Hex): Point {
470
686
  const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
471
687
  P.assertValidity();
472
688
  return P;
473
689
  }
474
690
 
475
- // Multiplies generator point by privateKey.
691
+ /** Multiplies generator point by privateKey. */
476
692
  static fromPrivateKey(privateKey: PrivKey) {
693
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(
694
+ Fn,
695
+ curveOpts.allowedPrivateKeyLengths,
696
+ curveOpts.wrapPrivateKey
697
+ );
477
698
  return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
478
699
  }
479
700
 
480
- // Multiscalar Multiplication
701
+ /** Multiscalar Multiplication */
481
702
  static msm(points: Point[], scalars: bigint[]): Point {
482
703
  return pippenger(Point, Fn, points, scalars);
483
704
  }
484
705
 
485
- // "Private method", don't use it directly
486
- _setWindowSize(windowSize: number) {
706
+ /**
707
+ *
708
+ * @param windowSize
709
+ * @param isLazy true will defer table computation until the first multiplication
710
+ * @returns
711
+ */
712
+ precompute(windowSize: number = 8, isLazy = true): Point {
487
713
  wnaf.setWindowSize(this, windowSize);
714
+ if (!isLazy) this.multiply(_3n); // random number
715
+ return this;
488
716
  }
489
717
 
490
- // A point on curve is valid if it conforms to equation.
718
+ /** "Private method", don't use it directly */
719
+ _setWindowSize(windowSize: number) {
720
+ this.precompute(windowSize);
721
+ }
722
+
723
+ // TODO: return `this`
724
+ /** A point on curve is valid if it conforms to equation. */
491
725
  assertValidity(): void {
492
726
  assertValidMemo(this);
493
727
  }
494
728
 
495
729
  hasEvenY(): boolean {
496
730
  const { y } = this.toAffine();
497
- if (Fp.isOdd) return !Fp.isOdd(y);
498
- throw new Error("Field doesn't support isOdd");
731
+ if (!Fp.isOdd) throw new Error("Field doesn't support isOdd");
732
+ return !Fp.isOdd(y);
499
733
  }
500
734
 
501
- /**
502
- * Compare one point to another.
503
- */
735
+ /** Compare one point to another. */
504
736
  equals(other: Point): boolean {
505
737
  aprjpoint(other);
506
738
  const { px: X1, py: Y1, pz: Z1 } = this;
@@ -510,9 +742,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
510
742
  return U1 && U2;
511
743
  }
512
744
 
513
- /**
514
- * Flips point to one corresponding to (x, -y) in Affine coordinates.
515
- */
745
+ /** Flips point to one corresponding to (x, -y) in Affine coordinates. */
516
746
  negate(): Point {
517
747
  return new Point(this.px, Fp.neg(this.py), this.pz);
518
748
  }
@@ -618,48 +848,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
618
848
  return this.add(other.negate());
619
849
  }
620
850
 
621
- is0() {
851
+ is0(): boolean {
622
852
  return this.equals(Point.ZERO);
623
853
  }
624
854
 
625
- private wNAF(n: bigint): { p: Point; f: Point } {
626
- return wnaf.wNAFCached(this, n, Point.normalizeZ);
627
- }
628
-
629
- /**
630
- * Non-constant-time multiplication. Uses double-and-add algorithm.
631
- * It's faster, but should only be used when you don't care about
632
- * an exposed private key e.g. sig verification, which works over *public* keys.
633
- */
634
- multiplyUnsafe(sc: bigint): Point {
635
- const { endo, n: N } = CURVE;
636
- aInRange('scalar', sc, _0n, N);
637
- const I = Point.ZERO;
638
- if (sc === _0n) return I;
639
- if (this.is0() || sc === _1n) return this;
640
-
641
- // Case a: no endomorphism. Case b: has precomputes.
642
- if (!endo || wnaf.hasPrecomputes(this))
643
- return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
644
-
645
- // Case c: endomorphism
646
- let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
647
- let k1p = I;
648
- let k2p = I;
649
- let d: Point = this;
650
- while (k1 > _0n || k2 > _0n) {
651
- if (k1 & _1n) k1p = k1p.add(d);
652
- if (k2 & _1n) k2p = k2p.add(d);
653
- d = d.double();
654
- k1 >>= _1n;
655
- k2 >>= _1n;
656
- }
657
- if (k1neg) k1p = k1p.negate();
658
- if (k2neg) k2p = k2p.negate();
659
- k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
660
- return k1p.add(k2p);
661
- }
662
-
663
855
  /**
664
856
  * Constant time multiplication.
665
857
  * Uses wNAF method. Windowed method may be 10% faster,
@@ -670,20 +862,19 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
670
862
  * @returns New point
671
863
  */
672
864
  multiply(scalar: bigint): Point {
673
- const { endo, n: N } = CURVE;
674
- aInRange('scalar', scalar, _1n, N);
865
+ const { endo } = curveOpts;
866
+ if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
675
867
  let point: Point, fake: Point; // Fake point is used to const-time mult
868
+ const mul = (n: bigint) => wnaf.wNAFCached(this, n, Point.normalizeZ);
869
+ /** See docs for {@link EndomorphismOpts} */
676
870
  if (endo) {
677
871
  const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
678
- let { p: k1p, f: f1p } = this.wNAF(k1);
679
- let { p: k2p, f: f2p } = this.wNAF(k2);
680
- k1p = wnaf.constTimeNegate(k1neg, k1p);
681
- k2p = wnaf.constTimeNegate(k2neg, k2p);
682
- k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
683
- point = k1p.add(k2p);
684
- fake = f1p.add(f2p);
872
+ const { p: k1p, f: k1f } = mul(k1);
873
+ const { p: k2p, f: k2f } = mul(k2);
874
+ fake = k1f.add(k2f);
875
+ point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
685
876
  } else {
686
- const { p, f } = this.wNAF(scalar);
877
+ const { p, f } = mul(scalar);
687
878
  point = p;
688
879
  fake = f;
689
880
  }
@@ -692,60 +883,88 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
692
883
  }
693
884
 
694
885
  /**
695
- * Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
696
- * Not using Strauss-Shamir trick: precomputation tables are faster.
697
- * The trick could be useful if both P and Q are not G (not in our case).
698
- * @returns non-zero affine point
886
+ * Non-constant-time multiplication. Uses double-and-add algorithm.
887
+ * It's faster, but should only be used when you don't care about
888
+ * an exposed private key e.g. sig verification, which works over *public* keys.
699
889
  */
890
+ multiplyUnsafe(sc: bigint): Point {
891
+ const { endo } = curveOpts;
892
+ const p = this;
893
+ if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
894
+ if (sc === _0n || p.is0()) return Point.ZERO;
895
+ if (sc === _1n) return p; // fast-path
896
+ if (wnaf.hasPrecomputes(this)) return this.multiply(sc);
897
+ if (endo) {
898
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
899
+ // `wNAFCachedUnsafe` is 30% slower
900
+ const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
901
+ return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
902
+ } else {
903
+ return wnaf.wNAFCachedUnsafe(p, sc);
904
+ }
905
+ }
906
+
700
907
  multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
701
- const G = Point.BASE; // No Strauss-Shamir trick: we have 10% faster G precomputes
702
- const mul = (
703
- P: Point,
704
- a: bigint // Select faster multiply() method
705
- ) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
706
- const sum = mul(this, a).add(mul(Q, b));
908
+ const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
707
909
  return sum.is0() ? undefined : sum;
708
910
  }
709
911
 
710
- // Converts Projective point to affine (x, y) coordinates.
711
- // Can accept precomputed Z^-1 - for example, from invertBatch.
712
- // (x, y, z) (x=x/z, y=y/z)
713
- toAffine(iz?: T): AffinePoint<T> {
714
- return toAffineMemo(this, iz);
912
+ /**
913
+ * Converts Projective point to affine (x, y) coordinates.
914
+ * @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
915
+ */
916
+ toAffine(invertedZ?: T): AffinePoint<T> {
917
+ return toAffineMemo(this, invertedZ);
715
918
  }
919
+
920
+ /**
921
+ * Checks whether Point is free of torsion elements (is in prime subgroup).
922
+ * Always torsion-free for cofactor=1 curves.
923
+ */
716
924
  isTorsionFree(): boolean {
717
- const { h: cofactor, isTorsionFree } = CURVE;
718
- if (cofactor === _1n) return true; // No subgroups, always torsion-free
925
+ const { isTorsionFree } = curveOpts;
926
+ if (cofactor === _1n) return true;
719
927
  if (isTorsionFree) return isTorsionFree(Point, this);
720
- throw new Error('isTorsionFree() has not been declared for the elliptic curve');
928
+ return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
721
929
  }
930
+
722
931
  clearCofactor(): Point {
723
- const { h: cofactor, clearCofactor } = CURVE;
932
+ const { clearCofactor } = curveOpts;
724
933
  if (cofactor === _1n) return this; // Fast-path
725
934
  if (clearCofactor) return clearCofactor(Point, this) as Point;
726
- return this.multiplyUnsafe(CURVE.h);
935
+ return this.multiplyUnsafe(cofactor);
727
936
  }
728
937
 
729
- toRawBytes(isCompressed = true): Uint8Array {
938
+ toBytes(isCompressed = true): Uint8Array {
730
939
  abool('isCompressed', isCompressed);
731
940
  this.assertValidity();
732
941
  return toBytes(Point, this, isCompressed);
733
942
  }
734
943
 
944
+ /** @deprecated use `toBytes` */
945
+ toRawBytes(isCompressed = true): Uint8Array {
946
+ return this.toBytes(isCompressed);
947
+ }
948
+
735
949
  toHex(isCompressed = true): string {
736
- abool('isCompressed', isCompressed);
737
- return bytesToHex(this.toRawBytes(isCompressed));
950
+ return bytesToHex(this.toBytes(isCompressed));
951
+ }
952
+
953
+ toString() {
954
+ return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
738
955
  }
739
956
  }
740
- const _bits = CURVE.nBitLength;
741
- const wnaf = wNAF(Point, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
742
- return {
743
- CURVE,
744
- ProjectivePoint: Point as ProjConstructor<T>,
745
- normPrivateKeyToScalar,
746
- weierstrassEquation,
747
- isWithinCurveOrder,
748
- };
957
+ const bits = Fn.BITS;
958
+ const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
959
+ return Point;
960
+ }
961
+
962
+ // _legacyWeierstrass
963
+ /** @deprecated use `weierstrassN` */
964
+ export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
965
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
966
+ const Point = weierstrassN(CURVE, curveOpts);
967
+ return _weierstrass_new_output_to_legacy(c, Point);
749
968
  }
750
969
 
751
970
  // Instance
@@ -760,57 +979,44 @@ export interface SignatureType {
760
979
  recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
761
980
  toCompactRawBytes(): Uint8Array;
762
981
  toCompactHex(): string;
763
- toDERRawBytes(isCompressed?: boolean): Uint8Array;
764
- toDERHex(isCompressed?: boolean): string;
982
+ toDERRawBytes(): Uint8Array;
983
+ toDERHex(): string;
984
+ // toBytes(format?: string): Uint8Array;
765
985
  }
766
986
  export type RecoveredSignatureType = SignatureType & {
767
987
  readonly recovery: number;
768
988
  };
769
989
  // Static methods
770
990
  export type SignatureConstructor = {
771
- new (r: bigint, s: bigint): SignatureType;
991
+ new (r: bigint, s: bigint, recovery?: number): SignatureType;
772
992
  fromCompact(hex: Hex): SignatureType;
773
993
  fromDER(hex: Hex): SignatureType;
774
994
  };
775
- type SignatureLike = { r: bigint; s: bigint };
776
-
995
+ export type SignatureLike = { r: bigint; s: bigint };
777
996
  export type PubKey = Hex | ProjPointType<bigint>;
778
997
 
779
998
  export type CurveType = BasicWCurve<bigint> & {
780
999
  hash: CHash; // CHash not FHash because we need outputLen for DRBG
781
- hmac: HmacFnSync;
782
- randomBytes: (bytesLength?: number) => Uint8Array;
1000
+ hmac?: HmacFnSync;
1001
+ randomBytes?: (bytesLength?: number) => Uint8Array;
783
1002
  lowS?: boolean;
784
1003
  bits2int?: (bytes: Uint8Array) => bigint;
785
1004
  bits2int_modN?: (bytes: Uint8Array) => bigint;
786
1005
  };
787
1006
 
788
- function validateOpts(
789
- curve: CurveType
790
- ): Readonly<CurveType & { nByteLength: number; nBitLength: number }> {
791
- const opts = validateBasic(curve);
792
- validateObject(
793
- opts,
794
- {
795
- hash: 'hash',
796
- hmac: 'function',
797
- randomBytes: 'function',
798
- },
799
- {
800
- bits2int: 'function',
801
- bits2int_modN: 'function',
802
- lowS: 'boolean',
803
- }
804
- );
805
- return Object.freeze({ lowS: true, ...opts } as const);
1007
+ // Points start with byte 0x02 when y is even; otherwise 0x03
1008
+ function pprefix(hasEvenY: boolean): Uint8Array {
1009
+ return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
806
1010
  }
807
1011
 
808
1012
  export type CurveFn = {
809
- CURVE: ReturnType<typeof validateOpts>;
1013
+ CURVE: CurvePointsType<bigint>;
810
1014
  getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
811
1015
  getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
812
1016
  sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
813
1017
  verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
1018
+ Point: ProjConstructor<bigint>;
1019
+ /** @deprecated use `Point` */
814
1020
  ProjectivePoint: ProjConstructor<bigint>;
815
1021
  Signature: SignatureConstructor;
816
1022
  utils: {
@@ -821,80 +1027,30 @@ export type CurveFn = {
821
1027
  };
822
1028
  };
823
1029
 
824
- /**
825
- * Creates short weierstrass curve and ECDSA signature methods for it.
826
- * @example
827
- * import { Field } from '@noble/curves/abstract/modular';
828
- * // Before that, define BigInt-s: a, b, p, n, Gx, Gy
829
- * const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
830
- */
831
- export function weierstrass(curveDef: CurveType): CurveFn {
832
- const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
833
- const { Fp, n: CURVE_ORDER } = CURVE;
834
- const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
835
- const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
836
-
837
- function modN(a: bigint) {
838
- return mod(a, CURVE_ORDER);
839
- }
840
- function invN(a: bigint) {
841
- return invert(a, CURVE_ORDER);
842
- }
1030
+ export function ecdsa(
1031
+ Point: ProjConstructor<bigint>,
1032
+ ecdsaOpts: ECDSAOpts,
1033
+ curveOpts: WeierstrassExtraOpts<bigint> = {}
1034
+ ): ECDSA {
1035
+ _validateObject(
1036
+ ecdsaOpts,
1037
+ { hash: 'function' },
1038
+ {
1039
+ hmac: 'function',
1040
+ lowS: 'boolean',
1041
+ randomBytes: 'function',
1042
+ bits2int: 'function',
1043
+ bits2int_modN: 'function',
1044
+ }
1045
+ );
843
1046
 
844
- const {
845
- ProjectivePoint: Point,
846
- normPrivateKeyToScalar,
847
- weierstrassEquation,
848
- isWithinCurveOrder,
849
- } = weierstrassPoints({
850
- ...CURVE,
851
- toBytes(_c, point, isCompressed: boolean): Uint8Array {
852
- const a = point.toAffine();
853
- const x = Fp.toBytes(a.x);
854
- const cat = concatBytes;
855
- abool('isCompressed', isCompressed);
856
- if (isCompressed) {
857
- return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
858
- } else {
859
- return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
860
- }
861
- },
862
- fromBytes(bytes: Uint8Array) {
863
- const len = bytes.length;
864
- const head = bytes[0];
865
- const tail = bytes.subarray(1);
866
- // this.assertValidity() is done inside of fromHex
867
- if (len === compressedLen && (head === 0x02 || head === 0x03)) {
868
- const x = bytesToNumberBE(tail);
869
- if (!inRange(x, _1n, Fp.ORDER)) throw new Error('Point is not on curve');
870
- const y2 = weierstrassEquation(x); // y² = x³ + ax + b
871
- let y: bigint;
872
- try {
873
- y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
874
- } catch (sqrtError) {
875
- const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
876
- throw new Error('Point is not on curve' + suffix);
877
- }
878
- const isYOdd = (y & _1n) === _1n;
879
- // ECDSA
880
- const isHeadOdd = (head & 1) === 1;
881
- if (isHeadOdd !== isYOdd) y = Fp.neg(y);
882
- return { x, y };
883
- } else if (len === uncompressedLen && head === 0x04) {
884
- const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
885
- const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
886
- return { x, y };
887
- } else {
888
- const cl = compressedLen;
889
- const ul = uncompressedLen;
890
- throw new Error(
891
- 'invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len
892
- );
893
- }
894
- },
895
- });
896
- const numToNByteHex = (num: bigint): string =>
897
- bytesToHex(numberToBytesBE(num, CURVE.nByteLength));
1047
+ const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
1048
+ const hmac_: HmacFnSync =
1049
+ ecdsaOpts.hmac ||
1050
+ (((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1051
+
1052
+ const { Fp, Fn } = Point;
1053
+ const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
898
1054
 
899
1055
  function isBiggerThanHalfOrder(number: bigint) {
900
1056
  const HALF = CURVE_ORDER >> _1n;
@@ -902,10 +1058,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
902
1058
  }
903
1059
 
904
1060
  function normalizeS(s: bigint) {
905
- return isBiggerThanHalfOrder(s) ? modN(-s) : s;
1061
+ return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
1062
+ }
1063
+ function aValidRS(title: string, num: bigint) {
1064
+ if (!Fn.isValidNot0(num))
1065
+ throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
906
1066
  }
907
- // slice bytes num
908
- const slcNum = (b: Uint8Array, from: number, to: number) => bytesToNumberBE(b.slice(from, to));
909
1067
 
910
1068
  /**
911
1069
  * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
@@ -915,8 +1073,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
915
1073
  readonly s: bigint;
916
1074
  readonly recovery?: number;
917
1075
  constructor(r: bigint, s: bigint, recovery?: number) {
918
- aInRange('r', r, _1n, CURVE_ORDER); // r in [1..N]
919
- aInRange('s', s, _1n, CURVE_ORDER); // s in [1..N]
1076
+ aValidRS('r', r); // r in [1..N-1]
1077
+ aValidRS('s', s); // s in [1..N-1]
920
1078
  this.r = r;
921
1079
  this.s = s;
922
1080
  if (recovery != null) this.recovery = recovery;
@@ -925,9 +1083,9 @@ export function weierstrass(curveDef: CurveType): CurveFn {
925
1083
 
926
1084
  // pair (bytes of r, bytes of s)
927
1085
  static fromCompact(hex: Hex) {
928
- const l = CURVE.nByteLength;
929
- hex = ensureBytes('compactSignature', hex, l * 2);
930
- return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l));
1086
+ const L = Fn.BYTES;
1087
+ const b = ensureBytes('compactSignature', hex, L * 2);
1088
+ return new Signature(Fn.fromBytes(b.subarray(0, L)), Fn.fromBytes(b.subarray(L, L * 2)));
931
1089
  }
932
1090
 
933
1091
  // DER encoded ECDSA signature
@@ -947,19 +1105,34 @@ export function weierstrass(curveDef: CurveType): CurveFn {
947
1105
  return new Signature(this.r, this.s, recovery) as RecoveredSignature;
948
1106
  }
949
1107
 
1108
+ // ProjPointType<bigint>
950
1109
  recoverPublicKey(msgHash: Hex): typeof Point.BASE {
1110
+ const FIELD_ORDER = Fp.ORDER;
951
1111
  const { r, s, recovery: rec } = this;
952
- const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
953
1112
  if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
954
- const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
955
- if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
956
- const prefix = (rec & 1) === 0 ? '02' : '03';
957
- const R = Point.fromHex(prefix + numToNByteHex(radj));
958
- const ir = invN(radj); // r^-1
959
- const u1 = modN(-h * ir); // -hr^-1
960
- const u2 = modN(s * ir); // sr^-1
961
- const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
962
- if (!Q) throw new Error('point at infinify'); // unsafe is fine: no priv data leaked
1113
+
1114
+ // ECDSA recovery is hard for cofactor > 1 curves.
1115
+ // In sign, `r = q.x mod n`, and here we recover q.x from r.
1116
+ // While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
1117
+ // However, for cofactor>1, r+n may not get q.x:
1118
+ // r+n*i would need to be done instead where i is unknown.
1119
+ // To easily get i, we either need to:
1120
+ // a. increase amount of valid recid values (4, 5...); OR
1121
+ // b. prohibit non-prime-order signatures (recid > 1).
1122
+ const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
1123
+ if (hasCofactor && rec > 1) throw new Error('recovery id is ambiguous for h>1 curve');
1124
+
1125
+ const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
1126
+ if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
1127
+ const x = Fp.toBytes(radj);
1128
+ const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
1129
+ const ir = Fn.inv(radj); // r^-1
1130
+ const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
1131
+ const u1 = Fn.create(-h * ir); // -hr^-1
1132
+ const u2 = Fn.create(s * ir); // sr^-1
1133
+ // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
1134
+ const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
1135
+ if (Q.is0()) throw new Error('point at infinify');
963
1136
  Q.assertValidity();
964
1137
  return Q;
965
1138
  }
@@ -970,27 +1143,39 @@ export function weierstrass(curveDef: CurveType): CurveFn {
970
1143
  }
971
1144
 
972
1145
  normalizeS() {
973
- return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this;
1146
+ return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1147
+ }
1148
+
1149
+ toBytes(format: 'compact' | 'der') {
1150
+ if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
1151
+ if (format === 'der') return hexToBytes(DER.hexFromSig(this));
1152
+ throw new Error('invalid format');
974
1153
  }
975
1154
 
976
1155
  // DER-encoded
977
1156
  toDERRawBytes() {
978
- return hexToBytes(this.toDERHex());
1157
+ return this.toBytes('der');
979
1158
  }
980
1159
  toDERHex() {
981
- return DER.hexFromSig(this);
1160
+ return bytesToHex(this.toBytes('der'));
982
1161
  }
983
1162
 
984
1163
  // padded bytes of r, then padded bytes of s
985
1164
  toCompactRawBytes() {
986
- return hexToBytes(this.toCompactHex());
1165
+ return this.toBytes('compact');
987
1166
  }
988
1167
  toCompactHex() {
989
- return numToNByteHex(this.r) + numToNByteHex(this.s);
1168
+ return bytesToHex(this.toBytes('compact'));
990
1169
  }
991
1170
  }
992
1171
  type RecoveredSignature = Signature & { recovery: number };
993
1172
 
1173
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(
1174
+ Fn,
1175
+ curveOpts.allowedPrivateKeyLengths,
1176
+ curveOpts.wrapPrivateKey
1177
+ );
1178
+
994
1179
  const utils = {
995
1180
  isValidPrivateKey(privateKey: PrivKey) {
996
1181
  try {
@@ -1007,22 +1192,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1007
1192
  * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1008
1193
  */
1009
1194
  randomPrivateKey: (): Uint8Array => {
1010
- const length = getMinHashLength(CURVE.n);
1011
- return mapHashToField(CURVE.randomBytes(length), CURVE.n);
1195
+ const n = CURVE_ORDER;
1196
+ return mapHashToField(randomBytes_(getMinHashLength(n)), n);
1012
1197
  },
1013
1198
 
1014
- /**
1015
- * Creates precompute table for an arbitrary EC point. Makes point "cached".
1016
- * Allows to massively speed-up `point.multiply(scalar)`.
1017
- * @returns cached point
1018
- * @example
1019
- * const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
1020
- * fast.multiply(privKey); // much faster ECDH now
1021
- */
1022
1199
  precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
1023
- point._setWindowSize(windowSize);
1024
- point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
1025
- return point;
1200
+ return point.precompute(windowSize, false);
1026
1201
  },
1027
1202
  };
1028
1203
 
@@ -1033,20 +1208,25 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1033
1208
  * @returns Public key, full when isCompressed=false; short when isCompressed=true
1034
1209
  */
1035
1210
  function getPublicKey(privateKey: PrivKey, isCompressed = true): Uint8Array {
1036
- return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
1211
+ return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
1037
1212
  }
1038
1213
 
1039
1214
  /**
1040
1215
  * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1041
1216
  */
1042
- function isProbPub(item: PrivKey | PubKey): boolean {
1043
- const arr = isBytes(item);
1044
- const str = typeof item === 'string';
1045
- const len = (arr || str) && (item as Hex).length;
1046
- if (arr) return len === compressedLen || len === uncompressedLen;
1047
- if (str) return len === 2 * compressedLen || len === 2 * uncompressedLen;
1217
+ function isProbPub(item: PrivKey | PubKey): boolean | undefined {
1218
+ if (typeof item === 'bigint') return false;
1048
1219
  if (item instanceof Point) return true;
1049
- return false;
1220
+ const arr = ensureBytes('key', item);
1221
+ const length = arr.length;
1222
+ const L = Fp.BYTES;
1223
+ const LC = L + 1; // e.g. 33 for 32
1224
+ const LU = 2 * L + 1; // e.g. 65 for 32
1225
+ if (curveOpts.allowedPrivateKeyLengths || Fn.BYTES === LC) {
1226
+ return undefined;
1227
+ } else {
1228
+ return length === LC || length === LU;
1229
+ }
1050
1230
  }
1051
1231
 
1052
1232
  /**
@@ -1060,10 +1240,10 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1060
1240
  * @returns shared public key
1061
1241
  */
1062
1242
  function getSharedSecret(privateA: PrivKey, publicB: Hex, isCompressed = true): Uint8Array {
1063
- if (isProbPub(privateA)) throw new Error('first arg must be private key');
1064
- if (!isProbPub(publicB)) throw new Error('second arg must be public key');
1243
+ if (isProbPub(privateA) === true) throw new Error('first arg must be private key');
1244
+ if (isProbPub(publicB) === false) throw new Error('second arg must be public key');
1065
1245
  const b = Point.fromHex(publicB); // check for being on-curve
1066
- return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
1246
+ return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
1067
1247
  }
1068
1248
 
1069
1249
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
@@ -1071,30 +1251,30 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1071
1251
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
1072
1252
  // int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
1073
1253
  const bits2int =
1074
- CURVE.bits2int ||
1254
+ ecdsaOpts.bits2int ||
1075
1255
  function (bytes: Uint8Array): bigint {
1076
- // Our custom check "just in case"
1256
+ // Our custom check "just in case", for protection against DoS
1077
1257
  if (bytes.length > 8192) throw new Error('input is too large');
1078
1258
  // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
1079
1259
  // for some cases, since bytes.length * 8 is not actual bitLength.
1080
1260
  const num = bytesToNumberBE(bytes); // check for == u8 done here
1081
- const delta = bytes.length * 8 - CURVE.nBitLength; // truncate to nBitLength leftmost bits
1261
+ const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
1082
1262
  return delta > 0 ? num >> BigInt(delta) : num;
1083
1263
  };
1084
1264
  const bits2int_modN =
1085
- CURVE.bits2int_modN ||
1265
+ ecdsaOpts.bits2int_modN ||
1086
1266
  function (bytes: Uint8Array): bigint {
1087
- return modN(bits2int(bytes)); // can't use bytesToNumberBE here
1267
+ return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
1088
1268
  };
1089
1269
  // NOTE: pads output with zero as per spec
1090
- const ORDER_MASK = bitMask(CURVE.nBitLength);
1270
+ const ORDER_MASK = bitMask(fnBits);
1091
1271
  /**
1092
1272
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
1093
1273
  */
1094
1274
  function int2octets(num: bigint): Uint8Array {
1095
- aInRange('num < 2^' + CURVE.nBitLength, num, _0n, ORDER_MASK);
1096
- // works with order, can have different size than numToField!
1097
- return numberToBytesBE(num, CURVE.nByteLength);
1275
+ // IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
1276
+ aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
1277
+ return Fn.toBytes(num);
1098
1278
  }
1099
1279
 
1100
1280
  // Steps A, D of RFC6979 3.2
@@ -1105,7 +1285,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1105
1285
  function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
1106
1286
  if (['recovered', 'canonical'].some((k) => k in opts))
1107
1287
  throw new Error('sign() legacy options not supported');
1108
- const { hash, randomBytes } = CURVE;
1288
+ const { hash } = ecdsaOpts;
1109
1289
  let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
1110
1290
  if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1111
1291
  msgHash = ensureBytes('msgHash', msgHash);
@@ -1113,7 +1293,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1113
1293
  if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
1114
1294
 
1115
1295
  // We can't later call bits2octets, since nested bits2int is broken for curves
1116
- // with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1296
+ // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1117
1297
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1118
1298
  const h1int = bits2int_modN(msgHash);
1119
1299
  const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
@@ -1121,24 +1301,25 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1121
1301
  // extraEntropy. RFC6979 3.6: additional k' (optional).
1122
1302
  if (ent != null && ent !== false) {
1123
1303
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
1124
- const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is
1304
+ const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
1125
1305
  seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
1126
1306
  }
1127
1307
  const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
1128
1308
  const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
1129
1309
  // Converts signature params into point w r/s, checks result for validity.
1310
+ // Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
1311
+ // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
1312
+ // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
1130
1313
  function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
1131
1314
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
1315
+ // Important: all mod() calls here must be done over N
1132
1316
  const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
1133
- if (!isWithinCurveOrder(k)) return; // Important: all mod() calls here must be done over N
1134
- const ik = invN(k); // k^-1 mod n
1317
+ if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
1318
+ const ik = Fn.inv(k); // k^-1 mod n
1135
1319
  const q = Point.BASE.multiply(k).toAffine(); // q = Gk
1136
- const r = modN(q.x); // r = q.x mod n
1320
+ const r = Fn.create(q.x); // r = q.x mod n
1137
1321
  if (r === _0n) return;
1138
- // Can use scalar blinding b^-1(bm + bdr) where b [1,q−1] according to
1139
- // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
1140
- // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
1141
- const s = modN(ik * modN(m + r * d)); // Not using blinding here
1322
+ const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
1142
1323
  if (s === _0n) return;
1143
1324
  let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
1144
1325
  let normS = s;
@@ -1150,8 +1331,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1150
1331
  }
1151
1332
  return { seed, k2sig };
1152
1333
  }
1153
- const defaultSigOpts: SignOpts = { lowS: CURVE.lowS, prehash: false };
1154
- const defaultVerOpts: VerOpts = { lowS: CURVE.lowS, prehash: false };
1334
+ const defaultSigOpts: SignOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1335
+ const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1155
1336
 
1156
1337
  /**
1157
1338
  * Signs message hash with a private key.
@@ -1168,14 +1349,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1168
1349
  */
1169
1350
  function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
1170
1351
  const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
1171
- const C = CURVE;
1172
- const drbg = createHmacDrbg<RecoveredSignature>(C.hash.outputLen, C.nByteLength, C.hmac);
1352
+ const drbg = createHmacDrbg<RecoveredSignature>(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
1173
1353
  return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1174
1354
  }
1175
1355
 
1176
1356
  // Enable precomputes. Slows down first publicKey computation by 20ms.
1177
- Point.BASE._setWindowSize(8);
1178
- // utils.precompute(8, ProjectivePoint.BASE)
1357
+ Point.BASE.precompute(8);
1179
1358
 
1180
1359
  /**
1181
1360
  * Verifies a signature against message hash and public key.
@@ -1199,13 +1378,16 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1199
1378
  const sg = signature;
1200
1379
  msgHash = ensureBytes('msgHash', msgHash);
1201
1380
  publicKey = ensureBytes('publicKey', publicKey);
1202
- const { lowS, prehash, format } = opts;
1203
1381
 
1204
- // Verify opts, deduce signature format
1382
+ // Verify opts
1205
1383
  validateSigVerOpts(opts);
1384
+ const { lowS, prehash, format } = opts;
1385
+
1386
+ // TODO: remove
1206
1387
  if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
1207
- if (format !== undefined && format !== 'compact' && format !== 'der')
1208
- throw new Error('format must be compact or der');
1388
+
1389
+ if (format !== undefined && !['compact', 'der', 'js'].includes(format))
1390
+ throw new Error('format must be "compact", "der" or "js"');
1209
1391
  const isHex = typeof sg === 'string' || isBytes(sg);
1210
1392
  const isObj =
1211
1393
  !isHex &&
@@ -1216,14 +1398,31 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1216
1398
  typeof sg.s === 'bigint';
1217
1399
  if (!isHex && !isObj)
1218
1400
  throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1219
-
1220
1401
  let _sig: Signature | undefined = undefined;
1221
1402
  let P: ProjPointType<bigint>;
1403
+
1404
+ // deduce signature format
1222
1405
  try {
1223
- if (isObj) _sig = new Signature(sg.r, sg.s);
1406
+ // if (format === 'js') {
1407
+ // if (sg != null && !isBytes(sg)) _sig = new Signature(sg.r, sg.s);
1408
+ // } else if (format === 'compact') {
1409
+ // _sig = Signature.fromCompact(sg);
1410
+ // } else if (format === 'der') {
1411
+ // _sig = Signature.fromDER(sg);
1412
+ // } else {
1413
+ // throw new Error('invalid format');
1414
+ // }
1415
+ if (isObj) {
1416
+ if (format === undefined || format === 'js') {
1417
+ _sig = new Signature(sg.r, sg.s);
1418
+ } else {
1419
+ throw new Error('invalid format');
1420
+ }
1421
+ }
1224
1422
  if (isHex) {
1225
- // Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
1226
- // Since DER can also be 2*nByteLength bytes, we check for it first.
1423
+ // TODO: remove this malleable check
1424
+ // Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
1425
+ // Since DER can also be 2*Fn.BYTES bytes, we check for it first.
1227
1426
  try {
1228
1427
  if (format !== 'compact') _sig = Signature.fromDER(sg);
1229
1428
  } catch (derError) {
@@ -1237,27 +1436,118 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1237
1436
  }
1238
1437
  if (!_sig) return false;
1239
1438
  if (lowS && _sig.hasHighS()) return false;
1240
- if (prehash) msgHash = CURVE.hash(msgHash);
1439
+ // todo: optional.hash => hash
1440
+ if (prehash) msgHash = ecdsaOpts.hash(msgHash);
1241
1441
  const { r, s } = _sig;
1242
1442
  const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1243
- const is = invN(s); // s^-1
1244
- const u1 = modN(h * is); // u1 = hs^-1 mod n
1245
- const u2 = modN(r * is); // u2 = rs^-1 mod n
1246
- const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine(); // R = u1⋅G + u2⋅P
1247
- if (!R) return false;
1248
- const v = modN(R.x);
1443
+ const is = Fn.inv(s); // s^-1
1444
+ const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1445
+ const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1446
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1447
+ if (R.is0()) return false;
1448
+ const v = Fn.create(R.x); // v = r.x mod n
1249
1449
  return v === r;
1250
1450
  }
1251
- return {
1252
- CURVE,
1451
+ // TODO: clarify API for cloning .clone({hash: sha512}) ? .createWith({hash: sha512})?
1452
+ // const clone = (hash: CHash): ECDSA => ecdsa(Point, { ...ecdsaOpts, ...getHash(hash) }, curveOpts);
1453
+ return Object.freeze({
1253
1454
  getPublicKey,
1254
1455
  getSharedSecret,
1255
1456
  sign,
1256
1457
  verify,
1257
- ProjectivePoint: Point,
1258
- Signature,
1259
1458
  utils,
1459
+ Point,
1460
+ Signature,
1461
+ });
1462
+ }
1463
+
1464
+ export type WsPointComposed<T> = {
1465
+ CURVE: WeierstrassOpts<T>;
1466
+ curveOpts: WeierstrassExtraOpts<T>;
1467
+ };
1468
+ export type WsComposed = {
1469
+ CURVE: WeierstrassOpts<bigint>;
1470
+ curveOpts: WeierstrassExtraOpts<bigint>;
1471
+ ecdsaOpts: ECDSAOpts;
1472
+ };
1473
+ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
1474
+ const CURVE: WeierstrassOpts<T> = {
1475
+ a: c.a,
1476
+ b: c.b,
1477
+ p: c.Fp.ORDER,
1478
+ n: c.n,
1479
+ h: c.h,
1480
+ Gx: c.Gx,
1481
+ Gy: c.Gy,
1260
1482
  };
1483
+ const Fp = c.Fp;
1484
+ const Fn = Field(CURVE.n, c.nBitLength);
1485
+ const curveOpts: WeierstrassExtraOpts<T> = {
1486
+ Fp,
1487
+ Fn,
1488
+ allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
1489
+ allowInfinityPoint: c.allowInfinityPoint,
1490
+ endo: c.endo,
1491
+ wrapPrivateKey: c.wrapPrivateKey,
1492
+ isTorsionFree: c.isTorsionFree,
1493
+ clearCofactor: c.clearCofactor,
1494
+ fromBytes: c.fromBytes,
1495
+ toBytes: c.toBytes,
1496
+ };
1497
+ return { CURVE, curveOpts };
1498
+ }
1499
+ function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
1500
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1501
+ const ecdsaOpts: ECDSAOpts = {
1502
+ hash: c.hash,
1503
+ hmac: c.hmac,
1504
+ randomBytes: c.randomBytes,
1505
+ lowS: c.lowS,
1506
+ bits2int: c.bits2int,
1507
+ bits2int_modN: c.bits2int_modN,
1508
+ };
1509
+ return { CURVE, curveOpts, ecdsaOpts };
1510
+ }
1511
+ function _weierstrass_new_output_to_legacy<T>(
1512
+ c: CurvePointsType<T>,
1513
+ Point: ProjConstructor<T>
1514
+ ): CurvePointsRes<T> {
1515
+ const { Fp, Fn } = Point;
1516
+ // TODO: remove
1517
+ function isWithinCurveOrder(num: bigint): boolean {
1518
+ return inRange(num, _1n, Fn.ORDER);
1519
+ }
1520
+ const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
1521
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(
1522
+ Fn,
1523
+ c.allowedPrivateKeyLengths,
1524
+ c.wrapPrivateKey
1525
+ );
1526
+ return Object.assign(
1527
+ {},
1528
+ {
1529
+ CURVE: c,
1530
+ Point: Point,
1531
+ ProjectivePoint: Point,
1532
+ normPrivateKeyToScalar,
1533
+ weierstrassEquation,
1534
+ isWithinCurveOrder,
1535
+ }
1536
+ );
1537
+ }
1538
+ function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
1539
+ return Object.assign({}, ecdsa, {
1540
+ ProjectivePoint: ecdsa.Point,
1541
+ CURVE: c,
1542
+ });
1543
+ }
1544
+
1545
+ // _ecdsa_legacy
1546
+ export function weierstrass(c: CurveType): CurveFn {
1547
+ const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
1548
+ const Point = weierstrassN(CURVE, curveOpts);
1549
+ const signs = ecdsa(Point, ecdsaOpts, curveOpts);
1550
+ return _ecdsa_new_output_to_legacy(c, signs);
1261
1551
  }
1262
1552
 
1263
1553
  /**
@@ -1353,30 +1643,31 @@ export function mapToCurveSimpleSWU<T>(
1353
1643
  }
1354
1644
  ): (u: T) => { x: T; y: T } {
1355
1645
  validateField(Fp);
1356
- if (!Fp.isValid(opts.A) || !Fp.isValid(opts.B) || !Fp.isValid(opts.Z))
1646
+ const { A, B, Z } = opts;
1647
+ if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
1357
1648
  throw new Error('mapToCurveSimpleSWU: invalid opts');
1358
- const sqrtRatio = SWUFpSqrtRatio(Fp, opts.Z);
1359
- if (!Fp.isOdd) throw new Error('Fp.isOdd is not implemented!');
1649
+ const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
1650
+ if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
1360
1651
  // Input: u, an element of F.
1361
1652
  // Output: (x, y), a point on E.
1362
1653
  return (u: T): { x: T; y: T } => {
1363
1654
  // prettier-ignore
1364
1655
  let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
1365
1656
  tv1 = Fp.sqr(u); // 1. tv1 = u^2
1366
- tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
1657
+ tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
1367
1658
  tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
1368
1659
  tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
1369
1660
  tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
1370
- tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
1371
- tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1372
- tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
1661
+ tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
1662
+ tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1663
+ tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
1373
1664
  tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
1374
1665
  tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
1375
- tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
1666
+ tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
1376
1667
  tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
1377
1668
  tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
1378
1669
  tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
1379
- tv5 = Fp.mul(tv6, opts.B); // 15. tv5 = B * tv6
1670
+ tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
1380
1671
  tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
1381
1672
  x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
1382
1673
  const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)