Skip to content

Commit

Permalink
bootloader: add OP_HARDWARE api endpoint
Browse files Browse the repository at this point in the history
This will allow the bbapp to flash different FW versions, depending
on the PCB variants. The initial implementation will respond with a
single byte that identifies the Secure Chip: 0 for ATECC, 1 for Optiga.
  • Loading branch information
Beerosagos committed Jan 15, 2025
1 parent d94935e commit 53efaf9
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,10 @@ customers cannot upgrade their bootloader, its changes are recorded separately.

## Bootloader

### v1.0.7
### v1.1.0
- Update manufacturer HID descriptor to bitbox.swiss
- Remove qtouch code from production bootloader
- Implement OP_HARDWARE endpoint, to identify the secure chip model

### v1.0.6
- Replace root pubkeys
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ endif()
# Example 'v1.0.0'. They MUST not contain a pre-release label such as '-beta'.
set(FIRMWARE_VERSION "v9.22.0")
set(FIRMWARE_BTC_ONLY_VERSION "v9.22.0")
set(BOOTLOADER_VERSION "v1.0.7")
set(BOOTLOADER_VERSION "v1.1.0")

find_package(PythonInterp 3.6 REQUIRED)

Expand Down
25 changes: 24 additions & 1 deletion py/bitbox02/bitbox02/bitbox02/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import io
import math
import hashlib
import enum

from bitbox02.communication import TransportLayer
from bitbox02.communication.devices import DeviceInfo

from bitbox02.communication.devices import DeviceInfo, parse_device_version

BOOTLOADER_CMD = 0x80 + 0x40 + 0x03
NUM_ROOT_KEYS = 3
Expand All @@ -44,6 +46,13 @@
SIGDATA_LEN = SIGNING_PUBKEYS_DATA_LEN + FIRMWARE_DATA_LEN


class Hardware(enum.Enum):
"""Hardware variant for the BitBox02 platform"""

ATECC = "ATECC"
OPTIGA = "Optiga"


def parse_signed_firmware(firmware: bytes) -> typing.Tuple[bytes, bytes, bytes]:
"""
Split raw firmware bytes into magic, sigdata and firmware
Expand Down Expand Up @@ -75,6 +84,9 @@ def __init__(self, transport: TransportLayer, device_info: DeviceInfo):
"bb02btc-bootloader": SIGDATA_MAGIC_BTCONLY,
"bitboxbase-bootloader": SIGDATA_MAGIC_BITBOXBASE_STANDARD,
}.get(device_info["product_string"])
self.version = parse_device_version(device_info["serial_number"])
# we remove the prerelease as it messes up same-version comparisons.
self.version = self.version.replace(prerelease=None)
assert self.expected_magic

def _query(self, msg: bytes) -> bytes:
Expand All @@ -94,6 +106,17 @@ def versions(self) -> typing.Tuple[int, int]:
firmware_v, signing_pubkeys_v = struct.unpack("<II", response[:8])
return firmware_v, signing_pubkeys_v

def hardware(self) -> Hardware:
"""
Returns (hardware variant).
"""
# Previous bootloader versions does not support the call and have ATECC SC.
if self.version >= "1.1.0":
response = self._query(b"W")
if response[:1] == b"\x01":
return Hardware.OPTIGA
return Hardware.ATECC

def get_hashes(
self, display_firmware_hash: bool = False, display_signing_keydata_hash: bool = False
) -> typing.Tuple[bytes, bytes]:
Expand Down
5 changes: 5 additions & 0 deletions py/send_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,10 @@ def _get_versions(self) -> None:
version = self._device.versions()
print(f"Firmware version: {version[0]}, Pubkeys version: {version[1]}")

def _get_hardware(self) -> None:
hw = self._device.hardware()
print(f"Hardware variant: {hw.value}")

def _erase(self) -> None:
self._device.erase()

Expand All @@ -1506,6 +1510,7 @@ def _menu(self) -> None:
choices = (
("Boot", self._boot),
("Print versions", self._get_versions),
("Print hardware variant", self._get_hardware),
("Erase firmware", self._erase),
("Show firmware hash at startup", self._show_fw_hash),
("Don't show firmware hash at startup", self._dont_show_fw_hash),
Expand Down
15 changes: 15 additions & 0 deletions src/bootloader/bootloader.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
#define OP_SCREEN_ROTATE ((uint8_t)'f') /* 0x66 */
// OP_SET_SHOW_FIRMWARE_HASH - Enable or disable the flag to automatically show the firmware hash.
#define OP_SET_SHOW_FIRMWARE_HASH ((uint8_t)'H') /* 0x4A */
// OP_HARDWARE - Return the secure chip variant.
#define OP_HARDWARE ((uint8_t)'W') /* 0x57 */

// API return codes
#define OP_STATUS_OK ((uint8_t)0)
Expand Down Expand Up @@ -766,6 +768,16 @@ static size_t _api_screen_rotate(uint8_t* output)
return _report_status(OP_STATUS_OK, output);
}

static size_t _api_hardware(uint8_t* output)
{
uint8_t type = 0;
if (memory_get_securechip_type() == MEMORY_SECURECHIP_TYPE_OPTIGA) {
type = 1;
}
output[BOOT_OP_LEN] = type;
return _report_status(OP_STATUS_OK, output) + 1;
}

static size_t _api_command(const uint8_t* input, uint8_t* output, const size_t max_out_len)
{
memset(output, 0, max_out_len);
Expand Down Expand Up @@ -809,6 +821,9 @@ static size_t _api_command(const uint8_t* input, uint8_t* output, const size_t m
case OP_SCREEN_ROTATE:
len = _api_screen_rotate(output);
break;
case OP_HARDWARE:
len = _api_hardware(output);
break;
default:
len = _report_status(OP_STATUS_ERR_INVALID_CMD, output);
_loading_ready = false;
Expand Down

0 comments on commit 53efaf9

Please sign in to comment.