diff --git a/.github/actions/install-sim/action.yml b/.github/actions/install-sim/action.yml index f9799f122..8817438d4 100644 --- a/.github/actions/install-sim/action.yml +++ b/.github/actions/install-sim/action.yml @@ -17,7 +17,14 @@ runs: apt-get update apt-get install -y libsdl2-image-2.0-0 libusb-1.0-0 tar -xvf trezor-firmware.tar.gz - + + - if: startsWith(inputs.device, 'onekey-') + shell: bash + run: | + apt-get update + apt-get install -y libsdl2-image-2.0-0 libusb-1.0-0 + tar -xvf onekey-firmware.tar.gz + - if: inputs.device == 'coldcard' shell: bash run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f45c7ffcd..216b1a90e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,7 @@ jobs: hwilib/devices/__init__.py hwilib/devices/keepkey.py hwilib/devices/ledger.py + hwilib/devices/onekey.py hwilib/devices/trezor.py hwilib/errors.py hwilib/_script.py @@ -153,7 +154,9 @@ jobs: - { name: 'jade', archive: 'jade', paths: 'test/work/jade/simulator' } - { name: 'ledger', archive: 'speculos', paths: 'test/work/speculos' } - { name: 'keepkey', archive: 'keepkey-firmware', paths: 'test/work/keepkey-firmware/bin' } - + - { name: 'onekey-1', archive: 'onekey-firmware', paths: 'test/work/onekey-firmware' } + - { name: 'onekey-t', archive: 'onekey-firmware', paths: 'test/work/onekey-firmware' } + steps: - uses: actions/checkout@v4 @@ -218,6 +221,8 @@ jobs: - 'ledger' - 'ledger-legacy' - 'keepkey' + - 'onekey-1' + - 'onekey-t' script: - name: 'Wheel' install: 'pip install dist/*.whl' @@ -232,8 +237,11 @@ jobs: env: DEVICE: '--${{ matrix.device }}' - container: python:${{ matrix.python-version }} - + container: + image: python:${{ matrix.python-version }} + volumes: + - ${{ github.workspace }}:${{ github.workspace }} + options: --workdir ${{ github.workspace }} steps: - uses: actions/checkout@v4 @@ -287,10 +295,15 @@ jobs: - 'ledger' - 'ledger-legacy' - 'keepkey' + - 'onekey-1' + - 'onekey-t' interface: [ 'library', 'cli', 'stdin' ] - container: python:${{ matrix.python-version }} - + container: + image: python:${{ matrix.python-version }} + volumes: + - ${{ github.workspace }}:${{ github.workspace }} + options: --workdir ${{ github.workspace }} steps: - uses: actions/checkout@v4 diff --git a/hwilib/devices/keepkey.py b/hwilib/devices/keepkey.py index 21845e955..72e572873 100644 --- a/hwilib/devices/keepkey.py +++ b/hwilib/devices/keepkey.py @@ -160,8 +160,7 @@ def __init__(self, path: str, password: Optional[str] = None, expert: bool = Fal if path.startswith("udp"): model.default_mapping.register(KeepkeyDebugLinkState) - super(KeepkeyClient, self).__init__(path, password, expert, chain, KEEPKEY_HID_IDS, KEEPKEY_WEBUSB_IDS, KEEPKEY_SIMULATOR_PATH, model) - self.type = 'Keepkey' + super(KeepkeyClient, self).__init__(path, password, expert, chain, KEEPKEY_HID_IDS, KEEPKEY_WEBUSB_IDS, KEEPKEY_SIMULATOR_PATH, model, device_type="Keepkey") def can_sign_taproot(self) -> bool: """ diff --git a/hwilib/devices/onekey.py b/hwilib/devices/onekey.py index 4bf0bb87b..9de52866f 100644 --- a/hwilib/devices/onekey.py +++ b/hwilib/devices/onekey.py @@ -5,7 +5,6 @@ """ -import sys from ..common import Chain from ..errors import ( DEVICE_NOT_INITIALIZED, @@ -13,7 +12,7 @@ common_err_msgs, handle_errors, ) -from .trezorlib import protobuf, debuglink +from .trezorlib import protobuf from .trezorlib.transport import ( udp, webusb, @@ -35,6 +34,7 @@ Optional, Sequence, ) +import copy py_enumerate = enumerate # Need to use the enumerate built-in but there's another function already named that @@ -223,8 +223,8 @@ def __init__( self.serial_no = serial_no self.boardloader_version = boardloader_version - -DEFAULT_MAPPING.register(OnekeyFeatures) +ONEKEY_MAPPING = copy.deepcopy(DEFAULT_MAPPING) +ONEKEY_MAPPING.register(OnekeyFeatures) USB_IDS = {(0x1209, 0x4F4A), (0x1209, 0x4F4B), } @@ -233,7 +233,7 @@ def __init__( minimum_version=(2, 11, 0), vendors=VENDORS, usb_ids=USB_IDS, - default_mapping=DEFAULT_MAPPING, + default_mapping=ONEKEY_MAPPING, ) ONEKEY_TOUCH = TrezorModel( @@ -241,7 +241,7 @@ def __init__( minimum_version=(4, 2, 0), vendors=VENDORS, usb_ids=USB_IDS, - default_mapping=DEFAULT_MAPPING, + default_mapping=ONEKEY_MAPPING, ) ONEKEYS = (ONEKEY_LEGACY, ONEKEY_TOUCH) @@ -256,29 +256,14 @@ def model_by_name(name: str) -> Optional[TrezorModel]: # ===============overwrite methods for onekey device begin============ +def retrieval_version(self: object): + version = (*map(int, self.features.onekey_version.split(".")), ) + return version -def _refresh_features(self: object, features: Features) -> None: - """Update internal fields based on passed-in Features message.""" - if not self.model: - self.model = model_by_name(features.model or "1") - if self.model is None: - raise RuntimeError("Unsupported OneKey model") - - if features.vendor not in self.model.vendors: - raise RuntimeError("Unsupported device") - self.features = features - self.version = (*map(int, self.features.onekey_version.split(".")), ) - self.check_firmware_version(warn_only=True) - if self.features.session_id is not None: - self.session_id = self.features.session_id - self.features.session_id = None - -def button_request(self: object, code: Optional[int]) -> None: - if not self.prompt_shown: - print("Please confirm action on your OneKey device", file=sys.stderr) - if not self.always_prompt: - self.prompt_shown = True - +def ensure_model(self: object, features): + assert self.model is not None, "Unsupported OneKey model" + # Correct the correct model + self.model = model_by_name(features.model or "1") # ===============overwrite methods for onekey device end============ @@ -291,12 +276,9 @@ def __init__( expert: bool = False, chain: Chain = Chain.MAIN, ) -> None: - super().__init__(path, password, expert, chain, webusb_ids=USB_IDS, sim_path=ONEKEY_EMULATOR_PATH) - self.client._refresh_features = MethodType(_refresh_features, self.client) - if not isinstance(self.client.ui, debuglink.DebugUI): - self.client.ui.button_request = MethodType(button_request, self.client.ui) - self.type = "OneKey" - + super().__init__(path, password, expert, chain, webusb_ids=USB_IDS, sim_path=ONEKEY_EMULATOR_PATH, model=ONEKEY_LEGACY, device_type="OneKey") + self.client.retrieval_version = MethodType(retrieval_version, self.client) + self.client.ensure_model = MethodType(ensure_model, self.client) def enumerate( password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN diff --git a/hwilib/devices/trezor.py b/hwilib/devices/trezor.py index bd5a79733..43694c454 100644 --- a/hwilib/devices/trezor.py +++ b/hwilib/devices/trezor.py @@ -229,16 +229,17 @@ def get_word(type: messages.WordRequestType) -> str: class PassphraseUI: - def __init__(self, passphrase: str) -> None: + def __init__(self, passphrase: str, device_type: str) -> None: self.passphrase = passphrase self.pinmatrix_shown = False self.prompt_shown = False self.always_prompt = False self.return_passphrase = True + self.device_type = device_type def button_request(self, code: Optional[int]) -> None: if not self.prompt_shown: - print("Please confirm action on your Trezor device", file=sys.stderr) + print(f"Please confirm action on your {self.device_type} device", file=sys.stderr) if not self.always_prompt: self.prompt_shown = True @@ -288,7 +289,8 @@ def __init__( hid_ids: Set[Tuple[int, int]] = HID_IDS, webusb_ids: Set[Tuple[int, int]] = WEBUSB_IDS, sim_path: str = SIMULATOR_PATH, - model: Optional[TrezorModel] = None + model: Optional[TrezorModel] = None, + device_type: str = "Trezor" ) -> None: if password is None: password = "" @@ -301,14 +303,14 @@ def __init__( self.simulator = True self.client.use_passphrase(password) else: - self.client = Trezor(transport=transport, ui=PassphraseUI(password), model=model, _init_device=False) + self.client = Trezor(transport=transport, ui=PassphraseUI(password, device_type), model=model, _init_device=False) # if it wasn't able to find a client, throw an error if not self.client: raise IOError("no Device") self.password = password - self.type = 'Trezor' + self.type = device_type def _prepare_device(self) -> None: self.coin_name = 'Bitcoin' if self.chain == Chain.MAIN else 'Testnet' diff --git a/hwilib/devices/trezorlib/client.py b/hwilib/devices/trezorlib/client.py index 5048340dd..d76d7446f 100644 --- a/hwilib/devices/trezorlib/client.py +++ b/hwilib/devices/trezorlib/client.py @@ -17,7 +17,7 @@ import logging import os import warnings -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Tuple from mnemonic import Mnemonic @@ -259,25 +259,31 @@ def call(self, msg: "MessageType", check_fw: bool = True) -> "MessageType": raise exceptions.TrezorFailure(resp) else: return resp + + def retrieval_version(self) -> Tuple[int, int, int]: - def _refresh_features(self, features: messages.Features) -> None: - """Update internal fields based on passed-in Features message.""" - + version = ( + self.features.major_version, + self.features.minor_version, + self.features.patch_version, + ) + return version + + def ensure_model(self, features): if not self.model: # Trezor Model One bootloader 1.8.0 or older does not send model name self.model = models.by_name(features.model or "1") if self.model is None: raise RuntimeError("Unsupported Trezor model") + + def _refresh_features(self, features: messages.Features) -> None: + """Update internal fields based on passed-in Features message.""" + self.ensure_model(features) if features.vendor not in self.model.vendors: raise RuntimeError("Unsupported device") - self.features = features - self.version = ( - self.features.major_version, - self.features.minor_version, - self.features.patch_version, - ) + self.version = self.retrieval_version() self.check_firmware_version(warn_only=True) if self.features.session_id is not None: self.session_id = self.features.session_id diff --git a/test/README.md b/test/README.md index 5e888e5cf..3e73805e8 100644 --- a/test/README.md +++ b/test/README.md @@ -80,12 +80,17 @@ $ pipenv run script/cibuild ### Dependencies -In order to build the Onekey emulator, the [Nix](https://nixos.org) will need to be installed: +In order to build the Onekey emulator, the following packages will need to be installed: ``` -sh <(curl -L https://nixos.org/nix/install) +build-essential curl git python3 python3-pip libsdl2-dev libsdl2-image-dev gcc-arm-none-eabi libnewlib-arm-none-eabi gcc-multilib ``` +For onekey Touch `Rust` needs to be installed: + +``` +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` ### Building Clone the repository: @@ -95,20 +100,26 @@ $ git clone --recursive https://github.com/OneKeyHQ/firmware.git onekey-firmware ``` For the Onekey Legacy firmware emulator: + ``` $ git checkout bixin_dev $ cd onekey-firmware -$ nix-shell $ poetry install $ export EMULATOR=1 DEBUG_LINK=1 $ poetry run script/setup $ poetry run script/cibuild ``` + For the Onekey Touch emulator: + ``` -$ git checkout touch +$ rustup update +$ rustup toolchain uninstall nightly +$ rustup toolchain install nightly +$ rustup default nightly $ cd onekey-firmware -$ nix-shell +$ git checkout touch +$ git submodule update --init --recursive vendor/lvgl_mp $ poetry install $ cd core $ poetry run make build_unix diff --git a/test/test_onekey.py b/test/test_onekey.py index 5489155e9..608a9cd5b 100755 --- a/test/test_onekey.py +++ b/test/test_onekey.py @@ -15,7 +15,7 @@ from hwilib.devices.trezorlib.transport.udp import UdpTransport from hwilib.devices.trezorlib.debuglink import TrezorClientDebugLink, load_device_by_mnemonic from hwilib.devices.trezorlib import device -from hwilib.devices.onekey import _refresh_features +from hwilib.devices.onekey import retrieval_version, ensure_model, ONEKEY_LEGACY from test_device import ( Bitcoind, DeviceEmulator, @@ -86,8 +86,9 @@ def start(self): time.sleep(1) # Setup the emulator wirelink = UdpTransport.enumerate("127.0.0.1:54935")[0] - client = TrezorClientDebugLink(wirelink) - client._refresh_features = MethodType(_refresh_features, client) + client = TrezorClientDebugLink(wirelink, model=ONEKEY_LEGACY) + client.retrieval_version = MethodType(retrieval_version, client) + client.ensure_model = MethodType(ensure_model, client) client.init_device() device.wipe(client) load_device_by_mnemonic(client=client, mnemonic='alcohol woman abuse must during monitor noble actual mixed trade anger aisle', pin='', passphrase_protection=False, label='test') # From Trezor device tests