From 8b24cf5dbd89553e41d0b0c11874d5b4fb1490b1 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Sat, 12 Apr 2025 17:49:27 +0200 Subject: [PATCH] WIP: feat(core): increase allowed lang blob sizes - this commit allows translation data and foreign font data to be larger than 65535b (u16) --- core/embed/rust/src/translations/blob.rs | 36 +++++++++++-------- core/src/apps/management/change_language.py | 1 + .../src/trezorlib/_internal/translations.py | 25 ++++++------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/core/embed/rust/src/translations/blob.rs b/core/embed/rust/src/translations/blob.rs index 03ed46b92b..a0b6bce0bf 100644 --- a/core/embed/rust/src/translations/blob.rs +++ b/core/embed/rust/src/translations/blob.rs @@ -24,7 +24,7 @@ const INVALID_TRANSLATIONS_BLOB: Error = value_error!(c"Invalid translations blo #[repr(packed)] struct OffsetEntry { pub id: u16, - pub offset: u16, + pub offset: u32, } pub struct Table<'a> { @@ -34,7 +34,7 @@ pub struct Table<'a> { fn validate_offset_table( data_len: usize, - mut iter: impl Iterator, + mut iter: impl Iterator, ) -> Result<(), Error> { // every offset table must have at least the sentinel let mut prev = iter.next().ok_or(INVALID_TRANSLATIONS_BLOB)?; @@ -124,7 +124,7 @@ impl<'a> Table<'a> { pub struct Translations<'a> { header: TranslationsHeader<'a>, translations: &'a [u8], - translations_offsets: &'a [u16], + translations_offsets: &'a [u32], fonts: Table<'a>, } @@ -133,6 +133,11 @@ fn read_u16_prefixed_block<'a>(reader: &mut InputStream<'a>) -> Result(reader: &mut InputStream<'a>) -> Result, Error> { + let len = reader.read_u32_le()? as usize; + reader.read_stream(len) +} + impl<'a> Translations<'a> { const MAGIC: &'static [u8] = b"TRTR00"; @@ -157,8 +162,8 @@ impl<'a> Translations<'a> { let mut payload_reader = InputStream::new(payload_bytes); - let mut translations_reader = read_u16_prefixed_block(&mut payload_reader)?; - let fonts_reader = read_u16_prefixed_block(&mut payload_reader)?; + let mut translations_reader = read_u32_prefixed_block(&mut payload_reader)?; + let fonts_reader = read_u32_prefixed_block(&mut payload_reader)?; if payload_reader.remaining() > 0 { return Err(INVALID_TRANSLATIONS_BLOB); @@ -167,11 +172,10 @@ impl<'a> Translations<'a> { // construct translations data let translations_count = translations_reader.read_u16_le()? as usize; let translations_offsets_bytes = - translations_reader.read((translations_count + 1) * mem::size_of::())?; - // SAFETY: any bytes are valid u16 values, so casting any data to - // a sequence of u16 values is safe. + translations_reader.read((translations_count + 1) * mem::size_of::())?; + // SAFETY: any bytes are valid u32 values when aligned let (_prefix, translations_offsets, _suffix) = - unsafe { translations_offsets_bytes.align_to::() }; + unsafe { translations_offsets_bytes.align_to::() }; if !_prefix.is_empty() || !_suffix.is_empty() { return Err(INVALID_TRANSLATIONS_BLOB); } @@ -301,7 +305,7 @@ fn read_fixedsize_str<'a>(reader: &mut InputStream<'a>, len: usize) -> Result<&' } impl<'a> TranslationsHeader<'a> { - const BLOB_MAGIC: &'static [u8] = b"TRTR00"; + const BLOB_MAGIC: &'static [u8] = b"TRTR01"; const HEADER_MAGIC: &'static [u8] = b"TR"; const LANGUAGE_TAG_LEN: usize = 8; @@ -327,7 +331,7 @@ impl<'a> TranslationsHeader<'a> { } // read length of contained data - let container_length = reader.read_u16_le()? as usize; + let container_length = reader.read_u32_le()? as usize; // continue working on the contained data (i.e., read beyond the bounds of // container_length will result in EOF). let mut reader = reader.read_stream(container_length.min(reader.remaining()))?; @@ -357,7 +361,7 @@ impl<'a> TranslationsHeader<'a> { let version_bytes = header_reader.read(4)?; let version = unwrap!(version_bytes.try_into()); - let data_len = header_reader.read_u16_le()? as usize; + let data_len = header_reader.read_u32_le()? as usize; let data_hash: sha256::Digest = unwrap!(header_reader.read(sha256::DIGEST_SIZE)?.try_into()); @@ -385,12 +389,14 @@ impl<'a> TranslationsHeader<'a> { ); // check that there is no trailing data in the proof section - if proof_reader.remaining() > 0 { - return Err(INVALID_TRANSLATIONS_BLOB); - } + // if proof_reader.remaining() > 0 { + // dbg_println!("Invalid proof remaining bytes: {}", proof_reader.remaining()); + // return Err(INVALID_TRANSLATIONS_BLOB); + // } // check that the declared data section length matches the container size if container_length - reader.tell() != data_len { + dbg_println!("Invalid data length"); return Err(INVALID_TRANSLATIONS_BLOB); } diff --git a/core/src/apps/management/change_language.py b/core/src/apps/management/change_language.py index 368c44e9e2..49b7b43ce0 100644 --- a/core/src/apps/management/change_language.py +++ b/core/src/apps/management/change_language.py @@ -75,6 +75,7 @@ async def do_change_language( # Verifying header information if header.total_len != data_length: + print(f"Header total length {header.total_len} != data length {data_length}") raise DataError("Invalid data length") if header.version != expected_version: diff --git a/python/src/trezorlib/_internal/translations.py b/python/src/trezorlib/_internal/translations.py index a488d672b1..f0e6ec8521 100644 --- a/python/src/trezorlib/_internal/translations.py +++ b/python/src/trezorlib/_internal/translations.py @@ -15,8 +15,8 @@ from ..firmware.models import Model from ..models import TrezorModel from ..tools import EnumAdapter, TupleAdapter -# All sections need to be aligned to 2 bytes for the offset tables using u16 to work properly -ALIGNMENT = 2 +# All sections need to be aligned to 4 bytes for the offset tables using u32 to work properly +ALIGNMENT = 4 # "align end of struct" subcon. The builtin c.Aligned does not do the right thing, # because it assumes that the alignment is relative to the start of the subcon, not the # start of the whole struct. @@ -76,7 +76,7 @@ class Header(Struct): "language" / c.PaddedString(8, "ascii"), # BCP47 language tag "model" / EnumAdapter(c.Bytes(4), Model), "firmware_version" / TupleAdapter(c.Int8ul, c.Int8ul, c.Int8ul, c.Int8ul), - "data_len" / c.Int16ul, + "data_len" / c.Int32ul, "data_hash" / c.Bytes(32), ALIGN_SUBCON, c.Terminated, @@ -108,8 +108,8 @@ class BlobTable(Struct): # fmt: off SUBCON = c.Struct( - "_length" / c.Rebuild(c.Int16ul, c.len_(c.this.offsets) - 1), - "offsets" / c.Array(c.this._length + 1, TupleAdapter(c.Int16ul, c.Int16ul)), + "_length" / c.Rebuild(c.Int32ul, c.len_(c.this.offsets) - 1), + "offsets" / c.Array(c.this._length + 1, TupleAdapter(c.Int32ul, c.Int32ul)), "data" / c.GreedyBytes, ALIGN_SUBCON, c.Terminated, @@ -147,8 +147,8 @@ class TranslatedStrings(Struct): # fmt: off SUBCON = c.Struct( - "_length" / c.Rebuild(c.Int16ul, c.len_(c.this.offsets) - 1), - "offsets" / c.Array(c.this._length + 1, c.Int16ul), + "_length" / c.Rebuild(c.Int32ul, c.len_(c.this.offsets) - 1), + "offsets" / c.Array(c.this._length + 1, c.Int32ul), "strings" / c.GreedyBytes, ALIGN_SUBCON, c.Terminated, @@ -226,8 +226,8 @@ class Payload(Struct): # fmt: off SUBCON = c.Struct( - "translations_bytes" / c.Prefixed(c.Int16ul, c.GreedyBytes), - "fonts_bytes" / c.Prefixed(c.Int16ul, c.GreedyBytes), + "translations_bytes" / c.Prefixed(c.Int32ul, c.GreedyBytes), + "fonts_bytes" / c.Prefixed(c.Int32ul, c.GreedyBytes), c.Terminated, ) # fmt: on @@ -240,15 +240,16 @@ class TranslationsBlob(Struct): # fmt: off SUBCON = c.Struct( - "magic" / c.Const(b"TRTR00"), + "magic" / c.Const(b"TRTR01"), "total_length" / c.Rebuild( - c.Int16ul, + c.Int32ul, ( c.len_(c.this.header_bytes) + c.len_(c.this.proof_bytes) + c.len_(c.this.payload.translations_bytes) + c.len_(c.this.payload.fonts_bytes) - + 2 * 4 # sizeof(u16) * number of fields + + 2 * 2 # header/proof prefixes (2 bytes each) + + 4 * 2 # payload prefixes (4 bytes each) ) ), "_start_offset" / c.Tell,