github.com/MetalBlockchain/metalgo@v1.11.9/utils/crypto/secp256k1/secp256k1_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package secp256k1
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/MetalBlockchain/metalgo/cache"
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/utils/cb58"
    14  	"github.com/MetalBlockchain/metalgo/utils/hashing"
    15  
    16  	secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
    17  )
    18  
    19  func TestRecover(t *testing.T) {
    20  	require := require.New(t)
    21  
    22  	key, err := NewPrivateKey()
    23  	require.NoError(err)
    24  
    25  	msg := []byte{1, 2, 3}
    26  	sig, err := key.Sign(msg)
    27  	require.NoError(err)
    28  
    29  	pub := key.PublicKey()
    30  	pubRec, err := RecoverPublicKey(msg, sig)
    31  	require.NoError(err)
    32  
    33  	require.Equal(pub, pubRec)
    34  
    35  	require.True(pub.Verify(msg, sig))
    36  }
    37  
    38  func TestCachedRecover(t *testing.T) {
    39  	require := require.New(t)
    40  
    41  	key, err := NewPrivateKey()
    42  	require.NoError(err)
    43  
    44  	msg := []byte{1, 2, 3}
    45  	sig, err := key.Sign(msg)
    46  	require.NoError(err)
    47  
    48  	r := RecoverCache{LRU: cache.LRU[ids.ID, *PublicKey]{Size: 1}}
    49  	pub1, err := r.RecoverPublicKey(msg, sig)
    50  	require.NoError(err)
    51  	pub2, err := r.RecoverPublicKey(msg, sig)
    52  	require.NoError(err)
    53  
    54  	require.Equal(key.PublicKey(), pub1)
    55  	require.Equal(key.PublicKey(), pub2)
    56  }
    57  
    58  func TestExtensive(t *testing.T) {
    59  	require := require.New(t)
    60  
    61  	hash := hashing.ComputeHash256([]byte{1, 2, 3})
    62  	for i := 0; i < 1000; i++ {
    63  		key, err := NewPrivateKey()
    64  		require.NoError(err)
    65  
    66  		_, err = key.SignHash(hash)
    67  		require.NoError(err)
    68  	}
    69  }
    70  
    71  func TestGenRecreate(t *testing.T) {
    72  	require := require.New(t)
    73  
    74  	for i := 0; i < 1000; i++ {
    75  		sk, err := NewPrivateKey()
    76  		require.NoError(err)
    77  
    78  		skBytes := sk.Bytes()
    79  		recoveredSk, err := ToPrivateKey(skBytes)
    80  		require.NoError(err)
    81  
    82  		require.Equal(sk.PublicKey(), recoveredSk.PublicKey())
    83  	}
    84  }
    85  
    86  func TestVerifyMutatedSignature(t *testing.T) {
    87  	require := require.New(t)
    88  
    89  	sk, err := NewPrivateKey()
    90  	require.NoError(err)
    91  
    92  	msg := []byte{'h', 'e', 'l', 'l', 'o'}
    93  	sig, err := sk.Sign(msg)
    94  	require.NoError(err)
    95  
    96  	var s secp256k1.ModNScalar
    97  	s.SetByteSlice(sig[32:64])
    98  	s.Negate()
    99  	newSBytes := s.Bytes()
   100  	copy(sig[32:], newSBytes[:])
   101  
   102  	_, err = RecoverPublicKey(msg, sig)
   103  	require.ErrorIs(err, errMutatedSig)
   104  }
   105  
   106  func TestPrivateKeySECP256K1RUnmarshalJSON(t *testing.T) {
   107  	require := require.New(t)
   108  
   109  	key, err := NewPrivateKey()
   110  	require.NoError(err)
   111  
   112  	keyJSON, err := key.MarshalJSON()
   113  	require.NoError(err)
   114  
   115  	key2 := PrivateKey{}
   116  	require.NoError(key2.UnmarshalJSON(keyJSON))
   117  	require.Equal(key.PublicKey(), key2.PublicKey())
   118  }
   119  
   120  func TestPrivateKeySECP256K1RUnmarshalJSONError(t *testing.T) {
   121  	tests := []struct {
   122  		label string
   123  		in    []byte
   124  		err   error
   125  	}{
   126  		{
   127  			label: "too short",
   128  			in:    []byte(`"`),
   129  			err:   errMissingQuotes,
   130  		},
   131  		{
   132  			label: "missing start quote",
   133  			in:    []byte(`PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"`),
   134  			err:   errMissingQuotes,
   135  		},
   136  		{
   137  			label: "missing end quote",
   138  			in:    []byte(`"PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN`),
   139  			err:   errMissingQuotes,
   140  		},
   141  		{
   142  			label: "incorrect prefix",
   143  			in:    []byte(`"PrivateKfy-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"`),
   144  			err:   errMissingKeyPrefix,
   145  		},
   146  		{
   147  			label: `"PrivateKey-"`,
   148  			in:    []byte(`"PrivateKey-"`),
   149  			err:   cb58.ErrBase58Decoding,
   150  		},
   151  		{
   152  			label: `"PrivateKey-1"`,
   153  			in:    []byte(`"PrivateKey-1"`),
   154  			err:   cb58.ErrMissingChecksum,
   155  		},
   156  		{
   157  			label: `"PrivateKey-1"`,
   158  			in:    []byte(`"PrivateKey-1"`),
   159  			err:   cb58.ErrMissingChecksum,
   160  		},
   161  		{
   162  			label: `"PrivateKey-1"`,
   163  			in:    []byte(`"PrivateKey-45PJLL"`),
   164  			err:   errInvalidPrivateKeyLength,
   165  		},
   166  	}
   167  	for _, tt := range tests {
   168  		t.Run(tt.label, func(t *testing.T) {
   169  			require := require.New(t)
   170  
   171  			foo := PrivateKey{}
   172  			err := foo.UnmarshalJSON(tt.in)
   173  			require.ErrorIs(err, tt.err)
   174  		})
   175  	}
   176  }
   177  
   178  func TestSigning(t *testing.T) {
   179  	tests := []struct {
   180  		msg []byte
   181  		sig []byte
   182  	}{
   183  		{
   184  			[]byte("hello world"),
   185  			[]byte{
   186  				0x17, 0x8c, 0xb6, 0x09, 0x6b, 0x3c, 0xa5, 0x82,
   187  				0x0a, 0x4c, 0x6e, 0xce, 0xdf, 0x15, 0xb6, 0x8b,
   188  				0x6f, 0x50, 0xe2, 0x52, 0xc2, 0xb6, 0x4f, 0x37,
   189  				0x74, 0x88, 0x86, 0x02, 0xcc, 0x9f, 0xa0, 0x8c,
   190  				0x5d, 0x01, 0x9d, 0x82, 0xfd, 0xde, 0x95, 0xfd,
   191  				0xf2, 0x34, 0xaa, 0x2d, 0x12, 0xad, 0x79, 0xb5,
   192  				0xab, 0xb3, 0x45, 0xfe, 0x95, 0x3a, 0x9f, 0x72,
   193  				0xf7, 0x09, 0x14, 0xfd, 0x31, 0x39, 0x06, 0x3b,
   194  				0x00,
   195  			},
   196  		},
   197  		{
   198  			[]byte("scooby doo"),
   199  			[]byte{
   200  				0xc2, 0x57, 0x3f, 0x29, 0xb0, 0xd1, 0x7a, 0xe7,
   201  				0x00, 0x9a, 0x9f, 0x17, 0xa4, 0x55, 0x8d, 0x32,
   202  				0x46, 0x2e, 0x5b, 0x8d, 0x05, 0x9e, 0x38, 0x32,
   203  				0xec, 0xb0, 0x32, 0x54, 0x1a, 0xbc, 0x7d, 0xaf,
   204  				0x57, 0x51, 0xf9, 0x6b, 0x85, 0x71, 0xbc, 0xb7,
   205  				0x18, 0xd2, 0x6b, 0xe8, 0xed, 0x8d, 0x59, 0xb0,
   206  				0xd6, 0x03, 0x69, 0xab, 0x57, 0xac, 0xc0, 0xf7,
   207  				0x13, 0x3b, 0x21, 0x94, 0x56, 0x03, 0x8e, 0xc7,
   208  				0x01,
   209  			},
   210  		},
   211  		{
   212  			[]byte("a really long string"),
   213  			[]byte{
   214  				0x1b, 0xf5, 0x61, 0xc3, 0x60, 0x07, 0xd2, 0xa6,
   215  				0x12, 0x68, 0xe9, 0xe1, 0x3a, 0x90, 0x2a, 0x9c,
   216  				0x2b, 0xa4, 0x3e, 0x28, 0xf8, 0xd4, 0x75, 0x54,
   217  				0x21, 0x57, 0x11, 0xdc, 0xdc, 0xc6, 0xd3, 0x5e,
   218  				0x78, 0x43, 0x18, 0xf6, 0x22, 0x91, 0x37, 0x3c,
   219  				0x95, 0x77, 0x9f, 0x67, 0x94, 0x91, 0x0a, 0x44,
   220  				0x16, 0xbf, 0xa3, 0xae, 0x9f, 0x25, 0xfa, 0x34,
   221  				0xa0, 0x14, 0xea, 0x9c, 0x6f, 0xe0, 0x20, 0x37,
   222  				0x00,
   223  			},
   224  		},
   225  	}
   226  
   227  	key := TestKeys()[0]
   228  
   229  	for _, tt := range tests {
   230  		t.Run(string(tt.msg), func(t *testing.T) {
   231  			require := require.New(t)
   232  
   233  			bytes, err := key.Sign(tt.msg)
   234  			require.NoError(err)
   235  			require.Equal(tt.sig, bytes)
   236  		})
   237  	}
   238  }
   239  
   240  func TestExportedMethods(t *testing.T) {
   241  	require := require.New(t)
   242  
   243  	key := TestKeys()[0]
   244  
   245  	pubKey := key.PublicKey()
   246  	require.Equal("111111111111111111116DBWJs", pubKey.addr.String())
   247  	require.Equal("Q4MzFZZDPHRPAHFeDs3NiyyaZDvxHKivf", pubKey.Address().String())
   248  	require.Equal("Q4MzFZZDPHRPAHFeDs3NiyyaZDvxHKivf", pubKey.addr.String())
   249  	require.Equal("Q4MzFZZDPHRPAHFeDs3NiyyaZDvxHKivf", key.Address().String())
   250  
   251  	expectedPubKeyBytes := []byte{
   252  		0x03, 0x73, 0x93, 0x53, 0x47, 0x88, 0x44, 0x78,
   253  		0xe4, 0x94, 0x5c, 0xd0, 0xfd, 0x94, 0x8e, 0xcf,
   254  		0x08, 0x8b, 0x94, 0xdf, 0xc9, 0x20, 0x74, 0xf0,
   255  		0xfb, 0x03, 0xda, 0x6f, 0x4d, 0xbc, 0x94, 0x35,
   256  		0x7d,
   257  	}
   258  	require.Equal(expectedPubKeyBytes, pubKey.bytes)
   259  
   260  	expectedPubKey, err := ToPublicKey(expectedPubKeyBytes)
   261  	require.NoError(err)
   262  	require.Equal(expectedPubKey.Address(), pubKey.Address())
   263  	require.Equal(expectedPubKeyBytes, expectedPubKey.Bytes())
   264  
   265  	expectedECDSAParams := struct {
   266  		X []byte
   267  		Y []byte
   268  	}{
   269  		[]byte{
   270  			0x73, 0x93, 0x53, 0x47, 0x88, 0x44, 0x78, 0xe4,
   271  			0x94, 0x5c, 0xd0, 0xfd, 0x94, 0x8e, 0xcf, 0x08,
   272  			0x8b, 0x94, 0xdf, 0xc9, 0x20, 0x74, 0xf0, 0xfb,
   273  			0x03, 0xda, 0x6f, 0x4d, 0xbc, 0x94, 0x35, 0x7d,
   274  		},
   275  		[]byte{
   276  			0x78, 0xe7, 0x39, 0x45, 0x6c, 0x3b, 0xdb, 0x9e,
   277  			0xe9, 0xb2, 0xa9, 0xf2, 0x84, 0xfa, 0x64, 0x32,
   278  			0xd8, 0x4e, 0xf0, 0xfa, 0x3f, 0x82, 0xf5, 0x56,
   279  			0x10, 0x40, 0x71, 0x7f, 0x1f, 0x5e, 0x8e, 0x27,
   280  		},
   281  	}
   282  	require.Equal(expectedECDSAParams.X, pubKey.ToECDSA().X.Bytes())
   283  	require.Equal(expectedECDSAParams.Y, pubKey.ToECDSA().Y.Bytes())
   284  
   285  	require.Equal(expectedECDSAParams.X, key.ToECDSA().X.Bytes())
   286  	require.Equal(expectedECDSAParams.Y, key.ToECDSA().Y.Bytes())
   287  }
   288  
   289  func FuzzVerifySignature(f *testing.F) {
   290  	f.Fuzz(func(t *testing.T, data []byte) {
   291  		require := require.New(t)
   292  
   293  		privateKey, err := NewPrivateKey()
   294  		require.NoError(err)
   295  
   296  		publicKey := privateKey.PublicKey()
   297  
   298  		sig, err := privateKey.Sign(data)
   299  		require.NoError(err)
   300  
   301  		recoveredPublicKey, err := RecoverPublicKey(data, sig)
   302  		require.NoError(err)
   303  
   304  		require.Equal(publicKey, recoveredPublicKey)
   305  	})
   306  }