1// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package elliptic
6
7import "math/big"
8
9// CurveParams contains the parameters of an elliptic curve and also provides
10// a generic, non-constant time implementation of [Curve].
11//
12// The generic Curve implementation is deprecated, and using custom curves
13// (those not returned by [P224], [P256], [P384], and [P521]) is not guaranteed
14// to provide any security property.
15type CurveParams struct {
16	P       *big.Int // the order of the underlying field
17	N       *big.Int // the order of the base point
18	B       *big.Int // the constant of the curve equation
19	Gx, Gy  *big.Int // (x,y) of the base point
20	BitSize int      // the size of the underlying field
21	Name    string   // the canonical name of the curve
22}
23
24func (curve *CurveParams) Params() *CurveParams {
25	return curve
26}
27
28// CurveParams operates, internally, on Jacobian coordinates. For a given
29// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
30// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
31// calculation can be performed within the transform (as in ScalarMult and
32// ScalarBaseMult). But even for Add and Double, it's faster to apply and
33// reverse the transform than to operate in affine coordinates.
34
35// polynomial returns x³ - 3x + b.
36func (curve *CurveParams) polynomial(x *big.Int) *big.Int {
37	x3 := new(big.Int).Mul(x, x)
38	x3.Mul(x3, x)
39
40	threeX := new(big.Int).Lsh(x, 1)
41	threeX.Add(threeX, x)
42
43	x3.Sub(x3, threeX)
44	x3.Add(x3, curve.B)
45	x3.Mod(x3, curve.P)
46
47	return x3
48}
49
50// IsOnCurve implements [Curve.IsOnCurve].
51//
52// Deprecated: the [CurveParams] methods are deprecated and are not guaranteed to
53// provide any security property. For ECDH, use the [crypto/ecdh] package.
54// For ECDSA, use the [crypto/ecdsa] package with a [Curve] value returned directly
55// from [P224], [P256], [P384], or [P521].
56func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
57	// If there is a dedicated constant-time implementation for this curve operation,
58	// use that instead of the generic one.
59	if specific, ok := matchesSpecificCurve(curve); ok {
60		return specific.IsOnCurve(x, y)
61	}
62
63	if x.Sign() < 0 || x.Cmp(curve.P) >= 0 ||
64		y.Sign() < 0 || y.Cmp(curve.P) >= 0 {
65		return false
66	}
67
68	// y² = x³ - 3x + b
69	y2 := new(big.Int).Mul(y, y)
70	y2.Mod(y2, curve.P)
71
72	return curve.polynomial(x).Cmp(y2) == 0
73}
74
75// zForAffine returns a Jacobian Z value for the affine point (x, y). If x and
76// y are zero, it assumes that they represent the point at infinity because (0,
77// 0) is not on the any of the curves handled here.
78func zForAffine(x, y *big.Int) *big.Int {
79	z := new(big.Int)
80	if x.Sign() != 0 || y.Sign() != 0 {
81		z.SetInt64(1)
82	}
83	return z
84}
85
86// affineFromJacobian reverses the Jacobian transform. See the comment at the
87// top of the file. If the point is ∞ it returns 0, 0.
88func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
89	if z.Sign() == 0 {
90		return new(big.Int), new(big.Int)
91	}
92
93	zinv := new(big.Int).ModInverse(z, curve.P)
94	zinvsq := new(big.Int).Mul(zinv, zinv)
95
96	xOut = new(big.Int).Mul(x, zinvsq)
97	xOut.Mod(xOut, curve.P)
98	zinvsq.Mul(zinvsq, zinv)
99	yOut = new(big.Int).Mul(y, zinvsq)
100	yOut.Mod(yOut, curve.P)
101	return
102}
103
104// Add implements [Curve.Add].
105//
106// Deprecated: the [CurveParams] methods are deprecated and are not guaranteed to
107// provide any security property. For ECDH, use the [crypto/ecdh] package.
108// For ECDSA, use the [crypto/ecdsa] package with a [Curve] value returned directly
109// from [P224], [P256], [P384], or [P521].
110func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
111	// If there is a dedicated constant-time implementation for this curve operation,
112	// use that instead of the generic one.
113	if specific, ok := matchesSpecificCurve(curve); ok {
114		return specific.Add(x1, y1, x2, y2)
115	}
116	panicIfNotOnCurve(curve, x1, y1)
117	panicIfNotOnCurve(curve, x2, y2)
118
119	z1 := zForAffine(x1, y1)
120	z2 := zForAffine(x2, y2)
121	return curve.affineFromJacobian(curve.addJacobian(x1, y1, z1, x2, y2, z2))
122}
123
124// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
125// (x2, y2, z2) and returns their sum, also in Jacobian form.
126func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
127	// See https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
128	x3, y3, z3 := new(big.Int), new(big.Int), new(big.Int)
129	if z1.Sign() == 0 {
130		x3.Set(x2)
131		y3.Set(y2)
132		z3.Set(z2)
133		return x3, y3, z3
134	}
135	if z2.Sign() == 0 {
136		x3.Set(x1)
137		y3.Set(y1)
138		z3.Set(z1)
139		return x3, y3, z3
140	}
141
142	z1z1 := new(big.Int).Mul(z1, z1)
143	z1z1.Mod(z1z1, curve.P)
144	z2z2 := new(big.Int).Mul(z2, z2)
145	z2z2.Mod(z2z2, curve.P)
146
147	u1 := new(big.Int).Mul(x1, z2z2)
148	u1.Mod(u1, curve.P)
149	u2 := new(big.Int).Mul(x2, z1z1)
150	u2.Mod(u2, curve.P)
151	h := new(big.Int).Sub(u2, u1)
152	xEqual := h.Sign() == 0
153	if h.Sign() == -1 {
154		h.Add(h, curve.P)
155	}
156	i := new(big.Int).Lsh(h, 1)
157	i.Mul(i, i)
158	j := new(big.Int).Mul(h, i)
159
160	s1 := new(big.Int).Mul(y1, z2)
161	s1.Mul(s1, z2z2)
162	s1.Mod(s1, curve.P)
163	s2 := new(big.Int).Mul(y2, z1)
164	s2.Mul(s2, z1z1)
165	s2.Mod(s2, curve.P)
166	r := new(big.Int).Sub(s2, s1)
167	if r.Sign() == -1 {
168		r.Add(r, curve.P)
169	}
170	yEqual := r.Sign() == 0
171	if xEqual && yEqual {
172		return curve.doubleJacobian(x1, y1, z1)
173	}
174	r.Lsh(r, 1)
175	v := new(big.Int).Mul(u1, i)
176
177	x3.Set(r)
178	x3.Mul(x3, x3)
179	x3.Sub(x3, j)
180	x3.Sub(x3, v)
181	x3.Sub(x3, v)
182	x3.Mod(x3, curve.P)
183
184	y3.Set(r)
185	v.Sub(v, x3)
186	y3.Mul(y3, v)
187	s1.Mul(s1, j)
188	s1.Lsh(s1, 1)
189	y3.Sub(y3, s1)
190	y3.Mod(y3, curve.P)
191
192	z3.Add(z1, z2)
193	z3.Mul(z3, z3)
194	z3.Sub(z3, z1z1)
195	z3.Sub(z3, z2z2)
196	z3.Mul(z3, h)
197	z3.Mod(z3, curve.P)
198
199	return x3, y3, z3
200}
201
202// Double implements [Curve.Double].
203//
204// Deprecated: the [CurveParams] methods are deprecated and are not guaranteed to
205// provide any security property. For ECDH, use the [crypto/ecdh] package.
206// For ECDSA, use the [crypto/ecdsa] package with a [Curve] value returned directly
207// from [P224], [P256], [P384], or [P521].
208func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
209	// If there is a dedicated constant-time implementation for this curve operation,
210	// use that instead of the generic one.
211	if specific, ok := matchesSpecificCurve(curve); ok {
212		return specific.Double(x1, y1)
213	}
214	panicIfNotOnCurve(curve, x1, y1)
215
216	z1 := zForAffine(x1, y1)
217	return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1))
218}
219
220// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
221// returns its double, also in Jacobian form.
222func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
223	// See https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
224	delta := new(big.Int).Mul(z, z)
225	delta.Mod(delta, curve.P)
226	gamma := new(big.Int).Mul(y, y)
227	gamma.Mod(gamma, curve.P)
228	alpha := new(big.Int).Sub(x, delta)
229	if alpha.Sign() == -1 {
230		alpha.Add(alpha, curve.P)
231	}
232	alpha2 := new(big.Int).Add(x, delta)
233	alpha.Mul(alpha, alpha2)
234	alpha2.Set(alpha)
235	alpha.Lsh(alpha, 1)
236	alpha.Add(alpha, alpha2)
237
238	beta := alpha2.Mul(x, gamma)
239
240	x3 := new(big.Int).Mul(alpha, alpha)
241	beta8 := new(big.Int).Lsh(beta, 3)
242	beta8.Mod(beta8, curve.P)
243	x3.Sub(x3, beta8)
244	if x3.Sign() == -1 {
245		x3.Add(x3, curve.P)
246	}
247	x3.Mod(x3, curve.P)
248
249	z3 := new(big.Int).Add(y, z)
250	z3.Mul(z3, z3)
251	z3.Sub(z3, gamma)
252	if z3.Sign() == -1 {
253		z3.Add(z3, curve.P)
254	}
255	z3.Sub(z3, delta)
256	if z3.Sign() == -1 {
257		z3.Add(z3, curve.P)
258	}
259	z3.Mod(z3, curve.P)
260
261	beta.Lsh(beta, 2)
262	beta.Sub(beta, x3)
263	if beta.Sign() == -1 {
264		beta.Add(beta, curve.P)
265	}
266	y3 := alpha.Mul(alpha, beta)
267
268	gamma.Mul(gamma, gamma)
269	gamma.Lsh(gamma, 3)
270	gamma.Mod(gamma, curve.P)
271
272	y3.Sub(y3, gamma)
273	if y3.Sign() == -1 {
274		y3.Add(y3, curve.P)
275	}
276	y3.Mod(y3, curve.P)
277
278	return x3, y3, z3
279}
280
281// ScalarMult implements [Curve.ScalarMult].
282//
283// Deprecated: the [CurveParams] methods are deprecated and are not guaranteed to
284// provide any security property. For ECDH, use the [crypto/ecdh] package.
285// For ECDSA, use the [crypto/ecdsa] package with a [Curve] value returned directly
286// from [P224], [P256], [P384], or [P521].
287func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
288	// If there is a dedicated constant-time implementation for this curve operation,
289	// use that instead of the generic one.
290	if specific, ok := matchesSpecificCurve(curve); ok {
291		return specific.ScalarMult(Bx, By, k)
292	}
293	panicIfNotOnCurve(curve, Bx, By)
294
295	Bz := new(big.Int).SetInt64(1)
296	x, y, z := new(big.Int), new(big.Int), new(big.Int)
297
298	for _, byte := range k {
299		for bitNum := 0; bitNum < 8; bitNum++ {
300			x, y, z = curve.doubleJacobian(x, y, z)
301			if byte&0x80 == 0x80 {
302				x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z)
303			}
304			byte <<= 1
305		}
306	}
307
308	return curve.affineFromJacobian(x, y, z)
309}
310
311// ScalarBaseMult implements [Curve.ScalarBaseMult].
312//
313// Deprecated: the [CurveParams] methods are deprecated and are not guaranteed to
314// provide any security property. For ECDH, use the [crypto/ecdh] package.
315// For ECDSA, use the [crypto/ecdsa] package with a [Curve] value returned directly
316// from [P224], [P256], [P384], or [P521].
317func (curve *CurveParams) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
318	// If there is a dedicated constant-time implementation for this curve operation,
319	// use that instead of the generic one.
320	if specific, ok := matchesSpecificCurve(curve); ok {
321		return specific.ScalarBaseMult(k)
322	}
323
324	return curve.ScalarMult(curve.Gx, curve.Gy, k)
325}
326
327func matchesSpecificCurve(params *CurveParams) (Curve, bool) {
328	for _, c := range []Curve{p224, p256, p384, p521} {
329		if params == c.Params() {
330			return c, true
331		}
332	}
333	return nil, false
334}
335