accounts/usbwallet: add missing components in vendor

This commit is contained in:
Guillaume Ballet 2019-05-08 09:31:11 +02:00
parent 6edbfc23fd
commit 9133bc851c
5 changed files with 693 additions and 0 deletions

View file

@ -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,
}
}

View file

@ -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)

View file

@ -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
}

View file

@ -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)))
}
}

View file

@ -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 <wchar.h>
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
}