accounts/usbwallet: trezor signed message support

This commit is contained in:
Guillaume Ballet 2019-07-10 12:20:06 +02:00
parent e8c9579fb7
commit 24d7201acd
4 changed files with 72 additions and 2 deletions

View file

@ -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.
//

View file

@ -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) {

View file

@ -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

View file

@ -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
}