github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/accounts/usbwallet/wallet.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package usbwallet implements support for USB hardware wallets.
    18  package usbwallet
    19  
    20  import (
    21  	"context"
    22  	"crypto/ecdsa"
    23  	"fmt"
    24  	blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
    25  	"io"
    26  	"math/big"
    27  	"sync"
    28  	"time"
    29  
    30  	ethereum "github.com/ethereum/go-ethereum"
    31  	"github.com/ethereum/go-ethereum/accounts"
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/log"
    35  	"github.com/karalabe/hid"
    36  )
    37  
    38  // Maximum time between wallet health checks to detect USB unplugs.
    39  const heartbeatCycle = time.Second
    40  
    41  // Minimum time to wait between self derivation attempts, even it the user is
    42  // requesting accounts like crazy.
    43  const selfDeriveThrottling = time.Second
    44  
    45  // driver defines the vendor specific functionality hardware wallets instances
    46  // must implement to allow using them with the wallet lifecycle management.
    47  type driver interface {
    48  	// Status returns a textual status to aid the user in the current state of the
    49  	// wallet. It also returns an error indicating any failure the wallet might have
    50  	// encountered.
    51  	Status() (string, error)
    52  
    53  	// Open initializes access to a wallet instance. The passphrase parameter may
    54  	// or may not be used by the implementation of a particular wallet instance.
    55  	Open(device io.ReadWriter, passphrase string) error
    56  
    57  	// Close releases any resources held by an open wallet instance.
    58  	Close() error
    59  
    60  	// Heartbeat performs a sanity check against the hardware wallet to see if it
    61  	// is still online and healthy.
    62  	Heartbeat() error
    63  
    64  	// Derive sends a derivation request to the USB device and returns the Ethereum
    65  	// address located on that path.
    66  	Derive(path accounts.DerivationPath) (common.Address, error)
    67  
    68  	// SignTx sends the transaction to the USB device and waits for the user to confirm
    69  	// or deny the transaction.
    70  	SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error)
    71  }
    72  
    73  // wallet represents the common functionality shared by all USB hardware
    74  // wallets to prevent reimplementing the same complex maintenance mechanisms
    75  // for different vendors.
    76  type wallet struct {
    77  	hub    *Hub          // USB hub scanning
    78  	driver driver        // Hardware implementation of the low level device operations
    79  	url    *accounts.URL // Textual URL uniquely identifying this wallet
    80  
    81  	info   hid.DeviceInfo // Known USB device infos about the wallet
    82  	device *hid.Device    // USB device advertising itself as a hardware wallet
    83  
    84  	accounts []accounts.Account                         // List of derive accounts pinned on the hardware wallet
    85  	paths    map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
    86  
    87  	deriveNextPath accounts.DerivationPath   // Next derivation path for account auto-discovery
    88  	deriveNextAddr common.Address            // Next derived account address for auto-discovery
    89  	deriveChain    ethereum.ChainStateReader // Blockchain state reader to discover used account with
    90  	deriveReq      chan chan struct{}        // Channel to request a self-derivation on
    91  	deriveQuit     chan chan error           // Channel to terminate the self-deriver with
    92  
    93  	healthQuit chan chan error
    94  
    95  	// Locking a hardware wallet is a bit special. Since hardware devices are lower
    96  	// performing, any communication with them might take a non negligible amount of
    97  	// time. Worse still, waiting for user confirmation can take arbitrarily long,
    98  	// but exclusive communication must be upheld during. Locking the entire wallet
    99  	// in the mean time however would stall any parts of the system that don't want
   100  	// to communicate, just read some state (e.g. list the accounts).
   101  	//
   102  	// As such, a hardware wallet needs two locks to function correctly. A state
   103  	// lock can be used to protect the wallet's software-side internal state, which
   104  	// must not be held exclusively during hardware communication. A communication
   105  	// lock can be used to achieve exclusive access to the device itself, this one
   106  	// however should allow "skipping" waiting for operations that might want to
   107  	// use the device, but can live without too (e.g. account self-derivation).
   108  	//
   109  	// Since we have two locks, it's important to know how to properly use them:
   110  	//   - Communication requires the `device` to not change, so obtaining the
   111  	//     commsLock should be done after having a stateLock.
   112  	//   - Communication must not disable read access to the wallet state, so it
   113  	//     must only ever hold a *read* lock to stateLock.
   114  	commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked
   115  	stateLock sync.RWMutex  // Protects read and write access to the wallet struct fields
   116  
   117  	log log.Logger // Contextual logger to tag the base with its id
   118  }
   119  
   120  // URL implements accounts.Wallet, returning the URL of the USB hardware device.
   121  func (w *wallet) URL() accounts.URL {
   122  	return *w.url // Immutable, no need for a lock
   123  }
   124  
   125  // Status implements accounts.Wallet, returning a custom status message from the
   126  // underlying vendor-specific hardware wallet implementation.
   127  func (w *wallet) Status() (string, error) {
   128  	w.stateLock.RLock() // No device communication, state lock is enough
   129  	defer w.stateLock.RUnlock()
   130  
   131  	status, failure := w.driver.Status()
   132  	if w.device == nil {
   133  		return "Closed", failure
   134  	}
   135  	return status, failure
   136  }
   137  
   138  // Open implements accounts.Wallet, attempting to open a USB connection to the
   139  // hardware wallet.
   140  func (w *wallet) Open(passphrase string) error {
   141  	w.stateLock.Lock() // State lock is enough since there's no connection yet at this point
   142  	defer w.stateLock.Unlock()
   143  
   144  	// If the device was already opened once, refuse to try again
   145  	if w.paths != nil {
   146  		return accounts.ErrWalletAlreadyOpen
   147  	}
   148  	// Make sure the actual device connection is done only once
   149  	if w.device == nil {
   150  		device, err := w.info.Open()
   151  		if err != nil {
   152  			return err
   153  		}
   154  		w.device = device
   155  		w.commsLock = make(chan struct{}, 1)
   156  		w.commsLock <- struct{}{} // Enable lock
   157  	}
   158  	// Delegate device initialization to the underlying driver
   159  	if err := w.driver.Open(w.device, passphrase); err != nil {
   160  		return err
   161  	}
   162  	// Connection successful, start life-cycle management
   163  	w.paths = make(map[common.Address]accounts.DerivationPath)
   164  
   165  	w.deriveReq = make(chan chan struct{})
   166  	w.deriveQuit = make(chan chan error)
   167  	w.healthQuit = make(chan chan error)
   168  
   169  	go w.heartbeat()
   170  	go w.selfDerive()
   171  
   172  	// Notify anyone listening for wallet events that a new device is accessible
   173  	go w.hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened})
   174  
   175  	return nil
   176  }
   177  
   178  // heartbeat is a health check loop for the USB wallets to periodically verify
   179  // whether they are still present or if they malfunctioned.
   180  func (w *wallet) heartbeat() {
   181  	w.log.Debug("USB wallet health-check started")
   182  	defer w.log.Debug("USB wallet health-check stopped")
   183  
   184  	// Execute heartbeat checks until termination or error
   185  	var (
   186  		errc chan error
   187  		err  error
   188  	)
   189  	for errc == nil && err == nil {
   190  		// Wait until termination is requested or the heartbeat cycle arrives
   191  		select {
   192  		case errc = <-w.healthQuit:
   193  			// Termination requested
   194  			continue
   195  		case <-time.After(heartbeatCycle):
   196  			// Heartbeat time
   197  		}
   198  		// Execute a tiny data exchange to see responsiveness
   199  		w.stateLock.RLock()
   200  		if w.device == nil {
   201  			// Terminated while waiting for the lock
   202  			w.stateLock.RUnlock()
   203  			continue
   204  		}
   205  		<-w.commsLock // Don't lock state while resolving version
   206  		err = w.driver.Heartbeat()
   207  		w.commsLock <- struct{}{}
   208  		w.stateLock.RUnlock()
   209  
   210  		if err != nil {
   211  			w.stateLock.Lock() // Lock state to tear the wallet down
   212  			w.close()
   213  			w.stateLock.Unlock()
   214  		}
   215  		// Ignore non hardware related errors
   216  		err = nil
   217  	}
   218  	// In case of error, wait for termination
   219  	if err != nil {
   220  		w.log.Debug("USB wallet health-check failed", "err", err)
   221  		errc = <-w.healthQuit
   222  	}
   223  	errc <- err
   224  }
   225  
   226  // Close implements accounts.Wallet, closing the USB connection to the device.
   227  func (w *wallet) Close() error {
   228  	// Ensure the wallet was opened
   229  	w.stateLock.RLock()
   230  	hQuit, dQuit := w.healthQuit, w.deriveQuit
   231  	w.stateLock.RUnlock()
   232  
   233  	// Terminate the health checks
   234  	var herr error
   235  	if hQuit != nil {
   236  		errc := make(chan error)
   237  		hQuit <- errc
   238  		herr = <-errc // Save for later, we *must* close the USB
   239  	}
   240  	// Terminate the self-derivations
   241  	var derr error
   242  	if dQuit != nil {
   243  		errc := make(chan error)
   244  		dQuit <- errc
   245  		derr = <-errc // Save for later, we *must* close the USB
   246  	}
   247  	// Terminate the device connection
   248  	w.stateLock.Lock()
   249  	defer w.stateLock.Unlock()
   250  
   251  	w.healthQuit = nil
   252  	w.deriveQuit = nil
   253  	w.deriveReq = nil
   254  
   255  	if err := w.close(); err != nil {
   256  		return err
   257  	}
   258  	if herr != nil {
   259  		return herr
   260  	}
   261  	return derr
   262  }
   263  
   264  // close is the internal wallet closer that terminates the USB connection and
   265  // resets all the fields to their defaults.
   266  //
   267  // Note, close assumes the state lock is held!
   268  func (w *wallet) close() error {
   269  	// Allow duplicate closes, especially for health-check failures
   270  	if w.device == nil {
   271  		return nil
   272  	}
   273  	// Close the device, clear everything, then return
   274  	w.device.Close()
   275  	w.device = nil
   276  
   277  	w.accounts, w.paths = nil, nil
   278  	w.driver.Close()
   279  
   280  	return nil
   281  }
   282  
   283  // Accounts implements accounts.Wallet, returning the list of accounts pinned to
   284  // the USB hardware wallet. If self-derivation was enabled, the account list is
   285  // periodically expanded based on current chain state.
   286  func (w *wallet) Accounts() []accounts.Account {
   287  	// Attempt self-derivation if it's running
   288  	reqc := make(chan struct{}, 1)
   289  	select {
   290  	case w.deriveReq <- reqc:
   291  		// Self-derivation request accepted, wait for it
   292  		<-reqc
   293  	default:
   294  		// Self-derivation offline, throttled or busy, skip
   295  	}
   296  	// Return whatever account list we ended up with
   297  	w.stateLock.RLock()
   298  	defer w.stateLock.RUnlock()
   299  
   300  	cpy := make([]accounts.Account, len(w.accounts))
   301  	copy(cpy, w.accounts)
   302  	return cpy
   303  }
   304  
   305  // selfDerive is an account derivation loop that upon request attempts to find
   306  // new non-zero accounts.
   307  func (w *wallet) selfDerive() {
   308  	w.log.Debug("USB wallet self-derivation started")
   309  	defer w.log.Debug("USB wallet self-derivation stopped")
   310  
   311  	// Execute self-derivations until termination or error
   312  	var (
   313  		reqc chan struct{}
   314  		errc chan error
   315  		err  error
   316  	)
   317  	for errc == nil && err == nil {
   318  		// Wait until either derivation or termination is requested
   319  		select {
   320  		case errc = <-w.deriveQuit:
   321  			// Termination requested
   322  			continue
   323  		case reqc = <-w.deriveReq:
   324  			// Account discovery requested
   325  		}
   326  		// Derivation needs a chain and device access, skip if either unavailable
   327  		w.stateLock.RLock()
   328  		if w.device == nil || w.deriveChain == nil {
   329  			w.stateLock.RUnlock()
   330  			reqc <- struct{}{}
   331  			continue
   332  		}
   333  		select {
   334  		case <-w.commsLock:
   335  		default:
   336  			w.stateLock.RUnlock()
   337  			reqc <- struct{}{}
   338  			continue
   339  		}
   340  		// Device lock obtained, derive the next batch of accounts
   341  		var (
   342  			accs  []accounts.Account
   343  			paths []accounts.DerivationPath
   344  
   345  			nextAddr = w.deriveNextAddr
   346  			nextPath = w.deriveNextPath
   347  
   348  			context = context.Background()
   349  		)
   350  		for empty := false; !empty; {
   351  			// Retrieve the next derived Ethereum account
   352  			if nextAddr == (common.Address{}) {
   353  				if nextAddr, err = w.driver.Derive(nextPath); err != nil {
   354  					w.log.Warn("USB wallet account derivation failed", "err", err)
   355  					break
   356  				}
   357  			}
   358  			// Check the account's status against the current chain state
   359  			var (
   360  				balance *big.Int
   361  				nonce   uint64
   362  			)
   363  			balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil)
   364  			if err != nil {
   365  				w.log.Warn("USB wallet balance retrieval failed", "err", err)
   366  				break
   367  			}
   368  			nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil)
   369  			if err != nil {
   370  				w.log.Warn("USB wallet nonce retrieval failed", "err", err)
   371  				break
   372  			}
   373  			// If the next account is empty, stop self-derivation, but add it nonetheless
   374  			if balance.Sign() == 0 && nonce == 0 {
   375  				empty = true
   376  			}
   377  			// We've just self-derived a new account, start tracking it locally
   378  			path := make(accounts.DerivationPath, len(nextPath))
   379  			copy(path[:], nextPath[:])
   380  			paths = append(paths, path)
   381  
   382  			account := accounts.Account{
   383  				Address: nextAddr,
   384  				URL:     accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
   385  			}
   386  			accs = append(accs, account)
   387  
   388  			// Display a log message to the user for new (or previously empty accounts)
   389  			if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) {
   390  				w.log.Info("USB wallet discovered new account", "address", nextAddr, "path", path, "balance", balance, "nonce", nonce)
   391  			}
   392  			// Fetch the next potential account
   393  			if !empty {
   394  				nextAddr = common.Address{}
   395  				nextPath[len(nextPath)-1]++
   396  			}
   397  		}
   398  		// Self derivation complete, release device lock
   399  		w.commsLock <- struct{}{}
   400  		w.stateLock.RUnlock()
   401  
   402  		// Insert any accounts successfully derived
   403  		w.stateLock.Lock()
   404  		for i := 0; i < len(accs); i++ {
   405  			if _, ok := w.paths[accs[i].Address]; !ok {
   406  				w.accounts = append(w.accounts, accs[i])
   407  				w.paths[accs[i].Address] = paths[i]
   408  			}
   409  		}
   410  		// Shift the self-derivation forward
   411  		// TODO(karalabe): don't overwrite changes from wallet.SelfDerive
   412  		w.deriveNextAddr = nextAddr
   413  		w.deriveNextPath = nextPath
   414  		w.stateLock.Unlock()
   415  
   416  		// Notify the user of termination and loop after a bit of time (to avoid trashing)
   417  		reqc <- struct{}{}
   418  		if err == nil {
   419  			select {
   420  			case errc = <-w.deriveQuit:
   421  				// Termination requested, abort
   422  			case <-time.After(selfDeriveThrottling):
   423  				// Waited enough, willing to self-derive again
   424  			}
   425  		}
   426  	}
   427  	// In case of error, wait for termination
   428  	if err != nil {
   429  		w.log.Debug("USB wallet self-derivation failed", "err", err)
   430  		errc = <-w.deriveQuit
   431  	}
   432  	errc <- err
   433  }
   434  
   435  // Contains implements accounts.Wallet, returning whether a particular account is
   436  // or is not pinned into this wallet instance. Although we could attempt to resolve
   437  // unpinned accounts, that would be an non-negligible hardware operation.
   438  func (w *wallet) Contains(account accounts.Account) bool {
   439  	w.stateLock.RLock()
   440  	defer w.stateLock.RUnlock()
   441  
   442  	_, exists := w.paths[account.Address]
   443  	return exists
   444  }
   445  
   446  func (w *wallet) Decrypt(a accounts.Account, c, s1, s2 []byte) ([]byte, error) {
   447  	return nil, accounts.ErrNotSupported
   448  }
   449  
   450  // Derive implements accounts.Wallet, deriving a new account at the specific
   451  // derivation path. If pin is set to true, the account will be added to the list
   452  // of tracked accounts.
   453  func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) {
   454  	// Try to derive the actual account and update its URL if successful
   455  	w.stateLock.RLock() // Avoid device disappearing during derivation
   456  
   457  	if w.device == nil {
   458  		w.stateLock.RUnlock()
   459  		return accounts.Account{}, accounts.ErrWalletClosed
   460  	}
   461  	<-w.commsLock // Avoid concurrent hardware access
   462  	address, err := w.driver.Derive(path)
   463  	w.commsLock <- struct{}{}
   464  
   465  	w.stateLock.RUnlock()
   466  
   467  	// If an error occurred or no pinning was requested, return
   468  	if err != nil {
   469  		return accounts.Account{}, err
   470  	}
   471  	account := accounts.Account{
   472  		Address: address,
   473  		URL:     accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
   474  	}
   475  	if !pin {
   476  		return account, nil
   477  	}
   478  	// Pinning needs to modify the state
   479  	w.stateLock.Lock()
   480  	defer w.stateLock.Unlock()
   481  
   482  	if _, ok := w.paths[address]; !ok {
   483  		w.accounts = append(w.accounts, account)
   484  		w.paths[address] = path
   485  	}
   486  	return account, nil
   487  }
   488  
   489  // SelfDerive implements accounts.Wallet, trying to discover accounts that the
   490  // user used previously (based on the chain state), but ones that he/she did not
   491  // explicitly pin to the wallet manually. To avoid chain head monitoring, self
   492  // derivation only runs during account listing (and even then throttled).
   493  func (w *wallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {
   494  	w.stateLock.Lock()
   495  	defer w.stateLock.Unlock()
   496  
   497  	w.deriveNextPath = make(accounts.DerivationPath, len(base))
   498  	copy(w.deriveNextPath[:], base[:])
   499  
   500  	w.deriveNextAddr = common.Address{}
   501  	w.deriveChain = chain
   502  }
   503  
   504  // SignHash implements accounts.Wallet, however signing arbitrary data is not
   505  // supported for hardware wallets, so this method will always return an error.
   506  func (w *wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
   507  	return nil, accounts.ErrNotSupported
   508  }
   509  
   510  func (w *wallet) GetPublicKey(account accounts.Account) (*ecdsa.PublicKey, error) {
   511  	return nil, accounts.ErrNotSupported
   512  }
   513  
   514  func (w *wallet) SignHashBLS(account accounts.Account, hash []byte) (blscrypto.SerializedSignature, error) {
   515  	return blscrypto.SerializedSignature{}, accounts.ErrNotSupported
   516  }
   517  
   518  func (w *wallet) SignMessageBLS(account accounts.Account, msg []byte, extraData []byte) (blscrypto.SerializedSignature, error) {
   519  	return blscrypto.SerializedSignature{}, accounts.ErrNotSupported
   520  }
   521  
   522  func (w *wallet) GenerateProofOfPossession(account accounts.Account, address common.Address) ([]byte, []byte, error) {
   523  	return nil, nil, accounts.ErrNotSupported
   524  }
   525  
   526  func (w *wallet) GenerateProofOfPossessionBLS(account accounts.Account, address common.Address) ([]byte, []byte, error) {
   527  	return nil, nil, accounts.ErrNotSupported
   528  }
   529  
   530  // SignTx implements accounts.Wallet. It sends the transaction over to the Ledger
   531  // wallet to request a confirmation from the user. It returns either the signed
   532  // transaction or a failure if the user denied the transaction.
   533  //
   534  // Note, if the version of the Ethereum application running on the Ledger wallet is
   535  // too old to sign EIP-155 transactions, but such is requested nonetheless, an error
   536  // will be returned opposed to silently signing in Homestead mode.
   537  func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
   538  	w.stateLock.RLock() // Comms have own mutex, this is for the state fields
   539  	defer w.stateLock.RUnlock()
   540  
   541  	// If the wallet is closed, abort
   542  	if w.device == nil {
   543  		return nil, accounts.ErrWalletClosed
   544  	}
   545  	// Make sure the requested account is contained within
   546  	path, ok := w.paths[account.Address]
   547  	if !ok {
   548  		return nil, accounts.ErrUnknownAccount
   549  	}
   550  	// All infos gathered and metadata checks out, request signing
   551  	<-w.commsLock
   552  	defer func() { w.commsLock <- struct{}{} }()
   553  
   554  	// Ensure the device isn't screwed with while user confirmation is pending
   555  	// TODO(karalabe): remove if hotplug lands on Windows
   556  	w.hub.commsLock.Lock()
   557  	w.hub.commsPend++
   558  	w.hub.commsLock.Unlock()
   559  
   560  	defer func() {
   561  		w.hub.commsLock.Lock()
   562  		w.hub.commsPend--
   563  		w.hub.commsLock.Unlock()
   564  	}()
   565  	// Sign the transaction and verify the sender to avoid hardware fault surprises
   566  	sender, signed, err := w.driver.SignTx(path, tx, chainID)
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	if sender != account.Address {
   571  		return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex())
   572  	}
   573  	return signed, nil
   574  }
   575  
   576  // SignHashWithPassphrase implements accounts.Wallet, however signing arbitrary
   577  // data is not supported for Ledger wallets, so this method will always return
   578  // an error.
   579  func (w *wallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
   580  	return w.SignHash(account, hash)
   581  }
   582  
   583  // SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
   584  // transaction with the given account using passphrase as extra authentication.
   585  // Since USB wallets don't rely on passphrases, these are silently ignored.
   586  func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
   587  	return w.SignTx(account, tx, chainID)
   588  }