1
- // Package goldilocks provides arithmetic operations on the Goldilocks curve.
1
+ // Package goldilocks provides arithmetic operations on the Goldilocks (edwards448) curve.
2
2
//
3
3
// Goldilocks Curve
4
4
//
@@ -19,111 +19,142 @@ package goldilocks
19
19
20
20
import (
21
21
"crypto/subtle"
22
+ "encoding/binary"
22
23
"errors"
23
24
"math/bits"
24
25
25
- "github.com/cloudflare/circl/internal/ted448"
26
+ ted "github.com/cloudflare/circl/ecc/goldilocks /internal/ted448"
26
27
fp "github.com/cloudflare/circl/math/fp448"
27
28
)
28
29
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
30
34
31
- type Point ted448.Point
35
+ // Identity returns the identity point.
36
+ func Identity () Point { return Point (ted .Identity ()) }
32
37
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 } }
35
40
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 [:] }
38
43
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 }
48
46
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
59
59
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
74
83
}
75
84
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 )
86
91
}
87
92
88
93
// ScalarBaseMult calculates P = kG, where G is the generator of the Goldilocks
89
94
// curve. This function runs in constant time.
90
95
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 )
95
101
push (P , & Q )
96
102
}
97
103
98
104
// CombinedMult calculates P = mG+nQ, where G is the generator of the Goldilocks
99
105
// curve. This function does NOT run in constant time as is only used for
100
106
// signature verification.
101
107
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
106
112
pull (& phiQ , Q )
107
- ted448 .CombinedMult (& R , m4 , n4 , & phiQ )
113
+ ted .CombinedMult (& R , & m4 , & n4 , & phiQ )
108
114
push (P , & R )
109
115
}
110
116
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 )
112
125
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
+ }
115
131
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
+ }
118
149
119
150
// 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 ) }
121
152
122
153
// 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 ) }
124
155
125
156
// 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 ) {
127
158
Px , Py , Pz := & P .X , & P .Y , & P .Z
128
159
a , b , c , d , e , f , g , h := & Q .X , & Q .Y , & Q .Z , & fp.Elt {}, & Q .Ta , & Q .X , & Q .Y , & Q .Tb
129
160
fp .Add (e , Px , Py ) // x+y
@@ -147,6 +178,78 @@ func isogeny4(Q, P *ted448.Point, isPull bool) {
147
178
fp .Mul (& Q .Y , g , h ) // Y = G * H, // T = E * H
148
179
}
149
180
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
+
150
253
// isLessThan returns 1 if 0 <= x < y, and assumes that slices are of the
151
254
// same length and are interpreted in little-endian order.
152
255
func isLessThan (x , y []byte ) int {
@@ -159,25 +262,56 @@ func isLessThan(x, y []byte) int {
159
262
return ((xi - yi ) >> (bits .UintSize - 1 )) & 1
160
263
}
161
264
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
182
298
}
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
183
314
)
315
+
316
+ // ErrInvalidDecoding alerts of an error during decoding a point.
317
+ var ErrInvalidDecoding = errors .New ("goldilocks: invalid point decoding" )
0 commit comments