py: make session reset an API setup helper
What changed, and why it matters
This is a routine Python code refactor. It moves an existing 'reset session' command from one internal class to another and adds a version check so older firmware simply skips it. There is no security bug being fixed here; it is purely organizational cleanup and test updates.
No security action required; review as normal code maintenance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit relocates reset_session() from BitBoxProtocol into BitBoxCommonAPI as a private static helper _reset_session(transport, version). The firmware version gate (>= v9.28.0) is preserved, now causing an early return for older versions. Tests are updated to call the new helper, and the CHANGELOG documents the session reset feature under Firmware / Unreleased.
Changed components
py/bitbox02/bitbox02/communication/bitbox_api_protocol.pypy/bitbox02/tests/test_session.pyCHANGELOG.mdInspect captured patch +28 / −16
### CHANGELOG.md
@@ -10,6 +10,7 @@ recorded separately.
- Reject malformed microSD backups instead of crashing
- Hold the screen reset pin low until firmware is ready initalize it.
- Cardano: limit xpub requests to 20 keypaths per batch
+- API: add a session reset command for clean host reconnects after interrupted operations
### v9.27.1
- API: include the installed bootloader version in the device info response
### py/bitbox02/bitbox02/communication/bitbox_api_protocol.py
@@ -302,18 +302,6 @@ def __init__(self, transport: TransportLayer):
def close(self) -> None:
self._transport.close()
- def reset_session(self) -> None:
- """Reset the previous session before attestation, unlock and the Noise handshake."""
- cid = self._transport.generate_cid()
- while True:
- response = self._transport.query(HwwRequestCode.REQ_RESET, HWW_CMD, cid)
- if response == HwwResponseCode.RSP_BUSY:
- time.sleep(1)
- continue
- if response != HwwResponseCode.RSP_ACK:
- raise Exception("Unexpected response to RESET.")
- return
-
def _raw_query(self, msg: bytes) -> bytes:
cid = self._transport.generate_cid()
return self._transport.query(msg, HWW_CMD, cid)
@@ -612,8 +600,7 @@ def __init__(
else:
self._bitbox_protocol = BitBoxProtocolV1(transport)
- if self.version >= semver.VersionInfo(9, 28, 0):
- self._bitbox_protocol.reset_session()
+ self._reset_session(transport, self.version)
if self.version >= semver.VersionInfo(2, 0, 0):
noise_config.attestation_check(self._perform_attestation())
@@ -711,6 +698,24 @@ def reboot(
return False
return True
+ @staticmethod
+ def _reset_session(transport: TransportLayer, version: semver.VersionInfo) -> None:
+ """Reset the previous session before attestation, unlock and the Noise handshake.
+
+ Skip firmware older than v9.28.0, which does not support session reset.
+ """
+ if version < semver.VersionInfo(9, 28, 0):
+ return
+ cid = transport.generate_cid()
+ while True:
+ response = transport.query(HwwRequestCode.REQ_RESET, HWW_CMD, cid)
+ if response == HwwResponseCode.RSP_BUSY:
+ time.sleep(1)
+ continue
+ if response != HwwResponseCode.RSP_ACK:
+ raise Exception("Unexpected response to RESET.")
+ return
+
@staticmethod
def get_info(
transport: TransportLayer,
### py/bitbox02/tests/test_session.py
@@ -5,6 +5,8 @@
import unittest
from unittest import mock
+import semver
+
from bitbox02.communication import bitbox_api_protocol as protocol
from bitbox02.communication.communication import TransportLayer
from bitbox02.communication.devices import BITBOX02MULTI
@@ -74,7 +76,9 @@ def test_reset_session_busy(self) -> None:
protocol.HwwResponseCode.RSP_ACK,
]
with mock.patch.object(protocol.time, "sleep") as sleep:
- protocol.BitBoxProtocolV7(transport).reset_session()
+ protocol.BitBoxCommonAPI._reset_session( # pylint: disable=protected-access
+ transport, semver.VersionInfo(9, 28, 0)
+ )
sleep.assert_called_once_with(1)
self.assertEqual(
transport.query.call_args_list,
@@ -88,7 +92,9 @@ def test_reset_session_requires_ack(self) -> None:
transport = mock.Mock(spec=TransportLayer)
transport.query.return_value = response
with self.assertRaisesRegex(Exception, "Unexpected response to RESET"):
- protocol.BitBoxProtocolV7(transport).reset_session()
+ protocol.BitBoxCommonAPI._reset_session( # pylint: disable=protected-access
+ transport, semver.VersionInfo(9, 28, 0)
+ )
if __name__ == "__main__":Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.