Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing connection issues in OSX #1

Merged
merged 6 commits into from
Jan 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[[constraint]]
name = "github.com/ZondaX/hid-go"
version = "v0.4.0"
name = "github.com/karalabe/hid"
branch = "master"

[[constraint]]
name = "github.com/pkg/errors"
Expand All @@ -13,3 +13,6 @@
[prune]
go-tests = true
unused-packages = true
[[prune.project]]
name = "github.com/karalabe/hid"
unused-packages = false
20 changes: 9 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# ledger-go

[![CircleCI](https://circleci.com/gh/ZondaX/ledger-goclient.svg?style=svg)](https://circleci.com/gh/ZondaX/ledger-goclient)
[![Build status](https://ci.appveyor.com/api/projects/status/aqv23r898rqegxqv/branch/master?svg=true)](https://ci.appveyor.com/project/zondax/ledger-goclient/branch/master)
[![Build Status](https://travis-ci.org/ZondaX/ledger-goclient.svg?branch=master)](https://travis-ci.org/ZondaX/ledger-goclient)
[![CircleCI](https://circleci.com/gh/ZondaX/ledger-go.svg?style=svg)](https://circleci.com/gh/ZondaX/ledger-go)
[![Build status](https://ci.appveyor.com/api/projects/status/m4wn7kuuuu98b3uh/branch/master?svg=true)](https://ci.appveyor.com/project/zondax/ledger-go/branch/master)
[![Build Status](https://travis-ci.org/ZondaX/ledger-goclient.svg?branch=master)](https://travis-ci.org/ZondaX/ledger-go)

This project is work in progress. Some aspects are subject to change.
This project provides a library to connect to ledger devices.

It handles USB (HID) communication and APDU encapsulation.

Linux, OSX and Windows are supported.

# Get source
Apart from cloning, be sure you install dep dependency management tool
Expand All @@ -18,11 +22,5 @@ dep ensure

# Building
```
go build ledger.go
go build
```

# Running
./ledger

Make sure that the app is launched in the Ledger before starting this command and Ledger is connected to the USB port.
This command line tool will try to send a simple json transaction and will return a signature when user agrees to sign.
43 changes: 20 additions & 23 deletions apduWrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
package ledger_go

import (
"github.com/pkg/errors"
"encoding/binary"
"github.com/pkg/errors"
)

var codec = binary.BigEndian
Expand All @@ -27,8 +27,7 @@ func SerializePacket(
channel uint16,
command []byte,
packetSize int,
sequenceIdx uint16,
ble bool) (result []byte, offset int, err error) {
sequenceIdx uint16) (result []byte, offset int, err error) {

if packetSize < 3 {
return nil, 0, errors.New("Packet size must be at least 3")
Expand All @@ -40,10 +39,8 @@ func SerializePacket(
var buffer = result

// Insert channel (2 bytes)
if !ble {
codec.PutUint16(buffer, channel)
headerOffset += 2
}
codec.PutUint16(buffer, channel)
headerOffset += 2

// Insert tag (1 byte)
buffer[headerOffset] = 0x05
Expand Down Expand Up @@ -71,21 +68,19 @@ func SerializePacket(
func DeserializePacket(
channel uint16,
buffer []byte,
sequenceIdx uint16,
ble bool) (result []byte, totalResponseLength uint16, err error) {
sequenceIdx uint16) (result []byte, totalResponseLength uint16, err error) {

if (sequenceIdx == 0 && len(buffer) < 7) || (sequenceIdx > 0 && len(buffer) < 5) {
return nil, 0, errors.New("Cannot deserialize the packet. Header information is missing.")
}

var headerOffset uint8

if !ble {
if codec.Uint16(buffer) != channel {
return nil, 0, errors.New("Invalid channel")
}
headerOffset += 2
if codec.Uint16(buffer) != channel {
return nil, 0, errors.New("Invalid channel")
}
headerOffset += 2

if buffer[headerOffset] != 0x05 {
return nil, 0, errors.New("Invalid tag")
}
Expand All @@ -101,7 +96,7 @@ func DeserializePacket(
headerOffset += 2
}

result = make([]byte, len(buffer) - int(headerOffset))
result = make([]byte, len(buffer)-int(headerOffset))
copy(result, buffer[headerOffset:])

return result, totalResponseLength, nil
Expand All @@ -111,36 +106,38 @@ func DeserializePacket(
func WrapCommandAPDU(
channel uint16,
command []byte,
packetSize int,
ble bool) (result []byte, err error) {
packetSize int) (result []byte, err error) {

var offset int
var totalResult []byte
var sequenceIdx uint16

for len(command) > 0 {
result, offset, err = SerializePacket(channel, command, packetSize, sequenceIdx, ble)
result, offset, err = SerializePacket(channel, command, packetSize, sequenceIdx)
if err != nil {
return nil, err
}
command = command[offset:]
totalResult = append(totalResult, result...)
sequenceIdx++
}

return totalResult, nil
}

// UnwrapResponseAPDU parses a response of 64 byte packets into the real data
func UnwrapResponseAPDU(channel uint16, pipe <- chan []byte, packetSize int, ble bool) ([]byte, error) {
func UnwrapResponseAPDU(channel uint16, pipe <- chan []byte, packetSize int) ([]byte, error) {
var sequenceIdx uint16

var totalResult []byte
var totalSize uint16
var finished = false
for !finished {
var done = false

for !done {
// Read next packet from the channel
buffer := <- pipe
result, responseSize, err := DeserializePacket(channel, buffer, sequenceIdx, ble)

result, responseSize, err := DeserializePacket(channel, buffer, sequenceIdx)
if err != nil {
return nil, err
}
Expand All @@ -153,7 +150,7 @@ func UnwrapResponseAPDU(channel uint16, pipe <- chan []byte, packetSize int, ble
sequenceIdx++

if len(totalResult) >= int(totalSize) {
finished = true
done = true
}
}

Expand Down
66 changes: 29 additions & 37 deletions apduWrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@
package ledger_go

import (
"testing"
"github.com/stretchr/testify/assert"
"unsafe"
"bytes"
"time"
"fmt"
"github.com/stretchr/testify/assert"
"math"
"testing"
"unsafe"
)

func Test_SerializePacket_EmptyCommand(t *testing.T) {
var command= make([]byte, 1)

_, _, err := SerializePacket(0x0101, command, 64, 0, false)
_, _, err := SerializePacket(0x0101, command, 64, 0)
assert.Nil(t, err, "Commands smaller than 3 bytes should return error")
}

Expand All @@ -50,8 +50,7 @@ func Test_SerializePacket_PacketSize(t *testing.T) {
h.channel,
command,
packetSize,
h.sequenceIdx,
false)
h.sequenceIdx)

assert.Equal(t, len(result), packetSize, "Packet size is wrong")
}
Expand All @@ -74,8 +73,7 @@ func Test_SerializePacket_Header(t *testing.T) {
h.channel,
command,
packetSize,
h.sequenceIdx,
false)
h.sequenceIdx)

assert.Equal(t, codec.Uint16(result), h.channel, "Channel not properly serialized")
assert.Equal(t, result[2], h.tag, "Tag not properly serialized")
Expand All @@ -101,8 +99,7 @@ func Test_SerializePacket_Offset(t *testing.T) {
h.channel,
command,
packetSize,
h.sequenceIdx,
false)
h.sequenceIdx)

assert.Equal(t, packetSize - int(unsafe.Sizeof(h))+1, offset, "Wrong offset returned. Offset must point to the next comamnd byte that needs to be packet-ized.")
}
Expand All @@ -129,8 +126,7 @@ func Test_WrapCommandAPDU_NumberOfPackets(t *testing.T) {
result, _ := WrapCommandAPDU(
h1.channel,
command,
packetSize,
false)
packetSize)

assert.Equal(t, packetSize*2, len(result), "Result buffer size is not correct")
}
Expand All @@ -157,8 +153,7 @@ func Test_WrapCommandAPDU_CheckHeaders(t *testing.T) {
result, _ := WrapCommandAPDU(
h1.channel,
command,
packetSize,
false)
packetSize)

assert.Equal(t, h1.channel, codec.Uint16(result), "Channel not properly serialized")
assert.Equal(t, h1.tag, result[2], "Tag not properly serialized")
Expand Down Expand Up @@ -197,8 +192,7 @@ func Test_WrapCommandAPDU_CheckData(t *testing.T) {
result, _ := WrapCommandAPDU(
h1.channel,
command,
packetSize,
false)
packetSize)

// Check data in the first packet
assert.True(t, bytes.Compare(command[0:64-7], result[7:64]) == 0)
Expand Down Expand Up @@ -230,9 +224,9 @@ func Test_DeserializePacket_FirstPacket(t *testing.T) {

var packetSize int = 64
var firstPacketHeaderSize int = 7
packet, _, _ := SerializePacket(0x0101, sampleCommand, packetSize, 0, false)
packet, _, _ := SerializePacket(0x0101, sampleCommand, packetSize, 0)

output, totalSize, err := DeserializePacket(0x0101, packet, 0, false)
output, totalSize, err := DeserializePacket(0x0101, packet, 0)

assert.Nil(t,err, "Simple deserialize should not have errors")
assert.Equal(t, len(sampleCommand), int(totalSize), "TotalSize is incorrect")
Expand All @@ -241,52 +235,50 @@ func Test_DeserializePacket_FirstPacket(t *testing.T) {
}

func Test_DeserializePacket_SecondMessage(t *testing.T) {

var sampleCommand = []byte{'H', 'e', 'l', 'l', 'o', 0}

var packetSize int = 64
var firstPacketHeaderSize int = 5 // second packet does not have responseLegth (uint16) in the header
packet, _, _ := SerializePacket(0x0101, sampleCommand, packetSize, 1, false)
packet, _, _ := SerializePacket(0x0101, sampleCommand, packetSize, 1)

output, totalSize, err := DeserializePacket(0x0101, packet, 1, false)
output, totalSize, err := DeserializePacket(0x0101, packet, 1)

assert.Nil(t,err, "Simple deserialize should not have errors")
assert.Equal(t, 0, int(totalSize), "TotalSize should not be returned from deserialization of non-first packet")
assert.Equal(t, packetSize - firstPacketHeaderSize, len(output), "Size of the deserialized packet is wrong")
assert.True(t, bytes.Compare(output[:len(sampleCommand)], sampleCommand) == 0, "Deserialized message does not match the original")
}

func WriteBuffer(pipe chan<- []byte, buffer []byte) {
time.Sleep(1 * time.Second)
pipe <- buffer
}

func Test_UnwrapApdu_SmokeTest(t *testing.T) {
const channel uint16 = 0x8002

inputSize := 200
var packetSize int = 64
var input= make([]byte, inputSize)
var channel uint16 = 0x8002

// Initialize some dummy input
var input= make([]byte, inputSize)
for i := range input {
input[i] = byte(i % 256)
}
serialized, _ := WrapCommandAPDU(
channel,
input,
packetSize,
false)

serialized, _ := WrapCommandAPDU(channel, input, packetSize)

// Allocate enough buffers to keep all the packets
pipe := make(chan []byte, int(math.Ceil(float64(inputSize) / float64(packetSize))))

// Send all the packets to the pipe
for len(serialized) > 0 {
pipe <- serialized[:packetSize]
serialized = serialized[packetSize:]
}

output, _ := UnwrapResponseAPDU(channel, pipe, packetSize, false)
output, _ := UnwrapResponseAPDU(channel, pipe, packetSize)

fmt.Printf("INPUT : %x\n", input)
fmt.Printf("SERIALIZED: %x\n", serialized)
fmt.Printf("OUTPUT : %x\n", output)

assert.Equal(t, len(input), len(output), "Input and output messages have different size")
assert.True(t, bytes.Compare(input, output) == 0, "Input message does not match message which was serialized and then deserialized")
assert.True(t,
bytes.Compare(input, output) == 0,
"Input message does not match message which was serialized and then deserialized")
}
Loading