cosmos-sdk/crypto/keyring/legacy.go

236 lines
5.6 KiB
Go

package keyring
import (
"fmt"
"io"
"strings"
"github.com/pkg/errors"
tmcrypto "github.com/tendermint/tendermint/crypto"
tmos "github.com/tendermint/tendermint/libs/os"
dbm "github.com/tendermint/tm-db"
"github.com/cosmos/cosmos-sdk/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// LegacyKeybase is implemented by the legacy keybase implementation.
type LegacyKeybase interface {
List() ([]Info, error)
Export(name string) (armor string, err error)
ExportPrivKey(name, decryptPassphrase, encryptPassphrase string) (armor string, err error)
ExportPubKey(name string) (armor string, err error)
Close() error
}
// NewLegacy creates a new instance of a legacy keybase.
func NewLegacy(name, dir string, opts ...KeybaseOption) (LegacyKeybase, error) {
if err := tmos.EnsureDir(dir, 0700); err != nil {
return nil, fmt.Errorf("failed to create Keybase directory: %s", err)
}
db, err := sdk.NewLevelDB(name, dir)
if err != nil {
return nil, err
}
return newDBKeybase(db), nil
}
var _ LegacyKeybase = dbKeybase{}
// dbKeybase combines encryption and storage implementation to provide a
// full-featured key manager.
//
// NOTE: dbKeybase will be deprecated in favor of keyringKeybase.
type dbKeybase struct {
db dbm.DB
}
// newDBKeybase creates a new dbKeybase instance using the provided DB for
// reading and writing keys.
func newDBKeybase(db dbm.DB) dbKeybase {
return dbKeybase{
db: db,
}
}
// List returns the keys from storage in alphabetical order.
func (kb dbKeybase) List() ([]Info, error) {
var res []Info
iter, err := kb.db.Iterator(nil, nil)
if err != nil {
return nil, err
}
defer iter.Close()
for ; iter.Valid(); iter.Next() {
key := string(iter.Key())
// need to include only keys in storage that have an info suffix
if strings.HasSuffix(key, infoSuffix) {
info, err := unmarshalInfo(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) {
bs, err := kb.db.Get(infoKey(name))
if err != nil {
return nil, err
}
if len(bs) == 0 {
return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, name)
}
return unmarshalInfo(bs)
}
// ExportPrivateKeyObject returns a PrivKey object given the key name and
// passphrase. An error is returned if the key does not exist or if the Info for
// the key is invalid.
func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcrypto.PrivKey, error) {
info, err := kb.Get(name)
if err != nil {
return nil, err
}
var priv tmcrypto.PrivKey
switch i := info.(type) {
case localInfo:
linfo := i
if linfo.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return nil, err
}
priv, _, err = crypto.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, err
}
case ledgerInfo, offlineInfo, multiInfo:
return nil, errors.New("only works on local private keys")
}
return priv, nil
}
func (kb dbKeybase) Export(name string) (armor string, err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return "", err
}
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
return crypto.ArmorInfoBytes(bz), nil
}
// ExportPubKey returns public keys in ASCII armored format. It retrieves a Info
// object by its name and return the public key in a portable format.
func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
bz, err := kb.db.Get(infoKey(name))
if err != nil {
return "", err
}
if bz == nil {
return "", fmt.Errorf("no key to export with name %s", name)
}
info, err := unmarshalInfo(bz)
if err != nil {
return
}
return crypto.ArmorPubKeyBytes(info.GetPubKey().Bytes(), string(info.GetAlgo())), nil
}
// ExportPrivKey returns a private key in ASCII armored format.
// It returns an error if the key does not exist or a wrong encryption passphrase
// is supplied.
func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
encryptPassphrase string) (armor string, err error) {
priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
if err != nil {
return "", err
}
info, err := kb.Get(name)
if err != nil {
return "", err
}
return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
}
// Close the underlying storage.
func (kb dbKeybase) Close() error { return kb.db.Close() }
func infoKey(name string) []byte { return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) }
// InfoImporter is implemented by those types that want to provide functions necessary
// to migrate keys from LegacyKeybase types to Keyring types.
type InfoImporter interface {
// Import imports ASCII-armored private keys.
Import(uid string, armor string) error
}
type keyringMigrator struct {
kr keystore
}
func NewInfoImporter(
appName, backend, rootDir string, userInput io.Reader, opts ...Option,
) (InfoImporter, error) {
keyring, err := New(appName, backend, rootDir, userInput, opts...)
if err != nil {
return keyringMigrator{}, err
}
kr := keyring.(keystore)
return keyringMigrator{kr}, nil
}
func (m keyringMigrator) Import(uid string, armor string) error {
_, err := m.kr.Key(uid)
if err == nil {
return fmt.Errorf("cannot overwrite key %q", uid)
}
infoBytes, err := crypto.UnarmorInfoBytes(armor)
if err != nil {
return err
}
info, err := unmarshalInfo(infoBytes)
if err != nil {
return err
}
return m.kr.writeInfo(info)
}
// KeybaseOption overrides options for the db.
type KeybaseOption func(*kbOptions)
type kbOptions struct {
}