2020-03-25 08:20:36 -07:00
|
|
|
package keyring
|
2019-09-21 09:54:14 -07:00
|
|
|
|
|
|
|
import (
|
2019-09-30 09:16:53 -07:00
|
|
|
"bufio"
|
2019-09-21 09:54:14 -07:00
|
|
|
"fmt"
|
2019-09-30 09:16:53 -07:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2019-09-21 09:54:14 -07:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/99designs/keyring"
|
|
|
|
"github.com/pkg/errors"
|
2019-09-30 09:16:53 -07:00
|
|
|
|
|
|
|
"github.com/tendermint/crypto/bcrypt"
|
2019-09-21 09:54:14 -07:00
|
|
|
tmcrypto "github.com/tendermint/tendermint/crypto"
|
|
|
|
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
|
|
|
|
2019-09-30 09:16:53 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/client/input"
|
2020-03-27 02:24:19 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/crypto"
|
2019-09-21 09:54:14 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/types"
|
2020-03-25 08:20:36 -07:00
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
2019-09-21 09:54:14 -07:00
|
|
|
)
|
|
|
|
|
2019-12-11 01:45:26 -08:00
|
|
|
const (
|
2020-01-23 08:48:00 -08:00
|
|
|
BackendFile = "file"
|
|
|
|
BackendOS = "os"
|
|
|
|
BackendKWallet = "kwallet"
|
|
|
|
BackendPass = "pass"
|
|
|
|
BackendTest = "test"
|
2020-01-22 09:54:56 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
keyringDirNameFmt = "keyring-%s"
|
|
|
|
testKeyringDirNameFmt = "keyring-test-%s"
|
2020-03-23 08:40:47 -07:00
|
|
|
passKeyringPrefix = keyringDirNameFmt
|
2019-12-11 01:45:26 -08:00
|
|
|
)
|
|
|
|
|
2019-09-21 09:54:14 -07:00
|
|
|
var _ Keybase = keyringKeybase{}
|
|
|
|
|
|
|
|
// keyringKeybase implements the Keybase interface by using the Keyring library
|
|
|
|
// for account key persistence.
|
|
|
|
type keyringKeybase struct {
|
|
|
|
base baseKeybase
|
2019-10-14 08:43:19 -07:00
|
|
|
db keyring.Keyring
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
2019-09-30 09:16:53 -07:00
|
|
|
var maxPassphraseEntryAttempts = 3
|
|
|
|
|
2020-01-15 09:29:36 -08:00
|
|
|
func newKeyringKeybase(db keyring.Keyring, opts ...KeybaseOption) Keybase {
|
|
|
|
return keyringKeybase{
|
|
|
|
db: db,
|
|
|
|
base: newBaseKeybase(opts...),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-12 13:52:24 -08:00
|
|
|
// NewKeyring creates a new instance of a keyring. Keybase
|
|
|
|
// options can be applied when generating this new Keybase.
|
2020-03-19 09:58:14 -07:00
|
|
|
// Available backends are "os", "file", "kwallet", "pass", "test".
|
2019-12-12 13:52:24 -08:00
|
|
|
func NewKeyring(
|
2020-01-23 10:47:11 -08:00
|
|
|
appName, backend, rootDir string, userInput io.Reader, opts ...KeybaseOption,
|
2019-12-12 13:52:24 -08:00
|
|
|
) (Keybase, error) {
|
2019-09-30 09:16:53 -07:00
|
|
|
|
2020-01-22 09:54:56 -08:00
|
|
|
var db keyring.Keyring
|
|
|
|
var err error
|
2019-09-30 09:16:53 -07:00
|
|
|
|
2020-01-22 09:54:56 -08:00
|
|
|
switch backend {
|
|
|
|
case BackendTest:
|
2020-01-23 10:47:11 -08:00
|
|
|
db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, nil, true))
|
2020-01-22 09:54:56 -08:00
|
|
|
case BackendFile:
|
2020-01-23 10:47:11 -08:00
|
|
|
db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput))
|
2020-01-22 09:54:56 -08:00
|
|
|
case BackendOS:
|
2020-01-23 10:47:11 -08:00
|
|
|
db, err = keyring.Open(lkbToKeyringConfig(appName, rootDir, userInput, false))
|
2020-01-23 08:48:00 -08:00
|
|
|
case BackendKWallet:
|
2020-01-23 10:47:11 -08:00
|
|
|
db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput))
|
2020-01-23 08:48:00 -08:00
|
|
|
case BackendPass:
|
2020-01-23 10:47:11 -08:00
|
|
|
db, err = keyring.Open(newPassBackendKeyringConfig(appName, rootDir, userInput))
|
2020-01-22 09:54:56 -08:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unknown keyring backend %v", backend)
|
2019-12-11 01:45:26 -08:00
|
|
|
}
|
2019-09-30 09:16:53 -07:00
|
|
|
if err != nil {
|
2019-10-09 11:53:54 -07:00
|
|
|
return nil, err
|
2019-09-30 09:16:53 -07:00
|
|
|
}
|
|
|
|
|
2019-12-12 13:52:24 -08:00
|
|
|
return newKeyringKeybase(db, opts...), nil
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateMnemonic generates a new key and persists it to storage, encrypted
|
|
|
|
// using the provided password. It returns the generated mnemonic and the key Info.
|
|
|
|
// An error is returned if it fails to generate a key for the given algo type,
|
|
|
|
// or if another key is already stored under the same name.
|
|
|
|
func (kb keyringKeybase) CreateMnemonic(
|
|
|
|
name string, language Language, passwd string, algo SigningAlgo,
|
|
|
|
) (info Info, mnemonic string, err error) {
|
|
|
|
|
|
|
|
return kb.base.CreateMnemonic(kb, name, language, passwd, algo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateAccount converts a mnemonic to a private key and persists it, encrypted
|
|
|
|
// with the given password.
|
|
|
|
func (kb keyringKeybase) CreateAccount(
|
2020-01-14 07:40:10 -08:00
|
|
|
name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
|
2019-09-21 09:54:14 -07:00
|
|
|
) (Info, error) {
|
|
|
|
|
2020-01-14 07:40:10 -08:00
|
|
|
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateLedger creates a new locally-stored reference to a Ledger keypair.
|
|
|
|
// It returns the created key info and an error if the Ledger could not be queried.
|
|
|
|
func (kb keyringKeybase) CreateLedger(
|
|
|
|
name string, algo SigningAlgo, hrp string, account, index uint32,
|
|
|
|
) (Info, error) {
|
|
|
|
|
|
|
|
return kb.base.CreateLedger(kb, name, algo, hrp, account, index)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateOffline creates a new reference to an offline keypair. It returns the
|
|
|
|
// created key info.
|
2020-01-14 07:40:10 -08:00
|
|
|
func (kb keyringKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
|
|
|
|
return kb.base.writeOfflineKey(kb, name, pub, algo), nil
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateMulti creates a new reference to a multisig (offline) keypair. It
|
|
|
|
// returns the created key Info object.
|
|
|
|
func (kb keyringKeybase) CreateMulti(name string, pub tmcrypto.PubKey) (Info, error) {
|
|
|
|
return kb.base.writeMultisigKey(kb, name, pub), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// List returns the keys from storage in alphabetical order.
|
|
|
|
func (kb keyringKeybase) List() ([]Info, error) {
|
|
|
|
var res []Info
|
|
|
|
keys, err := kb.db.Keys()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
for _, key := range keys {
|
|
|
|
if strings.HasSuffix(key, infoSuffix) {
|
|
|
|
rawInfo, err := kb.db.Get(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(rawInfo.Data) == 0 {
|
2020-03-25 08:20:36 -07:00
|
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, key)
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
info, err := unmarshalInfo(rawInfo.Data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
res = append(res, info)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the public information about one key.
|
|
|
|
func (kb keyringKeybase) Get(name string) (Info, error) {
|
|
|
|
key := infoKey(name)
|
|
|
|
|
|
|
|
bs, err := kb.db.Get(string(key))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(bs.Data) == 0 {
|
2020-03-25 08:20:36 -07:00
|
|
|
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, name)
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return unmarshalInfo(bs.Data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetByAddress fetches a key by address and returns its public information.
|
|
|
|
func (kb keyringKeybase) GetByAddress(address types.AccAddress) (Info, error) {
|
2020-03-30 07:54:01 -07:00
|
|
|
ik, err := kb.db.Get(string(addrHexKey(address)))
|
2019-09-21 09:54:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ik.Data) == 0 {
|
|
|
|
return nil, fmt.Errorf("key with address %s not found", address)
|
|
|
|
}
|
|
|
|
|
|
|
|
bs, err := kb.db.Get(string(ik.Data))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return unmarshalInfo(bs.Data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sign signs an arbitrary set of bytes with the named key. It returns an error
|
|
|
|
// if the key doesn't exist or the decryption fails.
|
|
|
|
func (kb keyringKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
|
|
|
|
info, err := kb.Get(name)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var priv tmcrypto.PrivKey
|
|
|
|
|
|
|
|
switch i := info.(type) {
|
|
|
|
case localInfo:
|
|
|
|
if i.PrivKeyArmor == "" {
|
|
|
|
return nil, nil, fmt.Errorf("private key not available")
|
|
|
|
}
|
|
|
|
|
|
|
|
priv, err = cryptoAmino.PrivKeyFromBytes([]byte(i.PrivKeyArmor))
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
case ledgerInfo:
|
2020-03-01 09:16:22 -08:00
|
|
|
return SignWithLedger(info, msg)
|
2019-09-21 09:54:14 -07:00
|
|
|
|
|
|
|
case offlineInfo, multiInfo:
|
2020-03-20 08:00:12 -07:00
|
|
|
return nil, info.GetPubKey(), errors.New("cannot sign with offline keys")
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
sig, err = priv.Sign(msg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return sig, priv.PubKey(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExportPrivateKeyObject exports an armored private key object.
|
|
|
|
func (kb keyringKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) {
|
|
|
|
info, err := kb.Get(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var priv tmcrypto.PrivKey
|
|
|
|
|
|
|
|
switch linfo := info.(type) {
|
|
|
|
case localInfo:
|
|
|
|
if linfo.PrivKeyArmor == "" {
|
|
|
|
err = fmt.Errorf("private key not available")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
priv, err = cryptoAmino.PrivKeyFromBytes([]byte(linfo.PrivKeyArmor))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
case ledgerInfo, offlineInfo, multiInfo:
|
|
|
|
return nil, errors.New("only works on local private keys")
|
|
|
|
}
|
|
|
|
|
|
|
|
return priv, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Export exports armored private key to the caller.
|
|
|
|
func (kb keyringKeybase) Export(name string) (armor string, err error) {
|
|
|
|
bz, err := kb.db.Get(string(infoKey(name)))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if bz.Data == nil {
|
|
|
|
return "", fmt.Errorf("no key to export with name: %s", name)
|
|
|
|
}
|
|
|
|
|
2020-03-27 02:24:19 -07:00
|
|
|
return crypto.ArmorInfoBytes(bz.Data), nil
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// ExportPubKey returns public keys in ASCII armored format. It retrieves an Info
|
|
|
|
// object by its name and return the public key in a portable format.
|
|
|
|
func (kb keyringKeybase) ExportPubKey(name string) (armor string, err error) {
|
|
|
|
bz, err := kb.Get(name)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if bz == nil {
|
|
|
|
return "", fmt.Errorf("no key to export with name: %s", name)
|
|
|
|
}
|
|
|
|
|
2020-03-27 02:24:19 -07:00
|
|
|
return crypto.ArmorPubKeyBytes(bz.GetPubKey().Bytes(), string(bz.GetAlgo())), nil
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Import imports armored private key.
|
|
|
|
func (kb keyringKeybase) Import(name string, armor string) error {
|
|
|
|
bz, _ := kb.Get(name)
|
|
|
|
|
|
|
|
if bz != nil {
|
|
|
|
pubkey := bz.GetPubKey()
|
|
|
|
|
|
|
|
if len(pubkey.Bytes()) > 0 {
|
|
|
|
return fmt.Errorf("cannot overwrite data for name: %s", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 02:24:19 -07:00
|
|
|
infoBytes, err := crypto.UnarmorInfoBytes(armor)
|
2019-09-21 09:54:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
info, err := unmarshalInfo(infoBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
kb.writeInfo(name, info)
|
|
|
|
|
|
|
|
err = kb.db.Set(keyring.Item{
|
2020-03-30 07:54:01 -07:00
|
|
|
Key: string(addrHexKey(info.GetAddress())),
|
2019-09-21 09:54:14 -07:00
|
|
|
Data: infoKey(name),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExportPrivKey returns a private key in ASCII armored format. An error is returned
|
|
|
|
// if the key does not exist or a wrong encryption passphrase is supplied.
|
|
|
|
func (kb keyringKeybase) ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error) {
|
|
|
|
priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2020-01-14 07:40:10 -08:00
|
|
|
info, err := kb.Get(name)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2020-03-27 02:24:19 -07:00
|
|
|
return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// ImportPrivKey imports a private key in ASCII armor format. An error is returned
|
|
|
|
// if a key with the same name exists or a wrong encryption passphrase is
|
|
|
|
// supplied.
|
|
|
|
func (kb keyringKeybase) ImportPrivKey(name, armor, passphrase string) error {
|
|
|
|
if kb.HasKey(name) {
|
|
|
|
return fmt.Errorf("cannot overwrite key: %s", name)
|
|
|
|
}
|
|
|
|
|
2020-03-27 02:24:19 -07:00
|
|
|
privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase)
|
2019-09-21 09:54:14 -07:00
|
|
|
if err != nil {
|
2019-09-30 08:49:12 -07:00
|
|
|
return errors.Wrap(err, "failed to decrypt private key")
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
2019-09-30 08:49:12 -07:00
|
|
|
// NOTE: The keyring keystore has no need for a passphrase.
|
2020-01-14 07:40:10 -08:00
|
|
|
kb.writeLocalKey(name, privKey, "", SigningAlgo(algo))
|
2019-09-21 09:54:14 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasKey returns whether the key exists in the keyring.
|
|
|
|
func (kb keyringKeybase) HasKey(name string) bool {
|
|
|
|
bz, _ := kb.Get(name)
|
|
|
|
return bz != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ImportPubKey imports an ASCII-armored public key. It will store a new Info
|
|
|
|
// object holding a public key only, i.e. it will not be possible to sign with
|
|
|
|
// it as it lacks the secret key.
|
|
|
|
func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
|
|
|
|
bz, _ := kb.Get(name)
|
|
|
|
if bz != nil {
|
|
|
|
pubkey := bz.GetPubKey()
|
|
|
|
|
|
|
|
if len(pubkey.Bytes()) > 0 {
|
|
|
|
return fmt.Errorf("cannot overwrite data for name: %s", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 02:24:19 -07:00
|
|
|
pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor)
|
2019-09-21 09:54:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
pubKey, err := cryptoAmino.PubKeyFromBytes(pubBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-01-14 07:40:10 -08:00
|
|
|
kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
|
2019-09-21 09:54:14 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removes key forever, but we must present the proper passphrase before
|
|
|
|
// deleting it (for security). It returns an error if the key doesn't exist or
|
|
|
|
// passphrases don't match. The passphrase is ignored when deleting references to
|
|
|
|
// offline and Ledger / HW wallet keys.
|
2019-11-14 06:17:21 -08:00
|
|
|
func (kb keyringKeybase) Delete(name, _ string, _ bool) error {
|
2019-09-21 09:54:14 -07:00
|
|
|
// verify we have the proper password before deleting
|
|
|
|
info, err := kb.Get(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-03-30 07:54:01 -07:00
|
|
|
err = kb.db.Remove(string(addrHexKey(info.GetAddress())))
|
2019-09-21 09:54:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = kb.db.Remove(string(infoKey(name)))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update changes the passphrase with which an already stored key is encrypted.
|
|
|
|
// The oldpass must be the current passphrase used for encryption, getNewpass is
|
|
|
|
// a function to get the passphrase to permanently replace the current passphrase.
|
|
|
|
func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string, error)) error {
|
2020-03-01 09:16:22 -08:00
|
|
|
return errors.New("unsupported operation")
|
2019-09-21 09:54:14 -07:00
|
|
|
}
|
|
|
|
|
2020-01-14 07:40:10 -08:00
|
|
|
// SupportedAlgos returns a list of supported signing algorithms.
|
|
|
|
func (kb keyringKeybase) SupportedAlgos() []SigningAlgo {
|
|
|
|
return kb.base.SupportedAlgos()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
|
|
|
|
func (kb keyringKeybase) SupportedAlgosLedger() []SigningAlgo {
|
|
|
|
return kb.base.SupportedAlgosLedger()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (kb keyringKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, _ string, algo SigningAlgo) Info {
|
2019-09-21 09:54:14 -07:00
|
|
|
// encrypt private key using keyring
|
|
|
|
pub := priv.PubKey()
|
2020-01-14 07:40:10 -08:00
|
|
|
info := newLocalInfo(name, pub, string(priv.Bytes()), algo)
|
2019-09-21 09:54:14 -07:00
|
|
|
|
|
|
|
kb.writeInfo(name, info)
|
|
|
|
return info
|
|
|
|
}
|
|
|
|
|
|
|
|
func (kb keyringKeybase) writeInfo(name string, info Info) {
|
|
|
|
// write the info by key
|
|
|
|
key := infoKey(name)
|
|
|
|
serializedInfo := marshalInfo(info)
|
|
|
|
|
|
|
|
err := kb.db.Set(keyring.Item{
|
|
|
|
Key: string(key),
|
|
|
|
Data: serializedInfo,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = kb.db.Set(keyring.Item{
|
2020-03-30 07:54:01 -07:00
|
|
|
Key: string(addrHexKey(info.GetAddress())),
|
2019-09-21 09:54:14 -07:00
|
|
|
Data: key,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
2019-09-30 09:16:53 -07:00
|
|
|
|
2020-01-23 10:47:11 -08:00
|
|
|
func lkbToKeyringConfig(appName, dir string, buf io.Reader, test bool) keyring.Config {
|
2019-09-30 09:16:53 -07:00
|
|
|
if test {
|
|
|
|
return keyring.Config{
|
2020-01-23 08:48:00 -08:00
|
|
|
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
2020-01-23 10:47:11 -08:00
|
|
|
ServiceName: appName,
|
|
|
|
FileDir: filepath.Join(dir, fmt.Sprintf(testKeyringDirNameFmt, appName)),
|
2020-01-15 09:29:36 -08:00
|
|
|
FilePasswordFunc: func(_ string) (string, error) {
|
|
|
|
return "test", nil
|
|
|
|
},
|
2019-09-30 09:16:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-11 01:45:26 -08:00
|
|
|
return keyring.Config{
|
2020-01-23 10:47:11 -08:00
|
|
|
ServiceName: appName,
|
2019-12-11 01:45:26 -08:00
|
|
|
FileDir: dir,
|
|
|
|
FilePasswordFunc: newRealPrompt(dir, buf),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-23 10:47:11 -08:00
|
|
|
func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config {
|
2020-01-23 08:48:00 -08:00
|
|
|
return keyring.Config{
|
|
|
|
AllowedBackends: []keyring.BackendType{keyring.KWalletBackend},
|
|
|
|
ServiceName: "kdewallet",
|
2020-01-23 10:47:11 -08:00
|
|
|
KWalletAppID: appName,
|
2020-01-23 08:48:00 -08:00
|
|
|
KWalletFolder: "",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-23 10:47:11 -08:00
|
|
|
func newPassBackendKeyringConfig(appName, dir string, _ io.Reader) keyring.Config {
|
2020-03-23 08:40:47 -07:00
|
|
|
prefix := fmt.Sprintf(passKeyringPrefix, appName)
|
2020-01-23 08:48:00 -08:00
|
|
|
return keyring.Config{
|
|
|
|
AllowedBackends: []keyring.BackendType{keyring.PassBackend},
|
2020-01-23 10:47:11 -08:00
|
|
|
ServiceName: appName,
|
2020-01-23 08:48:00 -08:00
|
|
|
PassPrefix: prefix,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-11 01:45:26 -08:00
|
|
|
func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config {
|
2020-01-22 09:54:56 -08:00
|
|
|
fileDir := filepath.Join(dir, fmt.Sprintf(keyringDirNameFmt, name))
|
2019-12-11 01:45:26 -08:00
|
|
|
return keyring.Config{
|
2020-01-23 08:48:00 -08:00
|
|
|
AllowedBackends: []keyring.BackendType{keyring.FileBackend},
|
2019-12-11 01:45:26 -08:00
|
|
|
ServiceName: name,
|
|
|
|
FileDir: fileDir,
|
|
|
|
FilePasswordFunc: newRealPrompt(fileDir, buf),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) {
|
|
|
|
return func(prompt string) (string, error) {
|
2019-09-30 09:16:53 -07:00
|
|
|
keyhashStored := false
|
|
|
|
keyhashFilePath := filepath.Join(dir, "keyhash")
|
|
|
|
|
|
|
|
var keyhash []byte
|
|
|
|
|
|
|
|
_, err := os.Stat(keyhashFilePath)
|
|
|
|
switch {
|
|
|
|
case err == nil:
|
|
|
|
keyhash, err = ioutil.ReadFile(keyhashFilePath)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to read %s: %v", keyhashFilePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
keyhashStored = true
|
|
|
|
|
|
|
|
case os.IsNotExist(err):
|
|
|
|
keyhashStored = false
|
|
|
|
|
|
|
|
default:
|
|
|
|
return "", fmt.Errorf("failed to open %s: %v", keyhashFilePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
failureCounter := 0
|
|
|
|
for {
|
|
|
|
failureCounter++
|
|
|
|
if failureCounter > maxPassphraseEntryAttempts {
|
|
|
|
return "", fmt.Errorf("too many failed passphrase attempts")
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := bufio.NewReader(buf)
|
|
|
|
pass, err := input.GetPassword("Enter keyring passphrase:", buf)
|
|
|
|
if err != nil {
|
2020-03-03 05:32:52 -08:00
|
|
|
fmt.Fprintln(os.Stderr, err)
|
2019-09-30 09:16:53 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyhashStored {
|
|
|
|
if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, "incorrect passphrase")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return pass, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if pass != reEnteredPass {
|
|
|
|
fmt.Fprintln(os.Stderr, "passphrase do not match")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-03-18 05:59:08 -07:00
|
|
|
saltBytes := tmcrypto.CRandBytes(16)
|
2019-09-30 09:16:53 -07:00
|
|
|
passwordHash, err := bcrypt.GenerateFromPassword(saltBytes, []byte(pass), 2)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := ioutil.WriteFile(dir+"/keyhash", passwordHash, 0555); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return pass, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|