|
|
@ -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") |
|
|
|
} |