@noble/curves 1.9.0 → 1.9.2

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