Skip to content

Commit 83e3e2d

Browse files
committed
Proposal for #236
1 parent 96875a8 commit 83e3e2d

File tree

2 files changed

+30
-18
lines changed

2 files changed

+30
-18
lines changed

speculos/client.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,31 @@ def check_status_code(response: requests.Response, url: str) -> None:
3333

3434
class ApduResponse:
3535
def __init__(self, response: requests.Response) -> None:
36-
self.response = response
36+
self.__response = response
3737

38-
def receive(self) -> bytes:
39-
check_status_code(self.response, "/apdu")
40-
data, status = split_apdu(bytes.fromhex(self.response.json()["data"]))
41-
if status != 0x9000:
42-
raise ApduException(status, data)
43-
return data
38+
def __receive(self):
39+
""" Get the response if not yet received and store it in `__data` and `__status` attributes """
40+
if self.__response is not None:
41+
check_status_code(self.__response, "/apdu")
42+
apdu_message = bytes.fromhex(self.__response.json()["data"])
43+
self.__data, self.__status = split_apdu(apdu_message)
44+
self.__response = None
45+
46+
def data(self, expected_sw: int = 0x9000) -> bytes:
47+
"""
48+
:return: Received data (status word stripped)
49+
:param expected_sw: Expected status word value.
50+
:raises ApduException: If received status word is different from `expected_sw` parameter.
51+
"""
52+
self.__receive()
53+
if self.__status != expected_sw:
54+
raise ApduException(self.__status, self.__data)
55+
return self.__data
56+
57+
def sw(self) -> int:
58+
""" :return: Received status word """
59+
self.__receive()
60+
return self.__status
4461

4562

4663
def split_apdu(data: bytes) -> Tuple[bytes, int]:
@@ -118,13 +135,8 @@ def get_screenshot(self) -> bytes:
118135
check_status_code(response, "/screenshot")
119136
return response.content
120137

121-
def _apdu_exchange(self, data: bytes) -> bytes:
122-
with self.session.post(f"{self.api_url}/apdu", json={"data": data.hex()}) as response:
123-
apdu_response = ApduResponse(response)
124-
return apdu_response.receive()
125-
126-
def _apdu_exchange_nowait(self, data: bytes) -> requests.Response:
127-
return self.session.post(f"{self.api_url}/apdu", json={"data": data.hex()}, stream=True)
138+
def _apdu_exchange(self, data: bytes, stream: bool = False) -> requests.Response:
139+
return self.session.post(f"{self.api_url}/apdu", json={"data": data.hex()}, stream=stream)
128140

129141
def set_automation_rules(self, rules: dict) -> None:
130142
with self.session.post(f"{self.api_url}/automation", json=rules) as response:
@@ -195,9 +207,9 @@ def __init__(self, app: str, args: List[str] = [], api_url: str = "http://127.0.
195207
SpeculosInstance.start(self)
196208
Api.__init__(self, api_url)
197209

198-
def apdu_exchange(self, cla: int, ins: int, data: bytes = b"", p1: int = 0, p2: int = 0) -> bytes:
210+
def apdu_exchange(self, cla: int, ins: int, data: bytes = b"", p1: int = 0, p2: int = 0) -> ApduResponse:
199211
apdu = bytes([cla, ins, p1, p2, len(data)]) + data
200-
return Api._apdu_exchange(self, apdu)
212+
return ApduResponse(Api._apdu_exchange(self, apdu))
201213

202214
@contextmanager
203215
def apdu_exchange_nowait(
@@ -206,7 +218,7 @@ def apdu_exchange_nowait(
206218
apdu = bytes([cla, ins, p1, p2, len(data)]) + data
207219
response = None
208220
try:
209-
response = Api._apdu_exchange_nowait(self, apdu)
221+
response = Api._apdu_exchange(self, apdu, stream=True)
210222
yield ApduResponse(response)
211223
finally:
212224
if response:

tests/apps/test_btc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_btc_get_public_key_with_user_approval(client, app):
6868
client.press_and_release("both")
6969

7070
with pytest.raises(speculos.client.ApduException) as e:
71-
response.receive()
71+
response.data()
7272
assert e.value.sw == 0x6985
7373

7474

0 commit comments

Comments
 (0)