mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-15 10:58:09 +00:00
3bfc0b61e5
[no changelog]
127 lines
3.4 KiB
Python
Executable File
127 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import click
|
|
|
|
from pathlib import Path
|
|
from hashlib import blake2s
|
|
|
|
from trezorlib.firmware.core import FirmwareImage, Model
|
|
|
|
ALIGNED_SIZE = 128 * 1024
|
|
|
|
HERE = Path(__file__).parent
|
|
BOOTLOADERS = HERE / ".." / "embed" / "models"
|
|
|
|
TEMPLATE = """\
|
|
#ifndef BOOTLOADER_HASHES_H
|
|
#define BOOTLOADER_HASHES_H
|
|
|
|
// Auto-generated file, do not edit.
|
|
|
|
// clang-format off
|
|
{patterns}
|
|
// clang-format on
|
|
|
|
#endif
|
|
"""
|
|
|
|
PATTERN = """\
|
|
// {name} version {version}
|
|
#define BOOTLOADER_{suffix}_00 {{{bytes_00}}}
|
|
#define BOOTLOADER_{suffix}_FF {{{bytes_ff}}}
|
|
"""
|
|
|
|
|
|
def aligned_digest(fn: Path, data: bytes, padding: bytes) -> bytes:
|
|
"""Calculate digest of data, aligned to ALIGNED_SIZE with
|
|
the specified padding.
|
|
|
|
Firmware needs to check the bootloader against a digest padded either by 0xff
|
|
(unwritten NOR-flash byte) or 0x00 (explicitly cleared byte).
|
|
"""
|
|
if len(data) > ALIGNED_SIZE:
|
|
raise ValueError(fn, "too big")
|
|
|
|
assert len(padding) == 1
|
|
digest_data = data + padding * (ALIGNED_SIZE - len(data))
|
|
assert len(digest_data) == ALIGNED_SIZE
|
|
return blake2s(digest_data).digest()
|
|
|
|
|
|
def to_uint_array(data: bytes) -> str:
|
|
"""Convert bytes to C array of uint8_t, like so:
|
|
|
|
>>> to_uint_array(b"\\x00\\x01\\x02")
|
|
"{0x00, 0x01, 0x02}"
|
|
"""
|
|
return ", ".join([f"0x{i:02x}" for i in data])
|
|
|
|
|
|
def bootloader_str(file: Path, model: str) -> str:
|
|
"""From a given file, generate the relevant C definition strings from PATTERN.
|
|
|
|
Calculates the two padded hashes, one with 0x00 and the other 0xFF, and returns
|
|
a string suitable for writing into bl_check.c.
|
|
"""
|
|
data = file.read_bytes()
|
|
|
|
suffix = file.stem[len("bootloader_") :].upper()
|
|
bytes_00 = to_uint_array(aligned_digest(file, data, b"\x00"))
|
|
bytes_ff = to_uint_array(aligned_digest(file, data, b"\xff"))
|
|
|
|
bl = FirmwareImage.parse(data)
|
|
version_str = ".".join(str(x) for x in bl.header.version)
|
|
if not isinstance(bl.header.hw_model, Model):
|
|
raise ValueError(
|
|
f"Model mismatch: {file.name} {model} (found {bytes(bl.header.hw_model).decode()})"
|
|
)
|
|
elif bl.header.hw_model.value != model.encode():
|
|
raise ValueError(
|
|
f"Model mismatch: {file.name} {model} (found {bl.header.hw_model.value})"
|
|
)
|
|
|
|
return PATTERN.format(
|
|
name=file.name,
|
|
version=version_str,
|
|
suffix=suffix,
|
|
bytes_00=bytes_00,
|
|
bytes_ff=bytes_ff,
|
|
)
|
|
|
|
|
|
@click.command()
|
|
@click.option("-c", "--check", is_flag=True, help="Do not write, only check.")
|
|
def bootloader_hashes(check):
|
|
|
|
models = list(BOOTLOADERS.iterdir())
|
|
|
|
models = [model for model in models if model.is_dir()]
|
|
|
|
for model in models:
|
|
|
|
path = model / "bootloaders"
|
|
|
|
if path.is_dir():
|
|
|
|
header_file = path / "bootloader_hashes.h"
|
|
|
|
patterns = []
|
|
|
|
bootloaders = sorted(path.glob("bootloader*.bin"))
|
|
for bootloader in bootloaders:
|
|
print(f"Processing {bootloader}")
|
|
patterns.append(bootloader_str(bootloader, model.name))
|
|
|
|
content = TEMPLATE.format(patterns="\n".join(patterns))
|
|
|
|
if not check:
|
|
header_file.write_text(content)
|
|
else:
|
|
actual = header_file.read_text()
|
|
if content != actual:
|
|
raise click.ClickException(f"{header_file} differs from expected")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
bootloader_hashes()
|