@noble/curves 1.9.1 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +56 -25
  2. package/_shortw_utils.d.ts +7 -5
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +2 -8
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +60 -24
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +158 -109
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +44 -9
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +86 -7
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +112 -25
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +138 -102
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/fft.d.ts +12 -10
  19. package/abstract/fft.d.ts.map +1 -1
  20. package/abstract/fft.js +12 -13
  21. package/abstract/fft.js.map +1 -1
  22. package/abstract/hash-to-curve.d.ts +25 -11
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +17 -14
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +24 -11
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +49 -20
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +1 -1
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +5 -4
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/poseidon.d.ts +5 -13
  35. package/abstract/poseidon.d.ts.map +1 -1
  36. package/abstract/poseidon.js +12 -7
  37. package/abstract/poseidon.js.map +1 -1
  38. package/abstract/tower.d.ts +20 -46
  39. package/abstract/tower.d.ts.map +1 -1
  40. package/abstract/tower.js +9 -3
  41. package/abstract/tower.js.map +1 -1
  42. package/abstract/utils.d.ts +1 -115
  43. package/abstract/utils.d.ts.map +1 -1
  44. package/abstract/utils.js +17 -371
  45. package/abstract/utils.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +132 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +462 -398
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +2 -0
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +504 -466
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +2 -0
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +44 -32
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +8 -5
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +67 -54
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +10 -6
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +80 -57
  65. package/ed448.js.map +1 -1
  66. package/esm/_shortw_utils.d.ts +7 -5
  67. package/esm/_shortw_utils.d.ts.map +1 -1
  68. package/esm/_shortw_utils.js +2 -8
  69. package/esm/_shortw_utils.js.map +1 -1
  70. package/esm/abstract/bls.d.ts +60 -24
  71. package/esm/abstract/bls.d.ts.map +1 -1
  72. package/esm/abstract/bls.js +158 -109
  73. package/esm/abstract/bls.js.map +1 -1
  74. package/esm/abstract/curve.d.ts +44 -9
  75. package/esm/abstract/curve.d.ts.map +1 -1
  76. package/esm/abstract/curve.js +83 -8
  77. package/esm/abstract/curve.js.map +1 -1
  78. package/esm/abstract/edwards.d.ts +112 -25
  79. package/esm/abstract/edwards.d.ts.map +1 -1
  80. package/esm/abstract/edwards.js +138 -104
  81. package/esm/abstract/edwards.js.map +1 -1
  82. package/esm/abstract/fft.d.ts +12 -10
  83. package/esm/abstract/fft.d.ts.map +1 -1
  84. package/esm/abstract/fft.js +10 -11
  85. package/esm/abstract/fft.js.map +1 -1
  86. package/esm/abstract/hash-to-curve.d.ts +25 -11
  87. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  88. package/esm/abstract/hash-to-curve.js +17 -14
  89. package/esm/abstract/hash-to-curve.js.map +1 -1
  90. package/esm/abstract/modular.d.ts +24 -11
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +48 -19
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +1 -1
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +5 -4
  97. package/esm/abstract/montgomery.js.map +1 -1
  98. package/esm/abstract/poseidon.d.ts +5 -13
  99. package/esm/abstract/poseidon.d.ts.map +1 -1
  100. package/esm/abstract/poseidon.js +12 -7
  101. package/esm/abstract/poseidon.js.map +1 -1
  102. package/esm/abstract/tower.d.ts +20 -46
  103. package/esm/abstract/tower.d.ts.map +1 -1
  104. package/esm/abstract/tower.js +9 -3
  105. package/esm/abstract/tower.js.map +1 -1
  106. package/esm/abstract/utils.d.ts +1 -115
  107. package/esm/abstract/utils.d.ts.map +1 -1
  108. package/esm/abstract/utils.js +3 -344
  109. package/esm/abstract/utils.js.map +1 -1
  110. package/esm/abstract/weierstrass.d.ts +132 -76
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +460 -400
  113. package/esm/abstract/weierstrass.js.map +1 -1
  114. package/esm/bls12-381.d.ts +2 -0
  115. package/esm/bls12-381.d.ts.map +1 -1
  116. package/esm/bls12-381.js +503 -465
  117. package/esm/bls12-381.js.map +1 -1
  118. package/esm/bn254.d.ts +2 -0
  119. package/esm/bn254.d.ts.map +1 -1
  120. package/esm/bn254.js +41 -29
  121. package/esm/bn254.js.map +1 -1
  122. package/esm/ed25519.d.ts +8 -5
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +62 -49
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +10 -6
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +74 -51
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/misc.d.ts.map +1 -1
  131. package/esm/misc.js +31 -26
  132. package/esm/misc.js.map +1 -1
  133. package/esm/nist.d.ts +7 -16
  134. package/esm/nist.d.ts.map +1 -1
  135. package/esm/nist.js +86 -97
  136. package/esm/nist.js.map +1 -1
  137. package/esm/p256.d.ts +3 -3
  138. package/esm/p384.d.ts +3 -3
  139. package/esm/p521.d.ts +3 -3
  140. package/esm/secp256k1.d.ts +6 -6
  141. package/esm/secp256k1.d.ts.map +1 -1
  142. package/esm/secp256k1.js +43 -40
  143. package/esm/secp256k1.js.map +1 -1
  144. package/esm/utils.d.ts +96 -0
  145. package/esm/utils.d.ts.map +1 -0
  146. package/esm/utils.js +279 -0
  147. package/esm/utils.js.map +1 -0
  148. package/misc.d.ts.map +1 -1
  149. package/misc.js +35 -30
  150. package/misc.js.map +1 -1
  151. package/nist.d.ts +7 -16
  152. package/nist.d.ts.map +1 -1
  153. package/nist.js +86 -97
  154. package/nist.js.map +1 -1
  155. package/p256.d.ts +3 -3
  156. package/p384.d.ts +3 -3
  157. package/p521.d.ts +3 -3
  158. package/package.json +14 -5
  159. package/secp256k1.d.ts +6 -6
  160. package/secp256k1.d.ts.map +1 -1
  161. package/secp256k1.js +46 -43
  162. package/secp256k1.js.map +1 -1
  163. package/src/_shortw_utils.ts +5 -15
  164. package/src/abstract/bls.ts +260 -145
  165. package/src/abstract/curve.ts +115 -13
  166. package/src/abstract/edwards.ts +279 -138
  167. package/src/abstract/fft.ts +30 -19
  168. package/src/abstract/hash-to-curve.ts +51 -27
  169. package/src/abstract/modular.ts +49 -28
  170. package/src/abstract/montgomery.ts +9 -7
  171. package/src/abstract/poseidon.ts +22 -18
  172. package/src/abstract/tower.ts +36 -67
  173. package/src/abstract/utils.ts +3 -378
  174. package/src/abstract/weierstrass.ts +700 -453
  175. package/src/bls12-381.ts +540 -489
  176. package/src/bn254.ts +47 -35
  177. package/src/ed25519.ts +80 -64
  178. package/src/ed448.ts +129 -92
  179. package/src/misc.ts +39 -34
  180. package/src/nist.ts +138 -127
  181. package/src/p256.ts +3 -3
  182. package/src/p384.ts +3 -3
  183. package/src/p521.ts +3 -3
  184. package/src/secp256k1.ts +58 -46
  185. package/src/utils.ts +328 -0
  186. package/utils.d.ts +96 -0
  187. package/utils.d.ts.map +1 -0
  188. package/utils.js +313 -0
  189. package/utils.js.map +1 -0
@@ -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 of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
11
- * * n: order of prime subgroup a.k.a total amount of valid curve points
12
- * * Gx: Base point (x, y) aka generator point. Gx = 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,45 +25,16 @@
38
25
  * @module
39
26
  */
40
27
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
41
- // prettier-ignore
42
- import { pippenger, validateBasic, wNAF } from "./curve.js";
43
- // prettier-ignore
44
- import { Field, FpInvertBatch, getMinHashLength, invert, mapHashToField, mod, validateField } from "./modular.js";
45
- // prettier-ignore
46
- import { aInRange, abool, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes, inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject } from "./utils.js";
28
+ import { hmac } from '@noble/hashes/hmac.js';
29
+ import { _validateObject, abool, abytes, aInRange, bitMask, bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes, inRange, isBytes, memoized, numberToHexUnpadded, randomBytes, } from "../utils.js";
30
+ import { _createCurveFields, mulEndoUnsafe, negateCt, normalizeZ, pippenger, wNAF, } from "./curve.js";
31
+ import { Field, FpInvertBatch, getMinHashLength, mapHashToField, validateField, } from "./modular.js";
47
32
  function validateSigVerOpts(opts) {
48
33
  if (opts.lowS !== undefined)
49
34
  abool('lowS', opts.lowS);
50
35
  if (opts.prehash !== undefined)
51
36
  abool('prehash', opts.prehash);
52
37
  }
53
- function validatePointOpts(curve) {
54
- const opts = validateBasic(curve);
55
- validateObject(opts, {
56
- a: 'field',
57
- b: 'field',
58
- }, {
59
- allowInfinityPoint: 'boolean',
60
- allowedPrivateKeyLengths: 'array',
61
- clearCofactor: 'function',
62
- fromBytes: 'function',
63
- isTorsionFree: 'function',
64
- toBytes: 'function',
65
- wrapPrivateKey: 'boolean',
66
- });
67
- const { endo, Fp, a } = opts;
68
- if (endo) {
69
- if (!Fp.eql(a, Fp.ZERO)) {
70
- throw new Error('invalid endo: CURVE.a must be 0');
71
- }
72
- if (typeof endo !== 'object' ||
73
- typeof endo.beta !== 'bigint' ||
74
- typeof endo.splitScalar !== 'function') {
75
- throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
76
- }
77
- }
78
- return Object.freeze({ ...opts });
79
- }
80
38
  export class DERErr extends Error {
81
39
  constructor(m = '') {
82
40
  super(m);
@@ -193,40 +151,139 @@ export const DER = {
193
151
  return tlv.encode(0x30, seq);
194
152
  },
195
153
  };
196
- function numToSizedHex(num, size) {
197
- return bytesToHex(numberToBytesBE(num, size));
198
- }
199
154
  // Be friendly to bad ECMAScript parsers by not using bigint literals
200
155
  // prettier-ignore
201
156
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
202
- export function weierstrassPoints(opts) {
203
- const CURVE = validatePointOpts(opts);
204
- const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
205
- const Fn = Field(CURVE.n, CURVE.nBitLength);
206
- const toBytes = CURVE.toBytes ||
207
- ((_c, point, _isCompressed) => {
208
- const a = point.toAffine();
209
- return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
210
- });
211
- const fromBytes = CURVE.fromBytes ||
212
- ((bytes) => {
213
- // const head = bytes[0];
214
- const tail = bytes.subarray(1);
215
- // if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
216
- const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
217
- const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
218
- return { x, y };
219
- });
157
+ // TODO: remove
158
+ export function _legacyHelperEquat(Fp, a, b) {
220
159
  /**
221
160
  * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
222
161
  * @returns y²
223
162
  */
224
163
  function weierstrassEquation(x) {
225
- const { a, b } = CURVE;
226
164
  const x2 = Fp.sqr(x); // x * x
227
165
  const x3 = Fp.mul(x2, x); // x² * x
228
166
  return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
229
167
  }
168
+ return weierstrassEquation;
169
+ }
170
+ export function _legacyHelperNormPriv(Fn, allowedPrivateKeyLengths, wrapPrivateKey) {
171
+ const { BYTES: expected } = Fn;
172
+ // Validates if priv key is valid and converts it to bigint.
173
+ function normPrivateKeyToScalar(key) {
174
+ let num;
175
+ if (typeof key === 'bigint') {
176
+ num = key;
177
+ }
178
+ else {
179
+ let bytes = ensureBytes('private key', key);
180
+ if (allowedPrivateKeyLengths) {
181
+ if (!allowedPrivateKeyLengths.includes(bytes.length * 2))
182
+ throw new Error('invalid private key');
183
+ const padded = new Uint8Array(expected);
184
+ padded.set(bytes, padded.length - bytes.length);
185
+ bytes = padded;
186
+ }
187
+ try {
188
+ num = Fn.fromBytes(bytes);
189
+ }
190
+ catch (error) {
191
+ throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
192
+ }
193
+ }
194
+ if (wrapPrivateKey)
195
+ num = Fn.create(num); // disabled by default, enabled for BLS
196
+ if (!Fn.isValidNot0(num))
197
+ throw new Error('invalid private key: out of range [1..N-1]');
198
+ return num;
199
+ }
200
+ return normPrivateKeyToScalar;
201
+ }
202
+ export function weierstrassN(CURVE, curveOpts = {}) {
203
+ const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
204
+ const { h: cofactor, n: CURVE_ORDER } = CURVE;
205
+ _validateObject(curveOpts, {}, {
206
+ allowInfinityPoint: 'boolean',
207
+ clearCofactor: 'function',
208
+ isTorsionFree: 'function',
209
+ fromBytes: 'function',
210
+ toBytes: 'function',
211
+ endo: 'object',
212
+ wrapPrivateKey: 'boolean',
213
+ });
214
+ const { endo } = curveOpts;
215
+ if (endo) {
216
+ // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
217
+ if (!Fp.is0(CURVE.a) ||
218
+ typeof endo.beta !== 'bigint' ||
219
+ typeof endo.splitScalar !== 'function') {
220
+ throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
221
+ }
222
+ }
223
+ function assertCompressionIsSupported() {
224
+ if (!Fp.isOdd)
225
+ throw new Error('compression is not supported: Field does not have .isOdd()');
226
+ }
227
+ // Implements IEEE P1363 point encoding
228
+ function pointToBytes(_c, point, isCompressed) {
229
+ const { x, y } = point.toAffine();
230
+ const bx = Fp.toBytes(x);
231
+ abool('isCompressed', isCompressed);
232
+ if (isCompressed) {
233
+ assertCompressionIsSupported();
234
+ const hasEvenY = !Fp.isOdd(y);
235
+ return concatBytes(pprefix(hasEvenY), bx);
236
+ }
237
+ else {
238
+ return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
239
+ }
240
+ }
241
+ function pointFromBytes(bytes) {
242
+ abytes(bytes);
243
+ const L = Fp.BYTES;
244
+ const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
245
+ const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
246
+ const length = bytes.length;
247
+ const head = bytes[0];
248
+ const tail = bytes.subarray(1);
249
+ // No actual validation is done here: use .assertValidity()
250
+ if (length === LC && (head === 0x02 || head === 0x03)) {
251
+ const x = Fp.fromBytes(tail);
252
+ if (!Fp.isValid(x))
253
+ throw new Error('bad point: is not on curve, wrong x');
254
+ const y2 = weierstrassEquation(x); // y² = x³ + ax + b
255
+ let y;
256
+ try {
257
+ y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
258
+ }
259
+ catch (sqrtError) {
260
+ const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
261
+ throw new Error('bad point: is not on curve, sqrt error' + err);
262
+ }
263
+ assertCompressionIsSupported();
264
+ const isYOdd = Fp.isOdd(y); // (y & _1n) === _1n;
265
+ const isHeadOdd = (head & 1) === 1; // ECDSA-specific
266
+ if (isHeadOdd !== isYOdd)
267
+ y = Fp.neg(y);
268
+ return { x, y };
269
+ }
270
+ else if (length === LU && head === 0x04) {
271
+ // TODO: more checks
272
+ const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
273
+ const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
274
+ if (!isValidXY(x, y))
275
+ throw new Error('bad point: is not on curve');
276
+ return { x, y };
277
+ }
278
+ else {
279
+ throw new Error(`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`);
280
+ }
281
+ }
282
+ const toBytes = curveOpts.toBytes || pointToBytes;
283
+ const fromBytes = curveOpts.fromBytes || pointFromBytes;
284
+ const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
285
+ // TODO: move top-level
286
+ /** Checks whether equation holds for given x, y: y² == x³ + ax + b */
230
287
  function isValidXY(x, y) {
231
288
  const left = Fp.sqr(y); // y²
232
289
  const right = weierstrassEquation(x); // x³ + ax + b
@@ -242,36 +299,11 @@ export function weierstrassPoints(opts) {
242
299
  const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
243
300
  if (Fp.is0(Fp.add(_4a3, _27b2)))
244
301
  throw new Error('bad curve params: a or b');
245
- // Valid group elements reside in range 1..n-1
246
- function isWithinCurveOrder(num) {
247
- return inRange(num, _1n, CURVE.n);
248
- }
249
- // Validates if priv key is valid and converts it to bigint.
250
- // Supports options allowedPrivateKeyLengths and wrapPrivateKey.
251
- function normPrivateKeyToScalar(key) {
252
- const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
253
- if (lengths && typeof key !== 'bigint') {
254
- if (isBytes(key))
255
- key = bytesToHex(key);
256
- // Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
257
- if (typeof key !== 'string' || !lengths.includes(key.length))
258
- throw new Error('invalid private key');
259
- key = key.padStart(nByteLength * 2, '0');
260
- }
261
- let num;
262
- try {
263
- num =
264
- typeof key === 'bigint'
265
- ? key
266
- : bytesToNumberBE(ensureBytes('private key', key, nByteLength));
267
- }
268
- catch (error) {
269
- throw new Error('invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key);
270
- }
271
- if (wrapPrivateKey)
272
- num = mod(num, N); // disabled by default, enabled for BLS
273
- aInRange('private key', num, _1n, N); // num in range [1..N-1]
274
- return num;
302
+ /** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
303
+ function acoord(title, n, banZero = false) {
304
+ if (!Fp.isValid(n) || (banZero && Fp.is0(n)))
305
+ throw new Error(`bad point coordinate ${title}`);
306
+ return n;
275
307
  }
276
308
  function aprjpoint(other) {
277
309
  if (!(other instanceof Point))
@@ -307,50 +339,48 @@ export function weierstrassPoints(opts) {
307
339
  // (0, 1, 0) aka ZERO is invalid in most contexts.
308
340
  // In BLS, ZERO can be serialized, so we allow it.
309
341
  // (0, 0, 0) is invalid representation of ZERO.
310
- if (CURVE.allowInfinityPoint && !Fp.is0(p.py))
342
+ if (curveOpts.allowInfinityPoint && !Fp.is0(p.py))
311
343
  return;
312
344
  throw new Error('bad point: ZERO');
313
345
  }
314
346
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
315
347
  const { x, y } = p.toAffine();
316
- // Check if x, y are valid field elements
317
348
  if (!Fp.isValid(x) || !Fp.isValid(y))
318
- throw new Error('bad point: x or y not FE');
349
+ throw new Error('bad point: x or y not field elements');
319
350
  if (!isValidXY(x, y))
320
351
  throw new Error('bad point: equation left != right');
321
352
  if (!p.isTorsionFree())
322
353
  throw new Error('bad point: not in prime-order subgroup');
323
354
  return true;
324
355
  });
356
+ function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
357
+ k2p = new Point(Fp.mul(k2p.px, endoBeta), k2p.py, k2p.pz);
358
+ k1p = negateCt(k1neg, k1p);
359
+ k2p = negateCt(k2neg, k2p);
360
+ return k1p.add(k2p);
361
+ }
325
362
  /**
326
- * Projective Point works in 3d / projective (homogeneous) coordinates: (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
327
- * Default Point works in 2d / affine coordinates: (x, y)
363
+ * Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
364
+ * Default Point works in 2d / affine coordinates: (x, y).
328
365
  * We're doing calculations in projective, because its operations don't require costly inversion.
329
366
  */
330
367
  class Point {
368
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
331
369
  constructor(px, py, pz) {
332
- if (px == null || !Fp.isValid(px))
333
- throw new Error('x required');
334
- if (py == null || !Fp.isValid(py) || Fp.is0(py))
335
- throw new Error('y required');
336
- if (pz == null || !Fp.isValid(pz))
337
- throw new Error('z required');
338
- this.px = px;
339
- this.py = py;
340
- this.pz = pz;
370
+ this.px = acoord('x', px);
371
+ this.py = acoord('y', py, true);
372
+ this.pz = acoord('z', pz);
341
373
  Object.freeze(this);
342
374
  }
343
- // Does not validate if the point is on-curve.
344
- // Use fromHex instead, or call assertValidity() later.
375
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
345
376
  static fromAffine(p) {
346
377
  const { x, y } = p || {};
347
378
  if (!p || !Fp.isValid(x) || !Fp.isValid(y))
348
379
  throw new Error('invalid affine point');
349
380
  if (p instanceof Point)
350
381
  throw new Error('projective point not allowed');
351
- const is0 = (i) => Fp.eql(i, Fp.ZERO);
352
- // fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
353
- if (is0(x) && is0(y))
382
+ // (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
383
+ if (Fp.is0(x) && Fp.is0(y))
354
384
  return Point.ZERO;
355
385
  return new Point(x, y, Fp.ONE);
356
386
  }
@@ -360,50 +390,56 @@ export function weierstrassPoints(opts) {
360
390
  get y() {
361
391
  return this.toAffine().y;
362
392
  }
363
- /**
364
- * Takes a bunch of Projective Points but executes only one
365
- * inversion on all of them. Inversion is very slow operation,
366
- * so this improves performance massively.
367
- * Optimization: converts a list of projective points to a list of identical points with Z=1.
368
- */
369
393
  static normalizeZ(points) {
370
- const toInv = FpInvertBatch(Fp, points.map((p) => p.pz));
371
- return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
394
+ return normalizeZ(Point, 'pz', points);
372
395
  }
373
- /**
374
- * Converts hash string or Uint8Array to Point.
375
- * @param hex short/long ECDSA hex
376
- */
396
+ static fromBytes(bytes) {
397
+ abytes(bytes);
398
+ return Point.fromHex(bytes);
399
+ }
400
+ /** Converts hash string or Uint8Array to Point. */
377
401
  static fromHex(hex) {
378
402
  const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
379
403
  P.assertValidity();
380
404
  return P;
381
405
  }
382
- // Multiplies generator point by privateKey.
406
+ /** Multiplies generator point by privateKey. */
383
407
  static fromPrivateKey(privateKey) {
408
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
384
409
  return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
385
410
  }
386
- // Multiscalar Multiplication
411
+ /** Multiscalar Multiplication */
387
412
  static msm(points, scalars) {
388
413
  return pippenger(Point, Fn, points, scalars);
389
414
  }
390
- // "Private method", don't use it directly
391
- _setWindowSize(windowSize) {
415
+ /**
416
+ *
417
+ * @param windowSize
418
+ * @param isLazy true will defer table computation until the first multiplication
419
+ * @returns
420
+ */
421
+ precompute(windowSize = 8, isLazy = true) {
392
422
  wnaf.setWindowSize(this, windowSize);
423
+ if (!isLazy)
424
+ this.multiply(_3n); // random number
425
+ return this;
393
426
  }
394
- // A point on curve is valid if it conforms to equation.
427
+ /** "Private method", don't use it directly */
428
+ _setWindowSize(windowSize) {
429
+ this.precompute(windowSize);
430
+ }
431
+ // TODO: return `this`
432
+ /** A point on curve is valid if it conforms to equation. */
395
433
  assertValidity() {
396
434
  assertValidMemo(this);
397
435
  }
398
436
  hasEvenY() {
399
437
  const { y } = this.toAffine();
400
- if (Fp.isOdd)
401
- return !Fp.isOdd(y);
402
- throw new Error("Field doesn't support isOdd");
438
+ if (!Fp.isOdd)
439
+ throw new Error("Field doesn't support isOdd");
440
+ return !Fp.isOdd(y);
403
441
  }
404
- /**
405
- * Compare one point to another.
406
- */
442
+ /** Compare one point to another. */
407
443
  equals(other) {
408
444
  aprjpoint(other);
409
445
  const { px: X1, py: Y1, pz: Z1 } = this;
@@ -412,9 +448,7 @@ export function weierstrassPoints(opts) {
412
448
  const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
413
449
  return U1 && U2;
414
450
  }
415
- /**
416
- * Flips point to one corresponding to (x, -y) in Affine coordinates.
417
- */
451
+ /** Flips point to one corresponding to (x, -y) in Affine coordinates. */
418
452
  negate() {
419
453
  return new Point(this.px, Fp.neg(this.py), this.pz);
420
454
  }
@@ -519,47 +553,6 @@ export function weierstrassPoints(opts) {
519
553
  is0() {
520
554
  return this.equals(Point.ZERO);
521
555
  }
522
- wNAF(n) {
523
- return wnaf.wNAFCached(this, n, Point.normalizeZ);
524
- }
525
- /**
526
- * Non-constant-time multiplication. Uses double-and-add algorithm.
527
- * It's faster, but should only be used when you don't care about
528
- * an exposed private key e.g. sig verification, which works over *public* keys.
529
- */
530
- multiplyUnsafe(sc) {
531
- const { endo, n: N } = CURVE;
532
- aInRange('scalar', sc, _0n, N);
533
- const I = Point.ZERO;
534
- if (sc === _0n)
535
- return I;
536
- if (this.is0() || sc === _1n)
537
- return this;
538
- // Case a: no endomorphism. Case b: has precomputes.
539
- if (!endo || wnaf.hasPrecomputes(this))
540
- return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
541
- // Case c: endomorphism
542
- /** See docs for {@link EndomorphismOpts} */
543
- let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
544
- let k1p = I;
545
- let k2p = I;
546
- let d = this;
547
- while (k1 > _0n || k2 > _0n) {
548
- if (k1 & _1n)
549
- k1p = k1p.add(d);
550
- if (k2 & _1n)
551
- k2p = k2p.add(d);
552
- d = d.double();
553
- k1 >>= _1n;
554
- k2 >>= _1n;
555
- }
556
- if (k1neg)
557
- k1p = k1p.negate();
558
- if (k2neg)
559
- k2p = k2p.negate();
560
- k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
561
- return k1p.add(k2p);
562
- }
563
556
  /**
564
557
  * Constant time multiplication.
565
558
  * Uses wNAF method. Windowed method may be 10% faster,
@@ -570,22 +563,21 @@ export function weierstrassPoints(opts) {
570
563
  * @returns New point
571
564
  */
572
565
  multiply(scalar) {
573
- const { endo, n: N } = CURVE;
574
- aInRange('scalar', scalar, _1n, N);
566
+ const { endo } = curveOpts;
567
+ if (!Fn.isValidNot0(scalar))
568
+ throw new Error('invalid scalar: out of range'); // 0 is invalid
575
569
  let point, fake; // Fake point is used to const-time mult
570
+ const mul = (n) => wnaf.wNAFCached(this, n, Point.normalizeZ);
576
571
  /** See docs for {@link EndomorphismOpts} */
577
572
  if (endo) {
578
573
  const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
579
- let { p: k1p, f: f1p } = this.wNAF(k1);
580
- let { p: k2p, f: f2p } = this.wNAF(k2);
581
- k1p = wnaf.constTimeNegate(k1neg, k1p);
582
- k2p = wnaf.constTimeNegate(k2neg, k2p);
583
- k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
584
- point = k1p.add(k2p);
585
- fake = f1p.add(f2p);
574
+ const { p: k1p, f: k1f } = mul(k1);
575
+ const { p: k2p, f: k2f } = mul(k2);
576
+ fake = k1f.add(k2f);
577
+ point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
586
578
  }
587
579
  else {
588
- const { p, f } = this.wNAF(scalar);
580
+ const { p, f } = mul(scalar);
589
581
  point = p;
590
582
  fake = f;
591
583
  }
@@ -593,162 +585,131 @@ export function weierstrassPoints(opts) {
593
585
  return Point.normalizeZ([point, fake])[0];
594
586
  }
595
587
  /**
596
- * Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
597
- * Not using Strauss-Shamir trick: precomputation tables are faster.
598
- * The trick could be useful if both P and Q are not G (not in our case).
599
- * @returns non-zero affine point
588
+ * Non-constant-time multiplication. Uses double-and-add algorithm.
589
+ * It's faster, but should only be used when you don't care about
590
+ * an exposed private key e.g. sig verification, which works over *public* keys.
600
591
  */
592
+ multiplyUnsafe(sc) {
593
+ const { endo } = curveOpts;
594
+ const p = this;
595
+ if (!Fn.isValid(sc))
596
+ throw new Error('invalid scalar: out of range'); // 0 is valid
597
+ if (sc === _0n || p.is0())
598
+ return Point.ZERO;
599
+ if (sc === _1n)
600
+ return p; // fast-path
601
+ if (wnaf.hasPrecomputes(this))
602
+ return this.multiply(sc);
603
+ if (endo) {
604
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
605
+ // `wNAFCachedUnsafe` is 30% slower
606
+ const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
607
+ return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
608
+ }
609
+ else {
610
+ return wnaf.wNAFCachedUnsafe(p, sc);
611
+ }
612
+ }
601
613
  multiplyAndAddUnsafe(Q, a, b) {
602
- const G = Point.BASE; // No Strauss-Shamir trick: we have 10% faster G precomputes
603
- const mul = (P, a // Select faster multiply() method
604
- ) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
605
- const sum = mul(this, a).add(mul(Q, b));
614
+ const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
606
615
  return sum.is0() ? undefined : sum;
607
616
  }
608
- // Converts Projective point to affine (x, y) coordinates.
609
- // Can accept precomputed Z^-1 - for example, from invertBatch.
610
- // (x, y, z) (x=x/z, y=y/z)
611
- toAffine(iz) {
612
- return toAffineMemo(this, iz);
617
+ /**
618
+ * Converts Projective point to affine (x, y) coordinates.
619
+ * @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
620
+ */
621
+ toAffine(invertedZ) {
622
+ return toAffineMemo(this, invertedZ);
613
623
  }
624
+ /**
625
+ * Checks whether Point is free of torsion elements (is in prime subgroup).
626
+ * Always torsion-free for cofactor=1 curves.
627
+ */
614
628
  isTorsionFree() {
615
- const { h: cofactor, isTorsionFree } = CURVE;
629
+ const { isTorsionFree } = curveOpts;
616
630
  if (cofactor === _1n)
617
- return true; // No subgroups, always torsion-free
631
+ return true;
618
632
  if (isTorsionFree)
619
633
  return isTorsionFree(Point, this);
620
- throw new Error('isTorsionFree() has not been declared for the elliptic curve');
634
+ return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
621
635
  }
622
636
  clearCofactor() {
623
- const { h: cofactor, clearCofactor } = CURVE;
637
+ const { clearCofactor } = curveOpts;
624
638
  if (cofactor === _1n)
625
639
  return this; // Fast-path
626
640
  if (clearCofactor)
627
641
  return clearCofactor(Point, this);
628
- return this.multiplyUnsafe(CURVE.h);
642
+ return this.multiplyUnsafe(cofactor);
629
643
  }
630
- toRawBytes(isCompressed = true) {
644
+ toBytes(isCompressed = true) {
631
645
  abool('isCompressed', isCompressed);
632
646
  this.assertValidity();
633
647
  return toBytes(Point, this, isCompressed);
634
648
  }
649
+ /** @deprecated use `toBytes` */
650
+ toRawBytes(isCompressed = true) {
651
+ return this.toBytes(isCompressed);
652
+ }
635
653
  toHex(isCompressed = true) {
636
- abool('isCompressed', isCompressed);
637
- return bytesToHex(this.toRawBytes(isCompressed));
654
+ return bytesToHex(this.toBytes(isCompressed));
655
+ }
656
+ toString() {
657
+ return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
638
658
  }
639
659
  }
640
660
  // base / generator point
641
661
  Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
642
662
  // zero / infinity / identity point
643
663
  Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
644
- const { endo, nBitLength } = CURVE;
645
- const wnaf = wNAF(Point, endo ? Math.ceil(nBitLength / 2) : nBitLength);
646
- return {
647
- CURVE,
648
- ProjectivePoint: Point,
649
- normPrivateKeyToScalar,
650
- weierstrassEquation,
651
- isWithinCurveOrder,
652
- };
664
+ // fields
665
+ Point.Fp = Fp;
666
+ Point.Fn = Fn;
667
+ const bits = Fn.BITS;
668
+ const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
669
+ return Point;
653
670
  }
654
- function validateOpts(curve) {
655
- const opts = validateBasic(curve);
656
- validateObject(opts, {
657
- hash: 'hash',
671
+ // _legacyWeierstrass
672
+ /** @deprecated use `weierstrassN` */
673
+ export function weierstrassPoints(c) {
674
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
675
+ const Point = weierstrassN(CURVE, curveOpts);
676
+ return _weierstrass_new_output_to_legacy(c, Point);
677
+ }
678
+ // Points start with byte 0x02 when y is even; otherwise 0x03
679
+ function pprefix(hasEvenY) {
680
+ return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
681
+ }
682
+ export function ecdsa(Point, ecdsaOpts, curveOpts = {}) {
683
+ _validateObject(ecdsaOpts, { hash: 'function' }, {
658
684
  hmac: 'function',
685
+ lowS: 'boolean',
659
686
  randomBytes: 'function',
660
- }, {
661
687
  bits2int: 'function',
662
688
  bits2int_modN: 'function',
663
- lowS: 'boolean',
664
- });
665
- return Object.freeze({ lowS: true, ...opts });
666
- }
667
- /**
668
- * Creates short weierstrass curve and ECDSA signature methods for it.
669
- * @example
670
- * import { Field } from '@noble/curves/abstract/modular';
671
- * // Before that, define BigInt-s: a, b, p, n, Gx, Gy
672
- * const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
673
- */
674
- export function weierstrass(curveDef) {
675
- const CURVE = validateOpts(curveDef);
676
- const { Fp, n: CURVE_ORDER, nByteLength, nBitLength } = CURVE;
677
- const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
678
- const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
679
- function modN(a) {
680
- return mod(a, CURVE_ORDER);
681
- }
682
- function invN(a) {
683
- return invert(a, CURVE_ORDER);
684
- }
685
- const { ProjectivePoint: Point, normPrivateKeyToScalar, weierstrassEquation, isWithinCurveOrder, } = weierstrassPoints({
686
- ...CURVE,
687
- toBytes(_c, point, isCompressed) {
688
- const a = point.toAffine();
689
- const x = Fp.toBytes(a.x);
690
- const cat = concatBytes;
691
- abool('isCompressed', isCompressed);
692
- if (isCompressed) {
693
- return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
694
- }
695
- else {
696
- return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
697
- }
698
- },
699
- fromBytes(bytes) {
700
- const len = bytes.length;
701
- const head = bytes[0];
702
- const tail = bytes.subarray(1);
703
- // this.assertValidity() is done inside of fromHex
704
- if (len === compressedLen && (head === 0x02 || head === 0x03)) {
705
- const x = bytesToNumberBE(tail);
706
- if (!inRange(x, _1n, Fp.ORDER))
707
- throw new Error('Point is not on curve');
708
- const y2 = weierstrassEquation(x); // y² = x³ + ax + b
709
- let y;
710
- try {
711
- y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
712
- }
713
- catch (sqrtError) {
714
- const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
715
- throw new Error('Point is not on curve' + suffix);
716
- }
717
- const isYOdd = (y & _1n) === _1n;
718
- // ECDSA
719
- const isHeadOdd = (head & 1) === 1;
720
- if (isHeadOdd !== isYOdd)
721
- y = Fp.neg(y);
722
- return { x, y };
723
- }
724
- else if (len === uncompressedLen && head === 0x04) {
725
- const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
726
- const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
727
- return { x, y };
728
- }
729
- else {
730
- const cl = compressedLen;
731
- const ul = uncompressedLen;
732
- throw new Error('invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len);
733
- }
734
- },
735
689
  });
690
+ const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
691
+ const hmac_ = ecdsaOpts.hmac ||
692
+ ((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes(...msgs)));
693
+ const { Fp, Fn } = Point;
694
+ const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
736
695
  function isBiggerThanHalfOrder(number) {
737
696
  const HALF = CURVE_ORDER >> _1n;
738
697
  return number > HALF;
739
698
  }
740
699
  function normalizeS(s) {
741
- return isBiggerThanHalfOrder(s) ? modN(-s) : s;
700
+ return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
701
+ }
702
+ function aValidRS(title, num) {
703
+ if (!Fn.isValidNot0(num))
704
+ throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
742
705
  }
743
- // slice bytes num
744
- const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to));
745
706
  /**
746
707
  * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
747
708
  */
748
709
  class Signature {
749
710
  constructor(r, s, recovery) {
750
- aInRange('r', r, _1n, CURVE_ORDER); // r in [1..N]
751
- aInRange('s', s, _1n, CURVE_ORDER); // s in [1..N]
711
+ aValidRS('r', r); // r in [1..N-1]
712
+ aValidRS('s', s); // s in [1..N-1]
752
713
  this.r = r;
753
714
  this.s = s;
754
715
  if (recovery != null)
@@ -757,9 +718,9 @@ export function weierstrass(curveDef) {
757
718
  }
758
719
  // pair (bytes of r, bytes of s)
759
720
  static fromCompact(hex) {
760
- const l = nByteLength;
761
- hex = ensureBytes('compactSignature', hex, l * 2);
762
- return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l));
721
+ const L = Fn.BYTES;
722
+ const b = ensureBytes('compactSignature', hex, L * 2);
723
+ return new Signature(Fn.fromBytes(b.subarray(0, L)), Fn.fromBytes(b.subarray(L, L * 2)));
763
724
  }
764
725
  // DER encoded ECDSA signature
765
726
  // https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
@@ -775,22 +736,36 @@ export function weierstrass(curveDef) {
775
736
  addRecoveryBit(recovery) {
776
737
  return new Signature(this.r, this.s, recovery);
777
738
  }
739
+ // ProjPointType<bigint>
778
740
  recoverPublicKey(msgHash) {
741
+ const FIELD_ORDER = Fp.ORDER;
779
742
  const { r, s, recovery: rec } = this;
780
- const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
781
743
  if (rec == null || ![0, 1, 2, 3].includes(rec))
782
744
  throw new Error('recovery id invalid');
783
- const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
784
- if (radj >= Fp.ORDER)
745
+ // ECDSA recovery is hard for cofactor > 1 curves.
746
+ // In sign, `r = q.x mod n`, and here we recover q.x from r.
747
+ // While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
748
+ // However, for cofactor>1, r+n may not get q.x:
749
+ // r+n*i would need to be done instead where i is unknown.
750
+ // To easily get i, we either need to:
751
+ // a. increase amount of valid recid values (4, 5...); OR
752
+ // b. prohibit non-prime-order signatures (recid > 1).
753
+ const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
754
+ if (hasCofactor && rec > 1)
755
+ throw new Error('recovery id is ambiguous for h>1 curve');
756
+ const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
757
+ if (!Fp.isValid(radj))
785
758
  throw new Error('recovery id 2 or 3 invalid');
786
- const prefix = (rec & 1) === 0 ? '02' : '03';
787
- const R = Point.fromHex(prefix + numToSizedHex(radj, Fp.BYTES));
788
- const ir = invN(radj); // r^-1
789
- const u1 = modN(-h * ir); // -hr^-1
790
- const u2 = modN(s * ir); // sr^-1
791
- const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
792
- if (!Q)
793
- throw new Error('point at infinify'); // unsafe is fine: no priv data leaked
759
+ const x = Fp.toBytes(radj);
760
+ const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
761
+ const ir = Fn.inv(radj); // r^-1
762
+ const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
763
+ const u1 = Fn.create(-h * ir); // -hr^-1
764
+ const u2 = Fn.create(s * ir); // sr^-1
765
+ // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
766
+ const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
767
+ if (Q.is0())
768
+ throw new Error('point at infinify');
794
769
  Q.assertValidity();
795
770
  return Q;
796
771
  }
@@ -799,24 +774,31 @@ export function weierstrass(curveDef) {
799
774
  return isBiggerThanHalfOrder(this.s);
800
775
  }
801
776
  normalizeS() {
802
- return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this;
777
+ return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
778
+ }
779
+ toBytes(format) {
780
+ if (format === 'compact')
781
+ return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
782
+ if (format === 'der')
783
+ return hexToBytes(DER.hexFromSig(this));
784
+ throw new Error('invalid format');
803
785
  }
804
786
  // DER-encoded
805
787
  toDERRawBytes() {
806
- return hexToBytes(this.toDERHex());
788
+ return this.toBytes('der');
807
789
  }
808
790
  toDERHex() {
809
- return DER.hexFromSig(this);
791
+ return bytesToHex(this.toBytes('der'));
810
792
  }
811
793
  // padded bytes of r, then padded bytes of s
812
794
  toCompactRawBytes() {
813
- return hexToBytes(this.toCompactHex());
795
+ return this.toBytes('compact');
814
796
  }
815
797
  toCompactHex() {
816
- const l = nByteLength;
817
- return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
798
+ return bytesToHex(this.toBytes('compact'));
818
799
  }
819
800
  }
801
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, curveOpts.allowedPrivateKeyLengths, curveOpts.wrapPrivateKey);
820
802
  const utils = {
821
803
  isValidPrivateKey(privateKey) {
822
804
  try {
@@ -833,21 +815,11 @@ export function weierstrass(curveDef) {
833
815
  * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
834
816
  */
835
817
  randomPrivateKey: () => {
836
- const length = getMinHashLength(CURVE.n);
837
- return mapHashToField(CURVE.randomBytes(length), CURVE.n);
818
+ const n = CURVE_ORDER;
819
+ return mapHashToField(randomBytes_(getMinHashLength(n)), n);
838
820
  },
839
- /**
840
- * Creates precompute table for an arbitrary EC point. Makes point "cached".
841
- * Allows to massively speed-up `point.multiply(scalar)`.
842
- * @returns cached point
843
- * @example
844
- * const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
845
- * fast.multiply(privKey); // much faster ECDH now
846
- */
847
821
  precompute(windowSize = 8, point = Point.BASE) {
848
- point._setWindowSize(windowSize);
849
- point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
850
- return point;
822
+ return point.precompute(windowSize, false);
851
823
  },
852
824
  };
853
825
  /**
@@ -857,7 +829,7 @@ export function weierstrass(curveDef) {
857
829
  * @returns Public key, full when isCompressed=false; short when isCompressed=true
858
830
  */
859
831
  function getPublicKey(privateKey, isCompressed = true) {
860
- return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
832
+ return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
861
833
  }
862
834
  /**
863
835
  * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
@@ -868,15 +840,15 @@ export function weierstrass(curveDef) {
868
840
  if (item instanceof Point)
869
841
  return true;
870
842
  const arr = ensureBytes('key', item);
871
- const len = arr.length;
872
- const fpl = Fp.BYTES;
873
- const compLen = fpl + 1; // e.g. 33 for 32
874
- const uncompLen = 2 * fpl + 1; // e.g. 65 for 32
875
- if (CURVE.allowedPrivateKeyLengths || nByteLength === compLen) {
843
+ const length = arr.length;
844
+ const L = Fp.BYTES;
845
+ const LC = L + 1; // e.g. 33 for 32
846
+ const LU = 2 * L + 1; // e.g. 65 for 32
847
+ if (curveOpts.allowedPrivateKeyLengths || Fn.BYTES === LC) {
876
848
  return undefined;
877
849
  }
878
850
  else {
879
- return len === compLen || len === uncompLen;
851
+ return length === LC || length === LU;
880
852
  }
881
853
  }
882
854
  /**
@@ -895,13 +867,13 @@ export function weierstrass(curveDef) {
895
867
  if (isProbPub(publicB) === false)
896
868
  throw new Error('second arg must be public key');
897
869
  const b = Point.fromHex(publicB); // check for being on-curve
898
- return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
870
+ return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
899
871
  }
900
872
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
901
873
  // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
902
874
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
903
875
  // int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
904
- const bits2int = CURVE.bits2int ||
876
+ const bits2int = ecdsaOpts.bits2int ||
905
877
  function (bytes) {
906
878
  // Our custom check "just in case", for protection against DoS
907
879
  if (bytes.length > 8192)
@@ -909,22 +881,22 @@ export function weierstrass(curveDef) {
909
881
  // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
910
882
  // for some cases, since bytes.length * 8 is not actual bitLength.
911
883
  const num = bytesToNumberBE(bytes); // check for == u8 done here
912
- const delta = bytes.length * 8 - nBitLength; // truncate to nBitLength leftmost bits
884
+ const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
913
885
  return delta > 0 ? num >> BigInt(delta) : num;
914
886
  };
915
- const bits2int_modN = CURVE.bits2int_modN ||
887
+ const bits2int_modN = ecdsaOpts.bits2int_modN ||
916
888
  function (bytes) {
917
- return modN(bits2int(bytes)); // can't use bytesToNumberBE here
889
+ return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
918
890
  };
919
891
  // NOTE: pads output with zero as per spec
920
- const ORDER_MASK = bitMask(nBitLength);
892
+ const ORDER_MASK = bitMask(fnBits);
921
893
  /**
922
894
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
923
895
  */
924
896
  function int2octets(num) {
925
- aInRange('num < 2^' + nBitLength, num, _0n, ORDER_MASK);
926
- // works with order, can have different size than numToField!
927
- return numberToBytesBE(num, nByteLength);
897
+ // IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
898
+ aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
899
+ return Fn.toBytes(num);
928
900
  }
929
901
  // Steps A, D of RFC6979 3.2
930
902
  // Creates RFC6979 seed; converts msg/privKey to numbers.
@@ -934,7 +906,7 @@ export function weierstrass(curveDef) {
934
906
  function prepSig(msgHash, privateKey, opts = defaultSigOpts) {
935
907
  if (['recovered', 'canonical'].some((k) => k in opts))
936
908
  throw new Error('sign() legacy options not supported');
937
- const { hash, randomBytes } = CURVE;
909
+ const { hash } = ecdsaOpts;
938
910
  let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
939
911
  if (lowS == null)
940
912
  lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
@@ -943,7 +915,7 @@ export function weierstrass(curveDef) {
943
915
  if (prehash)
944
916
  msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
945
917
  // We can't later call bits2octets, since nested bits2int is broken for curves
946
- // with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call.
918
+ // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
947
919
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
948
920
  const h1int = bits2int_modN(msgHash);
949
921
  const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
@@ -951,26 +923,27 @@ export function weierstrass(curveDef) {
951
923
  // extraEntropy. RFC6979 3.6: additional k' (optional).
952
924
  if (ent != null && ent !== false) {
953
925
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
954
- const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is
926
+ const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
955
927
  seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
956
928
  }
957
929
  const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
958
930
  const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
959
931
  // Converts signature params into point w r/s, checks result for validity.
932
+ // Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
933
+ // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
934
+ // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
960
935
  function k2sig(kBytes) {
961
936
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
937
+ // Important: all mod() calls here must be done over N
962
938
  const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
963
- if (!isWithinCurveOrder(k))
964
- return; // Important: all mod() calls here must be done over N
965
- const ik = invN(k); // k^-1 mod n
939
+ if (!Fn.isValidNot0(k))
940
+ return; // Valid scalars (including k) must be in 1..N-1
941
+ const ik = Fn.inv(k); // k^-1 mod n
966
942
  const q = Point.BASE.multiply(k).toAffine(); // q = Gk
967
- const r = modN(q.x); // r = q.x mod n
943
+ const r = Fn.create(q.x); // r = q.x mod n
968
944
  if (r === _0n)
969
945
  return;
970
- // Can use scalar blinding b^-1(bm + bdr) where b [1,q−1] according to
971
- // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
972
- // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
973
- const s = modN(ik * modN(m + r * d)); // Not using blinding here
946
+ const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
974
947
  if (s === _0n)
975
948
  return;
976
949
  let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
@@ -983,8 +956,8 @@ export function weierstrass(curveDef) {
983
956
  }
984
957
  return { seed, k2sig };
985
958
  }
986
- const defaultSigOpts = { lowS: CURVE.lowS, prehash: false };
987
- const defaultVerOpts = { lowS: CURVE.lowS, prehash: false };
959
+ const defaultSigOpts = { lowS: ecdsaOpts.lowS, prehash: false };
960
+ const defaultVerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
988
961
  /**
989
962
  * Signs message hash with a private key.
990
963
  * ```
@@ -1000,13 +973,11 @@ export function weierstrass(curveDef) {
1000
973
  */
1001
974
  function sign(msgHash, privKey, opts = defaultSigOpts) {
1002
975
  const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
1003
- const C = CURVE;
1004
- const drbg = createHmacDrbg(C.hash.outputLen, C.nByteLength, C.hmac);
976
+ const drbg = createHmacDrbg(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
1005
977
  return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1006
978
  }
1007
979
  // Enable precomputes. Slows down first publicKey computation by 20ms.
1008
- Point.BASE._setWindowSize(8);
1009
- // utils.precompute(8, ProjectivePoint.BASE)
980
+ Point.BASE.precompute(8);
1010
981
  /**
1011
982
  * Verifies a signature against message hash and public key.
1012
983
  * Rejects lowS signatures by default: to override,
@@ -1024,13 +995,14 @@ export function weierstrass(curveDef) {
1024
995
  const sg = signature;
1025
996
  msgHash = ensureBytes('msgHash', msgHash);
1026
997
  publicKey = ensureBytes('publicKey', publicKey);
1027
- const { lowS, prehash, format } = opts;
1028
- // Verify opts, deduce signature format
998
+ // Verify opts
1029
999
  validateSigVerOpts(opts);
1000
+ const { lowS, prehash, format } = opts;
1001
+ // TODO: remove
1030
1002
  if ('strict' in opts)
1031
1003
  throw new Error('options.strict was renamed to lowS');
1032
- if (format !== undefined && format !== 'compact' && format !== 'der')
1033
- throw new Error('format must be compact or der');
1004
+ if (format !== undefined && !['compact', 'der', 'js'].includes(format))
1005
+ throw new Error('format must be "compact", "der" or "js"');
1034
1006
  const isHex = typeof sg === 'string' || isBytes(sg);
1035
1007
  const isObj = !isHex &&
1036
1008
  !format &&
@@ -1042,12 +1014,29 @@ export function weierstrass(curveDef) {
1042
1014
  throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1043
1015
  let _sig = undefined;
1044
1016
  let P;
1017
+ // deduce signature format
1045
1018
  try {
1046
- if (isObj)
1047
- _sig = new Signature(sg.r, sg.s);
1019
+ // if (format === 'js') {
1020
+ // if (sg != null && !isBytes(sg)) _sig = new Signature(sg.r, sg.s);
1021
+ // } else if (format === 'compact') {
1022
+ // _sig = Signature.fromCompact(sg);
1023
+ // } else if (format === 'der') {
1024
+ // _sig = Signature.fromDER(sg);
1025
+ // } else {
1026
+ // throw new Error('invalid format');
1027
+ // }
1028
+ if (isObj) {
1029
+ if (format === undefined || format === 'js') {
1030
+ _sig = new Signature(sg.r, sg.s);
1031
+ }
1032
+ else {
1033
+ throw new Error('invalid format');
1034
+ }
1035
+ }
1048
1036
  if (isHex) {
1049
- // Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
1050
- // Since DER can also be 2*nByteLength bytes, we check for it first.
1037
+ // TODO: remove this malleable check
1038
+ // Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
1039
+ // Since DER can also be 2*Fn.BYTES bytes, we check for it first.
1051
1040
  try {
1052
1041
  if (format !== 'compact')
1053
1042
  _sig = Signature.fromDER(sg);
@@ -1068,29 +1057,99 @@ export function weierstrass(curveDef) {
1068
1057
  return false;
1069
1058
  if (lowS && _sig.hasHighS())
1070
1059
  return false;
1060
+ // todo: optional.hash => hash
1071
1061
  if (prehash)
1072
- msgHash = CURVE.hash(msgHash);
1062
+ msgHash = ecdsaOpts.hash(msgHash);
1073
1063
  const { r, s } = _sig;
1074
1064
  const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1075
- const is = invN(s); // s^-1
1076
- const u1 = modN(h * is); // u1 = hs^-1 mod n
1077
- const u2 = modN(r * is); // u2 = rs^-1 mod n
1078
- const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine(); // R = u1⋅G + u2⋅P
1079
- if (!R)
1065
+ const is = Fn.inv(s); // s^-1
1066
+ const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1067
+ const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1068
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1069
+ if (R.is0())
1080
1070
  return false;
1081
- const v = modN(R.x);
1071
+ const v = Fn.create(R.x); // v = r.x mod n
1082
1072
  return v === r;
1083
1073
  }
1084
- return {
1085
- CURVE,
1074
+ // TODO: clarify API for cloning .clone({hash: sha512}) ? .createWith({hash: sha512})?
1075
+ // const clone = (hash: CHash): ECDSA => ecdsa(Point, { ...ecdsaOpts, ...getHash(hash) }, curveOpts);
1076
+ return Object.freeze({
1086
1077
  getPublicKey,
1087
1078
  getSharedSecret,
1088
1079
  sign,
1089
1080
  verify,
1090
- ProjectivePoint: Point,
1091
- Signature,
1092
1081
  utils,
1082
+ Point,
1083
+ Signature,
1084
+ });
1085
+ }
1086
+ function _weierstrass_legacy_opts_to_new(c) {
1087
+ const CURVE = {
1088
+ a: c.a,
1089
+ b: c.b,
1090
+ p: c.Fp.ORDER,
1091
+ n: c.n,
1092
+ h: c.h,
1093
+ Gx: c.Gx,
1094
+ Gy: c.Gy,
1095
+ };
1096
+ const Fp = c.Fp;
1097
+ const Fn = Field(CURVE.n, c.nBitLength);
1098
+ const curveOpts = {
1099
+ Fp,
1100
+ Fn,
1101
+ allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
1102
+ allowInfinityPoint: c.allowInfinityPoint,
1103
+ endo: c.endo,
1104
+ wrapPrivateKey: c.wrapPrivateKey,
1105
+ isTorsionFree: c.isTorsionFree,
1106
+ clearCofactor: c.clearCofactor,
1107
+ fromBytes: c.fromBytes,
1108
+ toBytes: c.toBytes,
1109
+ };
1110
+ return { CURVE, curveOpts };
1111
+ }
1112
+ function _ecdsa_legacy_opts_to_new(c) {
1113
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1114
+ const ecdsaOpts = {
1115
+ hash: c.hash,
1116
+ hmac: c.hmac,
1117
+ randomBytes: c.randomBytes,
1118
+ lowS: c.lowS,
1119
+ bits2int: c.bits2int,
1120
+ bits2int_modN: c.bits2int_modN,
1093
1121
  };
1122
+ return { CURVE, curveOpts, ecdsaOpts };
1123
+ }
1124
+ function _weierstrass_new_output_to_legacy(c, Point) {
1125
+ const { Fp, Fn } = Point;
1126
+ // TODO: remove
1127
+ function isWithinCurveOrder(num) {
1128
+ return inRange(num, _1n, Fn.ORDER);
1129
+ }
1130
+ const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
1131
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(Fn, c.allowedPrivateKeyLengths, c.wrapPrivateKey);
1132
+ return Object.assign({}, {
1133
+ CURVE: c,
1134
+ Point: Point,
1135
+ ProjectivePoint: Point,
1136
+ normPrivateKeyToScalar,
1137
+ weierstrassEquation,
1138
+ isWithinCurveOrder,
1139
+ });
1140
+ }
1141
+ function _ecdsa_new_output_to_legacy(c, ecdsa) {
1142
+ return Object.assign({}, ecdsa, {
1143
+ ProjectivePoint: ecdsa.Point,
1144
+ CURVE: c,
1145
+ });
1146
+ }
1147
+ // _ecdsa_legacy
1148
+ export function weierstrass(c) {
1149
+ const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
1150
+ const Point = weierstrassN(CURVE, curveOpts);
1151
+ const signs = ecdsa(Point, ecdsaOpts, curveOpts);
1152
+ return _ecdsa_new_output_to_legacy(c, signs);
1094
1153
  }
1095
1154
  /**
1096
1155
  * Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
@@ -1176,31 +1235,32 @@ export function SWUFpSqrtRatio(Fp, Z) {
1176
1235
  */
1177
1236
  export function mapToCurveSimpleSWU(Fp, opts) {
1178
1237
  validateField(Fp);
1179
- if (!Fp.isValid(opts.A) || !Fp.isValid(opts.B) || !Fp.isValid(opts.Z))
1238
+ const { A, B, Z } = opts;
1239
+ if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
1180
1240
  throw new Error('mapToCurveSimpleSWU: invalid opts');
1181
- const sqrtRatio = SWUFpSqrtRatio(Fp, opts.Z);
1241
+ const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
1182
1242
  if (!Fp.isOdd)
1183
- throw new Error('Fp.isOdd is not implemented!');
1243
+ throw new Error('Field does not have .isOdd()');
1184
1244
  // Input: u, an element of F.
1185
1245
  // Output: (x, y), a point on E.
1186
1246
  return (u) => {
1187
1247
  // prettier-ignore
1188
1248
  let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
1189
1249
  tv1 = Fp.sqr(u); // 1. tv1 = u^2
1190
- tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
1250
+ tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
1191
1251
  tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
1192
1252
  tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
1193
1253
  tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
1194
- tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
1195
- tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1196
- tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
1254
+ tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
1255
+ tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1256
+ tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
1197
1257
  tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
1198
1258
  tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
1199
- tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
1259
+ tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
1200
1260
  tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
1201
1261
  tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
1202
1262
  tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
1203
- tv5 = Fp.mul(tv6, opts.B); // 15. tv5 = B * tv6
1263
+ tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
1204
1264
  tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
1205
1265
  x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
1206
1266
  const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)