@noble/curves 1.9.4 → 1.9.6

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 (124) hide show
  1. package/abstract/bls.d.ts +2 -2
  2. package/abstract/bls.d.ts.map +1 -1
  3. package/abstract/curve.d.ts +47 -46
  4. package/abstract/curve.d.ts.map +1 -1
  5. package/abstract/curve.js +9 -6
  6. package/abstract/curve.js.map +1 -1
  7. package/abstract/edwards.d.ts +41 -33
  8. package/abstract/edwards.d.ts.map +1 -1
  9. package/abstract/edwards.js +166 -170
  10. package/abstract/edwards.js.map +1 -1
  11. package/abstract/modular.d.ts +1 -1
  12. package/abstract/modular.d.ts.map +1 -1
  13. package/abstract/modular.js +4 -4
  14. package/abstract/modular.js.map +1 -1
  15. package/abstract/montgomery.d.ts +2 -6
  16. package/abstract/montgomery.d.ts.map +1 -1
  17. package/abstract/montgomery.js +13 -10
  18. package/abstract/montgomery.js.map +1 -1
  19. package/abstract/tower.d.ts +9 -7
  20. package/abstract/tower.d.ts.map +1 -1
  21. package/abstract/tower.js +569 -357
  22. package/abstract/tower.js.map +1 -1
  23. package/abstract/weierstrass.d.ts +162 -92
  24. package/abstract/weierstrass.d.ts.map +1 -1
  25. package/abstract/weierstrass.js +394 -336
  26. package/abstract/weierstrass.js.map +1 -1
  27. package/bls12-381.d.ts.map +1 -1
  28. package/bls12-381.js +9 -42
  29. package/bls12-381.js.map +1 -1
  30. package/bn254.d.ts.map +1 -1
  31. package/bn254.js +2 -34
  32. package/bn254.js.map +1 -1
  33. package/ed25519.d.ts +15 -15
  34. package/ed25519.d.ts.map +1 -1
  35. package/ed25519.js +51 -48
  36. package/ed25519.js.map +1 -1
  37. package/ed448.d.ts +16 -17
  38. package/ed448.d.ts.map +1 -1
  39. package/ed448.js +67 -48
  40. package/ed448.js.map +1 -1
  41. package/esm/abstract/bls.d.ts +2 -2
  42. package/esm/abstract/bls.d.ts.map +1 -1
  43. package/esm/abstract/curve.d.ts +47 -46
  44. package/esm/abstract/curve.d.ts.map +1 -1
  45. package/esm/abstract/curve.js +9 -6
  46. package/esm/abstract/curve.js.map +1 -1
  47. package/esm/abstract/edwards.d.ts +41 -33
  48. package/esm/abstract/edwards.d.ts.map +1 -1
  49. package/esm/abstract/edwards.js +167 -171
  50. package/esm/abstract/edwards.js.map +1 -1
  51. package/esm/abstract/modular.d.ts +1 -1
  52. package/esm/abstract/modular.d.ts.map +1 -1
  53. package/esm/abstract/modular.js +4 -4
  54. package/esm/abstract/modular.js.map +1 -1
  55. package/esm/abstract/montgomery.d.ts +2 -6
  56. package/esm/abstract/montgomery.d.ts.map +1 -1
  57. package/esm/abstract/montgomery.js +14 -11
  58. package/esm/abstract/montgomery.js.map +1 -1
  59. package/esm/abstract/tower.d.ts +9 -7
  60. package/esm/abstract/tower.d.ts.map +1 -1
  61. package/esm/abstract/tower.js +570 -358
  62. package/esm/abstract/tower.js.map +1 -1
  63. package/esm/abstract/weierstrass.d.ts +162 -92
  64. package/esm/abstract/weierstrass.d.ts.map +1 -1
  65. package/esm/abstract/weierstrass.js +395 -338
  66. package/esm/abstract/weierstrass.js.map +1 -1
  67. package/esm/bls12-381.d.ts.map +1 -1
  68. package/esm/bls12-381.js +10 -43
  69. package/esm/bls12-381.js.map +1 -1
  70. package/esm/bn254.d.ts.map +1 -1
  71. package/esm/bn254.js +3 -35
  72. package/esm/bn254.js.map +1 -1
  73. package/esm/ed25519.d.ts +15 -15
  74. package/esm/ed25519.d.ts.map +1 -1
  75. package/esm/ed25519.js +51 -48
  76. package/esm/ed25519.js.map +1 -1
  77. package/esm/ed448.d.ts +16 -17
  78. package/esm/ed448.d.ts.map +1 -1
  79. package/esm/ed448.js +68 -49
  80. package/esm/ed448.js.map +1 -1
  81. package/esm/misc.js +2 -2
  82. package/esm/misc.js.map +1 -1
  83. package/esm/nist.d.ts +6 -0
  84. package/esm/nist.d.ts.map +1 -1
  85. package/esm/nist.js +6 -0
  86. package/esm/nist.js.map +1 -1
  87. package/esm/secp256k1.d.ts +2 -6
  88. package/esm/secp256k1.d.ts.map +1 -1
  89. package/esm/secp256k1.js +34 -35
  90. package/esm/secp256k1.js.map +1 -1
  91. package/esm/utils.d.ts +14 -0
  92. package/esm/utils.d.ts.map +1 -1
  93. package/esm/utils.js +43 -0
  94. package/esm/utils.js.map +1 -1
  95. package/misc.js +2 -2
  96. package/misc.js.map +1 -1
  97. package/nist.d.ts +6 -0
  98. package/nist.d.ts.map +1 -1
  99. package/nist.js +7 -1
  100. package/nist.js.map +1 -1
  101. package/package.json +2 -2
  102. package/secp256k1.d.ts +2 -6
  103. package/secp256k1.d.ts.map +1 -1
  104. package/secp256k1.js +33 -34
  105. package/secp256k1.js.map +1 -1
  106. package/src/abstract/bls.ts +2 -2
  107. package/src/abstract/curve.ts +131 -68
  108. package/src/abstract/edwards.ts +210 -219
  109. package/src/abstract/modular.ts +4 -4
  110. package/src/abstract/montgomery.ts +16 -16
  111. package/src/abstract/tower.ts +630 -382
  112. package/src/abstract/weierstrass.ts +587 -484
  113. package/src/bls12-381.ts +10 -42
  114. package/src/bn254.ts +3 -34
  115. package/src/ed25519.ts +62 -58
  116. package/src/ed448.ts +74 -77
  117. package/src/misc.ts +2 -2
  118. package/src/nist.ts +7 -0
  119. package/src/secp256k1.ts +35 -36
  120. package/src/utils.ts +48 -0
  121. package/utils.d.ts +14 -0
  122. package/utils.d.ts.map +1 -1
  123. package/utils.js +47 -0
  124. package/utils.js.map +1 -1
@@ -25,12 +25,12 @@
25
25
  * @module
26
26
  */
27
27
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
28
- import { hmac } from '@noble/hashes/hmac.js';
28
+ import { hmac as nobleHmac } from '@noble/hashes/hmac.js';
29
29
  import { ahash } from '@noble/hashes/utils';
30
30
  import {
31
31
  _validateObject,
32
- abool,
33
- abytes,
32
+ _abool2 as abool,
33
+ _abytes2 as abytes,
34
34
  aInRange,
35
35
  bitLen,
36
36
  bitMask,
@@ -44,7 +44,7 @@ import {
44
44
  isBytes,
45
45
  memoized,
46
46
  numberToHexUnpadded,
47
- randomBytes,
47
+ randomBytes as randomBytesWeb,
48
48
  type CHash,
49
49
  type Hex,
50
50
  type PrivKey,
@@ -58,7 +58,7 @@ import {
58
58
  wNAF,
59
59
  type AffinePoint,
60
60
  type BasicCurve,
61
- type CurveInfo,
61
+ type CurveLengths,
62
62
  type CurvePoint,
63
63
  type CurvePointCons,
64
64
  } from './curve.ts';
@@ -67,6 +67,7 @@ import {
67
67
  FpInvertBatch,
68
68
  getMinHashLength,
69
69
  mapHashToField,
70
+ nLength,
70
71
  validateField,
71
72
  type IField,
72
73
  type NLength,
@@ -104,21 +105,6 @@ export type EndomorphismOpts = {
104
105
  basises?: EndoBasis;
105
106
  splitScalar?: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
106
107
  };
107
- export type BasicWCurve<T> = BasicCurve<T> & {
108
- // Params: a, b
109
- a: T;
110
- b: T;
111
-
112
- // Optional params
113
- allowedPrivateKeyLengths?: readonly number[]; // for P521
114
- wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
115
- endo?: EndomorphismOpts;
116
- // When a cofactor != 1, there can be an effective methods to:
117
- // 1. Determine whether a point is torsion-free
118
- isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
119
- // 2. Clear torsion component
120
- clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
121
- };
122
108
 
123
109
  // We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
124
110
  const divNearest = (num: bigint, den: bigint) => (num + (num >= 0 ? den : -den) / _2n) / den;
@@ -152,23 +138,41 @@ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): Scalar
152
138
  return { k1neg, k1, k2neg, k2 };
153
139
  }
154
140
 
155
- export type ECDSASigFormat = 'compact' | 'der';
156
- export type Entropy = Hex | boolean;
157
- export type SignOpts = Partial<{
158
- lowS: boolean;
159
- extraEntropy: Entropy;
160
- prehash: boolean;
161
- format: ECDSASigFormat | 'js';
162
- }>;
163
- export type VerOpts = Partial<{
164
- lowS: boolean;
165
- prehash: boolean;
166
- format: ECDSASigFormat | 'js' | undefined;
167
- }>;
141
+ export type ECDSASigFormat = 'compact' | 'recovered' | 'der';
142
+ export type ECDSARecoverOpts = {
143
+ prehash?: boolean;
144
+ };
145
+ export type ECDSAVerifyOpts = {
146
+ prehash?: boolean;
147
+ lowS?: boolean;
148
+ format?: ECDSASigFormat;
149
+ };
150
+ export type ECDSASignOpts = {
151
+ prehash?: boolean;
152
+ lowS?: boolean;
153
+ format?: ECDSASigFormat;
154
+ extraEntropy?: Uint8Array | boolean;
155
+ };
156
+
157
+ function validateSigFormat(format: string): ECDSASigFormat {
158
+ if (!['compact', 'recovered', 'der'].includes(format))
159
+ throw new Error('Signature format must be "compact", "recovered", or "der"');
160
+ return format as ECDSASigFormat;
161
+ }
168
162
 
169
- function validateSigVerOpts(opts: SignOpts | VerOpts) {
170
- if (opts.lowS !== undefined) abool('lowS', opts.lowS);
171
- if (opts.prehash !== undefined) abool('prehash', opts.prehash);
163
+ function validateSigOpts<T extends ECDSASignOpts, D extends Required<ECDSASignOpts>>(
164
+ opts: T,
165
+ def: D
166
+ ): Required<ECDSASignOpts> {
167
+ const optsn: ECDSASignOpts = {};
168
+ for (let optName of Object.keys(def)) {
169
+ // @ts-ignore
170
+ optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
171
+ }
172
+ abool(optsn.lowS!, 'lowS');
173
+ abool(optsn.prehash!, 'prehash');
174
+ if (optsn.format !== undefined) validateSigFormat(optsn.format);
175
+ return optsn as Required<ECDSASignOpts>;
172
176
  }
173
177
 
174
178
  /** Instance methods for 3D XYZ projective points. */
@@ -187,11 +191,11 @@ export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>>
187
191
  toBytes(isCompressed?: boolean): Uint8Array;
188
192
  toHex(isCompressed?: boolean): string;
189
193
 
190
- /** @deprecated use .X */
194
+ /** @deprecated use `.X` */
191
195
  readonly px: T;
192
- /** @deprecated use .Y */
196
+ /** @deprecated use `.Y` */
193
197
  readonly py: T;
194
- /** @deprecated use .Z */
198
+ /** @deprecated use `.Z` */
195
199
  readonly pz: T;
196
200
  /** @deprecated use `toBytes` */
197
201
  toRawBytes(isCompressed?: boolean): Uint8Array;
@@ -208,9 +212,10 @@ export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>>
208
212
  }
209
213
 
210
214
  /** Static methods for 3D XYZ projective points. */
211
- export interface WeierstrassPointCons<T> extends CurvePointCons<T, WeierstrassPoint<T>> {
215
+ export interface WeierstrassPointCons<T> extends CurvePointCons<WeierstrassPoint<T>> {
212
216
  /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
213
217
  new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
218
+ CURVE(): WeierstrassOpts<T>;
214
219
  /** @deprecated use `Point.BASE.multiply(Point.Fn.fromBytes(privateKey))` */
215
220
  fromPrivateKey(privateKey: PrivKey): WeierstrassPoint<T>;
216
221
  /** @deprecated use `import { normalizeZ } from '@noble/curves/abstract/curve.js';` */
@@ -219,48 +224,6 @@ export interface WeierstrassPointCons<T> extends CurvePointCons<T, WeierstrassPo
219
224
  msm(points: WeierstrassPoint<T>[], scalars: bigint[]): WeierstrassPoint<T>;
220
225
  }
221
226
 
222
- /** @deprecated use WeierstrassPoint */
223
- export type ProjPointType<T> = WeierstrassPoint<T>;
224
- /** @deprecated use WeierstrassPointCons */
225
- export type ProjConstructor<T> = WeierstrassPointCons<T>;
226
-
227
- // TODO: remove
228
- export type CurvePointsType<T> = BasicWCurve<T> & {
229
- fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
230
- toBytes?: (
231
- c: WeierstrassPointCons<T>,
232
- point: WeierstrassPoint<T>,
233
- isCompressed: boolean
234
- ) => Uint8Array;
235
- };
236
-
237
- // LegacyWeierstrassOpts
238
- export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
239
-
240
- // LegacyWeierstrass
241
- export type CurvePointsRes<T> = {
242
- Point: WeierstrassPointCons<T>;
243
-
244
- /** @deprecated the property will be removed in next release */
245
- CURVE: CurvePointsType<T>;
246
- /** @deprecated use `Point` */
247
- ProjectivePoint: WeierstrassPointCons<T>;
248
- /** @deprecated use `Point.Fn.fromBytes(privateKey)` */
249
- normPrivateKeyToScalar: (key: PrivKey) => bigint;
250
- /** @deprecated */
251
- weierstrassEquation: (x: T) => T;
252
- /** @deprecated use `Point.Fn.isValidNot0(num)` */
253
- isWithinCurveOrder: (num: bigint) => boolean;
254
- };
255
-
256
- // Aliases to legacy types
257
- // export type CurveType = LegacyECDSAOpts;
258
- // export type CurveFn = LegacyECDSA;
259
- // export type CurvePointsRes<T> = LegacyWeierstrass<T>;
260
- // export type CurvePointsType<T> = LegacyWeierstrassOpts<T>;
261
- // export type CurvePointsTypeWithLength<T> = LegacyWeierstrassOpts<T>;
262
- // export type BasicWCurve<T> = LegacyWeierstrassOpts<T>;
263
-
264
227
  /**
265
228
  * Weierstrass curve options.
266
229
  *
@@ -303,6 +266,11 @@ export type WeierstrassExtraOpts<T> = Partial<{
303
266
 
304
267
  /**
305
268
  * Options for ECDSA signatures over a Weierstrass curve.
269
+ *
270
+ * * lowS: (default: true) whether produced / verified signatures occupy low half of ecdsaOpts.p. Prevents malleability.
271
+ * * hmac: (default: noble-hashes hmac) function, would be used to init hmac-drbg for k generation.
272
+ * * randomBytes: (default: webcrypto os-level CSPRNG) custom method for fetching secure randomness.
273
+ * * bits2int, bits2int_modN: used in sigs, sometimes overridden by curves
306
274
  */
307
275
  export type ECDSAOpts = Partial<{
308
276
  lowS: boolean;
@@ -312,20 +280,19 @@ export type ECDSAOpts = Partial<{
312
280
  bits2int_modN: (bytes: Uint8Array) => bigint;
313
281
  }>;
314
282
 
315
- /** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
316
- export interface ECDSA {
283
+ /**
284
+ * Elliptic Curve Diffie-Hellman interface.
285
+ * Provides keygen, secret-to-public conversion, calculating shared secrets.
286
+ */
287
+ export interface ECDH {
317
288
  keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
318
289
  getPublicKey: (secretKey: PrivKey, isCompressed?: boolean) => Uint8Array;
319
- sign: (msgHash: Hex, secretKey: PrivKey, opts?: SignOpts) => ECDSASigRecovered;
320
- verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
321
290
  getSharedSecret: (secretKeyA: PrivKey, publicKeyB: Hex, isCompressed?: boolean) => Uint8Array;
322
291
  Point: WeierstrassPointCons<bigint>;
323
- Signature: ECDSASignatureCons;
324
292
  utils: {
325
293
  isValidSecretKey: (secretKey: PrivKey) => boolean;
326
294
  isValidPublicKey: (publicKey: Uint8Array, isCompressed?: boolean) => boolean;
327
295
  randomSecretKey: (seed?: Uint8Array) => Uint8Array;
328
-
329
296
  /** @deprecated use `randomSecretKey` */
330
297
  randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
331
298
  /** @deprecated use `isValidSecretKey` */
@@ -335,7 +302,23 @@ export interface ECDSA {
335
302
  /** @deprecated use `point.precompute()` */
336
303
  precompute: (windowSize?: number, point?: WeierstrassPoint<bigint>) => WeierstrassPoint<bigint>;
337
304
  };
338
- info: CurveInfo;
305
+ lengths: CurveLengths;
306
+ }
307
+
308
+ /**
309
+ * ECDSA interface.
310
+ * Only supported for prime fields, not Fp2 (extension fields).
311
+ */
312
+ export interface ECDSA extends ECDH {
313
+ sign: (message: Hex, secretKey: PrivKey, opts?: ECDSASignOpts) => ECDSASigRecovered;
314
+ verify: (
315
+ signature: Uint8Array,
316
+ message: Uint8Array,
317
+ publicKey: Uint8Array,
318
+ opts?: ECDSAVerifyOpts
319
+ ) => boolean;
320
+ recoverPublicKey(signature: Uint8Array, message: Uint8Array, opts?: ECDSARecoverOpts): Uint8Array;
321
+ Signature: ECDSASignatureCons;
339
322
  }
340
323
  export class DERErr extends Error {
341
324
  constructor(m = '') {
@@ -459,19 +442,6 @@ export const DER: IDER = {
459
442
  // prettier-ignore
460
443
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
461
444
 
462
- // TODO: remove
463
- export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
464
- /**
465
- * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
466
- * @returns y²
467
- */
468
- function weierstrassEquation(x: T): T {
469
- const x2 = Fp.sqr(x); // x * x
470
- const x3 = Fp.mul(x2, x); // x² * x
471
- return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
472
- }
473
- return weierstrassEquation;
474
- }
475
445
  export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
476
446
  const { BYTES: expected } = Fn;
477
447
  let num: bigint;
@@ -489,14 +459,33 @@ export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
489
459
  return num;
490
460
  }
491
461
 
462
+ /**
463
+ * Creates weierstrass Point constructor, based on specified curve options.
464
+ *
465
+ * @example
466
+ ```js
467
+ const opts = {
468
+ p: BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'),
469
+ n: BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'),
470
+ h: BigInt(1),
471
+ a: BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'),
472
+ b: BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'),
473
+ Gx: BigInt('0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296'),
474
+ Gy: BigInt('0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'),
475
+ };
476
+ const p256_Point = weierstrass(opts);
477
+ ```
478
+ */
492
479
  export function weierstrassN<T>(
493
- CURVE: WeierstrassOpts<T>,
494
- curveOpts: WeierstrassExtraOpts<T> = {}
480
+ params: WeierstrassOpts<T>,
481
+ extraOpts: WeierstrassExtraOpts<T> = {}
495
482
  ): WeierstrassPointCons<T> {
496
- const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
483
+ const validated = _createCurveFields('weierstrass', params, extraOpts);
484
+ const { Fp, Fn } = validated;
485
+ let CURVE = validated.CURVE as WeierstrassOpts<T>;
497
486
  const { h: cofactor, n: CURVE_ORDER } = CURVE;
498
487
  _validateObject(
499
- curveOpts,
488
+ extraOpts,
500
489
  {},
501
490
  {
502
491
  allowInfinityPoint: 'boolean',
@@ -509,7 +498,7 @@ export function weierstrassN<T>(
509
498
  }
510
499
  );
511
500
 
512
- const { endo } = curveOpts;
501
+ const { endo } = extraOpts;
513
502
  if (endo) {
514
503
  // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
515
504
  if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
@@ -517,6 +506,8 @@ export function weierstrassN<T>(
517
506
  }
518
507
  }
519
508
 
509
+ const lengths = getWLengths(Fp, Fn);
510
+
520
511
  function assertCompressionIsSupported() {
521
512
  if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
522
513
  }
@@ -529,7 +520,7 @@ export function weierstrassN<T>(
529
520
  ): Uint8Array {
530
521
  const { x, y } = point.toAffine();
531
522
  const bx = Fp.toBytes(x);
532
- abool('isCompressed', isCompressed);
523
+ abool(isCompressed, 'isCompressed');
533
524
  if (isCompressed) {
534
525
  assertCompressionIsSupported();
535
526
  const hasEvenY = !Fp.isOdd!(y);
@@ -539,15 +530,13 @@ export function weierstrassN<T>(
539
530
  }
540
531
  }
541
532
  function pointFromBytes(bytes: Uint8Array) {
542
- abytes(bytes);
543
- const L = Fp.BYTES;
544
- const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
545
- const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
533
+ abytes(bytes, undefined, 'Point');
534
+ const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
546
535
  const length = bytes.length;
547
536
  const head = bytes[0];
548
537
  const tail = bytes.subarray(1);
549
538
  // No actual validation is done here: use .assertValidity()
550
- if (length === LC && (head === 0x02 || head === 0x03)) {
539
+ if (length === comp && (head === 0x02 || head === 0x03)) {
551
540
  const x = Fp.fromBytes(tail);
552
541
  if (!Fp.isValid(x)) throw new Error('bad point: is not on curve, wrong x');
553
542
  const y2 = weierstrassEquation(x); // y² = x³ + ax + b
@@ -563,22 +552,27 @@ export function weierstrassN<T>(
563
552
  const isHeadOdd = (head & 1) === 1; // ECDSA-specific
564
553
  if (isHeadOdd !== isYOdd) y = Fp.neg(y);
565
554
  return { x, y };
566
- } else if (length === LU && head === 0x04) {
555
+ } else if (length === uncomp && head === 0x04) {
567
556
  // TODO: more checks
568
- const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
569
- const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
557
+ const L = Fp.BYTES;
558
+ const x = Fp.fromBytes(tail.subarray(0, L));
559
+ const y = Fp.fromBytes(tail.subarray(L, L * 2));
570
560
  if (!isValidXY(x, y)) throw new Error('bad point: is not on curve');
571
561
  return { x, y };
572
562
  } else {
573
563
  throw new Error(
574
- `bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`
564
+ `bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`
575
565
  );
576
566
  }
577
567
  }
578
568
 
579
- const toBytes = curveOpts.toBytes || pointToBytes;
580
- const fromBytes = curveOpts.fromBytes || pointFromBytes;
581
- const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
569
+ const encodePoint = extraOpts.toBytes || pointToBytes;
570
+ const decodePoint = extraOpts.fromBytes || pointFromBytes;
571
+ function weierstrassEquation(x: T): T {
572
+ const x2 = Fp.sqr(x); // x * x
573
+ const x3 = Fp.mul(x2, x); // x² * x
574
+ return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b); // x³ + a * x + b
575
+ }
582
576
 
583
577
  // TODO: move top-level
584
578
  /** Checks whether equation holds for given x, y: y² == x³ + ax + b */
@@ -640,7 +634,7 @@ export function weierstrassN<T>(
640
634
  // (0, 1, 0) aka ZERO is invalid in most contexts.
641
635
  // In BLS, ZERO can be serialized, so we allow it.
642
636
  // (0, 0, 0) is invalid representation of ZERO.
643
- if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
637
+ if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
644
638
  throw new Error('bad point: ZERO');
645
639
  }
646
640
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
@@ -674,8 +668,9 @@ export function weierstrassN<T>(
674
668
  static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
675
669
  // zero / infinity / identity point
676
670
  static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
677
- // fields
671
+ // math field
678
672
  static readonly Fp = Fp;
673
+ // scalar field
679
674
  static readonly Fn = Fn;
680
675
 
681
676
  readonly X: T;
@@ -690,6 +685,10 @@ export function weierstrassN<T>(
690
685
  Object.freeze(this);
691
686
  }
692
687
 
688
+ static CURVE(): WeierstrassOpts<T> {
689
+ return CURVE;
690
+ }
691
+
693
692
  /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
694
693
  static fromAffine(p: AffinePoint<T>): Point {
695
694
  const { x, y } = p || {};
@@ -700,50 +699,20 @@ export function weierstrassN<T>(
700
699
  return new Point(x, y, Fp.ONE);
701
700
  }
702
701
 
703
- get x(): T {
704
- return this.toAffine().x;
705
- }
706
- get y(): T {
707
- return this.toAffine().y;
708
- }
709
-
710
- // TODO: remove
711
- get px(): T {
712
- return this.X;
713
- }
714
- get py(): T {
715
- return this.X;
716
- }
717
- get pz(): T {
718
- return this.Z;
719
- }
720
- static normalizeZ(points: Point[]): Point[] {
721
- return normalizeZ(Point, points);
722
- }
723
-
724
702
  static fromBytes(bytes: Uint8Array): Point {
725
- abytes(bytes);
726
- return Point.fromHex(bytes);
727
- }
728
-
729
- /** Converts hash string or Uint8Array to Point. */
730
- static fromHex(hex: Hex): Point {
731
- const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
703
+ const P = Point.fromAffine(decodePoint(abytes(bytes, undefined, 'point')));
732
704
  P.assertValidity();
733
705
  return P;
734
706
  }
735
-
736
- /** Multiplies generator point by privateKey. */
737
- static fromPrivateKey(privateKey: PrivKey) {
738
- return Point.BASE.multiply(_normFnElement(Fn, privateKey));
707
+ static fromHex(hex: Hex): Point {
708
+ return Point.fromBytes(ensureBytes('pointHex', hex));
739
709
  }
740
710
 
741
- // TODO: remove
742
- static msm(points: Point[], scalars: bigint[]): Point {
743
- return pippenger(Point, Fn, points, scalars);
711
+ get x(): T {
712
+ return this.toAffine().x;
744
713
  }
745
- _setWindowSize(windowSize: number) {
746
- this.precompute(windowSize);
714
+ get y(): T {
715
+ return this.toAffine().y;
747
716
  }
748
717
 
749
718
  /**
@@ -900,7 +869,7 @@ export function weierstrassN<T>(
900
869
  * @returns New point
901
870
  */
902
871
  multiply(scalar: bigint): Point {
903
- const { endo } = curveOpts;
872
+ const { endo } = extraOpts;
904
873
  if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
905
874
  let point: Point, fake: Point; // Fake point is used to const-time mult
906
875
  const mul = (n: bigint) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
@@ -926,8 +895,8 @@ export function weierstrassN<T>(
926
895
  * an exposed secret key e.g. sig verification, which works over *public* keys.
927
896
  */
928
897
  multiplyUnsafe(sc: bigint): Point {
929
- const { endo } = curveOpts;
930
- const p = this;
898
+ const { endo } = extraOpts;
899
+ const p = this as Point;
931
900
  if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
932
901
  if (sc === _0n || p.is0()) return Point.ZERO;
933
902
  if (sc === _1n) return p; // fast-path
@@ -959,14 +928,14 @@ export function weierstrassN<T>(
959
928
  * Always torsion-free for cofactor=1 curves.
960
929
  */
961
930
  isTorsionFree(): boolean {
962
- const { isTorsionFree } = curveOpts;
931
+ const { isTorsionFree } = extraOpts;
963
932
  if (cofactor === _1n) return true;
964
933
  if (isTorsionFree) return isTorsionFree(Point, this);
965
934
  return wnaf.unsafe(this, CURVE_ORDER).is0();
966
935
  }
967
936
 
968
937
  clearCofactor(): Point {
969
- const { clearCofactor } = curveOpts;
938
+ const { clearCofactor } = extraOpts;
970
939
  if (cofactor === _1n) return this; // Fast-path
971
940
  if (clearCofactor) return clearCofactor(Point, this) as Point;
972
941
  return this.multiplyUnsafe(cofactor);
@@ -978,14 +947,9 @@ export function weierstrassN<T>(
978
947
  }
979
948
 
980
949
  toBytes(isCompressed = true): Uint8Array {
981
- abool('isCompressed', isCompressed);
950
+ abool(isCompressed, 'isCompressed');
982
951
  this.assertValidity();
983
- return toBytes(Point, this, isCompressed);
984
- }
985
-
986
- /** @deprecated use `toBytes` */
987
- toRawBytes(isCompressed = true): Uint8Array {
988
- return this.toBytes(isCompressed);
952
+ return encodePoint(Point, this, isCompressed);
989
953
  }
990
954
 
991
955
  toHex(isCompressed = true): string {
@@ -995,35 +959,55 @@ export function weierstrassN<T>(
995
959
  toString() {
996
960
  return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
997
961
  }
962
+
963
+ // TODO: remove
964
+ get px(): T {
965
+ return this.X;
966
+ }
967
+ get py(): T {
968
+ return this.X;
969
+ }
970
+ get pz(): T {
971
+ return this.Z;
972
+ }
973
+ toRawBytes(isCompressed = true): Uint8Array {
974
+ return this.toBytes(isCompressed);
975
+ }
976
+ _setWindowSize(windowSize: number) {
977
+ this.precompute(windowSize);
978
+ }
979
+ static normalizeZ(points: Point[]): Point[] {
980
+ return normalizeZ(Point, points);
981
+ }
982
+ static msm(points: Point[], scalars: bigint[]): Point {
983
+ return pippenger(Point, Fn, points, scalars);
984
+ }
985
+ static fromPrivateKey(privateKey: PrivKey) {
986
+ return Point.BASE.multiply(_normFnElement(Fn, privateKey));
987
+ }
998
988
  }
999
989
  const bits = Fn.BITS;
1000
- const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
990
+ const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
991
+ Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
1001
992
  return Point;
1002
993
  }
1003
994
 
1004
- // _legacyWeierstrass
1005
- // TODO: remove
1006
- /** @deprecated use `weierstrass` in newer releases */
1007
- export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
1008
- const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1009
- const Point = weierstrassN(CURVE, curveOpts);
1010
- return _weierstrass_new_output_to_legacy(c, Point);
1011
- }
1012
-
1013
- // Instance
995
+ /** Methods of ECDSA signature instance. */
1014
996
  export interface ECDSASignature {
1015
997
  readonly r: bigint;
1016
998
  readonly s: bigint;
1017
999
  readonly recovery?: number;
1018
1000
  addRecoveryBit(recovery: number): ECDSASigRecovered;
1019
1001
  hasHighS(): boolean;
1020
- normalizeS(): ECDSASignature;
1021
- recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
1022
1002
  toBytes(format?: string): Uint8Array;
1023
1003
  toHex(format?: string): string;
1024
1004
 
1025
1005
  /** @deprecated */
1026
1006
  assertValidity(): void;
1007
+ /** @deprecated */
1008
+ normalizeS(): ECDSASignature;
1009
+ /** @deprecated use standalone method `curve.recoverPublicKey(sig.toBytes('recovered'), msg)` */
1010
+ recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
1027
1011
  /** @deprecated use `.toBytes('compact')` */
1028
1012
  toCompactRawBytes(): Uint8Array;
1029
1013
  /** @deprecated use `.toBytes('compact')` */
@@ -1033,12 +1017,10 @@ export interface ECDSASignature {
1033
1017
  /** @deprecated use `.toBytes('der')` */
1034
1018
  toDERHex(): string;
1035
1019
  }
1036
- export type SignatureType = ECDSASignature;
1037
1020
  export type ECDSASigRecovered = ECDSASignature & {
1038
1021
  readonly recovery: number;
1039
1022
  };
1040
- export type RecoveredSignatureType = ECDSASigRecovered;
1041
- // Static methods
1023
+ /** Methods of ECDSA signature constructor. */
1042
1024
  export type ECDSASignatureCons = {
1043
1025
  new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
1044
1026
  fromBytes(bytes: Uint8Array, format?: ECDSASigFormat): ECDSASignature;
@@ -1049,42 +1031,12 @@ export type ECDSASignatureCons = {
1049
1031
  /** @deprecated use `.fromBytes(bytes, 'der')` */
1050
1032
  fromDER(hex: Hex): ECDSASignature;
1051
1033
  };
1052
- export type SignatureLike = { r: bigint; s: bigint };
1053
- // TODO: remove
1054
- export type PubKey = Hex | WeierstrassPoint<bigint>;
1055
-
1056
- // TODO: remove
1057
- export type CurveType = BasicWCurve<bigint> & {
1058
- hash: CHash; // CHash not FHash because we need outputLen for DRBG
1059
- hmac?: HmacFnSync;
1060
- randomBytes?: (bytesLength?: number) => Uint8Array;
1061
- lowS?: boolean;
1062
- bits2int?: (bytes: Uint8Array) => bigint;
1063
- bits2int_modN?: (bytes: Uint8Array) => bigint;
1064
- };
1065
1034
 
1066
1035
  // Points start with byte 0x02 when y is even; otherwise 0x03
1067
1036
  function pprefix(hasEvenY: boolean): Uint8Array {
1068
1037
  return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
1069
1038
  }
1070
1039
 
1071
- // TODO: remove
1072
- export type CurveFn = {
1073
- /** @deprecated the property will be removed in next release */
1074
- CURVE: CurvePointsType<bigint>;
1075
- keygen: ECDSA['keygen'];
1076
- getPublicKey: ECDSA['getPublicKey'];
1077
- getSharedSecret: ECDSA['getSharedSecret'];
1078
- sign: ECDSA['sign'];
1079
- verify: ECDSA['verify'];
1080
- Point: WeierstrassPointCons<bigint>;
1081
- /** @deprecated use `Point` */
1082
- ProjectivePoint: WeierstrassPointCons<bigint>;
1083
- Signature: ECDSASignatureCons;
1084
- utils: ECDSA['utils'];
1085
- info: CurveInfo;
1086
- };
1087
-
1088
1040
  /**
1089
1041
  * Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
1090
1042
  * TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
@@ -1218,87 +1170,211 @@ export function mapToCurveSimpleSWU<T>(
1218
1170
  };
1219
1171
  }
1220
1172
 
1173
+ function getWLengths<T>(Fp: IField<T>, Fn: IField<bigint>) {
1174
+ return {
1175
+ secretKey: Fn.BYTES,
1176
+ publicKey: 1 + Fp.BYTES,
1177
+ publicKeyUncompressed: 1 + 2 * Fp.BYTES,
1178
+ publicKeyHasPrefix: true,
1179
+ signature: 2 * Fn.BYTES,
1180
+ };
1181
+ }
1182
+
1221
1183
  /**
1222
- * Creates ECDSA for given elliptic curve Point and hash function.
1184
+ * Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
1185
+ * This helper ensures no signature functionality is present. Less code, smaller bundle size.
1223
1186
  */
1224
- export function ecdsa(
1187
+ export function ecdh(
1225
1188
  Point: WeierstrassPointCons<bigint>,
1226
- hash: CHash,
1227
- ecdsaOpts: ECDSAOpts = {}
1228
- ): ECDSA {
1229
- ahash(hash);
1230
- _validateObject(
1231
- ecdsaOpts,
1232
- {},
1233
- {
1234
- hmac: 'function',
1235
- lowS: 'boolean',
1236
- randomBytes: 'function',
1237
- bits2int: 'function',
1238
- bits2int_modN: 'function',
1239
- }
1240
- );
1241
-
1242
- const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
1243
- const hmac_: HmacFnSync =
1244
- ecdsaOpts.hmac ||
1245
- (((key, ...msgs) => hmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1246
-
1247
- const { Fp, Fn } = Point;
1248
- const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
1249
-
1250
- const seedLen = getMinHashLength(CURVE_ORDER);
1251
- const lengths = {
1252
- secret: Fn.BYTES,
1253
- public: 1 + Fp.BYTES,
1254
- publicUncompressed: 1 + 2 * Fp.BYTES,
1255
- signature: 2 * Fn.BYTES,
1256
- seed: seedLen,
1257
- };
1189
+ ecdhOpts: { randomBytes?: (bytesLength?: number) => Uint8Array } = {}
1190
+ ): ECDH {
1191
+ const { Fn } = Point;
1192
+ const randomBytes_ = ecdhOpts.randomBytes || randomBytesWeb;
1193
+ const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
1258
1194
 
1259
- function isBiggerThanHalfOrder(number: bigint) {
1260
- const HALF = CURVE_ORDER >> _1n;
1261
- return number > HALF;
1195
+ function isValidSecretKey(secretKey: PrivKey) {
1196
+ try {
1197
+ return !!_normFnElement(Fn, secretKey);
1198
+ } catch (error) {
1199
+ return false;
1200
+ }
1262
1201
  }
1263
1202
 
1264
- function normalizeS(s: bigint) {
1265
- return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
1203
+ function isValidPublicKey(publicKey: Uint8Array, isCompressed?: boolean): boolean {
1204
+ const { publicKey: comp, publicKeyUncompressed } = lengths;
1205
+ try {
1206
+ const l = publicKey.length;
1207
+ if (isCompressed === true && l !== comp) return false;
1208
+ if (isCompressed === false && l !== publicKeyUncompressed) return false;
1209
+ return !!Point.fromBytes(publicKey);
1210
+ } catch (error) {
1211
+ return false;
1212
+ }
1266
1213
  }
1267
- function aValidRS(title: string, num: bigint) {
1268
- if (!Fn.isValidNot0(num))
1269
- throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
1214
+
1215
+ /**
1216
+ * Produces cryptographically secure secret key from random of size
1217
+ * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1218
+ */
1219
+ function randomSecretKey(seed = randomBytes_(lengths.seed)): Uint8Array {
1220
+ return mapHashToField(abytes(seed, lengths.seed, 'seed'), Fn.ORDER);
1221
+ }
1222
+
1223
+ /**
1224
+ * Computes public key for a secret key. Checks for validity of the secret key.
1225
+ * @param isCompressed whether to return compact (default), or full key
1226
+ * @returns Public key, full when isCompressed=false; short when isCompressed=true
1227
+ */
1228
+ function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
1229
+ return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
1230
+ }
1231
+
1232
+ function keygen(seed?: Uint8Array) {
1233
+ const secretKey = randomSecretKey(seed);
1234
+ return { secretKey, publicKey: getPublicKey(secretKey) };
1235
+ }
1236
+
1237
+ /**
1238
+ * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1239
+ */
1240
+ function isProbPub(item: PrivKey | PubKey): boolean | undefined {
1241
+ if (typeof item === 'bigint') return false;
1242
+ if (item instanceof Point) return true;
1243
+ const { secretKey, publicKey, publicKeyUncompressed } = lengths;
1244
+ if (Fn.allowedLengths || secretKey === publicKey) return undefined;
1245
+ const l = ensureBytes('key', item).length;
1246
+ return l === publicKey || l === publicKeyUncompressed;
1270
1247
  }
1271
1248
 
1272
1249
  /**
1273
- * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
1250
+ * ECDH (Elliptic Curve Diffie Hellman).
1251
+ * Computes shared public key from secret key A and public key B.
1252
+ * Checks: 1) secret key validity 2) shared key is on-curve.
1253
+ * Does NOT hash the result.
1254
+ * @param isCompressed whether to return compact (default), or full key
1255
+ * @returns shared public key
1256
+ */
1257
+ function getSharedSecret(secretKeyA: PrivKey, publicKeyB: Hex, isCompressed = true): Uint8Array {
1258
+ if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
1259
+ if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
1260
+ const s = _normFnElement(Fn, secretKeyA);
1261
+ const b = Point.fromHex(publicKeyB); // checks for being on-curve
1262
+ return b.multiply(s).toBytes(isCompressed);
1263
+ }
1264
+
1265
+ const utils = {
1266
+ isValidSecretKey,
1267
+ isValidPublicKey,
1268
+ randomSecretKey,
1269
+
1270
+ // TODO: remove
1271
+ isValidPrivateKey: isValidSecretKey,
1272
+ randomPrivateKey: randomSecretKey,
1273
+ normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
1274
+ precompute(windowSize = 8, point = Point.BASE): WeierstrassPoint<bigint> {
1275
+ return point.precompute(windowSize, false);
1276
+ },
1277
+ };
1278
+
1279
+ return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
1280
+ }
1281
+
1282
+ /**
1283
+ * Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
1284
+ * We need `hash` for 2 features:
1285
+ * 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
1286
+ * 2. k generation in `sign`, using HMAC-drbg(hash)
1287
+ *
1288
+ * ECDSAOpts are only rarely needed.
1289
+ *
1290
+ * @example
1291
+ * ```js
1292
+ * const p256_Point = weierstrass(...);
1293
+ * const p256_sha256 = ecdsa(p256_Point, sha256);
1294
+ * const p256_sha224 = ecdsa(p256_Point, sha224);
1295
+ * const p256_sha224_r = ecdsa(p256_Point, sha224, { randomBytes: (length) => { ... } });
1296
+ * ```
1297
+ */
1298
+ export function ecdsa(
1299
+ Point: WeierstrassPointCons<bigint>,
1300
+ hash: CHash,
1301
+ ecdsaOpts: ECDSAOpts = {}
1302
+ ): ECDSA {
1303
+ ahash(hash);
1304
+ _validateObject(
1305
+ ecdsaOpts,
1306
+ {},
1307
+ {
1308
+ hmac: 'function',
1309
+ lowS: 'boolean',
1310
+ randomBytes: 'function',
1311
+ bits2int: 'function',
1312
+ bits2int_modN: 'function',
1313
+ }
1314
+ );
1315
+
1316
+ const randomBytes = ecdsaOpts.randomBytes || randomBytesWeb;
1317
+ const hmac: HmacFnSync =
1318
+ ecdsaOpts.hmac ||
1319
+ (((key, ...msgs) => nobleHmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1320
+
1321
+ const { Fp, Fn } = Point;
1322
+ const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
1323
+ const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
1324
+ const defaultSigOpts: Required<ECDSASignOpts> = {
1325
+ prehash: false,
1326
+ lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : false,
1327
+ format: undefined as any, //'compact' as ECDSASigFormat,
1328
+ extraEntropy: false,
1329
+ };
1330
+ const defaultSigOpts_format = 'compact';
1331
+
1332
+ function isBiggerThanHalfOrder(number: bigint) {
1333
+ const HALF = CURVE_ORDER >> _1n;
1334
+ return number > HALF;
1335
+ }
1336
+ function validateRS(title: string, num: bigint): bigint {
1337
+ if (!Fn.isValidNot0(num))
1338
+ throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
1339
+ return num;
1340
+ }
1341
+ function validateSigLength(bytes: Uint8Array, format: ECDSASigFormat) {
1342
+ validateSigFormat(format);
1343
+ const size = lengths.signature!;
1344
+ const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
1345
+ return abytes(bytes, sizer, `${format} signature`);
1346
+ }
1347
+
1348
+ /**
1349
+ * ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
1274
1350
  */
1275
1351
  class Signature implements ECDSASignature {
1276
1352
  readonly r: bigint;
1277
1353
  readonly s: bigint;
1278
1354
  readonly recovery?: number;
1279
1355
  constructor(r: bigint, s: bigint, recovery?: number) {
1280
- aValidRS('r', r); // r in [1..N-1]
1281
- aValidRS('s', s); // s in [1..N-1]
1282
- this.r = r;
1283
- this.s = s;
1356
+ this.r = validateRS('r', r); // r in [1..N-1];
1357
+ this.s = validateRS('s', s); // s in [1..N-1];
1284
1358
  if (recovery != null) this.recovery = recovery;
1285
1359
  Object.freeze(this);
1286
1360
  }
1287
1361
 
1288
- static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = 'compact') {
1289
- if (format === 'compact') {
1290
- const L = Fn.BYTES;
1291
- abytes(bytes, L * 2);
1292
- const r = bytes.subarray(0, L);
1293
- const s = bytes.subarray(L, L * 2);
1294
- return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
1295
- }
1362
+ static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = defaultSigOpts_format): Signature {
1363
+ validateSigLength(bytes, format);
1364
+ let recid: number | undefined;
1296
1365
  if (format === 'der') {
1297
- abytes(bytes);
1298
- const { r, s } = DER.toSig(bytes);
1366
+ const { r, s } = DER.toSig(abytes(bytes));
1299
1367
  return new Signature(r, s);
1300
1368
  }
1301
- throw new Error('invalid format');
1369
+ if (format === 'recovered') {
1370
+ recid = bytes[0];
1371
+ format = 'compact';
1372
+ bytes = bytes.subarray(1);
1373
+ }
1374
+ const L = Fn.BYTES;
1375
+ const r = bytes.subarray(0, L);
1376
+ const s = bytes.subarray(L, L * 2);
1377
+ return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
1302
1378
  }
1303
1379
 
1304
1380
  static fromHex(hex: string, format?: ECDSASigFormat) {
@@ -1309,8 +1385,7 @@ export function ecdsa(
1309
1385
  return new Signature(this.r, this.s, recovery) as RecoveredSignature;
1310
1386
  }
1311
1387
 
1312
- // ProjPointType<bigint>
1313
- recoverPublicKey(msgHash: Hex): typeof Point.BASE {
1388
+ recoverPublicKey(messageHash: Hex): WeierstrassPoint<bigint> {
1314
1389
  const FIELD_ORDER = Fp.ORDER;
1315
1390
  const { r, s, recovery: rec } = this;
1316
1391
  if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
@@ -1329,9 +1404,9 @@ export function ecdsa(
1329
1404
  const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
1330
1405
  if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
1331
1406
  const x = Fp.toBytes(radj);
1332
- const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
1407
+ const R = Point.fromBytes(concatBytes(pprefix((rec & 1) === 0), x));
1333
1408
  const ir = Fn.inv(radj); // r^-1
1334
- const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
1409
+ const h = bits2int_modN(ensureBytes('msgHash', messageHash)); // Truncate hash
1335
1410
  const u1 = Fn.create(-h * ir); // -hr^-1
1336
1411
  const u2 = Fn.create(s * ir); // sr^-1
1337
1412
  // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
@@ -1346,14 +1421,16 @@ export function ecdsa(
1346
1421
  return isBiggerThanHalfOrder(this.s);
1347
1422
  }
1348
1423
 
1349
- normalizeS() {
1350
- return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1351
- }
1352
-
1353
- toBytes(format: ECDSASigFormat = 'compact') {
1354
- if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
1424
+ toBytes(format: ECDSASigFormat = defaultSigOpts_format) {
1425
+ validateSigFormat(format);
1355
1426
  if (format === 'der') return hexToBytes(DER.hexFromSig(this));
1356
- throw new Error('invalid format');
1427
+ const r = Fn.toBytes(this.r);
1428
+ const s = Fn.toBytes(this.s);
1429
+ if (format === 'recovered') {
1430
+ if (this.recovery == null) throw new Error('recovery bit must be present');
1431
+ return concatBytes(Uint8Array.of(this.recovery), r, s);
1432
+ }
1433
+ return concatBytes(r, s);
1357
1434
  }
1358
1435
 
1359
1436
  toHex(format?: ECDSASigFormat) {
@@ -1368,6 +1445,9 @@ export function ecdsa(
1368
1445
  static fromDER(hex: Hex) {
1369
1446
  return Signature.fromBytes(ensureBytes('sig', hex), 'der');
1370
1447
  }
1448
+ normalizeS() {
1449
+ return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1450
+ }
1371
1451
  toDERRawBytes() {
1372
1452
  return this.toBytes('der');
1373
1453
  }
@@ -1383,90 +1463,13 @@ export function ecdsa(
1383
1463
  }
1384
1464
  type RecoveredSignature = Signature & { recovery: number };
1385
1465
 
1386
- function isValidSecretKey(privateKey: PrivKey) {
1387
- try {
1388
- return !!_normFnElement(Fn, privateKey);
1389
- } catch (error) {
1390
- return false;
1391
- }
1392
- }
1393
- function isValidPublicKey(publicKey: Uint8Array, isCompressed?: boolean): boolean {
1394
- try {
1395
- const l = publicKey.length;
1396
- if (isCompressed === true && l !== lengths.public) return false;
1397
- if (isCompressed === false && l !== lengths.publicUncompressed) return false;
1398
- return !!Point.fromBytes(publicKey);
1399
- } catch (error) {
1400
- return false;
1401
- }
1402
- }
1403
- /**
1404
- * Produces cryptographically secure secret key from random of size
1405
- * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1406
- */
1407
- function randomSecretKey(seed = randomBytes_(seedLen)): Uint8Array {
1408
- return mapHashToField(seed, CURVE_ORDER);
1409
- }
1410
-
1411
- const utils = {
1412
- isValidSecretKey,
1413
- isValidPublicKey,
1414
- randomSecretKey,
1415
-
1416
- // TODO: remove
1417
- isValidPrivateKey: isValidSecretKey,
1418
- randomPrivateKey: randomSecretKey,
1419
- normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
1420
- precompute(windowSize = 8, point = Point.BASE): WeierstrassPoint<bigint> {
1421
- return point.precompute(windowSize, false);
1422
- },
1423
- };
1424
-
1425
- /**
1426
- * Computes public key for a secret key. Checks for validity of the secret key.
1427
- * @param isCompressed whether to return compact (default), or full key
1428
- * @returns Public key, full when isCompressed=false; short when isCompressed=true
1429
- */
1430
- function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
1431
- return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
1432
- }
1433
-
1434
- /**
1435
- * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1436
- */
1437
- function isProbPub(item: PrivKey | PubKey): boolean | undefined {
1438
- // TODO: remove
1439
- if (typeof item === 'bigint') return false;
1440
- // TODO: remove
1441
- if (item instanceof Point) return true;
1442
- if (Fn.allowedLengths || lengths.secret === lengths.public) return undefined;
1443
- const l = ensureBytes('key', item).length;
1444
- return l === lengths.public || l === lengths.publicUncompressed;
1445
- }
1446
-
1447
- /**
1448
- * ECDH (Elliptic Curve Diffie Hellman).
1449
- * Computes shared public key from secret key A and public key B.
1450
- * Checks: 1) secret key validity 2) shared key is on-curve.
1451
- * Does NOT hash the result.
1452
- * @param isCompressed whether to return compact (default), or full key
1453
- * @returns shared public key
1454
- */
1455
- function getSharedSecret(secretKeyA: PrivKey, publicKeyB: Hex, isCompressed = true): Uint8Array {
1456
- if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
1457
- if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
1458
- const s = _normFnElement(Fn, secretKeyA);
1459
- const b = Point.fromHex(publicKeyB); // checks for being on-curve
1460
- return b.multiply(s).toBytes(isCompressed);
1461
- }
1462
-
1463
1466
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
1464
1467
  // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
1465
1468
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
1466
1469
  // int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
1467
1470
  const bits2int =
1468
1471
  ecdsaOpts.bits2int ||
1469
- function (bytes: Uint8Array): bigint {
1472
+ function bits2int_def(bytes: Uint8Array): bigint {
1470
1473
  // Our custom check "just in case", for protection against DoS
1471
1474
  if (bytes.length > 8192) throw new Error('input is too large');
1472
1475
  // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
@@ -1477,44 +1480,47 @@ export function ecdsa(
1477
1480
  };
1478
1481
  const bits2int_modN =
1479
1482
  ecdsaOpts.bits2int_modN ||
1480
- function (bytes: Uint8Array): bigint {
1483
+ function bits2int_modN_def(bytes: Uint8Array): bigint {
1481
1484
  return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
1482
1485
  };
1483
- // NOTE: pads output with zero as per spec
1486
+ // Pads output with zero as per spec
1484
1487
  const ORDER_MASK = bitMask(fnBits);
1485
- /**
1486
- * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
1487
- */
1488
+ /** Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`. */
1488
1489
  function int2octets(num: bigint): Uint8Array {
1489
1490
  // IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
1490
1491
  aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
1491
1492
  return Fn.toBytes(num);
1492
1493
  }
1493
1494
 
1494
- // Steps A, D of RFC6979 3.2
1495
- // Creates RFC6979 seed; converts msg/privKey to numbers.
1496
- // Used only in sign, not in verify.
1497
- // NOTE: we cannot assume here that msgHash has same amount of bytes as curve order,
1498
- // this will be invalid at least for P521. Also it can be bigger for P224 + SHA256
1499
- function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
1495
+ function validateMsgAndHash(message: Uint8Array, prehash: boolean) {
1496
+ abytes(message, undefined, 'message');
1497
+ return prehash ? abytes(hash(message), undefined, 'prehashed message') : message;
1498
+ }
1499
+
1500
+ /**
1501
+ * Steps A, D of RFC6979 3.2.
1502
+ * Creates RFC6979 seed; converts msg/privKey to numbers.
1503
+ * Used only in sign, not in verify.
1504
+ *
1505
+ * Warning: we cannot assume here that message has same amount of bytes as curve order,
1506
+ * this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
1507
+ */
1508
+ function prepSig(message: Uint8Array, privateKey: PrivKey, opts: ECDSASignOpts) {
1500
1509
  if (['recovered', 'canonical'].some((k) => k in opts))
1501
1510
  throw new Error('sign() legacy options not supported');
1502
- let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
1503
- if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1504
- msgHash = ensureBytes('msgHash', msgHash);
1505
- validateSigVerOpts(opts);
1506
- if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
1507
-
1511
+ const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
1512
+ message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
1508
1513
  // We can't later call bits2octets, since nested bits2int is broken for curves
1509
1514
  // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1510
1515
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1511
- const h1int = bits2int_modN(msgHash);
1516
+ const h1int = bits2int_modN(message);
1512
1517
  const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
1513
1518
  const seedArgs = [int2octets(d), int2octets(h1int)];
1514
1519
  // extraEntropy. RFC6979 3.6: additional k' (optional).
1515
- if (ent != null && ent !== false) {
1520
+ if (extraEntropy != null && extraEntropy !== false) {
1516
1521
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
1517
- const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
1522
+ // gen random bytes OR pass as-is
1523
+ const e = extraEntropy === true ? randomBytes(lengths.secretKey) : extraEntropy;
1518
1524
  seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
1519
1525
  }
1520
1526
  const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
@@ -1530,7 +1536,7 @@ export function ecdsa(
1530
1536
  function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
1531
1537
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
1532
1538
  // Important: all mod() calls here must be done over N
1533
- const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
1539
+ const k = bits2int(kBytes); // mod n, not mod p
1534
1540
  if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
1535
1541
  const ik = Fn.inv(k); // k^-1 mod n
1536
1542
  const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
@@ -1541,122 +1547,102 @@ export function ecdsa(
1541
1547
  let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
1542
1548
  let normS = s;
1543
1549
  if (lowS && isBiggerThanHalfOrder(s)) {
1544
- normS = normalizeS(s); // if lowS was passed, ensure s is always
1550
+ normS = Fn.neg(s); // if lowS was passed, ensure s is always
1545
1551
  recovery ^= 1; // // in the bottom half of N
1546
1552
  }
1547
1553
  return new Signature(r, normS, recovery) as RecoveredSignature; // use normS, not s
1548
1554
  }
1549
1555
  return { seed, k2sig };
1550
1556
  }
1551
- const defaultSigOpts: SignOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1552
- const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1553
1557
 
1554
1558
  /**
1555
1559
  * Signs message hash with a secret key.
1560
+ *
1556
1561
  * ```
1557
- * sign(m, d, k) where
1562
+ * sign(m, d) where
1563
+ * k = rfc6979_hmac_drbg(m, d)
1558
1564
  * (x, y) = G × k
1559
1565
  * r = x mod n
1560
- * s = (m + dr)/k mod n
1566
+ * s = (m + dr) / k mod n
1561
1567
  * ```
1562
1568
  */
1563
- function sign(msgHash: Hex, secretKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
1564
- const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
1565
- const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac_);
1566
- return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1569
+ function sign(message: Hex, secretKey: PrivKey, opts: ECDSASignOpts = {}): RecoveredSignature {
1570
+ message = ensureBytes('message', message);
1571
+ const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
1572
+ const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac);
1573
+ const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
1574
+ return sig;
1567
1575
  }
1568
1576
 
1569
- // Enable precomputes. Slows down first publicKey computation by 20ms.
1570
- Point.BASE.precompute(8);
1577
+ function tryParsingSig(sg: Hex | SignatureLike) {
1578
+ // Try to deduce format
1579
+ let sig: Signature | undefined = undefined;
1580
+ const isHex = typeof sg === 'string' || isBytes(sg);
1581
+ const isObj =
1582
+ !isHex &&
1583
+ sg !== null &&
1584
+ typeof sg === 'object' &&
1585
+ typeof sg.r === 'bigint' &&
1586
+ typeof sg.s === 'bigint';
1587
+ if (!isHex && !isObj)
1588
+ throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1589
+ if (isObj) {
1590
+ sig = new Signature(sg.r, sg.s);
1591
+ } else if (isHex) {
1592
+ try {
1593
+ sig = Signature.fromBytes(ensureBytes('sig', sg), 'der');
1594
+ } catch (derError) {
1595
+ if (!(derError instanceof DER.Err)) throw derError;
1596
+ }
1597
+ if (!sig) {
1598
+ try {
1599
+ sig = Signature.fromBytes(ensureBytes('sig', sg), 'compact');
1600
+ } catch (error) {
1601
+ return false;
1602
+ }
1603
+ }
1604
+ }
1605
+ if (!sig) return false;
1606
+ return sig;
1607
+ }
1571
1608
 
1572
1609
  /**
1573
- * Verifies a signature against message hash and public key.
1574
- * Rejects lowS signatures by default: to override,
1575
- * specify option `{lowS: false}`. Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
1610
+ * Verifies a signature against message and public key.
1611
+ * Rejects lowS signatures by default: see {@link ECDSAVerifyOpts}.
1612
+ * Implements section 4.1.4 from https://www.secg.org/sec1-v2.pdf:
1576
1613
  *
1577
1614
  * ```
1578
1615
  * verify(r, s, h, P) where
1579
- * U1 = hs^-1 mod n
1580
- * U2 = rs^-1 mod n
1581
- * R = U1⋅G - U2⋅P
1616
+ * u1 = hs^-1 mod n
1617
+ * u2 = rs^-1 mod n
1618
+ * R = u1⋅G + u2⋅P
1582
1619
  * mod(R.x, n) == r
1583
1620
  * ```
1584
1621
  */
1585
1622
  function verify(
1586
1623
  signature: Hex | SignatureLike,
1587
- msgHash: Hex,
1624
+ message: Hex,
1588
1625
  publicKey: Hex,
1589
- opts = defaultVerOpts
1626
+ opts: ECDSAVerifyOpts = {}
1590
1627
  ): boolean {
1591
- const sg = signature;
1592
- msgHash = ensureBytes('msgHash', msgHash);
1628
+ const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
1593
1629
  publicKey = ensureBytes('publicKey', publicKey);
1594
-
1595
- // Verify opts
1596
- validateSigVerOpts(opts);
1597
- const { lowS, prehash, format } = opts;
1598
-
1599
- // TODO: remove
1630
+ message = validateMsgAndHash(ensureBytes('message', message), prehash);
1600
1631
  if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
1601
-
1602
- let _sig: Signature | undefined = undefined;
1603
- let P: WeierstrassPoint<bigint>;
1604
-
1605
- if (format === undefined) {
1606
- // Try to deduce format
1607
- const isHex = typeof sg === 'string' || isBytes(sg);
1608
- const isObj =
1609
- !isHex &&
1610
- sg !== null &&
1611
- typeof sg === 'object' &&
1612
- typeof sg.r === 'bigint' &&
1613
- typeof sg.s === 'bigint';
1614
- if (!isHex && !isObj)
1615
- throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1616
- if (isObj) {
1617
- _sig = new Signature(sg.r, sg.s);
1618
- } else if (isHex) {
1619
- // TODO: remove this malleable check
1620
- // Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
1621
- // Since DER can also be 2*Fn.BYTES bytes, we check for it first.
1622
- try {
1623
- _sig = Signature.fromDER(sg);
1624
- } catch (derError) {
1625
- if (!(derError instanceof DER.Err)) throw derError;
1626
- }
1627
- if (!_sig) {
1628
- try {
1629
- _sig = Signature.fromCompact(sg);
1630
- } catch (error) {
1631
- return false;
1632
- }
1633
- }
1634
- }
1635
- } else {
1636
- if (format === 'compact' || format === 'der') {
1637
- if (typeof sg !== 'string' && !isBytes(sg))
1638
- throw new Error('"der" / "compact" format expects Uint8Array signature');
1639
- _sig = Signature.fromBytes(ensureBytes('sig', sg), format);
1640
- } else if (format === 'js') {
1641
- if (!(sg instanceof Signature)) throw new Error('"js" format expects Signature instance');
1642
- _sig = sg;
1643
- } else {
1644
- throw new Error('format must be "compact", "der" or "js"');
1645
- }
1646
- }
1647
-
1648
- if (!_sig) return false;
1632
+ const sig =
1633
+ format === undefined
1634
+ ? tryParsingSig(signature)
1635
+ : Signature.fromBytes(ensureBytes('sig', signature as Hex), format);
1636
+ if (sig === false) return false;
1649
1637
  try {
1650
- P = Point.fromHex(publicKey);
1651
- if (lowS && _sig.hasHighS()) return false;
1652
- // todo: optional.hash => hash
1653
- if (prehash) msgHash = hash(msgHash);
1654
- const { r, s } = _sig;
1655
- const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1656
- const is = Fn.inv(s); // s^-1
1638
+ const P = Point.fromBytes(publicKey);
1639
+ if (lowS && sig.hasHighS()) return false;
1640
+ const { r, s } = sig;
1641
+ const h = bits2int_modN(message); // mod n, not mod p
1642
+ const is = Fn.inv(s); // s^-1 mod n
1657
1643
  const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1658
1644
  const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1659
- const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1645
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2)); // u1⋅G + u2⋅P
1660
1646
  if (R.is0()) return false;
1661
1647
  const v = Fn.create(R.x); // v = r.x mod n
1662
1648
  return v === r;
@@ -1665,37 +1651,144 @@ export function ecdsa(
1665
1651
  }
1666
1652
  }
1667
1653
 
1668
- function keygen(seed?: Uint8Array) {
1669
- const secretKey = utils.randomSecretKey(seed);
1670
- return { secretKey, publicKey: getPublicKey(secretKey) };
1654
+ function recoverPublicKey(
1655
+ signature: Uint8Array,
1656
+ message: Uint8Array,
1657
+ opts: ECDSARecoverOpts = {}
1658
+ ): Uint8Array {
1659
+ const { prehash } = validateSigOpts(opts, defaultSigOpts);
1660
+ message = validateMsgAndHash(message, prehash);
1661
+ return Signature.fromBytes(signature, 'recovered').recoverPublicKey(message).toBytes();
1671
1662
  }
1672
1663
 
1673
1664
  return Object.freeze({
1674
1665
  keygen,
1675
1666
  getPublicKey,
1676
- sign,
1677
- verify,
1678
1667
  getSharedSecret,
1679
1668
  utils,
1669
+ lengths,
1680
1670
  Point,
1671
+ sign,
1672
+ verify,
1673
+ recoverPublicKey,
1681
1674
  Signature,
1682
- info: { type: 'weierstrass' as const, lengths, publicKeyHasPrefix: true },
1675
+ hash,
1683
1676
  });
1684
1677
  }
1685
1678
 
1679
+ // TODO: remove everything below
1680
+ /** @deprecated */
1681
+ export type SignatureType = ECDSASignature;
1682
+ /** @deprecated */
1683
+ export type RecoveredSignatureType = ECDSASigRecovered;
1684
+ /** @deprecated */
1685
+ export type SignatureLike = { r: bigint; s: bigint };
1686
+ /** @deprecated use `Uint8Array | boolean` */
1687
+ export type Entropy = Hex | boolean;
1688
+ export type BasicWCurve<T> = BasicCurve<T> & {
1689
+ // Params: a, b
1690
+ a: T;
1691
+ b: T;
1692
+
1693
+ // Optional params
1694
+ allowedPrivateKeyLengths?: readonly number[]; // for P521
1695
+ wrapPrivateKey?: boolean; // bls12-381 requires mod(n) instead of rejecting keys >= n
1696
+ endo?: EndomorphismOpts;
1697
+ // When a cofactor != 1, there can be an effective methods to:
1698
+ // 1. Determine whether a point is torsion-free
1699
+ isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
1700
+ // 2. Clear torsion component
1701
+ clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
1702
+ };
1703
+ /** @deprecated use ECDSASignOpts */
1704
+ export type SignOpts = ECDSASignOpts;
1705
+ /** @deprecated use ECDSASignOpts */
1706
+ export type VerOpts = ECDSAVerifyOpts;
1707
+
1708
+ /** @deprecated use WeierstrassPoint */
1709
+ export type ProjPointType<T> = WeierstrassPoint<T>;
1710
+ /** @deprecated use WeierstrassPointCons */
1711
+ export type ProjConstructor<T> = WeierstrassPointCons<T>;
1712
+
1686
1713
  // TODO: remove
1714
+ export type CurvePointsType<T> = BasicWCurve<T> & {
1715
+ fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
1716
+ toBytes?: (
1717
+ c: WeierstrassPointCons<T>,
1718
+ point: WeierstrassPoint<T>,
1719
+ isCompressed: boolean
1720
+ ) => Uint8Array;
1721
+ };
1722
+
1723
+ // LegacyWeierstrassOpts
1724
+ export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
1725
+
1726
+ // LegacyWeierstrass
1727
+ export type CurvePointsRes<T> = {
1728
+ Point: WeierstrassPointCons<T>;
1729
+
1730
+ /** @deprecated use `Point.CURVE()` */
1731
+ CURVE: CurvePointsType<T>;
1732
+ /** @deprecated use `Point` */
1733
+ ProjectivePoint: WeierstrassPointCons<T>;
1734
+ /** @deprecated use `Point.Fn.fromBytes(privateKey)` */
1735
+ normPrivateKeyToScalar: (key: PrivKey) => bigint;
1736
+ /** @deprecated */
1737
+ weierstrassEquation: (x: T) => T;
1738
+ /** @deprecated use `Point.Fn.isValidNot0(num)` */
1739
+ isWithinCurveOrder: (num: bigint) => boolean;
1740
+ };
1741
+
1742
+ // Aliases to legacy types
1743
+ // export type CurveType = LegacyECDSAOpts;
1744
+ // export type CurveFn = LegacyECDSA;
1745
+ // export type CurvePointsRes<T> = LegacyWeierstrass<T>;
1746
+ // export type CurvePointsType<T> = LegacyWeierstrassOpts<T>;
1747
+ // export type CurvePointsTypeWithLength<T> = LegacyWeierstrassOpts<T>;
1748
+ // export type BasicWCurve<T> = LegacyWeierstrassOpts<T>;
1749
+
1750
+ /** @deprecated use `Uint8Array` */
1751
+ export type PubKey = Hex | WeierstrassPoint<bigint>;
1752
+ export type CurveType = BasicWCurve<bigint> & {
1753
+ hash: CHash; // CHash not FHash because we need outputLen for DRBG
1754
+ hmac?: HmacFnSync;
1755
+ randomBytes?: (bytesLength?: number) => Uint8Array;
1756
+ lowS?: boolean;
1757
+ bits2int?: (bytes: Uint8Array) => bigint;
1758
+ bits2int_modN?: (bytes: Uint8Array) => bigint;
1759
+ };
1760
+ export type CurveFn = {
1761
+ /** @deprecated use `Point.CURVE()` */
1762
+ CURVE: CurvePointsType<bigint>;
1763
+ keygen: ECDSA['keygen'];
1764
+ getPublicKey: ECDSA['getPublicKey'];
1765
+ getSharedSecret: ECDSA['getSharedSecret'];
1766
+ sign: ECDSA['sign'];
1767
+ verify: ECDSA['verify'];
1768
+ Point: WeierstrassPointCons<bigint>;
1769
+ /** @deprecated use `Point` */
1770
+ ProjectivePoint: WeierstrassPointCons<bigint>;
1771
+ Signature: ECDSASignatureCons;
1772
+ utils: ECDSA['utils'];
1773
+ lengths: ECDSA['lengths'];
1774
+ };
1775
+ /** @deprecated use `weierstrass` in newer releases */
1776
+ export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
1777
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1778
+ const Point = weierstrassN(CURVE, curveOpts);
1779
+ return _weierstrass_new_output_to_legacy(c, Point);
1780
+ }
1687
1781
  export type WsPointComposed<T> = {
1688
1782
  CURVE: WeierstrassOpts<T>;
1689
1783
  curveOpts: WeierstrassExtraOpts<T>;
1690
1784
  };
1691
- // TODO: remove
1692
1785
  export type WsComposed = {
1786
+ /** @deprecated use `Point.CURVE()` */
1693
1787
  CURVE: WeierstrassOpts<bigint>;
1694
1788
  hash: CHash;
1695
1789
  curveOpts: WeierstrassExtraOpts<bigint>;
1696
1790
  ecdsaOpts: ECDSAOpts;
1697
1791
  };
1698
- // TODO: remove
1699
1792
  function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
1700
1793
  const CURVE: WeierstrassOpts<T> = {
1701
1794
  a: c.a,
@@ -1713,7 +1806,7 @@ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointCompo
1713
1806
  const Fn = Field(CURVE.n, {
1714
1807
  BITS: c.nBitLength,
1715
1808
  allowedLengths: allowedLengths,
1716
- modOnDecode: c.wrapPrivateKey,
1809
+ modFromBytes: c.wrapPrivateKey,
1717
1810
  });
1718
1811
  const curveOpts: WeierstrassExtraOpts<T> = {
1719
1812
  Fp,
@@ -1738,13 +1831,23 @@ function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
1738
1831
  };
1739
1832
  return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
1740
1833
  }
1741
- // TODO: remove
1834
+ export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
1835
+ /**
1836
+ * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
1837
+ * @returns y²
1838
+ */
1839
+ function weierstrassEquation(x: T): T {
1840
+ const x2 = Fp.sqr(x); // x * x
1841
+ const x3 = Fp.mul(x2, x); // x² * x
1842
+ return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
1843
+ }
1844
+ return weierstrassEquation;
1845
+ }
1742
1846
  function _weierstrass_new_output_to_legacy<T>(
1743
1847
  c: CurvePointsType<T>,
1744
1848
  Point: WeierstrassPointCons<T>
1745
1849
  ): CurvePointsRes<T> {
1746
1850
  const { Fp, Fn } = Point;
1747
- // TODO: remove
1748
1851
  function isWithinCurveOrder(num: bigint): boolean {
1749
1852
  return inRange(num, _1n, Fn.ORDER);
1750
1853
  }
@@ -1761,11 +1864,11 @@ function _weierstrass_new_output_to_legacy<T>(
1761
1864
  }
1762
1865
  );
1763
1866
  }
1764
- // TODO: remove
1765
- function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
1766
- return Object.assign({}, ecdsa, {
1767
- ProjectivePoint: ecdsa.Point,
1768
- CURVE: c,
1867
+ function _ecdsa_new_output_to_legacy(c: CurveType, _ecdsa: ECDSA): CurveFn {
1868
+ const Point = _ecdsa.Point;
1869
+ return Object.assign({}, _ecdsa, {
1870
+ ProjectivePoint: Point,
1871
+ CURVE: Object.assign({}, c, nLength(Point.Fn.ORDER, Point.Fn.BITS)),
1769
1872
  });
1770
1873
  }
1771
1874