github.com/Finschia/finschia-sdk@v0.48.1/crypto/keyring/keyring.go (about)

     1  package keyring
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/hex"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"strings"
    12  
    13  	"github.com/99designs/keyring"
    14  	occrypto "github.com/Finschia/ostracon/crypto"
    15  	bip39 "github.com/cosmos/go-bip39"
    16  	"github.com/pkg/errors"
    17  	"github.com/tendermint/crypto/bcrypt"
    18  
    19  	"github.com/Finschia/finschia-sdk/client/input"
    20  	"github.com/Finschia/finschia-sdk/codec/legacy"
    21  	"github.com/Finschia/finschia-sdk/crypto"
    22  	"github.com/Finschia/finschia-sdk/crypto/hd"
    23  	"github.com/Finschia/finschia-sdk/crypto/ledger"
    24  	"github.com/Finschia/finschia-sdk/crypto/types"
    25  	sdk "github.com/Finschia/finschia-sdk/types"
    26  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    27  )
    28  
    29  // Backend options for Keyring
    30  const (
    31  	BackendFile    = "file"
    32  	BackendOS      = "os"
    33  	BackendKWallet = "kwallet"
    34  	BackendPass    = "pass"
    35  	BackendTest    = "test"
    36  	BackendMemory  = "memory"
    37  )
    38  
    39  const (
    40  	keyringFileDirName = "keyring-file"
    41  	keyringTestDirName = "keyring-test"
    42  	passKeyringPrefix  = "keyring-%s"
    43  )
    44  
    45  var (
    46  	_                          Keyring = &keystore{}
    47  	maxPassphraseEntryAttempts         = 3
    48  )
    49  
    50  // Keyring exposes operations over a backend supported by github.com/99designs/keyring.
    51  type Keyring interface {
    52  	// List all keys.
    53  	List() ([]Info, error)
    54  
    55  	// Supported signing algorithms for Keyring and Ledger respectively.
    56  	SupportedAlgorithms() (SigningAlgoList, SigningAlgoList)
    57  
    58  	// Key and KeyByAddress return keys by uid and address respectively.
    59  	Key(uid string) (Info, error)
    60  	KeyByAddress(address sdk.Address) (Info, error)
    61  
    62  	// Delete and DeleteByAddress remove keys from the keyring.
    63  	Delete(uid string) error
    64  	DeleteByAddress(address sdk.Address) error
    65  
    66  	// NewMnemonic generates a new mnemonic, derives a hierarchical deterministic key from it, and
    67  	// persists the key to storage. Returns the generated mnemonic and the key Info.
    68  	// It returns an error if it fails to generate a key for the given algo type, or if
    69  	// another key is already stored under the same name or address.
    70  	//
    71  	// A passphrase set to the empty string will set the passphrase to the DefaultBIP39Passphrase value.
    72  	NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (Info, string, error)
    73  
    74  	// NewAccount converts a mnemonic to a private key and BIP-39 HD Path and persists it.
    75  	// It fails if there is an existing key Info with the same address.
    76  	NewAccount(uid, mnemonic, bip39Passphrase, hdPath string, algo SignatureAlgo) (Info, error)
    77  
    78  	// SaveLedgerKey retrieves a public key reference from a Ledger device and persists it.
    79  	SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error)
    80  
    81  	// SavePubKey stores a public key and returns the persisted Info structure.
    82  	SavePubKey(uid string, pubkey types.PubKey, algo hd.PubKeyType) (Info, error)
    83  
    84  	// SaveMultisig stores and returns a new multsig (offline) key reference.
    85  	SaveMultisig(uid string, pubkey types.PubKey) (Info, error)
    86  
    87  	Signer
    88  
    89  	Importer
    90  	Exporter
    91  }
    92  
    93  // UnsafeKeyring exposes unsafe operations such as unsafe unarmored export in
    94  // addition to those that are made available by the Keyring interface.
    95  type UnsafeKeyring interface {
    96  	Keyring
    97  	UnsafeExporter
    98  }
    99  
   100  // Signer is implemented by key stores that want to provide signing capabilities.
   101  type Signer interface {
   102  	// Sign sign byte messages with a user key.
   103  	Sign(uid string, msg []byte) ([]byte, types.PubKey, error)
   104  
   105  	// SignByAddress sign byte messages with a user key providing the address.
   106  	SignByAddress(address sdk.Address, msg []byte) ([]byte, types.PubKey, error)
   107  }
   108  
   109  // Importer is implemented by key stores that support import of public and private keys.
   110  type Importer interface {
   111  	// ImportPrivKey imports ASCII armored passphrase-encrypted private keys.
   112  	ImportPrivKey(uid, armor, passphrase string) error
   113  
   114  	// ImportPubKey imports ASCII armored public keys.
   115  	ImportPubKey(uid string, armor string) error
   116  }
   117  
   118  // LegacyInfoImporter is implemented by key stores that support import of Info types.
   119  type LegacyInfoImporter interface {
   120  	// ImportInfo import a keyring.Info into the current keyring.
   121  	// It is used to migrate multisig, ledger, and public key Info structure.
   122  	ImportInfo(oldInfo Info) error
   123  }
   124  
   125  // Exporter is implemented by key stores that support export of public and private keys.
   126  type Exporter interface {
   127  	// Export public key
   128  	ExportPubKeyArmor(uid string) (string, error)
   129  	ExportPubKeyArmorByAddress(address sdk.Address) (string, error)
   130  
   131  	// ExportPrivKeyArmor returns a private key in ASCII armored format.
   132  	// It returns an error if the key does not exist or a wrong encryption passphrase is supplied.
   133  	ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error)
   134  	ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error)
   135  }
   136  
   137  // UnsafeExporter is implemented by key stores that support unsafe export
   138  // of private keys' material.
   139  type UnsafeExporter interface {
   140  	// UnsafeExportPrivKeyHex returns a private key in unarmored hex format
   141  	UnsafeExportPrivKeyHex(uid string) (string, error)
   142  }
   143  
   144  // Option overrides keyring configuration options.
   145  type Option func(options *Options)
   146  
   147  // Options define the options of the Keyring.
   148  type Options struct {
   149  	// supported signing algorithms for keyring
   150  	SupportedAlgos SigningAlgoList
   151  	// supported signing algorithms for Ledger
   152  	SupportedAlgosLedger SigningAlgoList
   153  }
   154  
   155  // NewInMemory creates a transient keyring useful for testing
   156  // purposes and on-the-fly key generation.
   157  // Keybase options can be applied when generating this new Keybase.
   158  func NewInMemory(opts ...Option) Keyring {
   159  	return newKeystore(keyring.NewArrayKeyring(nil), opts...)
   160  }
   161  
   162  // New creates a new instance of a keyring.
   163  // Keyring ptions can be applied when generating the new instance.
   164  // Available backends are "os", "file", "kwallet", "memory", "pass", "test".
   165  func New(
   166  	appName, backend, rootDir string, userInput io.Reader, opts ...Option,
   167  ) (Keyring, error) {
   168  	var (
   169  		db  keyring.Keyring
   170  		err error
   171  	)
   172  
   173  	switch backend {
   174  	case BackendMemory:
   175  		return NewInMemory(opts...), err
   176  	case BackendTest:
   177  		db, err = keyring.Open(newTestBackendKeyringConfig(appName, rootDir))
   178  	case BackendFile:
   179  		db, err = keyring.Open(newFileBackendKeyringConfig(appName, rootDir, userInput))
   180  	case BackendOS:
   181  		db, err = keyring.Open(newOSBackendKeyringConfig(appName, rootDir, userInput))
   182  	case BackendKWallet:
   183  		db, err = keyring.Open(newKWalletBackendKeyringConfig(appName, rootDir, userInput))
   184  	case BackendPass:
   185  		db, err = keyring.Open(newPassBackendKeyringConfig(appName, rootDir, userInput))
   186  	default:
   187  		return nil, fmt.Errorf("unknown keyring backend %v", backend)
   188  	}
   189  
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	return newKeystore(db, opts...), nil
   195  }
   196  
   197  type keystore struct {
   198  	db      keyring.Keyring
   199  	options Options
   200  }
   201  
   202  func infoKey(name string) string   { return fmt.Sprintf("%s.%s", name, infoSuffix) }
   203  func infoKeyBz(name string) []byte { return []byte(infoKey(name)) }
   204  
   205  func newKeystore(kr keyring.Keyring, opts ...Option) keystore {
   206  	// Default options for keybase
   207  	options := Options{
   208  		SupportedAlgos:       SigningAlgoList{hd.Secp256k1},
   209  		SupportedAlgosLedger: SigningAlgoList{hd.Secp256k1},
   210  	}
   211  
   212  	for _, optionFn := range opts {
   213  		optionFn(&options)
   214  	}
   215  
   216  	return keystore{kr, options}
   217  }
   218  
   219  func (ks keystore) ExportPubKeyArmor(uid string) (string, error) {
   220  	bz, err := ks.Key(uid)
   221  	if err != nil {
   222  		return "", err
   223  	}
   224  
   225  	if bz == nil {
   226  		return "", fmt.Errorf("no key to export with name: %s", uid)
   227  	}
   228  
   229  	return crypto.ArmorPubKeyBytes(legacy.Cdc.MustMarshal(bz.GetPubKey()), string(bz.GetAlgo())), nil
   230  }
   231  
   232  func (ks keystore) ExportPubKeyArmorByAddress(address sdk.Address) (string, error) {
   233  	info, err := ks.KeyByAddress(address)
   234  	if err != nil {
   235  		return "", err
   236  	}
   237  
   238  	return ks.ExportPubKeyArmor(info.GetName())
   239  }
   240  
   241  func (ks keystore) ExportPrivKeyArmor(uid, encryptPassphrase string) (armor string, err error) {
   242  	priv, err := ks.ExportPrivateKeyObject(uid)
   243  	if err != nil {
   244  		return "", err
   245  	}
   246  
   247  	info, err := ks.Key(uid)
   248  	if err != nil {
   249  		return "", err
   250  	}
   251  
   252  	return crypto.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
   253  }
   254  
   255  // ExportPrivateKeyObject exports an armored private key object.
   256  func (ks keystore) ExportPrivateKeyObject(uid string) (types.PrivKey, error) {
   257  	info, err := ks.Key(uid)
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	var priv types.PrivKey
   263  
   264  	switch linfo := info.(type) {
   265  	case localInfo:
   266  		if linfo.PrivKeyArmor == "" {
   267  			err = fmt.Errorf("private key not available")
   268  			return nil, err
   269  		}
   270  
   271  		priv, err = legacy.PrivKeyFromBytes([]byte(linfo.PrivKeyArmor))
   272  		if err != nil {
   273  			return nil, err
   274  		}
   275  
   276  	case ledgerInfo, offlineInfo, multiInfo:
   277  		return nil, errors.New("only works on local private keys")
   278  	}
   279  
   280  	return priv, nil
   281  }
   282  
   283  func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassphrase string) (armor string, err error) {
   284  	byAddress, err := ks.KeyByAddress(address)
   285  	if err != nil {
   286  		return "", err
   287  	}
   288  
   289  	return ks.ExportPrivKeyArmor(byAddress.GetName(), encryptPassphrase)
   290  }
   291  
   292  func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error {
   293  	if _, err := ks.Key(uid); err == nil {
   294  		return fmt.Errorf("cannot overwrite key: %s", uid)
   295  	}
   296  
   297  	privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase)
   298  	if err != nil {
   299  		return errors.Wrap(err, "failed to decrypt private key")
   300  	}
   301  
   302  	_, err = ks.writeLocalKey(uid, privKey, hd.PubKeyType(algo))
   303  	if err != nil {
   304  		return err
   305  	}
   306  
   307  	return nil
   308  }
   309  
   310  func (ks keystore) ImportPubKey(uid string, armor string) error {
   311  	if _, err := ks.Key(uid); err == nil {
   312  		return fmt.Errorf("cannot overwrite key: %s", uid)
   313  	}
   314  
   315  	pubBytes, algo, err := crypto.UnarmorPubKeyBytes(armor)
   316  	if err != nil {
   317  		return err
   318  	}
   319  
   320  	pubKey, err := legacy.PubKeyFromBytes(pubBytes)
   321  	if err != nil {
   322  		return err
   323  	}
   324  
   325  	_, err = ks.writeOfflineKey(uid, pubKey, hd.PubKeyType(algo))
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	return nil
   331  }
   332  
   333  // ImportInfo implements Importer.MigrateInfo.
   334  func (ks keystore) ImportInfo(oldInfo Info) error {
   335  	if _, err := ks.Key(oldInfo.GetName()); err == nil {
   336  		return fmt.Errorf("cannot overwrite key: %s", oldInfo.GetName())
   337  	}
   338  
   339  	return ks.writeInfo(oldInfo)
   340  }
   341  
   342  func (ks keystore) Sign(uid string, msg []byte) ([]byte, types.PubKey, error) {
   343  	info, err := ks.Key(uid)
   344  	if err != nil {
   345  		return nil, nil, err
   346  	}
   347  
   348  	var priv types.PrivKey
   349  
   350  	switch i := info.(type) {
   351  	case localInfo:
   352  		if i.PrivKeyArmor == "" {
   353  			return nil, nil, fmt.Errorf("private key not available")
   354  		}
   355  
   356  		priv, err = legacy.PrivKeyFromBytes([]byte(i.PrivKeyArmor))
   357  		if err != nil {
   358  			return nil, nil, err
   359  		}
   360  
   361  	case ledgerInfo:
   362  		return SignWithLedger(info, msg)
   363  
   364  	case offlineInfo, multiInfo:
   365  		return nil, info.GetPubKey(), errors.New("cannot sign with offline keys")
   366  	}
   367  
   368  	sig, err := priv.Sign(msg)
   369  	if err != nil {
   370  		return nil, nil, err
   371  	}
   372  
   373  	return sig, priv.PubKey(), nil
   374  }
   375  
   376  func (ks keystore) SignByAddress(address sdk.Address, msg []byte) ([]byte, types.PubKey, error) {
   377  	key, err := ks.KeyByAddress(address)
   378  	if err != nil {
   379  		return nil, nil, err
   380  	}
   381  
   382  	return ks.Sign(key.GetName(), msg)
   383  }
   384  
   385  func (ks keystore) SaveLedgerKey(uid string, algo SignatureAlgo, hrp string, coinType, account, index uint32) (Info, error) {
   386  	if !ks.options.SupportedAlgosLedger.Contains(algo) {
   387  		return nil, fmt.Errorf(
   388  			"%w: signature algo %s is not defined in the keyring options",
   389  			ErrUnsupportedSigningAlgo, algo.Name(),
   390  		)
   391  	}
   392  
   393  	hdPath := hd.NewFundraiserParams(account, coinType, index)
   394  
   395  	priv, _, err := ledger.NewPrivKeySecp256k1(*hdPath, hrp)
   396  	if err != nil {
   397  		return nil, fmt.Errorf("failed to generate ledger key: %w", err)
   398  	}
   399  
   400  	return ks.writeLedgerKey(uid, priv.PubKey(), *hdPath, algo.Name())
   401  }
   402  
   403  func (ks keystore) writeLedgerKey(name string, pub types.PubKey, path hd.BIP44Params, algo hd.PubKeyType) (Info, error) {
   404  	info := newLedgerInfo(name, pub, path, algo)
   405  	if err := ks.writeInfo(info); err != nil {
   406  		return nil, err
   407  	}
   408  
   409  	return info, nil
   410  }
   411  
   412  func (ks keystore) SaveMultisig(uid string, pubkey types.PubKey) (Info, error) {
   413  	return ks.writeMultisigKey(uid, pubkey)
   414  }
   415  
   416  func (ks keystore) SavePubKey(uid string, pubkey types.PubKey, algo hd.PubKeyType) (Info, error) {
   417  	return ks.writeOfflineKey(uid, pubkey, algo)
   418  }
   419  
   420  func (ks keystore) DeleteByAddress(address sdk.Address) error {
   421  	info, err := ks.KeyByAddress(address)
   422  	if err != nil {
   423  		return err
   424  	}
   425  
   426  	err = ks.Delete(info.GetName())
   427  	if err != nil {
   428  		return err
   429  	}
   430  
   431  	return nil
   432  }
   433  
   434  func (ks keystore) Delete(uid string) error {
   435  	info, err := ks.Key(uid)
   436  	if err != nil {
   437  		return err
   438  	}
   439  
   440  	err = ks.db.Remove(addrHexKeyAsString(info.GetAddress()))
   441  	if err != nil {
   442  		return err
   443  	}
   444  
   445  	err = ks.db.Remove(infoKey(uid))
   446  	if err != nil {
   447  		return err
   448  	}
   449  
   450  	return nil
   451  }
   452  
   453  func (ks keystore) KeyByAddress(address sdk.Address) (Info, error) {
   454  	ik, err := ks.db.Get(addrHexKeyAsString(address))
   455  	if err != nil {
   456  		return nil, wrapKeyNotFound(err, fmt.Sprint("key with address", address, "not found"))
   457  	}
   458  
   459  	if len(ik.Data) == 0 {
   460  		return nil, wrapKeyNotFound(err, fmt.Sprint("key with address", address, "not found"))
   461  	}
   462  	return ks.key(string(ik.Data))
   463  }
   464  
   465  func wrapKeyNotFound(err error, msg string) error {
   466  	if err == keyring.ErrKeyNotFound {
   467  		return sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, msg)
   468  	}
   469  	return err
   470  }
   471  
   472  func (ks keystore) List() ([]Info, error) {
   473  	res := []Info{}
   474  
   475  	keys, err := ks.db.Keys()
   476  	if err != nil {
   477  		return nil, err
   478  	}
   479  
   480  	if len(keys) == 0 {
   481  		return res, nil
   482  	}
   483  
   484  	sort.Strings(keys)
   485  	for _, key := range keys {
   486  		if strings.HasSuffix(key, infoSuffix) {
   487  			rawInfo, err := ks.db.Get(key)
   488  			if err != nil {
   489  				fmt.Printf("err for key %s: %q\n", key, err)
   490  
   491  				// add the name of the key in case the user wants to retrieve it
   492  				// afterwards
   493  				info := newOfflineInfo(key, nil, hd.PubKeyType(""))
   494  				res = append(res, info)
   495  				continue
   496  			}
   497  
   498  			if len(rawInfo.Data) == 0 {
   499  				fmt.Println(sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, key))
   500  
   501  				// add the name of the key in case the user wants to retrieve it
   502  				// afterwards
   503  				info := newOfflineInfo(key, nil, hd.PubKeyType(""))
   504  				res = append(res, info)
   505  				continue
   506  			}
   507  
   508  			info, err := unmarshalInfo(rawInfo.Data)
   509  			if err != nil {
   510  				fmt.Printf("err for key %s: %q\n", key, err)
   511  
   512  				// add the name of the key in case the user wants to retrieve it
   513  				// afterwards
   514  				info = newOfflineInfo(key, nil, hd.PubKeyType(""))
   515  			}
   516  
   517  			res = append(res, info)
   518  		}
   519  	}
   520  
   521  	return res, nil
   522  }
   523  
   524  func (ks keystore) NewMnemonic(uid string, language Language, hdPath, bip39Passphrase string, algo SignatureAlgo) (Info, string, error) {
   525  	if language != English {
   526  		return nil, "", ErrUnsupportedLanguage
   527  	}
   528  
   529  	if !ks.isSupportedSigningAlgo(algo) {
   530  		return nil, "", ErrUnsupportedSigningAlgo
   531  	}
   532  
   533  	// Default number of words (24): This generates a mnemonic directly from the
   534  	// number of words by reading system entropy.
   535  	entropy, err := bip39.NewEntropy(defaultEntropySize)
   536  	if err != nil {
   537  		return nil, "", err
   538  	}
   539  
   540  	mnemonic, err := bip39.NewMnemonic(entropy)
   541  	if err != nil {
   542  		return nil, "", err
   543  	}
   544  
   545  	if bip39Passphrase == "" {
   546  		bip39Passphrase = DefaultBIP39Passphrase
   547  	}
   548  
   549  	info, err := ks.NewAccount(uid, mnemonic, bip39Passphrase, hdPath, algo)
   550  	if err != nil {
   551  		return nil, "", err
   552  	}
   553  
   554  	return info, mnemonic, nil
   555  }
   556  
   557  func (ks keystore) NewAccount(name string, mnemonic string, bip39Passphrase string, hdPath string, algo SignatureAlgo) (Info, error) {
   558  	if !ks.isSupportedSigningAlgo(algo) {
   559  		return nil, ErrUnsupportedSigningAlgo
   560  	}
   561  
   562  	// create master key and derive first key for keyring
   563  	derivedPriv, err := algo.Derive()(mnemonic, bip39Passphrase, hdPath)
   564  	if err != nil {
   565  		return nil, err
   566  	}
   567  
   568  	privKey := algo.Generate()(derivedPriv)
   569  
   570  	// check if the a key already exists with the same address and return an error
   571  	// if found
   572  	address := sdk.AccAddress(privKey.PubKey().Address())
   573  	if _, err := ks.KeyByAddress(address); err == nil {
   574  		return nil, fmt.Errorf("account with address %s already exists in keyring, delete the key first if you want to recreate it", address)
   575  	}
   576  
   577  	return ks.writeLocalKey(name, privKey, algo.Name())
   578  }
   579  
   580  func (ks keystore) isSupportedSigningAlgo(algo SignatureAlgo) bool {
   581  	return ks.options.SupportedAlgos.Contains(algo)
   582  }
   583  
   584  func (ks keystore) key(infoKey string) (Info, error) {
   585  	bs, err := ks.db.Get(infoKey)
   586  	if err != nil {
   587  		return nil, wrapKeyNotFound(err, infoKey)
   588  	}
   589  	if len(bs.Data) == 0 {
   590  		return nil, sdkerrors.Wrap(sdkerrors.ErrKeyNotFound, infoKey)
   591  	}
   592  	return unmarshalInfo(bs.Data)
   593  }
   594  
   595  func (ks keystore) Key(uid string) (Info, error) {
   596  	return ks.key(infoKey(uid))
   597  }
   598  
   599  // SupportedAlgorithms returns the keystore Options' supported signing algorithm.
   600  // for the keyring and Ledger.
   601  func (ks keystore) SupportedAlgorithms() (SigningAlgoList, SigningAlgoList) {
   602  	return ks.options.SupportedAlgos, ks.options.SupportedAlgosLedger
   603  }
   604  
   605  // SignWithLedger signs a binary message with the ledger device referenced by an Info object
   606  // and returns the signed bytes and the public key. It returns an error if the device could
   607  // not be queried or it returned an error.
   608  func SignWithLedger(info Info, msg []byte) (sig []byte, pub types.PubKey, err error) {
   609  	switch info.(type) {
   610  	case *ledgerInfo, ledgerInfo:
   611  	default:
   612  		return nil, nil, errors.New("not a ledger object")
   613  	}
   614  
   615  	path, err := info.GetPath()
   616  	if err != nil {
   617  		return
   618  	}
   619  
   620  	priv, err := ledger.NewPrivKeySecp256k1Unsafe(*path)
   621  	if err != nil {
   622  		return
   623  	}
   624  
   625  	sig, err = priv.Sign(msg)
   626  	if err != nil {
   627  		return nil, nil, err
   628  	}
   629  
   630  	return sig, priv.PubKey(), nil
   631  }
   632  
   633  func newOSBackendKeyringConfig(appName, dir string, buf io.Reader) keyring.Config {
   634  	return keyring.Config{
   635  		ServiceName:              appName,
   636  		FileDir:                  dir,
   637  		KeychainTrustApplication: true,
   638  		FilePasswordFunc:         newRealPrompt(dir, buf),
   639  	}
   640  }
   641  
   642  func newTestBackendKeyringConfig(appName, dir string) keyring.Config {
   643  	return keyring.Config{
   644  		AllowedBackends: []keyring.BackendType{keyring.FileBackend},
   645  		ServiceName:     appName,
   646  		FileDir:         filepath.Join(dir, keyringTestDirName),
   647  		FilePasswordFunc: func(_ string) (string, error) {
   648  			return "test", nil
   649  		},
   650  	}
   651  }
   652  
   653  func newKWalletBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config {
   654  	return keyring.Config{
   655  		AllowedBackends: []keyring.BackendType{keyring.KWalletBackend},
   656  		ServiceName:     "kdewallet",
   657  		KWalletAppID:    appName,
   658  		KWalletFolder:   "",
   659  	}
   660  }
   661  
   662  func newPassBackendKeyringConfig(appName, _ string, _ io.Reader) keyring.Config {
   663  	prefix := fmt.Sprintf(passKeyringPrefix, appName)
   664  
   665  	return keyring.Config{
   666  		AllowedBackends: []keyring.BackendType{keyring.PassBackend},
   667  		ServiceName:     appName,
   668  		PassPrefix:      prefix,
   669  	}
   670  }
   671  
   672  func newFileBackendKeyringConfig(name, dir string, buf io.Reader) keyring.Config {
   673  	fileDir := filepath.Join(dir, keyringFileDirName)
   674  
   675  	return keyring.Config{
   676  		AllowedBackends:  []keyring.BackendType{keyring.FileBackend},
   677  		ServiceName:      name,
   678  		FileDir:          fileDir,
   679  		FilePasswordFunc: newRealPrompt(fileDir, buf),
   680  	}
   681  }
   682  
   683  func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) {
   684  	return func(prompt string) (string, error) {
   685  		keyhashStored := false
   686  		keyhashFilePath := filepath.Join(dir, "keyhash")
   687  
   688  		var keyhash []byte
   689  
   690  		_, err := os.Stat(keyhashFilePath)
   691  
   692  		switch {
   693  		case err == nil:
   694  			keyhash, err = os.ReadFile(keyhashFilePath)
   695  			if err != nil {
   696  				return "", fmt.Errorf("failed to read %s: %v", keyhashFilePath, err)
   697  			}
   698  
   699  			keyhashStored = true
   700  
   701  		case os.IsNotExist(err):
   702  			keyhashStored = false
   703  
   704  		default:
   705  			return "", fmt.Errorf("failed to open %s: %v", keyhashFilePath, err)
   706  		}
   707  
   708  		failureCounter := 0
   709  
   710  		for {
   711  			failureCounter++
   712  			if failureCounter > maxPassphraseEntryAttempts {
   713  				return "", fmt.Errorf("too many failed passphrase attempts")
   714  			}
   715  
   716  			buf := bufio.NewReader(buf)
   717  			pass, err := input.GetPassword("Enter keyring passphrase:", buf)
   718  			if err != nil {
   719  				// NOTE: LGTM.io reports a false positive alert that states we are printing the password,
   720  				// but we only log the error.
   721  				//
   722  				// lgtm [go/clear-text-logging]
   723  				fmt.Fprintln(os.Stderr, err)
   724  				continue
   725  			}
   726  
   727  			if keyhashStored {
   728  				if err := bcrypt.CompareHashAndPassword(keyhash, []byte(pass)); err != nil {
   729  					fmt.Fprintln(os.Stderr, "incorrect passphrase")
   730  					continue
   731  				}
   732  
   733  				return pass, nil
   734  			}
   735  
   736  			reEnteredPass, err := input.GetPassword("Re-enter keyring passphrase:", buf)
   737  			if err != nil {
   738  				// NOTE: LGTM.io reports a false positive alert that states we are printing the password,
   739  				// but we only log the error.
   740  				//
   741  				// lgtm [go/clear-text-logging]
   742  				fmt.Fprintln(os.Stderr, err)
   743  				continue
   744  			}
   745  
   746  			if pass != reEnteredPass {
   747  				fmt.Fprintln(os.Stderr, "passphrase do not match")
   748  				continue
   749  			}
   750  
   751  			saltBytes := occrypto.CRandBytes(16)
   752  			passwordHash, err := bcrypt.GenerateFromPassword(saltBytes, []byte(pass), 2)
   753  			if err != nil {
   754  				fmt.Fprintln(os.Stderr, err)
   755  				continue
   756  			}
   757  
   758  			if err := os.WriteFile(dir+"/keyhash", passwordHash, 0o600); err != nil {
   759  				return "", err
   760  			}
   761  
   762  			return pass, nil
   763  		}
   764  	}
   765  }
   766  
   767  func (ks keystore) writeLocalKey(name string, priv types.PrivKey, algo hd.PubKeyType) (Info, error) {
   768  	// encrypt private key using keyring
   769  	pub := priv.PubKey()
   770  	info := newLocalInfo(name, pub, string(legacy.Cdc.MustMarshal(priv)), algo)
   771  	if err := ks.writeInfo(info); err != nil {
   772  		return nil, err
   773  	}
   774  
   775  	return info, nil
   776  }
   777  
   778  func (ks keystore) writeInfo(info Info) error {
   779  	key := infoKeyBz(info.GetName())
   780  	serializedInfo := marshalInfo(info)
   781  
   782  	exists, err := ks.existsInDb(info)
   783  	if err != nil {
   784  		return err
   785  	}
   786  	if exists {
   787  		return errors.New("public key already exists in keybase")
   788  	}
   789  
   790  	err = ks.db.Set(keyring.Item{
   791  		Key:  string(key),
   792  		Data: serializedInfo,
   793  	})
   794  	if err != nil {
   795  		return err
   796  	}
   797  
   798  	err = ks.db.Set(keyring.Item{
   799  		Key:  addrHexKeyAsString(info.GetAddress()),
   800  		Data: key,
   801  	})
   802  	if err != nil {
   803  		return err
   804  	}
   805  
   806  	return nil
   807  }
   808  
   809  // existsInDb returns true if key is in DB. Error is returned only when we have error
   810  // different thant ErrKeyNotFound
   811  func (ks keystore) existsInDb(info Info) (bool, error) {
   812  	if _, err := ks.db.Get(addrHexKeyAsString(info.GetAddress())); err == nil {
   813  		return true, nil // address lookup succeeds - info exists
   814  	} else if err != keyring.ErrKeyNotFound {
   815  		return false, err // received unexpected error - returns error
   816  	}
   817  
   818  	if _, err := ks.db.Get(infoKey(info.GetName())); err == nil {
   819  		return true, nil // uid lookup succeeds - info exists
   820  	} else if err != keyring.ErrKeyNotFound {
   821  		return false, err // received unexpected error - returns
   822  	}
   823  
   824  	// both lookups failed, info does not exist
   825  	return false, nil
   826  }
   827  
   828  func (ks keystore) writeOfflineKey(name string, pub types.PubKey, algo hd.PubKeyType) (Info, error) {
   829  	info := newOfflineInfo(name, pub, algo)
   830  	err := ks.writeInfo(info)
   831  	if err != nil {
   832  		return nil, err
   833  	}
   834  
   835  	return info, nil
   836  }
   837  
   838  func (ks keystore) writeMultisigKey(name string, pub types.PubKey) (Info, error) {
   839  	info, err := NewMultiInfo(name, pub)
   840  	if err != nil {
   841  		return nil, err
   842  	}
   843  	if err = ks.writeInfo(info); err != nil {
   844  		return nil, err
   845  	}
   846  
   847  	return info, nil
   848  }
   849  
   850  type unsafeKeystore struct {
   851  	keystore
   852  }
   853  
   854  // NewUnsafe returns a new keyring that provides support for unsafe operations.
   855  func NewUnsafe(kr Keyring) UnsafeKeyring {
   856  	// The type assertion is against the only keystore
   857  	// implementation that is currently provided.
   858  	ks := kr.(keystore)
   859  
   860  	return unsafeKeystore{ks}
   861  }
   862  
   863  // UnsafeExportPrivKeyHex exports private keys in unarmored hexadecimal format.
   864  func (ks unsafeKeystore) UnsafeExportPrivKeyHex(uid string) (privkey string, err error) {
   865  	priv, err := ks.ExportPrivateKeyObject(uid)
   866  	if err != nil {
   867  		return "", err
   868  	}
   869  
   870  	return hex.EncodeToString(priv.Bytes()), nil
   871  }
   872  
   873  func addrHexKeyAsString(address sdk.Address) string {
   874  	return fmt.Sprintf("%s.%s", hex.EncodeToString(address.Bytes()), addressSuffix)
   875  }