github.com/klaytn/klaytn@v1.12.1/crypto/crypto_test.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2014 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from crypto/crypto_test.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package crypto
    22  
    23  import (
    24  	"bytes"
    25  	"crypto/ecdsa"
    26  	"encoding/hex"
    27  	"math/big"
    28  	"os"
    29  	"reflect"
    30  	"runtime"
    31  	"sync"
    32  	"testing"
    33  
    34  	"github.com/klaytn/klaytn/common"
    35  	"github.com/klaytn/klaytn/common/hexutil"
    36  )
    37  
    38  var (
    39  	testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
    40  	testPrivHex = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
    41  )
    42  
    43  // These tests are sanity checks.
    44  // They should ensure that we don't e.g. use Sha3-224 instead of Sha3-256
    45  // and that the sha3 library uses keccak-f permutation.
    46  func TestKeccak256Hash(t *testing.T) {
    47  	msg := []byte("abc")
    48  	exp, _ := hex.DecodeString("4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45")
    49  	checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp)
    50  }
    51  
    52  func TestToECDSAErrors(t *testing.T) {
    53  	if _, err := HexToECDSA("0000000000000000000000000000000000000000000000000000000000000000"); err == nil {
    54  		t.Fatal("HexToECDSA should've returned error")
    55  	}
    56  	if _, err := HexToECDSA("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); err == nil {
    57  		t.Fatal("HexToECDSA should've returned error")
    58  	}
    59  }
    60  
    61  func BenchmarkSha3(b *testing.B) {
    62  	a := []byte("hello world")
    63  	for i := 0; i < b.N; i++ {
    64  		Keccak256(a)
    65  	}
    66  }
    67  
    68  func TestUnmarshalPubkey(t *testing.T) {
    69  	key, err := UnmarshalPubkey(nil)
    70  	if err != errInvalidPubkey || key != nil {
    71  		t.Fatalf("expected error, got %v, %v", err, key)
    72  	}
    73  	key, err = UnmarshalPubkey([]byte{1, 2, 3})
    74  	if err != errInvalidPubkey || key != nil {
    75  		t.Fatalf("expected error, got %v, %v", err, key)
    76  	}
    77  
    78  	var (
    79  		enc, _ = hex.DecodeString("04760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")
    80  		dec    = &ecdsa.PublicKey{
    81  			Curve: S256(),
    82  			X:     hexutil.MustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"),
    83  			Y:     hexutil.MustDecodeBig("0xb01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d"),
    84  		}
    85  	)
    86  	key, err = UnmarshalPubkey(enc)
    87  	if err != nil {
    88  		t.Fatalf("expected no error, got %v", err)
    89  	}
    90  	if !reflect.DeepEqual(key, dec) {
    91  		t.Fatal("wrong result")
    92  	}
    93  }
    94  
    95  func TestSign(t *testing.T) {
    96  	key, _ := HexToECDSA(testPrivHex)
    97  	addr := common.HexToAddress(testAddrHex)
    98  
    99  	msg := Keccak256([]byte("foo"))
   100  	sig, err := Sign(msg, key)
   101  	if err != nil {
   102  		t.Errorf("Sign error: %s", err)
   103  	}
   104  	recoveredPub, err := Ecrecover(msg, sig)
   105  	if err != nil {
   106  		t.Errorf("ECRecover error: %s", err)
   107  	}
   108  	pubKey, _ := UnmarshalPubkey(recoveredPub)
   109  	recoveredAddr := PubkeyToAddress(*pubKey)
   110  	if addr != recoveredAddr {
   111  		t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr)
   112  	}
   113  
   114  	// should be equal to SigToPub
   115  	recoveredPub2, err := SigToPub(msg, sig)
   116  	if err != nil {
   117  		t.Errorf("ECRecover error: %s", err)
   118  	}
   119  	recoveredAddr2 := PubkeyToAddress(*recoveredPub2)
   120  	if addr != recoveredAddr2 {
   121  		t.Errorf("Address mismatch: want: %x have: %x", addr, recoveredAddr2)
   122  	}
   123  }
   124  
   125  func TestInvalidSign(t *testing.T) {
   126  	if _, err := Sign(make([]byte, 1), nil); err == nil {
   127  		t.Errorf("expected sign with hash 1 byte to error")
   128  	}
   129  	if _, err := Sign(make([]byte, 33), nil); err == nil {
   130  		t.Errorf("expected sign with hash 33 byte to error")
   131  	}
   132  }
   133  
   134  func TestNewContractAddress(t *testing.T) {
   135  	key, _ := HexToECDSA(testPrivHex)
   136  	addr := common.HexToAddress(testAddrHex)
   137  	genAddr := PubkeyToAddress(key.PublicKey)
   138  	// sanity check before using addr to create contract address
   139  	checkAddr(t, genAddr, addr)
   140  
   141  	caddr0 := CreateAddress(addr, 0)
   142  	caddr1 := CreateAddress(addr, 1)
   143  	caddr2 := CreateAddress(addr, 2)
   144  	checkAddr(t, common.HexToAddress("333c3310824b7c685133f2bedb2ca4b8b4df633d"), caddr0)
   145  	checkAddr(t, common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), caddr1)
   146  	checkAddr(t, common.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2)
   147  }
   148  
   149  func TestLoadECDSAFile(t *testing.T) {
   150  	keyBytes := common.FromHex(testPrivHex)
   151  	fileName0 := "test_key0"
   152  	fileName1 := "test_key1"
   153  	checkKey := func(k *ecdsa.PrivateKey) {
   154  		checkAddr(t, PubkeyToAddress(k.PublicKey), common.HexToAddress(testAddrHex))
   155  		loadedKeyBytes := FromECDSA(k)
   156  		if !bytes.Equal(loadedKeyBytes, keyBytes) {
   157  			t.Fatalf("private key mismatch: want: %x have: %x", keyBytes, loadedKeyBytes)
   158  		}
   159  	}
   160  
   161  	os.WriteFile(fileName0, []byte(testPrivHex), 0o600)
   162  	defer os.Remove(fileName0)
   163  
   164  	key0, err := LoadECDSA(fileName0)
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	checkKey(key0)
   169  
   170  	// again, this time with SaveECDSA instead of manual save:
   171  	err = SaveECDSA(fileName1, key0)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  	defer os.Remove(fileName1)
   176  
   177  	key1, err := LoadECDSA(fileName1)
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	checkKey(key1)
   182  }
   183  
   184  func TestValidateSignatureValues(t *testing.T) {
   185  	check := func(expected bool, v byte, r, s *big.Int) {
   186  		if ValidateSignatureValues(v, r, s, false) != expected {
   187  			t.Errorf("mismatch for v: %d r: %d s: %d want: %v", v, r, s, expected)
   188  		}
   189  	}
   190  	minusOne := big.NewInt(-1)
   191  	one := common.Big1
   192  	zero := common.Big0
   193  	secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1)
   194  
   195  	// correct v,r,s
   196  	check(true, 0, one, one)
   197  	check(true, 1, one, one)
   198  	// incorrect v, correct r,s,
   199  	check(false, 2, one, one)
   200  	check(false, 3, one, one)
   201  
   202  	// incorrect v, combinations of incorrect/correct r,s at lower limit
   203  	check(false, 2, zero, zero)
   204  	check(false, 2, zero, one)
   205  	check(false, 2, one, zero)
   206  	check(false, 2, one, one)
   207  
   208  	// correct v for any combination of incorrect r,s
   209  	check(false, 0, zero, zero)
   210  	check(false, 0, zero, one)
   211  	check(false, 0, one, zero)
   212  
   213  	check(false, 1, zero, zero)
   214  	check(false, 1, zero, one)
   215  	check(false, 1, one, zero)
   216  
   217  	// correct sig with max r,s
   218  	check(true, 0, secp256k1nMinus1, secp256k1nMinus1)
   219  	// correct v, combinations of incorrect r,s at upper limit
   220  	check(false, 0, secp256k1N, secp256k1nMinus1)
   221  	check(false, 0, secp256k1nMinus1, secp256k1N)
   222  	check(false, 0, secp256k1N, secp256k1N)
   223  
   224  	// current callers ensures r,s cannot be negative, but let's test for that too
   225  	// as crypto package could be used stand-alone
   226  	check(false, 0, minusOne, one)
   227  	check(false, 0, one, minusOne)
   228  }
   229  
   230  func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte) {
   231  	sum := f(msg)
   232  	if !bytes.Equal(exp, sum) {
   233  		t.Fatalf("hash %s mismatch: want: %x have: %x", name, exp, sum)
   234  	}
   235  }
   236  
   237  func checkAddr(t *testing.T, addr0, addr1 common.Address) {
   238  	if addr0 != addr1 {
   239  		t.Fatalf("address mismatch: want: %x have: %x", addr0, addr1)
   240  	}
   241  }
   242  
   243  // test to help Python team with integration of libsecp256k1
   244  // skip but keep it after they are done
   245  func TestPythonIntegration(t *testing.T) {
   246  	kh := "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
   247  	k0, _ := HexToECDSA(kh)
   248  
   249  	msg0 := Keccak256([]byte("foo"))
   250  	sig0, _ := Sign(msg0, k0)
   251  
   252  	msg1 := common.FromHex("00000000000000000000000000000000")
   253  	sig1, _ := Sign(msg0, k0)
   254  
   255  	t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
   256  	t.Logf("msg: %x, privkey: %s sig: %x\n", msg1, kh, sig1)
   257  }
   258  
   259  func BenchmarkEcrecover(b *testing.B) {
   260  	data, sigs := generateBenchmarkMaterial(b.N)
   261  	b.ResetTimer()
   262  	for i := 0; i < b.N; i++ {
   263  		Ecrecover(data, sigs[i])
   264  	}
   265  }
   266  
   267  func BenchmarkParallelEcrecover(b *testing.B) {
   268  	run := func(size int) func(b *testing.B) {
   269  		return func(b *testing.B) {
   270  			data, sigs := generateBenchmarkMaterial(size)
   271  			b.ResetTimer()
   272  			for i := 0; i < b.N; i++ {
   273  				parallelEcrecover(data, sigs)
   274  			}
   275  		}
   276  	}
   277  
   278  	b.Run("1", run(1))
   279  	b.Run("30", run(30))
   280  	b.Run("100", run(100))
   281  	b.Run("1000", run(1000))
   282  }
   283  
   284  func generateBenchmarkMaterial(L int) (data []byte, sigs [][]byte) {
   285  	data = common.HexToHash("0xabcd").Bytes()
   286  	sigs = make([][]byte, L)
   287  	for i := 0; i < L; i++ {
   288  		// any nonzero integer less than the order is a valid privkey
   289  		priv := ToECDSAUnsafe(big.NewInt(int64(i + 1)).Bytes())
   290  		sig, _ := Sign(data, priv)
   291  		sigs[i] = sig
   292  	}
   293  	return
   294  }
   295  
   296  func parallelEcrecover(data []byte, sigs [][]byte) {
   297  	threads := runtime.NumCPU()
   298  	var wg sync.WaitGroup
   299  	wg.Add(threads)
   300  
   301  	jobs := make(chan []byte, len(sigs))
   302  	for _, sig := range sigs {
   303  		jobs <- sig
   304  	}
   305  
   306  	for i := 0; i < threads; i++ {
   307  		go func() {
   308  			for sig := range jobs {
   309  				Ecrecover(data, sig)
   310  			}
   311  			wg.Done()
   312  		}()
   313  	}
   314  
   315  	close(jobs)
   316  	wg.Wait()
   317  }