Skip to content

Commit

Permalink
Merge branch 'bootloader-sc'
Browse files Browse the repository at this point in the history
  • Loading branch information
Beerosagos committed Jan 22, 2025
2 parents c016621 + 699fba6 commit 10f2229
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 8 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
41 changes: 40 additions & 1 deletion py/bitbox02/bitbox02/bitbox02/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
import io
import math
import hashlib
import enum
from typing import TypedDict

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 +47,19 @@
SIGDATA_LEN = SIGNING_PUBKEYS_DATA_LEN + FIRMWARE_DATA_LEN


class SecureChipModel(enum.Enum):
"""Secure chip model variants for the BitBox02 platform."""

ATECC = "ATECC"
OPTIGA = "Optiga"


class Hardware(TypedDict):
"""Hardware configuration containing secure chip model information."""

secure_chip_model: SecureChipModel


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 +91,10 @@ 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"])
# Delete the prelease part, as it messes with the comparison (e.g. 3.0.0-pre < 3.0.0 is
# True, but the 3.0.0-pre has already the same API breaking changes like 3.0.0...).
self.version = self.version.replace(prerelease=None)
assert self.expected_magic

def _query(self, msg: bytes) -> bytes:
Expand All @@ -94,6 +114,25 @@ 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).
"""
secure_chip: SecureChipModel = SecureChipModel.ATECC

# Previous bootloader versions do not support the call and have ATECC SC.
if self.version >= "1.1.0":
response = self._query(b"W")
response_code = response[:1]

if response_code == b"\x00":
secure_chip = SecureChipModel.ATECC
elif response_code == b"\x01":
secure_chip = SecureChipModel.OPTIGA
else:
raise ValueError(f"Unrecognized securechip model: {response_code!r}")
return {"secure_chip_model": secure_chip}

def get_hashes(
self, display_firmware_hash: bool = False, display_signing_keydata_hash: bool = False
) -> typing.Tuple[bytes, bytes]:
Expand Down
4 changes: 1 addition & 3 deletions py/bitbox02/bitbox02/communication/bitbox_api_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,9 +557,7 @@ def __init__(

# Delete the prelease part, as it messes with the comparison (e.g. 3.0.0-pre < 3.0.0 is
# True, but the 3.0.0-pre has already the same API breaking changes like 3.0.0...).
self.version = semver.VersionInfo(
self.version.major, self.version.minor, self.version.patch, build=self.version.build
)
self.version = self.version.replace(prerelease=None)

# raises exceptions if the library is out of date
self._check_max_version()
Expand Down
6 changes: 6 additions & 0 deletions py/send_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,11 @@ def _get_versions(self) -> None:
version = self._device.versions()
print(f"Firmware version: {version[0]}, Pubkeys version: {version[1]}")

def _get_hardware(self) -> None:
secure_chip = self._device.hardware()["secure_chip_model"]
print(f"Hardware variant:")
print(f"- Secure Chip: {secure_chip.value}")

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

Expand All @@ -1506,6 +1511,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
19 changes: 17 additions & 2 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 @@ -731,7 +733,7 @@ static size_t _api_set_show_firmware_hash(const uint8_t* input, uint8_t* output)
}

/*
* output filled with bootloader version | firmware version | signing pubkeys version
* output filled with firmware version | signing pubkeys version
*/
static size_t _api_versions(uint8_t* output)
{
Expand All @@ -742,7 +744,7 @@ static size_t _api_versions(uint8_t* output)
(const uint8_t*)&data->fields.signing_pubkeys_version,
sizeof(version_t));
_report_status(OP_STATUS_OK, output);
return BOOT_OP_LEN + sizeof(version_t) * 3;
return BOOT_OP_LEN + sizeof(version_t) * 2;
}

static void _api_reboot(void)
Expand All @@ -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 10f2229

Please sign in to comment.