package keyring import ( "encoding/hex" "fmt" "strings" "testing" "github.com/99designs/keyring" "github.com/cosmos/go-bip39" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" ) const ( someKey = "theKey" theID = "theID" otherID = "otherID" ) func init() { crypto.BcryptSecurityParameter = 1 } func getCodec() codec.Codec { registry := codectypes.NewInterfaceRegistry() cryptocodec.RegisterInterfaces(registry) return codec.NewProtoCodec(registry) } func TestNewKeyring(t *testing.T) { dir := t.TempDir() mockIn := strings.NewReader("") cdc := getCodec() kr, err := New("cosmos", BackendFile, dir, mockIn, cdc) require.NoError(t, err) nilKr, err := New("cosmos", "fuzzy", dir, mockIn, cdc) require.Error(t, err) require.Nil(t, nilKr) require.Equal(t, "unknown keyring backend fuzzy", err.Error()) mockIn.Reset("password\npassword\n") k, _, err := kr.NewMnemonic("foo", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.Equal(t, "foo", k.Name) } func TestKeyManagementKeyRing(t *testing.T) { cdc := getCodec() kb, err := New("keybasename", "test", t.TempDir(), nil, cdc) require.NoError(t, err) require.NotNil(t, cdc) algo := hd.Secp256k1 n1, n2, n3 := "personal", "business", "other" // Check empty state records, err := kb.List() require.NoError(t, err) require.Empty(t, records) _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.Error(t, err, "ed25519 keys are currently not supported by keybase") // create some keys _, err = kb.Key(n1) require.Error(t, err) // save localKey with "n1`" k, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) require.Equal(t, n1, k.Name) // save localKey with "n2" k1, _, err := kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) require.Equal(t, n2, k1.Name) k2, err := kb.Key(n2) require.NoError(t, err) _, err = kb.Key(n3) require.NotNil(t, err) addr, err := k2.GetAddress() require.NoError(t, err) _, err = kb.KeyByAddress(addr) require.NoError(t, err) addr, err = sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") require.NoError(t, err) _, err = kb.KeyByAddress(addr) require.Error(t, err) // list shows them in order keyS, err := kb.List() require.NoError(t, err) require.Equal(t, 2, len(keyS)) // note these are in alphabetical order require.Equal(t, n2, keyS[0].Name) require.Equal(t, n1, keyS[1].Name) key1, err := k2.GetPubKey() require.NoError(t, err) require.NotNil(t, key1) key2, err := keyS[0].GetPubKey() require.NoError(t, err) require.NotNil(t, key2) require.Equal(t, key1, key2) // deleting a key removes it err = kb.Delete("bad name") require.NotNil(t, err) err = kb.Delete(n1) require.NoError(t, err) keyS, err = kb.List() require.NoError(t, err) require.Equal(t, 1, len(keyS)) _, err = kb.Key(n1) require.Error(t, err) // create an offline key o1 := "offline" priv1 := ed25519.GenPrivKey() pub1 := priv1.PubKey() k3, err := kb.SaveOfflineKey(o1, pub1) require.Nil(t, err) key1, err = k3.GetPubKey() require.NoError(t, err) require.NotNil(t, key1) require.Equal(t, pub1, key1) require.Equal(t, o1, k3.Name) keyS, err = kb.List() require.NoError(t, err) require.Equal(t, 2, len(keyS)) // delete the offline key err = kb.Delete(o1) require.NoError(t, err) keyS, err = kb.List() require.NoError(t, err) require.Equal(t, 1, len(keyS)) // addr cache gets nuked - and test skip flag require.NoError(t, kb.Delete(n2)) } func TestSignVerifyKeyRing(t *testing.T) { dir := t.TempDir() cdc := getCodec() kb, err := New("keybasename", "test", dir, nil, cdc) require.NoError(t, err) algo := hd.Secp256k1 n1, n2, n3 := "some dude", "a dudette", "dude-ish" // create two users and get their info kr1, _, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) kr2, _, err := kb.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) // let's try to sign some messages d1 := []byte("my first message") d2 := []byte("some other important info!") d3 := []byte("feels like I forgot something...") // try signing both data with both .. s11, pub1, err := kb.Sign(n1, d1) require.NoError(t, err) key1, err := kr1.GetPubKey() require.NoError(t, err) require.NotNil(t, key1) require.Equal(t, key1, pub1) s12, pub1, err := kb.Sign(n1, d2) require.Nil(t, err) require.Equal(t, key1, pub1) s21, pub2, err := kb.Sign(n2, d1) require.Nil(t, err) key2, err := kr2.GetPubKey() require.NoError(t, err) require.NotNil(t, key2) require.Equal(t, key2, pub2) s22, pub2, err := kb.Sign(n2, d2) require.Nil(t, err) require.Equal(t, key2, pub2) // let's try to validate and make sure it only works when everything is proper cases := []struct { key types.PubKey data []byte sig []byte valid bool }{ // proper matches {key1, d1, s11, true}, // change data, pubkey, or signature leads to fail {key1, d2, s11, false}, {key2, d1, s11, false}, {key1, d1, s21, false}, // make sure other successes {key1, d2, s12, true}, {key2, d1, s21, true}, {key2, d2, s22, true}, } for i, tc := range cases { valid := tc.key.VerifySignature(tc.data, tc.sig) require.Equal(t, tc.valid, valid, "%d", i) } // Now try to sign data with a secret-less key // Import a public key armor, err := kb.ExportPubKeyArmor(n2) require.NoError(t, err) require.NoError(t, kb.Delete(n2)) require.NoError(t, kb.ImportPubKey(n3, armor)) i3, err := kb.Key(n3) require.NoError(t, err) require.Equal(t, i3.Name, n3) _, _, err = kb.Sign(n3, d3) require.Error(t, err) require.Equal(t, "cannot sign with offline keys", err.Error()) } func TestExportImportKeyRing(t *testing.T) { cdc := getCodec() kb, err := New("keybasename", "test", t.TempDir(), nil, cdc) require.NoError(t, err) k, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) name := k.Name require.NoError(t, err) require.Equal(t, name, "john") john, err := kb.Key("john") require.NoError(t, err) require.Equal(t, name, "john") key, err := k.GetPubKey() require.NoError(t, err) johnAddr := key.Address() armor, err := kb.ExportPrivKeyArmor("john", "apassphrase") require.NoError(t, err) err = kb.Delete("john") require.NoError(t, err) err = kb.ImportPrivKey("john2", armor, "apassphrase") require.NoError(t, err) john2, err := kb.Key("john2") require.NoError(t, err) require.Equal(t, key.Address(), johnAddr) require.Equal(t, john.Name, "john") addr, err := john.GetAddress() require.NoError(t, err) addr2, err := john2.GetAddress() require.NoError(t, err) require.Equal(t, addr, addr2) key, err = john.GetPubKey() require.NoError(t, err) key2, err := john2.GetPubKey() require.NoError(t, err) require.True(t, key.Equals(key2)) } func TestExportImportPubKeyKeyRing(t *testing.T) { cdc := getCodec() kb, err := New("keybasename", "test", t.TempDir(), nil, cdc) require.NoError(t, err) algo := hd.Secp256k1 // CreateMnemonic a private-public key pair and ensure consistency k, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) require.NotNil(t, k) require.Equal(t, k.Name, "john") key, err := k.GetPubKey() require.NoError(t, err) addr := key.Address() john, err := kb.Key("john") require.NoError(t, err) require.Equal(t, john.Name, "john") key, err = john.GetPubKey() require.NoError(t, err) require.Equal(t, key.Address(), addr) // Export the public key only armor, err := kb.ExportPubKeyArmor("john") require.NoError(t, err) err = kb.Delete("john") require.NoError(t, err) // Import it under a different name err = kb.ImportPubKey("john-pubkey-only", armor) require.NoError(t, err) // Ensure consistency john2, err := kb.Key("john-pubkey-only") require.NoError(t, err) key2, err := john2.GetPubKey() require.NoError(t, err) // Compare the public keys require.True(t, key.Equals(key2)) // Ensure keys cannot be overwritten err = kb.ImportPubKey("john-pubkey-only", armor) require.NotNil(t, err) } func TestAdvancedKeyManagementKeyRing(t *testing.T) { dir := t.TempDir() cdc := getCodec() kb, err := New("keybasename", "test", dir, nil, cdc) require.NoError(t, err) algo := hd.Secp256k1 n1, n2 := "old-name", "new name" // make sure key works with initial password _, _, err = kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) _, err = kb.ExportPubKeyArmor(n1 + ".notreal") require.NotNil(t, err) _, err = kb.ExportPubKeyArmor(" " + n1) require.NotNil(t, err) _, err = kb.ExportPubKeyArmor(n1 + " ") require.NotNil(t, err) _, err = kb.ExportPubKeyArmor("") require.NotNil(t, err) exported, err := kb.ExportPubKeyArmor(n1) require.Nil(t, err, "%+v", err) err = kb.Delete(n1) require.NoError(t, err) // import succeeds err = kb.ImportPubKey(n2, exported) require.NoError(t, err) // second import fails err = kb.ImportPubKey(n2, exported) require.NotNil(t, err) } func TestSeedPhraseKeyRing(t *testing.T) { dir := t.TempDir() cdc := getCodec() kb, err := New("keybasename", "test", dir, nil, cdc) require.NoError(t, err) algo := hd.Secp256k1 n1, n2 := "lost-key", "found-again" // make sure key works with initial password k, mnemonic, err := kb.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) require.Equal(t, n1, k.Name) require.NotEmpty(t, mnemonic) key, err := k.GetPubKey() require.NoError(t, err) // now, let us delete this key err = kb.Delete(n1) require.Nil(t, err, "%+v", err) _, err = kb.Key(n1) require.NotNil(t, err) // let us re-create it from the mnemonic-phrase hdPath := hd.NewFundraiserParams(0, sdk.CoinType, 0).String() k1, err := kb.NewAccount(n2, mnemonic, DefaultBIP39Passphrase, hdPath, hd.Secp256k1) require.NoError(t, err) require.Equal(t, n2, k1.Name) newKey, err := k1.GetPubKey() require.NoError(t, err) require.Equal(t, key.Address(), newKey.Address()) require.Equal(t, key, newKey) } func TestKeyringKeybaseExportImportPrivKey(t *testing.T) { cdc := getCodec() kb, err := New("keybasename", "test", t.TempDir(), nil, cdc) require.NoError(t, err) _, _, err = kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) keystr, err := kb.ExportPrivKeyArmor("john", "somepassword") require.NoError(t, err) require.NotEmpty(t, keystr) err = kb.Delete("john") require.NoError(t, err) // try import the key - wrong password err = kb.ImportPrivKey("john2", keystr, "bad pass") require.Equal(t, "failed to decrypt private key: ciphertext decryption failed", err.Error()) // try import the key with the correct password require.NoError(t, kb.ImportPrivKey("john2", keystr, "somepassword")) // overwrite is not allowed err = kb.ImportPrivKey("john2", keystr, "password") require.Equal(t, "cannot overwrite key: john2", err.Error()) // try export non existing key _, err = kb.ExportPrivKeyArmor("john3", "wrongpassword") require.EqualError(t, err, "john3.info: key not found") } func TestInMemoryLanguage(t *testing.T) { cdc := getCodec() kb := NewInMemory(cdc) _, _, err := kb.NewMnemonic("something", Japanese, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.Error(t, err) require.Equal(t, "unsupported language: only english is supported", err.Error()) } func TestInMemoryCreateMultisig(t *testing.T) { cdc := getCodec() kb, err := New("keybasename", "memory", "", nil, cdc) require.NoError(t, err) multi := multisig.NewLegacyAminoPubKey( 1, []types.PubKey{ secp256k1.GenPrivKey().PubKey(), }, ) _, err = kb.SaveMultisig("multi", multi) require.NoError(t, err) } func TestInMemoryCreateAccountInvalidMnemonic(t *testing.T) { cdc := getCodec() kb := NewInMemory(cdc) _, err := kb.NewAccount( "some_account", "malarkey pair crucial catch public canyon evil outer stage ten gym tornado", "", hd.CreateHDPath(118, 0, 0).String(), hd.Secp256k1) require.Error(t, err) require.Equal(t, "Invalid mnemonic", err.Error()) } // TestInMemoryKeyManagement makes sure we can manipulate these keys well func TestInMemoryKeyManagement(t *testing.T) { // make the storage with reasonable defaults cdc := getCodec() cstore := NewInMemory(cdc) algo := hd.Secp256k1 n1, n2, n3 := "personal", "business", "other" // Check empty state l, err := cstore.List() require.NoError(t, err) require.Empty(t, l) _, _, err = cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.Error(t, err, "ed25519 keys are currently not supported by keybase") // create some keys _, err = cstore.Key(n1) require.Error(t, err) k, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) require.Equal(t, n1, k.Name) _, _, err = cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.NoError(t, err) // we can get these keys k2, err := cstore.Key(n2) require.NoError(t, err) _, err = cstore.Key(n3) require.NotNil(t, err) addr, err := accAddr(k2) require.NoError(t, err) _, err = cstore.KeyByAddress(addr) require.NoError(t, err) addr, err = sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") require.NoError(t, err) _, err = cstore.KeyByAddress(addr) require.NotNil(t, err) // list shows them in order keyS, err := cstore.List() require.NoError(t, err) require.Equal(t, 2, len(keyS)) // note these are in alphabetical order require.Equal(t, n2, keyS[0].Name) require.Equal(t, n1, keyS[1].Name) key1, err := k2.GetPubKey() require.NoError(t, err) key2, err := keyS[0].GetPubKey() require.NoError(t, err) require.True(t, key1.Equals(key2)) // deleting a key removes it err = cstore.Delete("bad name") require.NotNil(t, err) err = cstore.Delete(n1) require.NoError(t, err) keyS, err = cstore.List() require.NoError(t, err) require.Equal(t, 1, len(keyS)) _, err = cstore.Key(n1) require.Error(t, err) // create an offline key o1 := "offline" priv1 := ed25519.GenPrivKey() pub1 := priv1.PubKey() k, err = cstore.SaveOfflineKey(o1, pub1) require.Nil(t, err) key, err := k.GetPubKey() require.NoError(t, err) require.Equal(t, pub1, key) require.Equal(t, o1, k.Name) require.NotNil(t, k.GetOffline()) keyS, err = cstore.List() require.NoError(t, err) require.Equal(t, 2, len(keyS)) // delete the offline key err = cstore.Delete(o1) require.NoError(t, err) keyS, err = cstore.List() require.NoError(t, err) require.Equal(t, 1, len(keyS)) // addr cache gets nuked - and test skip flag err = cstore.Delete(n2) require.NoError(t, err) } // TestInMemorySignVerify does some detailed checks on how we sign and validate // signatures func TestInMemorySignVerify(t *testing.T) { cdc := getCodec() cstore := NewInMemory(cdc) algo := hd.Secp256k1 n1, n2, n3 := "some dude", "a dudette", "dude-ish" // create two users and get their info kr1, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) kr2, _, err := cstore.NewMnemonic(n2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err) // let's try to sign some messages d1 := []byte("my first message") d2 := []byte("some other important info!") d3 := []byte("feels like I forgot something...") // try signing both data with both .. s11, pub1, err := cstore.Sign(n1, d1) require.Nil(t, err) key1, err := kr1.GetPubKey() require.NoError(t, err) require.Equal(t, key1, pub1) s12, pub1, err := cstore.Sign(n1, d2) require.Nil(t, err) require.Equal(t, key1, pub1) s21, pub2, err := cstore.Sign(n2, d1) require.Nil(t, err) key2, err := kr2.GetPubKey() require.NoError(t, err) require.Equal(t, key2, pub2) s22, pub2, err := cstore.Sign(n2, d2) require.Nil(t, err) require.Equal(t, key2, pub2) // let's try to validate and make sure it only works when everything is proper cases := []struct { key types.PubKey data []byte sig []byte valid bool }{ // proper matches {key1, d1, s11, true}, // change data, pubkey, or signature leads to fail {key1, d2, s11, false}, {key2, d1, s11, false}, {key1, d1, s21, false}, // make sure other successes {key1, d2, s12, true}, {key2, d1, s21, true}, {key2, d2, s22, true}, } for i, tc := range cases { valid := tc.key.VerifySignature(tc.data, tc.sig) require.Equal(t, tc.valid, valid, "%d", i) } // Import a public key armor, err := cstore.ExportPubKeyArmor(n2) require.Nil(t, err) err = cstore.Delete(n2) require.NoError(t, err) err = cstore.ImportPubKey(n3, armor) require.NoError(t, err) i3, err := cstore.Key(n3) require.NoError(t, err) require.Equal(t, i3.Name, n3) // Now try to sign data with a secret-less key _, _, err = cstore.Sign(n3, d3) require.Error(t, err) require.Equal(t, "cannot sign with offline keys", err.Error()) } // TestInMemoryExportImport tests exporting and importing func TestInMemoryExportImport(t *testing.T) { // make the storage with reasonable defaults cdc := getCodec() cstore := NewInMemory(cdc) k, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.Equal(t, k.Name, "john") john, err := cstore.Key("john") require.NoError(t, err) require.Equal(t, k.Name, "john") johnKey, err := k.GetPubKey() require.NoError(t, err) johnAddr := johnKey.Address() armor, err := cstore.ExportPubKeyArmor("john") require.NoError(t, err) err = cstore.Delete("john") require.NoError(t, err) err = cstore.ImportPubKey("john2", armor) require.NoError(t, err) john2, err := cstore.Key("john2") require.NoError(t, err) require.Equal(t, johnKey.Address(), johnAddr) require.Equal(t, john.Name, "john") johnSdkAddress, err := john.GetAddress() require.NoError(t, err) john2SdkAddress, err := john2.GetAddress() require.NoError(t, err) require.Equal(t, johnSdkAddress, john2SdkAddress) john2Key, err := john2.GetPubKey() require.NoError(t, err) require.True(t, johnKey.Equals(john2Key)) } func TestInMemoryExportImportPrivKey(t *testing.T) { cdc := getCodec() kb := NewInMemory(cdc) k, _, err := kb.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.Equal(t, k.Name, "john") priv1, err := kb.Key("john") require.NoError(t, err) armored, err := kb.ExportPrivKeyArmor("john", "secretcpw") require.NoError(t, err) // delete exported key require.NoError(t, kb.Delete("john")) _, err = kb.Key("john") require.Error(t, err) // import armored key require.NoError(t, kb.ImportPrivKey("john", armored, "secretcpw")) // ensure old and new keys match priv2, err := kb.Key("john") require.NoError(t, err) key1, err := priv1.GetPubKey() require.NoError(t, err) key2, err := priv2.GetPubKey() require.NoError(t, err) require.True(t, key1.Equals(key2)) } func TestInMemoryExportImportPubKey(t *testing.T) { // make the storage with reasonable defaults cdc := getCodec() cstore := NewInMemory(cdc) // CreateMnemonic a private-public key pair and ensure consistency k, _, err := cstore.NewMnemonic("john", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.NotNil(t, k) require.Equal(t, k.Name, "john") key, err := k.GetPubKey() require.NoError(t, err) addr := key.Address() john, err := cstore.Key("john") require.NoError(t, err) require.Equal(t, john.Name, "john") johnKey, err := john.GetPubKey() require.NoError(t, err) require.Equal(t, johnKey.Address(), addr) // Export the public key only armor, err := cstore.ExportPubKeyArmor("john") require.NoError(t, err) err = cstore.Delete("john") require.NoError(t, err) // Import it under a different name err = cstore.ImportPubKey("john-pubkey-only", armor) require.NoError(t, err) // Ensure consistency john2, err := cstore.Key("john-pubkey-only") require.NoError(t, err) // Compare the public keys john2Key, err := john2.GetPubKey() require.NoError(t, err) require.True(t, johnKey.Equals(john2Key)) // Ensure keys cannot be overwritten err = cstore.ImportPubKey("john-pubkey-only", armor) require.NotNil(t, err) } // TestInMemoryAdvancedKeyManagement verifies update, import, export functionality func TestInMemoryAdvancedKeyManagement(t *testing.T) { // make the storage with reasonable defaults cdc := getCodec() cstore := NewInMemory(cdc) algo := hd.Secp256k1 n1, n2 := "old-name", "new name" // make sure key works with initial password _, _, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) // exporting requires the proper name and passphrase _, err = cstore.ExportPubKeyArmor(n1 + ".notreal") require.NotNil(t, err) _, err = cstore.ExportPubKeyArmor(" " + n1) require.NotNil(t, err) _, err = cstore.ExportPubKeyArmor(n1 + " ") require.NotNil(t, err) _, err = cstore.ExportPubKeyArmor("") require.NotNil(t, err) exported, err := cstore.ExportPubKeyArmor(n1) require.Nil(t, err, "%+v", err) err = cstore.Delete(n1) require.NoError(t, err) // import succeeds err = cstore.ImportPubKey(n2, exported) require.NoError(t, err) // second import fails err = cstore.ImportPubKey(n2, exported) require.NotNil(t, err) } // TestInMemorySeedPhrase verifies restoring from a seed phrase func TestInMemorySeedPhrase(t *testing.T) { // make the storage with reasonable defaults cdc := getCodec() cstore := NewInMemory(cdc) algo := hd.Secp256k1 n1, n2 := "lost-key", "found-again" // make sure key works with initial password k, mnemonic, err := cstore.NewMnemonic(n1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, algo) require.Nil(t, err, "%+v", err) require.Equal(t, n1, k.Name) require.NotEmpty(t, mnemonic) // now, let us delete this key err = cstore.Delete(n1) require.Nil(t, err, "%+v", err) _, err = cstore.Key(n1) require.NotNil(t, err) // let us re-create it from the mnemonic-phrase hdPath := hd.NewFundraiserParams(0, sdk.CoinType, 0).String() k1, err := cstore.NewAccount(n2, mnemonic, DefaultBIP39Passphrase, hdPath, algo) require.NoError(t, err) require.Equal(t, n2, k1.Name) key, err := k.GetPubKey() require.NoError(t, err) key1, err := k1.GetPubKey() require.NoError(t, err) require.Equal(t, key.Address(), key1.Address()) require.Equal(t, key, key1) } func TestKeyChain_ShouldFailWhenAddingSameGeneratedAccount(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) // Given we create a mnemonic _, seed, err := kr.NewMnemonic("test", English, "", DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) require.NoError(t, kr.Delete("test")) path := hd.CreateHDPath(118, 0, 0).String() _, err = kr.NewAccount("test1", seed, "", path, hd.Secp256k1) require.NoError(t, err) // Creating another account with different uid but same seed should fail due to have same pub address _, err = kr.NewAccount("test2", seed, "", path, hd.Secp256k1) require.Error(t, err) } func ExampleNew() { // Select the encryption and storage for your cryptostore cdc := getCodec() cstore := NewInMemory(cdc) sec := hd.Secp256k1 // Add keys and see they return in alphabetical order bob, _, err := cstore.NewMnemonic("Bob", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, sec) if err != nil { // this should never happen fmt.Println(err) } else { // return info here just like in List fmt.Println(bob.Name) } _, _, _ = cstore.NewMnemonic("Alice", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, sec) _, _, _ = cstore.NewMnemonic("Carl", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, sec) records, _ := cstore.List() for _, k := range records { fmt.Println(k.Name) } // We need to use passphrase to generate a signature tx := []byte("deadbeef") sig, pub, err := cstore.Sign("Bob", tx) if err != nil { fmt.Println("don't accept real passphrase") } // and we can validate the signature with publicly available info bRecord, _ := cstore.Key("Bob") key, _ := bRecord.GetPubKey() bobKey, _ := bob.GetPubKey() if !key.Equals(bobKey) { fmt.Println("Get and Create return different keys") } if pub.Equals(key) { fmt.Println("signed by Bob") } if !pub.VerifySignature(tx, sig) { fmt.Println("invalid signature") } // Output: // Bob // Alice // Bob // Carl // signed by Bob } func TestAltKeyring_List(t *testing.T) { dir := t.TempDir() cdc := getCodec() kr, err := New(t.Name(), BackendTest, dir, nil, cdc) require.NoError(t, err) list, err := kr.List() require.NoError(t, err) require.Empty(t, list) // Fails on creating unsupported pubKeyType _, _, err = kr.NewMnemonic("failing", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.EqualError(t, err, ErrUnsupportedSigningAlgo.Error()) // Create 3 keys uid1, uid2, uid3 := "Zkey", "Bkey", "Rkey" _, _, err = kr.NewMnemonic(uid1, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) _, _, err = kr.NewMnemonic(uid2, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) _, _, err = kr.NewMnemonic(uid3, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) list, err = kr.List() require.NoError(t, err) require.Len(t, list, 3) // Check they are in alphabetical order require.Equal(t, uid2, list[0].Name) require.Equal(t, uid3, list[1].Name) require.Equal(t, uid1, list[2].Name) } func TestAltKeyring_NewAccount(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) entropy, err := bip39.NewEntropy(defaultEntropySize) require.NoError(t, err) mnemonic, err := bip39.NewMnemonic(entropy) require.NoError(t, err) uid := "newUid" // Fails on creating unsupported pubKeyType _, err = kr.NewAccount(uid, mnemonic, DefaultBIP39Passphrase, sdk.FullFundraiserPath, notSupportedAlgo{}) require.EqualError(t, err, ErrUnsupportedSigningAlgo.Error()) k, err := kr.NewAccount(uid, mnemonic, DefaultBIP39Passphrase, sdk.FullFundraiserPath, hd.Secp256k1) require.NoError(t, err) require.Equal(t, uid, k.Name) list, err := kr.List() require.NoError(t, err) require.Len(t, list, 1) } func TestAltKeyring_Get(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := someKey mnemonic, _, err := kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) key, err := kr.Key(uid) require.NoError(t, err) requireEqualRenamedKey(t, mnemonic, key, true) } func TestAltKeyring_KeyByAddress(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := someKey mnemonic, _, err := kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) addr, err := mnemonic.GetAddress() require.NoError(t, err) key, err := kr.KeyByAddress(addr) require.NoError(t, err) requireEqualRenamedKey(t, key, mnemonic, true) } func TestAltKeyring_Delete(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := someKey _, _, err = kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) list, err := kr.List() require.NoError(t, err) require.Len(t, list, 1) err = kr.Delete(uid) require.NoError(t, err) list, err = kr.List() require.NoError(t, err) require.Empty(t, list) } func TestAltKeyring_DeleteByAddress(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := someKey mnemonic, _, err := kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) list, err := kr.List() require.NoError(t, err) require.Len(t, list, 1) addr, err := mnemonic.GetAddress() require.NoError(t, err) err = kr.DeleteByAddress(addr) require.NoError(t, err) list, err = kr.List() require.NoError(t, err) require.Empty(t, list) } func TestAltKeyring_SaveOfflineKey(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) list, err := kr.List() require.NoError(t, err) require.Empty(t, list) key := someKey priv := ed25519.GenPrivKey() pub := priv.PubKey() k, err := kr.SaveOfflineKey(key, pub) require.Nil(t, err) pubKey, err := k.GetPubKey() require.NoError(t, err) require.Equal(t, pub, pubKey) require.Equal(t, key, k.Name) list, err = kr.List() require.NoError(t, err) require.Len(t, list, 1) } func TestAltKeyring_SaveMultisig(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) mnemonic1, _, err := kr.NewMnemonic("key1", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) mnemonic2, _, err := kr.NewMnemonic("key2", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) key := "multi" key1, err := mnemonic1.GetPubKey() require.NoError(t, err) key2, err := mnemonic2.GetPubKey() require.NoError(t, err) pub := multisig.NewLegacyAminoPubKey( 2, []types.PubKey{ &secp256k1.PubKey{Key: key1.Bytes()}, &secp256k1.PubKey{Key: key2.Bytes()}, }, ) k, err := kr.SaveMultisig(key, pub) require.Nil(t, err) infoKey, err := k.GetPubKey() require.NoError(t, err) require.Equal(t, pub, infoKey) require.Equal(t, key, k.Name) list, err := kr.List() require.NoError(t, err) require.Len(t, list, 3) } func TestAltKeyring_Sign(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := "jack" _, _, err = kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) msg := []byte("some message") sign, key, err := kr.Sign(uid, msg) require.NoError(t, err) require.True(t, key.VerifySignature(msg, sign)) } func TestAltKeyring_SignByAddress(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := "jack" mnemonic, _, err := kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) msg := []byte("some message") addr, err := mnemonic.GetAddress() require.NoError(t, err) sign, key, err := kr.SignByAddress(addr, msg) require.NoError(t, err) require.True(t, key.VerifySignature(msg, sign)) } func TestAltKeyring_ImportExportPrivKey(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := theID _, _, err = kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) passphrase := "somePass" armor, err := kr.ExportPrivKeyArmor(uid, passphrase) require.NoError(t, err) err = kr.Delete(uid) require.NoError(t, err) newUID := otherID // Should fail importing with wrong password err = kr.ImportPrivKey(newUID, armor, "wrongPass") require.EqualError(t, err, "failed to decrypt private key: ciphertext decryption failed") err = kr.ImportPrivKey(newUID, armor, passphrase) require.NoError(t, err) // Should fail importing private key on existing key. err = kr.ImportPrivKey(newUID, armor, passphrase) require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) } func TestAltKeyring_ImportExportPrivKey_ByAddress(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := theID mnemonic, _, err := kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) passphrase := "somePass" addr, err := mnemonic.GetAddress() require.NoError(t, err) armor, err := kr.ExportPrivKeyArmorByAddress(addr, passphrase) require.NoError(t, err) err = kr.Delete(uid) require.NoError(t, err) newUID := otherID // Should fail importing with wrong password err = kr.ImportPrivKey(newUID, armor, "wrongPass") require.EqualError(t, err, "failed to decrypt private key: ciphertext decryption failed") err = kr.ImportPrivKey(newUID, armor, passphrase) require.NoError(t, err) // Should fail importing private key on existing key. err = kr.ImportPrivKey(newUID, armor, passphrase) require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) } func TestAltKeyring_ImportExportPubKey(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := theID _, _, err = kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) armor, err := kr.ExportPubKeyArmor(uid) require.NoError(t, err) err = kr.Delete(uid) require.NoError(t, err) newUID := otherID err = kr.ImportPubKey(newUID, armor) require.NoError(t, err) // Should fail importing private key on existing key. err = kr.ImportPubKey(newUID, armor) require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) } func TestAltKeyring_ImportExportPubKey_ByAddress(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := theID mnemonic, _, err := kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) addr, err := mnemonic.GetAddress() require.NoError(t, err) armor, err := kr.ExportPubKeyArmorByAddress(addr) require.NoError(t, err) err = kr.Delete(uid) require.NoError(t, err) newUID := otherID err = kr.ImportPubKey(newUID, armor) require.NoError(t, err) // Should fail importing private key on existing key. err = kr.ImportPubKey(newUID, armor) require.EqualError(t, err, fmt.Sprintf("cannot overwrite key: %s", newUID)) } func TestAltKeyring_UnsafeExportPrivKeyHex(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) uid := theID _, _, err = kr.NewMnemonic(uid, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) unsafeKeyring := NewUnsafe(kr) privKey, err := unsafeKeyring.UnsafeExportPrivKeyHex(uid) require.NoError(t, err) require.Equal(t, 64, len(privKey)) _, err = hex.DecodeString(privKey) require.NoError(t, err) // test error on non existing key _, err = unsafeKeyring.UnsafeExportPrivKeyHex("non-existing") require.Error(t, err) } func TestAltKeyring_ConstructorSupportedAlgos(t *testing.T) { cdc := getCodec() kr, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc) require.NoError(t, err) // should fail when using unsupported signing algorythm. _, _, err = kr.NewMnemonic("test", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.EqualError(t, err, "unsupported signing algo") // but works with default signing algo. _, _, err = kr.NewMnemonic("test", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) // but we can create a new keybase with our provided algos. kr2, err := New(t.Name(), BackendTest, t.TempDir(), nil, cdc, func(options *Options) { options.SupportedAlgos = SigningAlgoList{ notSupportedAlgo{}, } }) require.NoError(t, err) // now this new keyring does not fail when signing with provided algo _, _, err = kr2.NewMnemonic("test", English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, notSupportedAlgo{}) require.NoError(t, err) } func TestBackendConfigConstructors(t *testing.T) { backend := newKWalletBackendKeyringConfig("test", "", nil) require.Equal(t, []keyring.BackendType{keyring.KWalletBackend}, backend.AllowedBackends) require.Equal(t, "kdewallet", backend.ServiceName) require.Equal(t, "test", backend.KWalletAppID) backend = newPassBackendKeyringConfig("test", "directory", nil) require.Equal(t, []keyring.BackendType{keyring.PassBackend}, backend.AllowedBackends) require.Equal(t, "test", backend.ServiceName) require.Equal(t, "keyring-test", backend.PassPrefix) } func TestRenameKey(t *testing.T) { testCases := []struct { name string run func(Keyring) }{ { name: "rename a key", run: func(kr Keyring) { oldKeyUID, newKeyUID := "old", "new" oldKeyRecord := newKeyRecord(t, kr, oldKeyUID) err := kr.Rename(oldKeyUID, newKeyUID) // rename from "old" to "new" require.NoError(t, err) newRecord, err := kr.Key(newKeyUID) // new key should be in keyring require.NoError(t, err) requireEqualRenamedKey(t, newRecord, oldKeyRecord, false) // oldKeyRecord and newRecord should be the same except name oldKeyRecord, err = kr.Key(oldKeyUID) // old key should be gone from keyring require.Error(t, err) }, }, { name: "cant rename a key that doesnt exist", run: func(kr Keyring) { err := kr.Rename("bogus", "bogus2") require.Error(t, err) }, }, { name: "cant rename a key to an already existing key name", run: func(kr Keyring) { key1, key2 := "existingKey", "existingKey2" // create 2 keys newKeyRecord(t, kr, key1) newKeyRecord(t, kr, key2) err := kr.Rename(key2, key1) require.Equal(t, fmt.Errorf("rename failed: %s already exists in the keyring", key1), err) assertKeysExist(t, kr, key1, key2) // keys should still exist after failed rename }, }, { name: "cant rename key to itself", run: func(kr Keyring) { keyName := "keyName" newKeyRecord(t, kr, keyName) err := kr.Rename(keyName, keyName) require.Equal(t, fmt.Errorf("rename failed: %s already exists in the keyring", keyName), err) assertKeysExist(t, kr, keyName) }, }, } for _, tc := range testCases { tc := tc kr := newKeyring(t, "testKeyring") t.Run(tc.name, func(t *testing.T) { tc.run(kr) }) } } func requireEqualRenamedKey(t *testing.T, key *Record, mnemonic *Record, nameMatch bool) { if nameMatch { require.Equal(t, key.Name, mnemonic.Name) } keyAddr, err := key.GetAddress() require.NoError(t, err) mnemonicAddr, err := mnemonic.GetAddress() require.NoError(t, err) require.Equal(t, keyAddr, mnemonicAddr) key1, err := key.GetPubKey() require.NoError(t, err) key2, err := mnemonic.GetPubKey() require.NoError(t, err) require.Equal(t, key1, key2) require.Equal(t, key.GetType(), mnemonic.GetType()) } func newKeyring(t *testing.T, name string) Keyring { cdc := getCodec() kr, err := New(name, "test", t.TempDir(), nil, cdc) require.NoError(t, err) return kr } func newKeyRecord(t *testing.T, kr Keyring, name string) *Record { k, _, err := kr.NewMnemonic(name, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) require.NoError(t, err) return k } func assertKeysExist(t *testing.T, kr Keyring, names ...string) { for _, n := range names { _, err := kr.Key(n) require.NoError(t, err) } } func accAddr(k *Record) (sdk.AccAddress, error) { return k.GetAddress() }