diff --git a/.github/workflows/build_and_functional_tests.yml b/.github/workflows/build_and_functional_tests.yml new file mode 100644 index 0000000..c92fc21 --- /dev/null +++ b/.github/workflows/build_and_functional_tests.yml @@ -0,0 +1,32 @@ +name: Build and run functional tests using ragger through reusable workflow + +# This workflow will build the app and then run functional tests using the Ragger framework upon Speculos emulation. +# It calls a reusable workflow developed by Ledger's internal developer team to build the application and upload the +# resulting binaries. +# It then calls another reusable workflow to run the Ragger tests on the compiled application binary. +# +# While this workflow is optional, having functional testing on your application is mandatory and this workflow and +# tooling environment is meant to be easy to use and adapt after forking your application + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + build_application: + name: Build application using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_build.yml@v1 + with: + upload_app_binaries_artifact: "compiled_app_binaries" + + ragger_tests: + name: Run ragger tests using the reusable workflow + needs: build_application + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_ragger_tests.yml@v1 + with: + download_app_binaries_artifact: "compiled_app_binaries" diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml deleted file mode 100644 index 41116f2..0000000 --- a/.github/workflows/ci-workflow.yml +++ /dev/null @@ -1,197 +0,0 @@ -name: Compilation & tests - -on: - workflow_dispatch: - push: - branches: - - main - - develop - pull_request: - branches: - - main - - develop - -jobs: - nano_build: - name: Build Aptos application for NanoS, X and S+ - strategy: - matrix: - include: - - SDK: "$NANOS_SDK" - artifact: aptos-app-nanos - - SDK: "$NANOX_SDK" - artifact: aptos-app-nanox - - SDK: "$NANOSP_SDK" - artifact: aptos-app-nanosp - runs-on: ubuntu-latest - container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Build - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - make BOLOS_SDK=${{ matrix.SDK }} - - - name: Upload app binary - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.artifact }} - path: bin - - job_scan_build: - name: Clang Static Analyzer - runs-on: ubuntu-latest - container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Build with Clang Static Analyzer - run: | - make clean - scan-build --use-cc=clang -analyze-headers -enable-checker security -enable-checker unix -enable-checker valist -o scan-build --status-bugs make default - - - name: Upload scan result - uses: actions/upload-artifact@v2 - if: failure() - with: - name: scan-build - path: scan-build - - job_unit_test: - name: Unit test - runs-on: ubuntu-latest - container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder - - steps: - - name: Install curl - run: apk add curl - - name: Clone - uses: actions/checkout@v2 - - name: Build unit tests - run: | - cd unit-tests/ - cmake -Bbuild -H. && make -C build && make -C build test - - name: Generate code coverage - run: | - cd unit-tests/ - lcov --directory . -b "$(realpath build/)" --capture --initial -o coverage.base && \ - lcov --rc lcov_branch_coverage=1 --directory . -b "$(realpath build/)" --capture -o coverage.capture && \ - lcov --directory . -b "$(realpath build/)" --add-tracefile coverage.base --add-tracefile coverage.capture -o coverage.info && \ - lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/unit-tests/*' -o coverage.info && \ - genhtml coverage.info -o coverage - - - uses: actions/upload-artifact@v2 - with: - name: code-coverage - path: unit-tests/coverage - - - name: Upload to codecov.io - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./unit-tests/coverage.info - flags: unittests - name: codecov-app-aptos - fail_ci_if_error: true - verbose: true - - job_generate_doc: - name: Generate project documentation - runs-on: ubuntu-latest - - container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: HTML documentation - run: doxygen .doxygen/Doxyfile - - - uses: actions/upload-artifact@v2 - with: - name: documentation - path: doc/html - - job_functional_test_1: - name: Functional tests with LedgerComm (on Speculos) - strategy: - matrix: - include: - - model: nanos - - model: nanox - - model: nanosp - needs: - - nano_build - runs-on: ubuntu-latest - - container: - image: ghcr.io/ledgerhq/speculos:sha-45cb9a6 - options: --entrypoint /bin/bash - ports: - - 9999:9999 - - 42000:42000 - - 43000:43000 - - steps: - - name: Clone - uses: actions/checkout@v2 - - name: Download app binary - uses: actions/download-artifact@v2 - with: - name: aptos-app-${{matrix.model}} - path: bin - - name: Install dependencies - run: | - apt update && apt install -qy gcc netcat # /!\ needed when installing pysha3 - pip install -r tests/ledgercomm/requirements.txt - - name: Run test - run: | - nohup bash -c "python /speculos/speculos.py bin/app.elf --apdu-port 9999 --button-port 42000 --automation-port 43000 --display headless --model=${{ matrix.model }}" > speculos.log 2<&1 & - until `nc -w5 -z -v 127.0.0.1 9999`; do sleep 1; done; - pytest --headless tests/ledgercomm/ --model=${{ matrix.model }} - - - name: Upload Speculos log - if: ${{ always() }} - uses: actions/upload-artifact@v2 - with: - name: speculos-log - path: speculos.log - - job_functional_test_2: - name: Functional tests with Speculos only - strategy: - matrix: - include: - - model: nanos - - model: nanox - - model: nanosp - needs: - - nano_build - runs-on: ubuntu-latest - - steps: - - name: Clone - uses: actions/checkout@v2 - - name: Download app binary - uses: actions/download-artifact@v2 - with: - name: aptos-app-${{matrix.model}} - path: bin - - name: Install tests dependencies - run: | - sudo apt-get update && sudo apt-get install -y qemu-user-static tesseract-ocr - pip install --extra-index-url https://test.pypi.org/simple/ -r tests/speculos/requirements.txt - - name: Run test - env: - CTEST_OUTPUT_ON_FAILURE: 1 - run: pytest tests/speculos/ --model=${{ matrix.model }} diff --git a/.github/workflows/clang-scan.yml b/.github/workflows/clang-scan.yml new file mode 100644 index 0000000..aea467e --- /dev/null +++ b/.github/workflows/clang-scan.yml @@ -0,0 +1,32 @@ +name: Clang Static Analyzer + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + job_scan_build: + runs-on: ubuntu-latest + container: + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Build with Clang Static Analyzer + run: | + make clean + scan-build --use-cc=clang -analyze-headers -enable-checker security -enable-checker unix -enable-checker valist -o scan-build --status-bugs make default + + - name: Upload scan result + uses: actions/upload-artifact@v2 + if: failure() + with: + name: scan-build + path: scan-build diff --git a/.github/workflows/codeql-workflow.yml b/.github/workflows/codeql_checks.yml similarity index 96% rename from .github/workflows/codeql-workflow.yml rename to .github/workflows/codeql_checks.yml index 3e5b455..2494182 100644 --- a/.github/workflows/codeql-workflow.yml +++ b/.github/workflows/codeql_checks.yml @@ -4,12 +4,10 @@ on: workflow_dispatch: push: branches: + - master - main - develop pull_request: - branches: - - main - - develop # Excluded path: add the paths you want to ignore instead of deleting the workflow paths-ignore: - '.github/workflows/*.yml' diff --git a/.github/workflows/coding_style_checks.yml b/.github/workflows/coding_style_checks.yml new file mode 100644 index 0000000..85ddb84 --- /dev/null +++ b/.github/workflows/coding_style_checks.yml @@ -0,0 +1,23 @@ +name: Run coding style check through reusable workflow + +# This workflow will run linting checks to ensure a level of uniformization among all Ledger applications. +# +# The presence of this workflow is mandatory as a minimal level of linting is required. + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + check_linting: + name: Check linting using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_lint.yml@v1 + with: + source: './src' + extensions: 'h,c' + version: 11 diff --git a/.github/workflows/documentation_generation.yml b/.github/workflows/documentation_generation.yml new file mode 100644 index 0000000..1d00c2e --- /dev/null +++ b/.github/workflows/documentation_generation.yml @@ -0,0 +1,29 @@ +name: Generate project documentation + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + job_generate_doc: + name: Generate project documentation + runs-on: ubuntu-latest + container: + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest + + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: HTML documentation + run: doxygen .doxygen/Doxyfile + + - uses: actions/upload-artifact@v3 + with: + name: documentation + path: doc/html diff --git a/.github/workflows/guidelines_enforcer.yml b/.github/workflows/guidelines_enforcer.yml index f1c6db2..fdaf9f2 100644 --- a/.github/workflows/guidelines_enforcer.yml +++ b/.github/workflows/guidelines_enforcer.yml @@ -21,6 +21,3 @@ jobs: guidelines_enforcer: name: Call Ledger guidelines_enforcer uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_guidelines_enforcer.yml@v1 - with: - run_for_devices: '["nanos", "nanox", "nanosp"]' - diff --git a/.github/workflows/lint-workflow.yml b/.github/workflows/lint-workflow.yml deleted file mode 100644 index 413d375..0000000 --- a/.github/workflows/lint-workflow.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Code style check - -on: - workflow_dispatch: - push: - branches: - - main - - develop - pull_request: - branches: - - main - - develop - -jobs: - job_lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - name: Clone - uses: actions/checkout@v2 - - - name: Lint - uses: DoozyX/clang-format-lint-action@v0.11 - with: - source: './src' - extensions: 'h,c' - clangFormatVersion: 11 - - misspell: - name: Check misspellings - runs-on: ubuntu-latest - steps: - - name: Clone - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Check misspellings - uses: codespell-project/actions-codespell@2391250ab05295bddd51e36a8c6295edb6343b0e - with: - builtin: clear,rare - check_filenames: true diff --git a/.github/workflows/misspellings_checks.yml b/.github/workflows/misspellings_checks.yml new file mode 100644 index 0000000..cad279b --- /dev/null +++ b/.github/workflows/misspellings_checks.yml @@ -0,0 +1,26 @@ +name: Misspellings checks + +# This workflow performs some misspelling checks on the repository + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + misspell: + name: Check misspellings + runs-on: ubuntu-latest + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: Check misspellings + uses: codespell-project/actions-codespell@v1 + with: + builtin: clear,rare + check_filenames: true diff --git a/.github/workflows/python_client_checks.yml b/.github/workflows/python_client_checks.yml new file mode 100644 index 0000000..98da256 --- /dev/null +++ b/.github/workflows/python_client_checks.yml @@ -0,0 +1,42 @@ +name: Checks on the Python client + +# This workflow performs some checks on the Python client used by the Aptos tests + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + + lint: + name: Aptos client linting + runs-on: ubuntu-latest + steps: + - name: Clone + uses: actions/checkout@v3 + - name: Installing PIP dependencies + run: | + pip install pylint + pip install --extra-index-url https://test.pypi.org/simple/ -r tests/requirements.txt + - name: Lint Python code + run: | + pylint --rc tests/setup.cfg tests/application_client/ + + mypy: + name: Type checking + runs-on: ubuntu-latest + steps: + - name: Clone + uses: actions/checkout@v3 + - name: Installing PIP dependencies + run: | + pip install mypy + pip install --extra-index-url https://test.pypi.org/simple/ -r tests/requirements.txt + - name: Mypy type checking + run: | + mypy tests/application_client/ diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 0000000..e779fef --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,57 @@ +name: Unit testing with Codecov coverage checking + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + job_unit_test: + name: Unit test + runs-on: ubuntu-latest + container: + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest + + steps: + - name: Clone + uses: actions/checkout@v3 + + - name: Clone SDK + uses: actions/checkout@v3 + with: + repository: ledgerHQ/ledger-secure-sdk + path: sdk + + - name: Build unit tests + run: | + cd unit-tests/ + export BOLOS_SDK=../sdk + cmake -Bbuild -H. && make -C build && make -C build test + + - name: Generate code coverage + run: | + cd unit-tests/ + lcov --directory . -b "$(realpath build/)" --capture --initial -o coverage.base && \ + lcov --rc lcov_branch_coverage=1 --directory . -b "$(realpath build/)" --capture -o coverage.capture && \ + lcov --directory . -b "$(realpath build/)" --add-tracefile coverage.base --add-tracefile coverage.capture -o coverage.info && \ + lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/unit-tests/*' -o coverage.info && \ + genhtml coverage.info -o coverage + + - uses: actions/upload-artifact@v3 + with: + name: code-coverage + path: unit-tests/coverage + + - name: Upload to codecov.io + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./unit-tests/coverage.info + flags: unittests + name: codecov-app-aptos + fail_ci_if_error: true + verbose: true diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 2234da8..82f13c1 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,6 +1,6 @@ { "env": { - "BOLOS_SDK": "/opt/nanosplus-secure-sdk", + "BOLOS_SDK": "/opt/ledger-secure-sdk", "BOLOS_ENV": "/opt/bolos-devenv" }, "configurations": [ @@ -11,9 +11,16 @@ "${workspaceFolder}/src", "${env:BOLOS_SDK}/include/*", "${env:BOLOS_SDK}/lib_ux/include/*", - "${env:BOLOS_SDK}/lib_cxng/include/*" + "${env:BOLOS_SDK}/lib_cxng/include/*", + "${env:BOLOS_SDK}/lib_bagl/include/*", + "${env:BOLOS_SDK}/lib_nbgl/include/*", + "${env:BOLOS_SDK}/lib_standard_app/*", + "${env:BOLOS_SDK}/target/nanos2/include", + // "./build/nanos2/gen_src" + "./build/stax/gen_src" ], "defines": [ + "HAVE_PIEZO_SOUND", "TARGET_NANOS", "OS_IO_SEPROXYHAL", "HAVE_ECC", @@ -27,6 +34,7 @@ "HAVE_ECC_WEIERSTRASS", "HAVE_SECP256K1_CURVE", "HAVE_BAGL", + "HAVE_NBGL", "HAVE_SPRINTF", "HAVE_IO_USB", "HAVE_L4_USBLIB", diff --git a/CHANGELOG.md b/CHANGELOG.md index b8caf07..e1656c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,110 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +## [0.6.9] - 2024-01-23 + +### Added + +- [Nano S/SP/X] Always inform about the blind signing, even if the option is activated in the settings. +- [Nano S/SP/X] Invite users to enable the blind signing setting during the signature request. +- Add MAX_TRANSACTION_PACKETS to Makefile for TARGET_STAX. +- Implement the UI for Ledger Stax. + - Separate the common UI code for BAGL and NBGL. + - Put general functions into utils. + - Refactor BAGL UI (display, menu, settings). + - Implement NBGL UI (display, menu, settings). + - Add Aptos icon for Ledger Stax. +- Expand the displayed info: + - Parse `0x1::aptos_account::transfer_coins` function + - Now show "Transaction Type" for all entry_function calls + - Add support for multisig payload variant +- Add new tests. + +### Fixed + +- Fix typo on Blind Signing warning screen. +- Fix the tests, remove the obsolete ones after an upgrade. +- Use "no_throw" crypto functions instead of deprecated ones. + +### Changed + +- Upgrade to a newer version of the SDK. +- Rewrite existing tests using the Ragger framework. +- Update workflows for misspellings checks, coding style checks, documentation generation, clang static analyzer, Python client checks, CodeQL checks, build and functional tests. + +## [0.4.17] - 2023-04-18 + +### Fixed + +- Reduce the RAM allocated for large transactions. +- Remove the hard-set DEBUG=1 flag in the Makefile. + +## [0.4.15] - 2023-04-12 + +### Added + +- Implement blind signing functionality. +- Add the "Allow Blind Signing" option to the "Settings". +- Add status word for fail to display gas fee (0xB009). + +### Fixed + +- Make sure there is no THROW operator in the CATCH_OTHER block. +- Check for null characters in the middle of the message. +- Make sure the message is a null-terminated string. +- Use blind signing for shortened messages. +- Do not display leading zeros for module addresses, display only meaningful bytes for visual verification. +- Clearer work with the global state. +- Fix the operation of P1 as a sequence number of the chunks on SIGN_TX request. + +### Changed + +- Refactoring transaction displaying. Do not calculate gas fee when displaying a message. +- Get rid of the use of H as a format specifier using the snprintf function while working with the UI. + +### Removed + +- Remove unnecessary transaction hash calculations. + +## [0.3.4] - 2023-02-28 + +### Added + +- Use the maximum SRAM capacity to store transactions for each Ledger model. +- Create a "Settings" step in the menu. + - Create data types for the NVM storage. + - Add the "Show Full Message" option. + - Move the "Version" info to the "About" submenu. + +### Fixed + +- Fix a problem with signing short messages. +- Add a string length check defining well-known transaction types. +- CI: Explicitly specify the ledger-app-builder version. + +### Removed + +- Get rid of the "app-boilerplate" legacy. + +## [0.1.0] - 2023-02-10 + +### Added + +- Improve display for well-known transaction types. + +### Fixed + +- Fix the bug in the CodeQL workflow. +- Fix Fuzzing test. + +### Removed + +- Remove irrelevant code. + ## [0.0.1] - 2022-09-27 ### Added -- Initial commit +- Initial commit. diff --git a/Makefile b/Makefile index f2f1e51..ee8f490 100644 --- a/Makefile +++ b/Makefile @@ -21,40 +21,9 @@ endif include $(BOLOS_SDK)/Makefile.defines -APP_LOAD_PARAMS = --curve ed25519 -ifeq ($(TARGET_NAME), TARGET_NANOX) -APP_LOAD_PARAMS += --appFlags 0x200 # APPLICATION_FLAG_BOLOS_SETTINGS -else -APP_LOAD_PARAMS += --appFlags 0x000 -endif -APP_LOAD_PARAMS += --path "44'/637'" -APP_LOAD_PARAMS += $(COMMON_LOAD_PARAMS) - -APPNAME = "Aptos" -APPVERSION_M = 0 -APPVERSION_N = 4 -APPVERSION_P = 17 -APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" - -ifeq ($(TARGET_NAME),TARGET_NANOS) - ICONNAME=icons/nanos_app_aptos.gif -else - ICONNAME=icons/nanox_app_aptos.gif -endif - -all: default - -DEFINES += $(DEFINES_LIB) -DEFINES += APPNAME=\"$(APPNAME)\" -DEFINES += APPVERSION=\"$(APPVERSION)\" -DEFINES += MAJOR_VERSION=$(APPVERSION_M) MINOR_VERSION=$(APPVERSION_N) PATCH_VERSION=$(APPVERSION_P) -DEFINES += OS_IO_SEPROXYHAL -DEFINES += HAVE_BAGL HAVE_UX_FLOW HAVE_SPRINTF -DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=6 IO_HID_EP_LENGTH=64 HAVE_USB_APDU -DEFINES += USB_SEGMENT_SIZE=64 -DEFINES += BLE_SEGMENT_SIZE=32 -DEFINES += HAVE_WEBUSB WEBUSB_URL_SIZE_B=0 WEBUSB_URL="" -DEFINES += UNUSED\(x\)=\(void\)x +######################################## +# Aptos configuration # +######################################## ifeq ($(TARGET_NAME),TARGET_NANOS) DEFINES += MAX_TRANSACTION_PACKETS=6 @@ -65,77 +34,83 @@ endif ifeq ($(TARGET_NAME),TARGET_NANOX) DEFINES += MAX_TRANSACTION_PACKETS=90 endif - -ifeq ($(TARGET_NAME),TARGET_NANOX) - DEFINES += HAVE_BLE BLE_COMMAND_TIMEOUT_MS=2000 HAVE_BLE_APDU -endif - -ifeq ($(TARGET_NAME),TARGET_NANOS) - DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=128 -else - DEFINES += IO_SEPROXYHAL_BUFFER_SIZE_B=300 - DEFINES += HAVE_GLO096 - DEFINES += BAGL_WIDTH=128 BAGL_HEIGHT=64 - DEFINES += HAVE_BAGL_ELLIPSIS - DEFINES += HAVE_BAGL_FONT_OPEN_SANS_REGULAR_11PX - DEFINES += HAVE_BAGL_FONT_OPEN_SANS_EXTRABOLD_11PX - DEFINES += HAVE_BAGL_FONT_OPEN_SANS_LIGHT_16PX -endif - -DEBUG ?= 0 -ifneq ($(DEBUG),0) - DEFINES += HAVE_PRINTF - ifeq ($(TARGET_NAME),TARGET_NANOS) - DEFINES += PRINTF=screen_printf - else - DEFINES += PRINTF=mcu_usb_printf - endif -else - DEFINES += PRINTF\(...\)= -endif - -ifneq ($(BOLOS_ENV),) -$(info BOLOS_ENV=$(BOLOS_ENV)) -CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/ -GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/ -else -$(info BOLOS_ENV is not set: falling back to CLANGPATH and GCCPATH) -endif -ifeq ($(CLANGPATH),) -$(info CLANGPATH is not set: clang will be used from PATH) -endif -ifeq ($(GCCPATH),) -$(info GCCPATH is not set: arm-none-eabi-* will be used from PATH) +ifeq ($(TARGET_NAME),TARGET_STAX) + # still need to find the right value + DEFINES += MAX_TRANSACTION_PACKETS=70 endif -CC := $(CLANGPATH)clang -CFLAGS += -O3 -Os -AS := $(GCCPATH)arm-none-eabi-gcc -LD := $(GCCPATH)arm-none-eabi-gcc -LDFLAGS += -O3 -Os -LDLIBS += -lm -lgcc -lc +######################################## +# Mandatory configuration # +######################################## +# Application name +APPNAME = "Aptos" -include $(BOLOS_SDK)/Makefile.glyphs +# Application version +APPVERSION_M = 0 +APPVERSION_N = 6 +APPVERSION_P = 9 +APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)" +# Application source files APP_SOURCE_PATH += src -SDK_SOURCE_PATH += lib_stusb lib_stusb_impl lib_ux - -ifeq ($(TARGET_NAME),TARGET_NANOX) - SDK_SOURCE_PATH += lib_blewbxx lib_blewbxx_impl -endif - -load: all - python3 -m ledgerblue.loadApp $(APP_LOAD_PARAMS) - -load-offline: all - python3 -m ledgerblue.loadApp $(APP_LOAD_PARAMS) --offline - -delete: - python3 -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS) - -include $(BOLOS_SDK)/Makefile.rules - -dep/%.d: %.c Makefile -listvariants: - @echo VARIANTS COIN APTOS +# Application icons +ICON_NANOS = icons/app_aptos_16px.gif +ICON_NANOX = icons/app_aptos_14px.gif +ICON_NANOSP = icons/app_aptos_14px.gif +ICON_STAX = icons/app_aptos_32px.gif + +# Application allowed derivation curves +CURVE_APP_LOAD_PARAMS = ed25519 + +# Application allowed derivation paths +PATH_APP_LOAD_PARAMS = "44'/637'" + +# Setting to allow building variant applications +# - is the name of the parameter which should be set +# to specify the variant that should be build. +# - a list of variant that can be build using this app code. +# * It must at least contains one value. +# * Values can be the app ticker or anything else but should be unique. +VARIANT_PARAM = COIN +VARIANT_VALUES = APTOS + +# Enabling DEBUG flag will enable PRINTF and disable optimizations +#DEBUG = 1 + +######################################## +# Application custom permissions # +######################################## +# See SDK `include/appflags.h` for the purpose of each permission +#HAVE_APPLICATION_FLAG_DERIVE_MASTER = 1 +#HAVE_APPLICATION_FLAG_GLOBAL_PIN = 1 +#HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1 +#HAVE_APPLICATION_FLAG_LIBRARY = 1 + +######################################## +# Application communication interfaces # +######################################## +ENABLE_BLUETOOTH = 1 +#ENABLE_NFC = 1 + +######################################## +# NBGL custom features # +######################################## +ENABLE_NBGL_QRCODE = 1 +#ENABLE_NBGL_KEYBOARD = 1 +#ENABLE_NBGL_KEYPAD = 1 + +######################################## +# Features disablers # +######################################## +# These advanced settings allow to disable some feature that are by +# default enabled in the SDK `Makefile.standard_app`. +#DISABLE_STANDARD_APP_FILES = 1 +#DISABLE_DEFAULT_IO_SEPROXY_BUFFER_SIZE = 1 # To allow custom size declaration +#DISABLE_STANDARD_APP_DEFINES = 1 # Will set all the following disablers +#DISABLE_STANDARD_SNPRINTF = 1 +#DISABLE_STANDARD_USB = 1 +#DISABLE_STANDARD_WEBUSB = 1 +#DISABLE_STANDARD_BAGL_UX_FLOW = 1 + +include $(BOLOS_SDK)/Makefile.standard_app diff --git a/fuzzing/extra/TxParser.cmake b/fuzzing/extra/TxParser.cmake index 221d127..74f2fa6 100644 --- a/fuzzing/extra/TxParser.cmake +++ b/fuzzing/extra/TxParser.cmake @@ -11,20 +11,25 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror -Wall -Wextra -Wno-unused-function -DFUZZ -pedantic -g -O0" ) +set(BOLOS_SDK $ENV{BOLOS_SDK}) + add_library(txparser SHARED ${CMAKE_CURRENT_SOURCE_DIR}/../src/bcs/init.c ${CMAKE_CURRENT_SOURCE_DIR}/../src/bcs/decoder.c ${CMAKE_CURRENT_SOURCE_DIR}/../src/bcs/utf8.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/bip32.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/varint.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/read.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/write.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/buffer.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/common/format.c ${CMAKE_CURRENT_SOURCE_DIR}/../src/transaction/utils.c ${CMAKE_CURRENT_SOURCE_DIR}/../src/transaction/deserialize.c + ${BOLOS_SDK}/lib_standard_app/format.c + ${BOLOS_SDK}/lib_standard_app/buffer.c + ${BOLOS_SDK}/lib_standard_app/varint.c + ${BOLOS_SDK}/lib_standard_app/read.c + ${BOLOS_SDK}/lib_standard_app/write.c + ${BOLOS_SDK}/lib_standard_app/bip32.c ) set_target_properties(txparser PROPERTIES SOVERSION 1) -target_include_directories(txparser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../src) +target_include_directories(txparser PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../src + ${BOLOS_SDK}/lib_standard_app +) diff --git a/fuzzing/fuzz_tx_parser.cc b/fuzzing/fuzz_tx_parser.cc index df4bf5c..7694597 100644 --- a/fuzzing/fuzz_tx_parser.cc +++ b/fuzzing/fuzz_tx_parser.cc @@ -5,13 +5,15 @@ extern "C" { #include "bcs/init.h" -#include "common/buffer.h" -#include "common/format.h" +#include "buffer.h" +#include "format.h" #include "transaction/deserialize.h" #include "transaction/utils.h" #include "transaction/types.h" } +#define DEBUG 0 + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { buffer_t buf = {.ptr = data, .size = size, .offset = 0}; transaction_t tx; @@ -21,7 +23,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { transaction_init(&tx); status = transaction_deserialize(&buf, &tx); - if (status == PARSING_OK && tx.tx_variant == TX_RAW && + if (DEBUG && status == PARSING_OK && tx.tx_variant == TX_RAW && tx.payload_variant == PAYLOAD_ENTRY_FUNCTION) { printf("\nTransaction size: %lu\n", size); printf("chain_id: %d\n", tx.chain_id); diff --git a/glyphs/aptos_logo.gif b/glyphs/aptos_logo_16px.gif similarity index 100% rename from glyphs/aptos_logo.gif rename to glyphs/aptos_logo_16px.gif diff --git a/glyphs/aptos_logo_64px.gif b/glyphs/aptos_logo_64px.gif new file mode 100644 index 0000000..bd87bd2 Binary files /dev/null and b/glyphs/aptos_logo_64px.gif differ diff --git a/glyphs/icon_back.gif b/glyphs/icon_back.gif deleted file mode 100644 index a2a7e6d..0000000 Binary files a/glyphs/icon_back.gif and /dev/null differ diff --git a/glyphs/icon_back_x.gif b/glyphs/icon_back_x.gif deleted file mode 100644 index ff04361..0000000 Binary files a/glyphs/icon_back_x.gif and /dev/null differ diff --git a/glyphs/icon_certificate.gif b/glyphs/icon_certificate.gif deleted file mode 100644 index 89b529f..0000000 Binary files a/glyphs/icon_certificate.gif and /dev/null differ diff --git a/glyphs/icon_coggle.gif b/glyphs/icon_coggle.gif deleted file mode 100644 index 01c43b2..0000000 Binary files a/glyphs/icon_coggle.gif and /dev/null differ diff --git a/glyphs/icon_crossmark.gif b/glyphs/icon_crossmark.gif deleted file mode 100644 index 2dcf9d9..0000000 Binary files a/glyphs/icon_crossmark.gif and /dev/null differ diff --git a/glyphs/icon_dashboard.gif b/glyphs/icon_dashboard.gif deleted file mode 100644 index 5c30551..0000000 Binary files a/glyphs/icon_dashboard.gif and /dev/null differ diff --git a/glyphs/icon_dashboard_x.gif b/glyphs/icon_dashboard_x.gif deleted file mode 100644 index 33d9b0a..0000000 Binary files a/glyphs/icon_dashboard_x.gif and /dev/null differ diff --git a/glyphs/icon_down.gif b/glyphs/icon_down.gif deleted file mode 100644 index 4f4e39e..0000000 Binary files a/glyphs/icon_down.gif and /dev/null differ diff --git a/glyphs/icon_eye.gif b/glyphs/icon_eye.gif deleted file mode 100644 index df4bb82..0000000 Binary files a/glyphs/icon_eye.gif and /dev/null differ diff --git a/glyphs/icon_left.gif b/glyphs/icon_left.gif deleted file mode 100644 index 524226b..0000000 Binary files a/glyphs/icon_left.gif and /dev/null differ diff --git a/glyphs/icon_processing.gif b/glyphs/icon_processing.gif deleted file mode 100644 index 8fe937f..0000000 Binary files a/glyphs/icon_processing.gif and /dev/null differ diff --git a/glyphs/icon_right.gif b/glyphs/icon_right.gif deleted file mode 100644 index 15ff3cf..0000000 Binary files a/glyphs/icon_right.gif and /dev/null differ diff --git a/glyphs/icon_up.gif b/glyphs/icon_up.gif deleted file mode 100644 index 4e13c06..0000000 Binary files a/glyphs/icon_up.gif and /dev/null differ diff --git a/glyphs/icon_validate_14.gif b/glyphs/icon_validate_14.gif deleted file mode 100644 index ccb5cab..0000000 Binary files a/glyphs/icon_validate_14.gif and /dev/null differ diff --git a/glyphs/icon_warning.gif b/glyphs/icon_warning.gif deleted file mode 100644 index 08bd4a7..0000000 Binary files a/glyphs/icon_warning.gif and /dev/null differ diff --git a/icons/nanox_app_aptos.gif b/icons/app_aptos_14px.gif similarity index 100% rename from icons/nanox_app_aptos.gif rename to icons/app_aptos_14px.gif diff --git a/icons/nanos_app_aptos.gif b/icons/app_aptos_16px.gif similarity index 100% rename from icons/nanos_app_aptos.gif rename to icons/app_aptos_16px.gif diff --git a/icons/app_aptos_32px.gif b/icons/app_aptos_32px.gif new file mode 100644 index 0000000..3869066 Binary files /dev/null and b/icons/app_aptos_32px.gif differ diff --git a/ledger_app.toml b/ledger_app.toml new file mode 100644 index 0000000..e557cc8 --- /dev/null +++ b/ledger_app.toml @@ -0,0 +1,8 @@ +[app] +build_directory = "./" +sdk = "C" +devices = ["nanos", "nanox", "nanos+", "stax"] + +[tests] +unit_directory = "./unit-tests/" +pytest_directory = "./tests/" diff --git a/src/address.c b/src/address.c index e74baa1..c13eb30 100644 --- a/src/address.c +++ b/src/address.c @@ -36,12 +36,43 @@ bool address_from_pubkey(const uint8_t public_key[static 32], uint8_t *out, size } cx_sha3_t sha3; - cx_sha3_init(&sha3, 256); - cx_hash_update((cx_hash_t *) &sha3, public_key, 32); - cx_hash_update((cx_hash_t *) &sha3, &signature_scheme_id, 1); - cx_hash_final((cx_hash_t *) &sha3, address); + cx_err_t error = cx_sha3_init_no_throw(&sha3, 256); + if (error != CX_OK) { + return false; + } + error = cx_hash_update((cx_hash_t *) &sha3, public_key, 32); + if (error != CX_OK) { + return false; + } + error = cx_hash_update((cx_hash_t *) &sha3, &signature_scheme_id, 1); + if (error != CX_OK) { + return false; + } + error = cx_hash_final((cx_hash_t *) &sha3, address); + if (error != CX_OK) { + return false; + } memmove(out, address, ADDRESS_LEN); return true; } + +bool validate_aptos_bip32_path(const uint32_t *path, size_t path_len) { + // m/purpose'/coin_type'/account'/change/address_index + // m/44' /637' /0' /0' /0' + const uint32_t aptos_prefix[2] = {0x8000002C, 0x8000027D}; + + // A 3-element HD path limit (`m/44'/637'/x'`) is enforced for: + // 1. BIP44 compliance, ensuring proper structure (`m/44'/637'` - purpose and coin type). + // 2. Operational flexibility, allowing basic account-level (`x'`) fund segregation. + // 3. A balance between security and usability, preventing the potential privacy and security + // risks associated with using an overly simple path (e.g., `m/44'/637'`), while not + // requiring the full 5-element path that may be unnecessary for users seeking + // straightforward wallet functionality. + if (path_len < 3) { + return false; + } + + return memcmp(path, aptos_prefix, sizeof(aptos_prefix)) == 0; +} diff --git a/src/address.h b/src/address.h index e676996..c81bc8b 100644 --- a/src/address.h +++ b/src/address.h @@ -21,3 +21,16 @@ * */ bool address_from_pubkey(const uint8_t public_key[static 32], uint8_t *out, size_t out_len); + +/** + * Validates BIP32 derivation path for Aptos. + * + * @param[in] path + * Pointer to an array of uint32_t representing the BIP32 path. + * @param[in] path_len + * The number of elements in the `path` array. Must be at least 3 for the function to succeed. + * + * @return true if success, false otherwise. + * + */ +bool validate_aptos_bip32_path(const uint32_t *path, size_t path_len); diff --git a/src/apdu/dispatcher.c b/src/apdu/dispatcher.c index 934cbbc..be49d6e 100644 --- a/src/apdu/dispatcher.c +++ b/src/apdu/dispatcher.c @@ -18,13 +18,14 @@ #include #include +#include "buffer.h" +#include "io.h" + #include "dispatcher.h" #include "../constants.h" #include "../globals.h" #include "../types.h" -#include "../io.h" #include "../sw.h" -#include "../common/buffer.h" #include "../handler/get_version.h" #include "../handler/get_app_name.h" #include "../handler/get_public_key.h" diff --git a/src/apdu/dispatcher.h b/src/apdu/dispatcher.h index 2847bb2..5eb297c 100644 --- a/src/apdu/dispatcher.h +++ b/src/apdu/dispatcher.h @@ -1,5 +1,7 @@ #pragma once +#include "parser.h" + #include "../types.h" /** diff --git a/src/apdu/parser.h b/src/apdu/parser.h deleted file mode 100644 index dfba3fd..0000000 --- a/src/apdu/parser.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include // size_t -#include // uint*_t -#include // bool - -#include "../types.h" - -/** - * Parse APDU command from byte buffer. - * - * @param[out] cmd - * Structured APDU command (CLA, INS, P1, P2, Lc, Command data). - * @param[in] buf - * Byte buffer with raw APDU command. - * @param[in] buf_len - * Length of byte buffer. - * - * @return true if success, false otherwise. - * - */ -bool apdu_parser(command_t *cmd, uint8_t *buf, size_t buf_len); diff --git a/src/main.c b/src/app_main.c similarity index 69% rename from src/main.c rename to src/app_main.c index 87b3e60..7f0c724 100644 --- a/src/main.c +++ b/src/app_main.c @@ -26,15 +26,21 @@ #include "io.h" #include "sw.h" #include "ui/menu.h" -#include "apdu/parser.h" #include "apdu/dispatcher.h" -uint8_t G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; -ux_state_t G_ux; -bolos_ux_params_t G_ux_params; global_ctx_t G_context; const app_storage_t N_app_storage; +void nvm_app_storage_init() { + if (N_storage.initialized != 0x01) { + app_storage_t storage; + storage.settings.show_full_message = 0x00; + storage.settings.allow_blind_signing = 0x00; + storage.initialized = 0x01; + nvm_write((void *) &N_storage, (void *) &storage, sizeof(app_storage_t)); + } +} + /** * Handle APDU command received and send back APDU response using handlers. */ @@ -45,6 +51,8 @@ void app_main() { command_t cmd; io_init(); + nvm_app_storage_init(); + ui_menu_main(); // Reset context explicit_bzero(&G_context, sizeof(G_context)); @@ -96,78 +104,3 @@ void app_main() { } } } - -/** - * Exit the application and go back to the dashboard. - */ -void app_exit() { - BEGIN_TRY_L(exit) { - TRY_L(exit) { - os_sched_exit(-1); - } - FINALLY_L(exit) { - } - } - END_TRY_L(exit); -} - -void nvm_app_storage_init() { - if (N_storage.initialized != 0x01) { - app_storage_t storage; - storage.settings.show_full_message = 0x00; - storage.settings.allow_blind_signing = 0x00; - storage.initialized = 0x01; - nvm_write((void *) &N_storage, (void *) &storage, sizeof(app_storage_t)); - } -} - -/** - * Main loop to setup USB, Bluetooth, UI and launch app_main(). - */ -__attribute__((section(".boot"))) int main() { - __asm volatile("cpsie i"); - - os_boot(); - - for (;;) { - // Reset UI - memset(&G_ux, 0, sizeof(G_ux)); - - BEGIN_TRY { - TRY { - io_seproxyhal_init(); - nvm_app_storage_init(); - -#ifdef TARGET_NANOX - G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0); -#endif // TARGET_NANOX - - USB_power(0); - USB_power(1); - - ui_menu_main(); - -#ifdef HAVE_BLE - BLE_power(0, NULL); - BLE_power(1, "Nano X"); -#endif // HAVE_BLE - app_main(); - } - CATCH(EXCEPTION_IO_RESET) { - CLOSE_TRY; - continue; - } - CATCH_ALL { - CLOSE_TRY; - break; - } - FINALLY { - } - } - END_TRY; - } - - app_exit(); - - return 0; -} diff --git a/src/bcs/decoder.c b/src/bcs/decoder.c index 3a78607..fb42c98 100644 --- a/src/bcs/decoder.c +++ b/src/bcs/decoder.c @@ -225,92 +225,3 @@ bool bcs_read_type_tag_fixed(buffer_t *buffer, type_tag_t *ty_val) { return false; } } - -/* TODO: optimize memory handling before use -bool bcs_read_type_tag_vector(buffer_t *buffer, type_tag_t *vector_val) { - if (!bcs_read_u32_from_uleb128(buffer, (uint32_t *) vector_val->size)) { - return false; - } - vector_val->value = (type_tag_t *) malloc(sizeof(type_tag_t) * vector_val->size); - for (size_t i = 0; i < vector_val->size; i++) { - if (!bcs_read_u32_from_uleb128(buffer, (uint32_t *) (*(type_tag_t *) -vector_val->value).type_tag)) { return false; - } - if ((*(type_tag_t *) vector_val->value).type_tag < TYPE_TAG_VECTOR) { - if (!bcs_read_type_tag_fixed(buffer, &(*(type_tag_t *) vector_val->value))) { - return false; - } - } else if ((*(type_tag_t *) vector_val->value).type_tag == TYPE_TAG_VECTOR) { - if (!bcs_read_type_tag_vector(buffer, &(*(type_tag_t *) vector_val->value))) { - return false; - } - } else if ((*(type_tag_t *) vector_val->value).type_tag == TYPE_TAG_STRUCT) { - (*(type_tag_t *) vector_val->value).size = sizeof(type_tag_struct_t); - (*(type_tag_t *) vector_val->value).value = (type_tag_struct_t *) -malloc(sizeof(type_tag_struct_t)); type_tag_struct_init((*(type_tag_t *) vector_val->value).value); - - if (!bcs_read_type_tag_struct(buffer, (*(type_tag_t *) vector_val->value).value)) { - return false; - } - } - } - return true; -} -*/ - -/* TODO: optimize memory handling before use -bool bcs_read_type_tag_struct(buffer_t *buffer, type_tag_struct_t *ty_struct) { - if (!bcs_read_fixed_bytes(buffer, (uint8_t *) &ty_struct->address, ADDRESS_LEN)) { - return false; - } - if (!bcs_read_u32_from_uleb128(buffer, (uint32_t *) &ty_struct->module_name.len)) { - return false; - } - ty_struct->module_name.bytes = (uint8_t *) malloc(sizeof(uint8_t) * ty_struct->module_name.len); - if (!bcs_read_fixed_bytes(buffer, (uint8_t *) &ty_struct->module_name.bytes, -ty_struct->module_name.len)) { return false; - } - if (!bcs_read_u32_from_uleb128(buffer, (uint32_t *) &ty_struct->name.len)) { - return false; - } - ty_struct->name.bytes = (uint8_t *) malloc(sizeof(uint8_t) * ty_struct->name.len); - if (!bcs_read_fixed_bytes(buffer, (uint8_t *) &ty_struct->name.bytes, ty_struct->name.len)) { - return false; - } - if (!bcs_read_u32_from_uleb128(buffer, (uint32_t *) &ty_struct->type_args_size)) { - return false; - } - if (ty_struct->type_args_size > 0) { - ty_struct->type_args = (type_tag_t *) malloc(sizeof(type_tag_t) * -ty_struct->type_args_size); for (size_t i = 0; i < ty_struct->type_args_size; i++) { - ty_struct->type_args[i].type_tag = 0; - ty_struct->type_args[i].size = 0; - ty_struct->type_args[i].value = NULL; - - if (!bcs_read_u32_from_uleb128(buffer, (uint32_t *) &ty_struct->type_args[i].type_tag)) -{ return false; - } - if (ty_struct->type_args[i].type_tag < TYPE_TAG_VECTOR) { - if (!bcs_read_type_tag_fixed(buffer, (type_tag_t *) &ty_struct->type_args[i].value)) -{ return false; - } - } else if (ty_struct->type_args[i].type_tag == TYPE_TAG_VECTOR) { - if (!bcs_read_type_tag_vector(buffer, &ty_struct->type_args[i])) { - return false; - } - } else if (ty_struct->type_args[i].type_tag == TYPE_TAG_STRUCT) { - ty_struct->type_args[i].size = sizeof(type_tag_struct_t); - ty_struct->type_args[i].value = (type_tag_struct_t *) -malloc(sizeof(type_tag_struct_t)); type_tag_struct_init(ty_struct->type_args[i].value); - - if (!bcs_read_type_tag_struct(buffer, ty_struct->type_args[i].value)) { - return false; - } - } else { - return false; - } - } - } - return true; -} -*/ diff --git a/src/bcs/decoder.h b/src/bcs/decoder.h index feb09d9..a1458d2 100644 --- a/src/bcs/decoder.h +++ b/src/bcs/decoder.h @@ -1,7 +1,8 @@ #pragma once +#include "buffer.h" + #include "types.h" -#include "../common/buffer.h" bool bcs_read_bool(buffer_t *buffer, bool *value); bool bcs_read_option_tag(buffer_t *buffer, bool *value); diff --git a/src/bcs/types.h b/src/bcs/types.h index 85c9de4..9315f3c 100644 --- a/src/bcs/types.h +++ b/src/bcs/types.h @@ -78,7 +78,8 @@ typedef struct { typedef enum { FUNC_UNKNOWN = 0, FUNC_APTOS_ACCOUNT_TRANSFER = 1, - FUNC_COIN_TRANSFER = 2 + FUNC_COIN_TRANSFER = 2, + FUNC_APTOS_ACCOUNT_TRANSFER_COINS = 3, } entry_function_known_type_t; typedef struct { @@ -120,11 +121,18 @@ typedef struct { fixed_bytes_t *args; } script_payload_t; -typedef enum { TX_RAW = 0, TX_RAW_WITH_DATA = 1, TX_MESSAGE = 2, TX_UNDEFINED = 1000 } tx_variant_t; +typedef enum { + TX_RAW = 0, + TX_RAW_WITH_DATA = 1, + TX_MESSAGE = 2, + TX_RAW_MESSAGE = 3, + TX_UNDEFINED = 1000 +} tx_variant_t; typedef enum { PAYLOAD_SCRIPT = 0, PAYLOAD_ENTRY_FUNCTION = 2, + PAYLOAD_MULTISIG = 3, PAYLOAD_UNDEFINED = 1000 } payload_variant_t; diff --git a/src/bcs/utf8.c b/src/bcs/utf8.c index bc486f0..b19aef1 100644 --- a/src/bcs/utf8.c +++ b/src/bcs/utf8.c @@ -79,7 +79,6 @@ int try_utf8_to_ascii(const uint8_t *in, return -1; } } - // out[out_len++] = '\0'; if (out_is_utf8) { *out_is_utf8 = is_utf8; diff --git a/src/common/base58.c b/src/common/base58.c deleted file mode 100644 index 4c15dbd..0000000 --- a/src/common/base58.c +++ /dev/null @@ -1,155 +0,0 @@ -/***************************************************************************** - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // size_t -#include // uint*_t -#include // memmove, memset -#include // bool - -#include "base58.h" - -uint8_t const BASE58_TABLE[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // - 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xFF, 0xFF, // - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // - 0x10, 0xFF, 0x11, 0x12, 0x13, 0x14, 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, // - 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // - 0xFF, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, // - 0xFF, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, // - 0x37, 0x38, 0x39, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // -}; - -char const BASE58_ALPHABET[] = { - '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', // - 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', // - 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', // - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' // -}; - -int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len) { - uint8_t tmp[MAX_DEC_INPUT_SIZE] = {0}; - uint8_t buffer[MAX_DEC_INPUT_SIZE] = {0}; - uint8_t j; - uint8_t start_at; - uint8_t zero_count = 0; - - if (in_len > MAX_DEC_INPUT_SIZE || in_len < 2) { - return -1; - } - - memmove(tmp, in, in_len); - - for (uint8_t i = 0; i < in_len; i++) { - if (in[i] >= sizeof(BASE58_TABLE)) { - return -1; - } - - tmp[i] = BASE58_TABLE[(int) in[i]]; - - if (tmp[i] == 0xFF) { - return -1; - } - } - - while ((zero_count < in_len) && (tmp[zero_count] == 0)) { - ++zero_count; - } - - j = in_len; - start_at = zero_count; - while (start_at < in_len) { - uint16_t remainder = 0; - for (uint8_t div_loop = start_at; div_loop < in_len; div_loop++) { - uint16_t digit256 = (uint16_t)(tmp[div_loop] & 0xFF); - uint16_t tmp_div = remainder * 58 + digit256; - tmp[div_loop] = (uint8_t)(tmp_div / 256); - remainder = tmp_div % 256; - } - - if (tmp[start_at] == 0) { - ++start_at; - } - - buffer[--j] = (uint8_t) remainder; - } - - while ((j < in_len) && (buffer[j] == 0)) { - ++j; - } - - int length = in_len - (j - zero_count); - - if ((int) out_len < length) { - return -1; - } - - memmove(out, buffer + j - zero_count, length); - - return length; -} - -int base58_encode(const uint8_t *in, size_t in_len, char *out, size_t out_len) { - uint8_t buffer[MAX_ENC_INPUT_SIZE * 138 / 100 + 1] = {0}; - size_t i, j; - size_t stop_at; - size_t zero_count = 0; - size_t output_size; - - if (in_len > MAX_ENC_INPUT_SIZE) { - return -1; - } - - while ((zero_count < in_len) && (in[zero_count] == 0)) { - ++zero_count; - } - - output_size = (in_len - zero_count) * 138 / 100 + 1; - stop_at = output_size - 1; - for (size_t start_at = zero_count; start_at < in_len; start_at++) { - int carry = in[start_at]; - for (j = output_size - 1; (int) j >= 0; j--) { - carry += 256 * buffer[j]; - buffer[j] = carry % 58; - carry /= 58; - - if (j <= stop_at - 1 && carry == 0) { - break; - } - } - stop_at = j; - } - - j = 0; - while (j < output_size && buffer[j] == 0) { - j += 1; - } - - if (out_len < zero_count + output_size - j) { - return -1; - } - - memset(out, BASE58_ALPHABET[0], zero_count); - - i = zero_count; - while (j < output_size) { - out[i++] = BASE58_ALPHABET[buffer[j++]]; - } - - return i; -} diff --git a/src/common/base58.h b/src/common/base58.h deleted file mode 100644 index f214afd..0000000 --- a/src/common/base58.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include // size_t -#include // uint*_t -#include // bool - -/** - * Maximum length of input when decoding in base 58. - */ -#define MAX_DEC_INPUT_SIZE 164 -/** - * Maximum length of input when encoding in base 58. - */ -#define MAX_ENC_INPUT_SIZE 120 - -/** - * Decode input string in base 58. - * - * @see https://tools.ietf.org/html/draft-msporny-base58-02 - * - * @param[in] in - * Pointer to input string buffer. - * @param[in] in_len - * Length of the input string buffer. - * @param[out] out - * Pointer to output byte buffer. - * @param[in] out_len - * Maximum length to write in output byte buffer. - * - * @return number of bytes decoded, -1 otherwise. - * - */ -int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len); - -/** - * Encode input bytes in base 58. - * - * @see https://tools.ietf.org/html/draft-msporny-base58-02 - * - * @param[in] in - * Pointer to input byte buffer. - * @param[in] in_len - * Length of the input byte buffer. - * @param[out] out - * Pointer to output string buffer. - * @param[in] out_len - * Maximum length to write in output byte buffer. - * - * @return number of bytes encoded, -1 otherwise. - * - */ -int base58_encode(const uint8_t *in, size_t in_len, char *out, size_t out_len); diff --git a/src/common/bip32.c b/src/common/bip32.c deleted file mode 100644 index a6ac6e5..0000000 --- a/src/common/bip32.c +++ /dev/null @@ -1,87 +0,0 @@ -/***************************************************************************** - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // snprintf -#include // memset, strlen -#include // size_t -#include // uint*_t -#include // bool - -#include "bip32.h" -#include "read.h" - -bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out_len) { - if (out_len == 0 || out_len > MAX_BIP32_PATH) { - return false; - } - - size_t offset = 0; - - for (size_t i = 0; i < out_len; i++) { - if (offset > in_len) { - return false; - } - out[i] = read_u32_be(in, offset); - offset += 4; - } - - return true; -} - -bool bip32_path_format(const uint32_t *bip32_path, - size_t bip32_path_len, - char *out, - size_t out_len) { - if (bip32_path_len == 0 || bip32_path_len > MAX_BIP32_PATH) { - return false; - } - - size_t offset = 0; - - for (uint16_t i = 0; i < bip32_path_len; i++) { - size_t written; - - snprintf(out + offset, out_len - offset, "%d", bip32_path[i] & 0x7FFFFFFFu); - written = strlen(out + offset); - if (written == 0 || written >= out_len - offset) { - memset(out, 0, out_len); - return false; - } - offset += written; - - if ((bip32_path[i] & 0x80000000u) != 0) { - snprintf(out + offset, out_len - offset, "'"); - written = strlen(out + offset); - if (written == 0 || written >= out_len - offset) { - memset(out, 0, out_len); - return false; - } - offset += written; - } - - if (i != bip32_path_len - 1) { - snprintf(out + offset, out_len - offset, "/"); - written = strlen(out + offset); - if (written == 0 || written >= out_len - offset) { - memset(out, 0, out_len); - return false; - } - offset += written; - } - } - - return true; -} diff --git a/src/common/bip32.h b/src/common/bip32.h deleted file mode 100644 index 3e2491e..0000000 --- a/src/common/bip32.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include // size_t -#include // uint*_t -#include // bool - -/** - * Maximum length of BIP32 path allowed. - */ -#define MAX_BIP32_PATH 10 - -/** - * Read BIP32 path from byte buffer. - * - * @param[in] in - * Pointer to input byte buffer. - * @param[in] in_len - * Length of input byte buffer. - * @param[out] out - * Pointer to output 32-bit integer buffer. - * @param[in] out_len - * Number of BIP32 paths read in the output buffer. - * - * @return true if success, false otherwise. - * - */ -bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out_len); - -/** - * Format BIP32 path as string. - * - * @param[in] bip32_path - * Pointer to 32-bit integer input buffer. - * @param[in] bip32_path_len - * Maximum number of BIP32 paths in the input buffer. - * @param[out] out string - * Pointer to output string. - * @param[in] out_len - * Length of the output string. - * - * @return true if success, false otherwise. - * - */ -bool bip32_path_format(const uint32_t *bip32_path, - size_t bip32_path_len, - char *out, - size_t out_len); diff --git a/src/common/buffer.c b/src/common/buffer.c deleted file mode 100644 index 3bd617b..0000000 --- a/src/common/buffer.c +++ /dev/null @@ -1,165 +0,0 @@ -/***************************************************************************** - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // uint*_t -#include // size_t -#include // bool -#include // memmove - -#include "buffer.h" -#include "read.h" -#include "varint.h" -#include "bip32.h" - -bool buffer_can_read(const buffer_t *buffer, size_t n) { - return buffer->size - buffer->offset >= n; -} - -bool buffer_seek_set(buffer_t *buffer, size_t offset) { - if (offset > buffer->size) { - return false; - } - - buffer->offset = offset; - - return true; -} - -bool buffer_seek_cur(buffer_t *buffer, size_t offset) { - if (buffer->offset + offset < buffer->offset || // overflow - buffer->offset + offset > buffer->size) { // exceed buffer size - return false; - } - - buffer->offset += offset; - - return true; -} - -bool buffer_seek_end(buffer_t *buffer, size_t offset) { - if (offset > buffer->size) { - return false; - } - - buffer->offset = buffer->size - offset; - - return true; -} - -bool buffer_read_u8(buffer_t *buffer, uint8_t *value) { - if (!buffer_can_read(buffer, 1)) { - *value = 0; - - return false; - } - - *value = buffer->ptr[buffer->offset]; - buffer_seek_cur(buffer, 1); - - return true; -} - -bool buffer_read_u16(buffer_t *buffer, uint16_t *value, endianness_t endianness) { - if (!buffer_can_read(buffer, 2)) { - *value = 0; - - return false; - } - - *value = ((endianness == BE) ? read_u16_be(buffer->ptr, buffer->offset) - : read_u16_le(buffer->ptr, buffer->offset)); - - buffer_seek_cur(buffer, 2); - - return true; -} - -bool buffer_read_u32(buffer_t *buffer, uint32_t *value, endianness_t endianness) { - if (!buffer_can_read(buffer, 4)) { - *value = 0; - - return false; - } - - *value = ((endianness == BE) ? read_u32_be(buffer->ptr, buffer->offset) - : read_u32_le(buffer->ptr, buffer->offset)); - - buffer_seek_cur(buffer, 4); - - return true; -} - -bool buffer_read_u64(buffer_t *buffer, uint64_t *value, endianness_t endianness) { - if (!buffer_can_read(buffer, 8)) { - *value = 0; - - return false; - } - - *value = ((endianness == BE) ? read_u64_be(buffer->ptr, buffer->offset) - : read_u64_le(buffer->ptr, buffer->offset)); - - buffer_seek_cur(buffer, 8); - - return true; -} - -bool buffer_read_varint(buffer_t *buffer, uint64_t *value) { - int length = varint_read(buffer->ptr + buffer->offset, buffer->size - buffer->offset, value); - - if (length < 0) { - *value = 0; - - return false; - } - - buffer_seek_cur(buffer, (size_t) length); - - return true; -} - -bool buffer_read_bip32_path(buffer_t *buffer, uint32_t *out, size_t out_len) { - if (!bip32_path_read(buffer->ptr + buffer->offset, - buffer->size - buffer->offset, - out, - out_len)) { - return false; - } - - buffer_seek_cur(buffer, sizeof(*out) * out_len); - - return true; -} - -bool buffer_copy(const buffer_t *buffer, uint8_t *out, size_t out_len) { - if (buffer->size - buffer->offset > out_len) { - return false; - } - - memmove(out, buffer->ptr + buffer->offset, buffer->size - buffer->offset); - - return true; -} - -bool buffer_move(buffer_t *buffer, uint8_t *out, size_t out_len) { - if (!buffer_copy(buffer, out, out_len)) { - return false; - } - - buffer_seek_cur(buffer, out_len); - - return true; -} diff --git a/src/common/buffer.h b/src/common/buffer.h deleted file mode 100644 index ae3e829..0000000 --- a/src/common/buffer.h +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once - -#include // uint*_t -#include // size_t -#include // bool - -/** - * Enumeration for endianness. - */ -typedef enum { - BE, /// Big Endian - LE /// Little Endian -} endianness_t; - -/** - * Struct for buffer with size and offset. - */ -typedef struct { - const uint8_t *ptr; /// Pointer to byte buffer - size_t size; /// Size of byte buffer - size_t offset; /// Offset in byte buffer -} buffer_t; - -/** - * Tell whether buffer can read bytes or not. - * - * @param[in] buffer - * Pointer to input buffer struct. - * @param[in] n - * Number of bytes to read in buffer. - * - * @return true if success, false otherwise. - * - */ -bool buffer_can_read(const buffer_t *buffer, size_t n); - -/** - * Seek the buffer to specific offset. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[in] offset - * Specific offset to seek. - * - * @return true if success, false otherwise. - * - */ -bool buffer_seek_set(buffer_t *buffer, size_t offset); - -/** - * Seek buffer relatively to current offset. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[in] offset - * Offset to seek relatively to `buffer->offset`. - * - * @return true if success, false otherwise. - * - */ -bool buffer_seek_cur(buffer_t *buffer, size_t offset); - -/** - * Seek the buffer relatively to the end. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[in] offset - * Offset to seek relatively to `buffer->size`. - * - * @return true if success, false otherwise. - * - */ -bool buffer_seek_end(buffer_t *buffer, size_t offset); - -/** - * Read 1 byte from buffer into uint8_t. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[out] value - * Pointer to 8-bit unsigned integer read from buffer. - * - * @return true if success, false otherwise. - * - */ -bool buffer_read_u8(buffer_t *buffer, uint8_t *value); - -/** - * Read 2 bytes from buffer into uint16_t. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[out] value - * Pointer to 16-bit unsigned integer read from buffer. - * @param[in] endianness - * Either BE (Big Endian) or LE (Little Endian). - * - * @return true if success, false otherwise. - * - */ -bool buffer_read_u16(buffer_t *buffer, uint16_t *value, endianness_t endianness); - -/** - * Read 4 bytes from buffer into uint32_t. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[out] value - * Pointer to 32-bit unsigned integer read from buffer. - * @param[in] endianness - * Either BE (Big Endian) or LE (Little Endian). - * - * @return true if success, false otherwise. - * - */ -bool buffer_read_u32(buffer_t *buffer, uint32_t *value, endianness_t endianness); - -/** - * Read 8 bytes from buffer into uint64_t. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[out] value - * Pointer to 64-bit unsigned integer read from buffer. - * @param[in] endianness - * Either BE (Big Endian) or LE (Little Endian). - * - * @return true if success, false otherwise. - * - */ -bool buffer_read_u64(buffer_t *buffer, uint64_t *value, endianness_t endianness); - -/** - * Read Bitcoin-like varint from buffer into uint64_t. - * - * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[out] value - * Pointer to 64-bit unsigned integer read from buffer. - * - * @return true if success, false otherwise. - * - */ -bool buffer_read_varint(buffer_t *buffer, uint64_t *value); - -/** - * Read BIP32 path from buffer. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[out] out - * Pointer to output 32-bit integer buffer. - * @param[in] out_len - * Number of BIP32 paths read in the output buffer. - * - * @return true if success, false otherwise. - * - */ -bool buffer_read_bip32_path(buffer_t *buffer, uint32_t *out, size_t out_len); - -/** - * Copy bytes from buffer without moving offset. - * - * @param[in] buffer - * Pointer to input buffer struct. - * @param[out] out - * Pointer to output byte buffer. - * @param[in] out_len - * Length of output byte buffer. - * - * @return true if success, false otherwise. - * - */ -bool buffer_copy(const buffer_t *buffer, uint8_t *out, size_t out_len); - -/** - * Move bytes from buffer. - * - * @param[in,out] buffer - * Pointer to input buffer struct. - * @param[out] out - * Pointer to output byte buffer. - * @param[in] out_len - * Length of output byte buffer. - * - * @return true if success, false otherwise. - * - */ -bool buffer_move(buffer_t *buffer, uint8_t *out, size_t out_len); diff --git a/src/common/format.c b/src/common/format.c deleted file mode 100644 index 4581907..0000000 --- a/src/common/format.c +++ /dev/null @@ -1,168 +0,0 @@ -/***************************************************************************** - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // size_t -#include // int*_t, uint*_t -#include // strncpy, memmove -#include // bool - -#include "format.h" - -bool format_i64(char *dst, size_t dst_len, const int64_t value) { - char temp[] = "-9223372036854775808"; - - char *ptr = temp; - int64_t num = value; - int sign = 1; - - if (value < 0) { - sign = -1; - } - - while (num != 0) { - *ptr++ = '0' + (num % 10) * sign; - num /= 10; - } - - if (value < 0) { - *ptr++ = '-'; - } else if (value == 0) { - *ptr++ = '0'; - } - - int distance = (ptr - temp) + 1; - - if ((int) dst_len < distance) { - return false; - } - - size_t index = 0; - - while (--ptr >= temp) { - dst[index++] = *ptr; - } - - dst[index] = '\0'; - - return true; -} - -bool format_u64(char *out, size_t outLen, uint64_t in) { - uint8_t i = 0; - - if (outLen == 0) { - return false; - } - outLen--; - - while (in > 9) { - out[i] = in % 10 + '0'; - in /= 10; - i++; - if (i + 1 > outLen) { - return false; - } - } - out[i] = in + '0'; - out[i + 1] = '\0'; - - uint8_t j = 0; - char tmp; - - // revert the string - while (j < i) { - // swap out[j] and out[i] - tmp = out[j]; - out[j] = out[i]; - out[i] = tmp; - - i--; - j++; - } - return true; -} - -bool format_fpu64(char *dst, size_t dst_len, const uint64_t value, uint8_t decimals) { - char buffer[21] = {0}; - - if (!format_u64(buffer, sizeof(buffer), value)) { - return false; - } - - size_t digits = strlen(buffer); - - if (digits <= decimals) { - if (dst_len <= 2 + decimals - digits) { - return false; - } - *dst++ = '0'; - *dst++ = '.'; - for (uint16_t i = 0; i < decimals - digits; i++, dst++) { - *dst = '0'; - } - dst_len -= 2 + decimals - digits; - strncpy(dst, buffer, dst_len); - } else { - if (dst_len <= digits + 1 + decimals) { - return false; - } - - const size_t shift = digits - decimals; - memmove(dst, buffer, shift); - dst[shift] = '.'; - strncpy(dst + shift + 1, buffer + shift, decimals); - } - - return true; -} - -int format_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len) { - if (out_len < 2 * in_len + 1) { - return -1; - } - - const char hex[] = "0123456789ABCDEF"; - size_t i = 0; - int written = 0; - - while (i < in_len && (i * 2 + (2 + 1)) <= out_len) { - uint8_t high_nibble = (in[i] & 0xF0) >> 4; - *out = hex[high_nibble]; - out++; - - uint8_t low_nibble = in[i] & 0x0F; - *out = hex[low_nibble]; - out++; - - i++; - written += 2; - } - - *out = '\0'; - - return written + 1; -} - -int format_prefixed_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len) { - const char prefix[] = "0x"; - const size_t prefix_len = sizeof(prefix) - 1; - - if (out_len < sizeof(prefix)) { - return -1; - } - strncpy(out, prefix, sizeof(prefix)); - return format_hex(in, in_len, out + prefix_len, out_len - prefix_len); -} \ No newline at end of file diff --git a/src/common/format.h b/src/common/format.h deleted file mode 100644 index 1c07386..0000000 --- a/src/common/format.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include // size_t -#include // int*_t, uint*_t -#include // bool - -/** - * Format 64-bit signed integer as string. - * - * @param[out] dst - * Pointer to output string. - * @param[in] dst_len - * Length of output string. - * @param[in] value - * 64-bit signed integer to format. - * - * @return true if success, false otherwise. - * - */ -bool format_i64(char *dst, size_t dst_len, const int64_t value); - -/** - * Format 64-bit unsigned integer as string. - * - * @param[out] dst - * Pointer to output string. - * @param[in] dst_len - * Length of output string. - * @param[in] value - * 64-bit unsigned integer to format. - * - * @return true if success, false otherwise. - * - */ -bool format_u64(char *dst, size_t dst_len, uint64_t value); - -/** - * Format 64-bit unsigned integer as string with decimals. - * - * @param[out] dst - * Pointer to output string. - * @param[in] dst_len - * Length of output string. - * @param[in] value - * 64-bit unsigned integer to format. - * @param[in] decimals - * Number of digits after decimal separator. - * - * @return true if success, false otherwise. - * - */ -bool format_fpu64(char *dst, size_t dst_len, const uint64_t value, uint8_t decimals); - -/** - * Format byte buffer to uppercase hexadecimal string. - * - * @param[in] in - * Pointer to input byte buffer. - * @param[in] in_len - * Length of input byte buffer. - * @param[out] out - * Pointer to output string. - * @param[in] out_len - * Length of output string. - * - * @return number of bytes written if success, -1 otherwise. - * - */ -int format_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len); - -/** - * Format byte buffer to uppercase hexadecimal string prefixed with '0x'. - * - * @param[in] in - * Pointer to input byte buffer. - * @param[in] in_len - * Length of input byte buffer. - * @param[out] out - * Pointer to output string. - * @param[in] out_len - * Length of output string. - * - * @return number of bytes written if success, -1 otherwise. - * - */ -int format_prefixed_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len); diff --git a/src/common/macros.h b/src/common/macros.h deleted file mode 100644 index 13f75a3..0000000 --- a/src/common/macros.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -/** - * Macro for the size of a specific structure field. - */ -#define MEMBER_SIZE(type, member) (sizeof(((type *) 0)->member)) diff --git a/src/common/read.c b/src/common/read.c deleted file mode 100644 index c8ee851..0000000 --- a/src/common/read.c +++ /dev/null @@ -1,64 +0,0 @@ -/***************************************************************************** - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // uint*_t -#include // size_t - -uint16_t read_u16_be(const uint8_t *ptr, size_t offset) { - return (uint16_t) ptr[offset + 0] << 8 | // - (uint16_t) ptr[offset + 1] << 0; -} - -uint32_t read_u32_be(const uint8_t *ptr, size_t offset) { - return (uint32_t) ptr[offset + 0] << 24 | // - (uint32_t) ptr[offset + 1] << 16 | // - (uint32_t) ptr[offset + 2] << 8 | // - (uint32_t) ptr[offset + 3] << 0; -} - -uint64_t read_u64_be(const uint8_t *ptr, size_t offset) { - return (uint64_t) ptr[offset + 0] << 56 | // - (uint64_t) ptr[offset + 1] << 48 | // - (uint64_t) ptr[offset + 2] << 40 | // - (uint64_t) ptr[offset + 3] << 32 | // - (uint64_t) ptr[offset + 4] << 24 | // - (uint64_t) ptr[offset + 5] << 16 | // - (uint64_t) ptr[offset + 6] << 8 | // - (uint64_t) ptr[offset + 7] << 0; -} - -uint16_t read_u16_le(const uint8_t *ptr, size_t offset) { - return (uint16_t) ptr[offset + 0] << 0 | // - (uint16_t) ptr[offset + 1] << 8; -} - -uint32_t read_u32_le(const uint8_t *ptr, size_t offset) { - return (uint32_t) ptr[offset + 0] << 0 | // - (uint32_t) ptr[offset + 1] << 8 | // - (uint32_t) ptr[offset + 2] << 16 | // - (uint32_t) ptr[offset + 3] << 24; -} - -uint64_t read_u64_le(const uint8_t *ptr, size_t offset) { - return (uint64_t) ptr[offset + 0] << 0 | // - (uint64_t) ptr[offset + 1] << 8 | // - (uint64_t) ptr[offset + 2] << 16 | // - (uint64_t) ptr[offset + 3] << 24 | // - (uint64_t) ptr[offset + 4] << 32 | // - (uint64_t) ptr[offset + 5] << 40 | // - (uint64_t) ptr[offset + 6] << 48 | // - (uint64_t) ptr[offset + 7] << 56; -} diff --git a/src/common/read.h b/src/common/read.h deleted file mode 100644 index 61cfa8c..0000000 --- a/src/common/read.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include // uint*_t -#include // size_t - -/** - * Read 2 bytes as Big Endian from byte buffer. - * - * @param[in] ptr - * Pointer to byte buffer. - * @param[in] offset - * Offset in the byte buffer. - * - * @return 2 bytes value read from buffer. - * - */ -uint16_t read_u16_be(const uint8_t *ptr, size_t offset); - -/** - * Read 4 bytes as Big Endian from byte buffer. - * - * @param[in] ptr - * Pointer to byte buffer. - * @param[in] offset - * Offset in the byte buffer. - * - * @return 4 bytes value read from buffer. - * - */ -uint32_t read_u32_be(const uint8_t *ptr, size_t offset); - -/** - * Read 8 bytes as Big Endian from byte buffer. - * - * @param[in] ptr - * Pointer to byte buffer. - * @param[in] offset - * Offset in the byte buffer. - * - * @return 8 bytes value read from buffer. - * - */ -uint64_t read_u64_be(const uint8_t *ptr, size_t offset); - -/** - * Read 2 bytes as Little Endian from byte buffer. - * - * @param[in] ptr - * Pointer to byte buffer. - * @param[in] offset - * Offset in the byte buffer. - * - * @return 2 bytes value read from buffer. - * - */ -uint16_t read_u16_le(const uint8_t *ptr, size_t offset); - -/** - * Read 4 bytes as Little Endian from byte buffer. - * - * @param[in] ptr - * Pointer to byte buffer. - * @param[in] offset - * Offset in the byte buffer. - * - * @return 4 bytes value read from buffer. - * - */ -uint32_t read_u32_le(const uint8_t *ptr, size_t offset); - -/** - * Read 8 bytes as Little Endian from byte buffer. - * - * @param[in] ptr - * Pointer to byte buffer. - * @param[in] offset - * Offset in the byte buffer. - * - * @return 8 bytes value read from buffer. - * - */ -uint64_t read_u64_le(const uint8_t *ptr, size_t offset); diff --git a/src/common/user_format.c b/src/common/user_format.c new file mode 100644 index 0000000..0e638e7 --- /dev/null +++ b/src/common/user_format.c @@ -0,0 +1,50 @@ +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#include // bool +#include // size_t +#include // int*_t, uint*_t +#include // strncpy, memmove + +#include "format.h" + +#include "user_format.h" + +int format_prefixed_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len) { + const char prefix[] = "0x"; + const size_t prefix_len = sizeof(prefix) - 1; + + if (out_len < sizeof(prefix)) { + return -1; + } + strncpy(out, prefix, sizeof(prefix)); + return format_hex(in, in_len, out + prefix_len, out_len - prefix_len); +} + +bool is_str_interrupted(const char *src, size_t len) { + bool interrupted = false; + for (size_t i = 0; i < len; i++) { + if (!interrupted && src[i] == 0) { + interrupted = true; + continue; + } + if (interrupted && src[i] != 0) { + return true; + } + } + return false; +} diff --git a/src/common/user_format.h b/src/common/user_format.h new file mode 100644 index 0000000..3b55f58 --- /dev/null +++ b/src/common/user_format.h @@ -0,0 +1,24 @@ +#pragma once + +#include // bool +#include // size_t +#include // int*_t, uint*_t + +/** + * Format byte buffer to uppercase hexadecimal string prefixed with '0x'. + * + * @param[in] in + * Pointer to input byte buffer. + * @param[in] in_len + * Length of input byte buffer. + * @param[out] out + * Pointer to output string. + * @param[in] out_len + * Length of output string. + * + * @return number of bytes written if success, -1 otherwise. + * + */ +int format_prefixed_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len); + +bool is_str_interrupted(const char *src, size_t len); diff --git a/src/common/varint.c b/src/common/varint.c deleted file mode 100644 index d04fbb2..0000000 --- a/src/common/varint.c +++ /dev/null @@ -1,101 +0,0 @@ -/***************************************************************************** - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // uint*_t -#include // size_t -#include // bool - -#include "varint.h" -#include "write.h" -#include "read.h" - -uint8_t varint_size(uint64_t value) { - if (value <= 0xFC) { - return 1; - } - - if (value <= UINT16_MAX) { - return 3; - } - - if (value <= UINT32_MAX) { - return 5; - } - - return 9; // <= UINT64_MAX -} - -int varint_read(const uint8_t *in, size_t in_len, uint64_t *value) { - if (in_len < 1) { - return -1; - } - - uint8_t prefix = in[0]; - - if (prefix == 0xFD) { - if (in_len < 3) { - return -1; - } - *value = (uint64_t) read_u16_le(in, 1); - return 3; - } - - if (prefix == 0xFE) { - if (in_len < 5) { - return -1; - } - *value = (uint64_t) read_u32_le(in, 1); - return 5; - } - - if (prefix == 0xFF) { - if (in_len < 9) { - return -1; - } - *value = (uint64_t) read_u64_le(in, 1); - return 9; - } - - *value = (uint64_t) prefix; // prefix <= 0xFC - - return 1; -} - -int varint_write(uint8_t *out, size_t offset, uint64_t value) { - uint8_t varint_len = varint_size(value); - - switch (varint_len) { - case 1: - out[offset] = (uint8_t) value; - break; - case 3: - out[offset++] = 0xFD; - write_u16_le(out, offset, (uint16_t) value); - break; - case 5: - out[offset++] = 0xFE; - write_u32_le(out, offset, (uint32_t) value); - break; - case 9: - out[offset++] = 0xFF; - write_u64_le(out, offset, (uint64_t) value); - break; - default: - return -1; - } - - return varint_len; -} diff --git a/src/common/varint.h b/src/common/varint.h deleted file mode 100644 index 80aeb43..0000000 --- a/src/common/varint.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include // uint*_t -#include // size_t -#include // bool - -/** - * Size of value represented as Bitcoin-like varint. - * - * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer - * - * @param[in] value - * 64-bit unsigned integer to compute varint size. - * - * @return number of bytes to write value as varint (1, 3, 5 or 9 bytes). - * - */ -uint8_t varint_size(uint64_t value); - -/** - * Read Bitcoin-like varint from byte buffer. - * - * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer - * - * @param[in] in - * Pointer to input byte buffer. - * @param[in] in_len - * Length of the input byte buffer. - * @param[out] value - * Pointer to 64-bit unsigned integer to output varint. - * - * @return number of bytes read (1, 3, 5 or 9 bytes), -1 otherwise. - * - */ -int varint_read(const uint8_t *in, size_t in_len, uint64_t *value); - -/** - * Write Bitcoin-like varint to byte buffer. - * - * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer - * - * @param[out] out - * Pointer to output byte buffer. - * @param[in] offset - * Offset in the output byte buffer. - * @param[in] value - * 64-bit unsigned integer to write as varint. - * - * @return number of bytes written (1, 3, 5 or 9 bytes), -1 otherwise. - * - */ -int varint_write(uint8_t *out, size_t offset, uint64_t value); diff --git a/src/common/write.c b/src/common/write.c deleted file mode 100644 index 90ec141..0000000 --- a/src/common/write.c +++ /dev/null @@ -1,64 +0,0 @@ -/***************************************************************************** - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include // uint*_t -#include // size_t - -void write_u16_be(uint8_t *ptr, size_t offset, uint16_t value) { - ptr[offset + 0] = (uint8_t)(value >> 8); - ptr[offset + 1] = (uint8_t)(value >> 0); -} - -void write_u32_be(uint8_t *ptr, size_t offset, uint32_t value) { - ptr[offset + 0] = (uint8_t)(value >> 24); - ptr[offset + 1] = (uint8_t)(value >> 16); - ptr[offset + 2] = (uint8_t)(value >> 8); - ptr[offset + 3] = (uint8_t)(value >> 0); -} - -void write_u64_be(uint8_t *ptr, size_t offset, uint64_t value) { - ptr[offset + 0] = (uint8_t)(value >> 56); - ptr[offset + 1] = (uint8_t)(value >> 48); - ptr[offset + 2] = (uint8_t)(value >> 40); - ptr[offset + 3] = (uint8_t)(value >> 32); - ptr[offset + 4] = (uint8_t)(value >> 24); - ptr[offset + 5] = (uint8_t)(value >> 16); - ptr[offset + 6] = (uint8_t)(value >> 8); - ptr[offset + 7] = (uint8_t)(value >> 0); -} - -void write_u16_le(uint8_t *ptr, size_t offset, uint16_t value) { - ptr[offset + 0] = (uint8_t)(value >> 0); - ptr[offset + 1] = (uint8_t)(value >> 8); -} - -void write_u32_le(uint8_t *ptr, size_t offset, uint32_t value) { - ptr[offset + 0] = (uint8_t)(value >> 0); - ptr[offset + 1] = (uint8_t)(value >> 8); - ptr[offset + 2] = (uint8_t)(value >> 16); - ptr[offset + 3] = (uint8_t)(value >> 24); -} - -void write_u64_le(uint8_t *ptr, size_t offset, uint64_t value) { - ptr[offset + 0] = (uint8_t)(value >> 0); - ptr[offset + 1] = (uint8_t)(value >> 8); - ptr[offset + 2] = (uint8_t)(value >> 16); - ptr[offset + 3] = (uint8_t)(value >> 24); - ptr[offset + 4] = (uint8_t)(value >> 32); - ptr[offset + 5] = (uint8_t)(value >> 40); - ptr[offset + 6] = (uint8_t)(value >> 48); - ptr[offset + 7] = (uint8_t)(value >> 56); -} diff --git a/src/common/write.h b/src/common/write.h deleted file mode 100644 index 0418f5c..0000000 --- a/src/common/write.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include // uint*_t -#include // size_t - -/** - * Write 16-bit unsigned integer value as Big Endian. - * - * @param[out] ptr - * Pointer to output byte buffer. - * @param[in] offset - * Offset in the output byte buffer. - * @param[in] value - * 16-bit unsigned integer to write in output byte buffer as Big Endian. - * - */ -void write_u16_be(const uint8_t *ptr, size_t offset, uint16_t value); - -/** - * Write 32-bit unsigned integer value as Big Endian. - * - * @param[out] ptr - * Pointer to output byte buffer. - * @param[in] offset - * Offset in the output byte buffer. - * @param[in] value - * 32-bit unsigned integer to write in output byte buffer as Big Endian. - * - */ -void write_u32_be(uint8_t *ptr, size_t offset, uint32_t value); - -/** - * Write 64-bit unsigned integer value as Big Endian. - * - * @param[out] ptr - * Pointer to output byte buffer. - * @param[in] offset - * Offset in the output byte buffer. - * @param[in] value - * 64-bit unsigned integer to write in output byte buffer as Big Endian. - * - */ -void write_u64_be(uint8_t *ptr, size_t offset, uint64_t value); - -/** - * Write 16-bit unsigned integer value as Little Endian. - * - * @param[out] ptr - * Pointer to output byte buffer. - * @param[in] offset - * Offset in the output byte buffer. - * @param[in] value - * 16-bit unsigned integer to write in output byte buffer as Little Endian. - * - */ -void write_u16_le(uint8_t *ptr, size_t offset, uint16_t value); - -/** - * Write 32-bit unsigned integer value as Little Endian. - * - * @param[out] ptr - * Pointer to output byte buffer. - * @param[in] offset - * Offset in the output byte buffer. - * @param[in] value - * 32-bit unsigned integer to write in output byte buffer as Little Endian. - * - */ -void write_u32_le(uint8_t *ptr, size_t offset, uint32_t value); - -/** - * Write 64-bit unsigned integer value as Little Endian. - * - * @param[out] ptr - * Pointer to output byte buffer. - * @param[in] offset - * Offset in the output byte buffer. - * @param[in] value - * 64-bit unsigned integer to write in output byte buffer as Little Endian. - * - */ -void write_u64_le(uint8_t *ptr, size_t offset, uint64_t value); diff --git a/src/crypto.c b/src/crypto.c index 3e59f37..de2e606 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -23,47 +23,43 @@ #include "globals.h" -int crypto_derive_private_key(cx_ecfp_private_key_t *private_key, - uint8_t chain_code[static 32], - const uint32_t *bip32_path, - uint8_t bip32_path_len) { - uint8_t raw_private_key[32] = {0}; - int error = 0; - - BEGIN_TRY { - TRY { - // derive the seed with bip32_path - os_perso_derive_node_bip32_seed_key(HDW_ED25519_SLIP10, - CX_CURVE_Ed25519, - bip32_path, - bip32_path_len, - raw_private_key, - chain_code, - (unsigned char *) "ed25519 seed", - 12); - // new private_key from raw - cx_ecfp_init_private_key(CX_CURVE_Ed25519, - raw_private_key, - sizeof(raw_private_key), - private_key); - } - CATCH_OTHER(e) { - error = e; - } - FINALLY { - explicit_bzero(&raw_private_key, sizeof(raw_private_key)); - } +cx_err_t crypto_derive_private_key(cx_ecfp_private_key_t *private_key, + uint8_t chain_code[static 32], + const uint32_t *bip32_path, + uint8_t bip32_path_len) { + uint8_t raw_private_key[64] = {0}; + cx_err_t error = CX_OK; + + // derive the seed with bip32_path + error = os_derive_bip32_with_seed_no_throw(HDW_ED25519_SLIP10, + CX_CURVE_Ed25519, + bip32_path, + bip32_path_len, + raw_private_key, + chain_code, + (unsigned char *) "ed25519 seed", + 12); + if (error != CX_OK) { + explicit_bzero(&raw_private_key, sizeof(raw_private_key)); + return error; } - END_TRY; + // new private_key from raw + error = cx_ecfp_init_private_key_no_throw(CX_CURVE_Ed25519, raw_private_key, 32, private_key); + + explicit_bzero(&raw_private_key, sizeof(raw_private_key)); return error; } -void crypto_init_public_key(cx_ecfp_private_key_t *private_key, - cx_ecfp_public_key_t *public_key, - uint8_t raw_public_key[static 32]) { +cx_err_t crypto_init_public_key(cx_ecfp_private_key_t *private_key, + cx_ecfp_public_key_t *public_key, + uint8_t raw_public_key[static 32]) { // generate corresponding public key - cx_ecfp_generate_pair(CX_CURVE_Ed25519, public_key, private_key, 1); + cx_err_t error = cx_ecfp_generate_pair_no_throw(CX_CURVE_Ed25519, public_key, private_key, 1); + + if (error != CX_OK) { + return error; + } for (int i = 0; i < 32; i++) { raw_public_key[i] = public_key->W[64 - i]; @@ -71,49 +67,46 @@ void crypto_init_public_key(cx_ecfp_private_key_t *private_key, if (public_key->W[32] & 1) { raw_public_key[31] |= 0x80; } + + return error; } -int crypto_sign_message() { +cx_err_t crypto_sign_message() { cx_ecfp_private_key_t private_key = {0}; uint8_t chain_code[32] = {0}; - int sig_len = 0; // derive private key according to BIP32 path - int error = crypto_derive_private_key(&private_key, - chain_code, - G_context.bip32_path, - G_context.bip32_path_len); - if (error != 0) { + cx_err_t error = crypto_derive_private_key(&private_key, + chain_code, + G_context.bip32_path, + G_context.bip32_path_len); + if (error != CX_OK) { explicit_bzero(&private_key, sizeof(private_key)); return error; } - BEGIN_TRY { - TRY { - sig_len = cx_eddsa_sign(&private_key, - CX_LAST, - CX_SHA512, - G_context.tx_info.raw_tx, - G_context.tx_info.raw_tx_len, - NULL, - 0, - G_context.tx_info.signature, - sizeof(G_context.tx_info.signature), - NULL); - PRINTF("Signature: %.*H\n", sig_len, G_context.tx_info.signature); - } - CATCH_OTHER(e) { - error = e; - } - FINALLY { - explicit_bzero(&private_key, sizeof(private_key)); - } + error = cx_eddsa_sign_no_throw(&private_key, + CX_SHA512, + G_context.tx_info.raw_tx, + G_context.tx_info.raw_tx_len, + G_context.tx_info.signature, + sizeof(G_context.tx_info.signature)); + + if (error != CX_OK) { + explicit_bzero(&private_key, sizeof(private_key)); + return error; } - END_TRY; - if (error == 0) { - G_context.tx_info.signature_len = sig_len; + size_t size; + error = cx_ecdomain_parameters_length(private_key.curve, &size); + if (error != CX_OK) { + explicit_bzero(&private_key, sizeof(private_key)); + return error; } + G_context.tx_info.signature_len = 2 * size; + + PRINTF("Signature: %.*H\n", G_context.tx_info.signature_len, G_context.tx_info.signature); + explicit_bzero(&private_key, sizeof(private_key)); return error; } diff --git a/src/crypto.h b/src/crypto.h index 72c66c0..a03d963 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -18,13 +18,13 @@ * @param[in] bip32_path_len * Number of path in BIP32 path. * - * @return 0 on success, error number otherwise. + * @return CX_OK on success, error number otherwise. * */ -int crypto_derive_private_key(cx_ecfp_private_key_t *private_key, - uint8_t chain_code[static 32], - const uint32_t *bip32_path, - uint8_t bip32_path_len); +cx_err_t crypto_derive_private_key(cx_ecfp_private_key_t *private_key, + uint8_t chain_code[static 32], + const uint32_t *bip32_path, + uint8_t bip32_path_len); /** * Initialize public key given private key. @@ -36,19 +36,19 @@ int crypto_derive_private_key(cx_ecfp_private_key_t *private_key, * @param[out] raw_public_key * Pointer to raw public key. * - * @throw INVALID_PARAMETER + * @return CX_OK on success, error number otherwise. * */ -void crypto_init_public_key(cx_ecfp_private_key_t *private_key, - cx_ecfp_public_key_t *public_key, - uint8_t raw_public_key[static 32]); +cx_err_t crypto_init_public_key(cx_ecfp_private_key_t *private_key, + cx_ecfp_public_key_t *public_key, + uint8_t raw_public_key[static 32]); /** * Sign message hash in global context. * * @see G_context.bip32_path, G_context.tx_info.signature. * - * @return 0 on success, error number otherwise. + * @return CX_OK on success, error number otherwise. * */ -int crypto_sign_message(void); +cx_err_t crypto_sign_message(void); diff --git a/src/globals.h b/src/globals.h index 3ca735e..e1c75e4 100644 --- a/src/globals.h +++ b/src/globals.h @@ -8,21 +8,6 @@ #include "types.h" #include "constants.h" -/** - * Global buffer for interactions between SE and MCU. - */ -extern uint8_t G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; - -/** - * Global structure to perform asynchronous UX aside IO operations. - */ -extern ux_state_t G_ux; - -/** - * Global structure with the parameters to exchange with the BOLOS UX application. - */ -extern bolos_ux_params_t G_ux_params; - /** * Global context for user requests. */ diff --git a/src/handler/get_app_name.c b/src/handler/get_app_name.c index 95383aa..a2e3873 100644 --- a/src/handler/get_app_name.c +++ b/src/handler/get_app_name.c @@ -17,18 +17,17 @@ #include // uint*_t +#include "io.h" +#include "buffer.h" + #include "get_app_name.h" #include "../constants.h" #include "../globals.h" -#include "../io.h" #include "../sw.h" #include "../types.h" -#include "common/buffer.h" int handler_get_app_name() { _Static_assert(APPNAME_LEN < MAX_APPNAME_LEN, "APPNAME must be at most 64 characters!"); - buffer_t rdata = {.ptr = (uint8_t *) PIC(APPNAME), .size = APPNAME_LEN, .offset = 0}; - - return io_send_response(&rdata, SW_OK); + return io_send_response_pointer(PIC(APPNAME), APPNAME_LEN, SW_OK); } diff --git a/src/handler/get_public_key.c b/src/handler/get_public_key.c index 51c1d32..b0bdcf9 100644 --- a/src/handler/get_public_key.c +++ b/src/handler/get_public_key.c @@ -22,14 +22,15 @@ #include "os.h" #include "cx.h" +#include "io.h" +#include "buffer.h" #include "get_public_key.h" #include "../globals.h" #include "../types.h" -#include "../io.h" #include "../sw.h" #include "../crypto.h" -#include "../common/buffer.h" +#include "../address.h" #include "../ui/display.h" #include "../helper/send_response.h" @@ -46,19 +47,32 @@ int handler_get_public_key(buffer_t *cdata, bool display) { return io_send_sw(SW_WRONG_DATA_LENGTH); } + if (!validate_aptos_bip32_path(G_context.bip32_path, G_context.bip32_path_len)) { + G_context.req_type = REQUEST_UNDEFINED; + return io_send_sw(SW_GET_PUB_KEY_FAIL); + } + // derive private key according to BIP32 path - int error = crypto_derive_private_key(&private_key, - G_context.pk_info.chain_code, - G_context.bip32_path, - G_context.bip32_path_len); - if (error != 0) { + cx_err_t error = crypto_derive_private_key(&private_key, + G_context.pk_info.chain_code, + G_context.bip32_path, + G_context.bip32_path_len); + if (error != CX_OK) { explicit_bzero(&private_key, sizeof(private_key)); - PRINTF("Error code: %x.\n", error); + PRINTF("crypto_derive_private_key error code: %x.\n", error); G_context.req_type = REQUEST_UNDEFINED; return io_send_sw(SW_GET_PUB_KEY_FAIL); } + // generate corresponding public key - crypto_init_public_key(&private_key, &public_key, G_context.pk_info.raw_public_key); + error = crypto_init_public_key(&private_key, &public_key, G_context.pk_info.raw_public_key); + + if (error != CX_OK) { + explicit_bzero(&private_key, sizeof(private_key)); + PRINTF("crypto_init_public_key error code: %x.\n", error); + G_context.req_type = REQUEST_UNDEFINED; + return io_send_sw(SW_GET_PUB_KEY_FAIL); + } // reset private key explicit_bzero(&private_key, sizeof(private_key)); diff --git a/src/handler/get_public_key.h b/src/handler/get_public_key.h index e0ff0d9..3cb538a 100644 --- a/src/handler/get_public_key.h +++ b/src/handler/get_public_key.h @@ -4,8 +4,9 @@ #include // bool #include // uint*_t +#include "buffer.h" + #include "../types.h" -#include "../common/buffer.h" /** * Handler for GET_PUBLIC_KEY command. If successfully parse BIP32 path, diff --git a/src/handler/get_version.c b/src/handler/get_version.c index cb9700a..dc4f76a 100644 --- a/src/handler/get_version.c +++ b/src/handler/get_version.c @@ -19,13 +19,14 @@ #include // UINT8_MAX #include // _Static_assert +#include "io.h" +#include "buffer.h" + #include "get_version.h" #include "../globals.h" #include "../constants.h" -#include "../io.h" #include "../sw.h" #include "../types.h" -#include "common/buffer.h" int handler_get_version() { _Static_assert(APPVERSION_LEN == 3, "Length of (MAJOR || MINOR || PATCH) must be 3!"); @@ -36,11 +37,10 @@ int handler_get_version() { _Static_assert(PATCH_VERSION >= 0 && PATCH_VERSION <= UINT8_MAX, "PATCH version must be between 0 and 255!"); - return io_send_response( - &(const buffer_t){.ptr = (uint8_t[APPVERSION_LEN]){(uint8_t) MAJOR_VERSION, - (uint8_t) MINOR_VERSION, - (uint8_t) PATCH_VERSION}, - .size = APPVERSION_LEN, - .offset = 0}, + return io_send_response_pointer( + (const uint8_t *) &(uint8_t[APPVERSION_LEN]){(uint8_t) MAJOR_VERSION, + (uint8_t) MINOR_VERSION, + (uint8_t) PATCH_VERSION}, + APPVERSION_LEN, SW_OK); } diff --git a/src/handler/sign_tx.c b/src/handler/sign_tx.c index eb4c688..2850752 100644 --- a/src/handler/sign_tx.c +++ b/src/handler/sign_tx.c @@ -22,13 +22,14 @@ #include "os.h" #include "cx.h" +#include "buffer.h" #include "sign_tx.h" #include "../sw.h" #include "../globals.h" #include "../crypto.h" +#include "../address.h" #include "../ui/display.h" -#include "../common/buffer.h" #include "../transaction/types.h" #include "../transaction/deserialize.h" @@ -50,6 +51,11 @@ int handler_sign_tx(buffer_t *cdata, uint8_t chunk, bool more) { return io_send_sw(SW_WRONG_DATA_LENGTH); } + if (!validate_aptos_bip32_path(G_context.bip32_path, G_context.bip32_path_len)) { + G_context.req_type = REQUEST_UNDEFINED; + return io_send_sw(SW_GET_PUB_KEY_FAIL); + } + return io_send_sw(SW_OK); } else { // parse transaction if (G_context.req_type != CONFIRM_TRANSACTION) { diff --git a/src/handler/sign_tx.h b/src/handler/sign_tx.h index af17299..633ddf6 100644 --- a/src/handler/sign_tx.h +++ b/src/handler/sign_tx.h @@ -3,7 +3,7 @@ #include // uint*_t #include // bool -#include "../common/buffer.h" +#include "buffer.h" /** * Handler for SIGN_TX command. If successfully parse BIP32 path diff --git a/src/helper/send_reponse.c b/src/helper/send_reponse.c index df0d9af..9339282 100644 --- a/src/helper/send_reponse.c +++ b/src/helper/send_reponse.c @@ -19,11 +19,12 @@ #include // uint*_t #include // memmove +#include "buffer.h" + #include "send_response.h" #include "../constants.h" #include "../globals.h" #include "../sw.h" -#include "common/buffer.h" int helper_send_response_pubkey() { uint8_t resp[1 + 1 + PUBKEY_LEN + 1 + CHAINCODE_LEN] = {0}; @@ -37,7 +38,7 @@ int helper_send_response_pubkey() { memmove(resp + offset, G_context.pk_info.chain_code, CHAINCODE_LEN); offset += CHAINCODE_LEN; - return io_send_response(&(const buffer_t){.ptr = resp, .size = offset, .offset = 0}, SW_OK); + return io_send_response_pointer(resp, offset, SW_OK); } int helper_send_response_sig() { @@ -48,5 +49,5 @@ int helper_send_response_sig() { memmove(resp + offset, G_context.tx_info.signature, G_context.tx_info.signature_len); offset += G_context.tx_info.signature_len; - return io_send_response(&(const buffer_t){.ptr = resp, .size = offset, .offset = 0}, SW_OK); + return io_send_response_pointer(resp, offset, SW_OK); } diff --git a/src/helper/send_response.h b/src/helper/send_response.h index 61fec70..52fb40d 100644 --- a/src/helper/send_response.h +++ b/src/helper/send_response.h @@ -1,8 +1,7 @@ #pragma once #include "os.h" - -#include "../common/macros.h" +#include "macros.h" /** * Length of public key. diff --git a/src/io.c b/src/io.c deleted file mode 100644 index 990dcc0..0000000 --- a/src/io.c +++ /dev/null @@ -1,162 +0,0 @@ -/***************************************************************************** - * Ledger App Aptos. - * (c) 2020 Ledger SAS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *****************************************************************************/ - -#include - -#include "os.h" -#include "ux.h" - -#include "io.h" -#include "globals.h" -#include "sw.h" -#include "common/buffer.h" -#include "common/write.h" - -void io_seproxyhal_display(const bagl_element_t *element) { - io_seproxyhal_display_default((bagl_element_t *) element); -} - -uint8_t io_event(uint8_t channel __attribute__((unused))) { - switch (G_io_seproxyhal_spi_buffer[0]) { - case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: - UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); - break; - case SEPROXYHAL_TAG_STATUS_EVENT: - if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && // - !(U4BE(G_io_seproxyhal_spi_buffer, 3) & // - SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { - THROW(EXCEPTION_IO_RESET); - } - __attribute__((fallthrough)); - case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT: - UX_DISPLAYED_EVENT({}); - break; - case SEPROXYHAL_TAG_TICKER_EVENT: - UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {}); - break; - default: - UX_DEFAULT_EVENT(); - break; - } - - if (!io_seproxyhal_spi_is_status_sent()) { - io_seproxyhal_general_status(); - } - - return 1; -} - -uint16_t io_exchange_al(uint8_t channel, uint16_t tx_len) { - switch (channel & ~(IO_FLAGS)) { - case CHANNEL_KEYBOARD: - break; - case CHANNEL_SPI: - if (tx_len) { - io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); - - if (channel & IO_RESET_AFTER_REPLIED) { - halt(); - } - - return 0; - } else { - return io_seproxyhal_spi_recv(G_io_apdu_buffer, sizeof(G_io_apdu_buffer), 0); - } - default: - THROW(INVALID_PARAMETER); - } - - return 0; -} - -/** - * Variable containing the length of the APDU response to send back. - */ -static uint32_t G_output_len = 0; - -/** - * IO state (READY, RECEIVING, WAITING). - */ -static io_state_e G_io_state = READY; - -void io_init() { - // Reset length of APDU response - G_output_len = 0; - G_io_state = READY; -} - -int io_recv_command() { - int ret = -1; - - switch (G_io_state) { - case READY: - G_io_state = RECEIVED; - ret = io_exchange(CHANNEL_APDU, G_output_len); - break; - case RECEIVED: - G_io_state = WAITING; - ret = io_exchange(CHANNEL_APDU | IO_ASYNCH_REPLY, G_output_len); - G_io_state = RECEIVED; - break; - case WAITING: - G_io_state = READY; - ret = -1; - break; - } - - return ret; -} - -int io_send_response(const buffer_t *rdata, uint16_t sw) { - int ret = -1; - - if (rdata != NULL) { - if (rdata->size - rdata->offset > IO_APDU_BUFFER_SIZE - 2 || // - !buffer_copy(rdata, G_io_apdu_buffer, sizeof(G_io_apdu_buffer))) { - return io_send_sw(SW_WRONG_RESPONSE_LENGTH); - } - G_output_len = rdata->size - rdata->offset; - PRINTF("<= SW=%04X | RData=%.*H\n", sw, rdata->size, rdata->ptr); - } else { - G_output_len = 0; - PRINTF("<= SW=%04X | RData=\n", sw); - } - - write_u16_be(G_io_apdu_buffer, G_output_len, sw); - G_output_len += 2; - - switch (G_io_state) { - case READY: - ret = -1; - break; - case RECEIVED: - G_io_state = READY; - ret = 0; - break; - case WAITING: - ret = io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, G_output_len); - G_output_len = 0; - G_io_state = READY; - break; - } - - return ret; -} - -int io_send_sw(uint16_t sw) { - return io_send_response(NULL, sw); -} diff --git a/src/io.h b/src/io.h deleted file mode 100644 index c441d12..0000000 --- a/src/io.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include - -#include "ux.h" -#include "os_io_seproxyhal.h" - -#include "types.h" -#include "common/buffer.h" - -void io_seproxyhal_display(const bagl_element_t *element); - -/** - * IO callback called when an interrupt based channel has received - * data to be processed. - * - * @return 1 if success, 0 otherwise. - * - */ -uint8_t io_event(uint8_t channel __attribute__((unused))); - -uint16_t io_exchange_al(uint8_t channel, uint16_t tx_len); - -/** - * Initialize the APDU I/O state. - * - * This function must be called before calling any other I/O function. - */ -void io_init(void); - -/** - * Receive APDU command in G_io_apdu_buffer. - * - * @return zero or positive integer if success, -1 otherwise. - * - */ -int io_recv_command(void); - -/** - * Send APDU response (response data + status word) by filling - * G_io_apdu_buffer. - * - * @param[in] rdata - * Buffer with APDU response data. - * @param[in] sw - * Status word of APDU response. - * - * @return zero or positive integer if success, -1 otherwise. - * - */ -int io_send_response(const buffer_t *rdata, uint16_t sw); - -/** - * Send APDU response (only status word) by filling - * G_io_apdu_buffer. - * - * @param[in] sw - * Status word of APDU response. - * - * @return zero or positive integer if success, -1 otherwise. - * - */ -int io_send_sw(uint16_t sw); diff --git a/src/offsets.h b/src/offsets.h deleted file mode 100644 index 3db4bc1..0000000 --- a/src/offsets.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/** - * Offset of instruction class. - */ -#define OFFSET_CLA 0 -/** - * Offset of instruction code. - */ -#define OFFSET_INS 1 -/** - * Offset of instruction parameter 1. - */ -#define OFFSET_P1 2 -/** - * Offset of instruction parameter 2. - */ -#define OFFSET_P2 3 -/** - * Offset of command data length. - */ -#define OFFSET_LC 4 -/** - * Offset of command data. - */ -#define OFFSET_CDATA 5 diff --git a/src/transaction/deserialize.c b/src/transaction/deserialize.c index 73156e1..bb6e79a 100644 --- a/src/transaction/deserialize.c +++ b/src/transaction/deserialize.c @@ -1,9 +1,11 @@ #include + +#include "buffer.h" + #include "deserialize.h" #include "utils.h" #include "types.h" #include "../constants.h" -#include "../common/buffer.h" #include "../bcs/init.h" #include "../bcs/decoder.h" @@ -22,6 +24,10 @@ parser_status_e transaction_deserialize(buffer_t *buf, transaction_t *tx) { case TX_RAW: return tx_raw_deserialize(buf, tx); case TX_RAW_WITH_DATA: + break; + case TX_RAW_MESSAGE: + break; // Since the raw message is processed before display without direct transaction + // buffer reads, null-termination concerns are mitigated. case TX_MESSAGE: // To make sure the message is a null-terminated string if (buf->size == MAX_TRANSACTION_LEN && buf->ptr[MAX_TRANSACTION_LEN - 1] != 0) { @@ -74,7 +80,8 @@ parser_status_e tx_raw_deserialize(buffer_t *buf, transaction_t *tx) { if (!bcs_read_u32_from_uleb128(buf, &payload_variant)) { return PAYLOAD_VARIANT_READ_ERROR; } - if (payload_variant != PAYLOAD_ENTRY_FUNCTION && payload_variant != PAYLOAD_SCRIPT) { + if (payload_variant != PAYLOAD_ENTRY_FUNCTION && payload_variant != PAYLOAD_SCRIPT && + payload_variant != PAYLOAD_MULTISIG) { return PAYLOAD_UNDEFINED_ERROR; } tx->payload_variant = payload_variant; @@ -93,6 +100,9 @@ parser_status_e tx_raw_deserialize(buffer_t *buf, transaction_t *tx) { case PAYLOAD_SCRIPT: // TODO: implement script fields parsing return PARSING_OK; + case PAYLOAD_MULTISIG: + // TODO: implement multisig fields parsing + return PARSING_OK; default: return PAYLOAD_UNDEFINED_ERROR; } @@ -101,7 +111,6 @@ parser_status_e tx_raw_deserialize(buffer_t *buf, transaction_t *tx) { } parser_status_e tx_variant_deserialize(buffer_t *buf, transaction_t *tx) { - parser_status_e status = TX_VARIANT_UNDEFINED_ERROR; if (buf->offset != 0) { return TX_VARIANT_READ_ERROR; } @@ -120,17 +129,16 @@ parser_status_e tx_variant_deserialize(buffer_t *buf, transaction_t *tx) { tx->tx_variant = TX_RAW; return PARSING_OK; } - } else { - status = HASHED_PREFIX_READ_ERROR; } - if (transaction_utils_check_encoding(buf->ptr, buf->size)) { - buf->offset = 0; - tx->tx_variant = TX_MESSAGE; - return PARSING_OK; - } + // Not a transaction prefix, so we reset the offer to consider the full message + buf->offset = 0; + + // Try to display the message as UTF8 if possible + tx->tx_variant = + transaction_utils_check_encoding(buf->ptr, buf->size) ? TX_MESSAGE : TX_RAW_MESSAGE; - return status; + return PARSING_OK; } parser_status_e entry_function_payload_deserialize(buffer_t *buf, transaction_t *tx) { @@ -172,6 +180,7 @@ parser_status_e entry_function_payload_deserialize(buffer_t *buf, transaction_t case FUNC_APTOS_ACCOUNT_TRANSFER: return aptos_account_transfer_function_deserialize(buf, tx); case FUNC_COIN_TRANSFER: + case FUNC_APTOS_ACCOUNT_TRANSFER_COINS: return coin_transfer_function_deserialize(buf, tx); default: return PARSING_OK; @@ -236,7 +245,8 @@ parser_status_e coin_transfer_function_deserialize(buffer_t *buf, transaction_t return PAYLOAD_UNDEFINED_ERROR; } entry_function_payload_t *payload = &tx->payload.entry_function; - if (payload->known_type != FUNC_COIN_TRANSFER) { + if (payload->known_type != FUNC_COIN_TRANSFER && + payload->known_type != FUNC_APTOS_ACCOUNT_TRANSFER_COINS) { return PAYLOAD_UNDEFINED_ERROR; } @@ -245,7 +255,7 @@ parser_status_e coin_transfer_function_deserialize(buffer_t *buf, transaction_t return TYPE_ARGS_SIZE_READ_ERROR; } if (payload->args.ty_size != 1) { - return -TYPE_ARGS_SIZE_UNEXPECTED_ERROR; + return TYPE_ARGS_SIZE_UNEXPECTED_ERROR; } uint32_t ty_arg_variant = TYPE_TAG_UNDEFINED; @@ -342,5 +352,11 @@ entry_function_known_type_t determine_function_type(transaction_t *tx) { return FUNC_COIN_TRANSFER; } + if (tx->payload.entry_function.module_id.address[ADDRESS_LEN - 1] == 0x01 && + bcs_cmp_bytes(&tx->payload.entry_function.module_id.name, "aptos_account", 13) && + bcs_cmp_bytes(&tx->payload.entry_function.function_name, "transfer_coins", 14)) { + return FUNC_APTOS_ACCOUNT_TRANSFER_COINS; + } + return FUNC_UNKNOWN; } diff --git a/src/transaction/deserialize.h b/src/transaction/deserialize.h index ff97576..e312cf0 100644 --- a/src/transaction/deserialize.h +++ b/src/transaction/deserialize.h @@ -1,7 +1,8 @@ #pragma once +#include "buffer.h" + #include "types.h" -#include "../common/buffer.h" /** * Deserialize raw transaction in structure. diff --git a/src/types.h b/src/types.h index 92c0463..5694e1d 100644 --- a/src/types.h +++ b/src/types.h @@ -3,18 +3,10 @@ #include // size_t #include // uint*_t +#include "bip32.h" + #include "constants.h" #include "transaction/types.h" -#include "common/bip32.h" - -/** - * Enumeration for the status of IO. - */ -typedef enum { - READY, /// ready for new event - RECEIVED, /// data received - WAITING /// waiting -} io_state_e; /** * Enumeration with expected INS of APDU commands. @@ -26,18 +18,6 @@ typedef enum { SIGN_TX = 0x06 /// sign transaction with BIP32 path } command_e; -/** - * Structure with fields of APDU command. - */ -typedef struct { - uint8_t cla; /// Instruction class - command_e ins; /// Instruction code - uint8_t p1; /// Instruction parameter 1 - uint8_t p2; /// Instruction parameter 2 - uint8_t lc; /// Length of command data - uint8_t *data; /// Command data -} command_t; - /** * Enumeration with parsing state. */ diff --git a/src/ui/action/validate.c b/src/ui/action/validate.c index a2c7289..e28c3bf 100644 --- a/src/ui/action/validate.c +++ b/src/ui/action/validate.c @@ -17,29 +17,27 @@ #include // bool +#include "io.h" + #include "validate.h" -#include "../menu.h" #include "../../sw.h" -#include "../../io.h" #include "../../crypto.h" #include "../../globals.h" #include "../../helper/send_response.h" -void ui_action_validate_pubkey(bool choice) { +void validate_pubkey(bool choice) { if (choice) { helper_send_response_pubkey(); } else { io_send_sw(SW_DENY); } - - ui_menu_main(); } -void ui_action_validate_transaction(bool choice) { +void validate_transaction(bool choice) { if (choice) { G_context.state = STATE_APPROVED; - if (crypto_sign_message() != 0) { + if (crypto_sign_message() != CX_OK) { G_context.state = STATE_NONE; io_send_sw(SW_SIGNATURE_FAIL); } else { @@ -49,6 +47,4 @@ void ui_action_validate_transaction(bool choice) { G_context.state = STATE_NONE; io_send_sw(SW_DENY); } - - ui_menu_main(); } diff --git a/src/ui/action/validate.h b/src/ui/action/validate.h index b9aca37..5b9e83c 100644 --- a/src/ui/action/validate.h +++ b/src/ui/action/validate.h @@ -9,7 +9,7 @@ * User choice (either approved or rejectd). * */ -void ui_action_validate_pubkey(bool choice); +void validate_pubkey(bool choice); /** * Action for transaction information validation. @@ -18,4 +18,4 @@ void ui_action_validate_pubkey(bool choice); * User choice (either approved or rejectd). * */ -void ui_action_validate_transaction(bool choice); +void validate_transaction(bool choice); diff --git a/src/ui/display.c b/src/ui/bagl_display.c similarity index 50% rename from src/ui/display.c rename to src/ui/bagl_display.c index c2d5866..c9484e7 100644 --- a/src/ui/display.c +++ b/src/ui/bagl_display.c @@ -15,55 +15,51 @@ * limitations under the License. *****************************************************************************/ +#ifdef HAVE_BAGL + #include // bool #include // memset #include "os.h" #include "ux.h" #include "glyphs.h" +#include "io.h" +#include "format.h" +#include "bagl_display.h" #include "display.h" +#include "settings.h" +#include "menu.h" #include "constants.h" #include "../globals.h" -#include "../io.h" #include "../sw.h" -#include "../address.h" #include "action/validate.h" -#include "../transaction/types.h" -#include "../common/bip32.h" -#include "../common/format.h" +#include "../common/user_format.h" #define DOTS "[...]" static action_validate_cb g_validate_callback; -static char g_amount[30]; -static char g_gas_fee[30]; -static char g_bip32_path[60]; -static char g_address[67]; -static char g_function[120]; -static char g_struct[120]; - -static size_t count_leading_zeros(const uint8_t *src, size_t len) { - for (size_t i = 0; i < len; i++) { - if (src[i] != 0) { - return i; - } - } - return len; +static action_extend_ctx_t g_allow_blind_sign_ctx; + +// Validate/Invalidate public key and go back to home +static void ui_action_validate_pubkey(bool choice) { + validate_pubkey(choice); + ui_menu_main(); } -static bool is_str_interrupted(const char *src, size_t len) { - bool interrupted = false; - for (size_t i = 0; i < len; i++) { - if (!interrupted && src[i] == 0) { - interrupted = true; - continue; - } - if (interrupted && src[i] != 0) { - return true; - } - } - return false; +// Validate/Invalidate transaction and go back to home +static void ui_action_validate_transaction(bool choice) { + validate_transaction(choice); + ui_menu_main(); +} + +// Action to allow blind signing in settings +static void ui_action_allow_blind_signing(const ux_flow_step_t *const *steps) { + settings_allow_blind_signing_change(1); + + // Passed UX_FLOW steps are expected to contain a blind signing warning on the first step. + // Skip it for better UX here. + ux_flow_init(0, steps, steps[1]); } #ifdef TARGET_NANOS @@ -99,22 +95,35 @@ UX_STEP_CB(ux_display_reject_step, &C_icon_crossmark, "Reject", }); +// Step with the button to change settings +UX_STEP_CB(ux_display_allow_blind_sign_step, + pnn, + { g_allow_blind_sign_ctx.call(g_allow_blind_sign_ctx.steps); }, + { + &C_icon_validate_14, + "Allow", + "Blind Signing", + }); // FLOW to display blind signing banner: // #1 screen : warning icon + "Blind signing must be enabled in Settings" // #2 screen : reject button UX_FLOW(ux_display_blind_sign_banner_flow, &ux_display_blind_sign_banner_step, + &ux_display_allow_blind_sign_step, &ux_display_reject_step); void ui_flow_display(const ux_flow_step_t *const *steps) { ux_flow_init(0, steps, NULL); } +// This function should always use UX_FLOW containing the blind signing warning on the first step! void ui_flow_verified_display(const ux_flow_step_t *const *steps) { if (N_storage.settings.allow_blind_signing) { ui_flow_display(steps); } else { + g_allow_blind_sign_ctx.call = &ui_action_allow_blind_signing; + g_allow_blind_sign_ctx.steps = steps; ui_flow_display(ux_display_blind_sign_banner_flow); } } @@ -150,34 +159,25 @@ UX_FLOW(ux_display_pubkey_flow, &ux_display_reject_step); int ui_display_address() { - if (G_context.req_type != CONFIRM_ADDRESS) { - return io_send_sw(SW_BAD_STATE); - } - - memset(g_bip32_path, 0, sizeof(g_bip32_path)); - if (!bip32_path_format(G_context.bip32_path, - G_context.bip32_path_len, - g_bip32_path, - sizeof(g_bip32_path))) { - return io_send_sw(SW_DISPLAY_BIP32_PATH_FAIL); - } - - memset(g_address, 0, sizeof(g_address)); - uint8_t address[ADDRESS_LEN] = {0}; - if (!address_from_pubkey(G_context.pk_info.raw_public_key, address, sizeof(address))) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); - } - if (0 > format_prefixed_hex(address, sizeof(address), g_address, sizeof(g_address))) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); - } - g_validate_callback = &ui_action_validate_pubkey; - ui_flow_display(ux_display_pubkey_flow); + const int ret = ui_prepare_address(); + if (ret == UI_PREPARED) { + ui_flow_display(ux_display_pubkey_flow); + return 0; + } - return 0; + return ret; } +// Step with icon and text +UX_STEP_NOCB(ux_display_blind_warn_step, + pnn, + { + &C_icon_warning, + "Blind", + "Signing", + }); // Step with icon and text UX_STEP_NOCB(ux_display_review_step, pnn, @@ -208,12 +208,19 @@ UX_STEP_NOCB(ux_display_short_msg_step, .title = "Message", .text = g_struct, }); +// Step with title/text for message in raw form +UX_STEP_NOCB(ux_display_raw_msg_step, + bnnn_paging, + { + .title = "Raw message", + .text = g_struct, + }); // Step with title/text for transaction type UX_STEP_NOCB(ux_display_tx_type_step, bnnn_paging, { .title = "Transaction Type", - .text = g_struct, + .text = g_tx_type, }); // Step with title/text for function UX_STEP_NOCB(ux_display_function_step, @@ -252,48 +259,68 @@ UX_STEP_NOCB(ux_display_gas_fee_step, }); // FLOW to display default transaction information: -// #1 screen : eye icon + "Review Transaction" -// #2 screen : display tx type -// #3 screen : display gas fee -// #4 screen : approve button -// #5 screen : reject button -UX_FLOW(ux_display_tx_default_flow, +// #1 screen : warning icon + "Blind Signing" +// #2 screen : eye icon + "Review Transaction" +// #3 screen : display tx type +// #4 screen : display gas fee +// #5 screen : approve button +// #6 screen : reject button +UX_FLOW(ux_display_blind_tx_default_flow, + &ux_display_blind_warn_step, &ux_display_review_step, &ux_display_tx_type_step, &ux_display_gas_fee_step, &ux_display_approve_step, &ux_display_reject_step); -// FLOW to display message information: +// SEQUENCE to display message information: // #1 screen : eye icon + "Review Message" // #2 screen : display message // #3 screen : approve button // #4 screen : reject button -UX_FLOW(ux_display_message_flow, - &ux_display_review_msg_step, - &ux_display_msg_step, - &ux_display_approve_step, - &ux_display_reject_step); +#define SEQUENCE_MESSAGE \ + &ux_display_review_msg_step, &ux_display_msg_step, &ux_display_approve_step, \ + &ux_display_reject_step +UX_FLOW(ux_display_message_flow, SEQUENCE_MESSAGE); +// preceding screen : warning icon + "Blind Signing" +UX_FLOW(ux_display_blind_message_flow, &ux_display_blind_warn_step, SEQUENCE_MESSAGE); // FLOW to display message information in short form: // #1 screen : eye icon + "Review Message" -// #2 screen : display message +// #2 screen : display short message // #3 screen : approve button // #4 screen : reject button -UX_FLOW(ux_display_short_message_flow, - &ux_display_review_msg_step, - &ux_display_short_msg_step, - &ux_display_approve_step, - &ux_display_reject_step); +#define SEQUENCE_SHORT_MESSAGE \ + &ux_display_review_msg_step, &ux_display_short_msg_step, &ux_display_approve_step, \ + &ux_display_reject_step +UX_FLOW(ux_display_short_message_flow, SEQUENCE_SHORT_MESSAGE); +// preceding screen : warning icon + "Blind Signing" +UX_FLOW(ux_display_blind_short_message_flow, &ux_display_blind_warn_step, SEQUENCE_SHORT_MESSAGE); + +// FLOW to display message information in raw form: +// #1 screen : eye icon + "Review Message" +// #2 screen : display raw message +// #3 screen : approve button +// #4 screen : reject button +#define SEQUENCE_RAW_MESSAGE \ + &ux_display_review_msg_step, &ux_display_raw_msg_step, &ux_display_approve_step, \ + &ux_display_reject_step +UX_FLOW(ux_display_raw_message_flow, SEQUENCE_RAW_MESSAGE); +// preceding screen : warning icon + "Blind Signing" +UX_FLOW(ux_display_blind_raw_message_flow, &ux_display_blind_warn_step, SEQUENCE_RAW_MESSAGE); // FLOW to display entry_function transaction information: -// #1 screen : eye icon + "Review Transaction" -// #2 screen : display function name -// #3 screen : display gas fee -// #4 screen : approve button -// #5 screen : reject button -UX_FLOW(ux_display_tx_entry_function_flow, +// #1 screen : warning icon + "Blind Signing" +// #2 screen : eye icon + "Review Transaction" +// #3 screen : display tx type +// #4 screen : display function name +// #5 screen : display gas fee +// #6 screen : approve button +// #7 screen : reject button +UX_FLOW(ux_display_blind_tx_entry_function_flow, + &ux_display_blind_warn_step, &ux_display_review_step, + &ux_display_tx_type_step, &ux_display_function_step, &ux_display_gas_fee_step, &ux_display_approve_step, @@ -320,15 +347,17 @@ UX_FLOW(ux_display_tx_aptos_account_transfer_flow, // FLOW to display coin_transfer transaction information: // #1 screen : eye icon + "Review Transaction" -// #2 screen : display function name -// #3 screen : display coin type -// #4 screen : display destination address -// #5 screen : display amount -// #6 screen : display gas fee -// #7 screen : approve button -// #8 screen : reject button +// #2 screen : display tx type +// #3 screen : display function name +// #4 screen : display coin type +// #5 screen : display destination address +// #6 screen : display amount +// #7 screen : display gas fee +// #8 screen : approve button +// #9 screen : reject button UX_FLOW(ux_display_tx_coin_transfer_flow, &ux_display_review_step, + &ux_display_tx_type_step, &ux_display_function_step, &ux_display_coin_type_step, &ux_display_receiver_step, @@ -338,65 +367,22 @@ UX_FLOW(ux_display_tx_coin_transfer_flow, &ux_display_reject_step); int ui_display_transaction() { - if (G_context.req_type != CONFIRM_TRANSACTION || G_context.state != STATE_PARSED) { - G_context.state = STATE_NONE; - return io_send_sw(SW_BAD_STATE); - } - g_validate_callback = &ui_action_validate_transaction; - transaction_t *transaction = &G_context.tx_info.transaction; - - if (transaction->tx_variant == TX_MESSAGE) { - return ui_display_message(); - } else if (transaction->tx_variant != TX_UNDEFINED) { - uint64_t gas_fee_value = transaction->gas_unit_price * transaction->max_gas_amount; - memset(g_gas_fee, 0, sizeof(g_gas_fee)); - char gas_fee[30] = {0}; - if (!format_fpu64(gas_fee, sizeof(gas_fee), gas_fee_value, 8)) { - return io_send_sw(SW_DISPLAY_GAS_FEE_FAIL); - } - snprintf(g_gas_fee, sizeof(g_gas_fee), "APT %.*s", sizeof(gas_fee), gas_fee); - PRINTF("Gas Fee: %s\n", g_gas_fee); - - if (transaction->tx_variant == TX_RAW) { - switch (transaction->payload_variant) { - case PAYLOAD_ENTRY_FUNCTION: - return ui_display_entry_function(); - case PAYLOAD_SCRIPT: - memset(g_struct, 0, sizeof(g_struct)); - snprintf(g_struct, - sizeof(g_struct), - "%s [payload = SCRIPT]", - RAW_TRANSACTION_SALT); - break; - default: - memset(g_struct, 0, sizeof(g_struct)); - snprintf(g_struct, - sizeof(g_struct), - "%s [payload = UNKNOWN]", - RAW_TRANSACTION_SALT); - break; - } - } else if (transaction->tx_variant == TX_RAW_WITH_DATA) { - memset(g_struct, 0, sizeof(g_struct)); - snprintf(g_struct, sizeof(g_struct), RAW_TRANSACTION_WITH_DATA_SALT); - } - } else { - memset(g_struct, 0, sizeof(g_struct)); - snprintf(g_struct, sizeof(g_struct), "unknown data type"); + const int ret = ui_prepare_transaction(); + if (ret == UI_PREPARED) { + ui_flow_verified_display(ux_display_blind_tx_default_flow); + return 0; } - ui_flow_verified_display(ux_display_tx_default_flow); - - return 0; + return ret; } int ui_display_message() { if (N_storage.settings.show_full_message) { if (is_str_interrupted((const char *) G_context.tx_info.raw_tx, G_context.tx_info.raw_tx_len)) { - ui_flow_verified_display(ux_display_message_flow); + ui_flow_verified_display(ux_display_blind_message_flow); } else { ui_flow_display(ux_display_message_flow); } @@ -412,118 +398,70 @@ int ui_display_message() { if (short_enough) { if (is_str_interrupted(g_struct, sizeof(g_struct))) { - ui_flow_verified_display(ux_display_short_message_flow); + ui_flow_verified_display(ux_display_blind_short_message_flow); } else { ui_flow_display(ux_display_short_message_flow); } } else { - ui_flow_verified_display(ux_display_short_message_flow); + ui_flow_verified_display(ux_display_blind_short_message_flow); } } return 0; } -int ui_display_entry_function() { - entry_function_payload_t *function = &G_context.tx_info.transaction.payload.entry_function; - char function_module_id_address_hex[67] = {0}; - - // Be sure to display at least 1 byte, even if it is zero - size_t leading_zeros = count_leading_zeros(function->module_id.address, ADDRESS_LEN - 1); - if (0 > format_prefixed_hex(function->module_id.address + leading_zeros, - ADDRESS_LEN - leading_zeros, - function_module_id_address_hex, - sizeof(function_module_id_address_hex))) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); +int ui_display_raw_message() { + memset(g_struct, 0, sizeof(g_struct)); + const bool short_enough = sizeof(g_struct) >= 2 * G_context.tx_info.raw_tx_len + 1; + if (short_enough) { + format_hex(G_context.tx_info.raw_tx, + G_context.tx_info.raw_tx_len, + g_struct, + sizeof(g_struct)); + } else { + const size_t cropped_bytes_len = (sizeof(g_struct) - sizeof(DOTS)) / 2; + format_hex(G_context.tx_info.raw_tx, cropped_bytes_len, g_struct, sizeof(g_struct)); + strncpy(g_struct + cropped_bytes_len * 2, DOTS, sizeof(DOTS)); } - memset(g_function, 0, sizeof(g_function)); - snprintf(g_function, - sizeof(g_function), - "%s::%.*s::%.*s", - function_module_id_address_hex, - function->module_id.name.len, - function->module_id.name.bytes, - function->function_name.len, - function->function_name.bytes); - PRINTF("Function: %s\n", g_function); - - switch (function->known_type) { - case FUNC_APTOS_ACCOUNT_TRANSFER: - return ui_display_tx_aptos_account_transfer(); - case FUNC_COIN_TRANSFER: - return ui_display_tx_coin_transfer(); - default: - ui_flow_verified_display(ux_display_tx_entry_function_flow); - break; + PRINTF("Message: %s\n", g_struct); + + if (short_enough) { + ui_flow_display(ux_display_raw_message_flow); + } else { + ui_flow_verified_display(ux_display_blind_raw_message_flow); } + return 0; } -int ui_display_tx_aptos_account_transfer() { - agrs_aptos_account_trasfer_t *transfer = - &G_context.tx_info.transaction.payload.entry_function.args.transfer; - - // For well-known functions, display the transaction type in human-readable format - memset(g_struct, 0, sizeof(g_struct)); - snprintf(g_struct, sizeof(g_struct), "APT transfer"); - PRINTF("Tx Type: %s\n", g_struct); - - memset(g_address, 0, sizeof(g_address)); - if (0 > format_prefixed_hex(transfer->receiver, ADDRESS_LEN, g_address, sizeof(g_address))) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); +int ui_display_entry_function() { + const int ret = ui_prepare_entry_function(); + if (ret == UI_PREPARED) { + ui_flow_verified_display(ux_display_blind_tx_entry_function_flow); + return 0; } - PRINTF("Receiver: %s\n", g_address); - memset(g_amount, 0, sizeof(g_amount)); - char amount[30] = {0}; - if (!format_fpu64(amount, sizeof(amount), transfer->amount, 8)) { - return io_send_sw(SW_DISPLAY_AMOUNT_FAIL); - } - snprintf(g_amount, sizeof(g_amount), "APT %.*s", sizeof(amount), amount); - PRINTF("Amount: %s\n", g_amount); + return ret; +} - ui_flow_display(ux_display_tx_aptos_account_transfer_flow); +int ui_display_tx_aptos_account_transfer() { + const int ret = ui_prepare_tx_aptos_account_transfer(); + if (ret == UI_PREPARED) { + ui_flow_display(ux_display_tx_aptos_account_transfer_flow); + return 0; + } - return 0; + return ret; } int ui_display_tx_coin_transfer() { - agrs_coin_trasfer_t *transfer = - &G_context.tx_info.transaction.payload.entry_function.args.coin_transfer; - char transfer_ty_coin_address_hex[67] = {0}; - - // Be sure to display at least 1 byte, even if it is zero - size_t leading_zeros = count_leading_zeros(transfer->ty_coin.address, ADDRESS_LEN - 1); - if (0 > format_prefixed_hex(transfer->ty_coin.address + leading_zeros, - ADDRESS_LEN - leading_zeros, - transfer_ty_coin_address_hex, - sizeof(transfer_ty_coin_address_hex))) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); - } - memset(g_struct, 0, sizeof(g_struct)); - snprintf(g_struct, - sizeof(g_struct), - "%s::%.*s::%.*s", - transfer_ty_coin_address_hex, - transfer->ty_coin.module_name.len, - transfer->ty_coin.module_name.bytes, - transfer->ty_coin.name.len, - transfer->ty_coin.name.bytes); - PRINTF("Coin Type: %s\n", g_struct); - - memset(g_address, 0, sizeof(g_address)); - if (0 > format_prefixed_hex(transfer->receiver, ADDRESS_LEN, g_address, sizeof(g_address))) { - return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); - } - PRINTF("Receiver: %s\n", g_address); - - memset(g_amount, 0, sizeof(g_amount)); - if (!format_fpu64(g_amount, sizeof(g_amount), transfer->amount, 8)) { - return io_send_sw(SW_DISPLAY_AMOUNT_FAIL); + const int ret = ui_prepare_tx_coin_transfer(); + if (ret == UI_PREPARED) { + ui_flow_display(ux_display_tx_coin_transfer_flow); + return 0; } - PRINTF("Amount: %s\n", g_amount); - - ui_flow_display(ux_display_tx_coin_transfer_flow); - return 0; + return ret; } + +#endif diff --git a/src/ui/bagl_display.h b/src/ui/bagl_display.h new file mode 100644 index 0000000..94589d5 --- /dev/null +++ b/src/ui/bagl_display.h @@ -0,0 +1,36 @@ +#pragma once + +#include // bool +#include "ux.h" + +/** + * Callback to reuse action with approve/reject in step FLOW. + */ +typedef void (*action_validate_cb)(bool); + +/** + * A convenient function for displaying the sequence of steps. + */ +void ui_flow_display(const ux_flow_step_t *const *steps); + +/** + * A function that allows you to check the blind signing permission before displaying the sequence + * of steps. + * + * NOTE: This function should always use UX_FLOW containing the blind signing warning on the first + * step! + */ +void ui_flow_verified_display(const ux_flow_step_t *const *steps); + +/** + * Callback to extend the active UX_FLOW. + */ +typedef void (*action_extend_cb)(const ux_flow_step_t *const *steps); + +/** + * Context to extend the active UX_FLOW. + */ +typedef struct action_extend_ctx_s { + action_extend_cb call; + const ux_flow_step_t *const *steps; +} action_extend_ctx_t; diff --git a/src/ui/menu.c b/src/ui/bagl_menu.c similarity index 91% rename from src/ui/menu.c rename to src/ui/bagl_menu.c index 8800096..6138b61 100644 --- a/src/ui/menu.c +++ b/src/ui/bagl_menu.c @@ -15,6 +15,8 @@ * limitations under the License. *****************************************************************************/ +#ifdef HAVE_BAGL + #include "os.h" #include "ux.h" #include "glyphs.h" @@ -22,9 +24,9 @@ #include "../globals.h" #include "menu.h" #include "settings.h" -#include "display.h" +#include "bagl_display.h" -UX_STEP_NOCB(ux_menu_ready_step, pnn, {&C_aptos_logo, "Aptos", "is ready"}); +UX_STEP_NOCB(ux_menu_ready_step, pnn, {&C_aptos_logo_16px, "Aptos", "is ready"}); UX_STEP_CB(ux_menu_settings_step, pb, ui_menu_settings(), {&C_icon_coggle, "Settings"}); UX_STEP_CB(ux_menu_about_step, pb, ui_menu_about(), {&C_icon_certificate, "About"}); UX_STEP_VALID(ux_menu_exit_step, pb, os_sched_exit(-1), {&C_icon_dashboard_x, "Quit"}); @@ -49,10 +51,6 @@ void ui_menu_main() { ui_flow_display(ux_menu_main_flow); } -void ui_menu_settings() { - ux_menulist_init(0, settings_submenu_getter, settings_submenu_selector); -} - UX_STEP_NOCB(ux_menu_info_step, bn, {"Aptos App", "(c) 2022 Pontem"}); UX_STEP_NOCB(ux_menu_version_step, bn, {"Version", APPVERSION}); UX_STEP_CB(ux_menu_back_step, pb, ui_menu_main(), {&C_icon_back, "Back"}); @@ -70,3 +68,5 @@ UX_FLOW(ux_menu_about_flow, void ui_menu_about() { ui_flow_display(ux_menu_about_flow); } + +#endif diff --git a/src/ui/settings.c b/src/ui/bagl_settings.c similarity index 58% rename from src/ui/settings.c rename to src/ui/bagl_settings.c index d5aaa6b..987993e 100644 --- a/src/ui/settings.c +++ b/src/ui/bagl_settings.c @@ -1,9 +1,34 @@ +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_BAGL + #include "os.h" #include "settings.h" #include "menu.h" #include "../globals.h" +static const char* settings_submenu_getter(unsigned int idx); + +static void settings_submenu_selector(unsigned int idx); + +enum menu_options { MENU_SHOW_FULL_MSG = 0, MENU_ALLOW_BLIND_SIGNING = 1 }; + static const char* const binary_choice_getter_values[] = {"No", "Yes", "Back"}; static const char* binary_choice_getter(unsigned int idx) { @@ -13,13 +38,9 @@ static const char* binary_choice_getter(unsigned int idx) { return NULL; } -static void show_full_message_change(uint8_t value) { - nvm_write((void*) &N_storage.settings.show_full_message, &value, sizeof(value)); -} - static void show_full_message_selector(unsigned int idx) { if (idx == 0 || idx == 1) { - show_full_message_change((uint8_t) idx); + settings_show_full_message_change((uint8_t) idx); } ux_menulist_init_select(0, settings_submenu_getter, @@ -27,13 +48,9 @@ static void show_full_message_selector(unsigned int idx) { MENU_SHOW_FULL_MSG); } -static void allow_blind_signing_change(uint8_t value) { - nvm_write((void*) &N_storage.settings.allow_blind_signing, &value, sizeof(value)); -} - static void allow_blind_signing_selector(unsigned int idx) { if (idx == 0 || idx == 1) { - allow_blind_signing_change((uint8_t) idx); + settings_allow_blind_signing_change((uint8_t) idx); } ux_menulist_init_select(0, settings_submenu_getter, @@ -47,14 +64,14 @@ static const char* const settings_submenu_getter_values[] = { "Back", }; -const char* settings_submenu_getter(unsigned int idx) { +static const char* settings_submenu_getter(unsigned int idx) { if (idx < ARRAYLEN(settings_submenu_getter_values)) { return settings_submenu_getter_values[idx]; } return NULL; } -void settings_submenu_selector(unsigned int idx) { +static void settings_submenu_selector(unsigned int idx) { switch (idx) { case MENU_SHOW_FULL_MSG: ux_menulist_init_select(0, @@ -72,3 +89,9 @@ void settings_submenu_selector(unsigned int idx) { ui_menu_main(); } } + +void ui_menu_settings() { + ux_menulist_init(0, settings_submenu_getter, settings_submenu_selector); +} + +#endif diff --git a/src/ui/common_display.c b/src/ui/common_display.c new file mode 100644 index 0000000..eeb4487 --- /dev/null +++ b/src/ui/common_display.c @@ -0,0 +1,247 @@ +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#include // bool +#include // memset + +#include "os.h" +#include "ux.h" +#include "glyphs.h" +#include "io.h" +#include "bip32.h" +#include "format.h" + +#include "display.h" +#include "settings.h" +#include "constants.h" +#include "../globals.h" +#include "../sw.h" +#include "../address.h" +#include "action/validate.h" +#include "../transaction/types.h" +#include "../common/user_format.h" + +char g_bip32_path[60]; +char g_tx_type[60]; +char g_address[67]; +char g_gas_fee[30]; +char g_struct[120]; +char g_function[120]; +char g_amount[30]; + +static size_t count_leading_zeros(const uint8_t *src, size_t len) { + for (size_t i = 0; i < len; i++) { + if (src[i] != 0) { + return i; + } + } + return len; +} + +int ui_prepare_address() { + if (G_context.req_type != CONFIRM_ADDRESS) { + return io_send_sw(SW_BAD_STATE); + } + + memset(g_bip32_path, 0, sizeof(g_bip32_path)); + if (!bip32_path_format(G_context.bip32_path, + G_context.bip32_path_len, + g_bip32_path, + sizeof(g_bip32_path))) { + return io_send_sw(SW_DISPLAY_BIP32_PATH_FAIL); + } + + memset(g_address, 0, sizeof(g_address)); + uint8_t address[ADDRESS_LEN] = {0}; + if (!address_from_pubkey(G_context.pk_info.raw_public_key, address, sizeof(address))) { + return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + } + if (0 > format_prefixed_hex(address, sizeof(address), g_address, sizeof(g_address))) { + return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + } + + return UI_PREPARED; +} + +int ui_prepare_transaction() { + if (G_context.req_type != CONFIRM_TRANSACTION || G_context.state != STATE_PARSED) { + G_context.state = STATE_NONE; + return io_send_sw(SW_BAD_STATE); + } + + transaction_t *transaction = &G_context.tx_info.transaction; + + if (transaction->tx_variant == TX_MESSAGE) { + return ui_display_message(); + } else if (transaction->tx_variant == TX_RAW_MESSAGE) { + return ui_display_raw_message(); + } else if (transaction->tx_variant != TX_UNDEFINED) { + uint64_t gas_fee_value = transaction->gas_unit_price * transaction->max_gas_amount; + memset(g_gas_fee, 0, sizeof(g_gas_fee)); + char gas_fee[30] = {0}; + if (!format_fpu64(gas_fee, sizeof(gas_fee), gas_fee_value, 8)) { + return io_send_sw(SW_DISPLAY_GAS_FEE_FAIL); + } + snprintf(g_gas_fee, sizeof(g_gas_fee), "APT %.*s", sizeof(gas_fee), gas_fee); + PRINTF("Gas Fee: %s\n", g_gas_fee); + + if (transaction->tx_variant == TX_RAW) { + switch (transaction->payload_variant) { + case PAYLOAD_ENTRY_FUNCTION: + return ui_display_entry_function(); + case PAYLOAD_SCRIPT: + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, + sizeof(g_tx_type), + "%s [payload = SCRIPT]", + RAW_TRANSACTION_SALT); + break; + case PAYLOAD_MULTISIG: + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, + sizeof(g_tx_type), + "%s [payload = MULTISIG]", + RAW_TRANSACTION_SALT); + break; + default: + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, + sizeof(g_tx_type), + "%s [payload = UNKNOWN]", + RAW_TRANSACTION_SALT); + break; + } + } else if (transaction->tx_variant == TX_RAW_WITH_DATA) { + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, sizeof(g_tx_type), RAW_TRANSACTION_WITH_DATA_SALT); + } + } else { + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, sizeof(g_tx_type), "unknown data type"); + } + + return UI_PREPARED; +} + +int ui_prepare_entry_function() { + entry_function_payload_t *function = &G_context.tx_info.transaction.payload.entry_function; + char function_module_id_address_hex[67] = {0}; + + // Be sure to display at least 1 byte, even if it is zero + size_t leading_zeros = count_leading_zeros(function->module_id.address, ADDRESS_LEN - 1); + if (0 > format_prefixed_hex(function->module_id.address + leading_zeros, + ADDRESS_LEN - leading_zeros, + function_module_id_address_hex, + sizeof(function_module_id_address_hex))) { + return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + } + memset(g_function, 0, sizeof(g_function)); + snprintf(g_function, + sizeof(g_function), + "%s::%.*s::%.*s", + function_module_id_address_hex, + function->module_id.name.len, + function->module_id.name.bytes, + function->function_name.len, + function->function_name.bytes); + PRINTF("Function: %s\n", g_function); + + switch (function->known_type) { + case FUNC_APTOS_ACCOUNT_TRANSFER: + return ui_display_tx_aptos_account_transfer(); + case FUNC_COIN_TRANSFER: + case FUNC_APTOS_ACCOUNT_TRANSFER_COINS: + return ui_display_tx_coin_transfer(); + default: + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, sizeof(g_tx_type), "Function call"); + PRINTF("Tx Type: %s\n", g_tx_type); + break; + } + + return UI_PREPARED; +} + +int ui_prepare_tx_aptos_account_transfer() { + agrs_aptos_account_trasfer_t *transfer = + &G_context.tx_info.transaction.payload.entry_function.args.transfer; + + // For well-known functions, display the transaction type in human-readable format + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, sizeof(g_tx_type), "APT transfer"); + PRINTF("Tx Type: %s\n", g_tx_type); + + memset(g_address, 0, sizeof(g_address)); + if (0 > format_prefixed_hex(transfer->receiver, ADDRESS_LEN, g_address, sizeof(g_address))) { + return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + } + PRINTF("Receiver: %s\n", g_address); + + memset(g_amount, 0, sizeof(g_amount)); + char amount[30] = {0}; + if (!format_fpu64(amount, sizeof(amount), transfer->amount, 8)) { + return io_send_sw(SW_DISPLAY_AMOUNT_FAIL); + } + snprintf(g_amount, sizeof(g_amount), "APT %.*s", sizeof(amount), amount); + PRINTF("Amount: %s\n", g_amount); + + return UI_PREPARED; +} + +int ui_prepare_tx_coin_transfer() { + agrs_coin_trasfer_t *transfer = + &G_context.tx_info.transaction.payload.entry_function.args.coin_transfer; + char transfer_ty_coin_address_hex[67] = {0}; + + // For well-known functions, display the transaction type in human-readable format + memset(g_tx_type, 0, sizeof(g_tx_type)); + snprintf(g_tx_type, sizeof(g_tx_type), "Coin transfer"); + PRINTF("Tx Type: %s\n", g_tx_type); + + // Be sure to display at least 1 byte, even if it is zero + size_t leading_zeros = count_leading_zeros(transfer->ty_coin.address, ADDRESS_LEN - 1); + if (0 > format_prefixed_hex(transfer->ty_coin.address + leading_zeros, + ADDRESS_LEN - leading_zeros, + transfer_ty_coin_address_hex, + sizeof(transfer_ty_coin_address_hex))) { + return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + } + memset(g_struct, 0, sizeof(g_struct)); + snprintf(g_struct, + sizeof(g_struct), + "%s::%.*s::%.*s", + transfer_ty_coin_address_hex, + transfer->ty_coin.module_name.len, + transfer->ty_coin.module_name.bytes, + transfer->ty_coin.name.len, + transfer->ty_coin.name.bytes); + PRINTF("Coin Type: %s\n", g_struct); + + memset(g_address, 0, sizeof(g_address)); + if (0 > format_prefixed_hex(transfer->receiver, ADDRESS_LEN, g_address, sizeof(g_address))) { + return io_send_sw(SW_DISPLAY_ADDRESS_FAIL); + } + PRINTF("Receiver: %s\n", g_address); + + memset(g_amount, 0, sizeof(g_amount)); + if (!format_fpu64(g_amount, sizeof(g_amount), transfer->amount, 8)) { + return io_send_sw(SW_DISPLAY_AMOUNT_FAIL); + } + PRINTF("Amount: %s\n", g_amount); + + return UI_PREPARED; +} diff --git a/src/apdu/parser.c b/src/ui/common_settings.c similarity index 55% rename from src/apdu/parser.c rename to src/ui/common_settings.c index fad86ef..f1bf7bc 100644 --- a/src/apdu/parser.c +++ b/src/ui/common_settings.c @@ -15,26 +15,15 @@ * limitations under the License. *****************************************************************************/ -#include // size_t -#include // uint*_t -#include // bool +#include "os.h" -#include "parser.h" -#include "../types.h" -#include "../offsets.h" +#include "settings.h" +#include "../globals.h" -bool apdu_parser(command_t *cmd, uint8_t *buf, size_t buf_len) { - // Check minimum length and Lc field of APDU command - if (buf_len < OFFSET_CDATA || buf_len - OFFSET_CDATA != buf[OFFSET_LC]) { - return false; - } - - cmd->cla = buf[OFFSET_CLA]; - cmd->ins = (command_e) buf[OFFSET_INS]; - cmd->p1 = buf[OFFSET_P1]; - cmd->p2 = buf[OFFSET_P2]; - cmd->lc = buf[OFFSET_LC]; - cmd->data = (buf[OFFSET_LC] > 0) ? buf + OFFSET_CDATA : NULL; +void settings_show_full_message_change(uint8_t value) { + nvm_write((void*) &N_storage.settings.show_full_message, &value, sizeof(value)); +} - return true; +void settings_allow_blind_signing_change(uint8_t value) { + nvm_write((void*) &N_storage.settings.allow_blind_signing, &value, sizeof(value)); } diff --git a/src/ui/display.h b/src/ui/display.h index 6e9f699..05e4759 100644 --- a/src/ui/display.h +++ b/src/ui/display.h @@ -1,16 +1,14 @@ #pragma once -#include // bool -#include "ux.h" +#define UI_PREPARED -10 -void ui_flow_display(const ux_flow_step_t *const *steps); - -void ui_flow_verified_display(const ux_flow_step_t *const *steps); - -/** - * Callback to reuse action with approve/reject in step FLOW. - */ -typedef void (*action_validate_cb)(bool); +extern char g_bip32_path[60]; +extern char g_tx_type[60]; +extern char g_address[67]; +extern char g_gas_fee[30]; +extern char g_struct[120]; +extern char g_function[120]; +extern char g_amount[30]; /** * Display address on the device and ask confirmation to export. @@ -19,6 +17,7 @@ typedef void (*action_validate_cb)(bool); * */ int ui_display_address(void); +int ui_prepare_address(void); /** * Display transaction information on the device and ask confirmation to sign. @@ -27,11 +26,16 @@ int ui_display_address(void); * */ int ui_display_transaction(void); +int ui_prepare_transaction(void); int ui_display_message(void); +int ui_display_raw_message(void); int ui_display_entry_function(void); +int ui_prepare_entry_function(void); int ui_display_tx_aptos_account_transfer(void); +int ui_prepare_tx_aptos_account_transfer(void); int ui_display_tx_coin_transfer(void); +int ui_prepare_tx_coin_transfer(void); diff --git a/src/ui/nbgl_display.c b/src/ui/nbgl_display.c new file mode 100644 index 0000000..f92e5f9 --- /dev/null +++ b/src/ui/nbgl_display.c @@ -0,0 +1,92 @@ +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_NBGL + +#include // bool +#include // memset + +#include "os.h" +#include "glyphs.h" +#include "nbgl_use_case.h" + +#include "nbgl_display.h" +#include "display.h" +#include "settings.h" +#include "menu.h" +#include "constants.h" +#include "../globals.h" + +static use_case_review_ctx_t blind_sign_ctx; + +nbgl_layoutTagValue_t pairs[6]; +nbgl_layoutTagValueList_t pairList; +nbgl_pageInfoLongPress_t infoLongPress; + +static void blind_sign_continue() { + nbgl_useCaseReviewStart(blind_sign_ctx.icon, + blind_sign_ctx.review_title, + blind_sign_ctx.review_sub_title, + blind_sign_ctx.reject_text, + blind_sign_ctx.continue_callback, + blind_sign_ctx.reject_callback); +} + +static void blind_sign_info() { + nbgl_useCaseReviewStart(&C_round_warning_64px, + "Blind Signing", + "This operation cannot be\nsecurely interpreted by\nLedger Stax.\n" + "It might put your assets\nat risk.", + blind_sign_ctx.reject_text, + blind_sign_continue, + blind_sign_ctx.reject_callback); +} + +static void blind_sign_choice(bool enable) { + if (enable) { + settings_allow_blind_signing_change(1); + nbgl_useCaseStatus("BLIND SIGNING\nENABLED", true, blind_sign_info); + } else { + blind_sign_ctx.reject_callback(); + } +} + +void nbgl_useCaseReviewVerify(const nbgl_icon_details_t *icon, + const char *review_title, + const char *review_sub_title, + const char *reject_text, + nbgl_callback_t continue_callback, + nbgl_callback_t reject_callback) { + blind_sign_ctx.icon = icon; + blind_sign_ctx.review_title = review_title; + blind_sign_ctx.review_sub_title = review_sub_title; + blind_sign_ctx.reject_text = reject_text; + blind_sign_ctx.continue_callback = continue_callback; + blind_sign_ctx.reject_callback = reject_callback; + if (N_storage.settings.allow_blind_signing) { + blind_sign_info(); + } else { + nbgl_useCaseChoice(&C_round_warning_64px, + "Enable blind signing to\nauthorize this\noperation", + NULL, + "Enable blind signing", + reject_text, + blind_sign_choice); + } +} + +#endif diff --git a/src/ui/nbgl_display.h b/src/ui/nbgl_display.h new file mode 100644 index 0000000..3f20010 --- /dev/null +++ b/src/ui/nbgl_display.h @@ -0,0 +1,23 @@ +#pragma once + +#include "nbgl_use_case.h" + +extern nbgl_layoutTagValue_t pairs[6]; +extern nbgl_layoutTagValueList_t pairList; +extern nbgl_pageInfoLongPress_t infoLongPress; + +typedef struct use_case_review_ctx_s { + const nbgl_icon_details_t *icon; + const char *review_title; + const char *review_sub_title; + const char *reject_text; + nbgl_callback_t continue_callback; + nbgl_callback_t reject_callback; +} use_case_review_ctx_t; + +void nbgl_useCaseReviewVerify(const nbgl_icon_details_t *icon, + const char *review_title, + const char *review_sub_title, + const char *reject_text, + nbgl_callback_t continue_callback, + nbgl_callback_t reject_callback); diff --git a/src/ui/nbgl_display_address.c b/src/ui/nbgl_display_address.c new file mode 100644 index 0000000..78cf488 --- /dev/null +++ b/src/ui/nbgl_display_address.c @@ -0,0 +1,71 @@ +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_NBGL + +#include // bool +#include // memset + +#include "os.h" +#include "glyphs.h" +#include "nbgl_use_case.h" + +#include "nbgl_display.h" +#include "display.h" +#include "menu.h" +#include "constants.h" +#include "../globals.h" +#include "../address.h" +#include "action/validate.h" + +static void confirm_address(bool choice) { + validate_pubkey(choice); + nbgl_useCaseStatus(choice ? "ADDRESS\nVERIFIED" : "Address verification\ncanceled", + choice, + ui_menu_main); +} + +static void confirm_address_rejection(void) { + confirm_address(false); +} + +static void continue_review(void) { + pairs[0].item = "Derivation Path"; + pairs[0].value = g_bip32_path; + + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 1; + pairList.pairs = pairs; + nbgl_useCaseAddressConfirmationExt(g_address, confirm_address, &pairList); +} + +int ui_display_address() { + const int ret = ui_prepare_address(); + if (ret == UI_PREPARED) { + nbgl_useCaseReviewStart(&C_aptos_logo_64px, + "Verify Aptos\naddress", + NULL, + "Cancel", + continue_review, + confirm_address_rejection); + return 0; + } + + return ret; +} + +#endif diff --git a/src/ui/nbgl_display_message.c b/src/ui/nbgl_display_message.c new file mode 100755 index 0000000..d04d1cb --- /dev/null +++ b/src/ui/nbgl_display_message.c @@ -0,0 +1,143 @@ +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_NBGL + +#include // bool +#include // memset + +#include "os.h" +#include "format.h" +#include "glyphs.h" +#include "nbgl_use_case.h" + +#include "nbgl_display.h" +#include "display.h" +#include "menu.h" +#include "constants.h" +#include "../globals.h" +#include "action/validate.h" +#include "../common/user_format.h" + +#define DOTS "[...]" + +static void confirm_message_rejection(void) { + validate_transaction(false); + nbgl_useCaseStatus("Message rejected", false, ui_menu_main); +} + +static void ask_message_rejection_confirmation(void) { + nbgl_useCaseConfirm("Reject message?", + NULL, + "Yes, Reject", + "Go back to message", + confirm_message_rejection); +} + +static void review_choice(bool confirm) { + if (confirm) { + validate_transaction(true); + nbgl_useCaseStatus("MESSAGE\nSIGNED", true, ui_menu_main); + } else { + ask_message_rejection_confirmation(); + } +} + +static void review_message_continue(void) { + pairs[0].item = "Message"; + pairs[0].value = (const char *) G_context.tx_info.raw_tx; + + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 1; + pairList.pairs = pairs; + + infoLongPress.icon = &C_Message_64px; + infoLongPress.text = "Sign message"; + infoLongPress.longPressText = "Hold to sign"; + + nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject message", review_choice); +} + +static void review_raw_message_continue(void) { + pairs[0].item = "Raw message"; + pairs[0].value = g_struct; + + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 1; + pairList.pairs = pairs; + + infoLongPress.icon = &C_Message_64px; + infoLongPress.text = "Sign message"; + infoLongPress.longPressText = "Hold to sign"; + + nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject message", review_choice); +} + +int ui_display_message() { + if (is_str_interrupted((const char *) G_context.tx_info.raw_tx, G_context.tx_info.raw_tx_len)) { + nbgl_useCaseReviewVerify(&C_Message_64px, + "Review message", + NULL, + "Reject message", + review_message_continue, + ask_message_rejection_confirmation); + } else { + nbgl_useCaseReviewStart(&C_Message_64px, + "Review message", + NULL, + "Reject message", + review_message_continue, + ask_message_rejection_confirmation); + } + + return 0; +} + +int ui_display_raw_message() { + memset(g_struct, 0, sizeof(g_struct)); + const bool short_enough = sizeof(g_struct) >= 2 * G_context.tx_info.raw_tx_len + 1; + if (short_enough) { + format_hex(G_context.tx_info.raw_tx, + G_context.tx_info.raw_tx_len, + g_struct, + sizeof(g_struct)); + } else { + const size_t cropped_bytes_len = (sizeof(g_struct) - sizeof(DOTS)) / 2; + format_hex(G_context.tx_info.raw_tx, cropped_bytes_len, g_struct, sizeof(g_struct)); + strncpy(g_struct + cropped_bytes_len * 2, DOTS, sizeof(DOTS)); + } + + if (!short_enough) { + nbgl_useCaseReviewVerify(&C_Message_64px, + "Review message", + NULL, + "Reject message", + review_raw_message_continue, + ask_message_rejection_confirmation); + } else { + nbgl_useCaseReviewStart(&C_Message_64px, + "Review message", + NULL, + "Reject message", + review_raw_message_continue, + ask_message_rejection_confirmation); + } + + return 0; +} + +#endif diff --git a/src/ui/nbgl_display_transaction.c b/src/ui/nbgl_display_transaction.c new file mode 100755 index 0000000..8fd0478 --- /dev/null +++ b/src/ui/nbgl_display_transaction.c @@ -0,0 +1,200 @@ +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_NBGL + +#include // bool +#include // memset + +#include "os.h" +#include "glyphs.h" +#include "nbgl_use_case.h" + +#include "nbgl_display.h" +#include "display.h" +#include "menu.h" +#include "constants.h" +#include "../globals.h" +#include "action/validate.h" + +static void confirm_transaction_rejection(void) { + validate_transaction(false); + nbgl_useCaseStatus("Transaction rejected", false, ui_menu_main); +} + +static void ask_transaction_rejection_confirmation(void) { + nbgl_useCaseConfirm("Reject transaction?", + NULL, + "Yes, Reject", + "Go back to transaction", + confirm_transaction_rejection); +} + +static void review_choice(bool confirm) { + if (confirm) { + validate_transaction(true); + nbgl_useCaseStatus("TRANSACTION\nSIGNED", true, ui_menu_main); + } else { + ask_transaction_rejection_confirmation(); + } +} + +static void review_default_continue(void) { + pairs[0].item = "Transaction Type"; + pairs[0].value = g_tx_type; + pairs[1].item = "Gas Fee"; + pairs[1].value = g_gas_fee; + + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 2; + pairList.pairs = pairs; + + infoLongPress.icon = &C_aptos_logo_64px; + infoLongPress.text = "Sign transaction"; + infoLongPress.longPressText = "Hold to sign"; + + nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject transaction", review_choice); +} + +static void review_entry_function_continue(void) { + pairs[0].item = "Transaction Type"; + pairs[0].value = g_tx_type; + pairs[1].item = "Function"; + pairs[1].value = g_function; + pairs[2].item = "Gas Fee"; + pairs[2].value = g_gas_fee; + + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 3; + pairList.pairs = pairs; + + infoLongPress.icon = &C_aptos_logo_64px; + infoLongPress.text = "Sign transaction"; + infoLongPress.longPressText = "Hold to sign"; + + nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject transaction", review_choice); +} + +static void review_tx_aptos_account_transfer_continue(void) { + pairs[0].item = "Transaction Type"; + pairs[0].value = g_tx_type; + pairs[1].item = "Function"; + pairs[1].value = g_function; + pairs[2].item = "Receiver"; + pairs[2].value = g_address; + pairs[3].item = "Amount"; + pairs[3].value = g_amount; + pairs[4].item = "Gas Fee"; + pairs[4].value = g_gas_fee; + + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 5; + pairList.pairs = pairs; + + infoLongPress.icon = &C_aptos_logo_64px; + infoLongPress.text = "Sign transaction"; + infoLongPress.longPressText = "Hold to sign"; + + nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject transaction", review_choice); +} + +static void review_tx_coin_transfer_continue(void) { + pairs[0].item = "Transaction Type"; + pairs[0].value = g_tx_type; + pairs[1].item = "Function"; + pairs[1].value = g_function; + pairs[2].item = "Coin Type"; + pairs[2].value = g_struct; + pairs[3].item = "Receiver"; + pairs[3].value = g_address; + pairs[4].item = "Amount"; + pairs[4].value = g_amount; + pairs[5].item = "Gas Fee"; + pairs[5].value = g_gas_fee; + + pairList.nbMaxLinesForValue = 0; + pairList.nbPairs = 6; + pairList.pairs = pairs; + + infoLongPress.icon = &C_aptos_logo_64px; + infoLongPress.text = "Sign transaction"; + infoLongPress.longPressText = "Hold to sign"; + + nbgl_useCaseStaticReview(&pairList, &infoLongPress, "Reject transaction", review_choice); +} + +int ui_display_transaction() { + const int ret = ui_prepare_transaction(); + if (ret == UI_PREPARED) { + nbgl_useCaseReviewVerify(&C_aptos_logo_64px, + "Review transaction", + NULL, + "Reject transaction", + review_default_continue, + ask_transaction_rejection_confirmation); + return 0; + } + + return ret; +} + +int ui_display_entry_function() { + const int ret = ui_prepare_entry_function(); + if (ret == UI_PREPARED) { + nbgl_useCaseReviewVerify(&C_aptos_logo_64px, + "Review transaction", + NULL, + "Reject transaction", + review_entry_function_continue, + ask_transaction_rejection_confirmation); + return 0; + } + + return ret; +} + +int ui_display_tx_aptos_account_transfer() { + const int ret = ui_prepare_tx_aptos_account_transfer(); + if (ret == UI_PREPARED) { + nbgl_useCaseReviewStart(&C_aptos_logo_64px, + "Review transaction\nto send Aptos", + NULL, + "Reject transaction", + review_tx_aptos_account_transfer_continue, + ask_transaction_rejection_confirmation); + return 0; + } + + return ret; +} + +int ui_display_tx_coin_transfer() { + const int ret = ui_prepare_tx_coin_transfer(); + if (ret == UI_PREPARED) { + nbgl_useCaseReviewStart(&C_aptos_logo_64px, + "Review transaction\nto transfer coins", + NULL, + "Reject transaction", + review_tx_coin_transfer_continue, + ask_transaction_rejection_confirmation); + return 0; + } + + return ret; +} + +#endif diff --git a/src/ui/nbgl_menu.c b/src/ui/nbgl_menu.c new file mode 100644 index 0000000..649329d --- /dev/null +++ b/src/ui/nbgl_menu.c @@ -0,0 +1,110 @@ + +/***************************************************************************** + * Ledger App Aptos. + * (c) 2020 Ledger SAS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *****************************************************************************/ + +#ifdef HAVE_NBGL + +#include "os.h" +#include "io.h" +#include "glyphs.h" +#include "nbgl_use_case.h" + +#include "../globals.h" +#include "menu.h" +#include "settings.h" + +#define SETTINGS_PAGE_NUMBER 2 + +enum { + SWITCH_BLIND_SIGNING = 0, + SWITCHES_COUNT, +}; + +enum { + TOKEN_BLIND_SIGNING = FIRST_USER_TOKEN, +}; + +static const char* const INFO_TYPES[] = {"Version", "Developer", "Copyright"}; +static const char* const INFO_CONTENTS[] = {APPVERSION, + "Pontem Network", + "(c) 2022 Pontem Network"}; + +static nbgl_layoutSwitch_t g_switches[SWITCHES_COUNT]; + +static bool settings_nav_callback(uint8_t page, nbgl_pageContent_t* content) { + switch (page) { + case 0: + content->type = INFOS_LIST; + content->infosList.nbInfos = ARRAYLEN(INFO_TYPES); + content->infosList.infoTypes = (const char**) INFO_TYPES; + content->infosList.infoContents = (const char**) INFO_CONTENTS; + + return true; + case 1: + if (N_storage.settings.allow_blind_signing == 0) { + g_switches[SWITCH_BLIND_SIGNING].initState = OFF_STATE; + } else { + g_switches[SWITCH_BLIND_SIGNING].initState = ON_STATE; + } + content->type = SWITCHES_LIST; + content->switchesList.nbSwitches = SWITCHES_COUNT; + content->switchesList.switches = g_switches; + + return true; + default: + break; + } + + return false; +} + +static void settings_controls_callback(int token, uint8_t index) { + switch (token) { + case TOKEN_BLIND_SIGNING: + if (index == 0 || index == 1) { + settings_allow_blind_signing_change(index); + } + break; + default: + break; + } +} + +void app_quit(void) { + os_sched_exit(-1); +} + +void ui_menu_main(void) { + nbgl_useCaseHome(APPNAME, &C_aptos_logo_64px, NULL, true, ui_menu_settings, app_quit); +} + +void ui_menu_settings(void) { + g_switches[SWITCH_BLIND_SIGNING].text = "Blind signing"; + g_switches[SWITCH_BLIND_SIGNING].subText = "Enable blind signing"; + g_switches[SWITCH_BLIND_SIGNING].token = TOKEN_BLIND_SIGNING; + g_switches[SWITCH_BLIND_SIGNING].tuneId = TUNE_TAP_CASUAL; + + nbgl_useCaseSettings(APPNAME " settings", + 0, + SETTINGS_PAGE_NUMBER, + false, + ui_menu_main, + settings_nav_callback, + settings_controls_callback); +} + +#endif diff --git a/src/ui/settings.h b/src/ui/settings.h index 44c795c..834c71e 100644 --- a/src/ui/settings.h +++ b/src/ui/settings.h @@ -1,7 +1,5 @@ #pragma once -enum menu_options { MENU_SHOW_FULL_MSG = 0, MENU_ALLOW_BLIND_SIGNING = 1 }; +void settings_show_full_message_change(uint8_t value); -const char* settings_submenu_getter(unsigned int idx); - -void settings_submenu_selector(unsigned int idx); +void settings_allow_blind_signing_change(uint8_t value); diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..dc1a0c8 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +snapshots-tmp/ diff --git a/tests/README.md b/tests/README.md index 070f490..be8890f 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,37 +1,74 @@ -# Functional tests +# How to use the Ragger test framework -> :point_right: Every path on this document assumes you are at the root of the repository +This framework allows testing the application on the Speculos emulator or on a real device using LedgerComm or LedgerWallet -This directory contains examples of functional tests: -- `tests/ledgercomm/` directory uses the - [Python LedgerComm library](https://github.com/LedgerHQ/ledgercomm), which - allows the tests to run either on an actual Nano, or on - [Speculos](https://github.com/LedgerHQ/speculos), -- `tests/speculos/` directory uses the Python client of - [Speculos](https://github.com/LedgerHQ/speculos) to run the tests directly on - the Speculos emulator +## Quickly get started with Ragger and Speculos -## Speculos or LedgerComm? +### Install ragger and dependencies -Speculos is a Nano S/X emulator which provides a Python HTTP client. Despite not -being able to emulate **every** feature of a Nano S/X, it is a fast and powerful -tool, and is broadly used to test application (directly, or through LedgerComm, -or third-party tool such as [Zemu](https://github.com/Zondax/zemu)) which helps -building strong tests and CIs. +``` +pip install --extra-index-url https://test.pypi.org/simple/ -r requirements.txt +sudo apt-get update && sudo apt-get install qemu-user-static +``` -LedgerComm is a library which eases the exchange of APDU though HID or TCP -sockets. It works with both a real Nano S/X wallet, or Speculos. As the CI of -this repository does not have access to a Nano S/X, it uses Speculos. +### Compile the application -The functional tests using Speculos (`tests/speculos/` directory) or LedgerComm -\+ Speculos (`tests/ledgercomm/` directory) are the same, use the same language -(Python) and most of the code is also the same, so it is a good opportunity to -compare how they behave. +The application to test must be compiled for all required devices. +You can use for this the container `ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite`: +``` +docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest +cd # replace with the name of your app, (eg boilerplate) +docker run --user "$(id -u)":"$(id -g)" --rm -ti -v "$(realpath .):/app" --privileged -v "/dev/bus/usb:/dev/bus/usb" ledger-app-builder-lite:latest +make clean && make BOLOS_SDK=$_SDK # replace with one of [NANOS, NANOX, NANOSP, STAX] +exit +``` -Speculos tests are a bit smaller and more straightforward, as the physical -interaction through buttons are managed through the client just like the APDU, -and the emulator is automatically spawned and stopped. +### Run a simple test using the Speculos emulator + +You can use the following command to get your first experience with Ragger and Speculos +``` +pytest -v --tb=short --device nanox --display +``` +Or you can refer to the section `Available pytest options` to configure the options you want to use + + +### Run a simple test using a real device + +The application to test must be loaded and started on a Ledger device plugged in USB. +You can use for this the container `ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite`: +``` +docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest +cd app-/ # replace with the name of your app, (eg boilerplate) +docker run --user "$(id -u)":"$(id -g)" --rm -ti -v "$(realpath .):/app" --privileged -v "/dev/bus/usb:/dev/bus/usb" ledger-app-builder-lite:latest +make clean && make BOLOS_SDK=$_SDK load # replace with one of [NANOS, NANOX, NANOSP, STAX] +exit +``` + +You can use the following command to get your first experience with Ragger and Ledgerwallet on a NANOX. +Make sure that the device is plugged, unlocked, and that the tested application is open. +``` +pytest -v --tb=short --device nanox --backend ledgerwallet +``` +Or you can refer to the section `Available pytest options` to configure the options you want to use + + +## Available pytest options + +Standard useful pytest options +``` + -v formats the test summary in a readable way + -s enable logs for successful tests, on Speculos it will enable app logs if compiled with DEBUG=1 + -k only run the tests that contain in their names + --tb=short in case of errors, formats the test traceback in a readable way +``` + +Custom pytest options +``` + --device run the test on the specified device [nanos,nanox,nanosp,stax,all]. This parameter is mandatory + --backend run the tests against the backend [speculos, ledgercomm, ledgerwallet]. Speculos is the default + --display on Speculos, enables the display of the app screen using QT + --golden_run on Speculos, screen comparison functions will save the current screen instead of comparing + --log_apdu_file log all apdu exchanges to the file in parameter. The previous file content is erased +``` -LedgerComm tests are a bit heavier, and need a backend (either Speculos, or a -physical device) up and running, but can be run on an actual Nano S/X. diff --git a/tests/aptos_client/__init__.py b/tests/application_client/__init__.py similarity index 100% rename from tests/aptos_client/__init__.py rename to tests/application_client/__init__.py diff --git a/tests/application_client/aptos_command_sender.py b/tests/application_client/aptos_command_sender.py new file mode 100644 index 0000000..7e01ebd --- /dev/null +++ b/tests/application_client/aptos_command_sender.py @@ -0,0 +1,128 @@ +from enum import IntEnum +from typing import Generator, List, Optional +from contextlib import contextmanager + +from ragger.backend.interface import BackendInterface, RAPDU +from ragger.bip import pack_derivation_path + + +MAX_APDU_LEN: int = 255 + +CLA: int = 0x5B + +class P1(IntEnum): + # Parameter 1 for first APDU number. + P1_START = 0x00 + # Parameter 1 for maximum APDU number. + P1_MAX = 0x03 + # Parameter 1 for screen confirmation for GET_PUBLIC_KEY. + P1_CONFIRM = 0x01 + +class P2(IntEnum): + # Parameter 2 for last APDU to receive. + P2_LAST = 0x00 + # Parameter 2 for more APDU to receive. + P2_MORE = 0x80 + +class InsType(IntEnum): + GET_VERSION = 0x03 + GET_APP_NAME = 0x04 + GET_PUBLIC_KEY = 0x05 + SIGN_TX = 0x06 + +class Errors(IntEnum): + SW_DENY = 0x6985 + SW_WRONG_P1P2 = 0x6A86 + SW_WRONG_DATA_LENGTH = 0x6A87 + SW_INS_NOT_SUPPORTED = 0x6D00 + SW_CLA_NOT_SUPPORTED = 0x6E00 + SW_WRONG_RESPONSE_LENGTH = 0xB000 + SW_DISPLAY_BIP32_PATH_FAIL = 0xB001 + SW_DISPLAY_ADDRESS_FAIL = 0xB002 + SW_DISPLAY_AMOUNT_FAIL = 0xB003 + SW_WRONG_TX_LENGTH = 0xB004 + SW_TX_PARSING_FAIL = 0xB005 + SW_GET_PUB_KEY_FAIL = 0xB006 + SW_BAD_STATE = 0xB007 + SW_SIGNATURE_FAIL = 0xB008 + SW_DISPLAY_GAS_FEE_FAIL = 0xB009 + + +def split_message(message: bytes, max_size: int) -> List[bytes]: + return [message[x:x + max_size] for x in range(0, len(message), max_size)] + + +class AptosCommandSender: + def __init__(self, backend: BackendInterface) -> None: + self.backend = backend + + + def get_app_and_version(self) -> RAPDU: + return self.backend.exchange(cla=0xB0, # specific CLA for BOLOS + ins=0x01, # specific INS for get_app_and_version + p1=P1.P1_START, + p2=P2.P2_LAST, + data=b"") + + + def get_version(self) -> RAPDU: + return self.backend.exchange(cla=CLA, + ins=InsType.GET_VERSION, + p1=P1.P1_START, + p2=P2.P2_LAST, + data=b"") + + + def get_app_name(self) -> RAPDU: + return self.backend.exchange(cla=CLA, + ins=InsType.GET_APP_NAME, + p1=P1.P1_START, + p2=P2.P2_LAST, + data=b"") + + + def get_public_key(self, path: str) -> RAPDU: + return self.backend.exchange(cla=CLA, + ins=InsType.GET_PUBLIC_KEY, + p1=P1.P1_START, + p2=P2.P2_LAST, + data=pack_derivation_path(path)) + + + @contextmanager + def get_public_key_with_confirmation(self, path: str) -> Generator[None, None, None]: + with self.backend.exchange_async(cla=CLA, + ins=InsType.GET_PUBLIC_KEY, + p1=P1.P1_CONFIRM, + p2=P2.P2_LAST, + data=pack_derivation_path(path)) as response: + yield response + + + @contextmanager + def sign_tx(self, path: str, transaction: bytes) -> Generator[None, None, None]: + self.backend.exchange(cla=CLA, + ins=InsType.SIGN_TX, + p1=P1.P1_START, + p2=P2.P2_MORE, + data=pack_derivation_path(path)) + messages = split_message(transaction, MAX_APDU_LEN) + idx: int = P1.P1_START + 1 + + for msg in messages[:-1]: + self.backend.exchange(cla=CLA, + ins=InsType.SIGN_TX, + p1=idx, + p2=P2.P2_MORE, + data=msg) + idx += 1 + + with self.backend.exchange_async(cla=CLA, + ins=InsType.SIGN_TX, + p1=idx, + p2=P2.P2_LAST, + data=messages[-1]) as response: + yield response + + def get_async_response(self) -> Optional[RAPDU]: + return self.backend.last_async_response diff --git a/tests/application_client/aptos_response_unpacker.py b/tests/application_client/aptos_response_unpacker.py new file mode 100644 index 0000000..cc8bc2d --- /dev/null +++ b/tests/application_client/aptos_response_unpacker.py @@ -0,0 +1,70 @@ +from typing import Tuple +from struct import unpack + +# remainder, data_len, data +def pop_sized_buf_from_buffer(buffer:bytes, size:int) -> Tuple[bytes, bytes]: + return buffer[size:], buffer[0:size] + +# remainder, data_len, data +def pop_size_prefixed_buf_from_buf(buffer:bytes) -> Tuple[bytes, int, bytes]: + data_len = buffer[0] + return buffer[1+data_len:], data_len, buffer[1:data_len+1] + +# Unpack from response: +# response = app_name (var) +def unpack_get_app_name_response(response: bytes) -> str: + return response.decode("ascii") + +# Unpack from response: +# response = MAJOR (1) +# MINOR (1) +# PATCH (1) +def unpack_get_version_response(response: bytes) -> Tuple[int, int, int]: + assert len(response) == 3 + major, minor, patch = unpack("BBB", response) + return (major, minor, patch) + +# Unpack from response: +# response = format_id (1) +# app_name_raw_len (1) +# app_name_raw (var) +# version_raw_len (1) +# version_raw (var) +# unused_len (1) +# unused (var) +def unpack_get_app_and_version_response(response: bytes) -> Tuple[str, str]: + response, _ = pop_sized_buf_from_buffer(response, 1) + response, _, app_name_raw = pop_size_prefixed_buf_from_buf(response) + response, _, version_raw = pop_size_prefixed_buf_from_buf(response) + response, _, _ = pop_size_prefixed_buf_from_buf(response) + + assert len(response) == 0 + + return app_name_raw.decode("ascii"), version_raw.decode("ascii") + +# Unpack from response: +# response = pub_key_len (1) +# pub_key (var) +# chain_code_len (1) +# chain_code (var) +def unpack_get_public_key_response(response: bytes) -> Tuple[int, bytes, int, bytes]: + response, pub_key_len, pub_key = pop_size_prefixed_buf_from_buf(response) + response, chain_code_len, chain_code = pop_size_prefixed_buf_from_buf(response) + + assert pub_key_len == 33 + assert chain_code_len == 32 + assert len(response) == 0 + + return pub_key_len, pub_key, chain_code_len, chain_code + +# Unpack from response: +# response = der_sig_len (1) +# der_sig (var) +# v (1) +def unpack_sign_tx_response(response: bytes) -> Tuple[int, bytes, int]: + response, sig_len, sig = pop_size_prefixed_buf_from_buf(response) + response, v = pop_sized_buf_from_buffer(response, 1) + + assert len(response) == 0 + + return sig_len, sig, int.from_bytes(v, byteorder='big') diff --git a/tests/aptos_client/utils.py b/tests/application_client/aptos_utils.py similarity index 66% rename from tests/aptos_client/utils.py rename to tests/application_client/aptos_utils.py index 6260ed2..fd96e62 100644 --- a/tests/aptos_client/utils.py +++ b/tests/application_client/aptos_utils.py @@ -1,24 +1,10 @@ from io import BytesIO -from typing import List, Optional, Literal +from typing import Optional, Literal -UINT64_MAX: int = 18446744073709551615 -UINT32_MAX: int = 4294967295 -UINT16_MAX: int = 65535 - - -def bip32_path_from_string(path: str) -> List[bytes]: - splitted_path: List[str] = path.split("/") - - if not splitted_path: - raise Exception(f"BIP32 path format error: '{path}'") - - if "m" in splitted_path and splitted_path[0] == "m": - splitted_path = splitted_path[1:] - - return [int(p).to_bytes(4, byteorder="big") if "'" not in p - else (0x80000000 | int(p[:-1])).to_bytes(4, byteorder="big") - for p in splitted_path] +UINT64_MAX: int = 2**64-1 +UINT32_MAX: int = 2**32-1 +UINT16_MAX: int = 2**16-1 def write_varint(n: int) -> bytes: @@ -42,7 +28,7 @@ def read_varint(buf: BytesIO, b: bytes = prefix if prefix else buf.read(1) if not b: - raise ValueError(f"Can't read prefix: '{b}'!") + raise ValueError(f"Can't read prefix: '{b.hex()}'!") n: int = {b"\xfd": 2, b"\xfe": 4, b"\xff": 8}.get(b, 1) # default to 1 diff --git a/tests/application_client/py.typed b/tests/application_client/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/aptos_client/aptos_cmd.py b/tests/aptos_client/aptos_cmd.py deleted file mode 100644 index f2ddde8..0000000 --- a/tests/aptos_client/aptos_cmd.py +++ /dev/null @@ -1,146 +0,0 @@ -import struct -from typing import Tuple - -from ledgercomm import Transport - -from aptos_client.aptos_cmd_builder import AptosCommandBuilder, InsType -from aptos_client.button import Button -from aptos_client.exception import DeviceException - - -class AptosCommand: - def __init__(self, - transport: Transport, - debug: bool = False) -> None: - self.transport = transport - self.builder = AptosCommandBuilder(debug=debug) - self.debug = debug - - def get_app_and_version(self) -> Tuple[str, str]: - sw, response = self.transport.exchange_raw( - self.builder.get_app_and_version() - ) # type: int, bytes - - if sw != 0x9000: - raise DeviceException(error_code=sw, ins=0x01) - - # response = format_id (1) || - # app_name_len (1) || - # app_name (var) || - # version_len (1) || - # version (var) || - offset: int = 0 - - format_id: int = response[offset] - offset += 1 - app_name_len: int = response[offset] - offset += 1 - app_name: str = response[offset:offset + app_name_len].decode("ascii") - offset += app_name_len - version_len: int = response[offset] - offset += 1 - version: str = response[offset:offset + version_len].decode("ascii") - offset += version_len - - return app_name, version - - def get_version(self) -> Tuple[int, int, int]: - sw, response = self.transport.exchange_raw( - self.builder.get_version() - ) # type: int, bytes - - if sw != 0x9000: - raise DeviceException(error_code=sw, ins=InsType.INS_GET_VERSION) - - # response = MAJOR (1) || MINOR (1) || PATCH (1) - assert len(response) == 3 - - major, minor, patch = struct.unpack( - "BBB", - response - ) # type: int, int, int - - return major, minor, patch - - def get_app_name(self) -> str: - sw, response = self.transport.exchange_raw( - self.builder.get_app_name() - ) # type: int, bytes - - if sw != 0x9000: - raise DeviceException(error_code=sw, ins=InsType.INS_GET_APP_NAME) - - return response.decode("ascii") - - def get_public_key(self, bip32_path: str, display: bool = False) -> Tuple[bytes, bytes]: - sw, response = self.transport.exchange_raw( - self.builder.get_public_key(bip32_path=bip32_path, - display=display) - ) # type: int, bytes - - if sw != 0x9000: - raise DeviceException(error_code=sw, ins=InsType.INS_GET_PUBLIC_KEY) - - # response = pub_key_len (1) || - # pub_key (var) || - # chain_code_len (1) || - # chain_code (var) - offset: int = 0 - - pub_key_len: int = response[offset] - offset += 1 - pub_key: bytes = response[offset:offset + pub_key_len] - offset += pub_key_len - chain_code_len: int = response[offset] - offset += 1 - chain_code: bytes = response[offset:offset + chain_code_len] - offset += chain_code_len - - assert len(response) == 1 + pub_key_len + 1 + chain_code_len - - return pub_key, chain_code - - def sign_raw(self, bip32_path: str, data: bytes, button: Button, model: str) -> Tuple[int, bytes]: - sw: int - response: bytes = b"" - - for is_last, chunk in self.builder.sign_raw(bip32_path=bip32_path, data=data): - self.transport.send_raw(chunk) - - if is_last: - # Review Transaction - button.right_click() - # Function - button.right_click() - # Coin Type - # Due to screen size, NanoS needs 1 more screens to display the coin type - if model == 'nanos': - button.right_click() - button.right_click() - # Receiver - # Due to screen size, NanoS needs 2 more screens to display the address - if model == 'nanos': - button.right_click() - button.right_click() - button.right_click() - button.right_click() - # Amount - button.right_click() - # Gas Fee - button.right_click() - # Approve - button.both_click() - - sw, response = self.transport.recv() # type: int, bytes - - if sw != 0x9000: - raise DeviceException(error_code=sw, ins=InsType.INS_SIGN_TX) - - # response = der_sig_len (1) || - # der_sig (var) - der_sig_len: int = response[0] - der_sig: bytes = response[1: 1 + der_sig_len] - - assert len(response) == 1 + der_sig_len - - return der_sig diff --git a/tests/aptos_client/aptos_cmd_builder.py b/tests/aptos_client/aptos_cmd_builder.py deleted file mode 100644 index 8f7a39e..0000000 --- a/tests/aptos_client/aptos_cmd_builder.py +++ /dev/null @@ -1,215 +0,0 @@ -import enum -import logging -import struct -from typing import List, Tuple, Union, Iterator, cast - -from aptos_client.utils import bip32_path_from_string - -MAX_APDU_LEN: int = 255 - - -def chunkify(data: bytes, chunk_len: int) -> Iterator[Tuple[bool, bytes]]: - size: int = len(data) - - if size <= chunk_len: - yield True, data - return - - chunk: int = size // chunk_len - remaining: int = size % chunk_len - offset: int = 0 - - for i in range(chunk): - yield False, data[offset:offset + chunk_len] - offset += chunk_len - - if remaining: - yield True, data[offset:] - - -class InsType(enum.IntEnum): - INS_GET_VERSION = 0x03 - INS_GET_APP_NAME = 0x04 - INS_GET_PUBLIC_KEY = 0x05 - INS_SIGN_TX = 0x06 - - -class AptosCommandBuilder: - """APDU command builder for the Aptos application. - - Parameters - ---------- - debug: bool - Whether you want to see logging or not. - - Attributes - ---------- - debug: bool - Whether you want to see logging or not. - - """ - CLA: int = 0x5B - - def __init__(self, debug: bool = False): - """Init constructor.""" - self.debug = debug - - def serialize(self, - cla: int, - ins: Union[int, enum.IntEnum], - p1: int = 0, - p2: int = 0, - cdata: bytes = b"") -> bytes: - """Serialize the whole APDU command (header + data). - - Parameters - ---------- - cla : int - Instruction class: CLA (1 byte) - ins : Union[int, IntEnum] - Instruction code: INS (1 byte) - p1 : int - Instruction parameter 1: P1 (1 byte). - p2 : int - Instruction parameter 2: P2 (1 byte). - cdata : bytes - Bytes of command data. - - Returns - ------- - bytes - Bytes of a complete APDU command. - - """ - ins = cast(int, ins.value) if isinstance(ins, enum.IntEnum) else cast(int, ins) - - header: bytes = struct.pack("BBBBB", - cla, - ins, - p1, - p2, - len(cdata)) # add Lc to APDU header - - if self.debug: - logging.info("header: %s", header.hex()) - logging.info("cdata: %s", cdata.hex()) - - return header + cdata - - def get_app_and_version(self) -> bytes: - """Command builder for GET_APP_AND_VERSION (builtin in BOLOS SDK). - - Returns - ------- - bytes - APDU command for GET_APP_AND_VERSION. - - """ - return self.serialize(cla=0xB0, # specific CLA for BOLOS - ins=0x01, - p1=0x00, - p2=0x00, - cdata=b"") - - def get_version(self) -> bytes: - """Command builder for GET_VERSION. - - Returns - ------- - bytes - APDU command for GET_VERSION. - - """ - return self.serialize(cla=self.CLA, - ins=InsType.INS_GET_VERSION, - p1=0x00, - p2=0x00, - cdata=b"") - - def get_app_name(self) -> bytes: - """Command builder for GET_APP_NAME. - - Returns - ------- - bytes - APDU command for GET_APP_NAME. - - """ - return self.serialize(cla=self.CLA, - ins=InsType.INS_GET_APP_NAME, - p1=0x00, - p2=0x00, - cdata=b"") - - def get_public_key(self, bip32_path: str, display: bool = False) -> bytes: - """Command builder for GET_PUBLIC_KEY. - - Parameters - ---------- - bip32_path: str - String representation of BIP32 path. - display : bool - Whether you want to display the address on the device. - - Returns - ------- - bytes - APDU command for GET_PUBLIC_KEY. - - """ - bip32_paths: List[bytes] = bip32_path_from_string(bip32_path) - - cdata: bytes = b"".join([ - len(bip32_paths).to_bytes(1, byteorder="big"), - *bip32_paths - ]) - - return self.serialize(cla=self.CLA, - ins=InsType.INS_GET_PUBLIC_KEY, - p1=0x01 if display else 0x00, - p2=0x00, - cdata=cdata) - - def sign_raw(self, bip32_path: str, data: bytes) -> Iterator[Tuple[bool, bytes]]: - """Command builder for INS_SIGN_TX. - - Parameters - ---------- - bip32_path : str - String representation of BIP32 path. - data : bytes - Representation of the transaction data to be signed. - - Yields - ------- - bytes - APDU command chunk for INS_SIGN_TX. - - """ - bip32_paths: List[bytes] = bip32_path_from_string(bip32_path) - - cdata: bytes = b"".join([ - len(bip32_paths).to_bytes(1, byteorder="big"), - *bip32_paths - ]) - - yield False, self.serialize(cla=self.CLA, - ins=InsType.INS_SIGN_TX, - p1=0x00, - p2=0x80, - cdata=cdata) - - for i, (is_last, chunk) in enumerate(chunkify(data, MAX_APDU_LEN)): - if is_last: - yield True, self.serialize(cla=self.CLA, - ins=InsType.INS_SIGN_TX, - p1=i + 1, - p2=0x00, - cdata=chunk) - return - else: - yield False, self.serialize(cla=self.CLA, - ins=InsType.INS_SIGN_TX, - p1=i + 1, - p2=0x80, - cdata=chunk) diff --git a/tests/aptos_client/aptos_speculos_cmd.py b/tests/aptos_client/aptos_speculos_cmd.py deleted file mode 100644 index 6adcad0..0000000 --- a/tests/aptos_client/aptos_speculos_cmd.py +++ /dev/null @@ -1,147 +0,0 @@ -import struct -from typing import Tuple - -from speculos.client import SpeculosClient, ApduException - -from aptos_client.aptos_cmd_builder import AptosCommandBuilder, InsType -from aptos_client.exception import DeviceException - - -class AptosSpeculosCommand: - def __init__(self, - client: SpeculosClient, - debug: bool = False) -> None: - self.client = client - self.builder = AptosCommandBuilder(debug=debug) - self.debug = debug - - def get_app_and_version(self) -> Tuple[str, str]: - try: - response = self.client._apdu_exchange( - self.builder.get_app_and_version() - ) # type: int, bytes - except ApduException as error: - raise DeviceException(error_code=error.sw, ins=0x01) - - # response = format_id (1) || - # app_name_len (1) || - # app_name (var) || - # version_len (1) || - # version (var) || - offset: int = 0 - - format_id: int = response[offset] - offset += 1 - app_name_len: int = response[offset] - offset += 1 - app_name: str = response[offset:offset + app_name_len].decode("ascii") - offset += app_name_len - version_len: int = response[offset] - offset += 1 - version: str = response[offset:offset + version_len].decode("ascii") - offset += version_len - - return app_name, version - - def get_version(self) -> Tuple[int, int, int]: - try: - response = self.client._apdu_exchange( - self.builder.get_version() - ) # type: int, bytes - except ApduException as error: - raise DeviceException(error_code=error.sw, - ins=InsType.INS_GET_VERSION) - - # response = MAJOR (1) || MINOR (1) || PATCH (1) - assert len(response) == 3 - - major, minor, patch = struct.unpack( - "BBB", - response - ) # type: int, int, int - - return major, minor, patch - - def get_app_name(self) -> str: - try: - response = self.client._apdu_exchange( - self.builder.get_app_name() - ) # type: int, bytes - except ApduException as error: - raise DeviceException(error_code=error.sw, - ins=InsType.INS_GET_APP_NAME) - - return response.decode("ascii") - - def get_public_key(self, bip32_path: str, display: bool = False) -> Tuple[bytes, bytes]: - try: - response = self.client._apdu_exchange( - self.builder.get_public_key(bip32_path=bip32_path, - display=display) - ) # type: int, bytes - except ApduException as error: - raise DeviceException(error_code=error.sw, - ins=InsType.INS_GET_PUBLIC_KEY) - - # response = pub_key_len (1) || - # pub_key (var) || - # chain_code_len (1) || - # chain_code (var) - offset: int = 0 - - pub_key_len: int = response[offset] - offset += 1 - pub_key: bytes = response[offset:offset + pub_key_len] - offset += pub_key_len - chain_code_len: int = response[offset] - offset += 1 - chain_code: bytes = response[offset:offset + chain_code_len] - offset += chain_code_len - - assert len(response) == 1 + pub_key_len + 1 + chain_code_len - - return pub_key, chain_code - - def sign_raw(self, bip32_path: str, data: bytes, model: str) -> Tuple[int, bytes]: - response: bytes = b"" - - for is_last, chunk in self.builder.sign_raw(bip32_path=bip32_path, data=data): - if is_last: - with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1], - p1=chunk[2], p2=chunk[3], - data=chunk[5:]) as exchange: - # Review Transaction - self.client.press_and_release('right') - # Function - self.client.press_and_release('right') - # Coin Type - # Due to screen size, NanoS needs 1 more screens to display the coin type - if model == 'nanos': - self.client.press_and_release('right') - self.client.press_and_release('right') - # Receiver - # Due to screen size, NanoS needs 2 more screens to display the address - if model == 'nanos': - self.client.press_and_release('right') - self.client.press_and_release('right') - self.client.press_and_release('right') - self.client.press_and_release('right') - # Amount - self.client.press_and_release('right') - # Gas Fee - self.client.press_and_release('right') - # Approve - self.client.press_and_release('both') - response = exchange.receive() - else: - response = self.client._apdu_exchange(chunk) - print(response) - - # response = der_sig_len (1) || - # der_sig (var) - der_sig_len: int = response[0] - der_sig: bytes = response[1: 1 + der_sig_len] - - assert len(response) == 1 + der_sig_len - - return der_sig diff --git a/tests/aptos_client/button.py b/tests/aptos_client/button.py deleted file mode 100644 index 8f89005..0000000 --- a/tests/aptos_client/button.py +++ /dev/null @@ -1,52 +0,0 @@ -from abc import ABCMeta, abstractmethod -import socket - - -class Button(metaclass=ABCMeta): - @abstractmethod - def right_click(self): - ... - - @abstractmethod - def left_click(self): - ... - - @abstractmethod - def both_click(self): - ... - - @abstractmethod - def close(self): - ... - - -class ButtonFake(Button): - def right_click(self): - pass - - def left_click(self): - pass - - def both_click(self): - pass - - def close(self): - pass - - -class ButtonTCP(Button): - def __init__(self, server: str, port: int) -> None: - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.connect((server, port)) - - def right_click(self): - self.socket.sendall(b"Rr") - - def left_click(self): - self.socket.sendall(b"Ll") - - def both_click(self): - self.socket.sendall(b"LRlr") - - def close(self): - self.socket.close() diff --git a/tests/aptos_client/exception/__init__.py b/tests/aptos_client/exception/__init__.py deleted file mode 100644 index e411c17..0000000 --- a/tests/aptos_client/exception/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -from .device_exception import DeviceException -from .errors import (UnknownDeviceError, - DenyError, - WrongP1P2Error, - WrongDataLengthError, - InsNotSupportedError, - ClaNotSupportedError, - WrongResponseLengthError, - DisplayBip32PathFailError, - DisplayAddressFailError, - DisplayAmountFailError, - WrongTxLengthError, - TxParsingFailError, - TxGetPubKeyFail, - BadStateError, - SignatureFailError) - -__all__ = [ - "DeviceException", - "DenyError", - "UnknownDeviceError", - "WrongP1P2Error", - "WrongDataLengthError", - "InsNotSupportedError", - "ClaNotSupportedError", - "WrongResponseLengthError", - "DisplayBip32PathFailError", - "DisplayAddressFailError", - "DisplayAmountFailError", - "WrongTxLengthError", - "TxParsingFailError", - "TxGetPubKeyFail", - "BadStateError", - "SignatureFailError" -] diff --git a/tests/aptos_client/exception/device_exception.py b/tests/aptos_client/exception/device_exception.py deleted file mode 100644 index e0f8459..0000000 --- a/tests/aptos_client/exception/device_exception.py +++ /dev/null @@ -1,39 +0,0 @@ -import enum -from typing import Dict, Any, Union - -from .errors import * - - -class DeviceException(Exception): # pylint: disable=too-few-public-methods - exc: Dict[int, Any] = { - 0x6985: DenyError, - 0x6A86: WrongP1P2Error, - 0x6A87: WrongDataLengthError, - 0x6D00: InsNotSupportedError, - 0x6E00: ClaNotSupportedError, - 0xB000: WrongResponseLengthError, - 0xB001: DisplayBip32PathFailError, - 0xB002: DisplayAddressFailError, - 0xB003: DisplayAmountFailError, - 0xB004: WrongTxLengthError, - 0xB005: TxParsingFailError, - 0xB006: TxGetPubKeyFail, - 0xB007: BadStateError, - 0xB008: SignatureFailError, - 0xB009: DisplayGasFeeFailError, - } - - def __new__(cls, - error_code: int, - ins: Union[int, enum.IntEnum, None] = None, - message: str = "" - ) -> Any: - error_message: str = (f"Error in {ins!r} command" - if ins else "Error in command") - - if error_code in DeviceException.exc: - return DeviceException.exc[error_code](hex(error_code), - error_message, - message) - - return UnknownDeviceError(hex(error_code), error_message, message) diff --git a/tests/aptos_client/exception/errors.py b/tests/aptos_client/exception/errors.py deleted file mode 100644 index 3abcdfb..0000000 --- a/tests/aptos_client/exception/errors.py +++ /dev/null @@ -1,62 +0,0 @@ -class UnknownDeviceError(Exception): - pass - - -class DenyError(Exception): - pass - - -class WrongP1P2Error(Exception): - pass - - -class WrongDataLengthError(Exception): - pass - - -class InsNotSupportedError(Exception): - pass - - -class ClaNotSupportedError(Exception): - pass - - -class WrongResponseLengthError(Exception): - pass - - -class DisplayBip32PathFailError(Exception): - pass - - -class DisplayAddressFailError(Exception): - pass - - -class DisplayAmountFailError(Exception): - pass - - -class WrongTxLengthError(Exception): - pass - - -class TxParsingFailError(Exception): - pass - - -class TxGetPubKeyFail(Exception): - pass - - -class BadStateError(Exception): - pass - - -class SignatureFailError(Exception): - pass - - -class DisplayGasFeeFailError(Exception): - pass diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..909ec8b --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,15 @@ +from ragger.conftest import configuration + +########################### +### CONFIGURATION START ### +########################### + +# You can configure optional parameters by overriding the value of ragger.configuration.OPTIONAL_CONFIGURATION +# Please refer to ragger/conftest/configuration.py for their descriptions and accepted values + +######################### +### CONFIGURATION END ### +######################### + +# Pull all features from the base ragger conftest using the overridden configuration +pytest_plugins = ("ragger.conftest.base_conftest", ) diff --git a/tests/ledgercomm/README.md b/tests/ledgercomm/README.md deleted file mode 100644 index 3ab5201..0000000 --- a/tests/ledgercomm/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# LedgerComm functional tests - -> :point_right: Every path on this document assumes you are at the root of the repository. - -These tests are implemented in Python and can be executed either using the -[Speculos](https://github.com/LedgerHQ/speculos) emulator or a Ledger Nano S/X. - -Python dependencies are listed in [requirements.txt](requirements.txt), install -them using [pip](https://pypi.org/project/pip/) - -``` -pip install -r tests/ledgercomm/requirements.txt -``` - -## Launch with Speculos - -You will need Speculos installed first: - -``` -pip install --extra-index-url https://test.pypi.org/simple/ speculos -``` - -The extra index allows to fetch the latest version of Speculos. - -Then, start the Aptos application on Speculos: - -``` -speculos bin/app.elf --button-port 42000 -``` - -then in the `tests` folder run - -``` -pytest --headless tests/ledgercomm/ -``` - -The `--headless` option means the tests will trigger the buttons themselves -(button actions are needed to approve the signing), by connecting on the `42000` -port opened by Speculos (`--button-port 42000`). - -By removing this option, the tests will be stuck, waiting for the someone to -approve the step. The user could validate this step manually, and the test will -continue thereafter. - -## Launch with your Nano S/X - -To run the tests on your Ledger Nano S/X, you also need to install an optional -dependency: - -``` -pip install ledgercomm[hid] -``` - -Be sure to have the Aptos application installed (see -[this page](https://developers.ledger.com/docs/nano-app/load/) for installing an -application on a Nano S) and opened on the device, and the device connected -through USB, without any other software interacting with it. Then run: - -``` -pytest --hid tests/ledgercomm/ -``` diff --git a/tests/ledgercomm/aptos_client b/tests/ledgercomm/aptos_client deleted file mode 120000 index 20b8dd6..0000000 --- a/tests/ledgercomm/aptos_client +++ /dev/null @@ -1 +0,0 @@ -../aptos_client \ No newline at end of file diff --git a/tests/ledgercomm/conftest.py b/tests/ledgercomm/conftest.py deleted file mode 100644 index 851920f..0000000 --- a/tests/ledgercomm/conftest.py +++ /dev/null @@ -1,75 +0,0 @@ -from pathlib import Path - -import pytest - -from ledgercomm import Transport - -from aptos_client.aptos_cmd import AptosCommand -from aptos_client.button import ButtonTCP, ButtonFake - - -def pytest_addoption(parser): - parser.addoption("--hid", - action="store_true") - parser.addoption("--headless", - action="store_true") - parser.addoption("--model", - action="store", - default="nanos") - - -@pytest.fixture(scope="module") -def sw_h_path(): - # path with tests - conftest_folder_path: Path = Path(__file__).parent - # sw.h should be in ../../src/sw.h - sw_h_path = conftest_folder_path.parent.parent / "src" / "sw.h" - - if not sw_h_path.is_file(): - raise FileNotFoundError(f"Can't find sw.h: '{sw_h_path}'") - - return sw_h_path - - -@pytest.fixture(scope="session") -def hid(pytestconfig): - return pytestconfig.getoption("hid") - - -@pytest.fixture(scope="session") -def headless(pytestconfig): - return pytestconfig.getoption("headless") - - -@pytest.fixture(scope="session") -def model(pytestconfig): - return pytestconfig.getoption("model") - - -@pytest.fixture(scope="module") -def button(headless): - if headless: - button_client = ButtonTCP(server="127.0.0.1", port=42000) - else: - button_client = ButtonFake() - - yield button_client - - button_client.close() - - -@pytest.fixture(scope="session") -def cmd(hid): - transport = (Transport(interface="hid", debug=True) - if hid else Transport(interface="tcp", - server="127.0.0.1", - port=9999, - debug=True)) - command = AptosCommand( - transport=transport, - debug=True - ) - - yield command - - command.transport.close() diff --git a/tests/ledgercomm/requirements.txt b/tests/ledgercomm/requirements.txt deleted file mode 100644 index 4e3c395..0000000 --- a/tests/ledgercomm/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -pytest==6.2.5 -ledgercomm==1.1.2 -ecdsa==0.16.1 -pysha3==1.0.2 -pynacl==1.3.0 diff --git a/tests/ledgercomm/test_appname_cmd.py b/tests/ledgercomm/test_appname_cmd.py deleted file mode 100644 index aa4c84d..0000000 --- a/tests/ledgercomm/test_appname_cmd.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_app_name(cmd): - assert cmd.get_app_name() == "Aptos" diff --git a/tests/ledgercomm/test_error_cmd.py b/tests/ledgercomm/test_error_cmd.py deleted file mode 100644 index 06b4fe9..0000000 --- a/tests/ledgercomm/test_error_cmd.py +++ /dev/null @@ -1,44 +0,0 @@ -import pytest - -from aptos_client.exception import * - - -@pytest.mark.xfail(raises=ClaNotSupportedError) -def test_bad_cla(cmd): - sw, _ = cmd.transport.exchange(cla=0xa0, # 0xa0 instead of 0x5b - ins=0x03, - p1=0x00, - p2=0x00, - cdata=b"") - - raise DeviceException(error_code=sw) - - -@pytest.mark.xfail(raises=InsNotSupportedError) -def test_bad_ins(cmd): - sw, _ = cmd.transport.exchange(cla=0x5b, - ins=0xff, # bad INS - p1=0x00, - p2=0x00, - cdata=b"") - - raise DeviceException(error_code=sw) - - -@pytest.mark.xfail(raises=WrongP1P2Error) -def test_wrong_p1p2(cmd): - sw, _ = cmd.transport.exchange(cla=0x5b, - ins=0x03, - p1=0x01, # 0x01 instead of 0x00 - p2=0x00, - cdata=b"") - - raise DeviceException(error_code=sw) - - -@pytest.mark.xfail(raises=WrongDataLengthError) -def test_wrong_data_length(cmd): - # APDUs must be at least 5 bytes: CLA, INS, P1, P2, Lc. - sw, _ = cmd.transport.exchange_raw("5B00") - - raise DeviceException(error_code=sw) diff --git a/tests/ledgercomm/test_name_version.py b/tests/ledgercomm/test_name_version.py deleted file mode 100644 index c487a14..0000000 --- a/tests/ledgercomm/test_name_version.py +++ /dev/null @@ -1,7 +0,0 @@ -def test_get_app_and_version(cmd, hid): - if hid: - # for now it doesn't work with Speculos - app_name, version = cmd.get_app_and_version() - - assert app_name == "Aptos" - assert version == "0.4.17" diff --git a/tests/ledgercomm/test_pubkey_cmd.py b/tests/ledgercomm/test_pubkey_cmd.py deleted file mode 100644 index 4d1e81b..0000000 --- a/tests/ledgercomm/test_pubkey_cmd.py +++ /dev/null @@ -1,8 +0,0 @@ -def test_get_public_key(cmd): - pub_key, chain_code = cmd.get_public_key( - bip32_path="m/44'/637'/1'/0'/0'", - display=False - ) # type: bytes, bytes - - assert len(pub_key) == 33 - assert len(chain_code) == 32 diff --git a/tests/ledgercomm/test_sign_raw_cmd.py b/tests/ledgercomm/test_sign_raw_cmd.py deleted file mode 100644 index 95e58a1..0000000 --- a/tests/ledgercomm/test_sign_raw_cmd.py +++ /dev/null @@ -1,26 +0,0 @@ -from nacl.signing import VerifyKey -from nacl.exceptions import BadSignatureError - -from aptos_client.exception import * - - -def test_sign_raw_tx(cmd, button, model): - message = bytes.fromhex("b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee000000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde082a00000000000000204e0000000000006400000000000000565c51630000000022") - bip32_path: str = "m/44'/637'/1'/0'/0'" - - pub_key, chain_code = cmd.get_public_key( - bip32_path=bip32_path, - display=False - ) # type: bytes, bytes - - pk = VerifyKey(pub_key[1:]) - - der_sig = cmd.sign_raw(bip32_path=bip32_path, - data=message, - button=button, - model=model) - - try: - pk.verify(signature=der_sig, smessage=message) - except BadSignatureError as exc: - assert False, exc diff --git a/tests/ledgercomm/test_status_word.py b/tests/ledgercomm/test_status_word.py deleted file mode 100644 index 2b929ba..0000000 --- a/tests/ledgercomm/test_status_word.py +++ /dev/null @@ -1,39 +0,0 @@ -from pathlib import Path -from typing import List, Dict, Any, Tuple -import re - -from aptos_client.exception import DeviceException - - -SW_RE = re.compile(r"""(?x) - \# # character '#' - define # string 'define' - \s+ # spaces - (?PSW(?:_[A-Z0-9]+)*) # identifier (e.g. 'SW_OK') - \s+ # spaces - 0x(?P[a-fA-F0-9]{4}) # 4 bytes status word -""") - - -def parse_sw(path: Path) -> List[Tuple[str, int]]: - if not path.is_file(): - raise FileNotFoundError(f"Can't find file: '{path}'") - - sw_h: str = path.read_text() - - return [(identifier, int(sw, base=16)) - for identifier, sw in SW_RE.findall(sw_h) if sw != "9000"] - - -def test_status_word(sw_h_path): - expected_status_words: List[Tuple[str, int]] = parse_sw(sw_h_path) - status_words: Dict[int, Any] = DeviceException.exc - - assert len(expected_status_words) == len(status_words), ( - f"{expected_status_words} doesn't match {status_words}") - - # just keep status words - expected_status_words = [sw for (identifier, sw) in expected_status_words] - - for sw in status_words.keys(): - assert sw in expected_status_words, f"{status_words[sw]}({hex(sw)}) not found in sw.h!" diff --git a/tests/ledgercomm/test_version_cmd.py b/tests/ledgercomm/test_version_cmd.py deleted file mode 100644 index 8eab8c8..0000000 --- a/tests/ledgercomm/test_version_cmd.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_version(cmd): - assert cmd.get_version() == (0, 4, 17) diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..e529676 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,4 @@ +pytest +ragger[speculos,ledgerwallet]>=1.11.4 +pysha3>=1.0.0,<2.0.0 +pynacl==1.3.0 diff --git a/tests/ledgercomm/setup.cfg b/tests/setup.cfg similarity index 91% rename from tests/ledgercomm/setup.cfg rename to tests/setup.cfg index c79fd88..7d0d7e3 100644 --- a/tests/ledgercomm/setup.cfg +++ b/tests/setup.cfg @@ -8,10 +8,11 @@ disable = C0114, # missing-module-docstring C0103, # invalid-name R0801, # duplicate-code R0913 # too-many-arguments +max-line-length=100 extension-pkg-whitelist=hid [pycodestyle] -max-line-length = 90 +max-line-length = 100 [mypy-hid.*] ignore_missing_imports = True diff --git a/tests/snapshots/nanos/test_app_mainmenu/00000.png b/tests/snapshots/nanos/test_app_mainmenu/00000.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/nanos/test_app_mainmenu/00001.png b/tests/snapshots/nanos/test_app_mainmenu/00001.png new file mode 100644 index 0000000..34cf547 Binary files /dev/null and b/tests/snapshots/nanos/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/nanos/test_app_mainmenu/00002.png b/tests/snapshots/nanos/test_app_mainmenu/00002.png new file mode 100644 index 0000000..3476b97 Binary files /dev/null and b/tests/snapshots/nanos/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/nanos/test_app_mainmenu/00003.png b/tests/snapshots/nanos/test_app_mainmenu/00003.png new file mode 100644 index 0000000..e227980 Binary files /dev/null and b/tests/snapshots/nanos/test_app_mainmenu/00003.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00000.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00000.png new file mode 100644 index 0000000..c2420af Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00000.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00001.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00001.png new file mode 100644 index 0000000..71fb65e Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00001.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00002.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00002.png new file mode 100644 index 0000000..2d1b940 Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part0/00002.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00000.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00000.png new file mode 100644 index 0000000..58b87c3 Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00000.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00001.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00001.png new file mode 100644 index 0000000..7df01d2 Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00001.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00002.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00002.png new file mode 100644 index 0000000..168128d Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00002.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00003.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00003.png new file mode 100644 index 0000000..20702e1 Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00003.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00004.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00004.png new file mode 100644 index 0000000..9efc0ae Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00004.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00005.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00005.png new file mode 100644 index 0000000..5662228 Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00005.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00006.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00006.png new file mode 100644 index 0000000..2001bf6 Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00006.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00007.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00007.png new file mode 100644 index 0000000..c2143b6 Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00007.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00008.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00008.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00008.png differ diff --git a/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00009.png b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00009.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_blind_sign_tx_long_tx/part1/00009.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00000.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 0000000..0e64723 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00000.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00001.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 0000000..5ddc2dd Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00001.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00002.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 0000000..3d84e93 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00002.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00003.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 0000000..be3ff7d Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00003.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00004.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00004.png new file mode 100644 index 0000000..c3ca14e Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00004.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00005.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00005.png new file mode 100644 index 0000000..cbe4902 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00005.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00006.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00006.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00006.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00007.png b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00007.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_accepted/00007.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00000.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00000.png new file mode 100644 index 0000000..0e64723 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00000.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00001.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00001.png new file mode 100644 index 0000000..5ddc2dd Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00001.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00002.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00002.png new file mode 100644 index 0000000..3d84e93 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00002.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00003.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00003.png new file mode 100644 index 0000000..be3ff7d Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00003.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00004.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00004.png new file mode 100644 index 0000000..c3ca14e Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00004.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00005.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00005.png new file mode 100644 index 0000000..cbe4902 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00005.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00006.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00006.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00006.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00007.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00007.png new file mode 100644 index 0000000..9c7e704 Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00007.png differ diff --git a/tests/snapshots/nanos/test_get_public_key_confirm_refused/00008.png b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00008.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_get_public_key_confirm_refused/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00000.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00000.png new file mode 100644 index 0000000..c2420af Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00001.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00001.png new file mode 100644 index 0000000..71fb65e Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00002.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00002.png new file mode 100644 index 0000000..2d1b940 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part0/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00000.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00000.png new file mode 100644 index 0000000..f06b65f Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00001.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00001.png new file mode 100644 index 0000000..c68716a Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00002.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00002.png new file mode 100644 index 0000000..03ca92b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00003.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00003.png new file mode 100644 index 0000000..ba6782f Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00004.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00004.png new file mode 100644 index 0000000..88c45cd Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00005.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00005.png new file mode 100644 index 0000000..c859023 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00006.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00006.png new file mode 100644 index 0000000..6dc8ceb Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00007.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00007.png new file mode 100644 index 0000000..0cdb498 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00008.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00008.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00009.png b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00009.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_long_raw_msg/part1/00009.png differ diff --git a/tests/snapshots/nanos/test_sign_short_raw_msg/00000.png b/tests/snapshots/nanos/test_sign_short_raw_msg/00000.png new file mode 100644 index 0000000..5caa4c0 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_short_raw_msg/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_short_raw_msg/00001.png b/tests/snapshots/nanos/test_sign_short_raw_msg/00001.png new file mode 100644 index 0000000..3efc0f6 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_short_raw_msg/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_short_raw_msg/00002.png b/tests/snapshots/nanos/test_sign_short_raw_msg/00002.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_short_raw_msg/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_short_raw_msg/00003.png b/tests/snapshots/nanos/test_sign_short_raw_msg/00003.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_short_raw_msg/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00000.png b/tests/snapshots/nanos/test_sign_tx_refused/00000.png new file mode 100644 index 0000000..8842989 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00001.png b/tests/snapshots/nanos/test_sign_tx_refused/00001.png new file mode 100644 index 0000000..2438bf2 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00002.png b/tests/snapshots/nanos/test_sign_tx_refused/00002.png new file mode 100644 index 0000000..a2c3f8a Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00003.png b/tests/snapshots/nanos/test_sign_tx_refused/00003.png new file mode 100644 index 0000000..9d538c5 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00004.png b/tests/snapshots/nanos/test_sign_tx_refused/00004.png new file mode 100644 index 0000000..70cf6df Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00005.png b/tests/snapshots/nanos/test_sign_tx_refused/00005.png new file mode 100644 index 0000000..2dd51ea Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00006.png b/tests/snapshots/nanos/test_sign_tx_refused/00006.png new file mode 100644 index 0000000..3217c6d Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00007.png b/tests/snapshots/nanos/test_sign_tx_refused/00007.png new file mode 100644 index 0000000..e70f29c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00008.png b/tests/snapshots/nanos/test_sign_tx_refused/00008.png new file mode 100644 index 0000000..ac72eab Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00009.png b/tests/snapshots/nanos/test_sign_tx_refused/00009.png new file mode 100644 index 0000000..282f54f Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00009.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00010.png b/tests/snapshots/nanos/test_sign_tx_refused/00010.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00010.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00011.png b/tests/snapshots/nanos/test_sign_tx_refused/00011.png new file mode 100644 index 0000000..9c7e704 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00011.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_refused/00012.png b/tests/snapshots/nanos/test_sign_tx_refused/00012.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_refused/00012.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_msg/00000.png b/tests/snapshots/nanos/test_sign_tx_short_msg/00000.png new file mode 100644 index 0000000..5caa4c0 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_msg/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_msg/00001.png b/tests/snapshots/nanos/test_sign_tx_short_msg/00001.png new file mode 100644 index 0000000..a5bad38 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_msg/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_msg/00002.png b/tests/snapshots/nanos/test_sign_tx_short_msg/00002.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_msg/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_msg/00003.png b/tests/snapshots/nanos/test_sign_tx_short_msg/00003.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_msg/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00000.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00000.png new file mode 100644 index 0000000..8842989 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00000.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png new file mode 100644 index 0000000..c636b10 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00001.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00002.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00002.png new file mode 100644 index 0000000..9895698 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00002.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00003.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00003.png new file mode 100644 index 0000000..b516c61 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00003.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00004.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00004.png new file mode 100644 index 0000000..161f7d3 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00004.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00005.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00005.png new file mode 100644 index 0000000..86db397 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00005.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00006.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00006.png new file mode 100644 index 0000000..231fb06 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00006.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00007.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00007.png new file mode 100644 index 0000000..81e05d2 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00007.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00008.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00008.png new file mode 100644 index 0000000..7ab267c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00008.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00009.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00009.png new file mode 100644 index 0000000..a76dbae Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00009.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00010.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00010.png new file mode 100644 index 0000000..cd5f607 Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00010.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00011.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00011.png new file mode 100644 index 0000000..66c411c Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00011.png differ diff --git a/tests/snapshots/nanos/test_sign_tx_short_tx/00012.png b/tests/snapshots/nanos/test_sign_tx_short_tx/00012.png new file mode 100644 index 0000000..02d922b Binary files /dev/null and b/tests/snapshots/nanos/test_sign_tx_short_tx/00012.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00000.png b/tests/snapshots/nanosp/test_app_mainmenu/00000.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00001.png b/tests/snapshots/nanosp/test_app_mainmenu/00001.png new file mode 100644 index 0000000..d885fe6 Binary files /dev/null and b/tests/snapshots/nanosp/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00002.png b/tests/snapshots/nanosp/test_app_mainmenu/00002.png new file mode 100644 index 0000000..7e1a28c Binary files /dev/null and b/tests/snapshots/nanosp/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/nanosp/test_app_mainmenu/00003.png b/tests/snapshots/nanosp/test_app_mainmenu/00003.png new file mode 100644 index 0000000..bcb20c6 Binary files /dev/null and b/tests/snapshots/nanosp/test_app_mainmenu/00003.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part0/00000.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part0/00000.png new file mode 100644 index 0000000..2d20155 Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part0/00000.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part0/00001.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part0/00001.png new file mode 100644 index 0000000..c4bbf90 Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part0/00001.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00000.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00000.png new file mode 100644 index 0000000..3d6158d Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00000.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00001.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00001.png new file mode 100644 index 0000000..f5226fc Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00001.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00002.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00002.png new file mode 100644 index 0000000..63fa6f5 Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00002.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00003.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00003.png new file mode 100644 index 0000000..27000e6 Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00003.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00004.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00004.png new file mode 100644 index 0000000..2c8ac7e Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00004.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00005.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00005.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00005.png differ diff --git a/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00006.png b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00006.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_blind_sign_tx_long_tx/part1/00006.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00000.png b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 0000000..19c9a3e Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00000.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00001.png b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 0000000..ee8e82f Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00001.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00002.png b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 0000000..2d42b36 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00002.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00003.png b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 0000000..ccb5e27 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00003.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00004.png b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00004.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00004.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00005.png b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00005.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_accepted/00005.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00000.png b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00000.png new file mode 100644 index 0000000..19c9a3e Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00000.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00001.png b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00001.png new file mode 100644 index 0000000..ee8e82f Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00001.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00002.png b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00002.png new file mode 100644 index 0000000..2d42b36 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00002.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00003.png b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00003.png new file mode 100644 index 0000000..ccb5e27 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00003.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00004.png b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00004.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00004.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00005.png b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00005.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00005.png differ diff --git a/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00006.png b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00006.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_get_public_key_confirm_refused/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part0/00000.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part0/00000.png new file mode 100644 index 0000000..2d20155 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part0/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part0/00001.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part0/00001.png new file mode 100644 index 0000000..c4bbf90 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part0/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00000.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00000.png new file mode 100644 index 0000000..6644903 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00001.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00001.png new file mode 100644 index 0000000..8dc82b3 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00002.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00002.png new file mode 100644 index 0000000..35a288b Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00003.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00003.png new file mode 100644 index 0000000..6d07ac6 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00004.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00004.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00005.png b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00005.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_long_raw_msg/part1/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_short_raw_msg/00000.png b/tests/snapshots/nanosp/test_sign_short_raw_msg/00000.png new file mode 100644 index 0000000..76560b8 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_short_raw_msg/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_short_raw_msg/00001.png b/tests/snapshots/nanosp/test_sign_short_raw_msg/00001.png new file mode 100644 index 0000000..29d2773 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_short_raw_msg/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_short_raw_msg/00002.png b/tests/snapshots/nanosp/test_sign_short_raw_msg/00002.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_short_raw_msg/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_short_raw_msg/00003.png b/tests/snapshots/nanosp/test_sign_short_raw_msg/00003.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_short_raw_msg/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00000.png b/tests/snapshots/nanosp/test_sign_tx_refused/00000.png new file mode 100644 index 0000000..df51419 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00001.png b/tests/snapshots/nanosp/test_sign_tx_refused/00001.png new file mode 100644 index 0000000..a585d6e Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00002.png b/tests/snapshots/nanosp/test_sign_tx_refused/00002.png new file mode 100644 index 0000000..3d1baf8 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00003.png b/tests/snapshots/nanosp/test_sign_tx_refused/00003.png new file mode 100644 index 0000000..1ccd507 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00004.png b/tests/snapshots/nanosp/test_sign_tx_refused/00004.png new file mode 100644 index 0000000..6d68d73 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00005.png b/tests/snapshots/nanosp/test_sign_tx_refused/00005.png new file mode 100644 index 0000000..9080078 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00006.png b/tests/snapshots/nanosp/test_sign_tx_refused/00006.png new file mode 100644 index 0000000..b74a015 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00007.png b/tests/snapshots/nanosp/test_sign_tx_refused/00007.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00007.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00008.png b/tests/snapshots/nanosp/test_sign_tx_refused/00008.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00008.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_refused/00009.png b/tests/snapshots/nanosp/test_sign_tx_refused/00009.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_refused/00009.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_msg/00000.png b/tests/snapshots/nanosp/test_sign_tx_short_msg/00000.png new file mode 100644 index 0000000..76560b8 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_msg/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_msg/00001.png b/tests/snapshots/nanosp/test_sign_tx_short_msg/00001.png new file mode 100644 index 0000000..bff1d22 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_msg/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_msg/00002.png b/tests/snapshots/nanosp/test_sign_tx_short_msg/00002.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_msg/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_msg/00003.png b/tests/snapshots/nanosp/test_sign_tx_short_msg/00003.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_msg/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00000.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00000.png new file mode 100644 index 0000000..df51419 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00000.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png new file mode 100644 index 0000000..ac30e02 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00001.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00002.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00002.png new file mode 100644 index 0000000..bf36753 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00002.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00003.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00003.png new file mode 100644 index 0000000..dba442a Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00003.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00004.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00004.png new file mode 100644 index 0000000..ebcd22e Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00004.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00005.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00005.png new file mode 100644 index 0000000..206f7d5 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00005.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00006.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00006.png new file mode 100644 index 0000000..7cf99cc Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00006.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00007.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00007.png new file mode 100644 index 0000000..180d8bb Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00007.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00008.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00008.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00008.png differ diff --git a/tests/snapshots/nanosp/test_sign_tx_short_tx/00009.png b/tests/snapshots/nanosp/test_sign_tx_short_tx/00009.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanosp/test_sign_tx_short_tx/00009.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00000.png b/tests/snapshots/nanox/test_app_mainmenu/00000.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00001.png b/tests/snapshots/nanox/test_app_mainmenu/00001.png new file mode 100644 index 0000000..d885fe6 Binary files /dev/null and b/tests/snapshots/nanox/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00002.png b/tests/snapshots/nanox/test_app_mainmenu/00002.png new file mode 100644 index 0000000..7e1a28c Binary files /dev/null and b/tests/snapshots/nanox/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/nanox/test_app_mainmenu/00003.png b/tests/snapshots/nanox/test_app_mainmenu/00003.png new file mode 100644 index 0000000..bcb20c6 Binary files /dev/null and b/tests/snapshots/nanox/test_app_mainmenu/00003.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part0/00000.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part0/00000.png new file mode 100644 index 0000000..2d20155 Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part0/00000.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part0/00001.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part0/00001.png new file mode 100644 index 0000000..c4bbf90 Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part0/00001.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00000.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00000.png new file mode 100644 index 0000000..3d6158d Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00000.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00001.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00001.png new file mode 100644 index 0000000..f5226fc Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00001.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00002.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00002.png new file mode 100644 index 0000000..63fa6f5 Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00002.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00003.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00003.png new file mode 100644 index 0000000..27000e6 Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00003.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00004.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00004.png new file mode 100644 index 0000000..2c8ac7e Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00004.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00005.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00005.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00005.png differ diff --git a/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00006.png b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00006.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_blind_sign_tx_long_tx/part1/00006.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00000.png b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 0000000..19c9a3e Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00000.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00001.png b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 0000000..ee8e82f Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00001.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00002.png b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 0000000..2d42b36 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00002.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00003.png b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 0000000..ccb5e27 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00003.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00004.png b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00004.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00004.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00005.png b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00005.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_accepted/00005.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_refused/00000.png b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00000.png new file mode 100644 index 0000000..19c9a3e Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00000.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_refused/00001.png b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00001.png new file mode 100644 index 0000000..ee8e82f Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00001.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_refused/00002.png b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00002.png new file mode 100644 index 0000000..2d42b36 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00002.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_refused/00003.png b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00003.png new file mode 100644 index 0000000..ccb5e27 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00003.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_refused/00004.png b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00004.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00004.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_refused/00005.png b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00005.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00005.png differ diff --git a/tests/snapshots/nanox/test_get_public_key_confirm_refused/00006.png b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00006.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_get_public_key_confirm_refused/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part0/00000.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part0/00000.png new file mode 100644 index 0000000..2d20155 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part0/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part0/00001.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part0/00001.png new file mode 100644 index 0000000..c4bbf90 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part0/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00000.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00000.png new file mode 100644 index 0000000..6644903 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00001.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00001.png new file mode 100644 index 0000000..8dc82b3 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00002.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00002.png new file mode 100644 index 0000000..35a288b Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00003.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00003.png new file mode 100644 index 0000000..6d07ac6 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00004.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00004.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00005.png b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00005.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_long_raw_msg/part1/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_short_raw_msg/00000.png b/tests/snapshots/nanox/test_sign_short_raw_msg/00000.png new file mode 100644 index 0000000..76560b8 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_short_raw_msg/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_short_raw_msg/00001.png b/tests/snapshots/nanox/test_sign_short_raw_msg/00001.png new file mode 100644 index 0000000..29d2773 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_short_raw_msg/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_short_raw_msg/00002.png b/tests/snapshots/nanox/test_sign_short_raw_msg/00002.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_short_raw_msg/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_short_raw_msg/00003.png b/tests/snapshots/nanox/test_sign_short_raw_msg/00003.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_short_raw_msg/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00000.png b/tests/snapshots/nanox/test_sign_tx_refused/00000.png new file mode 100644 index 0000000..df51419 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00001.png b/tests/snapshots/nanox/test_sign_tx_refused/00001.png new file mode 100644 index 0000000..a585d6e Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00002.png b/tests/snapshots/nanox/test_sign_tx_refused/00002.png new file mode 100644 index 0000000..3d1baf8 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00003.png b/tests/snapshots/nanox/test_sign_tx_refused/00003.png new file mode 100644 index 0000000..1ccd507 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00004.png b/tests/snapshots/nanox/test_sign_tx_refused/00004.png new file mode 100644 index 0000000..6d68d73 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00005.png b/tests/snapshots/nanox/test_sign_tx_refused/00005.png new file mode 100644 index 0000000..9080078 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00006.png b/tests/snapshots/nanox/test_sign_tx_refused/00006.png new file mode 100644 index 0000000..b74a015 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00007.png b/tests/snapshots/nanox/test_sign_tx_refused/00007.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00007.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00008.png b/tests/snapshots/nanox/test_sign_tx_refused/00008.png new file mode 100644 index 0000000..e90cd9d Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00008.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_refused/00009.png b/tests/snapshots/nanox/test_sign_tx_refused/00009.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_refused/00009.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_msg/00000.png b/tests/snapshots/nanox/test_sign_tx_short_msg/00000.png new file mode 100644 index 0000000..76560b8 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_msg/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_msg/00001.png b/tests/snapshots/nanox/test_sign_tx_short_msg/00001.png new file mode 100644 index 0000000..bff1d22 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_msg/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_msg/00002.png b/tests/snapshots/nanox/test_sign_tx_short_msg/00002.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_msg/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_msg/00003.png b/tests/snapshots/nanox/test_sign_tx_short_msg/00003.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_msg/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00000.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00000.png new file mode 100644 index 0000000..df51419 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00000.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00001.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00001.png new file mode 100644 index 0000000..ac30e02 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00001.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00002.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00002.png new file mode 100644 index 0000000..bf36753 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00002.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00003.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00003.png new file mode 100644 index 0000000..dba442a Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00003.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00004.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00004.png new file mode 100644 index 0000000..ebcd22e Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00004.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00005.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00005.png new file mode 100644 index 0000000..206f7d5 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00005.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00006.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00006.png new file mode 100644 index 0000000..7cf99cc Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00006.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00007.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00007.png new file mode 100644 index 0000000..180d8bb Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00007.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00008.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00008.png new file mode 100644 index 0000000..53ae651 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00008.png differ diff --git a/tests/snapshots/nanox/test_sign_tx_short_tx/00009.png b/tests/snapshots/nanox/test_sign_tx_short_tx/00009.png new file mode 100644 index 0000000..79857a0 Binary files /dev/null and b/tests/snapshots/nanox/test_sign_tx_short_tx/00009.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00000.png b/tests/snapshots/stax/test_app_mainmenu/00000.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00000.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00001.png b/tests/snapshots/stax/test_app_mainmenu/00001.png new file mode 100644 index 0000000..33e5cf9 Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00001.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00002.png b/tests/snapshots/stax/test_app_mainmenu/00002.png new file mode 100644 index 0000000..bdf16f2 Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00002.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00003.png b/tests/snapshots/stax/test_app_mainmenu/00003.png new file mode 100644 index 0000000..3845f3a Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00003.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00004.png b/tests/snapshots/stax/test_app_mainmenu/00004.png new file mode 100644 index 0000000..bdf16f2 Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00004.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00005.png b/tests/snapshots/stax/test_app_mainmenu/00005.png new file mode 100644 index 0000000..33e5cf9 Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00005.png differ diff --git a/tests/snapshots/stax/test_app_mainmenu/00006.png b/tests/snapshots/stax/test_app_mainmenu/00006.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_app_mainmenu/00006.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part0/00000.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part0/00000.png new file mode 100644 index 0000000..2519750 Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part0/00000.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part0/00001.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part0/00001.png new file mode 100644 index 0000000..279acd5 Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part0/00001.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00000.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00000.png new file mode 100644 index 0000000..0155f62 Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00000.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00001.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00001.png new file mode 100644 index 0000000..0af716f Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00001.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00002.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00002.png new file mode 100644 index 0000000..873f85b Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00002.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00003.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00003.png new file mode 100644 index 0000000..06b34b8 Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00003.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00004.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00004.png new file mode 100644 index 0000000..90c9f75 Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00004.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00005.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00005.png new file mode 100644 index 0000000..cd2f527 Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00005.png differ diff --git a/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00006.png b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00006.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_blind_sign_tx_long_tx/part1/00006.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00000.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00000.png new file mode 100644 index 0000000..503dc1c Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00000.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png new file mode 100644 index 0000000..58dacfe Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png new file mode 100644 index 0000000..1d83684 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00002.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png new file mode 100644 index 0000000..58dacfe Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00003.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00004.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00004.png new file mode 100644 index 0000000..a14a46a Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00004.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00005.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00005.png new file mode 100644 index 0000000..f59899e Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00005.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_accepted/00006.png b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00006.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_accepted/00006.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00000.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00000.png new file mode 100644 index 0000000..503dc1c Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00000.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00001.png new file mode 100644 index 0000000..6213400 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00002.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part0/00002.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00000.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00000.png new file mode 100644 index 0000000..503dc1c Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00000.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00001.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00001.png new file mode 100644 index 0000000..58dacfe Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00001.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00002.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00002.png new file mode 100644 index 0000000..6213400 Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00002.png differ diff --git a/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00003.png b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00003.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_get_public_key_confirm_refused/part1/00003.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part0/00000.png b/tests/snapshots/stax/test_sign_long_raw_msg/part0/00000.png new file mode 100644 index 0000000..7edbd37 Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part0/00000.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part0/00001.png b/tests/snapshots/stax/test_sign_long_raw_msg/part0/00001.png new file mode 100644 index 0000000..279acd5 Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part0/00001.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part1/00000.png b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00000.png new file mode 100644 index 0000000..8581a71 Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00000.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part1/00001.png b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00001.png new file mode 100644 index 0000000..aa60a57 Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00001.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part1/00002.png b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00002.png new file mode 100644 index 0000000..90a2294 Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00002.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part1/00003.png b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00003.png new file mode 100644 index 0000000..78bbaaf Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00003.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part1/00004.png b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00004.png new file mode 100644 index 0000000..c1a8fd4 Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00004.png differ diff --git a/tests/snapshots/stax/test_sign_long_raw_msg/part1/00005.png b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00005.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_long_raw_msg/part1/00005.png differ diff --git a/tests/snapshots/stax/test_sign_short_raw_msg/00000.png b/tests/snapshots/stax/test_sign_short_raw_msg/00000.png new file mode 100644 index 0000000..aa60a57 Binary files /dev/null and b/tests/snapshots/stax/test_sign_short_raw_msg/00000.png differ diff --git a/tests/snapshots/stax/test_sign_short_raw_msg/00001.png b/tests/snapshots/stax/test_sign_short_raw_msg/00001.png new file mode 100644 index 0000000..5b00255 Binary files /dev/null and b/tests/snapshots/stax/test_sign_short_raw_msg/00001.png differ diff --git a/tests/snapshots/stax/test_sign_short_raw_msg/00002.png b/tests/snapshots/stax/test_sign_short_raw_msg/00002.png new file mode 100644 index 0000000..78bbaaf Binary files /dev/null and b/tests/snapshots/stax/test_sign_short_raw_msg/00002.png differ diff --git a/tests/snapshots/stax/test_sign_short_raw_msg/00003.png b/tests/snapshots/stax/test_sign_short_raw_msg/00003.png new file mode 100644 index 0000000..c1a8fd4 Binary files /dev/null and b/tests/snapshots/stax/test_sign_short_raw_msg/00003.png differ diff --git a/tests/snapshots/stax/test_sign_short_raw_msg/00004.png b/tests/snapshots/stax/test_sign_short_raw_msg/00004.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_short_raw_msg/00004.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part0/00000.png b/tests/snapshots/stax/test_sign_tx_refused/part0/00000.png new file mode 100644 index 0000000..beac77b Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part0/00000.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part0/00001.png b/tests/snapshots/stax/test_sign_tx_refused/part0/00001.png new file mode 100644 index 0000000..babad98 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part0/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part0/00002.png b/tests/snapshots/stax/test_sign_tx_refused/part0/00002.png new file mode 100644 index 0000000..cebc8be Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part0/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part0/00003.png b/tests/snapshots/stax/test_sign_tx_refused/part0/00003.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part0/00003.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part1/00000.png b/tests/snapshots/stax/test_sign_tx_refused/part1/00000.png new file mode 100644 index 0000000..beac77b Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part1/00000.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part1/00001.png b/tests/snapshots/stax/test_sign_tx_refused/part1/00001.png new file mode 100644 index 0000000..be3564b Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part1/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part1/00002.png b/tests/snapshots/stax/test_sign_tx_refused/part1/00002.png new file mode 100644 index 0000000..babad98 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part1/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part1/00003.png b/tests/snapshots/stax/test_sign_tx_refused/part1/00003.png new file mode 100644 index 0000000..cebc8be Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part1/00003.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part1/00004.png b/tests/snapshots/stax/test_sign_tx_refused/part1/00004.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part1/00004.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part2/00000.png b/tests/snapshots/stax/test_sign_tx_refused/part2/00000.png new file mode 100644 index 0000000..beac77b Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part2/00000.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part2/00001.png b/tests/snapshots/stax/test_sign_tx_refused/part2/00001.png new file mode 100644 index 0000000..be3564b Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part2/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part2/00002.png b/tests/snapshots/stax/test_sign_tx_refused/part2/00002.png new file mode 100644 index 0000000..c461a42 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part2/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part2/00003.png b/tests/snapshots/stax/test_sign_tx_refused/part2/00003.png new file mode 100644 index 0000000..babad98 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part2/00003.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part2/00004.png b/tests/snapshots/stax/test_sign_tx_refused/part2/00004.png new file mode 100644 index 0000000..cebc8be Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part2/00004.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part2/00005.png b/tests/snapshots/stax/test_sign_tx_refused/part2/00005.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part2/00005.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part3/00000.png b/tests/snapshots/stax/test_sign_tx_refused/part3/00000.png new file mode 100644 index 0000000..beac77b Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part3/00000.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part3/00001.png b/tests/snapshots/stax/test_sign_tx_refused/part3/00001.png new file mode 100644 index 0000000..be3564b Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part3/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part3/00002.png b/tests/snapshots/stax/test_sign_tx_refused/part3/00002.png new file mode 100644 index 0000000..c461a42 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part3/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part3/00003.png b/tests/snapshots/stax/test_sign_tx_refused/part3/00003.png new file mode 100644 index 0000000..90c9f75 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part3/00003.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part3/00004.png b/tests/snapshots/stax/test_sign_tx_refused/part3/00004.png new file mode 100644 index 0000000..babad98 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part3/00004.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part3/00005.png b/tests/snapshots/stax/test_sign_tx_refused/part3/00005.png new file mode 100644 index 0000000..cebc8be Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part3/00005.png differ diff --git a/tests/snapshots/stax/test_sign_tx_refused/part3/00006.png b/tests/snapshots/stax/test_sign_tx_refused/part3/00006.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_refused/part3/00006.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_msg/00000.png b/tests/snapshots/stax/test_sign_tx_short_msg/00000.png new file mode 100644 index 0000000..aa60a57 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_msg/00000.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_msg/00001.png b/tests/snapshots/stax/test_sign_tx_short_msg/00001.png new file mode 100644 index 0000000..650341d Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_msg/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_msg/00002.png b/tests/snapshots/stax/test_sign_tx_short_msg/00002.png new file mode 100644 index 0000000..78bbaaf Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_msg/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_msg/00003.png b/tests/snapshots/stax/test_sign_tx_short_msg/00003.png new file mode 100644 index 0000000..c1a8fd4 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_msg/00003.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_msg/00004.png b/tests/snapshots/stax/test_sign_tx_short_msg/00004.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_msg/00004.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx/00000.png b/tests/snapshots/stax/test_sign_tx_short_tx/00000.png new file mode 100644 index 0000000..0a6307e Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx/00000.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx/00001.png b/tests/snapshots/stax/test_sign_tx_short_tx/00001.png new file mode 100644 index 0000000..250d31f Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx/00001.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx/00002.png b/tests/snapshots/stax/test_sign_tx_short_tx/00002.png new file mode 100644 index 0000000..5d03eb5 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx/00002.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx/00003.png b/tests/snapshots/stax/test_sign_tx_short_tx/00003.png new file mode 100644 index 0000000..90c9f75 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx/00003.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx/00004.png b/tests/snapshots/stax/test_sign_tx_short_tx/00004.png new file mode 100644 index 0000000..cd2f527 Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx/00004.png differ diff --git a/tests/snapshots/stax/test_sign_tx_short_tx/00005.png b/tests/snapshots/stax/test_sign_tx_short_tx/00005.png new file mode 100644 index 0000000..733caca Binary files /dev/null and b/tests/snapshots/stax/test_sign_tx_short_tx/00005.png differ diff --git a/tests/speculos/README.md b/tests/speculos/README.md deleted file mode 100644 index d2f492d..0000000 --- a/tests/speculos/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Speculos functional tests - -> :point_right: Every path on this document assumes you are at the root of the repository. - -These tests are implemented in Python with the `SpeculosClient` interface which -allows easy execution on the [Speculos](https://github.com/LedgerHQ/speculos) -emulator. - - -## Dependencies - -Python dependencies are listed in [requirements.txt](requirements.txt), install -them using [pip](https://pypi.org/project/pip/) - -``` -pip install --extra-index-url https://test.pypi.org/simple/ -r requirements.txt -``` - -The extra index allows to fetch the latest version of Speculos. - - -## Launch the tests - -Given the requirements are installed, just do: - -``` -pytest tests/speculos/ -``` \ No newline at end of file diff --git a/tests/speculos/aptos_client b/tests/speculos/aptos_client deleted file mode 120000 index 20b8dd6..0000000 --- a/tests/speculos/aptos_client +++ /dev/null @@ -1 +0,0 @@ -../aptos_client \ No newline at end of file diff --git a/tests/speculos/conftest.py b/tests/speculos/conftest.py deleted file mode 100644 index fda2939..0000000 --- a/tests/speculos/conftest.py +++ /dev/null @@ -1,53 +0,0 @@ -from collections import namedtuple -from pathlib import Path - -import pytest - -from speculos.client import SpeculosClient - -from aptos_client.aptos_speculos_cmd import AptosSpeculosCommand - - -SCRIPT_DIR = Path(__file__).absolute().parent -API_URL = "http://127.0.0.1:5000" - - -def pytest_addoption(parser): - parser.addoption("--model", - action="store", - default="nanos") - - -@pytest.fixture(scope="session") -def model(pytestconfig): - return pytestconfig.getoption("model") - - - -@pytest.fixture(scope="module") -def sw_h_path(): - # path with tests - conftest_folder_path: Path = Path(__file__).parent - # sw.h should be in ../../src/sw.h - sw_h_path = conftest_folder_path.parent.parent / "src" / "sw.h" - - if not sw_h_path.is_file(): - raise FileNotFoundError(f"Can't find sw.h: '{sw_h_path}'") - - return sw_h_path - - -@pytest.fixture -def client(model): - file_path = SCRIPT_DIR.parent.parent / "bin" / "app.elf" - args = ['--model', model] - with SpeculosClient(app=str(file_path), args=args) as client: - yield client - - -@pytest.fixture -def cmd(client): - yield AptosSpeculosCommand( - client=client, - debug=True - ) diff --git a/tests/speculos/requirements.txt b/tests/speculos/requirements.txt deleted file mode 100644 index f8ec13f..0000000 --- a/tests/speculos/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -speculos -pytest==6.2.5 -ledgercomm==1.1.2 -ecdsa==0.16.1 -pysha3==1.0.2 -pynacl==1.3.0 diff --git a/tests/speculos/setup.cfg b/tests/speculos/setup.cfg deleted file mode 100644 index c79fd88..0000000 --- a/tests/speculos/setup.cfg +++ /dev/null @@ -1,20 +0,0 @@ -[tool:pytest] -addopts = --strict-markers - -[pylint] -disable = C0114, # missing-module-docstring - C0115, # missing-class-docstring - C0116, # missing-function-docstring - C0103, # invalid-name - R0801, # duplicate-code - R0913 # too-many-arguments -extension-pkg-whitelist=hid - -[pycodestyle] -max-line-length = 90 - -[mypy-hid.*] -ignore_missing_imports = True - -[mypy-pytest.*] -ignore_missing_imports = True diff --git a/tests/speculos/test_appname_cmd.py b/tests/speculos/test_appname_cmd.py deleted file mode 100644 index aa4c84d..0000000 --- a/tests/speculos/test_appname_cmd.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_app_name(cmd): - assert cmd.get_app_name() == "Aptos" diff --git a/tests/speculos/test_error_cmd.py b/tests/speculos/test_error_cmd.py deleted file mode 100644 index 2341e25..0000000 --- a/tests/speculos/test_error_cmd.py +++ /dev/null @@ -1,42 +0,0 @@ -import pytest - -from speculos.client import ApduException - -from aptos_client.exception import * - - -@pytest.mark.xfail(raises=ClaNotSupportedError) -def test_bad_cla(client): - try: - client.apdu_exchange(cla=0xa0, # 0xa0 instead of 0x5b - ins=0x03) - except ApduException as error: - raise DeviceException(error_code=error.sw) - - -@pytest.mark.xfail(raises=InsNotSupportedError) -def test_bad_ins(client): - try: - client.apdu_exchange(cla=0x5b, - ins=0xff) # bad INS - except ApduException as error: - raise DeviceException(error_code=error.sw) - - -@pytest.mark.xfail(raises=WrongP1P2Error) -def test_wrong_p1p2(client): - try: - client.apdu_exchange(cla=0x5b, - ins=0x03, - p1=0x01) # 0x01 instead of 0x00 - except ApduException as error: - raise DeviceException(error_code=error.sw) - - -@pytest.mark.xfail(raises=WrongDataLengthError) -def test_wrong_data_length(client): - try: - # APDUs must be at least 5 bytes: CLA, INS, P1, P2, Lc. - client._apdu_exchange(b"5B00") - except ApduException as error: - raise DeviceException(error_code=error.sw) diff --git a/tests/speculos/test_name_version.py b/tests/speculos/test_name_version.py deleted file mode 100644 index 82126d4..0000000 --- a/tests/speculos/test_name_version.py +++ /dev/null @@ -1,5 +0,0 @@ -def test_get_app_and_version(cmd): - app_name, version = cmd.get_app_and_version() - - assert app_name == "Aptos" - assert version == "0.4.17" diff --git a/tests/speculos/test_pubkey_cmd.py b/tests/speculos/test_pubkey_cmd.py deleted file mode 100644 index 4d1e81b..0000000 --- a/tests/speculos/test_pubkey_cmd.py +++ /dev/null @@ -1,8 +0,0 @@ -def test_get_public_key(cmd): - pub_key, chain_code = cmd.get_public_key( - bip32_path="m/44'/637'/1'/0'/0'", - display=False - ) # type: bytes, bytes - - assert len(pub_key) == 33 - assert len(chain_code) == 32 diff --git a/tests/speculos/test_sign_raw_cmd.py b/tests/speculos/test_sign_raw_cmd.py deleted file mode 100644 index 1f111bf..0000000 --- a/tests/speculos/test_sign_raw_cmd.py +++ /dev/null @@ -1,25 +0,0 @@ -from nacl.signing import VerifyKey -from nacl.exceptions import BadSignatureError - -from aptos_client.exception import * - - -def test_sign_raw_tx(cmd, model): - message = bytes.fromhex("b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee000000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde082a00000000000000204e0000000000006400000000000000565c51630000000022") - bip32_path: str = "m/44'/637'/1'/0'/0'" - - pub_key, chain_code = cmd.get_public_key( - bip32_path=bip32_path, - display=False - ) # type: bytes, bytes - - pk = VerifyKey(pub_key[1:]) - - der_sig = cmd.sign_raw(bip32_path=bip32_path, - data=message, - model=model) - - try: - pk.verify(signature=der_sig, smessage=message) - except BadSignatureError as exc: - assert False, exc diff --git a/tests/speculos/test_status_word.py b/tests/speculos/test_status_word.py deleted file mode 100644 index 2b929ba..0000000 --- a/tests/speculos/test_status_word.py +++ /dev/null @@ -1,39 +0,0 @@ -from pathlib import Path -from typing import List, Dict, Any, Tuple -import re - -from aptos_client.exception import DeviceException - - -SW_RE = re.compile(r"""(?x) - \# # character '#' - define # string 'define' - \s+ # spaces - (?PSW(?:_[A-Z0-9]+)*) # identifier (e.g. 'SW_OK') - \s+ # spaces - 0x(?P[a-fA-F0-9]{4}) # 4 bytes status word -""") - - -def parse_sw(path: Path) -> List[Tuple[str, int]]: - if not path.is_file(): - raise FileNotFoundError(f"Can't find file: '{path}'") - - sw_h: str = path.read_text() - - return [(identifier, int(sw, base=16)) - for identifier, sw in SW_RE.findall(sw_h) if sw != "9000"] - - -def test_status_word(sw_h_path): - expected_status_words: List[Tuple[str, int]] = parse_sw(sw_h_path) - status_words: Dict[int, Any] = DeviceException.exc - - assert len(expected_status_words) == len(status_words), ( - f"{expected_status_words} doesn't match {status_words}") - - # just keep status words - expected_status_words = [sw for (identifier, sw) in expected_status_words] - - for sw in status_words.keys(): - assert sw in expected_status_words, f"{status_words[sw]}({hex(sw)}) not found in sw.h!" diff --git a/tests/speculos/test_version_cmd.py b/tests/speculos/test_version_cmd.py deleted file mode 100644 index 8eab8c8..0000000 --- a/tests/speculos/test_version_cmd.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_version(cmd): - assert cmd.get_version() == (0, 4, 17) diff --git a/tests/test_app_mainmenu.py b/tests/test_app_mainmenu.py new file mode 100644 index 0000000..4ef565d --- /dev/null +++ b/tests/test_app_mainmenu.py @@ -0,0 +1,25 @@ +from ragger.navigator import NavInsID, NavIns + +from utils import ROOT_SCREENSHOT_PATH + + +# In this test we check the behavior of the device main menu +def test_app_mainmenu(firmware, navigator, test_name): + # Navigate in the main menu + if firmware.device.startswith("nano"): + instructions = [ + NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK, + NavInsID.RIGHT_CLICK + ] + else: + instructions = [ + NavInsID.USE_CASE_HOME_SETTINGS, + NavInsID.USE_CASE_SETTINGS_NEXT, + NavIns(NavInsID.TOUCH, (200, 113)), + NavIns(NavInsID.TOUCH, (200, 113)), + NavInsID.USE_CASE_CHOICE_REJECT, + NavInsID.USE_CASE_SETTINGS_MULTI_PAGE_EXIT + ] + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, test_name, instructions, + screen_change_before_first_instruction=False) diff --git a/tests/test_appname_cmd.py b/tests/test_appname_cmd.py new file mode 100644 index 0000000..0f53315 --- /dev/null +++ b/tests/test_appname_cmd.py @@ -0,0 +1,12 @@ +from application_client.aptos_command_sender import AptosCommandSender +from application_client.aptos_response_unpacker import unpack_get_app_name_response + + +# In this test we check that the GET_APP_NAME replies the application name +def test_app_name(backend): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + # Send the GET_APP_NAME instruction to the app + response = client.get_app_name() + # Assert that we have received the correct appname + assert unpack_get_app_name_response(response.data) == "Aptos" diff --git a/tests/test_error_cmd.py b/tests/test_error_cmd.py new file mode 100644 index 0000000..5ad45a5 --- /dev/null +++ b/tests/test_error_cmd.py @@ -0,0 +1,57 @@ +import pytest + +from ragger.error import ExceptionRAPDU +from application_client.aptos_command_sender import CLA, InsType, P1, P2, Errors + + +# Ensure the app returns an error when a bad CLA is used +def test_bad_cla(backend): + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange(cla=CLA + 1, ins=InsType.GET_VERSION) + assert e.value.status == Errors.SW_CLA_NOT_SUPPORTED + + +# Ensure the app returns an error when a bad INS is used +def test_bad_ins(backend): + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange(cla=CLA, ins=0xff) + assert e.value.status == Errors.SW_INS_NOT_SUPPORTED + + +# Ensure the app returns an error when a bad P1 or P2 is used +def test_wrong_p1p2(backend): + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange(cla=CLA, ins=InsType.GET_VERSION, p1=P1.P1_START + 1, p2=P2.P2_LAST) + assert e.value.status == Errors.SW_WRONG_P1P2 + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange(cla=CLA, ins=InsType.GET_VERSION, p1=P1.P1_START, p2=P2.P2_MORE) + assert e.value.status == Errors.SW_WRONG_P1P2 + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange(cla=CLA, ins=InsType.GET_APP_NAME, p1=P1.P1_START + 1, p2=P2.P2_LAST) + assert e.value.status == Errors.SW_WRONG_P1P2 + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange(cla=CLA, ins=InsType.GET_APP_NAME, p1=P1.P1_START, p2=P2.P2_MORE) + assert e.value.status == Errors.SW_WRONG_P1P2 + + +# Ensure the app returns an error when a bad data length is used +def test_wrong_data_length(backend): + # APDUs must be at least 5 bytes: CLA, INS, P1, P2, Lc. + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange_raw(b"E0030000") + assert e.value.status == Errors.SW_WRONG_DATA_LENGTH + # APDUs advertises a too long length + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange_raw(b"E003000005") + assert e.value.status == Errors.SW_WRONG_DATA_LENGTH + + +# Ensure there is no state confusion when trying wrong APDU sequences +def test_invalid_state(backend): + with pytest.raises(ExceptionRAPDU) as e: + backend.exchange(cla=CLA, + ins=InsType.SIGN_TX, + p1=P1.P1_START + 1, # Try to continue a flow instead of start a new one + p2=P2.P2_MORE, + data=b"abcde") # data is not parsed in this case + assert e.value.status == Errors.SW_BAD_STATE diff --git a/tests/test_name_version.py b/tests/test_name_version.py new file mode 100644 index 0000000..a7d6d24 --- /dev/null +++ b/tests/test_name_version.py @@ -0,0 +1,15 @@ +from application_client.aptos_command_sender import AptosCommandSender +from application_client.aptos_response_unpacker import unpack_get_app_and_version_response + + +# Test a specific APDU asking BOLOS (and not the app) the name and version of the current app +def test_get_app_and_version(backend, backend_name): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + # Send the special instruction to BOLOS + response = client.get_app_and_version() + # Use an helper to parse the response, assert the values + app_name, version = unpack_get_app_and_version_response(response.data) + + assert app_name == "Aptos" + assert version == "0.6.9" diff --git a/tests/test_pubkey_cmd.py b/tests/test_pubkey_cmd.py new file mode 100644 index 0000000..3626a71 --- /dev/null +++ b/tests/test_pubkey_cmd.py @@ -0,0 +1,101 @@ +import pytest + +from application_client.aptos_command_sender import AptosCommandSender, Errors +from application_client.aptos_response_unpacker import unpack_get_public_key_response +from ragger.bip import calculate_public_key_and_chaincode, CurveChoice +from ragger.error import ExceptionRAPDU +from ragger.navigator import NavInsID, NavIns +from utils import ROOT_SCREENSHOT_PATH + + +# In this test we check that the GET_PUBLIC_KEY works in non-confirmation mode +def test_get_public_key_no_confirm(backend): + for path in ["m/44'/637'/1'/0'/0'", "m/44'/637'/0'/0'/1'", "m/44'/637'/255'/255'/255'", "m/44'/637'/2147483647'/0'/0'/0'/0'/0'/0'/0'"]: + client = AptosCommandSender(backend) + response = client.get_public_key(path=path).data + _, public_key, _, chain_code = unpack_get_public_key_response(response) + + ref_public_key, ref_chain_code = calculate_public_key_and_chaincode(CurveChoice.Ed25519Slip, path=path) + ref_public_key_bytes = bytearray.fromhex(ref_public_key) + ref_public_key_bytes[0] = 0x04 # Set the first byte to 0x04 to indicate that it is uncompressed + + assert public_key.hex() == ref_public_key_bytes.hex() + assert chain_code.hex() == ref_chain_code + + +# In this test we check that the GET_PUBLIC_KEY works in confirmation mode +def test_get_public_key_confirm_accepted(firmware, backend, navigator, test_name): + client = AptosCommandSender(backend) + path = "m/44'/637'/1'/0'/0'" + with client.get_public_key_with_confirmation(path=path): + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name) + else: + instructions = [ + NavInsID.USE_CASE_REVIEW_TAP, + NavIns(NavInsID.TOUCH, (200, 335)), + NavInsID.USE_CASE_ADDRESS_CONFIRMATION_EXIT_QR, + NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CONFIRM, + NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS + ] + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, + test_name, + instructions) + response = client.get_async_response().data + _, public_key, _, chain_code = unpack_get_public_key_response(response) + + ref_public_key, ref_chain_code = calculate_public_key_and_chaincode(CurveChoice.Ed25519Slip, path=path) + ref_public_key_bytes = bytearray.fromhex(ref_public_key) + ref_public_key_bytes[0] = 0x04 # Set the first byte to 0x04 to indicate that it is uncompressed + print(f"MUTABLE DATA: {ref_public_key_bytes.hex()}") + + print(f"Public key: {public_key.hex()}") + print(f"Ref public key: {ref_public_key}") + print(f"Chain code: {chain_code.hex()}") + print(f"Ref chain code: {ref_chain_code}") + assert public_key.hex() == ref_public_key_bytes.hex() + assert chain_code.hex() == ref_chain_code + + +# In this test we check that the GET_PUBLIC_KEY in confirmation mode replies an error if the user refuses +def test_get_public_key_confirm_refused(firmware, backend, navigator, test_name): + client = AptosCommandSender(backend) + path = "m/44'/637'/1'/0'/0'" + + if firmware.device.startswith("nano"): + with pytest.raises(ExceptionRAPDU) as e: + with client.get_public_key_with_confirmation(path=path): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Reject", + ROOT_SCREENSHOT_PATH, + test_name) + # Assert that we have received a refusal + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 + else: + instructions_set = [ + [ + NavInsID.USE_CASE_REVIEW_REJECT, + NavInsID.USE_CASE_STATUS_DISMISS + ], + [ + NavInsID.USE_CASE_REVIEW_TAP, + NavInsID.USE_CASE_ADDRESS_CONFIRMATION_CANCEL, + NavInsID.USE_CASE_STATUS_DISMISS + ] + ] + for i, instructions in enumerate(instructions_set): + with pytest.raises(ExceptionRAPDU) as e: + with client.get_public_key_with_confirmation(path=path): + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, + test_name + f"/part{i}", + instructions) + # Assert that we have received a refusal + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 diff --git a/tests/test_sign_cmd.py b/tests/test_sign_cmd.py new file mode 100644 index 0000000..7c9b663 --- /dev/null +++ b/tests/test_sign_cmd.py @@ -0,0 +1,282 @@ +import pytest + +from application_client.aptos_command_sender import AptosCommandSender, Errors +from application_client.aptos_response_unpacker import unpack_get_public_key_response, unpack_sign_tx_response +from ragger.error import ExceptionRAPDU +from ragger.navigator import NavInsID, NavIns +from utils import ROOT_SCREENSHOT_PATH, check_signature_validity + +# In this tests we check the behavior of the device when asked to sign a transaction + +# This fixture is used to disable the blind signing after a test that enabled it +@pytest.fixture +def disable_blind_signing(firmware, backend, navigator): + yield + + if firmware.device.startswith("nano"): + backend.right_click() + backend.both_click() + backend.right_click() + backend.both_click() + backend.left_click() + backend.both_click() + backend.right_click() + backend.both_click() + else: + instructions = [ + NavInsID.USE_CASE_HOME_SETTINGS, + NavInsID.USE_CASE_SETTINGS_NEXT, + NavIns(NavInsID.TOUCH, (200, 113)), + NavInsID.USE_CASE_CHOICE_REJECT, + NavInsID.USE_CASE_SETTINGS_MULTI_PAGE_EXIT + ] + navigator.navigate(instructions, screen_change_before_first_instruction=False) + + +# In this test we send to the device a transaction to sign and validate it on screen +# The transaction is short and will be sent in one chunk +# We will ensure that the displayed information is correct by using screenshots comparison +def test_sign_tx_short_tx(firmware, backend, navigator, test_name): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + # The path used for this entire test + path: str = "m/44'/637'/1'/0'/0'" + + # First we need to get the public key of the device in order to build the transaction + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + # Create the transaction that will be sent to the device for signing + transaction = bytes.fromhex("b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee000000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde082a00000000000000204e0000000000006400000000000000565c51630000000022") + + # Send the sign device instruction. + # As it requires on-screen validation, the function is asynchronous. + # It will yield the result when the navigation is done + with client.sign_tx(path=path, transaction=transaction): + # Validate the on-screen request by performing the navigation appropriate for this device + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name) + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name) + + # The device as yielded the result, parse it and ensure that the signature is correct + response = client.get_async_response().data + _, sig, _ = unpack_sign_tx_response(response) + assert check_signature_validity(public_key, sig, transaction) + + +# In this test we send to the device a transaction to sign and validate it on screen +# The transaction will be sent in multiple chunks +# Also, this transaction has a request for blind signing activation +def test_blind_sign_tx_long_tx(firmware, backend, navigator, test_name, disable_blind_signing): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + path: str = "m/44'/637'/1'/0'/0'" + + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + transaction = bytes.fromhex("b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde1b0000000000000002190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e120a736372697074735f76320473776170030700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e7304555344540007190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12066375727665730c556e636f7272656c6174656400020800e1f5050000000008decbb30000000000480000000000000064000000000000008a9ba4640000000002") + + with client.sign_tx(path=path, transaction=transaction): + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Allow", + ROOT_SCREENSHOT_PATH, + test_name + "/part0", + screen_change_after_last_instruction=False) + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name + "/part1") + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_CHOICE_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Enable blind signing", + ROOT_SCREENSHOT_PATH, + test_name + "/part0", + screen_change_after_last_instruction=False) + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name + "/part1") + response = client.get_async_response().data + _, sig, _ = unpack_sign_tx_response(response) + assert check_signature_validity(public_key, sig, transaction) + + +# Transaction signature refused test +# The test will ask for a transaction signature that will be refused on screen +def test_sign_tx_refused(firmware, backend, navigator, test_name): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + path: str = "m/44'/637'/1'/0'/0'" + + transaction = bytes.fromhex("b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde1b000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e736665720002203835075df1bf469c336eabed8ac87052ee4485f3ec93380a5382fbf76b7a33070840420f000000000006000000000000006400000000000000c39aa4640000000002") + + if firmware.device.startswith("nano"): + with pytest.raises(ExceptionRAPDU) as e: + with client.sign_tx(path=path, transaction=transaction): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Reject", + ROOT_SCREENSHOT_PATH, + test_name) + + # Assert that we have received a refusal + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 + else: + for i in range(4): + instructions = [NavInsID.USE_CASE_REVIEW_TAP] * i + instructions += [NavInsID.USE_CASE_REVIEW_REJECT, + NavInsID.USE_CASE_CHOICE_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS] + with pytest.raises(ExceptionRAPDU) as e: + with client.sign_tx(path=path, transaction=transaction): + navigator.navigate_and_compare(ROOT_SCREENSHOT_PATH, + test_name + f"/part{i}", + instructions) + # Assert that we have received a refusal + assert e.value.status == Errors.SW_DENY + assert len(e.value.data) == 0 + +# In this test we send to the device a message to sign and validate it on screen +# We will ensure that the displayed information is correct by using screenshots comparison +def test_sign_tx_short_msg(firmware, backend, navigator, test_name): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + # The path used for this entire test + path: str = "m/44'/637'/1'/0'/0'" + + # First we need to get the public key of the device in order to build the transaction + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + # Create the mes that will be sent to the device for signing + message = bytes("Hello Ledger!", 'utf-8') + + # Send the sign device instruction. + # As it requires on-screen validation, the function is asynchronous. + # It will yield the result when the navigation is done + with client.sign_tx(path=path, transaction=message): + # Validate the on-screen request by performing the navigation appropriate for this device + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name) + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name) + + # The device as yielded the result, parse it and ensure that the signature is correct + response = client.get_async_response().data + _, sig, _ = unpack_sign_tx_response(response) + assert check_signature_validity(public_key, sig, message) + +# In this test we send to the device a message to sign and validate it on screen +# We will ensure that the displayed information is correct by using screenshots comparison +def test_sign_short_raw_msg(firmware, backend, navigator, test_name): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + # The path used for this entire test + path: str = "m/44'/637'/1'/0'/0'" + + # First we need to get the public key of the device in order to build the transaction + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + # Create the mes that will be sent to the device for signing + message = bytes.fromhex("01020304ff") + + # Send the sign device instruction. + # As it requires on-screen validation, the function is asynchronous. + # It will yield the result when the navigation is done + with client.sign_tx(path=path, transaction=message): + # Validate the on-screen request by performing the navigation appropriate for this device + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name) + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name) + + # The device as yielded the result, parse it and ensure that the signature is correct + response = client.get_async_response().data + _, sig, _ = unpack_sign_tx_response(response) + assert check_signature_validity(public_key, sig, message) + +# In this test we send to the device a message to sign and validate it on screen +# We will ensure that the displayed information is correct by using screenshots comparison +def test_sign_long_raw_msg(firmware, backend, navigator, test_name, disable_blind_signing): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + # The path used for this entire test + path: str = "m/44'/637'/1'/0'/0'" + + # First we need to get the public key of the device in order to build the transaction + rapdu = client.get_public_key(path=path) + _, public_key, _, _ = unpack_get_public_key_response(rapdu.data) + + # Create the mes that will be sent to the device for signing + message = bytes.fromhex("bc6f6693bddc1a9fec9e674a461eaa00b193094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde1b0000000000000002190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e120a736372697074735f76320473776170030700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e7304555344540007190d44266241744264b964a37b8f09863167a12d3e70cda39376cfb4e3561e12066375727665730c556e636f7272656c6174656400020800e1f5050000000008decbb30000000000480000000000000064000000000000008a9ba4640000000002") + + with client.sign_tx(path=path, transaction=message): + if firmware.device.startswith("nano"): + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Allow", + ROOT_SCREENSHOT_PATH, + test_name + "/part0", + screen_change_after_last_instruction=False) + navigator.navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, + [NavInsID.BOTH_CLICK], + "Approve", + ROOT_SCREENSHOT_PATH, + test_name + "/part1") + else: + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_CHOICE_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Enable blind signing", + ROOT_SCREENSHOT_PATH, + test_name + "/part0", + screen_change_after_last_instruction=False) + navigator.navigate_until_text_and_compare(NavInsID.USE_CASE_REVIEW_TAP, + [NavInsID.USE_CASE_REVIEW_CONFIRM, + NavInsID.USE_CASE_STATUS_DISMISS], + "Hold to sign", + ROOT_SCREENSHOT_PATH, + test_name + "/part1") + + # The device as yielded the result, parse it and ensure that the signature is correct + response = client.get_async_response().data + _, sig, _ = unpack_sign_tx_response(response) + assert check_signature_validity(public_key, sig, message) diff --git a/tests/test_version_cmd.py b/tests/test_version_cmd.py new file mode 100644 index 0000000..535d3d1 --- /dev/null +++ b/tests/test_version_cmd.py @@ -0,0 +1,16 @@ +from application_client.aptos_command_sender import AptosCommandSender +from application_client.aptos_response_unpacker import unpack_get_version_response + +# Taken from the Makefile, to update every time the Makefile version is bumped +MAJOR = 0 +MINOR = 6 +PATCH = 9 + +# In this test we check the behavior of the device when asked to provide the app version +def test_version(backend): + # Use the app interface instead of raw interface + client = AptosCommandSender(backend) + # Send the GET_VERSION instruction + rapdu = client.get_version() + # Use an helper to parse the response, assert the values + assert unpack_get_version_response(rapdu.data) == (MAJOR, MINOR, PATCH) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..b75a6ba --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,16 @@ +from pathlib import Path +from hashlib import sha256 + +from nacl.signing import VerifyKey +from nacl.exceptions import BadSignatureError + + +ROOT_SCREENSHOT_PATH = Path(__file__).parent.resolve() + + +# Check if a signature of a given message is valid +def check_signature_validity(public_key: bytes, signature: bytes, message: bytes) -> bool: + pk = VerifyKey(public_key[1:]) + + return pk.verify(signature=signature, smessage=message) + diff --git a/unit-tests/CMakeLists.txt b/unit-tests/CMakeLists.txt index 191d004..6148dfe 100644 --- a/unit-tests/CMakeLists.txt +++ b/unit-tests/CMakeLists.txt @@ -36,36 +36,25 @@ endif() add_compile_definitions(TEST) include_directories(../src) +include_directories($ENV{BOLOS_SDK}/lib_standard_app) add_executable(test_bcs test_bcs.c) -add_executable(test_base58 test_base58.c) -add_executable(test_bip32 test_bip32.c) -add_executable(test_buffer test_buffer.c) -add_executable(test_format test_format.c) -add_executable(test_write test_write.c) -add_executable(test_apdu_parser test_apdu_parser.c) add_executable(test_tx_parser test_tx_parser.c) add_executable(test_tx_utils test_tx_utils.c) +add_library(base58 SHARED $ENV{BOLOS_SDK}/lib_standard_app/base58.c) +add_library(bip32 SHARED $ENV{BOLOS_SDK}/lib_standard_app/bip32.c) +add_library(buffer SHARED $ENV{BOLOS_SDK}/lib_standard_app/buffer.c) +add_library(read SHARED $ENV{BOLOS_SDK}/lib_standard_app/read.c) +add_library(write SHARED $ENV{BOLOS_SDK}/lib_standard_app/write.c) +add_library(format SHARED $ENV{BOLOS_SDK}/lib_standard_app/format.c) +add_library(varint SHARED $ENV{BOLOS_SDK}/lib_standard_app/varint.c) +add_library(apdu_parser SHARED $ENV{BOLOS_SDK}/lib_standard_app/parser.c) add_library(bcs SHARED ../src/bcs/init.c ../src/bcs/decoder.c ../src/bcs/utf8.c) -add_library(base58 SHARED ../src/common/base58.c) -add_library(bip32 SHARED ../src/common/bip32.c) -add_library(buffer SHARED ../src/common/buffer.c) -add_library(read SHARED ../src/common/read.c) -add_library(write SHARED ../src/common/write.c) -add_library(format SHARED ../src/common/format.c) -add_library(varint SHARED ../src/common/varint.c) -add_library(apdu_parser SHARED ../src/apdu/parser.c) add_library(transaction_deserialize ../src/transaction/deserialize.c) add_library(transaction_utils ../src/transaction/utils.c) target_link_libraries(test_bcs PUBLIC cmocka gcov bcs buffer bip32 varint write read) -target_link_libraries(test_base58 PUBLIC cmocka gcov base58) -target_link_libraries(test_bip32 PUBLIC cmocka gcov bip32 read) -target_link_libraries(test_buffer PUBLIC cmocka gcov buffer bip32 varint write read) -target_link_libraries(test_format PUBLIC cmocka gcov format) -target_link_libraries(test_write PUBLIC cmocka gcov write) -target_link_libraries(test_apdu_parser PUBLIC cmocka gcov apdu_parser) target_link_libraries(test_tx_parser PUBLIC transaction_deserialize bcs @@ -83,11 +72,5 @@ target_link_libraries(test_tx_utils PUBLIC transaction_utils) add_test(test_bcs test_bcs) -add_test(test_base58 test_base58) -add_test(test_bip32 test_bip32) -add_test(test_buffer test_buffer) -add_test(test_format test_format) -add_test(test_write test_write) -add_test(test_apdu_parser test_apdu_parser) add_test(test_tx_parser test_tx_parser) add_test(test_tx_utils test_tx_utils) diff --git a/unit-tests/test_apdu_parser.c b/unit-tests/test_apdu_parser.c deleted file mode 100644 index 99ee07d..0000000 --- a/unit-tests/test_apdu_parser.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "types.h" -#include "apdu/parser.h" - -static void test_apdu_parser(void **state) { - (void) state; - uint8_t apdu_bad_min_len[] = {0xE0, 0x03, 0x00, 0x00}; // less than 5 bytes - uint8_t apdu_bad_lc[] = {0xE0, 0x03, 0x00, 0x00, 0x01}; // Lc = 1 but no data - uint8_t apdu[] = {0xE0, 0x03, 0x01, 0x02, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04}; - - command_t cmd; - - memset(&cmd, 0, sizeof(cmd)); - assert_false(apdu_parser(&cmd, apdu_bad_min_len, sizeof(apdu_bad_min_len))); - - memset(&cmd, 0, sizeof(cmd)); - assert_false(apdu_parser(&cmd, apdu_bad_lc, sizeof(apdu_bad_min_len))); - - memset(&cmd, 0, sizeof(cmd)); - assert_true(apdu_parser(&cmd, apdu, sizeof(apdu))); - assert_int_equal(cmd.cla, 0xE0); - assert_int_equal(cmd.ins, 0x03); - assert_int_equal(cmd.p1, 0x01); - assert_int_equal(cmd.p2, 0x02); - assert_int_equal(cmd.lc, 5); - assert_memory_equal(cmd.data, ((uint8_t[]){0x00, 0x01, 0x02, 0x03, 0x04}), cmd.lc); -} - -int main() { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_apdu_parser)}; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/unit-tests/test_base58.c b/unit-tests/test_base58.c deleted file mode 100644 index f7f5e45..0000000 --- a/unit-tests/test_base58.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "common/base58.h" - -static void test_base58(void **state) { - (void) state; - - const char in[] = "USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z"; - const char expected_out[] = "The quick brown fox jumps over the lazy dog."; - uint8_t out[100] = {0}; - int out_len = base58_decode(in, sizeof(in) - 1, out, sizeof(out)); - assert_int_equal(out_len, strlen(expected_out)); - assert_string_equal((char *) out, expected_out); - - const char in2[] = "The quick brown fox jumps over the lazy dog."; - const char expected_out2[] = "USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z"; - char out2[100] = {0}; - int out_len2 = base58_encode((uint8_t *) in2, sizeof(in2) - 1, out2, sizeof(out2)); - assert_int_equal(out_len2, strlen(expected_out2)); - assert_string_equal((char *) out2, expected_out2); -} - -int main() { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_base58)}; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/unit-tests/test_bip32.c b/unit-tests/test_bip32.c deleted file mode 100644 index f71383b..0000000 --- a/unit-tests/test_bip32.c +++ /dev/null @@ -1,101 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include "common/bip32.h" - -static void test_bip32_format(void **state) { - (void) state; - - char output[30]; - bool b = false; - - b = bip32_path_format((const uint32_t[5]){0x8000002C, 0x80000000, 0x80000000, 0, 0}, - 5, - output, - sizeof(output)); - assert_true(b); - assert_string_equal(output, "44'/0'/0'/0/0"); - - b = bip32_path_format((const uint32_t[5]){0x8000002C, 0x80000001, 0x80000000, 0, 0}, - 5, - output, - sizeof(output)); - assert_true(b); - assert_string_equal(output, "44'/1'/0'/0/0"); -} - -static void test_bad_bip32_format(void **state) { - (void) state; - - char output[30]; - bool b = true; - - // More than MAX_BIP32_PATH (=10) - b = bip32_path_format( - (const uint32_t[11]){0x8000002C, 0x80000000, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0}, - 11, - output, - sizeof(output)); - assert_false(b); - - // No BIP32 path (=0) - b = bip32_path_format(NULL, 0, output, sizeof(output)); - assert_false(b); -} - -static void test_bip32_read(void **state) { - (void) state; - - // clang-format off - uint8_t input[20] = { - 0x80, 0x00, 0x00, 0x2C, - 0x80, 0x00, 0x00, 0x01, - 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - uint32_t expected[5] = {0x8000002C, 0x80000001, 0x80000000, 0, 0}; - uint32_t output[5] = {0}; - bool b = false; - - b = bip32_path_read(input, sizeof(input), output, 5); - assert_true(b); - assert_memory_equal(output, expected, 5); -} - -static void test_bad_bip32_read(void **state) { - (void) state; - - // clang-format off - uint8_t input[20] = { - 0x80, 0x00, 0x00, 0x2C, - 0x80, 0x00, 0x00, 0x01, - 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - uint32_t output[10] = {0}; - - // buffer too small (5 BIP32 paths instead of 10) - assert_false(bip32_path_read(input, sizeof(input), output, 10)); - - // No BIP32 path - assert_false(bip32_path_read(input, sizeof(input), output, 0)); - - // More than MAX_BIP32_PATH (=10) - assert_false(bip32_path_read(input, sizeof(input), output, 20)); -} - -int main() { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_bip32_format), - cmocka_unit_test(test_bad_bip32_format), - cmocka_unit_test(test_bip32_read), - cmocka_unit_test(test_bad_bip32_read)}; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/unit-tests/test_buffer.c b/unit-tests/test_buffer.c deleted file mode 100644 index e2d1efa..0000000 --- a/unit-tests/test_buffer.c +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include "common/buffer.h" - -static void test_buffer_can_read(void **state) { - (void) state; - - uint8_t temp[20] = {0}; - buffer_t buf = {.ptr = temp, .size = sizeof(temp), .offset = 0}; - - assert_true(buffer_can_read(&buf, 20)); - - assert_true(buffer_seek_cur(&buf, 20)); - assert_false(buffer_can_read(&buf, 1)); -} - -static void test_buffer_seek(void **state) { - (void) state; - - uint8_t temp[20] = {0}; - buffer_t buf = {.ptr = temp, .size = sizeof(temp), .offset = 0}; - - assert_true(buffer_can_read(&buf, 20)); - - assert_true(buffer_seek_cur(&buf, 20)); // seek at offset 20 - assert_false(buffer_can_read(&buf, 1)); // can't read 1 byte - assert_false(buffer_seek_cur(&buf, 1)); // can't move at offset 21 - - assert_true(buffer_seek_end(&buf, 19)); - assert_int_equal(buf.offset, 1); - assert_false(buffer_seek_end(&buf, 21)); // can't seek at offset -1 - - assert_true(buffer_seek_set(&buf, 10)); - assert_int_equal(buf.offset, 10); - assert_false(buffer_seek_set(&buf, 21)); // can't seek at offset 21 -} - -static void test_buffer_read(void **state) { - (void) state; - - // clang-format off - uint8_t temp[15] = { - 0xFF, - 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E - }; - buffer_t buf = {.ptr = temp, .size = sizeof(temp), .offset = 0}; - - uint8_t first = 0; - assert_true(buffer_read_u8(&buf, &first)); - assert_int_equal(first, 255); // 0xFF - assert_true(buffer_seek_end(&buf, 0)); // seek at offset 19 - assert_false(buffer_read_u8(&buf, &first)); // can't read 1 byte - - uint16_t second = 0; - assert_true(buffer_seek_set(&buf, 1)); // set back to offset 1 - assert_true(buffer_read_u16(&buf, &second, BE)); // big endian - assert_int_equal(second, 258); // 0x01 0x02 - assert_true(buffer_seek_set(&buf, 1)); // set back to offset 1 - assert_true(buffer_read_u16(&buf, &second, LE)); // little endian - assert_int_equal(second, 513); // 0x02 0x01 - assert_true(buffer_seek_set(&buf, 14)); // seek at offset 14 - assert_false(buffer_read_u16(&buf, &second, BE)); // can't read 2 bytes - - uint32_t third = 0; - assert_true(buffer_seek_set(&buf, 3)); // set back to offset 3 - assert_true(buffer_read_u32(&buf, &third, BE)); // big endian - assert_int_equal(third, 50595078); // 0x03 0x04 0x05 0x06 - assert_true(buffer_seek_set(&buf, 3)); // set back to offset 3 - assert_true(buffer_read_u32(&buf, &third, LE)); // little endian - assert_int_equal(third, 100992003); // 0x06 0x05 0x04 0x03 - assert_true(buffer_seek_set(&buf, 12)); // seek at offset 12 - assert_false(buffer_read_u32(&buf, &third, BE)); // can't read 4 bytes - - uint64_t fourth = 0; - assert_true(buffer_seek_set(&buf, 7)); // set back to offset 7 - assert_true(buffer_read_u64(&buf, &fourth, BE)); // big endian - assert_int_equal(fourth, 506664896818842894); // 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E - assert_true(buffer_seek_set(&buf, 7)); // set back to offset 7 - assert_true(buffer_read_u64(&buf, &fourth, LE)); // little endian - assert_int_equal(fourth, 1012478732780767239); // 0x0E 0x0D 0x0C 0x0B 0x0A 0x09 0x08 0x07 - assert_true(buffer_seek_set(&buf, 8)); // seek at offset 8 - assert_false(buffer_read_u64(&buf, &fourth, BE)); // can't read 8 bytes - - // clang-format off - uint8_t temp_varint[] = { - 0xFC, // 1 byte varint - 0xFD, 0x00, 0x01, // 2 bytes varint - 0xFE, 0x00, 0x01, 0x02, 0x03, // 4 bytes varint - 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 // 8 bytes varint - - }; - buffer_t buf_varint = {.ptr = temp_varint, .size = sizeof(temp_varint), .offset = 0}; - uint64_t varint = 0; - assert_true(buffer_read_varint(&buf_varint, &varint)); - assert_int_equal(varint, 0xFC); - assert_true(buffer_read_varint(&buf_varint, &varint)); - assert_int_equal(varint, 0x0100); - assert_true(buffer_read_varint(&buf_varint, &varint)); - assert_int_equal(varint, 0x03020100); - assert_true(buffer_read_varint(&buf_varint, &varint)); - assert_int_equal(varint, 0x0706050403020100); - assert_false(buffer_read_varint(&buf_varint, &varint)); -} - -static void test_buffer_copy(void **state) { - (void) state; - - uint8_t output[5] = {0}; - uint8_t temp[5] = {0x01, 0x02, 0x03, 0x04, 0x05}; - buffer_t buf = {.ptr = temp, .size = sizeof(temp), .offset = 0}; - - assert_true(buffer_copy(&buf, output, sizeof(output))); - assert_memory_equal(output, temp, sizeof(output)); - - uint8_t output2[3] = {0}; - assert_true(buffer_seek_set(&buf, 2)); - assert_true(buffer_copy(&buf, output2, sizeof(output2))); - assert_memory_equal(output2, ((uint8_t[3]){0x03, 0x04, 0x05}), 3); - assert_true(buffer_seek_set(&buf, 0)); // seek at offset 0 - assert_false(buffer_copy(&buf, output2, sizeof(output2))); // can't read 5 bytes -} - -static void test_buffer_move(void **state) { - (void) state; - - uint8_t output[5] = {0}; - uint8_t temp[5] = {0x01, 0x02, 0x03, 0x04, 0x05}; - buffer_t buf = {.ptr = temp, .size = sizeof(temp), .offset = 0}; - - assert_true(buffer_move(&buf, output, sizeof(output))); - assert_memory_equal(output, temp, sizeof(output)); - assert_int_equal(buf.offset, sizeof(output)); - - uint8_t output2[3] = {0}; - assert_true(buffer_seek_set(&buf, 0)); // seek at offset 0 - assert_false(buffer_move(&buf, output2, sizeof(output2))); // can't read 5 bytes -} - -int main() { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_buffer_can_read), - cmocka_unit_test(test_buffer_seek), - cmocka_unit_test(test_buffer_read), - cmocka_unit_test(test_buffer_copy), - cmocka_unit_test(test_buffer_move)}; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/unit-tests/test_format.c b/unit-tests/test_format.c deleted file mode 100644 index 7423e18..0000000 --- a/unit-tests/test_format.c +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "common/format.h" - -static void test_format_i64(void **state) { - (void) state; - - char temp[22] = {0}; - - int64_t value = 0; - assert_true(format_i64(temp, sizeof(temp), value)); - assert_string_equal(temp, "0"); - - value = (int64_t) 9223372036854775807ull; // MAX_INT64 - memset(temp, 0, sizeof(temp)); - assert_true(format_i64(temp, sizeof(temp), value)); - assert_string_equal(temp, "9223372036854775807"); - - // buffer too small - assert_false(format_i64(temp, sizeof(temp) - 5, value)); - - value = (int64_t) -9223372036854775808ull; // MIN_INT64 - memset(temp, 0, sizeof(temp)); - assert_true(format_i64(temp, sizeof(temp), value)); - assert_string_equal(temp, "-9223372036854775808"); -} - -static void test_format_u64(void **state) { - (void) state; - - char temp[21] = {0}; - - uint64_t value = 0; - assert_true(format_u64(temp, sizeof(temp), value)); - assert_string_equal(temp, "0"); - - value = (uint64_t) 18446744073709551615ull; // MAX_UNT64 - memset(temp, 0, sizeof(temp)); - assert_true(format_u64(temp, sizeof(temp), value)); - assert_string_equal(temp, "18446744073709551615"); - - // buffer too small - assert_false(format_u64(temp, sizeof(temp) - 5, value)); -} - -static void test_format_fpu64(void **state) { - (void) state; - - char temp[22] = {0}; - - uint64_t amount = 100000000ull; // satoshi - memset(temp, 0, sizeof(temp)); - assert_true(format_fpu64(temp, sizeof(temp), amount, 8)); - assert_string_equal(temp, "1.00000000"); // BTC - - amount = 24964823ull; // satoshi - memset(temp, 0, sizeof(temp)); - assert_true(format_fpu64(temp, sizeof(temp), amount, 8)); - assert_string_equal(temp, "0.24964823"); // BTC - - amount = 100ull; // satoshi - memset(temp, 0, sizeof(temp)); - assert_true(format_fpu64(temp, sizeof(temp), amount, 8)); - assert_string_equal(temp, "0.00000100"); // BTC - // buffer too small - assert_false(format_fpu64(temp, sizeof(temp) - 16, amount, 8)); - - char temp2[50] = {0}; - - amount = 1000000000000000000ull; // wei - assert_true(format_fpu64(temp2, sizeof(temp2), amount, 18)); - assert_string_equal(temp2, "1.000000000000000000"); // ETH - - // buffer too small - assert_false(format_fpu64(temp2, sizeof(temp2) - 20, amount, 18)); -} - -static void test_format_hex(void **state) { - (void) state; - - uint8_t address[] = {0xde, 0xb, 0x29, 0x56, 0x69, 0xa9, 0xfd, 0x93, 0xd5, 0xf2, - 0x8d, 0x9e, 0xc8, 0x5e, 0x40, 0xf4, 0xcb, 0x69, 0x7b, 0xae}; - char output[2 * sizeof(address) + 1] = {0}; - - assert_int_equal(2 * sizeof(address) + 1, - format_hex(address, sizeof(address), output, sizeof(output))); - assert_string_equal(output, "DE0B295669A9FD93D5F28D9EC85E40F4CB697BAE"); - assert_int_equal(-1, format_hex(address, sizeof(address), output, sizeof(address))); -} - -int main() { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_format_i64), - cmocka_unit_test(test_format_u64), - cmocka_unit_test(test_format_fpu64), - cmocka_unit_test(test_format_hex)}; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/unit-tests/test_write.c b/unit-tests/test_write.c deleted file mode 100644 index 08f37f7..0000000 --- a/unit-tests/test_write.c +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "common/write.h" - -static void test_write(void **state) { - (void) state; - - uint8_t tmp2[2] = {0}; - - uint8_t expected2[2] = {0x01, 0x07}; - write_u16_be(tmp2, 0, (uint16_t) 263U); - assert_memory_equal(tmp2, expected2, sizeof(expected2)); - - memset(tmp2, 0, sizeof(tmp2)); - expected2[0] = 0x07; - expected2[1] = 0x01; - write_u16_le(tmp2, 0, (uint16_t) 263U); - assert_memory_equal(tmp2, expected2, sizeof(expected2)); - - uint8_t tmp4[4] = {0}; - - uint8_t expected4[4] = {0x01, 0x3B, 0xAC, 0xC7}; - write_u32_be(tmp4, 0, (uint32_t) 20688071UL); - assert_memory_equal(tmp4, expected4, sizeof(expected4)); - - memset(tmp4, 0, sizeof(tmp4)); - expected4[0] = 0xC7; - expected4[1] = 0xAC; - expected4[2] = 0x3B; - expected4[3] = 0x01; - write_u32_le(tmp4, 0, (uint32_t) 20688071UL); - assert_memory_equal(tmp4, expected4, sizeof(expected4)); - - uint8_t tmp8[8] = {0}; - - uint8_t expected8[8] = {0xEB, 0x68, 0x44, 0xC0, 0x2C, 0x61, 0xB0, 0x99}; - write_u64_be(tmp8, 0, (uint64_t) 16962883588659982489ULL); - assert_memory_equal(tmp8, expected8, sizeof(expected8)); - - memset(tmp8, 0, sizeof(tmp8)); - expected8[0] = 0x99; - expected8[1] = 0xB0; - expected8[2] = 0x61; - expected8[3] = 0x2C; - expected8[4] = 0xC0; - expected8[5] = 0x44; - expected8[6] = 0x68; - expected8[7] = 0xEB; - write_u64_le(tmp8, 0, (uint64_t) 16962883588659982489ULL); - assert_memory_equal(tmp8, expected8, sizeof(expected8)); -} - -int main() { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_write)}; - - return cmocka_run_group_tests(tests, NULL, NULL); -}