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 }