Skip to content

Commit 1a58619

Browse files
committed
Adding decaf v1.1 and kat tests.
1 parent d38cd5d commit 1a58619

File tree

7 files changed

+378
-131
lines changed

7 files changed

+378
-131
lines changed

ecc/goldilocks/constants.go

+21-13
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,28 @@ var (
3737
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3838
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3939
}
40-
// aMinusD is paramA-paramD used for Decaf.
41-
aMinusD = fp.Elt{0xaa, 0x98}
42-
// aMinusD is paramA-paramD used for Decaf.
40+
// paramDTwist is -39082 in Fp. The D parameter of the twist curve.
41+
paramDTwist = fp.Elt{
42+
0x55, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
43+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
45+
0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
46+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
47+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
49+
}
50+
// aMinusD is paramA-paramD used in Decaf.
4351
aMinusDTwist = fp.Elt{0xa9, 0x98}
52+
// sqrtAMinusDTwist is the smallest sqrt(paramATwist-paramDTwist) used in Decaf.
53+
sqrtAMinusDTwist = fp.Elt{
54+
0x36, 0x27, 0x57, 0x45, 0x0f, 0xef, 0x42, 0x96,
55+
0x52, 0xce, 0x20, 0xaa, 0xf6, 0x7b, 0x33, 0x60,
56+
0xd2, 0xde, 0x6e, 0xfd, 0xf4, 0x66, 0x9a, 0x83,
57+
0xba, 0x14, 0x8c, 0x96, 0x80, 0xd7, 0xa2, 0x64,
58+
0x4b, 0xd5, 0xb8, 0xa5, 0xb8, 0xa7, 0xf1, 0xa1,
59+
0xa0, 0x6a, 0xa2, 0x2f, 0x72, 0x8d, 0xf6, 0x3b,
60+
0x68, 0xf7, 0x24, 0xeb, 0xfb, 0x62, 0xd9, 0x22,
61+
}
4462
// order is 2^446-0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d,
4563
// which is the number of points in the prime subgroup.
4664
order = Scalar{
@@ -66,15 +84,5 @@ var (
6684
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
6785
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
6886
}
69-
// paramDTwist is -39082 in Fp. The D parameter of the twist curve.
70-
paramDTwist = fp.Elt{
71-
0x55, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
72-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
73-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
74-
0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
75-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
76-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
77-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
78-
}
7987
errInvalidDecoding = errors.New("invalid decoding")
8088
)

ecc/goldilocks/decaf.go

+62-60
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package goldilocks
22

3-
import fp "github.com/cloudflare/circl/math/fp448"
3+
import (
4+
fp "github.com/cloudflare/circl/math/fp448"
5+
)
46

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

34+
// Double is
35+
func (d Decaf) Double(c, a *Elt) { c.p = a.p; c.p.Double() }
36+
3237
// Neg is
3338
func (d Decaf) Neg(c, a *Elt) { c.p = a.p; c.p.cneg(1) }
3439

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

5055
// Marshal is
5156
func (e *Elt) Marshal() []byte {
52-
r, u := &fp.Elt{}, &fp.Elt{}
53-
one, s := &fp.Elt{}, &fp.Elt{}
54-
x, y, ta, tb, z := e.p.x, e.p.y, e.p.ta, e.p.tb, e.p.z
55-
t0, t1 := z, y
57+
x, ta, tb, z := e.p.x, e.p.ta, e.p.tb, e.p.z
58+
one, t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
5659
fp.SetOne(one)
57-
fp.AddSub(&t0, &t1) // (t0,t1) = (z+y,z-y)
58-
fp.Mul(&t0, &t0, &t1) // t0 = (z+y)*(z-y)
59-
fp.Mul(&t0, &t0, &aMinusDTwist) // t0 = (a-d)*(z+y)*(z-y)
60-
fp.InvSqrt(r, one, &t0) // r = 1/sqrt( (a-d)*(z+y)*(z-y) )
61-
fp.Mul(u, r, &aMinusDTwist) // u = (a-d)*r
62-
fp.Mul(&t0, u, &z) // t0 = u*Z
63-
fp.Add(&t0, &t0, &t0) // t0 = 2*u*Z
64-
fp.Neg(&t0, &t0) // t0 = -2*u*Z
65-
isNeg := fp.Parity(&t0) // isNeg = sgn(t0)
66-
fp.Neg(&t1, r) // t1 = -r
67-
fp.Cmov(r, &t1, uint(isNeg)) // if -2*u*Z is negative then r = -r
68-
fp.Mul(&t1, &ta, &tb) // t1 = Ta*Tb = T
69-
fp.Mul(&t1, &t1, &y) // t1 = Y*T
70-
fp.Mul(&t1, &t1, &paramDTwist) // t1 = d*Y*T
71-
fp.Mul(&t0, &z, &x) // t0 = Z*X
72-
fp.Neg(&t0, &t0) // t0 = a*Z*X
73-
fp.Sub(&t0, &t0, &t1) // t0 = a*Z*X - d*Y*T
74-
fp.Mul(&t0, &t0, r) // t0 = r*(a*Z*X - d*Y*T)
75-
fp.Add(&t0, &t0, &y) // t0 = r*(a*Z*X - d*Y*T) + Y
76-
fp.Mul(s, &t0, u) // s = (u)*(r*(a*Z*X - d*Y*T) + Y)
77-
fp.Neg(s, s) // s = (u/a)*(r*(a*Z*X - d*Y*T) + Y)
78-
isNeg = fp.Parity(s) // isNeg = sgn(s)
79-
fp.Neg(&t1, s) // t1 = -s
80-
fp.Cmov(s, &t1, uint(isNeg)) // if s is negative then s = -s
60+
fp.Mul(t, &ta, &tb) // t = ta*tb
61+
t0, t1 := x, *t // (t0,t1) = (x,t)
62+
fp.Sqr(t2, &x) // t2 = x^2
63+
fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t)
64+
fp.Mul(&t1, &t0, &t1) // t1 = (x+t)*(x-t)
65+
fp.Mul(&t0, &t1, &aMinusDTwist) // t0 = (a-d)*(x+t)*(x-t)
66+
fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t)
67+
fp.InvSqrt(&t0, one, &t0) // t0 = 1/sqrt( x^2*(a-d)*(z+y)*(z-y) )
68+
fp.Mul(&t1, &t1, &t0) // t1 = (z+y)*(z-y)/sqrt( x^2*(a-d)*(z+y)*(z-y) )
69+
fp.Mul(t2, &t1, &sqrtAMinusDTwist) // t2 = sqrt( (z+y)*(z-y) )/z
70+
isNeg := fp.Parity(t2) // isNeg = sgn(t2)
71+
fp.Neg(t2, &t1) // t2 = -t1
72+
fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1
73+
fp.Mul(s, &t1, &z) // s = t1*z
74+
fp.Sub(s, s, t) // s = t1*z - t
75+
fp.Mul(s, s, &x) // s = x*(t1*z - t)
76+
fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t)
77+
fp.Mul(s, s, &aMinusDTwist) // s = (a-d)*isr*x*(t1*z - t)
78+
isNeg = fp.Parity(s) // isNeg = sgn(s)
79+
fp.Neg(&t0, s) // t0 = -s
80+
fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s
8181

8282
var encS [fp.Size]byte
8383
_ = fp.ToBytes(encS[:], s)
8484
return encS[:]
8585
}
8686

8787
// Unmarshal is
88-
func (d Decaf) Unmarshal(b []byte) (*Elt, error) {
88+
func (e *Elt) Unmarshal(b []byte) error {
8989
if len(b) < fp.Size {
90-
return nil, errInvalidDecoding
90+
return errInvalidDecoding
9191
}
9292

9393
s := &fp.Elt{}
9494
copy(s[:], b[:fp.Size])
9595
isNeg := fp.Parity(s)
9696
p := fp.P()
9797
if isNeg == 1 || !isLessThan(b[:fp.Size], p[:]) {
98-
return nil, errInvalidDecoding
98+
return errInvalidDecoding
9999
}
100100

101-
one, u, v, w := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
101+
one, s2, den, num := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
102+
isr, altx := &fp.Elt{}, &fp.Elt{}
102103
t0, t1 := &fp.Elt{}, &fp.Elt{}
103-
e := &Elt{}
104104
fp.SetOne(one)
105-
fp.Add(&e.p.x, s, s) // X = 2*s
106-
fp.Sqr(t0, s) // t0 = s^2
107-
fp.Sub(&e.p.z, one, t0) // Z = 1 + a*s^2
108-
fp.Mul(t1, t0, &paramDTwist) // t1 = d*s^2
109-
fp.Add(t1, t1, t1) // t1 = 2*d*s^2
110-
fp.Add(t1, t1, t1) // t1 = 4*d*s^2
111-
fp.Sqr(u, &e.p.z) // u = Z^2
112-
fp.Sub(u, u, t1) // u = Z^2 - 4*d*s^2
113-
fp.Mul(t0, t0, u) // t0 = u*s^2
114-
isQR := fp.InvSqrt(v, one, t0) // v = 1/sqrt(u*s^2)
115-
var isZero byte
105+
fp.Sqr(s2, s) // s2 = s^2
106+
fp.Sub(den, one, s2) // den = 1 + a*s^2
107+
fp.Mul(t1, s2, &paramDTwist) // t1 = d*s^2
108+
fp.Add(t1, t1, t1) // t1 = 2*d*s^2
109+
fp.Add(t1, t1, t1) // t1 = 4*d*s^2
110+
fp.Sqr(t0, den) // num = (1 + a*s^2)^2
111+
fp.Sub(num, t0, t1) // num = (1 + a*s^2)^2 - 4*d*s^2
112+
fp.Mul(t0, t0, num) // t0 = num*den^2
113+
isQR := fp.InvSqrt(isr, one, t0) // v = 1/sqrt(num*den^2)
116114
if !isQR {
117-
if !fp.IsZero(t0) {
118-
return nil, errInvalidDecoding
119-
}
120-
isZero = 1
115+
return errInvalidDecoding
121116
}
122-
fp.Mul(t0, u, v) // t0 = u*v
123-
isNeg = fp.Parity(t0) // isNeg = sgn(u*v)
124-
fp.Neg(t1, v) // t1 = -v
125-
fp.Cmov(v, t1, uint(isNeg)) // if u*v is negative then v = -v
126-
fp.Sub(w, &fp.Elt{2}, &e.p.z) // w = 2-Z
127-
fp.Mul(w, w, s) // w = s*(2-Z)
128-
fp.Mul(w, w, v) // w = v*s*(2-Z)
129-
fp.Add(w, w, &fp.Elt{isZero}) // if s=0 then w = w+1
130-
fp.Mul(&e.p.y, &e.p.z, w) // Y = w*Z
131-
e.p.ta, e.p.tb = e.p.x, *w // T = Ta*Tb = w*X
132-
return e, nil
117+
fp.Mul(t1, den, isr) // altx = isr*den
118+
fp.Mul(t1, t1, s) // altx = s*isr*den
119+
fp.Add(t1, t1, t1) // t1 = 2*s*isr*den
120+
fp.Mul(altx, t1, &sqrtAMinusDTwist) // altx = 2*s*isr*den*sqrt(A-D)
121+
isNeg = fp.Parity(altx) // isNeg = sgn(altx)
122+
fp.Neg(t0, isr) // t0 = -isr
123+
fp.Cmov(isr, t0, uint(isNeg)) // if altx is negative then isr = -isr
124+
fp.Sqr(&e.p.x, isr) // x = isr^2
125+
fp.Mul(&e.p.x, &e.p.x, den) // x = isr^2*den
126+
fp.Mul(&e.p.x, &e.p.x, num) // x = isr^2*den*num
127+
fp.Mul(&e.p.x, &e.p.x, s) // x = s*isr^2*den*num
128+
fp.Add(&e.p.x, &e.p.x, &e.p.x) // x = 2*s*isr^2*den*num
129+
fp.Mul(&e.p.y, isr, den) // y = isr*den
130+
fp.Add(t0, one, s2) // t0 = 1 - a*s^2
131+
fp.Mul(&e.p.y, &e.p.y, t0) // y = (1 - a*s^2)*isr*den
132+
e.p.ta, e.p.tb = e.p.x, e.p.y // T = Ta*Tb = x*y
133+
fp.SetOne(&e.p.z)
134+
return nil
133135
}

ecc/goldilocks/decaf_test.go

+104-33
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package goldilocks_test
22

33
import (
4+
"bytes"
45
"crypto/rand"
56
"encoding/hex"
7+
"encoding/json"
68
"fmt"
9+
"io/ioutil"
10+
"os"
711
"testing"
812

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

1317
func TestDecafDevel(t *testing.T) {
1418
var d goldilocks.Decaf
19+
var P goldilocks.Elt
20+
1521
G := d.Generator()
16-
fmt.Printf("G: %v\n%v\n\n", G, hex.EncodeToString(G.Marshal()))
17-
18-
Q := d.Identity()
19-
for i := 0; i < 18; i++ {
20-
enc := Q.Marshal()
21-
decP, err := d.Unmarshal(enc)
22-
if err != nil {
23-
t.Fatalf("dd")
24-
}
25-
got := d.AreEqual(Q, decP)
26-
want := true
27-
if got != want {
28-
fmt.Printf("%v\n", Q)
29-
fmt.Printf("%v\n", decP)
30-
test.ReportError(t, got, want, i)
31-
}
32-
d.Add(Q, Q, G)
22+
d.Double(G, G)
23+
encG := G.Marshal()
24+
P.Unmarshal(encG)
25+
encP := P.Marshal()
26+
// fmt.Printf("G: %v\n", G)
27+
fmt.Printf("encG: %v\n", hex.EncodeToString(encG))
28+
// fmt.Printf("P: %v\n", P)
29+
fmt.Printf("valid: %v\n", d.IsValid(&P))
30+
fmt.Printf("equal: %v\n", d.AreEqual(G, &P))
31+
fmt.Printf("encP: %v\n", hex.EncodeToString(encP))
32+
33+
}
34+
35+
type testJSONFile struct {
36+
Group string `json:"group"`
37+
Version string `json:"version"`
38+
Generator struct {
39+
X string `json:"x"`
40+
Y string `json:"y"`
41+
T string `json:"t"`
42+
Z string `json:"z"`
43+
} `json:"generator"`
44+
Vectors []struct {
45+
K string `json:"k"`
46+
KG string `json:"kG"`
47+
KP string `json:"kP"`
48+
} `json:"vectors"`
49+
}
50+
51+
func (kat *testJSONFile) readFile(t *testing.T, fileName string) {
52+
jsonFile, err := os.Open(fileName)
53+
if err != nil {
54+
t.Fatalf("File %v can not be opened. Error: %v", fileName, err)
55+
}
56+
defer jsonFile.Close()
57+
input, _ := ioutil.ReadAll(jsonFile)
58+
59+
err = json.Unmarshal(input, &kat)
60+
if err != nil {
61+
t.Fatalf("File %v can not be loaded. Error: %v", fileName, err)
62+
}
63+
}
64+
65+
func verify(t *testing.T, i int, gotkG *goldilocks.Elt, wantEnckG []byte) {
66+
var d goldilocks.Decaf
67+
wantkG := &goldilocks.Elt{}
68+
69+
gotEnckG := gotkG.Marshal()
70+
got := bytes.Equal(gotEnckG, wantEnckG)
71+
want := true
72+
if got != want {
73+
test.ReportError(t, got, want, i)
74+
}
75+
76+
err := wantkG.Unmarshal(wantEnckG)
77+
got = err == nil &&
78+
d.IsValid(gotkG) &&
79+
d.IsValid(wantkG) &&
80+
d.AreEqual(gotkG, wantkG)
81+
want = true
82+
if got != want {
83+
test.ReportError(t, got, want, i)
84+
}
85+
}
86+
87+
func TestDecafv1_1(t *testing.T) {
88+
var kat testJSONFile
89+
kat.readFile(t, "testdata/decafv1.1_vectors.json")
90+
91+
got := kat.Group
92+
want := "decaf"
93+
if got != want {
94+
test.ReportError(t, got, want)
95+
}
96+
got = kat.Version
97+
want = "v1.1"
98+
if got != want {
99+
test.ReportError(t, got, want)
100+
}
101+
var d goldilocks.Decaf
102+
var scalar goldilocks.Scalar
103+
var P goldilocks.Elt
104+
105+
for i := range kat.Vectors {
106+
k, _ := hex.DecodeString(kat.Vectors[i].K)
107+
wantEnckG, _ := hex.DecodeString(kat.Vectors[i].KG)
108+
wantEnckP, _ := hex.DecodeString(kat.Vectors[i].KP)
109+
scalar.FromBytes(k)
110+
111+
d.MulGen(&P, &scalar)
112+
verify(t, i, &P, wantEnckG)
113+
114+
d.Mul(&P, &scalar, d.Generator())
115+
verify(t, i, &P, wantEnckG)
116+
117+
d.Mul(&P, &scalar, &P)
118+
verify(t, i, &P, wantEnckP)
33119
}
34-
// fmt.Printf("2GE: %v\n%v\n\n", GE, enc(GE))
35-
36-
// GT := c.push(GE)
37-
// GT.ToAffine()
38-
// fmt.Printf("GT: %v\n%v\n", GT, enc(GT))
39-
40-
// fmt.Printf("0: %v\n", hex.EncodeToString(d.Marshal(d.Identity())))
41-
// fmt.Printf("G: %v\n", hex.EncodeToString(d.Marshal(d.Generator())))
42-
// P := d.Generator()
43-
// fmt.Printf("G:\n%v\n%v\n", P, hex.EncodeToString(d.Marshal(P)))
44-
// for i := 1; i < 2; i++ {
45-
// P = d.Add(P, P)
46-
// fmt.Printf("[2^%v]G:\n%v\n", i, P)
47-
// fmt.Printf("[2^%v]G: %v\n", i, hex.EncodeToString(d.Marshal(P)))
48-
// }
49120
}
50121

51122
func BenchmarkDecaf(b *testing.B) {
@@ -79,7 +150,7 @@ func BenchmarkDecaf(b *testing.B) {
79150
})
80151
b.Run("Unmarshal", func(b *testing.B) {
81152
for i := 0; i < b.N; i++ {
82-
d.Unmarshal(enc)
153+
P.Unmarshal(enc)
83154
}
84155
})
85156
}

0 commit comments

Comments
 (0)