diff --git a/accounts/usbwallet/ledger.go b/accounts/usbwallet/ledger.go index c30903b5b7..15c1d7b8d3 100644 --- a/accounts/usbwallet/ledger.go +++ b/accounts/usbwallet/ledger.go @@ -168,6 +168,12 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio return w.ledgerSign(path, tx, chainID) } +// SignData sends a blob of data to the USB device and waits for the user to confirm +// or deny the transaction. +func (w *ledgerDriver) SignData(path accounts.DerivationPath, hash []byte) (common.Address, []byte, error) { + return common.Address{}, nil, accounts.ErrNotSupported +} + // ledgerVersion retrieves the current version of the Ethereum wallet app running // on the Ledger wallet. // diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go index 1892097baf..a1edb250d3 100644 --- a/accounts/usbwallet/trezor.go +++ b/accounts/usbwallet/trezor.go @@ -185,6 +185,29 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio return w.trezorSign(path, tx, chainID) } +// SignData sends a blob of data to the xUSB device and waits for the user to confirm +// or deny the transaction. +func (w *trezorDriver) SignData(path accounts.DerivationPath, hash []byte) (common.Address, []byte, error) { + request := &trezor.EthereumSignMessage{ + AddressN: path, + Message: hash, + } + response := new(trezor.EthereumMessageSignature) + if _, err := w.trezorExchange(request, response); err != nil { + return common.Address{}, nil, err + } + + var address common.Address + if addr := response.GetAddressBin(); len(addr) > 0 { // Older firmwares use binary fomats + address = common.BytesToAddress(addr) + } + if addr := response.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal fomats + address = common.HexToAddress(addr) + } + + return address, response.Signature, nil +} + // trezorDerive sends a derivation request to the Trezor device and returns the // Ethereum address located on that path. func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, error) { diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go index ee539d9653..6622836896 100644 --- a/accounts/usbwallet/wallet.go +++ b/accounts/usbwallet/wallet.go @@ -67,6 +67,10 @@ type driver interface { // SignTx sends the transaction to the USB device and waits for the user to confirm // or deny the transaction. SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) + + // SignData sends a blob of data to the USB device and waits for the user to confirm + // or deny the transaction. + SignData(path accounts.DerivationPath, hash []byte) (common.Address, []byte, error) } // wallet represents the common functionality shared by all USB hardware @@ -515,7 +519,44 @@ func (w *wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.Chai // signHash implements accounts.Wallet, however signing arbitrary data is not // supported for hardware wallets, so this method will always return an error. func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error) { - return nil, accounts.ErrNotSupported + w.stateLock.RLock() // Comms have their own mutex + defer w.stateLock.RUnlock() + + // If the wallet is closed, abort + if w.device == nil { + return nil, accounts.ErrWalletClosed + } + // Make sure the requested account is contained within + path, ok := w.paths[account.Address] + if !ok { + return nil, accounts.ErrUnknownAccount + } + + // All infos gathered and metadata checks out, request signing + <-w.commsLock + defer func() { w.commsLock <- struct{}{} }() + + // Ensure the device isn't screwed with while user confirmation is pending + // TODO(karalabe): remove if hotplug lands on Windows + w.hub.commsLock.Lock() + w.hub.commsPend++ + w.hub.commsLock.Unlock() + + defer func() { + w.hub.commsLock.Lock() + w.hub.commsPend-- + w.hub.commsLock.Unlock() + }() + + // Sign the transaction and verify the sender to avoid hardware fault surprises + sender, signed, err := w.driver.SignData(path, hash) + if err != nil { + return nil, err + } + if sender != account.Address { + return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex()) + } + return signed, nil } // SignData signs keccak256(data). The mimetype parameter describes the type of data being signed diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go index 91e6cc7dac..e7056c1e27 100644 --- a/signer/core/signed_data.go +++ b/signer/core/signed_data.go @@ -144,7 +144,7 @@ func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, l } pw, err := api.lookupOrQueryPassword(account.Address, "Password for signing", - fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex())) + fmt.Sprintf("Please enter password for signing data with account %s, or just press 'RETURN'", account.Address.Hex())) if err != nil { return nil, err }