tendermint/keys/keybase.go

249 lines
6.9 KiB
Go
Raw Normal View History

2017-11-02 14:09:59 -05:00
package keys
import (
2017-11-02 18:32:12 -05:00
"fmt"
"strings"
2017-11-02 16:31:29 -05:00
"github.com/pkg/errors"
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-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
// a full-featured key manager
2017-11-01 16:38:11 -05:00
type dbKeybase struct {
db dbm.DB
codec Codec
}
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,
codec: codec,
}
}
2017-11-01 16:38:11 -05:00
var _ Keybase = dbKeybase{}
2017-10-04 18:16:48 -04: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) {
// 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-11-02 18:32:12 -05:00
public := kb.writeKey(key, name, passphrase)
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)
phrase := strings.Join(seed, " ")
2017-11-02 18:32:12 -05:00
return public, phrase, err
}
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) {
words := strings.Split(strings.TrimSpace(seedphrase), " ")
2017-11-01 16:38:11 -05:00
secret, err := kb.codec.WordsToBytes(words)
if err != nil {
2017-11-01 16:38:11 -05:00
return Info{}, err
}
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
}
// d00d, it worked! create the bugger....
2017-11-02 18:32:12 -05:00
public := kb.writeKey(key, name, passphrase)
return public, err
}
// 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) {
2017-11-02 18:32:12 -05:00
var res []Info
2017-12-27 14:37:37 -08:00
iter := kb.db.Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
2017-11-02 18:32:12 -05:00
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
2017-11-02 16:46:10 -05:00
func (kb dbKeybase) Get(name string) (Info, error) {
2017-11-02 18:32:12 -05:00
bs := kb.db.Get(pubName(name))
return readInfo(bs)
}
// 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
2017-11-02 18:32:12 -05:00
bs := kb.db.Get(privName(name))
key, err = unarmorDecryptPrivKey(string(bs), passphrase)
if err != nil {
2017-11-02 16:46:10 -05:00
return
}
2017-11-02 18:32:12 -05:00
2017-11-02 16:46:10 -05:00
sig = key.Sign(msg)
pk = key.PubKey()
return
}
// 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
//
// 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-02 18:32:12 -05:00
bs := kb.db.Get(privName(name))
key, err := unarmorDecryptPrivKey(string(bs), oldpass)
if err != nil {
return nil, err
}
2017-11-02 18:32:12 -05:00
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.
2017-11-02 18:32:12 -05:00
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
}
2017-11-02 18:32:12 -05:00
kb.writeKey(key, name, newpass)
return nil
}
// 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 {
// verify we have the proper password before deleting
2017-11-02 18:32:12 -05:00
bs := kb.db.Get(privName(name))
_, err := unarmorDecryptPrivKey(string(bs), passphrase)
if err != nil {
return err
}
2017-11-02 18:32:12 -05:00
kb.db.DeleteSync(pubName(name))
kb.db.DeleteSync(privName(name))
return nil
}
// 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-02 18:32:12 -05:00
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
2017-11-02 18:32:12 -05:00
kb.db.DeleteSync(pubName(name))
kb.db.DeleteSync(privName(name))
kb.writeKey(key, name, newpass)
return nil
}
2017-11-02 18:32:12 -05:00
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
}
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:
2017-11-13 14:54:14 +01:00
return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil
2017-11-02 16:31:29 -05:00
case crypto.TypeSecp256k1:
2017-11-13 14:54:14 +01:00
return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil
2017-11-02 16:31:29 -05:00
case nano.TypeLedgerEd25519:
2017-11-13 14:54:14 +01:00
return nano.NewPrivKeyLedgerEd25519Ed25519()
2017-11-02 16:31:29 -05:00
default:
err := errors.Errorf("Cannot generate keys for algorithm: %X", typ)
return crypto.PrivKey{}, err
}
}
2017-11-02 16:46:10 -05:00
2017-11-02 18:32:12 -05:00
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")
2017-11-02 16:46:10 -05:00
}