From 53e19e3dfaac5a9242204175e7a2d66c81574e8b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 20 Jun 2017 18:35:16 +0200 Subject: [PATCH] Add codec to keys.Manager, recovery test passes --- keys/cryptostore/holder.go | 37 ++++++++++++++++++++++++++------ keys/cryptostore/holder_test.go | 38 +++++++++++++++++++++++++++++++++ keys/server/keys_test.go | 1 + keys/tx/multi_test.go | 1 + keys/tx/one_test.go | 1 + keys/tx/reader_test.go | 2 ++ keys/wordcodec.go | 10 +++++++++ 7 files changed, 83 insertions(+), 7 deletions(-) diff --git a/keys/cryptostore/holder.go b/keys/cryptostore/holder.go index 0b139a14e..6709687d6 100644 --- a/keys/cryptostore/holder.go +++ b/keys/cryptostore/holder.go @@ -1,19 +1,26 @@ package cryptostore -import keys "github.com/tendermint/go-crypto/keys" +import ( + "strings" + + crypto "github.com/tendermint/go-crypto" + keys "github.com/tendermint/go-crypto/keys" +) // Manager combines encyption and storage implementation to provide // a full-featured key manager type Manager struct { - es encryptedStorage + es encryptedStorage + codec keys.Codec } -func New(coder Encoder, store keys.Storage) Manager { +func New(coder Encoder, store keys.Storage, codec keys.Codec) Manager { return Manager{ es: encryptedStorage{ coder: coder, store: store, }, + codec: codec, } } @@ -39,13 +46,29 @@ func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error } key := gen.Generate() err = s.es.Put(name, passphrase, key) - // TODO - return info(name, key), "", err + if err != nil { + return keys.Info{}, "", err + } + seed, err := s.codec.BytesToWords(key.Bytes()) + phrase := strings.Join(seed, " ") + return info(name, key), phrase, err } func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) { - // TODO - return keys.Info{}, nil + words := strings.Split(strings.TrimSpace(seedphrase), " ") + data, err := s.codec.WordsToBytes(words) + if err != nil { + return keys.Info{}, err + } + + key, err := crypto.PrivKeyFromBytes(data) + if err != nil { + return keys.Info{}, err + } + + // d00d, it worked! create the bugger.... + err = s.es.Put(name, passphrase, key) + return info(name, key), err } // List loads the keys from the storage and enforces alphabetical order diff --git a/keys/cryptostore/holder_test.go b/keys/cryptostore/holder_test.go index 9ad98bb9c..c17eef24b 100644 --- a/keys/cryptostore/holder_test.go +++ b/keys/cryptostore/holder_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto/keys" "github.com/tendermint/go-crypto/keys/cryptostore" "github.com/tendermint/go-crypto/keys/storage/memstorage" ) @@ -18,6 +19,7 @@ func TestKeyManagement(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) algo := crypto.NameEd25519 @@ -154,6 +156,7 @@ func TestAdvancedKeyManagement(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) algo := crypto.NameSecp256k1 @@ -199,6 +202,41 @@ func TestAdvancedKeyManagement(t *testing.T) { assertPassword(assert, cstore, n2, p3, pt) } +// TestSeedPhrase verifies restoring from a seed phrase +func TestSeedPhrase(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + // make the storage with reasonable defaults + cstore := cryptostore.New( + cryptostore.SecretBox, + memstorage.New(), + keys.MustLoadCodec("english"), + ) + + algo := crypto.NameEd25519 + n1, n2 := "lost-key", "found-again" + p1, p2 := "1234", "foobar" + + // make sure key works with initial password + info, seed, err := cstore.Create(n1, p1, algo) + require.Nil(err, "%+v", err) + assert.Equal(n1, info.Name) + assert.NotEmpty(seed) + + // now, let us delete this key + err = cstore.Delete(n1, p1) + require.Nil(err, "%+v", err) + _, err = cstore.Get(n1) + require.NotNil(err) + + // let us re-create it from the seed-phrase + newInfo, err := cstore.Recover(n2, p2, seed) + require.Nil(err, "%+v", err) + assert.Equal(n2, newInfo.Name) + assert.Equal(info.Address, newInfo.Address) + assert.Equal(info.PubKey, newInfo.PubKey) +} + // func ExampleStore() { // // Select the encryption and storage for your cryptostore // cstore := cryptostore.New( diff --git a/keys/server/keys_test.go b/keys/server/keys_test.go index a0bca8bd3..2aa17753c 100644 --- a/keys/server/keys_test.go +++ b/keys/server/keys_test.go @@ -91,6 +91,7 @@ func setupServer() http.Handler { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) // build your http server diff --git a/keys/tx/multi_test.go b/keys/tx/multi_test.go index e9477c8cd..22fb0ed75 100644 --- a/keys/tx/multi_test.go +++ b/keys/tx/multi_test.go @@ -18,6 +18,7 @@ func TestMultiSig(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) n, p := "foo", "bar" n2, p2 := "other", "thing" diff --git a/keys/tx/one_test.go b/keys/tx/one_test.go index 0480f5a33..e0a3f1056 100644 --- a/keys/tx/one_test.go +++ b/keys/tx/one_test.go @@ -18,6 +18,7 @@ func TestOneSig(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) n, p := "foo", "bar" n2, p2 := "other", "thing" diff --git a/keys/tx/reader_test.go b/keys/tx/reader_test.go index 7c622bff9..f0561301f 100644 --- a/keys/tx/reader_test.go +++ b/keys/tx/reader_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto/keys" "github.com/tendermint/go-crypto/keys/cryptostore" "github.com/tendermint/go-crypto/keys/storage/memstorage" data "github.com/tendermint/go-wire/data" @@ -18,6 +19,7 @@ func TestReader(t *testing.T) { cstore := cryptostore.New( cryptostore.SecretBox, memstorage.New(), + keys.MustLoadCodec("english"), ) type sigs struct{ name, pass string } u := sigs{"alice", "1234"} diff --git a/keys/wordcodec.go b/keys/wordcodec.go index f66d1e983..ee1374644 100644 --- a/keys/wordcodec.go +++ b/keys/wordcodec.go @@ -40,6 +40,7 @@ func NewCodec(words []string) (codec *WordCodec, err error) { return res, nil } +// LoadCodec loads a pre-compiled language file func LoadCodec(bank string) (codec *WordCodec, err error) { words, err := loadBank(bank) if err != nil { @@ -48,6 +49,15 @@ func LoadCodec(bank string) (codec *WordCodec, err error) { return NewCodec(words) } +// MustLoadCodec panics if word bank is missing, only for tests +func MustLoadCodec(bank string) *WordCodec { + codec, err := LoadCodec(bank) + if err != nil { + panic(err) + } + return codec +} + // loadBank opens a wordlist file and returns all words inside func loadBank(bank string) ([]string, error) { filename := "keys/wordlist/" + bank + ".txt"