Skip to content

Commit

Permalink
Updating interface for decaf and curve.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jul 24, 2020
1 parent a99153c commit 9b048c0
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 249 deletions.
9 changes: 9 additions & 0 deletions ecc/goldilocks/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import (
fp "github.com/cloudflare/circl/math/fp448"
)

const (
// ScalarSize is the size (in bytes) of scalars.
ScalarSize = 56
// CurveEncodingSize is the size (in bytes) of an encoded point on the Goldilocks curve.
CurveEncodingSize = fp.Size + 1
// DecafEncodingSize is the size (in bytes) of an encoded Decaf element.
DecafEncodingSize = fp.Size
)

var (
// genX is the x-coordinate of the generator of Goldilocks curve.
genX = fp.Elt{
Expand Down
56 changes: 27 additions & 29 deletions ecc/goldilocks/curve.go
Original file line number Diff line number Diff line change
@@ -1,61 +1,59 @@
package goldilocks

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

// Curve provides operations on the Goldilocks curve.
// Curve is a zero-length datatype.
type Curve struct{}

// Identity returns the identity point.
func (Curve) Identity() *Point {
return &Point{
y: fp.One(),
z: fp.One(),
}
}
func (Curve) Identity() *Point { return &Point{y: fp.One(), z: fp.One()} }

// Generator returns the generator point.
func (Curve) Generator() *Point {
return &Point{
x: genX,
y: genY,
z: fp.One(),
ta: genX,
tb: genY,
}
}
func (Curve) Generator() *Point { return &Point{x: genX, y: genY, z: fp.One(), ta: genX, tb: genY} }

// IsOnCurve returns true if the point lies on the curve.
func (Curve) IsOnCurve(P *Point) bool { return isOnCurve(&P.x, &P.y, &P.ta, &P.tb, &P.z, false) }

// Order returns the number of points in the prime subgroup.
func (Curve) Order() Scalar { return order }

// Double returns R = 2P.
func (Curve) Double(R, P *Point) *Point { R := *P; R.Double(); return &R }
// Double calculates R = 2P.
func (Curve) Double(R, P *Point) { *R = *P; R.Double() }

// Add returns P+Q.
func (Curve) Add(P, Q *Point) *Point { R := *P; R.Add(Q); return &R }
// Add calculates R = P+Q.
func (Curve) Add(R, P, Q *Point) { S := *P; S.Add(Q); *R = S }

// ScalarMult returns kP. This function runs in constant time.
func (e Curve) ScalarMult(k *Scalar, P *Point) *Point {
// ScalarMult calculates Q = kP. This function runs in constant time.
func (Curve) ScalarMult(Q *Point, k *Scalar, P *Point) {
var t twistCurve
k4 := &Scalar{}
k4.divBy4(k)
return e.pull(twistCurve{}.ScalarMult(k4, e.push(P)))
R := &twistPoint{}
t.ScalarMult(R, k4, t.pull(P))
*Q = *t.push(R)
}

// ScalarBaseMult returns kG where G is the generator point. This function runs in constant time.
func (e Curve) ScalarBaseMult(k *Scalar) *Point {
// ScalarBaseMult calculates Q = kG, where G is the generator of the Goldilocks curve. This function runs in constant time.
func (Curve) ScalarBaseMult(Q *Point, k *Scalar) {
var t twistCurve
k4 := &Scalar{}
k4.divBy4(k)
return e.pull(twistCurve{}.ScalarBaseMult(k4))
R := &twistPoint{}
t.ScalarBaseMult(R, k4)
*Q = *t.push(R)
}

// CombinedMult returns mG+nP, where G is the generator point. This function is non-constant time.
func (e Curve) CombinedMult(m, n *Scalar, P *Point) *Point {
// CombinedMult calculates Q = mG+nP, where G is the generator of the Goldilocks curve. This function does NOT run in constant time.
func (Curve) CombinedMult(Q *Point, m, n *Scalar, P *Point) {
var t twistCurve
m4 := &Scalar{}
n4 := &Scalar{}
m4.divBy4(m)
n4.divBy4(n)
return e.pull(twistCurve{}.CombinedMult(m4, n4, twistCurve{}.pull(P)))
R := &twistPoint{}
t.CombinedMult(R, m4, n4, t.pull(P))
*Q = *t.push(R)
}
47 changes: 34 additions & 13 deletions ecc/goldilocks/curve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ func TestScalarMult(t *testing.T) {
zero := &goldilocks.Scalar{}

t.Run("rG=0", func(t *testing.T) {
got := &goldilocks.Point{}
order := e.Order()
for i := 0; i < testTimes; i++ {
got := e.ScalarBaseMult(&order)
e.ScalarBaseMult(got, &order)
got.ToAffine()
want := e.Identity()

Expand All @@ -28,11 +29,12 @@ func TestScalarMult(t *testing.T) {
}
})
t.Run("rP=0", func(t *testing.T) {
got := &goldilocks.Point{}
order := e.Order()
for i := 0; i < testTimes; i++ {
P := randomPoint()

got := e.ScalarMult(&order, P)
e.ScalarMult(got, &order, P)
got.ToAffine()
want := e.Identity()

Expand All @@ -43,43 +45,51 @@ func TestScalarMult(t *testing.T) {
}
})
t.Run("kG", func(t *testing.T) {
got := &goldilocks.Point{}
want := &goldilocks.Point{}
I := e.Identity()
for i := 0; i < testTimes; i++ {
_, _ = rand.Read(k[:])

got := e.ScalarBaseMult(k)
want := e.CombinedMult(k, zero, I) // k*G + 0*I
e.ScalarBaseMult(got, k)
e.CombinedMult(want, k, zero, I) // k*G + 0*I

if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) {
test.ReportError(t, got, want, k)
}
}
})
t.Run("kP", func(t *testing.T) {
got := &goldilocks.Point{}
want := &goldilocks.Point{}
for i := 0; i < testTimes; i++ {
P := randomPoint()
_, _ = rand.Read(k[:])

got := e.ScalarMult(k, P)
want := e.CombinedMult(zero, k, P)
e.ScalarMult(got, k, P)
e.CombinedMult(want, zero, k, P)

if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) {
test.ReportError(t, got, want, P, k)
}
}
})
t.Run("kG+lP", func(t *testing.T) {
got := &goldilocks.Point{}
want := &goldilocks.Point{}
kG := &goldilocks.Point{}
lP := &goldilocks.Point{}
G := e.Generator()
l := &goldilocks.Scalar{}
for i := 0; i < testTimes; i++ {
P := randomPoint()
_, _ = rand.Read(k[:])
_, _ = rand.Read(l[:])

kG := e.ScalarMult(k, G)
lP := e.ScalarMult(l, P)
got := e.Add(kG, lP)
want := e.CombinedMult(k, l, P)
e.ScalarMult(kG, k, G)
e.ScalarMult(lP, l, P)
e.Add(got, kG, lP)
e.CombinedMult(want, k, l, P)

if !e.IsOnCurve(got) || !e.IsOnCurve(want) || !got.IsEqual(want) {
test.ReportError(t, got, want, P, k, l)
Expand All @@ -94,20 +104,31 @@ func BenchmarkCurve(b *testing.B) {
_, _ = rand.Read(k[:])
_, _ = rand.Read(l[:])
P := randomPoint()
Q := randomPoint()

b.Run("Add", func(b *testing.B) {
for i := 0; i < b.N; i++ {
P.Add(Q)
}
})
b.Run("ScalarMult", func(b *testing.B) {
for i := 0; i < b.N; i++ {
P = e.ScalarMult(&k, P)
e.ScalarMult(P, &k, P)
}
})
b.Run("ScalarBaseMult", func(b *testing.B) {
for i := 0; i < b.N; i++ {
e.ScalarBaseMult(&k)
e.ScalarBaseMult(P, &k)
}
})
b.Run("CombinedMult", func(b *testing.B) {
for i := 0; i < b.N; i++ {
P = e.CombinedMult(&k, &l, P)
e.CombinedMult(P, &k, &l, P)
}
})
b.Run("ToAffine", func(b *testing.B) {
for i := 0; i < b.N; i++ {
P.ToAffine()
}
})
}
97 changes: 47 additions & 50 deletions ecc/goldilocks/decaf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package goldilocks

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

// DecafEncodingSize is the size (in bytes) for storing a decaf element.
const DecafEncodingSize = fp.Size

// Decaf provides operations of a prime-order group from goldilocks curve.
// Its internal implementation uses the twist of the goldilocks curve.
// This uses version 1.1 of the encoding. Decaf is a zero-length datatype.
Expand All @@ -19,9 +16,6 @@ func (e Elt) String() string { return e.p.String() }
// IsValid returns True if a is a valid element of the group.
func (d Decaf) IsValid(a *Elt) bool { return d.c.IsOnCurve(&a.p) }

// IsIdentity returns True if a is the identity of the group.
func (d Decaf) IsIdentity(a *Elt) bool { return fp.IsZero(&a.p.x) }

// Identity returns the identity element of the group.
func (d Decaf) Identity() *Elt { return &Elt{*d.c.Identity()} }

Expand All @@ -32,7 +26,7 @@ func (d Decaf) Generator() *Elt { return &Elt{*d.c.pull(Curve{}.Generator())} }
func (d Decaf) Order() Scalar { return order }

// Add calculates c=a+b, where + is the group operation.
func (d Decaf) Add(c, a, b *Elt) { c.p = a.p; c.p.Add(&b.p) }
func (d Decaf) Add(c, a, b *Elt) { q := a.p; q.Add(&b.p); c.p = q }

// Double calculates c=a+a, where + is the group operation.
func (d Decaf) Double(c, a *Elt) { c.p = a.p; c.p.Double() }
Expand All @@ -41,65 +35,36 @@ func (d Decaf) Double(c, a *Elt) { c.p = a.p; c.p.Double() }
func (d Decaf) Neg(c, a *Elt) { c.p = a.p; c.p.cneg(1) }

// Mul calculates c=n*a, where * is scalar multiplication on the group.
func (d Decaf) Mul(c *Elt, n *Scalar, a *Elt) { c.p = *d.c.ScalarMult(n, &a.p) }
func (d Decaf) Mul(c *Elt, n *Scalar, a *Elt) { d.c.ScalarMult(&c.p, n, &a.p) }

// MulGen calculates c=n*g, where * is scalar multiplication on the group,
// and g is the generator of the group.
func (d Decaf) MulGen(c *Elt, n *Scalar) { c.p = *d.c.ScalarBaseMult(n) }
func (d Decaf) MulGen(c *Elt, n *Scalar) { d.c.ScalarBaseMult(&c.p, n) }

// AreEqual returns True if a=b, where = is an equivalence relation.
func (d Decaf) AreEqual(a, b *Elt) bool {
// IsIdentity returns True if e is the identity of the group.
func (e *Elt) IsIdentity() bool { return fp.IsZero(&e.p.x) }

// IsEqual returns True if e=a, where = is an equivalence relation.
func (e *Elt) IsEqual(a *Elt) bool {
l, r := &fp.Elt{}, &fp.Elt{}
fp.Mul(l, &a.p.x, &b.p.y)
fp.Mul(r, &b.p.x, &a.p.y)
fp.Mul(l, &e.p.x, &a.p.y)
fp.Mul(r, &a.p.x, &e.p.y)
fp.Sub(l, l, r)
return fp.IsZero(l)
}

// Marshal returns a unique encoding of the element e.
func (e *Elt) Marshal() []byte {
x, ta, tb, z := e.p.x, e.p.ta, e.p.tb, e.p.z
one, t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
fp.SetOne(one)
fp.Mul(t, &ta, &tb) // t = ta*tb
t0, t1 := x, *t // (t0,t1) = (x,t)
fp.Sqr(t2, &x) // t2 = x^2
fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t)
fp.Mul(&t1, &t0, &t1) // t1 = (x+t)*(x-t)
fp.Mul(&t0, &t1, &aMinusDTwist) // t0 = (a-d)*(x+t)*(x-t)
fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t)
fp.InvSqrt(&t0, one, &t0) // t0 = 1/sqrt( x^2*(a-d)*(z+y)*(z-y) )
fp.Mul(&t1, &t1, &t0) // t1 = (z+y)*(z-y)/sqrt( x^2*(a-d)*(z+y)*(z-y) )
fp.Mul(t2, &t1, &sqrtAMinusDTwist) // t2 = sqrt( (z+y)*(z-y) )/z
isNeg := fp.Parity(t2) // isNeg = sgn(t2)
fp.Neg(t2, &t1) // t2 = -t1
fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1
fp.Mul(s, &t1, &z) // s = t1*z
fp.Sub(s, s, t) // s = t1*z - t
fp.Mul(s, s, &x) // s = x*(t1*z - t)
fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t)
fp.Mul(s, s, &aMinusDTwist) // s = (a-d)*isr*x*(t1*z - t)
isNeg = fp.Parity(s) // isNeg = sgn(s)
fp.Neg(&t0, s) // t0 = -s
fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s

var encS [fp.Size]byte
_ = fp.ToBytes(encS[:], s)
return encS[:]
}

// Unmarshal if succeeds returns nil and constructs an element e from an
// encoding stored in a slice b of DecafEncodingSize bytes.
func (e *Elt) Unmarshal(b []byte) error {
// UnmarshalBinary if succeeds returns a Decaf element by decoding the first
// DecafEncodingSize bytes of b.
func (e *Elt) UnmarshalBinary(b []byte) error {
if len(b) < DecafEncodingSize {
return errInvalidDecoding
}

s := &fp.Elt{}
copy(s[:], b[:DecafEncodingSize])
isNeg := fp.Parity(s)
p := fp.P()
if isNeg == 1 || !isLessThan(b[:DecafEncodingSize], p[:]) {
modulus := fp.P()
if isNeg == 1 || !isLessThan(b[:DecafEncodingSize], modulus[:]) {
return errInvalidDecoding
}

Expand Down Expand Up @@ -138,3 +103,35 @@ func (e *Elt) Unmarshal(b []byte) error {
fp.SetOne(&e.p.z)
return nil
}

// MarshalBinary returns a unique encoding of the element e.
func (e *Elt) MarshalBinary() ([]byte, error) {
x, ta, tb, z := e.p.x, e.p.ta, e.p.tb, e.p.z
one, t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
fp.SetOne(one)
fp.Mul(t, &ta, &tb) // t = ta*tb
t0, t1 := x, *t // (t0,t1) = (x,t)
fp.Sqr(t2, &x) // t2 = x^2
fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t)
fp.Mul(&t1, &t0, &t1) // t1 = (x+t)*(x-t)
fp.Mul(&t0, &t1, &aMinusDTwist) // t0 = (a-d)*(x+t)*(x-t)
fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t)
fp.InvSqrt(&t0, one, &t0) // t0 = 1/sqrt( x^2*(a-d)*(z+y)*(z-y) )
fp.Mul(&t1, &t1, &t0) // t1 = (z+y)*(z-y)/sqrt( x^2*(a-d)*(z+y)*(z-y) )
fp.Mul(t2, &t1, &sqrtAMinusDTwist) // t2 = sqrt( (z+y)*(z-y) )/z
isNeg := fp.Parity(t2) // isNeg = sgn(t2)
fp.Neg(t2, &t1) // t2 = -t1
fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1
fp.Mul(s, &t1, &z) // s = t1*z
fp.Sub(s, s, t) // s = t1*z - t
fp.Mul(s, s, &x) // s = x*(t1*z - t)
fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t)
fp.Mul(s, s, &aMinusDTwist) // s = (a-d)*isr*x*(t1*z - t)
isNeg = fp.Parity(s) // isNeg = sgn(s)
fp.Neg(&t0, s) // t0 = -s
fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s

var encS [fp.Size]byte
err := fp.ToBytes(encS[:], s)
return encS[:], err
}

0 comments on commit 9b048c0

Please sign in to comment.