| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | // Copyright 2017 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This file contains the implementation for interacting with the Trezor hardware | 
					
						
							|  |  |  | // wallets. The wire protocol spec can be found on the SatoshiLabs website: | 
					
						
							|  |  |  | // https://doc.satoshilabs.com/trezor-tech/api-protobuf.html | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package usbwallet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/binary" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/accounts" | 
					
						
							| 
									
										
										
										
											2019-01-26 14:30:47 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/accounts/usbwallet/trezor" | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common/hexutil" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							|  |  |  | 	"github.com/golang/protobuf/proto" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In | 
					
						
							|  |  |  | // this case, the calling application should display a pinpad and send back the | 
					
						
							|  |  |  | // encoded passphrase. | 
					
						
							|  |  |  | var ErrTrezorPINNeeded = errors.New("trezor: pin needed") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | // ErrTrezorPassphraseNeeded is returned if opening the trezor requires a passphrase | 
					
						
							|  |  |  | var ErrTrezorPassphraseNeeded = errors.New("trezor: passphrase needed") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | // errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange | 
					
						
							|  |  |  | // if the device replies with a mismatching header. This usually means the device | 
					
						
							|  |  |  | // is in browser mode. | 
					
						
							|  |  |  | var errTrezorReplyInvalidHeader = errors.New("trezor: invalid reply header") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // trezorDriver implements the communication with a Trezor hardware wallet. | 
					
						
							|  |  |  | type trezorDriver struct { | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | 	device         io.ReadWriter // USB device connection to communicate through | 
					
						
							|  |  |  | 	version        [3]uint32     // Current version of the Trezor firmware | 
					
						
							|  |  |  | 	label          string        // Current textual label of the Trezor device | 
					
						
							|  |  |  | 	pinwait        bool          // Flags whether the device is waiting for PIN entry | 
					
						
							|  |  |  | 	passphrasewait bool          // Flags whether the device is waiting for passphrase entry | 
					
						
							|  |  |  | 	failure        error         // Any failure that would make the device unusable | 
					
						
							|  |  |  | 	log            log.Logger    // Contextual logger to tag the trezor with its id | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newTrezorDriver creates a new instance of a Trezor USB protocol driver. | 
					
						
							|  |  |  | func newTrezorDriver(logger log.Logger) driver { | 
					
						
							|  |  |  | 	return &trezorDriver{ | 
					
						
							|  |  |  | 		log: logger, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Status implements accounts.Wallet, always whether the Trezor is opened, closed | 
					
						
							|  |  |  | // or whether the Ethereum app was not started on it. | 
					
						
							|  |  |  | func (w *trezorDriver) Status() (string, error) { | 
					
						
							|  |  |  | 	if w.failure != nil { | 
					
						
							|  |  |  | 		return fmt.Sprintf("Failed: %v", w.failure), w.failure | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if w.device == nil { | 
					
						
							|  |  |  | 		return "Closed", w.failure | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if w.pinwait { | 
					
						
							|  |  |  | 		return fmt.Sprintf("Trezor v%d.%d.%d '%s' waiting for PIN", w.version[0], w.version[1], w.version[2], w.label), w.failure | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Sprintf("Trezor v%d.%d.%d '%s' online", w.version[0], w.version[1], w.version[2], w.label), w.failure | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Open implements usbwallet.driver, attempting to initialize the connection to | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | // the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation: | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | //  * The first phase is to initialize the connection and read the wallet's | 
					
						
							| 
									
										
										
										
											2019-05-07 18:22:24 +02:00
										 |  |  | //    features. This phase is invoked if the provided passphrase is empty. The | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | //    device will display the pinpad as a result and will return an appropriate | 
					
						
							|  |  |  | //    error to notify the user that a second open phase is needed. | 
					
						
							|  |  |  | //  * The second phase is to unlock access to the Trezor, which is done by the | 
					
						
							|  |  |  | //    user actually providing a passphrase mapping a keyboard keypad to the pin | 
					
						
							|  |  |  | //    number of the user (shuffled according to the pinpad displayed). | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | //  * If needed the device will ask for passphrase which will require calling | 
					
						
							|  |  |  | //    open again with the actual passphrase (3rd phase) | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error { | 
					
						
							|  |  |  | 	w.device, w.failure = device, nil | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If phase 1 is requested, init the connection and wait for user callback | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | 	if passphrase == "" && !w.passphrasewait { | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 		// If we're already waiting for a PIN entry, insta-return | 
					
						
							|  |  |  | 		if w.pinwait { | 
					
						
							|  |  |  | 			return ErrTrezorPINNeeded | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Initialize a connection to the device | 
					
						
							|  |  |  | 		features := new(trezor.Features) | 
					
						
							|  |  |  | 		if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()} | 
					
						
							|  |  |  | 		w.label = features.GetLabel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | 		// Do a manual ping, forcing the device to ask for its PIN and Passphrase | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 		askPin := true | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | 		askPassphrase := true | 
					
						
							|  |  |  | 		res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success)) | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Only return the PIN request if the device wasn't unlocked until now | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | 		switch res { | 
					
						
							|  |  |  | 		case 0: | 
					
						
							|  |  |  | 			w.pinwait = true | 
					
						
							|  |  |  | 			return ErrTrezorPINNeeded | 
					
						
							|  |  |  | 		case 1: | 
					
						
							|  |  |  | 			w.pinwait = false | 
					
						
							|  |  |  | 			w.passphrasewait = true | 
					
						
							|  |  |  | 			return ErrTrezorPassphraseNeeded | 
					
						
							|  |  |  | 		case 2: | 
					
						
							|  |  |  | 			return nil // responded with trezor.Success | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Phase 2 requested with actual PIN entry | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | 	if w.pinwait { | 
					
						
							|  |  |  | 		w.pinwait = false | 
					
						
							|  |  |  | 		res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			w.failure = err | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if res == 1 { | 
					
						
							|  |  |  | 			w.passphrasewait = true | 
					
						
							|  |  |  | 			return ErrTrezorPassphraseNeeded | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if w.passphrasewait { | 
					
						
							|  |  |  | 		w.passphrasewait = false | 
					
						
							|  |  |  | 		if _, err := w.trezorExchange(&trezor.PassphraseAck{Passphrase: &passphrase}, new(trezor.Success)); err != nil { | 
					
						
							|  |  |  | 			w.failure = err | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-24 13:21:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close implements usbwallet.driver, cleaning up and metadata maintained within | 
					
						
							|  |  |  | // the Trezor driver. | 
					
						
							|  |  |  | func (w *trezorDriver) Close() error { | 
					
						
							|  |  |  | 	w.version, w.label, w.pinwait = [3]uint32{}, "", false | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Heartbeat implements usbwallet.driver, performing a sanity check against the | 
					
						
							|  |  |  | // Trezor to see if it's still online. | 
					
						
							|  |  |  | func (w *trezorDriver) Heartbeat() error { | 
					
						
							|  |  |  | 	if _, err := w.trezorExchange(&trezor.Ping{}, new(trezor.Success)); err != nil { | 
					
						
							|  |  |  | 		w.failure = err | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Derive implements usbwallet.driver, sending a derivation request to the Trezor | 
					
						
							|  |  |  | // and returning the Ethereum address located on that derivation path. | 
					
						
							|  |  |  | func (w *trezorDriver) Derive(path accounts.DerivationPath) (common.Address, error) { | 
					
						
							|  |  |  | 	return w.trezorDerive(path) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SignTx implements usbwallet.driver, sending the transaction to the Trezor and | 
					
						
							|  |  |  | // waiting for the user to confirm or deny the transaction. | 
					
						
							|  |  |  | func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) { | 
					
						
							|  |  |  | 	if w.device == nil { | 
					
						
							|  |  |  | 		return common.Address{}, nil, accounts.ErrWalletClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return w.trezorSign(path, tx, chainID) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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) { | 
					
						
							|  |  |  | 	address := new(trezor.EthereumAddress) | 
					
						
							|  |  |  | 	if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil { | 
					
						
							|  |  |  | 		return common.Address{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-28 23:17:00 +03:00
										 |  |  | 	if addr := address.GetAddressBin(); len(addr) > 0 { // Older firmwares use binary fomats | 
					
						
							|  |  |  | 		return common.BytesToAddress(addr), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal fomats | 
					
						
							|  |  |  | 		return common.HexToAddress(addr), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return common.Address{}, errors.New("missing derived address") | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // trezorSign sends the transaction to the Trezor wallet, and waits for the user | 
					
						
							|  |  |  | // to confirm or deny the transaction. | 
					
						
							|  |  |  | func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) { | 
					
						
							|  |  |  | 	// Create the transaction initiation message | 
					
						
							|  |  |  | 	data := tx.Data() | 
					
						
							|  |  |  | 	length := uint32(len(data)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	request := &trezor.EthereumSignTx{ | 
					
						
							|  |  |  | 		AddressN:   derivationPath, | 
					
						
							|  |  |  | 		Nonce:      new(big.Int).SetUint64(tx.Nonce()).Bytes(), | 
					
						
							|  |  |  | 		GasPrice:   tx.GasPrice().Bytes(), | 
					
						
							| 
									
										
										
										
											2017-11-13 13:47:27 +02:00
										 |  |  | 		GasLimit:   new(big.Int).SetUint64(tx.Gas()).Bytes(), | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 		Value:      tx.Value().Bytes(), | 
					
						
							|  |  |  | 		DataLength: &length, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if to := tx.To(); to != nil { | 
					
						
							| 
									
										
										
										
											2019-05-28 23:17:00 +03:00
										 |  |  | 		// Non contract deploy, set recipient explicitly | 
					
						
							|  |  |  | 		hex := to.Hex() | 
					
						
							|  |  |  | 		request.ToHex = &hex     // Newer firmwares (old will ignore) | 
					
						
							|  |  |  | 		request.ToBin = (*to)[:] // Older firmwares (new will ignore) | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if length > 1024 { // Send the data chunked if that was requested | 
					
						
							|  |  |  | 		request.DataInitialChunk, data = data[:1024], data[1024:] | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		request.DataInitialChunk, data = data, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?) | 
					
						
							|  |  |  | 		id := uint32(chainID.Int64()) | 
					
						
							|  |  |  | 		request.ChainId = &id | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Send the initiation message and stream content until a signature is returned | 
					
						
							|  |  |  | 	response := new(trezor.EthereumTxRequest) | 
					
						
							|  |  |  | 	if _, err := w.trezorExchange(request, response); err != nil { | 
					
						
							|  |  |  | 		return common.Address{}, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for response.DataLength != nil && int(*response.DataLength) <= len(data) { | 
					
						
							|  |  |  | 		chunk := data[:*response.DataLength] | 
					
						
							|  |  |  | 		data = data[*response.DataLength:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if _, err := w.trezorExchange(&trezor.EthereumTxAck{DataChunk: chunk}, response); err != nil { | 
					
						
							|  |  |  | 			return common.Address{}, nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Extract the Ethereum signature and do a sanity validation | 
					
						
							|  |  |  | 	if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 { | 
					
						
							|  |  |  | 		return common.Address{}, nil, errors.New("reply lacks signature") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create the correct signer and signature transform based on the chain ID | 
					
						
							|  |  |  | 	var signer types.Signer | 
					
						
							|  |  |  | 	if chainID == nil { | 
					
						
							|  |  |  | 		signer = new(types.HomesteadSigner) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		signer = types.NewEIP155Signer(chainID) | 
					
						
							| 
									
										
										
										
											2018-10-16 01:34:50 +03:00
										 |  |  | 		signature[64] -= byte(chainID.Uint64()*2 + 35) | 
					
						
							| 
									
										
										
										
											2017-08-09 12:51:16 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Inject the final signature into the transaction and sanity check the sender | 
					
						
							|  |  |  | 	signed, err := tx.WithSignature(signer, signature) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.Address{}, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sender, err := types.Sender(signer, signed) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return common.Address{}, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sender, signed, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // trezorExchange performs a data exchange with the Trezor wallet, sending it a | 
					
						
							|  |  |  | // message and retrieving the response. If multiple responses are possible, the | 
					
						
							|  |  |  | // method will also return the index of the destination object used. | 
					
						
							|  |  |  | func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Message) (int, error) { | 
					
						
							|  |  |  | 	// Construct the original message payload to chunk up | 
					
						
							|  |  |  | 	data, err := proto.Marshal(req) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	payload := make([]byte, 8+len(data)) | 
					
						
							|  |  |  | 	copy(payload, []byte{0x23, 0x23}) | 
					
						
							|  |  |  | 	binary.BigEndian.PutUint16(payload[2:], trezor.Type(req)) | 
					
						
							|  |  |  | 	binary.BigEndian.PutUint32(payload[4:], uint32(len(data))) | 
					
						
							|  |  |  | 	copy(payload[8:], data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Stream all the chunks to the device | 
					
						
							|  |  |  | 	chunk := make([]byte, 64) | 
					
						
							|  |  |  | 	chunk[0] = 0x3f // Report ID magic number | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for len(payload) > 0 { | 
					
						
							|  |  |  | 		// Construct the new message to stream, padding with zeroes if needed | 
					
						
							|  |  |  | 		if len(payload) > 63 { | 
					
						
							|  |  |  | 			copy(chunk[1:], payload[:63]) | 
					
						
							|  |  |  | 			payload = payload[63:] | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			copy(chunk[1:], payload) | 
					
						
							|  |  |  | 			copy(chunk[1+len(payload):], make([]byte, 63-len(payload))) | 
					
						
							|  |  |  | 			payload = nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Send over to the device | 
					
						
							|  |  |  | 		w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk)) | 
					
						
							|  |  |  | 		if _, err := w.device.Write(chunk); err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Stream the reply back from the wallet in 64 byte chunks | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		kind  uint16 | 
					
						
							|  |  |  | 		reply []byte | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		// Read the next chunk from the Trezor wallet | 
					
						
							|  |  |  | 		if _, err := io.ReadFull(w.device, chunk); err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Make sure the transport header matches | 
					
						
							|  |  |  | 		if chunk[0] != 0x3f || (len(reply) == 0 && (chunk[1] != 0x23 || chunk[2] != 0x23)) { | 
					
						
							|  |  |  | 			return 0, errTrezorReplyInvalidHeader | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// If it's the first chunk, retrieve the reply message type and total message length | 
					
						
							|  |  |  | 		var payload []byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(reply) == 0 { | 
					
						
							|  |  |  | 			kind = binary.BigEndian.Uint16(chunk[3:5]) | 
					
						
							|  |  |  | 			reply = make([]byte, 0, int(binary.BigEndian.Uint32(chunk[5:9]))) | 
					
						
							|  |  |  | 			payload = chunk[9:] | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			payload = chunk[1:] | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Append to the reply and stop when filled up | 
					
						
							|  |  |  | 		if left := cap(reply) - len(reply); left > len(payload) { | 
					
						
							|  |  |  | 			reply = append(reply, payload...) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			reply = append(reply, payload[:left]...) | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Try to parse the reply into the requested reply message | 
					
						
							|  |  |  | 	if kind == uint16(trezor.MessageType_MessageType_Failure) { | 
					
						
							|  |  |  | 		// Trezor returned a failure, extract and return the message | 
					
						
							|  |  |  | 		failure := new(trezor.Failure) | 
					
						
							|  |  |  | 		if err := proto.Unmarshal(reply, failure); err != nil { | 
					
						
							|  |  |  | 			return 0, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0, errors.New("trezor: " + failure.GetMessage()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) { | 
					
						
							|  |  |  | 		// Trezor is waiting for user confirmation, ack and wait for the next message | 
					
						
							|  |  |  | 		return w.trezorExchange(&trezor.ButtonAck{}, results...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, res := range results { | 
					
						
							|  |  |  | 		if trezor.Type(res) == kind { | 
					
						
							|  |  |  | 			return i, proto.Unmarshal(reply, res) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	expected := make([]string, len(results)) | 
					
						
							|  |  |  | 	for i, res := range results { | 
					
						
							|  |  |  | 		expected[i] = trezor.Name(trezor.Type(res)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind)) | 
					
						
							|  |  |  | } |