Skip to content

Commit

Permalink
py: detect BitBox02 Plus
Browse files Browse the repository at this point in the history
This also introduces new signed firmware magic headers, chosen randomly.
  • Loading branch information
benma committed Feb 12, 2025
1 parent 23a7d69 commit 574d85b
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 15 deletions.
1 change: 1 addition & 0 deletions py/bitbox02/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- SD card: Remove API to prompt removal of the microSD card from the device
- Add support for regtest (supported from firmware version v9.21.0)
- btc_sign: allow identifying outputs belonging to different accounts of the same keystore
- Detect BitBox02 Plus

# 6.3.0
- Allow infering product and version via API call instead of via USB descriptor
Expand Down
30 changes: 20 additions & 10 deletions py/bitbox02/bitbox02/bitbox02/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@

from bitbox02.communication import TransportLayer

from bitbox02.communication.devices import DeviceInfo, parse_device_version
from bitbox02.communication.devices import (
DeviceInfo,
parse_device_version,
BB02MULTI_BOOTLOADER,
BB02BTC_BOOTLOADER,
BITBOX02PLUS_MULTI_BOOTLOADER,
BITBOX02PLUS_BTC_BOOTLOADER,
)

BOOTLOADER_CMD = 0x80 + 0x40 + 0x03
NUM_ROOT_KEYS = 3
Expand All @@ -35,9 +42,10 @@
assert MAX_FIRMWARE_SIZE % CHUNK_SIZE == 0
FIRMWARE_CHUNKS = MAX_FIRMWARE_SIZE // CHUNK_SIZE

SIGDATA_MAGIC_STANDARD = struct.pack(">I", 0x653F362B)
SIGDATA_MAGIC_BTCONLY = struct.pack(">I", 0x11233B0B)
SIGDATA_MAGIC_BITBOXBASE_STANDARD = struct.pack(">I", 0xAB6BD345)
SIGDATA_MAGIC_BITBOX02_MULTI = struct.pack(">I", 0x653F362B)
SIGDATA_MAGIC_BITBOX02_BTCONLY = struct.pack(">I", 0x11233B0B)
SIGDATA_MAGIC_BITBOX02PLUS_MULTI = struct.pack(">I", 0x5B648CEB)
SIGDATA_MAGIC_BITBOX02PLUS_BTCONLY = struct.pack(">I", 0x48714774)

MAGIC_LEN = 4

Expand Down Expand Up @@ -69,9 +77,10 @@ def parse_signed_firmware(firmware: bytes) -> typing.Tuple[bytes, bytes, bytes]:
raise ValueError("firmware too small")
magic, firmware = firmware[:MAGIC_LEN], firmware[MAGIC_LEN:]
if magic not in (
SIGDATA_MAGIC_STANDARD,
SIGDATA_MAGIC_BTCONLY,
SIGDATA_MAGIC_BITBOXBASE_STANDARD,
SIGDATA_MAGIC_BITBOX02_MULTI,
SIGDATA_MAGIC_BITBOX02_BTCONLY,
SIGDATA_MAGIC_BITBOX02PLUS_MULTI,
SIGDATA_MAGIC_BITBOX02PLUS_BTCONLY,
):
raise ValueError("invalid magic")

Expand All @@ -87,9 +96,10 @@ class Bootloader:
def __init__(self, transport: TransportLayer, device_info: DeviceInfo):
self._transport = transport
self.expected_magic = {
"bb02-bootloader": SIGDATA_MAGIC_STANDARD,
"bb02btc-bootloader": SIGDATA_MAGIC_BTCONLY,
"bitboxbase-bootloader": SIGDATA_MAGIC_BITBOXBASE_STANDARD,
BB02MULTI_BOOTLOADER: SIGDATA_MAGIC_BITBOX02_MULTI,
BB02BTC_BOOTLOADER: SIGDATA_MAGIC_BITBOX02_BTCONLY,
BITBOX02PLUS_MULTI_BOOTLOADER: SIGDATA_MAGIC_BITBOX02PLUS_MULTI,
BITBOX02PLUS_BTC_BOOTLOADER: SIGDATA_MAGIC_BITBOX02PLUS_BTCONLY,
}.get(device_info["product_string"])
self.version = parse_device_version(device_info["serial_number"])
# Delete the prelease part, as it messes with the comparison (e.g. 3.0.0-pre < 3.0.0 is
Expand Down
11 changes: 6 additions & 5 deletions py/bitbox02/bitbox02/communication/bitbox_api_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from .devices import parse_device_version, DeviceInfo

from .communication import TransportLayer
from .devices import BITBOX02MULTI, BITBOX02BTC
from .devices import BITBOX02MULTI, BITBOX02BTC, BITBOX02PLUS_MULTI, BITBOX02PLUS_BTC

try:
from .generated import hww_pb2 as hww
Expand Down Expand Up @@ -182,6 +182,7 @@ class Platform(enum.Enum):
"""Available hardware platforms"""

BITBOX02 = "bitbox02"
BITBOX02PLUS = "bitbox02-plus"


class BitBox02Edition(enum.Enum):
Expand Down Expand Up @@ -541,9 +542,9 @@ def __init__(

if device_info is not None:
version = device_info["serial_number"]
if device_info["product_string"] == BITBOX02MULTI:
if device_info["product_string"] in (BITBOX02MULTI, BITBOX02PLUS_MULTI):
edition = BitBox02Edition.MULTI
elif device_info["product_string"] == BITBOX02BTC:
elif device_info["product_string"] in (BITBOX02BTC, BITBOX02PLUS_BTC):
edition = BitBox02Edition.BTCONLY
else:
version, _, edition, _, _ = self.get_info(transport)
Expand Down Expand Up @@ -696,11 +697,11 @@ def get_info(
version_str = version.rstrip(b"\0").decode("ascii")

platform_byte, response = response[0], response[1:]
platform = {0x00: Platform.BITBOX02}[platform_byte]
platform = {0x00: Platform.BITBOX02, 0x02: Platform.BITBOX02PLUS}[platform_byte]

edition_byte, response = response[0], response[1:]
edition: Union[BitBox02Edition]
if platform == Platform.BITBOX02:
if platform in (Platform.BITBOX02, Platform.BITBOX02PLUS):
edition = {0x00: BitBox02Edition.MULTI, 0x01: BitBox02Edition.BTCONLY}[edition_byte]
else:
raise Exception("Unknown platform: {}".format(platform))
Expand Down
9 changes: 9 additions & 0 deletions py/bitbox02/bitbox02/communication/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
BITBOX02MULTI = "BitBox02"
BITBOX02BTC = "BitBox02BTC"

BITBOX02PLUS_MULTI_BOOTLOADER = "bb02p-bl-multi"
BITBOX02PLUS_BTC_BOOTLOADER = "bb02p-bl-btconly"
BITBOX02PLUS_MULTI = "bb02p-multi"
BITBOX02PLUS_BTC = "bb02p-btconly"


class TooManyFoundException(Exception):
def __init__(self, count: int) -> None:
Expand Down Expand Up @@ -111,6 +116,8 @@ def get_any_bitbox02s() -> List[DeviceInfo]:
"""
devices = get_bitbox02multi_devices()
devices.extend(get_bitbox02btc_devices())
devices.extend(get_devices(BITBOX02PLUS_MULTI))
devices.extend(get_devices(BITBOX02PLUS_BTC))
return devices


Expand Down Expand Up @@ -138,6 +145,8 @@ def get_any_bitbox02_bootloaders() -> List[DeviceInfo]:
"""
devices = get_bitbox02multi_bootloaders()
devices.extend(get_bitbox02btc_bootloaders())
devices.extend(get_devices(BITBOX02PLUS_MULTI_BOOTLOADER))
devices.extend(get_devices(BITBOX02PLUS_BTC_BOOTLOADER))
return devices


Expand Down

0 comments on commit 574d85b

Please sign in to comment.