Skip to content

Commit f78a675

Browse files
committed
Returning int instead of bool in fp448 predicates.
1 parent 87b9a72 commit f78a675

File tree

10 files changed

+162
-87
lines changed

10 files changed

+162
-87
lines changed

ecc/decaf/constants.go group/decaf448/constants.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package decaf
1+
package decaf448
22

33
import (
44
"errors"

ecc/decaf/decaf.go group/decaf448/decaf.go

+22-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Package decaf provides a prime-order group derived from a quotient of
1+
// Package decaf448 provides a prime-order group derived from a quotient of
22
// Edwards curves.
33
//
44
// Decaf Group
@@ -29,10 +29,11 @@
2929
// (4) https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/
3030
//
3131
// (5) https://mailarchive.ietf.org/arch/msg/cfrg/S4YUTt_5eD4kwYbDuhEK0tXT1aM/
32-
package decaf
32+
package decaf448
3333

3434
import (
35-
"unsafe"
35+
"crypto/subtle"
36+
"math/bits"
3637

3738
"github.com/cloudflare/circl/internal/ted448"
3839
fp "github.com/cloudflare/circl/math/fp448"
@@ -79,15 +80,20 @@ func Mul(c *Elt, n *Scalar, a *Elt) { ted448.ScalarMult(&c.p, n, &a.p) }
7980
func MulGen(c *Elt, n *Scalar) { ted448.ScalarBaseMult(&c.p, n) }
8081

8182
// IsIdentity returns True if e is the identity of the group.
82-
func (e *Elt) IsIdentity() bool { return fp.IsZero(&e.p.X) && !fp.IsZero(&e.p.Y) && !fp.IsZero(&e.p.Z) }
83+
func (e *Elt) IsIdentity() bool {
84+
b0 := fp.IsZero(&e.p.X)
85+
b1 := 1 - fp.IsZero(&e.p.Y)
86+
b2 := 1 - fp.IsZero(&e.p.Z)
87+
return subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7) == 1
88+
}
8389

8490
// IsEqual returns True if e=a, where = is an equivalence relation.
8591
func (e *Elt) IsEqual(a *Elt) bool {
8692
l, r := &fp.Elt{}, &fp.Elt{}
8793
fp.Mul(l, &e.p.X, &a.p.Y)
8894
fp.Mul(r, &a.p.X, &e.p.Y)
8995
fp.Sub(l, l, r)
90-
return fp.IsZero(l)
96+
return fp.IsZero(l) == 1
9197
}
9298

9399
// UnmarshalBinary interprets the first EncodingSize bytes passed in data, and
@@ -101,7 +107,7 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
101107
copy(s[:], data[:EncodingSize])
102108
p := fp.P()
103109
isLessThanP := isLessThan(s[:], p[:])
104-
isPositiveS := fp.Parity(s) == 0
110+
isPositiveS := 1 - fp.Parity(s)
105111

106112
den, num := &fp.Elt{}, &fp.Elt{}
107113
isr, altx, t0 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
@@ -132,14 +138,16 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
132138
fp.Add(x, x, x) // x = 2*s*isr^2*den*num
133139
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den
134140

135-
isValid := isPositiveS && isLessThanP && isQR
136-
b := uint(*((*byte)(unsafe.Pointer(&isValid))))
141+
b0 := isPositiveS
142+
b1 := isLessThanP
143+
b2 := isQR
144+
b := uint(subtle.ConstantTimeEq(int32(4*b2+2*b1+b0), 0x7))
137145
fp.Cmov(&e.p.X, x, b)
138146
fp.Cmov(&e.p.Y, y, b)
139147
fp.Cmov(&e.p.Ta, x, b)
140148
fp.Cmov(&e.p.Tb, y, b)
141149
fp.Cmov(&e.p.Z, &one, b)
142-
if !isValid {
150+
if b == 0 {
143151
return ErrInvalidDecoding
144152
}
145153
return nil
@@ -180,12 +188,14 @@ func (e *Elt) marshalBinary(enc []byte) error {
180188
return fp.ToBytes(enc[:], s)
181189
}
182190

183-
// isLessThan returns true if 0 <= x < y, and assumes that slices are of the
191+
// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the
184192
// same length and are interpreted in little-endian order.
185-
func isLessThan(x, y []byte) bool {
193+
func isLessThan(x, y []byte) int {
186194
i := len(x) - 1
187195
for i > 0 && x[i] == y[i] {
188196
i--
189197
}
190-
return x[i] < y[i]
198+
xi := int(x[i])
199+
yi := int(y[i])
200+
return ((xi - yi) >> (bits.UintSize - 1)) & 1
191201
}

ecc/decaf/decaf_test.go group/decaf448/decaf_test.go

+69-41
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package decaf_test
1+
package decaf448_test
22

33
import (
44
"bytes"
@@ -9,7 +9,7 @@ import (
99
"os"
1010
"testing"
1111

12-
"github.com/cloudflare/circl/ecc/decaf"
12+
"github.com/cloudflare/circl/group/decaf448"
1313
"github.com/cloudflare/circl/internal/test"
1414
fp "github.com/cloudflare/circl/math/fp448"
1515
)
@@ -44,8 +44,8 @@ func (kat *testJSONFile) readFile(t *testing.T, fileName string) {
4444
}
4545
}
4646

47-
func verify(t *testing.T, i int, gotkG *decaf.Elt, wantEnckG []byte) {
48-
wantkG := &decaf.Elt{}
47+
func verify(t *testing.T, i int, gotkG *decaf448.Elt, wantEnckG []byte) {
48+
wantkG := &decaf448.Elt{}
4949

5050
gotEnckG, err := gotkG.MarshalBinary()
5151
got := err == nil && bytes.Equal(gotEnckG, wantEnckG)
@@ -56,8 +56,8 @@ func verify(t *testing.T, i int, gotkG *decaf.Elt, wantEnckG []byte) {
5656

5757
err = wantkG.UnmarshalBinary(wantEnckG)
5858
got = err == nil &&
59-
decaf.IsValid(gotkG) &&
60-
decaf.IsValid(wantkG) &&
59+
decaf448.IsValid(gotkG) &&
60+
decaf448.IsValid(wantkG) &&
6161
gotkG.IsEqual(wantkG)
6262
want = true
6363
if got != want {
@@ -76,34 +76,34 @@ func TestDecafv1_0(t *testing.T) {
7676
test.ReportError(t, got, want)
7777
}
7878
got = kat.Version
79-
want = decaf.Version
79+
want = decaf448.Version
8080
if got != want {
8181
test.ReportError(t, got, want)
8282
}
83-
var scalar decaf.Scalar
84-
var P decaf.Elt
85-
G := decaf.Generator()
83+
var scalar decaf448.Scalar
84+
var P decaf448.Elt
85+
G := decaf448.Generator()
8686
for i := range kat.Vectors {
8787
k, _ := hex.DecodeString(kat.Vectors[i].K)
8888
wantEnckG, _ := hex.DecodeString(kat.Vectors[i].KG)
8989
wantEnckP, _ := hex.DecodeString(kat.Vectors[i].KP)
9090
scalar.FromBytes(k)
9191

92-
decaf.MulGen(&P, &scalar)
92+
decaf448.MulGen(&P, &scalar)
9393
verify(t, i, &P, wantEnckG)
9494

95-
decaf.Mul(&P, &scalar, G)
95+
decaf448.Mul(&P, &scalar, G)
9696
verify(t, i, &P, wantEnckG)
9797

98-
decaf.Mul(&P, &scalar, &P)
98+
decaf448.Mul(&P, &scalar, &P)
9999
verify(t, i, &P, wantEnckP)
100100
}
101101
}
102102

103103
func TestDecafRandom(t *testing.T) {
104104
const testTimes = 1 << 10
105-
var e decaf.Elt
106-
var enc [decaf.EncodingSize]byte
105+
var e decaf448.Elt
106+
var enc [decaf448.EncodingSize]byte
107107

108108
for i := 0; i < testTimes; i++ {
109109
for found := false; !found; {
@@ -119,44 +119,44 @@ func TestDecafRandom(t *testing.T) {
119119
}
120120
}
121121

122-
func randomPoint() decaf.Elt {
123-
var k decaf.Scalar
122+
func randomPoint() decaf448.Elt {
123+
var k decaf448.Scalar
124124
_, _ = rand.Read(k[:])
125-
var P decaf.Elt
126-
decaf.MulGen(&P, &k)
125+
var P decaf448.Elt
126+
decaf448.MulGen(&P, &k)
127127
return P
128128
}
129129

130130
func TestPointAdd(t *testing.T) {
131131
const testTimes = 1 << 10
132-
Q := &decaf.Elt{}
132+
Q := &decaf448.Elt{}
133133
for i := 0; i < testTimes; i++ {
134134
P := randomPoint()
135135
// Q = 16P = 2^4P
136-
decaf.Double(Q, &P) // 2P
137-
decaf.Double(Q, Q) // 4P
138-
decaf.Double(Q, Q) // 8P
139-
decaf.Double(Q, Q) // 16P
136+
decaf448.Double(Q, &P) // 2P
137+
decaf448.Double(Q, Q) // 4P
138+
decaf448.Double(Q, Q) // 8P
139+
decaf448.Double(Q, Q) // 16P
140140
got := Q
141141
// R = 16P = P+P...+P
142-
R := decaf.Identity()
142+
R := decaf448.Identity()
143143
for j := 0; j < 16; j++ {
144-
decaf.Add(R, R, &P)
144+
decaf448.Add(R, R, &P)
145145
}
146146
want := R
147-
if !decaf.IsValid(got) || !decaf.IsValid(want) || !got.IsEqual(want) {
147+
if !decaf448.IsValid(got) || !decaf448.IsValid(want) || !got.IsEqual(want) {
148148
test.ReportError(t, got, want, P)
149149
}
150150
}
151151
}
152152

153153
func TestPointNeg(t *testing.T) {
154154
const testTimes = 1 << 10
155-
Q := &decaf.Elt{}
155+
Q := &decaf448.Elt{}
156156
for i := 0; i < testTimes; i++ {
157157
P := randomPoint()
158-
decaf.Neg(Q, &P)
159-
decaf.Add(Q, Q, &P)
158+
decaf448.Neg(Q, &P)
159+
decaf448.Add(Q, Q, &P)
160160
got := Q.IsIdentity()
161161
want := true
162162
if got != want {
@@ -167,12 +167,12 @@ func TestPointNeg(t *testing.T) {
167167

168168
func TestDecafOrder(t *testing.T) {
169169
const testTimes = 1 << 10
170-
Q := &decaf.Elt{}
171-
order := decaf.Order()
170+
Q := &decaf448.Elt{}
171+
order := decaf448.Order()
172172
for i := 0; i < testTimes; i++ {
173173
P := randomPoint()
174174

175-
decaf.Mul(Q, &order, &P)
175+
decaf448.Mul(Q, &order, &P)
176176
got := Q.IsIdentity()
177177
want := true
178178
if got != want {
@@ -193,37 +193,65 @@ func TestDecafInvalid(t *testing.T) {
193193
nonQR[:], // s=4 and (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR.
194194
}
195195

196-
var e decaf.Elt
196+
var e decaf448.Elt
197197
for _, enc := range badEncodings {
198198
got := e.UnmarshalBinary(enc)
199-
want := decaf.ErrInvalidDecoding
199+
want := decaf448.ErrInvalidDecoding
200200
if got != want {
201201
test.ReportError(t, got, want, enc)
202202
}
203203
}
204204
}
205205

206206
func BenchmarkDecaf(b *testing.B) {
207-
var k, l decaf.Scalar
207+
var k, l decaf448.Scalar
208208
_, _ = rand.Read(k[:])
209209
_, _ = rand.Read(l[:])
210-
G := decaf.Generator()
211-
P := decaf.Generator()
210+
G := decaf448.Generator()
211+
P := decaf448.Generator()
212+
Z := decaf448.Identity()
212213
enc, _ := G.MarshalBinary()
213214

215+
x := &fp.Elt{}
216+
y := &fp.Elt{}
217+
_, _ = rand.Read(y[:])
218+
// p := fp.P()
219+
// one := fp.One()
220+
// fp.Sub(y, &p, &one)
221+
214222
b.Run("Add", func(b *testing.B) {
215223
for i := 0; i < b.N; i++ {
216-
decaf.Add(P, P, G)
224+
decaf448.Add(P, P, G)
225+
}
226+
})
227+
b.Run("IsZeroSub0", func(b *testing.B) {
228+
for i := 0; i < b.N; i++ {
229+
fp.IsZero(x)
230+
}
231+
})
232+
b.Run("IsZeroSub1", func(b *testing.B) {
233+
for i := 0; i < b.N; i++ {
234+
fp.IsZero(y)
235+
}
236+
})
237+
b.Run("IsZeroSi", func(b *testing.B) {
238+
for i := 0; i < b.N; i++ {
239+
_ = Z.IsIdentity()
240+
}
241+
})
242+
b.Run("IsZero", func(b *testing.B) {
243+
for i := 0; i < b.N; i++ {
244+
_ = P.IsIdentity()
217245
}
218246
})
219247
b.Run("Mul", func(b *testing.B) {
220248
for i := 0; i < b.N; i++ {
221-
decaf.Mul(G, &k, G)
249+
decaf448.Mul(G, &k, G)
222250
}
223251
})
224252
b.Run("MulGen", func(b *testing.B) {
225253
for i := 0; i < b.N; i++ {
226-
decaf.MulGen(P, &k)
254+
decaf448.MulGen(P, &k)
227255
}
228256
})
229257
b.Run("Marshal", func(b *testing.B) {

internal/ted448/curve.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ func ParamD() fp.Elt { return paramD }
4646

4747
// IsOnCurve returns true if the point lies on the curve.
4848
func IsOnCurve(P *Point) bool {
49-
eq0 := *P != Point{}
49+
eq0 := fp.IsZero(&P.X)
50+
eq0 &= fp.IsZero(&P.Y)
51+
eq0 &= fp.IsZero(&P.Z)
52+
eq0 &= fp.IsZero(&P.Ta)
53+
eq0 &= fp.IsZero(&P.Tb)
54+
eq0 = 1 - eq0
5055
x2, y2, t, t2, z2 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
5156
rhs, lhs := &fp.Elt{}, &fp.Elt{}
5257
fp.Mul(t, &P.Ta, &P.Tb) // t = ta*tb
@@ -63,7 +68,7 @@ func IsOnCurve(P *Point) bool {
6368
fp.Mul(rhs, t, &P.Z) // tz
6469
fp.Sub(lhs, lhs, rhs) // xy - tz
6570
eq2 := fp.IsZero(lhs)
66-
return eq0 && eq1 && eq2
71+
return subtle.ConstantTimeByteEq(byte(4*eq2+2*eq1+eq0), 0x7) == 1
6772
}
6873

6974
// subYDiv16 update x = (x - y) / 16.

internal/ted448/curve_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,23 @@ func TestPointAdd(t *testing.T) {
3939
}
4040
}
4141

42+
func TestIsOnCurve(t *testing.T) {
43+
for _, v := range []struct {
44+
P ted448.Point
45+
want bool
46+
}{
47+
{ted448.Point{}, false},
48+
{ted448.Identity(), true},
49+
{ted448.Generator(), true},
50+
{randomPoint(), true},
51+
} {
52+
got := ted448.IsOnCurve(&v.P)
53+
if got != v.want {
54+
test.ReportError(t, got, v.want, v.P)
55+
}
56+
}
57+
}
58+
4259
func TestPointNeg(t *testing.T) {
4360
const testTimes = 1 << 10
4461
for i := 0; i < testTimes; i++ {

0 commit comments

Comments
 (0)