From cda0ae8b47120acb99aeee92028665dc36e2ef0d Mon Sep 17 00:00:00 2001 From: highlander Date: Sun, 7 Jun 2026 16:11:06 -0500 Subject: [PATCH] fix(keepkey-webusb): clearHalt the IN pipe on a stalled read, not OUT readChunk() reads the IN endpoint (transferIn) but on a "stall" it cleared clearHalt("out", ...) -- the wrong endpoint and direction. A halted IN pipe is only resumed by resetting that same pipe, so it stayed halted. The browser transport also fell through and returned the stalled (empty/short) buffer, which the framing parser then rejected as "message not valid". Reset the IN pipe and throw a retryable "bad read" instead of surfacing a non-packet. Applied to both hdwallet-keepkey-nodewebusb (Node/libusb) and hdwallet-keepkey-webusb (browser). Split out from #44 (which also added transfer timeouts); this is the isolated, low-risk correctness fix. --- packages/hdwallet-keepkey-nodewebusb/src/transport.ts | 7 +++++-- packages/hdwallet-keepkey-webusb/src/transport.ts | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/hdwallet-keepkey-nodewebusb/src/transport.ts b/packages/hdwallet-keepkey-nodewebusb/src/transport.ts index 2a6e8954..0c2fca01 100644 --- a/packages/hdwallet-keepkey-nodewebusb/src/transport.ts +++ b/packages/hdwallet-keepkey-nodewebusb/src/transport.ts @@ -79,8 +79,11 @@ export class TransportDelegate implements keepkey.TransportDelegate { async readChunk(debugLink?: boolean): Promise { const result = await this.usbDevice.transferIn(debugLink ? 2 : 1, keepkey.SEGMENT_SIZE + 1); - if (result.status === "stall" && result.data !== undefined) { - await this.usbDevice.clearHalt("out", debugLink ? 2 : 1); + if (result.status === "stall") { + // Reset the halt on the IN pipe we just read (not OUT), then surface a + // retryable error -- a stalled transfer's buffer is not a valid packet. + await this.usbDevice.clearHalt("in", debugLink ? 2 : 1); + throw new Error("bad read"); } else if (result.status !== "ok" || result.data === undefined) { throw new Error("bad read"); } diff --git a/packages/hdwallet-keepkey-webusb/src/transport.ts b/packages/hdwallet-keepkey-webusb/src/transport.ts index 6b3a6b87..122e1749 100644 --- a/packages/hdwallet-keepkey-webusb/src/transport.ts +++ b/packages/hdwallet-keepkey-webusb/src/transport.ts @@ -76,7 +76,10 @@ export class TransportDelegate implements keepkey.TransportDelegate { const { status, data } = await this.usbDevice.transferIn(debugLink ? 2 : 1, keepkey.SEGMENT_SIZE + 1); if (status === "stall") { - await this.usbDevice.clearHalt("out", debugLink ? 2 : 1); + // Reset the halt on the IN pipe we just read (not OUT), then surface a + // retryable error -- a stalled transfer's buffer is not a valid packet. + await this.usbDevice.clearHalt("in", debugLink ? 2 : 1); + throw new Error("bad read"); } if (data === undefined) throw new Error("bad read");