From 5f87dd81a275fcf31bf4c1e1599abdd602084b6f Mon Sep 17 00:00:00 2001 From: tdejoigny-ledger Date: Sun, 5 May 2024 13:55:48 +0200 Subject: [PATCH 1/2] Add detailed descriptions for some error codes to ease debug. --- README.md | 8 +++++--- ledgerblue/comm.py | 24 ++++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 373e719..c166002 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Ledgerblue - Python tools for Ledger Blue, Nano S and Nano X +# Ledgerblue - Python tools for Ledger devices -This package contains Python tools to communicate with Ledger Blue, Nano S and Nano X and manage applications life cycle. +This package contains Python tools to communicate with Ledger devices and manage applications life cycle. ## Installation @@ -14,7 +14,7 @@ pip install ledgerblue ## Supported devices -At the moment these tools work for all Nano S, Nano S+, and Blue devices, but only for special Nano X developer units which are not available to the general public. +At the moment these tools work for all ledger devices, but only for special Nano X developer units which are not available to the general public. The Recover scripts, will work with Nano X starting from a specific version. Please check [Ledger Developer Portal](https://developers.ledger.com/docs/nano-app/introduction/) to see how to debug your application on a Nano X simulator using [Speculos](https://github.com/LedgerHQ/speculos) @@ -55,6 +55,8 @@ Use the following Target IDs (--targetId option) when running commands directly: | Device name | Firmware Version | Target ID | |------------------|------------------------------------|--------------| +| `Flex` | all | `0x33300004` | +| `Stax` | all | `0x33200004` | | `Nano S Plus` | all | `0x33100004` | | `Nano X` | < 2.2.1 (**developer units only**) | `0x33000004` | | `Nano X` | \>= 2.2.1 | `0x33000004` | diff --git a/ledgerblue/comm.py b/ledgerblue/comm.py index f9445a9..62cb91a 100644 --- a/ledgerblue/comm.py +++ b/ledgerblue/comm.py @@ -154,6 +154,14 @@ def exchange(self, apdu, timeout=TIMEOUT): possibleCause = "Unexpected state of device: verify that the right application is opened?" if sw == 0x6e00: possibleCause = "Unexpected state of device: verify that the right application is opened?" + if sw == 0x5515: + possibleCause = "Did you unlock the device?" + if sw == 0x6814: + possibleCause = "Unexpected target device: verify that your are using the right device?" + if sw == 0x511F: + possibleCause = "The OS version on your device does not seem compatible with the SDK version used to build the app" + if sw == 0x5120: + possibleCause = "Sideload is not supported on Nano X" raise CommException("Invalid status %04x (%s)" % (sw, possibleCause), sw, response) return response @@ -209,6 +217,14 @@ def exchange(self, apdu, timeout=TIMEOUT): possibleCause = "Unexpected state of device: verify that the right application is opened?" if sw == 0x6e00: possibleCause = "Unexpected state of device: verify that the right application is opened?" + if sw == 0x5515: + possibleCause = "Did you unlock the device?" + if sw == 0x6814: + possibleCause = "Unexpected target device: verify that your are using the right device?" + if sw == 0x511F: + possibleCause = "The OS version on your device does not seem compatible with the SDK version used to build the app" + if sw == 0x5120: + possibleCause = "Sideload is not supported on Nano X" raise CommException("Invalid status %04x (%s)" % (sw, possibleCause), sw, response) if self.debug: print(f"[NFC] <= {response.hex()}") @@ -256,6 +272,14 @@ def exchange(self, apdu, timeout=TIMEOUT): possibleCause = "Unexpected state of device: verify that the right application is opened?" if sw == 0x6e00: possibleCause = "Unexpected state of device: verify that the right application is opened?" + if sw == 0x5515: + possibleCause = "Did you unlock the device?" + if sw == 0x6814: + possibleCause = "Unexpected target device: verify that your are using the right device?" + if sw == 0x511F: + possibleCause = "The OS version on your device does not seem compatible with the SDK version used to build the app" + if sw == 0x5120: + possibleCause = "Sideload is not supported on Nano X" self.close() raise CommException("Invalid status %04x (%s)" % (sw, possibleCause), sw, response) return response diff --git a/pyproject.toml b/pyproject.toml index c8df586..3b8bd44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ name = "ledgerblue" authors = [ { name = "Ledger", email = "hello@ledger.fr" } ] -description = "Python library to communicate with Ledger Blue/Nano S" +description = "Python library to communicate with Ledger devices" readme = { file = "README.md", content-type = "text/markdown" } license = { file = "LICENSE" } classifiers = [ From 2f2bee6ee17611bbead890ea4187d9e959c4a1ea Mon Sep 17 00:00:00 2001 From: tdejoigny-ledger Date: Mon, 6 May 2024 15:43:23 +0200 Subject: [PATCH 2/2] fix PR review remarks --- README.md | 3 +- ledgerblue/comm.py | 93 ++++++++++++---------------------------------- 2 files changed, 25 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index c166002..8833764 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,7 @@ Use the following Target IDs (--targetId option) when running commands directly: | `Flex` | all | `0x33300004` | | `Stax` | all | `0x33200004` | | `Nano S Plus` | all | `0x33100004` | -| `Nano X` | < 2.2.1 (**developer units only**) | `0x33000004` | -| `Nano X` | \>= 2.2.1 | `0x33000004` | +| `Nano X` | (**developer units only**) | `0x33000004` | | `Nano S` | <= 1.3.1 | `0x31100002` | | `Nano S` | 1.4.x | `0x31100003` | | `Nano S` | \>= 1.5.x | `0x31100004` | diff --git a/ledgerblue/comm.py b/ledgerblue/comm.py index 62cb91a..8fe7fcd 100644 --- a/ledgerblue/comm.py +++ b/ledgerblue/comm.py @@ -71,6 +71,26 @@ except ImportError: PCSC = False +def get_possible_error_cause(sw): + cause_map = { + 0x6982: "Have you uninstalled the existing CA with resetCustomCA first?", + 0x6985: "Condition of use not satisfied (denied by the user?)", + 0x6a84: "Not enough space?", + 0x6a85: "Not enough space?", + 0x6a83: "Maybe this app requires a library to be installed first?", + 0x6484: "Are you using the correct targetId?", + 0x6d00: "Unexpected state of device: verify that the right application is opened?", + 0x6e00: "Unexpected state of device: verify that the right application is opened?", + 0x5515: "Did you unlock the device?", + 0x6814: "Unexpected target device: verify that you are using the right device?", + 0x511F: "The OS version on your device does not seem compatible with the SDK version used to build the app", + 0x5120: "Sideload is not supported on Nano X", + } + + # If the status word is in the map, return the corresponding cause, otherwise return a default message + return cause_map.get(sw, "Unknown reason") + + class HIDDongleHIDAPI(Dongle, DongleWait): def __init__(self, device, ledger=False, debug=False): @@ -139,29 +159,7 @@ def exchange(self, apdu, timeout=TIMEOUT): if self.debug: print("HID <= %s%.2x" % (response.hex(), sw)) if sw != 0x9000 and (sw & 0xFF00) != 0x6100 and (sw & 0xFF00) != 0x6C00: - possibleCause = "Unknown reason" - if sw == 0x6982: - possibleCause = "Have you uninstalled the existing CA with resetCustomCA first?" - if sw == 0x6985: - possibleCause = "Condition of use not satisfied (denied by the user?)" - if sw == 0x6a84 or sw == 0x6a85: - possibleCause = "Not enough space?" - if sw == 0x6a83: - possibleCause = "Maybe this app requires a library to be installed first?" - if sw == 0x6484: - possibleCause = "Are you using the correct targetId?" - if sw == 0x6d00: - possibleCause = "Unexpected state of device: verify that the right application is opened?" - if sw == 0x6e00: - possibleCause = "Unexpected state of device: verify that the right application is opened?" - if sw == 0x5515: - possibleCause = "Did you unlock the device?" - if sw == 0x6814: - possibleCause = "Unexpected target device: verify that your are using the right device?" - if sw == 0x511F: - possibleCause = "The OS version on your device does not seem compatible with the SDK version used to build the app" - if sw == 0x5120: - possibleCause = "Sideload is not supported on Nano X" + possibleCause = get_possible_error_cause(sw) raise CommException("Invalid status %04x (%s)" % (sw, possibleCause), sw, response) return response @@ -202,29 +200,8 @@ def exchange(self, apdu, timeout=TIMEOUT): response = self.tag.transceive(apdu, 5.0) sw = (response[-2] << 8) + response[-1] if sw != 0x9000 and (sw & 0xFF00) != 0x6100 and (sw & 0xFF00) != 0x6C00: - possibleCause = "Unknown reason" - if sw == 0x6982: - possibleCause = "Have you uninstalled the existing CA with resetCustomCA first?" - if sw == 0x6985: - possibleCause = "Condition of use not satisfied (denied by the user?)" - if sw == 0x6a84 or sw == 0x6a85: - possibleCause = "Not enough space?" - if sw == 0x6a83: - possibleCause = "Maybe this app requires a library to be installed first?" - if sw == 0x6484: - possibleCause = "Are you using the correct targetId?" - if sw == 0x6d00: - possibleCause = "Unexpected state of device: verify that the right application is opened?" - if sw == 0x6e00: - possibleCause = "Unexpected state of device: verify that the right application is opened?" - if sw == 0x5515: - possibleCause = "Did you unlock the device?" - if sw == 0x6814: - possibleCause = "Unexpected target device: verify that your are using the right device?" - if sw == 0x511F: - possibleCause = "The OS version on your device does not seem compatible with the SDK version used to build the app" - if sw == 0x5120: - possibleCause = "Sideload is not supported on Nano X" + possibleCause = get_possible_error_cause(sw) + self.close() raise CommException("Invalid status %04x (%s)" % (sw, possibleCause), sw, response) if self.debug: print(f"[NFC] <= {response.hex()}") @@ -257,29 +234,7 @@ def exchange(self, apdu, timeout=TIMEOUT): if self.debug: print("[BLE] <= %s%.2x" % (response.hex(), sw)) if sw != 0x9000 and (sw & 0xFF00) != 0x6100 and (sw & 0xFF00) != 0x6C00: - possibleCause = "Unknown reason" - if sw == 0x6982: - possibleCause = "Have you uninstalled the existing CA with resetCustomCA first?" - if sw == 0x6985: - possibleCause = "Condition of use not satisfied (denied by the user?)" - if sw == 0x6a84 or sw == 0x6a85: - possibleCause = "Not enough space?" - if sw == 0x6a83: - possibleCause = "Maybe this app requires a library to be installed first?" - if sw == 0x6484: - possibleCause = "Are you using the correct targetId?" - if sw == 0x6d00: - possibleCause = "Unexpected state of device: verify that the right application is opened?" - if sw == 0x6e00: - possibleCause = "Unexpected state of device: verify that the right application is opened?" - if sw == 0x5515: - possibleCause = "Did you unlock the device?" - if sw == 0x6814: - possibleCause = "Unexpected target device: verify that your are using the right device?" - if sw == 0x511F: - possibleCause = "The OS version on your device does not seem compatible with the SDK version used to build the app" - if sw == 0x5120: - possibleCause = "Sideload is not supported on Nano X" + possibleCause = get_possible_error_cause(sw) self.close() raise CommException("Invalid status %04x (%s)" % (sw, possibleCause), sw, response) return response