You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

170 lines
4.8 KiB

package cryptostore
import (
"strings"
crypto "github.com/tendermint/go-crypto"
keys "github.com/tendermint/go-crypto/keys"
)
// Manager combines encyption and storage implementation to provide
// a full-featured key manager
type Manager struct {
es encryptedStorage
codec keys.Codec
}
func New(coder Encoder, store keys.Storage, codec keys.Codec) Manager {
return Manager{
es: encryptedStorage{
coder: coder,
store: store,
},
codec: codec,
}
}
// assert Manager satisfies keys.Signer and keys.Manager interfaces
var _ keys.Signer = Manager{}
var _ keys.Manager = Manager{}
// 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) {
gen, err := getGenerator(algo)
if err != nil {
return keys.Info{}, "", err
}
// 128-bits are the all the randomness we can make use of
secret := crypto.CRandBytes(16)
key := gen.Generate(secret)
err = s.es.Put(name, passphrase, key)
if err != nil {
return keys.Info{}, "", err
}
// we append the type byte to the serialized secret to help with recovery
// ie [secret] = [secret] + [type]
typ := key.Bytes()[0]
secret = append(secret, typ)
seed, err := s.codec.BytesToWords(secret)
phrase := strings.Join(seed, " ")
return info(name, key), phrase, err
}
// 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...
func (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) {
words := strings.Split(strings.TrimSpace(seedphrase), " ")
secret, err := s.codec.WordsToBytes(words)
if err != nil {
return keys.Info{}, err
}
// secret is comprised of the actual secret with the type appended
// ie [secret] = [secret] + [type]
l := len(secret)
secret, typ := secret[:l-1], secret[l-1]
gen, err := getGeneratorByType(typ)
if err != nil {
return keys.Info{}, err
}
key := gen.Generate(secret)
// d00d, it worked! create the bugger....
err = s.es.Put(name, passphrase, key)
return info(name, key), err
}
// List loads the keys from the storage and enforces alphabetical order
func (s Manager) List() (keys.Infos, error) {
res, err := s.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)
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
func (s Manager) Sign(name, passphrase string, tx keys.Signable) error {
key, _, err := s.es.Get(name, passphrase)
if err != nil {
return err
}
sig := key.Sign(tx.SignBytes())
pubkey := key.PubKey()
return tx.Sign(pubkey, sig)
}
// 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
//
// This is designed to copy from one device to another, or provide backups
// during version updates.
// TODO: How to handle Export with salt?
func (s Manager) Export(name, oldpass, transferpass string) (salt, data []byte, err error) {
key, _, err := s.es.Get(name, oldpass)
if err != nil {
return nil, nil, err
}
salt, data, err = s.es.coder.Encrypt(key, transferpass)
return salt, data, err
}
// Import accepts bytes generated by Export along with the same transferpass
// If they are valid, it stores the key under the given name with the
// new passphrase.
// TODO: How to handle Import with salt?
func (s Manager) Import(name, newpass, transferpass string, salt, data []byte) error {
key, err := s.es.coder.Decrypt(salt, data, transferpass)
if err != nil {
return err
}
return s.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 {
// verify we have the proper password before deleting
_, _, err := s.es.Get(name, passphrase)
if err != nil {
return err
}
return s.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)
if err != nil {
return err
}
// we must delete first, as Putting over an existing name returns an error
s.Delete(name, oldpass)
return s.es.Put(name, newpass, key)
}