From 18e3e76e2805681549274a0ca7d1f3f0197be522 Mon Sep 17 00:00:00 2001 From: solipsis Date: Thu, 10 Jan 2019 15:12:15 -0700 Subject: [PATCH] refactor device transport and keepkey struct --- pkg/keepkey/keepkey.go | 98 ++++++++++++++++++++++++ pkg/keepkey/messages.go | 6 +- pkg/keepkey/transport.go | 158 ++++++++------------------------------- pkg/keepkey/webusb.go | 7 -- 4 files changed, 131 insertions(+), 138 deletions(-) create mode 100644 pkg/keepkey/keepkey.go diff --git a/pkg/keepkey/keepkey.go b/pkg/keepkey/keepkey.go new file mode 100644 index 0000000..d62427b --- /dev/null +++ b/pkg/keepkey/keepkey.go @@ -0,0 +1,98 @@ +package keepkey + +import ( + "io" + "log" + "os" +) + +var ( + vendorID uint16 = 0x2B24 + productIDs = []uint16{0x0001, 0x0002} + + // TREZOR vendorID + // vendorID uint16 = 0x534c +) + +// Keepkey represents an open HID connection to a keepkey and possibly a +// connection to the debug link if enabled +type Keepkey struct { + transport *transport + autoButton bool // Automatically send button presses. DebugLink must be enabled in the firmware + vendorID uint16 + productID uint16 + label, serial string // Used for specifying which device to send commands if multiple are connected + logger + deviceQueue, debugQueue chan *deviceResponse // for subscribing to responses over different interfaces +} + +// transport contains handles to the primary and debug interfaces of the target device +type transport struct { + conn io.ReadWriteCloser // primary interface to device + debug io.ReadWriteCloser // debug link connection to device if enabled +} + +// deviceResponse contains the protobuf response from the device as well as the integer +// representing its type +type deviceResponse struct { + reply []byte + kind uint16 +} + +// Config specifies various attributes that can be set on a Keepkey connection such as +// where to write debug logs and whether to automatically push the button on a debugLink enabled device +type Config struct { + Logger logger + AutoButton bool // Automatically send button presses. DebugLink must be enabled in the firmware +} + +// logger is a simple printf style output interface +type logger interface { + Printf(string, ...interface{}) +} + +// SetLogger sets the logging device for this keepkey +func (kk *Keepkey) SetLogger(l logger) { + kk.logger = l +} + +func (kk *Keepkey) log(str string, args ...interface{}) { + if kk.logger != nil { + kk.logger.Printf(str, args...) + } +} + +// Serial returns the serial id of the device +func (kk *Keepkey) Serial() string { + return kk.serial +} + +// Label returns the user set lable identifying the device +func (kk *Keepkey) Label() string { + return kk.label +} + +// DeviceID returns the device hardware device ID. NOTE: This is different than the deviceID +// advertised by the device for the HID and webUSB protocols. This deviceID plays no part in +// the communiaction protocols and is purely for device identification +func (kk *Keepkey) DeviceID() string { + return kk.deviceID +} + +func newKeepkey() *Keepkey { + return &Keepkey{ + vendorID: vendorID, + autoButton: true, + logger: log.New(os.Stdout, "", 0), + } +} + +func newKeepkeyFromConfig(cfg *Config) *Keepkey { + kk := newKeepkey() + kk.logger = cfg.Logger + kk.autoButton = cfg.AutoButton + kk.deviceQueue = make(chan *deviceResponse, 1) + kk.debugQueue = make(chan *deviceResponse, 1) + + return kk +} diff --git a/pkg/keepkey/messages.go b/pkg/keepkey/messages.go index 34d252b..dd696b9 100644 --- a/pkg/keepkey/messages.go +++ b/pkg/keepkey/messages.go @@ -35,9 +35,9 @@ func (kk *Keepkey) ApplyPolicy(name string, enabled bool) error { return nil } -// Initialize assigns a hid connection to this keepkey and send initialize message to device -func (kk *Keepkey) Initialize(device io.ReadWriteCloser) (*kkProto.Features, error) { - kk.device = device +// Initialize sends initialize message to device forcing the device to its neutral state +// This should be the first message sent when communicating with a device for the first time +func (kk *Keepkey) Initialize() (*kkProto.Features, error) { features := new(kkProto.Features) if _, err := kk.keepkeyExchange(&kkProto.Initialize{}, features); err != nil { diff --git a/pkg/keepkey/transport.go b/pkg/keepkey/transport.go index 0a55f1b..db0910e 100644 --- a/pkg/keepkey/transport.go +++ b/pkg/keepkey/transport.go @@ -8,7 +8,6 @@ import ( "io" "io/ioutil" "log" - "os" "strings" "github.com/golang/protobuf/proto" @@ -16,92 +15,15 @@ import ( "github.com/solipsis/go-keepkey/pkg/kkProto" ) -var ( - vendorID uint16 = 0x2B24 - productIDs = []uint16{0x0001, 0x0002} - //vendorID uint16 = 0x534c TREZOR -) - -// Keepkey represents an open HID connection to a keepkey and possibly a -// connection to the debug link if enabled -type Keepkey struct { - info hid.DeviceInfo - device, debug, infoOut io.ReadWriteCloser - autoButton bool // Automatically send button presses. DebugLink must be enabled in the firmware - vendorID uint16 - productID uint16 - label, serial string // Used for specifying which device to send commands if multiple are connected - logger - deviceQueue, debugQueue, infoQueue chan *deviceResponse -} - -type deviceResponse struct { - reply []byte - kind uint16 -} - -// Config specifies various attributes that can be set on a Keepkey connection such as -// where to write debug logs and whether to automatically push the button on a debugLink enabled device -type Config struct { - Logger logger - AutoButton bool // Automatically send button presses. DebugLink must be enabled in the firmware -} - -func newKeepkey() *Keepkey { - return &Keepkey{ - vendorID: vendorID, - //productID: productID, - autoButton: true, - //logger: log.New(ioutil.Discard, "", 0), - logger: log.New(os.Stdout, "", 0), - } -} - -func newKeepkeyFromConfig(cfg *Config) *Keepkey { - kk := newKeepkey() - kk.logger = cfg.Logger - kk.autoButton = cfg.AutoButton - kk.deviceQueue = make(chan *deviceResponse, 1) - kk.debugQueue = make(chan *deviceResponse, 1) - - return kk -} - -type logger interface { - Printf(string, ...interface{}) -} - -func (kk *Keepkey) log(str string, args ...interface{}) { - if kk.logger != nil { - kk.logger.Printf(str, args...) - } -} - -// Serial returns the serial id of the device -func (kk *Keepkey) Serial() string { - return kk.serial -} - -// Label returns the user set lable identifying the device -func (kk *Keepkey) Label() string { - return kk.label -} - -// SetLogger sets the logging device for this keepkey -func (kk *Keepkey) SetLogger(l logger) { - kk.logger = l -} - -// tuple of keepkey and optionally its debug/info interfaces +// tuple of HID/debug interfaces type hidInterfaces struct { - device, debug, info hid.DeviceInfo + device, debug hid.DeviceInfo } // HID INTERFACE DESCRIPTORS const ( HIDInterfaceStandard = "0" HIDInterfaceDebug = "1" - //HID_INFO = "2" ) // TransportType defines the interface to interact with the device @@ -116,7 +38,7 @@ const ( // discoverKeepkeys searches advertised hid interfaces for devices // that appear to be keepkeys -func discoverKeepkeys() map[string]*hidInterfaces { +func discoverHIDKeepkeys() map[string]*hidInterfaces { // Iterate over all connected keepkeys pairing each one with its // corresponding debug link if enabled @@ -155,37 +77,39 @@ func GetDevices() ([]*Keepkey, error) { // their enabled HID interfaces (primary/debug/info) // the provided config is applied to all found keepkeys func GetDevicesWithConfig(cfg *Config) ([]*Keepkey, error) { - //enumerateWebUSB() // Open HID connections to all devices found in the previous step - var deviceIFace, debugIFace, infoIFace hid.DeviceInfo + var deviceIFace, debugIFace hid.DeviceInfo devices := make([]*Keepkey, 0) webUSBDevices, err := enumerateWebUSB() if err != nil { - fmt.Println("Unable to connect to device of webusb, ", err) // TODO: Can't find good way to tell if device is webusb or hid because it is advertised on both? - //return nil, err + // TODO: Can't find good way to tell if device is webusb or hid because it is advertised on both? + // Swallow output if pid=0001 because this device is probably in HID->webUSB limbo and will + // soon be updated + if !strings.Contains(err.Error(), "pid=0001") { + fmt.Println("Unable to connect to device of webusb, ", err) + } } for _, dev := range webUSBDevices { kk := newKeepkeyFromConfig(cfg) - kk.device = dev.conn + kk.transport = dev if dev.debug != nil { - kk.debug = dev.debug - go listenForMessages(kk.debug, kk.debugQueue) + go listenForMessages(kk.transport.debug, kk.debugQueue) fmt.Println("DebugLink established over WebUSB") } devices = append(devices, kk) - go listenForMessages(kk.device, kk.deviceQueue) - kk.Initialize(kk.device) + go listenForMessages(kk.transport.conn, kk.deviceQueue) + kk.Initialize() } // HID TODO: move to seperate implementation file - for _, IFaces := range discoverKeepkeys() { + for _, IFaces := range discoverHIDKeepkeys() { kk := newKeepkeyFromConfig(cfg) + kk.transport = new(transport) deviceIFace = IFaces.device debugIFace = IFaces.debug - infoIFace = IFaces.info if deviceIFace.Path == "" { continue @@ -197,7 +121,7 @@ func GetDevicesWithConfig(cfg *Config) ([]*Keepkey, error) { fmt.Printf("Unable to connect to HID: %v dropping..., %s\n", deviceIFace, err) continue } - kk.device = device + kk.transport.conn = device go listenForMessages(device, kk.deviceQueue) // debug HID interface @@ -207,25 +131,13 @@ func GetDevicesWithConfig(cfg *Config) ([]*Keepkey, error) { fmt.Println("unable to initiate debug link, skipping...") continue } - fmt.Println("Debug link established") - kk.debug = debug + fmt.Println("Debug link established over HID") + kk.transport.debug = debug go listenForMessages(debug, kk.debugQueue) } - // info HID interface - if infoIFace.Path != "" { - info, err := infoIFace.Open() - if err != nil { - fmt.Println("unable to connect to Info HID interface, skipping...") - continue - } - fmt.Println("Connected to Info HID interface") - kk.infoOut = info - go listenForMessages(info, kk.infoQueue) - } - // Ping the device and ask for its features - features, err := kk.Initialize(device) + features, err := kk.Initialize() if err != nil { fmt.Println("Device failed to respond to initial request, dropping: ", err) continue @@ -278,16 +190,6 @@ func listenForMessages(in io.Reader, out chan *deviceResponse) { break } } - // TODO: refactor into handler funclist per hid interface - // TODO: gracefully terminate message listeners at program termination - if kind == uint16(kkProto.MessageType_MessageType_DebugLinkInfo) { - info := new(kkProto.DebugLinkInfo) - err := proto.Unmarshal(reply, info) - if err != nil { - fmt.Println("Unable to parse INFO message") - } - fmt.Println("INFO: ", info.GetMsg()) - } out <- &deviceResponse{reply, kind} } @@ -309,11 +211,11 @@ func pretty(m proto.Message) string { func (kk *Keepkey) keepkeyExchange(req proto.Message, results ...proto.Message) (int, error) { kk.log("Sending payload to device:\n%s:\n%s", kkProto.Name(kkProto.Type(req)), pretty(req)) - device := kk.device + device := kk.transport.conn debug := false // If debug is enabled send over the debug HID interface - if isDebugMessage(req) && kk.debug != nil { - device = kk.debug + if isDebugMessage(req) && kk.transport.debug != nil { + device = kk.transport.debug debug = true } @@ -379,7 +281,7 @@ func (kk *Keepkey) keepkeyExchange(req proto.Message, results ...proto.Message) // handle button requests and forward the results if kind == uint16(kkProto.MessageType_MessageType_ButtonRequest) { promptButton() - if kk.autoButton && kk.debug != nil { + if kk.autoButton && kk.transport.debug != nil { t := true fmt.Println("sending debug press") kk.keepkeyExchange(&kkProto.DebugLinkDecision{YesNo: &t}, &kkProto.Success{}) @@ -467,12 +369,12 @@ func isDebugMessage(req interface{}) bool { // Close closes the transport connection and unassoctiates that nterface // with the calling Keepkey func (kk *Keepkey) Close() { - if kk.device != nil { - kk.device.Close() - kk.device = nil + if kk.transport.conn != nil { + kk.transport.conn.Close() + kk.transport.conn = nil } - if kk.debug != nil { - kk.debug.Close() - kk.debug = nil + if kk.transport.debug != nil { + kk.transport.debug.Close() + kk.transport.debug = nil } } diff --git a/pkg/keepkey/webusb.go b/pkg/keepkey/webusb.go index 5a0e46e..3397d65 100644 --- a/pkg/keepkey/webusb.go +++ b/pkg/keepkey/webusb.go @@ -1,8 +1,6 @@ package keepkey import ( - "io" - "github.com/google/gousb" ) @@ -14,11 +12,6 @@ const ( debugEndpoint = 2 ) -type transport struct { - conn io.ReadWriteCloser // primary interface to device - debug io.ReadWriteCloser // debug link connection to device if enabled -} - // webUSBEndpoints holds handles to the input and output interfaces for a webUSB device // as well as the configuration for cleaup when done with the device type webUSBEndpoints struct {