@noble/curves 1.4.2 → 1.5.0

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 (116) hide show
  1. package/README.md +135 -123
  2. package/_shortw_utils.d.ts.map +1 -1
  3. package/abstract/bls.d.ts +37 -34
  4. package/abstract/bls.d.ts.map +1 -1
  5. package/abstract/bls.js +167 -115
  6. package/abstract/bls.js.map +1 -1
  7. package/abstract/curve.d.ts +2 -1
  8. package/abstract/curve.d.ts.map +1 -1
  9. package/abstract/curve.js +22 -7
  10. package/abstract/curve.js.map +1 -1
  11. package/abstract/edwards.d.ts +11 -0
  12. package/abstract/edwards.d.ts.map +1 -1
  13. package/abstract/edwards.js +79 -75
  14. package/abstract/edwards.js.map +1 -1
  15. package/abstract/modular.d.ts +4 -0
  16. package/abstract/modular.d.ts.map +1 -1
  17. package/abstract/modular.js +13 -2
  18. package/abstract/modular.js.map +1 -1
  19. package/abstract/montgomery.d.ts.map +1 -1
  20. package/abstract/montgomery.js +4 -9
  21. package/abstract/montgomery.js.map +1 -1
  22. package/abstract/tower.d.ts +106 -0
  23. package/abstract/tower.d.ts.map +1 -0
  24. package/abstract/tower.js +497 -0
  25. package/abstract/tower.js.map +1 -0
  26. package/abstract/utils.d.ts +17 -0
  27. package/abstract/utils.d.ts.map +1 -1
  28. package/abstract/utils.js +50 -1
  29. package/abstract/utils.js.map +1 -1
  30. package/abstract/weierstrass.d.ts +7 -0
  31. package/abstract/weierstrass.d.ts.map +1 -1
  32. package/abstract/weierstrass.js +88 -72
  33. package/abstract/weierstrass.js.map +1 -1
  34. package/bls12-381.d.ts +1 -65
  35. package/bls12-381.d.ts.map +1 -1
  36. package/bls12-381.js +48 -575
  37. package/bls12-381.js.map +1 -1
  38. package/bn254.d.ts +10 -6
  39. package/bn254.d.ts.map +1 -1
  40. package/bn254.js +207 -10
  41. package/bn254.js.map +1 -1
  42. package/ed25519.d.ts +7 -4
  43. package/ed25519.d.ts.map +1 -1
  44. package/ed25519.js +3 -0
  45. package/ed25519.js.map +1 -1
  46. package/esm/_shortw_utils.d.ts.map +1 -1
  47. package/esm/abstract/bls.d.ts +37 -34
  48. package/esm/abstract/bls.d.ts.map +1 -1
  49. package/esm/abstract/bls.js +168 -116
  50. package/esm/abstract/bls.js.map +1 -1
  51. package/esm/abstract/curve.d.ts +2 -1
  52. package/esm/abstract/curve.d.ts.map +1 -1
  53. package/esm/abstract/curve.js +22 -7
  54. package/esm/abstract/curve.js.map +1 -1
  55. package/esm/abstract/edwards.d.ts +11 -0
  56. package/esm/abstract/edwards.d.ts.map +1 -1
  57. package/esm/abstract/edwards.js +80 -76
  58. package/esm/abstract/edwards.js.map +1 -1
  59. package/esm/abstract/modular.d.ts +4 -0
  60. package/esm/abstract/modular.d.ts.map +1 -1
  61. package/esm/abstract/modular.js +12 -2
  62. package/esm/abstract/modular.js.map +1 -1
  63. package/esm/abstract/montgomery.d.ts.map +1 -1
  64. package/esm/abstract/montgomery.js +5 -10
  65. package/esm/abstract/montgomery.js.map +1 -1
  66. package/esm/abstract/tower.d.ts +106 -0
  67. package/esm/abstract/tower.d.ts.map +1 -0
  68. package/esm/abstract/tower.js +493 -0
  69. package/esm/abstract/tower.js.map +1 -0
  70. package/esm/abstract/utils.d.ts +17 -0
  71. package/esm/abstract/utils.d.ts.map +1 -1
  72. package/esm/abstract/utils.js +44 -0
  73. package/esm/abstract/utils.js.map +1 -1
  74. package/esm/abstract/weierstrass.d.ts +7 -0
  75. package/esm/abstract/weierstrass.d.ts.map +1 -1
  76. package/esm/abstract/weierstrass.js +89 -73
  77. package/esm/abstract/weierstrass.js.map +1 -1
  78. package/esm/bls12-381.d.ts +1 -65
  79. package/esm/bls12-381.d.ts.map +1 -1
  80. package/esm/bls12-381.js +50 -577
  81. package/esm/bls12-381.js.map +1 -1
  82. package/esm/bn254.d.ts +10 -6
  83. package/esm/bn254.d.ts.map +1 -1
  84. package/esm/bn254.js +206 -9
  85. package/esm/bn254.js.map +1 -1
  86. package/esm/ed25519.d.ts +7 -4
  87. package/esm/ed25519.d.ts.map +1 -1
  88. package/esm/ed25519.js +3 -0
  89. package/esm/ed25519.js.map +1 -1
  90. package/esm/p256.d.ts.map +1 -1
  91. package/esm/p384.d.ts.map +1 -1
  92. package/esm/p521.d.ts.map +1 -1
  93. package/esm/secp256k1.d.ts +6 -0
  94. package/esm/secp256k1.d.ts.map +1 -1
  95. package/esm/secp256k1.js +17 -13
  96. package/esm/secp256k1.js.map +1 -1
  97. package/p256.d.ts.map +1 -1
  98. package/p384.d.ts.map +1 -1
  99. package/p521.d.ts.map +1 -1
  100. package/package.json +2 -1
  101. package/secp256k1.d.ts +6 -0
  102. package/secp256k1.d.ts.map +1 -1
  103. package/secp256k1.js +16 -12
  104. package/secp256k1.js.map +1 -1
  105. package/src/abstract/bls.ts +222 -168
  106. package/src/abstract/curve.ts +23 -7
  107. package/src/abstract/edwards.ts +81 -68
  108. package/src/abstract/modular.ts +13 -3
  109. package/src/abstract/montgomery.ts +11 -10
  110. package/src/abstract/tower.ts +604 -0
  111. package/src/abstract/utils.ts +49 -0
  112. package/src/abstract/weierstrass.ts +85 -68
  113. package/src/bls12-381.ts +53 -707
  114. package/src/bn254.ts +224 -9
  115. package/src/ed25519.ts +5 -2
  116. package/src/secp256k1.ts +24 -12
@@ -3,7 +3,7 @@
3
3
  import { AffinePoint, BasicCurve, Group, GroupConstructor, validateBasic, wNAF } from './curve.js';
4
4
  import * as mod from './modular.js';
5
5
  import * as ut from './utils.js';
6
- import { CHash, Hex, PrivKey, ensureBytes } from './utils.js';
6
+ import { CHash, Hex, PrivKey, ensureBytes, memoized, abool } from './utils.js';
7
7
 
8
8
  export type { AffinePoint };
9
9
  type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
@@ -31,6 +31,11 @@ type Entropy = Hex | boolean;
31
31
  export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
32
32
  export type VerOpts = { lowS?: boolean; prehash?: boolean };
33
33
 
34
+ function validateSigVerOpts(opts: SignOpts | VerOpts) {
35
+ if (opts.lowS !== undefined) abool('lowS', opts.lowS);
36
+ if (opts.prehash !== undefined) abool('prehash', opts.prehash);
37
+ }
38
+
34
39
  /**
35
40
  * ### Design rationale for types
36
41
  *
@@ -228,15 +233,12 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
228
233
 
229
234
  // Valid group elements reside in range 1..n-1
230
235
  function isWithinCurveOrder(num: bigint): boolean {
231
- return typeof num === 'bigint' && _0n < num && num < CURVE.n;
232
- }
233
- function assertGE(num: bigint) {
234
- if (!isWithinCurveOrder(num)) throw new Error('Expected valid bigint: 0 < bigint < curve.n');
236
+ return ut.inRange(num, _1n, CURVE.n);
235
237
  }
236
238
  // Validates if priv key is valid and converts it to bigint.
237
239
  // Supports options allowedPrivateKeyLengths and wrapPrivateKey.
238
240
  function normPrivateKeyToScalar(key: PrivKey): bigint {
239
- const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE;
241
+ const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
240
242
  if (lengths && typeof key !== 'bigint') {
241
243
  if (ut.isBytes(key)) key = ut.bytesToHex(key);
242
244
  // Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
@@ -252,15 +254,56 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
252
254
  } catch (error) {
253
255
  throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`);
254
256
  }
255
- if (wrapPrivateKey) num = mod.mod(num, n); // disabled by default, enabled for BLS
256
- assertGE(num); // num in range [1..N-1]
257
+ if (wrapPrivateKey) num = mod.mod(num, N); // disabled by default, enabled for BLS
258
+ ut.aInRange('private key', num, _1n, N); // num in range [1..N-1]
257
259
  return num;
258
260
  }
259
261
 
260
- const pointPrecomputes = new Map<Point, Point[]>();
261
262
  function assertPrjPoint(other: unknown) {
262
263
  if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
263
264
  }
265
+
266
+ // Memoized toAffine / validity check. They are heavy. Points are immutable.
267
+
268
+ // Converts Projective point to affine (x, y) coordinates.
269
+ // Can accept precomputed Z^-1 - for example, from invertBatch.
270
+ // (x, y, z) ∋ (x=x/z, y=y/z)
271
+ const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
272
+ const { px: x, py: y, pz: z } = p;
273
+ // Fast-path for normalized points
274
+ if (Fp.eql(z, Fp.ONE)) return { x, y };
275
+ const is0 = p.is0();
276
+ // If invZ was 0, we return zero point. However we still want to execute
277
+ // all operations, so we replace invZ with a random number, 1.
278
+ if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
279
+ const ax = Fp.mul(x, iz);
280
+ const ay = Fp.mul(y, iz);
281
+ const zz = Fp.mul(z, iz);
282
+ if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
283
+ if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
284
+ return { x: ax, y: ay };
285
+ });
286
+ // NOTE: on exception this will crash 'cached' and no value will be set.
287
+ // Otherwise true will be return
288
+ const assertValidMemo = memoized((p: Point) => {
289
+ if (p.is0()) {
290
+ // (0, 1, 0) aka ZERO is invalid in most contexts.
291
+ // In BLS, ZERO can be serialized, so we allow it.
292
+ // (0, 0, 0) is wrong representation of ZERO and is always invalid.
293
+ if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return;
294
+ throw new Error('bad point: ZERO');
295
+ }
296
+ // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
297
+ const { x, y } = p.toAffine();
298
+ // Check if x, y are valid field elements
299
+ if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
300
+ const left = Fp.sqr(y); // y²
301
+ const right = weierstrassEquation(x); // x³ + ax + b
302
+ if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
303
+ if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
304
+ return true;
305
+ });
306
+
264
307
  /**
265
308
  * Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
266
309
  * Default Point works in 2d / affine coordinates: (x, y)
@@ -278,6 +321,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
278
321
  if (px == null || !Fp.isValid(px)) throw new Error('x required');
279
322
  if (py == null || !Fp.isValid(py)) throw new Error('y required');
280
323
  if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
324
+ Object.freeze(this);
281
325
  }
282
326
 
283
327
  // Does not validate if the point is on-curve.
@@ -325,35 +369,16 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
325
369
  return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
326
370
  }
327
371
 
328
- // We calculate precomputes for elliptic curve point multiplication
329
- // using windowed method. This specifies window size and
330
- // stores precomputed values. Usually only base point would be precomputed.
331
- _WINDOW_SIZE?: number;
332
-
333
372
  // "Private method", don't use it directly
334
373
  _setWindowSize(windowSize: number) {
335
- this._WINDOW_SIZE = windowSize;
336
- pointPrecomputes.delete(this);
374
+ wnaf.setWindowSize(this, windowSize);
337
375
  }
338
376
 
339
377
  // A point on curve is valid if it conforms to equation.
340
378
  assertValidity(): void {
341
- if (this.is0()) {
342
- // (0, 1, 0) aka ZERO is invalid in most contexts.
343
- // In BLS, ZERO can be serialized, so we allow it.
344
- // (0, 0, 0) is wrong representation of ZERO and is always invalid.
345
- if (CURVE.allowInfinityPoint && !Fp.is0(this.py)) return;
346
- throw new Error('bad point: ZERO');
347
- }
348
- // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
349
- const { x, y } = this.toAffine();
350
- // Check if x, y are valid field elements
351
- if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
352
- const left = Fp.sqr(y); // y²
353
- const right = weierstrassEquation(x); // x³ + ax + b
354
- if (!Fp.eql(left, right)) throw new Error('bad point: equation left != right');
355
- if (!this.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
379
+ assertValidMemo(this);
356
380
  }
381
+
357
382
  hasEvenY(): boolean {
358
383
  const { y } = this.toAffine();
359
384
  if (Fp.isOdd) return !Fp.isOdd(y);
@@ -480,14 +505,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
480
505
  return this.add(other.negate());
481
506
  }
482
507
 
483
- private is0() {
508
+ is0() {
484
509
  return this.equals(Point.ZERO);
485
510
  }
486
511
  private wNAF(n: bigint): { p: Point; f: Point } {
487
- return wnaf.wNAFCached(this, pointPrecomputes, n, (comp: Point[]) => {
488
- const toInv = Fp.invertBatch(comp.map((p) => p.pz));
489
- return comp.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
490
- });
512
+ return wnaf.wNAFCached(this, n, Point.normalizeZ);
491
513
  }
492
514
 
493
515
  /**
@@ -495,16 +517,16 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
495
517
  * It's faster, but should only be used when you don't care about
496
518
  * an exposed private key e.g. sig verification, which works over *public* keys.
497
519
  */
498
- multiplyUnsafe(n: bigint): Point {
520
+ multiplyUnsafe(sc: bigint): Point {
521
+ ut.aInRange('scalar', sc, _0n, CURVE.n);
499
522
  const I = Point.ZERO;
500
- if (n === _0n) return I;
501
- assertGE(n); // Will throw on 0
502
- if (n === _1n) return this;
523
+ if (sc === _0n) return I;
524
+ if (sc === _1n) return this;
503
525
  const { endo } = CURVE;
504
- if (!endo) return wnaf.unsafeLadder(this, n);
526
+ if (!endo) return wnaf.unsafeLadder(this, sc);
505
527
 
506
528
  // Apply endomorphism
507
- let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
529
+ let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
508
530
  let k1p = I;
509
531
  let k2p = I;
510
532
  let d: Point = this;
@@ -531,12 +553,11 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
531
553
  * @returns New point
532
554
  */
533
555
  multiply(scalar: bigint): Point {
534
- assertGE(scalar);
535
- let n = scalar;
556
+ const { endo, n: N } = CURVE;
557
+ ut.aInRange('scalar', scalar, _1n, N);
536
558
  let point: Point, fake: Point; // Fake point is used to const-time mult
537
- const { endo } = CURVE;
538
559
  if (endo) {
539
- const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n);
560
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
540
561
  let { p: k1p, f: f1p } = this.wNAF(k1);
541
562
  let { p: k2p, f: f2p } = this.wNAF(k2);
542
563
  k1p = wnaf.constTimeNegate(k1neg, k1p);
@@ -545,7 +566,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
545
566
  point = k1p.add(k2p);
546
567
  fake = f1p.add(f2p);
547
568
  } else {
548
- const { p, f } = this.wNAF(n);
569
+ const { p, f } = this.wNAF(scalar);
549
570
  point = p;
550
571
  fake = f;
551
572
  }
@@ -573,17 +594,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
573
594
  // Can accept precomputed Z^-1 - for example, from invertBatch.
574
595
  // (x, y, z) ∋ (x=x/z, y=y/z)
575
596
  toAffine(iz?: T): AffinePoint<T> {
576
- const { px: x, py: y, pz: z } = this;
577
- const is0 = this.is0();
578
- // If invZ was 0, we return zero point. However we still want to execute
579
- // all operations, so we replace invZ with a random number, 1.
580
- if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
581
- const ax = Fp.mul(x, iz);
582
- const ay = Fp.mul(y, iz);
583
- const zz = Fp.mul(z, iz);
584
- if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
585
- if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
586
- return { x: ax, y: ay };
597
+ return toAffineMemo(this, iz);
587
598
  }
588
599
  isTorsionFree(): boolean {
589
600
  const { h: cofactor, isTorsionFree } = CURVE;
@@ -599,11 +610,13 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
599
610
  }
600
611
 
601
612
  toRawBytes(isCompressed = true): Uint8Array {
613
+ abool('isCompressed', isCompressed);
602
614
  this.assertValidity();
603
615
  return toBytes(Point, this, isCompressed);
604
616
  }
605
617
 
606
618
  toHex(isCompressed = true): string {
619
+ abool('isCompressed', isCompressed);
607
620
  return ut.bytesToHex(this.toRawBytes(isCompressed));
608
621
  }
609
622
  }
@@ -691,15 +704,19 @@ export type CurveFn = {
691
704
  };
692
705
  };
693
706
 
707
+ /**
708
+ * Creates short weierstrass curve and ECDSA signature methods for it.
709
+ * @example
710
+ * import { Field } from '@noble/curves/abstract/modular';
711
+ * // Before that, define BigInt-s: a, b, p, n, Gx, Gy
712
+ * const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
713
+ */
694
714
  export function weierstrass(curveDef: CurveType): CurveFn {
695
715
  const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
696
716
  const { Fp, n: CURVE_ORDER } = CURVE;
697
717
  const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
698
718
  const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
699
719
 
700
- function isValidFieldElement(num: bigint): boolean {
701
- return _0n < num && num < Fp.ORDER; // 0 is banned since it's not invertible FE
702
- }
703
720
  function modN(a: bigint) {
704
721
  return mod.mod(a, CURVE_ORDER);
705
722
  }
@@ -718,6 +735,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
718
735
  const a = point.toAffine();
719
736
  const x = Fp.toBytes(a.x);
720
737
  const cat = ut.concatBytes;
738
+ abool('isCompressed', isCompressed);
721
739
  if (isCompressed) {
722
740
  return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
723
741
  } else {
@@ -731,7 +749,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
731
749
  // this.assertValidity() is done inside of fromHex
732
750
  if (len === compressedLen && (head === 0x02 || head === 0x03)) {
733
751
  const x = ut.bytesToNumberBE(tail);
734
- if (!isValidFieldElement(x)) throw new Error('Point is not on curve');
752
+ if (!ut.inRange(x, _1n, Fp.ORDER)) throw new Error('Point is not on curve');
735
753
  const y2 = weierstrassEquation(x); // y² = x³ + ax + b
736
754
  let y: bigint;
737
755
  try {
@@ -797,9 +815,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
797
815
  }
798
816
 
799
817
  assertValidity(): void {
800
- // can use assertGE here
801
- if (!isWithinCurveOrder(this.r)) throw new Error('r must be 0 < r < CURVE.n');
802
- if (!isWithinCurveOrder(this.s)) throw new Error('s must be 0 < s < CURVE.n');
818
+ ut.aInRange('r', this.r, _1n, CURVE_ORDER); // r in [1..N]
819
+ ut.aInRange('s', this.s, _1n, CURVE_ORDER); // s in [1..N]
803
820
  }
804
821
 
805
822
  addRecoveryBit(recovery: number): RecoveredSignature {
@@ -949,9 +966,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
949
966
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
950
967
  */
951
968
  function int2octets(num: bigint): Uint8Array {
952
- if (typeof num !== 'bigint') throw new Error('bigint expected');
953
- if (!(_0n <= num && num < ORDER_MASK))
954
- throw new Error(`bigint expected < 2^${CURVE.nBitLength}`);
969
+ ut.aInRange(`num < 2^${CURVE.nBitLength}`, num, _0n, ORDER_MASK);
955
970
  // works with order, can have different size than numToField!
956
971
  return ut.numberToBytesBE(num, CURVE.nByteLength);
957
972
  }
@@ -968,6 +983,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
968
983
  let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
969
984
  if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
970
985
  msgHash = ensureBytes('msgHash', msgHash);
986
+ validateSigVerOpts(opts);
971
987
  if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
972
988
 
973
989
  // We can't later call bits2octets, since nested bits2int is broken for curves
@@ -1058,6 +1074,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1058
1074
  msgHash = ensureBytes('msgHash', msgHash);
1059
1075
  publicKey = ensureBytes('publicKey', publicKey);
1060
1076
  if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
1077
+ validateSigVerOpts(opts);
1061
1078
  const { lowS, prehash } = opts;
1062
1079
 
1063
1080
  let _sig: Signature | undefined = undefined;