diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 982f4cf..0549b3c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -17,7 +17,7 @@ jobs: if: github.event.repository.private == false strategy: matrix: - sdk: ["$NANOS_SDK", "$NANOX_SDK", "$NANOSP_SDK", "$STAX_SDK", "$FLEX_SDK"] + sdk: ["$NANOX_SDK", "$NANOSP_SDK", "$STAX_SDK", "$FLEX_SDK"] container: image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 22231d3..fc5462c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,7 +24,18 @@ jobs: with: upload_app_binaries_artifact: "compiled_app_binaries" - + cpp_tests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + - name: Install deps + run: | + sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10 + make deps + - run: make cpp_test build: runs-on: ${{ github.repository_owner == 'zondax' && 'zondax-runners' || 'ubuntu-latest' }} @@ -54,7 +65,6 @@ jobs: make deps - name: Run CMake run: mkdir -p build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make - - run: make cpp_test build_ledger: needs: configure diff --git a/.gitmodules b/.gitmodules index 1f41577..cbb2133 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "deps/ledger-secure-sdk"] path = deps/ledger-secure-sdk url = https://github.com/LedgerHQ/ledger-secure-sdk.git +[submodule "js"] + path = js + url = https://github.com/Zondax/ledger-sovereign-js.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 59f19cc..7888452 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ #******************************************************************************* -#* (c) 2018 -2024 Zondax AG +#* (c) 2018 - 2025 Zondax AG #* #* Licensed under the Apache License, Version 2.0 (the "License"); #* you may not use this file except in compliance with the License. @@ -138,6 +138,15 @@ file(GLOB_RECURSE LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl.c ${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto_helper.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/schema_reader.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/borsh.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/schema_proof.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/schema_helper.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/schema_txn_parser.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/ui_item_manager.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/ui_item_buffer.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/stack_manager.c + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/render.c ) add_library(app_lib STATIC ${LIB_SRC}) @@ -147,6 +156,8 @@ target_include_directories(app_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/app/src ${CMAKE_CURRENT_SOURCE_DIR}/app/src/lib ${CMAKE_CURRENT_SOURCE_DIR}/app/src/common + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/txdefs + ${CMAKE_CURRENT_SOURCE_DIR}/app/src/modules ${CMAKE_CURRENT_SOURCE_DIR}/deps/picohash ) diff --git a/LICENSE b/LICENSE index f490813..ec89d82 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2024 Zondax AG + Copyright 2018-2025 Zondax AG Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index 0dcaf35..28d3ec2 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ #******************************************************************************* -#* (c) 2018 -2024 Zondax AG +#* (c) 2018 - 2025 Zondax AG #* #* Licensed under the Apache License, Version 2.0 (the "License"); #* you may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ # BOLOS_SDK IS DEFINED We use the plain Makefile for Ledger # BOLOS_SDK NOT DEFINED We use a containerized build approach -TESTS_JS_PACKAGE = "@zondax/ledger-sovereign" -TESTS_JS_DIR = $(CURDIR)/../ledger-sovereign-js +# TESTS_JS_PACKAGE = "@zondax/ledger-sovereign" +# TESTS_JS_DIR = $(CURDIR)/../ledger-sovereign-js ifeq ($(BOLOS_SDK),) # In this case, there is not predefined SDK and we run dockerized diff --git a/app/LICENSE b/app/LICENSE index bfd6018..43e5b34 100644 --- a/app/LICENSE +++ b/app/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2023 Zondax AG + Copyright 2018- 2025 Zondax AG Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/app/Makefile b/app/Makefile index dd5bccb..32d1a94 100755 --- a/app/Makefile +++ b/app/Makefile @@ -1,6 +1,6 @@ #******************************************************************************* # Ledger App -# (c) 2018 - 2024 Zondax AG +# (c) 2018 - 2025 Zondax AG # (c) 2017 Ledger # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -76,6 +76,7 @@ VARIANT_PARAM=COIN VARIANT_VALUES=$(COIN) INCLUDES_PATH += $(CURDIR)/src/common +INCLUDES_PATH += $(BOLOS_SDK)/lib_cxng/src ######################################## # Application communication interfaces # diff --git a/app/Makefile.version b/app/Makefile.version index 3881cb0..9a374df 100644 --- a/app/Makefile.version +++ b/app/Makefile.version @@ -3,4 +3,4 @@ APPVERSION_M=0 # This is the minor version APPVERSION_N=0 # This is the patch version -APPVERSION_P=1 +APPVERSION_P=2 diff --git a/app/rust/.gitignore b/app/rust/.gitignore deleted file mode 100644 index 2f7896d..0000000 --- a/app/rust/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target/ diff --git a/app/rust/Cargo.lock b/app/rust/Cargo.lock deleted file mode 100644 index b3d425b..0000000 --- a/app/rust/Cargo.lock +++ /dev/null @@ -1,590 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d70693199b3cf4552f3fa720b54163927a3ebed2aef240efaf556033ab336a11" -dependencies = [ - "hex-literal-impl", - "proc-macro-hack", -] - -[[package]] -name = "hex-literal-impl" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59448fc2f82a5fb6907f78c3d69d843e82ff5b051923313cc4438cb0c7b745a8" -dependencies = [ - "proc-macro-hack", -] - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "merlin" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" -dependencies = [ - "byteorder", - "keccak", - "rand_core", - "zeroize", -] - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "panic-halt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "regex" -version = "1.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - -[[package]] -name = "rslib" -version = "0.1.0" -dependencies = [ - "curve25519-dalek 3.2.1", - "env_logger", - "getrandom", - "hex", - "hex-literal", - "log", - "merlin", - "panic-halt", - "rand", - "schnorrkel", - "zeroize", -] - -[[package]] -name = "schnorrkel" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" -dependencies = [ - "arrayref", - "arrayvec", - "curve25519-dalek 2.1.3", - "merlin", - "rand_core", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer", - "digest 0.8.1", - "fake-simd", - "opaque-debug", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "2.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" - -[[package]] -name = "zeroize" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/app/rust/Cargo.toml b/app/rust/Cargo.toml deleted file mode 100644 index 08fcd9f..0000000 --- a/app/rust/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -authors = ["Zondax AG "] -name = "rslib" -version = "0.1.0" -edition = "2018" -readme = "README.md" - -[lib] -name = "rslib" -crate-type = ["staticlib"] - -[dependencies] -rand={ version = "0.7.3", default-features = false} -merlin = {version = "2.0.0", default-features=false} -zeroize = {version = "1.1.1", default-features=false} - -[target.'cfg(target_arch = "x86_64")'.dependencies] -getrandom = {version="0.1.14", default-features=false} - -[dependencies.curve25519-dalek] -version = "3.0.0" -default-features = false -features=["u32_backend"] - -[dependencies.schnorrkel] -version = "0.9.1" -default-features = false -features=["u32_backend"] - -[dev-dependencies] -hex-literal = "0.2.1" -hex = "0.4.2" -env_logger = "0.7.1" -log = "0.4.8" - -[target.thumbv6m-none-eabi.dev-dependencies] -panic-halt = "0.2.0" - -[profile.release] -lto=false -codegen-units = 1 -debug=true -opt-level = "s" - -[profile.dev] -panic = "abort" - diff --git a/app/rust/include/rslib.h b/app/rust/include/rslib.h deleted file mode 100644 index 85f6c99..0000000 --- a/app/rust/include/rslib.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -void get_sr25519_sk(uint8_t *sk_ed25519_expanded); - -void sign_sr25519_phase1(const uint8_t *sk_ed25519_expanded, const uint8_t *pk, const uint8_t *context_ptr, - uint32_t context_len, const uint8_t *msg_ptr, uint32_t msg_len, uint8_t *sig_ptr); -void sign_sr25519_phase2(const uint8_t *sk_ed25519_expanded, const uint8_t *pk, const uint8_t *context_ptr, - uint32_t context_len, const uint8_t *msg_ptr, uint32_t msg_len, uint8_t *sig_ptr); diff --git a/app/rust/src/bolos.rs b/app/rust/src/bolos.rs deleted file mode 100644 index f98ae76..0000000 --- a/app/rust/src/bolos.rs +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************* -* (c) 2018 - 2023 Zondax AG -* -* 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. -********************************************************************************/ -//! Rust interfaces to Ledger SDK APIs. -#[cfg(test)] -use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; -use curve25519_dalek::scalar::Scalar; -#[cfg(test)] -#[cfg(target_arch = "x86_64")] -use getrandom::getrandom; -use merlin::TranscriptRng; -use rand::{CryptoRng, RngCore}; - -extern "C" { - fn cx_rng(buffer: *mut u8, len: u32); - fn zemu_log_stack(buffer: *const u8); - fn check_app_canary(); -} - -#[cfg(not(test))] -pub fn c_zemu_log_stack(s: &[u8]) { - unsafe { zemu_log_stack(s.as_ptr()) } -} - -#[cfg(test)] -pub fn c_zemu_log_stack(s: &[u8]) {} - -pub fn c_check_app_canary() { - unsafe { check_app_canary() } -} - -pub struct Trng; - -impl RngCore for Trng { - fn next_u32(&mut self) -> u32 { - let mut out = [0; 4]; - self.fill_bytes(&mut out); - u32::from_le_bytes(out) - } - - fn next_u64(&mut self) -> u64 { - let mut out = [0; 8]; - self.fill_bytes(&mut out); - u64::from_le_bytes(out) - } - - #[cfg(not(target_arch = "x86_64"))] - fn fill_bytes(&mut self, dest: &mut [u8]) { - c_zemu_log_stack(b"fill_bytes\x00".as_ref()); - - unsafe { - cx_rng(dest.as_mut_ptr(), dest.len() as u32); - } - } - - #[cfg(target_arch = "x86_64")] - #[cfg(test)] - fn fill_bytes(&mut self, dest: &mut [u8]) { - getrandom(dest); - } - - #[cfg(target_arch = "x86_64")] - #[cfg(not(test))] - fn fill_bytes(&mut self, _dest: &mut [u8]) {} - - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { - self.fill_bytes(dest); - Ok(()) - } -} - -impl CryptoRng for Trng {} diff --git a/app/rust/src/lib.rs b/app/rust/src/lib.rs deleted file mode 100644 index 7a5c2e5..0000000 --- a/app/rust/src/lib.rs +++ /dev/null @@ -1,281 +0,0 @@ -/******************************************************************************* -* (c) 2018 - 2023 Zondax AG -* -* 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. -********************************************************************************/ -#![no_std] -#![no_builtins] -#![allow(dead_code, unused_imports)] - -extern crate core; -#[cfg(test)] -#[macro_use] -extern crate hex_literal; - -use core::convert::TryInto; -use core::mem; -use core::panic::PanicInfo; -use core::slice::{from_raw_parts, from_raw_parts_mut}; - -use curve25519_dalek::scalar::Scalar; -use merlin::{Transcript, TranscriptRng, TranscriptRngBuilder}; -use rand::RngCore; -use schnorrkel::context::{SigningContext, SigningTranscript}; -use schnorrkel::{PublicKey, SecretKey}; -use zeroize::Zeroize; - -use crate::bolos::*; - -mod bolos; - -fn debug(_msg: &str) {} - -#[cfg(not(test))] -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} - -#[inline(never)] -fn mult_with_secret(k: &mut Scalar, sk: &[u8]) { - let mut skbytes = [0u8; 32]; - skbytes.copy_from_slice(&sk[0..32]); - let s = Scalar::from_bits(skbytes); - *k *= s; -} - -#[inline(never)] -fn add_witness(k: &mut Scalar, x: [u8; 32]) -> [u8; 32] { - let r = Scalar::from_bits(x); - *k += r; - k.to_bytes() -} - -#[inline(never)] -fn get_challenge_scalar(k: &mut Scalar, tr: &mut Transcript) { - let mut kbytes = [0u8; 64]; - tr.challenge_bytes(b"sign:c", &mut kbytes); - *k += Scalar::from_bytes_mod_order_wide(&kbytes); -} - -#[inline(never)] -fn get_witness_bytes_custom(br: &mut Transcript, nonce_seeds: &[&[u8]]) -> [u8; 32] { - c_zemu_log_stack(b"witness_bytes\x00".as_ref()); - let mut x = [0u8; 32]; - for ns in nonce_seeds { - br.append_message(b"nonce-bytes", ns); - } - { - let random_bytes = { - let mut bytes = [0u8; 32]; - Trng.fill_bytes(&mut bytes); - bytes - }; - br.append_message(b"rng", &random_bytes); - } - br.challenge_bytes(b"witness-bytes", &mut x); - br.zeroize(); - x -} - -#[no_mangle] -pub extern "C" fn sign_sr25519_phase1( - sk_ristretto_expanded_ptr: *const u8, - pk_ptr: *const u8, - context_ptr: *const u8, - context_len: usize, - msg_ptr: *const u8, - msg_len: usize, - sig_ptr: *mut u8, -) { - c_zemu_log_stack(b"sign_sr25519\x00".as_ref()); - - let sk_ristretto_expanded = - unsafe { from_raw_parts(sk_ristretto_expanded_ptr as *const u8, 64) }; - let pk = unsafe { from_raw_parts(pk_ptr as *const u8, 32) }; - let context = unsafe { from_raw_parts(context_ptr as *const u8, context_len) }; - let message = unsafe { from_raw_parts(msg_ptr as *const u8, msg_len) }; - let signature = unsafe { from_raw_parts_mut(sig_ptr as *mut u8, 64) }; - - let mut signtranscript = Transcript::new(b"SigningContext"); - signtranscript.append_message(b"", context); - signtranscript.append_message(b"sign-bytes", message); - signtranscript.append_message(b"proto-name", b"Schnorr-sig"); //proto name - signtranscript.append_message(b"sign:pk", pk); //commitpoint: pk - - let x = get_witness_bytes_custom(&mut signtranscript, &[&sk_ristretto_expanded[32..]]); - signature[32..64].copy_from_slice(&x); -} - -#[no_mangle] -pub extern "C" fn sign_sr25519_phase2( - sk_ristretto_expanded_ptr: *const u8, - pk_ptr: *const u8, - context_ptr: *const u8, - context_len: usize, - msg_ptr: *const u8, - msg_len: usize, - sig_ptr: *mut u8, -) { - c_zemu_log_stack(b"sign_sr25519\x00".as_ref()); - - let sk_ristretto_expanded = - unsafe { from_raw_parts(sk_ristretto_expanded_ptr as *const u8, 64) }; - let pk = unsafe { from_raw_parts(pk_ptr as *const u8, 32) }; - let context = unsafe { from_raw_parts(context_ptr as *const u8, context_len) }; - let message = unsafe { from_raw_parts(msg_ptr as *const u8, msg_len) }; - let signature = unsafe { from_raw_parts_mut(sig_ptr as *mut u8, 64) }; - - let mut signtranscript = Transcript::new(b"SigningContext"); - signtranscript.append_message(b"", context); - signtranscript.append_message(b"sign-bytes", message); - signtranscript.append_message(b"proto-name", b"Schnorr-sig"); //proto name - signtranscript.append_message(b"sign:pk", pk); //commitpoint: pk - signtranscript.append_message(b"sign:R", &signature[0..32]); //commitpoint: pk - - let mut x = [0u8; 32]; - x.copy_from_slice(&signature[32..64]); - - let mut k = Scalar::zero(); - get_challenge_scalar(&mut k, &mut signtranscript); - - mult_with_secret(&mut k, sk_ristretto_expanded); - signature[32..].copy_from_slice(&add_witness(&mut k, x)); - signature[63] |= 128; -} - -#[no_mangle] -pub extern "C" fn get_sr25519_sk(sk_ed25519_expanded_ptr: *mut u8) { - let sk_ed25519_expanded = unsafe { from_raw_parts_mut(sk_ed25519_expanded_ptr as *mut u8, 64) }; - let secret: SecretKey = SecretKey::from_ed25519_bytes(&sk_ed25519_expanded[..]).unwrap(); - sk_ed25519_expanded.copy_from_slice(&secret.to_bytes()); -} - -#[cfg(test)] -mod tests { - use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; - use curve25519_dalek::edwards::EdwardsPoint; - use curve25519_dalek::scalar::Scalar; - use log::{debug, info}; - use schnorrkel::{context::*, Keypair, PublicKey, SecretKey, Signature}; - - use crate::*; - use core::ops::Mul; - - fn init_logging() { - let _ = env_logger::builder().is_test(true).try_init(); - } - - fn ristretto_scalarmult(sk: &[u8], pk: &mut [u8]) { - let mut seckey = [0u8; 32]; - seckey.copy_from_slice(&sk[0..32]); - let pubkey = RISTRETTO_BASEPOINT_POINT - .mul(Scalar::from_bits(seckey)) - .compress() - .0; - pk.copy_from_slice(&pubkey); - } - - #[test] - fn test_sign_verify() { - let mut sk_ed25519_expanded = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - ]; - - let pk_expected = "b65abc66a8fdeac1197d03daa6c3791d0c0799a52db6b7127b1cd12d46e34364"; - - let secret = SecretKey::from_ed25519_bytes(&sk_ed25519_expanded).unwrap(); - - let mut pk = [0u8; 32]; - get_sr25519_sk(sk_ed25519_expanded.as_mut_ptr()); - - ristretto_scalarmult(&sk_ed25519_expanded, &mut pk); - - assert_eq!(hex::encode(pk), pk_expected); - - let context = b"good"; - let msg = b"test message"; - let mut signature = [0u8; 64]; - - sign_sr25519_phase1( - secret.to_bytes().as_ptr(), - pk.as_ptr(), - context.as_ptr(), - context.len(), - msg.as_ptr(), - msg.len(), - signature.as_mut_ptr(), - ); - - let mut x = [0u8; 32]; - x.copy_from_slice(&signature[32..64]); - - ristretto_scalarmult(&x, &mut signature[0..32]); - - sign_sr25519_phase2( - secret.to_bytes().as_ptr(), - pk.as_ptr(), - context.as_ptr(), - context.len(), - msg.as_ptr(), - msg.len(), - signature.as_mut_ptr(), - ); - - let keypair: Keypair = Keypair::from(secret); - - let mut sigledger = [0u8; 64]; - hex::decode_to_slice("48fdbe5cf3524bdd078ac711565d658a3053d10660749959883c4710f49d9948b2d5f829bea6800897dc6ea0150ca11075cc36b75bfcf3712aafb8e1bd10bf8f",&mut sigledger).expect("dec"); - - let self_sig = Signature::from_bytes(&signature).unwrap(); - let self_sig_ledger = Signature::from_bytes(&sigledger).unwrap(); - - let vers = signing_context(context); - - assert!( - keypair.verify(vers.bytes(msg), &self_sig).is_ok(), - "Verification of a valid signature failed!" - ); - assert!( - keypair.verify(vers.bytes(msg), &self_sig_ledger).is_ok(), - "Verification of a valid signature from ledger failed!" - ); - } - - #[test] - fn get_public_key_c() { - init_logging(); - - let mut sk_ed25519_expanded = [ - 0x00, 0x01, 0x02, 0x03, 04, 0x5, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 04, 0x5, 0x06, - 0x07, 0x00, 0x01, 0x02, 0x03, 04, 0x5, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 04, 0x5, - 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 04, 0x5, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 04, - 0x5, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 04, 0x5, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, - 04, 0x5, 0x06, 0x07, - ]; - - let pk_expected = "b65abc66a8fdeac1197d03daa6c3791d0c0799a52db6b7127b1cd12d46e34364"; - - let mut pk = [0u8; 32]; - get_sr25519_sk(sk_ed25519_expanded.as_mut_ptr()); - - ristretto_scalarmult(&sk_ed25519_expanded, &mut pk); - - info!("{:?}", hex::encode(pk)); - assert_eq!(hex::encode(pk), pk_expected); - } -} diff --git a/app/src/addr.c b/app/src/addr.c index 75ecf61..5e0389d 100644 --- a/app/src/addr.c +++ b/app/src/addr.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/addr.h b/app/src/addr.h index daf26bd..fce9bb3 100644 --- a/app/src/addr.h +++ b/app/src/addr.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index ee0c05b..fc29fea 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * (c) 2016 Ledger * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/app/src/borsh.c b/app/src/borsh.c new file mode 100644 index 0000000..a2ce4ac --- /dev/null +++ b/app/src/borsh.c @@ -0,0 +1,154 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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 "borsh.h" + +#include "bech32.h" +#include "crypto_helper.h" +#include "parser_impl.h" +#include "zxformat.h" + +parser_error_t read_u8(parser_context_t *ctx, uint8_t *val) { + CHECK_INPUT(ctx); + CHECK_INPUT(val); + *val = *(uint8_t *)(ctx->buffer.ptr + ctx->offset); + CTX_CHECK_AND_ADVANCE(ctx, OFFSET_U8); + return parser_ok; +} + +parser_error_t read_u16(parser_context_t *ctx, uint16_t *val) { + CHECK_INPUT(ctx); + CHECK_INPUT(val); + *val = *(uint16_t *)(ctx->buffer.ptr + ctx->offset); + CTX_CHECK_AND_ADVANCE(ctx, OFFSET_U16); + return parser_ok; +} + +parser_error_t read_u32(parser_context_t *ctx, uint32_t *val) { + CHECK_INPUT(ctx); + CHECK_INPUT(val); + *val = *(uint32_t *)(ctx->buffer.ptr + ctx->offset); + CTX_CHECK_AND_ADVANCE(ctx, OFFSET_U32); + return parser_ok; +} + +parser_error_t read_u64(parser_context_t *ctx, uint64_t *val) { + CHECK_INPUT(ctx); + CHECK_INPUT(val); + *val = *(uint64_t *)(ctx->buffer.ptr + ctx->offset); + CTX_CHECK_AND_ADVANCE(ctx, OFFSET_U64); + return parser_ok; +} + +// void print_buffer(bytes_t *buffer, const char *title) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(50, "%s\n", title); +// char print[1000] = {0}; +// array_to_hexstr(print, sizeof(print), buffer->ptr, buffer->len); +// ZEMU_LOGF(1000, "%s\n", print); +// #else +// printf("%s %d: ", title, buffer->len); +// for (uint16_t i = 0; i < buffer->len; i++) { +// printf("%02x", buffer->ptr[i]); +// } +// printf("\n"); +// #endif +// } + +// void print_buffer_u8(bytes_t *buffer, const char *title) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(50, "%s\n", title); +// char print[1000] = {0}; +// array_to_hexstr(print, sizeof(print), buffer->ptr, buffer->len); +// ZEMU_LOGF(1000, "%s\n", print); +// #else +// printf("%s %d: [", title, buffer->len); +// for (uint16_t i = 0; i < buffer->len; i++) { +// printf("%d, ", buffer->ptr[i]); +// } +// printf("]\n"); +// #endif +// } + +// void print_buffer_str(bytes_t *buffer, const char *title) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(50, "%s\n", title); +// char print[1000] = {0}; +// array_to_hexstr(print, sizeof(print), buffer->ptr, buffer->len); +// ZEMU_LOGF(1000, "%s\n", print); +// #else +// uint8_t buff[1000] = {0}; +// memcpy(buff, buffer->ptr, buffer->len); +// printf("%s %s\n", title, buff); +// #endif +// } + +void print_string(const char *str) { +#if defined(LEDGER_SPECIFIC) + char print[1000] = {0}; + MEMCPY(print, str, strlen(str)); + ZEMU_LOGF(100, "%s\n", str); +#else + printf("%s\n", str); +#endif +} + +// void print_string_title(const char *str, const char *title) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(100, "%s: %s\n", title, str); +// #else +// printf("%s: %s\n", title, str); +// #endif +// } + +// void print_u8(const char *str, uint8_t val) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(100, "%s: %d\n", str, val); +// #else +// printf("%s: %d\n", str, val); +// #endif +// } + +// void print_u16(const char *str, uint16_t val) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(100, "%s: %d\n", str, val); +// #else +// printf("%s: %d\n", str, val); +// #endif +// } + +// void print_u32(const char *str, uint32_t val) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(100, "%s: %d\n", str, val); +// #else +// printf("%s: %u\n", str, val); +// #endif +// } + +// void print_u64(const char *str, uint64_t val) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(100, "%s: %lu\n", str, val); +// #else +// printf("%s: %llu\n", str, val); +// #endif +// } + +// void print_u64_hex(const char *str, uint64_t val) { +// #if defined(LEDGER_SPECIFIC) +// ZEMU_LOGF(100, "%s: %lu\n", str, val); +// #else +// printf("%s: 0x%llx\n", str, val); +// #endif +// } diff --git a/app/src/borsh.h b/app/src/borsh.h new file mode 100644 index 0000000..4a8889a --- /dev/null +++ b/app/src/borsh.h @@ -0,0 +1,52 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "parser_impl.h" + +#define OFFSET_U8 1 +#define OFFSET_U16 2 +#define OFFSET_U32 4 +#define OFFSET_U64 8 + +parser_error_t read_u8(parser_context_t *ctx, uint8_t *val); +parser_error_t read_u16(parser_context_t *ctx, uint16_t *val); +parser_error_t read_u32(parser_context_t *ctx, uint32_t *val); +parser_error_t read_u64(parser_context_t *ctx, uint64_t *val); + +// TODO: Remove these functions +// void print_buffer(bytes_t *buffer, const char *title); +// void print_buffer_str(bytes_t *buffer, const char *title); +// void print_buffer_u8(bytes_t *buffer, const char *title); +void print_string(const char *str); +// void print_string_title(const char *str, const char *title); +// void print_u8(const char *str, uint8_t val); +// void print_u16(const char *str, uint16_t val); +// void print_u32(const char *str, uint32_t val); +// void print_u64(const char *str, uint64_t val); +// void print_u64_hex(const char *str, uint64_t val); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/coin.h b/app/src/coin.h index c07f679..60c4e21 100644 --- a/app/src/coin.h +++ b/app/src/coin.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/common/actions.c b/app/src/common/actions.c index a006e3f..01090ed 100644 --- a/app/src/common/actions.c +++ b/app/src/common/actions.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/common/actions.h b/app/src/common/actions.h index ec2da6d..56a56c0 100644 --- a/app/src/common/actions.h +++ b/app/src/common/actions.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/common/main.c b/app/src/common/main.c index 2fbe4bf..69b0df4 100644 --- a/app/src/common/main.c +++ b/app/src/common/main.c @@ -1,6 +1,6 @@ /******************************************************************************* * (c) 2016 Ledger - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/common/parser.h b/app/src/common/parser.h index 07b9012..f98e4d9 100644 --- a/app/src/common/parser.h +++ b/app/src/common/parser.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/common/parser_common.h b/app/src/common/parser_common.h index cc09e98..0605f5f 100644 --- a/app/src/common/parser_common.h +++ b/app/src/common/parser_common.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,9 @@ typedef enum { parser_unexpected_type, parser_unexpected_method, parser_unexpected_buffer_end, + parser_encoding_failed, + parser_invalid_crypto_settings, + parser_ledger_api_error, parser_unexpected_value, parser_unexpected_number_items, parser_unexpected_version, @@ -54,15 +57,29 @@ typedef enum { parser_invalid_address, parser_unexpected_chain, parser_missing_field, - paser_unknown_transaction, -} parser_error_t; + parser_unknown_transaction, + parser_running_out_of_stack, + parser_root_type_indices_overflow, + parser_unexpected_root_hash, + parser_unexpected_chain_hash, + parser_schema_index_not_found, + parser_scheme_discriminant_overflow, + parser_name_registry_not_found, + parser_too_many_items, + parser_push_item_too_long, + + // parser specific + parser_schema_unknown_type, + parser_schema_parser_txn_failed, + parser_schema_merkle_proofs_indices_mismatch, -typedef struct { - const uint8_t *buffer; - uint16_t bufferLen; - uint16_t offset; - parser_tx_t *tx_obj; -} parser_context_t; + // ui specific + parser_ui_item_title_empty, + parser_ui_separator_not_found, + parser_ui_buffer_not_initialized, + parser_ui_buffer_init_failed, + parser_ui_buffer_too_small, +} parser_error_t; #ifdef __cplusplus } diff --git a/app/src/common/tx.c b/app/src/common/tx.c index 2e3d255..9ebec0e 100644 --- a/app/src/common/tx.c +++ b/app/src/common/tx.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2024 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/common/tx.h b/app/src/common/tx.h index ab2376d..1cbb4fc 100644 --- a/app/src/common/tx.h +++ b/app/src/common/tx.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/crypto.c b/app/src/crypto.c index e9ebd55..98c72e3 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/crypto.h b/app/src/crypto.h index 22c57f0..6cb784b 100644 --- a/app/src/crypto.h +++ b/app/src/crypto.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/crypto_helper.c b/app/src/crypto_helper.c index 1db60a9..616b11f 100644 --- a/app/src/crypto_helper.c +++ b/app/src/crypto_helper.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,42 @@ #include "zxformat.h" #if defined(LEDGER_SPECIFIC) #include "cx.h" +#include "cx_sha256.h" +cx_sha256_t ctx; #else #include "picohash.h" -#define CX_SHA256_SIZE 32 +picohash_ctx_t ctx; #endif -zxerr_t crypto_computeSha256(uint8_t *output, uint16_t outputLen, const uint8_t *input, uint16_t inputLen) { +zxerr_t crypto_sha256_init() { +#if defined(LEDGER_SPECIFIC) + MEMZERO(&ctx, sizeof(ctx)); + cx_sha256_init_no_throw(&ctx); +#else + picohash_init_sha256(&ctx); +#endif + return zxerr_ok; +} + +zxerr_t crypto_sha256_update(const uint8_t *input, uint16_t inputLen) { +#if defined(LEDGER_SPECIFIC) + CHECK_CX_OK(cx_sha256_update(&ctx, input, inputLen)); +#else + picohash_update(&ctx, input, inputLen); +#endif + return zxerr_ok; +} + +zxerr_t crypto_sha256_final(uint8_t *output) { +#if defined(LEDGER_SPECIFIC) + CHECK_CX_OK(cx_sha256_final(&ctx, output)); +#else + picohash_final(&ctx, output); +#endif + return zxerr_ok; +} + +zxerr_t crypto_sha256_one_shot(uint8_t *output, uint16_t outputLen, const uint8_t *input, uint16_t inputLen) { if (output == NULL || outputLen == 0 || input == NULL) { return zxerr_invalid_crypto_settings; } @@ -35,14 +65,10 @@ zxerr_t crypto_computeSha256(uint8_t *output, uint16_t outputLen, const uint8_t MEMZERO(output, outputLen); -#if defined(LEDGER_SPECIFIC) - cx_hash_sha256(input, inputLen, output, outputLen); -#else - picohash_ctx_t ctx; - picohash_init_sha256(&ctx); - picohash_update(&ctx, input, inputLen); - picohash_final(&ctx, output); -#endif + CHECK_ZXERR(crypto_sha256_init()); + CHECK_ZXERR(crypto_sha256_update(input, inputLen)); + CHECK_ZXERR(crypto_sha256_final(output)); + return zxerr_ok; } @@ -59,7 +85,7 @@ zxerr_t crypto_computeAddress(uint8_t *address, uint16_t addressLen, const uint8 MEMZERO(address, addressLen); uint8_t sha[PK_LEN_25519] = {0}; - CHECK_ZXERR(crypto_computeSha256(sha, sizeof(sha), pubkey, PK_LEN_25519)); + CHECK_ZXERR(crypto_sha256_one_shot(sha, sizeof(sha), pubkey, PK_LEN_25519)); CHECK_ZXERR( bech32EncodeFromBytes((char *)address, ADDRESS_MAX_LENGTH, HRP, sha, PUBKEY_SHA_LEN, 1, BECH32_ENCODING_BECH32M)); diff --git a/app/src/crypto_helper.h b/app/src/crypto_helper.h index 09eb789..77fa75e 100644 --- a/app/src/crypto_helper.h +++ b/app/src/crypto_helper.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,13 @@ extern "C" { #include "zxmacros.h" #define PUBKEY_SHA_LEN 28 +#define CX_SHA256_SIZE 32 #define HRP "sov" -zxerr_t crypto_computeSha256(uint8_t *output, uint16_t outputLen, const uint8_t *input, uint16_t inputLen); +zxerr_t crypto_sha256_init(); +zxerr_t crypto_sha256_update(const uint8_t *input, uint16_t inputLen); +zxerr_t crypto_sha256_final(uint8_t *output); +zxerr_t crypto_sha256_one_shot(uint8_t *output, uint16_t outputLen, const uint8_t *input, uint16_t inputLen); zxerr_t crypto_computeAddress(uint8_t *address, uint16_t addressLen, const uint8_t *pubkey); #ifdef __cplusplus diff --git a/app/src/parser.c b/app/src/parser.c index 044deb6..412a353 100644 --- a/app/src/parser.c +++ b/app/src/parser.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,24 +28,24 @@ parser_error_t parser_init_context(parser_context_t *ctx, const uint8_t *buffer, uint16_t bufferSize) { ctx->offset = 0; - ctx->buffer = NULL; - ctx->bufferLen = 0; + ctx->buffer.ptr = NULL; + ctx->buffer.len = 0; if (bufferSize == 0 || buffer == NULL) { // Not available, use defaults return parser_init_context_empty; } - ctx->buffer = buffer; - ctx->bufferLen = bufferSize; + ctx->buffer.ptr = buffer; + ctx->buffer.len = bufferSize; return parser_ok; } parser_error_t parser_parse(parser_context_t *ctx, const uint8_t *data, size_t dataLen, parser_tx_t *tx_obj) { CHECK_ERROR(parser_init_context(ctx, data, dataLen)) - tx_obj->tx_blind_signature.ptr = data; - tx_obj->tx_blind_signature.len = dataLen; - ctx->tx_obj = tx_obj; + // tx_obj->tx_blind_signature.ptr = data; + // tx_obj->tx_blind_signature.len = dataLen; + // ctx->tx_obj = tx_obj; return _read(ctx, tx_obj); } @@ -109,8 +109,7 @@ parser_error_t parser_getItem(const parser_context_t *ctx, uint8_t displayIdx, c case 1: // Display Item 0 snprintf(outKey, outKeyLen, "Txn"); - pageStringHex(outVal, outValLen, (char *)ctx->tx_obj->tx_blind_signature.ptr, - ctx->tx_obj->tx_blind_signature.len, pageIdx, pageCount); + pageStringHex(outVal, outValLen, (char *)ctx->buffer.ptr, ctx->buffer.len, pageIdx, pageCount); return parser_ok; default: break; diff --git a/app/src/parser_impl.c b/app/src/parser_impl.c index d7d4e1b..5695b52 100644 --- a/app/src/parser_impl.c +++ b/app/src/parser_impl.c @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,24 @@ #include "parser_impl.h" +#include "borsh.h" +#include "schema_reader.h" +// #include "schema_txn_parser.h" +#include "stack_manager.h" #include "zxerror.h" parser_error_t _read(parser_context_t *c, parser_tx_t *v) { - UNUSED(c); - UNUSED(v); - // #{TODO} --> parse parameters: read from c->buffer and store in v + checkStack(); + + CHECK_ERROR(schema_merkle_proofs_read(c, v)); + CHECK_ERROR(schema_extra_data_read(c, v)); + // CHECK_ERROR(schema_parser_transaction(c, v)); + CHECK_ERROR(schema_chain_hash_read(c, v)); + + if (c->offset != c->buffer.len) { + return parser_unexpected_error; + } + return parser_ok; } @@ -35,6 +47,16 @@ const char *parser_getErrorDescription(parser_error_t err) { return "Initialized empty context"; case parser_unexpected_buffer_end: return "Unexpected buffer end"; + case parser_encoding_failed: + return "Encoding failed"; + case parser_invalid_crypto_settings: + return "Invalid crypto settings"; + case parser_ledger_api_error: + return "Ledger API error"; + case parser_unexpected_type: + return "Unexpected type"; + case parser_unexpected_method: + return "Unexpected method"; case parser_unexpected_version: return "Unexpected version"; case parser_unexpected_characters: @@ -49,11 +71,51 @@ const char *parser_getErrorDescription(parser_error_t err) { return "Unexpected chain"; case parser_missing_field: return "missing field"; + case parser_unknown_transaction: + return "unknown transaction"; + case parser_running_out_of_stack: + return "running out of stack"; case parser_display_idx_out_of_range: return "display index out of range"; case parser_display_page_out_of_range: return "display page out of range"; + case parser_root_type_indices_overflow: + return "root type indices overflow"; + case parser_unexpected_root_hash: + return "unexpected root hash"; + case parser_unexpected_chain_hash: + return "unexpected chain hash"; + case parser_schema_index_not_found: + return "schema index not found"; + case parser_scheme_discriminant_overflow: + return "scheme discriminant overflow"; + case parser_name_registry_not_found: + return "name registry not found"; + case parser_too_many_items: + return "too many items"; + case parser_push_item_too_long: + return "push item too long"; + + // parser specific + case parser_schema_unknown_type: + return "unknown type"; + case parser_schema_parser_txn_failed: + return "parser txn failed"; + case parser_schema_merkle_proofs_indices_mismatch: + return "merkle proofs indices mismatch"; + + // ui specific + case parser_ui_item_title_empty: + return "item title empty"; + case parser_ui_separator_not_found: + return "separator not found"; + case parser_ui_buffer_not_initialized: + return "buffer not initialized"; + case parser_ui_buffer_init_failed: + return "buffer init failed"; + case parser_ui_buffer_too_small: + return "buffer too small"; default: return "Unrecognized error code"; diff --git a/app/src/parser_impl.h b/app/src/parser_impl.h index 9944891..9d67ae8 100644 --- a/app/src/parser_impl.h +++ b/app/src/parser_impl.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,33 @@ extern "C" { #endif +#define CHECK_INPUT(val) \ + if (val == NULL) { \ + return parser_no_data; \ + } + +// Checks that there are at least SIZE bytes available in the buffer +#define CTX_CHECK(CTX, SIZE) \ + if ((CTX) == NULL || ((CTX)->offset + (SIZE)) > (CTX)->buffer.len) { \ + return parser_unexpected_buffer_end; \ + } + +#define CTX_CHECK_AND_ADVANCE(CTX, SIZE) \ + { \ + CTX_CHECK((CTX), (SIZE)) \ + (CTX)->offset += (SIZE); \ + } + +#define MAP_ZXERR_TO_PARSER_ERR(zxerr) \ + ((zxerr) == zxerr_ok ? parser_ok \ + : (zxerr) == zxerr_no_data ? parser_no_data \ + : (zxerr) == zxerr_buffer_too_small ? parser_unexpected_buffer_end \ + : (zxerr) == zxerr_out_of_bounds ? parser_unexpected_buffer_end \ + : (zxerr) == zxerr_encoding_failed ? parser_encoding_failed \ + : (zxerr) == zxerr_invalid_crypto_settings ? parser_invalid_crypto_settings \ + : (zxerr) == zxerr_ledger_api_error ? parser_ledger_api_error \ + : parser_unexpected_error) + // #{TODO} --> functions to parse, get, process transaction fields parser_error_t _read(parser_context_t *c, parser_tx_t *v); diff --git a/app/src/schema_helper.c b/app/src/schema_helper.c new file mode 100644 index 0000000..cf8361a --- /dev/null +++ b/app/src/schema_helper.c @@ -0,0 +1,149 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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 "schema_helper.h" + +#include "borsh.h" +#include "schema_reader.h" +/** + * @brief Find the position of a given index in the indices array. + * + * @param index_leaf The index value to search for. + * @param indices Pointer to the indices array structure. + * @param index_vec Output: position of the found index in the array (if found). + * @param found Output: set to true if the index is found, false otherwise. + * @return parser_error_t Error code indicating success or failure. + */ +parser_error_t schema_find_index(uint64_t index_leaf, merkle_leaves_indices_t *indices, uint64_t *index_vec, bool *found) { + CHECK_INPUT(indices); + CHECK_INPUT(index_vec); + + *index_vec = 0; + for (uint64_t i = 0; i < indices->entries; i++) { + uint64_t index_tmp = 0; + CHECK_ERROR(read_u64(&indices->indices, &index_tmp)); + if (index_tmp == index_leaf) { + *index_vec = i; + indices->indices.offset = 0; + *found = true; + return parser_ok; + } + } + + // reset offset + indices->indices.offset = 0; + *found = false; + return parser_schema_index_not_found; +} + +/** + * @brief Move the offset of the leaves data. + * + * @param leaves The leaves data. + * @param index The index to move the offset to. + * @return parser_error_t The error code. + */ +parser_error_t schema_move_leaf_offset(merkle_leaves_data_t *leaves, uint64_t index) { + CHECK_INPUT(leaves); + uint32_t data_length = 0; + for (uint64_t i = 0; i < index; i++) { + CHECK_ERROR(read_u32(&leaves->data, &data_length)); + if (leaves->data.offset + data_length > leaves->data.buffer.len) { + return parser_unexpected_buffer_end; + } + leaves->data.offset += data_length; + } + return parser_ok; +} + +/** + * @brief Reset the offset of the leaves data. + * + * @param leaves The leaves data. + * @return parser_error_t The error code. + */ +parser_error_t schema_reset_leaf_offset(merkle_leaves_data_t *leaves) { + CHECK_INPUT(leaves); + leaves->data.offset = 0; + return parser_ok; +} + +/** + * @brief Get the schema type of a given index in the transaction. + * + * @param txObj Pointer to the transaction object. + * @param index The index to get the schema type for. + * @param type Output: the schema type of the index. + * @return parser_error_t Error code indicating success or failure. + */ +parser_error_t get_schema_type(parser_tx_t *txObj, uint32_t index, uint8_t *type) { + CHECK_INPUT(txObj); + CHECK_INPUT(type); + + uint64_t mem_offset = txObj->merkle_proof.leaves.data.offset; + txObj->merkle_proof.leaves.data.offset = 0; + + uint64_t index_vec = 0; + bool found = false; + CHECK_ERROR(schema_find_index(index, &txObj->merkle_proof.indices, &index_vec, &found)); + + CHECK_ERROR(schema_move_leaf_offset(&txObj->merkle_proof.leaves, index_vec)); + + uint32_t len = 0; + CHECK_ERROR(read_u32(&txObj->merkle_proof.leaves.data, &len)); + + CHECK_ERROR(read_u8(&txObj->merkle_proof.leaves.data, type)); + + txObj->merkle_proof.leaves.data.offset = mem_offset; + + return parser_ok; +} + +/** + * @brief Get the root index of the unsigned transaction. + * + * @param txObj Pointer to the transaction object. + * @param root_index Output: the root index of the unsigned transaction. + * @return parser_error_t Error code indicating success or failure. + */ +parser_error_t schema_get_unsigned_transaction_index(parser_tx_t *txObj, uint64_t *root_index) { + CHECK_INPUT(txObj); + + // Get root index + if (txObj->schema.root_type_indices.qty <= ROLLUP_ROOTS_UNSIGNED_TRANSACTION) { + return parser_root_type_indices_overflow; + } + + for (uint8_t i = 0; i <= (uint16_t)ROLLUP_ROOTS_UNSIGNED_TRANSACTION; i++) { + CHECK_ERROR(read_u64(&txObj->schema.root_type_indices.indices, root_index)); + } + txObj->schema.root_type_indices.indices.offset = 0; + + return parser_ok; +} + +/** + * @brief Check if a link is a skip link. + * + * @param link Pointer to the link structure. + * @return bool True if the link is a skip link, false otherwise. + */ +bool is_link_skip(link_t *link) { + if (link->tag == LINK_IMMEDIATE && link->data.immediate.type == PRIMITIVE_SKIP) { + return true; + } + return false; +} diff --git a/app/src/schema_helper.h b/app/src/schema_helper.h new file mode 100644 index 0000000..90a0520 --- /dev/null +++ b/app/src/schema_helper.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "parser_common.h" + +parser_error_t schema_find_index(uint64_t index_leaf, merkle_leaves_indices_t *indices, uint64_t *index_vec, bool *found); +parser_error_t schema_move_leaf_offset(merkle_leaves_data_t *leaves, uint64_t index); +parser_error_t schema_reset_leaf_offset(merkle_leaves_data_t *leaves); +parser_error_t get_schema_type(parser_tx_t *txObj, uint32_t index, uint8_t *type); +parser_error_t schema_get_unsigned_transaction_index(parser_tx_t *txObj, uint64_t *root_index); +bool is_link_skip(link_t *link); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/schema_proof.c b/app/src/schema_proof.c new file mode 100644 index 0000000..3e40872 --- /dev/null +++ b/app/src/schema_proof.c @@ -0,0 +1,307 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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 "schema_proof.h" + +#include +#include +#include + +#include "borsh.h" +#include "parser_common.h" +#include "parser_impl.h" +#include "schema_helper.h" +#include "schema_reader.h" +#include "stack_manager.h" + +const uint8_t LEAF_PREFIX = 0x00; +const uint8_t INNER_PREFIX = 0x01; +const uint8_t HASH_BUFFER_QTY = 2; + +/** + * @brief Calculate the largest power of two which is strictly less than the argument. + * + * @param n The input value. + * @return The largest power of two which is strictly less than the argument. + */ +static uint32_t next_smaller_po2(uint32_t n) { + if (n == 0) { + return 0; + } + + if ((n & (n - 1)) == 0) { + return n >> 1; + } + // Find the next power of two greater than or equal to n + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + + if (n == UINT32_MAX) { + return (UINT32_MAX >> 1) + 1; + } + + n += 1; + + // Return the next smaller power of two + return n >> 1; +} + +/** + * @brief Compute the size of the tree needed to prove a given index. + * + * @param num_right_siblings The number of right siblings needed. + * @param index_of_last_included_leaf The index of the last included leaf. + * @param tree_size The output buffer for the computed tree size. + * @return parser_error_t Error code indicating the result of the operation. + */ +parser_error_t compute_tree_size(uint32_t num_right_siblings, uint32_t index_of_last_included_leaf, uint32_t *tree_size) { + uint32_t index_of_final_node = index_of_last_included_leaf; + uint32_t mask = 1; + uint32_t remaining_right_siblings = num_right_siblings; + + while (remaining_right_siblings > 0) { + if ((index_of_final_node & mask) == 0) { + if (index_of_final_node > UINT32_MAX - mask) { + return parser_value_out_of_range; + } + index_of_final_node |= mask; + remaining_right_siblings--; + } + mask <<= 1; + if (index_of_final_node == UINT32_MAX) { + return parser_value_out_of_range; + } + } + + *tree_size = index_of_final_node + 1; + return parser_ok; +} + +/** + * @brief Compute the hash of a leaf node at a given index in the leaves data. + * + * @param leaves Pointer to the merkle_leaves_data_t structure containing the leaves data. + * @param index The index of the leaf to hash. + * @param hash Output buffer for the computed hash (must be at least CX_SHA256_SIZE bytes). + * @return parser_error_t Error code indicating the result of the operation. + */ +static parser_error_t hash_index_leaf(merkle_leaves_data_t *leaves, uint64_t index, uint8_t *hash) { + CHECK_INPUT(leaves); + CHECK_INPUT(hash); + + // move the offset where leaves_data[index] starts + CHECK_ERROR(schema_move_leaf_offset(leaves, index)); + + // get data length + uint32_t data_length = 0; + CHECK_ERROR(read_u32(&leaves->data, &data_length)); + + // Compute hash from entry and store it in proof->hash + crypto_sha256_init(); + crypto_sha256_update(&LEAF_PREFIX, 1); + crypto_sha256_update(leaves->data.buffer.ptr + leaves->data.offset, data_length); + crypto_sha256_final(hash); + + // reset offset + CHECK_ERROR(schema_reset_leaf_offset(leaves)); + + return parser_ok; +} + +/** + * @brief Merge two branches in a binary tree and compute their combined hash. + * + * @param left The hash of the left branch. + * @param right The hash of the right branch. + * @param output The output buffer for the combined hash. + * @return parser_error_t Error code indicating the result of the operation. + */ +// Declaring array sizes as static will trigger a warning if the user pass an array smaller than the size +static parser_error_t merge_branches(const uint8_t left[CX_SHA256_SIZE], const uint8_t right[CX_SHA256_SIZE], + uint8_t output[CX_SHA256_SIZE]) { + CHECK_INPUT(left); + CHECK_INPUT(right); + CHECK_INPUT(output); + + crypto_sha256_init(); + crypto_sha256_update(&INNER_PREFIX, 1); + crypto_sha256_update(left, CX_SHA256_SIZE); + crypto_sha256_update(right, CX_SHA256_SIZE); + crypto_sha256_final(output); + + return parser_ok; +} + +/** + * @brief Get the next lemma hash. + * + * @param lemmas The lemmas structure containing the necessary data. + * @param hash The output buffer for the hash. + * @return parser_error_t Error code indicating the result of the operation. + */ +static parser_error_t get_next_lemma_hash(merkle_lemmas_t *lemmas, uint8_t *hash) { + CHECK_INPUT(lemmas); + CHECK_INPUT(hash); + + MEMCPY(hash, lemmas->data.buffer.ptr + lemmas->data.offset, CX_SHA256_SIZE); + CTX_CHECK_AND_ADVANCE(&lemmas->data, CX_SHA256_SIZE); + + return parser_ok; +} + +/** + * @brief Check if there are leaves in the specified range. + * + * @param start The start index (inclusive). + * @param end The end index (exclusive). + * @param indices Pointer to the merkle_leaves_indices_t structure containing the leaf indices. + * @param has_leaves Output pointer; set to true if there are leaves in the range, false otherwise. + * @return parser_error_t Error code indicating the result of the operation. + */ +static parser_error_t has_leaves(uint64_t start, uint64_t end, merkle_leaves_indices_t *indices, bool *has_leaves) { + CHECK_INPUT(indices); + CHECK_INPUT(has_leaves); + + for (uint64_t i = 0; i < indices->entries; i++) { + uint64_t index_tmp = 0; + CHECK_ERROR(read_u64(&indices->indices, &index_tmp)); + if (index_tmp >= start && index_tmp < end) { + indices->indices.offset = 0; + *has_leaves = true; + return parser_ok; + } + } + + // reset offset + indices->indices.offset = 0; + *has_leaves = false; + return parser_ok; +} + +/** + * @brief Recursively check the range proof inner to merge the branches of the proof. + * + * @param proof The proof structure containing the necessary data. + * @param index_start The start index. + * @param index_end The end index. + * @param hash Output buffer for the computed hash (must be at least CX_SHA256_SIZE bytes). + * @return parser_error_t Error code indicating the result of the operation. + */ +static parser_error_t verify_multiproof_inner(proof_t *proof, uint64_t index_start, uint64_t index_end, uint8_t *hash) { + CHECK_INPUT(proof); + CHECK_INPUT(hash); + + if (index_end <= index_start) { + return parser_unexpected_buffer_end; + } + + // If this is a single node, return the hash of the node + if (index_end - index_start == 1) { + uint64_t index_vec = 0; + bool found = false; + CHECK_ERROR(schema_find_index(index_start, &proof->indices, &index_vec, &found)); + if (found) { + CHECK_ERROR(hash_index_leaf(&proof->leaves, index_vec, hash)); + return parser_ok; + } + + CHECK_ERROR(get_next_lemma_hash(&proof->lemmas, hash)); + + return parser_ok; + } + + uint32_t mid = index_start + next_smaller_po2(index_end - index_start); + if (mid > index_end) { + return parser_unexpected_buffer_end; + } + + bool left_has_leaves = false; + bool right_has_leaves = false; + CHECK_ERROR(has_leaves(index_start, mid, &proof->indices, &left_has_leaves)); + CHECK_ERROR(has_leaves(mid, index_end, &proof->indices, &right_has_leaves)); + + // Check left subtree + CHECK_ERROR(checkStack()); + uint8_t left[CX_SHA256_SIZE]; + if (left_has_leaves) { + CHECK_ERROR(verify_multiproof_inner(proof, index_start, mid, left)); + } else { + CHECK_ERROR(get_next_lemma_hash(&proof->lemmas, left)); + } + + // Check right subtree + CHECK_ERROR(checkStack()); + uint8_t right[CX_SHA256_SIZE]; + if (right_has_leaves) { + CHECK_ERROR(verify_multiproof_inner(proof, mid, index_end, right)); + } else { + CHECK_ERROR(get_next_lemma_hash(&proof->lemmas, right)); + } + + merge_branches(left, right, hash); + + // free stack for left and right + return freeStack(HASH_BUFFER_QTY); +} + +/** + * @brief Compute the root hash for a given metadata structure. + * + * @param metadata Pointer to the merkle_proof_t structure containing the necessary data. + * @param hash Output buffer for the computed root hash (must be at least CX_SHA256_SIZE bytes). + * @return parser_error_t Error code indicating the result of the operation. + */ +parser_error_t get_root_hash(const merkle_proof_t *metadata, uint8_t *hash) { + CHECK_INPUT(metadata); + CHECK_INPUT(hash); + + proof_t proof = {0}; + proof.leaves = metadata->leaves; + proof.lemmas = metadata->lemmas; + proof.indices = metadata->indices; + proof.lemma_index = metadata->lemmas.entries - 1; + proof.tree_size = metadata->tree_size; + + if (proof.tree_size > INT32_MAX) { + return parser_value_out_of_range; + } + + CHECK_ERROR(verify_multiproof_inner(&proof, 0, proof.tree_size, hash)); + + return parser_ok; +} + +parser_error_t verify_merkle_proofs(const merkle_proof_t *metadata) { + CHECK_INPUT(metadata); + CHECK_INPUT(metadata->root_hash.ptr); + + if (metadata->root_hash.len < CX_SHA256_SIZE) { + return parser_unexpected_buffer_end; + } + + uint8_t root_hash[CX_SHA256_SIZE] = {0}; + CHECK_ERROR(get_root_hash(metadata, root_hash)); + + // compare root_hash received with computed root_hash + if (MEMCMP(metadata->root_hash.ptr, root_hash, CX_SHA256_SIZE) != 0) { + return parser_unexpected_root_hash; + } + + return parser_ok; +} diff --git a/app/src/schema_proof.h b/app/src/schema_proof.h new file mode 100644 index 0000000..b03b9de --- /dev/null +++ b/app/src/schema_proof.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ + +#pragma once + +#include +#include + +#include "crypto_helper.h" +#include "merkle_txdef.h" +#include "parser_common.h" +#include "parser_txdef.h" +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + merkle_leaves_data_t leaves; + merkle_leaves_indices_t indices; + merkle_lemmas_t lemmas; + int64_t lemma_index; + uint64_t tree_size; +} proof_t; + +parser_error_t verify_merkle_proofs(const merkle_proof_t *metadata); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/schema_reader.c b/app/src/schema_reader.c new file mode 100644 index 0000000..f85a06c --- /dev/null +++ b/app/src/schema_reader.c @@ -0,0 +1,842 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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 "schema_reader.h" + +#include "borsh.h" +#include "crypto_helper.h" +#include "schema_helper.h" +#include "schema_proof.h" +#include "stack_manager.h" + +parser_error_t read_fixed_point_display(parser_context_t *ctx, fixed_point_display_t *display) { + CHECK_INPUT(display); + CHECK_INPUT(ctx); + + // read type + CHECK_ERROR(read_u8(ctx, (uint8_t *)&display->type)); + + switch (display->type) { + case FIXED_POINT_DISPLAY_DECIMALS: + CHECK_ERROR(read_u8(ctx, (uint8_t *)&display->decimals)); + break; + case FIXED_POINT_DISPLAY_FROM_SIBLING_FIELD: + CHECK_ERROR(read_u64(ctx, (uint64_t *)&display->from_sibling_field.field_index)); + CHECK_ERROR(read_u64(ctx, (uint64_t *)&display->from_sibling_field.byte_offset)); + break; + default: + return parser_schema_unknown_type; + } + + return parser_ok; +} + +parser_error_t read_integer_display(parser_context_t *ctx, integer_display_t *display) { + CHECK_INPUT(display); + CHECK_INPUT(ctx); + + // read type + CHECK_ERROR(read_u8(ctx, (uint8_t *)&display->type)); + + switch (display->type) { + case INTEGER_DISPLAY_HEX: + print_string("integer display hex"); + break; + case INTEGER_DISPLAY_DECIMAL: + // nothing to do + break; + case INTEGER_DISPLAY_FIXED_POINT: + CHECK_ERROR(read_fixed_point_display(ctx, &display->fixed_point)); + break; + default: + return parser_schema_unknown_type; + } + return parser_ok; +} + +parser_error_t read_hrp(parser_context_t *ctx, hrp_t *hrp) { + CHECK_INPUT(hrp); + CHECK_INPUT(ctx); + + CHECK_ERROR(read_u32(ctx, (uint32_t *)&hrp->prefix.len)); + if (hrp->prefix.len > 0) { + hrp->prefix.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, hrp->prefix.len); + } + + return parser_ok; +} + +parser_error_t read_byte_display(parser_context_t *ctx, byte_display_t *display) { + CHECK_INPUT(display); + CHECK_INPUT(ctx); + + // read type + CHECK_ERROR(read_u8(ctx, (uint8_t *)&display->type)); + + switch (display->type) { + case BYTE_DISPLAY_HEX: + print_string("byte display hex"); + break; + case BYTE_DISPLAY_DECIMAL: + print_string("byte display decimal"); + return parser_no_data; + case BYTE_DISPLAY_BECH32: + CHECK_ERROR(read_hrp(ctx, &display->bech32.prefix)); + break; + case BYTE_DISPLAY_BECH32M: + CHECK_ERROR(read_hrp(ctx, &display->bech32m.prefix)); + break; + case BYTE_DISPLAY_BASE58: + print_string("byte display base58"); + return parser_no_data; + default: + return parser_schema_unknown_type; + } + + return parser_ok; +} + +parser_error_t read_structured_display_overrides(parser_context_t *ctx, structured_display_overrides_t *display) { + CHECK_INPUT(display); + CHECK_INPUT(ctx); + + // read has_title_elements + CHECK_ERROR(read_u8(ctx, (uint8_t *)&display->has_title_elements)); + if (display->has_title_elements) { + // read title_elements + CHECK_ERROR(read_u64(ctx, (uint64_t *)&display->title_elements)); + } + + // read has_title_lines + CHECK_ERROR(read_u8(ctx, (uint8_t *)&display->has_title_lines)); + if (display->has_title_lines) { + // read title_lines + CHECK_ERROR(read_u64(ctx, (uint64_t *)&display->title_lines)); + } + + return parser_ok; +} + +parser_error_t read_primitive_integer(parser_context_t *ctx, primitive_integer_t *primitive) { + CHECK_INPUT(primitive); + CHECK_INPUT(ctx); + + // read type + CHECK_ERROR(read_u8(ctx, (uint8_t *)&primitive->type)); + + // read display + CHECK_ERROR(read_integer_display(ctx, &primitive->display)); + + return parser_ok; +} + +parser_error_t read_primitive_byte_array(parser_context_t *ctx, primitive_byte_array_t *primitive) { + CHECK_INPUT(primitive); + CHECK_INPUT(ctx); + + // read length + CHECK_ERROR(read_u64(ctx, (uint64_t *)&primitive->len)); + + // read display + CHECK_ERROR(read_byte_display(ctx, &primitive->display)); + + // read has_name_registry + CHECK_ERROR(read_u8(ctx, (uint8_t *)&primitive->has_name_registry)); + + // read name_registry + if (primitive->has_name_registry) { + CHECK_ERROR(read_u32(ctx, (uint32_t *)&primitive->name_registry.len)); + if (primitive->name_registry.len > 0) { + primitive->name_registry.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, primitive->name_registry.len); + } + } + + return parser_ok; +} + +parser_error_t read_primitive_byte_vec(parser_context_t *ctx, primitive_byte_vec_t *primitive) { + CHECK_INPUT(primitive); + CHECK_INPUT(ctx); + + // read display + CHECK_ERROR(read_byte_display(ctx, &primitive->display)); + + // read has_name_registry + CHECK_ERROR(read_u8(ctx, (uint8_t *)&primitive->has_name_registry)); + + // read name_registry + if (primitive->has_name_registry) { + CHECK_ERROR(read_u32(ctx, (uint32_t *)&primitive->name_registry.len)); + if (primitive->name_registry.len > 0) { + primitive->name_registry.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, primitive->name_registry.len); + } + } + + return parser_ok; +} + +parser_error_t read_immediate(parser_context_t *ctx, primitive_t *primitive) { + CHECK_INPUT(primitive); + CHECK_INPUT(ctx); + + // read type + CHECK_ERROR(read_u8(ctx, (uint8_t *)&primitive->type)); + + // read data + switch (primitive->type) { + case PRIMITIVE_INTEGER: + CHECK_ERROR(read_primitive_integer(ctx, &primitive->integer)); + break; + case PRIMITIVE_BYTE_ARRAY: + CHECK_ERROR(read_primitive_byte_array(ctx, &primitive->byte_array)); + break; + case PRIMITIVE_BYTE_VEC: + CHECK_ERROR(read_primitive_byte_vec(ctx, &primitive->byte_vec)); + break; + case PRIMITIVE_FLOAT32: + print_string("Primitive float32"); + break; + case PRIMITIVE_FLOAT64: + print_string("Primitive float64"); + break; + case PRIMITIVE_STRING: + print_string("Primitive string"); + break; + case PRIMITIVE_BOOLEAN: + print_string("Primitive boolean"); + break; + case PRIMITIVE_SKIP: + break; + default: + return parser_schema_unknown_type; + } + return parser_ok; +} + +parser_error_t read_link(parser_context_t *ctx, link_t *link) { + CHECK_INPUT(link); + CHECK_INPUT(ctx); + + // read tag + CHECK_ERROR(read_u8(ctx, (uint8_t *)&link->tag)); + + // read data + switch (link->tag) { + case LINK_BY_INDEX: + CHECK_ERROR(read_u64(ctx, (uint64_t *)&link->data.by_index)); + break; + case LINK_IMMEDIATE: + CHECK_ERROR(read_immediate(ctx, &link->data.immediate)); + break; + default: + return parser_schema_unknown_type; + } + + return parser_ok; +} + +parser_error_t read_enum_variant(parser_context_t *ctx, enum_variant_t *variant) { + CHECK_INPUT(variant); + CHECK_INPUT(ctx); + + // read name + CHECK_ERROR(read_u32(ctx, (uint32_t *)&variant->name.len)); + if (variant->name.len > 0) { + variant->name.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, variant->name.len); + } + + // read discriminant + CHECK_ERROR(read_u8(ctx, (uint8_t *)&variant->discriminant)); + + // read hide_tag + CHECK_ERROR(read_u8(ctx, (uint8_t *)&variant->hide_tag)); + + // read has_value + CHECK_ERROR(read_u8(ctx, (uint8_t *)&variant->has_value)); + if (variant->has_value) { + // read value + CHECK_ERROR(read_link(ctx, &variant->value)); + } + + return parser_ok; +} + +parser_error_t read_named_field(parser_context_t *ctx, named_field_t *field) { + CHECK_INPUT(field); + CHECK_INPUT(ctx); + + // read display_name + CHECK_ERROR(read_u32(ctx, (uint32_t *)&field->display_name.len)); + if (field->display_name.len > 0) { + field->display_name.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, field->display_name.len); + } + + // read silent + CHECK_ERROR(read_u8(ctx, (uint8_t *)&field->silent)); + + // read is_expert + CHECK_ERROR(read_u8(ctx, (uint8_t *)&field->is_expert)); + + // read value + CHECK_ERROR(read_link(ctx, &field->value)); + + // read doc + CHECK_ERROR(read_u32(ctx, (uint32_t *)&field->doc.len)); + if (field->doc.len > 0) { + field->doc.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, field->doc.len); + } + + return parser_ok; +} + +parser_error_t read_unnamed_field(parser_context_t *ctx, unnamed_field_t *field) { + CHECK_INPUT(field); + CHECK_INPUT(ctx); + + // read value + CHECK_ERROR(read_link(ctx, &field->value)); + + // read silent + CHECK_ERROR(read_u8(ctx, (uint8_t *)&field->silent)); + + // read is_expert + CHECK_ERROR(read_u8(ctx, (uint8_t *)&field->is_expert)); + + // read doc + CHECK_ERROR(read_u32(ctx, (uint32_t *)&field->doc.len)); + if (field->doc.len > 0) { + field->doc.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, field->doc.len); + } + + return parser_ok; +} + +parser_error_t read_enum(parser_context_t *ctx, schema_enum_t *schema_enum) { + CHECK_INPUT(schema_enum); + CHECK_INPUT(ctx); + + // read length of display name + CHECK_ERROR(read_u32(ctx, (uint32_t *)&schema_enum->type_name.len)); + if (schema_enum->type_name.len > 0) { + schema_enum->type_name.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, schema_enum->type_name.len); + } + + // read variants_qty + CHECK_ERROR(read_u32(ctx, &schema_enum->variants_qty)); + + schema_enum->enum_variants.buffer.ptr = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem = ctx->offset; + for (uint32_t i = 0; i < schema_enum->variants_qty; i++) { + enum_variant_t variant = {0}; + CHECK_ERROR(read_enum_variant(ctx, &variant)); + } + schema_enum->enum_variants.buffer.len = ctx->offset - offset_mem; + + // read hide_tag + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_enum->hide_tag)); + + // read structured_display_overrides + CHECK_ERROR(read_structured_display_overrides(ctx, &schema_enum->structured_display_overrides)); + + return parser_ok; +} + +parser_error_t read_struct(parser_context_t *ctx, schema_struct_t *schema_struct) { + CHECK_INPUT(schema_struct); + CHECK_INPUT(ctx); + + // read length of display name + CHECK_ERROR(read_u32(ctx, (uint32_t *)&schema_struct->type_name.len)); + if (schema_struct->type_name.len > 0) { + schema_struct->type_name.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, schema_struct->type_name.len); + } + + // read has_show_as + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_struct->has_show_as)); + if (schema_struct->has_show_as) { + CHECK_ERROR(read_u32(ctx, (uint32_t *)&schema_struct->show_as.len)); + if (schema_struct->show_as.len == 0) { + return parser_unexpected_error; + } + if (schema_struct->show_as.len > 0) { + schema_struct->show_as.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, schema_struct->show_as.len); + } + } + + // read has_structured_show_as + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_struct->has_structured_show_as)); + if (schema_struct->has_structured_show_as) { + CHECK_ERROR(read_u32(ctx, (uint32_t *)&schema_struct->structured_show_as.len)); + if (schema_struct->structured_show_as.len == 0) { + return parser_unexpected_error; + } + if (schema_struct->structured_show_as.len > 0) { + schema_struct->structured_show_as.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, schema_struct->structured_show_as.len); + } + } + + // read peekable + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_struct->peekable)); + + // read fields + CHECK_ERROR(read_u32(ctx, &schema_struct->fields_qty)); + + schema_struct->named_fields.buffer.ptr = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem = ctx->offset; + for (uint32_t i = 0; i < schema_struct->fields_qty; i++) { + named_field_t field = {0}; + CHECK_ERROR(read_named_field(ctx, &field)); + } + schema_struct->named_fields.buffer.len = ctx->offset - offset_mem; + + // read structured_display_overrides + CHECK_ERROR(read_structured_display_overrides(ctx, &schema_struct->structured_display_overrides)); + + return parser_ok; +} + +parser_error_t read_tuple(parser_context_t *ctx, schema_tuple_t *schema_tuple) { + CHECK_INPUT(schema_tuple); + CHECK_INPUT(ctx); + + // read has_show_as + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_tuple->has_show_as)); + if (schema_tuple->has_show_as) { + CHECK_ERROR(read_u32(ctx, (uint32_t *)&schema_tuple->show_as.len)); + if (schema_tuple->show_as.len == 0) { + return parser_unexpected_error; + } + if (schema_tuple->show_as.len > 0) { + schema_tuple->show_as.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, schema_tuple->show_as.len); + } + } + + // read has_structured_show_as + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_tuple->has_structured_show_as)); + if (schema_tuple->has_structured_show_as) { + CHECK_ERROR(read_u32(ctx, (uint32_t *)&schema_tuple->structured_show_as.len)); + if (schema_tuple->structured_show_as.len == 0) { + return parser_unexpected_error; + } + if (schema_tuple->structured_show_as.len > 0) { + schema_tuple->structured_show_as.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, schema_tuple->structured_show_as.len); + } + } + + // read peekable + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_tuple->peekable)); + + // read fields + CHECK_ERROR(read_u32(ctx, &schema_tuple->fields_qty)); + + schema_tuple->unnamed_fields.buffer.ptr = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem = ctx->offset; + for (uint32_t i = 0; i < schema_tuple->fields_qty; i++) { + unnamed_field_t field = {0}; + CHECK_ERROR(read_unnamed_field(ctx, &field)); + } + schema_tuple->unnamed_fields.buffer.len = ctx->offset - offset_mem; + + // read structured_display_overrides + CHECK_ERROR(read_structured_display_overrides(ctx, &schema_tuple->structured_display_overrides)); + + return parser_ok; +} + +parser_error_t read_option(parser_context_t *ctx, schema_option_t *schema_option) { + CHECK_INPUT(schema_option); + CHECK_INPUT(ctx); + + // read value + CHECK_ERROR(read_link(ctx, &schema_option->value)); + + return parser_ok; +} + +parser_error_t read_array(parser_context_t *ctx, schema_array_t *schema_array) { + CHECK_INPUT(schema_array); + CHECK_INPUT(ctx); + + // read length + CHECK_ERROR(read_u64(ctx, (uint64_t *)&schema_array->len)); + + // read vec_type + CHECK_ERROR(read_link(ctx, &schema_array->value)); + + return parser_ok; +} + +parser_error_t read_root_type_indices(parser_context_t *ctx, root_type_indices_t *root_type_indices) { + CHECK_INPUT(root_type_indices); + CHECK_INPUT(ctx); + + // save complete borsh data + root_type_indices->complete_borsh_data.ptr = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem_complete_borsh_data = ctx->offset; + + // read length + CHECK_ERROR(read_u32(ctx, &root_type_indices->qty)); + + const uint8_t *ptr_mem_indices = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem_indices = ctx->offset; + for (uint32_t i = 0; i < root_type_indices->qty; i++) { + uint64_t index = 0; + CHECK_ERROR(read_u64(ctx, &index)); + } + root_type_indices->indices.buffer.ptr = ptr_mem_indices; + root_type_indices->indices.buffer.len = ctx->offset - offset_mem_indices; + + root_type_indices->complete_borsh_data.len = ctx->offset - offset_mem_complete_borsh_data; + + return parser_ok; +} + +parser_error_t read_registry(parser_context_t *ctx, registry_t *registry) { + CHECK_INPUT(registry); + CHECK_INPUT(ctx); + // read data + CHECK_ERROR(read_u32(ctx, (uint32_t *)®istry->data.len)); + if (registry->data.len > 0) { + registry->data.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, registry->data.len); + } + // read name + CHECK_ERROR(read_u32(ctx, (uint32_t *)®istry->name.len)); + if (registry->name.len > 0) { + registry->name.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, registry->name.len); + } + + return parser_ok; +} + +parser_error_t read_name_registry(parser_context_t *ctx, name_registry_t *name_registry) { + CHECK_INPUT(name_registry); + CHECK_INPUT(ctx); + + // read name + CHECK_ERROR(read_u32(ctx, (uint32_t *)&name_registry->name.len)); + if (name_registry->name.len > 0) { + name_registry->name.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, name_registry->name.len); + } + + // read qty + CHECK_ERROR(read_u32(ctx, &name_registry->qty)); + + // read registries + name_registry->registry.buffer.ptr = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem = ctx->offset; + for (uint32_t i = 0; i < name_registry->qty; i++) { + registry_t registry = {0}; + CHECK_ERROR(read_registry(ctx, ®istry)); + } + name_registry->registry.buffer.len = ctx->offset - offset_mem; + return parser_ok; +} + +parser_error_t read_name_registries(parser_context_t *ctx, name_registries_t *name_registries) { + CHECK_INPUT(name_registries); + CHECK_INPUT(ctx); + + // read qty + CHECK_ERROR(read_u32(ctx, &name_registries->qty)); + + // read registries + name_registries->vec_registries.buffer.ptr = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem = ctx->offset; + for (uint32_t i = 0; i < name_registries->qty; i++) { + name_registry_t name_registry = {0}; + CHECK_ERROR(read_name_registry(ctx, &name_registry)); + } + name_registries->vec_registries.buffer.len = ctx->offset - offset_mem; + return parser_ok; +} + +parser_error_t read_chain_data(parser_context_t *ctx, chain_data_t *chain_data) { + CHECK_INPUT(chain_data); + CHECK_INPUT(ctx); + + // save complete borsh data + chain_data->complete_borsh_data.ptr = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem = ctx->offset; + + // read chain_id + CHECK_ERROR(read_u64(ctx, &chain_data->chain_id)); + + // read chain_name + CHECK_ERROR(read_u32(ctx, (uint32_t *)&chain_data->chain_name.len)); + if (chain_data->chain_name.len > 0) { + chain_data->chain_name.ptr = ctx->buffer.ptr + ctx->offset; + CTX_CHECK_AND_ADVANCE(ctx, chain_data->chain_name.len); + } + + // read gas_token_decimals + CHECK_ERROR(read_u8(ctx, &chain_data->gas_token_decimals)); + + // read name_registries + CHECK_ERROR(read_name_registries(ctx, &chain_data->name_registries)); + + chain_data->complete_borsh_data.len = ctx->offset - offset_mem; + + return parser_ok; +} + +parser_error_t read_schema_type(parser_context_t *ctx, uint8_t type) { + CHECK_INPUT(ctx); + + switch (type) { + case LINKING_SCHEME_ENUM: { + schema_enum_t enum_type = {0}; + CHECK_ERROR(read_enum(ctx, &enum_type)); + break; + } + case LINKING_SCHEME_STRUCT: { + schema_struct_t struct_type = {0}; + CHECK_ERROR(read_struct(ctx, &struct_type)); + break; + } + case LINKING_SCHEME_TUPLE: { + schema_tuple_t tuple_type = {0}; + CHECK_ERROR(read_tuple(ctx, &tuple_type)); + break; + } + case LINKING_SCHEME_OPTION: { + link_t option_type = {0}; + CHECK_ERROR(read_link(ctx, &option_type)); + break; + } + case LINKING_SCHEME_INTEGER: { + primitive_integer_t integer_type = {0}; + CHECK_ERROR(read_primitive_integer(ctx, &integer_type)); + break; + } + case LINKING_SCHEME_BYTE_ARRAY: { + primitive_byte_array_t byte_array_type = {0}; + CHECK_ERROR(read_primitive_byte_array(ctx, &byte_array_type)); + break; + } + case LINKING_SCHEME_FLOAT32: { + print_string("READING FLOAT32"); + return parser_no_data; + } + case LINKING_SCHEME_FLOAT64: { + print_string("READING FLOAT64"); + return parser_no_data; + } + case LINKING_SCHEME_STRING: { + print_string("READING STRING"); + return parser_no_data; + } + case LINKING_SCHEME_BOOLEAN: { + print_string("READING BOOLEAN"); + return parser_no_data; + } + case LINKING_SCHEME_SKIP: { + uint64_t skip_type = 0; + CHECK_ERROR(read_u64(ctx, &skip_type)); + break; + } + case LINKING_SCHEME_BYTE_VEC: { + byte_display_t byte_vec_type = {0}; + CHECK_ERROR(read_byte_display(ctx, &byte_vec_type)); + break; + } + case LINKING_SCHEME_ARRAY: { + schema_array_t array_type = {0}; + CHECK_ERROR(read_array(ctx, &array_type)); + break; + } + case LINKING_SCHEME_VEC: { + link_t vec_type = {0}; + CHECK_ERROR(read_link(ctx, &vec_type)); + break; + } + case LINKING_SCHEME_MAP: { + link_t key = {0}; + link_t value = {0}; + CHECK_ERROR(read_link(ctx, &key)); + CHECK_ERROR(read_link(ctx, &value)) + break; + } + default: { + return parser_schema_unknown_type; + } + } + + return parser_ok; +} + +parser_error_t metadata_read(parser_context_t *ctx, parser_tx_t *txObj) { + CHECK_INPUT(ctx); + CHECK_INPUT(txObj); + + uint32_t qty = 0; + CHECK_ERROR(read_u32(ctx, &qty)); + + for (uint32_t i = 0; i < qty; i++) { + // read type + uint8_t type = 0; + CHECK_ERROR(read_u8(ctx, (uint8_t *)&type)); + CHECK_ERROR(read_schema_type(ctx, type)); + } + + // read root_type_indices + CHECK_ERROR(read_root_type_indices(ctx, &txObj->schema.root_type_indices)); + + // read chain_data + CHECK_ERROR(read_chain_data(ctx, &txObj->schema.chain_data)); + + // read extra_metadata_hash + txObj->schema.extra_metadata_hash.ptr = ctx->buffer.ptr + ctx->offset; + txObj->schema.extra_metadata_hash.len = CX_SHA256_SIZE; + CTX_CHECK_AND_ADVANCE(ctx, CX_SHA256_SIZE); + + if (ctx->offset != ctx->buffer.len) { + return parser_schema_parser_txn_failed; + } + + return parser_ok; +} + +parser_error_t compute_internal_data_hash(parser_tx_t *txObj, uint8_t *internal_data_hash) { + CHECK_INPUT(txObj); + CHECK_INPUT(internal_data_hash); + + crypto_sha256_init(); + crypto_sha256_update(txObj->schema.root_type_indices.complete_borsh_data.ptr, + txObj->schema.root_type_indices.complete_borsh_data.len); + crypto_sha256_update(txObj->schema.chain_data.complete_borsh_data.ptr, txObj->schema.chain_data.complete_borsh_data.len); + crypto_sha256_final(internal_data_hash); + + return parser_ok; +} + +parser_error_t compute_chain_hash(parser_tx_t *txObj) { + CHECK_INPUT(txObj); + + uint8_t internal_data_hash[CX_SHA256_SIZE] = {0}; + uint8_t computed_chain_hash[CX_SHA256_SIZE] = {0}; + CHECK_ERROR(compute_internal_data_hash(txObj, internal_data_hash)); + + crypto_sha256_init(); + crypto_sha256_update(txObj->merkle_proof.root_hash.ptr, txObj->merkle_proof.root_hash.len); + crypto_sha256_update(internal_data_hash, CX_SHA256_SIZE); + crypto_sha256_update(txObj->schema.extra_metadata_hash.ptr, txObj->schema.extra_metadata_hash.len); + crypto_sha256_final(computed_chain_hash); + + if (MEMCMP(computed_chain_hash, txObj->schema.chain_hash.ptr, CX_SHA256_SIZE) != 0) { + return parser_unexpected_chain_hash; + } + return parser_ok; +} + +// | borsh(leaves_data) | borsh(indices_leaves) | borsh(lemmas) | borsh(tree_size) | borsh(root_hash) +parser_error_t schema_merkle_proofs_read(parser_context_t *ctx, parser_tx_t *txObj) { + CHECK_INPUT(ctx); + CHECK_INPUT(txObj); + + // read leaf data + CHECK_ERROR(read_u32(ctx, &txObj->merkle_proof.leaves.entries)); + const uint8_t *ptr_mem = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem = ctx->offset; + for (uint32_t i = 0; i < txObj->merkle_proof.leaves.entries; i++) { + uint32_t length = 0; + CHECK_ERROR(read_u32(ctx, &length)); + uint8_t schema_type = 0; + CHECK_ERROR(read_u8(ctx, (uint8_t *)&schema_type)); + CHECK_ERROR(read_schema_type(ctx, schema_type)); + } + + txObj->merkle_proof.leaves.data.buffer.ptr = ptr_mem; + txObj->merkle_proof.leaves.data.buffer.len = ctx->offset - offset_mem; + + // read indices + CHECK_ERROR(read_u32(ctx, &txObj->merkle_proof.indices.entries)); + if (txObj->merkle_proof.indices.entries != txObj->merkle_proof.leaves.entries) { + return parser_schema_merkle_proofs_indices_mismatch; + } + const uint8_t *ptr_mem_indices = ctx->buffer.ptr + ctx->offset; + uint16_t offset_mem_indices = ctx->offset; + for (uint32_t i = 0; i < txObj->merkle_proof.indices.entries; i++) { + uint64_t index = 0; + CHECK_ERROR(read_u64(ctx, &index)); + } + txObj->merkle_proof.indices.indices.buffer.ptr = ptr_mem_indices; + txObj->merkle_proof.indices.indices.buffer.len = ctx->offset - offset_mem_indices; + + // read lemmas + CHECK_ERROR(read_u32(ctx, &txObj->merkle_proof.lemmas.entries)); + txObj->merkle_proof.lemmas.data.buffer.ptr = ctx->buffer.ptr + ctx->offset; + txObj->merkle_proof.lemmas.data.buffer.len = txObj->merkle_proof.lemmas.entries * 32; + CTX_CHECK_AND_ADVANCE(ctx, txObj->merkle_proof.lemmas.data.buffer.len); + + // read tree_size + CHECK_ERROR(read_u64(ctx, &txObj->merkle_proof.tree_size)); + + // read root_hash + txObj->merkle_proof.root_hash.ptr = ctx->buffer.ptr + ctx->offset; + txObj->merkle_proof.root_hash.len = CX_SHA256_SIZE; + CTX_CHECK_AND_ADVANCE(ctx, txObj->merkle_proof.root_hash.len); + + CHECK_ERROR(verify_merkle_proofs(&txObj->merkle_proof)); + + return parser_ok; +} + +// borsh(root_indexes) | borsh(chain_data) | borsh(extra_metadata_hash) +parser_error_t schema_extra_data_read(parser_context_t *ctx, parser_tx_t *txObj) { + CHECK_INPUT(ctx); + CHECK_INPUT(txObj); + + // read root_type_indices + CHECK_ERROR(read_root_type_indices(ctx, &txObj->schema.root_type_indices)); + + // read chain_data + CHECK_ERROR(read_chain_data(ctx, &txObj->schema.chain_data)); + + // read extra_metadata_hash + txObj->schema.extra_metadata_hash.ptr = ctx->buffer.ptr + ctx->offset; + txObj->schema.extra_metadata_hash.len = CX_SHA256_SIZE; + CTX_CHECK_AND_ADVANCE(ctx, CX_SHA256_SIZE); + + return parser_ok; +} + +parser_error_t schema_chain_hash_read(parser_context_t *ctx, parser_tx_t *txObj) { + // read chain hash + txObj->schema.chain_hash.ptr = ctx->buffer.ptr + ctx->offset; + txObj->schema.chain_hash.len = CX_SHA256_SIZE; + CTX_CHECK_AND_ADVANCE(ctx, CX_SHA256_SIZE); + + // compute chain hash + CHECK_ERROR(compute_chain_hash(txObj)); + + return parser_ok; +} diff --git a/app/src/schema_reader.h b/app/src/schema_reader.h new file mode 100644 index 0000000..060b711 --- /dev/null +++ b/app/src/schema_reader.h @@ -0,0 +1,44 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "parser_common.h" + +parser_error_t read_struct(parser_context_t *ctx, schema_struct_t *schema_struct); +parser_error_t read_enum(parser_context_t *ctx, schema_enum_t *schema_enum); +parser_error_t read_tuple(parser_context_t *ctx, schema_tuple_t *schema_tuple); +parser_error_t read_option(parser_context_t *ctx, schema_option_t *schema_option); +parser_error_t read_array(parser_context_t *ctx, schema_array_t *schema_array); +parser_error_t read_link(parser_context_t *ctx, link_t *link); +parser_error_t read_enum_variant(parser_context_t *ctx, enum_variant_t *variant); +parser_error_t read_name_registry(parser_context_t *ctx, name_registry_t *name_registry); +parser_error_t read_named_field(parser_context_t *ctx, named_field_t *field); +parser_error_t read_unnamed_field(parser_context_t *ctx, unnamed_field_t *field); +parser_error_t read_registry(parser_context_t *ctx, registry_t *registry); + +parser_error_t metadata_read(parser_context_t *ctx, parser_tx_t *txObj); +parser_error_t schema_merkle_proofs_read(parser_context_t *ctx, parser_tx_t *txObj); +parser_error_t schema_extra_data_read(parser_context_t *ctx, parser_tx_t *txObj); +parser_error_t schema_chain_hash_read(parser_context_t *ctx, parser_tx_t *txObj); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/stack_manager.c b/app/src/stack_manager.c new file mode 100644 index 0000000..f165a6d --- /dev/null +++ b/app/src/stack_manager.c @@ -0,0 +1,73 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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 + +#include "parser_common.h" +#include "zxmacros.h" + +#if defined(LEDGER_SPECIFIC) +#define STACK_SHIFT 0x20 +#define MINIMUM_STACK 0x400 +#else +static int16_t recursionDepthCounter = 0; +#define MAX_RECURSION_DEPTH 50 +#endif + +/** + * @brief Checks the available stack space to prevent stack overflow. + * + * @return parser_error_t Returns parser_running_out_of_stack if stack space is insufficient, otherwise parser_ok. + */ +parser_error_t checkStack() { +#if defined(LEDGER_SPECIFIC) + // NOLINTNEXTLINE(readability-identifier-length): here `p` is fine + void *p = NULL; + const uint32_t availableStack = (uint32_t)((void *)&p) + STACK_SHIFT - (uint32_t)&app_stack_canary; + if (availableStack <= MINIMUM_STACK) { + return parser_running_out_of_stack; + } +#else + if (recursionDepthCounter >= MAX_RECURSION_DEPTH) { + return parser_running_out_of_stack; + } + recursionDepthCounter++; +#endif + return parser_ok; +} + +/** + * @brief Frees the stack space by decrementing the recursion depth counter. + * @param depth The depth of the stack to free. + * + * @return parser_error_t Always returns parser_ok. + */ +parser_error_t freeStack(uint8_t depth) { +#if !defined(LEDGER_SPECIFIC) + if (recursionDepthCounter > 0) { + recursionDepthCounter -= depth; + } +#else + (void)depth; + void *p = NULL; + const uint32_t availableStack = (uint32_t)((void *)&p) - (uint32_t)&app_stack_canary; + if (availableStack <= MINIMUM_STACK) { + return parser_running_out_of_stack; + } +#endif + return parser_ok; +} diff --git a/app/src/stack_manager.h b/app/src/stack_manager.h new file mode 100644 index 0000000..647e0d7 --- /dev/null +++ b/app/src/stack_manager.h @@ -0,0 +1,44 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ + +#pragma once + +#include +#include + +#include "parser_common.h" +#include "zxmacros.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Checks the available stack space to prevent stack overflow. + * + * @return parser_error_t Returns parser_running_out_of_stack if stack space is insufficient, otherwise parser_ok. + */ +parser_error_t checkStack(); + +/** + * @brief Frees the stack space by decrementing the recursion depth counter. + * + * @return parser_error_t Always returns parser_ok. + */ +parser_error_t freeStack(uint8_t depth); + +#ifdef __cplusplus +} +#endif diff --git a/app/src/txdefs/common_txdef.h b/app/src/txdefs/common_txdef.h new file mode 100644 index 0000000..c924862 --- /dev/null +++ b/app/src/txdefs/common_txdef.h @@ -0,0 +1,101 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define GAS_DIMENSIONS 2 +#define TOKEN_ID_SIZE 32 +#define ADDRESS_SIZE 28 +#define DEFAULT_SAFE_VEC_LEN 20 +#define MAX_INPUT_CHUNK 256 + +typedef enum { + RUNTIME_CALL_BANK = 0, + RUNTIME_CALL_SEQUENCER_REGISTRY, + RUNTIME_CALL_VALUE_SETTER, + RUNTIME_CALL_ATTESTER_INCENTIVES, + RUNTIME_CALL_PROVER_INCENTIVES, + RUNTIME_CALL_ACCOUNTS, + RUNTIME_CALL_UNIQUENESS, + RUNTIME_CALL_CHAIN_STATE, + RUNTIME_CALL_BLOB_STORAGE, + RUNTIME_CALL_PAYMASTER, + RUNTIME_CALL_EVM, + RUNTIME_CALL_ACCESS_PATTERN, +} runtime_call_e; + +typedef enum { + ADDRESS_TYPE_STANDARD = 0, + ADDRESS_TYPE_VM, +} address_e; + +typedef struct { + uint64_t hi; + uint64_t lo; +} uint128_t; + +typedef struct { + const uint8_t *ptr; + uint16_t len; +} bytes_t; + +typedef struct { + bytes_t buffer; + uint16_t offset; +} parser_context_t; + +typedef struct { + uint64_t hi; + uint64_t lo; +} amount_t; + +typedef struct { + bytes_t token; +} token_id_t; + +typedef struct { + amount_t amount; + token_id_t token_id; +} coins_t; + +typedef struct { + uint64_t gas[GAS_DIMENSIONS]; +} gas_t; + +typedef struct { + amount_t gas[GAS_DIMENSIONS]; +} gas_u128_t; + +typedef struct { + address_e type; + bytes_t address; +} address_t; + +typedef struct { + uint32_t length; + address_t address[DEFAULT_SAFE_VEC_LEN]; +} address_list_t; + +#ifdef __cplusplus +} +#endif diff --git a/app/src/txdefs/merkle_txdef.h b/app/src/txdefs/merkle_txdef.h new file mode 100644 index 0000000..23230bf --- /dev/null +++ b/app/src/txdefs/merkle_txdef.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common_txdef.h" + +typedef struct { + uint32_t entries; + parser_context_t data; +} merkle_leaves_data_t; + +typedef struct { + uint32_t entries; + parser_context_t indices; +} merkle_leaves_indices_t; + +typedef struct { + uint32_t entries; + parser_context_t data; +} merkle_lemmas_t; + +typedef struct { + merkle_leaves_data_t leaves; // The list of the original data items at the bottom level of the Merkle tree + merkle_leaves_indices_t indices; // The list of indices of the leaves in the Merkle tree + merkle_lemmas_t lemmas; // The list of hashes needed to reconstruct the Merkle root from the leaf + uint64_t tree_size; // The size of the Merkle tree + bytes_t root_hash; // The hash of the Merkle root +} merkle_proof_t; + +#ifdef __cplusplus +} +#endif diff --git a/app/src/parser_txdef.h b/app/src/txdefs/parser_txdef.h similarity index 79% rename from app/src/parser_txdef.h rename to app/src/txdefs/parser_txdef.h index 06b49c1..8d02bc7 100644 --- a/app/src/parser_txdef.h +++ b/app/src/txdefs/parser_txdef.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,16 +19,14 @@ extern "C" { #endif -#include -#include +#include "schema_txdef.h" +#include "ui_txdef.h" typedef struct { - const uint8_t *ptr; - uint16_t len; -} bytes_t; - -typedef struct { - bytes_t tx_blind_signature; + schema_t schema; + merkle_proof_t merkle_proof; + parser_context_t unsigned_transaction_raw; + ui_items_new_t ui_items_new; } parser_tx_t; #ifdef __cplusplus diff --git a/app/src/txdefs/schema_txdef.h b/app/src/txdefs/schema_txdef.h new file mode 100644 index 0000000..9767c69 --- /dev/null +++ b/app/src/txdefs/schema_txdef.h @@ -0,0 +1,311 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common_txdef.h" +#include "crypto_helper.h" +#include "merkle_txdef.h" + +#define MAX_HRP_LEN 83 + +typedef enum { + ROLLUP_ROOTS_TRANSACTION = 0, + ROLLUP_ROOTS_UNSIGNED_TRANSACTION, + ROLLUP_ROOTS_RUNTIME_CALL, + ROLLUP_ROOTS_ADDRESS, +} rollup_roots_e; + +typedef enum { + LINKING_SCHEME_ENUM = 0, + LINKING_SCHEME_STRUCT, + LINKING_SCHEME_TUPLE, + LINKING_SCHEME_OPTION, + LINKING_SCHEME_INTEGER, + LINKING_SCHEME_BYTE_ARRAY, + LINKING_SCHEME_FLOAT32, + LINKING_SCHEME_FLOAT64, + LINKING_SCHEME_STRING, + LINKING_SCHEME_BOOLEAN, + LINKING_SCHEME_SKIP, + LINKING_SCHEME_BYTE_VEC, + LINKING_SCHEME_ARRAY, + LINKING_SCHEME_VEC, + LINKING_SCHEME_MAP, +} linking_scheme_e; + +typedef enum { + PRIMITIVE_INTEGER = 0, + PRIMITIVE_BYTE_ARRAY, + PRIMITIVE_BYTE_VEC, + PRIMITIVE_FLOAT32, + PRIMITIVE_FLOAT64, + PRIMITIVE_STRING, + PRIMITIVE_BOOLEAN, + PRIMITIVE_SKIP +} primitive_e; + +typedef enum { + BYTE_DISPLAY_HEX = 0, + BYTE_DISPLAY_DECIMAL, + BYTE_DISPLAY_BECH32, + BYTE_DISPLAY_BECH32M, + BYTE_DISPLAY_BASE58 +} byte_display_e; +typedef enum { + INTEGER_DISPLAY_HEX = 0, + INTEGER_DISPLAY_DECIMAL, + INTEGER_DISPLAY_FIXED_POINT, +} integer_display_e; + +typedef enum { + INTEGER_I8 = 0, + INTEGER_I16, + INTEGER_I32, + INTEGER_I64, + INTEGER_I128, + INTEGER_U8, + INTEGER_U16, + INTEGER_U32, + INTEGER_U64, + INTEGER_U128, +} integer_e; + +typedef enum { FIXED_POINT_DISPLAY_DECIMALS = 0, FIXED_POINT_DISPLAY_FROM_SIBLING_FIELD } fixed_point_display_e; + +typedef enum { LINK_BY_INDEX = 0, LINK_IMMEDIATE, LINK_PLACEHOLDER, LINK_INDEXED_PLACEHOLDER } link_e; + +typedef struct { + uint64_t field_index; + uint64_t byte_offset; +} fixed_point_display_from_sibling_field_t; + +typedef struct { + fixed_point_display_e type; + union { + uint8_t decimals; + fixed_point_display_from_sibling_field_t from_sibling_field; + }; +} fixed_point_display_t; + +typedef struct { + integer_display_e type; + union { + fixed_point_display_t fixed_point; + }; +} integer_display_t; + +typedef struct { + bytes_t prefix; +} hrp_t; + +typedef struct { + hrp_t prefix; +} byte_display_bech32_t; + +typedef struct { + hrp_t prefix; +} byte_display_bech32m_t; + +typedef struct { + bool has_title_elements; + uint64_t title_elements; + bool has_title_lines; + uint64_t title_lines; +} structured_display_overrides_t; + +typedef struct { + byte_display_e type; + union { + byte_display_bech32_t bech32; + byte_display_bech32m_t bech32m; + }; +} byte_display_t; + +typedef struct { + integer_e type; + integer_display_t display; +} primitive_integer_t; + +typedef struct { + uint64_t len; + byte_display_t display; + bool has_name_registry; + bytes_t name_registry; +} primitive_byte_array_t; + +typedef struct { + byte_display_t display; + bool has_name_registry; + bytes_t name_registry; +} primitive_byte_vec_t; + +typedef struct { + uint64_t len; +} primitive_skip_t; + +typedef struct { + primitive_e type; + union { + primitive_integer_t integer; + primitive_byte_array_t byte_array; + primitive_byte_vec_t byte_vec; + primitive_skip_t skip; + }; +} primitive_t; + +typedef struct { + link_e tag; + union { + uint64_t by_index; + primitive_t immediate; + size_t indexed_placeholder; + } data; +} link_t; + +typedef struct { + bytes_t display_name; + bool silent; + bool is_expert; + link_t value; + bytes_t doc; +} named_field_t; + +typedef struct { + link_t value; + bool silent; + bool is_expert; + bytes_t doc; +} unnamed_field_t; + +typedef struct { + bytes_t name; + uint8_t discriminant; + bool hide_tag; + bool has_value; + link_t value; +} enum_variant_t; + +typedef struct { + bytes_t type_name; + uint32_t variants_qty; + parser_context_t enum_variants; + bool hide_tag; + structured_display_overrides_t structured_display_overrides; +} schema_enum_t; + +typedef struct { + bytes_t type_name; + bool has_show_as; + bytes_t show_as; + bool has_structured_show_as; + bytes_t structured_show_as; + bool peekable; + uint32_t fields_qty; + parser_context_t named_fields; + structured_display_overrides_t structured_display_overrides; +} schema_struct_t; + +typedef struct { + bool has_show_as; + bytes_t show_as; + bool has_structured_show_as; + bytes_t structured_show_as; + bool peekable; + uint32_t fields_qty; + parser_context_t unnamed_fields; + structured_display_overrides_t structured_display_overrides; +} schema_tuple_t; + +typedef struct { + link_t value; +} schema_option_t; + +typedef struct { + uint64_t len; + link_t value; +} schema_array_t; + +typedef struct { + uint32_t len; + link_t value; +} schema_vec_t; + +typedef struct { + link_t key; + link_t value; +} schema_map_t; + +typedef struct { + linking_scheme_e type; + union { + schema_enum_t enum_type; + schema_struct_t struct_type; + schema_tuple_t tuple_type; + link_t option_type; + primitive_integer_t integer_type; + primitive_byte_array_t byte_array_type; + uint64_t skip_type; + byte_display_t byte_vec_type; + schema_array_t array_type; + link_t vec_type; + schema_map_t map_type; + }; +} linking_scheme_t; + +typedef struct { + uint32_t qty; + parser_context_t indices; + bytes_t complete_borsh_data; +} root_type_indices_t; + +typedef struct { + bytes_t data; + bytes_t name; +} registry_t; + +typedef struct { + bytes_t name; + uint32_t qty; + parser_context_t registry; +} name_registry_t; + +typedef struct { + uint32_t qty; + parser_context_t vec_registries; +} name_registries_t; + +typedef struct { + uint64_t chain_id; + bytes_t chain_name; + uint8_t gas_token_decimals; + name_registries_t name_registries; + bytes_t complete_borsh_data; +} chain_data_t; + +typedef struct { + root_type_indices_t root_type_indices; + chain_data_t chain_data; + bytes_t extra_metadata_hash; + bytes_t chain_hash; +} schema_t; + +#ifdef __cplusplus +} +#endif diff --git a/app/src/txdefs/ui_txdef.h b/app/src/txdefs/ui_txdef.h new file mode 100644 index 0000000..f96255e --- /dev/null +++ b/app/src/txdefs/ui_txdef.h @@ -0,0 +1,61 @@ +/******************************************************************************* + * (c) 2018 - 2025 Zondax AG + * + * 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. + ********************************************************************************/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common_txdef.h" +#include "schema_txdef.h" + +#define MAX_ITEMS 32 +#define MAX_STRING_LENGTH 100 +#define MAX_TITLE_LENGTH 60 +#define U128_STR_MAX_LEN 40 +#define MAX_SEPARATOR_LENGTH 100 +#define SEPARATOR_TITLE_OPEN "|" +#define SEPARATOR_TITLE_CLOSE "|" +#define SEPARATOR_DATA_OPEN "{" +#define SEPARATOR_DATA_CLOSE "}" +#define SEPARATOR_TITLE_DISPLAY "/" +#define PAGE_BREAK ">" +#define NAVIGATE_UP "navigating back up" +// Buffer to store item data for array or vector display +typedef struct { + bool initialized; + uint8_t qty; + char separator_open[MAX_SEPARATOR_LENGTH]; + uint8_t separator_open_len; + char separator_close[MAX_SEPARATOR_LENGTH]; + uint8_t separator_close_len; + char data[MAX_STRING_LENGTH]; +} item_buffer_t; + +typedef struct { + char title[MAX_STRING_LENGTH]; + primitive_t primitive; + parser_context_t data_context; +} ui_item_new_t; + +typedef struct { + uint16_t qty; + ui_item_new_t items[MAX_ITEMS]; +} ui_items_new_t; + +#ifdef __cplusplus +} +#endif diff --git a/deps/ledger-zxlib b/deps/ledger-zxlib index fcda7f0..49c3e7d 160000 --- a/deps/ledger-zxlib +++ b/deps/ledger-zxlib @@ -1 +1 @@ -Subproject commit fcda7f03e6a787c58ef2495028eec70fbf452fc7 +Subproject commit 49c3e7da66710d7eeb7d4244f98b6649945734c7 diff --git a/js b/js new file mode 160000 index 0000000..cb8736e --- /dev/null +++ b/js @@ -0,0 +1 @@ +Subproject commit cb8736e66502141a00ec8c39c67dffa8228d8cea diff --git a/tests/address_encoding.cpp b/tests/address_encoding.cpp index 0ffdf72..9df1215 100644 --- a/tests/address_encoding.cpp +++ b/tests/address_encoding.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2024 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/address_encoding.h b/tests/address_encoding.h index b5778ab..f52c8cc 100644 --- a/tests/address_encoding.h +++ b/tests/address_encoding.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2024 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/parser_impl.cpp b/tests/parser_impl.cpp index d5101ce..7252b35 100644 --- a/tests/parser_impl.cpp +++ b/tests/parser_impl.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,37 +26,259 @@ #include "gmock/gmock.h" #include "parser.h" #include "parser_txdef.h" +#include "schema_reader.h" using namespace std; -TEST(SCALE, ReadBytes) { - parser_context_t ctx; - parser_tx_t tx_obj; +TEST(SCALE, ReadMetadata) { + parser_context_t ctx = {0}; + parser_tx_t tx_obj = {0}; parser_error_t err; - uint8_t buffer[100]; - auto bufferLen = parseHexString(buffer, sizeof(buffer), - "45" - "123456" - "12345678901234567890"); - - parser_parse(&ctx, buffer, bufferLen, &tx_obj); - - // uint8_t bytesArray[100] = {0}; - // err = _readBytes(&ctx, bytesArray, 1); - // EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); - // EXPECT_EQ(bytesArray[0], 0x45); - - // uint8_t testArray[3] = {0x12, 0x34, 0x56}; - // err = _readBytes(&ctx, bytesArray+1, 3); - // EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); - // for (uint8_t i = 0; i < 3; i++) { - // EXPECT_EQ(testArray[i], bytesArray[i+1]); - // } - - // uint8_t testArray2[10] = {0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90}; - // err = _readBytes(&ctx, bytesArray+4, 10); - // EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); - // for (uint8_t i = 0; i < 10; i++) { - // EXPECT_EQ(testArray2[i], bytesArray[i+4]); - // } + uint8_t buffer[12000]; + auto bufferLen = parseHexString( + buffer, sizeof(buffer), + "6c000000010b0000005472616e73616374696f6e00000005000000090000007369676e617475726500000001000000000000000000000007000" + "0007075625f6b65790000000200000000000000000000000c00000072756e74696d655f63616c6c0000000300000000000000000000000a0000" + "0067656e65726174696f6e000001000801000000000700000064657461696c73000000690000000000000000000000000001100000004564323" + "53531395369676e617475726500000001000000070000006d73675f736967000001014000000000000000000000000000000001100000004564" + "32353531395075626c69634b657900000001000000070000007075625f6b65790000010120000000000000000000000000000000000b0000005" + "2756e74696d6543616c6c0c0000000400000042616e6b0000010004000000000000001100000053657175656e63657252656769737472790100" + "010016000000000000000b00000056616c7565536574746572020001001d00000000000000120000004174746573746572496e63656e7469766" + "5730300010025000000000000001000000050726f766572496e63656e7469766573040001002a00000000000000080000004163636f756e7473" + "050001002e000000000000000a000000556e697175656e6573730600010033000000000000000a000000436861696e537461746507000100350" + "00000000000000b000000426c6f6253746f72616765080001003600000000000000090000005061796d61737465720900010037000000000000" + "000300000045766d0a00010051000000000000000d0000004163636573735061747465726e0b000100540000000000000001000002000000010" + "000000005000000000000000000000000000000000b00000043616c6c4d657373616765050000000b000000437265617465546f6b656e000001" + "000600000000000000080000005472616e73666572010001001000000000000000040000004275726e020001001300000000000000040000004" + "d696e7403000100140000000000000006000000467265657a65040001001500000000000000000000012a0000005f5f536f765669727475616c" + "57616c6c65745f43616c6c4d6573736167655f437265617465546f6b656e000000060000000a000000746f6b656e5f6e616d650000010500000" + "0000e000000746f6b656e5f646563696d616c730000000700000000000000000000000f000000696e697469616c5f62616c616e636500000008" + "00000000000000000000000f0000006d696e745f746f5f616464726573730000000900000000000000000000000600000061646d696e7300000" + "00e00000000000000000000000a000000737570706c795f6361700000000f000000000000000000000000000301000501020000000100000001" + "0009010000000000000000000c0000004d756c74694164647265737302000000080000005374616e64617264000001000a00000000000000020" + "00000566d010001000c000000000000000100000200000001000000000b000000000000000000000000000000020000000100000001011c0000" + "00000000000303000000736f760000000000000000000200000001000000000d000000000000000000000000000000020000000100000001011" + "400000000000000000000000000000000000d0009000000000000000300080000000000000001270000005f5f536f765669727475616c57616c" + "6c65745f43616c6c4d6573736167655f5472616e73666572011a0000005472616e7366657220746f2061646472657373207b7d207b7d2e01070" + "000007c7b546f7d7b7d000200000002000000746f00000009000000000000000000000005000000636f696e7300000011000000000000000000" + "0000010100000000000000000105000000436f696e7301170000007b7d20636f696e73206f6620746f6b656e204944207b7d01140000007b416" + "d6f756e747c7d7b546f6b656e2049447c7d010200000006000000616d6f756e740000010009020101000000000000001f000000000000000000" + "000008000000746f6b656e5f696400000012000000000000000000000000000200000001000000010120000000000000000306000000746f6b6" + "56e5f0109000000746f6b656e5f696473000000000000000001230000005f5f536f765669727475616c57616c6c65745f43616c6c4d65737361" + "67655f4275726e0000000100000005000000636f696e73000000110000000000000000000000000001230000005f5f536f765669727475616c5" + "7616c6c65745f43616c6c4d6573736167655f4d696e740000000200000005000000636f696e730000001100000000000000000000000f000000" + "6d696e745f746f5f61646472657373000000090000000000000000000000000001250000005f5f536f765669727475616c57616c6c65745f436" + "16c6c4d6573736167655f467265657a650000000100000008000000746f6b656e5f696400000012000000000000000000000000000200000001" + "0000000017000000000000000000000000000000000b00000043616c6c4d6573736167650400000008000000526567697374657200000100180" + "0000000000000070000004465706f736974010001001a0000000000000012000000496e6974696174655769746864726177616c020001001b00" + "000000000000080000005769746864726177030001001c0000000000000000000001270000005f5f536f765669727475616c57616c6c65745f4" + "3616c6c4d6573736167655f5265676973746572000000020000000a00000064615f616464726573730000001900000000000000000000000600" + "0000616d6f756e74000000080000000000000000000000000002000000010000000101200000000000000000000000000000000000012600000" + "05f5f536f765669727475616c57616c6c65745f43616c6c4d6573736167655f4465706f736974000000020000000a00000064615f6164647265" + "737300000019000000000000000000000006000000616d6f756e74000000080000000000000000000000000001310000005f5f536f765669727" + "475616c57616c6c65745f43616c6c4d6573736167655f496e6974696174655769746864726177616c000000010000000a00000064615f616464" + "72657373000000190000000000000000000000000001270000005f5f536f765669727475616c57616c6c65745f43616c6c4d6573736167655f5" + "769746864726177000000010000000a00000064615f6164647265737300000019000000000000000000000000000200000001000000001e0000" + "00000000000000000000000000000b00000043616c6c4d657373616765030000000800000053657456616c7565000001001f000000000000000" + "d0000005365744d616e7956616c7565730100010023000000000000001700000041737365727456697369626c65536c6f744e756d6265720200" + "0100240000000000000000000001270000005f5f536f765669727475616c57616c6c65745f43616c6c4d6573736167655f53657456616c75650" + "00000020000000500000076616c7565000001000701000000000300000067617300000020000000000000000000000000000300210000000000" + "0000020000000100000000220000000000000000000000000000000c02000000000000000100080102000000010000000102000000000000000" + "0000001360000005f5f536f765669727475616c57616c6c65745f43616c6c4d6573736167655f41737365727456697369626c65536c6f744e75" + "6d626572000000010000001c00000065787065637465645f76697369626c655f736c6f745f6e756d62657200000100080100000000000002000" + "000010000000026000000000000000000000000000000000b00000043616c6c4d65737361676506000000100000005265676973746572417474" + "657374657200000100270000000000000011000000426567696e4578697441747465737465720100000c0000004578697441747465737465720" + "200001200000052656769737465724368616c6c656e6765720300010028000000000000000e000000457869744368616c6c656e676572040000" + "0f0000004465706f736974417474657374657205000100290000000000000000000002000000010000000008000000000000000000000000000" + "0000200000001000000000800000000000000000000000000000002000000010000000008000000000000000000000000000000020000000100" + "0000002b000000000000000000000000000000000b00000043616c6c4d65737361676503000000080000005265676973746572000001002c000" + "00000000000070000004465706f736974010001002d000000000000000400000045786974020000000000020000000100000000080000000000" + "00000000000000000000020000000100000000080000000000000000000000000000000200000001000000002f0000000000000000000000000" + "00000000b00000043616c6c4d6573736167650100000012000000496e7365727443726564656e7469616c496400000100300000000000000000" + "0000020000000100000000310000000000000000000000000000000200000001000000003200000000000000000000000000000002000000010" + "00000010120000000000000000000000000000000000002000000010000000034000000000000000000000000000000000f0000004e6f74496e" + "7374616e746961626c6500000000000000020000000100000000340000000000000000000000000000000200000001000000003400000000000" + "000000000000000000002000000010000000038000000000000000000000000000000000b00000043616c6c4d65737361676503000000110000" + "0052656769737465725061796d6173746572000001003900000000000000140000005365745061796572466f7253657175656e6365720100010" + "046000000000000000c000000557064617465506f6c69637902000100470000000000000000000001300000005f5f536f765669727475616c57" + "616c6c65745f43616c6c4d6573736167655f52656769737465725061796d61737465720000000100000006000000706f6c6963790000003a000" + "0000000000000000000010300000000000000010200000000000000011a0000005061796d6173746572506f6c696379496e697469616c697a65" + "72000000040000001400000064656661756c745f70617965655f706f6c6963790000003b0000000000000000000000060000007061796565730" + "0000041000000000000000000000013000000617574686f72697a65645f75706461746572730000000e00000000000000000000001500000061" + "7574686f72697a65645f73657175656e636572730000004300000000000000000000000000000b0000005061796565506f6c696379020000000" + "5000000416c6c6f77000001003c000000000000000400000044656e7901000000000001240000005f5f536f765669727475616c57616c6c6574" + "5f5061796565506f6c6963795f416c6c6f7700000004000000070000006d61785f6665650000000f00000000000000000000000900000067617" + "35f6c696d69740000002000000000000000000000000d0000006d61785f6761735f70726963650000003d000000000000000000000011000000" + "7472616e73616374696f6e5f6c696d6974000000400000000000000000000000000003003e00000000000000010800000047617350726963650" + "00000010000000500000076616c75650000003f000000000000000000000000000c020000000000000000080000000000000003010008010d00" + "42000000000000000200000002000000000900000000000000000000000000003b0000000000000000000000000000000014000000417574686" + "f72697a656453657175656e636572730200000003000000416c6c00000004000000536f6d650100010044000000000000000000000200000001" + "00000000450000000000000000000000000000000d00190000000000000001330000005f5f536f765669727475616c57616c6c65745f43616c6" + "c4d6573736167655f5365745061796572466f7253657175656e6365720000000100000005000000706179657200000009000000000000000000" + "00000000012b0000005f5f536f765669727475616c57616c6c65745f43616c6c4d6573736167655f557064617465506f6c69637900000002000" + "000050000007061796572000000090000000000000000000000060000007570646174650000004800000000000000000000000000010c000000" + "506f6c696379557064617465000000060000001000000073657175656e6365725f7570646174650000004900000000000000000000000f00000" + "075706461746572735f746f5f6164640000004e00000000000000000000001200000075706461746572735f746f5f72656d6f76650000004e00" + "000000000000000000001500000070617965655f706f6c69636965735f746f5f7365740000004f0000000000000000000000180000007061796" + "5655f706f6c69636965735f746f5f64656c6574650000004e00000000000000000000000e00000064656661756c745f706f6c69637900000050" + "0000000000000000000000000003004a00000000000000001200000053657175656e6365725365745570646174650200000008000000416c6c6" + "f77416c6c00000006000000557064617465010001004b000000000000000000000200000001000000004c000000000000000000000000000000" + "0116000000416c6c6f77656453657175656e6365725570646174650000000200000006000000746f5f6164640000004d0000000000000000000" + "00009000000746f5f72656d6f76650000004d000000000000000000000000000300450000000000000003000e00000000000000030041000000" + "0000000003003b0000000000000002000000010000000052000000000000000000000000000000010b00000043616c6c4d65737361676500000" + "00100000003000000726c7000000053000000000000000000000000000111000000526c7045766d5472616e73616374696f6e00000001000000" + "03000000726c7000000102000000000000000002000000010000000055000000000000000000000000000000001500000041636365737350617" + "47465726e4d657373616765730e0000000a000000577269746543656c6c730000010056000000000000000b0000005772697465437573746f6d" + "010001005700000000000000090000005265616443656c6c7302000100590000000000000009000000486173684279746573030001005a00000" + "0000000000a00000048617368437573746f6d040001005b000000000000000e00000053746f72655369676e6174757265050001005c00000000" + "0000000f0000005665726966795369676e617475726506000015000000566572696679437573746f6d5369676e6174757265070001005d00000" + "0000000001500000053746f726553657269616c697a6564537472696e67080001005e0000000000000018000000446573657269616c697a6542" + "797465734173537472696e6709000017000000446573657269616c697a65437573746f6d537472696e670a0001005f000000000000000b00000" + "044656c65746543656c6c730b000100600000000000000007000000536574486f6f6b0c00010061000000000000000b00000055706461746541" + "646d696e0d000100680000000000000000000001330000005f5f536f765669727475616c57616c6c65745f4163636573735061747465726e4d6" + "57373616765735f577269746543656c6c730000000300000005000000626567696e00000100080100000000090000006e756d5f63656c6c7300" + "00010008010000000009000000646174615f73697a6500000100070100000000000001340000005f5f536f765669727475616c57616c6c65745" + "f4163636573735061747465726e4d657373616765735f5772697465437573746f6d0000000200000005000000626567696e0000010008010000" + "000007000000636f6e74656e7400000058000000000000000000000000000d010501320000005f5f536f765669727475616c57616c6c65745f4" + "163636573735061747465726e4d657373616765735f5265616443656c6c730000000200000005000000626567696e0000010008010000000009" + "0000006e756d5f63656c6c7300000100080100000000000001320000005f5f536f765669727475616c57616c6c65745f4163636573735061747" + "465726e4d657373616765735f486173684279746573000000020000000600000066696c6c6572000001000501000000000400000073697a6500" + "000100070100000000000001330000005f5f536f765669727475616c57616c6c65745f4163636573735061747465726e4d657373616765735f4" + "8617368437573746f6d0000000100000005000000696e70757400000102000000000000000001370000005f5f536f765669727475616c57616c" + "6c65745f4163636573735061747465726e4d657373616765735f53746f72655369676e617475726500000003000000040000007369676e00000" + "0010000000000000000000000070000007075625f6b6579000000020000000000000000000000070000006d6573736167650000010500000000" + "0000013e0000005f5f536f765669727475616c57616c6c65745f4163636573735061747465726e4d657373616765735f5665726966794375737" + "46f6d5369676e617475726500000003000000040000007369676e000000010000000000000000000000070000007075625f6b65790000000200" + "00000000000000000000070000006d65737361676500000105000000000000013e0000005f5f536f765669727475616c57616c6c65745f41636" + "36573735061747465726e4d657373616765735f53746f726553657269616c697a6564537472696e670000000100000005000000696e70757400" + "000102000000000000000001400000005f5f536f765669727475616c57616c6c65745f4163636573735061747465726e4d657373616765735f4" + "46573657269616c697a65437573746f6d537472696e670000000100000005000000696e70757400000102000000000000000001340000005f5f" + "536f765669727475616c57616c6c65745f4163636573735061747465726e4d657373616765735f44656c65746543656c6c73000000020000000" + "5000000626567696e00000100080100000000090000006e756d5f63656c6c7300000100080100000000000001300000005f5f536f7656697274" + "75616c57616c6c65745f4163636573735061747465726e4d657373616765735f536574486f6f6b0000000200000003000000707265000000620" + "00000000000000000000004000000706f73740000006200000000000000000000000000030063000000000000000d006400000000000000000b" + "000000486f6f6b73436f6e666967030000000400000052656164000001006500000000000000050000005772697465010001006600000000000" + "0000600000044656c65746502000100670000000000000000000001230000005f5f536f765669727475616c57616c6c65745f486f6f6b73436f" + "6e6669675f526561640000000200000005000000626567696e000001000801000000000400000073697a6500000100080100000000000001240" + "000005f5f536f765669727475616c57616c6c65745f486f6f6b73436f6e6669675f57726974650000000300000005000000626567696e000001" + "000801000000000400000073697a650000010008010000000009000000646174615f73697a6500000100070100000000000001250000005f5f5" + "36f765669727475616c57616c6c65745f486f6f6b73436f6e6669675f44656c6574650000000200000005000000626567696e00000100080100" + "0000000400000073697a6500000100080100000000000001340000005f5f536f765669727475616c57616c6c65745f416363657373506174746" + "5726e4d657373616765735f55706461746541646d696e00000001000000090000006e65775f61646d696e000000090000000000000000000000" + "00000109000000547844657461696c730001370000007b4d6178205072696f72697479204665657c7d7c7b4d6178204665657c7d7c7b4761732" + "04c696d69747c7d7c7b436861696e2049447c7d0004000000150000006d61785f7072696f726974795f6665655f626970730001006a00000000" + "00000000000000070000006d61785f666565000000080000000000000000000000090000006761735f6c696d697400010020000000000000000" + "000000008000000636861696e5f696400010100080100000000000002000000010000000100080100000000000000000113000000556e736967" + "6e65645472616e73616374696f6e0001120000007b7d7c7b47656e65726174696f6e7d7c7b7d00030000000c00000072756e74696d655f63616" + "c6c0000000300000000000000000000000a00000067656e65726174696f6e000101000801000000000700000064657461696c73000000690000" + "00000000000000000000000400000000000000000000006b0000000000000003000000000000000b00000000000000e11000000000000009000" + "00054657374436861696e060100000009000000746f6b656e5f6964730100000020000000171717171717171717171717171717171717171717" + "17171717171717171717060900000054657374546f6b656e77f986b27d5c6e676fdb865f44833b5fe97f5cccafaf5b4733c21ba027f8b725"); + + ctx.buffer.ptr = buffer; + ctx.buffer.len = bufferLen; + err = metadata_read(&ctx, &tx_obj); + + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); +} + +TEST(SCALE, MultiProofTest) { + parser_context_t ctx = {0}; + parser_tx_t tx_obj = {0}; + parser_error_t err; + uint8_t buffer[12000]; + + // leave data + auto bufferLen = parseHexString( + buffer, sizeof(buffer), + "1000000059010000000b00000052756e74696d6543616c6c0c0000000400000042616e6b0000010004000000000000001100000053657175656" + "e63657252656769737472790100010016000000000000000b00000056616c7565536574746572020001001d0000000000000012000000417474" + "6573746572496e63656e74697665730300010025000000000000001000000050726f766572496e63656e7469766573040001002a00000000000" + "000080000004163636f756e7473050001002e000000000000000a000000556e697175656e6573730600010033000000000000000a0000004368" + "61696e53746174650700010035000000000000000b000000426c6f6253746f72616765080001003600000000000000090000005061796d61737" + "465720900010037000000000000000300000045766d0a00010051000000000000000d0000004163636573735061747465726e0b000100540000" + "0000000000010000190000000200000001000000000500000000000000000000000000000088000000000b00000043616c6c4d6573736167650" + "50000000b000000437265617465546f6b656e000001000600000000000000080000005472616e73666572010001001000000000000000040000" + "004275726e020001001300000000000000040000004d696e7403000100140000000000000006000000467265657a65040001001500000000000" + "00000000014000000020000000100000001000901000000000000000042000000000c0000004d756c7469416464726573730200000008000000" + "5374616e64617264000001000a0000000000000002000000566d010001000c00000000000000010000190000000200000001000000000b00000" + "000000000000000000000000023000000020000000100000001011c000000000000000303000000736f76000000000000000000930000000127" + "0000005f5f536f765669727475616c57616c6c65745f43616c6c4d6573736167655f5472616e73666572011a0000005472616e7366657220746" + "f2061646472657373207b7d207b7d2e01070000007c7b546f7d7b7d000200000002000000746f00000009000000000000000000000005000000" + "636f696e7300000011000000000000000000000001010000000000000000860000000105000000436f696e7301170000007b7d20636f696e732" + "06f6620746f6b656e204944207b7d01140000007b416d6f756e747c7d7b546f6b656e2049447c7d010200000006000000616d6f756e74000001" + "0009020101000000000000001f000000000000000000000008000000746f6b656e5f69640000001200000000000000000000000000330000000" + "200000001000000010120000000000000000306000000746f6b656e5f0109000000746f6b656e5f69647300000000000000000a000000030021" + "0000000000000019000000020000000100000000220000000000000000000000000000000d0000000c020000000000000001000801c60000000" + "109000000547844657461696c730001370000007b4d6178205072696f72697479204665657c7d7c7b4d6178204665657c7d7c7b476173204c69" + "6d69747c7d7c7b436861696e2049447c7d0004000000150000006d61785f7072696f726974795f6665655f626970730001006a0000000000000" + "000000000070000006d61785f666565000000080000000000000000000000090000006761735f6c696d69740001002000000000000000000000" + "0008000000636861696e5f696400010100080100000000000014000000020000000100000001000801000000000000000088000000011300000" + "0556e7369676e65645472616e73616374696f6e0001120000007b7d7c7b47656e65726174696f6e7d7c7b7d00030000000c00000072756e7469" + "6d655f63616c6c0000000300000000000000000000000a00000067656e65726174696f6e000101000801000000000700000064657461696c730" + "000006900000000000000000000000000"); + + // leaves_index + bufferLen += parseHexString(buffer + bufferLen, sizeof(buffer), + "10000000030000000000000004000000000000000500000000000000080000000000000009000000000000000a0" + "00000000000000b0000000000000010000000000000001100000000000000120000000000000020000000000000" + "002100000000000000220000000000000069000000000000006a000000000000006b00000000000000"); + + // lemmas + bufferLen += parseHexString( + buffer + bufferLen, sizeof(buffer), + "0e00000077acc7048b564d54cfb2cf87279320144ab1a3fd7d71c84c0912051d08ebf36bdf0ff86a776897f89216f36f60dc259efadc0fe9691" + "6b464bb0da54ccefd6c4a2c65338f3d4530a8df68c748139d75f43900873871b57da86e605a7ea2c195d2e02cecb6e76e86aa450de420490f1e" + "1c882e3cf23e9863df984884e4c48592302313aec76ea72f21d539d0ed27a1fa9431af9d270b3834b4a2d270e04495e06e339800d039914b263" + "0ac0e2ec7302447a84e7d27b0fa14c0afaf0beb979534159aa898b4dfa154a4bb5fd9a9cc75f9f8627d691fe4207dfd3621276e43f1d0e92c1a" + "96629574b47179a89ce11de505fc1f972b986eb3216ecdcbea928260316362f0db9c5a1c9552c49c574fd68ced31216afcf50ee00e4037b14f7" + "b815178825370d5c58a70ac1bac18a37025400bd9a5c1d8d635b9c02fe561324965c9b325fba7cdd8412a830cdb48bad5d88e1fcefc60d3fcbb" + "6cf8a95a20322e72b49e5284cd0a4c2cf32bc3a719948f3db013f6c0f33854cab1a02d61d6755bb541611faa801c77d4ab726b3400e77f090db" + "c1b39ea47027845b39f9e7dd5ad22fc78b5ed27560feb925637a3a176e3c9e1d357504d186d5fe6140abff97bcb464bcca2"); + + // tree_size + bufferLen += parseHexString(buffer + bufferLen, sizeof(buffer), "6c00000000000000"); + + // root_hash + bufferLen += parseHexString(buffer + bufferLen, sizeof(buffer), + "f55d28d6582a2e492b325f097e1ebcc0a19eca8f506fb5a7c3473067d48f06f8"); + + ctx.buffer.ptr = buffer; + ctx.buffer.len = bufferLen; + err = schema_merkle_proofs_read(&ctx, &tx_obj); + + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); + EXPECT_EQ(ctx.offset, ctx.buffer.len) << "Buffer not fully consumed"; + + // root_type_index + bufferLen += parseHexString(buffer + bufferLen, sizeof(buffer), + "0400000000000000000000006b0000000000000003000000000000000b00000000000000"); + + // chain_data + bufferLen += + parseHexString(buffer + bufferLen, sizeof(buffer), + "e1100000000000000900000054657374436861696e060100000009000000746f6b656e5f6964730100000020000000171717" + "17171717171717171717171717171717171717171717171717171717060900000054657374546f6b656e"); + + // extra_data_hash + bufferLen += parseHexString(buffer + bufferLen, sizeof(buffer), + "77f986b27d5c6e676fdb865f44833b5fe97f5cccafaf5b4733c21ba027f8b725"); + + ctx.buffer.len = bufferLen; + err = schema_extra_data_read(&ctx, &tx_obj); + + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); + EXPECT_EQ(ctx.offset, ctx.buffer.len) << "Buffer not fully consumed"; + + // chain_hash + bufferLen += parseHexString(buffer + bufferLen, sizeof(buffer), + "a43850ba5603f89c05080e14ff868fec09f35fbfaa3c28ad76258fbac95d7432"); + ctx.buffer.len = bufferLen; + err = schema_chain_hash_read(&ctx, &tx_obj); + + EXPECT_EQ(err, parser_ok) << parser_getErrorDescription(err); + uint8_t expected_hash[CX_SHA256_SIZE] = {0}; + parseHexString(expected_hash, sizeof(expected_hash), "a43850ba5603f89c05080e14ff868fec09f35fbfaa3c28ad76258fbac95d7432"); + + EXPECT_EQ(memcmp(tx_obj.schema.chain_hash.ptr, expected_hash, CX_SHA256_SIZE), 0) << "Chain hash mismatch"; } diff --git a/tests/ui_tests.cpp b/tests/ui_tests.cpp index c6f1802..7a9d446 100644 --- a/tests/ui_tests.cpp +++ b/tests/ui_tests.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,6 +124,6 @@ void check_testcase(const testcase_t &tc, bool expert_mode) { INSTANTIATE_TEST_SUITE_P - (JsonTestCasesCurrentTxVer, JsonTestsA, ::testing::ValuesIn(GetJsonTestCases("testcases.json")), + (DISABLED_JsonTestCasesCurrentTxVer, JsonTestsA, ::testing::ValuesIn(GetJsonTestCases("testcases.json")), JsonTestsA::PrintToStringParamName()); TEST_P(JsonTestsA, CheckUIOutput_CurrentTX_Expert) { check_testcase(GetParam(), true); } diff --git a/tests/utils/common.cpp b/tests/utils/common.cpp index 1822116..99f8e6f 100644 --- a/tests/utils/common.cpp +++ b/tests/utils/common.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests/utils/common.h b/tests/utils/common.h index 23f20f9..112ec42 100644 --- a/tests/utils/common.h +++ b/tests/utils/common.h @@ -1,5 +1,5 @@ /******************************************************************************* - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/tests_zemu/snapshots/fl-mainmenu/00004.png b/tests_zemu/snapshots/fl-mainmenu/00004.png index cc88462..a99a170 100644 Binary files a/tests_zemu/snapshots/fl-mainmenu/00004.png and b/tests_zemu/snapshots/fl-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/sp-mainmenu/00004.png b/tests_zemu/snapshots/sp-mainmenu/00004.png index e92a04b..bf5a4cb 100644 Binary files a/tests_zemu/snapshots/sp-mainmenu/00004.png and b/tests_zemu/snapshots/sp-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/sp-mainmenu/00010.png b/tests_zemu/snapshots/sp-mainmenu/00010.png index e92a04b..bf5a4cb 100644 Binary files a/tests_zemu/snapshots/sp-mainmenu/00010.png and b/tests_zemu/snapshots/sp-mainmenu/00010.png differ diff --git a/tests_zemu/snapshots/st-mainmenu/00004.png b/tests_zemu/snapshots/st-mainmenu/00004.png index 1ae07d6..24e4793 100644 Binary files a/tests_zemu/snapshots/st-mainmenu/00004.png and b/tests_zemu/snapshots/st-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/x-mainmenu/00004.png b/tests_zemu/snapshots/x-mainmenu/00004.png index e92a04b..bf5a4cb 100644 Binary files a/tests_zemu/snapshots/x-mainmenu/00004.png and b/tests_zemu/snapshots/x-mainmenu/00004.png differ diff --git a/tests_zemu/snapshots/x-mainmenu/00010.png b/tests_zemu/snapshots/x-mainmenu/00010.png index e92a04b..bf5a4cb 100644 Binary files a/tests_zemu/snapshots/x-mainmenu/00010.png and b/tests_zemu/snapshots/x-mainmenu/00010.png differ diff --git a/tests_zemu/tests/standard.test.ts b/tests_zemu/tests/standard.test.ts index fe7bc3d..be897d3 100644 --- a/tests_zemu/tests/standard.test.ts +++ b/tests_zemu/tests/standard.test.ts @@ -1,5 +1,5 @@ /** ****************************************************************************** - * (c) 2018 - 2023 Zondax AG + * (c) 2018 - 2025 Zondax AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -138,31 +138,31 @@ describe('Standard', function () { }) // TODO: remove this test case - test.concurrent.each(models)('blind sign', async function (m) { - const sim = new Zemu(m.path) - try { - await sim.start({ ...defaultOptions, model: m.name }) - const app = new SovereignApp(sim.getTransport()) - - const txBlob = Buffer.from(txBlobExample, 'hex') - const responseAddr = await app.getAddressAndPubKey(PATH, false) - const pubKey = responseAddr.pubkey - - // do not wait here.. we need to navigate - const signatureRequest = app.sign(PATH, txBlob) - - // Wait until we are not in the main menu - await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()) - await sim.compareSnapshotsAndApprove('.', `${m.prefix.toLowerCase()}-blind_sign`) - - const signatureResponse = await signatureRequest - console.log('Signature:', signatureResponse.signature.toString('hex')) - - // Now verify the signature - const valid = ed25519.verify(signatureResponse.signature, txBlob, pubKey) - expect(valid).toEqual(true) - } finally { - await sim.close() - } - }) + // test.concurrent.each(models)('blind sign', async function (m) { + // const sim = new Zemu(m.path) + // try { + // await sim.start({ ...defaultOptions, model: m.name }) + // const app = new SovereignApp(sim.getTransport()) + + // const txBlob = Buffer.from(txBlobExample, 'hex') + // const responseAddr = await app.getAddressAndPubKey(PATH, false) + // const pubKey = responseAddr.pubkey + + // // do not wait here.. we need to navigate + // const signatureRequest = app.sign(PATH, txBlob) + + // // Wait until we are not in the main menu + // await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot()) + // await sim.compareSnapshotsAndApprove('.', `${m.prefix.toLowerCase()}-blind_sign`) + + // const signatureResponse = await signatureRequest + // console.log('Signature:', signatureResponse.signature.toString('hex')) + + // // Now verify the signature + // const valid = ed25519.verify(signatureResponse.signature, txBlob, pubKey) + // expect(valid).toEqual(true) + // } finally { + // await sim.close() + // } + // }) })