@noble/curves 1.9.2 → 1.9.4

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 (179) hide show
  1. package/README.md +186 -206
  2. package/_shortw_utils.d.ts +1 -0
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +1 -0
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +87 -62
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +170 -163
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +109 -23
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +158 -156
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +126 -70
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +212 -62
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/hash-to-curve.d.ts +8 -4
  19. package/abstract/hash-to-curve.d.ts.map +1 -1
  20. package/abstract/hash-to-curve.js +23 -11
  21. package/abstract/hash-to-curve.js.map +1 -1
  22. package/abstract/modular.d.ts +8 -3
  23. package/abstract/modular.d.ts.map +1 -1
  24. package/abstract/modular.js +79 -35
  25. package/abstract/modular.js.map +1 -1
  26. package/abstract/montgomery.d.ts +17 -4
  27. package/abstract/montgomery.d.ts.map +1 -1
  28. package/abstract/montgomery.js +19 -3
  29. package/abstract/montgomery.js.map +1 -1
  30. package/abstract/tower.d.ts +3 -3
  31. package/abstract/tower.d.ts.map +1 -1
  32. package/abstract/tower.js.map +1 -1
  33. package/abstract/weierstrass.d.ts +145 -118
  34. package/abstract/weierstrass.d.ts.map +1 -1
  35. package/abstract/weierstrass.js +415 -336
  36. package/abstract/weierstrass.js.map +1 -1
  37. package/bls12-381.d.ts.map +1 -1
  38. package/bls12-381.js +4 -4
  39. package/bls12-381.js.map +1 -1
  40. package/ed25519.d.ts +52 -66
  41. package/ed25519.d.ts.map +1 -1
  42. package/ed25519.js +128 -155
  43. package/ed25519.js.map +1 -1
  44. package/ed448.d.ts +57 -58
  45. package/ed448.d.ts.map +1 -1
  46. package/ed448.js +114 -131
  47. package/ed448.js.map +1 -1
  48. package/esm/_shortw_utils.d.ts +1 -0
  49. package/esm/_shortw_utils.d.ts.map +1 -1
  50. package/esm/_shortw_utils.js +1 -0
  51. package/esm/_shortw_utils.js.map +1 -1
  52. package/esm/abstract/bls.d.ts +87 -62
  53. package/esm/abstract/bls.d.ts.map +1 -1
  54. package/esm/abstract/bls.js +171 -164
  55. package/esm/abstract/bls.js.map +1 -1
  56. package/esm/abstract/curve.d.ts +109 -23
  57. package/esm/abstract/curve.d.ts.map +1 -1
  58. package/esm/abstract/curve.js +156 -155
  59. package/esm/abstract/curve.js.map +1 -1
  60. package/esm/abstract/edwards.d.ts +126 -70
  61. package/esm/abstract/edwards.d.ts.map +1 -1
  62. package/esm/abstract/edwards.js +210 -62
  63. package/esm/abstract/edwards.js.map +1 -1
  64. package/esm/abstract/hash-to-curve.d.ts +8 -4
  65. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  66. package/esm/abstract/hash-to-curve.js +22 -11
  67. package/esm/abstract/hash-to-curve.js.map +1 -1
  68. package/esm/abstract/modular.d.ts +8 -3
  69. package/esm/abstract/modular.d.ts.map +1 -1
  70. package/esm/abstract/modular.js +79 -35
  71. package/esm/abstract/modular.js.map +1 -1
  72. package/esm/abstract/montgomery.d.ts +17 -4
  73. package/esm/abstract/montgomery.d.ts.map +1 -1
  74. package/esm/abstract/montgomery.js +19 -3
  75. package/esm/abstract/montgomery.js.map +1 -1
  76. package/esm/abstract/tower.d.ts +3 -3
  77. package/esm/abstract/tower.d.ts.map +1 -1
  78. package/esm/abstract/tower.js.map +1 -1
  79. package/esm/abstract/weierstrass.d.ts +145 -118
  80. package/esm/abstract/weierstrass.d.ts.map +1 -1
  81. package/esm/abstract/weierstrass.js +412 -334
  82. package/esm/abstract/weierstrass.js.map +1 -1
  83. package/esm/bls12-381.d.ts.map +1 -1
  84. package/esm/bls12-381.js +4 -4
  85. package/esm/bls12-381.js.map +1 -1
  86. package/esm/ed25519.d.ts +52 -66
  87. package/esm/ed25519.d.ts.map +1 -1
  88. package/esm/ed25519.js +131 -157
  89. package/esm/ed25519.js.map +1 -1
  90. package/esm/ed448.d.ts +57 -58
  91. package/esm/ed448.d.ts.map +1 -1
  92. package/esm/ed448.js +116 -132
  93. package/esm/ed448.js.map +1 -1
  94. package/esm/index.js +7 -9
  95. package/esm/index.js.map +1 -1
  96. package/esm/jubjub.d.ts +3 -3
  97. package/esm/jubjub.d.ts.map +1 -1
  98. package/esm/jubjub.js +3 -3
  99. package/esm/jubjub.js.map +1 -1
  100. package/esm/misc.d.ts +3 -5
  101. package/esm/misc.d.ts.map +1 -1
  102. package/esm/misc.js +0 -3
  103. package/esm/misc.js.map +1 -1
  104. package/esm/nist.d.ts +0 -6
  105. package/esm/nist.d.ts.map +1 -1
  106. package/esm/nist.js +31 -15
  107. package/esm/nist.js.map +1 -1
  108. package/esm/p256.d.ts +4 -0
  109. package/esm/p256.d.ts.map +1 -1
  110. package/esm/p256.js +4 -0
  111. package/esm/p256.js.map +1 -1
  112. package/esm/p384.d.ts +4 -1
  113. package/esm/p384.d.ts.map +1 -1
  114. package/esm/p384.js +4 -1
  115. package/esm/p384.js.map +1 -1
  116. package/esm/p521.d.ts +4 -0
  117. package/esm/p521.d.ts.map +1 -1
  118. package/esm/p521.js +4 -0
  119. package/esm/p521.js.map +1 -1
  120. package/esm/secp256k1.d.ts +32 -15
  121. package/esm/secp256k1.d.ts.map +1 -1
  122. package/esm/secp256k1.js +72 -67
  123. package/esm/secp256k1.js.map +1 -1
  124. package/esm/utils.d.ts +1 -1
  125. package/esm/utils.js +1 -1
  126. package/index.js +7 -9
  127. package/index.js.map +1 -1
  128. package/jubjub.d.ts +3 -3
  129. package/jubjub.d.ts.map +1 -1
  130. package/jubjub.js +3 -3
  131. package/jubjub.js.map +1 -1
  132. package/misc.d.ts +3 -5
  133. package/misc.d.ts.map +1 -1
  134. package/misc.js +0 -3
  135. package/misc.js.map +1 -1
  136. package/nist.d.ts +0 -6
  137. package/nist.d.ts.map +1 -1
  138. package/nist.js +31 -15
  139. package/nist.js.map +1 -1
  140. package/p256.d.ts +4 -0
  141. package/p256.d.ts.map +1 -1
  142. package/p256.js +4 -0
  143. package/p256.js.map +1 -1
  144. package/p384.d.ts +4 -1
  145. package/p384.d.ts.map +1 -1
  146. package/p384.js +4 -1
  147. package/p384.js.map +1 -1
  148. package/p521.d.ts +4 -0
  149. package/p521.d.ts.map +1 -1
  150. package/p521.js +4 -0
  151. package/p521.js.map +1 -1
  152. package/package.json +4 -2
  153. package/secp256k1.d.ts +32 -15
  154. package/secp256k1.d.ts.map +1 -1
  155. package/secp256k1.js +70 -65
  156. package/secp256k1.js.map +1 -1
  157. package/src/_shortw_utils.ts +1 -0
  158. package/src/abstract/bls.ts +319 -257
  159. package/src/abstract/curve.ts +226 -170
  160. package/src/abstract/edwards.ts +352 -139
  161. package/src/abstract/hash-to-curve.ts +33 -16
  162. package/src/abstract/modular.ts +86 -35
  163. package/src/abstract/montgomery.ts +36 -9
  164. package/src/abstract/tower.ts +4 -4
  165. package/src/abstract/weierstrass.ts +570 -476
  166. package/src/bls12-381.ts +28 -20
  167. package/src/ed25519.ts +161 -179
  168. package/src/ed448.ts +150 -156
  169. package/src/index.ts +7 -9
  170. package/src/jubjub.ts +3 -3
  171. package/src/misc.ts +3 -7
  172. package/src/nist.ts +40 -16
  173. package/src/p256.ts +4 -0
  174. package/src/p384.ts +4 -2
  175. package/src/p521.ts +4 -0
  176. package/src/secp256k1.ts +91 -73
  177. package/src/utils.ts +1 -1
  178. package/utils.d.ts +1 -1
  179. package/utils.js +1 -1
@@ -26,11 +26,13 @@
26
26
  */
27
27
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
28
28
  import { hmac } from '@noble/hashes/hmac.js';
29
+ import { ahash } from '@noble/hashes/utils';
29
30
  import {
30
31
  _validateObject,
31
32
  abool,
32
33
  abytes,
33
34
  aInRange,
35
+ bitLen,
34
36
  bitMask,
35
37
  bytesToHex,
36
38
  bytesToNumberBE,
@@ -56,8 +58,9 @@ import {
56
58
  wNAF,
57
59
  type AffinePoint,
58
60
  type BasicCurve,
59
- type Group,
60
- type GroupConstructor,
61
+ type CurveInfo,
62
+ type CurvePoint,
63
+ type CurvePointCons,
61
64
  } from './curve.ts';
62
65
  import {
63
66
  Field,
@@ -71,6 +74,8 @@ import {
71
74
 
72
75
  export type { AffinePoint };
73
76
  export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
77
+
78
+ type EndoBasis = [[bigint, bigint], [bigint, bigint]];
74
79
  /**
75
80
  * When Weierstrass curve has `a=0`, it becomes Koblitz curve.
76
81
  * Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
@@ -96,7 +101,8 @@ export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Ar
96
101
  */
97
102
  export type EndomorphismOpts = {
98
103
  beta: bigint;
99
- splitScalar: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
104
+ basises?: EndoBasis;
105
+ splitScalar?: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
100
106
  };
101
107
  export type BasicWCurve<T> = BasicCurve<T> & {
102
108
  // Params: a, b
@@ -109,86 +115,123 @@ export type BasicWCurve<T> = BasicCurve<T> & {
109
115
  endo?: EndomorphismOpts;
110
116
  // When a cofactor != 1, there can be an effective methods to:
111
117
  // 1. Determine whether a point is torsion-free
112
- isTorsionFree?: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
118
+ isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
113
119
  // 2. Clear torsion component
114
- clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
120
+ clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
115
121
  };
116
122
 
123
+ // We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
124
+ const divNearest = (num: bigint, den: bigint) => (num + (num >= 0 ? den : -den) / _2n) / den;
125
+
126
+ export type ScalarEndoParts = { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
127
+
128
+ /**
129
+ * Splits scalar for GLV endomorphism.
130
+ */
131
+ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): ScalarEndoParts {
132
+ // Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
133
+ // Since part can be negative, we need to do this on point.
134
+ // TODO: verifyScalar function which consumes lambda
135
+ const [[a1, b1], [a2, b2]] = basis;
136
+ const c1 = divNearest(b2 * k, n);
137
+ const c2 = divNearest(-b1 * k, n);
138
+ // |k1|/|k2| is < sqrt(N), but can be negative.
139
+ // If we do `k1 mod N`, we'll get big scalar (`> sqrt(N)`): so, we do cheaper negation instead.
140
+ let k1 = k - c1 * a1 - c2 * a2;
141
+ let k2 = -c1 * b1 - c2 * b2;
142
+ const k1neg = k1 < _0n;
143
+ const k2neg = k2 < _0n;
144
+ if (k1neg) k1 = -k1;
145
+ if (k2neg) k2 = -k2;
146
+ // Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
147
+ // This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
148
+ const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N
149
+ if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
150
+ throw new Error('splitScalar (endomorphism): failed, k=' + k);
151
+ }
152
+ return { k1neg, k1, k2neg, k2 };
153
+ }
154
+
155
+ export type ECDSASigFormat = 'compact' | 'der';
117
156
  export type Entropy = Hex | boolean;
118
- export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
119
- export type VerOpts = {
120
- lowS?: boolean;
121
- prehash?: boolean;
122
- format?: 'compact' | 'der' | 'js' | undefined;
123
- };
157
+ export type SignOpts = Partial<{
158
+ lowS: boolean;
159
+ extraEntropy: Entropy;
160
+ prehash: boolean;
161
+ format: ECDSASigFormat | 'js';
162
+ }>;
163
+ export type VerOpts = Partial<{
164
+ lowS: boolean;
165
+ prehash: boolean;
166
+ format: ECDSASigFormat | 'js' | undefined;
167
+ }>;
124
168
 
125
169
  function validateSigVerOpts(opts: SignOpts | VerOpts) {
126
170
  if (opts.lowS !== undefined) abool('lowS', opts.lowS);
127
171
  if (opts.prehash !== undefined) abool('prehash', opts.prehash);
128
172
  }
129
173
 
130
- /** Instance methods for 3D XYZ points. */
131
- export interface ProjPointType<T> extends Group<ProjPointType<T>> {
132
- /** projective x coordinate. Note: different from .x */
133
- readonly px: T;
134
- /** projective y coordinate. Note: different from .y */
135
- readonly py: T;
174
+ /** Instance methods for 3D XYZ projective points. */
175
+ export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>> {
176
+ /** projective X coordinate. Different from affine x. */
177
+ readonly X: T;
178
+ /** projective Y coordinate. Different from affine y. */
179
+ readonly Y: T;
136
180
  /** projective z coordinate */
137
- readonly pz: T;
138
- /** affine x coordinate */
181
+ readonly Z: T;
182
+ /** affine x coordinate. Different from projective X. */
139
183
  get x(): T;
140
- /** affine y coordinate */
184
+ /** affine y coordinate. Different from projective Y. */
141
185
  get y(): T;
142
- assertValidity(): void;
143
- clearCofactor(): ProjPointType<T>;
144
- is0(): boolean;
145
- isTorsionFree(): boolean;
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
186
  /** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
161
187
  toBytes(isCompressed?: boolean): Uint8Array;
162
188
  toHex(isCompressed?: boolean): string;
163
189
 
190
+ /** @deprecated use .X */
191
+ readonly px: T;
192
+ /** @deprecated use .Y */
193
+ readonly py: T;
194
+ /** @deprecated use .Z */
195
+ readonly pz: T;
164
196
  /** @deprecated use `toBytes` */
165
197
  toRawBytes(isCompressed?: boolean): Uint8Array;
166
198
  /** @deprecated use `multiplyUnsafe` */
167
- multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
199
+ multiplyAndAddUnsafe(
200
+ Q: WeierstrassPoint<T>,
201
+ a: bigint,
202
+ b: bigint
203
+ ): WeierstrassPoint<T> | undefined;
168
204
  /** @deprecated use `p.y % 2n === 0n` */
169
205
  hasEvenY(): boolean;
170
206
  /** @deprecated use `p.precompute(windowSize)` */
171
207
  _setWindowSize(windowSize: number): void;
172
208
  }
173
209
 
174
- /** Static methods for 3D XYZ points. */
175
- export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
176
- Fp: IField<T>;
177
- Fn: IField<bigint>;
210
+ /** Static methods for 3D XYZ projective points. */
211
+ export interface WeierstrassPointCons<T> extends CurvePointCons<T, WeierstrassPoint<T>> {
178
212
  /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
179
- new (x: T, y: T, z: T): ProjPointType<T>;
180
- /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
181
- fromAffine(p: AffinePoint<T>): ProjPointType<T>;
182
- fromBytes(encodedPoint: Uint8Array): ProjPointType<T>;
183
- fromHex(hex: Hex): ProjPointType<T>;
184
- fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
185
- normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
186
- msm(points: ProjPointType<T>[], scalars: bigint[]): ProjPointType<T>;
213
+ new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
214
+ /** @deprecated use `Point.BASE.multiply(Point.Fn.fromBytes(privateKey))` */
215
+ fromPrivateKey(privateKey: PrivKey): WeierstrassPoint<T>;
216
+ /** @deprecated use `import { normalizeZ } from '@noble/curves/abstract/curve.js';` */
217
+ normalizeZ(points: WeierstrassPoint<T>[]): WeierstrassPoint<T>[];
218
+ /** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
219
+ msm(points: WeierstrassPoint<T>[], scalars: bigint[]): WeierstrassPoint<T>;
187
220
  }
188
221
 
222
+ /** @deprecated use WeierstrassPoint */
223
+ export type ProjPointType<T> = WeierstrassPoint<T>;
224
+ /** @deprecated use WeierstrassPointCons */
225
+ export type ProjConstructor<T> = WeierstrassPointCons<T>;
226
+
227
+ // TODO: remove
189
228
  export type CurvePointsType<T> = BasicWCurve<T> & {
190
229
  fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
191
- toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
230
+ toBytes?: (
231
+ c: WeierstrassPointCons<T>,
232
+ point: WeierstrassPoint<T>,
233
+ isCompressed: boolean
234
+ ) => Uint8Array;
192
235
  };
193
236
 
194
237
  // LegacyWeierstrassOpts
@@ -196,12 +239,13 @@ export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial
196
239
 
197
240
  // LegacyWeierstrass
198
241
  export type CurvePointsRes<T> = {
199
- /** @deprecated import individual CURVE params */
242
+ Point: WeierstrassPointCons<T>;
243
+
244
+ /** @deprecated the property will be removed in next release */
200
245
  CURVE: CurvePointsType<T>;
201
- Point: ProjConstructor<T>;
202
246
  /** @deprecated use `Point` */
203
- ProjectivePoint: ProjConstructor<T>;
204
- /** @deprecated */
247
+ ProjectivePoint: WeierstrassPointCons<T>;
248
+ /** @deprecated use `Point.Fn.fromBytes(privateKey)` */
205
249
  normPrivateKeyToScalar: (key: PrivKey) => bigint;
206
250
  /** @deprecated */
207
251
  weierstrassEquation: (x: T) => T;
@@ -245,45 +289,53 @@ export type WeierstrassOpts<T> = Readonly<{
245
289
  export type WeierstrassExtraOpts<T> = Partial<{
246
290
  Fp: IField<T>;
247
291
  Fn: IField<bigint>;
248
- // TODO: remove
249
- allowedPrivateKeyLengths: readonly number[]; // for P521
250
292
  allowInfinityPoint: boolean;
251
293
  endo: EndomorphismOpts;
252
- wrapPrivateKey: boolean;
253
- isTorsionFree: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
254
- clearCofactor: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
294
+ isTorsionFree: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
295
+ clearCofactor: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
255
296
  fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
256
- toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
297
+ toBytes: (
298
+ c: WeierstrassPointCons<T>,
299
+ point: WeierstrassPoint<T>,
300
+ isCompressed: boolean
301
+ ) => Uint8Array;
257
302
  }>;
258
303
 
259
304
  /**
260
305
  * Options for ECDSA signatures over a Weierstrass curve.
261
306
  */
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
- };
307
+ export type ECDSAOpts = Partial<{
308
+ lowS: boolean;
309
+ hmac: HmacFnSync;
310
+ randomBytes: (bytesLength?: number) => Uint8Array;
311
+ bits2int: (bytes: Uint8Array) => bigint;
312
+ bits2int_modN: (bytes: Uint8Array) => bigint;
313
+ }>;
270
314
 
271
315
  /** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
272
316
  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;
317
+ keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
318
+ getPublicKey: (secretKey: PrivKey, isCompressed?: boolean) => Uint8Array;
319
+ sign: (msgHash: Hex, secretKey: PrivKey, opts?: SignOpts) => ECDSASigRecovered;
276
320
  verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
277
- Point: ProjConstructor<bigint>;
278
- Signature: SignatureConstructor;
321
+ getSharedSecret: (secretKeyA: PrivKey, publicKeyB: Hex, isCompressed?: boolean) => Uint8Array;
322
+ Point: WeierstrassPointCons<bigint>;
323
+ Signature: ECDSASignatureCons;
279
324
  utils: {
280
- isValidPrivateKey(privateKey: PrivKey): boolean;
281
- randomPrivateKey: () => Uint8Array;
282
- // TODO: deprecate those two
325
+ isValidSecretKey: (secretKey: PrivKey) => boolean;
326
+ isValidPublicKey: (publicKey: Uint8Array, isCompressed?: boolean) => boolean;
327
+ randomSecretKey: (seed?: Uint8Array) => Uint8Array;
328
+
329
+ /** @deprecated use `randomSecretKey` */
330
+ randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
331
+ /** @deprecated use `isValidSecretKey` */
332
+ isValidPrivateKey: (secretKey: PrivKey) => boolean;
333
+ /** @deprecated use `Point.Fn.fromBytes()` */
283
334
  normPrivateKeyToScalar: (key: PrivKey) => bigint;
284
- /** @deprecated */
285
- precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
335
+ /** @deprecated use `point.precompute()` */
336
+ precompute: (windowSize?: number, point?: WeierstrassPoint<bigint>) => WeierstrassPoint<bigint>;
286
337
  };
338
+ info: CurveInfo;
287
339
  }
288
340
  export class DERErr extends Error {
289
341
  constructor(m = '') {
@@ -420,45 +472,27 @@ export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
420
472
  }
421
473
  return weierstrassEquation;
422
474
  }
423
- export function _legacyHelperNormPriv(
424
- Fn: IField<bigint>,
425
- allowedPrivateKeyLengths?: readonly number[],
426
- wrapPrivateKey?: boolean
427
- ): (key: PrivKey) => bigint {
475
+ export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
428
476
  const { BYTES: expected } = Fn;
429
- // Validates if priv key is valid and converts it to bigint.
430
- function normPrivateKeyToScalar(key: PrivKey): bigint {
431
- let num: bigint;
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
- }
477
+ let num: bigint;
478
+ if (typeof key === 'bigint') {
479
+ num = key;
480
+ } else {
481
+ let bytes = ensureBytes('private key', key);
482
+ try {
483
+ num = Fn.fromBytes(bytes);
484
+ } catch (error) {
485
+ throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
450
486
  }
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
487
  }
455
- return normPrivateKeyToScalar;
488
+ if (!Fn.isValidNot0(num)) throw new Error('invalid private key: out of range [1..N-1]');
489
+ return num;
456
490
  }
457
491
 
458
492
  export function weierstrassN<T>(
459
493
  CURVE: WeierstrassOpts<T>,
460
494
  curveOpts: WeierstrassExtraOpts<T> = {}
461
- ): ProjConstructor<T> {
495
+ ): WeierstrassPointCons<T> {
462
496
  const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
463
497
  const { h: cofactor, n: CURVE_ORDER } = CURVE;
464
498
  _validateObject(
@@ -478,12 +512,8 @@ export function weierstrassN<T>(
478
512
  const { endo } = curveOpts;
479
513
  if (endo) {
480
514
  // 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');
515
+ if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
516
+ throw new Error('invalid endo: expected "beta": bigint and "basises": array');
487
517
  }
488
518
  }
489
519
 
@@ -493,8 +523,8 @@ export function weierstrassN<T>(
493
523
 
494
524
  // Implements IEEE P1363 point encoding
495
525
  function pointToBytes(
496
- _c: ProjConstructor<T>,
497
- point: ProjPointType<T>,
526
+ _c: WeierstrassPointCons<T>,
527
+ point: WeierstrassPoint<T>,
498
528
  isCompressed: boolean
499
529
  ): Uint8Array {
500
530
  const { x, y } = point.toAffine();
@@ -578,25 +608,30 @@ export function weierstrassN<T>(
578
608
  if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
579
609
  }
580
610
 
611
+ function splitEndoScalarN(k: bigint) {
612
+ if (!endo || !endo.basises) throw new Error('no endo');
613
+ return _splitEndoScalar(k, endo.basises, Fn.ORDER);
614
+ }
615
+
581
616
  // Memoized toAffine / validity check. They are heavy. Points are immutable.
582
617
 
583
618
  // Converts Projective point to affine (x, y) coordinates.
584
619
  // Can accept precomputed Z^-1 - for example, from invertBatch.
585
620
  // (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
586
621
  const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
587
- const { px: x, py: y, pz: z } = p;
622
+ const { X, Y, Z } = p;
588
623
  // Fast-path for normalized points
589
- if (Fp.eql(z, Fp.ONE)) return { x, y };
624
+ if (Fp.eql(Z, Fp.ONE)) return { x: X, y: Y };
590
625
  const is0 = p.is0();
591
626
  // If invZ was 0, we return zero point. However we still want to execute
592
627
  // all operations, so we replace invZ with a random number, 1.
593
- if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
594
- const ax = Fp.mul(x, iz);
595
- const ay = Fp.mul(y, iz);
596
- const zz = Fp.mul(z, iz);
628
+ if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(Z);
629
+ const x = Fp.mul(X, iz);
630
+ const y = Fp.mul(Y, iz);
631
+ const zz = Fp.mul(Z, iz);
597
632
  if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
598
633
  if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
599
- return { x: ax, y: ay };
634
+ return { x, y };
600
635
  });
601
636
  // NOTE: on exception this will crash 'cached' and no value will be set.
602
637
  // Otherwise true will be return
@@ -605,7 +640,7 @@ export function weierstrassN<T>(
605
640
  // (0, 1, 0) aka ZERO is invalid in most contexts.
606
641
  // In BLS, ZERO can be serialized, so we allow it.
607
642
  // (0, 0, 0) is invalid representation of ZERO.
608
- if (curveOpts.allowInfinityPoint && !Fp.is0(p.py)) return;
643
+ if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
609
644
  throw new Error('bad point: ZERO');
610
645
  }
611
646
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
@@ -623,7 +658,7 @@ export function weierstrassN<T>(
623
658
  k1neg: boolean,
624
659
  k2neg: boolean
625
660
  ) {
626
- k2p = new Point(Fp.mul(k2p.px, endoBeta), k2p.py, k2p.pz);
661
+ k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
627
662
  k1p = negateCt(k1neg, k1p);
628
663
  k2p = negateCt(k2neg, k2p);
629
664
  return k1p.add(k2p);
@@ -634,7 +669,7 @@ export function weierstrassN<T>(
634
669
  * Default Point works in 2d / affine coordinates: (x, y).
635
670
  * We're doing calculations in projective, because its operations don't require costly inversion.
636
671
  */
637
- class Point implements ProjPointType<T> {
672
+ class Point implements WeierstrassPoint<T> {
638
673
  // base / generator point
639
674
  static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
640
675
  // zero / infinity / identity point
@@ -643,15 +678,15 @@ export function weierstrassN<T>(
643
678
  static readonly Fp = Fp;
644
679
  static readonly Fn = Fn;
645
680
 
646
- readonly px: T;
647
- readonly py: T;
648
- readonly pz: T;
681
+ readonly X: T;
682
+ readonly Y: T;
683
+ readonly Z: T;
649
684
 
650
685
  /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
651
- constructor(px: T, py: T, pz: T) {
652
- this.px = acoord('x', px);
653
- this.py = acoord('y', py, true);
654
- this.pz = acoord('z', pz);
686
+ constructor(X: T, Y: T, Z: T) {
687
+ this.X = acoord('x', X);
688
+ this.Y = acoord('y', Y, true);
689
+ this.Z = acoord('z', Z);
655
690
  Object.freeze(this);
656
691
  }
657
692
 
@@ -672,8 +707,18 @@ export function weierstrassN<T>(
672
707
  return this.toAffine().y;
673
708
  }
674
709
 
710
+ // TODO: remove
711
+ get px(): T {
712
+ return this.X;
713
+ }
714
+ get py(): T {
715
+ return this.X;
716
+ }
717
+ get pz(): T {
718
+ return this.Z;
719
+ }
675
720
  static normalizeZ(points: Point[]): Point[] {
676
- return normalizeZ(Point, 'pz', points);
721
+ return normalizeZ(Point, points);
677
722
  }
678
723
 
679
724
  static fromBytes(bytes: Uint8Array): Point {
@@ -690,18 +735,16 @@ export function weierstrassN<T>(
690
735
 
691
736
  /** Multiplies generator point by privateKey. */
692
737
  static fromPrivateKey(privateKey: PrivKey) {
693
- const normPrivateKeyToScalar = _legacyHelperNormPriv(
694
- Fn,
695
- curveOpts.allowedPrivateKeyLengths,
696
- curveOpts.wrapPrivateKey
697
- );
698
- return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
738
+ return Point.BASE.multiply(_normFnElement(Fn, privateKey));
699
739
  }
700
740
 
701
- /** Multiscalar Multiplication */
741
+ // TODO: remove
702
742
  static msm(points: Point[], scalars: bigint[]): Point {
703
743
  return pippenger(Point, Fn, points, scalars);
704
744
  }
745
+ _setWindowSize(windowSize: number) {
746
+ this.precompute(windowSize);
747
+ }
705
748
 
706
749
  /**
707
750
  *
@@ -710,16 +753,11 @@ export function weierstrassN<T>(
710
753
  * @returns
711
754
  */
712
755
  precompute(windowSize: number = 8, isLazy = true): Point {
713
- wnaf.setWindowSize(this, windowSize);
756
+ wnaf.createCache(this, windowSize);
714
757
  if (!isLazy) this.multiply(_3n); // random number
715
758
  return this;
716
759
  }
717
760
 
718
- /** "Private method", don't use it directly */
719
- _setWindowSize(windowSize: number) {
720
- this.precompute(windowSize);
721
- }
722
-
723
761
  // TODO: return `this`
724
762
  /** A point on curve is valid if it conforms to equation. */
725
763
  assertValidity(): void {
@@ -735,8 +773,8 @@ export function weierstrassN<T>(
735
773
  /** Compare one point to another. */
736
774
  equals(other: Point): boolean {
737
775
  aprjpoint(other);
738
- const { px: X1, py: Y1, pz: Z1 } = this;
739
- const { px: X2, py: Y2, pz: Z2 } = other;
776
+ const { X: X1, Y: Y1, Z: Z1 } = this;
777
+ const { X: X2, Y: Y2, Z: Z2 } = other;
740
778
  const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
741
779
  const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
742
780
  return U1 && U2;
@@ -744,7 +782,7 @@ export function weierstrassN<T>(
744
782
 
745
783
  /** Flips point to one corresponding to (x, -y) in Affine coordinates. */
746
784
  negate(): Point {
747
- return new Point(this.px, Fp.neg(this.py), this.pz);
785
+ return new Point(this.X, Fp.neg(this.Y), this.Z);
748
786
  }
749
787
 
750
788
  // Renes-Costello-Batina exception-free doubling formula.
@@ -754,7 +792,7 @@ export function weierstrassN<T>(
754
792
  double() {
755
793
  const { a, b } = CURVE;
756
794
  const b3 = Fp.mul(b, _3n);
757
- const { px: X1, py: Y1, pz: Z1 } = this;
795
+ const { X: X1, Y: Y1, Z: Z1 } = this;
758
796
  let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
759
797
  let t0 = Fp.mul(X1, X1); // step 1
760
798
  let t1 = Fp.mul(Y1, Y1);
@@ -796,8 +834,8 @@ export function weierstrassN<T>(
796
834
  // Cost: 12M + 0S + 3*a + 3*b3 + 23add.
797
835
  add(other: Point): Point {
798
836
  aprjpoint(other);
799
- const { px: X1, py: Y1, pz: Z1 } = this;
800
- const { px: X2, py: Y2, pz: Z2 } = other;
837
+ const { X: X1, Y: Y1, Z: Z1 } = this;
838
+ const { X: X2, Y: Y2, Z: Z2 } = other;
801
839
  let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
802
840
  const a = CURVE.a;
803
841
  const b3 = Fp.mul(CURVE.b, _3n);
@@ -865,10 +903,10 @@ export function weierstrassN<T>(
865
903
  const { endo } = curveOpts;
866
904
  if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
867
905
  let point: Point, fake: Point; // Fake point is used to const-time mult
868
- const mul = (n: bigint) => wnaf.wNAFCached(this, n, Point.normalizeZ);
906
+ const mul = (n: bigint) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
869
907
  /** See docs for {@link EndomorphismOpts} */
870
908
  if (endo) {
871
- const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
909
+ const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
872
910
  const { p: k1p, f: k1f } = mul(k1);
873
911
  const { p: k2p, f: k2f } = mul(k2);
874
912
  fake = k1f.add(k2f);
@@ -879,13 +917,13 @@ export function weierstrassN<T>(
879
917
  fake = f;
880
918
  }
881
919
  // Normalize `z` for both points, but return only real one
882
- return Point.normalizeZ([point, fake])[0];
920
+ return normalizeZ(Point, [point, fake])[0];
883
921
  }
884
922
 
885
923
  /**
886
924
  * Non-constant-time multiplication. Uses double-and-add algorithm.
887
925
  * 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.
926
+ * an exposed secret key e.g. sig verification, which works over *public* keys.
889
927
  */
890
928
  multiplyUnsafe(sc: bigint): Point {
891
929
  const { endo } = curveOpts;
@@ -893,14 +931,13 @@ export function weierstrassN<T>(
893
931
  if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
894
932
  if (sc === _0n || p.is0()) return Point.ZERO;
895
933
  if (sc === _1n) return p; // fast-path
896
- if (wnaf.hasPrecomputes(this)) return this.multiply(sc);
934
+ if (wnaf.hasCache(this)) return this.multiply(sc);
897
935
  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);
936
+ const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
937
+ const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
901
938
  return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
902
939
  } else {
903
- return wnaf.wNAFCachedUnsafe(p, sc);
940
+ return wnaf.unsafe(p, sc);
904
941
  }
905
942
  }
906
943
 
@@ -925,7 +962,7 @@ export function weierstrassN<T>(
925
962
  const { isTorsionFree } = curveOpts;
926
963
  if (cofactor === _1n) return true;
927
964
  if (isTorsionFree) return isTorsionFree(Point, this);
928
- return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
965
+ return wnaf.unsafe(this, CURVE_ORDER).is0();
929
966
  }
930
967
 
931
968
  clearCofactor(): Point {
@@ -935,6 +972,11 @@ export function weierstrassN<T>(
935
972
  return this.multiplyUnsafe(cofactor);
936
973
  }
937
974
 
975
+ isSmallOrder(): boolean {
976
+ // can we use this.clearCofactor()?
977
+ return this.multiplyUnsafe(cofactor).is0();
978
+ }
979
+
938
980
  toBytes(isCompressed = true): Uint8Array {
939
981
  abool('isCompressed', isCompressed);
940
982
  this.assertValidity();
@@ -955,12 +997,13 @@ export function weierstrassN<T>(
955
997
  }
956
998
  }
957
999
  const bits = Fn.BITS;
958
- const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
1000
+ const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
959
1001
  return Point;
960
1002
  }
961
1003
 
962
1004
  // _legacyWeierstrass
963
- /** @deprecated use `weierstrassN` */
1005
+ // TODO: remove
1006
+ /** @deprecated use `weierstrass` in newer releases */
964
1007
  export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
965
1008
  const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
966
1009
  const Point = weierstrassN(CURVE, curveOpts);
@@ -968,33 +1011,49 @@ export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePoin
968
1011
  }
969
1012
 
970
1013
  // Instance
971
- export interface SignatureType {
1014
+ export interface ECDSASignature {
972
1015
  readonly r: bigint;
973
1016
  readonly s: bigint;
974
1017
  readonly recovery?: number;
975
- assertValidity(): void;
976
- addRecoveryBit(recovery: number): RecoveredSignatureType;
1018
+ addRecoveryBit(recovery: number): ECDSASigRecovered;
977
1019
  hasHighS(): boolean;
978
- normalizeS(): SignatureType;
979
- recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
1020
+ normalizeS(): ECDSASignature;
1021
+ recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
1022
+ toBytes(format?: string): Uint8Array;
1023
+ toHex(format?: string): string;
1024
+
1025
+ /** @deprecated */
1026
+ assertValidity(): void;
1027
+ /** @deprecated use `.toBytes('compact')` */
980
1028
  toCompactRawBytes(): Uint8Array;
1029
+ /** @deprecated use `.toBytes('compact')` */
981
1030
  toCompactHex(): string;
1031
+ /** @deprecated use `.toBytes('der')` */
982
1032
  toDERRawBytes(): Uint8Array;
1033
+ /** @deprecated use `.toBytes('der')` */
983
1034
  toDERHex(): string;
984
- // toBytes(format?: string): Uint8Array;
985
1035
  }
986
- export type RecoveredSignatureType = SignatureType & {
1036
+ export type SignatureType = ECDSASignature;
1037
+ export type ECDSASigRecovered = ECDSASignature & {
987
1038
  readonly recovery: number;
988
1039
  };
1040
+ export type RecoveredSignatureType = ECDSASigRecovered;
989
1041
  // Static methods
990
- export type SignatureConstructor = {
991
- new (r: bigint, s: bigint, recovery?: number): SignatureType;
992
- fromCompact(hex: Hex): SignatureType;
993
- fromDER(hex: Hex): SignatureType;
1042
+ export type ECDSASignatureCons = {
1043
+ new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
1044
+ fromBytes(bytes: Uint8Array, format?: ECDSASigFormat): ECDSASignature;
1045
+ fromHex(hex: string, format?: ECDSASigFormat): ECDSASignature;
1046
+
1047
+ /** @deprecated use `.fromBytes(bytes, 'compact')` */
1048
+ fromCompact(hex: Hex): ECDSASignature;
1049
+ /** @deprecated use `.fromBytes(bytes, 'der')` */
1050
+ fromDER(hex: Hex): ECDSASignature;
994
1051
  };
995
1052
  export type SignatureLike = { r: bigint; s: bigint };
996
- export type PubKey = Hex | ProjPointType<bigint>;
1053
+ // TODO: remove
1054
+ export type PubKey = Hex | WeierstrassPoint<bigint>;
997
1055
 
1056
+ // TODO: remove
998
1057
  export type CurveType = BasicWCurve<bigint> & {
999
1058
  hash: CHash; // CHash not FHash because we need outputLen for DRBG
1000
1059
  hmac?: HmacFnSync;
@@ -1009,32 +1068,168 @@ function pprefix(hasEvenY: boolean): Uint8Array {
1009
1068
  return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
1010
1069
  }
1011
1070
 
1071
+ // TODO: remove
1012
1072
  export type CurveFn = {
1073
+ /** @deprecated the property will be removed in next release */
1013
1074
  CURVE: CurvePointsType<bigint>;
1014
- getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
1015
- getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
1016
- sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
1017
- verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
1018
- Point: ProjConstructor<bigint>;
1075
+ keygen: ECDSA['keygen'];
1076
+ getPublicKey: ECDSA['getPublicKey'];
1077
+ getSharedSecret: ECDSA['getSharedSecret'];
1078
+ sign: ECDSA['sign'];
1079
+ verify: ECDSA['verify'];
1080
+ Point: WeierstrassPointCons<bigint>;
1019
1081
  /** @deprecated use `Point` */
1020
- ProjectivePoint: ProjConstructor<bigint>;
1021
- Signature: SignatureConstructor;
1022
- utils: {
1023
- normPrivateKeyToScalar: (key: PrivKey) => bigint;
1024
- isValidPrivateKey(privateKey: PrivKey): boolean;
1025
- randomPrivateKey: () => Uint8Array;
1026
- precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
1027
- };
1082
+ ProjectivePoint: WeierstrassPointCons<bigint>;
1083
+ Signature: ECDSASignatureCons;
1084
+ utils: ECDSA['utils'];
1085
+ info: CurveInfo;
1028
1086
  };
1029
1087
 
1088
+ /**
1089
+ * Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
1090
+ * TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
1091
+ * b = True and y = sqrt(u / v) if (u / v) is square in F, and
1092
+ * b = False and y = sqrt(Z * (u / v)) otherwise.
1093
+ * @param Fp
1094
+ * @param Z
1095
+ * @returns
1096
+ */
1097
+ export function SWUFpSqrtRatio<T>(
1098
+ Fp: IField<T>,
1099
+ Z: T
1100
+ ): (u: T, v: T) => { isValid: boolean; value: T } {
1101
+ // Generic implementation
1102
+ const q = Fp.ORDER;
1103
+ let l = _0n;
1104
+ for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
1105
+ const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
1106
+ // We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
1107
+ // 2n ** c1 == 2n << (c1-1)
1108
+ const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
1109
+ const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
1110
+ const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
1111
+ const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
1112
+ const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
1113
+ const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
1114
+ const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
1115
+ const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
1116
+ let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
1117
+ let tv1 = c6; // 1. tv1 = c6
1118
+ let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
1119
+ let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
1120
+ tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
1121
+ let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
1122
+ tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
1123
+ tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
1124
+ tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
1125
+ tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
1126
+ let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
1127
+ tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
1128
+ let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
1129
+ tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
1130
+ tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
1131
+ tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
1132
+ tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
1133
+ // 17. for i in (c1, c1 - 1, ..., 2):
1134
+ for (let i = c1; i > _1n; i--) {
1135
+ let tv5 = i - _2n; // 18. tv5 = i - 2
1136
+ tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
1137
+ let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
1138
+ const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
1139
+ tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
1140
+ tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
1141
+ tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
1142
+ tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
1143
+ tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
1144
+ }
1145
+ return { isValid: isQR, value: tv3 };
1146
+ };
1147
+ if (Fp.ORDER % _4n === _3n) {
1148
+ // sqrt_ratio_3mod4(u, v)
1149
+ const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
1150
+ const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
1151
+ sqrtRatio = (u: T, v: T) => {
1152
+ let tv1 = Fp.sqr(v); // 1. tv1 = v^2
1153
+ const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
1154
+ tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
1155
+ let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
1156
+ y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
1157
+ const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
1158
+ const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
1159
+ const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
1160
+ let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
1161
+ return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
1162
+ };
1163
+ }
1164
+ // No curves uses that
1165
+ // if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
1166
+ return sqrtRatio;
1167
+ }
1168
+ /**
1169
+ * Simplified Shallue-van de Woestijne-Ulas Method
1170
+ * https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
1171
+ */
1172
+ export function mapToCurveSimpleSWU<T>(
1173
+ Fp: IField<T>,
1174
+ opts: {
1175
+ A: T;
1176
+ B: T;
1177
+ Z: T;
1178
+ }
1179
+ ): (u: T) => { x: T; y: T } {
1180
+ validateField(Fp);
1181
+ const { A, B, Z } = opts;
1182
+ if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
1183
+ throw new Error('mapToCurveSimpleSWU: invalid opts');
1184
+ const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
1185
+ if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
1186
+ // Input: u, an element of F.
1187
+ // Output: (x, y), a point on E.
1188
+ return (u: T): { x: T; y: T } => {
1189
+ // prettier-ignore
1190
+ let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
1191
+ tv1 = Fp.sqr(u); // 1. tv1 = u^2
1192
+ tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
1193
+ tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
1194
+ tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
1195
+ tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
1196
+ tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
1197
+ tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1198
+ tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
1199
+ tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
1200
+ tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
1201
+ tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
1202
+ tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
1203
+ tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
1204
+ tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
1205
+ tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
1206
+ tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
1207
+ x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
1208
+ const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
1209
+ y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
1210
+ y = Fp.mul(y, value); // 20. y = y * y1
1211
+ x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
1212
+ y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
1213
+ const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
1214
+ y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
1215
+ const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
1216
+ x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
1217
+ return { x, y };
1218
+ };
1219
+ }
1220
+
1221
+ /**
1222
+ * Creates ECDSA for given elliptic curve Point and hash function.
1223
+ */
1030
1224
  export function ecdsa(
1031
- Point: ProjConstructor<bigint>,
1032
- ecdsaOpts: ECDSAOpts,
1033
- curveOpts: WeierstrassExtraOpts<bigint> = {}
1225
+ Point: WeierstrassPointCons<bigint>,
1226
+ hash: CHash,
1227
+ ecdsaOpts: ECDSAOpts = {}
1034
1228
  ): ECDSA {
1229
+ ahash(hash);
1035
1230
  _validateObject(
1036
1231
  ecdsaOpts,
1037
- { hash: 'function' },
1232
+ {},
1038
1233
  {
1039
1234
  hmac: 'function',
1040
1235
  lowS: 'boolean',
@@ -1047,11 +1242,20 @@ export function ecdsa(
1047
1242
  const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
1048
1243
  const hmac_: HmacFnSync =
1049
1244
  ecdsaOpts.hmac ||
1050
- (((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1245
+ (((key, ...msgs) => hmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1051
1246
 
1052
1247
  const { Fp, Fn } = Point;
1053
1248
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
1054
1249
 
1250
+ const seedLen = getMinHashLength(CURVE_ORDER);
1251
+ const lengths = {
1252
+ secret: Fn.BYTES,
1253
+ public: 1 + Fp.BYTES,
1254
+ publicUncompressed: 1 + 2 * Fp.BYTES,
1255
+ signature: 2 * Fn.BYTES,
1256
+ seed: seedLen,
1257
+ };
1258
+
1055
1259
  function isBiggerThanHalfOrder(number: bigint) {
1056
1260
  const HALF = CURVE_ORDER >> _1n;
1057
1261
  return number > HALF;
@@ -1068,7 +1272,7 @@ export function ecdsa(
1068
1272
  /**
1069
1273
  * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
1070
1274
  */
1071
- class Signature implements SignatureType {
1275
+ class Signature implements ECDSASignature {
1072
1276
  readonly r: bigint;
1073
1277
  readonly s: bigint;
1074
1278
  readonly recovery?: number;
@@ -1081,26 +1285,26 @@ export function ecdsa(
1081
1285
  Object.freeze(this);
1082
1286
  }
1083
1287
 
1084
- // pair (bytes of r, bytes of s)
1085
- static fromCompact(hex: Hex) {
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)));
1288
+ static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = 'compact') {
1289
+ if (format === 'compact') {
1290
+ const L = Fn.BYTES;
1291
+ abytes(bytes, L * 2);
1292
+ const r = bytes.subarray(0, L);
1293
+ const s = bytes.subarray(L, L * 2);
1294
+ return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
1295
+ }
1296
+ if (format === 'der') {
1297
+ abytes(bytes);
1298
+ const { r, s } = DER.toSig(bytes);
1299
+ return new Signature(r, s);
1300
+ }
1301
+ throw new Error('invalid format');
1089
1302
  }
1090
1303
 
1091
- // DER encoded ECDSA signature
1092
- // https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
1093
- static fromDER(hex: Hex) {
1094
- const { r, s } = DER.toSig(ensureBytes('DER', hex));
1095
- return new Signature(r, s);
1304
+ static fromHex(hex: string, format?: ECDSASigFormat) {
1305
+ return this.fromBytes(hexToBytes(hex), format);
1096
1306
  }
1097
1307
 
1098
- /**
1099
- * @todo remove
1100
- * @deprecated
1101
- */
1102
- assertValidity(): void {}
1103
-
1104
1308
  addRecoveryBit(recovery: number): RecoveredSignature {
1105
1309
  return new Signature(this.r, this.s, recovery) as RecoveredSignature;
1106
1310
  }
@@ -1146,21 +1350,30 @@ export function ecdsa(
1146
1350
  return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1147
1351
  }
1148
1352
 
1149
- toBytes(format: 'compact' | 'der') {
1353
+ toBytes(format: ECDSASigFormat = 'compact') {
1150
1354
  if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
1151
1355
  if (format === 'der') return hexToBytes(DER.hexFromSig(this));
1152
1356
  throw new Error('invalid format');
1153
1357
  }
1154
1358
 
1155
- // DER-encoded
1359
+ toHex(format?: ECDSASigFormat) {
1360
+ return bytesToHex(this.toBytes(format));
1361
+ }
1362
+
1363
+ // TODO: remove
1364
+ assertValidity(): void {}
1365
+ static fromCompact(hex: Hex) {
1366
+ return Signature.fromBytes(ensureBytes('sig', hex), 'compact');
1367
+ }
1368
+ static fromDER(hex: Hex) {
1369
+ return Signature.fromBytes(ensureBytes('sig', hex), 'der');
1370
+ }
1156
1371
  toDERRawBytes() {
1157
1372
  return this.toBytes('der');
1158
1373
  }
1159
1374
  toDERHex() {
1160
1375
  return bytesToHex(this.toBytes('der'));
1161
1376
  }
1162
-
1163
- // padded bytes of r, then padded bytes of s
1164
1377
  toCompactRawBytes() {
1165
1378
  return this.toBytes('compact');
1166
1379
  }
@@ -1170,80 +1383,81 @@ export function ecdsa(
1170
1383
  }
1171
1384
  type RecoveredSignature = Signature & { recovery: number };
1172
1385
 
1173
- const normPrivateKeyToScalar = _legacyHelperNormPriv(
1174
- Fn,
1175
- curveOpts.allowedPrivateKeyLengths,
1176
- curveOpts.wrapPrivateKey
1177
- );
1386
+ function isValidSecretKey(privateKey: PrivKey) {
1387
+ try {
1388
+ return !!_normFnElement(Fn, privateKey);
1389
+ } catch (error) {
1390
+ return false;
1391
+ }
1392
+ }
1393
+ function isValidPublicKey(publicKey: Uint8Array, isCompressed?: boolean): boolean {
1394
+ try {
1395
+ const l = publicKey.length;
1396
+ if (isCompressed === true && l !== lengths.public) return false;
1397
+ if (isCompressed === false && l !== lengths.publicUncompressed) return false;
1398
+ return !!Point.fromBytes(publicKey);
1399
+ } catch (error) {
1400
+ return false;
1401
+ }
1402
+ }
1403
+ /**
1404
+ * Produces cryptographically secure secret key from random of size
1405
+ * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1406
+ */
1407
+ function randomSecretKey(seed = randomBytes_(seedLen)): Uint8Array {
1408
+ return mapHashToField(seed, CURVE_ORDER);
1409
+ }
1178
1410
 
1179
1411
  const utils = {
1180
- isValidPrivateKey(privateKey: PrivKey) {
1181
- try {
1182
- normPrivateKeyToScalar(privateKey);
1183
- return true;
1184
- } catch (error) {
1185
- return false;
1186
- }
1187
- },
1188
- normPrivateKeyToScalar: normPrivateKeyToScalar,
1189
-
1190
- /**
1191
- * Produces cryptographically secure private key from random of size
1192
- * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1193
- */
1194
- randomPrivateKey: (): Uint8Array => {
1195
- const n = CURVE_ORDER;
1196
- return mapHashToField(randomBytes_(getMinHashLength(n)), n);
1197
- },
1412
+ isValidSecretKey,
1413
+ isValidPublicKey,
1414
+ randomSecretKey,
1198
1415
 
1199
- precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
1416
+ // TODO: remove
1417
+ isValidPrivateKey: isValidSecretKey,
1418
+ randomPrivateKey: randomSecretKey,
1419
+ normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
1420
+ precompute(windowSize = 8, point = Point.BASE): WeierstrassPoint<bigint> {
1200
1421
  return point.precompute(windowSize, false);
1201
1422
  },
1202
1423
  };
1203
1424
 
1204
1425
  /**
1205
- * Computes public key for a private key. Checks for validity of the private key.
1206
- * @param privateKey private key
1426
+ * Computes public key for a secret key. Checks for validity of the secret key.
1207
1427
  * @param isCompressed whether to return compact (default), or full key
1208
1428
  * @returns Public key, full when isCompressed=false; short when isCompressed=true
1209
1429
  */
1210
- function getPublicKey(privateKey: PrivKey, isCompressed = true): Uint8Array {
1211
- return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
1430
+ function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
1431
+ return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
1212
1432
  }
1213
1433
 
1214
1434
  /**
1215
1435
  * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1216
1436
  */
1217
1437
  function isProbPub(item: PrivKey | PubKey): boolean | undefined {
1438
+ // TODO: remove
1218
1439
  if (typeof item === 'bigint') return false;
1440
+ // TODO: remove
1219
1441
  if (item instanceof Point) return true;
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
- }
1442
+ if (Fn.allowedLengths || lengths.secret === lengths.public) return undefined;
1443
+ const l = ensureBytes('key', item).length;
1444
+ return l === lengths.public || l === lengths.publicUncompressed;
1230
1445
  }
1231
1446
 
1232
1447
  /**
1233
1448
  * ECDH (Elliptic Curve Diffie Hellman).
1234
- * Computes shared public key from private key and public key.
1235
- * Checks: 1) private key validity 2) shared key is on-curve.
1449
+ * Computes shared public key from secret key A and public key B.
1450
+ * Checks: 1) secret key validity 2) shared key is on-curve.
1236
1451
  * Does NOT hash the result.
1237
- * @param privateA private key
1238
- * @param publicB different public key
1239
1452
  * @param isCompressed whether to return compact (default), or full key
1240
1453
  * @returns shared public key
1241
1454
  */
1242
- function getSharedSecret(privateA: PrivKey, publicB: Hex, isCompressed = true): Uint8Array {
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');
1245
- const b = Point.fromHex(publicB); // check for being on-curve
1246
- return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
1455
+ function getSharedSecret(secretKeyA: PrivKey, publicKeyB: Hex, isCompressed = true): Uint8Array {
1456
+ if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
1457
+ if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
1458
+ const s = _normFnElement(Fn, secretKeyA);
1459
+ const b = Point.fromHex(publicKeyB); // checks for being on-curve
1460
+ return b.multiply(s).toBytes(isCompressed);
1247
1461
  }
1248
1462
 
1249
1463
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
@@ -1285,7 +1499,6 @@ export function ecdsa(
1285
1499
  function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
1286
1500
  if (['recovered', 'canonical'].some((k) => k in opts))
1287
1501
  throw new Error('sign() legacy options not supported');
1288
- const { hash } = ecdsaOpts;
1289
1502
  let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
1290
1503
  if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1291
1504
  msgHash = ensureBytes('msgHash', msgHash);
@@ -1296,17 +1509,21 @@ export function ecdsa(
1296
1509
  // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1297
1510
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1298
1511
  const h1int = bits2int_modN(msgHash);
1299
- const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
1512
+ const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
1300
1513
  const seedArgs = [int2octets(d), int2octets(h1int)];
1301
1514
  // extraEntropy. RFC6979 3.6: additional k' (optional).
1302
1515
  if (ent != null && ent !== false) {
1303
1516
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
1304
- const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
1517
+ const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
1305
1518
  seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
1306
1519
  }
1307
1520
  const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
1308
1521
  const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
1309
1522
  // Converts signature params into point w r/s, checks result for validity.
1523
+ // To transform k => Signature:
1524
+ // q = k⋅G
1525
+ // r = q.x mod n
1526
+ // s = k^-1(m + rd) mod n
1310
1527
  // Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
1311
1528
  // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
1312
1529
  // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
@@ -1316,7 +1533,7 @@ export function ecdsa(
1316
1533
  const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
1317
1534
  if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
1318
1535
  const ik = Fn.inv(k); // k^-1 mod n
1319
- const q = Point.BASE.multiply(k).toAffine(); // q = Gk
1536
+ const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
1320
1537
  const r = Fn.create(q.x); // r = q.x mod n
1321
1538
  if (r === _0n) return;
1322
1539
  const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
@@ -1335,21 +1552,17 @@ export function ecdsa(
1335
1552
  const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1336
1553
 
1337
1554
  /**
1338
- * Signs message hash with a private key.
1555
+ * Signs message hash with a secret key.
1339
1556
  * ```
1340
1557
  * sign(m, d, k) where
1341
1558
  * (x, y) = G × k
1342
1559
  * r = x mod n
1343
1560
  * s = (m + dr)/k mod n
1344
1561
  * ```
1345
- * @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
1346
- * @param privKey private key
1347
- * @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
1348
- * @returns signature with recovery param
1349
1562
  */
1350
- function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
1351
- const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
1352
- const drbg = createHmacDrbg<RecoveredSignature>(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
1563
+ function sign(msgHash: Hex, secretKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
1564
+ const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
1565
+ const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac_);
1353
1566
  return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1354
1567
  }
1355
1568
 
@@ -1386,90 +1599,103 @@ export function ecdsa(
1386
1599
  // TODO: remove
1387
1600
  if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
1388
1601
 
1389
- if (format !== undefined && !['compact', 'der', 'js'].includes(format))
1390
- throw new Error('format must be "compact", "der" or "js"');
1391
- const isHex = typeof sg === 'string' || isBytes(sg);
1392
- const isObj =
1393
- !isHex &&
1394
- !format &&
1395
- typeof sg === 'object' &&
1396
- sg !== null &&
1397
- typeof sg.r === 'bigint' &&
1398
- typeof sg.s === 'bigint';
1399
- if (!isHex && !isObj)
1400
- throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1401
1602
  let _sig: Signature | undefined = undefined;
1402
- let P: ProjPointType<bigint>;
1403
-
1404
- // deduce signature format
1405
- try {
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
- // }
1603
+ let P: WeierstrassPoint<bigint>;
1604
+
1605
+ if (format === undefined) {
1606
+ // Try to deduce format
1607
+ const isHex = typeof sg === 'string' || isBytes(sg);
1608
+ const isObj =
1609
+ !isHex &&
1610
+ sg !== null &&
1611
+ typeof sg === 'object' &&
1612
+ typeof sg.r === 'bigint' &&
1613
+ typeof sg.s === 'bigint';
1614
+ if (!isHex && !isObj)
1615
+ throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1415
1616
  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
- }
1422
- if (isHex) {
1617
+ _sig = new Signature(sg.r, sg.s);
1618
+ } else if (isHex) {
1423
1619
  // TODO: remove this malleable check
1424
1620
  // Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
1425
1621
  // Since DER can also be 2*Fn.BYTES bytes, we check for it first.
1426
1622
  try {
1427
- if (format !== 'compact') _sig = Signature.fromDER(sg);
1623
+ _sig = Signature.fromDER(sg);
1428
1624
  } catch (derError) {
1429
1625
  if (!(derError instanceof DER.Err)) throw derError;
1430
1626
  }
1431
- if (!_sig && format !== 'der') _sig = Signature.fromCompact(sg);
1627
+ if (!_sig) {
1628
+ try {
1629
+ _sig = Signature.fromCompact(sg);
1630
+ } catch (error) {
1631
+ return false;
1632
+ }
1633
+ }
1634
+ }
1635
+ } else {
1636
+ if (format === 'compact' || format === 'der') {
1637
+ if (typeof sg !== 'string' && !isBytes(sg))
1638
+ throw new Error('"der" / "compact" format expects Uint8Array signature');
1639
+ _sig = Signature.fromBytes(ensureBytes('sig', sg), format);
1640
+ } else if (format === 'js') {
1641
+ if (!(sg instanceof Signature)) throw new Error('"js" format expects Signature instance');
1642
+ _sig = sg;
1643
+ } else {
1644
+ throw new Error('format must be "compact", "der" or "js"');
1432
1645
  }
1646
+ }
1647
+
1648
+ if (!_sig) return false;
1649
+ try {
1433
1650
  P = Point.fromHex(publicKey);
1434
- } catch (error) {
1651
+ if (lowS && _sig.hasHighS()) return false;
1652
+ // todo: optional.hash => hash
1653
+ if (prehash) msgHash = hash(msgHash);
1654
+ const { r, s } = _sig;
1655
+ const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1656
+ const is = Fn.inv(s); // s^-1
1657
+ const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1658
+ const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1659
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1660
+ if (R.is0()) return false;
1661
+ const v = Fn.create(R.x); // v = r.x mod n
1662
+ return v === r;
1663
+ } catch (e) {
1435
1664
  return false;
1436
1665
  }
1437
- if (!_sig) return false;
1438
- if (lowS && _sig.hasHighS()) return false;
1439
- // todo: optional.hash => hash
1440
- if (prehash) msgHash = ecdsaOpts.hash(msgHash);
1441
- const { r, s } = _sig;
1442
- const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
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
1449
- return v === r;
1450
1666
  }
1451
- // TODO: clarify API for cloning .clone({hash: sha512}) ? .createWith({hash: sha512})?
1452
- // const clone = (hash: CHash): ECDSA => ecdsa(Point, { ...ecdsaOpts, ...getHash(hash) }, curveOpts);
1667
+
1668
+ function keygen(seed?: Uint8Array) {
1669
+ const secretKey = utils.randomSecretKey(seed);
1670
+ return { secretKey, publicKey: getPublicKey(secretKey) };
1671
+ }
1672
+
1453
1673
  return Object.freeze({
1674
+ keygen,
1454
1675
  getPublicKey,
1455
- getSharedSecret,
1456
1676
  sign,
1457
1677
  verify,
1678
+ getSharedSecret,
1458
1679
  utils,
1459
1680
  Point,
1460
1681
  Signature,
1682
+ info: { type: 'weierstrass' as const, lengths, publicKeyHasPrefix: true },
1461
1683
  });
1462
1684
  }
1463
1685
 
1686
+ // TODO: remove
1464
1687
  export type WsPointComposed<T> = {
1465
1688
  CURVE: WeierstrassOpts<T>;
1466
1689
  curveOpts: WeierstrassExtraOpts<T>;
1467
1690
  };
1691
+ // TODO: remove
1468
1692
  export type WsComposed = {
1469
1693
  CURVE: WeierstrassOpts<bigint>;
1694
+ hash: CHash;
1470
1695
  curveOpts: WeierstrassExtraOpts<bigint>;
1471
1696
  ecdsaOpts: ECDSAOpts;
1472
1697
  };
1698
+ // TODO: remove
1473
1699
  function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
1474
1700
  const CURVE: WeierstrassOpts<T> = {
1475
1701
  a: c.a,
@@ -1481,14 +1707,19 @@ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointCompo
1481
1707
  Gy: c.Gy,
1482
1708
  };
1483
1709
  const Fp = c.Fp;
1484
- const Fn = Field(CURVE.n, c.nBitLength);
1710
+ let allowedLengths = c.allowedPrivateKeyLengths
1711
+ ? Array.from(new Set(c.allowedPrivateKeyLengths.map((l) => Math.ceil(l / 2))))
1712
+ : undefined;
1713
+ const Fn = Field(CURVE.n, {
1714
+ BITS: c.nBitLength,
1715
+ allowedLengths: allowedLengths,
1716
+ modOnDecode: c.wrapPrivateKey,
1717
+ });
1485
1718
  const curveOpts: WeierstrassExtraOpts<T> = {
1486
1719
  Fp,
1487
1720
  Fn,
1488
- allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
1489
1721
  allowInfinityPoint: c.allowInfinityPoint,
1490
1722
  endo: c.endo,
1491
- wrapPrivateKey: c.wrapPrivateKey,
1492
1723
  isTorsionFree: c.isTorsionFree,
1493
1724
  clearCofactor: c.clearCofactor,
1494
1725
  fromBytes: c.fromBytes,
@@ -1499,18 +1730,18 @@ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointCompo
1499
1730
  function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
1500
1731
  const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1501
1732
  const ecdsaOpts: ECDSAOpts = {
1502
- hash: c.hash,
1503
1733
  hmac: c.hmac,
1504
1734
  randomBytes: c.randomBytes,
1505
1735
  lowS: c.lowS,
1506
1736
  bits2int: c.bits2int,
1507
1737
  bits2int_modN: c.bits2int_modN,
1508
1738
  };
1509
- return { CURVE, curveOpts, ecdsaOpts };
1739
+ return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
1510
1740
  }
1741
+ // TODO: remove
1511
1742
  function _weierstrass_new_output_to_legacy<T>(
1512
1743
  c: CurvePointsType<T>,
1513
- Point: ProjConstructor<T>
1744
+ Point: WeierstrassPointCons<T>
1514
1745
  ): CurvePointsRes<T> {
1515
1746
  const { Fp, Fn } = Point;
1516
1747
  // TODO: remove
@@ -1518,23 +1749,19 @@ function _weierstrass_new_output_to_legacy<T>(
1518
1749
  return inRange(num, _1n, Fn.ORDER);
1519
1750
  }
1520
1751
  const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
1521
- const normPrivateKeyToScalar = _legacyHelperNormPriv(
1522
- Fn,
1523
- c.allowedPrivateKeyLengths,
1524
- c.wrapPrivateKey
1525
- );
1526
1752
  return Object.assign(
1527
1753
  {},
1528
1754
  {
1529
1755
  CURVE: c,
1530
1756
  Point: Point,
1531
1757
  ProjectivePoint: Point,
1532
- normPrivateKeyToScalar,
1758
+ normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
1533
1759
  weierstrassEquation,
1534
1760
  isWithinCurveOrder,
1535
1761
  }
1536
1762
  );
1537
1763
  }
1764
+ // TODO: remove
1538
1765
  function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
1539
1766
  return Object.assign({}, ecdsa, {
1540
1767
  ProjectivePoint: ecdsa.Point,
@@ -1544,141 +1771,8 @@ function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
1544
1771
 
1545
1772
  // _ecdsa_legacy
1546
1773
  export function weierstrass(c: CurveType): CurveFn {
1547
- const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
1774
+ const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
1548
1775
  const Point = weierstrassN(CURVE, curveOpts);
1549
- const signs = ecdsa(Point, ecdsaOpts, curveOpts);
1776
+ const signs = ecdsa(Point, hash, ecdsaOpts);
1550
1777
  return _ecdsa_new_output_to_legacy(c, signs);
1551
1778
  }
1552
-
1553
- /**
1554
- * Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
1555
- * TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
1556
- * b = True and y = sqrt(u / v) if (u / v) is square in F, and
1557
- * b = False and y = sqrt(Z * (u / v)) otherwise.
1558
- * @param Fp
1559
- * @param Z
1560
- * @returns
1561
- */
1562
- export function SWUFpSqrtRatio<T>(
1563
- Fp: IField<T>,
1564
- Z: T
1565
- ): (u: T, v: T) => { isValid: boolean; value: T } {
1566
- // Generic implementation
1567
- const q = Fp.ORDER;
1568
- let l = _0n;
1569
- for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
1570
- const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
1571
- // We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
1572
- // 2n ** c1 == 2n << (c1-1)
1573
- const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
1574
- const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
1575
- const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
1576
- const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
1577
- const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
1578
- const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
1579
- const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
1580
- const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
1581
- let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
1582
- let tv1 = c6; // 1. tv1 = c6
1583
- let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
1584
- let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
1585
- tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
1586
- let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
1587
- tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
1588
- tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
1589
- tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
1590
- tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
1591
- let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
1592
- tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
1593
- let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
1594
- tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
1595
- tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
1596
- tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
1597
- tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
1598
- // 17. for i in (c1, c1 - 1, ..., 2):
1599
- for (let i = c1; i > _1n; i--) {
1600
- let tv5 = i - _2n; // 18. tv5 = i - 2
1601
- tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
1602
- let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
1603
- const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
1604
- tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
1605
- tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
1606
- tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
1607
- tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
1608
- tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
1609
- }
1610
- return { isValid: isQR, value: tv3 };
1611
- };
1612
- if (Fp.ORDER % _4n === _3n) {
1613
- // sqrt_ratio_3mod4(u, v)
1614
- const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
1615
- const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
1616
- sqrtRatio = (u: T, v: T) => {
1617
- let tv1 = Fp.sqr(v); // 1. tv1 = v^2
1618
- const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
1619
- tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
1620
- let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
1621
- y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
1622
- const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
1623
- const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
1624
- const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
1625
- let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
1626
- return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
1627
- };
1628
- }
1629
- // No curves uses that
1630
- // if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
1631
- return sqrtRatio;
1632
- }
1633
- /**
1634
- * Simplified Shallue-van de Woestijne-Ulas Method
1635
- * https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
1636
- */
1637
- export function mapToCurveSimpleSWU<T>(
1638
- Fp: IField<T>,
1639
- opts: {
1640
- A: T;
1641
- B: T;
1642
- Z: T;
1643
- }
1644
- ): (u: T) => { x: T; y: T } {
1645
- validateField(Fp);
1646
- const { A, B, Z } = opts;
1647
- if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
1648
- throw new Error('mapToCurveSimpleSWU: invalid opts');
1649
- const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
1650
- if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
1651
- // Input: u, an element of F.
1652
- // Output: (x, y), a point on E.
1653
- return (u: T): { x: T; y: T } => {
1654
- // prettier-ignore
1655
- let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
1656
- tv1 = Fp.sqr(u); // 1. tv1 = u^2
1657
- tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
1658
- tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
1659
- tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
1660
- tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
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
1664
- tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
1665
- tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
1666
- tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
1667
- tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
1668
- tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
1669
- tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
1670
- tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
1671
- tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
1672
- x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
1673
- const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
1674
- y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
1675
- y = Fp.mul(y, value); // 20. y = y * y1
1676
- x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
1677
- y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
1678
- const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
1679
- y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
1680
- const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
1681
- x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
1682
- return { x, y };
1683
- };
1684
- }