From 4173d1031e6a7361392fb80e46f0cc5469cb4499 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 2 Nov 2017 18:32:12 -0500 Subject: [PATCH] go build compiles --- keys/keybase.go | 113 ++++++++++++++++++++++++++++++------------------ keys/mintkey.go | 73 +++++++++++++++++++++++++++++++ keys/types.go | 11 +++++ 3 files changed, 154 insertions(+), 43 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 pubName(name string) []byte { + return []byte(fmt.Sprintf("%s.pub", name)) } -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 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,