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 }