Skip to content

Commit 3bf0eae

Browse files
committed
Completing the Group interface and hash to curve.
1 parent 45008f1 commit 3bf0eae

25 files changed

+1322
-1014
lines changed

ecc/goldilocks/goldilocks.go

+222-88
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Package goldilocks provides arithmetic operations on the Goldilocks curve.
1+
// Package goldilocks provides arithmetic operations on the Goldilocks (edwards448) curve.
22
//
33
// Goldilocks Curve
44
//
@@ -19,111 +19,142 @@ package goldilocks
1919

2020
import (
2121
"crypto/subtle"
22+
"encoding/binary"
2223
"errors"
2324
"math/bits"
2425

25-
"github.com/cloudflare/circl/internal/ted448"
26+
ted "github.com/cloudflare/circl/ecc/goldilocks/internal/ted448"
2627
fp "github.com/cloudflare/circl/math/fp448"
2728
)
2829

29-
type Scalar = ted448.Scalar
30+
// Point defines a point on the Goldilocks curve using extended projective
31+
// coordinates. For any affine point (x,y) it holds x = X/Z, y = Y/Z, and
32+
// T = Ta*Tb = X*Y/Z.
33+
type Point ted.Point
3034

31-
type Point ted448.Point
35+
// Identity returns the identity point.
36+
func Identity() Point { return Point(ted.Identity()) }
3237

33-
// EncodingSize is the size (in bytes) of an encoded point on the Goldilocks curve.
34-
const EncodingSize = fp.Size + 1
38+
// Generator returns the generator point.
39+
func Generator() Point { return Point{X: genX, Y: genY, Z: fp.One(), Ta: genX, Tb: genY} }
3540

36-
// ErrInvalidDecoding alerts of an error during decoding a point.
37-
var ErrInvalidDecoding = errors.New("invalid decoding")
41+
// Order returns the number of points in the prime subgroup in little-endian order.
42+
func Order() []byte { r := ted.Order(); return r[:] }
3843

39-
// Decode if succeeds constructs a point by decoding the first
40-
// EncodingSize bytes of data.
41-
func (P *Point) Decode(data *[EncodingSize]byte) error {
42-
x, y := &fp.Elt{}, &fp.Elt{}
43-
isByteZero := subtle.ConstantTimeByteEq(data[EncodingSize-1]&0x7F, 0x00)
44-
signX := int(data[EncodingSize-1] >> 7)
45-
copy(y[:], data[:fp.Size])
46-
p := fp.P()
47-
isLessThanP := isLessThan(y[:], p[:])
44+
// ParamD is the D parameter of the Goldilocks curve, D=-39081 in Fp.
45+
func ParamD() fp.Elt { return paramD }
4846

49-
u, v := &fp.Elt{}, &fp.Elt{}
50-
one := fp.One()
51-
fp.Sqr(u, y) // u = y^2
52-
fp.Mul(v, u, &paramD) // v = dy^2
53-
fp.Sub(u, u, &one) // u = y^2-1
54-
fp.Sub(v, v, &one) // v = dy^2-a
55-
isQR := fp.InvSqrt(x, u, v) // x = sqrt(u/v)
56-
isValidXSign := 1 - (fp.IsZero(x) & signX)
57-
fp.Neg(u, x) // u = -x
58-
fp.Cmov(x, u, uint(signX^fp.Parity(x))) // if signX != x mod 2
47+
func (P Point) String() string { return ted.Point(P).String() }
48+
func (P *Point) ToAffine() { (*ted.Point)(P).ToAffine() }
49+
func (P *Point) Neg() { (*ted.Point)(P).Neg() }
50+
func (P *Point) IsEqual(Q *Point) int { return (*ted.Point)(P).IsEqual((*ted.Point)(Q)) }
51+
func (P *Point) Double() { P.Add(P) }
52+
func (P *Point) Add(Q *Point) {
53+
// Formula as in Eq.(5) of "Twisted Edwards Curves Revisited" by
54+
// Hisil H., Wong K.KH., Carter G., Dawson E. (2008)
55+
// https://doi.org/10.1007/978-3-540-89255-7_20
56+
// Formula for curves with a=1.
57+
Px, Py, Pz, Pta, Ptb := &P.X, &P.Y, &P.Z, &P.Ta, &P.Tb
58+
Qx, Qy, Qz, Qta, Qtb := &Q.X, &Q.Y, &Q.Z, &Q.Ta, &Q.Tb
5959

60-
b0 := isByteZero
61-
b1 := isLessThanP
62-
b2 := isQR
63-
b3 := isValidXSign
64-
b := uint(subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF))
65-
fp.Cmov(&P.X, x, b)
66-
fp.Cmov(&P.Y, y, b)
67-
fp.Cmov(&P.Ta, x, b)
68-
fp.Cmov(&P.Tb, y, b)
69-
fp.Cmov(&P.Z, &one, b)
70-
if b == 0 {
71-
return ErrInvalidDecoding
72-
}
73-
return nil
60+
a, b, c, d := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
61+
e, f, g, h := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
62+
ee, ff := &fp.Elt{}, &fp.Elt{}
63+
64+
fp.Mul(a, Px, Qx) // A = x1*x2
65+
fp.Mul(b, Py, Qy) // B = y1*y2
66+
fp.Mul(c, Pta, Ptb) // C = d*t1*t2
67+
fp.Mul(c, c, Qta) //
68+
fp.Mul(c, c, Qtb) //
69+
fp.Mul(c, c, &paramD) //
70+
fp.Mul(d, Pz, Qz) // D = z1*z2
71+
fp.Add(ee, Px, Py) // x1+y1
72+
fp.Add(ff, Qx, Qy) // x2+y2
73+
fp.Mul(e, ee, ff) // E = (x1+y1)*(x2+y2)-A-B
74+
fp.Sub(e, e, a) //
75+
fp.Sub(e, e, b) //
76+
fp.Sub(f, d, c) // F = D-C
77+
fp.Add(g, d, c) // g = D+C
78+
fp.Sub(h, b, a) // H = B-A
79+
fp.Mul(Px, e, f) // X = E * F
80+
fp.Mul(Py, g, h) // Y = G * H
81+
fp.Mul(Pz, f, g) // Z = F * G
82+
P.Ta, P.Tb = *e, *h // T = E * H
7483
}
7584

76-
// Encode sets data with the unique encoding of the point P.
77-
func (P *Point) Encode(data *[EncodingSize]byte) error {
78-
x, y, invZ := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
79-
fp.Inv(invZ, &P.Z) // 1/z
80-
fp.Mul(x, &P.X, invZ) // x/z
81-
fp.Mul(y, &P.Y, invZ) // y/z
82-
fp.Modp(x)
83-
fp.Modp(y)
84-
data[EncodingSize-1] = (x[0] & 1) << 7
85-
return fp.ToBytes(data[:fp.Size], y)
85+
func (P *Point) CMov(Q *Point, b uint) {
86+
fp.Cmov(&P.X, &Q.X, b)
87+
fp.Cmov(&P.Y, &Q.Y, b)
88+
fp.Cmov(&P.Z, &Q.Z, b)
89+
fp.Cmov(&P.Ta, &Q.Ta, b)
90+
fp.Cmov(&P.Tb, &Q.Tb, b)
8691
}
8792

8893
// ScalarBaseMult calculates P = kG, where G is the generator of the Goldilocks
8994
// curve. This function runs in constant time.
9095
func (P *Point) ScalarBaseMult(k *Scalar) {
91-
k4 := &Scalar{}
92-
divBy4(k4, k)
93-
var Q ted448.Point
94-
ted448.ScalarBaseMult(&Q, k4)
96+
// TODO: recheck if this works for any scalar, likely yes.
97+
k4 := &ted.Scalar{}
98+
divBy4ModOrder(k4, &k.k)
99+
var Q ted.Point
100+
ted.ScalarBaseMult(&Q, k4)
95101
push(P, &Q)
96102
}
97103

98104
// CombinedMult calculates P = mG+nQ, where G is the generator of the Goldilocks
99105
// curve. This function does NOT run in constant time as is only used for
100106
// signature verification.
101107
func (P *Point) CombinedMult(m, n *Scalar, Q *Point) {
102-
m4, n4 := &Scalar{}, &Scalar{}
103-
divBy4(m4, m)
104-
divBy4(n4, n)
105-
var R, phiQ ted448.Point
108+
var m4, n4 ted.Scalar
109+
divBy4ModOrder(&m4, &m.k)
110+
divBy4ModOrder(&n4, &n.k)
111+
var R, phiQ ted.Point
106112
pull(&phiQ, Q)
107-
ted448.CombinedMult(&R, m4, n4, &phiQ)
113+
ted.CombinedMult(&R, &m4, &n4, &phiQ)
108114
push(P, &R)
109115
}
110116

111-
func (P *Point) Neg() { fp.Neg(&P.X, &P.X); fp.Neg(&P.Ta, &P.Ta) }
117+
func (P *Point) ScalarMult(k *Scalar, Q *Point) {
118+
var T [4]Point
119+
T[0] = Identity()
120+
T[1] = *Q
121+
T[2] = *Q
122+
T[2].Double()
123+
T[3] = T[2]
124+
T[3].Add(Q)
112125

113-
// Order returns a scalar with the order of the group.
114-
func Order() Scalar { return ted448.Order() }
126+
var R Point
127+
kMod4 := int32(k.k[0] & 0b11)
128+
for i := range T {
129+
R.CMov(&T[i], uint(subtle.ConstantTimeEq(int32(i), kMod4)))
130+
}
115131

116-
// divBy4 calculates z = x/4 mod order.
117-
func divBy4(z, x *Scalar) { z.Mul(x, &invFour) }
132+
var kDiv4 ted.Scalar
133+
for i := 0; i < ScalarSize-1; i++ {
134+
kDiv4[i] = (k.k[i+1] << 6) | (k.k[i] >> 2)
135+
}
136+
kDiv4[ScalarSize-1] = k.k[ScalarSize-1] >> 2
137+
138+
var phikQ, phiQ ted.Point
139+
pull(&phiQ, Q)
140+
ted.ScalarMult(&phikQ, &kDiv4, &phiQ)
141+
push(P, &phikQ)
142+
P.Add(&R)
143+
}
144+
145+
// divBy4ModOrder calculates z = x/4 mod order.
146+
func divBy4ModOrder(z, x *ted.Scalar) {
147+
z.Mul(x, &invFour)
148+
}
118149

119150
// pull calculates Q = Iso4(P), where P is a Goldilocks point and Q is a ted448 point.
120-
func pull(Q *ted448.Point, P *Point) { isogeny4(Q, (*ted448.Point)(P), true) }
151+
func pull(Q *ted.Point, P *Point) { isogeny4(Q, (*ted.Point)(P), true) }
121152

122153
// push calculates Q = Iso4^-1(P), where P is a ted448 point and Q is a Goldilocks point.
123-
func push(Q *Point, P *ted448.Point) { isogeny4((*ted448.Point)(Q), P, false) }
154+
func push(Q *Point, P *ted.Point) { isogeny4((*ted.Point)(Q), P, false) }
124155

125156
// isogeny4 is a birational map between ted448 and Goldilocks curves.
126-
func isogeny4(Q, P *ted448.Point, isPull bool) {
157+
func isogeny4(Q, P *ted.Point, isPull bool) {
127158
Px, Py, Pz := &P.X, &P.Y, &P.Z
128159
a, b, c, d, e, f, g, h := &Q.X, &Q.Y, &Q.Z, &fp.Elt{}, &Q.Ta, &Q.X, &Q.Y, &Q.Tb
129160
fp.Add(e, Px, Py) // x+y
@@ -147,6 +178,78 @@ func isogeny4(Q, P *ted448.Point, isPull bool) {
147178
fp.Mul(&Q.Y, g, h) // Y = G * H, // T = E * H
148179
}
149180

181+
type Scalar struct{ k ted.Scalar }
182+
183+
func (z Scalar) String() string { return z.k.String() }
184+
func (z *Scalar) Add(x, y *Scalar) { z.k.Add(&x.k, &y.k) }
185+
func (z *Scalar) Sub(x, y *Scalar) { z.k.Sub(&x.k, &y.k) }
186+
func (z *Scalar) Mul(x, y *Scalar) { z.k.Mul(&x.k, &y.k) }
187+
func (z *Scalar) Neg(x *Scalar) { z.k.Neg(&x.k) }
188+
func (z *Scalar) Inv(x *Scalar) { z.k.Inv(&x.k) }
189+
func (z *Scalar) IsEqual(x *Scalar) int { return subtle.ConstantTimeCompare(z.k[:], x.k[:]) }
190+
func (z *Scalar) SetUint64(n uint64) { z.k = ted.Scalar{}; binary.LittleEndian.PutUint64(z.k[:], n) }
191+
192+
// UnmarshalBinary recovers the scalar from its byte representation in big-endian order.
193+
func (z *Scalar) UnmarshalBinary(b []byte) error { return z.k.UnmarshalBinary(b) }
194+
195+
// MarshalBinary returns the scalar byte representation in big-endian order.
196+
func (z *Scalar) MarshalBinary() ([]byte, error) { return z.k.MarshalBinary() }
197+
198+
// ToBytesLE returns the scalar byte representation in little-endian order.
199+
func (z *Scalar) ToBytesLE() []byte { return z.k.ToBytesLE() }
200+
201+
// ToBytesBE returns the scalar byte representation in big-endian order.
202+
func (z *Scalar) ToBytesBE() []byte { return z.k.ToBytesBE() }
203+
204+
// FromBytesLE stores z = x mod order, where x is a number stored in little-endian order.
205+
func (z *Scalar) FromBytesLE(x []byte) { z.k.FromBytesLE(x) }
206+
207+
// FromBytesBE stores z = x mod order, where x is a number stored in big-endian order.
208+
func (z *Scalar) FromBytesBE(x []byte) { z.k.FromBytesBE(x) }
209+
210+
var (
211+
// genX is the x-coordinate of the generator of Goldilocks curve.
212+
genX = fp.Elt{ // little-endian
213+
0x5e, 0xc0, 0x0c, 0xc7, 0x2b, 0xa8, 0x26, 0x26,
214+
0x8e, 0x93, 0x00, 0x8b, 0xe1, 0x80, 0x3b, 0x43,
215+
0x11, 0x65, 0xb6, 0x2a, 0xf7, 0x1a, 0xae, 0x12,
216+
0x64, 0xa4, 0xd3, 0xa3, 0x24, 0xe3, 0x6d, 0xea,
217+
0x67, 0x17, 0x0f, 0x47, 0x70, 0x65, 0x14, 0x9e,
218+
0xda, 0x36, 0xbf, 0x22, 0xa6, 0x15, 0x1d, 0x22,
219+
0xed, 0x0d, 0xed, 0x6b, 0xc6, 0x70, 0x19, 0x4f,
220+
}
221+
// genY is the y-coordinate of the generator of Goldilocks curve.
222+
genY = fp.Elt{ // little-endian
223+
0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98,
224+
0xad, 0xc8, 0xd7, 0x4e, 0x2c, 0x13, 0xbd, 0xfd,
225+
0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a,
226+
0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87,
227+
0x40, 0x98, 0xa3, 0x6c, 0x73, 0x73, 0xea, 0x4b,
228+
0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88,
229+
0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69,
230+
}
231+
// paramD is the D parameter of the Goldilocks curve, D=-39081 in Fp.
232+
paramD = fp.Elt{ // little-endian
233+
0x56, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
234+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
235+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
236+
0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
237+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
238+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
239+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
240+
}
241+
// invFour is 1/4 mod order, where order = ted.Order().
242+
invFour = ted.Scalar{ // little-endian
243+
0x3d, 0x11, 0xd6, 0xaa, 0xa4, 0x30, 0xde, 0x48,
244+
0xd5, 0x63, 0x71, 0xa3, 0x9c, 0x30, 0x5b, 0x08,
245+
0xa4, 0x8d, 0xb5, 0x6b, 0xd2, 0xb6, 0x13, 0x71,
246+
0xfa, 0x88, 0x32, 0xdf, 0xff, 0xff, 0xff, 0xff,
247+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
248+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
249+
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
250+
}
251+
)
252+
150253
// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the
151254
// same length and are interpreted in little-endian order.
152255
func isLessThan(x, y []byte) int {
@@ -159,25 +262,56 @@ func isLessThan(x, y []byte) int {
159262
return ((xi - yi) >> (bits.UintSize - 1)) & 1
160263
}
161264

162-
var (
163-
// invFour is 1/4 mod order, where order = ted448.Order().
164-
invFour = Scalar{
165-
0x3d, 0x11, 0xd6, 0xaa, 0xa4, 0x30, 0xde, 0x48,
166-
0xd5, 0x63, 0x71, 0xa3, 0x9c, 0x30, 0x5b, 0x08,
167-
0xa4, 0x8d, 0xb5, 0x6b, 0xd2, 0xb6, 0x13, 0x71,
168-
0xfa, 0x88, 0x32, 0xdf, 0xff, 0xff, 0xff, 0xff,
169-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
170-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
171-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
172-
}
173-
// paramD is the D parameter of the Goldilocks curve, D=-39081 in Fp.
174-
paramD = fp.Elt{
175-
0x56, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
176-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
177-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
178-
0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff,
179-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
180-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
181-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
265+
// Decode if succeeds constructs a point by decoding the first
266+
// EncodingSize bytes of data.
267+
func (P *Point) Decode(data *[EncodingSize]byte) error {
268+
x, y := &fp.Elt{}, &fp.Elt{}
269+
isByteZero := subtle.ConstantTimeByteEq(data[EncodingSize-1]&0x7F, 0x00)
270+
signX := int(data[EncodingSize-1] >> 7)
271+
copy(y[:], data[:fp.Size])
272+
p := fp.P()
273+
isLessThanP := isLessThan(y[:], p[:])
274+
275+
u, v := &fp.Elt{}, &fp.Elt{}
276+
one := fp.One()
277+
fp.Sqr(u, y) // u = y^2
278+
fp.Mul(v, u, &paramD) // v = dy^2
279+
fp.Sub(u, u, &one) // u = y^2-1
280+
fp.Sub(v, v, &one) // v = dy^2-a
281+
isQR := fp.InvSqrt(x, u, v) // x = sqrt(u/v)
282+
isValidXSign := 1 - (fp.IsZero(x) & signX)
283+
fp.Neg(u, x) // u = -x
284+
fp.Cmov(x, u, uint(signX^fp.Parity(x))) // if signX != x mod 2
285+
286+
b0 := isByteZero
287+
b1 := isLessThanP
288+
b2 := isQR
289+
b3 := isValidXSign
290+
b := uint(subtle.ConstantTimeEq(int32(8*b3+4*b2+2*b1+b0), 0xF))
291+
fp.Cmov(&P.X, x, b)
292+
fp.Cmov(&P.Y, y, b)
293+
fp.Cmov(&P.Ta, x, b)
294+
fp.Cmov(&P.Tb, y, b)
295+
fp.Cmov(&P.Z, &one, b)
296+
if b == 0 {
297+
return ErrInvalidDecoding
182298
}
299+
return nil
300+
}
301+
302+
// Encode sets data with the unique encoding of the point P.
303+
func (P *Point) Encode(data *[EncodingSize]byte) error {
304+
P.ToAffine()
305+
data[EncodingSize-1] = (P.X[0] & 1) << 7
306+
return fp.ToBytes(data[:fp.Size], &P.Y)
307+
}
308+
309+
const (
310+
// EncodingSize is the size (in bytes) of an encoded point on the Goldilocks curve.
311+
EncodingSize = fp.Size + 1
312+
// ScalarSize is the size (in bytes) of scalars.
313+
ScalarSize = ted.ScalarSize
183314
)
315+
316+
// ErrInvalidDecoding alerts of an error during decoding a point.
317+
var ErrInvalidDecoding = errors.New("goldilocks: invalid point decoding")

0 commit comments

Comments
 (0)