Skip to content

Commit 3194be0

Browse files
authored
Merge pull request #626 from eljobe/fix/curvepoint-equal-mutation
Fix curvePoint.Equal() mutating its operands
2 parents 64ff188 + a050925 commit 3194be0

2 files changed

Lines changed: 101 additions & 10 deletions

File tree

group/p256/curve.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ func (P *curvePoint) Equal(P2 kyber.Point) bool {
3131

3232
// Make sure both coordinates are normalized.
3333
// Apparently Go's elliptic curve code doesn't always ensure this.
34+
// Use temporary big.Ints to avoid mutating the operands.
3435
M := P.c.p.P
35-
P.x.Mod(P.x, M)
36-
P.y.Mod(P.y, M)
37-
cp2.x.Mod(cp2.x, M)
38-
cp2.y.Mod(cp2.y, M)
36+
x1 := new(big.Int).Mod(P.x, M)
37+
y1 := new(big.Int).Mod(P.y, M)
38+
x2 := new(big.Int).Mod(cp2.x, M)
39+
y2 := new(big.Int).Mod(cp2.y, M)
3940

40-
return P.x.Cmp(cp2.x) == 0 && P.y.Cmp(cp2.y) == 0
41+
return x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0
4142
}
4243

4344
func (P *curvePoint) Null() kyber.Point {
@@ -47,8 +48,8 @@ func (P *curvePoint) Null() kyber.Point {
4748
}
4849

4950
func (P *curvePoint) Base() kyber.Point {
50-
P.x = P.c.p.Gx
51-
P.y = P.c.p.Gy
51+
P.x = new(big.Int).Set(P.c.p.Gx)
52+
P.y = new(big.Int).Set(P.c.p.Gy)
5253
return P
5354
}
5455

@@ -271,13 +272,17 @@ func (P *curvePoint) Set(A kyber.Point) kyber.Point {
271272
if !ok {
272273
panic(ErrTypeCast)
273274
}
274-
P.x = aCurvePoint.x
275-
P.y = aCurvePoint.y
275+
P.x = new(big.Int).Set(aCurvePoint.x)
276+
P.y = new(big.Int).Set(aCurvePoint.y)
276277
return P
277278
}
278279

279280
func (P *curvePoint) Clone() kyber.Point {
280-
return &curvePoint{x: P.x, y: P.y, c: P.c}
281+
return &curvePoint{
282+
x: new(big.Int).Set(P.x),
283+
y: new(big.Int).Set(P.y),
284+
c: P.c,
285+
}
281286
}
282287

283288
// Return the order of this curve: the prime N in the curve parameters.

group/p256/equal_mutation_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//go:build !constantTime
2+
3+
package p256
4+
5+
import (
6+
"math/big"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// TestEqualDoesNotMutate verifies that Equal does not modify either operand.
13+
// Regression test for https://github.com/dedis/kyber/issues/625
14+
func TestEqualDoesNotMutate(t *testing.T) {
15+
suite := NewBlakeSHA256P256()
16+
17+
a := suite.Point().Pick(suite.RandomStream()).(*curvePoint)
18+
b := suite.Point().Pick(suite.RandomStream())
19+
20+
origX := new(big.Int).Set(a.x)
21+
22+
// Make x non-normalized by adding the field modulus.
23+
// Mathematically equivalent (mod P), but numerically different.
24+
a.x.Add(a.x, a.c.p.P)
25+
require.NotEqual(t, 0, a.x.Cmp(origX), "sanity: x should differ after adding P")
26+
27+
a.Equal(b)
28+
29+
require.NotEqual(t, 0, a.x.Cmp(origX),
30+
"Equal() should not normalize the receiver's coordinates")
31+
}
32+
33+
// TestEqualDoesNotMutateArgument verifies Equal doesn't modify the argument.
34+
func TestEqualDoesNotMutateArgument(t *testing.T) {
35+
suite := NewBlakeSHA256P256()
36+
37+
a := suite.Point().Pick(suite.RandomStream())
38+
b := suite.Point().Pick(suite.RandomStream()).(*curvePoint)
39+
40+
origBX := new(big.Int).Set(b.x)
41+
b.x.Add(b.x, b.c.p.P)
42+
43+
a.Equal(b)
44+
45+
require.NotEqual(t, 0, b.x.Cmp(origBX),
46+
"Equal() should not normalize the argument's coordinates")
47+
}
48+
49+
// TestSetDeepCopies verifies that Set copies coordinate values, not pointers.
50+
func TestSetDeepCopies(t *testing.T) {
51+
suite := NewBlakeSHA256P256()
52+
53+
a := suite.Point().Pick(suite.RandomStream()).(*curvePoint)
54+
b := suite.Point().(*curvePoint)
55+
b.c = a.c
56+
b.Set(a)
57+
58+
require.NotSame(t, a.x, b.x, "Set should deep-copy x, not alias the pointer")
59+
require.NotSame(t, a.y, b.y, "Set should deep-copy y, not alias the pointer")
60+
require.True(t, a.Equal(b), "Set copy should be equal to original")
61+
}
62+
63+
// TestCloneDeepCopies verifies that Clone copies coordinate values, not pointers.
64+
func TestCloneDeepCopies(t *testing.T) {
65+
suite := NewBlakeSHA256P256()
66+
67+
a := suite.Point().Pick(suite.RandomStream()).(*curvePoint)
68+
b := a.Clone().(*curvePoint)
69+
70+
require.NotSame(t, a.x, b.x, "Clone should deep-copy x, not alias the pointer")
71+
require.NotSame(t, a.y, b.y, "Clone should deep-copy y, not alias the pointer")
72+
require.True(t, a.Equal(b), "Clone should be equal to original")
73+
}
74+
75+
// TestBaseDeepCopies verifies that Base does not alias the curve's global
76+
// generator coordinates.
77+
func TestBaseDeepCopies(t *testing.T) {
78+
suite := NewBlakeSHA256P256()
79+
80+
base := suite.Point().Base().(*curvePoint)
81+
82+
require.NotSame(t, base.c.p.Gx, base.x,
83+
"Base should deep-copy Gx, not alias the curve parameter")
84+
require.NotSame(t, base.c.p.Gy, base.y,
85+
"Base should deep-copy Gy, not alias the curve parameter")
86+
}

0 commit comments

Comments
 (0)