Skip to content

Commit dc50683

Browse files
crypto/elliptic: upgrade from generic curve impl to specific if available
This change alters the CurveParam methods to upgrade from the generic curve implementation to the specific P224 or P256 implementations when called on the embedded CurveParams. This removes the trap of using elliptic.P224().Params() instead of elliptic.P224(), for example, which results in using the generic implementation instead of the optimized constant time one. For P224 this is done for all of the CurveParams methods, except Params, as the optimized implementation covers all these methods. For P256 this is only done for ScalarMult and ScalarBaseMult, as despite having implementations of addition and doubling they aren't exposed and instead the generic implementation is used. For P256 an additional check that there actually is a specific implementation is added, as unlike the P224 implementation the P256 one is only available on certain platforms. This change takes the simple, fast approach to checking this, it simply compares pointers. This removes the most obvious class of mistakes people make, but still allows edge cases where the embedded CurveParams pointer has been dereferenced (as seen in the unit tests) or when someone has manually constructed their own CurveParams that matches one of the standard curves. A more complex approach could be taken to also address these cases, but it would require directly comparing all of the CurveParam fields which would, in the worst case, require comparing against two standard CurveParam sets in the ScalarMult and ScalarBaseMult paths, which are likely to be the hottest already. Updates #34648 Change-Id: I82d752f979260394632905c15ffe4f65f4ffa376 Reviewed-on: https://go-review.googlesource.com/c/go/+/233939 Trust: Roland Shoemaker <[email protected]> Trust: Katie Hockman <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Filippo Valsorda <[email protected]> Reviewed-by: Katie Hockman <[email protected]>
1 parent 73d5aef commit dc50683

File tree

4 files changed

+55
-10
lines changed

4 files changed

+55
-10
lines changed

src/crypto/elliptic/elliptic.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ type Curve interface {
4040
ScalarBaseMult(k []byte) (x, y *big.Int)
4141
}
4242

43+
func matchesSpecificCurve(params *CurveParams, available ...Curve) (Curve, bool) {
44+
for _, c := range available {
45+
if params == c.Params() {
46+
return c, true
47+
}
48+
}
49+
return nil, false
50+
}
51+
4352
// CurveParams contains the parameters of an elliptic curve and also provides
4453
// a generic, non-constant time implementation of Curve.
4554
type CurveParams struct {
@@ -71,6 +80,12 @@ func (curve *CurveParams) polynomial(x *big.Int) *big.Int {
7180
}
7281

7382
func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
83+
// If there is a dedicated constant-time implementation for this curve operation,
84+
// use that instead of the generic one.
85+
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
86+
return specific.IsOnCurve(x, y)
87+
}
88+
7489
// y² = x³ - 3x + b
7590
y2 := new(big.Int).Mul(y, y)
7691
y2.Mod(y2, curve.P)
@@ -108,6 +123,12 @@ func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.
108123
}
109124

110125
func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
126+
// If there is a dedicated constant-time implementation for this curve operation,
127+
// use that instead of the generic one.
128+
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
129+
return specific.Add(x1, y1, x2, y2)
130+
}
131+
111132
z1 := zForAffine(x1, y1)
112133
z2 := zForAffine(x2, y2)
113134
return curve.affineFromJacobian(curve.addJacobian(x1, y1, z1, x2, y2, z2))
@@ -192,6 +213,12 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
192213
}
193214

194215
func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
216+
// If there is a dedicated constant-time implementation for this curve operation,
217+
// use that instead of the generic one.
218+
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
219+
return specific.Double(x1, y1)
220+
}
221+
195222
z1 := zForAffine(x1, y1)
196223
return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1))
197224
}
@@ -258,6 +285,12 @@ func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int,
258285
}
259286

260287
func (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, p224, p256, p521); ok {
291+
return specific.ScalarMult(Bx, By, k)
292+
}
293+
261294
Bz := new(big.Int).SetInt64(1)
262295
x, y, z := new(big.Int), new(big.Int), new(big.Int)
263296

@@ -275,6 +308,12 @@ func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.
275308
}
276309

277310
func (curve *CurveParams) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
311+
// If there is a dedicated constant-time implementation for this curve operation,
312+
// use that instead of the generic one.
313+
if specific, ok := matchesSpecificCurve(curve, p224, p256, p521); ok {
314+
return specific.ScalarBaseMult(k)
315+
}
316+
278317
return curve.ScalarMult(curve.Gx, curve.Gy, k)
279318
}
280319

src/crypto/elliptic/elliptic_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,29 @@ import (
1212
"testing"
1313
)
1414

15+
// genericParamsForCurve returns the dereferenced CurveParams for
16+
// the specified curve. This is used to avoid the logic for
17+
// upgrading a curve to it's specific implementation, forcing
18+
// usage of the generic implementation. This is only relevant
19+
// for the P224, P256, and P521 curves.
20+
func genericParamsForCurve(c Curve) *CurveParams {
21+
d := *(c.Params())
22+
return &d
23+
}
24+
1525
func testAllCurves(t *testing.T, f func(*testing.T, Curve)) {
1626
tests := []struct {
1727
name string
1828
curve Curve
1929
}{
2030
{"P256", P256()},
21-
{"P256/Params", P256().Params()},
31+
{"P256/Params", genericParamsForCurve(P256())},
2232
{"P224", P224()},
23-
{"P224/Params", P224().Params()},
33+
{"P224/Params", genericParamsForCurve(P224())},
2434
{"P384", P384()},
25-
{"P384/Params", P384().Params()},
35+
{"P384/Params", genericParamsForCurve(P384())},
2636
{"P521", P521()},
27-
{"P521/Params", P521().Params()},
37+
{"P521/Params", genericParamsForCurve(P521())},
2838
}
2939
if testing.Short() {
3040
tests = tests[:1]

src/crypto/elliptic/p256_asm.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@ type (
2929
}
3030
)
3131

32-
var (
33-
p256 p256Curve
34-
)
32+
var p256 p256Curve
3533

3634
func initP256() {
3735
// See FIPS 186-3, section D.2.3

src/crypto/elliptic/p256_generic.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77

88
package elliptic
99

10-
var (
11-
p256 p256Curve
12-
)
10+
var p256 p256Curve
1311

1412
func initP256Arch() {
1513
// Use pure Go implementation.

0 commit comments

Comments
 (0)