forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge PR cosmos#4005: Increase crypto coverage
Test LazyLeybase and crypto/keys/mintkey extensively
- Loading branch information
1 parent
b85f528
commit 9556393
Showing
2 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,344 @@ | ||
package keys | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"github.com/tendermint/tendermint/crypto" | ||
"github.com/tendermint/tendermint/crypto/ed25519" | ||
|
||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd" | ||
"github.com/cosmos/cosmos-sdk/tests" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
func TestNew(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
lazykb, ok := kb.(lazyKeybase) | ||
require.True(t, ok) | ||
require.Equal(t, lazykb.name, "keybasename") | ||
require.Equal(t, lazykb.dir, dir) | ||
} | ||
|
||
func TestLazyKeyManagement(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
|
||
algo := Secp256k1 | ||
n1, n2, n3 := "personal", "business", "other" | ||
p1, p2 := "1234", "really-secure!@#$" | ||
|
||
// Check empty state | ||
l, err := kb.List() | ||
require.Nil(t, err) | ||
assert.Empty(t, l) | ||
|
||
_, _, err = kb.CreateMnemonic(n1, English, p1, Ed25519) | ||
require.Error(t, err, "ed25519 keys are currently not supported by keybase") | ||
|
||
// create some keys | ||
_, err = kb.Get(n1) | ||
require.Error(t, err) | ||
i, _, err := kb.CreateMnemonic(n1, English, p1, algo) | ||
|
||
require.NoError(t, err) | ||
require.Equal(t, n1, i.GetName()) | ||
_, _, err = kb.CreateMnemonic(n2, English, p2, algo) | ||
require.NoError(t, err) | ||
|
||
// we can get these keys | ||
i2, err := kb.Get(n2) | ||
require.NoError(t, err) | ||
_, err = kb.Get(n3) | ||
require.NotNil(t, err) | ||
_, err = kb.GetByAddress(accAddr(i2)) | ||
require.NoError(t, err) | ||
addr, err := sdk.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") | ||
require.NoError(t, err) | ||
_, err = kb.GetByAddress(addr) | ||
require.NotNil(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].GetName()) | ||
require.Equal(t, n1, keyS[1].GetName()) | ||
require.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey()) | ||
|
||
// deleting a key removes it | ||
err = kb.Delete("bad name", "foo", false) | ||
require.NotNil(t, err) | ||
err = kb.Delete(n1, p1, false) | ||
require.NoError(t, err) | ||
keyS, err = kb.List() | ||
require.NoError(t, err) | ||
require.Equal(t, 1, len(keyS)) | ||
_, err = kb.Get(n1) | ||
require.Error(t, err) | ||
|
||
// create an offline key | ||
o1 := "offline" | ||
priv1 := ed25519.GenPrivKey() | ||
pub1 := priv1.PubKey() | ||
i, err = kb.CreateOffline(o1, pub1) | ||
require.Nil(t, err) | ||
require.Equal(t, pub1, i.GetPubKey()) | ||
require.Equal(t, o1, i.GetName()) | ||
keyS, err = kb.List() | ||
require.NoError(t, err) | ||
require.Equal(t, 2, len(keyS)) | ||
|
||
// delete the offline key | ||
err = kb.Delete(o1, "", false) | ||
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 | ||
err = kb.Delete(n2, "", true) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestLazySignVerify(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
algo := Secp256k1 | ||
|
||
n1, n2, n3 := "some dude", "a dudette", "dude-ish" | ||
p1, p2, p3 := "1234", "foobar", "foobar" | ||
|
||
// create two users and get their info | ||
i1, _, err := kb.CreateMnemonic(n1, English, p1, algo) | ||
require.Nil(t, err) | ||
|
||
i2, _, err := kb.CreateMnemonic(n2, English, p2, algo) | ||
require.Nil(t, err) | ||
|
||
// Import a public key | ||
armor, err := kb.ExportPubKey(n2) | ||
require.Nil(t, err) | ||
kb.ImportPubKey(n3, armor) | ||
i3, err := kb.Get(n3) | ||
require.NoError(t, err) | ||
require.Equal(t, i3.GetName(), n3) | ||
|
||
// 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, p1, d1) | ||
require.Nil(t, err) | ||
require.Equal(t, i1.GetPubKey(), pub1) | ||
|
||
s12, pub1, err := kb.Sign(n1, p1, d2) | ||
require.Nil(t, err) | ||
require.Equal(t, i1.GetPubKey(), pub1) | ||
|
||
s21, pub2, err := kb.Sign(n2, p2, d1) | ||
require.Nil(t, err) | ||
require.Equal(t, i2.GetPubKey(), pub2) | ||
|
||
s22, pub2, err := kb.Sign(n2, p2, d2) | ||
require.Nil(t, err) | ||
require.Equal(t, i2.GetPubKey(), pub2) | ||
|
||
// let's try to validate and make sure it only works when everything is proper | ||
cases := []struct { | ||
key crypto.PubKey | ||
data []byte | ||
sig []byte | ||
valid bool | ||
}{ | ||
// proper matches | ||
{i1.GetPubKey(), d1, s11, true}, | ||
// change data, pubkey, or signature leads to fail | ||
{i1.GetPubKey(), d2, s11, false}, | ||
{i2.GetPubKey(), d1, s11, false}, | ||
{i1.GetPubKey(), d1, s21, false}, | ||
// make sure other successes | ||
{i1.GetPubKey(), d2, s12, true}, | ||
{i2.GetPubKey(), d1, s21, true}, | ||
{i2.GetPubKey(), d2, s22, true}, | ||
} | ||
|
||
for i, tc := range cases { | ||
valid := tc.key.VerifyBytes(tc.data, tc.sig) | ||
require.Equal(t, tc.valid, valid, "%d", i) | ||
} | ||
|
||
// Now try to sign data with a secret-less key | ||
_, _, err = kb.Sign(n3, p3, d3) | ||
require.NotNil(t, err) | ||
} | ||
|
||
func TestLazyExportImport(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
|
||
info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1) | ||
require.NoError(t, err) | ||
require.Equal(t, info.GetName(), "john") | ||
|
||
john, err := kb.Get("john") | ||
require.NoError(t, err) | ||
require.Equal(t, info.GetName(), "john") | ||
johnAddr := info.GetPubKey().Address() | ||
|
||
armor, err := kb.Export("john") | ||
require.NoError(t, err) | ||
|
||
err = kb.Import("john2", armor) | ||
require.NoError(t, err) | ||
|
||
john2, err := kb.Get("john2") | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, john.GetPubKey().Address(), johnAddr) | ||
require.Equal(t, john.GetName(), "john") | ||
require.Equal(t, john, john2) | ||
} | ||
|
||
func TestLazyExportImportPubKey(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
|
||
// CreateMnemonic a private-public key pair and ensure consistency | ||
notPasswd := "n9y25ah7" | ||
info, _, err := kb.CreateMnemonic("john", English, notPasswd, Secp256k1) | ||
require.Nil(t, err) | ||
require.NotEqual(t, info, "") | ||
require.Equal(t, info.GetName(), "john") | ||
addr := info.GetPubKey().Address() | ||
john, err := kb.Get("john") | ||
require.NoError(t, err) | ||
require.Equal(t, john.GetName(), "john") | ||
require.Equal(t, john.GetPubKey().Address(), addr) | ||
|
||
// Export the public key only | ||
armor, err := kb.ExportPubKey("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.Get("john-pubkey-only") | ||
require.NoError(t, err) | ||
// Compare the public keys | ||
require.True(t, john.GetPubKey().Equals(john2.GetPubKey())) | ||
// Ensure the original key hasn't changed | ||
john, err = kb.Get("john") | ||
require.NoError(t, err) | ||
require.Equal(t, john.GetPubKey().Address(), addr) | ||
require.Equal(t, john.GetName(), "john") | ||
|
||
// Ensure keys cannot be overwritten | ||
err = kb.ImportPubKey("john-pubkey-only", armor) | ||
require.NotNil(t, err) | ||
} | ||
|
||
func TestLazyExportPrivateKeyObject(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
|
||
info, _, err := kb.CreateMnemonic("john", English, "secretcpw", Secp256k1) | ||
require.NoError(t, err) | ||
require.Equal(t, info.GetName(), "john") | ||
|
||
// export private key object | ||
_, err = kb.ExportPrivateKeyObject("john", "invalid") | ||
require.NotNil(t, err, "%+v", err) | ||
exported, err := kb.ExportPrivateKeyObject("john", "secretcpw") | ||
require.Nil(t, err, "%+v", err) | ||
require.True(t, exported.PubKey().Equals(info.GetPubKey())) | ||
} | ||
|
||
func TestLazyAdvancedKeyManagement(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
|
||
algo := Secp256k1 | ||
n1, n2 := "old-name", "new name" | ||
p1, p2 := "1234", "foobar" | ||
|
||
// make sure key works with initial password | ||
_, _, err := kb.CreateMnemonic(n1, English, p1, algo) | ||
require.Nil(t, err, "%+v", err) | ||
assertPassword(t, kb, n1, p1, p2) | ||
|
||
// update password requires the existing password | ||
getNewpass := func() (string, error) { return p2, nil } | ||
err = kb.Update(n1, "jkkgkg", getNewpass) | ||
require.NotNil(t, err) | ||
assertPassword(t, kb, n1, p1, p2) | ||
|
||
// then it changes the password when correct | ||
err = kb.Update(n1, p1, getNewpass) | ||
require.NoError(t, err) | ||
// p2 is now the proper one! | ||
assertPassword(t, kb, n1, p2, p1) | ||
|
||
// exporting requires the proper name and passphrase | ||
_, err = kb.Export(n1 + ".notreal") | ||
require.NotNil(t, err) | ||
_, err = kb.Export(" " + n1) | ||
require.NotNil(t, err) | ||
_, err = kb.Export(n1 + " ") | ||
require.NotNil(t, err) | ||
_, err = kb.Export("") | ||
require.NotNil(t, err) | ||
exported, err := kb.Export(n1) | ||
require.Nil(t, err, "%+v", err) | ||
|
||
// import succeeds | ||
err = kb.Import(n2, exported) | ||
require.NoError(t, err) | ||
|
||
// second import fails | ||
err = kb.Import(n2, exported) | ||
require.NotNil(t, err) | ||
} | ||
|
||
// TestSeedPhrase verifies restoring from a seed phrase | ||
func TestLazySeedPhrase(t *testing.T) { | ||
dir, cleanup := tests.NewTestCaseDir(t) | ||
defer cleanup() | ||
kb := New("keybasename", dir) | ||
|
||
algo := Secp256k1 | ||
n1, n2 := "lost-key", "found-again" | ||
p1, p2 := "1234", "foobar" | ||
|
||
// make sure key works with initial password | ||
info, mnemonic, err := kb.CreateMnemonic(n1, English, p1, algo) | ||
require.Nil(t, err, "%+v", err) | ||
require.Equal(t, n1, info.GetName()) | ||
assert.NotEmpty(t, mnemonic) | ||
|
||
// now, let us delete this key | ||
err = kb.Delete(n1, p1, false) | ||
require.Nil(t, err, "%+v", err) | ||
_, err = kb.Get(n1) | ||
require.NotNil(t, err) | ||
|
||
// let us re-create it from the mnemonic-phrase | ||
params := *hd.NewFundraiserParams(0, 0) | ||
newInfo, err := kb.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params) | ||
require.NoError(t, err) | ||
require.Equal(t, n2, newInfo.GetName()) | ||
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) | ||
require.Equal(t, info.GetPubKey(), newInfo.GetPubKey()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package mintkey_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cosmos/cosmos-sdk/crypto/keys" | ||
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" | ||
"github.com/stretchr/testify/require" | ||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" | ||
"github.com/tendermint/tendermint/crypto/secp256k1" | ||
) | ||
|
||
func TestArmorUnarmorPrivKey(t *testing.T) { | ||
priv := secp256k1.GenPrivKey() | ||
armor := mintkey.EncryptArmorPrivKey(priv, "passphrase") | ||
_, err := mintkey.UnarmorDecryptPrivKey(armor, "wrongpassphrase") | ||
require.Error(t, err) | ||
decrypted, err := mintkey.UnarmorDecryptPrivKey(armor, "passphrase") | ||
require.NoError(t, err) | ||
require.True(t, priv.Equals(decrypted)) | ||
} | ||
|
||
func TestArmorUnarmorPubKey(t *testing.T) { | ||
// Select the encryption and storage for your cryptostore | ||
cstore := keys.NewInMemory() | ||
|
||
// Add keys and see they return in alphabetical order | ||
info, _, err := cstore.CreateMnemonic("Bob", keys.English, "passphrase", keys.Secp256k1) | ||
require.NoError(t, err) | ||
armor := mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes()) | ||
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor) | ||
require.NoError(t, err) | ||
pub, err := cryptoAmino.PubKeyFromBytes(pubBytes) | ||
require.NoError(t, err) | ||
require.True(t, pub.Equals(info.GetPubKey())) | ||
} |