@noble/curves 1.9.1 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +56 -25
  2. package/_shortw_utils.d.ts +7 -5
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +2 -8
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +60 -24
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +158 -109
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +44 -9
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +86 -7
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +112 -25
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +138 -102
  17. package/abstract/edwards.js.map +1 -1
  18. package/abstract/fft.d.ts +12 -10
  19. package/abstract/fft.d.ts.map +1 -1
  20. package/abstract/fft.js +12 -13
  21. package/abstract/fft.js.map +1 -1
  22. package/abstract/hash-to-curve.d.ts +25 -11
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +17 -14
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +24 -11
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +49 -20
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +1 -1
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +5 -4
  33. package/abstract/montgomery.js.map +1 -1
  34. package/abstract/poseidon.d.ts +5 -13
  35. package/abstract/poseidon.d.ts.map +1 -1
  36. package/abstract/poseidon.js +12 -7
  37. package/abstract/poseidon.js.map +1 -1
  38. package/abstract/tower.d.ts +20 -46
  39. package/abstract/tower.d.ts.map +1 -1
  40. package/abstract/tower.js +9 -3
  41. package/abstract/tower.js.map +1 -1
  42. package/abstract/utils.d.ts +1 -115
  43. package/abstract/utils.d.ts.map +1 -1
  44. package/abstract/utils.js +17 -371
  45. package/abstract/utils.js.map +1 -1
  46. package/abstract/weierstrass.d.ts +132 -76
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +462 -398
  49. package/abstract/weierstrass.js.map +1 -1
  50. package/bls12-381.d.ts +2 -0
  51. package/bls12-381.d.ts.map +1 -1
  52. package/bls12-381.js +504 -466
  53. package/bls12-381.js.map +1 -1
  54. package/bn254.d.ts +2 -0
  55. package/bn254.d.ts.map +1 -1
  56. package/bn254.js +44 -32
  57. package/bn254.js.map +1 -1
  58. package/ed25519.d.ts +8 -5
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +67 -54
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +10 -6
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +80 -57
  65. package/ed448.js.map +1 -1
  66. package/esm/_shortw_utils.d.ts +7 -5
  67. package/esm/_shortw_utils.d.ts.map +1 -1
  68. package/esm/_shortw_utils.js +2 -8
  69. package/esm/_shortw_utils.js.map +1 -1
  70. package/esm/abstract/bls.d.ts +60 -24
  71. package/esm/abstract/bls.d.ts.map +1 -1
  72. package/esm/abstract/bls.js +158 -109
  73. package/esm/abstract/bls.js.map +1 -1
  74. package/esm/abstract/curve.d.ts +44 -9
  75. package/esm/abstract/curve.d.ts.map +1 -1
  76. package/esm/abstract/curve.js +83 -8
  77. package/esm/abstract/curve.js.map +1 -1
  78. package/esm/abstract/edwards.d.ts +112 -25
  79. package/esm/abstract/edwards.d.ts.map +1 -1
  80. package/esm/abstract/edwards.js +138 -104
  81. package/esm/abstract/edwards.js.map +1 -1
  82. package/esm/abstract/fft.d.ts +12 -10
  83. package/esm/abstract/fft.d.ts.map +1 -1
  84. package/esm/abstract/fft.js +10 -11
  85. package/esm/abstract/fft.js.map +1 -1
  86. package/esm/abstract/hash-to-curve.d.ts +25 -11
  87. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  88. package/esm/abstract/hash-to-curve.js +17 -14
  89. package/esm/abstract/hash-to-curve.js.map +1 -1
  90. package/esm/abstract/modular.d.ts +24 -11
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +48 -19
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +1 -1
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +5 -4
  97. package/esm/abstract/montgomery.js.map +1 -1
  98. package/esm/abstract/poseidon.d.ts +5 -13
  99. package/esm/abstract/poseidon.d.ts.map +1 -1
  100. package/esm/abstract/poseidon.js +12 -7
  101. package/esm/abstract/poseidon.js.map +1 -1
  102. package/esm/abstract/tower.d.ts +20 -46
  103. package/esm/abstract/tower.d.ts.map +1 -1
  104. package/esm/abstract/tower.js +9 -3
  105. package/esm/abstract/tower.js.map +1 -1
  106. package/esm/abstract/utils.d.ts +1 -115
  107. package/esm/abstract/utils.d.ts.map +1 -1
  108. package/esm/abstract/utils.js +3 -344
  109. package/esm/abstract/utils.js.map +1 -1
  110. package/esm/abstract/weierstrass.d.ts +132 -76
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +460 -400
  113. package/esm/abstract/weierstrass.js.map +1 -1
  114. package/esm/bls12-381.d.ts +2 -0
  115. package/esm/bls12-381.d.ts.map +1 -1
  116. package/esm/bls12-381.js +503 -465
  117. package/esm/bls12-381.js.map +1 -1
  118. package/esm/bn254.d.ts +2 -0
  119. package/esm/bn254.d.ts.map +1 -1
  120. package/esm/bn254.js +41 -29
  121. package/esm/bn254.js.map +1 -1
  122. package/esm/ed25519.d.ts +8 -5
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +62 -49
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +10 -6
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +74 -51
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/misc.d.ts.map +1 -1
  131. package/esm/misc.js +31 -26
  132. package/esm/misc.js.map +1 -1
  133. package/esm/nist.d.ts +7 -16
  134. package/esm/nist.d.ts.map +1 -1
  135. package/esm/nist.js +86 -97
  136. package/esm/nist.js.map +1 -1
  137. package/esm/p256.d.ts +3 -3
  138. package/esm/p384.d.ts +3 -3
  139. package/esm/p521.d.ts +3 -3
  140. package/esm/secp256k1.d.ts +6 -6
  141. package/esm/secp256k1.d.ts.map +1 -1
  142. package/esm/secp256k1.js +43 -40
  143. package/esm/secp256k1.js.map +1 -1
  144. package/esm/utils.d.ts +96 -0
  145. package/esm/utils.d.ts.map +1 -0
  146. package/esm/utils.js +279 -0
  147. package/esm/utils.js.map +1 -0
  148. package/misc.d.ts.map +1 -1
  149. package/misc.js +35 -30
  150. package/misc.js.map +1 -1
  151. package/nist.d.ts +7 -16
  152. package/nist.d.ts.map +1 -1
  153. package/nist.js +86 -97
  154. package/nist.js.map +1 -1
  155. package/p256.d.ts +3 -3
  156. package/p384.d.ts +3 -3
  157. package/p521.d.ts +3 -3
  158. package/package.json +14 -5
  159. package/secp256k1.d.ts +6 -6
  160. package/secp256k1.d.ts.map +1 -1
  161. package/secp256k1.js +46 -43
  162. package/secp256k1.js.map +1 -1
  163. package/src/_shortw_utils.ts +5 -15
  164. package/src/abstract/bls.ts +260 -145
  165. package/src/abstract/curve.ts +115 -13
  166. package/src/abstract/edwards.ts +279 -138
  167. package/src/abstract/fft.ts +30 -19
  168. package/src/abstract/hash-to-curve.ts +51 -27
  169. package/src/abstract/modular.ts +49 -28
  170. package/src/abstract/montgomery.ts +9 -7
  171. package/src/abstract/poseidon.ts +22 -18
  172. package/src/abstract/tower.ts +36 -67
  173. package/src/abstract/utils.ts +3 -378
  174. package/src/abstract/weierstrass.ts +700 -453
  175. package/src/bls12-381.ts +540 -489
  176. package/src/bn254.ts +47 -35
  177. package/src/ed25519.ts +80 -64
  178. package/src/ed448.ts +129 -92
  179. package/src/misc.ts +39 -34
  180. package/src/nist.ts +138 -127
  181. package/src/p256.ts +3 -3
  182. package/src/p384.ts +3 -3
  183. package/src/p521.ts +3 -3
  184. package/src/secp256k1.ts +58 -46
  185. package/src/utils.ts +328 -0
  186. package/utils.d.ts +96 -0
  187. package/utils.d.ts.map +1 -0
  188. package/utils.js +313 -0
  189. package/utils.js.map +1 -0
@@ -1,19 +1,6 @@
1
1
  /**
2
2
  * Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
3
3
  *
4
- * ### Parameters
5
- *
6
- * To initialize a weierstrass curve, one needs to pass following params:
7
- *
8
- * * a: formula param
9
- * * b: formula param
10
- * * Fp: finite field of prime characteristic P; may be complex (Fp2). Arithmetics is done in field
11
- * * n: order of prime subgroup a.k.a total amount of valid curve points
12
- * * Gx: Base point (x, y) aka generator point. Gx = x coordinate
13
- * * Gy: ...y coordinate
14
- * * h: cofactor, usually 1. h*n = curve group order (n is only subgroup order)
15
- * * lowS: whether to enable (default) or disable "low-s" non-malleable signatures
16
- *
17
4
  * ### Design rationale for types
18
5
  *
19
6
  * * Interaction between classes from different curves should fail:
@@ -38,29 +25,52 @@
38
25
  * @module
39
26
  */
40
27
  /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
41
- // prettier-ignore
28
+ import { hmac } from '@noble/hashes/hmac.js';
42
29
  import {
43
- pippenger, validateBasic, wNAF,
44
- type AffinePoint, type BasicCurve, type Group, type GroupConstructor
30
+ _validateObject,
31
+ abool,
32
+ abytes,
33
+ aInRange,
34
+ bitMask,
35
+ bytesToHex,
36
+ bytesToNumberBE,
37
+ concatBytes,
38
+ createHmacDrbg,
39
+ ensureBytes,
40
+ hexToBytes,
41
+ inRange,
42
+ isBytes,
43
+ memoized,
44
+ numberToHexUnpadded,
45
+ randomBytes,
46
+ type CHash,
47
+ type Hex,
48
+ type PrivKey,
49
+ } from '../utils.ts';
50
+ import {
51
+ _createCurveFields,
52
+ mulEndoUnsafe,
53
+ negateCt,
54
+ normalizeZ,
55
+ pippenger,
56
+ wNAF,
57
+ type AffinePoint,
58
+ type BasicCurve,
59
+ type Group,
60
+ type GroupConstructor,
45
61
  } from './curve.ts';
46
- // prettier-ignore
47
62
  import {
48
63
  Field,
49
64
  FpInvertBatch,
50
- getMinHashLength, invert, mapHashToField, mod, validateField,
51
- type IField
65
+ getMinHashLength,
66
+ mapHashToField,
67
+ validateField,
68
+ type IField,
69
+ type NLength,
52
70
  } from './modular.ts';
53
- // prettier-ignore
54
- import {
55
- aInRange, abool,
56
- bitMask,
57
- bytesToHex, bytesToNumberBE, concatBytes, createHmacDrbg, ensureBytes, hexToBytes,
58
- inRange, isBytes, memoized, numberToBytesBE, numberToHexUnpadded, validateObject,
59
- type CHash, type Hex, type PrivKey
60
- } from './utils.ts';
61
71
 
62
72
  export type { AffinePoint };
63
- type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
73
+ export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
64
74
  /**
65
75
  * When Weierstrass curve has `a=0`, it becomes Koblitz curve.
66
76
  * Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
@@ -106,36 +116,70 @@ export type BasicWCurve<T> = BasicCurve<T> & {
106
116
 
107
117
  export type Entropy = Hex | boolean;
108
118
  export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
109
- export type VerOpts = { lowS?: boolean; prehash?: boolean; format?: 'compact' | 'der' | undefined };
119
+ export type VerOpts = {
120
+ lowS?: boolean;
121
+ prehash?: boolean;
122
+ format?: 'compact' | 'der' | 'js' | undefined;
123
+ };
110
124
 
111
125
  function validateSigVerOpts(opts: SignOpts | VerOpts) {
112
126
  if (opts.lowS !== undefined) abool('lowS', opts.lowS);
113
127
  if (opts.prehash !== undefined) abool('prehash', opts.prehash);
114
128
  }
115
129
 
116
- // Instance for 3d XYZ points
130
+ /** Instance methods for 3D XYZ points. */
117
131
  export interface ProjPointType<T> extends Group<ProjPointType<T>> {
132
+ /** projective x coordinate. Note: different from .x */
118
133
  readonly px: T;
134
+ /** projective y coordinate. Note: different from .y */
119
135
  readonly py: T;
136
+ /** projective z coordinate */
120
137
  readonly pz: T;
138
+ /** affine x coordinate */
121
139
  get x(): T;
140
+ /** affine y coordinate */
122
141
  get y(): T;
123
- toAffine(iz?: T): AffinePoint<T>;
124
- toHex(isCompressed?: boolean): string;
125
- toRawBytes(isCompressed?: boolean): Uint8Array;
126
-
127
142
  assertValidity(): void;
128
- hasEvenY(): boolean;
143
+ clearCofactor(): ProjPointType<T>;
144
+ is0(): boolean;
145
+ isTorsionFree(): boolean;
129
146
  multiplyUnsafe(scalar: bigint): ProjPointType<T>;
147
+ /**
148
+ * Massively speeds up `p.multiply(n)` by using wnaf precompute tables (caching).
149
+ * Table generation takes 30MB of ram and 10ms on high-end CPU, but may take
150
+ * much longer on slow devices.
151
+ * Actual generation will happen on first call of `.multiply()`.
152
+ * By default, BASE point is precomputed.
153
+ * @param windowSize - table window size
154
+ * @param isLazy - (default true) allows to defer generation
155
+ */
156
+ precompute(windowSize?: number, isLazy?: boolean): ProjPointType<T>;
157
+
158
+ /** Converts 3D XYZ projective point to 2D xy affine coordinates */
159
+ toAffine(invertedZ?: T): AffinePoint<T>;
160
+ /** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
161
+ toBytes(isCompressed?: boolean): Uint8Array;
162
+ toHex(isCompressed?: boolean): string;
163
+
164
+ /** @deprecated use `toBytes` */
165
+ toRawBytes(isCompressed?: boolean): Uint8Array;
166
+ /** @deprecated use `multiplyUnsafe` */
130
167
  multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
131
- isTorsionFree(): boolean;
132
- clearCofactor(): ProjPointType<T>;
168
+ /** @deprecated use `p.y % 2n === 0n` */
169
+ hasEvenY(): boolean;
170
+ /** @deprecated use `p.precompute(windowSize)` */
133
171
  _setWindowSize(windowSize: number): void;
134
172
  }
135
- // Static methods for 3d XYZ points
173
+
174
+ /** Static methods for 3D XYZ points. */
136
175
  export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
176
+ Fp: IField<T>;
177
+ Fn: IField<bigint>;
178
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
137
179
  new (x: T, y: T, z: T): ProjPointType<T>;
180
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
138
181
  fromAffine(p: AffinePoint<T>): ProjPointType<T>;
182
+ fromBytes(encodedPoint: Uint8Array): ProjPointType<T>;
139
183
  fromHex(hex: Hex): ProjPointType<T>;
140
184
  fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
141
185
  normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
@@ -143,57 +187,104 @@ export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
143
187
  }
144
188
 
145
189
  export type CurvePointsType<T> = BasicWCurve<T> & {
146
- // Bytes
147
190
  fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
148
191
  toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
149
192
  };
150
193
 
151
- export type CurvePointsTypeWithLength<T> = Readonly<
152
- CurvePointsType<T> & { nByteLength: number; nBitLength: number }
153
- >;
154
-
155
- function validatePointOpts<T>(curve: CurvePointsType<T>): CurvePointsTypeWithLength<T> {
156
- const opts = validateBasic(curve);
157
- validateObject(
158
- opts,
159
- {
160
- a: 'field',
161
- b: 'field',
162
- },
163
- {
164
- allowInfinityPoint: 'boolean',
165
- allowedPrivateKeyLengths: 'array',
166
- clearCofactor: 'function',
167
- fromBytes: 'function',
168
- isTorsionFree: 'function',
169
- toBytes: 'function',
170
- wrapPrivateKey: 'boolean',
171
- }
172
- );
173
- const { endo, Fp, a } = opts;
174
- if (endo) {
175
- if (!Fp.eql(a, Fp.ZERO)) {
176
- throw new Error('invalid endo: CURVE.a must be 0');
177
- }
178
- if (
179
- typeof endo !== 'object' ||
180
- typeof endo.beta !== 'bigint' ||
181
- typeof endo.splitScalar !== 'function'
182
- ) {
183
- throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
184
- }
185
- }
186
- return Object.freeze({ ...opts } as const);
187
- }
194
+ // LegacyWeierstrassOpts
195
+ export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
188
196
 
197
+ // LegacyWeierstrass
189
198
  export type CurvePointsRes<T> = {
190
- CURVE: ReturnType<typeof validatePointOpts<T>>;
199
+ /** @deprecated import individual CURVE params */
200
+ CURVE: CurvePointsType<T>;
201
+ Point: ProjConstructor<T>;
202
+ /** @deprecated use `Point` */
191
203
  ProjectivePoint: ProjConstructor<T>;
204
+ /** @deprecated */
192
205
  normPrivateKeyToScalar: (key: PrivKey) => bigint;
206
+ /** @deprecated */
193
207
  weierstrassEquation: (x: T) => T;
208
+ /** @deprecated use `Point.Fn.isValidNot0(num)` */
194
209
  isWithinCurveOrder: (num: bigint) => boolean;
195
210
  };
196
211
 
212
+ // Aliases to legacy types
213
+ // export type CurveType = LegacyECDSAOpts;
214
+ // export type CurveFn = LegacyECDSA;
215
+ // export type CurvePointsRes<T> = LegacyWeierstrass<T>;
216
+ // export type CurvePointsType<T> = LegacyWeierstrassOpts<T>;
217
+ // export type CurvePointsTypeWithLength<T> = LegacyWeierstrassOpts<T>;
218
+ // export type BasicWCurve<T> = LegacyWeierstrassOpts<T>;
219
+
220
+ /**
221
+ * Weierstrass curve options.
222
+ *
223
+ * * p: prime characteristic (order) of finite field, in which arithmetics is done
224
+ * * n: order of prime subgroup a.k.a total amount of valid curve points
225
+ * * h: cofactor, usually 1. h*n is group order; n is subgroup order
226
+ * * a: formula param, must be in field of p
227
+ * * b: formula param, must be in field of p
228
+ * * Gx: x coordinate of generator point a.k.a. base point
229
+ * * Gy: y coordinate of generator point
230
+ */
231
+ export type WeierstrassOpts<T> = Readonly<{
232
+ p: bigint;
233
+ n: bigint;
234
+ h: bigint;
235
+ a: T;
236
+ b: T;
237
+ Gx: T;
238
+ Gy: T;
239
+ }>;
240
+
241
+ // When a cofactor != 1, there can be an effective methods to:
242
+ // 1. Determine whether a point is torsion-free
243
+ // 2. Clear torsion component
244
+ // wrapPrivateKey: bls12-381 requires mod(n) instead of rejecting keys >= n
245
+ export type WeierstrassExtraOpts<T> = Partial<{
246
+ Fp: IField<T>;
247
+ Fn: IField<bigint>;
248
+ // TODO: remove
249
+ allowedPrivateKeyLengths: readonly number[]; // for P521
250
+ allowInfinityPoint: boolean;
251
+ endo: EndomorphismOpts;
252
+ wrapPrivateKey: boolean;
253
+ isTorsionFree: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
254
+ clearCofactor: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
255
+ fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
256
+ toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
257
+ }>;
258
+
259
+ /**
260
+ * Options for ECDSA signatures over a Weierstrass curve.
261
+ */
262
+ export type ECDSAOpts = {
263
+ hash: CHash;
264
+ hmac?: HmacFnSync;
265
+ randomBytes?: (bytesLength?: number) => Uint8Array;
266
+ lowS?: boolean;
267
+ bits2int?: (bytes: Uint8Array) => bigint;
268
+ bits2int_modN?: (bytes: Uint8Array) => bigint;
269
+ };
270
+
271
+ /** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
272
+ export interface ECDSA {
273
+ getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
274
+ getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
275
+ sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
276
+ verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
277
+ Point: ProjConstructor<bigint>;
278
+ Signature: SignatureConstructor;
279
+ utils: {
280
+ isValidPrivateKey(privateKey: PrivKey): boolean;
281
+ randomPrivateKey: () => Uint8Array;
282
+ // TODO: deprecate those two
283
+ normPrivateKeyToScalar: (key: PrivKey) => bigint;
284
+ /** @deprecated */
285
+ precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
286
+ };
287
+ }
197
288
  export class DERErr extends Error {
198
289
  constructor(m = '') {
199
290
  super(m);
@@ -312,47 +403,155 @@ export const DER: IDER = {
312
403
  },
313
404
  };
314
405
 
315
- function numToSizedHex(num: bigint, size: number): string {
316
- return bytesToHex(numberToBytesBE(num, size));
317
- }
318
-
319
406
  // Be friendly to bad ECMAScript parsers by not using bigint literals
320
407
  // prettier-ignore
321
408
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
322
409
 
323
- export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T> {
324
- const CURVE = validatePointOpts(opts);
325
- const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ
326
- const Fn = Field(CURVE.n, CURVE.nBitLength);
327
-
328
- const toBytes =
329
- CURVE.toBytes ||
330
- ((_c: ProjConstructor<T>, point: ProjPointType<T>, _isCompressed: boolean) => {
331
- const a = point.toAffine();
332
- return concatBytes(Uint8Array.from([0x04]), Fp.toBytes(a.x), Fp.toBytes(a.y));
333
- });
334
- const fromBytes =
335
- CURVE.fromBytes ||
336
- ((bytes: Uint8Array) => {
337
- // const head = bytes[0];
338
- const tail = bytes.subarray(1);
339
- // if (head !== 0x04) throw new Error('Only non-compressed encoding is supported');
340
- const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
341
- const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
342
- return { x, y };
343
- });
344
-
410
+ // TODO: remove
411
+ export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
345
412
  /**
346
413
  * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
347
414
  * @returns y²
348
415
  */
349
416
  function weierstrassEquation(x: T): T {
350
- const { a, b } = CURVE;
351
417
  const x2 = Fp.sqr(x); // x * x
352
418
  const x3 = Fp.mul(x2, x); // x² * x
353
419
  return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
354
420
  }
421
+ return weierstrassEquation;
422
+ }
423
+ export function _legacyHelperNormPriv(
424
+ Fn: IField<bigint>,
425
+ allowedPrivateKeyLengths?: readonly number[],
426
+ wrapPrivateKey?: boolean
427
+ ): (key: PrivKey) => bigint {
428
+ const { BYTES: expected } = Fn;
429
+ // Validates if priv key is valid and converts it to bigint.
430
+ function normPrivateKeyToScalar(key: PrivKey): bigint {
431
+ let num: bigint;
432
+ if (typeof key === 'bigint') {
433
+ num = key;
434
+ } else {
435
+ let bytes = ensureBytes('private key', key);
436
+ if (allowedPrivateKeyLengths) {
437
+ if (!allowedPrivateKeyLengths.includes(bytes.length * 2))
438
+ throw new Error('invalid private key');
439
+ const padded = new Uint8Array(expected);
440
+ padded.set(bytes, padded.length - bytes.length);
441
+ bytes = padded;
442
+ }
443
+ try {
444
+ num = Fn.fromBytes(bytes);
445
+ } catch (error) {
446
+ throw new Error(
447
+ `invalid private key: expected ui8a of size ${expected}, got ${typeof key}`
448
+ );
449
+ }
450
+ }
451
+ if (wrapPrivateKey) num = Fn.create(num); // disabled by default, enabled for BLS
452
+ if (!Fn.isValidNot0(num)) throw new Error('invalid private key: out of range [1..N-1]');
453
+ return num;
454
+ }
455
+ return normPrivateKeyToScalar;
456
+ }
457
+
458
+ export function weierstrassN<T>(
459
+ CURVE: WeierstrassOpts<T>,
460
+ curveOpts: WeierstrassExtraOpts<T> = {}
461
+ ): ProjConstructor<T> {
462
+ const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
463
+ const { h: cofactor, n: CURVE_ORDER } = CURVE;
464
+ _validateObject(
465
+ curveOpts,
466
+ {},
467
+ {
468
+ allowInfinityPoint: 'boolean',
469
+ clearCofactor: 'function',
470
+ isTorsionFree: 'function',
471
+ fromBytes: 'function',
472
+ toBytes: 'function',
473
+ endo: 'object',
474
+ wrapPrivateKey: 'boolean',
475
+ }
476
+ );
477
+
478
+ const { endo } = curveOpts;
479
+ if (endo) {
480
+ // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
481
+ if (
482
+ !Fp.is0(CURVE.a) ||
483
+ typeof endo.beta !== 'bigint' ||
484
+ typeof endo.splitScalar !== 'function'
485
+ ) {
486
+ throw new Error('invalid endo: expected "beta": bigint and "splitScalar": function');
487
+ }
488
+ }
355
489
 
490
+ function assertCompressionIsSupported() {
491
+ if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
492
+ }
493
+
494
+ // Implements IEEE P1363 point encoding
495
+ function pointToBytes(
496
+ _c: ProjConstructor<T>,
497
+ point: ProjPointType<T>,
498
+ isCompressed: boolean
499
+ ): Uint8Array {
500
+ const { x, y } = point.toAffine();
501
+ const bx = Fp.toBytes(x);
502
+ abool('isCompressed', isCompressed);
503
+ if (isCompressed) {
504
+ assertCompressionIsSupported();
505
+ const hasEvenY = !Fp.isOdd!(y);
506
+ return concatBytes(pprefix(hasEvenY), bx);
507
+ } else {
508
+ return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
509
+ }
510
+ }
511
+ function pointFromBytes(bytes: Uint8Array) {
512
+ abytes(bytes);
513
+ const L = Fp.BYTES;
514
+ const LC = L + 1; // length compressed, e.g. 33 for 32-byte field
515
+ const LU = 2 * L + 1; // length uncompressed, e.g. 65 for 32-byte field
516
+ const length = bytes.length;
517
+ const head = bytes[0];
518
+ const tail = bytes.subarray(1);
519
+ // No actual validation is done here: use .assertValidity()
520
+ if (length === LC && (head === 0x02 || head === 0x03)) {
521
+ const x = Fp.fromBytes(tail);
522
+ if (!Fp.isValid(x)) throw new Error('bad point: is not on curve, wrong x');
523
+ const y2 = weierstrassEquation(x); // y² = x³ + ax + b
524
+ let y: T;
525
+ try {
526
+ y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
527
+ } catch (sqrtError) {
528
+ const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
529
+ throw new Error('bad point: is not on curve, sqrt error' + err);
530
+ }
531
+ assertCompressionIsSupported();
532
+ const isYOdd = Fp.isOdd!(y); // (y & _1n) === _1n;
533
+ const isHeadOdd = (head & 1) === 1; // ECDSA-specific
534
+ if (isHeadOdd !== isYOdd) y = Fp.neg(y);
535
+ return { x, y };
536
+ } else if (length === LU && head === 0x04) {
537
+ // TODO: more checks
538
+ const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
539
+ const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
540
+ if (!isValidXY(x, y)) throw new Error('bad point: is not on curve');
541
+ return { x, y };
542
+ } else {
543
+ throw new Error(
544
+ `bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`
545
+ );
546
+ }
547
+ }
548
+
549
+ const toBytes = curveOpts.toBytes || pointToBytes;
550
+ const fromBytes = curveOpts.fromBytes || pointFromBytes;
551
+ const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
552
+
553
+ // TODO: move top-level
554
+ /** Checks whether equation holds for given x, y: y² == x³ + ax + b */
356
555
  function isValidXY(x: T, y: T): boolean {
357
556
  const left = Fp.sqr(y); // y²
358
557
  const right = weierstrassEquation(x); // x³ + ax + b
@@ -369,35 +568,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
369
568
  const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
370
569
  if (Fp.is0(Fp.add(_4a3, _27b2))) throw new Error('bad curve params: a or b');
371
570
 
372
- // Valid group elements reside in range 1..n-1
373
- function isWithinCurveOrder(num: bigint): boolean {
374
- return inRange(num, _1n, CURVE.n);
375
- }
376
- // Validates if priv key is valid and converts it to bigint.
377
- // Supports options allowedPrivateKeyLengths and wrapPrivateKey.
378
- function normPrivateKeyToScalar(key: PrivKey): bigint {
379
- const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n: N } = CURVE;
380
- if (lengths && typeof key !== 'bigint') {
381
- if (isBytes(key)) key = bytesToHex(key);
382
- // Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes
383
- if (typeof key !== 'string' || !lengths.includes(key.length))
384
- throw new Error('invalid private key');
385
- key = key.padStart(nByteLength * 2, '0');
386
- }
387
- let num: bigint;
388
- try {
389
- num =
390
- typeof key === 'bigint'
391
- ? key
392
- : bytesToNumberBE(ensureBytes('private key', key, nByteLength));
393
- } catch (error) {
394
- throw new Error(
395
- 'invalid private key, expected hex or ' + nByteLength + ' bytes, got ' + typeof key
396
- );
397
- }
398
- if (wrapPrivateKey) num = mod(num, N); // disabled by default, enabled for BLS
399
- aInRange('private key', num, _1n, N); // num in range [1..N-1]
400
- return num;
571
+ /** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
572
+ function acoord(title: string, n: T, banZero = false) {
573
+ if (!Fp.isValid(n) || (banZero && Fp.is0(n))) throw new Error(`bad point coordinate ${title}`);
574
+ return n;
401
575
  }
402
576
 
403
577
  function aprjpoint(other: unknown) {
@@ -431,21 +605,33 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
431
605
  // (0, 1, 0) aka ZERO is invalid in most contexts.
432
606
  // In BLS, ZERO can be serialized, so we allow it.
433
607
  // (0, 0, 0) is invalid representation of ZERO.
434
- if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return;
608
+ if (curveOpts.allowInfinityPoint && !Fp.is0(p.py)) return;
435
609
  throw new Error('bad point: ZERO');
436
610
  }
437
611
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
438
612
  const { x, y } = p.toAffine();
439
- // Check if x, y are valid field elements
440
- if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not FE');
613
+ if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not field elements');
441
614
  if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
442
615
  if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
443
616
  return true;
444
617
  });
445
618
 
619
+ function finishEndo(
620
+ endoBeta: EndomorphismOpts['beta'],
621
+ k1p: Point,
622
+ k2p: Point,
623
+ k1neg: boolean,
624
+ k2neg: boolean
625
+ ) {
626
+ k2p = new Point(Fp.mul(k2p.px, endoBeta), k2p.py, k2p.pz);
627
+ k1p = negateCt(k1neg, k1p);
628
+ k2p = negateCt(k2neg, k2p);
629
+ return k1p.add(k2p);
630
+ }
631
+
446
632
  /**
447
- * Projective Point works in 3d / projective (homogeneous) coordinates: (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
448
- * Default Point works in 2d / affine coordinates: (x, y)
633
+ * Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
634
+ * Default Point works in 2d / affine coordinates: (x, y).
449
635
  * We're doing calculations in projective, because its operations don't require costly inversion.
450
636
  */
451
637
  class Point implements ProjPointType<T> {
@@ -453,29 +639,29 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
453
639
  static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
454
640
  // zero / infinity / identity point
455
641
  static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
642
+ // fields
643
+ static readonly Fp = Fp;
644
+ static readonly Fn = Fn;
645
+
456
646
  readonly px: T;
457
647
  readonly py: T;
458
648
  readonly pz: T;
459
649
 
650
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
460
651
  constructor(px: T, py: T, pz: T) {
461
- if (px == null || !Fp.isValid(px)) throw new Error('x required');
462
- if (py == null || !Fp.isValid(py) || Fp.is0(py)) throw new Error('y required');
463
- if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
464
- this.px = px;
465
- this.py = py;
466
- this.pz = pz;
652
+ this.px = acoord('x', px);
653
+ this.py = acoord('y', py, true);
654
+ this.pz = acoord('z', pz);
467
655
  Object.freeze(this);
468
656
  }
469
657
 
470
- // Does not validate if the point is on-curve.
471
- // Use fromHex instead, or call assertValidity() later.
658
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
472
659
  static fromAffine(p: AffinePoint<T>): Point {
473
660
  const { x, y } = p || {};
474
661
  if (!p || !Fp.isValid(x) || !Fp.isValid(y)) throw new Error('invalid affine point');
475
662
  if (p instanceof Point) throw new Error('projective point not allowed');
476
- const is0 = (i: T) => Fp.eql(i, Fp.ZERO);
477
- // fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
478
- if (is0(x) && is0(y)) return Point.ZERO;
663
+ // (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
664
+ if (Fp.is0(x) && Fp.is0(y)) return Point.ZERO;
479
665
  return new Point(x, y, Fp.ONE);
480
666
  }
481
667
 
@@ -486,59 +672,67 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
486
672
  return this.toAffine().y;
487
673
  }
488
674
 
489
- /**
490
- * Takes a bunch of Projective Points but executes only one
491
- * inversion on all of them. Inversion is very slow operation,
492
- * so this improves performance massively.
493
- * Optimization: converts a list of projective points to a list of identical points with Z=1.
494
- */
495
675
  static normalizeZ(points: Point[]): Point[] {
496
- const toInv = FpInvertBatch(
497
- Fp,
498
- points.map((p) => p.pz)
499
- );
500
- return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
676
+ return normalizeZ(Point, 'pz', points);
501
677
  }
502
678
 
503
- /**
504
- * Converts hash string or Uint8Array to Point.
505
- * @param hex short/long ECDSA hex
506
- */
679
+ static fromBytes(bytes: Uint8Array): Point {
680
+ abytes(bytes);
681
+ return Point.fromHex(bytes);
682
+ }
683
+
684
+ /** Converts hash string or Uint8Array to Point. */
507
685
  static fromHex(hex: Hex): Point {
508
686
  const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
509
687
  P.assertValidity();
510
688
  return P;
511
689
  }
512
690
 
513
- // Multiplies generator point by privateKey.
691
+ /** Multiplies generator point by privateKey. */
514
692
  static fromPrivateKey(privateKey: PrivKey) {
693
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(
694
+ Fn,
695
+ curveOpts.allowedPrivateKeyLengths,
696
+ curveOpts.wrapPrivateKey
697
+ );
515
698
  return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
516
699
  }
517
700
 
518
- // Multiscalar Multiplication
701
+ /** Multiscalar Multiplication */
519
702
  static msm(points: Point[], scalars: bigint[]): Point {
520
703
  return pippenger(Point, Fn, points, scalars);
521
704
  }
522
705
 
523
- // "Private method", don't use it directly
524
- _setWindowSize(windowSize: number) {
706
+ /**
707
+ *
708
+ * @param windowSize
709
+ * @param isLazy true will defer table computation until the first multiplication
710
+ * @returns
711
+ */
712
+ precompute(windowSize: number = 8, isLazy = true): Point {
525
713
  wnaf.setWindowSize(this, windowSize);
714
+ if (!isLazy) this.multiply(_3n); // random number
715
+ return this;
526
716
  }
527
717
 
528
- // A point on curve is valid if it conforms to equation.
718
+ /** "Private method", don't use it directly */
719
+ _setWindowSize(windowSize: number) {
720
+ this.precompute(windowSize);
721
+ }
722
+
723
+ // TODO: return `this`
724
+ /** A point on curve is valid if it conforms to equation. */
529
725
  assertValidity(): void {
530
726
  assertValidMemo(this);
531
727
  }
532
728
 
533
729
  hasEvenY(): boolean {
534
730
  const { y } = this.toAffine();
535
- if (Fp.isOdd) return !Fp.isOdd(y);
536
- throw new Error("Field doesn't support isOdd");
731
+ if (!Fp.isOdd) throw new Error("Field doesn't support isOdd");
732
+ return !Fp.isOdd(y);
537
733
  }
538
734
 
539
- /**
540
- * Compare one point to another.
541
- */
735
+ /** Compare one point to another. */
542
736
  equals(other: Point): boolean {
543
737
  aprjpoint(other);
544
738
  const { px: X1, py: Y1, pz: Z1 } = this;
@@ -548,9 +742,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
548
742
  return U1 && U2;
549
743
  }
550
744
 
551
- /**
552
- * Flips point to one corresponding to (x, -y) in Affine coordinates.
553
- */
745
+ /** Flips point to one corresponding to (x, -y) in Affine coordinates. */
554
746
  negate(): Point {
555
747
  return new Point(this.px, Fp.neg(this.py), this.pz);
556
748
  }
@@ -656,49 +848,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
656
848
  return this.add(other.negate());
657
849
  }
658
850
 
659
- is0() {
851
+ is0(): boolean {
660
852
  return this.equals(Point.ZERO);
661
853
  }
662
854
 
663
- private wNAF(n: bigint): { p: Point; f: Point } {
664
- return wnaf.wNAFCached(this, n, Point.normalizeZ);
665
- }
666
-
667
- /**
668
- * Non-constant-time multiplication. Uses double-and-add algorithm.
669
- * It's faster, but should only be used when you don't care about
670
- * an exposed private key e.g. sig verification, which works over *public* keys.
671
- */
672
- multiplyUnsafe(sc: bigint): Point {
673
- const { endo, n: N } = CURVE;
674
- aInRange('scalar', sc, _0n, N);
675
- const I = Point.ZERO;
676
- if (sc === _0n) return I;
677
- if (this.is0() || sc === _1n) return this;
678
-
679
- // Case a: no endomorphism. Case b: has precomputes.
680
- if (!endo || wnaf.hasPrecomputes(this))
681
- return wnaf.wNAFCachedUnsafe(this, sc, Point.normalizeZ);
682
-
683
- // Case c: endomorphism
684
- /** See docs for {@link EndomorphismOpts} */
685
- let { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
686
- let k1p = I;
687
- let k2p = I;
688
- let d: Point = this;
689
- while (k1 > _0n || k2 > _0n) {
690
- if (k1 & _1n) k1p = k1p.add(d);
691
- if (k2 & _1n) k2p = k2p.add(d);
692
- d = d.double();
693
- k1 >>= _1n;
694
- k2 >>= _1n;
695
- }
696
- if (k1neg) k1p = k1p.negate();
697
- if (k2neg) k2p = k2p.negate();
698
- k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
699
- return k1p.add(k2p);
700
- }
701
-
702
855
  /**
703
856
  * Constant time multiplication.
704
857
  * Uses wNAF method. Windowed method may be 10% faster,
@@ -709,21 +862,19 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
709
862
  * @returns New point
710
863
  */
711
864
  multiply(scalar: bigint): Point {
712
- const { endo, n: N } = CURVE;
713
- aInRange('scalar', scalar, _1n, N);
865
+ const { endo } = curveOpts;
866
+ if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
714
867
  let point: Point, fake: Point; // Fake point is used to const-time mult
868
+ const mul = (n: bigint) => wnaf.wNAFCached(this, n, Point.normalizeZ);
715
869
  /** See docs for {@link EndomorphismOpts} */
716
870
  if (endo) {
717
871
  const { k1neg, k1, k2neg, k2 } = endo.splitScalar(scalar);
718
- let { p: k1p, f: f1p } = this.wNAF(k1);
719
- let { p: k2p, f: f2p } = this.wNAF(k2);
720
- k1p = wnaf.constTimeNegate(k1neg, k1p);
721
- k2p = wnaf.constTimeNegate(k2neg, k2p);
722
- k2p = new Point(Fp.mul(k2p.px, endo.beta), k2p.py, k2p.pz);
723
- point = k1p.add(k2p);
724
- fake = f1p.add(f2p);
872
+ const { p: k1p, f: k1f } = mul(k1);
873
+ const { p: k2p, f: k2f } = mul(k2);
874
+ fake = k1f.add(k2f);
875
+ point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
725
876
  } else {
726
- const { p, f } = this.wNAF(scalar);
877
+ const { p, f } = mul(scalar);
727
878
  point = p;
728
879
  fake = f;
729
880
  }
@@ -732,60 +883,88 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
732
883
  }
733
884
 
734
885
  /**
735
- * Efficiently calculate `aP + bQ`. Unsafe, can expose private key, if used incorrectly.
736
- * Not using Strauss-Shamir trick: precomputation tables are faster.
737
- * The trick could be useful if both P and Q are not G (not in our case).
738
- * @returns non-zero affine point
886
+ * Non-constant-time multiplication. Uses double-and-add algorithm.
887
+ * It's faster, but should only be used when you don't care about
888
+ * an exposed private key e.g. sig verification, which works over *public* keys.
739
889
  */
890
+ multiplyUnsafe(sc: bigint): Point {
891
+ const { endo } = curveOpts;
892
+ const p = this;
893
+ if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
894
+ if (sc === _0n || p.is0()) return Point.ZERO;
895
+ if (sc === _1n) return p; // fast-path
896
+ if (wnaf.hasPrecomputes(this)) return this.multiply(sc);
897
+ if (endo) {
898
+ const { k1neg, k1, k2neg, k2 } = endo.splitScalar(sc);
899
+ // `wNAFCachedUnsafe` is 30% slower
900
+ const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
901
+ return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
902
+ } else {
903
+ return wnaf.wNAFCachedUnsafe(p, sc);
904
+ }
905
+ }
906
+
740
907
  multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
741
- const G = Point.BASE; // No Strauss-Shamir trick: we have 10% faster G precomputes
742
- const mul = (
743
- P: Point,
744
- a: bigint // Select faster multiply() method
745
- ) => (a === _0n || a === _1n || !P.equals(G) ? P.multiplyUnsafe(a) : P.multiply(a));
746
- const sum = mul(this, a).add(mul(Q, b));
908
+ const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
747
909
  return sum.is0() ? undefined : sum;
748
910
  }
749
911
 
750
- // Converts Projective point to affine (x, y) coordinates.
751
- // Can accept precomputed Z^-1 - for example, from invertBatch.
752
- // (x, y, z) (x=x/z, y=y/z)
753
- toAffine(iz?: T): AffinePoint<T> {
754
- return toAffineMemo(this, iz);
912
+ /**
913
+ * Converts Projective point to affine (x, y) coordinates.
914
+ * @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
915
+ */
916
+ toAffine(invertedZ?: T): AffinePoint<T> {
917
+ return toAffineMemo(this, invertedZ);
755
918
  }
919
+
920
+ /**
921
+ * Checks whether Point is free of torsion elements (is in prime subgroup).
922
+ * Always torsion-free for cofactor=1 curves.
923
+ */
756
924
  isTorsionFree(): boolean {
757
- const { h: cofactor, isTorsionFree } = CURVE;
758
- if (cofactor === _1n) return true; // No subgroups, always torsion-free
925
+ const { isTorsionFree } = curveOpts;
926
+ if (cofactor === _1n) return true;
759
927
  if (isTorsionFree) return isTorsionFree(Point, this);
760
- throw new Error('isTorsionFree() has not been declared for the elliptic curve');
928
+ return wnaf.wNAFCachedUnsafe(this, CURVE_ORDER).is0();
761
929
  }
930
+
762
931
  clearCofactor(): Point {
763
- const { h: cofactor, clearCofactor } = CURVE;
932
+ const { clearCofactor } = curveOpts;
764
933
  if (cofactor === _1n) return this; // Fast-path
765
934
  if (clearCofactor) return clearCofactor(Point, this) as Point;
766
- return this.multiplyUnsafe(CURVE.h);
935
+ return this.multiplyUnsafe(cofactor);
767
936
  }
768
937
 
769
- toRawBytes(isCompressed = true): Uint8Array {
938
+ toBytes(isCompressed = true): Uint8Array {
770
939
  abool('isCompressed', isCompressed);
771
940
  this.assertValidity();
772
941
  return toBytes(Point, this, isCompressed);
773
942
  }
774
943
 
944
+ /** @deprecated use `toBytes` */
945
+ toRawBytes(isCompressed = true): Uint8Array {
946
+ return this.toBytes(isCompressed);
947
+ }
948
+
775
949
  toHex(isCompressed = true): string {
776
- abool('isCompressed', isCompressed);
777
- return bytesToHex(this.toRawBytes(isCompressed));
950
+ return bytesToHex(this.toBytes(isCompressed));
951
+ }
952
+
953
+ toString() {
954
+ return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
778
955
  }
779
956
  }
780
- const { endo, nBitLength } = CURVE;
781
- const wnaf = wNAF(Point, endo ? Math.ceil(nBitLength / 2) : nBitLength);
782
- return {
783
- CURVE,
784
- ProjectivePoint: Point as ProjConstructor<T>,
785
- normPrivateKeyToScalar,
786
- weierstrassEquation,
787
- isWithinCurveOrder,
788
- };
957
+ const bits = Fn.BITS;
958
+ const wnaf = wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
959
+ return Point;
960
+ }
961
+
962
+ // _legacyWeierstrass
963
+ /** @deprecated use `weierstrassN` */
964
+ export function weierstrassPoints<T>(c: CurvePointsTypeWithLength<T>): CurvePointsRes<T> {
965
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
966
+ const Point = weierstrassN(CURVE, curveOpts);
967
+ return _weierstrass_new_output_to_legacy(c, Point);
789
968
  }
790
969
 
791
970
  // Instance
@@ -800,57 +979,44 @@ export interface SignatureType {
800
979
  recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
801
980
  toCompactRawBytes(): Uint8Array;
802
981
  toCompactHex(): string;
803
- toDERRawBytes(isCompressed?: boolean): Uint8Array;
804
- toDERHex(isCompressed?: boolean): string;
982
+ toDERRawBytes(): Uint8Array;
983
+ toDERHex(): string;
984
+ // toBytes(format?: string): Uint8Array;
805
985
  }
806
986
  export type RecoveredSignatureType = SignatureType & {
807
987
  readonly recovery: number;
808
988
  };
809
989
  // Static methods
810
990
  export type SignatureConstructor = {
811
- new (r: bigint, s: bigint): SignatureType;
991
+ new (r: bigint, s: bigint, recovery?: number): SignatureType;
812
992
  fromCompact(hex: Hex): SignatureType;
813
993
  fromDER(hex: Hex): SignatureType;
814
994
  };
815
- type SignatureLike = { r: bigint; s: bigint };
816
-
995
+ export type SignatureLike = { r: bigint; s: bigint };
817
996
  export type PubKey = Hex | ProjPointType<bigint>;
818
997
 
819
998
  export type CurveType = BasicWCurve<bigint> & {
820
999
  hash: CHash; // CHash not FHash because we need outputLen for DRBG
821
- hmac: HmacFnSync;
822
- randomBytes: (bytesLength?: number) => Uint8Array;
1000
+ hmac?: HmacFnSync;
1001
+ randomBytes?: (bytesLength?: number) => Uint8Array;
823
1002
  lowS?: boolean;
824
1003
  bits2int?: (bytes: Uint8Array) => bigint;
825
1004
  bits2int_modN?: (bytes: Uint8Array) => bigint;
826
1005
  };
827
1006
 
828
- function validateOpts(
829
- curve: CurveType
830
- ): Readonly<CurveType & { nByteLength: number; nBitLength: number }> {
831
- const opts = validateBasic(curve);
832
- validateObject(
833
- opts,
834
- {
835
- hash: 'hash',
836
- hmac: 'function',
837
- randomBytes: 'function',
838
- },
839
- {
840
- bits2int: 'function',
841
- bits2int_modN: 'function',
842
- lowS: 'boolean',
843
- }
844
- );
845
- return Object.freeze({ lowS: true, ...opts } as const);
1007
+ // Points start with byte 0x02 when y is even; otherwise 0x03
1008
+ function pprefix(hasEvenY: boolean): Uint8Array {
1009
+ return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
846
1010
  }
847
1011
 
848
1012
  export type CurveFn = {
849
- CURVE: ReturnType<typeof validateOpts>;
1013
+ CURVE: CurvePointsType<bigint>;
850
1014
  getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
851
1015
  getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
852
1016
  sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
853
1017
  verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
1018
+ Point: ProjConstructor<bigint>;
1019
+ /** @deprecated use `Point` */
854
1020
  ProjectivePoint: ProjConstructor<bigint>;
855
1021
  Signature: SignatureConstructor;
856
1022
  utils: {
@@ -861,78 +1027,30 @@ export type CurveFn = {
861
1027
  };
862
1028
  };
863
1029
 
864
- /**
865
- * Creates short weierstrass curve and ECDSA signature methods for it.
866
- * @example
867
- * import { Field } from '@noble/curves/abstract/modular';
868
- * // Before that, define BigInt-s: a, b, p, n, Gx, Gy
869
- * const curve = weierstrass({ a, b, Fp: Field(p), n, Gx, Gy, h: 1n })
870
- */
871
- export function weierstrass(curveDef: CurveType): CurveFn {
872
- const CURVE = validateOpts(curveDef) as ReturnType<typeof validateOpts>;
873
- const { Fp, n: CURVE_ORDER, nByteLength, nBitLength } = CURVE;
874
- const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32
875
- const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32
876
-
877
- function modN(a: bigint) {
878
- return mod(a, CURVE_ORDER);
879
- }
880
- function invN(a: bigint) {
881
- return invert(a, CURVE_ORDER);
882
- }
1030
+ export function ecdsa(
1031
+ Point: ProjConstructor<bigint>,
1032
+ ecdsaOpts: ECDSAOpts,
1033
+ curveOpts: WeierstrassExtraOpts<bigint> = {}
1034
+ ): ECDSA {
1035
+ _validateObject(
1036
+ ecdsaOpts,
1037
+ { hash: 'function' },
1038
+ {
1039
+ hmac: 'function',
1040
+ lowS: 'boolean',
1041
+ randomBytes: 'function',
1042
+ bits2int: 'function',
1043
+ bits2int_modN: 'function',
1044
+ }
1045
+ );
883
1046
 
884
- const {
885
- ProjectivePoint: Point,
886
- normPrivateKeyToScalar,
887
- weierstrassEquation,
888
- isWithinCurveOrder,
889
- } = weierstrassPoints({
890
- ...CURVE,
891
- toBytes(_c, point, isCompressed: boolean): Uint8Array {
892
- const a = point.toAffine();
893
- const x = Fp.toBytes(a.x);
894
- const cat = concatBytes;
895
- abool('isCompressed', isCompressed);
896
- if (isCompressed) {
897
- return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
898
- } else {
899
- return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
900
- }
901
- },
902
- fromBytes(bytes: Uint8Array) {
903
- const len = bytes.length;
904
- const head = bytes[0];
905
- const tail = bytes.subarray(1);
906
- // this.assertValidity() is done inside of fromHex
907
- if (len === compressedLen && (head === 0x02 || head === 0x03)) {
908
- const x = bytesToNumberBE(tail);
909
- if (!inRange(x, _1n, Fp.ORDER)) throw new Error('Point is not on curve');
910
- const y2 = weierstrassEquation(x); // y² = x³ + ax + b
911
- let y: bigint;
912
- try {
913
- y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
914
- } catch (sqrtError) {
915
- const suffix = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
916
- throw new Error('Point is not on curve' + suffix);
917
- }
918
- const isYOdd = (y & _1n) === _1n;
919
- // ECDSA
920
- const isHeadOdd = (head & 1) === 1;
921
- if (isHeadOdd !== isYOdd) y = Fp.neg(y);
922
- return { x, y };
923
- } else if (len === uncompressedLen && head === 0x04) {
924
- const x = Fp.fromBytes(tail.subarray(0, Fp.BYTES));
925
- const y = Fp.fromBytes(tail.subarray(Fp.BYTES, 2 * Fp.BYTES));
926
- return { x, y };
927
- } else {
928
- const cl = compressedLen;
929
- const ul = uncompressedLen;
930
- throw new Error(
931
- 'invalid Point, expected length of ' + cl + ', or uncompressed ' + ul + ', got ' + len
932
- );
933
- }
934
- },
935
- });
1047
+ const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
1048
+ const hmac_: HmacFnSync =
1049
+ ecdsaOpts.hmac ||
1050
+ (((key, ...msgs) => hmac(ecdsaOpts.hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1051
+
1052
+ const { Fp, Fn } = Point;
1053
+ const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
936
1054
 
937
1055
  function isBiggerThanHalfOrder(number: bigint) {
938
1056
  const HALF = CURVE_ORDER >> _1n;
@@ -940,10 +1058,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
940
1058
  }
941
1059
 
942
1060
  function normalizeS(s: bigint) {
943
- return isBiggerThanHalfOrder(s) ? modN(-s) : s;
1061
+ return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
1062
+ }
1063
+ function aValidRS(title: string, num: bigint) {
1064
+ if (!Fn.isValidNot0(num))
1065
+ throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
944
1066
  }
945
- // slice bytes num
946
- const slcNum = (b: Uint8Array, from: number, to: number) => bytesToNumberBE(b.slice(from, to));
947
1067
 
948
1068
  /**
949
1069
  * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
@@ -953,8 +1073,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
953
1073
  readonly s: bigint;
954
1074
  readonly recovery?: number;
955
1075
  constructor(r: bigint, s: bigint, recovery?: number) {
956
- aInRange('r', r, _1n, CURVE_ORDER); // r in [1..N]
957
- aInRange('s', s, _1n, CURVE_ORDER); // s in [1..N]
1076
+ aValidRS('r', r); // r in [1..N-1]
1077
+ aValidRS('s', s); // s in [1..N-1]
958
1078
  this.r = r;
959
1079
  this.s = s;
960
1080
  if (recovery != null) this.recovery = recovery;
@@ -963,9 +1083,9 @@ export function weierstrass(curveDef: CurveType): CurveFn {
963
1083
 
964
1084
  // pair (bytes of r, bytes of s)
965
1085
  static fromCompact(hex: Hex) {
966
- const l = nByteLength;
967
- hex = ensureBytes('compactSignature', hex, l * 2);
968
- return new Signature(slcNum(hex, 0, l), slcNum(hex, l, 2 * l));
1086
+ const L = Fn.BYTES;
1087
+ const b = ensureBytes('compactSignature', hex, L * 2);
1088
+ return new Signature(Fn.fromBytes(b.subarray(0, L)), Fn.fromBytes(b.subarray(L, L * 2)));
969
1089
  }
970
1090
 
971
1091
  // DER encoded ECDSA signature
@@ -985,19 +1105,34 @@ export function weierstrass(curveDef: CurveType): CurveFn {
985
1105
  return new Signature(this.r, this.s, recovery) as RecoveredSignature;
986
1106
  }
987
1107
 
1108
+ // ProjPointType<bigint>
988
1109
  recoverPublicKey(msgHash: Hex): typeof Point.BASE {
1110
+ const FIELD_ORDER = Fp.ORDER;
989
1111
  const { r, s, recovery: rec } = this;
990
- const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
991
1112
  if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
992
- const radj = rec === 2 || rec === 3 ? r + CURVE.n : r;
993
- if (radj >= Fp.ORDER) throw new Error('recovery id 2 or 3 invalid');
994
- const prefix = (rec & 1) === 0 ? '02' : '03';
995
- const R = Point.fromHex(prefix + numToSizedHex(radj, Fp.BYTES));
996
- const ir = invN(radj); // r^-1
997
- const u1 = modN(-h * ir); // -hr^-1
998
- const u2 = modN(s * ir); // sr^-1
999
- const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
1000
- if (!Q) throw new Error('point at infinify'); // unsafe is fine: no priv data leaked
1113
+
1114
+ // ECDSA recovery is hard for cofactor > 1 curves.
1115
+ // In sign, `r = q.x mod n`, and here we recover q.x from r.
1116
+ // While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
1117
+ // However, for cofactor>1, r+n may not get q.x:
1118
+ // r+n*i would need to be done instead where i is unknown.
1119
+ // To easily get i, we either need to:
1120
+ // a. increase amount of valid recid values (4, 5...); OR
1121
+ // b. prohibit non-prime-order signatures (recid > 1).
1122
+ const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
1123
+ if (hasCofactor && rec > 1) throw new Error('recovery id is ambiguous for h>1 curve');
1124
+
1125
+ const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
1126
+ if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
1127
+ const x = Fp.toBytes(radj);
1128
+ const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
1129
+ const ir = Fn.inv(radj); // r^-1
1130
+ const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
1131
+ const u1 = Fn.create(-h * ir); // -hr^-1
1132
+ const u2 = Fn.create(s * ir); // sr^-1
1133
+ // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
1134
+ const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
1135
+ if (Q.is0()) throw new Error('point at infinify');
1001
1136
  Q.assertValidity();
1002
1137
  return Q;
1003
1138
  }
@@ -1008,28 +1143,39 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1008
1143
  }
1009
1144
 
1010
1145
  normalizeS() {
1011
- return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this;
1146
+ return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1147
+ }
1148
+
1149
+ toBytes(format: 'compact' | 'der') {
1150
+ if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
1151
+ if (format === 'der') return hexToBytes(DER.hexFromSig(this));
1152
+ throw new Error('invalid format');
1012
1153
  }
1013
1154
 
1014
1155
  // DER-encoded
1015
1156
  toDERRawBytes() {
1016
- return hexToBytes(this.toDERHex());
1157
+ return this.toBytes('der');
1017
1158
  }
1018
1159
  toDERHex() {
1019
- return DER.hexFromSig(this);
1160
+ return bytesToHex(this.toBytes('der'));
1020
1161
  }
1021
1162
 
1022
1163
  // padded bytes of r, then padded bytes of s
1023
1164
  toCompactRawBytes() {
1024
- return hexToBytes(this.toCompactHex());
1165
+ return this.toBytes('compact');
1025
1166
  }
1026
1167
  toCompactHex() {
1027
- const l = nByteLength;
1028
- return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
1168
+ return bytesToHex(this.toBytes('compact'));
1029
1169
  }
1030
1170
  }
1031
1171
  type RecoveredSignature = Signature & { recovery: number };
1032
1172
 
1173
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(
1174
+ Fn,
1175
+ curveOpts.allowedPrivateKeyLengths,
1176
+ curveOpts.wrapPrivateKey
1177
+ );
1178
+
1033
1179
  const utils = {
1034
1180
  isValidPrivateKey(privateKey: PrivKey) {
1035
1181
  try {
@@ -1046,22 +1192,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1046
1192
  * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1047
1193
  */
1048
1194
  randomPrivateKey: (): Uint8Array => {
1049
- const length = getMinHashLength(CURVE.n);
1050
- return mapHashToField(CURVE.randomBytes(length), CURVE.n);
1195
+ const n = CURVE_ORDER;
1196
+ return mapHashToField(randomBytes_(getMinHashLength(n)), n);
1051
1197
  },
1052
1198
 
1053
- /**
1054
- * Creates precompute table for an arbitrary EC point. Makes point "cached".
1055
- * Allows to massively speed-up `point.multiply(scalar)`.
1056
- * @returns cached point
1057
- * @example
1058
- * const fast = utils.precompute(8, ProjectivePoint.fromHex(someonesPubKey));
1059
- * fast.multiply(privKey); // much faster ECDH now
1060
- */
1061
1199
  precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
1062
- point._setWindowSize(windowSize);
1063
- point.multiply(BigInt(3)); // 3 is arbitrary, just need any number here
1064
- return point;
1200
+ return point.precompute(windowSize, false);
1065
1201
  },
1066
1202
  };
1067
1203
 
@@ -1072,7 +1208,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1072
1208
  * @returns Public key, full when isCompressed=false; short when isCompressed=true
1073
1209
  */
1074
1210
  function getPublicKey(privateKey: PrivKey, isCompressed = true): Uint8Array {
1075
- return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
1211
+ return Point.fromPrivateKey(privateKey).toBytes(isCompressed);
1076
1212
  }
1077
1213
 
1078
1214
  /**
@@ -1082,14 +1218,14 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1082
1218
  if (typeof item === 'bigint') return false;
1083
1219
  if (item instanceof Point) return true;
1084
1220
  const arr = ensureBytes('key', item);
1085
- const len = arr.length;
1086
- const fpl = Fp.BYTES;
1087
- const compLen = fpl + 1; // e.g. 33 for 32
1088
- const uncompLen = 2 * fpl + 1; // e.g. 65 for 32
1089
- if (CURVE.allowedPrivateKeyLengths || nByteLength === compLen) {
1221
+ const length = arr.length;
1222
+ const L = Fp.BYTES;
1223
+ const LC = L + 1; // e.g. 33 for 32
1224
+ const LU = 2 * L + 1; // e.g. 65 for 32
1225
+ if (curveOpts.allowedPrivateKeyLengths || Fn.BYTES === LC) {
1090
1226
  return undefined;
1091
1227
  } else {
1092
- return len === compLen || len === uncompLen;
1228
+ return length === LC || length === LU;
1093
1229
  }
1094
1230
  }
1095
1231
 
@@ -1107,7 +1243,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1107
1243
  if (isProbPub(privateA) === true) throw new Error('first arg must be private key');
1108
1244
  if (isProbPub(publicB) === false) throw new Error('second arg must be public key');
1109
1245
  const b = Point.fromHex(publicB); // check for being on-curve
1110
- return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
1246
+ return b.multiply(normPrivateKeyToScalar(privateA)).toBytes(isCompressed);
1111
1247
  }
1112
1248
 
1113
1249
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
@@ -1115,30 +1251,30 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1115
1251
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
1116
1252
  // int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
1117
1253
  const bits2int =
1118
- CURVE.bits2int ||
1254
+ ecdsaOpts.bits2int ||
1119
1255
  function (bytes: Uint8Array): bigint {
1120
1256
  // Our custom check "just in case", for protection against DoS
1121
1257
  if (bytes.length > 8192) throw new Error('input is too large');
1122
1258
  // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
1123
1259
  // for some cases, since bytes.length * 8 is not actual bitLength.
1124
1260
  const num = bytesToNumberBE(bytes); // check for == u8 done here
1125
- const delta = bytes.length * 8 - nBitLength; // truncate to nBitLength leftmost bits
1261
+ const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
1126
1262
  return delta > 0 ? num >> BigInt(delta) : num;
1127
1263
  };
1128
1264
  const bits2int_modN =
1129
- CURVE.bits2int_modN ||
1265
+ ecdsaOpts.bits2int_modN ||
1130
1266
  function (bytes: Uint8Array): bigint {
1131
- return modN(bits2int(bytes)); // can't use bytesToNumberBE here
1267
+ return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
1132
1268
  };
1133
1269
  // NOTE: pads output with zero as per spec
1134
- const ORDER_MASK = bitMask(nBitLength);
1270
+ const ORDER_MASK = bitMask(fnBits);
1135
1271
  /**
1136
1272
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
1137
1273
  */
1138
1274
  function int2octets(num: bigint): Uint8Array {
1139
- aInRange('num < 2^' + nBitLength, num, _0n, ORDER_MASK);
1140
- // works with order, can have different size than numToField!
1141
- return numberToBytesBE(num, nByteLength);
1275
+ // IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
1276
+ aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
1277
+ return Fn.toBytes(num);
1142
1278
  }
1143
1279
 
1144
1280
  // Steps A, D of RFC6979 3.2
@@ -1149,7 +1285,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1149
1285
  function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
1150
1286
  if (['recovered', 'canonical'].some((k) => k in opts))
1151
1287
  throw new Error('sign() legacy options not supported');
1152
- const { hash, randomBytes } = CURVE;
1288
+ const { hash } = ecdsaOpts;
1153
1289
  let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
1154
1290
  if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1155
1291
  msgHash = ensureBytes('msgHash', msgHash);
@@ -1157,7 +1293,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1157
1293
  if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
1158
1294
 
1159
1295
  // We can't later call bits2octets, since nested bits2int is broken for curves
1160
- // with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1296
+ // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1161
1297
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1162
1298
  const h1int = bits2int_modN(msgHash);
1163
1299
  const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
@@ -1165,24 +1301,25 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1165
1301
  // extraEntropy. RFC6979 3.6: additional k' (optional).
1166
1302
  if (ent != null && ent !== false) {
1167
1303
  // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
1168
- const e = ent === true ? randomBytes(Fp.BYTES) : ent; // generate random bytes OR pass as-is
1304
+ const e = ent === true ? randomBytes_(Fp.BYTES) : ent; // generate random bytes OR pass as-is
1169
1305
  seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
1170
1306
  }
1171
1307
  const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
1172
1308
  const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
1173
1309
  // Converts signature params into point w r/s, checks result for validity.
1310
+ // Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
1311
+ // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
1312
+ // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
1174
1313
  function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
1175
1314
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
1315
+ // Important: all mod() calls here must be done over N
1176
1316
  const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
1177
- if (!isWithinCurveOrder(k)) return; // Important: all mod() calls here must be done over N
1178
- const ik = invN(k); // k^-1 mod n
1317
+ if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
1318
+ const ik = Fn.inv(k); // k^-1 mod n
1179
1319
  const q = Point.BASE.multiply(k).toAffine(); // q = Gk
1180
- const r = modN(q.x); // r = q.x mod n
1320
+ const r = Fn.create(q.x); // r = q.x mod n
1181
1321
  if (r === _0n) return;
1182
- // Can use scalar blinding b^-1(bm + bdr) where b [1,q−1] according to
1183
- // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
1184
- // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
1185
- const s = modN(ik * modN(m + r * d)); // Not using blinding here
1322
+ const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
1186
1323
  if (s === _0n) return;
1187
1324
  let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
1188
1325
  let normS = s;
@@ -1194,8 +1331,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1194
1331
  }
1195
1332
  return { seed, k2sig };
1196
1333
  }
1197
- const defaultSigOpts: SignOpts = { lowS: CURVE.lowS, prehash: false };
1198
- const defaultVerOpts: VerOpts = { lowS: CURVE.lowS, prehash: false };
1334
+ const defaultSigOpts: SignOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1335
+ const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1199
1336
 
1200
1337
  /**
1201
1338
  * Signs message hash with a private key.
@@ -1212,14 +1349,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1212
1349
  */
1213
1350
  function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
1214
1351
  const { seed, k2sig } = prepSig(msgHash, privKey, opts); // Steps A, D of RFC6979 3.2.
1215
- const C = CURVE;
1216
- const drbg = createHmacDrbg<RecoveredSignature>(C.hash.outputLen, C.nByteLength, C.hmac);
1352
+ const drbg = createHmacDrbg<RecoveredSignature>(ecdsaOpts.hash.outputLen, Fn.BYTES, hmac_);
1217
1353
  return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1218
1354
  }
1219
1355
 
1220
1356
  // Enable precomputes. Slows down first publicKey computation by 20ms.
1221
- Point.BASE._setWindowSize(8);
1222
- // utils.precompute(8, ProjectivePoint.BASE)
1357
+ Point.BASE.precompute(8);
1223
1358
 
1224
1359
  /**
1225
1360
  * Verifies a signature against message hash and public key.
@@ -1243,13 +1378,16 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1243
1378
  const sg = signature;
1244
1379
  msgHash = ensureBytes('msgHash', msgHash);
1245
1380
  publicKey = ensureBytes('publicKey', publicKey);
1246
- const { lowS, prehash, format } = opts;
1247
1381
 
1248
- // Verify opts, deduce signature format
1382
+ // Verify opts
1249
1383
  validateSigVerOpts(opts);
1384
+ const { lowS, prehash, format } = opts;
1385
+
1386
+ // TODO: remove
1250
1387
  if ('strict' in opts) throw new Error('options.strict was renamed to lowS');
1251
- if (format !== undefined && format !== 'compact' && format !== 'der')
1252
- throw new Error('format must be compact or der');
1388
+
1389
+ if (format !== undefined && !['compact', 'der', 'js'].includes(format))
1390
+ throw new Error('format must be "compact", "der" or "js"');
1253
1391
  const isHex = typeof sg === 'string' || isBytes(sg);
1254
1392
  const isObj =
1255
1393
  !isHex &&
@@ -1260,14 +1398,31 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1260
1398
  typeof sg.s === 'bigint';
1261
1399
  if (!isHex && !isObj)
1262
1400
  throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1263
-
1264
1401
  let _sig: Signature | undefined = undefined;
1265
1402
  let P: ProjPointType<bigint>;
1403
+
1404
+ // deduce signature format
1266
1405
  try {
1267
- if (isObj) _sig = new Signature(sg.r, sg.s);
1406
+ // if (format === 'js') {
1407
+ // if (sg != null && !isBytes(sg)) _sig = new Signature(sg.r, sg.s);
1408
+ // } else if (format === 'compact') {
1409
+ // _sig = Signature.fromCompact(sg);
1410
+ // } else if (format === 'der') {
1411
+ // _sig = Signature.fromDER(sg);
1412
+ // } else {
1413
+ // throw new Error('invalid format');
1414
+ // }
1415
+ if (isObj) {
1416
+ if (format === undefined || format === 'js') {
1417
+ _sig = new Signature(sg.r, sg.s);
1418
+ } else {
1419
+ throw new Error('invalid format');
1420
+ }
1421
+ }
1268
1422
  if (isHex) {
1269
- // Signature can be represented in 2 ways: compact (2*nByteLength) & DER (variable-length).
1270
- // Since DER can also be 2*nByteLength bytes, we check for it first.
1423
+ // TODO: remove this malleable check
1424
+ // Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
1425
+ // Since DER can also be 2*Fn.BYTES bytes, we check for it first.
1271
1426
  try {
1272
1427
  if (format !== 'compact') _sig = Signature.fromDER(sg);
1273
1428
  } catch (derError) {
@@ -1281,27 +1436,118 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1281
1436
  }
1282
1437
  if (!_sig) return false;
1283
1438
  if (lowS && _sig.hasHighS()) return false;
1284
- if (prehash) msgHash = CURVE.hash(msgHash);
1439
+ // todo: optional.hash => hash
1440
+ if (prehash) msgHash = ecdsaOpts.hash(msgHash);
1285
1441
  const { r, s } = _sig;
1286
1442
  const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1287
- const is = invN(s); // s^-1
1288
- const u1 = modN(h * is); // u1 = hs^-1 mod n
1289
- const u2 = modN(r * is); // u2 = rs^-1 mod n
1290
- const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine(); // R = u1⋅G + u2⋅P
1291
- if (!R) return false;
1292
- const v = modN(R.x);
1443
+ const is = Fn.inv(s); // s^-1
1444
+ const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1445
+ const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1446
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1447
+ if (R.is0()) return false;
1448
+ const v = Fn.create(R.x); // v = r.x mod n
1293
1449
  return v === r;
1294
1450
  }
1295
- return {
1296
- CURVE,
1451
+ // TODO: clarify API for cloning .clone({hash: sha512}) ? .createWith({hash: sha512})?
1452
+ // const clone = (hash: CHash): ECDSA => ecdsa(Point, { ...ecdsaOpts, ...getHash(hash) }, curveOpts);
1453
+ return Object.freeze({
1297
1454
  getPublicKey,
1298
1455
  getSharedSecret,
1299
1456
  sign,
1300
1457
  verify,
1301
- ProjectivePoint: Point,
1302
- Signature,
1303
1458
  utils,
1459
+ Point,
1460
+ Signature,
1461
+ });
1462
+ }
1463
+
1464
+ export type WsPointComposed<T> = {
1465
+ CURVE: WeierstrassOpts<T>;
1466
+ curveOpts: WeierstrassExtraOpts<T>;
1467
+ };
1468
+ export type WsComposed = {
1469
+ CURVE: WeierstrassOpts<bigint>;
1470
+ curveOpts: WeierstrassExtraOpts<bigint>;
1471
+ ecdsaOpts: ECDSAOpts;
1472
+ };
1473
+ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
1474
+ const CURVE: WeierstrassOpts<T> = {
1475
+ a: c.a,
1476
+ b: c.b,
1477
+ p: c.Fp.ORDER,
1478
+ n: c.n,
1479
+ h: c.h,
1480
+ Gx: c.Gx,
1481
+ Gy: c.Gy,
1304
1482
  };
1483
+ const Fp = c.Fp;
1484
+ const Fn = Field(CURVE.n, c.nBitLength);
1485
+ const curveOpts: WeierstrassExtraOpts<T> = {
1486
+ Fp,
1487
+ Fn,
1488
+ allowedPrivateKeyLengths: c.allowedPrivateKeyLengths,
1489
+ allowInfinityPoint: c.allowInfinityPoint,
1490
+ endo: c.endo,
1491
+ wrapPrivateKey: c.wrapPrivateKey,
1492
+ isTorsionFree: c.isTorsionFree,
1493
+ clearCofactor: c.clearCofactor,
1494
+ fromBytes: c.fromBytes,
1495
+ toBytes: c.toBytes,
1496
+ };
1497
+ return { CURVE, curveOpts };
1498
+ }
1499
+ function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
1500
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1501
+ const ecdsaOpts: ECDSAOpts = {
1502
+ hash: c.hash,
1503
+ hmac: c.hmac,
1504
+ randomBytes: c.randomBytes,
1505
+ lowS: c.lowS,
1506
+ bits2int: c.bits2int,
1507
+ bits2int_modN: c.bits2int_modN,
1508
+ };
1509
+ return { CURVE, curveOpts, ecdsaOpts };
1510
+ }
1511
+ function _weierstrass_new_output_to_legacy<T>(
1512
+ c: CurvePointsType<T>,
1513
+ Point: ProjConstructor<T>
1514
+ ): CurvePointsRes<T> {
1515
+ const { Fp, Fn } = Point;
1516
+ // TODO: remove
1517
+ function isWithinCurveOrder(num: bigint): boolean {
1518
+ return inRange(num, _1n, Fn.ORDER);
1519
+ }
1520
+ const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
1521
+ const normPrivateKeyToScalar = _legacyHelperNormPriv(
1522
+ Fn,
1523
+ c.allowedPrivateKeyLengths,
1524
+ c.wrapPrivateKey
1525
+ );
1526
+ return Object.assign(
1527
+ {},
1528
+ {
1529
+ CURVE: c,
1530
+ Point: Point,
1531
+ ProjectivePoint: Point,
1532
+ normPrivateKeyToScalar,
1533
+ weierstrassEquation,
1534
+ isWithinCurveOrder,
1535
+ }
1536
+ );
1537
+ }
1538
+ function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
1539
+ return Object.assign({}, ecdsa, {
1540
+ ProjectivePoint: ecdsa.Point,
1541
+ CURVE: c,
1542
+ });
1543
+ }
1544
+
1545
+ // _ecdsa_legacy
1546
+ export function weierstrass(c: CurveType): CurveFn {
1547
+ const { CURVE, curveOpts, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
1548
+ const Point = weierstrassN(CURVE, curveOpts);
1549
+ const signs = ecdsa(Point, ecdsaOpts, curveOpts);
1550
+ return _ecdsa_new_output_to_legacy(c, signs);
1305
1551
  }
1306
1552
 
1307
1553
  /**
@@ -1397,30 +1643,31 @@ export function mapToCurveSimpleSWU<T>(
1397
1643
  }
1398
1644
  ): (u: T) => { x: T; y: T } {
1399
1645
  validateField(Fp);
1400
- if (!Fp.isValid(opts.A) || !Fp.isValid(opts.B) || !Fp.isValid(opts.Z))
1646
+ const { A, B, Z } = opts;
1647
+ if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
1401
1648
  throw new Error('mapToCurveSimpleSWU: invalid opts');
1402
- const sqrtRatio = SWUFpSqrtRatio(Fp, opts.Z);
1403
- if (!Fp.isOdd) throw new Error('Fp.isOdd is not implemented!');
1649
+ const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
1650
+ if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
1404
1651
  // Input: u, an element of F.
1405
1652
  // Output: (x, y), a point on E.
1406
1653
  return (u: T): { x: T; y: T } => {
1407
1654
  // prettier-ignore
1408
1655
  let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
1409
1656
  tv1 = Fp.sqr(u); // 1. tv1 = u^2
1410
- tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
1657
+ tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
1411
1658
  tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
1412
1659
  tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
1413
1660
  tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
1414
- tv3 = Fp.mul(tv3, opts.B); // 6. tv3 = B * tv3
1415
- tv4 = Fp.cmov(opts.Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1416
- tv4 = Fp.mul(tv4, opts.A); // 8. tv4 = A * tv4
1661
+ tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
1662
+ tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1663
+ tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
1417
1664
  tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
1418
1665
  tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
1419
- tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
1666
+ tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
1420
1667
  tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
1421
1668
  tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
1422
1669
  tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
1423
- tv5 = Fp.mul(tv6, opts.B); // 15. tv5 = B * tv6
1670
+ tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
1424
1671
  tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
1425
1672
  x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
1426
1673
  const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)