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
2020import (
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.
9095func (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.
101107func (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.
152255func 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