Unverified Commit 4799b5ab authored by Guillaume Ballet's avatar Guillaume Ballet Committed by Péter Szilágyi

accounts/usbwallet: support webusb for Trezor wallets

parent 7a22da98
...@@ -89,6 +89,12 @@ func NewTrezorHub() (*Hub, error) { ...@@ -89,6 +89,12 @@ func NewTrezorHub() (*Hub, error) {
return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor 1 */}, 0xff00, 0, newTrezorDriver) 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. // 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) { func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
if !hid.Supported() { if !hid.Supported() {
...@@ -148,9 +154,19 @@ func (hub *Hub) refreshWallets() { ...@@ -148,9 +154,19 @@ func (hub *Hub) refreshWallets() {
return 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 { 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) devices = append(devices, info)
break break
} }
...@@ -169,7 +185,7 @@ func (hub *Hub) refreshWallets() { ...@@ -169,7 +185,7 @@ func (hub *Hub) refreshWallets() {
) )
for _, device := range devices { 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 // Drop wallets in front of the next device or those that failed for some reason
for len(hub.wallets) > 0 { for len(hub.wallets) > 0 {
......
...@@ -36,6 +36,8 @@ import ( ...@@ -36,6 +36,8 @@ import (
"github.com/golang/protobuf/proto" "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 // 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 // this case, the calling application should display a pinpad and send back the
// encoded passphrase. // encoded passphrase.
......
...@@ -78,7 +78,7 @@ type wallet struct { ...@@ -78,7 +78,7 @@ type wallet struct {
url *accounts.URL // Textual URL uniquely identifying this wallet url *accounts.URL // Textual URL uniquely identifying this wallet
info hid.DeviceInfo // Known USB device infos about the 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 accounts []accounts.Account // List of derive accounts pinned on the hardware wallet
paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
......
...@@ -507,6 +507,12 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { ...@@ -507,6 +507,12 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
} else { } else {
backends = append(backends, trezorhub) 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 { if len(conf.SmartCardDaemonPath) > 0 {
// Start a smart card hub // Start a smart card hub
......
...@@ -151,6 +151,13 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts. ...@@ -151,6 +151,13 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts.
backends = append(backends, trezorhub) backends = append(backends, trezorhub)
log.Debug("Trezor support enabled") 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. // Clef doesn't allow insecure http account unlock.
return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...) return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, backends...)
......
// 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
}
...@@ -17,8 +17,8 @@ var ErrDeviceClosed = errors.New("hid: device closed") ...@@ -17,8 +17,8 @@ var ErrDeviceClosed = errors.New("hid: device closed")
// operating system is not supported by the library. // operating system is not supported by the library.
var ErrUnsupportedPlatform = errors.New("hid: unsupported platform") var ErrUnsupportedPlatform = errors.New("hid: unsupported platform")
// DeviceInfo is a hidapi info structure. // HidDeviceInfo is a hidapi info structure.
type DeviceInfo struct { type HidDeviceInfo struct {
Path string // Platform-specific device path Path string // Platform-specific device path
VendorID uint16 // Device Vendor ID VendorID uint16 // Device Vendor ID
ProductID uint16 // Device Product ID ProductID uint16 // Device Product ID
...@@ -35,3 +35,18 @@ type DeviceInfo struct { ...@@ -35,3 +35,18 @@ type DeviceInfo struct {
// only if the device contains more than one interface. // only if the device contains more than one interface.
Interface int 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
}
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// This file is released under the 3-clause BSD license. Note however that Linux // 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. // 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 package hid
...@@ -22,30 +22,55 @@ func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { ...@@ -22,30 +22,55 @@ func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
return nil 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. // implements the type lacks the actual HID device and all methods are noop.
type Device struct { type HidDevice struct {
DeviceInfo // Embed the infos for easier access HidDeviceInfo // Embed the infos for easier access
} }
// Open connects to an HID device by its path name. On platforms that this file // Open connects to an HID device by its path name. On platforms that this file
// implements the method just returns an error. // implements the method just returns an error.
func (info DeviceInfo) Open() (*Device, error) { func (info HidDeviceInfo) Open() (*Device, error) {
return nil, ErrUnsupportedPlatform return nil, ErrUnsupportedPlatform
} }
// Close releases the HID USB device handle. On platforms that this file implements // Close releases the HID USB device handle. On platforms that this file implements
// the method is just a noop. // 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 // Write sends an output report to a HID device. On platforms that this file
// implements the method just returns an error. // 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 return 0, ErrUnsupportedPlatform
} }
// Read retrieves an input report from a HID device. On platforms that this file // Read retrieves an input report from a HID device. On platforms that this file
// implements the method just returns an error. // 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 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
}
// hid - Gopher Interface Devices (USB HID)
// Copyright (c) 2019 Péter Szilágyi, Guillaume Ballet. All rights reserved.
//
// 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.
// Package usb provide interfaces for generic USB devices.
package hid
// DeviceType represents the type of a USB device (generic or HID)
type DeviceType int
// List of supported device types
const (
DeviceTypeGeneric DeviceType = 0
DeviceTypeHID DeviceType = 1
)
// Enumerate returns a list of all the HID devices attached to the system which
// match the vendor and product id:
// - If the vendor id is set to 0 then any vendor matches.
// - If the product id is set to 0 then any product matches.
// - If the vendor and product id are both 0, all HID devices are returned.
// func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
// }
// DeviceInfo is a generic libusb info interface
type DeviceInfo interface {
// Type returns the type of the device (generic or HID)
Type() DeviceType
// Platform-specific device path
GetPath() string
// IDs returns the vendor and product IDs for the device,
// as well as the endpoint id and the usage page.
IDs() (uint16, uint16, int, uint16)
// Open tries to open the USB device represented by the current DeviceInfo
Open() (Device, error)
}
// Device is a generic libusb device interface
type Device interface {
Close() error
Write(b []byte) (int, error)
Read(b []byte) (int, error)
// Type returns the type of the device (generic or HID)
Type() DeviceType
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
// https://github.com/orofarne/gowchar/blob/master/LICENSE // https://github.com/orofarne/gowchar/blob/master/LICENSE
// +build !ios // +build !ios
// +build linux darwin windows // +build freebsd linux darwin windows
package hid package hid
......
...@@ -267,10 +267,10 @@ ...@@ -267,10 +267,10 @@
"revisionTime": "2017-04-30T22:20:11Z" "revisionTime": "2017-04-30T22:20:11Z"
}, },
{ {
"checksumSHA1": "6XsjAARQFvlW6dS15al0ibTFPOQ=", "checksumSHA1": "p6UjFsx/1ACWAhsdEOWrXAHptGY=",
"path": "github.com/karalabe/hid", "path": "github.com/karalabe/hid",
"revision": "d815e0c1a2e2082a287a2806bc90bc8fc7b276a9", "revision": "e40407cce1c217644c09da5415bbfb07d330ea5e",
"revisionTime": "2018-11-28T19:21:57Z", "revisionTime": "2019-05-28T15:16:06Z",
"tree": true "tree": true
}, },
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment