From a2583e278334c35b170352e467a741166474b6da Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Wed, 1 Nov 2017 16:38:11 -0500 Subject: [PATCH 1/9] Draft of suggested changes --- keys/cryptostore/docs.go | 25 --- keys/cryptostore/enc_storage.go | 49 ----- keys/cryptostore/encoder.go | 60 ------ keys/cryptostore/encoder_test.go | 105 ----------- keys/cryptostore/generator.go | 88 --------- keys/cryptostore/storage_test.go | 48 ----- keys/{cryptostore/holder.go => keybase.go} | 84 +++++---- .../holder_test.go => keybase_test.go} | 0 keys/storage/filestorage/main.go | 177 ------------------ keys/storage/filestorage/main_test.go | 106 ----------- keys/storage/memstorage/main.go | 68 ------- keys/storage/memstorage/main_test.go | 69 ------- keys/types.go | 124 +----------- 13 files changed, 52 insertions(+), 951 deletions(-) delete mode 100644 keys/cryptostore/docs.go delete mode 100644 keys/cryptostore/enc_storage.go delete mode 100644 keys/cryptostore/encoder.go delete mode 100644 keys/cryptostore/encoder_test.go delete mode 100644 keys/cryptostore/generator.go delete mode 100644 keys/cryptostore/storage_test.go rename keys/{cryptostore/holder.go => keybase.go} (61%) rename keys/{cryptostore/holder_test.go => keybase_test.go} (100%) delete mode 100644 keys/storage/filestorage/main.go delete mode 100644 keys/storage/filestorage/main_test.go delete mode 100644 keys/storage/memstorage/main.go delete mode 100644 keys/storage/memstorage/main_test.go diff --git a/keys/cryptostore/docs.go b/keys/cryptostore/docs.go deleted file mode 100644 index 5c66fba2d..000000000 --- a/keys/cryptostore/docs.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -package cryptostore maintains everything needed for doing public-key signing and -key management in software, based on the go-crypto library from tendermint. - -It is flexible, and allows the user to provide a key generation algorithm -(currently Ed25519 or Secp256k1), an encoder to passphrase-encrypt our keys -when storing them (currently SecretBox from NaCl), and a method to persist -the keys (currently FileStorage like ssh, or MemStorage for tests). -It should be relatively simple to write your own implementation of these -interfaces to match your specific security requirements. - -Note that the private keys are never exposed outside the package, and the -interface of Manager could be implemented by an HSM in the future for -enhanced security. It would require a completely different implementation -however. - -This Manager aims to implement Signer and KeyManager interfaces, along -with some extensions to allow importing/exporting keys and updating the -passphrase. - -Encoder and Generator implementations are currently in this package, -keys.Storage implementations exist as subpackages of -keys/storage -*/ -package cryptostore diff --git a/keys/cryptostore/enc_storage.go b/keys/cryptostore/enc_storage.go deleted file mode 100644 index daeb220b5..000000000 --- a/keys/cryptostore/enc_storage.go +++ /dev/null @@ -1,49 +0,0 @@ -package cryptostore - -import ( - crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-crypto/keys" -) - -// encryptedStorage needs passphrase to get private keys -type encryptedStorage struct { - coder Encoder - store keys.Storage -} - -func (es encryptedStorage) Put(name, pass string, key crypto.PrivKey) error { - secret, err := es.coder.Encrypt(key, pass) - if err != nil { - return err - } - - ki := info(name, key) - return es.store.Put(name, secret, ki) -} - -func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.Info, error) { - secret, info, err := es.store.Get(name) - if err != nil { - return crypto.PrivKey{}, info, err - } - key, err := es.coder.Decrypt(secret, pass) - return key, info, err -} - -func (es encryptedStorage) List() (keys.Infos, error) { - return es.store.List() -} - -func (es encryptedStorage) Delete(name string) error { - return es.store.Delete(name) -} - -// info hardcodes the encoding of keys -func info(name string, key crypto.PrivKey) keys.Info { - pub := key.PubKey() - return keys.Info{ - Name: name, - Address: pub.Address(), - PubKey: pub, - } -} diff --git a/keys/cryptostore/encoder.go b/keys/cryptostore/encoder.go deleted file mode 100644 index 31cbc2e54..000000000 --- a/keys/cryptostore/encoder.go +++ /dev/null @@ -1,60 +0,0 @@ -package cryptostore - -import ( - "github.com/pkg/errors" - crypto "github.com/tendermint/go-crypto" -) - -var ( - // SecretBox uses the algorithm from NaCL to store secrets securely - SecretBox Encoder = secretbox{} - // Noop doesn't do any encryption, should only be used in test code - Noop Encoder = noop{} -) - -// Encoder is used to encrypt any key with a passphrase for storage. -// -// This should use a well-designed symetric encryption algorithm -type Encoder interface { - Encrypt(key crypto.PrivKey, pass string) ([]byte, error) - Decrypt(data []byte, pass string) (crypto.PrivKey, error) -} - -func secret(passphrase string) []byte { - // TODO: Sha256(Bcrypt(passphrase)) - return crypto.Sha256([]byte(passphrase)) -} - -type secretbox struct{} - -func (e secretbox) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) { - if pass == "" { - return key.Bytes(), nil - } - s := secret(pass) - cipher := crypto.EncryptSymmetric(key.Bytes(), s) - return cipher, nil -} - -func (e secretbox) Decrypt(data []byte, pass string) (key crypto.PrivKey, err error) { - private := data - if pass != "" { - s := secret(pass) - private, err = crypto.DecryptSymmetric(data, s) - if err != nil { - return crypto.PrivKey{}, errors.Wrap(err, "Invalid Passphrase") - } - } - key, err = crypto.PrivKeyFromBytes(private) - return key, errors.Wrap(err, "Invalid Passphrase") -} - -type noop struct{} - -func (n noop) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) { - return key.Bytes(), nil -} - -func (n noop) Decrypt(data []byte, pass string) (crypto.PrivKey, error) { - return crypto.PrivKeyFromBytes(data) -} diff --git a/keys/cryptostore/encoder_test.go b/keys/cryptostore/encoder_test.go deleted file mode 100644 index 9a0757b73..000000000 --- a/keys/cryptostore/encoder_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package cryptostore_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - cmn "github.com/tendermint/tmlibs/common" - - "github.com/tendermint/go-crypto/keys/cryptostore" -) - -func TestNoopEncoder(t *testing.T) { - assert, require := assert.New(t), require.New(t) - noop := cryptostore.Noop - - key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16)) - require.NoError(err) - key2, err := cryptostore.GenSecp256k1.Generate(cmn.RandBytes(16)) - require.NoError(err) - - b, err := noop.Encrypt(key, "encode") - require.Nil(err) - assert.NotEmpty(b) - - b2, err := noop.Encrypt(key2, "encode") - require.Nil(err) - assert.NotEmpty(b2) - assert.NotEqual(b, b2) - - // note the decode with a different password works - not secure! - pk, err := noop.Decrypt(b, "decode") - require.Nil(err) - require.NotNil(pk) - assert.Equal(key, pk) - - pk2, err := noop.Decrypt(b2, "kggugougp") - require.Nil(err) - require.NotNil(pk2) - assert.Equal(key2, pk2) -} - -func TestSecretBox(t *testing.T) { - assert, require := assert.New(t), require.New(t) - enc := cryptostore.SecretBox - - key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16)) - require.NoError(err) - pass := "some-special-secret" - - b, err := enc.Encrypt(key, pass) - require.Nil(err) - assert.NotEmpty(b) - - // decoding with a different pass is an error - pk, err := enc.Decrypt(b, "decode") - require.NotNil(err) - require.True(pk.Empty()) - - // but decoding with the same passphrase gets us our key - pk, err = enc.Decrypt(b, pass) - require.Nil(err) - assert.Equal(key, pk) -} - -func TestSecretBoxNoPass(t *testing.T) { - assert, require := assert.New(t), require.New(t) - enc := cryptostore.SecretBox - - key, rerr := cryptostore.GenEd25519.Generate(cmn.RandBytes(16)) - require.NoError(rerr) - - cases := []struct { - encode string - decode string - valid bool - }{ - {"foo", "foo", true}, - {"foo", "food", false}, - {"", "", true}, - {"", "a", false}, - {"a", "", false}, - } - - for i, tc := range cases { - b, err := enc.Encrypt(key, tc.encode) - require.Nil(err, "%d: %+v", i, err) - assert.NotEmpty(b, "%d", i) - - pk, err := enc.Decrypt(b, tc.decode) - if tc.valid { - require.Nil(err, "%d: %+v", i, err) - assert.Equal(key, pk, "%d", i) - } else { - require.NotNil(err, "%d", i) - } - } - - // now let's make sure raw bytes also work... - b := key.Bytes() - pk, err := enc.Decrypt(b, "") - require.Nil(err, "%+v", err) - assert.Equal(key, pk) -} diff --git a/keys/cryptostore/generator.go b/keys/cryptostore/generator.go deleted file mode 100644 index 81277eda9..000000000 --- a/keys/cryptostore/generator.go +++ /dev/null @@ -1,88 +0,0 @@ -package cryptostore - -import ( - "github.com/pkg/errors" - crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/go-crypto/nano" -) - -var ( - // GenEd25519 produces Ed25519 private keys - GenEd25519 Generator = GenFunc(genEd25519) - // GenSecp256k1 produces Secp256k1 private keys - GenSecp256k1 Generator = GenFunc(genSecp256) - // GenLedgerEd25519 used Ed25519 keys stored on nano ledger s with cosmos app - GenLedgerEd25519 Generator = GenFunc(genLedgerEd25519) -) - -// Generator determines the type of private key the keystore creates -type Generator interface { - Generate(secret []byte) (crypto.PrivKey, error) -} - -// GenFunc is a helper to transform a function into a Generator -type GenFunc func(secret []byte) (crypto.PrivKey, error) - -func (f GenFunc) Generate(secret []byte) (crypto.PrivKey, error) { - return f(secret) -} - -func genEd25519(secret []byte) (crypto.PrivKey, error) { - key := crypto.GenPrivKeyEd25519FromSecret(secret).Wrap() - return key, nil -} - -func genSecp256(secret []byte) (crypto.PrivKey, error) { - key := crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap() - return key, nil -} - -// secret is completely ignored for the ledger... -// just for interface compatibility -func genLedgerEd25519(secret []byte) (crypto.PrivKey, error) { - return nano.NewPrivKeyLedgerEd25519Ed25519() -} - -type genInvalidByte struct { - typ byte -} - -func (g genInvalidByte) Generate(secret []byte) (crypto.PrivKey, error) { - err := errors.Errorf("Cannot generate keys for algorithm: %X", g.typ) - return crypto.PrivKey{}, err -} - -type genInvalidAlgo struct { - algo string -} - -func (g genInvalidAlgo) Generate(secret []byte) (crypto.PrivKey, error) { - err := errors.Errorf("Cannot generate keys for algorithm: %s", g.algo) - return crypto.PrivKey{}, err -} - -func getGenerator(algo string) Generator { - switch algo { - case crypto.NameEd25519: - return GenEd25519 - case crypto.NameSecp256k1: - return GenSecp256k1 - case nano.NameLedgerEd25519: - return GenLedgerEd25519 - default: - return genInvalidAlgo{algo} - } -} - -func getGeneratorByType(typ byte) Generator { - switch typ { - case crypto.TypeEd25519: - return GenEd25519 - case crypto.TypeSecp256k1: - return GenSecp256k1 - case nano.TypeLedgerEd25519: - return GenLedgerEd25519 - default: - return genInvalidByte{typ} - } -} diff --git a/keys/cryptostore/storage_test.go b/keys/cryptostore/storage_test.go deleted file mode 100644 index 6d475fdc9..000000000 --- a/keys/cryptostore/storage_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package cryptostore - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - crypto "github.com/tendermint/go-crypto" - cmn "github.com/tendermint/tmlibs/common" - - keys "github.com/tendermint/go-crypto/keys" -) - -func TestSortKeys(t *testing.T) { - assert := assert.New(t) - - gen := func() crypto.PrivKey { - key, _ := GenEd25519.Generate(cmn.RandBytes(16)) - return key - } - assert.NotEqual(gen(), gen()) - - // alphabetical order is n3, n1, n2 - n1, n2, n3 := "john", "mike", "alice" - infos := keys.Infos{ - info(n1, gen()), - info(n2, gen()), - info(n3, gen()), - } - - // make sure they are initialized unsorted - assert.Equal(n1, infos[0].Name) - assert.Equal(n2, infos[1].Name) - assert.Equal(n3, infos[2].Name) - - // now they are sorted - infos.Sort() - assert.Equal(n3, infos[0].Name) - assert.Equal(n1, infos[1].Name) - assert.Equal(n2, infos[2].Name) - - // make sure info put some real data there... - assert.NotEmpty(infos[0].PubKey) - assert.NotEmpty(infos[0].PubKey.Address()) - assert.NotEmpty(infos[1].PubKey) - assert.NotEmpty(infos[1].PubKey.Address()) - assert.NotEqual(infos[0].PubKey, infos[1].PubKey) -} diff --git a/keys/cryptostore/holder.go b/keys/keybase.go similarity index 61% rename from keys/cryptostore/holder.go rename to keys/keybase.go index 7e89dda38..df75b08b7 100644 --- a/keys/cryptostore/holder.go +++ b/keys/keybase.go @@ -4,47 +4,49 @@ import ( "strings" crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-crypto/keys" + dbm "github.com/tendermint/tmlibs/db" ) -// Manager combines encyption and storage implementation to provide +// XXX Lets use go-crypto/bcrypt and ascii encoding directly in here without +// further wrappers around a store or DB. +// Copy functions from: https://github.com/tendermint/mintkey/blob/master/cmd/mintkey/common.go +// +// dbKeybase combines encyption and storage implementation to provide // a full-featured key manager -type Manager struct { - es encryptedStorage - codec keys.Codec +type dbKeybase struct { + db dbm.DB + codec Codec } -func New(coder Encoder, store keys.Storage, codec keys.Codec) Manager { - return Manager{ +func New(db dbm.DB, codec Codec) dbKeybase { + return dbKeybase{ es: encryptedStorage{ + db: db, coder: coder, - store: store, }, codec: codec, } } -// assert Manager satisfies keys.Signer and keys.Manager interfaces -var _ keys.Signer = Manager{} -var _ keys.Manager = Manager{} +var _ Keybase = dbKeybase{} // Create adds a new key to the storage engine, returning error if // another key already stored under this name // // algo must be a supported go-crypto algorithm: ed25519, secp256k1 -func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error) { +func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) { // 128-bits are the all the randomness we can make use of secret := crypto.CRandBytes(16) gen := getGenerator(algo) key, err := gen.Generate(secret) if err != nil { - return keys.Info{}, "", err + return Info{}, "", err } - err = s.es.Put(name, passphrase, key) + err = kb.es.Put(name, passphrase, key) if err != nil { - return keys.Info{}, "", err + return Info{}, "", err } // we append the type byte to the serialized secret to help with recovery @@ -52,7 +54,7 @@ func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error typ := key.Bytes()[0] secret = append(secret, typ) - seed, err := s.codec.BytesToWords(secret) + seed, err := kb.codec.BytesToWords(secret) phrase := strings.Join(seed, " ") return info(name, key), phrase, err } @@ -63,11 +65,11 @@ func (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error // it under name, protected by passphrase. // // Result similar to New(), except it doesn't return the seed again... -func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) { +func (s dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { words := strings.Split(strings.TrimSpace(seedphrase), " ") - secret, err := s.codec.WordsToBytes(words) + secret, err := kb.codec.WordsToBytes(words) if err != nil { - return keys.Info{}, err + return Info{}, err } // secret is comprised of the actual secret with the type appended @@ -78,24 +80,24 @@ func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) gen := getGeneratorByType(typ) key, err := gen.Generate(secret) if err != nil { - return keys.Info{}, err + return Info{}, err } // d00d, it worked! create the bugger.... - err = s.es.Put(name, passphrase, key) + err = kb.es.Put(name, passphrase, key) return info(name, key), err } // List loads the keys from the storage and enforces alphabetical order -func (s Manager) List() (keys.Infos, error) { - res, err := s.es.List() +func (s dbKeybase) List() (Infos, error) { + res, err := kb.es.List() res.Sort() return res, err } // Get returns the public information about one key -func (s Manager) Get(name string) (keys.Info, error) { - _, info, err := s.es.store.Get(name) +func (s dbKeybase) Get(name string) (Info, error) { + _, info, err := kb.es.store.Get(name) return info, err } @@ -103,8 +105,8 @@ func (s Manager) Get(name string) (keys.Info, error) { // this public key // // If no key for this name, or the passphrase doesn't match, returns an error -func (s Manager) Sign(name, passphrase string, tx keys.Signable) error { - key, _, err := s.es.Get(name, passphrase) +func (s dbKeybase) Sign(name, passphrase string, msg []byte) error { + key, _, err := kb.es.Get(name, passphrase) if err != nil { return err } @@ -115,55 +117,55 @@ func (s Manager) Sign(name, passphrase string, tx keys.Signable) error { // Export decodes the private key with the current password, encodes // it with a secure one-time password and generates a sequence that can be -// Imported by another Manager +// Imported by another dbKeybase // // This is designed to copy from one device to another, or provide backups // during version updates. -func (s Manager) Export(name, oldpass, transferpass string) ([]byte, error) { - key, _, err := s.es.Get(name, oldpass) +func (s dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) { + key, _, err := kb.es.Get(name, oldpass) if err != nil { return nil, err } - res, err := s.es.coder.Encrypt(key, transferpass) + res, err := kb.es.coder.Encrypt(key, transferpass) return res, err } // Import accepts bytes generated by Export along with the same transferpass // If they are valid, it stores the password under the given name with the // new passphrase. -func (s Manager) Import(name, newpass, transferpass string, data []byte) error { - key, err := s.es.coder.Decrypt(data, transferpass) +func (s dbKeybase) Import(name, newpass, transferpass string, data []byte) error { + key, err := kb.es.coder.Decrypt(data, transferpass) if err != nil { return err } - return s.es.Put(name, newpass, key) + return kb.es.Put(name, newpass, key) } // Delete removes key forever, but we must present the // proper passphrase before deleting it (for security) -func (s Manager) Delete(name, passphrase string) error { +func (s dbKeybase) Delete(name, passphrase string) error { // verify we have the proper password before deleting - _, _, err := s.es.Get(name, passphrase) + _, _, err := kb.es.Get(name, passphrase) if err != nil { return err } - return s.es.Delete(name) + return kb.es.Delete(name) } // Update changes the passphrase with which a already stored key is encoded. // // oldpass must be the current passphrase used for encoding, newpass will be // the only valid passphrase from this time forward -func (s Manager) Update(name, oldpass, newpass string) error { - key, _, err := s.es.Get(name, oldpass) +func (s dbKeybase) Update(name, oldpass, newpass string) error { + key, _, err := kb.es.Get(name, oldpass) if err != nil { return err } // we must delete first, as Putting over an existing name returns an error - s.Delete(name, oldpass) + kb.Delete(name, oldpass) - return s.es.Put(name, newpass, key) + return kb.es.Put(name, newpass, key) } diff --git a/keys/cryptostore/holder_test.go b/keys/keybase_test.go similarity index 100% rename from keys/cryptostore/holder_test.go rename to keys/keybase_test.go diff --git a/keys/storage/filestorage/main.go b/keys/storage/filestorage/main.go deleted file mode 100644 index 1f9702995..000000000 --- a/keys/storage/filestorage/main.go +++ /dev/null @@ -1,177 +0,0 @@ -/* -package filestorage provides a secure on-disk storage of private keys and -metadata. Security is enforced by file and directory permissions, much -like standard ssh key storage. -*/ -package filestorage - -import ( - "fmt" - "io/ioutil" - "os" - "path" - "strings" - - "github.com/pkg/errors" - crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-crypto/keys" -) - -const ( - BlockType = "Tendermint Light Client" - - // PrivExt is the extension for private keys. - PrivExt = "tlc" - // PubExt is the extensions for public keys. - PubExt = "pub" - - keyPerm = os.FileMode(0600) - // pubPerm = os.FileMode(0644) - dirPerm = os.FileMode(0700) -) - -type FileStore struct { - keyDir string -} - -// New creates an instance of file-based key storage with tight permissions -// -// dir should be an absolute path of a directory owner by this user. It will -// be created if it doesn't exist already. -func New(dir string) FileStore { - err := os.MkdirAll(dir, dirPerm) - if err != nil { - panic(err) - } - return FileStore{dir} -} - -// assert FileStore satisfies keys.Storage -var _ keys.Storage = FileStore{} - -// Put creates two files, one with the public info as json, the other -// with the (encoded) private key as gpg ascii-armor style -func (s FileStore) Put(name string, key []byte, info keys.Info) error { - pub, priv := s.nameToPaths(name) - - // write public info - err := writeInfo(pub, info) - if err != nil { - return err - } - - // write private info - return write(priv, name, key) -} - -// Get loads the info and (encoded) private key from the directory -// It uses `name` to generate the filename, and returns an error if the -// files don't exist or are in the incorrect format -func (s FileStore) Get(name string) ([]byte, keys.Info, error) { - pub, priv := s.nameToPaths(name) - - info, err := readInfo(pub) - if err != nil { - return nil, info, err - } - - key, _, err := read(priv) - return key, info.Format(), err -} - -// List parses the key directory for public info and returns a list of -// Info for all keys located in this directory. -func (s FileStore) List() (keys.Infos, error) { - dir, err := os.Open(s.keyDir) - if err != nil { - return nil, errors.Wrap(err, "List Keys") - } - defer dir.Close() - - names, err := dir.Readdirnames(0) - if err != nil { - return nil, errors.Wrap(err, "List Keys") - } - - // filter names for .pub ending and load them one by one - // half the files is a good guess for pre-allocating the slice - infos := make([]keys.Info, 0, len(names)/2) - for _, name := range names { - if strings.HasSuffix(name, PubExt) { - p := path.Join(s.keyDir, name) - info, err := readInfo(p) - if err != nil { - return nil, err - } - infos = append(infos, info.Format()) - } - } - - return infos, nil -} - -// Delete permanently removes the public and private info for the named key -// The calling function should provide some security checks first. -func (s FileStore) Delete(name string) error { - pub, priv := s.nameToPaths(name) - err := os.Remove(priv) - if err != nil { - return errors.Wrap(err, "Deleting Private Key") - } - err = os.Remove(pub) - return errors.Wrap(err, "Deleting Public Key") -} - -func (s FileStore) nameToPaths(name string) (pub, priv string) { - privName := fmt.Sprintf("%s.%s", name, PrivExt) - pubName := fmt.Sprintf("%s.%s", name, PubExt) - return path.Join(s.keyDir, pubName), path.Join(s.keyDir, privName) -} - -func writeInfo(path string, info keys.Info) error { - return write(path, info.Name, info.PubKey.Bytes()) -} - -func readInfo(path string) (info keys.Info, err error) { - var data []byte - data, info.Name, err = read(path) - if err != nil { - return - } - pk, err := crypto.PubKeyFromBytes(data) - info.PubKey = pk - return -} - -func read(path string) ([]byte, string, error) { - f, err := os.Open(path) - if err != nil { - return nil, "", errors.Wrap(err, "Reading data") - } - defer f.Close() - - d, err := ioutil.ReadAll(f) - if err != nil { - return nil, "", errors.Wrap(err, "Reading data") - } - block, headers, key, err := crypto.DecodeArmor(string(d)) - if err != nil { - return nil, "", errors.Wrap(err, "Invalid Armor") - } - if block != BlockType { - return nil, "", errors.Errorf("Unknown key type: %s", block) - } - return key, headers["name"], nil -} - -func write(path, name string, key []byte) error { - f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, keyPerm) - if err != nil { - return errors.Wrap(err, "Writing data") - } - defer f.Close() - headers := map[string]string{"name": name} - text := crypto.EncodeArmor(BlockType, headers, key) - _, err = f.WriteString(text) - return errors.Wrap(err, "Writing data") -} diff --git a/keys/storage/filestorage/main_test.go b/keys/storage/filestorage/main_test.go deleted file mode 100644 index 28c950c2c..000000000 --- a/keys/storage/filestorage/main_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package filestorage - -import ( - "io/ioutil" - "os" - "path" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-crypto/keys" -) - -func TestBasicCRUD(t *testing.T) { - assert, require := assert.New(t), require.New(t) - - dir, err := ioutil.TempDir("", "filestorage-test") - assert.Nil(err) - defer os.RemoveAll(dir) - store := New(dir) - - name := "bar" - key := []byte("secret-key-here") - pubkey := crypto.GenPrivKeyEd25519().PubKey() - info := keys.Info{ - Name: name, - PubKey: pubkey.Wrap(), - } - - // No data: Get and Delete return nothing - _, _, err = store.Get(name) - assert.NotNil(err) - err = store.Delete(name) - assert.NotNil(err) - // List returns empty list - l, err := store.List() - assert.Nil(err) - assert.Empty(l) - - // Putting the key in the store must work - err = store.Put(name, key, info) - assert.Nil(err) - // But a second time is a failure - err = store.Put(name, key, info) - assert.NotNil(err) - - // Now, we can get and list properly - k, i, err := store.Get(name) - require.Nil(err, "%+v", err) - assert.Equal(key, k) - assert.Equal(info.Name, i.Name) - assert.Equal(info.PubKey, i.PubKey) - assert.NotEmpty(i.Address) - l, err = store.List() - require.Nil(err, "%+v", err) - assert.Equal(1, len(l)) - assert.Equal(i, l[0]) - - // querying a non-existent key fails - _, _, err = store.Get("badname") - assert.NotNil(err) - - // We can only delete once - err = store.Delete(name) - assert.Nil(err) - err = store.Delete(name) - assert.NotNil(err) - - // and then Get and List don't work - _, _, err = store.Get(name) - assert.NotNil(err) - // List returns empty list - l, err = store.List() - assert.Nil(err) - assert.Empty(l) -} - -func TestDirectoryHandling(t *testing.T) { - assert, require := assert.New(t), require.New(t) - - // prepare a temp dir and make sure it is not there - newDir := path.Join(os.TempDir(), "file-test-dir") - _, err := os.Open(newDir) - assert.True(os.IsNotExist(err)) - defer os.RemoveAll(newDir) - - // now, check with two levels deep.... - parentDir := path.Join(os.TempDir(), "missing-dir") - nestedDir := path.Join(parentDir, "lots", "of", "levels", "here") - _, err = os.Open(parentDir) - assert.True(os.IsNotExist(err)) - defer os.RemoveAll(parentDir) - - // create a new storage, and verify it creates the directory with good permissions - for _, dir := range []string{newDir, nestedDir, newDir} { - New(dir) - d, err := os.Open(dir) - require.Nil(err) - defer d.Close() - - stat, err := d.Stat() - require.Nil(err) - assert.Equal(dirPerm, stat.Mode()&os.ModePerm) - } -} diff --git a/keys/storage/memstorage/main.go b/keys/storage/memstorage/main.go deleted file mode 100644 index a988fe0ff..000000000 --- a/keys/storage/memstorage/main.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -package memstorage provides a simple in-memory key store designed for -use in test cases, particularly to isolate them from the filesystem, -concurrency, and cleanup issues. -*/ -package memstorage - -import ( - "github.com/pkg/errors" - keys "github.com/tendermint/go-crypto/keys" -) - -type data struct { - info keys.Info - key []byte -} - -type MemStore map[string]data - -// New creates an instance of file-based key storage with tight permissions -func New() MemStore { - return MemStore{} -} - -// assert MemStore satisfies keys.Storage -var _ keys.Storage = MemStore{} - -// Put adds the given key, returns an error if it another key -// is already stored under this name -func (s MemStore) Put(name string, key []byte, info keys.Info) error { - if _, ok := s[name]; ok { - return errors.Errorf("Key named '%s' already exists", name) - } - s[name] = data{info, key} - return nil -} - -// Get returns the key stored under the name, or returns an error if not present -func (s MemStore) Get(name string) ([]byte, keys.Info, error) { - var err error - d, ok := s[name] - if !ok { - err = errors.Errorf("Key named '%s' doesn't exist", name) - } - return d.key, d.info.Format(), err -} - -// List returns the public info of all keys in the MemStore in unsorted order -func (s MemStore) List() (keys.Infos, error) { - res := make([]keys.Info, len(s)) - i := 0 - for _, d := range s { - res[i] = d.info.Format() - i++ - } - return res, nil -} - -// Delete removes the named key from the MemStore, raising an error if it -// wasn't present yet. -func (s MemStore) Delete(name string) error { - _, ok := s[name] - if !ok { - return errors.Errorf("Key named '%s' doesn't exist", name) - } - delete(s, name) - return nil -} diff --git a/keys/storage/memstorage/main_test.go b/keys/storage/memstorage/main_test.go deleted file mode 100644 index feccb387f..000000000 --- a/keys/storage/memstorage/main_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package memstorage - -import ( - "testing" - - "github.com/stretchr/testify/assert" - crypto "github.com/tendermint/go-crypto" - keys "github.com/tendermint/go-crypto/keys" -) - -func TestBasicCRUD(t *testing.T) { - assert := assert.New(t) - store := New() - - name := "foo" - key := []byte("secret-key-here") - pubkey := crypto.GenPrivKeyEd25519().PubKey() - info := keys.Info{ - Name: name, - PubKey: pubkey, - } - - // No data: Get and Delete return nothing - _, _, err := store.Get(name) - assert.NotNil(err) - err = store.Delete(name) - assert.NotNil(err) - // List returns empty list - l, err := store.List() - assert.Nil(err) - assert.Empty(l) - - // Putting the key in the store must work - err = store.Put(name, key, info) - assert.Nil(err) - // But a second time is a failure - err = store.Put(name, key, info) - assert.NotNil(err) - - // Now, we can get and list properly - k, i, err := store.Get(name) - assert.Nil(err) - assert.Equal(key, k) - assert.Equal(info.Name, i.Name) - assert.Equal(info.PubKey, i.PubKey) - assert.NotEmpty(i.Address) - l, err = store.List() - assert.Nil(err) - assert.Equal(1, len(l)) - assert.Equal(i, l[0]) - - // querying a non-existent key fails - _, _, err = store.Get("badname") - assert.NotNil(err) - - // We can only delete once - err = store.Delete(name) - assert.Nil(err) - err = store.Delete(name) - assert.NotNil(err) - - // and then Get and List don't work - _, _, err = store.Get(name) - assert.NotNil(err) - // List returns empty list - l, err = store.List() - assert.Nil(err) - assert.Empty(l) -} diff --git a/keys/types.go b/keys/types.go index b3d3a374a..534da0720 100644 --- a/keys/types.go +++ b/keys/types.go @@ -1,134 +1,28 @@ package keys import ( - "fmt" "sort" crypto "github.com/tendermint/go-crypto" - wire "github.com/tendermint/go-wire" data "github.com/tendermint/go-wire/data" ) -// Storage has many implementation, based on security and sharing requirements -// like disk-backed, mem-backed, vault, db, etc. -type Storage interface { - Put(name string, key []byte, info Info) error - Get(name string) (key []byte, info Info, err error) - List() (Infos, error) - Delete(name string) error -} - // Info is the public information about a key type Info struct { - Name string `json:"name"` - Address data.Bytes `json:"address"` - PubKey crypto.PubKey `json:"pubkey"` -} - -func (i *Info) Format() Info { - if !i.PubKey.Empty() { - i.Address = i.PubKey.Address() - } - return *i -} - -// Infos is a wrapper to allows alphabetical sorting of the keys -type Infos []Info - -func (k Infos) Len() int { return len(k) } -func (k Infos) Less(i, j int) bool { return k[i].Name < k[j].Name } -func (k Infos) Swap(i, j int) { k[i], k[j] = k[j], k[i] } -func (k Infos) Sort() { - if k != nil { - sort.Sort(k) - } -} - -// Signable represents any transaction we wish to send to tendermint core -// These methods allow us to sign arbitrary Tx with the KeyStore -type Signable interface { - // SignBytes is the immutable data, which needs to be signed - SignBytes() []byte - - // Sign will add a signature and pubkey. - // - // Depending on the Signable, one may be able to call this multiple times for multisig - // Returns error if called with invalid data or too many times - Sign(pubkey crypto.PubKey, sig crypto.Signature) error - - // Signers will return the public key(s) that signed if the signature - // is valid, or an error if there is any issue with the signature, - // including if there are no signatures - Signers() ([]crypto.PubKey, error) - - // TxBytes returns the transaction data as well as all signatures - // It should return an error if Sign was never called - TxBytes() ([]byte, error) -} - -// Signer allows one to use a keystore to sign transactions -type Signer interface { - Sign(name, passphrase string, tx Signable) error + Name string `json:"name"` + PubKey crypto.PubKey `json:"pubkey"` } -// Manager allows simple CRUD on a keystore, as an aid to signing -type Manager interface { - Signer - // Create also returns a seed phrase for cold-storage - Create(name, passphrase, algo string) (Info, string, error) - // Recover takes a seedphrase and loads in the private key +// Keybase allows simple CRUD on a keystore, as an aid to signing +type Keybase interface { + // Sign some bytes + Sign(name, passphrase string, msg []byte) (crypto.Signature, error) + // Create a new keypair + Create(name, passphrase, algo string) (_ Info, seedphrase string, _ error) + // Recover takes a seedphrase and loads in the key Recover(name, passphrase, seedphrase string) (Info, error) List() (Infos, error) Get(name string) (Info, error) Update(name, oldpass, newpass string) error Delete(name, passphrase string) error } - -/**** MockSignable allows us to view data ***/ - -// MockSignable lets us wrap arbitrary data with a go-crypto signature -type MockSignable struct { - Data []byte - PubKey crypto.PubKey - Signature crypto.Signature -} - -var _ Signable = &MockSignable{} - -// NewMockSignable sets the data to sign -func NewMockSignable(data []byte) *MockSignable { - return &MockSignable{Data: data} -} - -// TxBytes returns the full data with signatures -func (s *MockSignable) TxBytes() ([]byte, error) { - return wire.BinaryBytes(s), nil -} - -// SignBytes returns the original data passed into `NewSig` -func (s *MockSignable) SignBytes() []byte { - return s.Data -} - -// Sign will add a signature and pubkey. -// -// Depending on the Signable, one may be able to call this multiple times for multisig -// Returns error if called with invalid data or too many times -func (s *MockSignable) Sign(pubkey crypto.PubKey, sig crypto.Signature) error { - s.PubKey = pubkey - s.Signature = sig - return nil -} - -// Signers will return the public key(s) that signed if the signature -// is valid, or an error if there is any issue with the signature, -// including if there are no signatures -func (s *MockSignable) Signers() ([]crypto.PubKey, error) { - if s.PubKey.Empty() { - return nil, fmt.Errorf("no signers") - } - if !s.PubKey.VerifyBytes(s.SignBytes(), s.Signature) { - return nil, fmt.Errorf("invalid signature") - } - return []crypto.PubKey{s.PubKey}, nil -} From ac841a6124a65cdb4d51e857f2fa7e6bfe7e3cae Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 2 Nov 2017 14:09:59 -0500 Subject: [PATCH 2/9] Fixed imports --- glide.lock | 30 ++++++++++++++++++++++++----- glide.yaml | 7 +++++++ keys/keybase.go | 2 +- keys/keybase_test.go | 45 ++++++++++++++++++-------------------------- 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/glide.lock b/glide.lock index 096ec5c15..c1400b3f9 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 6e06a42eafe0aeff112cee86aef6b2cab0e2f62c2e6bfccfb629aa22f6b62773 -updated: 2017-10-27T18:45:18.350198941+02:00 +hash: 86d02329c537ccb46ccb062918f0cb9e839f2cf894dfd57223c3ea4de14266db +updated: 2017-11-02T14:09:11.208038182-05:00 imports: - name: github.com/btcsuite/btcd version: c7588cbf7690cd9f047a28efa2dcd8f2435a4e5e @@ -29,29 +29,49 @@ imports: version: 71201497bace774495daed26a3874fd339e0b538 - name: github.com/go-stack/stack version: 817915b46b97fd7bb80e8ab6b69f01a53ac3eebf +- name: github.com/golang/snappy + version: 553a641470496b2327abcac10b36396bd98e45c9 - name: github.com/howeyc/crc16 version: 96a97a1abb579c7ff1a8ffa77f2e72d1c314b57f +- name: github.com/jmhodges/levigo + version: c42d9e0ca023e2198120196f842701bb4c55d7b9 - name: github.com/kr/logfmt version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 - name: github.com/pkg/errors version: 645ef00459ed84a119197bfb8d8205042c6df63d +- name: github.com/syndtr/goleveldb + version: b89cc31ef7977104127d34c1bd31ebd1a9db2199 + subpackages: + - leveldb + - leveldb/cache + - leveldb/comparer + - leveldb/errors + - leveldb/filter + - leveldb/iterator + - leveldb/journal + - leveldb/memdb + - leveldb/opt + - leveldb/storage + - leveldb/table + - leveldb/util - name: github.com/tendermint/ed25519 version: d8387025d2b9d158cf4efb07e7ebf814bcce2057 subpackages: - edwards25519 - extra25519 - name: github.com/tendermint/go-wire - version: 8ee84b5b2581530168daf66fc89c548d27403c57 + version: 2baffcb6b690057568bc90ef1d457efb150b979a subpackages: - data - data/base58 - name: github.com/tendermint/tmlibs - version: 092eb701c7276907cdbed258750e22ce895b6735 + version: d9525c0fb671204450b160807480e1263053fb20 subpackages: - common + - db - log - name: golang.org/x/crypto - version: edd5e9b0879d13ee6970a50153d85b8fec9f7686 + version: bd6f299fb381e4c3393d1c4b1f0b94f5e77650c8 subpackages: - bcrypt - blowfish diff --git a/glide.yaml b/glide.yaml index 2f3e72471..a0b6650fa 100644 --- a/glide.yaml +++ b/glide.yaml @@ -6,6 +6,13 @@ import: - package: github.com/btcsuite/btcutil subpackages: - base58 +- package: github.com/syndtr/goleveldb + subpackages: + - leveldb + - leveldb/errors + - leveldb/iterator + - leveldb/opt + - leveldb/util - package: github.com/tendermint/ed25519 subpackages: - extra25519 diff --git a/keys/keybase.go b/keys/keybase.go index df75b08b7..54827d3a9 100644 --- a/keys/keybase.go +++ b/keys/keybase.go @@ -1,4 +1,4 @@ -package cryptostore +package keys import ( "strings" diff --git a/keys/keybase_test.go b/keys/keybase_test.go index a9056f0de..a57284e9c 100644 --- a/keys/keybase_test.go +++ b/keys/keybase_test.go @@ -1,4 +1,4 @@ -package cryptostore_test +package keys_test import ( "bytes" @@ -10,11 +10,10 @@ import ( "github.com/stretchr/testify/require" cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" 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" "github.com/tendermint/go-crypto/nano" ) @@ -23,9 +22,8 @@ func TestKeyManagement(t *testing.T) { assert, require := assert.New(t), require.New(t) // make the storage with reasonable defaults - cstore := cryptostore.New( - cryptostore.SecretBox, - memstorage.New(), + cstore := keys.New( + dbm.NewMemDB(), keys.MustLoadCodec("english"), ) @@ -92,9 +90,8 @@ func TestSignVerify(t *testing.T) { assert, require := assert.New(t), require.New(t) // make the storage with reasonable defaults - cstore := cryptostore.New( - cryptostore.SecretBox, - memstorage.New(), + cstore := keys.New( + dbm.NewMemDB(), keys.MustLoadCodec("english"), ) algo := crypto.NameSecp256k1 @@ -164,9 +161,8 @@ func TestSignWithLedger(t *testing.T) { } // make the storage with reasonable defaults - cstore := cryptostore.New( - cryptostore.SecretBox, - memstorage.New(), + cstore := keys.New( + dbm.NewMemDB(), keys.MustLoadCodec("english"), ) n := "nano-s" @@ -206,7 +202,7 @@ func TestSignWithLedger(t *testing.T) { assert.False(key.VerifyBytes(d1, s2.Signature)) } -func assertPassword(assert *assert.Assertions, cstore cryptostore.Manager, name, pass, badpass string) { +func assertPassword(assert *assert.Assertions, cstore keys.KeyBase, name, pass, badpass string) { err := cstore.Update(name, badpass, pass) assert.NotNil(err) err = cstore.Update(name, pass, pass) @@ -218,13 +214,12 @@ func TestImportUnencrypted(t *testing.T) { require := require.New(t) // make the storage with reasonable defaults - cstore := cryptostore.New( - cryptostore.SecretBox, - memstorage.New(), + cstore := keys.New( + dbm.NewMemDB(), keys.MustLoadCodec("english"), ) - key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16)) + key, err := keys.GenEd25519.Generate(cmn.RandBytes(16)) require.NoError(err) addr := key.PubKey().Address() @@ -246,9 +241,8 @@ func TestAdvancedKeyManagement(t *testing.T) { assert, require := assert.New(t), require.New(t) // make the storage with reasonable defaults - cstore := cryptostore.New( - cryptostore.SecretBox, - memstorage.New(), + cstore := keys.New( + dbm.NewMemDB(), keys.MustLoadCodec("english"), ) @@ -290,9 +284,8 @@ 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(), + cstore := keys.New( + dbm.NewMemDB(), keys.MustLoadCodec("english"), ) @@ -322,10 +315,8 @@ func TestSeedPhrase(t *testing.T) { func ExampleNew() { // Select the encryption and storage for your cryptostore - cstore := cryptostore.New( - cryptostore.SecretBox, - // Note: use filestorage.New(dir) for real data - memstorage.New(), + cstore := keys.New( + dbm.NewMemDB(), keys.MustLoadCodec("english"), ) ed := crypto.NameEd25519 From 946c9c573e3615fec3473288832a84c4c85946e4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 2 Nov 2017 16:31:29 -0500 Subject: [PATCH 3/9] Handle generating keys --- keys/keybase.go | 46 +++++++++++++++++++++++++++++++++++++--------- keys/types.go | 7 ++----- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/keys/keybase.go b/keys/keybase.go index 54827d3a9..bdffb9dce 100644 --- a/keys/keybase.go +++ b/keys/keybase.go @@ -3,8 +3,11 @@ package keys import ( "strings" + "github.com/pkg/errors" crypto "github.com/tendermint/go-crypto" dbm "github.com/tendermint/tmlibs/db" + + "github.com/tendermint/go-crypto/nano" ) // XXX Lets use go-crypto/bcrypt and ascii encoding directly in here without @@ -37,9 +40,7 @@ var _ Keybase = dbKeybase{} func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) { // 128-bits are the all the randomness we can make use of secret := crypto.CRandBytes(16) - gen := getGenerator(algo) - - key, err := gen.Generate(secret) + key, err := generate(algo, secret) if err != nil { return Info{}, "", err } @@ -77,8 +78,7 @@ func (s dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { l := len(secret) secret, typ := secret[:l-1], secret[l-1] - gen := getGeneratorByType(typ) - key, err := gen.Generate(secret) + key, err := generateByType(typ, secret) if err != nil { return Info{}, err } @@ -89,9 +89,9 @@ func (s dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { } // List loads the keys from the storage and enforces alphabetical order -func (s dbKeybase) List() (Infos, error) { - res, err := kb.es.List() - res.Sort() +func (s dbKeybase) List() ([]Info, error) { + res, err := s.es.List() + sort.SortSlice(res) return res, err } @@ -105,7 +105,7 @@ func (s dbKeybase) Get(name string) (Info, error) { // this public key // // If no key for this name, or the passphrase doesn't match, returns an error -func (s dbKeybase) Sign(name, passphrase string, msg []byte) error { +func (s dbKeybase) Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error) { key, _, err := kb.es.Get(name, passphrase) if err != nil { return err @@ -169,3 +169,31 @@ func (s dbKeybase) Update(name, oldpass, newpass string) error { return kb.es.Put(name, newpass, key) } + +func generate(algo string, secret []byte) (crypto.PrivKey, error) { + switch algo { + case crypto.NameEd25519: + return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil + case crypto.NameSecp256k1: + return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil + case nano.NameLedgerEd25519: + return nano.NewPrivKeyLedgerEd25519Ed25519() + default: + err := errors.Errorf("Cannot generate keys for algorithm: %s", algo) + return crypto.PrivKey{}, err + } +} + +func generateByType(typ byte, secret []byte) (crypto.PrivKey, error) { + switch typ { + case crypto.TypeEd25519: + return generate(crypto.NameEd25519, secret) + case crypto.TypeSecp256k1: + return generate(crypto.NameSecp256k1, secret) + case nano.TypeLedgerEd25519: + return generate(nano.NameLedgerEd25519, secret) + default: + err := errors.Errorf("Cannot generate keys for algorithm: %X", typ) + return crypto.PrivKey{}, err + } +} diff --git a/keys/types.go b/keys/types.go index 534da0720..110cd3ded 100644 --- a/keys/types.go +++ b/keys/types.go @@ -1,10 +1,7 @@ package keys import ( - "sort" - crypto "github.com/tendermint/go-crypto" - data "github.com/tendermint/go-wire/data" ) // Info is the public information about a key @@ -16,12 +13,12 @@ type Info struct { // Keybase allows simple CRUD on a keystore, as an aid to signing type Keybase interface { // Sign some bytes - Sign(name, passphrase string, msg []byte) (crypto.Signature, error) + Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error) // Create a new keypair Create(name, passphrase, algo string) (_ Info, seedphrase string, _ error) // Recover takes a seedphrase and loads in the key Recover(name, passphrase, seedphrase string) (Info, error) - List() (Infos, error) + List() ([]Info, error) Get(name string) (Info, error) Update(name, oldpass, newpass string) error Delete(name, passphrase string) error From 9ef978c5ecb32c22db58a91c46f78239c7a8c588 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 2 Nov 2017 16:46:10 -0500 Subject: [PATCH 4/9] Fix errors except for es missing --- keys/keybase.go | 59 +++++++++++++++++++++++++++++++++---------------- keys/types.go | 7 ++++++ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/keys/keybase.go b/keys/keybase.go index bdffb9dce..5fa32635e 100644 --- a/keys/keybase.go +++ b/keys/keybase.go @@ -1,6 +1,7 @@ package keys import ( + "sort" "strings" "github.com/pkg/errors" @@ -23,10 +24,7 @@ type dbKeybase struct { func New(db dbm.DB, codec Codec) dbKeybase { return dbKeybase{ - es: encryptedStorage{ - db: db, - coder: coder, - }, + db: db, codec: codec, } } @@ -66,7 +64,7 @@ func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) // it under name, protected by passphrase. // // Result similar to New(), except it doesn't return the seed again... -func (s dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { +func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { words := strings.Split(strings.TrimSpace(seedphrase), " ") secret, err := kb.codec.WordsToBytes(words) if err != nil { @@ -89,14 +87,14 @@ func (s dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { } // List loads the keys from the storage and enforces alphabetical order -func (s dbKeybase) List() ([]Info, error) { - res, err := s.es.List() - sort.SortSlice(res) +func (kb dbKeybase) List() ([]Info, error) { + res, err := kb.es.List() + sort.Slice(res, func(a, b int) bool { return res[a].Name < res[b].Name }) return res, err } // Get returns the public information about one key -func (s dbKeybase) Get(name string) (Info, error) { +func (kb dbKeybase) Get(name string) (Info, error) { _, info, err := kb.es.store.Get(name) return info, err } @@ -105,14 +103,15 @@ func (s dbKeybase) Get(name string) (Info, error) { // this public key // // If no key for this name, or the passphrase doesn't match, returns an error -func (s dbKeybase) Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error) { - key, _, err := kb.es.Get(name, passphrase) +func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pk crypto.PubKey, err error) { + var key crypto.PrivKey + key, _, err = kb.es.Get(name, passphrase) if err != nil { - return err + return } - sig := key.Sign(tx.SignBytes()) - pubkey := key.PubKey() - return tx.Sign(pubkey, sig) + sig = key.Sign(msg) + pk = key.PubKey() + return } // Export decodes the private key with the current password, encodes @@ -121,7 +120,7 @@ func (s dbKeybase) Sign(name, passphrase string, msg []byte) (crypto.Signature, // // This is designed to copy from one device to another, or provide backups // during version updates. -func (s dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) { +func (kb dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) { key, _, err := kb.es.Get(name, oldpass) if err != nil { return nil, err @@ -134,7 +133,7 @@ func (s dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) { // Import accepts bytes generated by Export along with the same transferpass // If they are valid, it stores the password under the given name with the // new passphrase. -func (s dbKeybase) Import(name, newpass, transferpass string, data []byte) error { +func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) error { key, err := kb.es.coder.Decrypt(data, transferpass) if err != nil { return err @@ -145,7 +144,7 @@ func (s dbKeybase) Import(name, newpass, transferpass string, data []byte) error // Delete removes key forever, but we must present the // proper passphrase before deleting it (for security) -func (s dbKeybase) Delete(name, passphrase string) error { +func (kb dbKeybase) Delete(name, passphrase string) error { // verify we have the proper password before deleting _, _, err := kb.es.Get(name, passphrase) if err != nil { @@ -158,7 +157,7 @@ func (s dbKeybase) Delete(name, passphrase string) error { // // oldpass must be the current passphrase used for encoding, newpass will be // the only valid passphrase from this time forward -func (s dbKeybase) Update(name, oldpass, newpass string) error { +func (kb dbKeybase) Update(name, oldpass, newpass string) error { key, _, err := kb.es.Get(name, oldpass) if err != nil { return err @@ -197,3 +196,25 @@ func generateByType(typ byte, secret []byte) (crypto.PrivKey, error) { return crypto.PrivKey{}, err } } + +func encrypt(key crypto.PrivKey, pass string) ([]byte, error) { + if pass == "" { + return key.Bytes(), nil + } + s := secret(pass) + cipher := crypto.EncryptSymmetric(key.Bytes(), s) + return cipher, nil +} + +func decrypt(data []byte, pass string) (key crypto.PrivKey, err error) { + private := data + if pass != "" { + s := secret(pass) + private, err = crypto.DecryptSymmetric(data, s) + if err != nil { + return crypto.PrivKey{}, errors.Wrap(err, "Invalid Passphrase") + } + } + key, err = crypto.PrivKeyFromBytes(private) + return key, errors.Wrap(err, "Invalid Passphrase") +} diff --git a/keys/types.go b/keys/types.go index 110cd3ded..b126f7267 100644 --- a/keys/types.go +++ b/keys/types.go @@ -10,6 +10,13 @@ type Info struct { PubKey crypto.PubKey `json:"pubkey"` } +func info(name string, privKey crypto.PrivKey) Info { + return Info{ + Name: name, + PubKey: privKey.PubKey(), + } +} + // Keybase allows simple CRUD on a keystore, as an aid to signing type Keybase interface { // Sign some bytes From 4173d1031e6a7361392fb80e46f0cc5469cb4499 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 2 Nov 2017 18:32:12 -0500 Subject: [PATCH 5/9] go build compiles --- keys/keybase.go | 117 +++++++++++++++++++++++++++++------------------- keys/mintkey.go | 73 ++++++++++++++++++++++++++++++ keys/types.go | 11 +++++ 3 files changed, 156 insertions(+), 45 deletions(-) create mode 100644 keys/mintkey.go diff --git a/keys/keybase.go b/keys/keybase.go index 5fa32635e..00de1ad55 100644 --- a/keys/keybase.go +++ b/keys/keybase.go @@ -1,7 +1,7 @@ package keys import ( - "sort" + "fmt" "strings" "github.com/pkg/errors" @@ -43,10 +43,7 @@ func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) return Info{}, "", err } - err = kb.es.Put(name, passphrase, key) - if err != nil { - return Info{}, "", err - } + public := kb.writeKey(key, name, passphrase) // we append the type byte to the serialized secret to help with recovery // ie [secret] = [secret] + [type] @@ -55,7 +52,7 @@ func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) seed, err := kb.codec.BytesToWords(secret) phrase := strings.Join(seed, " ") - return info(name, key), phrase, err + return public, phrase, err } // Recover takes a seed phrase and tries to recover the private key. @@ -82,21 +79,31 @@ func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { } // d00d, it worked! create the bugger.... - err = kb.es.Put(name, passphrase, key) - return info(name, key), err + public := kb.writeKey(key, name, passphrase) + return public, err } // List loads the keys from the storage and enforces alphabetical order func (kb dbKeybase) List() ([]Info, error) { - res, err := kb.es.List() - sort.Slice(res, func(a, b int) bool { return res[a].Name < res[b].Name }) - return res, err + var res []Info + var more = true + for iter := kb.db.Iterator(); more; more = iter.Next() { + key := iter.Key() + if isPub(key) { + info, err := readInfo(iter.Value()) + if err != nil { + return nil, err + } + res = append(res, info) + } + } + return res, nil } // Get returns the public information about one key func (kb dbKeybase) Get(name string) (Info, error) { - _, info, err := kb.es.store.Get(name) - return info, err + bs := kb.db.Get(pubName(name)) + return readInfo(bs) } // Sign will modify the Signable in order to attach a valid signature with @@ -105,10 +112,12 @@ func (kb dbKeybase) Get(name string) (Info, error) { // If no key for this name, or the passphrase doesn't match, returns an error func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pk crypto.PubKey, err error) { var key crypto.PrivKey - key, _, err = kb.es.Get(name, passphrase) + bs := kb.db.Get(privName(name)) + key, err = unarmorDecryptPrivKey(string(bs), passphrase) if err != nil { return } + sig = key.Sign(msg) pk = key.PubKey() return @@ -121,36 +130,49 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signat // This is designed to copy from one device to another, or provide backups // during version updates. func (kb dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) { - key, _, err := kb.es.Get(name, oldpass) + bs := kb.db.Get(privName(name)) + key, err := unarmorDecryptPrivKey(string(bs), oldpass) if err != nil { return nil, err } - res, err := kb.es.coder.Encrypt(key, transferpass) - return res, err + if transferpass == "" { + return key.Bytes(), nil + } + res := encryptArmorPrivKey(key, transferpass) + return []byte(res), nil } // Import accepts bytes generated by Export along with the same transferpass // If they are valid, it stores the password under the given name with the // new passphrase. -func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) error { - key, err := kb.es.coder.Decrypt(data, transferpass) +func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err error) { + var key crypto.PrivKey + if transferpass == "" { + key, err = crypto.PrivKeyFromBytes(data) + } else { + key, err = unarmorDecryptPrivKey(string(data), transferpass) + } if err != nil { return err } - return kb.es.Put(name, newpass, key) + kb.writeKey(key, name, newpass) + return nil } // Delete removes key forever, but we must present the // proper passphrase before deleting it (for security) func (kb dbKeybase) Delete(name, passphrase string) error { // verify we have the proper password before deleting - _, _, err := kb.es.Get(name, passphrase) + bs := kb.db.Get(privName(name)) + _, err := unarmorDecryptPrivKey(string(bs), passphrase) if err != nil { return err } - return kb.es.Delete(name) + kb.db.DeleteSync(pubName(name)) + kb.db.DeleteSync(privName(name)) + return nil } // Update changes the passphrase with which a already stored key is encoded. @@ -158,15 +180,30 @@ func (kb dbKeybase) Delete(name, passphrase string) error { // oldpass must be the current passphrase used for encoding, newpass will be // the only valid passphrase from this time forward func (kb dbKeybase) Update(name, oldpass, newpass string) error { - key, _, err := kb.es.Get(name, oldpass) + bs := kb.db.Get(privName(name)) + key, err := unarmorDecryptPrivKey(string(bs), oldpass) if err != nil { return err } // we must delete first, as Putting over an existing name returns an error - kb.Delete(name, oldpass) + kb.db.DeleteSync(pubName(name)) + kb.db.DeleteSync(privName(name)) + kb.writeKey(key, name, newpass) + return nil +} - return kb.es.Put(name, newpass, key) +func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info { + // generate the public bytes + public := info(name, priv) + // generate the encrypted privkey + private := encryptArmorPrivKey(priv, passphrase) + + // write them both + kb.db.SetSync(pubName(name), public.bytes()) + kb.db.SetSync(privName(name), []byte(private)) + + return public } func generate(algo string, secret []byte) (crypto.PrivKey, error) { @@ -197,24 +234,14 @@ func generateByType(typ byte, secret []byte) (crypto.PrivKey, error) { } } -func encrypt(key crypto.PrivKey, pass string) ([]byte, error) { - if pass == "" { - return key.Bytes(), nil - } - s := secret(pass) - cipher := crypto.EncryptSymmetric(key.Bytes(), s) - return cipher, nil -} - -func decrypt(data []byte, pass string) (key crypto.PrivKey, err error) { - private := data - if pass != "" { - s := secret(pass) - private, err = crypto.DecryptSymmetric(data, s) - if err != nil { - return crypto.PrivKey{}, errors.Wrap(err, "Invalid Passphrase") - } - } - key, err = crypto.PrivKeyFromBytes(private) - return key, errors.Wrap(err, "Invalid Passphrase") +func pubName(name string) []byte { + return []byte(fmt.Sprintf("%s.pub", name)) +} + +func privName(name string) []byte { + return []byte(fmt.Sprintf("%s.priv", name)) +} + +func isPub(name []byte) bool { + return strings.HasSuffix(string(name), ".pub") } diff --git a/keys/mintkey.go b/keys/mintkey.go new file mode 100644 index 000000000..c948848be --- /dev/null +++ b/keys/mintkey.go @@ -0,0 +1,73 @@ +package keys + +import ( + "encoding/hex" + "fmt" + + cmn "github.com/tendermint/tmlibs/common" + + "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto/bcrypt" +) + +const ( + blockTypePrivKey = "TENDERMINT PRIVATE KEY" +) + +func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string { + saltBytes, encBytes := encryptPrivKey(privKey, passphrase) + header := map[string]string{ + "kdf": "bcrypt", + "salt": fmt.Sprintf("%X", saltBytes), + } + armorStr := crypto.EncodeArmor(blockTypePrivKey, header, encBytes) + return armorStr +} + +func unarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) { + var privKey crypto.PrivKey + blockType, header, encBytes, err := crypto.DecodeArmor(armorStr) + if err != nil { + return privKey, err + } + if blockType != blockTypePrivKey { + return privKey, fmt.Errorf("Unrecognized armor type: %v", blockType) + } + if header["kdf"] != "bcrypt" { + return privKey, fmt.Errorf("Unrecognized KDF type: %v", header["KDF"]) + } + if header["salt"] == "" { + return privKey, fmt.Errorf("Missing salt bytes") + } + saltBytes, err := hex.DecodeString(header["salt"]) + if err != nil { + return privKey, fmt.Errorf("Error decoding salt: %v", err.Error()) + } + privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) + return privKey, err +} + +func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) { + saltBytes = crypto.CRandBytes(16) + key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016) + if err != nil { + cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error()) + } + key = crypto.Sha256(key) // Get 32 bytes + privKeyBytes := privKey.Bytes() + return saltBytes, crypto.EncryptSymmetric(privKeyBytes, key) +} + +func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) { + key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016) + if err != nil { + cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error()) + } + key = crypto.Sha256(key) // Get 32 bytes + privKeyBytes, err := crypto.DecryptSymmetric(encBytes, key) + if err != nil { + return privKey, err + } + privKey, err = crypto.PrivKeyFromBytes(privKeyBytes) + return privKey, err +} diff --git a/keys/types.go b/keys/types.go index b126f7267..4c546cca7 100644 --- a/keys/types.go +++ b/keys/types.go @@ -1,6 +1,8 @@ package keys import ( + wire "github.com/tendermint/go-wire" + crypto "github.com/tendermint/go-crypto" ) @@ -10,6 +12,15 @@ type Info struct { PubKey crypto.PubKey `json:"pubkey"` } +func (i Info) bytes() []byte { + return wire.BinaryBytes(i) +} + +func readInfo(bs []byte) (info Info, err error) { + err = wire.ReadBinaryBytes(bs, &info) + return +} + func info(name string, privKey crypto.PrivKey) Info { return Info{ Name: name, From 2d4544d6ce40d3a26e4f9ca3edb19ffae21889db Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 2 Nov 2017 18:45:37 -0500 Subject: [PATCH 6/9] Tests compile --- keys/keybase_test.go | 74 ++++++++++++++++++++++---------------------- keys/types.go | 5 +++ 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/keys/keybase_test.go b/keys/keybase_test.go index a57284e9c..bb6602b48 100644 --- a/keys/keybase_test.go +++ b/keys/keybase_test.go @@ -1,7 +1,6 @@ package keys_test import ( - "bytes" "fmt" "os" "testing" @@ -111,18 +110,21 @@ func TestSignVerify(t *testing.T) { d2 := []byte("some other important info!") // try signing both data with both keys... - s11 := keys.NewMockSignable(d1) - err = cstore.Sign(n1, p1, s11) + s11, pub1, err := cstore.Sign(n1, p1, d1) require.Nil(err) - s12 := keys.NewMockSignable(d2) - err = cstore.Sign(n1, p1, s12) + require.Equal(i1.PubKey, pub1) + + s12, pub1, err := cstore.Sign(n1, p1, d2) require.Nil(err) - s21 := keys.NewMockSignable(d1) - err = cstore.Sign(n2, p2, s21) + require.Equal(i1.PubKey, pub1) + + s21, pub2, err := cstore.Sign(n2, p2, d1) require.Nil(err) - s22 := keys.NewMockSignable(d2) - err = cstore.Sign(n2, p2, s22) + require.Equal(i2.PubKey, pub2) + + s22, pub2, err := cstore.Sign(n2, p2, d2) require.Nil(err) + require.Equal(i2.PubKey, pub2) // let's try to validate and make sure it only works when everything is proper cases := []struct { @@ -132,15 +134,15 @@ func TestSignVerify(t *testing.T) { valid bool }{ // proper matches - {i1.PubKey, d1, s11.Signature, true}, + {i1.PubKey, d1, s11, true}, // change data, pubkey, or signature leads to fail - {i1.PubKey, d2, s11.Signature, false}, - {i2.PubKey, d1, s11.Signature, false}, - {i1.PubKey, d1, s21.Signature, false}, + {i1.PubKey, d2, s11, false}, + {i2.PubKey, d1, s11, false}, + {i1.PubKey, d1, s21, false}, // make sure other successes - {i1.PubKey, d2, s12.Signature, true}, - {i2.PubKey, d1, s21.Signature, true}, - {i2.PubKey, d2, s22.Signature, true}, + {i1.PubKey, d2, s12, true}, + {i2.PubKey, d1, s21, true}, + {i2.PubKey, d2, s22, true}, } for i, tc := range cases { @@ -188,21 +190,22 @@ func TestSignWithLedger(t *testing.T) { d2 := []byte("please turn on the app") // try signing both data with the ledger... - s1 := keys.NewMockSignable(d1) - err = cstore.Sign(n, p, s1) + s1, pub, err := cstore.Sign(n, p, d1) require.Nil(err) - s2 := keys.NewMockSignable(d2) - err = cstore.Sign(n, p, s2) + require.Equal(info.PubKey, pub) + + s2, pub, err := cstore.Sign(n, p, d2) require.Nil(err) + require.Equal(info.PubKey, pub) // now, let's check those signatures work - assert.True(key.VerifyBytes(d1, s1.Signature)) - assert.True(key.VerifyBytes(d2, s2.Signature)) + assert.True(key.VerifyBytes(d1, s1)) + assert.True(key.VerifyBytes(d2, s2)) // and mismatched signatures don't - assert.False(key.VerifyBytes(d1, s2.Signature)) + assert.False(key.VerifyBytes(d1, s2)) } -func assertPassword(assert *assert.Assertions, cstore keys.KeyBase, name, pass, badpass string) { +func assertPassword(assert *assert.Assertions, cstore keys.Keybase, name, pass, badpass string) { err := cstore.Update(name, badpass, pass) assert.NotNil(err) err = cstore.Update(name, pass, pass) @@ -219,21 +222,20 @@ func TestImportUnencrypted(t *testing.T) { keys.MustLoadCodec("english"), ) - key, err := keys.GenEd25519.Generate(cmn.RandBytes(16)) - require.NoError(err) + key := crypto.GenPrivKeyEd25519FromSecret(cmn.RandBytes(16)).Wrap() addr := key.PubKey().Address() name := "john" pass := "top-secret" // import raw bytes - err = cstore.Import(name, pass, "", key.Bytes()) + err := cstore.Import(name, pass, "", key.Bytes()) require.Nil(err, "%+v", err) // make sure the address matches info, err := cstore.Get(name) require.Nil(err, "%+v", err) - require.EqualValues(addr, info.Address) + require.EqualValues(addr, info.Address()) } // TestAdvancedKeyManagement verifies update, import, export functionality @@ -309,7 +311,7 @@ func TestSeedPhrase(t *testing.T) { 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.Address(), newInfo.Address()) assert.Equal(info.PubKey, newInfo.PubKey) } @@ -339,8 +341,8 @@ func ExampleNew() { } // We need to use passphrase to generate a signature - tx := keys.NewMockSignable([]byte("deadbeef")) - err = cstore.Sign("Bob", "friend", tx) + tx := []byte("deadbeef") + sig, pub, err := cstore.Sign("Bob", "friend", tx) if err != nil { fmt.Println("don't accept real passphrase") } @@ -351,13 +353,11 @@ func ExampleNew() { fmt.Println("Get and Create return different keys") } - sigs, err := tx.Signers() - if err != nil { - fmt.Println("badly signed") - } else if bytes.Equal(sigs[0].Bytes(), binfo.PubKey.Bytes()) { + if pub.Equals(binfo.PubKey) { fmt.Println("signed by Bob") - } else { - fmt.Println("signed by someone else") + } + if !pub.VerifyBytes(tx, sig) { + fmt.Println("invalid signature") } // Output: diff --git a/keys/types.go b/keys/types.go index 4c546cca7..541234b37 100644 --- a/keys/types.go +++ b/keys/types.go @@ -12,6 +12,11 @@ type Info struct { PubKey crypto.PubKey `json:"pubkey"` } +// Address is a helper function to calculate the address from the pubkey +func (i Info) Address() []byte { + return i.PubKey.Address() +} + func (i Info) bytes() []byte { return wire.BinaryBytes(i) } From 9d78be41b5826cc5e8d52f214856c7bd55d900fe Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 Nov 2017 14:41:00 +0100 Subject: [PATCH 7/9] Update tmlibs to sdk-develop, fix broken test --- glide.lock | 12 ++++++------ glide.yaml | 2 +- keys/keybase.go | 3 +-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/glide.lock b/glide.lock index c1400b3f9..c8c922c6a 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 86d02329c537ccb46ccb062918f0cb9e839f2cf894dfd57223c3ea4de14266db -updated: 2017-11-02T14:09:11.208038182-05:00 +hash: 765599cf3dcac232d3d2ece65c3b016d0abe55d0536adfe93d11e0dd29287de4 +updated: 2017-11-13T14:37:54.127163059+01:00 imports: - name: github.com/btcsuite/btcd version: c7588cbf7690cd9f047a28efa2dcd8f2435a4e5e @@ -10,7 +10,7 @@ imports: subpackages: - base58 - name: github.com/ethanfrey/hid - version: 660bb717bd4e7cbcdf0f7cd5cadf1cb2e4be452a + version: ea8f4c923d3bc8c9c312b7d38724a294f184258a - name: github.com/ethanfrey/ledger version: 23a7bb9d74bc83a862fcb4bddde24215b2295ad9 - name: github.com/go-kit/kit @@ -60,18 +60,18 @@ imports: - edwards25519 - extra25519 - name: github.com/tendermint/go-wire - version: 2baffcb6b690057568bc90ef1d457efb150b979a + version: 1c96861c03231361546944d883d99593b2e6b408 subpackages: - data - data/base58 - name: github.com/tendermint/tmlibs - version: d9525c0fb671204450b160807480e1263053fb20 + version: 56e51bc1133bd54a331e0314d5f80fb547460362 subpackages: - common - db - log - name: golang.org/x/crypto - version: bd6f299fb381e4c3393d1c4b1f0b94f5e77650c8 + version: edd5e9b0879d13ee6970a50153d85b8fec9f7686 subpackages: - bcrypt - blowfish diff --git a/glide.yaml b/glide.yaml index a0b6650fa..64b275f10 100644 --- a/glide.yaml +++ b/glide.yaml @@ -17,7 +17,7 @@ import: subpackages: - extra25519 - package: github.com/tendermint/tmlibs - version: develop + version: sdk-develop - package: github.com/tendermint/go-wire version: develop subpackages: diff --git a/keys/keybase.go b/keys/keybase.go index 00de1ad55..14775060a 100644 --- a/keys/keybase.go +++ b/keys/keybase.go @@ -86,8 +86,7 @@ func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { // List loads the keys from the storage and enforces alphabetical order func (kb dbKeybase) List() ([]Info, error) { var res []Info - var more = true - for iter := kb.db.Iterator(); more; more = iter.Next() { + for iter := kb.db.Iterator(); iter.Valid(); iter.Next() { key := iter.Key() if isPub(key) { info, err := readInfo(iter.Value()) From 2649c056cd37716ab7f9dcea759874d97c22870a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 13 Nov 2017 14:54:14 +0100 Subject: [PATCH 8/9] Minor cleanup --- Makefile | 3 ++- keys/keybase.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c1974c410..31ea0214a 100644 --- a/Makefile +++ b/Makefile @@ -56,11 +56,12 @@ metalinter_test: ensure_tools --enable=structcheck \ --enable=unconvert \ --enable=unused \ - --enable=vetshadow \ --enable=vet \ --enable=varcheck \ ./... + # --enable=vetshadow \ <= doesn't like assert := assert.New(t) + #--enable=dupl \ #--enable=errcheck \ #--enable=goimports \ diff --git a/keys/keybase.go b/keys/keybase.go index 14775060a..7f53b2979 100644 --- a/keys/keybase.go +++ b/keys/keybase.go @@ -222,11 +222,11 @@ func generate(algo string, secret []byte) (crypto.PrivKey, error) { func generateByType(typ byte, secret []byte) (crypto.PrivKey, error) { switch typ { case crypto.TypeEd25519: - return generate(crypto.NameEd25519, secret) + return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil case crypto.TypeSecp256k1: - return generate(crypto.NameSecp256k1, secret) + return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil case nano.TypeLedgerEd25519: - return generate(nano.NameLedgerEd25519, secret) + return nano.NewPrivKeyLedgerEd25519Ed25519() default: err := errors.Errorf("Cannot generate keys for algorithm: %X", typ) return crypto.PrivKey{}, err From 086e1f6508e6b32159b82ded17f335abd76caaf7 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 15 Nov 2017 13:57:17 +0100 Subject: [PATCH 9/9] Make vetshadow and zach happy --- Makefile | 2 +- encode_test.go | 4 +--- keys/ecc_test.go | 6 +++--- keys/keybase_test.go | 18 +++++++++--------- keys/wordcodec_test.go | 12 ++++++------ nano/keys_test.go | 10 +++++----- nano/sign_test.go | 9 +++++---- signature_test.go | 6 ++---- 8 files changed, 32 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 31ea0214a..9e2530a94 100644 --- a/Makefile +++ b/Makefile @@ -58,9 +58,9 @@ metalinter_test: ensure_tools --enable=unused \ --enable=vet \ --enable=varcheck \ + --enable=vetshadow \ ./... - # --enable=vetshadow \ <= doesn't like assert := assert.New(t) #--enable=dupl \ #--enable=errcheck \ diff --git a/encode_test.go b/encode_test.go index 1b70d88ec..947d91901 100644 --- a/encode_test.go +++ b/encode_test.go @@ -145,8 +145,6 @@ func (s SigMessage) Bytes() []byte { } func TestEmbededWireEncodings(t *testing.T) { - assert := assert.New(t) - cases := []struct { privKey PrivKey keyType byte @@ -171,7 +169,7 @@ func TestEmbededWireEncodings(t *testing.T) { for i, tc := range cases { pubKey := tc.privKey.PubKey() sig := tc.privKey.Sign(payload) - assert.True(pubKey.VerifyBytes(payload, sig), "%d", i) + assert.True(t, pubKey.VerifyBytes(payload, sig), "%d", i) msg := SigMessage{ Key: pubKey, diff --git a/keys/ecc_test.go b/keys/ecc_test.go index d6b536aaa..6d3e3bec8 100644 --- a/keys/ecc_test.go +++ b/keys/ecc_test.go @@ -3,7 +3,7 @@ package keys import ( "testing" - "github.com/stretchr/testify/assert" + asrt "github.com/stretchr/testify/assert" cmn "github.com/tendermint/tmlibs/common" ) @@ -21,7 +21,7 @@ var codecs = []ECC{ // TestECCPasses makes sure that the AddECC/CheckECC methods are symetric func TestECCPasses(t *testing.T) { - assert := assert.New(t) + assert := asrt.New(t) checks := append(codecs, NoECC{}) @@ -41,7 +41,7 @@ func TestECCPasses(t *testing.T) { // TestECCFails makes sure random data will (usually) fail the checksum func TestECCFails(t *testing.T) { - assert := assert.New(t) + assert := asrt.New(t) checks := codecs attempts := 2000 diff --git a/keys/keybase_test.go b/keys/keybase_test.go index bb6602b48..72476a02e 100644 --- a/keys/keybase_test.go +++ b/keys/keybase_test.go @@ -5,8 +5,8 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + asrt "github.com/stretchr/testify/assert" + rqr "github.com/stretchr/testify/require" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -18,7 +18,7 @@ import ( // TestKeyManagement makes sure we can manipulate these keys well func TestKeyManagement(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( @@ -86,7 +86,7 @@ func TestKeyManagement(t *testing.T) { // TestSignVerify does some detailed checks on how we sign and validate // signatures func TestSignVerify(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( @@ -157,7 +157,7 @@ func TestSignVerify(t *testing.T) { // This test will only succeed with a ledger attached to the computer // and the cosmos app open func TestSignWithLedger(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) if os.Getenv("WITH_LEDGER") == "" { t.Skip("Set WITH_LEDGER to run code on real ledger") } @@ -205,7 +205,7 @@ func TestSignWithLedger(t *testing.T) { assert.False(key.VerifyBytes(d1, s2)) } -func assertPassword(assert *assert.Assertions, cstore keys.Keybase, name, pass, badpass string) { +func assertPassword(assert *asrt.Assertions, cstore keys.Keybase, name, pass, badpass string) { err := cstore.Update(name, badpass, pass) assert.NotNil(err) err = cstore.Update(name, pass, pass) @@ -214,7 +214,7 @@ func assertPassword(assert *assert.Assertions, cstore keys.Keybase, name, pass, // TestImportUnencrypted tests accepting raw priv keys bytes as input func TestImportUnencrypted(t *testing.T) { - require := require.New(t) + require := rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( @@ -240,7 +240,7 @@ func TestImportUnencrypted(t *testing.T) { // TestAdvancedKeyManagement verifies update, import, export functionality func TestAdvancedKeyManagement(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( @@ -283,7 +283,7 @@ func TestAdvancedKeyManagement(t *testing.T) { // TestSeedPhrase verifies restoring from a seed phrase func TestSeedPhrase(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( diff --git a/keys/wordcodec_test.go b/keys/wordcodec_test.go index a44607be2..f79ebcad0 100644 --- a/keys/wordcodec_test.go +++ b/keys/wordcodec_test.go @@ -3,14 +3,14 @@ package keys import ( "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + asrt "github.com/stretchr/testify/assert" + rqr "github.com/stretchr/testify/require" cmn "github.com/tendermint/tmlibs/common" ) func TestLengthCalc(t *testing.T) { - assert := assert.New(t) + assert := asrt.New(t) cases := []struct { bytes, words int @@ -50,7 +50,7 @@ func TestLengthCalc(t *testing.T) { } func TestEncodeDecode(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) codec, err := LoadCodec("english") require.Nil(err, "%+v", err) @@ -82,7 +82,7 @@ func TestEncodeDecode(t *testing.T) { } func TestCheckInvalidLists(t *testing.T) { - assert := assert.New(t) + assert := asrt.New(t) trivial := []string{"abc", "def"} short := make([]string, 1234) @@ -144,7 +144,7 @@ func getDiffWord(c *WordCodec, not string) string { } func TestCheckTypoDetection(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) banks := []string{"english", "spanish", "japanese", "chinese_simplified"} diff --git a/nano/keys_test.go b/nano/keys_test.go index 15aa0d545..2e5142e30 100644 --- a/nano/keys_test.go +++ b/nano/keys_test.go @@ -5,14 +5,14 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + asrt "github.com/stretchr/testify/assert" + rqr "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" ) func TestLedgerKeys(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) cases := []struct { msg, pubkey, sig string @@ -76,7 +76,7 @@ func TestLedgerKeys(t *testing.T) { } func TestRealLedger(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) if os.Getenv("WITH_LEDGER") == "" { t.Skip("Set WITH_LEDGER to run code on real ledger") @@ -115,7 +115,7 @@ func TestRealLedger(t *testing.T) { // TestRealLedgerErrorHandling calls. These tests assume // the ledger is not plugged in.... func TestRealLedgerErrorHandling(t *testing.T) { - require := require.New(t) + require := rqr.New(t) if os.Getenv("WITH_LEDGER") != "" { t.Skip("Skipping on WITH_LEDGER as it tests unplugged cases") diff --git a/nano/sign_test.go b/nano/sign_test.go index 04a6d0be7..18e4e0d0b 100644 --- a/nano/sign_test.go +++ b/nano/sign_test.go @@ -5,8 +5,9 @@ import ( "testing" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + asrt "github.com/stretchr/testify/assert" + rqr "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" ) @@ -29,7 +30,7 @@ func parseSig(data []byte) (key crypto.Signature, err error) { } func TestParseDigest(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) cases := []struct { output string @@ -91,7 +92,7 @@ func toBytes(c cryptoCase) (msg, key, sig []byte, err error) { } func TestCryptoConvert(t *testing.T) { - assert, require := assert.New(t), require.New(t) + assert, require := asrt.New(t), rqr.New(t) cases := []cryptoCase{ 0: { diff --git a/signature_test.go b/signature_test.go index 5e9f06723..6659c5b9d 100644 --- a/signature_test.go +++ b/signature_test.go @@ -109,8 +109,6 @@ func TestSignatureEncodings(t *testing.T) { } func TestWrapping(t *testing.T) { - assert := assert.New(t) - // construct some basic constructs msg := CRandBytes(128) priv := GenPrivKeyEd25519() @@ -126,7 +124,7 @@ func TestWrapping(t *testing.T) { } for _, p := range pubs { _, ok := p.PubKeyInner.(PubKey) - assert.False(ok) + assert.False(t, ok) } sigs := []Signature{ @@ -137,7 +135,7 @@ func TestWrapping(t *testing.T) { } for _, s := range sigs { _, ok := s.SignatureInner.(Signature) - assert.False(ok) + assert.False(t, ok) } }