diff --git a/vendor/github.com/trezor/trezord-go/memorywriter/memorywriter.go b/vendor/github.com/trezor/trezord-go/memorywriter/memorywriter.go new file mode 100644 index 0000000000..adf8f5b505 --- /dev/null +++ b/vendor/github.com/trezor/trezord-go/memorywriter/memorywriter.go @@ -0,0 +1,194 @@ +package memorywriter + +import ( + "bytes" + "compress/gzip" + "errors" + "fmt" + "io" + "runtime" + "strings" + "sync" + "time" +) + +// This is a helper package that writes logs to memory, +// rotates the lines, but remembers some lines on the start +// It is useful for detailed logging, that would take too much memory + +// to prevent possible memory issues, hardcode max line length +const maxLineLength = 500 + +type MemoryWriter struct { + maxLineCount int + lines [][]byte // lines include newlines + startCount int + startLines [][]byte + startTime time.Time + printTime bool + mutex sync.Mutex + + outWriter io.Writer +} + +func findInternalPrefix() string { + pc := make([]uintptr, 15) + n := runtime.Callers(1, pc) + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + file := frame.File + return strings.TrimSuffix(file, "memorywriter/memorywriter.go") +} + +var internalPrefix = findInternalPrefix() + +func (m *MemoryWriter) Log(s string) { + pc := make([]uintptr, 15) + n := runtime.Callers(2, pc) + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + file := frame.File + file = strings.TrimPrefix(file, internalPrefix) + function := frame.Function + function = strings.TrimPrefix(function, "github.com/trezor/trezord-go/") + r := fmt.Sprintf("[%s %d %s]", file, frame.Line, function) + m.println(r + " " + s) + +} + +func (m *MemoryWriter) println(s string) { + long := []byte(s + "\n") + _, err := m.Write(long) + if err != nil { + // give up, just print on stdout + fmt.Println(err) + } +} + +// Writer remembers lines in memory +func (m *MemoryWriter) Write(p []byte) (int, error) { + m.mutex.Lock() + defer func() { + m.mutex.Unlock() + }() + if len(p) > maxLineLength { + return 0, errors.New("input too long") + } + + var newline []byte + if !m.printTime { + newline = make([]byte, len(p)) + copy(newline, p) + } else { + now := time.Now() + elapsed := now.Sub(m.startTime) + + elapsedS := fmt.Sprintf("%.6f", elapsed.Seconds()) + nowS := now.Format("15:04:05") + + newline = []byte(fmt.Sprintf("[%s : %s] %s", elapsedS, nowS, string(p))) + } + + if len(m.startLines) < m.startCount { + // do not rotate + m.startLines = append(m.startLines, newline) + } else { + // rotate + for len(m.lines) >= m.maxLineCount { + m.lines = m.lines[1:] + } + + m.lines = append(m.lines, newline) + } + if m.outWriter != nil { + _, wrErr := m.outWriter.Write(newline) + if wrErr != nil { + // give up, just print on stdout + fmt.Println(wrErr) + } + } + return len(p), nil +} + +// Exports lines to a writer, plus adds additional text on top +// In our case, additional text is devcon exports and trezord version +func (m *MemoryWriter) writeTo(start string, w io.Writer) error { + m.mutex.Lock() + defer func() { + m.mutex.Unlock() + }() + _, err := w.Write([]byte(start)) + if err != nil { + return err + } + + // Write end lines (latest on up) + for i := len(m.lines) - 1; i >= 0; i-- { + line := m.lines[i] + _, err = w.Write(line) + if err != nil { + return err + } + } + + // ... to make space between start and end + _, err = w.Write([]byte("...\n")) + if err != nil { + return err + } + + // Write start lines + for i := len(m.startLines) - 1; i >= 0; i-- { + line := m.startLines[i] + _, err = w.Write(line) + if err != nil { + return err + } + } + + return nil +} + +// String exports as string +func (m *MemoryWriter) String(start string) (string, error) { + var b bytes.Buffer + err := m.writeTo(start, &b) + if err != nil { + return "", err + } + return b.String(), nil +} + +// Gzip exports as GZip bytes +func (m *MemoryWriter) Gzip(start string) ([]byte, error) { + var buf bytes.Buffer + gw, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) + if err != nil { + return nil, err + } + + gw.Name = "log.txt" + err = m.writeTo(start, gw) + if err != nil { + return nil, err + } + + err = gw.Close() + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +func New(size int, startSize int, printTime bool, out io.Writer) *MemoryWriter { + return &MemoryWriter{ + maxLineCount: size, + lines: make([][]byte, 0, size), + startCount: startSize, + startLines: make([][]byte, 0, startSize), + startTime: time.Now(), + printTime: printTime, + outWriter: out, + } +} diff --git a/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/README.md b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/README.md new file mode 100644 index 0000000000..1990d8e924 --- /dev/null +++ b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/README.md @@ -0,0 +1,14 @@ +# hidapi go wrapper + +This is a go wrapper around hidapi. + +The code is mostly copied from https://github.com/karalabe/hid + +ALSO NOTE - there is a hardcoded device filter because of random windows errors. + +## License + +Code is under GNU LGPL 2.1. + +* (C) Karel Bilek 2017 +* (C) 2017 Péter Szilágyi (also see https://github.com/karalabe/hid for comprehensive list) diff --git a/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/hid.go b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/hid.go new file mode 100644 index 0000000000..3529dd7a0d --- /dev/null +++ b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/hid.go @@ -0,0 +1,238 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. 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 hid provides an interface for USB HID devices. + +// +build darwin,!ios,cgo windows,cgo + +package hidapi + +/* +extern void goHidLog(const char *s); + +#cgo CFLAGS: -I./c + +#cgo darwin CFLAGS: -DOS_DARWIN +#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit +#cgo windows CFLAGS: -DOS_WINDOWS +#cgo windows LDFLAGS: -lsetupapi + +#ifdef OS_DARWIN + #include "mac/hid.c" +#elif OS_WINDOWS + #define HARDCODED_HIDAPI_DEVICE_FILTER "vid_534c" + #include "windows/hid.c" +#endif + +*/ +import "C" + +import ( + "errors" + "runtime" + "sync" + "unsafe" +) + +// ErrDeviceClosed is returned for operations where the device closed before or +// during the execution. +var ErrDeviceClosed = errors.New("hid: device closed") + +// ErrUnsupportedPlatform is returned for all operations where the underlying +// operating system is not supported by the library. +var ErrUnsupportedPlatform = errors.New("hid: unsupported platform") + +// 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 + Release uint16 // Device Release Number in binary-coded decimal, also known as Device Version Number + Serial string // Serial Number + Manufacturer string // Manufacturer String + Product string // Product string + UsagePage uint16 // Usage Page for this Device/Interface (Windows/Mac only) + Usage uint16 // Usage for this Device/Interface (Windows/Mac only) + + // The USB interface which this logical device + // represents. Valid on both Linux implementations + // in all cases, and valid on the Windows implementation + // only if the device contains more than one interface. + Interface int +} + +// enumerateLock is a mutex serializing access to USB device enumeration needed +// by the macOS USB HID system calls, which require 2 consecutive method calls +// for enumeration, causing crashes if called concurrently. +// +// For more details, see: +// https://developer.apple.com/documentation/iokit/1438371-iohidmanagersetdevicematching +// > "subsequent calls will cause the hid manager to release previously enumerated devices" +var enumerateLock sync.Mutex + +func init() { + // Initialize the HIDAPI library + C.hid_init() +} + +// 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 HidEnumerate(vendorID uint16, productID uint16) []HidDeviceInfo { + enumerateLock.Lock() + defer enumerateLock.Unlock() + + // Gather all device infos and ensure they are freed before returning + head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID)) + if head == nil { + return nil + } + defer C.hid_free_enumeration(head) + + // Iterate the list and retrieve the device details + var infos []HidDeviceInfo + for ; head != nil; head = head.next { + info := HidDeviceInfo{ + Path: C.GoString(head.path), + VendorID: uint16(head.vendor_id), + ProductID: uint16(head.product_id), + Release: uint16(head.release_number), + UsagePage: uint16(head.usage_page), + Usage: uint16(head.usage), + Interface: int(head.interface_number), + } + if head.serial_number != nil { + info.Serial, _ = wcharTToString(head.serial_number) + } + if head.product_string != nil { + info.Product, _ = wcharTToString(head.product_string) + } + if head.manufacturer_string != nil { + info.Manufacturer, _ = wcharTToString(head.manufacturer_string) + } + infos = append(infos, info) + } + return infos +} + +// Open connects to an HID device by its path name. +func (info HidDeviceInfo) Open() (*HidDevice, error) { + path := C.CString(info.Path) + defer C.free(unsafe.Pointer(path)) + + device := C.hid_open_path(path) + if device == nil { + return nil, errors.New("hidapi: failed to open device") + } + return &HidDevice{ + HidDeviceInfo: info, + device: device, + }, nil +} + +// Device is a live HID USB connected device handle. +type HidDevice struct { + HidDeviceInfo // Embed the infos for easier access + + device *C.hid_device // Low level HID device to communicate through + lock sync.Mutex +} + +// Close releases the HID USB device handle. +func (dev *HidDevice) Close() error { + dev.lock.Lock() + defer dev.lock.Unlock() + + if dev.device != nil { + C.hid_close(dev.device) + dev.device = nil + } + return nil +} + +// Write sends an output report to a HID device. +// +// Write will send the data on the first OUT endpoint, if one exists. If it does +// not, it will send the data through the Control Endpoint (Endpoint 0). +func (dev *HidDevice) Write(b []byte, prepend bool) (int, error) { + // Abort if nothing to write + if len(b) == 0 { + return 0, nil + } + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Prepend a HID report ID on Windows, other OSes don't need it + var report []byte + if prepend && runtime.GOOS == "windows" { + report = append([]byte{0x00}, b...) + } else { + report = b + } + // Execute the write operation + written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report)))) + if written == -1 { + // If the write failed, verify if closed or other error + dev.lock.Lock() + device = dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Device not closed, some other error occurred + message := C.hid_error(device) + if message == nil { + return 0, errors.New("hidapi: unknown failure") + } + failure, _ := wcharTToString(message) + return 0, errors.New("hidapi: " + failure) + } + return written, nil +} + +// Read retrieves an input report from a HID device. +func (dev *HidDevice) Read(b []byte, milliseconds int) (int, error) { + // Aborth if nothing to read + if len(b) == 0 { + return 0, nil + } + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Execute the read operation + read := int(C.hid_read_timeout(device, (*C.uchar)(&b[0]), C.size_t(len(b)), C.int(milliseconds))) + if read == -1 { + // If the read failed, verify if closed or other error + dev.lock.Lock() + device = dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Device not closed, some other error occurred + message := C.hid_error(device) + if message == nil { + return 0, errors.New("hidapi: unknown failure") + } + failure, _ := wcharTToString(message) + return 0, errors.New("hidapi: " + failure) + } + return read, nil +} diff --git a/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/log.go b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/log.go new file mode 100644 index 0000000000..c6c8b55f54 --- /dev/null +++ b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/log.go @@ -0,0 +1,20 @@ +package hidapi + +import ( + "io" +) + +import "C" + +var writer io.Writer + +func SetLogWriter(l io.Writer) { + writer = l +} + +//export goHidLog +func goHidLog(s *C.char) { + if writer != nil { + writer.Write([]byte(C.GoString(s))) + } +} diff --git a/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/wchar.go b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/wchar.go new file mode 100644 index 0000000000..2278c6b9e1 --- /dev/null +++ b/vendor/github.com/trezor/trezord-go/usb/lowlevel/hidapi/wchar.go @@ -0,0 +1,227 @@ +// This file is https://github.com/orofarne/gowchar/blob/master/gowchar.go +// +// It was vendored inline to work around CGO limitations that don't allow C types +// to directly cross package API boundaries. +// +// The vendored file is licensed under the 3-clause BSD license, according to: +// https://github.com/orofarne/gowchar/blob/master/LICENSE + +// +build !ios +// +build linux freebsd darwin windows + +package hidapi + +/* +#include + +const size_t SIZEOF_WCHAR_T = sizeof(wchar_t); + +void gowchar_set (wchar_t *arr, int pos, wchar_t val) +{ + arr[pos] = val; +} + +wchar_t gowchar_get (wchar_t *arr, int pos) +{ + return arr[pos]; +} +*/ +import "C" + +import ( + "fmt" + "unicode/utf16" + "unicode/utf8" +) + +var sizeofWcharT C.size_t = C.size_t(C.SIZEOF_WCHAR_T) + +func stringToWcharT(s string) (*C.wchar_t, C.size_t) { + switch sizeofWcharT { + case 2: + return stringToWchar2(s) // Windows + case 4: + return stringToWchar4(s) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +func wcharTToString(s *C.wchar_t) (string, error) { + switch sizeofWcharT { + case 2: + return wchar2ToString(s) // Windows + case 4: + return wchar4ToString(s) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +func wcharTNToString(s *C.wchar_t, size C.size_t) (string, error) { + switch sizeofWcharT { + case 2: + return wchar2NToString(s, size) // Windows + case 4: + return wchar4NToString(s, size) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +// Windows +func stringToWchar2(s string) (*C.wchar_t, C.size_t) { + var slen int + s1 := s + for len(s1) > 0 { + r, size := utf8.DecodeRuneInString(s1) + if er, _ := utf16.EncodeRune(r); er == '\uFFFD' { + slen += 1 + } else { + slen += 2 + } + s1 = s1[size:] + } + slen++ // \0 + res := C.malloc(C.size_t(slen) * sizeofWcharT) + var i int + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + if r1, r2 := utf16.EncodeRune(r); r1 != '\uFFFD' { + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r1)) + i++ + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r2)) + i++ + } else { + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r)) + i++ + } + s = s[size:] + } + C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0 + return (*C.wchar_t)(res), C.size_t(slen) +} + +// Unix +func stringToWchar4(s string) (*C.wchar_t, C.size_t) { + slen := utf8.RuneCountInString(s) + slen++ // \0 + res := C.malloc(C.size_t(slen) * sizeofWcharT) + var i int + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r)) + s = s[size:] + i++ + } + C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0 + return (*C.wchar_t)(res), C.size_t(slen) +} + +// Windows +func wchar2ToString(s *C.wchar_t) (string, error) { + var i int + var res string + for { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + i++ + if !utf16.IsSurrogate(r) { + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + } else { + ch2 := C.gowchar_get(s, C.int(i)) + r2 := rune(ch2) + r12 := utf16.DecodeRune(r, r2) + if r12 == '\uFFFD' { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + res += string(r12) + i++ + } + } + return res, nil +} + +// Unix +func wchar4ToString(s *C.wchar_t) (string, error) { + var i int + var res string + for { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + i++ + } + return res, nil +} + +// Windows +func wchar2NToString(s *C.wchar_t, size C.size_t) (string, error) { + var i int + var res string + N := int(size) + for i < N { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + i++ + if !utf16.IsSurrogate(r) { + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + + res += string(r) + } else { + if i >= N { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + ch2 := C.gowchar_get(s, C.int(i)) + r2 := rune(ch2) + r12 := utf16.DecodeRune(r, r2) + if r12 == '\uFFFD' { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + res += string(r12) + i++ + } + } + return res, nil +} + +// Unix +func wchar4NToString(s *C.wchar_t, size C.size_t) (string, error) { + var i int + var res string + N := int(size) + for i < N { + ch := C.gowchar_get(s, C.int(i)) + r := rune(ch) + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + i++ + } + return res, nil +}