github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/keys/keybase.go (about)

     1  package keys
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  
     8  	"github.com/gnolang/gno/tm2/pkg/crypto"
     9  	"github.com/gnolang/gno/tm2/pkg/crypto/bip39"
    10  	"github.com/gnolang/gno/tm2/pkg/crypto/hd"
    11  	"github.com/gnolang/gno/tm2/pkg/crypto/keys/armor"
    12  	"github.com/gnolang/gno/tm2/pkg/crypto/keys/keyerror"
    13  	"github.com/gnolang/gno/tm2/pkg/crypto/ledger"
    14  	"github.com/gnolang/gno/tm2/pkg/crypto/secp256k1"
    15  	dbm "github.com/gnolang/gno/tm2/pkg/db"
    16  	"github.com/gnolang/gno/tm2/pkg/db/memdb"
    17  	"github.com/gnolang/gno/tm2/pkg/errors"
    18  )
    19  
    20  var _ Keybase = dbKeybase{}
    21  
    22  // Language is a language to create the BIP 39 mnemonic in.
    23  // Currently, only english is supported though.
    24  // Find a list of all supported languages in the BIP 39 spec (word lists).
    25  type Language int
    26  
    27  // noinspection ALL
    28  const (
    29  	// English is the default language to create a mnemonic.
    30  	// It is the only supported language by this package.
    31  	English Language = iota + 1
    32  	// Japanese is currently not supported.
    33  	Japanese
    34  	// Korean is currently not supported.
    35  	Korean
    36  	// Spanish is currently not supported.
    37  	Spanish
    38  	// ChineseSimplified is currently not supported.
    39  	ChineseSimplified
    40  	// ChineseTraditional is currently not supported.
    41  	ChineseTraditional
    42  	// French is currently not supported.
    43  	French
    44  	// Italian is currently not supported.
    45  	Italian
    46  )
    47  
    48  const (
    49  	addressSuffix = "address"
    50  	infoSuffix    = "info"
    51  )
    52  
    53  var (
    54  	// ErrUnsupportedSigningAlgo is raised when the caller tries to use a
    55  	// different signing scheme than secp256k1.
    56  	ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo: only secp256k1 is supported")
    57  
    58  	// ErrUnsupportedLanguage is raised when the caller tries to use a
    59  	// different language than english for creating a mnemonic sentence.
    60  	ErrUnsupportedLanguage = errors.New("unsupported language: only english is supported")
    61  )
    62  
    63  // dbKeybase combines encryption and storage implementation to provide
    64  // a full-featured key manager
    65  type dbKeybase struct {
    66  	db dbm.DB
    67  }
    68  
    69  // NewDBKeybase creates a new keybase instance using the passed DB for reading and writing keys.
    70  func NewDBKeybase(db dbm.DB) Keybase {
    71  	return dbKeybase{
    72  		db: db,
    73  	}
    74  }
    75  
    76  // NewInMemory creates a transient keybase on top of in-memory storage
    77  // instance useful for testing purposes and on-the-fly key generation.
    78  func NewInMemory() Keybase { return dbKeybase{memdb.NewMemDB()} }
    79  
    80  // CreateAccount converts a mnemonic to a private key and persists it, encrypted with the given password.
    81  // XXX Info could include the separately derived ed25519 key,
    82  // XXX and a signature from the sec2561key as certificate.
    83  // XXX NOTE: we are not saving the derivation path.
    84  // XXX but this doesn't help encrypted communication.
    85  // XXX also there is no document structure.
    86  func (kb dbKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) {
    87  	coinType := crypto.CoinType
    88  	hdPath := hd.NewFundraiserParams(account, coinType, index)
    89  	return kb.CreateAccountBip44(name, mnemonic, bip39Passwd, encryptPasswd, *hdPath)
    90  }
    91  
    92  func (kb dbKeybase) CreateAccountBip44(name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params) (info Info, err error) {
    93  	seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
    94  	if err != nil {
    95  		return
    96  	}
    97  
    98  	info, err = kb.persistDerivedKey(seed, encryptPasswd, name, params.String())
    99  	return
   100  }
   101  
   102  // CreateLedger creates a new locally-stored reference to a Ledger keypair
   103  // It returns the created key info and an error if the Ledger could not be queried
   104  func (kb dbKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (Info, error) {
   105  	if algo != Secp256k1 {
   106  		return nil, ErrUnsupportedSigningAlgo
   107  	}
   108  
   109  	coinType := crypto.CoinType
   110  	hdPath := hd.NewFundraiserParams(account, coinType, index)
   111  	priv, _, err := ledger.NewPrivKeyLedgerSecp256k1(*hdPath, hrp)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	pub := priv.PubKey()
   116  
   117  	// Note: Once Cosmos App v1.3.1 is compulsory, it could be possible to check that pubkey and addr match
   118  	return kb.writeLedgerKey(name, pub, *hdPath), nil
   119  }
   120  
   121  // CreateOffline creates a new reference to an offline keypair. It returns the
   122  // created key info.
   123  func (kb dbKeybase) CreateOffline(name string, pub crypto.PubKey) (Info, error) {
   124  	return kb.writeOfflineKey(name, pub), nil
   125  }
   126  
   127  // CreateMulti creates a new reference to a multisig (offline) keypair. It
   128  // returns the created key info.
   129  func (kb dbKeybase) CreateMulti(name string, pub crypto.PubKey) (Info, error) {
   130  	return kb.writeMultisigKey(name, pub), nil
   131  }
   132  
   133  func (kb *dbKeybase) persistDerivedKey(seed []byte, passwd, name, fullHdPath string) (info Info, err error) {
   134  	// create master key and derive first key:
   135  	masterPriv, ch := hd.ComputeMastersFromSeed(seed)
   136  	derivedPriv, err := hd.DerivePrivateKeyForPath(masterPriv, ch, fullHdPath)
   137  	if err != nil {
   138  		return
   139  	}
   140  
   141  	// use possibly blank password to encrypt the private
   142  	// key and store it. User must enforce good passwords.
   143  	info = kb.writeLocalKey(name, secp256k1.PrivKeySecp256k1(derivedPriv), passwd)
   144  	return
   145  }
   146  
   147  // List returns the keys from storage in alphabetical order.
   148  func (kb dbKeybase) List() ([]Info, error) {
   149  	var res []Info
   150  	iter := kb.db.Iterator(nil, nil)
   151  	defer iter.Close()
   152  	for ; iter.Valid(); iter.Next() {
   153  		key := string(iter.Key())
   154  
   155  		// need to include only keys in storage that have an info suffix
   156  		if strings.HasSuffix(key, infoSuffix) {
   157  			info, err := readInfo(iter.Value())
   158  			if err != nil {
   159  				return nil, err
   160  			}
   161  			res = append(res, info)
   162  		}
   163  	}
   164  	return res, nil
   165  }
   166  
   167  // HasByNameOrAddress checks if a key with the name or bech32 string address is in the keybase.
   168  func (kb dbKeybase) HasByNameOrAddress(nameOrBech32 string) (bool, error) {
   169  	address, err := crypto.AddressFromBech32(nameOrBech32)
   170  	if err != nil {
   171  		return kb.HasByName(nameOrBech32)
   172  	}
   173  	return kb.HasByAddress(address)
   174  }
   175  
   176  // HasByName checks if a key with the name is in the keybase.
   177  func (kb dbKeybase) HasByName(name string) (bool, error) {
   178  	return kb.db.Has(infoKey(name)), nil
   179  }
   180  
   181  // HasByAddress checks if a key with the address is in the keybase.
   182  func (kb dbKeybase) HasByAddress(address crypto.Address) (bool, error) {
   183  	return kb.db.Has(addrKey(address)), nil
   184  }
   185  
   186  // Get returns the public information about one key.
   187  func (kb dbKeybase) GetByNameOrAddress(nameOrBech32 string) (Info, error) {
   188  	addr, err := crypto.AddressFromBech32(nameOrBech32)
   189  	if err != nil {
   190  		return kb.GetByName(nameOrBech32)
   191  	}
   192  	return kb.GetByAddress(addr)
   193  }
   194  
   195  func (kb dbKeybase) GetByName(name string) (Info, error) {
   196  	bs := kb.db.Get(infoKey(name))
   197  	if len(bs) == 0 {
   198  		return nil, keyerror.NewErrKeyNotFound(name)
   199  	}
   200  	return readInfo(bs)
   201  }
   202  
   203  func (kb dbKeybase) GetByAddress(address crypto.Address) (Info, error) {
   204  	ik := kb.db.Get(addrKey(address))
   205  	if len(ik) == 0 {
   206  		return nil, keyerror.NewErrKeyNotFound(fmt.Sprintf("key with address %s not found", address))
   207  	}
   208  	bs := kb.db.Get(ik)
   209  	return readInfo(bs)
   210  }
   211  
   212  // Sign signs the msg with the named key.
   213  // It returns an error if the key doesn't exist or the decryption fails.
   214  func (kb dbKeybase) Sign(nameOrBech32, passphrase string, msg []byte) (sig []byte, pub crypto.PubKey, err error) {
   215  	info, err := kb.GetByNameOrAddress(nameOrBech32)
   216  	if err != nil {
   217  		return
   218  	}
   219  
   220  	var priv crypto.PrivKey
   221  
   222  	switch info.(type) {
   223  	case localInfo:
   224  		linfo := info.(localInfo)
   225  		if linfo.PrivKeyArmor == "" {
   226  			err = fmt.Errorf("private key not available")
   227  			return
   228  		}
   229  
   230  		priv, err = armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
   231  		if err != nil {
   232  			return nil, nil, err
   233  		}
   234  
   235  	case ledgerInfo:
   236  		linfo := info.(ledgerInfo)
   237  		priv, err = ledger.NewPrivKeyLedgerSecp256k1Unsafe(linfo.Path)
   238  		if err != nil {
   239  			return
   240  		}
   241  
   242  	case offlineInfo, multiInfo:
   243  		err = fmt.Errorf("cannot sign with key or addr %s", nameOrBech32)
   244  		return
   245  	}
   246  
   247  	sig, err = priv.Sign(msg)
   248  	if err != nil {
   249  		return nil, nil, err
   250  	}
   251  
   252  	pub = priv.PubKey()
   253  	return sig, pub, nil
   254  }
   255  
   256  // Verify verifies the sig+msg with the named key.
   257  // It returns an error if the key doesn't exist or verification fails.
   258  func (kb dbKeybase) Verify(nameOrBech32 string, msg []byte, sig []byte) (err error) {
   259  	info, err := kb.GetByNameOrAddress(nameOrBech32)
   260  	if err != nil {
   261  		return
   262  	}
   263  
   264  	var pub crypto.PubKey
   265  	pub = info.GetPubKey()
   266  	if !pub.VerifyBytes(msg, sig) {
   267  		return errors.New("invalid signature")
   268  	}
   269  	return nil
   270  }
   271  
   272  func (kb dbKeybase) ExportPrivateKeyObject(nameOrBech32 string, passphrase string) (crypto.PrivKey, error) {
   273  	info, err := kb.GetByNameOrAddress(nameOrBech32)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  
   278  	var priv crypto.PrivKey
   279  
   280  	switch info.(type) {
   281  	case localInfo:
   282  		linfo := info.(localInfo)
   283  		if linfo.PrivKeyArmor == "" {
   284  			err = fmt.Errorf("private key not available")
   285  			return nil, err
   286  		}
   287  		priv, err = armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
   288  		if err != nil {
   289  			return nil, err
   290  		}
   291  
   292  	case ledgerInfo, offlineInfo, multiInfo:
   293  		return nil, errors.New("only works on local private keys")
   294  	}
   295  
   296  	return priv, nil
   297  }
   298  
   299  func (kb dbKeybase) Export(nameOrBech32 string) (astr string, err error) {
   300  	info, err := kb.GetByNameOrAddress(nameOrBech32)
   301  	if err != nil {
   302  		return "", errors.Wrap(err, "getting info for name %s", nameOrBech32)
   303  	}
   304  	bz := kb.db.Get(infoKey(info.GetName()))
   305  	if bz == nil {
   306  		return "", fmt.Errorf("no key to export with name %s", nameOrBech32)
   307  	}
   308  	return armor.ArmorInfoBytes(bz), nil
   309  }
   310  
   311  // ExportPubKey returns public keys in ASCII armored format.
   312  // Retrieve a Info object by its name and return the public key in
   313  // a portable format.
   314  func (kb dbKeybase) ExportPubKey(nameOrBech32 string) (astr string, err error) {
   315  	info, err := kb.GetByNameOrAddress(nameOrBech32)
   316  	if err != nil {
   317  		return "", errors.Wrap(err, "getting info for name %s", nameOrBech32)
   318  	}
   319  	return armor.ArmorPubKeyBytes(info.GetPubKey().Bytes()), nil
   320  }
   321  
   322  // ExportPrivKey returns a private key in ASCII armored format.
   323  // It returns an error if the key does not exist or a wrong encryption passphrase is supplied.
   324  func (kb dbKeybase) ExportPrivKey(
   325  	name,
   326  	decryptPassphrase,
   327  	encryptPassphrase string,
   328  ) (astr string, err error) {
   329  	priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
   330  	if err != nil {
   331  		return "", err
   332  	}
   333  
   334  	return armor.EncryptArmorPrivKey(priv, encryptPassphrase), nil
   335  }
   336  
   337  // ExportPrivKeyUnsafe returns a private key in ASCII armored format.
   338  // The returned armor is unencrypted.
   339  // It returns an error if the key does not exist
   340  func (kb dbKeybase) ExportPrivKeyUnsafe(
   341  	name,
   342  	decryptPassphrase string,
   343  ) (string, error) {
   344  	priv, err := kb.ExportPrivateKeyObject(name, decryptPassphrase)
   345  	if err != nil {
   346  		return "", err
   347  	}
   348  
   349  	return armor.ArmorPrivateKey(priv), nil
   350  }
   351  
   352  // ImportPrivKey imports a private key in ASCII armor format.
   353  // It returns an error if a key with the same name exists or a wrong encryption passphrase is
   354  // supplied.
   355  func (kb dbKeybase) ImportPrivKey(
   356  	name,
   357  	astr,
   358  	decryptPassphrase,
   359  	encryptPassphrase string,
   360  ) error {
   361  	if _, err := kb.GetByNameOrAddress(name); err == nil {
   362  		return errors.New("Cannot overwrite key " + name)
   363  	}
   364  	privKey, err := armor.UnarmorDecryptPrivKey(astr, decryptPassphrase)
   365  	if err != nil {
   366  		return errors.Wrap(err, "couldn't import private key")
   367  	}
   368  
   369  	kb.writeLocalKey(name, privKey, encryptPassphrase)
   370  	return nil
   371  }
   372  
   373  func (kb dbKeybase) ImportPrivKeyUnsafe(
   374  	name,
   375  	armorStr,
   376  	encryptPassphrase string,
   377  ) error {
   378  	if _, err := kb.GetByNameOrAddress(name); err == nil {
   379  		return fmt.Errorf("cannot overwrite key %s", name)
   380  	}
   381  
   382  	privKey, err := armor.UnarmorPrivateKey(armorStr)
   383  	if err != nil {
   384  		return errors.Wrap(err, "couldn't import private key")
   385  	}
   386  
   387  	kb.writeLocalKey(name, privKey, encryptPassphrase)
   388  	return nil
   389  }
   390  
   391  func (kb dbKeybase) Import(name, astr string) (err error) {
   392  	if _, err := kb.GetByNameOrAddress(name); err == nil {
   393  		return errors.New("Cannot overwrite key " + name)
   394  	}
   395  	infoBytes, err := armor.UnarmorInfoBytes(astr)
   396  	if err != nil {
   397  		return
   398  	}
   399  	kb.db.Set(infoKey(name), infoBytes)
   400  	return nil
   401  }
   402  
   403  // ImportPubKey imports ASCII-armored public keys.
   404  // Store a new Info object holding a public key only, i.e. it will
   405  // not be possible to sign with it as it lacks the secret key.
   406  func (kb dbKeybase) ImportPubKey(name, astr string) (err error) {
   407  	if _, err := kb.GetByNameOrAddress(name); err == nil {
   408  		return errors.New("Cannot overwrite data for name " + name)
   409  	}
   410  	pubBytes, err := armor.UnarmorPubKeyBytes(astr)
   411  	if err != nil {
   412  		return
   413  	}
   414  	pubKey, err := crypto.PubKeyFromBytes(pubBytes)
   415  	if err != nil {
   416  		return
   417  	}
   418  	kb.writeOfflineKey(name, pubKey)
   419  	return
   420  }
   421  
   422  // Delete removes key forever, but we must present the
   423  // proper passphrase before deleting it (for security).
   424  // It returns an error if the key doesn't exist or
   425  // passphrases don't match.
   426  // Passphrase is ignored when deleting references to
   427  // offline and Ledger / HW wallet keys.
   428  func (kb dbKeybase) Delete(nameOrBech32, passphrase string, skipPass bool) error {
   429  	// verify we have the proper password before deleting
   430  	info, err := kb.GetByNameOrAddress(nameOrBech32)
   431  	if err != nil {
   432  		return err
   433  	}
   434  	if linfo, ok := info.(localInfo); ok && !skipPass {
   435  		if _, err = armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil {
   436  			return err
   437  		}
   438  	}
   439  	kb.db.DeleteSync(addrKey(info.GetAddress()))
   440  	kb.db.DeleteSync(infoKey(info.GetName()))
   441  	return nil
   442  }
   443  
   444  // Update changes the passphrase with which an already stored key is
   445  // encrypted.
   446  //
   447  // oldpass must be the current passphrase used for encryption,
   448  // getNewpass is a function to get the passphrase to permanently replace
   449  // the current passphrase
   450  func (kb dbKeybase) Update(nameOrBech32, oldpass string, getNewpass func() (string, error)) error {
   451  	info, err := kb.GetByNameOrAddress(nameOrBech32)
   452  	if err != nil {
   453  		return err
   454  	}
   455  	switch info.(type) {
   456  	case localInfo:
   457  		linfo := info.(localInfo)
   458  		key, err := armor.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
   459  		if err != nil {
   460  			return err
   461  		}
   462  		newpass, err := getNewpass()
   463  		if err != nil {
   464  			return err
   465  		}
   466  		kb.writeLocalKey(info.GetName(), key, newpass)
   467  		return nil
   468  	default:
   469  		return fmt.Errorf("locally stored key required. Received: %v", reflect.TypeOf(info).String())
   470  	}
   471  }
   472  
   473  // CloseDB releases the lock and closes the storage backend.
   474  func (kb dbKeybase) CloseDB() {
   475  	kb.db.Close()
   476  }
   477  
   478  func (kb dbKeybase) writeLocalKey(name string, priv crypto.PrivKey, passphrase string) Info {
   479  	// encrypt private key using passphrase
   480  	privArmor := armor.EncryptArmorPrivKey(priv, passphrase)
   481  	// make Info
   482  	pub := priv.PubKey()
   483  	info := newLocalInfo(name, pub, privArmor)
   484  	kb.writeInfo(name, info)
   485  	return info
   486  }
   487  
   488  func (kb dbKeybase) writeLedgerKey(name string, pub crypto.PubKey, path hd.BIP44Params) Info {
   489  	info := newLedgerInfo(name, pub, path)
   490  	kb.writeInfo(name, info)
   491  	return info
   492  }
   493  
   494  func (kb dbKeybase) writeOfflineKey(name string, pub crypto.PubKey) Info {
   495  	info := newOfflineInfo(name, pub)
   496  	kb.writeInfo(name, info)
   497  	return info
   498  }
   499  
   500  func (kb dbKeybase) writeMultisigKey(name string, pub crypto.PubKey) Info {
   501  	info := NewMultiInfo(name, pub)
   502  	kb.writeInfo(name, info)
   503  	return info
   504  }
   505  
   506  func (kb dbKeybase) writeInfo(name string, info Info) {
   507  	// write the info by key
   508  	key := infoKey(name)
   509  	serializedInfo := writeInfo(info)
   510  	kb.db.SetSync(key, serializedInfo)
   511  	// store a pointer to the infokey by address for fast lookup
   512  	kb.db.SetSync(addrKey(info.GetAddress()), key)
   513  }
   514  
   515  func addrKey(address crypto.Address) []byte {
   516  	return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix))
   517  }
   518  
   519  func infoKey(name string) []byte {
   520  	return []byte(fmt.Sprintf("%s.%s", name, infoSuffix))
   521  }