diff --git a/accounts/usbwallet/hub.go b/accounts/usbwallet/hub.go index fc711bb3e..35695dd1c 100644 --- a/accounts/usbwallet/hub.go +++ b/accounts/usbwallet/hub.go @@ -89,6 +89,12 @@ func NewTrezorHub() (*Hub, error) { return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor 1 */}, 0xff00, 0, newTrezorDriver) } +// NewWebUSBTrezorHub creates a new hardware wallet manager for Trezor devices with +// firmware version > 1.8.0 +func NewWebUSBTrezorHub() (*Hub, error) { + return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor 1 WebUSB */}, 0, 0, newTrezorDriver) +} + // newHub creates a new hardware wallet manager for generic USB devices. func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) { if !hid.Supported() { @@ -148,9 +154,19 @@ func (hub *Hub) refreshWallets() { return } } - for _, info := range hid.Enumerate(hub.vendorID, 0) { + infos, err := hid.Enumerate(hub.vendorID, 0) + if err != nil { + if runtime.GOOS == "linux" { + // See rationale before the enumeration why this is needed and only on Linux. + hub.commsLock.Unlock() + } + log.Error("error enumerating USB enumeration: ", "code", err) + return + } + for _, info := range infos { for _, id := range hub.productIDs { - if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) { + _, pid, endpoint, _ /* FIXME usageID */ := info.IDs() + if pid == id && ( /* FIXME usageID == hub.usageID || */ endpoint == hub.endpointID) { devices = append(devices, info) break } @@ -169,7 +185,7 @@ func (hub *Hub) refreshWallets() { ) for _, device := range devices { - url := accounts.URL{Scheme: hub.scheme, Path: device.Path} + url := accounts.URL{Scheme: hub.scheme, Path: device.GetPath()} // Drop wallets in front of the next device or those that failed for some reason for len(hub.wallets) > 0 { diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go index 31c2a7d8a..9453b52c3 100644 --- a/accounts/usbwallet/trezor.go +++ b/accounts/usbwallet/trezor.go @@ -36,6 +36,8 @@ import ( "github.com/golang/protobuf/proto" ) +var ErrInvalidDeviceType = errors.New("trezor: invalid device type") + // 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. diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go index 7ff9c5372..df38f953d 100644 --- a/accounts/usbwallet/wallet.go +++ b/accounts/usbwallet/wallet.go @@ -78,7 +78,7 @@ type wallet struct { url *accounts.URL // Textual URL uniquely identifying this wallet info hid.DeviceInfo // Known USB device infos about the wallet - device *hid.Device // USB device advertising itself as a hardware wallet + device hid.Device // USB device advertising itself as a hardware wallet accounts []accounts.Account // List of derive accounts pinned on the hardware wallet paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations diff --git a/node/config.go b/node/config.go index d76311ba6..c864fea9c 100644 --- a/node/config.go +++ b/node/config.go @@ -507,6 +507,12 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { } else { backends = append(backends, trezorhub) } + // Start a USB hub for Trezor hardware wallets (WebUSB version) + if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil { + log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) + } else { + backends = append(backends, trezorhub) + } } if len(conf.SmartCardDaemonPath) > 0 { // Start a smart card hub diff --git a/signer/core/api.go b/signer/core/api.go index 783aaece4..12bbd7bd3 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -151,6 +151,13 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts. backends = append(backends, trezorhub) log.Debug("Trezor support enabled") } + // Start a USB hub for Trezor hardware wallets (WebUSB version) + if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil { + log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) + } else { + backends = append(backends, trezorhub) + log.Debug("Trezor support enabled") + } } // Clef doesn't allow insecure http account unlock. return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...) diff --git a/vendor/github.com/karalabe/hid/generic.go b/vendor/github.com/karalabe/hid/generic.go new file mode 100644 index 000000000..8de16fcbf --- /dev/null +++ b/vendor/github.com/karalabe/hid/generic.go @@ -0,0 +1,54 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2019 Péter Szilágyi, Guillaume Ballet. All rights reserved. + +package hid + +import ( + "C" +) + +type GenericEndpointDirection uint8 + +// List of endpoint direction types +const ( + GenericEndpointDirectionOut = 0x00 + GenericEndpointDirectionIn = 0x80 +) + +// List of endpoint attributes +const ( + GenericEndpointAttributeInterrupt = 3 +) + +// GenericEndpoint represents a USB endpoint +type GenericEndpoint struct { + Address uint8 + Direction GenericEndpointDirection + Attributes uint8 +} + +type GenericDeviceInfo struct { + Path string // Platform-specific device path + VendorID uint16 // Device Vendor ID + ProductID uint16 // Device Product ID + + device *GenericDevice + + Interface int + + Endpoints []GenericEndpoint +} + +func (gdi *GenericDeviceInfo) Type() DeviceType { + return DeviceTypeGeneric +} + +// Platform-specific device path +func (gdi *GenericDeviceInfo) GetPath() string { + return gdi.Path +} + +// IDs returns the vendor and product IDs for the device +func (gdi *GenericDeviceInfo) IDs() (uint16, uint16, int, uint16) { + return gdi.VendorID, gdi.ProductID, gdi.Interface, 0 +} diff --git a/vendor/github.com/karalabe/hid/hid.go b/vendor/github.com/karalabe/hid/hid.go index 60a40b8c2..f929006d8 100644 --- a/vendor/github.com/karalabe/hid/hid.go +++ b/vendor/github.com/karalabe/hid/hid.go @@ -17,8 +17,8 @@ var ErrDeviceClosed = errors.New("hid: device closed") // operating system is not supported by the library. var ErrUnsupportedPlatform = errors.New("hid: unsupported platform") -// DeviceInfo is a hidapi info structure. -type DeviceInfo struct { +// HidDeviceInfo is a hidapi info structure. +type HidDeviceInfo struct { Path string // Platform-specific device path VendorID uint16 // Device Vendor ID ProductID uint16 // Device Product ID @@ -35,3 +35,18 @@ type DeviceInfo struct { // only if the device contains more than one interface. Interface int } + +// GetPath returns the system-dependent path to the device +func (hdi *HidDeviceInfo) GetPath() string { + return hdi.Path +} + +// IDs returns the vendor and product id of the device +func (hdi *HidDeviceInfo) IDs() (uint16, uint16, int, uint16) { + return hdi.VendorID, hdi.ProductID, hdi.Interface, hdi.UsagePage +} + +// Type returns the type of the device (HID or generic) +func (hdi *HidDeviceInfo) Type() DeviceType { + return DeviceTypeHID +} diff --git a/vendor/github.com/karalabe/hid/hid_disabled.go b/vendor/github.com/karalabe/hid/hid_disabled.go index 0f266ba58..ce9827953 100644 --- a/vendor/github.com/karalabe/hid/hid_disabled.go +++ b/vendor/github.com/karalabe/hid/hid_disabled.go @@ -4,7 +4,7 @@ // This file is released under the 3-clause BSD license. Note however that Linux // support depends on libusb, released under GNU LGPL 2.1 or later. -// +build !linux,!darwin,!windows ios !cgo +// +build !freebsd,!linux,!darwin,!windows ios !cgo package hid @@ -22,30 +22,55 @@ func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { return nil } -// Device is a live HID USB connected device handle. On platforms that this file +// HidDevice is a live HID USB connected device handle. On platforms that this file // implements the type lacks the actual HID device and all methods are noop. -type Device struct { - DeviceInfo // Embed the infos for easier access +type HidDevice struct { + HidDeviceInfo // Embed the infos for easier access } // Open connects to an HID device by its path name. On platforms that this file // implements the method just returns an error. -func (info DeviceInfo) Open() (*Device, error) { +func (info HidDeviceInfo) Open() (*Device, error) { return nil, ErrUnsupportedPlatform } // Close releases the HID USB device handle. On platforms that this file implements // the method is just a noop. -func (dev *Device) Close() error { return nil } +func (dev *HidDevice) Close() error { return ErrUnsupportedPlatform } // Write sends an output report to a HID device. On platforms that this file // implements the method just returns an error. -func (dev *Device) Write(b []byte) (int, error) { +func (dev *HidDevice) Write(b []byte) (int, error) { return 0, ErrUnsupportedPlatform } // Read retrieves an input report from a HID device. On platforms that this file // implements the method just returns an error. -func (dev *Device) Read(b []byte) (int, error) { +func (dev *HidDevice) Read(b []byte) (int, error) { return 0, ErrUnsupportedPlatform } + +// Open tries to open the USB device represented by the current DeviceInfo +func (gdi *GenericDeviceInfo) Open() (Device, error) { + return nil, ErrUnsupportedPlatform +} + +// GenericDevice represents a generic USB device +type GenericDevice struct { + *GenericDeviceInfo // Embed the infos for easier access +} + +// Write implements io.ReaderWriter +func (gd *GenericDevice) Write(b []byte) (int, error) { + return 0, ErrUnsupportedPlatform +} + +// Read implements io.ReaderWriter +func (gd *GenericDevice) Read(b []byte) (int, error) { + return 0, ErrUnsupportedPlatform +} + +// Close a previously opened generic USB device +func (gd *GenericDevice) Close() error { + return ErrUnsupportedPlatform +} diff --git a/vendor/github.com/karalabe/hid/hid_enabled.go b/vendor/github.com/karalabe/hid/hid_enabled.go index e95e5792d..ee27ff98d 100644 --- a/vendor/github.com/karalabe/hid/hid_enabled.go +++ b/vendor/github.com/karalabe/hid/hid_enabled.go @@ -4,7 +4,7 @@ // This file is released under the 3-clause BSD license. Note however that Linux // support depends on libusb, released under LGNU GPL 2.1 or later. -// +build linux,cgo darwin,!ios,cgo windows,cgo +// +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo package hid @@ -13,13 +13,15 @@ package hid #cgo linux CFLAGS: -I./libusb/libusb -DDEFAULT_VISIBILITY="" -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int #cgo linux,!android LDFLAGS: -lrt -#cgo darwin CFLAGS: -DOS_DARWIN -#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit +#cgo darwin CFLAGS: -DOS_DARWIN -I./libusb/libusb +#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit -lusb-1.0.0 #cgo windows CFLAGS: -DOS_WINDOWS #cgo windows LDFLAGS: -lsetupapi +#cgo freebsd CFLAGS: -DOS_FREEBSD +#cgo freebsd LDFLAGS: -lusb #ifdef OS_LINUX - #include + #include #include "os/threads_posix.c" #include "os/poll_posix.c" @@ -35,15 +37,50 @@ package hid #include "hidapi/libusb/hid.c" #elif OS_DARWIN + #include #include "hidapi/mac/hid.c" #elif OS_WINDOWS #include "hidapi/windows/hid.c" +#elif OS_FREEBSD + #include + #include + #include "hidapi/libusb/hid.c" #endif + +#if defined(OS_LINUX) || defined(OS_WINDOWS) + void copy_device_list_to_slice(struct libusb_device **data, struct libusb_device **list, int count) + { + int i; + struct libusb_device *current = *list; + for (i=0; ilist.next, struct libusb_device, list); + } + } +#elif defined(OS_DARWIN) || defined(OS_FREEBSD) + void copy_device_list_to_slice(struct libusb_device **data, struct libusb_device **list, int count) + { + int i; + // No memcopy because the struct size isn't available for a sizeof() + for (i=0; i