2017-04-01 13:45:50 +00:00
|
|
|
#!/usr/bin/env python3
|
2024-04-12 12:20:32 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2020-01-03 15:43:44 +00:00
|
|
|
import json
|
2023-08-10 11:31:51 +00:00
|
|
|
from pathlib import Path
|
2017-04-01 13:45:50 +00:00
|
|
|
|
2020-01-03 15:43:44 +00:00
|
|
|
import click
|
2018-07-31 09:35:09 +00:00
|
|
|
|
2023-03-02 15:06:09 +00:00
|
|
|
from trezorlib import firmware, toif
|
2017-04-01 13:45:50 +00:00
|
|
|
|
2017-06-13 14:50:03 +00:00
|
|
|
|
2024-04-12 12:20:32 +00:00
|
|
|
def minimum_header_len(spec, quiet):
|
2020-02-05 11:23:34 +00:00
|
|
|
spec = spec.copy()
|
|
|
|
spec["header_len"] = 512000
|
2023-03-02 15:06:09 +00:00
|
|
|
reparsed = firmware.VendorHeader.SUBCON.parse(
|
|
|
|
firmware.VendorHeader.SUBCON.build(spec)
|
|
|
|
)
|
2020-02-05 11:23:34 +00:00
|
|
|
data_length = reparsed._end_offset - reparsed._start_offset
|
|
|
|
# data length + 65 for signatures, rounded up to nearest multiple of 512
|
2024-04-12 12:20:32 +00:00
|
|
|
if not quiet:
|
|
|
|
click.echo(f"Minimum header length: {data_length + 65} bytes.")
|
2020-02-05 11:23:34 +00:00
|
|
|
return (data_length + 65 + 511) // 512 * 512
|
|
|
|
|
|
|
|
|
2024-04-12 12:20:32 +00:00
|
|
|
FilePath = click.Path(dir_okay=False, path_type=Path)
|
|
|
|
|
|
|
|
|
2020-01-03 15:43:44 +00:00
|
|
|
@click.command()
|
2024-04-12 12:20:32 +00:00
|
|
|
@click.argument("specfile", type=FilePath)
|
|
|
|
@click.option("-i", "--image", type=FilePath)
|
|
|
|
@click.option("-o", "--outfile", type=FilePath)
|
2023-08-10 11:31:51 +00:00
|
|
|
@click.option("-c", "--check", is_flag=True, help="Check but do not write header.")
|
|
|
|
@click.option("-q", "--quiet", is_flag=True, help="Do not print anything.")
|
2024-04-12 12:20:32 +00:00
|
|
|
def build_vendorheader(
|
|
|
|
specfile: Path, image: Path | None, outfile: Path | None, check: bool, quiet: bool
|
|
|
|
):
|
2023-08-10 11:31:51 +00:00
|
|
|
if quiet:
|
|
|
|
echo = lambda *args, **kwargs: None
|
|
|
|
else:
|
|
|
|
echo = click.echo
|
|
|
|
|
2024-04-12 12:20:32 +00:00
|
|
|
if not specfile.exists():
|
|
|
|
raise click.ClickException(f"Spec file {specfile.name} does not exist.")
|
|
|
|
|
|
|
|
if image is None:
|
|
|
|
image = specfile.with_suffix(".toif")
|
|
|
|
if not image.exists():
|
|
|
|
raise click.ClickException(f"Image file {image.name} does not exist.")
|
|
|
|
|
|
|
|
if outfile is None:
|
|
|
|
vh_stem = specfile.stem
|
|
|
|
if vh_stem.startswith("vendor_"):
|
|
|
|
vh_stem = vh_stem[len("vendor_") :]
|
|
|
|
outfile = specfile.parent / f"vendorheader_{vh_stem}_unsigned.bin"
|
|
|
|
|
|
|
|
spec = json.loads(specfile.read_text())
|
2020-01-03 15:43:44 +00:00
|
|
|
spec["pubkeys"] = [bytes.fromhex(k) for k in spec["pubkeys"]]
|
2024-04-12 12:20:32 +00:00
|
|
|
spec["image"] = toif.ToifStruct.parse(image.read_bytes())
|
2020-01-03 15:43:44 +00:00
|
|
|
spec["sigmask"] = 0
|
|
|
|
spec["signature"] = b"\x00" * 64
|
2023-05-31 12:43:20 +00:00
|
|
|
if spec["hw_model"] is None:
|
|
|
|
spec["hw_model"] = b"\x00\x00\x00\x00"
|
|
|
|
else:
|
|
|
|
spec["hw_model"] = spec["hw_model"].encode("ascii")
|
2020-02-05 11:23:34 +00:00
|
|
|
|
2024-04-12 12:20:32 +00:00
|
|
|
min_length = minimum_header_len(spec, quiet)
|
2020-02-05 11:23:34 +00:00
|
|
|
if "header_len" not in spec:
|
|
|
|
spec["header_len"] = min_length
|
|
|
|
elif spec["header_len"] < min_length:
|
|
|
|
raise click.ClickException(
|
|
|
|
f"Specified header_len {spec['header_len']} too low. "
|
|
|
|
f"Minimum allowable value is {min_length}."
|
|
|
|
)
|
2023-03-02 15:06:09 +00:00
|
|
|
elif spec["header_len"] == min_length:
|
2023-08-10 11:31:51 +00:00
|
|
|
echo(f"{specfile.name}: Header has correct length.")
|
2023-03-02 15:06:09 +00:00
|
|
|
else:
|
2023-08-10 11:31:51 +00:00
|
|
|
echo(
|
2023-03-02 15:06:09 +00:00
|
|
|
f"{specfile.name}: Extending header ({min_length} bytes) to {spec['header_len']} bytes."
|
|
|
|
)
|
2020-02-05 11:23:34 +00:00
|
|
|
|
|
|
|
if spec["header_len"] % 512 != 0:
|
|
|
|
raise click.ClickException("Invalid header_len: must be a multiple of 512")
|
|
|
|
|
2023-08-10 11:31:51 +00:00
|
|
|
vh_bytes = firmware.VendorHeader.SUBCON.build(spec)
|
|
|
|
if check:
|
|
|
|
if not outfile.exists():
|
|
|
|
raise click.ClickException(f"Header file {outfile.name} does not exist.")
|
|
|
|
outfile_bytes = outfile.read_bytes()
|
|
|
|
if outfile_bytes != vh_bytes:
|
|
|
|
raise click.ClickException(
|
|
|
|
f"Header file {outfile.name} differs from expected header."
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
outfile.write_bytes(firmware.VendorHeader.SUBCON.build(spec))
|
2017-04-01 13:45:50 +00:00
|
|
|
|
2017-06-13 14:50:03 +00:00
|
|
|
|
2020-01-03 15:43:44 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
build_vendorheader()
|