refactor(python): move out TOIF related structs to trezorlib.toif

pull/2597/head
matejcik 2 years ago committed by matejcik
parent 6905f9c486
commit a7d6e194a1

@ -18,7 +18,7 @@ from typing import TYPE_CHECKING, Optional, cast
import click import click
from .. import device, firmware, messages, toif from .. import device, messages, toif
from . import AliasedGroup, ChoiceType, with_client from . import AliasedGroup, ChoiceType, with_client
if TYPE_CHECKING: if TYPE_CHECKING:
@ -83,7 +83,7 @@ def image_to_tt(filename: str) -> bytes:
if toif_image.size != (144, 144): if toif_image.size != (144, 144):
raise click.ClickException("Wrong size of image - should be 144x144") raise click.ClickException("Wrong size of image - should be 144x144")
if toif_image.mode != firmware.ToifMode.full_color: if toif_image.mode != toif.ToifMode.full_color:
raise click.ClickException("Wrong image mode - should be full_color") raise click.ClickException("Wrong image mode - should be full_color")
return toif_image.to_bytes() return toif_image.to_bytes()

@ -23,7 +23,8 @@ import construct as c
import ecdsa import ecdsa
from . import cosi, messages from . import cosi, messages
from .tools import expect, session from .toif import ToifStruct
from .tools import expect, session, EnumAdapter
if TYPE_CHECKING: if TYPE_CHECKING:
from .client import TrezorClient from .client import TrezorClient
@ -98,43 +99,12 @@ class Unsigned(FirmwareIntegrityError):
pass pass
class ToifMode(Enum):
full_color = b"f" # big endian
grayscale = b"g" # odd hi
full_color_le = b"F" # little endian
grayscale_eh = b"G" # even hi
class HeaderType(Enum): class HeaderType(Enum):
FIRMWARE = b"TRZF" FIRMWARE = b"TRZF"
BOOTLOADER = b"TRZB" BOOTLOADER = b"TRZB"
class EnumAdapter(c.Adapter):
def __init__(self, subcon: Any, enum: Any) -> None:
self.enum = enum
super().__init__(subcon)
def _encode(self, obj: Any, ctx: Any, path: Any):
return obj.value
def _decode(self, obj: Any, ctx: Any, path: Any):
try:
return self.enum(obj)
except ValueError:
return obj
# fmt: off # fmt: off
Toif = c.Struct(
"magic" / c.Const(b"TOI"),
"format" / EnumAdapter(c.Bytes(1), ToifMode),
"width" / c.Int16ul,
"height" / c.Int16ul,
"data" / c.Prefixed(c.Int32ul, c.GreedyBytes),
)
VendorTrust = c.Transformed(c.BitStruct( VendorTrust = c.Transformed(c.BitStruct(
"_reserved" / c.Default(c.BitsInteger(9), 0), "_reserved" / c.Default(c.BitsInteger(9), 0),
"show_vendor_string" / c.Flag, "show_vendor_string" / c.Flag,
@ -159,7 +129,7 @@ VendorHeader = c.Struct(
"_reserved" / c.Padding(14), "_reserved" / c.Padding(14),
"pubkeys" / c.Bytes(32)[c.this.sig_n], "pubkeys" / c.Bytes(32)[c.this.sig_n],
"text" / c.Aligned(4, c.PascalString(c.Int8ul, "utf-8")), "text" / c.Aligned(4, c.PascalString(c.Int8ul, "utf-8")),
"image" / Toif, "image" / ToifStruct,
"_end_offset" / c.Tell, "_end_offset" / c.Tell,
"_min_header_len" / c.Check(c.this.header_len > (c.this._end_offset - c.this._start_offset) + 65), "_min_header_len" / c.Check(c.this.header_len > (c.this._end_offset - c.this._start_offset) + 65),

@ -17,11 +17,13 @@
import struct import struct
import zlib import zlib
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum
from typing import Sequence, Tuple from typing import Sequence, Tuple
import construct as c
from typing_extensions import Literal from typing_extensions import Literal
from . import firmware from .tools import EnumAdapter
try: try:
# Explanation of having to use "Image.Image" in typing: # Explanation of having to use "Image.Image" in typing:
@ -36,6 +38,22 @@ except ImportError:
RGBPixel = Tuple[int, int, int] RGBPixel = Tuple[int, int, int]
class ToifMode(Enum):
full_color = b"f" # big endian
grayscale = b"g" # odd hi
full_color_le = b"F" # little endian
grayscale_eh = b"G" # even hi
ToifStruct = c.Struct(
"magic" / c.Const(b"TOI"),
"format" / EnumAdapter(c.Bytes(1), ToifMode),
"width" / c.Int16ul,
"height" / c.Int16ul,
"data" / c.Prefixed(c.Int32ul, c.GreedyBytes),
)
def _compress(data: bytes) -> bytes: def _compress(data: bytes) -> bytes:
z = zlib.compressobj(level=9, wbits=-10) z = zlib.compressobj(level=9, wbits=-10)
return z.compress(data) + z.flush() return z.compress(data) + z.flush()
@ -113,17 +131,14 @@ def _to_grayscale(data: bytes, right_hi: bool) -> bytes:
@dataclass @dataclass
class Toif: class Toif:
mode: firmware.ToifMode mode: ToifMode
size: Tuple[int, int] size: Tuple[int, int]
data: bytes data: bytes
def __post_init__(self) -> None: def __post_init__(self) -> None:
# checking the data size # checking the data size
width, height = self.size width, height = self.size
if ( if self.mode is ToifMode.grayscale or self.mode is ToifMode.grayscale_eh:
self.mode is firmware.ToifMode.grayscale
or self.mode is firmware.ToifMode.grayscale_eh
):
expected_size = width * height // 2 expected_size = width * height // 2
else: else:
expected_size = width * height * 2 expected_size = width * height * 2
@ -142,16 +157,16 @@ class Toif:
uncompressed = _decompress(self.data) uncompressed = _decompress(self.data)
pil_mode: Literal["L", "RGB"] pil_mode: Literal["L", "RGB"]
if self.mode is firmware.ToifMode.grayscale: if self.mode is ToifMode.grayscale:
pil_mode = "L" pil_mode = "L"
raw_data = _to_grayscale(uncompressed, right_hi=False) raw_data = _to_grayscale(uncompressed, right_hi=False)
elif self.mode is firmware.ToifMode.grayscale_eh: elif self.mode is ToifMode.grayscale_eh:
pil_mode = "L" pil_mode = "L"
raw_data = _to_grayscale(uncompressed, right_hi=True) raw_data = _to_grayscale(uncompressed, right_hi=True)
elif self.mode is firmware.ToifMode.full_color: elif self.mode is ToifMode.full_color:
pil_mode = "RGB" pil_mode = "RGB"
raw_data = _to_rgb(uncompressed, little_endian=False) raw_data = _to_rgb(uncompressed, little_endian=False)
else: # self.mode is firmware.ToifMode.full_color_le: else: # self.mode is ToifMode.full_color_le:
pil_mode = "RGB" pil_mode = "RGB"
raw_data = _to_rgb(uncompressed, little_endian=True) raw_data = _to_rgb(uncompressed, little_endian=True)
@ -159,7 +174,7 @@ class Toif:
def to_bytes(self) -> bytes: def to_bytes(self) -> bytes:
width, height = self.size width, height = self.size
return firmware.Toif.build( return ToifStruct.build(
dict(format=self.mode, width=width, height=height, data=self.data) dict(format=self.mode, width=width, height=height, data=self.data)
) )
@ -169,7 +184,10 @@ class Toif:
def from_bytes(data: bytes) -> Toif: def from_bytes(data: bytes) -> Toif:
parsed = firmware.Toif.parse(data) return from_struct(ToifStruct.parse(data))
def from_struct(parsed: c.Container) -> Toif:
return Toif(parsed.format, (parsed.width, parsed.height), parsed.data) return Toif(parsed.format, (parsed.width, parsed.height), parsed.data)
@ -200,27 +218,27 @@ def from_image(
if image.size[0] % 2 != 0: if image.size[0] % 2 != 0:
raise ValueError("Only even-width grayscale images are supported") raise ValueError("Only even-width grayscale images are supported")
if not legacy_format: if not legacy_format:
toif_mode = firmware.ToifMode.grayscale_eh toif_mode = ToifMode.grayscale_eh
toif_data = _from_pil_grayscale(image.getdata(), right_hi=True) toif_data = _from_pil_grayscale(image.getdata(), right_hi=True)
else: else:
toif_mode = firmware.ToifMode.grayscale toif_mode = ToifMode.grayscale
toif_data = _from_pil_grayscale(image.getdata(), right_hi=False) toif_data = _from_pil_grayscale(image.getdata(), right_hi=False)
elif image.mode == "LA": elif image.mode == "LA":
toif_mode = firmware.ToifMode.grayscale toif_mode = ToifMode.grayscale
if image.size[0] % 2 != 0: if image.size[0] % 2 != 0:
raise ValueError("Only even-width grayscale images are supported") raise ValueError("Only even-width grayscale images are supported")
if not legacy_format: if not legacy_format:
toif_mode = firmware.ToifMode.grayscale_eh toif_mode = ToifMode.grayscale_eh
toif_data = _from_pil_grayscale_alpha(image.getdata(), right_hi=True) toif_data = _from_pil_grayscale_alpha(image.getdata(), right_hi=True)
else: else:
toif_mode = firmware.ToifMode.grayscale toif_mode = ToifMode.grayscale
toif_data = _from_pil_grayscale_alpha(image.getdata(), right_hi=False) toif_data = _from_pil_grayscale_alpha(image.getdata(), right_hi=False)
elif image.mode == "RGB": elif image.mode == "RGB":
if not legacy_format: if not legacy_format:
toif_mode = firmware.ToifMode.full_color_le toif_mode = ToifMode.full_color_le
toif_data = _from_pil_rgb(image.getdata(), little_endian=True) toif_data = _from_pil_rgb(image.getdata(), little_endian=True)
else: else:
toif_mode = firmware.ToifMode.full_color toif_mode = ToifMode.full_color
toif_data = _from_pil_rgb(image.getdata(), little_endian=False) toif_data = _from_pil_rgb(image.getdata(), little_endian=False)
else: else:
raise ValueError(f"Unsupported image mode: {image.mode}") raise ValueError(f"Unsupported image mode: {image.mode}")

@ -33,6 +33,8 @@ from typing import (
overload, overload,
) )
import construct
if TYPE_CHECKING: if TYPE_CHECKING:
from .client import TrezorClient from .client import TrezorClient
from .protobuf import MessageType from .protobuf import MessageType
@ -372,3 +374,18 @@ def descriptor_checksum(desc: str) -> str:
for j in range(0, 8): for j in range(0, 8):
ret[j] = CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31] ret[j] = CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31]
return "".join(ret) return "".join(ret)
class EnumAdapter(construct.Adapter):
def __init__(self, subcon: Any, enum: Any) -> None:
self.enum = enum
super().__init__(subcon)
def _encode(self, obj: Any, ctx: Any, path: Any):
return obj.value
def _decode(self, obj: Any, ctx: Any, path: Any):
try:
return self.enum(obj)
except ValueError:
return obj

Loading…
Cancel
Save