github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/crypto/keys/keybase_base.go (about)

     1  package keys
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"github.com/mitchellh/go-homedir"
     7  	"os"
     8  	"strings"
     9  
    10  	"github.com/cosmos/go-bip39"
    11  	tmcrypto "github.com/fibonacci-chain/fbc/libs/tendermint/crypto"
    12  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto/secp256k1"
    13  	"github.com/pkg/errors"
    14  
    15  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto"
    16  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto/keys/hd"
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    18  )
    19  
    20  type (
    21  	kbOptions struct {
    22  		keygenFunc           PrivKeyGenFunc
    23  		deriveFunc           DeriveKeyFunc
    24  		supportedAlgos       []SigningAlgo
    25  		supportedAlgosLedger []SigningAlgo
    26  	}
    27  
    28  	// baseKeybase is an auxiliary type that groups Keybase storage agnostic features
    29  	// together.
    30  	baseKeybase struct {
    31  		options kbOptions
    32  	}
    33  
    34  	keyWriter interface {
    35  		writeLocalKeyer
    36  		infoWriter
    37  	}
    38  
    39  	writeLocalKeyer interface {
    40  		writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info
    41  	}
    42  
    43  	infoWriter interface {
    44  		writeInfo(name string, info Info)
    45  	}
    46  )
    47  
    48  // WithKeygenFunc applies an overridden key generation function to generate the private key.
    49  func WithKeygenFunc(f PrivKeyGenFunc) KeybaseOption {
    50  	return func(o *kbOptions) {
    51  		o.keygenFunc = f
    52  	}
    53  }
    54  
    55  // WithDeriveFunc applies an overridden key derivation function to generate the private key.
    56  func WithDeriveFunc(f DeriveKeyFunc) KeybaseOption {
    57  	return func(o *kbOptions) {
    58  		o.deriveFunc = f
    59  	}
    60  }
    61  
    62  // WithSupportedAlgos defines the list of accepted SigningAlgos.
    63  func WithSupportedAlgos(algos []SigningAlgo) KeybaseOption {
    64  	return func(o *kbOptions) {
    65  		o.supportedAlgos = algos
    66  	}
    67  }
    68  
    69  // WithSupportedAlgosLedger defines the list of accepted SigningAlgos compatible with Ledger.
    70  func WithSupportedAlgosLedger(algos []SigningAlgo) KeybaseOption {
    71  	return func(o *kbOptions) {
    72  		o.supportedAlgosLedger = algos
    73  	}
    74  }
    75  
    76  // newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type
    77  func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase {
    78  	// Default options for keybase
    79  	options := kbOptions{
    80  		keygenFunc:           StdPrivKeyGen,
    81  		deriveFunc:           StdDeriveKey,
    82  		supportedAlgos:       []SigningAlgo{Secp256k1},
    83  		supportedAlgosLedger: []SigningAlgo{Secp256k1},
    84  	}
    85  
    86  	for _, optionFn := range optionsFns {
    87  		optionFn(&options)
    88  	}
    89  
    90  	return baseKeybase{options: options}
    91  }
    92  
    93  // StdPrivKeyGen is the default PrivKeyGen function in the keybase.
    94  // For now, it only supports Secp256k1
    95  func StdPrivKeyGen(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) {
    96  	if algo == Secp256k1 {
    97  		return SecpPrivKeyGen(bz), nil
    98  	}
    99  	return nil, ErrUnsupportedSigningAlgo
   100  }
   101  
   102  // SecpPrivKeyGen generates a secp256k1 private key from the given bytes
   103  func SecpPrivKeyGen(bz []byte) tmcrypto.PrivKey {
   104  	var bzArr [32]byte
   105  	copy(bzArr[:], bz)
   106  	return secp256k1.PrivKeySecp256k1(bzArr)
   107  }
   108  
   109  // SignWithLedger signs a binary message with the ledger device referenced by an Info object
   110  // and returns the signed bytes and the public key. It returns an error if the device could
   111  // not be queried or it returned an error.
   112  func (kb baseKeybase) SignWithLedger(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
   113  	i := info.(ledgerInfo)
   114  	priv, err := crypto.NewPrivKeyLedgerSecp256k1Unsafe(i.Path)
   115  	if err != nil {
   116  		return
   117  	}
   118  
   119  	sig, err = priv.Sign(msg)
   120  	if err != nil {
   121  		return nil, nil, err
   122  	}
   123  
   124  	return sig, priv.PubKey(), nil
   125  }
   126  
   127  // DecodeSignature decodes a an length-prefixed binary signature from standard input
   128  // and return it as a byte slice.
   129  func (kb baseKeybase) DecodeSignature(info Info, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) {
   130  	_, err = fmt.Fprintf(os.Stderr, "Message to sign:\n\n%s\n", msg)
   131  	if err != nil {
   132  		return nil, nil, err
   133  	}
   134  
   135  	buf := bufio.NewReader(os.Stdin)
   136  	_, err = fmt.Fprintf(os.Stderr, "\nEnter Amino-encoded signature:\n")
   137  	if err != nil {
   138  		return nil, nil, err
   139  	}
   140  
   141  	// will block until user inputs the signature
   142  	signed, err := buf.ReadString('\n')
   143  	if err != nil {
   144  		return nil, nil, err
   145  	}
   146  
   147  	if err := CryptoCdc.UnmarshalBinaryLengthPrefixed([]byte(signed), sig); err != nil {
   148  		return nil, nil, errors.Wrap(err, "failed to decode signature")
   149  	}
   150  
   151  	return sig, info.GetPubKey(), nil
   152  }
   153  
   154  // CreateAccount creates an account Info object.
   155  func (kb baseKeybase) CreateAccount(
   156  	keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd, hdPath string, algo SigningAlgo,
   157  ) (Info, error) {
   158  
   159  	var derivedPriv []byte
   160  	var err error
   161  
   162  	// create master key and derive first key for keyring
   163  	if !strings.Contains(mnemonic, " ") {
   164  		derivedPriv, err = deriveKeyByPrivKey(mnemonic, algo)
   165  	} else {
   166  		derivedPriv, err = kb.options.deriveFunc(mnemonic, bip39Passphrase, hdPath, algo)
   167  	}
   168  
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	privKey, err := kb.options.keygenFunc(derivedPriv, algo)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	var info Info
   179  
   180  	if encryptPasswd != "" {
   181  		info = keyWriter.writeLocalKey(name, privKey, encryptPasswd, algo)
   182  	} else {
   183  		info = kb.writeOfflineKey(keyWriter, name, privKey.PubKey(), algo)
   184  	}
   185  
   186  	return info, nil
   187  }
   188  
   189  // CreateLedger creates a new reference to a Ledger key pair. It returns a public
   190  // key and a derivation path. It returns an error if the device could not be queried.
   191  func (kb baseKeybase) CreateLedger(
   192  	w infoWriter, name string, algo SigningAlgo, hrp string, account, index uint32,
   193  ) (Info, error) {
   194  
   195  	if !IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) {
   196  		return nil, ErrUnsupportedSigningAlgo
   197  	}
   198  
   199  	coinType := types.GetConfig().GetCoinType()
   200  	hdPath := hd.NewFundraiserParams(account, coinType, index)
   201  
   202  	priv, _, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath, hrp)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath, algo), nil
   208  }
   209  
   210  // CreateMnemonic generates a new key with the given algorithm and language pair.
   211  func (kb baseKeybase) CreateMnemonic(
   212  	keyWriter keyWriter, name string, language Language, passwd string, algo SigningAlgo, mnemonicInput string,
   213  ) (info Info, mnemonic string, err error) {
   214  
   215  	if language != English {
   216  		return nil, "", ErrUnsupportedLanguage
   217  	}
   218  
   219  	if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
   220  		return nil, "", ErrUnsupportedSigningAlgo
   221  	}
   222  
   223  	// Default number of words (24): This generates a mnemonic directly from the
   224  	// number of words by reading system entropy.
   225  	entropy, err := bip39.NewEntropy(defaultEntropySize)
   226  	if err != nil {
   227  		return nil, "", err
   228  	}
   229  
   230  	mnemonic, err = bip39.NewMnemonic(entropy)
   231  	if err != nil {
   232  		return nil, "", err
   233  	}
   234  
   235  	if len(mnemonicInput) > 0 {
   236  		mnemonic = mnemonicInput
   237  	}
   238  
   239  	info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, types.GetConfig().GetFullFundraiserPath(), algo)
   240  	if err != nil {
   241  		return nil, "", err
   242  	}
   243  
   244  	return info, mnemonic, err
   245  }
   246  
   247  // CreateMnemonicWithHDPath generates a new key with the given algorithm and language pair.
   248  func (kb baseKeybase) CreateMnemonicWithHDPath(
   249  	keyWriter keyWriter, name string, language Language, passwd string, algo SigningAlgo, mnemonicInput string, hdPath string,
   250  ) (info Info, mnemonic string, err error) {
   251  
   252  	if language != English {
   253  		return nil, "", ErrUnsupportedLanguage
   254  	}
   255  
   256  	if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
   257  		return nil, "", ErrUnsupportedSigningAlgo
   258  	}
   259  
   260  	// Default number of words (24): This generates a mnemonic directly from the
   261  	// number of words by reading system entropy.
   262  	entropy, err := bip39.NewEntropy(defaultEntropySize)
   263  	if err != nil {
   264  		return nil, "", err
   265  	}
   266  
   267  	mnemonic, err = bip39.NewMnemonic(entropy)
   268  	if err != nil {
   269  		return nil, "", err
   270  	}
   271  
   272  	if len(mnemonicInput) > 0 {
   273  		mnemonic = mnemonicInput
   274  	}
   275  
   276  	info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, hdPath, algo)
   277  	if err != nil {
   278  		return nil, "", err
   279  	}
   280  
   281  	return info, mnemonic, err
   282  }
   283  
   284  func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info {
   285  	info := newLedgerInfo(name, pub, path, algo)
   286  	w.writeInfo(name, info)
   287  	return info
   288  }
   289  
   290  func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey, algo SigningAlgo) Info {
   291  	info := newOfflineInfo(name, pub, algo)
   292  	w.writeInfo(name, info)
   293  	return info
   294  }
   295  
   296  func (kb baseKeybase) writeMultisigKey(w infoWriter, name string, pub tmcrypto.PubKey) Info {
   297  	info := NewMultiInfo(name, pub)
   298  	w.writeInfo(name, info)
   299  	return info
   300  }
   301  
   302  // StdDeriveKey is the default DeriveKey function in the keybase.
   303  // For now, it only supports Secp256k1
   304  func StdDeriveKey(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error) {
   305  	if algo == Secp256k1 {
   306  		return SecpDeriveKey(mnemonic, bip39Passphrase, hdPath)
   307  	}
   308  	return nil, ErrUnsupportedSigningAlgo
   309  }
   310  
   311  // SecpDeriveKey derives and returns the secp256k1 private key for the given seed and HD path.
   312  func SecpDeriveKey(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) {
   313  	seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  
   318  	masterPriv, ch := hd.ComputeMastersFromSeed(seed)
   319  	if len(hdPath) == 0 {
   320  		return masterPriv[:], nil
   321  	}
   322  	derivedKey, err := hd.DerivePrivateKeyForPath(masterPriv, ch, hdPath)
   323  	return derivedKey[:], err
   324  }
   325  
   326  // CreateHDPath returns BIP 44 object from account and index parameters.
   327  func CreateHDPath(account uint32, index uint32) *hd.BIP44Params {
   328  	return hd.NewFundraiserParams(account, types.GetConfig().GetCoinType(), index)
   329  }
   330  
   331  // CreateHDPath returns BIP 44 object from account and index parameters.
   332  func CreateHDPathEx(cointype, account, index uint32) *hd.BIP44Params {
   333  	return hd.NewFundraiserParams(account, cointype, index)
   334  }
   335  
   336  // SupportedAlgos returns a list of supported signing algorithms.
   337  func (kb baseKeybase) SupportedAlgos() []SigningAlgo {
   338  	return kb.options.supportedAlgos
   339  }
   340  
   341  // SupportedAlgosLedger returns a list of supported ledger signing algorithms.
   342  func (kb baseKeybase) SupportedAlgosLedger() []SigningAlgo {
   343  	return kb.options.supportedAlgosLedger
   344  }
   345  
   346  // IsSupportedAlgorithm returns whether the signing algorithm is in the passed-in list of supported algorithms.
   347  func IsSupportedAlgorithm(supported []SigningAlgo, algo SigningAlgo) bool {
   348  	for _, supportedAlgo := range supported {
   349  		if algo == supportedAlgo {
   350  			return true
   351  		}
   352  	}
   353  	return false
   354  }
   355  
   356  // resolvePath resolve to a absolute path
   357  func resolvePath(path string) (string, error) {
   358  	var err error
   359  	// expand tilde for home directory
   360  	if strings.HasPrefix(path, "~") {
   361  		home, err := homedir.Dir()
   362  		if err != nil {
   363  			return "", err
   364  		}
   365  		path = strings.Replace(path, "~", home, 1)
   366  	}
   367  
   368  	stat, err := os.Stat(path)
   369  	if os.IsNotExist(err) {
   370  		err = os.MkdirAll(path, 0700)
   371  	} else if err != nil && !stat.IsDir() {
   372  		err = fmt.Errorf("%s is a file, not a directory", path)
   373  	}
   374  	return path, err
   375  }