github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/crypto/keys/keybase_test.go (about) 1 // nolint: goconst 2 package keys 3 4 import ( 5 "fmt" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto" 12 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto/ed25519" 13 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto/secp256k1" 14 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto/keys/hd" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto/keys/mintkey" 17 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 18 ) 19 20 func init() { 21 mintkey.BcryptSecurityParameter = 1 22 } 23 24 const ( 25 nums = "1234" 26 foobar = "foobar" 27 ) 28 29 func TestLanguage(t *testing.T) { 30 kb := NewInMemory() 31 _, _, err := kb.CreateMnemonic("something", Japanese, "no_pass", Secp256k1, "") 32 assert.Error(t, err) 33 assert.Equal(t, "unsupported language: only english is supported", err.Error()) 34 } 35 36 func TestCreateAccountInvalidMnemonic(t *testing.T) { 37 kb := NewInMemory() 38 _, err := kb.CreateAccount( 39 "some_account", 40 "malarkey pair crucial catch public canyon evil outer stage ten gym tornado", 41 "", "", CreateHDPath(0, 0).String(), Secp256k1) 42 assert.Error(t, err) 43 assert.Equal(t, "Invalid mnemonic", err.Error()) 44 } 45 46 func TestCreateLedgerUnsupportedAlgo(t *testing.T) { 47 kb := NewInMemory() 48 49 supportedLedgerAlgos := kb.SupportedAlgosLedger() 50 for _, supportedAlgo := range supportedLedgerAlgos { 51 if Ed25519 == supportedAlgo { 52 assert.FailNow(t, "Was not an unsupported algorithm") 53 } 54 } 55 56 _, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1) 57 assert.Error(t, err) 58 assert.Equal(t, "unsupported signing algo", err.Error()) 59 } 60 61 func TestCreateLedger(t *testing.T) { 62 kb := NewInMemory(WithSupportedAlgosLedger([]SigningAlgo{Secp256k1, Ed25519})) 63 64 // test_cover and test_unit will result in different answers 65 // test_cover does not compile some dependencies so ledger is disabled 66 // test_unit may add a ledger mock 67 // both cases are acceptable 68 supportedLedgerAlgos := kb.SupportedAlgosLedger() 69 secpSupported := false 70 edSupported := false 71 for _, supportedAlgo := range supportedLedgerAlgos { 72 secpSupported = secpSupported || (supportedAlgo == Secp256k1) 73 edSupported = edSupported || (supportedAlgo == Ed25519) 74 } 75 assert.True(t, secpSupported) 76 assert.True(t, edSupported) 77 78 ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1) 79 80 if err != nil { 81 assert.Error(t, err) 82 assert.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error()) 83 assert.Nil(t, ledger) 84 t.Skip("ledger nano S: support for ledger devices is not available in this executable") 85 return 86 } 87 88 // The mock is available, check that the address is correct 89 pubKey := ledger.GetPubKey() 90 pk, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) 91 assert.NoError(t, err) 92 assert.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) 93 94 // Check that restoring the key gets the same results 95 restoredKey, err := kb.Get("some_account") 96 assert.NoError(t, err) 97 assert.NotNil(t, restoredKey) 98 assert.Equal(t, "some_account", restoredKey.GetName()) 99 assert.Equal(t, TypeLedger, restoredKey.GetType()) 100 pubKey = restoredKey.GetPubKey() 101 pk, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pubKey) 102 assert.NoError(t, err) 103 assert.Equal(t, "cosmospub1addwnpepqdszcr95mrqqs8lw099aa9h8h906zmet22pmwe9vquzcgvnm93eqygufdlv", pk) 104 105 path, err := restoredKey.GetPath() 106 assert.NoError(t, err) 107 assert.Equal(t, "m/44'/118'/3'/0/1", path.String()) 108 } 109 110 // TestKeyManagement makes sure we can manipulate these keys well 111 func TestKeyManagement(t *testing.T) { 112 // make the storage with reasonable defaults 113 cstore := NewInMemory(WithSupportedAlgos([]SigningAlgo{Secp256k1, Sr25519})) 114 115 // Test modified supported algos 116 supportedAlgos := cstore.SupportedAlgos() 117 secpSupported := false 118 edSupported := false 119 srSupported := false 120 for _, supportedAlgo := range supportedAlgos { 121 secpSupported = secpSupported || (supportedAlgo == Secp256k1) 122 edSupported = edSupported || (supportedAlgo == Ed25519) 123 srSupported = srSupported || (supportedAlgo == Sr25519) 124 } 125 assert.True(t, secpSupported) 126 assert.False(t, edSupported) 127 assert.True(t, srSupported) 128 129 algo := Secp256k1 130 n1, n2, n3 := "personal", "business", "other" 131 p1, p2 := nums, "really-secure!@#$" 132 133 // Check empty state 134 l, err := cstore.List() 135 require.Nil(t, err) 136 assert.Empty(t, l) 137 138 _, _, err = cstore.CreateMnemonic(n1, English, p1, Ed25519, "") 139 require.Error(t, err, "ed25519 keys are currently not supported by keybase") 140 141 // create some keys 142 _, err = cstore.Get(n1) 143 require.Error(t, err) 144 i, _, err := cstore.CreateMnemonic(n1, English, p1, algo, "") 145 146 require.NoError(t, err) 147 require.Equal(t, n1, i.GetName()) 148 _, _, err = cstore.CreateMnemonic(n2, English, p2, algo, "") 149 require.NoError(t, err) 150 151 // we can get these keys 152 i2, err := cstore.Get(n2) 153 require.NoError(t, err) 154 _, err = cstore.Get(n3) 155 require.NotNil(t, err) 156 _, err = cstore.GetByAddress(accAddr(i2)) 157 require.NoError(t, err) 158 addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") 159 require.NoError(t, err) 160 _, err = cstore.GetByAddress(addr) 161 require.NotNil(t, err) 162 163 // list shows them in order 164 keyS, err := cstore.List() 165 require.NoError(t, err) 166 require.Equal(t, 2, len(keyS)) 167 // note these are in alphabetical order 168 require.Equal(t, n2, keyS[0].GetName()) 169 require.Equal(t, n1, keyS[1].GetName()) 170 require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey()) 171 172 // deleting a key removes it 173 err = cstore.Delete("bad name", "foo", false) 174 require.NotNil(t, err) 175 err = cstore.Delete(n1, p1, false) 176 require.NoError(t, err) 177 keyS, err = cstore.List() 178 require.NoError(t, err) 179 require.Equal(t, 1, len(keyS)) 180 _, err = cstore.Get(n1) 181 require.Error(t, err) 182 183 // create an offline key 184 o1 := "offline" 185 priv1 := ed25519.GenPrivKey() 186 pub1 := priv1.PubKey() 187 i, err = cstore.CreateOffline(o1, pub1, algo) 188 require.Nil(t, err) 189 require.Equal(t, pub1, i.GetPubKey()) 190 require.Equal(t, o1, i.GetName()) 191 iOffline := i.(*offlineInfo) 192 require.Equal(t, algo, iOffline.GetAlgo()) 193 keyS, err = cstore.List() 194 require.NoError(t, err) 195 require.Equal(t, 2, len(keyS)) 196 197 // delete the offline key 198 err = cstore.Delete(o1, "", false) 199 require.NoError(t, err) 200 keyS, err = cstore.List() 201 require.NoError(t, err) 202 require.Equal(t, 1, len(keyS)) 203 204 // addr cache gets nuked - and test skip flag 205 err = cstore.Delete(n2, "", true) 206 require.NoError(t, err) 207 } 208 209 // TestSignVerify does some detailed checks on how we sign and validate 210 // signatures 211 func TestSignVerify(t *testing.T) { 212 cstore := NewInMemory() 213 algo := Secp256k1 214 215 n1, n2, n3 := "some dude", "a dudette", "dude-ish" 216 p1, p2, p3 := nums, foobar, foobar 217 218 // create two users and get their info 219 i1, _, err := cstore.CreateMnemonic(n1, English, p1, algo, "") 220 require.Nil(t, err) 221 222 i2, _, err := cstore.CreateMnemonic(n2, English, p2, algo, "") 223 require.Nil(t, err) 224 225 // Import a public key 226 armor, err := cstore.ExportPubKey(n2) 227 require.Nil(t, err) 228 require.NoError(t, cstore.ImportPubKey(n3, armor)) 229 i3, err := cstore.Get(n3) 230 require.NoError(t, err) 231 require.Equal(t, i3.GetName(), n3) 232 233 // let's try to sign some messages 234 d1 := []byte("my first message") 235 d2 := []byte("some other important info!") 236 d3 := []byte("feels like I forgot something...") 237 238 // try signing both data with both .. 239 s11, pub1, err := cstore.Sign(n1, p1, d1) 240 require.Nil(t, err) 241 require.Equal(t, i1.GetPubKey(), pub1) 242 243 s12, pub1, err := cstore.Sign(n1, p1, d2) 244 require.Nil(t, err) 245 require.Equal(t, i1.GetPubKey(), pub1) 246 247 s21, pub2, err := cstore.Sign(n2, p2, d1) 248 require.Nil(t, err) 249 require.Equal(t, i2.GetPubKey(), pub2) 250 251 s22, pub2, err := cstore.Sign(n2, p2, d2) 252 require.Nil(t, err) 253 require.Equal(t, i2.GetPubKey(), pub2) 254 255 // let's try to validate and make sure it only works when everything is proper 256 cases := []struct { 257 key crypto.PubKey 258 data []byte 259 sig []byte 260 valid bool 261 }{ 262 // proper matches 263 {i1.GetPubKey(), d1, s11, true}, 264 // change data, pubkey, or signature leads to fail 265 {i1.GetPubKey(), d2, s11, false}, 266 {i2.GetPubKey(), d1, s11, false}, 267 {i1.GetPubKey(), d1, s21, false}, 268 // make sure other successes 269 {i1.GetPubKey(), d2, s12, true}, 270 {i2.GetPubKey(), d1, s21, true}, 271 {i2.GetPubKey(), d2, s22, true}, 272 } 273 274 for i, tc := range cases { 275 valid := tc.key.VerifyBytes(tc.data, tc.sig) 276 require.Equal(t, tc.valid, valid, "%d", i) 277 } 278 279 // Now try to sign data with a secret-less key 280 _, _, err = cstore.Sign(n3, p3, d3) 281 require.NotNil(t, err) 282 } 283 284 func assertPassword(t *testing.T, cstore Keybase, name, pass, badpass string) { 285 getNewpass := func() (string, error) { return pass, nil } 286 err := cstore.Update(name, badpass, getNewpass) 287 require.NotNil(t, err) 288 err = cstore.Update(name, pass, getNewpass) 289 require.Nil(t, err, "%+v", err) 290 } 291 292 // TestExportImport tests exporting and importing 293 func TestExportImport(t *testing.T) { 294 // make the storage with reasonable defaults 295 cstore := NewInMemory() 296 297 info, _, err := cstore.CreateMnemonic("john", English, "secretcpw", Secp256k1, "") 298 require.NoError(t, err) 299 require.Equal(t, info.GetName(), "john") 300 301 john, err := cstore.Get("john") 302 require.NoError(t, err) 303 require.Equal(t, info.GetName(), "john") 304 johnAddr := info.GetPubKey().Address() 305 306 armor, err := cstore.Export("john") 307 require.NoError(t, err) 308 309 err = cstore.Import("john2", armor) 310 require.NoError(t, err) 311 312 john2, err := cstore.Get("john2") 313 require.NoError(t, err) 314 315 require.Equal(t, john.GetPubKey().Address(), johnAddr) 316 require.Equal(t, john.GetName(), "john") 317 require.Equal(t, john, john2) 318 } 319 320 func TestExportImportPubKey(t *testing.T) { 321 // make the storage with reasonable defaults 322 cstore := NewInMemory() 323 324 // CreateMnemonic a private-public key pair and ensure consistency 325 notPasswd := "n9y25ah7" 326 info, _, err := cstore.CreateMnemonic("john", English, notPasswd, Secp256k1, "") 327 require.Nil(t, err) 328 require.NotEqual(t, info, "") 329 require.Equal(t, info.GetName(), "john") 330 addr := info.GetPubKey().Address() 331 john, err := cstore.Get("john") 332 require.NoError(t, err) 333 require.Equal(t, john.GetName(), "john") 334 require.Equal(t, john.GetPubKey().Address(), addr) 335 336 // Export the public key only 337 armor, err := cstore.ExportPubKey("john") 338 require.NoError(t, err) 339 // Import it under a different name 340 err = cstore.ImportPubKey("john-pubkey-only", armor) 341 require.NoError(t, err) 342 // Ensure consistency 343 john2, err := cstore.Get("john-pubkey-only") 344 require.NoError(t, err) 345 // Compare the public keys 346 require.True(t, john.GetPubKey().Equals(john2.GetPubKey())) 347 // Ensure the original key hasn't changed 348 john, err = cstore.Get("john") 349 require.NoError(t, err) 350 require.Equal(t, john.GetPubKey().Address(), addr) 351 require.Equal(t, john.GetName(), "john") 352 353 // Ensure keys cannot be overwritten 354 err = cstore.ImportPubKey("john-pubkey-only", armor) 355 require.NotNil(t, err) 356 } 357 358 // TestAdvancedKeyManagement verifies update, import, export functionality 359 func TestAdvancedKeyManagement(t *testing.T) { 360 // make the storage with reasonable defaults 361 cstore := NewInMemory() 362 363 algo := Secp256k1 364 n1, n2 := "old-name", "new name" 365 p1, p2 := nums, foobar 366 367 // make sure key works with initial password 368 _, _, err := cstore.CreateMnemonic(n1, English, p1, algo, "") 369 require.Nil(t, err, "%+v", err) 370 assertPassword(t, cstore, n1, p1, p2) 371 372 // update password requires the existing password 373 getNewpass := func() (string, error) { return p2, nil } 374 err = cstore.Update(n1, "jkkgkg", getNewpass) 375 require.NotNil(t, err) 376 assertPassword(t, cstore, n1, p1, p2) 377 378 // then it changes the password when correct 379 err = cstore.Update(n1, p1, getNewpass) 380 require.NoError(t, err) 381 // p2 is now the proper one! 382 assertPassword(t, cstore, n1, p2, p1) 383 384 // exporting requires the proper name and passphrase 385 _, err = cstore.Export(n1 + ".notreal") 386 require.NotNil(t, err) 387 _, err = cstore.Export(" " + n1) 388 require.NotNil(t, err) 389 _, err = cstore.Export(n1 + " ") 390 require.NotNil(t, err) 391 _, err = cstore.Export("") 392 require.NotNil(t, err) 393 exported, err := cstore.Export(n1) 394 require.Nil(t, err, "%+v", err) 395 396 // import succeeds 397 err = cstore.Import(n2, exported) 398 require.NoError(t, err) 399 400 // second import fails 401 err = cstore.Import(n2, exported) 402 require.NotNil(t, err) 403 } 404 405 // TestSeedPhrase verifies restoring from a seed phrase 406 func TestSeedPhrase(t *testing.T) { 407 408 // make the storage with reasonable defaults 409 cstore := NewInMemory() 410 411 algo := Secp256k1 412 n1, n2 := "lost-key", "found-again" 413 p1, p2 := nums, foobar 414 415 // make sure key works with initial password 416 info, mnemonic, err := cstore.CreateMnemonic(n1, English, p1, algo, "") 417 require.Nil(t, err, "%+v", err) 418 require.Equal(t, n1, info.GetName()) 419 assert.NotEmpty(t, mnemonic) 420 421 // now, let us delete this key 422 err = cstore.Delete(n1, p1, false) 423 require.Nil(t, err, "%+v", err) 424 _, err = cstore.Get(n1) 425 require.NotNil(t, err) 426 427 // let us re-create it from the mnemonic-phrase 428 params := *hd.NewFundraiserParams(0, sdk.CoinType, 0) 429 hdPath := params.String() 430 newInfo, err := cstore.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1) 431 require.NoError(t, err) 432 require.Equal(t, n2, newInfo.GetName()) 433 require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) 434 require.Equal(t, info.GetPubKey(), newInfo.GetPubKey()) 435 } 436 437 func TestCreateHDPath(t *testing.T) { 438 type args struct { 439 account uint32 440 index uint32 441 } 442 tests := []struct { 443 name string 444 args args 445 want hd.BIP44Params 446 }{ 447 {"m/44'/0'/0'/0/0", args{0, 0}, hd.BIP44Params{CoinType: 118, Purpose: 44}}, 448 {"m/44'/114'/0'/0/0", args{0, 0}, hd.BIP44Params{Purpose: 44, CoinType: 118, Account: 0, AddressIndex: 0}}, 449 {"m/44'/114'/1'/1/0", args{1, 1}, hd.BIP44Params{Purpose: 44, CoinType: 118, Account: 1, AddressIndex: 1}}, 450 } 451 for _, tt := range tests { 452 tt := tt 453 t.Run(tt.name, func(t *testing.T) { 454 tt := tt 455 require.Equal(t, tt.want, *CreateHDPath(tt.args.account, tt.args.index)) 456 }) 457 } 458 } 459 460 func ExampleNew() { 461 // Select the encryption and storage for your cryptostore 462 customKeyGenFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) { 463 var bzArr [32]byte 464 copy(bzArr[:], bz) 465 return secp256k1.PrivKeySecp256k1(bzArr), nil 466 } 467 cstore := NewInMemory(WithKeygenFunc(customKeyGenFunc)) 468 469 sec := Secp256k1 470 471 // Add keys and see they return in alphabetical order 472 bob, _, err := cstore.CreateMnemonic("Bob", English, "friend", sec, "") 473 if err != nil { 474 // this should never happen 475 fmt.Println(err) 476 } else { 477 // return info here just like in List 478 fmt.Println(bob.GetName()) 479 } 480 _, _, _ = cstore.CreateMnemonic("Alice", English, "secret", sec, "") 481 _, _, _ = cstore.CreateMnemonic("Carl", English, "mitm", sec, "") 482 info, _ := cstore.List() 483 for _, i := range info { 484 fmt.Println(i.GetName()) 485 } 486 487 // We need to use passphrase to generate a signature 488 tx := []byte("deadbeef") 489 sig, pub, err := cstore.Sign("Bob", "friend", tx) 490 if err != nil { 491 fmt.Println("don't accept real passphrase") 492 } 493 494 // and we can validate the signature with publicly available info 495 binfo, _ := cstore.Get("Bob") 496 if !binfo.GetPubKey().Equals(bob.GetPubKey()) { 497 fmt.Println("Get and Create return different keys") 498 } 499 500 if pub.Equals(binfo.GetPubKey()) { 501 fmt.Println("signed by Bob") 502 } 503 if !pub.VerifyBytes(tx, sig) { 504 fmt.Println("invalid signature") 505 } 506 507 // Output: 508 // Bob 509 // Alice 510 // Bob 511 // Carl 512 // signed by Bob 513 } 514 515 func accAddr(info Info) sdk.AccAddress { 516 return (sdk.AccAddress)(info.GetPubKey().Address()) 517 }