Skip to content

Commit a218b35

Browse files
committed
crypto/elliptic: panic when operating on invalid points
Fixes #50975 For #52182 Change-Id: I4a98d965436c7034877b8c0146bb0bd5b802d6fa Reviewed-on: https://go-review.googlesource.com/c/go/+/382995 Reviewed-by: David Chase <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 52e24b4 commit a218b35

File tree

5 files changed

+38
-31
lines changed

5 files changed

+38
-31
lines changed

src/crypto/elliptic/elliptic.go

+15
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ func GenerateKey(curve Curve, rand io.Reader) (priv []byte, x, y *big.Int, err e
7272
// SEC 1, Version 2.0, Section 2.3.3. If the point is not on the curve (or is
7373
// the conventional point at infinity), the behavior is undefined.
7474
func Marshal(curve Curve, x, y *big.Int) []byte {
75+
panicIfNotOnCurve(curve, x, y)
76+
7577
byteLen := (curve.Params().BitSize + 7) / 8
7678

7779
ret := make([]byte, 1+2*byteLen)
@@ -87,6 +89,7 @@ func Marshal(curve Curve, x, y *big.Int) []byte {
8789
// specified in SEC 1, Version 2.0, Section 2.3.3. If the point is not on the
8890
// curve (or is the conventional point at infinity), the behavior is undefined.
8991
func MarshalCompressed(curve Curve, x, y *big.Int) []byte {
92+
panicIfNotOnCurve(curve, x, y)
9093
byteLen := (curve.Params().BitSize + 7) / 8
9194
compressed := make([]byte, 1+byteLen)
9295
compressed[0] = byte(y.Bit(0)) | 2
@@ -168,6 +171,18 @@ func UnmarshalCompressed(curve Curve, data []byte) (x, y *big.Int) {
168171
return
169172
}
170173

174+
func panicIfNotOnCurve(curve Curve, x, y *big.Int) {
175+
// (0, 0) is the point at infinity by convention. It's ok to operate on it,
176+
// although IsOnCurve is documented to return false for it. See Issue 37294.
177+
if x.Sign() == 0 && y.Sign() == 0 {
178+
return
179+
}
180+
181+
if !curve.IsOnCurve(x, y) {
182+
panic("crypto/elliptic: attempted operation on invalid point")
183+
}
184+
}
185+
171186
var initonce sync.Once
172187

173188
func initAll() {

src/crypto/elliptic/elliptic_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,13 @@ func TestOffCurve(t *testing.T) {
6161
if curve.IsOnCurve(x, y) {
6262
t.Errorf("point off curve is claimed to be on the curve")
6363
}
64-
b := Marshal(curve, x, y)
64+
65+
byteLen := (curve.Params().BitSize + 7) / 8
66+
b := make([]byte, 1+2*byteLen)
67+
b[0] = 4 // uncompressed point
68+
x.FillBytes(b[1 : 1+byteLen])
69+
y.FillBytes(b[1+byteLen : 1+2*byteLen])
70+
6571
x1, y1 := Unmarshal(curve, b)
6672
if x1 != nil || y1 != nil {
6773
t.Errorf("unmarshaling a point not on the curve succeeded")

src/crypto/elliptic/nistec.go

+11-29
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package elliptic
66

77
import (
88
"crypto/elliptic/internal/nistec"
9-
"crypto/rand"
109
"errors"
1110
"math/big"
1211
)
@@ -173,39 +172,22 @@ func (curve *nistCurve[Point]) pointToAffine(p Point) (x, y *big.Int) {
173172
return x, y
174173
}
175174

176-
// randomPoint returns a random point on the curve. It's used when Add,
177-
// Double, or ScalarMult are fed a point not on the curve, which is undefined
178-
// behavior. Originally, we used to do the math on it anyway (which allows
179-
// invalid curve attacks) and relied on the caller and Unmarshal to avoid this
180-
// happening in the first place. Now, we just can't construct a nistec Point
181-
// for an invalid pair of coordinates, because that API is safer. If we panic,
182-
// we risk introducing a DoS. If we return nil, we risk a panic. If we return
183-
// the input, ecdsa.Verify might fail open. The safest course seems to be to
184-
// return a valid, random point, which hopefully won't help the attacker.
185-
func (curve *nistCurve[Point]) randomPoint() (x, y *big.Int) {
186-
_, x, y, err := GenerateKey(curve, rand.Reader)
187-
if err != nil {
188-
panic("crypto/elliptic: failed to generate random point")
189-
}
190-
return x, y
191-
}
192-
193175
func (curve *nistCurve[Point]) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
194176
p1, err := curve.pointFromAffine(x1, y1)
195177
if err != nil {
196-
return curve.randomPoint()
178+
panic("crypto/elliptic: Add was called on an invalid point")
197179
}
198180
p2, err := curve.pointFromAffine(x2, y2)
199181
if err != nil {
200-
return curve.randomPoint()
182+
panic("crypto/elliptic: Add was called on an invalid point")
201183
}
202184
return curve.pointToAffine(p1.Add(p1, p2))
203185
}
204186

205187
func (curve *nistCurve[Point]) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
206188
p, err := curve.pointFromAffine(x1, y1)
207189
if err != nil {
208-
return curve.randomPoint()
190+
panic("crypto/elliptic: Double was called on an invalid point")
209191
}
210192
return curve.pointToAffine(p.Double(p))
211193
}
@@ -228,12 +210,12 @@ func (curve *nistCurve[Point]) normalizeScalar(scalar []byte) []byte {
228210
func (curve *nistCurve[Point]) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
229211
p, err := curve.pointFromAffine(Bx, By)
230212
if err != nil {
231-
return curve.randomPoint()
213+
panic("crypto/elliptic: ScalarMult was called on an invalid point")
232214
}
233215
scalar = curve.normalizeScalar(scalar)
234216
p, err = p.ScalarMult(p, scalar)
235217
if err != nil {
236-
panic("elliptic: nistec rejected normalized scalar")
218+
panic("crypto/elliptic: nistec rejected normalized scalar")
237219
}
238220
return curve.pointToAffine(p)
239221
}
@@ -242,7 +224,7 @@ func (curve *nistCurve[Point]) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int
242224
scalar = curve.normalizeScalar(scalar)
243225
p, err := curve.newPoint().ScalarBaseMult(scalar)
244226
if err != nil {
245-
panic("elliptic: nistec rejected normalized scalar")
227+
panic("crypto/elliptic: nistec rejected normalized scalar")
246228
}
247229
return curve.pointToAffine(p)
248230
}
@@ -253,16 +235,16 @@ func (curve *nistCurve[Point]) CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x,
253235
s1 = curve.normalizeScalar(s1)
254236
q, err := curve.newPoint().ScalarBaseMult(s1)
255237
if err != nil {
256-
panic("elliptic: nistec rejected normalized scalar")
238+
panic("crypto/elliptic: nistec rejected normalized scalar")
257239
}
258240
p, err := curve.pointFromAffine(Px, Py)
259241
if err != nil {
260-
return curve.randomPoint()
242+
panic("crypto/elliptic: CombinedMult was called on an invalid point")
261243
}
262244
s2 = curve.normalizeScalar(s2)
263245
p, err = p.ScalarMult(p, s2)
264246
if err != nil {
265-
panic("elliptic: nistec rejected normalized scalar")
247+
panic("crypto/elliptic: nistec rejected normalized scalar")
266248
}
267249
return curve.pointToAffine(p.Add(p, q))
268250
}
@@ -299,15 +281,15 @@ func (curve *nistCurve[Point]) UnmarshalCompressed(data []byte) (x, y *big.Int)
299281
func bigFromDecimal(s string) *big.Int {
300282
b, ok := new(big.Int).SetString(s, 10)
301283
if !ok {
302-
panic("invalid encoding")
284+
panic("crypto/elliptic: internal error: invalid encoding")
303285
}
304286
return b
305287
}
306288

307289
func bigFromHex(s string) *big.Int {
308290
b, ok := new(big.Int).SetString(s, 16)
309291
if !ok {
310-
panic("invalid encoding")
292+
panic("crypto/elliptic: internal error: invalid encoding")
311293
}
312294
return b
313295
}

src/crypto/elliptic/nistec_p256.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func (c p256Curve) Inverse(k *big.Int) *big.Int {
2323
scalar := k.FillBytes(make([]byte, 32))
2424
inverse, err := nistec.P256OrdInverse(scalar)
2525
if err != nil {
26-
panic("elliptic: nistec rejected normalized scalar")
26+
panic("crypto/elliptic: nistec rejected normalized scalar")
2727
}
2828
return new(big.Int).SetBytes(inverse)
2929
}

src/crypto/elliptic/params.go

+4
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
9797
if specific, ok := matchesSpecificCurve(curve); ok {
9898
return specific.Add(x1, y1, x2, y2)
9999
}
100+
panicIfNotOnCurve(curve, x1, y1)
101+
panicIfNotOnCurve(curve, x2, y2)
100102

101103
z1 := zForAffine(x1, y1)
102104
z2 := zForAffine(x2, y2)
@@ -187,6 +189,7 @@ func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
187189
if specific, ok := matchesSpecificCurve(curve); ok {
188190
return specific.Double(x1, y1)
189191
}
192+
panicIfNotOnCurve(curve, x1, y1)
190193

191194
z1 := zForAffine(x1, y1)
192195
return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1))
@@ -259,6 +262,7 @@ func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.
259262
if specific, ok := matchesSpecificCurve(curve); ok {
260263
return specific.ScalarMult(Bx, By, k)
261264
}
265+
panicIfNotOnCurve(curve, Bx, By)
262266

263267
Bz := new(big.Int).SetInt64(1)
264268
x, y, z := new(big.Int), new(big.Int), new(big.Int)

0 commit comments

Comments
 (0)