2017-11-02 14:09:59 -05:00
|
|
|
package keys
|
2017-02-28 18:07:59 +01:00
|
|
|
|
2017-06-20 18:35:16 +02:00
|
|
|
import (
|
2017-11-02 16:46:10 -05:00
|
|
|
"sort"
|
2017-06-20 18:35:16 +02:00
|
|
|
"strings"
|
|
|
|
|
2017-11-02 16:31:29 -05:00
|
|
|
"github.com/pkg/errors"
|
2017-06-20 18:35:16 +02:00
|
|
|
crypto "github.com/tendermint/go-crypto"
|
2017-11-01 16:38:11 -05:00
|
|
|
dbm "github.com/tendermint/tmlibs/db"
|
2017-11-02 16:31:29 -05:00
|
|
|
|
|
|
|
"github.com/tendermint/go-crypto/nano"
|
2017-06-20 18:35:16 +02:00
|
|
|
)
|
2017-02-28 18:07:59 +01:00
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
// 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
|
2017-02-28 18:07:59 +01:00
|
|
|
// a full-featured key manager
|
2017-11-01 16:38:11 -05:00
|
|
|
type dbKeybase struct {
|
|
|
|
db dbm.DB
|
|
|
|
codec Codec
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
func New(db dbm.DB, codec Codec) dbKeybase {
|
|
|
|
return dbKeybase{
|
2017-11-02 16:46:10 -05:00
|
|
|
db: db,
|
2017-06-20 18:35:16 +02:00
|
|
|
codec: codec,
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
var _ Keybase = dbKeybase{}
|
2017-10-04 18:16:48 -04:00
|
|
|
|
2017-02-28 18:07:59 +01:00
|
|
|
// Create adds a new key to the storage engine, returning error if
|
|
|
|
// another key already stored under this name
|
2017-02-28 19:43:18 +01:00
|
|
|
//
|
2017-06-21 18:57:32 +02:00
|
|
|
// algo must be a supported go-crypto algorithm: ed25519, secp256k1
|
2017-11-01 16:38:11 -05:00
|
|
|
func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) {
|
2017-10-23 16:35:26 +02:00
|
|
|
// 128-bits are the all the randomness we can make use of
|
|
|
|
secret := crypto.CRandBytes(16)
|
2017-11-02 16:31:29 -05:00
|
|
|
key, err := generate(algo, secret)
|
2017-02-28 19:43:18 +01:00
|
|
|
if err != nil {
|
2017-11-01 16:38:11 -05:00
|
|
|
return Info{}, "", err
|
2017-02-28 19:43:18 +01:00
|
|
|
}
|
2017-07-22 05:44:09 -04:00
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
err = kb.es.Put(name, passphrase, key)
|
2017-06-20 18:35:16 +02:00
|
|
|
if err != nil {
|
2017-11-01 16:38:11 -05:00
|
|
|
return Info{}, "", err
|
2017-06-20 18:35:16 +02:00
|
|
|
}
|
2017-07-22 05:44:09 -04:00
|
|
|
|
2017-07-27 15:59:59 -04:00
|
|
|
// we append the type byte to the serialized secret to help with recovery
|
|
|
|
// ie [secret] = [secret] + [type]
|
2017-07-22 05:53:46 -04:00
|
|
|
typ := key.Bytes()[0]
|
|
|
|
secret = append(secret, typ)
|
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
seed, err := kb.codec.BytesToWords(secret)
|
2017-06-20 18:35:16 +02:00
|
|
|
phrase := strings.Join(seed, " ")
|
|
|
|
return info(name, key), phrase, err
|
2017-06-20 18:15:49 +02:00
|
|
|
}
|
|
|
|
|
2017-06-21 18:57:32 +02:00
|
|
|
// Recover takes a seed phrase and tries to recover the private key.
|
|
|
|
//
|
|
|
|
// If the seed phrase is valid, it will create the private key and store
|
|
|
|
// it under name, protected by passphrase.
|
|
|
|
//
|
|
|
|
// Result similar to New(), except it doesn't return the seed again...
|
2017-11-02 16:46:10 -05:00
|
|
|
func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) {
|
2017-06-20 18:35:16 +02:00
|
|
|
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
2017-11-01 16:38:11 -05:00
|
|
|
secret, err := kb.codec.WordsToBytes(words)
|
2017-06-20 18:35:16 +02:00
|
|
|
if err != nil {
|
2017-11-01 16:38:11 -05:00
|
|
|
return Info{}, err
|
2017-06-20 18:35:16 +02:00
|
|
|
}
|
|
|
|
|
2017-07-27 15:59:59 -04:00
|
|
|
// secret is comprised of the actual secret with the type appended
|
|
|
|
// ie [secret] = [secret] + [type]
|
2017-07-22 05:53:46 -04:00
|
|
|
l := len(secret)
|
|
|
|
secret, typ := secret[:l-1], secret[l-1]
|
|
|
|
|
2017-11-02 16:31:29 -05:00
|
|
|
key, err := generateByType(typ, secret)
|
2017-07-22 05:53:46 -04:00
|
|
|
if err != nil {
|
2017-11-01 16:38:11 -05:00
|
|
|
return Info{}, err
|
2017-07-22 05:53:46 -04:00
|
|
|
}
|
2017-06-20 18:35:16 +02:00
|
|
|
|
|
|
|
// d00d, it worked! create the bugger....
|
2017-11-01 16:38:11 -05:00
|
|
|
err = kb.es.Put(name, passphrase, key)
|
2017-06-20 18:35:16 +02:00
|
|
|
return info(name, key), err
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// List loads the keys from the storage and enforces alphabetical order
|
2017-11-02 16:46:10 -05:00
|
|
|
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 })
|
2017-02-28 18:07:59 +01:00
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the public information about one key
|
2017-11-02 16:46:10 -05:00
|
|
|
func (kb dbKeybase) Get(name string) (Info, error) {
|
2017-11-01 16:38:11 -05:00
|
|
|
_, info, err := kb.es.store.Get(name)
|
2017-02-28 18:07:59 +01:00
|
|
|
return info, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sign will modify the Signable in order to attach a valid signature with
|
|
|
|
// this public key
|
|
|
|
//
|
|
|
|
// If no key for this name, or the passphrase doesn't match, returns an error
|
2017-11-02 16:46:10 -05:00
|
|
|
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)
|
2017-02-28 18:07:59 +01:00
|
|
|
if err != nil {
|
2017-11-02 16:46:10 -05:00
|
|
|
return
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
2017-11-02 16:46:10 -05:00
|
|
|
sig = key.Sign(msg)
|
|
|
|
pk = key.PubKey()
|
|
|
|
return
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Export decodes the private key with the current password, encodes
|
|
|
|
// it with a secure one-time password and generates a sequence that can be
|
2017-11-01 16:38:11 -05:00
|
|
|
// Imported by another dbKeybase
|
2017-02-28 18:07:59 +01:00
|
|
|
//
|
|
|
|
// This is designed to copy from one device to another, or provide backups
|
|
|
|
// during version updates.
|
2017-11-02 16:46:10 -05:00
|
|
|
func (kb dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) {
|
2017-11-01 16:38:11 -05:00
|
|
|
key, _, err := kb.es.Get(name, oldpass)
|
2017-02-28 18:07:59 +01:00
|
|
|
if err != nil {
|
2017-10-27 22:02:44 -04:00
|
|
|
return nil, err
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
res, err := kb.es.coder.Encrypt(key, transferpass)
|
2017-10-27 22:02:44 -04:00
|
|
|
return res, err
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Import accepts bytes generated by Export along with the same transferpass
|
2017-10-27 22:02:44 -04:00
|
|
|
// If they are valid, it stores the password under the given name with the
|
2017-02-28 18:07:59 +01:00
|
|
|
// new passphrase.
|
2017-11-02 16:46:10 -05:00
|
|
|
func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) error {
|
2017-11-01 16:38:11 -05:00
|
|
|
key, err := kb.es.coder.Decrypt(data, transferpass)
|
2017-02-28 18:07:59 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
return kb.es.Put(name, newpass, key)
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removes key forever, but we must present the
|
|
|
|
// proper passphrase before deleting it (for security)
|
2017-11-02 16:46:10 -05:00
|
|
|
func (kb dbKeybase) Delete(name, passphrase string) error {
|
2017-02-28 18:07:59 +01:00
|
|
|
// verify we have the proper password before deleting
|
2017-11-01 16:38:11 -05:00
|
|
|
_, _, err := kb.es.Get(name, passphrase)
|
2017-02-28 18:07:59 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-01 16:38:11 -05:00
|
|
|
return kb.es.Delete(name)
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2017-11-02 16:46:10 -05:00
|
|
|
func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
2017-11-01 16:38:11 -05:00
|
|
|
key, _, err := kb.es.Get(name, oldpass)
|
2017-02-28 18:07:59 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// we must delete first, as Putting over an existing name returns an error
|
2017-11-01 16:38:11 -05:00
|
|
|
kb.Delete(name, oldpass)
|
2017-02-28 18:07:59 +01:00
|
|
|
|
2017-11-01 16:38:11 -05:00
|
|
|
return kb.es.Put(name, newpass, key)
|
2017-02-28 18:07:59 +01:00
|
|
|
}
|
2017-11-02 16:31:29 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2017-11-02 16:46:10 -05:00
|
|
|
|
|
|
|
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")
|
|
|
|
}
|