github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/crypto/internal/edwards25519/scalar_test.go (about) 1 // Copyright (c) 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 var scOneBytes = [32]byte{1} 18 var scOne, _ = new(Scalar).SetCanonicalBytes(scOneBytes[:]) 19 var scMinusOne, _ = new(Scalar).SetCanonicalBytes(scalarMinusOneBytes[:]) 20 21 // Generate returns a valid (reduced modulo l) Scalar with a distribution 22 // weighted towards high, low, and edge values. 23 func (Scalar) Generate(rand *mathrand.Rand, size int) reflect.Value { 24 var s [32]byte 25 diceRoll := rand.Intn(100) 26 switch { 27 case diceRoll == 0: 28 case diceRoll == 1: 29 s = scOneBytes 30 case diceRoll == 2: 31 s = scalarMinusOneBytes 32 case diceRoll < 5: 33 // Generate a low scalar in [0, 2^125). 34 rand.Read(s[:16]) 35 s[15] &= (1 << 5) - 1 36 case diceRoll < 10: 37 // Generate a high scalar in [2^252, 2^252 + 2^124). 38 s[31] = 1 << 4 39 rand.Read(s[:16]) 40 s[15] &= (1 << 4) - 1 41 default: 42 // Generate a valid scalar in [0, l) by returning [0, 2^252) which has a 43 // negligibly different distribution (the former has a 2^-127.6 chance 44 // of being out of the latter range). 45 rand.Read(s[:]) 46 s[31] &= (1 << 4) - 1 47 } 48 49 val := Scalar{} 50 fiatScalarFromBytes((*[4]uint64)(&val.s), &s) 51 fiatScalarToMontgomery(&val.s, (*fiatScalarNonMontgomeryDomainFieldElement)(&val.s)) 52 53 return reflect.ValueOf(val) 54 } 55 56 // quickCheckConfig1024 will make each quickcheck test run (1024 * -quickchecks) 57 // times. The default value of -quickchecks is 100. 58 var quickCheckConfig1024 = &quick.Config{MaxCountScale: 1 << 10} 59 60 func TestScalarGenerate(t *testing.T) { 61 f := func(sc Scalar) bool { 62 return isReduced(sc.Bytes()) 63 } 64 if err := quick.Check(f, quickCheckConfig1024); err != nil { 65 t.Errorf("generated unreduced scalar: %v", err) 66 } 67 } 68 69 func TestScalarSetCanonicalBytes(t *testing.T) { 70 f1 := func(in [32]byte, sc Scalar) bool { 71 // Mask out top 4 bits to guarantee value falls in [0, l). 72 in[len(in)-1] &= (1 << 4) - 1 73 if _, err := sc.SetCanonicalBytes(in[:]); err != nil { 74 return false 75 } 76 repr := sc.Bytes() 77 return bytes.Equal(in[:], repr) && isReduced(repr) 78 } 79 if err := quick.Check(f1, quickCheckConfig1024); err != nil { 80 t.Errorf("failed bytes->scalar->bytes round-trip: %v", err) 81 } 82 83 f2 := func(sc1, sc2 Scalar) bool { 84 if _, err := sc2.SetCanonicalBytes(sc1.Bytes()); err != nil { 85 return false 86 } 87 return sc1 == sc2 88 } 89 if err := quick.Check(f2, quickCheckConfig1024); err != nil { 90 t.Errorf("failed scalar->bytes->scalar round-trip: %v", err) 91 } 92 93 b := scalarMinusOneBytes 94 b[31] += 1 95 s := scOne 96 if out, err := s.SetCanonicalBytes(b[:]); err == nil { 97 t.Errorf("SetCanonicalBytes worked on a non-canonical value") 98 } else if s != scOne { 99 t.Errorf("SetCanonicalBytes modified its receiver") 100 } else if out != nil { 101 t.Errorf("SetCanonicalBytes did not return nil with an error") 102 } 103 } 104 105 func TestScalarSetUniformBytes(t *testing.T) { 106 mod, _ := new(big.Int).SetString("27742317777372353535851937790883648493", 10) 107 mod.Add(mod, new(big.Int).Lsh(big.NewInt(1), 252)) 108 f := func(in [64]byte, sc Scalar) bool { 109 sc.SetUniformBytes(in[:]) 110 repr := sc.Bytes() 111 if !isReduced(repr) { 112 return false 113 } 114 scBig := bigIntFromLittleEndianBytes(repr[:]) 115 inBig := bigIntFromLittleEndianBytes(in[:]) 116 return inBig.Mod(inBig, mod).Cmp(scBig) == 0 117 } 118 if err := quick.Check(f, quickCheckConfig1024); err != nil { 119 t.Error(err) 120 } 121 } 122 123 func TestScalarSetBytesWithClamping(t *testing.T) { 124 // Generated with libsodium.js 1.0.18 crypto_scalarmult_ed25519_base. 125 126 random := "633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e" 127 s, _ := new(Scalar).SetBytesWithClamping(decodeHex(random)) 128 p := new(Point).ScalarBaseMult(s) 129 want := "1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94" 130 if got := hex.EncodeToString(p.Bytes()); got != want { 131 t.Errorf("random: got %q, want %q", got, want) 132 } 133 134 zero := "0000000000000000000000000000000000000000000000000000000000000000" 135 s, _ = new(Scalar).SetBytesWithClamping(decodeHex(zero)) 136 p = new(Point).ScalarBaseMult(s) 137 want = "693e47972caf527c7883ad1b39822f026f47db2ab0e1919955b8993aa04411d1" 138 if got := hex.EncodeToString(p.Bytes()); got != want { 139 t.Errorf("zero: got %q, want %q", got, want) 140 } 141 142 one := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 143 s, _ = new(Scalar).SetBytesWithClamping(decodeHex(one)) 144 p = new(Point).ScalarBaseMult(s) 145 want = "12e9a68b73fd5aacdbcaf3e88c46fea6ebedb1aa84eed1842f07f8edab65e3a7" 146 if got := hex.EncodeToString(p.Bytes()); got != want { 147 t.Errorf("one: got %q, want %q", got, want) 148 } 149 } 150 151 func bigIntFromLittleEndianBytes(b []byte) *big.Int { 152 bb := make([]byte, len(b)) 153 for i := range b { 154 bb[i] = b[len(b)-i-1] 155 } 156 return new(big.Int).SetBytes(bb) 157 } 158 159 func TestScalarMultiplyDistributesOverAdd(t *testing.T) { 160 multiplyDistributesOverAdd := func(x, y, z Scalar) bool { 161 // Compute t1 = (x+y)*z 162 var t1 Scalar 163 t1.Add(&x, &y) 164 t1.Multiply(&t1, &z) 165 166 // Compute t2 = x*z + y*z 167 var t2 Scalar 168 var t3 Scalar 169 t2.Multiply(&x, &z) 170 t3.Multiply(&y, &z) 171 t2.Add(&t2, &t3) 172 173 reprT1, reprT2 := t1.Bytes(), t2.Bytes() 174 175 return t1 == t2 && isReduced(reprT1) && isReduced(reprT2) 176 } 177 178 if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig1024); err != nil { 179 t.Error(err) 180 } 181 } 182 183 func TestScalarAddLikeSubNeg(t *testing.T) { 184 addLikeSubNeg := func(x, y Scalar) bool { 185 // Compute t1 = x - y 186 var t1 Scalar 187 t1.Subtract(&x, &y) 188 189 // Compute t2 = -y + x 190 var t2 Scalar 191 t2.Negate(&y) 192 t2.Add(&t2, &x) 193 194 return t1 == t2 && isReduced(t1.Bytes()) 195 } 196 197 if err := quick.Check(addLikeSubNeg, quickCheckConfig1024); err != nil { 198 t.Error(err) 199 } 200 } 201 202 func TestScalarNonAdjacentForm(t *testing.T) { 203 s, _ := (&Scalar{}).SetCanonicalBytes([]byte{ 204 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 205 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d, 206 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 207 0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09, 208 }) 209 210 expectedNaf := [256]int8{ 211 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, 212 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, 213 -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, 214 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, 215 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, 216 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, 217 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, 218 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, 219 } 220 221 sNaf := s.nonAdjacentForm(5) 222 223 for i := 0; i < 256; i++ { 224 if expectedNaf[i] != sNaf[i] { 225 t.Errorf("Wrong digit at position %d, got %d, expected %d", i, sNaf[i], expectedNaf[i]) 226 } 227 } 228 } 229 230 type notZeroScalar Scalar 231 232 func (notZeroScalar) Generate(rand *mathrand.Rand, size int) reflect.Value { 233 var s Scalar 234 var isNonZero uint64 235 for isNonZero == 0 { 236 s = Scalar{}.Generate(rand, size).Interface().(Scalar) 237 fiatScalarNonzero(&isNonZero, (*[4]uint64)(&s.s)) 238 } 239 return reflect.ValueOf(notZeroScalar(s)) 240 } 241 242 func TestScalarEqual(t *testing.T) { 243 if scOne.Equal(scMinusOne) == 1 { 244 t.Errorf("scOne.Equal(&scMinusOne) is true") 245 } 246 if scMinusOne.Equal(scMinusOne) == 0 { 247 t.Errorf("scMinusOne.Equal(&scMinusOne) is false") 248 } 249 }