@noble/curves 1.9.1 → 1.9.3

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 (223) hide show
  1. package/README.md +238 -227
  2. package/_shortw_utils.d.ts +8 -5
  3. package/_shortw_utils.d.ts.map +1 -1
  4. package/_shortw_utils.js +3 -8
  5. package/_shortw_utils.js.map +1 -1
  6. package/abstract/bls.d.ts +123 -62
  7. package/abstract/bls.d.ts.map +1 -1
  8. package/abstract/bls.js +219 -163
  9. package/abstract/bls.js.map +1 -1
  10. package/abstract/curve.d.ts +142 -21
  11. package/abstract/curve.d.ts.map +1 -1
  12. package/abstract/curve.js +224 -143
  13. package/abstract/curve.js.map +1 -1
  14. package/abstract/edwards.d.ts +190 -49
  15. package/abstract/edwards.d.ts.map +1 -1
  16. package/abstract/edwards.js +322 -136
  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 +31 -13
  23. package/abstract/hash-to-curve.d.ts.map +1 -1
  24. package/abstract/hash-to-curve.js +34 -19
  25. package/abstract/hash-to-curve.js.map +1 -1
  26. package/abstract/modular.d.ts +31 -13
  27. package/abstract/modular.d.ts.map +1 -1
  28. package/abstract/modular.js +125 -52
  29. package/abstract/modular.js.map +1 -1
  30. package/abstract/montgomery.d.ts +18 -5
  31. package/abstract/montgomery.d.ts.map +1 -1
  32. package/abstract/montgomery.js +23 -6
  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 +23 -49
  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 +206 -124
  47. package/abstract/weierstrass.d.ts.map +1 -1
  48. package/abstract/weierstrass.js +747 -604
  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 +55 -66
  59. package/ed25519.d.ts.map +1 -1
  60. package/ed25519.js +172 -186
  61. package/ed25519.js.map +1 -1
  62. package/ed448.d.ts +60 -57
  63. package/ed448.d.ts.map +1 -1
  64. package/ed448.js +172 -166
  65. package/ed448.js.map +1 -1
  66. package/esm/_shortw_utils.d.ts +8 -5
  67. package/esm/_shortw_utils.d.ts.map +1 -1
  68. package/esm/_shortw_utils.js +3 -8
  69. package/esm/_shortw_utils.js.map +1 -1
  70. package/esm/abstract/bls.d.ts +123 -62
  71. package/esm/abstract/bls.d.ts.map +1 -1
  72. package/esm/abstract/bls.js +220 -164
  73. package/esm/abstract/bls.js.map +1 -1
  74. package/esm/abstract/curve.d.ts +142 -21
  75. package/esm/abstract/curve.d.ts.map +1 -1
  76. package/esm/abstract/curve.js +219 -143
  77. package/esm/abstract/curve.js.map +1 -1
  78. package/esm/abstract/edwards.d.ts +190 -49
  79. package/esm/abstract/edwards.d.ts.map +1 -1
  80. package/esm/abstract/edwards.js +320 -138
  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 +31 -13
  87. package/esm/abstract/hash-to-curve.d.ts.map +1 -1
  88. package/esm/abstract/hash-to-curve.js +33 -19
  89. package/esm/abstract/hash-to-curve.js.map +1 -1
  90. package/esm/abstract/modular.d.ts +31 -13
  91. package/esm/abstract/modular.d.ts.map +1 -1
  92. package/esm/abstract/modular.js +124 -51
  93. package/esm/abstract/modular.js.map +1 -1
  94. package/esm/abstract/montgomery.d.ts +18 -5
  95. package/esm/abstract/montgomery.d.ts.map +1 -1
  96. package/esm/abstract/montgomery.js +23 -6
  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 +23 -49
  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 +206 -124
  111. package/esm/abstract/weierstrass.d.ts.map +1 -1
  112. package/esm/abstract/weierstrass.js +743 -605
  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 +55 -66
  123. package/esm/ed25519.d.ts.map +1 -1
  124. package/esm/ed25519.js +170 -183
  125. package/esm/ed25519.js.map +1 -1
  126. package/esm/ed448.d.ts +60 -57
  127. package/esm/ed448.d.ts.map +1 -1
  128. package/esm/ed448.js +169 -162
  129. package/esm/ed448.js.map +1 -1
  130. package/esm/index.js +7 -9
  131. package/esm/index.js.map +1 -1
  132. package/esm/jubjub.d.ts +3 -3
  133. package/esm/jubjub.d.ts.map +1 -1
  134. package/esm/jubjub.js +3 -3
  135. package/esm/jubjub.js.map +1 -1
  136. package/esm/misc.d.ts +3 -5
  137. package/esm/misc.d.ts.map +1 -1
  138. package/esm/misc.js +31 -29
  139. package/esm/misc.js.map +1 -1
  140. package/esm/nist.d.ts +7 -22
  141. package/esm/nist.d.ts.map +1 -1
  142. package/esm/nist.js +106 -101
  143. package/esm/nist.js.map +1 -1
  144. package/esm/p256.d.ts +7 -3
  145. package/esm/p256.d.ts.map +1 -1
  146. package/esm/p256.js +4 -0
  147. package/esm/p256.js.map +1 -1
  148. package/esm/p384.d.ts +7 -4
  149. package/esm/p384.d.ts.map +1 -1
  150. package/esm/p384.js +4 -1
  151. package/esm/p384.js.map +1 -1
  152. package/esm/p521.d.ts +7 -3
  153. package/esm/p521.d.ts.map +1 -1
  154. package/esm/p521.js +4 -0
  155. package/esm/p521.js.map +1 -1
  156. package/esm/secp256k1.d.ts +38 -21
  157. package/esm/secp256k1.d.ts.map +1 -1
  158. package/esm/secp256k1.js +112 -104
  159. package/esm/secp256k1.js.map +1 -1
  160. package/esm/utils.d.ts +96 -0
  161. package/esm/utils.d.ts.map +1 -0
  162. package/esm/utils.js +279 -0
  163. package/esm/utils.js.map +1 -0
  164. package/index.js +7 -9
  165. package/index.js.map +1 -1
  166. package/jubjub.d.ts +3 -3
  167. package/jubjub.d.ts.map +1 -1
  168. package/jubjub.js +3 -3
  169. package/jubjub.js.map +1 -1
  170. package/misc.d.ts +3 -5
  171. package/misc.d.ts.map +1 -1
  172. package/misc.js +35 -33
  173. package/misc.js.map +1 -1
  174. package/nist.d.ts +7 -22
  175. package/nist.d.ts.map +1 -1
  176. package/nist.js +106 -101
  177. package/nist.js.map +1 -1
  178. package/p256.d.ts +7 -3
  179. package/p256.d.ts.map +1 -1
  180. package/p256.js +4 -0
  181. package/p256.js.map +1 -1
  182. package/p384.d.ts +7 -4
  183. package/p384.d.ts.map +1 -1
  184. package/p384.js +4 -1
  185. package/p384.js.map +1 -1
  186. package/p521.d.ts +7 -3
  187. package/p521.d.ts.map +1 -1
  188. package/p521.js +4 -0
  189. package/p521.js.map +1 -1
  190. package/package.json +17 -6
  191. package/secp256k1.d.ts +38 -21
  192. package/secp256k1.d.ts.map +1 -1
  193. package/secp256k1.js +112 -104
  194. package/secp256k1.js.map +1 -1
  195. package/src/_shortw_utils.ts +6 -15
  196. package/src/abstract/bls.ts +428 -251
  197. package/src/abstract/curve.ts +307 -149
  198. package/src/abstract/edwards.ts +555 -203
  199. package/src/abstract/fft.ts +30 -19
  200. package/src/abstract/hash-to-curve.ts +75 -34
  201. package/src/abstract/modular.ts +131 -59
  202. package/src/abstract/montgomery.ts +44 -15
  203. package/src/abstract/poseidon.ts +22 -18
  204. package/src/abstract/tower.ts +40 -71
  205. package/src/abstract/utils.ts +3 -378
  206. package/src/abstract/weierstrass.ts +1086 -746
  207. package/src/bls12-381.ts +549 -490
  208. package/src/bn254.ts +47 -35
  209. package/src/ed25519.ts +214 -216
  210. package/src/ed448.ts +251 -220
  211. package/src/index.ts +7 -9
  212. package/src/jubjub.ts +3 -3
  213. package/src/misc.ts +41 -40
  214. package/src/nist.ts +161 -126
  215. package/src/p256.ts +7 -3
  216. package/src/p384.ts +7 -5
  217. package/src/p521.ts +7 -3
  218. package/src/secp256k1.ts +145 -115
  219. package/src/utils.ts +328 -0
  220. package/utils.d.ts +96 -0
  221. package/utils.d.ts.map +1 -0
  222. package/utils.js +313 -0
  223. 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,57 @@
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';
29
+ import { ahash } from '@noble/hashes/utils';
30
+ import {
31
+ _validateObject,
32
+ abool,
33
+ abytes,
34
+ aInRange,
35
+ bitLen,
36
+ bitMask,
37
+ bytesToHex,
38
+ bytesToNumberBE,
39
+ concatBytes,
40
+ createHmacDrbg,
41
+ ensureBytes,
42
+ hexToBytes,
43
+ inRange,
44
+ isBytes,
45
+ memoized,
46
+ numberToHexUnpadded,
47
+ randomBytes,
48
+ type CHash,
49
+ type Hex,
50
+ type PrivKey,
51
+ } from '../utils.ts';
42
52
  import {
43
- pippenger, validateBasic, wNAF,
44
- type AffinePoint, type BasicCurve, type Group, type GroupConstructor
53
+ _createCurveFields,
54
+ mulEndoUnsafe,
55
+ negateCt,
56
+ normalizeZ,
57
+ pippenger,
58
+ wNAF,
59
+ type AffinePoint,
60
+ type BasicCurve,
61
+ type CurveInfo,
62
+ type CurvePoint,
63
+ type CurvePointCons,
45
64
  } from './curve.ts';
46
- // prettier-ignore
47
65
  import {
48
66
  Field,
49
67
  FpInvertBatch,
50
- getMinHashLength, invert, mapHashToField, mod, validateField,
51
- type IField
68
+ getMinHashLength,
69
+ mapHashToField,
70
+ validateField,
71
+ type IField,
72
+ type NLength,
52
73
  } 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
74
 
62
75
  export type { AffinePoint };
63
- type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
76
+ export type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
77
+
78
+ type EndoBasis = [[bigint, bigint], [bigint, bigint]];
64
79
  /**
65
80
  * When Weierstrass curve has `a=0`, it becomes Koblitz curve.
66
81
  * Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
@@ -86,7 +101,8 @@ type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array;
86
101
  */
87
102
  export type EndomorphismOpts = {
88
103
  beta: bigint;
89
- splitScalar: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
104
+ basises?: EndoBasis;
105
+ splitScalar?: (k: bigint) => { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
90
106
  };
91
107
  export type BasicWCurve<T> = BasicCurve<T> & {
92
108
  // Params: a, b
@@ -99,101 +115,228 @@ export type BasicWCurve<T> = BasicCurve<T> & {
99
115
  endo?: EndomorphismOpts;
100
116
  // When a cofactor != 1, there can be an effective methods to:
101
117
  // 1. Determine whether a point is torsion-free
102
- isTorsionFree?: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
118
+ isTorsionFree?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
103
119
  // 2. Clear torsion component
104
- clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
120
+ clearCofactor?: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
105
121
  };
106
122
 
123
+ // 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
+ const divNearest = (num: bigint, den: bigint) => (num + (num >= 0 ? den : -den) / _2n) / den;
125
+
126
+ export type ScalarEndoParts = { k1neg: boolean; k1: bigint; k2neg: boolean; k2: bigint };
127
+
128
+ /**
129
+ * Splits scalar for GLV endomorphism.
130
+ */
131
+ export function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): ScalarEndoParts {
132
+ // Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
133
+ // Since part can be negative, we need to do this on point.
134
+ // TODO: verifyScalar function which consumes lambda
135
+ const [[a1, b1], [a2, b2]] = basis;
136
+ const c1 = divNearest(b2 * k, n);
137
+ const c2 = divNearest(-b1 * k, n);
138
+ // |k1|/|k2| is < sqrt(N), but can be negative.
139
+ // If we do `k1 mod N`, we'll get big scalar (`> sqrt(N)`): so, we do cheaper negation instead.
140
+ let k1 = k - c1 * a1 - c2 * a2;
141
+ let k2 = -c1 * b1 - c2 * b2;
142
+ const k1neg = k1 < _0n;
143
+ const k2neg = k2 < _0n;
144
+ if (k1neg) k1 = -k1;
145
+ if (k2neg) k2 = -k2;
146
+ // Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
147
+ // This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
148
+ const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n; // Half bits of N
149
+ if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
150
+ throw new Error('splitScalar (endomorphism): failed, k=' + k);
151
+ }
152
+ return { k1neg, k1, k2neg, k2 };
153
+ }
154
+
155
+ export type ECDSASigFormat = 'compact' | 'der';
107
156
  export type Entropy = Hex | boolean;
108
- export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy; prehash?: boolean };
109
- export type VerOpts = { lowS?: boolean; prehash?: boolean; format?: 'compact' | 'der' | undefined };
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
+ }>;
110
168
 
111
169
  function validateSigVerOpts(opts: SignOpts | VerOpts) {
112
170
  if (opts.lowS !== undefined) abool('lowS', opts.lowS);
113
171
  if (opts.prehash !== undefined) abool('prehash', opts.prehash);
114
172
  }
115
173
 
116
- // Instance for 3d XYZ points
117
- export interface ProjPointType<T> extends Group<ProjPointType<T>> {
118
- readonly px: T;
119
- readonly py: T;
120
- readonly pz: T;
174
+ /** Instance methods for 3D XYZ projective points. */
175
+ export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>> {
176
+ /** projective X coordinate. Different from affine x. */
177
+ readonly X: T;
178
+ /** projective Y coordinate. Different from affine y. */
179
+ readonly Y: T;
180
+ /** projective z coordinate */
181
+ readonly Z: T;
182
+ /** affine x coordinate. Different from projective X. */
121
183
  get x(): T;
184
+ /** affine y coordinate. Different from projective Y. */
122
185
  get y(): T;
123
- toAffine(iz?: T): AffinePoint<T>;
186
+ /** Encodes point using IEEE P1363 (DER) encoding. First byte is 2/3/4. Default = isCompressed. */
187
+ toBytes(isCompressed?: boolean): Uint8Array;
124
188
  toHex(isCompressed?: boolean): string;
125
- toRawBytes(isCompressed?: boolean): Uint8Array;
126
189
 
127
- assertValidity(): void;
190
+ /** @deprecated use .X */
191
+ readonly px: T;
192
+ /** @deprecated use .Y */
193
+ readonly py: T;
194
+ /** @deprecated use .Z */
195
+ readonly pz: T;
196
+ /** @deprecated use `toBytes` */
197
+ toRawBytes(isCompressed?: boolean): Uint8Array;
198
+ /** @deprecated use `multiplyUnsafe` */
199
+ multiplyAndAddUnsafe(
200
+ Q: WeierstrassPoint<T>,
201
+ a: bigint,
202
+ b: bigint
203
+ ): WeierstrassPoint<T> | undefined;
204
+ /** @deprecated use `p.y % 2n === 0n` */
128
205
  hasEvenY(): boolean;
129
- multiplyUnsafe(scalar: bigint): ProjPointType<T>;
130
- multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
131
- isTorsionFree(): boolean;
132
- clearCofactor(): ProjPointType<T>;
206
+ /** @deprecated use `p.precompute(windowSize)` */
133
207
  _setWindowSize(windowSize: number): void;
134
208
  }
135
- // Static methods for 3d XYZ points
136
- export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
137
- new (x: T, y: T, z: T): ProjPointType<T>;
138
- fromAffine(p: AffinePoint<T>): ProjPointType<T>;
139
- fromHex(hex: Hex): ProjPointType<T>;
140
- fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
141
- normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
142
- msm(points: ProjPointType<T>[], scalars: bigint[]): ProjPointType<T>;
209
+
210
+ /** Static methods for 3D XYZ projective points. */
211
+ export interface WeierstrassPointCons<T> extends CurvePointCons<T, WeierstrassPoint<T>> {
212
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
213
+ new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
214
+ /** @deprecated use `Point.BASE.multiply(Point.Fn.fromBytes(privateKey))` */
215
+ fromPrivateKey(privateKey: PrivKey): WeierstrassPoint<T>;
216
+ /** @deprecated use `import { normalizeZ } from '@noble/curves/abstract/curve.js';` */
217
+ normalizeZ(points: WeierstrassPoint<T>[]): WeierstrassPoint<T>[];
218
+ /** @deprecated use `import { pippenger } from '@noble/curves/abstract/curve.js';` */
219
+ msm(points: WeierstrassPoint<T>[], scalars: bigint[]): WeierstrassPoint<T>;
143
220
  }
144
221
 
222
+ /** @deprecated use WeierstrassPoint */
223
+ export type ProjPointType<T> = WeierstrassPoint<T>;
224
+ /** @deprecated use WeierstrassPointCons */
225
+ export type ProjConstuctor<T> = WeierstrassPointCons<T>;
226
+
227
+ // TODO: remove
145
228
  export type CurvePointsType<T> = BasicWCurve<T> & {
146
- // Bytes
147
229
  fromBytes?: (bytes: Uint8Array) => AffinePoint<T>;
148
- toBytes?: (c: ProjConstructor<T>, point: ProjPointType<T>, isCompressed: boolean) => Uint8Array;
230
+ toBytes?: (
231
+ c: WeierstrassPointCons<T>,
232
+ point: WeierstrassPoint<T>,
233
+ isCompressed: boolean
234
+ ) => Uint8Array;
149
235
  };
150
236
 
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
- }
237
+ // LegacyWeierstrassOpts
238
+ export type CurvePointsTypeWithLength<T> = Readonly<CurvePointsType<T> & Partial<NLength>>;
188
239
 
240
+ // LegacyWeierstrass
189
241
  export type CurvePointsRes<T> = {
190
- CURVE: ReturnType<typeof validatePointOpts<T>>;
191
- ProjectivePoint: ProjConstructor<T>;
242
+ Point: WeierstrassPointCons<T>;
243
+
244
+ /** @deprecated import individual CURVE params */
245
+ CURVE: CurvePointsType<T>;
246
+ /** @deprecated use `Point` */
247
+ ProjectivePoint: WeierstrassPointCons<T>;
248
+ /** @deprecated use `Point.Fn.fromBytes(privateKey)` */
192
249
  normPrivateKeyToScalar: (key: PrivKey) => bigint;
250
+ /** @deprecated */
193
251
  weierstrassEquation: (x: T) => T;
252
+ /** @deprecated use `Point.Fn.isValidNot0(num)` */
194
253
  isWithinCurveOrder: (num: bigint) => boolean;
195
254
  };
196
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
+ /**
265
+ * Weierstrass curve options.
266
+ *
267
+ * * p: prime characteristic (order) of finite field, in which arithmetics is done
268
+ * * n: order of prime subgroup a.k.a total amount of valid curve points
269
+ * * h: cofactor, usually 1. h*n is group order; n is subgroup order
270
+ * * a: formula param, must be in field of p
271
+ * * b: formula param, must be in field of p
272
+ * * Gx: x coordinate of generator point a.k.a. base point
273
+ * * Gy: y coordinate of generator point
274
+ */
275
+ export type WeierstrassOpts<T> = Readonly<{
276
+ p: bigint;
277
+ n: bigint;
278
+ h: bigint;
279
+ a: T;
280
+ b: T;
281
+ Gx: T;
282
+ Gy: T;
283
+ }>;
284
+
285
+ // When a cofactor != 1, there can be an effective methods to:
286
+ // 1. Determine whether a point is torsion-free
287
+ // 2. Clear torsion component
288
+ // wrapPrivateKey: bls12-381 requires mod(n) instead of rejecting keys >= n
289
+ export type WeierstrassExtraOpts<T> = Partial<{
290
+ Fp: IField<T>;
291
+ Fn: IField<bigint>;
292
+ allowInfinityPoint: boolean;
293
+ endo: EndomorphismOpts;
294
+ isTorsionFree: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
295
+ clearCofactor: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
296
+ fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
297
+ toBytes: (
298
+ c: WeierstrassPointCons<T>,
299
+ point: WeierstrassPoint<T>,
300
+ isCompressed: boolean
301
+ ) => Uint8Array;
302
+ }>;
303
+
304
+ /**
305
+ * Options for ECDSA signatures over a Weierstrass curve.
306
+ */
307
+ export type ECDSAOpts = Partial<{
308
+ lowS: boolean;
309
+ hmac: HmacFnSync;
310
+ randomBytes: (bytesLength?: number) => Uint8Array;
311
+ bits2int: (bytes: Uint8Array) => bigint;
312
+ bits2int_modN: (bytes: Uint8Array) => bigint;
313
+ }>;
314
+
315
+ /** ECDSA is only supported for prime fields, not Fp2 (extension fields). */
316
+ export interface ECDSA {
317
+ keygen: (seed?: Uint8Array) => { secretKey: Uint8Array; publicKey: Uint8Array };
318
+ 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
+ getSharedSecret: (secretKeyA: PrivKey, publicKeyB: Hex, isCompressed?: boolean) => Uint8Array;
322
+ Point: WeierstrassPointCons<bigint>;
323
+ Signature: ECDSASignatureCons;
324
+ utils: {
325
+ isValidSecretKey: (secretKey: PrivKey) => boolean;
326
+ isValidPublicKey: (publicKey: Uint8Array, isCompressed?: boolean) => boolean;
327
+ randomSecretKey: (seed?: Uint8Array) => Uint8Array;
328
+
329
+ /** @deprecated use `randomSecretKey` */
330
+ randomPrivateKey: (seed?: Uint8Array) => Uint8Array;
331
+ /** @deprecated use `isValidSecretKey` */
332
+ isValidPrivateKey: (secretKey: PrivKey) => boolean;
333
+ /** @deprecated use `Point.Fn.fromBytes()` */
334
+ normPrivateKeyToScalar: (key: PrivKey) => bigint;
335
+ /** @deprecated use `point.precompute()` */
336
+ precompute: (windowSize?: number, point?: WeierstrassPoint<bigint>) => WeierstrassPoint<bigint>;
337
+ };
338
+ info: CurveInfo;
339
+ }
197
340
  export class DERErr extends Error {
198
341
  constructor(m = '') {
199
342
  super(m);
@@ -312,47 +455,133 @@ export const DER: IDER = {
312
455
  },
313
456
  };
314
457
 
315
- function numToSizedHex(num: bigint, size: number): string {
316
- return bytesToHex(numberToBytesBE(num, size));
317
- }
318
-
319
458
  // Be friendly to bad ECMAScript parsers by not using bigint literals
320
459
  // prettier-ignore
321
460
  const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
322
461
 
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
-
462
+ // TODO: remove
463
+ export function _legacyHelperEquat<T>(Fp: IField<T>, a: T, b: T): (x: T) => T {
345
464
  /**
346
465
  * y² = x³ + ax + b: Short weierstrass curve formula. Takes x, returns y².
347
466
  * @returns y²
348
467
  */
349
468
  function weierstrassEquation(x: T): T {
350
- const { a, b } = CURVE;
351
469
  const x2 = Fp.sqr(x); // x * x
352
470
  const x3 = Fp.mul(x2, x); // x² * x
353
471
  return Fp.add(Fp.add(x3, Fp.mul(x, a)), b); // x³ + a * x + b
354
472
  }
473
+ return weierstrassEquation;
474
+ }
475
+ export function _normFnElement(Fn: IField<bigint>, key: PrivKey): bigint {
476
+ const { BYTES: expected } = Fn;
477
+ let num: bigint;
478
+ if (typeof key === 'bigint') {
479
+ num = key;
480
+ } else {
481
+ let bytes = ensureBytes('private key', key);
482
+ try {
483
+ num = Fn.fromBytes(bytes);
484
+ } catch (error) {
485
+ throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
486
+ }
487
+ }
488
+ if (!Fn.isValidNot0(num)) throw new Error('invalid private key: out of range [1..N-1]');
489
+ return num;
490
+ }
491
+
492
+ export function weierstrassN<T>(
493
+ CURVE: WeierstrassOpts<T>,
494
+ curveOpts: WeierstrassExtraOpts<T> = {}
495
+ ): WeierstrassPointCons<T> {
496
+ const { Fp, Fn } = _createCurveFields('weierstrass', CURVE, curveOpts);
497
+ const { h: cofactor, n: CURVE_ORDER } = CURVE;
498
+ _validateObject(
499
+ curveOpts,
500
+ {},
501
+ {
502
+ allowInfinityPoint: 'boolean',
503
+ clearCofactor: 'function',
504
+ isTorsionFree: 'function',
505
+ fromBytes: 'function',
506
+ toBytes: 'function',
507
+ endo: 'object',
508
+ wrapPrivateKey: 'boolean',
509
+ }
510
+ );
511
+
512
+ const { endo } = curveOpts;
513
+ if (endo) {
514
+ // validateObject(endo, { beta: 'bigint', splitScalar: 'function' });
515
+ if (!Fp.is0(CURVE.a) || typeof endo.beta !== 'bigint' || !Array.isArray(endo.basises)) {
516
+ throw new Error('invalid endo: expected "beta": bigint and "basises": array');
517
+ }
518
+ }
519
+
520
+ function assertCompressionIsSupported() {
521
+ if (!Fp.isOdd) throw new Error('compression is not supported: Field does not have .isOdd()');
522
+ }
523
+
524
+ // Implements IEEE P1363 point encoding
525
+ function pointToBytes(
526
+ _c: WeierstrassPointCons<T>,
527
+ point: WeierstrassPoint<T>,
528
+ isCompressed: boolean
529
+ ): Uint8Array {
530
+ const { x, y } = point.toAffine();
531
+ const bx = Fp.toBytes(x);
532
+ abool('isCompressed', isCompressed);
533
+ if (isCompressed) {
534
+ assertCompressionIsSupported();
535
+ const hasEvenY = !Fp.isOdd!(y);
536
+ return concatBytes(pprefix(hasEvenY), bx);
537
+ } else {
538
+ return concatBytes(Uint8Array.of(0x04), bx, Fp.toBytes(y));
539
+ }
540
+ }
541
+ 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
546
+ const length = bytes.length;
547
+ const head = bytes[0];
548
+ const tail = bytes.subarray(1);
549
+ // No actual validation is done here: use .assertValidity()
550
+ if (length === LC && (head === 0x02 || head === 0x03)) {
551
+ const x = Fp.fromBytes(tail);
552
+ if (!Fp.isValid(x)) throw new Error('bad point: is not on curve, wrong x');
553
+ const y2 = weierstrassEquation(x); // y² = x³ + ax + b
554
+ let y: T;
555
+ try {
556
+ y = Fp.sqrt(y2); // y = y² ^ (p+1)/4
557
+ } catch (sqrtError) {
558
+ const err = sqrtError instanceof Error ? ': ' + sqrtError.message : '';
559
+ throw new Error('bad point: is not on curve, sqrt error' + err);
560
+ }
561
+ assertCompressionIsSupported();
562
+ const isYOdd = Fp.isOdd!(y); // (y & _1n) === _1n;
563
+ const isHeadOdd = (head & 1) === 1; // ECDSA-specific
564
+ if (isHeadOdd !== isYOdd) y = Fp.neg(y);
565
+ return { x, y };
566
+ } else if (length === LU && head === 0x04) {
567
+ // 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));
570
+ if (!isValidXY(x, y)) throw new Error('bad point: is not on curve');
571
+ return { x, y };
572
+ } else {
573
+ throw new Error(
574
+ `bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`
575
+ );
576
+ }
577
+ }
355
578
 
579
+ const toBytes = curveOpts.toBytes || pointToBytes;
580
+ const fromBytes = curveOpts.fromBytes || pointFromBytes;
581
+ const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
582
+
583
+ // TODO: move top-level
584
+ /** Checks whether equation holds for given x, y: y² == x³ + ax + b */
356
585
  function isValidXY(x: T, y: T): boolean {
357
586
  const left = Fp.sqr(y); // y²
358
587
  const right = weierstrassEquation(x); // x³ + ax + b
@@ -369,60 +598,40 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
369
598
  const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
370
599
  if (Fp.is0(Fp.add(_4a3, _27b2))) throw new Error('bad curve params: a or b');
371
600
 
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;
601
+ /** Asserts coordinate is valid: 0 <= n < Fp.ORDER. */
602
+ function acoord(title: string, n: T, banZero = false) {
603
+ if (!Fp.isValid(n) || (banZero && Fp.is0(n))) throw new Error(`bad point coordinate ${title}`);
604
+ return n;
401
605
  }
402
606
 
403
607
  function aprjpoint(other: unknown) {
404
608
  if (!(other instanceof Point)) throw new Error('ProjectivePoint expected');
405
609
  }
406
610
 
611
+ function splitEndoScalarN(k: bigint) {
612
+ if (!endo || !endo.basises) throw new Error('no endo');
613
+ return _splitEndoScalar(k, endo.basises, Fn.ORDER);
614
+ }
615
+
407
616
  // Memoized toAffine / validity check. They are heavy. Points are immutable.
408
617
 
409
618
  // Converts Projective point to affine (x, y) coordinates.
410
619
  // Can accept precomputed Z^-1 - for example, from invertBatch.
411
620
  // (X, Y, Z) ∋ (x=X/Z, y=Y/Z)
412
621
  const toAffineMemo = memoized((p: Point, iz?: T): AffinePoint<T> => {
413
- const { px: x, py: y, pz: z } = p;
622
+ const { X, Y, Z } = p;
414
623
  // Fast-path for normalized points
415
- if (Fp.eql(z, Fp.ONE)) return { x, y };
624
+ if (Fp.eql(Z, Fp.ONE)) return { x: X, y: Y };
416
625
  const is0 = p.is0();
417
626
  // If invZ was 0, we return zero point. However we still want to execute
418
627
  // all operations, so we replace invZ with a random number, 1.
419
- if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(z);
420
- const ax = Fp.mul(x, iz);
421
- const ay = Fp.mul(y, iz);
422
- const zz = Fp.mul(z, iz);
628
+ if (iz == null) iz = is0 ? Fp.ONE : Fp.inv(Z);
629
+ const x = Fp.mul(X, iz);
630
+ const y = Fp.mul(Y, iz);
631
+ const zz = Fp.mul(Z, iz);
423
632
  if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
424
633
  if (!Fp.eql(zz, Fp.ONE)) throw new Error('invZ was invalid');
425
- return { x: ax, y: ay };
634
+ return { x, y };
426
635
  });
427
636
  // NOTE: on exception this will crash 'cached' and no value will be set.
428
637
  // Otherwise true will be return
@@ -431,51 +640,63 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
431
640
  // (0, 1, 0) aka ZERO is invalid in most contexts.
432
641
  // In BLS, ZERO can be serialized, so we allow it.
433
642
  // (0, 0, 0) is invalid representation of ZERO.
434
- if (CURVE.allowInfinityPoint && !Fp.is0(p.py)) return;
643
+ if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y)) return;
435
644
  throw new Error('bad point: ZERO');
436
645
  }
437
646
  // Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
438
647
  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');
648
+ if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error('bad point: x or y not field elements');
441
649
  if (!isValidXY(x, y)) throw new Error('bad point: equation left != right');
442
650
  if (!p.isTorsionFree()) throw new Error('bad point: not in prime-order subgroup');
443
651
  return true;
444
652
  });
445
653
 
654
+ function finishEndo(
655
+ endoBeta: EndomorphismOpts['beta'],
656
+ k1p: Point,
657
+ k2p: Point,
658
+ k1neg: boolean,
659
+ k2neg: boolean
660
+ ) {
661
+ k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
662
+ k1p = negateCt(k1neg, k1p);
663
+ k2p = negateCt(k2neg, k2p);
664
+ return k1p.add(k2p);
665
+ }
666
+
446
667
  /**
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)
668
+ * Projective Point works in 3d / projective (homogeneous) coordinates:(X, Y, Z) ∋ (x=X/Z, y=Y/Z).
669
+ * Default Point works in 2d / affine coordinates: (x, y).
449
670
  * We're doing calculations in projective, because its operations don't require costly inversion.
450
671
  */
451
- class Point implements ProjPointType<T> {
672
+ class Point implements WeierstrassPoint<T> {
452
673
  // base / generator point
453
674
  static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
454
675
  // zero / infinity / identity point
455
676
  static readonly ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
456
- readonly px: T;
457
- readonly py: T;
458
- readonly pz: T;
459
-
460
- 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;
677
+ // fields
678
+ static readonly Fp = Fp;
679
+ static readonly Fn = Fn;
680
+
681
+ readonly X: T;
682
+ readonly Y: T;
683
+ readonly Z: T;
684
+
685
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
686
+ constructor(X: T, Y: T, Z: T) {
687
+ this.X = acoord('x', X);
688
+ this.Y = acoord('y', Y, true);
689
+ this.Z = acoord('z', Z);
467
690
  Object.freeze(this);
468
691
  }
469
692
 
470
- // Does not validate if the point is on-curve.
471
- // Use fromHex instead, or call assertValidity() later.
693
+ /** Does NOT validate if the point is valid. Use `.assertValidity()`. */
472
694
  static fromAffine(p: AffinePoint<T>): Point {
473
695
  const { x, y } = p || {};
474
696
  if (!p || !Fp.isValid(x) || !Fp.isValid(y)) throw new Error('invalid affine point');
475
697
  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;
698
+ // (0, 0) would've produced (0, 0, 1) - instead, we need (0, 1, 0)
699
+ if (Fp.is0(x) && Fp.is0(y)) return Point.ZERO;
479
700
  return new Point(x, y, Fp.ONE);
480
701
  }
481
702
 
@@ -486,73 +707,82 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
486
707
  return this.toAffine().y;
487
708
  }
488
709
 
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
- */
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
+ }
495
720
  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);
721
+ return normalizeZ(Point, points);
501
722
  }
502
723
 
503
- /**
504
- * Converts hash string or Uint8Array to Point.
505
- * @param hex short/long ECDSA hex
506
- */
724
+ static fromBytes(bytes: Uint8Array): Point {
725
+ abytes(bytes);
726
+ return Point.fromHex(bytes);
727
+ }
728
+
729
+ /** Converts hash string or Uint8Array to Point. */
507
730
  static fromHex(hex: Hex): Point {
508
731
  const P = Point.fromAffine(fromBytes(ensureBytes('pointHex', hex)));
509
732
  P.assertValidity();
510
733
  return P;
511
734
  }
512
735
 
513
- // Multiplies generator point by privateKey.
736
+ /** Multiplies generator point by privateKey. */
514
737
  static fromPrivateKey(privateKey: PrivKey) {
515
- return Point.BASE.multiply(normPrivateKeyToScalar(privateKey));
738
+ return Point.BASE.multiply(_normFnElement(Fn, privateKey));
516
739
  }
517
740
 
518
- // Multiscalar Multiplication
741
+ // TODO: remove
519
742
  static msm(points: Point[], scalars: bigint[]): Point {
520
743
  return pippenger(Point, Fn, points, scalars);
521
744
  }
522
-
523
- // "Private method", don't use it directly
524
745
  _setWindowSize(windowSize: number) {
525
- wnaf.setWindowSize(this, windowSize);
746
+ this.precompute(windowSize);
747
+ }
748
+
749
+ /**
750
+ *
751
+ * @param windowSize
752
+ * @param isLazy true will defer table computation until the first multiplication
753
+ * @returns
754
+ */
755
+ precompute(windowSize: number = 8, isLazy = true): Point {
756
+ wnaf.createCache(this, windowSize);
757
+ if (!isLazy) this.multiply(_3n); // random number
758
+ return this;
526
759
  }
527
760
 
528
- // A point on curve is valid if it conforms to equation.
761
+ // TODO: return `this`
762
+ /** A point on curve is valid if it conforms to equation. */
529
763
  assertValidity(): void {
530
764
  assertValidMemo(this);
531
765
  }
532
766
 
533
767
  hasEvenY(): boolean {
534
768
  const { y } = this.toAffine();
535
- if (Fp.isOdd) return !Fp.isOdd(y);
536
- throw new Error("Field doesn't support isOdd");
769
+ if (!Fp.isOdd) throw new Error("Field doesn't support isOdd");
770
+ return !Fp.isOdd(y);
537
771
  }
538
772
 
539
- /**
540
- * Compare one point to another.
541
- */
773
+ /** Compare one point to another. */
542
774
  equals(other: Point): boolean {
543
775
  aprjpoint(other);
544
- const { px: X1, py: Y1, pz: Z1 } = this;
545
- const { px: X2, py: Y2, pz: Z2 } = other;
776
+ const { X: X1, Y: Y1, Z: Z1 } = this;
777
+ const { X: X2, Y: Y2, Z: Z2 } = other;
546
778
  const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
547
779
  const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
548
780
  return U1 && U2;
549
781
  }
550
782
 
551
- /**
552
- * Flips point to one corresponding to (x, -y) in Affine coordinates.
553
- */
783
+ /** Flips point to one corresponding to (x, -y) in Affine coordinates. */
554
784
  negate(): Point {
555
- return new Point(this.px, Fp.neg(this.py), this.pz);
785
+ return new Point(this.X, Fp.neg(this.Y), this.Z);
556
786
  }
557
787
 
558
788
  // Renes-Costello-Batina exception-free doubling formula.
@@ -562,7 +792,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
562
792
  double() {
563
793
  const { a, b } = CURVE;
564
794
  const b3 = Fp.mul(b, _3n);
565
- const { px: X1, py: Y1, pz: Z1 } = this;
795
+ const { X: X1, Y: Y1, Z: Z1 } = this;
566
796
  let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
567
797
  let t0 = Fp.mul(X1, X1); // step 1
568
798
  let t1 = Fp.mul(Y1, Y1);
@@ -604,8 +834,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
604
834
  // Cost: 12M + 0S + 3*a + 3*b3 + 23add.
605
835
  add(other: Point): Point {
606
836
  aprjpoint(other);
607
- const { px: X1, py: Y1, pz: Z1 } = this;
608
- const { px: X2, py: Y2, pz: Z2 } = other;
837
+ const { X: X1, Y: Y1, Z: Z1 } = this;
838
+ const { X: X2, Y: Y2, Z: Z2 } = other;
609
839
  let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO; // prettier-ignore
610
840
  const a = CURVE.a;
611
841
  const b3 = Fp.mul(CURVE.b, _3n);
@@ -656,49 +886,10 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
656
886
  return this.add(other.negate());
657
887
  }
658
888
 
659
- is0() {
889
+ is0(): boolean {
660
890
  return this.equals(Point.ZERO);
661
891
  }
662
892
 
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
893
  /**
703
894
  * Constant time multiplication.
704
895
  * Uses wNAF method. Windowed method may be 10% faster,
@@ -709,295 +900,442 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>): CurvePointsRes<T
709
900
  * @returns New point
710
901
  */
711
902
  multiply(scalar: bigint): Point {
712
- const { endo, n: N } = CURVE;
713
- aInRange('scalar', scalar, _1n, N);
903
+ const { endo } = curveOpts;
904
+ if (!Fn.isValidNot0(scalar)) throw new Error('invalid scalar: out of range'); // 0 is invalid
714
905
  let point: Point, fake: Point; // Fake point is used to const-time mult
906
+ const mul = (n: bigint) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
715
907
  /** See docs for {@link EndomorphismOpts} */
716
908
  if (endo) {
717
- 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);
909
+ const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
910
+ const { p: k1p, f: k1f } = mul(k1);
911
+ const { p: k2p, f: k2f } = mul(k2);
912
+ fake = k1f.add(k2f);
913
+ point = finishEndo(endo.beta, k1p, k2p, k1neg, k2neg);
725
914
  } else {
726
- const { p, f } = this.wNAF(scalar);
915
+ const { p, f } = mul(scalar);
727
916
  point = p;
728
917
  fake = f;
729
918
  }
730
919
  // Normalize `z` for both points, but return only real one
731
- return Point.normalizeZ([point, fake])[0];
920
+ return normalizeZ(Point, [point, fake])[0];
732
921
  }
733
922
 
734
923
  /**
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
924
+ * Non-constant-time multiplication. Uses double-and-add algorithm.
925
+ * It's faster, but should only be used when you don't care about
926
+ * an exposed secret key e.g. sig verification, which works over *public* keys.
739
927
  */
928
+ multiplyUnsafe(sc: bigint): Point {
929
+ const { endo } = curveOpts;
930
+ const p = this;
931
+ if (!Fn.isValid(sc)) throw new Error('invalid scalar: out of range'); // 0 is valid
932
+ if (sc === _0n || p.is0()) return Point.ZERO;
933
+ if (sc === _1n) return p; // fast-path
934
+ if (wnaf.hasCache(this)) return this.multiply(sc);
935
+ if (endo) {
936
+ const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
937
+ const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
938
+ return finishEndo(endo.beta, p1, p2, k1neg, k2neg);
939
+ } else {
940
+ return wnaf.unsafe(p, sc);
941
+ }
942
+ }
943
+
740
944
  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));
945
+ const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
747
946
  return sum.is0() ? undefined : sum;
748
947
  }
749
948
 
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);
949
+ /**
950
+ * Converts Projective point to affine (x, y) coordinates.
951
+ * @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
952
+ */
953
+ toAffine(invertedZ?: T): AffinePoint<T> {
954
+ return toAffineMemo(this, invertedZ);
755
955
  }
956
+
957
+ /**
958
+ * Checks whether Point is free of torsion elements (is in prime subgroup).
959
+ * Always torsion-free for cofactor=1 curves.
960
+ */
756
961
  isTorsionFree(): boolean {
757
- const { h: cofactor, isTorsionFree } = CURVE;
758
- if (cofactor === _1n) return true; // No subgroups, always torsion-free
962
+ const { isTorsionFree } = curveOpts;
963
+ if (cofactor === _1n) return true;
759
964
  if (isTorsionFree) return isTorsionFree(Point, this);
760
- throw new Error('isTorsionFree() has not been declared for the elliptic curve');
965
+ return wnaf.unsafe(this, CURVE_ORDER).is0();
761
966
  }
967
+
762
968
  clearCofactor(): Point {
763
- const { h: cofactor, clearCofactor } = CURVE;
969
+ const { clearCofactor } = curveOpts;
764
970
  if (cofactor === _1n) return this; // Fast-path
765
971
  if (clearCofactor) return clearCofactor(Point, this) as Point;
766
- return this.multiplyUnsafe(CURVE.h);
972
+ return this.multiplyUnsafe(cofactor);
767
973
  }
768
974
 
769
- toRawBytes(isCompressed = true): Uint8Array {
975
+ isSmallOrder(): boolean {
976
+ // can we use this.clearCofactor()?
977
+ return this.multiplyUnsafe(cofactor).is0();
978
+ }
979
+
980
+ toBytes(isCompressed = true): Uint8Array {
770
981
  abool('isCompressed', isCompressed);
771
982
  this.assertValidity();
772
983
  return toBytes(Point, this, isCompressed);
773
984
  }
774
985
 
986
+ /** @deprecated use `toBytes` */
987
+ toRawBytes(isCompressed = true): Uint8Array {
988
+ return this.toBytes(isCompressed);
989
+ }
990
+
775
991
  toHex(isCompressed = true): string {
776
- abool('isCompressed', isCompressed);
777
- return bytesToHex(this.toRawBytes(isCompressed));
992
+ return bytesToHex(this.toBytes(isCompressed));
993
+ }
994
+
995
+ toString() {
996
+ return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
778
997
  }
779
998
  }
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
- };
999
+ const bits = Fn.BITS;
1000
+ const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
1001
+ return Point;
1002
+ }
1003
+
1004
+ // _legacyWeierstrass
1005
+ // TODO: remove
1006
+ /** @deprecated use `weierstrassN` */
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);
789
1011
  }
790
1012
 
791
1013
  // Instance
792
- export interface SignatureType {
1014
+ export interface ECDSASignature {
793
1015
  readonly r: bigint;
794
1016
  readonly s: bigint;
795
1017
  readonly recovery?: number;
796
- assertValidity(): void;
797
- addRecoveryBit(recovery: number): RecoveredSignatureType;
1018
+ addRecoveryBit(recovery: number): ECDSASigRecovered;
798
1019
  hasHighS(): boolean;
799
- normalizeS(): SignatureType;
800
- recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
1020
+ normalizeS(): ECDSASignature;
1021
+ recoverPublicKey(msgHash: Hex): WeierstrassPoint<bigint>;
1022
+ toBytes(format?: string): Uint8Array;
1023
+ toHex(format?: string): string;
1024
+
1025
+ /** @deprecated */
1026
+ assertValidity(): void;
1027
+ /** @deprecated use `.toBytes('compact')` */
801
1028
  toCompactRawBytes(): Uint8Array;
1029
+ /** @deprecated use `.toBytes('compact')` */
802
1030
  toCompactHex(): string;
803
- toDERRawBytes(isCompressed?: boolean): Uint8Array;
804
- toDERHex(isCompressed?: boolean): string;
1031
+ /** @deprecated use `.toBytes('der')` */
1032
+ toDERRawBytes(): Uint8Array;
1033
+ /** @deprecated use `.toBytes('der')` */
1034
+ toDERHex(): string;
805
1035
  }
806
- export type RecoveredSignatureType = SignatureType & {
1036
+ export type SignatureType = ECDSASignature;
1037
+ export type ECDSASigRecovered = ECDSASignature & {
807
1038
  readonly recovery: number;
808
1039
  };
1040
+ export type RecoveredSignatureType = ECDSASigRecovered;
809
1041
  // Static methods
810
- export type SignatureConstructor = {
811
- new (r: bigint, s: bigint): SignatureType;
812
- fromCompact(hex: Hex): SignatureType;
813
- fromDER(hex: Hex): SignatureType;
1042
+ export type ECDSASignatureCons = {
1043
+ new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
1044
+ fromBytes(bytes: Uint8Array, format?: ECDSASigFormat): ECDSASignature;
1045
+ fromHex(hex: string, format?: ECDSASigFormat): ECDSASignature;
1046
+
1047
+ /** @deprecated use `.fromBytes(bytes, 'compact')` */
1048
+ fromCompact(hex: Hex): ECDSASignature;
1049
+ /** @deprecated use `.fromBytes(bytes, 'der')` */
1050
+ fromDER(hex: Hex): ECDSASignature;
814
1051
  };
815
- type SignatureLike = { r: bigint; s: bigint };
816
-
817
- export type PubKey = Hex | ProjPointType<bigint>;
1052
+ export type SignatureLike = { r: bigint; s: bigint };
1053
+ // TODO: remove
1054
+ export type PubKey = Hex | WeierstrassPoint<bigint>;
818
1055
 
1056
+ // TODO: remove
819
1057
  export type CurveType = BasicWCurve<bigint> & {
820
1058
  hash: CHash; // CHash not FHash because we need outputLen for DRBG
821
- hmac: HmacFnSync;
822
- randomBytes: (bytesLength?: number) => Uint8Array;
1059
+ hmac?: HmacFnSync;
1060
+ randomBytes?: (bytesLength?: number) => Uint8Array;
823
1061
  lowS?: boolean;
824
1062
  bits2int?: (bytes: Uint8Array) => bigint;
825
1063
  bits2int_modN?: (bytes: Uint8Array) => bigint;
826
1064
  };
827
1065
 
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);
1066
+ // Points start with byte 0x02 when y is even; otherwise 0x03
1067
+ function pprefix(hasEvenY: boolean): Uint8Array {
1068
+ return Uint8Array.of(hasEvenY ? 0x02 : 0x03);
846
1069
  }
847
1070
 
1071
+ // TODO: remove
848
1072
  export type CurveFn = {
849
- CURVE: ReturnType<typeof validateOpts>;
850
- getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
851
- getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
852
- sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => RecoveredSignatureType;
853
- verify: (signature: Hex | SignatureLike, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
854
- ProjectivePoint: ProjConstructor<bigint>;
855
- Signature: SignatureConstructor;
856
- utils: {
857
- normPrivateKeyToScalar: (key: PrivKey) => bigint;
858
- isValidPrivateKey(privateKey: PrivKey): boolean;
859
- randomPrivateKey: () => Uint8Array;
860
- precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
861
- };
1073
+ CURVE: CurvePointsType<bigint>;
1074
+ keygen: ECDSA['keygen'];
1075
+ getPublicKey: ECDSA['getPublicKey'];
1076
+ getSharedSecret: ECDSA['getSharedSecret'];
1077
+ sign: ECDSA['sign'];
1078
+ verify: ECDSA['verify'];
1079
+ Point: WeierstrassPointCons<bigint>;
1080
+ /** @deprecated use `Point` */
1081
+ ProjectivePoint: WeierstrassPointCons<bigint>;
1082
+ Signature: ECDSASignatureCons;
1083
+ utils: ECDSA['utils'];
1084
+ info: CurveInfo;
862
1085
  };
863
1086
 
864
1087
  /**
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 })
1088
+ * Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
1089
+ * TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
1090
+ * b = True and y = sqrt(u / v) if (u / v) is square in F, and
1091
+ * b = False and y = sqrt(Z * (u / v)) otherwise.
1092
+ * @param Fp
1093
+ * @param Z
1094
+ * @returns
870
1095
  */
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
- }
883
-
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); // = x³ + ax + b
911
- let y: bigint;
912
- try {
913
- y = Fp.sqrt(y2); // 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
- });
936
-
937
- function isBiggerThanHalfOrder(number: bigint) {
938
- const HALF = CURVE_ORDER >> _1n;
939
- return number > HALF;
940
- }
941
-
942
- function normalizeS(s: bigint) {
943
- return isBiggerThanHalfOrder(s) ? modN(-s) : s;
944
- }
945
- // slice bytes num
946
- const slcNum = (b: Uint8Array, from: number, to: number) => bytesToNumberBE(b.slice(from, to));
947
-
948
- /**
949
- * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
950
- */
951
- class Signature implements SignatureType {
952
- readonly r: bigint;
953
- readonly s: bigint;
954
- readonly recovery?: number;
955
- 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]
958
- this.r = r;
959
- this.s = s;
960
- if (recovery != null) this.recovery = recovery;
961
- Object.freeze(this);
962
- }
963
-
964
- // pair (bytes of r, bytes of s)
965
- 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));
969
- }
970
-
971
- // DER encoded ECDSA signature
972
- // https://bitcoin.stackexchange.com/questions/57644/what-are-the-parts-of-a-bitcoin-transaction-input-script
973
- static fromDER(hex: Hex) {
974
- const { r, s } = DER.toSig(ensureBytes('DER', hex));
975
- return new Signature(r, s);
976
- }
977
-
978
- /**
979
- * @todo remove
980
- * @deprecated
981
- */
982
- assertValidity(): void {}
983
-
984
- addRecoveryBit(recovery: number): RecoveredSignature {
985
- return new Signature(this.r, this.s, recovery) as RecoveredSignature;
986
- }
987
-
988
- recoverPublicKey(msgHash: Hex): typeof Point.BASE {
989
- const { r, s, recovery: rec } = this;
990
- const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
991
- 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
1096
+ export function SWUFpSqrtRatio<T>(
1097
+ Fp: IField<T>,
1098
+ Z: T
1099
+ ): (u: T, v: T) => { isValid: boolean; value: T } {
1100
+ // Generic implementation
1101
+ const q = Fp.ORDER;
1102
+ let l = _0n;
1103
+ for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
1104
+ const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
1105
+ // We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
1106
+ // 2n ** c1 == 2n << (c1-1)
1107
+ const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
1108
+ const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
1109
+ const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
1110
+ const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
1111
+ const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
1112
+ const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
1113
+ const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
1114
+ const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
1115
+ let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
1116
+ let tv1 = c6; // 1. tv1 = c6
1117
+ let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
1118
+ let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
1119
+ tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
1120
+ let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
1121
+ tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
1122
+ tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
1123
+ tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
1124
+ tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
1125
+ let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
1126
+ tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
1127
+ let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
1128
+ tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
1129
+ tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
1130
+ tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
1131
+ tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
1132
+ // 17. for i in (c1, c1 - 1, ..., 2):
1133
+ for (let i = c1; i > _1n; i--) {
1134
+ let tv5 = i - _2n; // 18. tv5 = i - 2
1135
+ tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
1136
+ let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
1137
+ const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
1138
+ tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
1139
+ tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
1140
+ tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
1141
+ tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
1142
+ tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
1143
+ }
1144
+ return { isValid: isQR, value: tv3 };
1145
+ };
1146
+ if (Fp.ORDER % _4n === _3n) {
1147
+ // sqrt_ratio_3mod4(u, v)
1148
+ const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
1149
+ const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
1150
+ sqrtRatio = (u: T, v: T) => {
1151
+ let tv1 = Fp.sqr(v); // 1. tv1 = v^2
1152
+ const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
1153
+ tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
1154
+ let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
1155
+ y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
1156
+ const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
1157
+ const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
1158
+ const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
1159
+ let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
1160
+ return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
1161
+ };
1162
+ }
1163
+ // No curves uses that
1164
+ // if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
1165
+ return sqrtRatio;
1166
+ }
1167
+ /**
1168
+ * Simplified Shallue-van de Woestijne-Ulas Method
1169
+ * https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
1170
+ */
1171
+ export function mapToCurveSimpleSWU<T>(
1172
+ Fp: IField<T>,
1173
+ opts: {
1174
+ A: T;
1175
+ B: T;
1176
+ Z: T;
1177
+ }
1178
+ ): (u: T) => { x: T; y: T } {
1179
+ validateField(Fp);
1180
+ const { A, B, Z } = opts;
1181
+ if (!Fp.isValid(A) || !Fp.isValid(B) || !Fp.isValid(Z))
1182
+ throw new Error('mapToCurveSimpleSWU: invalid opts');
1183
+ const sqrtRatio = SWUFpSqrtRatio(Fp, Z);
1184
+ if (!Fp.isOdd) throw new Error('Field does not have .isOdd()');
1185
+ // Input: u, an element of F.
1186
+ // Output: (x, y), a point on E.
1187
+ return (u: T): { x: T; y: T } => {
1188
+ // prettier-ignore
1189
+ let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
1190
+ tv1 = Fp.sqr(u); // 1. tv1 = u^2
1191
+ tv1 = Fp.mul(tv1, Z); // 2. tv1 = Z * tv1
1192
+ tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
1193
+ tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
1194
+ tv3 = Fp.add(tv2, Fp.ONE); // 5. tv3 = tv2 + 1
1195
+ tv3 = Fp.mul(tv3, B); // 6. tv3 = B * tv3
1196
+ tv4 = Fp.cmov(Z, Fp.neg(tv2), !Fp.eql(tv2, Fp.ZERO)); // 7. tv4 = CMOV(Z, -tv2, tv2 != 0)
1197
+ tv4 = Fp.mul(tv4, A); // 8. tv4 = A * tv4
1198
+ tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
1199
+ tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
1200
+ tv5 = Fp.mul(tv6, A); // 11. tv5 = A * tv6
1201
+ tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
1202
+ tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
1203
+ tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
1204
+ tv5 = Fp.mul(tv6, B); // 15. tv5 = B * tv6
1205
+ tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
1206
+ x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
1207
+ const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
1208
+ y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
1209
+ y = Fp.mul(y, value); // 20. y = y * y1
1210
+ x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
1211
+ y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
1212
+ const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
1213
+ y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
1214
+ const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
1215
+ x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
1216
+ return { x, y };
1217
+ };
1218
+ }
1219
+
1220
+ /**
1221
+ * Creates ECDSA for given elliptic curve Point and hash function.
1222
+ */
1223
+ export function ecdsa(
1224
+ Point: WeierstrassPointCons<bigint>,
1225
+ hash: CHash,
1226
+ ecdsaOpts: ECDSAOpts = {}
1227
+ ): ECDSA {
1228
+ ahash(hash);
1229
+ _validateObject(
1230
+ ecdsaOpts,
1231
+ {},
1232
+ {
1233
+ hmac: 'function',
1234
+ lowS: 'boolean',
1235
+ randomBytes: 'function',
1236
+ bits2int: 'function',
1237
+ bits2int_modN: 'function',
1238
+ }
1239
+ );
1240
+
1241
+ const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
1242
+ const hmac_: HmacFnSync =
1243
+ ecdsaOpts.hmac ||
1244
+ (((key, ...msgs) => hmac(hash, key, concatBytes(...msgs))) satisfies HmacFnSync);
1245
+
1246
+ const { Fp, Fn } = Point;
1247
+ const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
1248
+
1249
+ const seedLen = getMinHashLength(CURVE_ORDER);
1250
+ const lengths = {
1251
+ secret: Fn.BYTES,
1252
+ public: 1 + Fp.BYTES,
1253
+ publicUncompressed: 1 + 2 * Fp.BYTES,
1254
+ signature: 2 * Fn.BYTES,
1255
+ seed: seedLen,
1256
+ };
1257
+
1258
+ function isBiggerThanHalfOrder(number: bigint) {
1259
+ const HALF = CURVE_ORDER >> _1n;
1260
+ return number > HALF;
1261
+ }
1262
+
1263
+ function normalizeS(s: bigint) {
1264
+ return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
1265
+ }
1266
+ function aValidRS(title: string, num: bigint) {
1267
+ if (!Fn.isValidNot0(num))
1268
+ throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
1269
+ }
1270
+
1271
+ /**
1272
+ * ECDSA signature with its (r, s) properties. Supports DER & compact representations.
1273
+ */
1274
+ class Signature implements ECDSASignature {
1275
+ readonly r: bigint;
1276
+ readonly s: bigint;
1277
+ readonly recovery?: number;
1278
+ constructor(r: bigint, s: bigint, recovery?: number) {
1279
+ aValidRS('r', r); // r in [1..N-1]
1280
+ aValidRS('s', s); // s in [1..N-1]
1281
+ this.r = r;
1282
+ this.s = s;
1283
+ if (recovery != null) this.recovery = recovery;
1284
+ Object.freeze(this);
1285
+ }
1286
+
1287
+ static fromBytes(bytes: Uint8Array, format: ECDSASigFormat = 'compact') {
1288
+ if (format === 'compact') {
1289
+ const L = Fn.BYTES;
1290
+ abytes(bytes, L * 2);
1291
+ const r = bytes.subarray(0, L);
1292
+ const s = bytes.subarray(L, L * 2);
1293
+ return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
1294
+ }
1295
+ if (format === 'der') {
1296
+ abytes(bytes);
1297
+ const { r, s } = DER.toSig(bytes);
1298
+ return new Signature(r, s);
1299
+ }
1300
+ throw new Error('invalid format');
1301
+ }
1302
+
1303
+ static fromHex(hex: string, format?: ECDSASigFormat) {
1304
+ return this.fromBytes(hexToBytes(hex), format);
1305
+ }
1306
+
1307
+ addRecoveryBit(recovery: number): RecoveredSignature {
1308
+ return new Signature(this.r, this.s, recovery) as RecoveredSignature;
1309
+ }
1310
+
1311
+ // ProjPointType<bigint>
1312
+ recoverPublicKey(msgHash: Hex): typeof Point.BASE {
1313
+ const FIELD_ORDER = Fp.ORDER;
1314
+ const { r, s, recovery: rec } = this;
1315
+ if (rec == null || ![0, 1, 2, 3].includes(rec)) throw new Error('recovery id invalid');
1316
+
1317
+ // ECDSA recovery is hard for cofactor > 1 curves.
1318
+ // In sign, `r = q.x mod n`, and here we recover q.x from r.
1319
+ // While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
1320
+ // However, for cofactor>1, r+n may not get q.x:
1321
+ // r+n*i would need to be done instead where i is unknown.
1322
+ // To easily get i, we either need to:
1323
+ // a. increase amount of valid recid values (4, 5...); OR
1324
+ // b. prohibit non-prime-order signatures (recid > 1).
1325
+ const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
1326
+ if (hasCofactor && rec > 1) throw new Error('recovery id is ambiguous for h>1 curve');
1327
+
1328
+ const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
1329
+ if (!Fp.isValid(radj)) throw new Error('recovery id 2 or 3 invalid');
1330
+ const x = Fp.toBytes(radj);
1331
+ const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
1332
+ const ir = Fn.inv(radj); // r^-1
1333
+ const h = bits2int_modN(ensureBytes('msgHash', msgHash)); // Truncate hash
1334
+ const u1 = Fn.create(-h * ir); // -hr^-1
1335
+ const u2 = Fn.create(s * ir); // sr^-1
1336
+ // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
1337
+ const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
1338
+ if (Q.is0()) throw new Error('point at infinify');
1001
1339
  Q.assertValidity();
1002
1340
  return Q;
1003
1341
  }
@@ -1008,106 +1346,117 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1008
1346
  }
1009
1347
 
1010
1348
  normalizeS() {
1011
- return this.hasHighS() ? new Signature(this.r, modN(-this.s), this.recovery) : this;
1349
+ return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
1350
+ }
1351
+
1352
+ toBytes(format: ECDSASigFormat = 'compact') {
1353
+ if (format === 'compact') return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
1354
+ if (format === 'der') return hexToBytes(DER.hexFromSig(this));
1355
+ throw new Error('invalid format');
1356
+ }
1357
+
1358
+ toHex(format?: ECDSASigFormat) {
1359
+ return bytesToHex(this.toBytes(format));
1012
1360
  }
1013
1361
 
1014
- // DER-encoded
1362
+ // TODO: remove
1363
+ assertValidity(): void {}
1364
+ static fromCompact(hex: Hex) {
1365
+ return Signature.fromBytes(ensureBytes('sig', hex), 'compact');
1366
+ }
1367
+ static fromDER(hex: Hex) {
1368
+ return Signature.fromBytes(ensureBytes('sig', hex), 'der');
1369
+ }
1015
1370
  toDERRawBytes() {
1016
- return hexToBytes(this.toDERHex());
1371
+ return this.toBytes('der');
1017
1372
  }
1018
1373
  toDERHex() {
1019
- return DER.hexFromSig(this);
1374
+ return bytesToHex(this.toBytes('der'));
1020
1375
  }
1021
-
1022
- // padded bytes of r, then padded bytes of s
1023
1376
  toCompactRawBytes() {
1024
- return hexToBytes(this.toCompactHex());
1377
+ return this.toBytes('compact');
1025
1378
  }
1026
1379
  toCompactHex() {
1027
- const l = nByteLength;
1028
- return numToSizedHex(this.r, l) + numToSizedHex(this.s, l);
1380
+ return bytesToHex(this.toBytes('compact'));
1029
1381
  }
1030
1382
  }
1031
1383
  type RecoveredSignature = Signature & { recovery: number };
1032
1384
 
1033
- const utils = {
1034
- isValidPrivateKey(privateKey: PrivKey) {
1035
- try {
1036
- normPrivateKeyToScalar(privateKey);
1037
- return true;
1038
- } catch (error) {
1039
- return false;
1040
- }
1041
- },
1042
- normPrivateKeyToScalar: normPrivateKeyToScalar,
1043
-
1044
- /**
1045
- * Produces cryptographically secure private key from random of size
1046
- * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1047
- */
1048
- randomPrivateKey: (): Uint8Array => {
1049
- const length = getMinHashLength(CURVE.n);
1050
- return mapHashToField(CURVE.randomBytes(length), CURVE.n);
1051
- },
1385
+ function isValidSecretKey(privateKey: PrivKey) {
1386
+ try {
1387
+ return !!_normFnElement(Fn, privateKey);
1388
+ } catch (error) {
1389
+ return false;
1390
+ }
1391
+ }
1392
+ function isValidPublicKey(publicKey: Uint8Array, isCompressed?: boolean): boolean {
1393
+ try {
1394
+ const l = publicKey.length;
1395
+ if (isCompressed === true && l !== lengths.public) return false;
1396
+ if (isCompressed === false && l !== lengths.publicUncompressed) return false;
1397
+ return !!Point.fromBytes(publicKey);
1398
+ } catch (error) {
1399
+ return false;
1400
+ }
1401
+ }
1402
+ /**
1403
+ * Produces cryptographically secure secret key from random of size
1404
+ * (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
1405
+ */
1406
+ function randomSecretKey(seed = randomBytes_(seedLen)): Uint8Array {
1407
+ return mapHashToField(seed, CURVE_ORDER);
1408
+ }
1052
1409
 
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
- 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;
1410
+ const utils = {
1411
+ isValidSecretKey,
1412
+ isValidPublicKey,
1413
+ randomSecretKey,
1414
+
1415
+ // TODO: remove
1416
+ isValidPrivateKey: isValidSecretKey,
1417
+ randomPrivateKey: randomSecretKey,
1418
+ normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
1419
+ precompute(windowSize = 8, point = Point.BASE): WeierstrassPoint<bigint> {
1420
+ return point.precompute(windowSize, false);
1065
1421
  },
1066
1422
  };
1067
1423
 
1068
1424
  /**
1069
- * Computes public key for a private key. Checks for validity of the private key.
1070
- * @param privateKey private key
1425
+ * Computes public key for a secret key. Checks for validity of the secret key.
1071
1426
  * @param isCompressed whether to return compact (default), or full key
1072
1427
  * @returns Public key, full when isCompressed=false; short when isCompressed=true
1073
1428
  */
1074
- function getPublicKey(privateKey: PrivKey, isCompressed = true): Uint8Array {
1075
- return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
1429
+ function getPublicKey(secretKey: PrivKey, isCompressed = true): Uint8Array {
1430
+ return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
1076
1431
  }
1077
1432
 
1078
1433
  /**
1079
1434
  * Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
1080
1435
  */
1081
1436
  function isProbPub(item: PrivKey | PubKey): boolean | undefined {
1437
+ // TODO: remove
1082
1438
  if (typeof item === 'bigint') return false;
1439
+ // TODO: remove
1083
1440
  if (item instanceof Point) return true;
1084
- 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) {
1090
- return undefined;
1091
- } else {
1092
- return len === compLen || len === uncompLen;
1093
- }
1441
+ if (Fn.allowedLengths || lengths.secret === lengths.public) return undefined;
1442
+ const l = ensureBytes('key', item).length;
1443
+ return l === lengths.public || l === lengths.publicUncompressed;
1094
1444
  }
1095
1445
 
1096
1446
  /**
1097
1447
  * ECDH (Elliptic Curve Diffie Hellman).
1098
- * Computes shared public key from private key and public key.
1099
- * Checks: 1) private key validity 2) shared key is on-curve.
1448
+ * Computes shared public key from secret key A and public key B.
1449
+ * Checks: 1) secret key validity 2) shared key is on-curve.
1100
1450
  * Does NOT hash the result.
1101
- * @param privateA private key
1102
- * @param publicB different public key
1103
1451
  * @param isCompressed whether to return compact (default), or full key
1104
1452
  * @returns shared public key
1105
1453
  */
1106
- function getSharedSecret(privateA: PrivKey, publicB: Hex, isCompressed = true): Uint8Array {
1107
- if (isProbPub(privateA) === true) throw new Error('first arg must be private key');
1108
- if (isProbPub(publicB) === false) throw new Error('second arg must be public key');
1109
- const b = Point.fromHex(publicB); // check for being on-curve
1110
- return b.multiply(normPrivateKeyToScalar(privateA)).toRawBytes(isCompressed);
1454
+ function getSharedSecret(secretKeyA: PrivKey, publicKeyB: Hex, isCompressed = true): Uint8Array {
1455
+ if (isProbPub(secretKeyA) === true) throw new Error('first arg must be private key');
1456
+ if (isProbPub(publicKeyB) === false) throw new Error('second arg must be public key');
1457
+ const s = _normFnElement(Fn, secretKeyA);
1458
+ const b = Point.fromHex(publicKeyB); // checks for being on-curve
1459
+ return b.multiply(s).toBytes(isCompressed);
1111
1460
  }
1112
1461
 
1113
1462
  // RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
@@ -1115,30 +1464,30 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1115
1464
  // bits2int can produce res>N, we can do mod(res, N) since the bitLen is the same.
1116
1465
  // int2octets can't be used; pads small msgs with 0: unacceptatble for trunc as per RFC vectors
1117
1466
  const bits2int =
1118
- CURVE.bits2int ||
1467
+ ecdsaOpts.bits2int ||
1119
1468
  function (bytes: Uint8Array): bigint {
1120
1469
  // Our custom check "just in case", for protection against DoS
1121
1470
  if (bytes.length > 8192) throw new Error('input is too large');
1122
1471
  // For curves with nBitLength % 8 !== 0: bits2octets(bits2octets(m)) !== bits2octets(m)
1123
1472
  // for some cases, since bytes.length * 8 is not actual bitLength.
1124
1473
  const num = bytesToNumberBE(bytes); // check for == u8 done here
1125
- const delta = bytes.length * 8 - nBitLength; // truncate to nBitLength leftmost bits
1474
+ const delta = bytes.length * 8 - fnBits; // truncate to nBitLength leftmost bits
1126
1475
  return delta > 0 ? num >> BigInt(delta) : num;
1127
1476
  };
1128
1477
  const bits2int_modN =
1129
- CURVE.bits2int_modN ||
1478
+ ecdsaOpts.bits2int_modN ||
1130
1479
  function (bytes: Uint8Array): bigint {
1131
- return modN(bits2int(bytes)); // can't use bytesToNumberBE here
1480
+ return Fn.create(bits2int(bytes)); // can't use bytesToNumberBE here
1132
1481
  };
1133
1482
  // NOTE: pads output with zero as per spec
1134
- const ORDER_MASK = bitMask(nBitLength);
1483
+ const ORDER_MASK = bitMask(fnBits);
1135
1484
  /**
1136
1485
  * Converts to bytes. Checks if num in `[0..ORDER_MASK-1]` e.g.: `[0..2^256-1]`.
1137
1486
  */
1138
1487
  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);
1488
+ // IMPORTANT: the check ensures working for case `Fn.BYTES != Fn.BITS * 8`
1489
+ aInRange('num < 2^' + fnBits, num, _0n, ORDER_MASK);
1490
+ return Fn.toBytes(num);
1142
1491
  }
1143
1492
 
1144
1493
  // Steps A, D of RFC6979 3.2
@@ -1149,7 +1498,6 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1149
1498
  function prepSig(msgHash: Hex, privateKey: PrivKey, opts = defaultSigOpts) {
1150
1499
  if (['recovered', 'canonical'].some((k) => k in opts))
1151
1500
  throw new Error('sign() legacy options not supported');
1152
- const { hash, randomBytes } = CURVE;
1153
1501
  let { lowS, prehash, extraEntropy: ent } = opts; // generates low-s sigs by default
1154
1502
  if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash
1155
1503
  msgHash = ensureBytes('msgHash', msgHash);
@@ -1157,32 +1505,37 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1157
1505
  if (prehash) msgHash = ensureBytes('prehashed msgHash', hash(msgHash));
1158
1506
 
1159
1507
  // 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.
1508
+ // with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
1161
1509
  // const bits2octets = (bits) => int2octets(bits2int_modN(bits))
1162
1510
  const h1int = bits2int_modN(msgHash);
1163
- const d = normPrivateKeyToScalar(privateKey); // validate private key, convert to bigint
1511
+ const d = _normFnElement(Fn, privateKey); // validate secret key, convert to bigint
1164
1512
  const seedArgs = [int2octets(d), int2octets(h1int)];
1165
1513
  // extraEntropy. RFC6979 3.6: additional k' (optional).
1166
1514
  if (ent != null && ent !== false) {
1167
1515
  // 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
1516
+ const e = ent === true ? randomBytes_(lengths.secret) : ent; // gen random bytes OR pass as-is
1169
1517
  seedArgs.push(ensureBytes('extraEntropy', e)); // check for being bytes
1170
1518
  }
1171
1519
  const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
1172
1520
  const m = h1int; // NOTE: no need to call bits2int second time here, it is inside truncateHash!
1173
1521
  // Converts signature params into point w r/s, checks result for validity.
1522
+ // To transform k => Signature:
1523
+ // q = k⋅G
1524
+ // r = q.x mod n
1525
+ // s = k^-1(m + rd) mod n
1526
+ // Can use scalar blinding b^-1(bm + bdr) where b ∈ [1,q−1] according to
1527
+ // https://tches.iacr.org/index.php/TCHES/article/view/7337/6509. We've decided against it:
1528
+ // a) dependency on CSPRNG b) 15% slowdown c) doesn't really help since bigints are not CT
1174
1529
  function k2sig(kBytes: Uint8Array): RecoveredSignature | undefined {
1175
1530
  // RFC 6979 Section 3.2, step 3: k = bits2int(T)
1531
+ // Important: all mod() calls here must be done over N
1176
1532
  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
1179
- const q = Point.BASE.multiply(k).toAffine(); // q = Gk
1180
- const r = modN(q.x); // r = q.x mod n
1533
+ if (!Fn.isValidNot0(k)) return; // Valid scalars (including k) must be in 1..N-1
1534
+ const ik = Fn.inv(k); // k^-1 mod n
1535
+ const q = Point.BASE.multiply(k).toAffine(); // q = k⋅G
1536
+ const r = Fn.create(q.x); // r = q.x mod n
1181
1537
  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
1538
+ const s = Fn.create(ik * Fn.create(m + r * d)); // Not using blinding here, see comment above
1186
1539
  if (s === _0n) return;
1187
1540
  let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); // recovery bit (2 or 3, when q.x > n)
1188
1541
  let normS = s;
@@ -1194,32 +1547,26 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1194
1547
  }
1195
1548
  return { seed, k2sig };
1196
1549
  }
1197
- const defaultSigOpts: SignOpts = { lowS: CURVE.lowS, prehash: false };
1198
- const defaultVerOpts: VerOpts = { lowS: CURVE.lowS, prehash: false };
1550
+ const defaultSigOpts: SignOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1551
+ const defaultVerOpts: VerOpts = { lowS: ecdsaOpts.lowS, prehash: false };
1199
1552
 
1200
1553
  /**
1201
- * Signs message hash with a private key.
1554
+ * Signs message hash with a secret key.
1202
1555
  * ```
1203
1556
  * sign(m, d, k) where
1204
1557
  * (x, y) = G × k
1205
1558
  * r = x mod n
1206
1559
  * s = (m + dr)/k mod n
1207
1560
  * ```
1208
- * @param msgHash NOT message. msg needs to be hashed to `msgHash`, or use `prehash`.
1209
- * @param privKey private key
1210
- * @param opts lowS for non-malleable sigs. extraEntropy for mixing randomness into k. prehash will hash first arg.
1211
- * @returns signature with recovery param
1212
1561
  */
1213
- function sign(msgHash: Hex, privKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
1214
- 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);
1562
+ function sign(msgHash: Hex, secretKey: PrivKey, opts = defaultSigOpts): RecoveredSignature {
1563
+ const { seed, k2sig } = prepSig(msgHash, secretKey, opts); // Steps A, D of RFC6979 3.2.
1564
+ const drbg = createHmacDrbg<RecoveredSignature>(hash.outputLen, Fn.BYTES, hmac_);
1217
1565
  return drbg(seed, k2sig); // Steps B, C, D, E, F, G
1218
1566
  }
1219
1567
 
1220
1568
  // Enable precomputes. Slows down first publicKey computation by 20ms.
1221
- Point.BASE._setWindowSize(8);
1222
- // utils.precompute(8, ProjectivePoint.BASE)
1569
+ Point.BASE.precompute(8);
1223
1570
 
1224
1571
  /**
1225
1572
  * Verifies a signature against message hash and public key.
@@ -1243,195 +1590,188 @@ export function weierstrass(curveDef: CurveType): CurveFn {
1243
1590
  const sg = signature;
1244
1591
  msgHash = ensureBytes('msgHash', msgHash);
1245
1592
  publicKey = ensureBytes('publicKey', publicKey);
1246
- const { lowS, prehash, format } = opts;
1247
1593
 
1248
- // Verify opts, deduce signature format
1594
+ // Verify opts
1249
1595
  validateSigVerOpts(opts);
1596
+ const { lowS, prehash, format } = opts;
1597
+
1598
+ // TODO: remove
1250
1599
  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');
1253
- const isHex = typeof sg === 'string' || isBytes(sg);
1254
- const isObj =
1255
- !isHex &&
1256
- !format &&
1257
- typeof sg === 'object' &&
1258
- sg !== null &&
1259
- typeof sg.r === 'bigint' &&
1260
- typeof sg.s === 'bigint';
1261
- if (!isHex && !isObj)
1262
- throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1263
1600
 
1264
1601
  let _sig: Signature | undefined = undefined;
1265
- let P: ProjPointType<bigint>;
1266
- try {
1267
- if (isObj) _sig = new Signature(sg.r, sg.s);
1268
- 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.
1602
+ let P: WeierstrassPoint<bigint>;
1603
+
1604
+ if (format === undefined) {
1605
+ // Try to deduce format
1606
+ const isHex = typeof sg === 'string' || isBytes(sg);
1607
+ const isObj =
1608
+ !isHex &&
1609
+ sg !== null &&
1610
+ typeof sg === 'object' &&
1611
+ typeof sg.r === 'bigint' &&
1612
+ typeof sg.s === 'bigint';
1613
+ if (!isHex && !isObj)
1614
+ throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
1615
+ if (isObj) {
1616
+ _sig = new Signature(sg.r, sg.s);
1617
+ } else if (isHex) {
1618
+ // TODO: remove this malleable check
1619
+ // Signature can be represented in 2 ways: compact (2*Fn.BYTES) & DER (variable-length).
1620
+ // Since DER can also be 2*Fn.BYTES bytes, we check for it first.
1271
1621
  try {
1272
- if (format !== 'compact') _sig = Signature.fromDER(sg);
1622
+ _sig = Signature.fromDER(sg);
1273
1623
  } catch (derError) {
1274
1624
  if (!(derError instanceof DER.Err)) throw derError;
1275
1625
  }
1276
- if (!_sig && format !== 'der') _sig = Signature.fromCompact(sg);
1626
+ if (!_sig) {
1627
+ try {
1628
+ _sig = Signature.fromCompact(sg);
1629
+ } catch (error) {
1630
+ return false;
1631
+ }
1632
+ }
1277
1633
  }
1634
+ } else {
1635
+ if (format === 'compact' || format === 'der') {
1636
+ if (typeof sg !== 'string' && !isBytes(sg))
1637
+ throw new Error('"der" / "compact" format expects Uint8Array signature');
1638
+ _sig = Signature.fromBytes(ensureBytes('sig', sg), format);
1639
+ } else if (format === 'js') {
1640
+ if (!(sg instanceof Signature)) throw new Error('"js" format expects Signature instance');
1641
+ _sig = sg;
1642
+ } else {
1643
+ throw new Error('format must be "compact", "der" or "js"');
1644
+ }
1645
+ }
1646
+
1647
+ if (!_sig) return false;
1648
+ try {
1278
1649
  P = Point.fromHex(publicKey);
1279
- } catch (error) {
1650
+ if (lowS && _sig.hasHighS()) return false;
1651
+ // todo: optional.hash => hash
1652
+ if (prehash) msgHash = hash(msgHash);
1653
+ const { r, s } = _sig;
1654
+ const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
1655
+ const is = Fn.inv(s); // s^-1
1656
+ const u1 = Fn.create(h * is); // u1 = hs^-1 mod n
1657
+ const u2 = Fn.create(r * is); // u2 = rs^-1 mod n
1658
+ const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
1659
+ if (R.is0()) return false;
1660
+ const v = Fn.create(R.x); // v = r.x mod n
1661
+ return v === r;
1662
+ } catch (e) {
1280
1663
  return false;
1281
1664
  }
1282
- if (!_sig) return false;
1283
- if (lowS && _sig.hasHighS()) return false;
1284
- if (prehash) msgHash = CURVE.hash(msgHash);
1285
- const { r, s } = _sig;
1286
- 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);
1293
- return v === r;
1294
1665
  }
1295
- return {
1296
- CURVE,
1666
+
1667
+ function keygen(seed?: Uint8Array) {
1668
+ const secretKey = utils.randomSecretKey(seed);
1669
+ return { secretKey, publicKey: getPublicKey(secretKey) };
1670
+ }
1671
+
1672
+ return Object.freeze({
1673
+ keygen,
1297
1674
  getPublicKey,
1298
- getSharedSecret,
1299
1675
  sign,
1300
1676
  verify,
1301
- ProjectivePoint: Point,
1302
- Signature,
1677
+ getSharedSecret,
1303
1678
  utils,
1304
- };
1679
+ Point,
1680
+ Signature,
1681
+ info: { type: 'weierstrass' as const, lengths, publicKeyHasPrefix: true },
1682
+ });
1305
1683
  }
1306
1684
 
1307
- /**
1308
- * Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
1309
- * TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
1310
- * b = True and y = sqrt(u / v) if (u / v) is square in F, and
1311
- * b = False and y = sqrt(Z * (u / v)) otherwise.
1312
- * @param Fp
1313
- * @param Z
1314
- * @returns
1315
- */
1316
- export function SWUFpSqrtRatio<T>(
1317
- Fp: IField<T>,
1318
- Z: T
1319
- ): (u: T, v: T) => { isValid: boolean; value: T } {
1320
- // Generic implementation
1321
- const q = Fp.ORDER;
1322
- let l = _0n;
1323
- for (let o = q - _1n; o % _2n === _0n; o /= _2n) l += _1n;
1324
- const c1 = l; // 1. c1, the largest integer such that 2^c1 divides q - 1.
1325
- // We need 2n ** c1 and 2n ** (c1-1). We can't use **; but we can use <<.
1326
- // 2n ** c1 == 2n << (c1-1)
1327
- const _2n_pow_c1_1 = _2n << (c1 - _1n - _1n);
1328
- const _2n_pow_c1 = _2n_pow_c1_1 * _2n;
1329
- const c2 = (q - _1n) / _2n_pow_c1; // 2. c2 = (q - 1) / (2^c1) # Integer arithmetic
1330
- const c3 = (c2 - _1n) / _2n; // 3. c3 = (c2 - 1) / 2 # Integer arithmetic
1331
- const c4 = _2n_pow_c1 - _1n; // 4. c4 = 2^c1 - 1 # Integer arithmetic
1332
- const c5 = _2n_pow_c1_1; // 5. c5 = 2^(c1 - 1) # Integer arithmetic
1333
- const c6 = Fp.pow(Z, c2); // 6. c6 = Z^c2
1334
- const c7 = Fp.pow(Z, (c2 + _1n) / _2n); // 7. c7 = Z^((c2 + 1) / 2)
1335
- let sqrtRatio = (u: T, v: T): { isValid: boolean; value: T } => {
1336
- let tv1 = c6; // 1. tv1 = c6
1337
- let tv2 = Fp.pow(v, c4); // 2. tv2 = v^c4
1338
- let tv3 = Fp.sqr(tv2); // 3. tv3 = tv2^2
1339
- tv3 = Fp.mul(tv3, v); // 4. tv3 = tv3 * v
1340
- let tv5 = Fp.mul(u, tv3); // 5. tv5 = u * tv3
1341
- tv5 = Fp.pow(tv5, c3); // 6. tv5 = tv5^c3
1342
- tv5 = Fp.mul(tv5, tv2); // 7. tv5 = tv5 * tv2
1343
- tv2 = Fp.mul(tv5, v); // 8. tv2 = tv5 * v
1344
- tv3 = Fp.mul(tv5, u); // 9. tv3 = tv5 * u
1345
- let tv4 = Fp.mul(tv3, tv2); // 10. tv4 = tv3 * tv2
1346
- tv5 = Fp.pow(tv4, c5); // 11. tv5 = tv4^c5
1347
- let isQR = Fp.eql(tv5, Fp.ONE); // 12. isQR = tv5 == 1
1348
- tv2 = Fp.mul(tv3, c7); // 13. tv2 = tv3 * c7
1349
- tv5 = Fp.mul(tv4, tv1); // 14. tv5 = tv4 * tv1
1350
- tv3 = Fp.cmov(tv2, tv3, isQR); // 15. tv3 = CMOV(tv2, tv3, isQR)
1351
- tv4 = Fp.cmov(tv5, tv4, isQR); // 16. tv4 = CMOV(tv5, tv4, isQR)
1352
- // 17. for i in (c1, c1 - 1, ..., 2):
1353
- for (let i = c1; i > _1n; i--) {
1354
- let tv5 = i - _2n; // 18. tv5 = i - 2
1355
- tv5 = _2n << (tv5 - _1n); // 19. tv5 = 2^tv5
1356
- let tvv5 = Fp.pow(tv4, tv5); // 20. tv5 = tv4^tv5
1357
- const e1 = Fp.eql(tvv5, Fp.ONE); // 21. e1 = tv5 == 1
1358
- tv2 = Fp.mul(tv3, tv1); // 22. tv2 = tv3 * tv1
1359
- tv1 = Fp.mul(tv1, tv1); // 23. tv1 = tv1 * tv1
1360
- tvv5 = Fp.mul(tv4, tv1); // 24. tv5 = tv4 * tv1
1361
- tv3 = Fp.cmov(tv2, tv3, e1); // 25. tv3 = CMOV(tv2, tv3, e1)
1362
- tv4 = Fp.cmov(tvv5, tv4, e1); // 26. tv4 = CMOV(tv5, tv4, e1)
1363
- }
1364
- return { isValid: isQR, value: tv3 };
1685
+ // TODO: remove
1686
+ export type WsPointComposed<T> = {
1687
+ CURVE: WeierstrassOpts<T>;
1688
+ curveOpts: WeierstrassExtraOpts<T>;
1689
+ };
1690
+ // TODO: remove
1691
+ export type WsComposed = {
1692
+ CURVE: WeierstrassOpts<bigint>;
1693
+ hash: CHash;
1694
+ curveOpts: WeierstrassExtraOpts<bigint>;
1695
+ ecdsaOpts: ECDSAOpts;
1696
+ };
1697
+ // TODO: remove
1698
+ function _weierstrass_legacy_opts_to_new<T>(c: CurvePointsType<T>): WsPointComposed<T> {
1699
+ const CURVE: WeierstrassOpts<T> = {
1700
+ a: c.a,
1701
+ b: c.b,
1702
+ p: c.Fp.ORDER,
1703
+ n: c.n,
1704
+ h: c.h,
1705
+ Gx: c.Gx,
1706
+ Gy: c.Gy,
1365
1707
  };
1366
- if (Fp.ORDER % _4n === _3n) {
1367
- // sqrt_ratio_3mod4(u, v)
1368
- const c1 = (Fp.ORDER - _3n) / _4n; // 1. c1 = (q - 3) / 4 # Integer arithmetic
1369
- const c2 = Fp.sqrt(Fp.neg(Z)); // 2. c2 = sqrt(-Z)
1370
- sqrtRatio = (u: T, v: T) => {
1371
- let tv1 = Fp.sqr(v); // 1. tv1 = v^2
1372
- const tv2 = Fp.mul(u, v); // 2. tv2 = u * v
1373
- tv1 = Fp.mul(tv1, tv2); // 3. tv1 = tv1 * tv2
1374
- let y1 = Fp.pow(tv1, c1); // 4. y1 = tv1^c1
1375
- y1 = Fp.mul(y1, tv2); // 5. y1 = y1 * tv2
1376
- const y2 = Fp.mul(y1, c2); // 6. y2 = y1 * c2
1377
- const tv3 = Fp.mul(Fp.sqr(y1), v); // 7. tv3 = y1^2; 8. tv3 = tv3 * v
1378
- const isQR = Fp.eql(tv3, u); // 9. isQR = tv3 == u
1379
- let y = Fp.cmov(y2, y1, isQR); // 10. y = CMOV(y2, y1, isQR)
1380
- return { isValid: isQR, value: y }; // 11. return (isQR, y) isQR ? y : y*c2
1381
- };
1382
- }
1383
- // No curves uses that
1384
- // if (Fp.ORDER % _8n === _5n) // sqrt_ratio_5mod8
1385
- return sqrtRatio;
1708
+ const Fp = c.Fp;
1709
+ let allowedLengths = c.allowedPrivateKeyLengths
1710
+ ? Array.from(new Set(c.allowedPrivateKeyLengths.map((l) => Math.ceil(l / 2))))
1711
+ : undefined;
1712
+ const Fn = Field(CURVE.n, {
1713
+ BITS: c.nBitLength,
1714
+ allowedLengths: allowedLengths,
1715
+ modOnDecode: c.wrapPrivateKey,
1716
+ });
1717
+ const curveOpts: WeierstrassExtraOpts<T> = {
1718
+ Fp,
1719
+ Fn,
1720
+ allowInfinityPoint: c.allowInfinityPoint,
1721
+ endo: c.endo,
1722
+ isTorsionFree: c.isTorsionFree,
1723
+ clearCofactor: c.clearCofactor,
1724
+ fromBytes: c.fromBytes,
1725
+ toBytes: c.toBytes,
1726
+ };
1727
+ return { CURVE, curveOpts };
1386
1728
  }
1387
- /**
1388
- * Simplified Shallue-van de Woestijne-Ulas Method
1389
- * https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2
1390
- */
1391
- export function mapToCurveSimpleSWU<T>(
1392
- Fp: IField<T>,
1393
- opts: {
1394
- A: T;
1395
- B: T;
1396
- Z: T;
1397
- }
1398
- ): (u: T) => { x: T; y: T } {
1399
- validateField(Fp);
1400
- if (!Fp.isValid(opts.A) || !Fp.isValid(opts.B) || !Fp.isValid(opts.Z))
1401
- 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!');
1404
- // Input: u, an element of F.
1405
- // Output: (x, y), a point on E.
1406
- return (u: T): { x: T; y: T } => {
1407
- // prettier-ignore
1408
- let tv1, tv2, tv3, tv4, tv5, tv6, x, y;
1409
- tv1 = Fp.sqr(u); // 1. tv1 = u^2
1410
- tv1 = Fp.mul(tv1, opts.Z); // 2. tv1 = Z * tv1
1411
- tv2 = Fp.sqr(tv1); // 3. tv2 = tv1^2
1412
- tv2 = Fp.add(tv2, tv1); // 4. tv2 = tv2 + tv1
1413
- 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
1417
- tv2 = Fp.sqr(tv3); // 9. tv2 = tv3^2
1418
- tv6 = Fp.sqr(tv4); // 10. tv6 = tv4^2
1419
- tv5 = Fp.mul(tv6, opts.A); // 11. tv5 = A * tv6
1420
- tv2 = Fp.add(tv2, tv5); // 12. tv2 = tv2 + tv5
1421
- tv2 = Fp.mul(tv2, tv3); // 13. tv2 = tv2 * tv3
1422
- tv6 = Fp.mul(tv6, tv4); // 14. tv6 = tv6 * tv4
1423
- tv5 = Fp.mul(tv6, opts.B); // 15. tv5 = B * tv6
1424
- tv2 = Fp.add(tv2, tv5); // 16. tv2 = tv2 + tv5
1425
- x = Fp.mul(tv1, tv3); // 17. x = tv1 * tv3
1426
- const { isValid, value } = sqrtRatio(tv2, tv6); // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6)
1427
- y = Fp.mul(tv1, u); // 19. y = tv1 * u -> Z * u^3 * y1
1428
- y = Fp.mul(y, value); // 20. y = y * y1
1429
- x = Fp.cmov(x, tv3, isValid); // 21. x = CMOV(x, tv3, is_gx1_square)
1430
- y = Fp.cmov(y, value, isValid); // 22. y = CMOV(y, y1, is_gx1_square)
1431
- const e1 = Fp.isOdd!(u) === Fp.isOdd!(y); // 23. e1 = sgn0(u) == sgn0(y)
1432
- y = Fp.cmov(Fp.neg(y), y, e1); // 24. y = CMOV(-y, y, e1)
1433
- const tv4_inv = FpInvertBatch(Fp, [tv4], true)[0];
1434
- x = Fp.mul(x, tv4_inv); // 25. x = x / tv4
1435
- return { x, y };
1729
+ function _ecdsa_legacy_opts_to_new(c: CurveType): WsComposed {
1730
+ const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
1731
+ const ecdsaOpts: ECDSAOpts = {
1732
+ hmac: c.hmac,
1733
+ randomBytes: c.randomBytes,
1734
+ lowS: c.lowS,
1735
+ bits2int: c.bits2int,
1736
+ bits2int_modN: c.bits2int_modN,
1436
1737
  };
1738
+ return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
1739
+ }
1740
+ // TODO: remove
1741
+ function _weierstrass_new_output_to_legacy<T>(
1742
+ c: CurvePointsType<T>,
1743
+ Point: WeierstrassPointCons<T>
1744
+ ): CurvePointsRes<T> {
1745
+ const { Fp, Fn } = Point;
1746
+ // TODO: remove
1747
+ function isWithinCurveOrder(num: bigint): boolean {
1748
+ return inRange(num, _1n, Fn.ORDER);
1749
+ }
1750
+ const weierstrassEquation = _legacyHelperEquat(Fp, c.a, c.b);
1751
+ return Object.assign(
1752
+ {},
1753
+ {
1754
+ CURVE: c,
1755
+ Point: Point,
1756
+ ProjectivePoint: Point,
1757
+ normPrivateKeyToScalar: (key: PrivKey) => _normFnElement(Fn, key),
1758
+ weierstrassEquation,
1759
+ isWithinCurveOrder,
1760
+ }
1761
+ );
1762
+ }
1763
+ // TODO: remove
1764
+ function _ecdsa_new_output_to_legacy(c: CurveType, ecdsa: ECDSA): CurveFn {
1765
+ return Object.assign({}, ecdsa, {
1766
+ ProjectivePoint: ecdsa.Point,
1767
+ CURVE: c,
1768
+ });
1769
+ }
1770
+
1771
+ // _ecdsa_legacy
1772
+ export function weierstrass(c: CurveType): CurveFn {
1773
+ const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
1774
+ const Point = weierstrassN(CURVE, curveOpts);
1775
+ const signs = ecdsa(Point, hash, ecdsaOpts);
1776
+ return _ecdsa_new_output_to_legacy(c, signs);
1437
1777
  }