Skip to content

Commit

Permalink
Adding decaf v1.1 and kat tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jul 24, 2020
1 parent c72bdfa commit a62d6bd
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 131 deletions.
34 changes: 21 additions & 13 deletions ecc/goldilocks/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,28 @@ var (
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
// aMinusD is paramA-paramD used for Decaf.
aMinusD = fp.Elt{0xaa, 0x98}
// aMinusD is paramA-paramD used for Decaf.
// paramDTwist is -39082 in Fp. The D parameter of the twist curve.
paramDTwist = fp.Elt{
0x55, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
// aMinusD is paramA-paramD used in Decaf.
aMinusDTwist = fp.Elt{0xa9, 0x98}
// sqrtAMinusDTwist is the smallest sqrt(paramATwist-paramDTwist) used in Decaf.
sqrtAMinusDTwist = fp.Elt{
0x36, 0x27, 0x57, 0x45, 0x0f, 0xef, 0x42, 0x96,
0x52, 0xce, 0x20, 0xaa, 0xf6, 0x7b, 0x33, 0x60,
0xd2, 0xde, 0x6e, 0xfd, 0xf4, 0x66, 0x9a, 0x83,
0xba, 0x14, 0x8c, 0x96, 0x80, 0xd7, 0xa2, 0x64,
0x4b, 0xd5, 0xb8, 0xa5, 0xb8, 0xa7, 0xf1, 0xa1,
0xa0, 0x6a, 0xa2, 0x2f, 0x72, 0x8d, 0xf6, 0x3b,
0x68, 0xf7, 0x24, 0xeb, 0xfb, 0x62, 0xd9, 0x22,
}
// order is 2^446-0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d,
// which is the number of points in the prime subgroup.
order = Scalar{
Expand All @@ -66,15 +84,5 @@ var (
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
}
// paramDTwist is -39082 in Fp. The D parameter of the twist curve.
paramDTwist = fp.Elt{
0x55, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
errInvalidDecoding = errors.New("invalid decoding")
)
122 changes: 62 additions & 60 deletions ecc/goldilocks/decaf.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package goldilocks

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

// Decaf provides a prime-order group.
// Internally, the implementation uses the twist of goldilocks curve.
Expand Down Expand Up @@ -29,6 +31,9 @@ func (d Decaf) Order() Scalar { return order }
// Add is
func (d Decaf) Add(c, a, b *Elt) { c.p = a.p; c.p.Add(&b.p) }

// Double is
func (d Decaf) Double(c, a *Elt) { c.p = a.p; c.p.Double() }

// Neg is
func (d Decaf) Neg(c, a *Elt) { c.p = a.p; c.p.cneg(1) }

Expand All @@ -49,85 +54,82 @@ func (d Decaf) AreEqual(a, b *Elt) bool {

// Marshal is
func (e *Elt) Marshal() []byte {
r, u := &fp.Elt{}, &fp.Elt{}
one, s := &fp.Elt{}, &fp.Elt{}
x, y, ta, tb, z := e.p.x, e.p.y, e.p.ta, e.p.tb, e.p.z
t0, t1 := z, y
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.AddSub(&t0, &t1) // (t0,t1) = (z+y,z-y)
fp.Mul(&t0, &t0, &t1) // t0 = (z+y)*(z-y)
fp.Mul(&t0, &t0, &aMinusDTwist) // t0 = (a-d)*(z+y)*(z-y)
fp.InvSqrt(r, one, &t0) // r = 1/sqrt( (a-d)*(z+y)*(z-y) )
fp.Mul(u, r, &aMinusDTwist) // u = (a-d)*r
fp.Mul(&t0, u, &z) // t0 = u*Z
fp.Add(&t0, &t0, &t0) // t0 = 2*u*Z
fp.Neg(&t0, &t0) // t0 = -2*u*Z
isNeg := fp.Parity(&t0) // isNeg = sgn(t0)
fp.Neg(&t1, r) // t1 = -r
fp.Cmov(r, &t1, uint(isNeg)) // if -2*u*Z is negative then r = -r
fp.Mul(&t1, &ta, &tb) // t1 = Ta*Tb = T
fp.Mul(&t1, &t1, &y) // t1 = Y*T
fp.Mul(&t1, &t1, &paramDTwist) // t1 = d*Y*T
fp.Mul(&t0, &z, &x) // t0 = Z*X
fp.Neg(&t0, &t0) // t0 = a*Z*X
fp.Sub(&t0, &t0, &t1) // t0 = a*Z*X - d*Y*T
fp.Mul(&t0, &t0, r) // t0 = r*(a*Z*X - d*Y*T)
fp.Add(&t0, &t0, &y) // t0 = r*(a*Z*X - d*Y*T) + Y
fp.Mul(s, &t0, u) // s = (u)*(r*(a*Z*X - d*Y*T) + Y)
fp.Neg(s, s) // s = (u/a)*(r*(a*Z*X - d*Y*T) + Y)
isNeg = fp.Parity(s) // isNeg = sgn(s)
fp.Neg(&t1, s) // t1 = -s
fp.Cmov(s, &t1, uint(isNeg)) // if s is negative then s = -s
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 is
func (d Decaf) Unmarshal(b []byte) (*Elt, error) {
func (e *Elt) Unmarshal(b []byte) error {
if len(b) < fp.Size {
return nil, errInvalidDecoding
return errInvalidDecoding
}

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

one, u, v, w := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
one, s2, den, num := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
isr, altx := &fp.Elt{}, &fp.Elt{}
t0, t1 := &fp.Elt{}, &fp.Elt{}
e := &Elt{}
fp.SetOne(one)
fp.Add(&e.p.x, s, s) // X = 2*s
fp.Sqr(t0, s) // t0 = s^2
fp.Sub(&e.p.z, one, t0) // Z = 1 + a*s^2
fp.Mul(t1, t0, &paramDTwist) // t1 = d*s^2
fp.Add(t1, t1, t1) // t1 = 2*d*s^2
fp.Add(t1, t1, t1) // t1 = 4*d*s^2
fp.Sqr(u, &e.p.z) // u = Z^2
fp.Sub(u, u, t1) // u = Z^2 - 4*d*s^2
fp.Mul(t0, t0, u) // t0 = u*s^2
isQR := fp.InvSqrt(v, one, t0) // v = 1/sqrt(u*s^2)
var isZero byte
fp.Sqr(s2, s) // s2 = s^2
fp.Sub(den, one, s2) // den = 1 + a*s^2
fp.Mul(t1, s2, &paramDTwist) // t1 = d*s^2
fp.Add(t1, t1, t1) // t1 = 2*d*s^2
fp.Add(t1, t1, t1) // t1 = 4*d*s^2
fp.Sqr(t0, den) // num = (1 + a*s^2)^2
fp.Sub(num, t0, t1) // num = (1 + a*s^2)^2 - 4*d*s^2
fp.Mul(t0, t0, num) // t0 = num*den^2
isQR := fp.InvSqrt(isr, one, t0) // v = 1/sqrt(num*den^2)
if !isQR {
if !fp.IsZero(t0) {
return nil, errInvalidDecoding
}
isZero = 1
return errInvalidDecoding
}
fp.Mul(t0, u, v) // t0 = u*v
isNeg = fp.Parity(t0) // isNeg = sgn(u*v)
fp.Neg(t1, v) // t1 = -v
fp.Cmov(v, t1, uint(isNeg)) // if u*v is negative then v = -v
fp.Sub(w, &fp.Elt{2}, &e.p.z) // w = 2-Z
fp.Mul(w, w, s) // w = s*(2-Z)
fp.Mul(w, w, v) // w = v*s*(2-Z)
fp.Add(w, w, &fp.Elt{isZero}) // if s=0 then w = w+1
fp.Mul(&e.p.y, &e.p.z, w) // Y = w*Z
e.p.ta, e.p.tb = e.p.x, *w // T = Ta*Tb = w*X
return e, nil
fp.Mul(t1, den, isr) // altx = isr*den
fp.Mul(t1, t1, s) // altx = s*isr*den
fp.Add(t1, t1, t1) // t1 = 2*s*isr*den
fp.Mul(altx, t1, &sqrtAMinusDTwist) // altx = 2*s*isr*den*sqrt(A-D)
isNeg = fp.Parity(altx) // isNeg = sgn(altx)
fp.Neg(t0, isr) // t0 = -isr
fp.Cmov(isr, t0, uint(isNeg)) // if altx is negative then isr = -isr
fp.Sqr(&e.p.x, isr) // x = isr^2
fp.Mul(&e.p.x, &e.p.x, den) // x = isr^2*den
fp.Mul(&e.p.x, &e.p.x, num) // x = isr^2*den*num
fp.Mul(&e.p.x, &e.p.x, s) // x = s*isr^2*den*num
fp.Add(&e.p.x, &e.p.x, &e.p.x) // x = 2*s*isr^2*den*num
fp.Mul(&e.p.y, isr, den) // y = isr*den
fp.Add(t0, one, s2) // t0 = 1 - a*s^2
fp.Mul(&e.p.y, &e.p.y, t0) // y = (1 - a*s^2)*isr*den
e.p.ta, e.p.tb = e.p.x, e.p.y // T = Ta*Tb = x*y
fp.SetOne(&e.p.z)
return nil
}
137 changes: 104 additions & 33 deletions ecc/goldilocks/decaf_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package goldilocks_test

import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"testing"

"github.com/cloudflare/circl/ecc/goldilocks"
Expand All @@ -12,40 +16,107 @@ import (

func TestDecafDevel(t *testing.T) {
var d goldilocks.Decaf
var P goldilocks.Elt

G := d.Generator()
fmt.Printf("G: %v\n%v\n\n", G, hex.EncodeToString(G.Marshal()))

Q := d.Identity()
for i := 0; i < 18; i++ {
enc := Q.Marshal()
decP, err := d.Unmarshal(enc)
if err != nil {
t.Fatalf("dd")
}
got := d.AreEqual(Q, decP)
want := true
if got != want {
fmt.Printf("%v\n", Q)
fmt.Printf("%v\n", decP)
test.ReportError(t, got, want, i)
}
d.Add(Q, Q, G)
d.Double(G, G)
encG := G.Marshal()
P.Unmarshal(encG)
encP := P.Marshal()
// fmt.Printf("G: %v\n", G)
fmt.Printf("encG: %v\n", hex.EncodeToString(encG))
// fmt.Printf("P: %v\n", P)
fmt.Printf("valid: %v\n", d.IsValid(&P))
fmt.Printf("equal: %v\n", d.AreEqual(G, &P))
fmt.Printf("encP: %v\n", hex.EncodeToString(encP))

}

type testJSONFile struct {
Group string `json:"group"`
Version string `json:"version"`
Generator struct {
X string `json:"x"`
Y string `json:"y"`
T string `json:"t"`
Z string `json:"z"`
} `json:"generator"`
Vectors []struct {
K string `json:"k"`
KG string `json:"kG"`
KP string `json:"kP"`
} `json:"vectors"`
}

func (kat *testJSONFile) readFile(t *testing.T, fileName string) {
jsonFile, err := os.Open(fileName)
if err != nil {
t.Fatalf("File %v can not be opened. Error: %v", fileName, err)
}
defer jsonFile.Close()
input, _ := ioutil.ReadAll(jsonFile)

err = json.Unmarshal(input, &kat)
if err != nil {
t.Fatalf("File %v can not be loaded. Error: %v", fileName, err)
}
}

func verify(t *testing.T, i int, gotkG *goldilocks.Elt, wantEnckG []byte) {
var d goldilocks.Decaf
wantkG := &goldilocks.Elt{}

gotEnckG := gotkG.Marshal()
got := bytes.Equal(gotEnckG, wantEnckG)
want := true
if got != want {
test.ReportError(t, got, want, i)
}

err := wantkG.Unmarshal(wantEnckG)
got = err == nil &&
d.IsValid(gotkG) &&
d.IsValid(wantkG) &&
d.AreEqual(gotkG, wantkG)
want = true
if got != want {
test.ReportError(t, got, want, i)
}
}

func TestDecafv1_1(t *testing.T) {
var kat testJSONFile
kat.readFile(t, "testdata/decafv1.1_vectors.json")

got := kat.Group
want := "decaf"
if got != want {
test.ReportError(t, got, want)
}
got = kat.Version
want = "v1.1"
if got != want {
test.ReportError(t, got, want)
}
var d goldilocks.Decaf
var scalar goldilocks.Scalar
var P goldilocks.Elt

for i := range kat.Vectors {
k, _ := hex.DecodeString(kat.Vectors[i].K)
wantEnckG, _ := hex.DecodeString(kat.Vectors[i].KG)
wantEnckP, _ := hex.DecodeString(kat.Vectors[i].KP)
scalar.FromBytes(k)

d.MulGen(&P, &scalar)
verify(t, i, &P, wantEnckG)

d.Mul(&P, &scalar, d.Generator())
verify(t, i, &P, wantEnckG)

d.Mul(&P, &scalar, &P)
verify(t, i, &P, wantEnckP)
}
// fmt.Printf("2GE: %v\n%v\n\n", GE, enc(GE))

// GT := c.push(GE)
// GT.ToAffine()
// fmt.Printf("GT: %v\n%v\n", GT, enc(GT))

// fmt.Printf("0: %v\n", hex.EncodeToString(d.Marshal(d.Identity())))
// fmt.Printf("G: %v\n", hex.EncodeToString(d.Marshal(d.Generator())))
// P := d.Generator()
// fmt.Printf("G:\n%v\n%v\n", P, hex.EncodeToString(d.Marshal(P)))
// for i := 1; i < 2; i++ {
// P = d.Add(P, P)
// fmt.Printf("[2^%v]G:\n%v\n", i, P)
// fmt.Printf("[2^%v]G: %v\n", i, hex.EncodeToString(d.Marshal(P)))
// }
}

func BenchmarkDecaf(b *testing.B) {
Expand Down Expand Up @@ -79,7 +150,7 @@ func BenchmarkDecaf(b *testing.B) {
})
b.Run("Unmarshal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
d.Unmarshal(enc)
P.Unmarshal(enc)
}
})
}

0 comments on commit a62d6bd

Please sign in to comment.