1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-21 14:08:13 +00:00

style: allow python style check in tools

[no changelog]
This commit is contained in:
tychovrahe 2025-06-28 18:27:33 +02:00 committed by TychoVrahe
parent 438fb2554c
commit 981ddb046f
43 changed files with 222 additions and 159 deletions

View File

@ -6,6 +6,7 @@ help: ## show this help
## style commands: ## style commands:
PY_FILES = $(shell find . -type f -name '*.py' | sed 'sO^\./OO' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude ) common/protob/pb2py PY_FILES = $(shell find . -type f -name '*.py' | sed 'sO^\./OO' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude ) common/protob/pb2py
PY_FILES_LIMITED = $(shell find . -type f -name '*.py' | sed 'sO^\./OO' | grep -f ./tools/style.py.include | grep -v -f ./tools/style.py.exclude | grep -v -f ./tools/style.py.typecheck.exclude ) common/protob/pb2py
C_FILES = $(shell find . -type f -name '*.[ch]' | grep -f ./tools/style.c.include | grep -v -f ./tools/style.c.exclude ) C_FILES = $(shell find . -type f -name '*.[ch]' | grep -f ./tools/style.c.include | grep -v -f ./tools/style.c.exclude )
@ -22,7 +23,9 @@ pystyle_check: ## run code style check on application sources and tests
@echo [TYPECHECK] @echo [TYPECHECK]
@make -C core typecheck @make -C core typecheck
@echo [FLAKE8] @echo [FLAKE8]
@flake8 $(PY_FILES) @flake8 $(PY_FILES_LIMITED)
@echo [FLAKE8 - limited]
@flake8 --extend-ignore=ANN $(PY_FILES)
@echo [ISORT] @echo [ISORT]
@isort --check-only $(PY_FILES) @isort --check-only $(PY_FILES)
@echo [BLACK] @echo [BLACK]
@ -45,7 +48,9 @@ pystyle: ## apply code style on application sources and tests
@echo [TYPECHECK] @echo [TYPECHECK]
@make -C core typecheck @make -C core typecheck
@echo [FLAKE8] @echo [FLAKE8]
@flake8 $(PY_FILES) @flake8 $(PY_FILES_LIMITED)
@echo [FLAKE8 - limited]
@flake8 --extend-ignore=ANN $(PY_FILES)
@echo [PYLINT] @echo [PYLINT]
@pylint $(PY_FILES) @pylint $(PY_FILES)
@echo [PYTHON] @echo [PYTHON]

View File

@ -5,14 +5,14 @@ from pathlib import Path
from types import SimpleNamespace from types import SimpleNamespace
from typing import TYPE_CHECKING, Dict, Protocol, TextIO from typing import TYPE_CHECKING, Dict, Protocol, TextIO
# for python37 support, is not present in typing there
from typing_extensions import TypedDict
import click import click
from dominate import document from dominate import document
from dominate.tags import * from dominate.tags import a, h3, meta, style, table, tbody, td, th, thead, tr
from dominate.util import raw from dominate.util import raw
# for python37 support, is not present in typing there
from typing_extensions import TypedDict
HERE = Path(__file__).resolve().parent HERE = Path(__file__).resolve().parent

View File

@ -2,9 +2,9 @@
import json import json
import sys import sys
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("""\ print(
"""\
USAGE: ./analyze-memory-dump.py somefile.json [memorymap.html] USAGE: ./analyze-memory-dump.py somefile.json [memorymap.html]
Where "somefile.json" was produced by using `trezor.utils.mem_dump("somefile.json")` Where "somefile.json" was produced by using `trezor.utils.mem_dump("somefile.json")`
@ -25,8 +25,8 @@ actually care about.
Modules are nothing but a link to a globals dict. The dict must be examined separately. Modules are nothing but a link to a globals dict. The dict must be examined separately.
Generators and closures are painful :( Generators and closures are painful :(
""") """
)
with open(sys.argv[1]) as f: with open(sys.argv[1]) as f:
@ -64,7 +64,7 @@ def is_ignored_ptr(ptr):
if isinstance(ptr, str): if isinstance(ptr, str):
ptr = int(ptr, 16) ptr = int(ptr, 16)
return not (min_ptr <= ptr < max_ptr) return not min_ptr <= ptr < max_ptr
def deref_or_shortval(maybe_ptr): def deref_or_shortval(maybe_ptr):
@ -204,7 +204,7 @@ pixels_per_line = len(
pixelsize = bytes_per_block pixelsize = bytes_per_block
bytes_per_line = bytes_per_block * pixels_per_line bytes_per_line = bytes_per_block * pixels_per_line
maxline = ((max_ptr - min_ptr) & ~(bytes_per_line - 1)) + (bytes_per_line * 2) maxline = ((max_ptr - min_ptr) & ~(bytes_per_line - 1)) + (bytes_per_line * 2)
pixelmap = [None] * 2*(maxline // pixelsize) pixelmap = [None] * 2 * (maxline // pixelsize)
def pixel_index(ptrval): def pixel_index(ptrval):

View File

@ -1,12 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import time
from pathlib import Path
import sys import sys
import time
import zlib import zlib
from pathlib import Path
import click import click
import serial import serial
def _compress(data: bytes) -> bytes: def _compress(data: bytes) -> bytes:
""" """
Compress data with zlib at max compression, raw deflate. Compress data with zlib at max compression, raw deflate.
@ -14,14 +15,15 @@ def _compress(data: bytes) -> bytes:
compressor = zlib.compressobj(level=9, wbits=-10) compressor = zlib.compressobj(level=9, wbits=-10)
return compressor.compress(data) + compressor.flush() return compressor.compress(data) + compressor.flush()
def send_cmd(ser, cmd, expect_ok=True): def send_cmd(ser, cmd, expect_ok=True):
"""Send a line, read response, and abort on CLI_ERROR.""" """Send a line, read response, and abort on CLI_ERROR."""
ser.write((cmd + "\r\n").encode()) ser.write((cmd + "\r\n").encode())
# Give the device a moment to process # Give the device a moment to process
time.sleep(0.05) time.sleep(0.05)
resp = ser.readline().decode(errors='ignore').strip() resp = ser.readline().decode(errors="ignore").strip()
while resp.startswith("#") or len(resp) == 0: while resp.startswith("#") or len(resp) == 0:
resp = ser.readline().decode(errors='ignore').strip() resp = ser.readline().decode(errors="ignore").strip()
click.echo(f"> {cmd}") click.echo(f"> {cmd}")
click.echo(f"< {resp}") click.echo(f"< {resp}")
if expect_ok and not resp.startswith("OK"): if expect_ok and not resp.startswith("OK"):
@ -29,6 +31,7 @@ def send_cmd(ser, cmd, expect_ok=True):
sys.exit(1) sys.exit(1)
return resp return resp
def upload_bootloader(port, bin_path, chunk_size): def upload_bootloader(port, bin_path, chunk_size):
# Read binary file # Read binary file
data = Path(bin_path).read_bytes() data = Path(bin_path).read_bytes()
@ -38,7 +41,9 @@ def upload_bootloader(port, bin_path, chunk_size):
# Compress the data # Compress the data
comp_data = _compress(data) comp_data = _compress(data)
comp_size = len(comp_data) comp_size = len(comp_data)
click.echo(f"Compressed to {comp_size} bytes ({comp_size*100//orig_size}% of original)") click.echo(
f"Compressed to {comp_size} bytes ({comp_size * 100 // orig_size}% of original)"
)
# Open USB-VCP port # Open USB-VCP port
ser = serial.Serial(port, timeout=2) ser = serial.Serial(port, timeout=2)
@ -52,7 +57,7 @@ def upload_bootloader(port, bin_path, chunk_size):
# 2) Stream compressed chunks # 2) Stream compressed chunks
offset = 0 offset = 0
while offset < comp_size: while offset < comp_size:
chunk = comp_data[offset:offset+chunk_size] chunk = comp_data[offset : offset + chunk_size]
hexstr = chunk.hex() hexstr = chunk.hex()
send_cmd(ser, f"bootloader-update chunk {hexstr}") send_cmd(ser, f"bootloader-update chunk {hexstr}")
offset += len(chunk) offset += len(chunk)
@ -63,11 +68,21 @@ def upload_bootloader(port, bin_path, chunk_size):
send_cmd(ser, "bootloader-update end") send_cmd(ser, "bootloader-update end")
click.echo("Bootloader upload complete.") click.echo("Bootloader upload complete.")
@click.command(context_settings={"help_option_names": ["-h", "--help"]}) @click.command(context_settings={"help_option_names": ["-h", "--help"]})
@click.argument("port", metavar="<serial-port>") @click.argument("port", metavar="<serial-port>")
@click.argument("binary", metavar="<bootloader-binary>", type=click.Path(exists=True, dir_okay=False)) @click.argument(
@click.option("--chunk-size", "-c", default=512, show_default=True, "binary",
help="Max bytes per chunk (in compressed form)") metavar="<bootloader-binary>",
type=click.Path(exists=True, dir_okay=False),
)
@click.option(
"--chunk-size",
"-c",
default=512,
show_default=True,
help="Max bytes per chunk (in compressed form)",
)
def main(port, binary, chunk_size): def main(port, binary, chunk_size):
""" """
Upload a (compressed) bootloader image via USB-VCP CLI. Upload a (compressed) bootloader image via USB-VCP CLI.
@ -84,5 +99,6 @@ def main(port, binary, chunk_size):
click.echo("Interrupted by user.", err=True) click.echo("Interrupted by user.", err=True)
sys.exit(1) sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path
import sys import sys
from pathlib import Path
import click import click
from PIL import Image from PIL import Image

View File

@ -2,20 +2,19 @@
from base64 import b64decode from base64 import b64decode
from hashlib import sha256 from hashlib import sha256
import requests
import requests
REPO = "certifi/python-certifi" REPO = "certifi/python-certifi"
def fetch_certdata(): def fetch_certdata():
r = requests.get("https://api.github.com/repos/%s/git/refs/heads/master" % REPO) r = requests.get(f"https://api.github.com/repos/{REPO}/git/refs/heads/master")
assert r.status_code == 200 assert r.status_code == 200
commithash = r.json()["object"]["sha"] commithash = r.json()["object"]["sha"]
r = requests.get( r = requests.get(
"https://raw.githubusercontent.com/%s/%s/certifi/cacert.pem" f"https://raw.githubusercontent.com/{REPO}/{commithash}/certifi/cacert.pem"
% (REPO, commithash)
) )
assert r.status_code == 200 assert r.status_code == 200
certdata = r.text certdata = r.text
@ -52,23 +51,22 @@ def process_certdata(data):
def main(): def main():
commithash, certdata = fetch_certdata() commithash, certdata = fetch_certdata()
print("# fetched from https://github.com/%s" % REPO) print(f"# fetched from https://github.com/{REPO}")
print("# commit %s" % commithash) print(f"# commit {commithash}")
certs = process_certdata(certdata) certs = process_certdata(certdata)
size = sum([len(x) for x in certs.values()]) size = sum([len(x) for x in certs.values()])
print( print(
"# certs: %d | digests size: %d | total size: %d" f"# certs: {len(certs)} | digests size: {len(certs) * 32} | total size: {size}"
% (len(certs), len(certs) * 32, size)
) )
print("cert_bundle = [") print("cert_bundle = [")
for k, v in certs.items(): for k, v in certs.items():
h = sha256(v) h = sha256(v)
print(" # %s" % k) print(f" # {k}")
print(" # %s" % h.hexdigest()) print(f" # {h.hexdigest()}" % h.hexdigest())
print(" %s," % h.digest()) print(f" {h.digest()},")
print("]") print("]")

View File

@ -4,17 +4,17 @@
from __future__ import annotations from __future__ import annotations
import json
import unicodedata import unicodedata
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
import click import click
import json
# pip install freetype-py # pip install freetype-py
import freetype import freetype
from mako.template import Template
from foreign_chars import all_languages from foreign_chars import all_languages
from mako.template import Template
def _normalize(s: str) -> str: def _normalize(s: str) -> str:
@ -177,12 +177,12 @@ class Glyph:
else: else:
raise ValueError(f"Negative bearingX for character '{c}'") raise ValueError(f"Negative bearingX for character '{c}'")
bearingY = metrics.horiBearingY // 64 bearingY = metrics.horiBearingY // 64
assert advance >= 0 and advance <= 255 assert 0 <= advance <= 255
assert bearingX >= 0 and bearingX <= 255 assert 0 <= bearingX <= 255
if bearingY < 0: # HACK if bearingY < 0: # HACK
print(f"normalizing bearingY {bearingY} for '{c}'") print(f"normalizing bearingY {bearingY} for '{c}'")
bearingY = 0 bearingY = 0
assert bearingY >= 0 and bearingY <= 255 assert 0 <= bearingY <= 255
buf = list(bitmap.buffer) buf = list(bitmap.buffer)
# discard non-space pixels on the left side # discard non-space pixels on the left side

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import binascii import binascii
from trezorlib import _ed25519 from trezorlib import _ed25519

View File

@ -8,31 +8,33 @@ import math
def gen_loader(model, outer, inner): def gen_loader(model, outer, inner):
with open(f"loader_{model}.h", "wt") as f: with open(f"loader_{model}.h", "wt") as f:
f.write("// clang-format off\n") f.write("// clang-format off\n")
f.write("static const int img_loader_size = %d;\n" % outer) f.write(f"static const int img_loader_size = {outer};\n" % outer)
f.write("static const uint16_t img_loader[%d][%d] = {\n" % (outer, outer)) f.write(f"static const uint16_t img_loader[{outer}][{outer}] = {{\n")
for y in range(outer): for y in range(outer):
f.write(" {") f.write(" {")
for x in range(outer): for x in range(outer):
d = math.sqrt((outer - 1 - x) ** 2 + (outer - 1 - y) ** 2) d = math.sqrt((outer - 1 - x) ** 2 + (outer - 1 - y) ** 2)
c = {} c = {}
for i in [5, 15]: for i in [5, 15]:
if (inner - 0.5 <= d) and (d <= inner + 0.5): if inner - 0.5 <= d <= inner + 0.5:
c[i] = 15 * (d - inner + 0.5) c[i] = 15 * (d - inner + 0.5)
elif (inner + 0.5 <= d) and (d <= inner + 1.5): elif inner + 0.5 <= d <= inner + 1.5:
c[i] = 15 c[i] = 15
elif (inner + 1.5 <= d) and (d <= inner + 2.5): elif inner + 1.5 <= d <= inner + 2.5:
c[i] = 15 if i == 15 else 15 - (15 - i) * (d - inner - 1.5) c[i] = 15 if i == 15 else 15 - (15 - i) * (d - inner - 1.5)
elif (outer - 1.5 <= d) and (d <= outer - 0.5): elif outer - 1.5 <= d <= outer - 0.5:
c[i] = i - i * (d - outer + 1.5) c[i] = i - i * (d - outer + 1.5)
elif (inner + 2.5 < d) and (d < outer - 1.5): elif inner + 2.5 < d < outer - 1.5:
c[i] = i c[i] = i
else: else:
c[i] = 0 c[i] = 0
# clamp (should not be needed) # clamp (should not be needed)
c[i] = max(0, min(int(c[i]), 15)) c[i] = max(0, min(int(c[i]), 15))
a = int(math.atan2((outer - 1 - x), (outer - 1 - y)) * 2 * 249 / math.pi) a = int(
math.atan2((outer - 1 - x), (outer - 1 - y)) * 2 * 249 / math.pi
)
v = (a << 8) | (c[15] << 4) | c[5] v = (a << 8) | (c[15] << 4) | c[5]
f.write("%d," % v) f.write(f"{v},")
f.write("},\n") f.write("},\n")
f.write("};\n") f.write("};\n")
@ -40,4 +42,3 @@ def gen_loader(model, outer, inner):
if __name__ == "__main__": if __name__ == "__main__":
gen_loader("T", 60, 42) gen_loader("T", 60, 42)
gen_loader("R", 20, 14) gen_loader("R", 20, 14)

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import binascii import binascii
from trezorlib.client import TrezorClient from trezorlib.client import TrezorClient
from trezorlib.transport_hid import HidTransport from trezorlib.transport_hid import HidTransport
@ -11,7 +12,7 @@ else:
raise Exception("No Trezor found") raise Exception("No Trezor found")
for i in [0, 1, 2]: for i in [0, 1, 2]:
path = "m/10018'/%d'" % i path = f"m/10018'/{i}'"
pk = t.get_public_node( pk = t.get_public_node(
t.expand_path(path), ecdsa_curve_name="ed25519", show_display=True t.expand_path(path), ecdsa_curve_name="ed25519", show_display=True
) )

View File

@ -1,10 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json import json
import sys import sys
from collections import defaultdict from collections import defaultdict
# Aggregate hits from all coverage JSON files # Aggregate hits from all coverage JSON files
data = defaultdict(lambda: defaultdict(int)) data = defaultdict(lambda: defaultdict(int))
for file_path in sys.argv[1:]: for file_path in sys.argv[1:]:

View File

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import coverage
import json import json
import sys import sys
import coverage
result_filename, *coverage_filenames = sys.argv[1:] result_filename, *coverage_filenames = sys.argv[1:]
data = coverage.CoverageData(result_filename) data = coverage.CoverageData(result_filename)

View File

@ -76,15 +76,13 @@ class HIDInterface:
def write_data(self, data): def write_data(self, data):
buf = uhid.create_input2_event(data) buf = uhid.create_input2_event(data)
self.__uhid_write(buf) self.__uhid_write(buf)
logger.log_uhid_event( logger.log_uhid_event("UHID_INPUT2", f"data=0x{data.hex()} size={len(data)}")
"UHID_INPUT2", f"data=0x{data.hex()} size={len(data)}"
)
logger.log_hid_packet("DEVICE_OUTPUT", f"0x{data.hex()}") logger.log_hid_packet("DEVICE_OUTPUT", f"0x{data.hex()}")
def process_event(self): def process_event(self):
ev_type, request = uhid.parse_event(self.__uhid_read(uhid.EVENT_LENGTH)) ev_type, request = uhid.parse_event(self.__uhid_read(uhid.EVENT_LENGTH))
if ev_type == uhid.EVENT_TYPE_START: if ev_type == uhid.EVENT_TYPE_START:
dev_flags, = request (dev_flags,) = request
logger.log_uhid_event("UHID_START", f"dev_flags=0b{dev_flags:08b}") logger.log_uhid_event("UHID_START", f"dev_flags=0b{dev_flags:08b}")
elif ev_type == uhid.EVENT_TYPE_STOP: elif ev_type == uhid.EVENT_TYPE_STOP:
logger.log_uhid_event("UHID_STOP") logger.log_uhid_event("UHID_STOP")
@ -105,3 +103,4 @@ class HIDInterface:
"UNKNOWN_EVENT", "UNKNOWN_EVENT",
f"ev_type={ev_type} request=0x{request.hex()}", f"ev_type={ev_type} request=0x{request.hex()}",
) )
return None

View File

@ -9,7 +9,7 @@ def __get_timestamp():
def __log_message(message): def __log_message(message):
if log_timestamps == True: if log_timestamps:
print(f"{__get_timestamp()}\t{message}") print(f"{__get_timestamp()}\t{message}")
else: else:
print(message) print(message)

View File

@ -2,9 +2,10 @@
Creates a header file containing image data. Creates a header file containing image data.
""" """
import click
import pathlib import pathlib
import click
h_file_template = """\ h_file_template = """\
// clang-format off // clang-format off
unsigned char {name}_jpg[] = {content}; unsigned char {name}_jpg[] = {content};
@ -40,7 +41,13 @@ def convert(infile):
image_bytes = image_bytes.rstrip(", \n") image_bytes = image_bytes.rstrip(", \n")
with open(h_file_name, "w") as f: with open(h_file_name, "w") as f:
f.write(h_file_template.format(name=img_name, content=content.format(image_bytes=image_bytes), length=len(image_data))) f.write(
h_file_template.format(
name=img_name,
content=content.format(image_bytes=image_bytes),
length=len(image_data),
)
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -4,7 +4,7 @@ import argparse
def gen(sources, dirs, defs): def gen(sources, dirs, defs):
target = "CMakeLists.txt" target = "CMakeLists.txt"
with open(target, 'w') as f: with open(target, "w") as f:
f.write("cmake_minimum_required(VERSION 3.20)\n") f.write("cmake_minimum_required(VERSION 3.20)\n")
f.write("project(core)\n") f.write("project(core)\n")
@ -16,20 +16,20 @@ def gen(sources, dirs, defs):
f.write("add_definitions(\n") f.write("add_definitions(\n")
for d in defs: for d in defs:
f.write(f' -D{d}\n') f.write(f" -D{d}\n")
f.write(")\n") f.write(")\n")
f.write("\n") f.write("\n")
f.write("\n") f.write("\n")
for d in dirs: for d in dirs:
f.write(f'include_directories({d})\n') f.write(f"include_directories({d})\n")
f.write("\n") f.write("\n")
f.write("\n") f.write("\n")
f.write("add_executable(core\n") f.write("add_executable(core\n")
for s in sources: for s in sources:
f.write(f' {s}\n') f.write(f" {s}\n")
f.write(")\n") f.write(")\n")
f.write("\n") f.write("\n")

View File

@ -1,11 +1,12 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
import time import time
from pathlib import Path from pathlib import Path
import sys
import click import click
import serial import serial
def send_cmd(ser, cmd, expect_ok=True): def send_cmd(ser, cmd, expect_ok=True):
"""Send a line, read response, and abort on non-OK.""" """Send a line, read response, and abort on non-OK."""
ser.write((cmd + "\r\n").encode()) ser.write((cmd + "\r\n").encode())
@ -22,6 +23,7 @@ def send_cmd(ser, cmd, expect_ok=True):
sys.exit(1) sys.exit(1)
return resp return resp
def upload_nrf(port, bin_path, chunk_size): def upload_nrf(port, bin_path, chunk_size):
# Read binary file # Read binary file
data = Path(bin_path).read_bytes() data = Path(bin_path).read_bytes()
@ -40,7 +42,7 @@ def upload_nrf(port, bin_path, chunk_size):
# 2) Stream chunks # 2) Stream chunks
offset = 0 offset = 0
while offset < total: while offset < total:
chunk = data[offset:offset + chunk_size] chunk = data[offset : offset + chunk_size]
hexstr = chunk.hex() hexstr = chunk.hex()
send_cmd(ser, f"nrf-update chunk {hexstr}") send_cmd(ser, f"nrf-update chunk {hexstr}")
offset += len(chunk) offset += len(chunk)
@ -51,11 +53,15 @@ def upload_nrf(port, bin_path, chunk_size):
send_cmd(ser, "nrf-update end") send_cmd(ser, "nrf-update end")
click.echo("nRF update complete.") click.echo("nRF update complete.")
@click.command(context_settings={"help_option_names": ["-h", "--help"]}) @click.command(context_settings={"help_option_names": ["-h", "--help"]})
@click.argument("port", metavar="<serial-port>") @click.argument("port", metavar="<serial-port>")
@click.argument("binary", metavar="<nrf-binary>", type=click.Path(exists=True, dir_okay=False)) @click.argument(
@click.option("--chunk-size", "-c", default=512, show_default=True, "binary", metavar="<nrf-binary>", type=click.Path(exists=True, dir_okay=False)
help="Max bytes per chunk") )
@click.option(
"--chunk-size", "-c", default=512, show_default=True, help="Max bytes per chunk"
)
def main(port, binary, chunk_size): def main(port, binary, chunk_size):
""" """
Upload an nRF firmware image via USB-VCP CLI. Upload an nRF firmware image via USB-VCP CLI.
@ -72,5 +78,6 @@ def main(port, binary, chunk_size):
click.echo("Interrupted by user.", err=True) click.echo("Interrupted by user.", err=True)
sys.exit(1) sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -2,14 +2,14 @@ from __future__ import annotations
import os import os
import secrets import secrets
import shlex
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
from typing_extensions import Self
import click import click
import requests import requests
import serial import serial
import shlex from typing_extensions import Self
SERVER_TOKEN = "" SERVER_TOKEN = ""
SERVER_URL = "http://localhost:8000/provision" SERVER_URL = "http://localhost:8000/provision"
@ -114,9 +114,9 @@ class Connection:
if res.startswith(b"ERROR"): if res.startswith(b"ERROR"):
error_args = res[len(b"ERROR ") :].decode() error_args = res[len(b"ERROR ") :].decode()
parts = shlex.split(error_args) parts = shlex.split(error_args)
error_text = parts[0] # error code error_text = parts[0] # error code
if len(parts) > 1: if len(parts) > 1:
error_text = parts[1] # error description error_text = parts[1] # error description
raise ProdtestException(error_text) raise ProdtestException(error_text)
elif res.startswith(b"OK"): elif res.startswith(b"OK"):
res_arg = res[len(b"OK ") :] res_arg = res[len(b"OK ") :]
@ -129,6 +129,7 @@ class Connection:
elif not res.startswith(b"#"): elif not res.startswith(b"#"):
raise ProdtestException("Unexpected response: " + res.decode()) raise ProdtestException("Unexpected response: " + res.decode())
def provision_request( def provision_request(
device: DeviceInfo, url: str, model: str, verify: bool = True device: DeviceInfo, url: str, model: str, verify: bool = True
) -> ProvisioningResult: ) -> ProvisioningResult:
@ -140,7 +141,7 @@ def provision_request(
"cert": device.device_cert.hex(), "cert": device.device_cert.hex(),
"model": model, "model": model,
} }
resp = requests.post(url + '/provision', json=request, verify=verify) resp = requests.post(url + "/provision", json=request, verify=verify)
if resp.status_code == 400: if resp.status_code == 400:
print("Server returned error:", resp.text) print("Server returned error:", resp.text)
resp.raise_for_status() resp.raise_for_status()

View File

@ -9,8 +9,7 @@ import re
import sys import sys
from pathlib import Path from pathlib import Path
from binsize import BinarySize, DataRow, StatisticsPlugin
from binsize import BinarySize, StatisticsPlugin, DataRow
HERE = Path(__file__).parent HERE = Path(__file__).parent
CORE_DIR = HERE.parent.parent CORE_DIR = HERE.parent.parent

View File

@ -18,10 +18,9 @@ from io import BytesIO
from pathlib import Path from pathlib import Path
from zipfile import ZipFile from zipfile import ZipFile
import requests
import click import click
import requests
from binsize import BinarySize, get_sections_sizes, show_binaries_diff, set_root_dir from binsize import BinarySize, get_sections_sizes, set_root_dir, show_binaries_diff
HERE = Path(__file__).parent HERE = Path(__file__).parent
CORE_DIR = HERE.parent.parent CORE_DIR = HERE.parent.parent

View File

@ -12,31 +12,31 @@ CORE_DIR = HERE.parent.parent
def process_line(infile: TextIO, outfile: BinaryIO) -> None: def process_line(infile: TextIO, outfile: BinaryIO) -> None:
line = infile.readline() line = infile.readline()
data = [x.strip().lower() for x in line.split(',')] data = [x.strip().lower() for x in line.split(",")]
for c in data: for c in data:
if len(c) == 4: if len(c) == 4:
outfile.write(bytes((int(c, 16),))) outfile.write(bytes((int(c, 16),)))
def header_to_toif(path: str | Path) -> str: def header_to_toif(path: str | Path) -> str:
with open(path, "r") as infile, open('tmp.toif', "wb") as outfile: with open(path, "r") as infile, open("tmp.toif", "wb") as outfile:
infile.readline() infile.readline()
name_line = infile.readline() name_line = infile.readline()
name = name_line.split(" ")[3].split("[")[0] name = name_line.split(" ")[3].split("[")[0]
infile.readline() infile.readline()
magic_line = infile.readline().split(',')[3] magic_line = infile.readline().split(",")[3]
outfile.write(bytes((0x54,))) outfile.write(bytes((0x54,)))
outfile.write(bytes((0x4f,))) outfile.write(bytes((0x4F,)))
outfile.write(bytes((0x49,))) outfile.write(bytes((0x49,)))
if "g" in magic_line: if "g" in magic_line:
outfile.write(bytes((ord('g'),))) outfile.write(bytes((ord("g"),)))
elif "G" in magic_line: elif "G" in magic_line:
outfile.write(bytes((ord('G'),))) outfile.write(bytes((ord("G"),)))
elif "f" in magic_line: elif "f" in magic_line:
outfile.write(bytes((ord('f'),))) outfile.write(bytes((ord("f"),)))
elif "F" in magic_line: elif "F" in magic_line:
outfile.write(bytes((ord('F'),))) outfile.write(bytes((ord("F"),)))
else: else:
print(magic_line) print(magic_line)
raise Exception("Unknown format") raise Exception("Unknown format")
@ -52,53 +52,71 @@ def header_to_toif(path: str | Path) -> str:
def toif_to_header(path: str | Path, name: str) -> None: def toif_to_header(path: str | Path, name: str) -> None:
with open('tmp_c.toif', "rb") as infile, open(path, "w") as outfile: with open("tmp_c.toif", "rb") as infile, open(path, "w") as outfile:
b = infile.read(4) b = infile.read(4)
outfile.write("// clang-format off\n") outfile.write("// clang-format off\n")
outfile.write(f'static const uint8_t {name}[] = {{\n',) outfile.write(
outfile.write(" // magic\n",) f"static const uint8_t {name}[] = {{\n",
if b[3] == ord('f'): )
outfile.write(" 'T', 'O', 'I', 'f',\n",) outfile.write(
elif b[3] == ord('F'): " // magic\n",
outfile.write(" 'T', 'O', 'I', 'F',\n",) )
elif b[3] == ord('g'): if b[3] == ord("f"):
outfile.write(" 'T', 'O', 'I', 'g',\n",) outfile.write(
elif b[3] == ord('G'): " 'T', 'O', 'I', 'f',\n",
outfile.write(" 'T', 'O', 'I', 'G',\n",) )
elif b[3] == ord("F"):
outfile.write(
" 'T', 'O', 'I', 'F',\n",
)
elif b[3] == ord("g"):
outfile.write(
" 'T', 'O', 'I', 'g',\n",
)
elif b[3] == ord("G"):
outfile.write(
" 'T', 'O', 'I', 'G',\n",
)
else: else:
raise Exception("Unknown format") raise Exception("Unknown format")
outfile.write(" // width (16-bit), height (16-bit)\n",) outfile.write(
" // width (16-bit), height (16-bit)\n",
)
outfile.write(" ") outfile.write(" ")
for i in range(4): for i in range(4):
hex_data = infile.read(1).hex() hex_data = infile.read(1).hex()
outfile.write(f'0x{hex_data},') outfile.write(f"0x{hex_data},")
if i != 3: if i != 3:
outfile.write(' ') outfile.write(" ")
outfile.write("\n") outfile.write("\n")
outfile.write(" // compressed data length (32-bit)\n",) outfile.write(
" // compressed data length (32-bit)\n",
)
outfile.write(" ") outfile.write(" ")
for i in range(4): for i in range(4):
hex_data = infile.read(1).hex() hex_data = infile.read(1).hex()
outfile.write(f'0x{hex_data},') outfile.write(f"0x{hex_data},")
if i != 3: if i != 3:
outfile.write(' ') outfile.write(" ")
outfile.write("\n") outfile.write("\n")
outfile.write(" // compressed data\n",) outfile.write(
" // compressed data\n",
)
outfile.write(" ") outfile.write(" ")
hex_data = infile.read(1).hex() hex_data = infile.read(1).hex()
first = True first = True
while hex_data: while hex_data:
if not first: if not first:
outfile.write(' ') outfile.write(" ")
first = False first = False
outfile.write(f'0x{hex_data},') outfile.write(f"0x{hex_data},")
hex_data = infile.read(1).hex() hex_data = infile.read(1).hex()
outfile.write("\n};\n") outfile.write("\n};\n")
_byte = infile.read(1) infile.read(1)
def reformat_c_icon(path: str | Path) -> None: def reformat_c_icon(path: str | Path) -> None:

View File

@ -1,6 +1,7 @@
import io import io
import sys import sys
from pathlib import Path from pathlib import Path
from trezorlib._internal import firmware_headers from trezorlib._internal import firmware_headers
from trezorlib.firmware.core import FirmwareHeader, HeaderType from trezorlib.firmware.core import FirmwareHeader, HeaderType

View File

@ -1,8 +1,8 @@
from __future__ import annotations from __future__ import annotations
import json import json
from dataclasses import dataclass
import sys import sys
from dataclasses import dataclass
from helpers import HERE, TRANSLATIONS_DIR from helpers import HERE, TRANSLATIONS_DIR

View File

@ -1,15 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from hashlib import blake2s
from pathlib import Path
import click import click
from pathlib import Path
from hashlib import blake2s
from trezorlib.firmware.core import FirmwareImage, Model from trezorlib.firmware.core import FirmwareImage, Model
from .layout_parser import find_value
from .common import MODELS_DIR from .common import MODELS_DIR
from .layout_parser import find_value
TEMPLATE = """\ TEMPLATE = """\
#ifndef BOOTLOADER_HASHES_H #ifndef BOOTLOADER_HASHES_H

View File

@ -7,15 +7,16 @@ CORE = HERE.parent.parent
MODELS_DIR = CORE / "embed" / "models" MODELS_DIR = CORE / "embed" / "models"
def get_layout_for_model(model: str, secmon: bool) -> Path: def get_layout_for_model(model: str, secmon: bool) -> Path:
if secmon: if secmon:
return MODELS_DIR / model / f"memory_secmon.h" return MODELS_DIR / model / "memory_secmon.h"
else: else:
return MODELS_DIR / model / f"memory.h" return MODELS_DIR / model / "memory.h"
def get_linkerscript_for_model(model: str, secmon: bool) -> Path: def get_linkerscript_for_model(model: str, secmon: bool) -> Path:
if secmon: if secmon:
return MODELS_DIR / model / f"memory_secmon.ld" return MODELS_DIR / model / "memory_secmon.ld"
else: else:
return MODELS_DIR / model / f"memory.ld" return MODELS_DIR / model / "memory.ld"

View File

@ -1,11 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from typing import List, Tuple
import click import click
from trezorlib import cosi, firmware from trezorlib import cosi, firmware
from trezorlib._internal import firmware_headers from trezorlib._internal import firmware_headers
from typing import List, Sequence, Tuple
# =========================== signing ========================= # =========================== signing =========================
@ -47,6 +47,11 @@ def do_replace_vendorheader(fw, vh_file) -> None:
fw.vendor_header = vh fw.vendor_header = vh
def no_echo(*args, **kwargs):
"""A no-op function to replace click.echo when quiet mode is enabled."""
pass
@click.command() @click.command()
@click.option("-n", "--dry-run", is_flag=True, help="Do not save changes.") @click.option("-n", "--dry-run", is_flag=True, help="Do not save changes.")
@click.option("-h", "--rehash", is_flag=True, help="Force recalculate hashes.") @click.option("-h", "--rehash", is_flag=True, help="Force recalculate hashes.")
@ -132,7 +137,7 @@ def cli(
traceback.print_exc() traceback.print_exc()
magic = firmware_data[:4] magic = firmware_data[:4]
raise click.ClickException( raise click.ClickException(
"Could not parse file (magic bytes: {!r})".format(magic) f"Could not parse file (magic bytes: {magic})"
) from e ) from e
digest = fw.digest() digest = fw.digest()
@ -141,7 +146,7 @@ def cli(
return return
if quiet: if quiet:
echo = lambda *args, **kwargs: None echo = no_echo
else: else:
echo = click.echo echo = click.echo

View File

@ -1,14 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import annotations from __future__ import annotations
import ast
import re import re
import click import click
import ast
from .common import get_layout_for_model from .common import get_layout_for_model
# the following regular expression matches a thing looking like those examples: # the following regular expression matches a thing looking like those examples:
# #define HEADER_START 0x123456 # #define HEADER_START 0x123456
# #define HEADER_START (1 * 2 * 3) // comment # #define HEADER_START (1 * 2 * 3) // comment
@ -48,7 +47,7 @@ def find_value(model: str, name: str, secmon: bool) -> int:
@click.argument("model") @click.argument("model")
@click.argument("name") @click.argument("name")
@click.option("--secmon", is_flag=True) @click.option("--secmon", is_flag=True)
def main(model: str, name: str, secmon:bool) -> None: def main(model: str, name: str, secmon: bool) -> None:
try: try:
print(find_value(model, name, secmon)) print(find_value(model, name, secmon))
except ValueError as e: except ValueError as e:

View File

@ -1,22 +1,23 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import annotations from __future__ import annotations
import click
import itertools import itertools
from .common import get_linkerscript_for_model, get_layout_for_model, MODELS_DIR import click
from .layout_parser import find_all_values
from .common import MODELS_DIR, get_layout_for_model, get_linkerscript_for_model
from .layout_parser import find_all_values
FILE_HEADER = """/* Auto-generated file, do not edit.*/ FILE_HEADER = """/* Auto-generated file, do not edit.*/
""" """
def create_linker_script(model: str, cmse: bool) -> str: def create_linker_script(model: str, cmse: bool) -> str:
content = FILE_HEADER content = FILE_HEADER
defines = find_all_values(model, cmse) defines = find_all_values(model, cmse)
for name, value in defines.items(): for name, value in defines.items():
content += f"{name} = {hex(value)};\n" content += f"{name} = {hex(value)};\n"
return content return content
@ -31,7 +32,7 @@ def main(check: bool) -> None:
path = get_layout_for_model(model.name, split) path = get_layout_for_model(model.name, split)
if not path.exists(): if not path.exists():
continue continue
path = get_linkerscript_for_model(model.name, split) path = get_linkerscript_for_model(model.name, split)
print(f"Processing {path}") print(f"Processing {path}")
@ -45,5 +46,6 @@ def main(check: bool) -> None:
else: else:
path.write_text(new_content) path.write_text(new_content)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -7,7 +7,7 @@ from pathlib import Path
import click import click
from mako.template import Template from mako.template import Template
from munch import munchify, Munch from munch import Munch, munchify
HERE = Path(__file__).parent HERE = Path(__file__).parent
ROOT = HERE.parent.resolve() ROOT = HERE.parent.resolve()

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from pathlib import Path
import re import re
import subprocess import subprocess
from pathlib import Path
import click import click
@ -40,7 +40,7 @@ def bump_python(filename, new_version):
def hex_lit(version): def hex_lit(version):
return r'"\x{:02X}"'.format(int(version)) return rf'"\x{int(version):02X}"'
@click.command() @click.command()

View File

@ -1,14 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import datetime import datetime
from pathlib import Path
import re import re
import subprocess import subprocess
from pathlib import Path
from typing import Iterator
import click import click
from typing import Iterator
LINK_RE = re.compile(r"\[#(\d+)\]") LINK_RE = re.compile(r"\[#(\d+)\]")
ISSUE_URL = "https://github.com/trezor/trezor-firmware/pull/{issue}" ISSUE_URL = "https://github.com/trezor/trezor-firmware/pull/{issue}"
@ -265,7 +264,7 @@ def generate(project, version, date, check, only_models):
if only_models: if only_models:
generate_filtered(project, changelog) generate_filtered(project, changelog)
return 0 return
args = ["towncrier", "build", "--yes", "--version", version, "--date", date] args = ["towncrier", "build", "--yes", "--version", version, "--date", date]
if check: if check:

View File

@ -19,7 +19,7 @@ elif len(sys.argv) == 2:
filenames = (FILE_T1, FILE_T2) filenames = (FILE_T1, FILE_T2)
else: else:
print(f"Usage: {sys.argv[0]} HEX_CHALLENGE [FILE]...") print(f"Usage: {sys.argv[0]} HEX_CHALLENGE [FILE]...")
print(f" HEX_CHALLENGE: a 0-32 byte challenge in hexadecimal") print(" HEX_CHALLENGE: a 0-32 byte challenge in hexadecimal")
exit(1) exit(1)

View File

@ -3,11 +3,13 @@ Taken from https://gist.github.com/patrickfuller/e2ea8a94badc5b6967ef3ca0a9452a
Currently writes all issues that have some Weight. Currently writes all issues that have some Weight.
""" """
import argparse import argparse
import csv import csv
import requests
import os.path import os.path
import requests
token = None token = None
path = os.path.dirname(os.path.realpath(__file__)) path = os.path.dirname(os.path.realpath(__file__))
@ -49,7 +51,6 @@ def write_issues(r, csvout):
if not weight: if not weight:
continue continue
labels = ", ".join(labels) labels = ", ".join(labels)
date = issue["created_at"].split("T")[0]
milestone = issue["milestone"]["title"] if issue["milestone"] else "" milestone = issue["milestone"]["title"] if issue["milestone"] else ""
assignee = issue["assignee"]["login"] if issue["assignee"] else "" assignee = issue["assignee"]["login"] if issue["assignee"] else ""

View File

@ -4,9 +4,8 @@ import subprocess
from pathlib import Path from pathlib import Path
import click import click
from elftools.construct import Struct, ULInt32, GreedyRange
from elftools.common.construct_utils import ULEB128 from elftools.common.construct_utils import ULEB128
from elftools.construct import GreedyRange, Struct, ULInt32
from elftools.elf.elffile import ELFFile from elftools.elf.elffile import ELFFile
SYMBOL_TYPES = ("t", "w") # text, weak SYMBOL_TYPES = ("t", "w") # text, weak

View File

@ -49,13 +49,13 @@ from pathlib import Path
from typing import Dict # for python38 support, must be used in type aliases from typing import Dict # for python38 support, must be used in type aliases
from typing import List # for python38 support, must be used in type aliases from typing import List # for python38 support, must be used in type aliases
from typing import TYPE_CHECKING, Any, Iterator from typing import TYPE_CHECKING, Any, Iterator
import click
from typing_extensions import ( # for python37 support, is not present in typing there from typing_extensions import ( # for python37 support, is not present in typing there
Final, Final,
TypedDict, TypedDict,
) )
import click
if TYPE_CHECKING: if TYPE_CHECKING:
LineIgnores = List["LineIgnore"] LineIgnores = List["LineIgnore"]
@ -542,7 +542,7 @@ class PyrightTool:
ignore_statements = self.get_ignore_statements(line) ignore_statements = self.get_ignore_statements(line)
if not ignore_statements: if not ignore_statements:
self.inconsistencies.append( self.inconsistencies.append(
f"There is an empty `{self.IGNORE_PATTERN}` in {file}:{index+1}" f"There is an empty `{self.IGNORE_PATTERN}` in {file}:{index + 1}"
) )
else: else:
ignores.append(LineIgnore(index, ignore_statements)) ignores.append(LineIgnore(index, ignore_statements))

View File

@ -17,11 +17,12 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>. # If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from __future__ import annotations from __future__ import annotations
import click
import requests
import zipfile import zipfile
from pathlib import Path from pathlib import Path
import click
import requests
from trezorlib import definitions, merkle_tree from trezorlib import definitions, merkle_tree
ZIP_FILENAME = "definitions-sparse.zip" ZIP_FILENAME = "definitions-sparse.zip"

View File

@ -4,9 +4,9 @@
from __future__ import annotations from __future__ import annotations
from typing_extensions import Literal
from typing import Tuple from typing import Tuple
from typing_extensions import Literal
Bit = Literal[0, 1] Bit = Literal[0, 1]
Point = Tuple[int, int] Point = Tuple[int, int]

View File

@ -68,11 +68,10 @@ def check_usage_of_functions(func_mapping: Dict[str, List[str]]) -> None:
is_used_mappings[func_name] = {"mapping": mapping, "is_used": False} is_used_mappings[func_name] = {"mapping": mapping, "is_used": False}
# Check if any of the mapping names is used - and mark it as used if so # Check if any of the mapping names is used - and mark it as used if so
for func_name in is_used_mappings: for func_name, info in is_used_mappings.items():
for mapping in is_used_mappings[func_name]["mapping"]: for m in info["mapping"]:
is_there = _is_used(mapping) if _is_used(m):
if is_there: info["is_used"] = True
is_used_mappings[func_name]["is_used"] = True
break break
# Find unused functions and generate a report # Find unused functions and generate a report

View File

@ -1,12 +1,12 @@
import sys import sys
from pathlib import Path from pathlib import Path
from pyasn1.codec.der.encoder import encode
from pyasn1.codec.der.decoder import decode
from pyasn1.type.univ import BitString
from pyasn1_modules import rfc2459
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import ec
from pyasn1.codec.der.decoder import decode
from pyasn1.codec.der.encoder import encode
from pyasn1.type.univ import BitString
from pyasn1_modules import rfc2459
def load_certificate(data: bytes): def load_certificate(data: bytes):

View File

@ -37,6 +37,7 @@ Usage:
- specifying FILES_TO_MODIFY - specifying FILES_TO_MODIFY
- call the script with possible flags - see `python unify_test_files.py --help` - call the script with possible flags - see `python unify_test_files.py --help`
""" """
import json import json
import os import os
import re import re
@ -92,7 +93,7 @@ class FileUnifier:
else: else:
self.translations = {} self.translations = {}
print( print(
f"{len(self.translations)} translations available (path/address and address/path)\n{80*'*'}" f"{len(self.translations)} translations available (path/address and address/path)\n{80 * '*'}"
) )
# For statistical purposes # For statistical purposes

View File

@ -2,3 +2,4 @@
^legacy/firmware/protob/options_pb2\.py ^legacy/firmware/protob/options_pb2\.py
^legacy/firmware/protob/messages_nem_pb2\.py ^legacy/firmware/protob/messages_nem_pb2\.py
^legacy/vendor ^legacy/vendor
^tools/automatic_battery_tester/.venv/

View File

@ -2,8 +2,10 @@
^core/src/ ^core/src/
^core/site_scons/ ^core/site_scons/
^core/tests/ ^core/tests/
^core/tools/
^crypto/ ^crypto/
^legacy/ ^legacy/
^storage/ ^storage/
^tools/
^tests/ ^tests/
^ci/ ^ci/

View File

@ -0,0 +1,2 @@
^core/tools/
^tools/