Skip to content

Commit

Permalink
Adding tests for detecting decaf/point invalid encodings.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jul 24, 2020
1 parent 3881ea8 commit ff821b5
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 29 deletions.
2 changes: 1 addition & 1 deletion ecc/goldilocks/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,5 @@ var (
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
}
errInvalidDecoding = errors.New("invalid decoding")
ErrInvalidDecoding = errors.New("invalid decoding")
)
27 changes: 20 additions & 7 deletions ecc/goldilocks/decaf.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package goldilocks

import fp "github.com/cloudflare/circl/math/fp448"
import (
"unsafe"

fp "github.com/cloudflare/circl/math/fp448"
)

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

s := &fp.Elt{}
copy(s[:], data[:DecafEncodingSize])
isPositiveS := fp.Parity(s) == 0
p := fp.P()
isLessThanP := isLessThan(s[:], p[:])
isPositiveS := fp.Parity(s) == 0

s2, den, num := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
isr, altx := &fp.Elt{}, &fp.Elt{}
Expand Down Expand Up @@ -95,11 +99,20 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
fp.Mul(y, isr, den) // y = isr*den
fp.Add(t0, &one, s2) // t0 = 1 - a*s^2
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den
if !(isPositiveS && isLessThanP && isQR) {
return errInvalidDecoding

isValid := isPositiveS && isLessThanP && isQR
b := *((*uint)(unsafe.Pointer(&isValid)))
fp.Cmov(&e.p.x, x, b)
fp.Cmov(&e.p.y, y, b)
fp.Cmov(&e.p.ta, x, b)
fp.Cmov(&e.p.tb, y, b)
fp.Cmov(&e.p.z, &one, b)

var err error
if !isValid {
err = ErrInvalidDecoding
}
e.p.x, e.p.y, e.p.ta, e.p.tb, e.p.z = *x, *y, *x, *y, one
return nil
return err
}

// MarshalBinary returns a unique encoding of the element e.
Expand Down
23 changes: 23 additions & 0 deletions ecc/goldilocks/decaf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/cloudflare/circl/ecc/goldilocks"
"github.com/cloudflare/circl/internal/test"
fp "github.com/cloudflare/circl/math/fp448"
)

type testJSONFile struct {
Expand Down Expand Up @@ -119,6 +120,28 @@ func TestDecafRandom(t *testing.T) {
}
}

func TestDecafInvalid(t *testing.T) {
bigS := fp.P()
negativeS := fp.Elt{1} // the smallest s that is negative
nonQR := fp.Elt{4} // the shortest s such that (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR.

badEncodings := [][]byte{
{}, // wrong size input
bigS[:], // s is out of the interval [0,p-1].
negativeS[:], // s is not positive
nonQR[:], // s=4 and (a^2s^4 + (2a - 4d)*s^2 + 1) is non-QR.
}

var e goldilocks.Elt
for _, enc := range badEncodings {
got := e.UnmarshalBinary(enc)
want := goldilocks.ErrInvalidDecoding
if got != want {
test.ReportError(t, got, want, enc)
}
}
}

func BenchmarkDecaf(b *testing.B) {
var d goldilocks.Decaf
var k, l goldilocks.Scalar
Expand Down
2 changes: 1 addition & 1 deletion ecc/goldilocks/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
//
// Both Goldilocks and Decaf use as internal representation the curve
// 4Iso-Goldilocks: ax^2+y^2 = 1 + dx^2y^2, where a=-1 and d=-39082.
// This curve is 4-degree isogeous to the Goldilocks curve, and 2-degree
// This curve is 4-degree isogeneous to the Goldilocks curve, and 2-degree
// isogeneous to the Jacobi quartic. The 4Iso-Goldilocks curve was chosen as
// provides faster arithmetic operations.
//
Expand Down
31 changes: 16 additions & 15 deletions ecc/goldilocks/point.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package goldilocks

import (
"fmt"
"unsafe"

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

// // FromAffine creates a point from affine coordinates.
// func FromAffine(x, y *fp.Elt) (*Point, error) {
// P := &Point{x: *x, y: *y, z: fp.One(), ta: *x, tb: *y}
// if !(Curve{}).IsOnCurve(P) {
// return nil, errors.New("point not on curve")
// }
// return P, nil
// }

// isLessThan returns true if 0 <= x < y, and assumes that slices are of the
// same length and are interpreted in little-endian order.
func isLessThan(x, y []byte) bool {
Expand Down Expand Up @@ -110,7 +102,7 @@ func (P *Point) Add(Q *Point) {
// CurveEncodingSize bytes of data.
func (P *Point) UnmarshalBinary(data []byte) error {
if len(data) < CurveEncodingSize {
return errInvalidDecoding
return ErrInvalidDecoding
}

x, y := &fp.Elt{}, &fp.Elt{}
Expand All @@ -123,17 +115,26 @@ func (P *Point) UnmarshalBinary(data []byte) error {
one := fp.One()
fp.Sqr(u, y) // u = y^2
fp.Mul(v, u, &paramD) // v = dy^2
fp.Sub(u, u, &one) // u = y^2-a
fp.Sub(u, u, &one) // u = y^2-1
fp.Sub(v, v, &one) // v = dy^2-a
isQR := fp.InvSqrt(x, u, v) // x = sqrt(u/v)
isValidXSign := !(fp.IsZero(x) && signX == 1)
fp.Neg(u, x) // u = -x
fp.Cmov(x, u, uint(signX^(x[0]&1))) // if signX != x mod 2
if !(isLessThanP && isQR && isValidXSign) {
return errInvalidDecoding

isValid := isLessThanP && isQR && isValidXSign
b := *((*uint)(unsafe.Pointer(&isValid)))
fp.Cmov(&P.x, x, b)
fp.Cmov(&P.y, y, b)
fp.Cmov(&P.ta, x, b)
fp.Cmov(&P.tb, y, b)
fp.Cmov(&P.z, &one, b)

var err error
if !isValid {
err = ErrInvalidDecoding
}
P.x, P.y, P.ta, P.tb, P.z = *x, *y, *x, *y, one
return nil
return err
}

// MarshalBinary returns a unique encoding of the point P.
Expand Down
28 changes: 28 additions & 0 deletions ecc/goldilocks/point_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/cloudflare/circl/ecc/goldilocks"
"github.com/cloudflare/circl/internal/test"
fp "github.com/cloudflare/circl/math/fp448"
)

func randomPoint() *goldilocks.Point {
Expand Down Expand Up @@ -75,3 +76,30 @@ func TestPointMarshal(t *testing.T) {
}
}
}

func TestPointInvalid(t *testing.T) {
p := fp.P()
one := fp.One()

var bigY, wrongSignX, nonQR [goldilocks.CurveEncodingSize]byte
copy(bigY[:], p[:])
copy(wrongSignX[:], one[:])
wrongSignX[goldilocks.CurveEncodingSize-1] = 1 << 7
nonQR[0] = 2 // smallest y such that (y^2+a)/(dy^2-a) is not a square.

badEncodings := [][]byte{
{}, // wrong size input.
bigY[:], // y is out of the interval [0,p-1].
wrongSignX[:], // x has wrong sign.
nonQR[:], // y=2 and (y^2+a)/(dy^2-a) is not a square.
}

var P goldilocks.Point
for _, enc := range badEncodings {
got := P.UnmarshalBinary(enc)
want := goldilocks.ErrInvalidDecoding
if got != want {
test.ReportError(t, got, want, enc)
}
}
}
9 changes: 4 additions & 5 deletions ecc/goldilocks/twist.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package goldilocks
import (
"crypto/subtle"
"math/bits"
"unsafe"

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

kk := *k

var isZero int
if kk.IsZero() {
isZero = 1
}
subtle.ConstantTimeCopy(isZero, kk[:], order[:])
isZero := kk.IsZero()
isZeroInt := *(*int)(unsafe.Pointer(&isZero))
subtle.ConstantTimeCopy(isZeroInt, kk[:], order[:])

minusK := kk
isEven := 1 - int(kk[0]&0x1)
Expand Down

0 comments on commit ff821b5

Please sign in to comment.