mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-16 04:29:08 +00:00
7f1a5ac4c1
WIP - refactor and extend font generation for non-ascii characters WIP - add czech characters mapping between UTF8 value and index WIP - regenerate font files with czech characters WIP - shorten czech button text, it was causing SHUTDOWN for some reason WIP - support UTF8 characters in fonts.c WIP - account for translation in tests WIP - small fixes WIP - fix last test WIP - support UTF8 also in Rust font operations WIP - add a script to find non-translated english strings in micropython code WIP - add a validator script for checking missing micropython translations WIP - translate remaining altcoins and other apps in core (fido, sdcard, TT layouts, ...) WIP - generate czech glyphs for TT fonts WIP - modify gen_font.py to account for negative bearing czech characters WIP - extend translation validation scripts, move them into core/tools WIP - translate TT layouts in Rust WIP - fix tests WIP - fix inverse coloring of nonprintable glyph WIP - add build and test pipelines for Czech language WIP - merge both JSON files together WIP - run new isort WIP - unify all the translation in Rust, expose to micropython TEMP - leave en_merged.json file, so it is accessible by translators with old link WIP - fixes WIP - add french characters and translation via Google Translator WIP - skip rustfmt in mako-created files WIP - revert all the font height changes causing false-positive UI diff WIP - fixes after rebase WIP - fix broken translations WIP - revert some wording changes causing UI diff WIP - improve validation and translate scripts, translate missing strings WIP - sort all keys alphabetically WIP - remove any usage of translation in bootloader WIP - add newline at the end of JSON file WIP - fix bitcoin-only strings check WIP - fix python support check WIP - add some missing translations WIP - fix SD card device test WIP - fix pystyle WIP - fix rust unittests WIP - fix click tests WIP - flag errors in french translations WIP - add script transferring translations data into a byte blob WIP - regenerate fr.rs WIP - store and read language translations from flash WIP - storing language name in storage WIP - sending language_data in apply_settings protobuf message WIP - separate protobuf message for translations, fixes WIP - set up translations area for TT as well WIP - get rid of TREZOR_LANG env variable during build WIP - make the firmware buildable for TT WIP - add basic device tests WIP - set language for tests WIP - counting with language when writing fixtures WIP - add todos WIP - fix CI WIP - unify translations, make titles CAPITAL WIP - translate missing english WIP - skip translations messages for T1 WIP - not changing tests names for english WIP - fix flake8 WIP - no test language setting for T1 WIP - clippy lint about complex data type WIP - fix some english UI diff for TR WIP - fix cstyle WIP - minimize the usage of #[cfg(feature = "micropython")] outside translations module WIP - minimize TT's UI diff WIP - fix ruststyle WIP - fix TR build WIP - advanced Shamir text change WIP - storing the language name as the first item in the translation data WIP - modify and extend tests after storing language name WIP - modify checklist sentence WIP - add TEST_LANG into Makefile for all the emu tests WIP - default arguments WIP - reimplement default arguments remove unneeded pub from get_info function WIP - Rust handling of object attributes lookups from upy - thanks Matejcik! WIP - generate mock interface for attribute-based translations lookups WIP - change function calls to object attributes WIP - symbolic link for unix/translations.c WIP - fix and improve the reading of translations - thanks Matejcik! WIP - add support for multiple languages in removing missing tests WIP - fix multiple-accounts warning in tests WIP - fix encoding of newlines in translations WIP - fix czech tutorial text WIP - fix czech click tests WIP - do not translate wire error messages WIP - add language options to click tests as well WIP - setup czech device tests in CI WIP - setup czech click tests in CI WIP - record czech device tests for TR WIP - record czech click tests for TR WIP - record czech device tests for TT WIP - record czech click tests for TT WIP - pystyle WIP - cstyle WIP - fix Rust micropython import dependency WIP - fix czech recordings WIP - support french translations in tests WIP - shorten some french words to fix the tests WIP - fix micropython cfg compilation WIP - record french click tests for TR WIP - record french device tests for TR WIP - record french device tests for TT WIP - record french click tests for TT WIP - fix french translations - shorten them WIP - translate missing french words WIP - fix click tests WIP - add french tests into CI WIP - pystyle WIP - allow for czech/french tests in update script WIP - update czech fixtures WIP - update french fixtures WIP - ruststyle WIP - disallow MPU to run it on hardware WIP - cstyle WIP - change translations delimiter from * to \x00 WIP - change translations protobufs WIP - remove language handling from storage WIP - add header into JSON files WIP - count with header in translations blob WIP - yml style fixes WIP - fix proto gen WIP - verify version and data hash WIP - fix loading test translations feat(core): allow access to translations area in firmware [no changelog] WIP - fixes after rebase WIP - increase the TT's translations area to 3 sectors WIP - dynamically read the maximum translations size WIP - record non-english tests from CI WIP - loading font data from translations blob WIP - bump translations version WIP - include czech and french glyph data WIP - whitelist another negative-bearing glyph WIP - remove czech/french glyphs from common font files WIP - fix language tests WIP - specific fonts for specific models WIP - revert the non-ascii font hardcoding WIP - include missing BIG font into nonprintable logic WIP - minor Rust code improvements WIP - include newlines at the end of json files WIP - move glyph Rust function to librust_fonts.h WIP - add all fonts into translations file WIP - move fonts into its own dir WIP - reflect separate dir for fonts WIP - not putting translations trezorhal into bootloader WIP - write and read multiple fonts into translations data WIP - silence pyright issue/notissue WIP - delete no more used translations/*.py imports WIP - fix bootloader builds by introducing translations feature and TRANSLATIONS flag WIP - fix TT's bootloader Rust build WIP - fix tests in non-english languages WIP - not search for UTF-8 when there are no translations data WIP - add colons to strings where missing WIP - fix language loading in tests WIP - fix signmessage input flow to work in all languages WIP - create offset table for translation strings WIP - code improvements WIP - record foreign language fixtures + sync with main in english WIP - do alignment check before reading u16 data WIP - allocate blob in RAM for translations data WIP - add TODO for blob generation WIP - record non-english device tests WIP - use bytes.align_to instead of messing with pointers WIP - fixtures WIP - remove unused import WIP - add order.py WIP - add order.json WIP - take order.json into account in creating general.rs WIP - take order.json into account in generating the blob WIP - style WIP - sort the language files WIP - remove unused file WIP - code improvements WIP - add TODO for homescreen notification WIP - translate plural forms WIP - translate time intervals WIP - sign translations with dev keys, validate signatures, improve robustness WIP - improve tests for translations WIP - add `trezorctl utils sign-translations` for production signing of the blob WIP - pyright fix WIP - changing TR progress loader offset - it was colliding with title WIP - show indeterminate loader when loading translations data WIP - record new and updated language tests WIP - show the change language title/prompt in the target language WIP - sort keys WIP - add crowdin-cli into shell.nix WIP - add crowdin sync script
294 lines
10 KiB
Python
294 lines
10 KiB
Python
# This file is part of the Trezor project.
|
|
#
|
|
# Copyright (C) 2012-2019 SatoshiLabs and contributors
|
|
#
|
|
# This library is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License version 3
|
|
# as published by the Free Software Foundation.
|
|
#
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the License along with this library.
|
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
|
|
|
import json
|
|
import re
|
|
import time
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, Generator, Optional
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from trezorlib import btc, messages, tools
|
|
from trezorlib.messages import ButtonRequestType
|
|
|
|
if TYPE_CHECKING:
|
|
from _pytest.mark.structures import MarkDecorator
|
|
|
|
from trezorlib.debuglink import DebugLink
|
|
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
|
from trezorlib.messages import ButtonRequest
|
|
|
|
|
|
BRGeneratorType = Generator[None, messages.ButtonRequest, None]
|
|
|
|
|
|
# fmt: off
|
|
# 1 2 3 4 5 6 7 8 9 10 11 12
|
|
MNEMONIC12 = "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"
|
|
MNEMONIC_SLIP39_BASIC_20_3of6 = [
|
|
"extra extend academic bishop cricket bundle tofu goat apart victim enlarge program behavior permit course armed jerky faint language modern",
|
|
"extra extend academic acne away best indicate impact square oasis prospect painting voting guest either argue username racism enemy eclipse",
|
|
"extra extend academic arcade born dive legal hush gross briefing talent drug much home firefly toxic analysis idea umbrella slice",
|
|
]
|
|
MNEMONIC_SLIP39_BASIC_20_3of6_SECRET = "491b795b80fc21ccdf466c0fbc98c8fc"
|
|
# Shamir shares (128 bits, 2 groups from 1 of 1, 1 of 1, 3 of 5, 2 of 6)
|
|
MNEMONIC_SLIP39_ADVANCED_20 = [
|
|
"eraser senior beard romp adorn nuclear spill corner cradle style ancient family general leader ambition exchange unusual garlic promise voice",
|
|
"eraser senior ceramic snake clay various huge numb argue hesitate auction category timber browser greatest hanger petition script leaf pickup",
|
|
"eraser senior ceramic shaft dynamic become junior wrist silver peasant force math alto coal amazing segment yelp velvet image paces",
|
|
"eraser senior ceramic round column hawk trust auction smug shame alive greatest sheriff living perfect corner chest sled fumes adequate",
|
|
]
|
|
# Shamir shares (256 bits, 2 groups from 1 of 1, 1 of 1, 3 of 5, 2 of 6):
|
|
MNEMONIC_SLIP39_ADVANCED_33 = [
|
|
"wildlife deal beard romp alcohol space mild usual clothes union nuclear testify course research heat listen task location thank hospital slice smell failure fawn helpful priest ambition average recover lecture process dough stadium",
|
|
"wildlife deal acrobat romp anxiety axis starting require metric flexible geology game drove editor edge screw helpful have huge holy making pitch unknown carve holiday numb glasses survive already tenant adapt goat fangs",
|
|
]
|
|
# External entropy mocked as received from trezorlib.
|
|
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
|
|
# fmt: on
|
|
|
|
TEST_ADDRESS_N = tools.parse_path("m/44h/1h/0h/0/0")
|
|
COMMON_FIXTURES_DIR = (
|
|
Path(__file__).resolve().parent.parent / "common" / "tests" / "fixtures"
|
|
)
|
|
|
|
# So that all the random things are consistent
|
|
MOCK_OS_URANDOM = mock.Mock(return_value=EXTERNAL_ENTROPY)
|
|
WITH_MOCK_URANDOM = mock.patch("os.urandom", MOCK_OS_URANDOM)
|
|
|
|
|
|
def parametrize_using_common_fixtures(*paths: str) -> "MarkDecorator":
|
|
fixtures = []
|
|
for path in paths:
|
|
fixtures.append(json.loads((COMMON_FIXTURES_DIR / path).read_text()))
|
|
|
|
tests = []
|
|
for fixture in fixtures:
|
|
for test in fixture["tests"]:
|
|
test_id = test.get("name")
|
|
if not test_id:
|
|
test_id = test.get("description")
|
|
if test_id is not None:
|
|
test_id = test_id.lower().replace(" ", "_")
|
|
|
|
skip_models = test.get("skip_models", [])
|
|
skip_marks = []
|
|
for skip_model in skip_models:
|
|
if skip_model == "t1":
|
|
skip_marks.append(pytest.mark.skip_t1)
|
|
if skip_model == "t2":
|
|
skip_marks.append(pytest.mark.skip_t2)
|
|
if skip_model == "tr":
|
|
skip_marks.append(pytest.mark.skip_tr)
|
|
|
|
tests.append(
|
|
pytest.param(
|
|
test["parameters"],
|
|
test["result"],
|
|
marks=[
|
|
pytest.mark.setup_client(
|
|
passphrase=fixture["setup"]["passphrase"],
|
|
mnemonic=fixture["setup"]["mnemonic"],
|
|
)
|
|
]
|
|
+ skip_marks,
|
|
id=test_id,
|
|
)
|
|
)
|
|
|
|
return pytest.mark.parametrize("parameters, result", tests)
|
|
|
|
|
|
def generate_entropy(
|
|
strength: int, internal_entropy: bytes, external_entropy: bytes
|
|
) -> bytes:
|
|
"""
|
|
strength - length of produced seed. One of 128, 192, 256
|
|
random - binary stream of random data from external HRNG
|
|
"""
|
|
import hashlib
|
|
|
|
if strength not in (128, 192, 256):
|
|
raise ValueError("Invalid strength")
|
|
|
|
if not internal_entropy:
|
|
raise ValueError("Internal entropy is not provided")
|
|
|
|
if len(internal_entropy) < 32:
|
|
raise ValueError("Internal entropy too short")
|
|
|
|
if not external_entropy:
|
|
raise ValueError("External entropy is not provided")
|
|
|
|
if len(external_entropy) < 32:
|
|
raise ValueError("External entropy too short")
|
|
|
|
entropy = hashlib.sha256(internal_entropy + external_entropy).digest()
|
|
entropy_stripped = entropy[: strength // 8]
|
|
|
|
if len(entropy_stripped) * 8 != strength:
|
|
raise ValueError("Entropy length mismatch")
|
|
|
|
return entropy_stripped
|
|
|
|
|
|
def click_through(
|
|
debug: "DebugLink", screens: int, code: Optional[ButtonRequestType] = None
|
|
) -> Generator[None, "ButtonRequest", None]:
|
|
"""Click through N dialog screens.
|
|
|
|
For use in an input flow function.
|
|
Example:
|
|
|
|
def input_flow():
|
|
# 1. Confirm reset
|
|
# 2. Backup your seed
|
|
# 3. Confirm warning
|
|
# 4. Shares info
|
|
yield from click_through(client.debug, screens=4, code=ButtonRequestType.ResetDevice)
|
|
"""
|
|
for _ in range(screens):
|
|
received = yield
|
|
if code is not None:
|
|
assert received.code == code
|
|
debug.press_yes()
|
|
|
|
|
|
def read_and_confirm_mnemonic(
|
|
debug: "DebugLink", choose_wrong: bool = False
|
|
) -> Generator[None, "ButtonRequest", Optional[str]]:
|
|
# TODO: these are very similar, reuse some code
|
|
if debug.model == "T":
|
|
mnemonic = yield from read_and_confirm_mnemonic_tt(debug, choose_wrong)
|
|
elif debug.model == "Safe 3":
|
|
mnemonic = yield from read_and_confirm_mnemonic_tr(debug, choose_wrong)
|
|
else:
|
|
raise ValueError(f"Unknown model: {debug.model}")
|
|
|
|
return mnemonic
|
|
|
|
|
|
def read_and_confirm_mnemonic_tt(
|
|
debug: "DebugLink", choose_wrong: bool = False
|
|
) -> Generator[None, "ButtonRequest", Optional[str]]:
|
|
"""Read a given number of mnemonic words from Trezor T screen and correctly
|
|
answer confirmation questions. Return the full mnemonic.
|
|
|
|
For use in an input flow function.
|
|
Example:
|
|
|
|
def input_flow():
|
|
yield from click_through(client.debug, screens=3)
|
|
|
|
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
|
|
"""
|
|
mnemonic: list[str] = []
|
|
br = yield
|
|
assert br.pages is not None
|
|
|
|
debug.wait_layout()
|
|
|
|
for i in range(br.pages):
|
|
words = debug.wait_layout().seed_words()
|
|
mnemonic.extend(words)
|
|
# Not swiping on the last page
|
|
if i < br.pages - 1:
|
|
debug.swipe_up()
|
|
|
|
debug.press_yes()
|
|
|
|
# check share
|
|
for _ in range(3):
|
|
# Word position is the first number in the text
|
|
word_pos_match = re.search(r"\d+", debug.wait_layout().text_content())
|
|
assert word_pos_match is not None
|
|
word_pos = int(word_pos_match.group(0))
|
|
|
|
index = word_pos - 1
|
|
if choose_wrong:
|
|
debug.input(mnemonic[(index + 1) % len(mnemonic)])
|
|
return None
|
|
else:
|
|
debug.input(mnemonic[index])
|
|
|
|
return " ".join(mnemonic)
|
|
|
|
|
|
def read_and_confirm_mnemonic_tr(
|
|
debug: "DebugLink", choose_wrong: bool = False
|
|
) -> Generator[None, "ButtonRequest", Optional[str]]:
|
|
mnemonic: list[str] = []
|
|
yield # write down all 12 words in order
|
|
debug.press_yes()
|
|
br = yield
|
|
assert br.pages is not None
|
|
for _ in range(br.pages - 1):
|
|
layout = debug.wait_layout()
|
|
words = layout.seed_words()
|
|
mnemonic.extend(words)
|
|
debug.press_right()
|
|
debug.press_yes()
|
|
|
|
yield # Select correct words...
|
|
debug.press_right()
|
|
|
|
# check share
|
|
for _ in range(3):
|
|
word_pos_match = re.search(r"\d+", debug.wait_layout().title())
|
|
assert word_pos_match is not None
|
|
word_pos = int(word_pos_match.group(0))
|
|
index = word_pos - 1
|
|
if choose_wrong:
|
|
debug.input(mnemonic[(index + 1) % len(mnemonic)])
|
|
return None
|
|
else:
|
|
debug.input(mnemonic[index])
|
|
|
|
return " ".join(mnemonic)
|
|
|
|
|
|
def click_info_button_tt(debug: "DebugLink"):
|
|
"""Click Shamir backup info button and return back."""
|
|
debug.press_info()
|
|
yield # Info screen with text
|
|
debug.press_yes()
|
|
|
|
|
|
def check_pin_backoff_time(attempts: int, start: float) -> None:
|
|
"""Helper to assert the exponentially growing delay after incorrect PIN attempts"""
|
|
expected = (2**attempts) - 1
|
|
got = round(time.time() - start, 2)
|
|
assert got >= expected
|
|
|
|
|
|
def get_test_address(client: "Client") -> str:
|
|
"""Fetch a testnet address on a fixed path. Useful to make a pin/passphrase
|
|
protected call, or to identify the root secret (seed+passphrase)"""
|
|
return btc.get_address(client, "Testnet", TEST_ADDRESS_N)
|
|
|
|
|
|
def compact_size(n: int) -> bytes:
|
|
if n < 253:
|
|
return n.to_bytes(1, "little")
|
|
elif n < 0x1_0000:
|
|
return bytes([253]) + n.to_bytes(2, "little")
|
|
elif n < 0x1_0000_0000:
|
|
return bytes([254]) + n.to_bytes(4, "little")
|
|
else:
|
|
return bytes([255]) + n.to_bytes(8, "little")
|