Skip to content

Commit 4e57dab

Browse files
authored
Merge pull request #23 from chillyvee/cv_mymacsad_zondax
Remove suprious OSX USB Reads
2 parents 8b40b50 + 4f033d2 commit 4e57dab

File tree

4 files changed

+85
-15
lines changed

4 files changed

+85
-15
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
*.dylib
1717
*.dll
1818

19+
# Fortran module files - Why? For now, Allow go.mod
20+
#*.mod
21+
1922
# Fortran module files
2023
*.smod
2124

apduWrapper.go

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package ledger_go
1919
import (
2020
"encoding/binary"
2121
"fmt"
22+
2223
"github.com/pkg/errors"
2324
)
2425

@@ -42,7 +43,7 @@ func ErrorMessage(errorCode uint16) string {
4243
case 0x6985:
4344
return "[APDU_CODE_CONDITIONS_NOT_SATISFIED] Conditions of use not satisfied"
4445
case 0x6986:
45-
return "[APDU_CODE_COMMAND_NOT_ALLOWED] Command not allowed (no current EF)"
46+
return "[APDU_CODE_COMMAND_NOT_ALLOWED] Command not allowed / User Rejected (no current EF)"
4647
case 0x6A80:
4748
return "[APDU_CODE_BAD_KEY_HANDLE] The parameters in the data field are incorrect"
4849
case 0x6B00:
@@ -51,6 +52,8 @@ func ErrorMessage(errorCode uint16) string {
5152
return "[APDU_CODE_INS_NOT_SUPPORTED] Instruction code not supported or invalid"
5253
case 0x6E00:
5354
return "[APDU_CODE_CLA_NOT_SUPPORTED] CLA not supported"
55+
case 0x6E01:
56+
return "[APDU_CODE_APP_NOT_OPEN] Ledger Connected but Chain Specific App Not Open"
5457
case 0x6F00:
5558
return "APDU_CODE_UNKNOWN"
5659
case 0x6F01:
@@ -105,26 +108,35 @@ func SerializePacket(
105108
func DeserializePacket(
106109
channel uint16,
107110
buffer []byte,
108-
sequenceIdx uint16) (result []byte, totalResponseLength uint16, err error) {
111+
sequenceIdx uint16) (result []byte, totalResponseLength uint16, isSequenceZero bool, err error) {
112+
113+
isSequenceZero = false
109114

110115
if (sequenceIdx == 0 && len(buffer) < 7) || (sequenceIdx > 0 && len(buffer) < 5) {
111-
return nil, 0, errors.New("Cannot deserialize the packet. Header information is missing.")
116+
return nil, 0, isSequenceZero, errors.New("Cannot deserialize the packet. Header information is missing.")
112117
}
113118

114119
var headerOffset uint8
115120

116121
if codec.Uint16(buffer) != channel {
117-
return nil, 0, errors.New("Invalid channel")
122+
return nil, 0, isSequenceZero, errors.New(fmt.Sprintf("Invalid channel. Expected %d, Got: %d", channel, codec.Uint16(buffer)))
118123
}
119124
headerOffset += 2
120125

121126
if buffer[headerOffset] != 0x05 {
122-
return nil, 0, errors.New("Invalid tag")
127+
return nil, 0, isSequenceZero, errors.New(fmt.Sprintf("Invalid tag. Expected %d, Got: %d", 0x05, buffer[headerOffset]))
123128
}
124129
headerOffset++
125130

126-
if codec.Uint16(buffer[headerOffset:]) != sequenceIdx {
127-
return nil, 0, errors.New("Wrong sequenceIdx")
131+
foundSequenceIdx := codec.Uint16(buffer[headerOffset:])
132+
if foundSequenceIdx == 0 {
133+
isSequenceZero = true
134+
} else {
135+
isSequenceZero = false
136+
}
137+
138+
if foundSequenceIdx != sequenceIdx {
139+
return nil, 0, isSequenceZero, errors.New(fmt.Sprintf("Wrong sequenceIdx. Expected %d, Got: %d", sequenceIdx, foundSequenceIdx))
128140
}
129141
headerOffset += 2
130142

@@ -136,7 +148,7 @@ func DeserializePacket(
136148
result = make([]byte, len(buffer)-int(headerOffset))
137149
copy(result, buffer[headerOffset:])
138150

139-
return result, totalResponseLength, nil
151+
return result, totalResponseLength, isSequenceZero, nil
140152
}
141153

142154
// WrapCommandAPDU turns the command into a sequence of 64 byte packets
@@ -170,15 +182,32 @@ func UnwrapResponseAPDU(channel uint16, pipe <-chan []byte, packetSize int) ([]b
170182
var totalSize uint16
171183
var done = false
172184

185+
// return values from DeserializePacket
186+
var result []byte
187+
var responseSize uint16
188+
var err error
189+
190+
foundZeroSequence := false
191+
isSequenceZero := false
192+
173193
for !done {
174194
// Read next packet from the channel
175195
buffer := <-pipe
176196

177-
result, responseSize, err := DeserializePacket(channel, buffer, sequenceIdx)
197+
result, responseSize, isSequenceZero, err = DeserializePacket(channel, buffer, sequenceIdx) // this may fail if the wrong sequence arrives (espeically if left over all 0000 was in the buffer from the last tx)
178198
if err != nil {
179199
return nil, err
180200
}
181-
if sequenceIdx == 0 {
201+
202+
// Recover from a known error condition:
203+
// * Discard messages left over from previous exchange until isSequenceZero == true
204+
if foundZeroSequence == false && isSequenceZero == false {
205+
continue
206+
}
207+
foundZeroSequence = true
208+
209+
// Initialize totalSize (previously we did this if sequenceIdx == 0, but sometimes Nano X can provide the first sequenceIdx == 0 packet with all zeros, then a useful packet with sequenceIdx == 1
210+
if totalSize == 0 {
182211
totalSize = responseSize
183212
}
184213

apduWrapper_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,12 @@ func Test_DeserializePacket_FirstPacket(t *testing.T) {
211211
var firstPacketHeaderSize = 7
212212
packet, _, _ := SerializePacket(0x0101, sampleCommand, packetSize, 0)
213213

214-
output, totalSize, err := DeserializePacket(0x0101, packet, 0)
214+
output, totalSize, isSequenceZero, err := DeserializePacket(0x0101, packet, 0)
215215

216216
assert.Nil(t, err, "Simple deserialize should not have errors")
217217
assert.Equal(t, len(sampleCommand), int(totalSize), "TotalSize is incorrect")
218218
assert.Equal(t, packetSize-firstPacketHeaderSize, len(output), "Size of the deserialized packet is wrong")
219+
assert.Equal(t, true, isSequenceZero, "Test Case Should Find Sequence == 0")
219220
assert.True(t, bytes.Compare(output[:len(sampleCommand)], sampleCommand) == 0, "Deserialized message does not match the original")
220221
}
221222

@@ -226,11 +227,12 @@ func Test_DeserializePacket_SecondMessage(t *testing.T) {
226227
var firstPacketHeaderSize = 5 // second packet does not have responseLength (uint16) in the header
227228
packet, _, _ := SerializePacket(0x0101, sampleCommand, packetSize, 1)
228229

229-
output, totalSize, err := DeserializePacket(0x0101, packet, 1)
230+
output, totalSize, isSequenceZero, err := DeserializePacket(0x0101, packet, 1)
230231

231232
assert.Nil(t, err, "Simple deserialize should not have errors")
232233
assert.Equal(t, 0, int(totalSize), "TotalSize should not be returned from deserialization of non-first packet")
233234
assert.Equal(t, packetSize-firstPacketHeaderSize, len(output), "Size of the deserialized packet is wrong")
235+
assert.Equal(t, false, isSequenceZero, "Test Case Should Find Sequence == 1")
234236
assert.True(t, bytes.Equal(output[:len(sampleCommand)], sampleCommand), "Deserialized message does not match the original")
235237
}
236238

ledger_hid.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"errors"
2424
"fmt"
2525
"sync"
26+
"time"
2627

2728
"github.com/zondax/hid"
2829
)
@@ -59,7 +60,7 @@ func (admin *LedgerAdminHID) ListDevices() ([]string, error) {
5960
devices := hid.Enumerate(0, 0)
6061

6162
if len(devices) == 0 {
62-
fmt.Printf("No devices")
63+
fmt.Printf("No devices. Ledger LOCKED OR Other Program/Web Browser may have control of device.")
6364
}
6465

6566
for _, d := range devices {
@@ -130,7 +131,7 @@ func (admin *LedgerAdminHID) Connect(requiredIndex int) (LedgerDevice, error) {
130131
}
131132
}
132133

133-
return nil, fmt.Errorf("LedgerHID device (idx %d) not found", requiredIndex)
134+
return nil, fmt.Errorf("LedgerHID device (idx %d) not found. Ledger LOCKED OR Other Program/Web Browser may have control of device.", requiredIndex)
134135
}
135136

136137
func (ledger *LedgerDeviceHID) write(buffer []byte) (int, error) {
@@ -165,17 +166,52 @@ func (ledger *LedgerDeviceHID) readThread() {
165166
buffer := make([]byte, PacketSize)
166167
readBytes, err := ledger.device.Read(buffer)
167168

169+
// Check for HID Read Error (May occur even during normal runtime)
168170
if err != nil {
169-
return
171+
continue
172+
}
173+
174+
// Discard all zero packets from Ledger Nano X on macOS
175+
allZeros := true
176+
for i := 0; i < len(buffer); i++ {
177+
if buffer[i] != 0 {
178+
allZeros = false
179+
break
180+
}
170181
}
182+
183+
// Discard all zero packet
184+
if allZeros {
185+
// HID Returned Empty Packet - Retry Read
186+
continue
187+
}
188+
171189
select {
172190
case ledger.readChannel <- buffer[:readBytes]:
191+
// Send data to UnwrapResponseAPDU
173192
default:
193+
// Possible source of bugs
194+
// Drop a buffer if ledger.readChannel is busy
195+
}
196+
}
197+
}
198+
199+
func (ledger *LedgerDeviceHID) drainRead() {
200+
// Allow time for late packet arrivals (When main program doesn't read enough packets)
201+
<-time.After(50 * time.Millisecond)
202+
for {
203+
select {
204+
case <-ledger.readChannel:
205+
default:
206+
return
174207
}
175208
}
176209
}
177210

178211
func (ledger *LedgerDeviceHID) Exchange(command []byte) ([]byte, error) {
212+
// Purge messages that arrived after previous exchange completed
213+
ledger.drainRead()
214+
179215
if len(command) < 5 {
180216
return nil, fmt.Errorf("APDU commands should not be smaller than 5")
181217
}

0 commit comments

Comments
 (0)