1
2
3
4
5 package edwards25519
6
7 import (
8 "bytes"
9 "encoding/hex"
10 "math/big"
11 mathrand "math/rand"
12 "reflect"
13 "testing"
14 "testing/quick"
15 )
16
17
18
19 func quickCheckConfig(slowScale int) *quick.Config {
20 cfg := new(quick.Config)
21 if !testing.Short() {
22 cfg.MaxCountScale = float64(slowScale)
23 }
24 return cfg
25 }
26
27 var scOneBytes = [32]byte{1}
28 var scOne, _ = new(Scalar).SetCanonicalBytes(scOneBytes[:])
29 var scMinusOne = new(Scalar).Subtract(new(Scalar), scOne)
30
31
32
33 func (Scalar) Generate(rand *mathrand.Rand, size int) reflect.Value {
34 var s [32]byte
35 diceRoll := rand.Intn(100)
36 switch {
37 case diceRoll == 0:
38 case diceRoll == 1:
39 s = scOneBytes
40 case diceRoll == 2:
41 s = [32]byte(scMinusOne.Bytes())
42 case diceRoll < 5:
43
44 rand.Read(s[:16])
45 s[15] &= (1 << 5) - 1
46 case diceRoll < 10:
47
48 s[31] = 1 << 4
49 rand.Read(s[:16])
50 s[15] &= (1 << 4) - 1
51 default:
52
53
54
55 rand.Read(s[:])
56 s[31] &= (1 << 4) - 1
57 }
58
59 val := Scalar{}
60 fiatScalarFromBytes((*[4]uint64)(&val.s), &s)
61 fiatScalarToMontgomery(&val.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&val.s))
62
63 return reflect.ValueOf(val)
64 }
65
66 func TestScalarGenerate(t *testing.T) {
67 f := func(sc Scalar) bool {
68 return isReduced(sc.Bytes())
69 }
70 if err := quick.Check(f, quickCheckConfig(1024)); err != nil {
71 t.Errorf("generated unreduced scalar: %v", err)
72 }
73 }
74
75 func TestScalarSetCanonicalBytes(t *testing.T) {
76 f1 := func(in [32]byte, sc Scalar) bool {
77
78 in[len(in)-1] &= (1 << 4) - 1
79 if _, err := sc.SetCanonicalBytes(in[:]); err != nil {
80 return false
81 }
82 repr := sc.Bytes()
83 return bytes.Equal(in[:], repr) && isReduced(repr)
84 }
85 if err := quick.Check(f1, quickCheckConfig(1024)); err != nil {
86 t.Errorf("failed bytes->scalar->bytes round-trip: %v", err)
87 }
88
89 f2 := func(sc1, sc2 Scalar) bool {
90 if _, err := sc2.SetCanonicalBytes(sc1.Bytes()); err != nil {
91 return false
92 }
93 return sc1 == sc2
94 }
95 if err := quick.Check(f2, quickCheckConfig(1024)); err != nil {
96 t.Errorf("failed scalar->bytes->scalar round-trip: %v", err)
97 }
98
99 expectReject := func(b []byte) {
100 t.Helper()
101 s := scOne
102 if out, err := s.SetCanonicalBytes(b[:]); err == nil {
103 t.Errorf("SetCanonicalBytes worked on a non-canonical value")
104 } else if s != scOne {
105 t.Errorf("SetCanonicalBytes modified its receiver")
106 } else if out != nil {
107 t.Errorf("SetCanonicalBytes did not return nil with an error")
108 }
109 }
110
111 b := scMinusOne.Bytes()
112 b[0] += 1
113 expectReject(b)
114
115 b = scMinusOne.Bytes()
116 b[31] += 1
117 expectReject(b)
118
119 b = scMinusOne.Bytes()
120 b[31] |= 0b1000_0000
121 expectReject(b)
122 }
123
124 func TestScalarSetUniformBytes(t *testing.T) {
125 mod, _ := new(big.Int).SetString("27742317777372353535851937790883648493", 10)
126 mod.Add(mod, new(big.Int).Lsh(big.NewInt(1), 252))
127 f := func(in [64]byte, sc Scalar) bool {
128 sc.SetUniformBytes(in[:])
129 repr := sc.Bytes()
130 if !isReduced(repr) {
131 return false
132 }
133 scBig := bigIntFromLittleEndianBytes(repr[:])
134 inBig := bigIntFromLittleEndianBytes(in[:])
135 return inBig.Mod(inBig, mod).Cmp(scBig) == 0
136 }
137 if err := quick.Check(f, quickCheckConfig(1024)); err != nil {
138 t.Error(err)
139 }
140 }
141
142 func TestScalarSetBytesWithClamping(t *testing.T) {
143
144
145 random := "633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e"
146 s, _ := new(Scalar).SetBytesWithClamping(decodeHex(random))
147 p := new(Point).ScalarBaseMult(s)
148 want := "1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94"
149 if got := hex.EncodeToString(p.Bytes()); got != want {
150 t.Errorf("random: got %q, want %q", got, want)
151 }
152
153 zero := "0000000000000000000000000000000000000000000000000000000000000000"
154 s, _ = new(Scalar).SetBytesWithClamping(decodeHex(zero))
155 p = new(Point).ScalarBaseMult(s)
156 want = "693e47972caf527c7883ad1b39822f026f47db2ab0e1919955b8993aa04411d1"
157 if got := hex.EncodeToString(p.Bytes()); got != want {
158 t.Errorf("zero: got %q, want %q", got, want)
159 }
160
161 one := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
162 s, _ = new(Scalar).SetBytesWithClamping(decodeHex(one))
163 p = new(Point).ScalarBaseMult(s)
164 want = "12e9a68b73fd5aacdbcaf3e88c46fea6ebedb1aa84eed1842f07f8edab65e3a7"
165 if got := hex.EncodeToString(p.Bytes()); got != want {
166 t.Errorf("one: got %q, want %q", got, want)
167 }
168 }
169
170 func bigIntFromLittleEndianBytes(b []byte) *big.Int {
171 bb := make([]byte, len(b))
172 for i := range b {
173 bb[i] = b[len(b)-i-1]
174 }
175 return new(big.Int).SetBytes(bb)
176 }
177
178 func TestScalarMultiplyDistributesOverAdd(t *testing.T) {
179 multiplyDistributesOverAdd := func(x, y, z Scalar) bool {
180
181 var t1 Scalar
182 t1.Add(&x, &y)
183 t1.Multiply(&t1, &z)
184
185
186 var t2 Scalar
187 var t3 Scalar
188 t2.Multiply(&x, &z)
189 t3.Multiply(&y, &z)
190 t2.Add(&t2, &t3)
191
192 reprT1, reprT2 := t1.Bytes(), t2.Bytes()
193
194 return t1 == t2 && isReduced(reprT1) && isReduced(reprT2)
195 }
196
197 if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig(1024)); err != nil {
198 t.Error(err)
199 }
200 }
201
202 func TestScalarAddLikeSubNeg(t *testing.T) {
203 addLikeSubNeg := func(x, y Scalar) bool {
204
205 var t1 Scalar
206 t1.Subtract(&x, &y)
207
208
209 var t2 Scalar
210 t2.Negate(&y)
211 t2.Add(&t2, &x)
212
213 return t1 == t2 && isReduced(t1.Bytes())
214 }
215
216 if err := quick.Check(addLikeSubNeg, quickCheckConfig(1024)); err != nil {
217 t.Error(err)
218 }
219 }
220
221 func TestScalarNonAdjacentForm(t *testing.T) {
222 s, _ := (&Scalar{}).SetCanonicalBytes([]byte{
223 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d,
224 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d,
225 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1,
226 0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09,
227 })
228
229 expectedNaf := [256]int8{
230 0, 13, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, -11, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1,
231 0, 0, 0, 0, 9, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 11, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0,
232 -9, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 9, 0,
233 0, 0, 0, -15, 0, 0, 0, 0, -7, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, -3, 0,
234 0, 0, 0, -11, 0, 0, 0, 0, -7, 0, 0, 0, 0, -13, 0, 0, 0, 0, 11, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 1, 0, 0,
235 0, 0, 0, -15, 0, 0, 0, 0, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 13, 0, 0, 0,
236 0, 0, 0, 11, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 7,
237 0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
238 }
239
240 sNaf := s.nonAdjacentForm(5)
241
242 for i := 0; i < 256; i++ {
243 if expectedNaf[i] != sNaf[i] {
244 t.Errorf("Wrong digit at position %d, got %d, expected %d", i, sNaf[i], expectedNaf[i])
245 }
246 }
247 }
248
249 type notZeroScalar Scalar
250
251 func (notZeroScalar) Generate(rand *mathrand.Rand, size int) reflect.Value {
252 var s Scalar
253 var isNonZero uint64
254 for isNonZero == 0 {
255 s = Scalar{}.Generate(rand, size).Interface().(Scalar)
256 fiatScalarNonzero(&isNonZero, (*[4]uint64)(&s.s))
257 }
258 return reflect.ValueOf(notZeroScalar(s))
259 }
260
261 func TestScalarEqual(t *testing.T) {
262 if scOne.Equal(scMinusOne) == 1 {
263 t.Errorf("scOne.Equal(&scMinusOne) is true")
264 }
265 if scMinusOne.Equal(scMinusOne) == 0 {
266 t.Errorf("scMinusOne.Equal(&scMinusOne) is false")
267 }
268 }
269
View as plain text