Skip to content

Commit b1f9861

Browse files
committed
Adding tests for detecting decaf/point invalid encodings.
1 parent 0fea778 commit b1f9861

File tree

7 files changed

+93
-29
lines changed

7 files changed

+93
-29
lines changed

ecc/goldilocks/constants.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,5 @@ var (
9494
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
9595
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
9696
}
97-
errInvalidDecoding = errors.New("invalid decoding")
97+
ErrInvalidDecoding = errors.New("invalid decoding")
9898
)

ecc/goldilocks/decaf.go

+20-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package goldilocks
22

3-
import fp "github.com/cloudflare/circl/math/fp448"
3+
import (
4+
"unsafe"
5+
6+
fp "github.com/cloudflare/circl/math/fp448"
7+
)
48

59
// Decaf provides operations of a prime-order group from goldilocks curve.
610
// Its internal implementation uses the twist of the goldilocks curve.
@@ -57,14 +61,14 @@ func (e *Elt) IsEqual(a *Elt) bool {
5761
// DecafEncodingSize bytes of data.
5862
func (e *Elt) UnmarshalBinary(data []byte) error {
5963
if len(data) < DecafEncodingSize {
60-
return errInvalidDecoding
64+
return ErrInvalidDecoding
6165
}
6266

6367
s := &fp.Elt{}
6468
copy(s[:], data[:DecafEncodingSize])
65-
isPositiveS := fp.Parity(s) == 0
6669
p := fp.P()
6770
isLessThanP := isLessThan(s[:], p[:])
71+
isPositiveS := fp.Parity(s) == 0
6872

6973
s2, den, num := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
7074
isr, altx := &fp.Elt{}, &fp.Elt{}
@@ -95,11 +99,20 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
9599
fp.Mul(y, isr, den) // y = isr*den
96100
fp.Add(t0, &one, s2) // t0 = 1 - a*s^2
97101
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den
98-
if !(isPositiveS && isLessThanP && isQR) {
99-
return errInvalidDecoding
102+
103+
isValid := isPositiveS && isLessThanP && isQR
104+
b := *((*uint)(unsafe.Pointer(&isValid)))
105+
fp.Cmov(&e.p.x, x, b)
106+
fp.Cmov(&e.p.y, y, b)
107+
fp.Cmov(&e.p.ta, x, b)
108+
fp.Cmov(&e.p.tb, y, b)
109+
fp.Cmov(&e.p.z, &one, b)
110+
111+
var err error
112+
if !isValid {
113+
err = ErrInvalidDecoding
100114
}
101-
e.p.x, e.p.y, e.p.ta, e.p.tb, e.p.z = *x, *y, *x, *y, one
102-
return nil
115+
return err
103116
}
104117

105118
// MarshalBinary returns a unique encoding of the element e.

ecc/goldilocks/decaf_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/cloudflare/circl/ecc/goldilocks"
1313
"github.com/cloudflare/circl/internal/test"
14+
fp "github.com/cloudflare/circl/math/fp448"
1415
)
1516

1617
type testJSONFile struct {
@@ -119,6 +120,28 @@ func TestDecafRandom(t *testing.T) {
119120
}
120121
}
121122

123+
func TestDecafInvalid(t *testing.T) {
124+
bigS := fp.P()
125+
negativeS := fp.Elt{1} // the smallest s that is negative
126+
nonQR := fp.Elt{4} // the shortest s such that (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR.
127+
128+
badEncodings := [][]byte{
129+
{}, // wrong size input
130+
bigS[:], // s is out of the interval [0,p-1].
131+
negativeS[:], // s is not positive
132+
nonQR[:], // s=4 and (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR.
133+
}
134+
135+
var e goldilocks.Elt
136+
for _, enc := range badEncodings {
137+
got := e.UnmarshalBinary(enc)
138+
want := goldilocks.ErrInvalidDecoding
139+
if got != want {
140+
test.ReportError(t, got, want, enc)
141+
}
142+
}
143+
}
144+
122145
func BenchmarkDecaf(b *testing.B) {
123146
var d goldilocks.Decaf
124147
var k, l goldilocks.Scalar

ecc/goldilocks/doc.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
//
2929
// Both Goldilocks and Decaf use as internal representation the curve
3030
// 4Iso-Goldilocks: ax^2+y^2 = 1 + dx^2y^2, where a=-1 and d=-39082.
31-
// This curve is 4-degree isogeous to the Goldilocks curve, and 2-degree
31+
// This curve is 4-degree isogeneous to the Goldilocks curve, and 2-degree
3232
// isogeneous to the Jacobi quartic. The 4Iso-Goldilocks curve was chosen as
3333
// provides faster arithmetic operations.
3434
//

ecc/goldilocks/point.go

+16-15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package goldilocks
22

33
import (
44
"fmt"
5+
"unsafe"
56

67
fp "github.com/cloudflare/circl/math/fp448"
78
)
@@ -13,15 +14,6 @@ func (P Point) String() string {
1314
return fmt.Sprintf("x: %v\ny: %v\nz: %v\nta: %v\ntb: %v", P.x, P.y, P.z, P.ta, P.tb)
1415
}
1516

16-
// // FromAffine creates a point from affine coordinates.
17-
// func FromAffine(x, y *fp.Elt) (*Point, error) {
18-
// P := &Point{x: *x, y: *y, z: fp.One(), ta: *x, tb: *y}
19-
// if !(Curve{}).IsOnCurve(P) {
20-
// return nil, errors.New("point not on curve")
21-
// }
22-
// return P, nil
23-
// }
24-
2517
// isLessThan returns true if 0 <= x < y, and assumes that slices are of the
2618
// same length and are interpreted in little-endian order.
2719
func isLessThan(x, y []byte) bool {
@@ -110,7 +102,7 @@ func (P *Point) Add(Q *Point) {
110102
// CurveEncodingSize bytes of data.
111103
func (P *Point) UnmarshalBinary(data []byte) error {
112104
if len(data) < CurveEncodingSize {
113-
return errInvalidDecoding
105+
return ErrInvalidDecoding
114106
}
115107

116108
x, y := &fp.Elt{}, &fp.Elt{}
@@ -123,17 +115,26 @@ func (P *Point) UnmarshalBinary(data []byte) error {
123115
one := fp.One()
124116
fp.Sqr(u, y) // u = y^2
125117
fp.Mul(v, u, &paramD) // v = dy^2
126-
fp.Sub(u, u, &one) // u = y^2-a
118+
fp.Sub(u, u, &one) // u = y^2-1
127119
fp.Sub(v, v, &one) // v = dy^2-a
128120
isQR := fp.InvSqrt(x, u, v) // x = sqrt(u/v)
129121
isValidXSign := !(fp.IsZero(x) && signX == 1)
130122
fp.Neg(u, x) // u = -x
131123
fp.Cmov(x, u, uint(signX^(x[0]&1))) // if signX != x mod 2
132-
if !(isLessThanP && isQR && isValidXSign) {
133-
return errInvalidDecoding
124+
125+
isValid := isLessThanP && isQR && isValidXSign
126+
b := *((*uint)(unsafe.Pointer(&isValid)))
127+
fp.Cmov(&P.x, x, b)
128+
fp.Cmov(&P.y, y, b)
129+
fp.Cmov(&P.ta, x, b)
130+
fp.Cmov(&P.tb, y, b)
131+
fp.Cmov(&P.z, &one, b)
132+
133+
var err error
134+
if !isValid {
135+
err = ErrInvalidDecoding
134136
}
135-
P.x, P.y, P.ta, P.tb, P.z = *x, *y, *x, *y, one
136-
return nil
137+
return err
137138
}
138139

139140
// MarshalBinary returns a unique encoding of the point P.

ecc/goldilocks/point_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/cloudflare/circl/ecc/goldilocks"
99
"github.com/cloudflare/circl/internal/test"
10+
fp "github.com/cloudflare/circl/math/fp448"
1011
)
1112

1213
func randomPoint() *goldilocks.Point {
@@ -75,3 +76,30 @@ func TestPointMarshal(t *testing.T) {
7576
}
7677
}
7778
}
79+
80+
func TestPointInvalid(t *testing.T) {
81+
p := fp.P()
82+
one := fp.One()
83+
84+
var bigY, wrongSignX, nonQR [goldilocks.CurveEncodingSize]byte
85+
copy(bigY[:], p[:])
86+
copy(wrongSignX[:], one[:])
87+
wrongSignX[goldilocks.CurveEncodingSize-1] = 1 << 7
88+
nonQR[0] = 2 // smallest y such that (y^2+a)/(dy^2-a) is not a square.
89+
90+
badEncodings := [][]byte{
91+
{}, // wrong size input.
92+
bigY[:], // y is out of the interval [0,p-1].
93+
wrongSignX[:], // x has wrong sign.
94+
nonQR[:], // y=2 and (y^2+a)/(dy^2-a) is not a square.
95+
}
96+
97+
var P goldilocks.Point
98+
for _, enc := range badEncodings {
99+
got := P.UnmarshalBinary(enc)
100+
want := goldilocks.ErrInvalidDecoding
101+
if got != want {
102+
test.ReportError(t, got, want, enc)
103+
}
104+
}
105+
}

ecc/goldilocks/twist.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package goldilocks
33
import (
44
"crypto/subtle"
55
"math/bits"
6+
"unsafe"
67

78
"github.com/cloudflare/circl/internal/conv"
89
"github.com/cloudflare/circl/math"
@@ -83,11 +84,9 @@ func (e twistCurve) ScalarMult(R *twistPoint, k *Scalar, P *twistPoint) {
8384

8485
kk := *k
8586

86-
var isZero int
87-
if kk.IsZero() {
88-
isZero = 1
89-
}
90-
subtle.ConstantTimeCopy(isZero, kk[:], order[:])
87+
isZero := kk.IsZero()
88+
isZeroInt := *(*int)(unsafe.Pointer(&isZero))
89+
subtle.ConstantTimeCopy(isZeroInt, kk[:], order[:])
9190

9291
minusK := kk
9392
isEven := 1 - int(kk[0]&0x1)

0 commit comments

Comments
 (0)