github.com/cosmos/cosmos-sdk@v0.50.1/crypto/keys/secp256k1/secp256k1_test.go (about)

     1  package secp256k1_test
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"encoding/base64"
     6  	"encoding/hex"
     7  	"math/big"
     8  	"testing"
     9  
    10  	"github.com/cometbft/cometbft/crypto"
    11  	tmsecp256k1 "github.com/cometbft/cometbft/crypto/secp256k1"
    12  	"github.com/cosmos/btcutil/base58"
    13  	secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
    14  	btcecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/cosmos/cosmos-sdk/codec"
    19  	"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
    20  	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
    21  	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    22  )
    23  
    24  type keyData struct {
    25  	priv string
    26  	pub  string
    27  	addr string
    28  }
    29  
    30  /*
    31  The following code snippet has been used to generate test vectors. The purpose of these vectors are to check our
    32  implementation of secp256k1 against go-ethereum's one. It has been commented to avoid dependencies.
    33  
    34  	github.com/btcsuite/btcutil v1.0.2
    35  	github.com/ethereum/go-ethereum v1.10.26
    36  	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
    37  
    38  ---
    39  
    40  	import (
    41  		"crypto/ecdsa"
    42  		"crypto/sha256"
    43  		"encoding/hex"
    44  		"fmt"
    45  		"github.com/btcsuite/btcutil/base58"
    46  		"github.com/ethereum/go-ethereum/crypto"
    47  		"golang.org/x/crypto/ripemd160"
    48  	)
    49  
    50  	func ethereumKeys() keyData {
    51  		// Generate private key with the go-ethereum
    52  		priv, err := crypto.GenerateKey()
    53  		if err != nil {
    54  			panic(err)
    55  		}
    56  		encPriv := make([]byte, len(priv.D.Bytes())*2)
    57  		hex.Encode(encPriv, priv.D.Bytes())
    58  
    59  		// Get go-ethereum public key
    60  		ethPub, ok := priv.Public().(*ecdsa.PublicKey)
    61  		if !ok {
    62  			panic(err)
    63  		}
    64  		ethPublicKeyBytes := crypto.FromECDSAPub(ethPub)
    65  
    66  		// Format byte depending on the oddness of the Y coordinate.
    67  		format := 0x02
    68  		if ethPub.Y.Bit(0) != 0 {
    69  			format = 0x03
    70  		}
    71  
    72  		// Public key in the 33-byte compressed format.
    73  		pub := ethPublicKeyBytes[:33]
    74  		encPub := make([]byte, len(pub)*2)
    75  		pub[0] = byte(format)
    76  		hex.Encode(encPub, pub)
    77  
    78  		//  Bitcoin style addresses
    79  		sha := sha256.Sum256(pub)
    80  		hasherRIPEMD160 := ripemd160.New()
    81  		hasherRIPEMD160.Write(sha[:])
    82  		addr := hasherRIPEMD160.Sum(nil)
    83  		return keyData{
    84  			priv: string(encPriv),
    85  			pub: string(encPub),
    86  			addr: base58.CheckEncode(addr[:], 0),
    87  		}
    88  	}
    89  */
    90  
    91  /*
    92  generateKeyForCheckingConsistency was used to create test vectors that matches consistency against prior versions.
    93  Here are the specific versions used to generate the vectors.
    94  
    95  github.com/cosmos/btcutil v1.0.5
    96  github.com/cosmos/cosmos-sdk v0.46.8
    97  */
    98  var _ = func() keyData {
    99  	priv := secp256k1.GenPrivKey()
   100  	encPriv := make([]byte, len(priv.Key)*2)
   101  	hex.Encode(encPriv, priv.Key)
   102  	pub := priv.PubKey()
   103  	encPub := make([]byte, len(pub.Bytes())*2)
   104  	hex.Encode(encPub, pub.Bytes())
   105  	addr := pub.Address()
   106  	return keyData{
   107  		priv: string(encPriv),
   108  		pub:  string(encPub),
   109  		addr: base58.CheckEncode(addr, 0),
   110  	}
   111  }
   112  
   113  var secpDataTable = []keyData{
   114  	{
   115  		priv: "a96e62ed3955e65be32703f12d87b6b5cf26039ecfa948dc5107a495418e5330",
   116  		pub:  "02950e1cdfcb133d6024109fd489f734eeb4502418e538c28481f22bce276f248c",
   117  		addr: "1CKZ9Nx4zgds8tU7nJHotKSDr4a9bYJCa3",
   118  	},
   119  	// matches consistency against a prior version of this library. Generated with generateKeyForCheckingConsistency
   120  	{
   121  		priv: "9af074dc32fe3e7173802cd72dcb1110582879a1990c90bdac60f2739986aa06",
   122  		pub:  "0285592121e2a5e0eb970a1a9d1879c5fa7b33badf7dbb61c44b1bfced94649efb",
   123  		addr: "1Q4mWVk2hotRVDEdGGtGf6waz622rEwvib",
   124  	},
   125  	// matches consistency against a prior version of this library. Generated with generateKeyForCheckingConsistency
   126  	{
   127  		priv: "ef9edc836bc4d47e9bc3cfab446836a737c41d60abb1d5f76a6d53ffe5b35f76",
   128  		pub:  "02f5bf88d72172cc2f9a52919b6b1b74a01ca606cad75d5f4f93aa1a6ff0374aaf",
   129  		addr: "1KtiSApteeKdLi5cdZVpnkNW1t5Eteksvf",
   130  	},
   131  	// matches consistency against a prior version of this library. Generated with generateKeyForCheckingConsistency
   132  	{
   133  		priv: "ab7715a1dd7cea7898c45b1f291550b83a6897fbdf0ec48330dd50187059b74b",
   134  		pub:  "028f3003b3e6cb40897138dba5858207357a6d116cc5bf556c942cf6081b58d5fe",
   135  		addr: "RnM1o5grgCHAmm45wt5vzGsQoCJdPK2n2",
   136  	},
   137  	// matches consistency against a prior version of this library. Generated with generateKeyForCheckingConsistency
   138  	{
   139  		priv: "db6b914d9a2d6ae4bab8f9b43de3b1e83940e1a309521128b13fdaf3cd15009a",
   140  		pub:  "022f8e4e07ae2705a3c425eafea16027041bcdc87a193b01ea6c36c1c7a0bfc300",
   141  		addr: "16MpKTksSpGABuHqMqU9RPBz26DfwY8cLY",
   142  	},
   143  	// matches consistency against go-ethereum's implementation. Generated with ethereumKeys
   144  	{
   145  		priv: "42ba4249f6fd9f1e31f8876a8d3d3bdef989fcc906164290c0be237f69f53718",
   146  		pub:  "033c2f6ea7a678f0afbb43d0fe7a2b2706a75c2fdea08c3b90fd038c8219b42959",
   147  		addr: "18iz5wdTdwzq6cGzoVhooZCPRAx61GfUMR",
   148  	},
   149  	// matches consistency against go-ethereum's implementation. Generated with ethereumKeys
   150  	{
   151  		priv: "86192b60369616574daabe8d7d6067f14ec3f0648cded5633c566c25c48e1f31",
   152  		pub:  "03ad9e97842d0f6f57804f29f55aac9bba207d2b24b98aaabc7d106250389e6d46",
   153  		addr: "1K31NqmdMBZiLeUiP4kfjLNnWSmx17a9aE",
   154  	},
   155  	// matches consistency against go-ethereum's implementation. Generated with ethereumKeys
   156  	{
   157  		priv: "1856b3a581aa1bf83daf61b1f8f4bb52b5223033f710e61d7e0b3086f48ba09a",
   158  		pub:  "03d681bb11e5ebc14d5d2f72881cb0b2a693ef12bc72fe863f980fc6542eafbd40",
   159  		addr: "1K29nsfH6qwmE3MzsoHpLcWLA4mQLstGgx",
   160  	},
   161  }
   162  
   163  func TestPubKeySecp256k1Address(t *testing.T) {
   164  	for _, d := range secpDataTable {
   165  		privB, _ := hex.DecodeString(d.priv)
   166  		pubB, _ := hex.DecodeString(d.pub)
   167  		addrBbz, _, _ := base58.CheckDecode(d.addr)
   168  		addrB := crypto.Address(addrBbz)
   169  
   170  		priv := secp256k1.PrivKey{Key: privB}
   171  
   172  		pubKey := priv.PubKey()
   173  		pubT, _ := pubKey.(*secp256k1.PubKey)
   174  
   175  		addr := pubKey.Address()
   176  		assert.Equal(t, pubT, &secp256k1.PubKey{Key: pubB}, "Expected pub keys to match")
   177  		assert.Equal(t, addr, addrB, "Expected addresses to match")
   178  	}
   179  }
   180  
   181  func TestSignAndValidateSecp256k1(t *testing.T) {
   182  	privKey := secp256k1.GenPrivKey()
   183  	pubKey := privKey.PubKey()
   184  
   185  	msg := crypto.CRandBytes(1000)
   186  	sig, err := privKey.Sign(msg)
   187  	require.Nil(t, err)
   188  	assert.True(t, pubKey.VerifySignature(msg, sig))
   189  
   190  	// ----
   191  	// Test cross packages verification
   192  	msgHash := crypto.Sha256(msg)
   193  	btcPrivKey := secp.PrivKeyFromBytes(privKey.Key)
   194  	btcPubKey := btcPrivKey.PubKey()
   195  	// This fails: malformed signature: no header magic
   196  	//   btcSig, err := secp256k1.ParseSignature(sig, secp256k1.S256())
   197  	//   require.NoError(t, err)
   198  	//   assert.True(t, btcSig.Verify(msgHash, btcPubKey))
   199  	// So we do a hacky way:
   200  	r := new(big.Int)
   201  	s := new(big.Int)
   202  	r.SetBytes(sig[:32])
   203  	s.SetBytes(sig[32:])
   204  	ok := ecdsa.Verify(btcPubKey.ToECDSA(), msgHash, r, s)
   205  	require.True(t, ok)
   206  
   207  	sig2 := btcecdsa.SignCompact(btcPrivKey, msgHash, false)
   208  	// Chop off compactSigRecoveryCode.
   209  	sig2 = sig2[1:]
   210  	require.NoError(t, err)
   211  	pubKey.VerifySignature(msg, sig2)
   212  
   213  	// ----
   214  	// Mutate the signature, just one bit.
   215  	sig[3] ^= byte(0x01)
   216  	assert.False(t, pubKey.VerifySignature(msg, sig))
   217  }
   218  
   219  // This test is intended to justify the removal of calls to the underlying library
   220  // in creating the privkey.
   221  func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) {
   222  	numberOfTests := 256
   223  	for i := 0; i < numberOfTests; i++ {
   224  		// Seed the test case with some random bytes
   225  		privKeyBytes := [32]byte{}
   226  		copy(privKeyBytes[:], crypto.CRandBytes(32))
   227  
   228  		// This function creates a private and public key in the underlying libraries format.
   229  		// The private key is basically calling new(big.Int).SetBytes(pk), which removes leading zero bytes
   230  		priv := secp.PrivKeyFromBytes(privKeyBytes[:])
   231  		// this takes the bytes returned by `(big int).Bytes()`, and if the length is less than 32 bytes,
   232  		// pads the bytes from the left with zero bytes. Therefore these two functions composed
   233  		// result in the identity function on privKeyBytes, hence the following equality check
   234  		// always returning true.
   235  		serializedBytes := priv.Serialize()
   236  		require.Equal(t, privKeyBytes[:], serializedBytes)
   237  	}
   238  }
   239  
   240  func TestGenPrivKeyFromSecret(t *testing.T) {
   241  	// curve oder N
   242  	N := secp.S256().N
   243  	tests := []struct {
   244  		name   string
   245  		secret []byte
   246  	}{
   247  		{"empty secret", []byte{}},
   248  		{
   249  			"some long secret",
   250  			[]byte("We live in a society exquisitely dependent on science and technology, " +
   251  				"in which hardly anyone knows anything about science and technology."),
   252  		},
   253  		{"another seed used in cosmos tests #1", []byte{0}},
   254  		{"another seed used in cosmos tests #2", []byte("mySecret")},
   255  		{"another seed used in cosmos tests #3", []byte("")},
   256  	}
   257  	for _, tt := range tests {
   258  		tt := tt
   259  		t.Run(tt.name, func(t *testing.T) {
   260  			gotPrivKey := secp256k1.GenPrivKeyFromSecret(tt.secret)
   261  			require.NotNil(t, gotPrivKey)
   262  			// interpret as a big.Int and make sure it is a valid field element:
   263  			fe := new(big.Int).SetBytes(gotPrivKey.Key)
   264  			require.True(t, fe.Cmp(N) < 0)
   265  			require.True(t, fe.Sign() > 0)
   266  		})
   267  	}
   268  }
   269  
   270  func TestPubKeyEquals(t *testing.T) {
   271  	secp256K1PubKey := secp256k1.GenPrivKey().PubKey().(*secp256k1.PubKey)
   272  
   273  	testCases := []struct {
   274  		msg      string
   275  		pubKey   cryptotypes.PubKey
   276  		other    cryptotypes.PubKey
   277  		expectEq bool
   278  	}{
   279  		{
   280  			"different bytes",
   281  			secp256K1PubKey,
   282  			secp256k1.GenPrivKey().PubKey(),
   283  			false,
   284  		},
   285  		{
   286  			"equals",
   287  			secp256K1PubKey,
   288  			&secp256k1.PubKey{
   289  				Key: secp256K1PubKey.Key,
   290  			},
   291  			true,
   292  		},
   293  		{
   294  			"different types",
   295  			secp256K1PubKey,
   296  			ed25519.GenPrivKey().PubKey(),
   297  			false,
   298  		},
   299  	}
   300  
   301  	for _, tc := range testCases {
   302  		t.Run(tc.msg, func(t *testing.T) {
   303  			eq := tc.pubKey.Equals(tc.other)
   304  			require.Equal(t, eq, tc.expectEq)
   305  		})
   306  	}
   307  }
   308  
   309  func TestPrivKeyEquals(t *testing.T) {
   310  	secp256K1PrivKey := secp256k1.GenPrivKey()
   311  
   312  	testCases := []struct {
   313  		msg      string
   314  		privKey  cryptotypes.PrivKey
   315  		other    cryptotypes.PrivKey
   316  		expectEq bool
   317  	}{
   318  		{
   319  			"different bytes",
   320  			secp256K1PrivKey,
   321  			secp256k1.GenPrivKey(),
   322  			false,
   323  		},
   324  		{
   325  			"equals",
   326  			secp256K1PrivKey,
   327  			&secp256k1.PrivKey{
   328  				Key: secp256K1PrivKey.Key,
   329  			},
   330  			true,
   331  		},
   332  		{
   333  			"different types",
   334  			secp256K1PrivKey,
   335  			ed25519.GenPrivKey(),
   336  			false,
   337  		},
   338  	}
   339  
   340  	for _, tc := range testCases {
   341  		t.Run(tc.msg, func(t *testing.T) {
   342  			eq := tc.privKey.Equals(tc.other)
   343  			require.Equal(t, eq, tc.expectEq)
   344  		})
   345  	}
   346  }
   347  
   348  func TestMarshalAmino(t *testing.T) {
   349  	aminoCdc := codec.NewLegacyAmino()
   350  	privKey := secp256k1.GenPrivKey()
   351  	pubKey := privKey.PubKey().(*secp256k1.PubKey)
   352  
   353  	testCases := []struct {
   354  		desc      string
   355  		msg       codec.AminoMarshaler
   356  		typ       interface{}
   357  		expBinary []byte
   358  		expJSON   string
   359  	}{
   360  		{
   361  			"secp256k1 private key",
   362  			privKey,
   363  			&secp256k1.PrivKey{},
   364  			append([]byte{32}, privKey.Bytes()...), // Length-prefixed.
   365  			"\"" + base64.StdEncoding.EncodeToString(privKey.Bytes()) + "\"",
   366  		},
   367  		{
   368  			"secp256k1 public key",
   369  			pubKey,
   370  			&secp256k1.PubKey{},
   371  			append([]byte{33}, pubKey.Bytes()...), // Length-prefixed.
   372  			"\"" + base64.StdEncoding.EncodeToString(pubKey.Bytes()) + "\"",
   373  		},
   374  	}
   375  
   376  	for _, tc := range testCases {
   377  		t.Run(tc.desc, func(t *testing.T) {
   378  			// Do a round trip of encoding/decoding binary.
   379  			bz, err := aminoCdc.Marshal(tc.msg)
   380  			require.NoError(t, err)
   381  			require.Equal(t, tc.expBinary, bz)
   382  
   383  			err = aminoCdc.Unmarshal(bz, tc.typ)
   384  			require.NoError(t, err)
   385  
   386  			require.Equal(t, tc.msg, tc.typ)
   387  
   388  			// Do a round trip of encoding/decoding JSON.
   389  			bz, err = aminoCdc.MarshalJSON(tc.msg)
   390  			require.NoError(t, err)
   391  			require.Equal(t, tc.expJSON, string(bz))
   392  
   393  			err = aminoCdc.UnmarshalJSON(bz, tc.typ)
   394  			require.NoError(t, err)
   395  
   396  			require.Equal(t, tc.msg, tc.typ)
   397  		})
   398  	}
   399  }
   400  
   401  func TestMarshalAmino_BackwardsCompatibility(t *testing.T) {
   402  	aminoCdc := codec.NewLegacyAmino()
   403  	// Create Tendermint keys.
   404  	tmPrivKey := tmsecp256k1.GenPrivKey()
   405  	tmPubKey := tmPrivKey.PubKey()
   406  	// Create our own keys, with the same private key as Tendermint's.
   407  	privKey := &secp256k1.PrivKey{Key: []byte(tmPrivKey)}
   408  	pubKey := privKey.PubKey().(*secp256k1.PubKey)
   409  
   410  	testCases := []struct {
   411  		desc      string
   412  		tmKey     interface{}
   413  		ourKey    interface{}
   414  		marshalFn func(o interface{}) ([]byte, error)
   415  	}{
   416  		{
   417  			"secp256k1 private key, binary",
   418  			tmPrivKey,
   419  			privKey,
   420  			aminoCdc.Marshal,
   421  		},
   422  		{
   423  			"secp256k1 private key, JSON",
   424  			tmPrivKey,
   425  			privKey,
   426  			aminoCdc.MarshalJSON,
   427  		},
   428  		{
   429  			"secp256k1 public key, binary",
   430  			tmPubKey,
   431  			pubKey,
   432  			aminoCdc.Marshal,
   433  		},
   434  		{
   435  			"secp256k1 public key, JSON",
   436  			tmPubKey,
   437  			pubKey,
   438  			aminoCdc.MarshalJSON,
   439  		},
   440  	}
   441  
   442  	for _, tc := range testCases {
   443  		t.Run(tc.desc, func(t *testing.T) {
   444  			// Make sure Amino encoding override is not breaking backwards compatibility.
   445  			bz1, err := tc.marshalFn(tc.tmKey)
   446  			require.NoError(t, err)
   447  			bz2, err := tc.marshalFn(tc.ourKey)
   448  			require.NoError(t, err)
   449  			require.Equal(t, bz1, bz2)
   450  		})
   451  	}
   452  }