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  }