From 9501be70e9a476c9bed541bb7c597dc921608ee4 Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Tue, 25 Nov 2025 14:49:58 +0100 Subject: [PATCH 1/2] Cleanup sources - Remove useless header file - Fix bit-field init - Fix parameters casting --- src/features/signMessageEIP712/filtering.c | 4 ++-- src/features/signMessageEIP712/ui_logic.c | 6 +++--- src/main.c | 1 - src/nbgl/ui_approve_tx.c | 2 +- src/nbgl/ui_gcs.c | 6 +++--- src/nbgl/ui_nbgl.h | 1 - src/nbgl/ui_safe_account.c | 2 +- src/nbgl/ui_tx_simulation.c | 1 - 8 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/features/signMessageEIP712/filtering.c b/src/features/signMessageEIP712/filtering.c index cf7f9cece..4eba820a8 100644 --- a/src/features/signMessageEIP712/filtering.c +++ b/src/features/signMessageEIP712/filtering.c @@ -950,8 +950,8 @@ bool filtering_trusted_name(const uint8_t *payload, } hash_filtering_path((cx_hash_t *) &hash_ctx, discarded, path_crc); hash_nbytes((uint8_t *) name, sizeof(char) * name_len, (cx_hash_t *) &hash_ctx); - hash_nbytes(types, type_count, (cx_hash_t *) &hash_ctx); - hash_nbytes(sources, source_count, (cx_hash_t *) &hash_ctx); + hash_nbytes((uint8_t *) types, type_count, (cx_hash_t *) &hash_ctx); + hash_nbytes((uint8_t *) sources, source_count, (cx_hash_t *) &hash_ctx); if (!sig_verif_end(&hash_ctx, sig, sig_len)) { return false; } diff --git a/src/features/signMessageEIP712/ui_logic.c b/src/features/signMessageEIP712/ui_logic.c index 137b722b3..49252dde6 100644 --- a/src/features/signMessageEIP712/ui_logic.c +++ b/src/features/signMessageEIP712/ui_logic.c @@ -1297,7 +1297,7 @@ void ui_712_push_pairs(void) { tx_idx++; // Replace "nn of mm" placeholder, initialized in ui_712_set_intent() snprintf(tmp->value, N_OF_M_LENGTH, "%d of %d", tx_idx, txContext.batch_nb_tx); - g_pairs[pair].centeredInfo = 1; + g_pairs[pair].centeredInfo = true; } g_pairs[pair].item = tmp->key; g_pairs[pair].value = tmp->value; @@ -1308,7 +1308,7 @@ void ui_712_push_pairs(void) { pair++; if ((tmp->end_intent) && (txContext.batch_nb_tx > 1)) { // End of batch transaction : start next info on full page - g_pairs[pair].forcePageStart = 1; + g_pairs[pair].forcePageStart = true; } tmp = (s_ui_712_pair *) ((s_flist_node *) tmp)->next; } @@ -1320,7 +1320,7 @@ void ui_712_push_pairs(void) { } // Prepare the pairs list with the hashes eip712_format_hash(pair); - g_pairs[pair].forcePageStart = 1; + g_pairs[pair].forcePageStart = true; } } diff --git a/src/main.c b/src/main.c index 337ef9e90..0cd078f9c 100644 --- a/src/main.c +++ b/src/main.c @@ -23,7 +23,6 @@ #include "io.h" #include "parser.h" -#include "glyphs.h" #include "common_utils.h" #include "eth_swap_utils.h" diff --git a/src/nbgl/ui_approve_tx.c b/src/nbgl/ui_approve_tx.c index a49f27fe1..bfeab2ddb 100644 --- a/src/nbgl/ui_approve_tx.c +++ b/src/nbgl/ui_approve_tx.c @@ -171,7 +171,7 @@ static void setTagValuePairs(bool displayNetwork, bool fromPlugin) { extension->fullValue = strings.common.toAddress; extension->explanation = strings.common.toAddress; g_pairs[nbPairs].extension = extension; - g_pairs[nbPairs].aliasValue = 1; + g_pairs[nbPairs].aliasValue = true; } else { g_pairs[nbPairs].value = strings.common.toAddress; } diff --git a/src/nbgl/ui_gcs.c b/src/nbgl/ui_gcs.c index dc7f6462c..2bf103b0a 100644 --- a/src/nbgl/ui_gcs.c +++ b/src/nbgl/ui_gcs.c @@ -338,7 +338,7 @@ bool ui_gcs(void) { } else { ext->backText = app_mem_strdup(ext->backText); } - g_pairs[pair].aliasValue = 1; + g_pairs[pair].aliasValue = true; pair++; // TX fields @@ -354,7 +354,7 @@ bool ui_gcs(void) { g_pairs[pair].item = app_mem_strdup("Review transaction"); g_pairs[pair].value = app_mem_strdup(tmp_buf); index_allocated[pair] = true; - g_pairs[pair].centeredInfo = 1; + g_pairs[pair].centeredInfo = true; pair++; } g_pairs[pair].item = field->key; @@ -362,7 +362,7 @@ bool ui_gcs(void) { pair++; if ((field->end_intent) && (txContext.batch_nb_tx > 1)) { // End of batch transaction : start next info on full page - g_pairs[pair].forcePageStart = 1; + g_pairs[pair].forcePageStart = true; } } diff --git a/src/nbgl/ui_nbgl.h b/src/nbgl/ui_nbgl.h index 5789c22e4..1e995a9f4 100644 --- a/src/nbgl/ui_nbgl.h +++ b/src/nbgl/ui_nbgl.h @@ -2,7 +2,6 @@ #include "nbgl_use_case.h" #include "shared_context.h" -#include "glyphs.h" #ifdef SCREEN_SIZE_WALLET #define ICON_APP_WARNING LARGE_WARNING_ICON diff --git a/src/nbgl/ui_safe_account.c b/src/nbgl/ui_safe_account.c index d2792a01e..404570b7d 100644 --- a/src/nbgl/ui_safe_account.c +++ b/src/nbgl/ui_safe_account.c @@ -152,7 +152,7 @@ static void setTagValuePairs(void) { nbPairs++; g_pairs[nbPairs].item = "Threshold"; g_pairs[nbPairs].value = strings.tmp.tmp + ADDRESS_LENGTH_STR; - g_pairs[nbPairs].aliasValue = 1; + g_pairs[nbPairs].aliasValue = true; g_pairs[nbPairs].extension = &extensions[1]; extensions[1].aliasType = TAG_VALUE_LIST_ALIAS; extensions[1].title = ""; diff --git a/src/nbgl/ui_tx_simulation.c b/src/nbgl/ui_tx_simulation.c index 1f1e9a6a5..355b7ea3c 100644 --- a/src/nbgl/ui_tx_simulation.c +++ b/src/nbgl/ui_tx_simulation.c @@ -1,7 +1,6 @@ #include "apdu_constants.h" #include "utils.h" #include "nbgl_use_case.h" -#include "glyphs.h" #include "ui_nbgl.h" #include "ui_callbacks.h" #include "ui_callbacks.h" From 429675a74316495abad288210822bef43cc6f33f Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Tue, 25 Nov 2025 14:47:47 +0100 Subject: [PATCH 2/2] Migrate to Generic Fuzzer --- .clusterfuzzlite/Dockerfile | 26 +- .clusterfuzzlite/build.sh | 23 +- .gitignore | 2 + tests/fuzzing/CMakeLists.txt | 298 +++++----------- tests/fuzzing/README.md | 104 ++++-- tests/fuzzing/harness/fuzz_calldata.c | 49 +++ tests/fuzzing/harness/fuzz_eip712.c | 56 +++ tests/fuzzing/harness/fuzz_eip7702.c | 28 ++ tests/fuzzing/harness/fuzz_generic_parser.c | 63 ++++ tests/fuzzing/harness/fuzz_networks.c | 30 ++ tests/fuzzing/harness/fuzz_nft.c | 16 + tests/fuzzing/harness/fuzz_proxy.c | 16 + tests/fuzzing/harness/fuzz_safe.c | 42 +++ tests/fuzzing/harness/fuzz_trusted_names.c | 27 ++ tests/fuzzing/harness/fuzz_tx_check.c | 22 ++ tests/fuzzing/local_run.sh | 43 --- tests/fuzzing/macros/add_macros.txt | 2 + tests/fuzzing/macros/exclude_macros.txt | 3 + tests/fuzzing/mock/mock.c | 63 ++++ tests/fuzzing/mock/net_icons.gen.h | 18 + tests/fuzzing/src/fuzz_utils.c | 62 ++++ tests/fuzzing/src/fuzz_utils.h | 11 + tests/fuzzing/src/fuzzer.c | 327 ------------------ tests/fuzzing/src/glyphs.h | 0 tests/fuzzing/src/mock.c | 355 -------------------- 25 files changed, 702 insertions(+), 984 deletions(-) create mode 100644 tests/fuzzing/harness/fuzz_calldata.c create mode 100644 tests/fuzzing/harness/fuzz_eip712.c create mode 100644 tests/fuzzing/harness/fuzz_eip7702.c create mode 100644 tests/fuzzing/harness/fuzz_generic_parser.c create mode 100644 tests/fuzzing/harness/fuzz_networks.c create mode 100644 tests/fuzzing/harness/fuzz_nft.c create mode 100644 tests/fuzzing/harness/fuzz_proxy.c create mode 100644 tests/fuzzing/harness/fuzz_safe.c create mode 100644 tests/fuzzing/harness/fuzz_trusted_names.c create mode 100644 tests/fuzzing/harness/fuzz_tx_check.c delete mode 100755 tests/fuzzing/local_run.sh create mode 100644 tests/fuzzing/macros/add_macros.txt create mode 100644 tests/fuzzing/macros/exclude_macros.txt create mode 100644 tests/fuzzing/mock/mock.c create mode 100644 tests/fuzzing/mock/net_icons.gen.h create mode 100644 tests/fuzzing/src/fuzz_utils.c create mode 100644 tests/fuzzing/src/fuzz_utils.h delete mode 100644 tests/fuzzing/src/fuzzer.c delete mode 100644 tests/fuzzing/src/glyphs.h delete mode 100644 tests/fuzzing/src/mock.c diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 32efe99a5..6e8a89c8b 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,17 +1,29 @@ -FROM ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest AS LITE_BUILDER +FROM ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest AS app-builder # Base image with clang toolchain FROM gcr.io/oss-fuzz-base/base-builder:v1 -# Copy the project's source code. -COPY . $SRC/app-ethereum -COPY --from=LITE_BUILDER /opt/ledger-secure-sdk $SRC/app-ethereum/BOLOS_SDK +# Install additional package dependencies. +RUN pip3 install --break-system-packages --no-cache-dir pillow>=3.4.0 +RUN apt update && apt install -y ninja-build zip libbsd-dev pkg-config -# Add the ethereum-plugin-sdk submodule -RUN git clone https://github.com/LedgerHQ/ethereum-plugin-sdk.git $SRC/app-ethereum/ethereum-plugin-sdk +# Copy the project's source code. +COPY . /app +COPY --from=app-builder /opt/flex-secure-sdk /ledger-secure-sdk # Working directory for build.sh -WORKDIR $SRC/app-ethereum +WORKDIR /app + +# Add the ethereum-plugin-sdk submodule (clone only if not exists) +RUN if [ ! -d "/app/ethereum-plugin-sdk/.git" ]; then \ + git config --global url."https://github.com/".insteadOf git@github.com: && \ + git config --global url."https://".insteadOf git:// && \ + (git submodule update --init --recursive || \ + git clone https://github.com/LedgerHQ/ethereum-plugin-sdk.git /app/ethereum-plugin-sdk); \ + else \ + echo "ethereum-plugin-sdk already exists, skipping clone"; \ + fi + # Copy build.sh into $SRC dir. COPY ./.clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 6fc75a929..5f9f7bf6b 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -1,9 +1,26 @@ #!/bin/bash -eu -# build fuzzers +export BOLOS_SDK=/ledger-secure-sdk +# build fuzzers using the docker images. pushd tests/fuzzing -cmake -DBOLOS_SDK=$(pwd)/../../BOLOS_SDK -B build -S . +cmake -S . -B build -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \ + -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=On \ + -DBOLOS_SDK="${BOLOS_SDK}" -DTARGET=flex \ + -DAPP_BUILD_PATH=/app + +# Generates .zip for initial corpus in clusterFuzz +for dir in harness/*; do + if [ -d "${dir}" ]; then + fuzzer_name=$(basename "${dir}") + zip_name="${fuzzer_name}_seed_corpus.zip" + echo "Zipping corpus from ${dir} into ${zip_name}" + + (cd "${dir}" && zip -q -r "${zip_name}" .) + + mv "${dir}/${zip_name}" "${OUT}" + fi +done cmake --build build -mv ./build/fuzzer "${OUT}" +mv ./build/fuzz_* "${OUT}" popd diff --git a/.gitignore b/.gitignore index 87e05487f..cbb8ccb8a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ __version__.py tests/fuzzing/corpus/ tests/fuzzing/out/ tests/fuzzing/CMakeFiles/ +tests/fuzzing/macros/generated/ + default.profraw default.profdata fuzz-*.log diff --git a/tests/fuzzing/CMakeLists.txt b/tests/fuzzing/CMakeLists.txt index 1da617bf3..da5e363df 100644 --- a/tests/fuzzing/CMakeLists.txt +++ b/tests/fuzzing/CMakeLists.txt @@ -1,239 +1,101 @@ cmake_minimum_required(VERSION 3.14) +if(${CMAKE_VERSION} VERSION_LESS 3.14) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +endif() + # project information project(EthereumAppFuzzer VERSION 1.0 - DESCRIPTION "Eth Fuzzer" + DESCRIPTION "App Ethereum Fuzzer" LANGUAGES C) -if (NOT CMAKE_C_COMPILER_ID MATCHES "Clang") - message(FATAL_ERROR "Fuzzer needs to be built with Clang") +if(NOT DEFINED BOLOS_SDK) + message(FATAL_ERROR "BOLOS_SDK must be defined, CMake will exit.") + return() endif() -# guard against bad build-type strings -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") -endif() +add_subdirectory(${BOLOS_SDK}/fuzzing ${CMAKE_CURRENT_BINARY_DIR}/ledger-secure-sdk EXCLUDE_FROM_ALL) -# default fuzz device target -if (NOT TARGET_DEVICE) - set(TARGET_DEVICE "flex") -endif() +set(DEFINES FUZZ) -if (NOT DEFINED BOLOS_SDK) - set(BOLOS_SDK /opt/${TARGET_DEVICE}-secure-sdk) -endif() +set(APP_SRC ${CMAKE_SOURCE_DIR}/../../src) +set(PLUGIN_SRC ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src) + +file(GLOB_RECURSE C_SOURCES + ${APP_SRC}/*.c + ${PLUGIN_SRC}/*.c + ${CMAKE_SOURCE_DIR}/mock/*.c + ${CMAKE_SOURCE_DIR}/src/fuzz_utils.c +) +list(REMOVE_ITEM C_SOURCES + ${APP_SRC}/main.c + ${PLUGIN_SRC}/main.c +) + +add_library(code_lib ${C_SOURCES}) + +target_include_directories( + code_lib + PUBLIC ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/mock + ${APP_SRC} + ${APP_SRC}/features/generic_tx_parser + ${APP_SRC}/features/getPublicKey + ${APP_SRC}/features/provide_enum_value + ${APP_SRC}/features/provide_network_info + ${APP_SRC}/features/signTx + ${APP_SRC}/features/provide_trusted_name + ${APP_SRC}/features/getChallenge + ${APP_SRC}/features/signMessageEIP712 + ${APP_SRC}/features/provide_proxy_info + ${APP_SRC}/features/provide_tx_simulation + ${APP_SRC}/features/signAuthorizationEIP7702 + ${APP_SRC}/features/provide_safe_account + ${APP_SRC}/features/signMessageEIP712_common + ${APP_SRC}/features/signMessageEIP712 + ${APP_SRC}/features/setPlugin + ${APP_SRC}/nbgl + ${PLUGIN_SRC} +) + +target_link_libraries(code_lib PUBLIC secure_sdk) +target_compile_definitions(code_lib PUBLIC ${DEFINES} FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1) + +# Find and add libbsd +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBBSD REQUIRED libbsd) -# some flags to mimic the embedded build (such as packed enums) -set(CUSTOM_C_FLAGS -fdata-sections -ffunction-sections -funsigned-char -fshort-enums) - -# compatible with ClusterFuzzLite -if (NOT DEFINED ENV{LIB_FUZZING_ENGINE}) - set(COMPILATION_FLAGS ${CUSTOM_C_FLAGS} -g -O0 -Wall -Wextra -fprofile-instr-generate -fcoverage-mapping) - if (SANITIZER MATCHES "address") - set(COMPILATION_FLAGS ${COMPILATION_FLAGS} -fsanitize=fuzzer,address,undefined) - elseif (SANITIZER MATCHES "memory") - set(COMPILATION_FLAGS ${COMPILATION_FLAGS} -fsanitize=fuzzer,memory,undefined -fsanitize-memory-track-origins -fsanitize=fuzzer-no-link) - else() - message(FATAL_ERROR "Unknown sanitizer type. It must be set to `address` or `memory`.") - endif() +# Try to find the static libbsd library, because the final run image doesn't have dynamic library +# Keep both options to fallback to dynamic if static is not found +find_library(LIBBSD_STATIC_LIB lib${LIBBSD_STATIC_LIBRARIES}.a HINTS ${LIBBSD_LIBRARY_DIRS}) + +if(LIBBSD_STATIC_LIB) + message(STATUS "Using static libbsd") + target_link_libraries(code_lib PUBLIC ${LIBBSD_STATIC_LIB}) else() - set(COMPILATION_FLAGS "$ENV{LIB_FUZZING_ENGINE} $ENV{CFLAGS} ${CUSTOM_C_FLAGS}") - separate_arguments(COMPILATION_FLAGS) + message(STATUS "Using dynamic libbsd") + target_link_libraries(code_lib PUBLIC ${LIBBSD_LIBRARIES}) endif() -# guard against in-source builds -if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) - message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ") -endif() +target_include_directories(code_lib PUBLIC ${LIBBSD_INCLUDE_DIRS}) +target_compile_options(code_lib PUBLIC ${LIBBSD_CFLAGS_OTHER}) -set(DEFINES - gcc - APPNAME=\"Fuzzing\" - API_LEVEL=21 - TARGET=\"flex\" - TARGET_NAME=\"TARGET_FUZZ\" - APPVERSION=\"1.1.0\" - SDK_NAME=\"ledger-secure-sdk\" - SDK_VERSION=\"v21.3.3\" - SDK_HASH=\"d88d4db3c93665f52b5b1f45099d9d36dfaa06ba\" - gcc - __IO=volatile - NDEBUG - HAVE_BAGL_FONT_INTER_REGULAR_28PX - HAVE_BAGL_FONT_INTER_SEMIBOLD_28PX - HAVE_BAGL_FONT_INTER_MEDIUM_36PX - HAVE_INAPP_BLE_PAIRING - HAVE_NBGL - HAVE_PIEZO_SOUND - HAVE_SE_TOUCH - HAVE_SE_EINK_DISPLAY - NBGL_PAGE - NBGL_USE_CASE - SCREEN_SIZE_WALLET - HAVE_FAST_HOLD_TO_APPROVE - HAVE_LEDGER_PKI - HAVE_NES_CRYPT - HAVE_ST_AES - NATIVE_LITTLE_ENDIAN - HAVE_CRC - HAVE_HASH - HAVE_RIPEMD160 - HAVE_SHA224 - HAVE_SHA256 - HAVE_SHA3 - HAVE_SHA384 - HAVE_SHA512 - HAVE_SHA512_WITH_BLOCK_ALT_METHOD - HAVE_SHA512_WITH_BLOCK_ALT_METHOD_M0 - HAVE_BLAKE2 - HAVE_HMAC - HAVE_PBKDF2 - HAVE_AES - HAVE_MATH - HAVE_RNG - HAVE_RNG_RFC6979 - HAVE_RNG_SP800_90A - HAVE_ECC - HAVE_ECC_WEIERSTRASS - HAVE_ECC_TWISTED_EDWARDS - HAVE_ECC_MONTGOMERY - HAVE_SECP256K1_CURVE - HAVE_SECP256R1_CURVE - HAVE_SECP384R1_CURVE - HAVE_SECP521R1_CURVE - HAVE_FR256V1_CURVE - HAVE_STARK256_CURVE - HAVE_BRAINPOOL_P256R1_CURVE - HAVE_BRAINPOOL_P256T1_CURVE - HAVE_BRAINPOOL_P320R1_CURVE - HAVE_BRAINPOOL_P320T1_CURVE - HAVE_BRAINPOOL_P384R1_CURVE - HAVE_BRAINPOOL_P384T1_CURVE - HAVE_BRAINPOOL_P512R1_CURVE - HAVE_BRAINPOOL_P512T1_CURVE - HAVE_BLS12_381_G1_CURVE - HAVE_CV25519_CURVE - HAVE_CV448_CURVE - HAVE_ED25519_CURVE - HAVE_ED448_CURVE - HAVE_ECDH - HAVE_ECDSA - HAVE_EDDSA - HAVE_ECSCHNORR - HAVE_X25519 - HAVE_X448 - HAVE_AES_GCM - HAVE_CMAC - HAVE_AES_SIV - COIN_VARIANT=1 - HAVE_BOLOS_APP_STACK_CANARY - IO_SEPROXYHAL_BUFFER_SIZE_B=300 - HAVE_BLE - BLE_COMMAND_TIMEOUT_MS=2000 - HAVE_BLE_APDU - BLE_SEGMENT_SIZE=32 - HAVE_DEBUG_THROWS - NBGL_QRCODE - MAJOR_VERSION=1 - MINOR_VERSION=1 - PATCH_VERSION=0 - IO_HID_EP_LENGTH=64 - HAVE_SPRINTF - HAVE_SNPRINTF_FORMAT_U - HAVE_IO_USB - HAVE_L4_USBLIB - IO_USB_MAX_ENDPOINTS=4 - HAVE_USB_APDU - USB_SEGMENT_SIZE=64 - HAVE_WEBUSB - WEBUSB_URL_SIZE_B=0 - WEBUSB_URL= - OS_IO_SEPROXYHAL - OS_IO_SEPH_BUFFER_SIZE=259 - STANDARD_APP_SYNC_RAPDU - HAVE_SWAP - HAVE_TRANSACTION_CHECKS - HAVE_SAFE_ACCOUNT - explicit_bzero=bzero # Fix for https://github.com/google/sanitizers/issues/1507 -) +# Create the different fuzzing targets automatically +file(GLOB FUZZ_HARNESSES "${CMAKE_SOURCE_DIR}/harness/fuzz_*.c") -add_compile_definitions(${DEFINES}) +foreach(HARNESS_FILE ${FUZZ_HARNESSES}) + # Extract the base name of the file without extension + get_filename_component(HARNESS_NAME ${HARNESS_FILE} NAME_WE) -FILE( - GLOB_RECURSE SDK_STD_SOURCES - ${BOLOS_SDK}/lib_standard_app/*.c - ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src/*.c - ./src/mock.c -) -list( - REMOVE_ITEM SDK_STD_SOURCES - ${BOLOS_SDK}/lib_standard_app/io.c - ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src/main.c - ${BOLOS_SDK}/lib_standard_app/main.c - ${BOLOS_SDK}/lib_standard_app/crypto_helpers.c -) + # Create the executable target + add_executable(${HARNESS_NAME} ${HARNESS_FILE}) -include_directories( - ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src/ - ${CMAKE_SOURCE_DIR}/../../src - ${CMAKE_SOURCE_DIR}/../../src/features/generic_tx_parser/ - ${CMAKE_SOURCE_DIR}/../../src/features/getPublicKey/ - ${CMAKE_SOURCE_DIR}/../../src/features/provide_enum_value/ - ${CMAKE_SOURCE_DIR}/../../src/features/provide_network_info/ - ${CMAKE_SOURCE_DIR}/../../src/features/signTx/ - ${CMAKE_SOURCE_DIR}/../../src/features/provide_trusted_name/ - ${CMAKE_SOURCE_DIR}/../../src/features/getChallenge/ - ${CMAKE_SOURCE_DIR}/../../src/features/signMessageEIP712/ - ${CMAKE_SOURCE_DIR}/../../src/features/provide_proxy_info/ - ${CMAKE_SOURCE_DIR}/../../src/features/provide_tx_simulation/ - ${CMAKE_SOURCE_DIR}/../../src/features/signAuthorizationEIP7702/ - ${CMAKE_SOURCE_DIR}/../../src/features/provide_safe_account/ - ${CMAKE_SOURCE_DIR}/../../src/features/signMessageEIP712_common/ - ${CMAKE_SOURCE_DIR}/../../src/features/signMessageEIP712/ - ${CMAKE_SOURCE_DIR}/../../src/nbgl/ - ${BOLOS_SDK}/include - ${BOLOS_SDK}/target/${TARGET_DEVICE}/include - ${BOLOS_SDK}/io/include - ${BOLOS_SDK}/io_legacy/include - ${BOLOS_SDK}/lib_cxng/include - ${BOLOS_SDK}/lib_cxng/src - ${BOLOS_SDK}/lib_ux_nbgl - ${BOLOS_SDK}/lib_nbgl/include - ${BOLOS_SDK}/lib_standard_app/ - ${BOLOS_SDK}/lib_alloc/ - ${CMAKE_SOURCE_DIR}/src/ -) + # Define the compilation options + target_compile_definitions(${HARNESS_NAME} PUBLIC macros FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1) -FILE(GLOB_RECURSE SOURCES - ${CMAKE_SOURCE_DIR}/../../src/features/generic_tx_parser/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/provide_trusted_name/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/getChallenge/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/provide_enum_value/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/provide_network_info/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/provideNFTInformation/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/provide_proxy_info/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/provide_tx_simulation/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/signAuthorizationEIP7702/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/provide_safe_account/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/signMessageEIP712/*.c - ${CMAKE_SOURCE_DIR}/../../src/features/signMessageEIP712_common/*.c - ${CMAKE_SOURCE_DIR}/../../src/list.c - ${CMAKE_SOURCE_DIR}/../../src/nbgl/ui_utils.c - ${CMAKE_SOURCE_DIR}/../../src/mem.c - ${CMAKE_SOURCE_DIR}/../../src/mem_utils.c - ${CMAKE_SOURCE_DIR}/../../src/network.c - ${CMAKE_SOURCE_DIR}/../../src/tlv.c - ${CMAKE_SOURCE_DIR}/../../src/tlv_apdu.c - ${CMAKE_SOURCE_DIR}/../../src/uint128.c - ${CMAKE_SOURCE_DIR}/../../src/uint256.c - ${CMAKE_SOURCE_DIR}/../../src/time_format.c - ${CMAKE_SOURCE_DIR}/../../src/uint_common.c - ${CMAKE_SOURCE_DIR}/../../src/utils.c - ${CMAKE_SOURCE_DIR}/../../src/manage_asset_info.c - ${CMAKE_SOURCE_DIR}/../../src/hash_bytes.c -) + # Link against the secure_sdk and code_lib libraries + target_link_libraries(${HARNESS_NAME} PUBLIC secure_sdk code_lib) -add_executable(fuzzer src/fuzzer.c ${SDK_STD_SOURCES} ${SOURCES}) -target_compile_options(fuzzer PRIVATE ${COMPILATION_FLAGS}) -target_link_options(fuzzer PRIVATE ${COMPILATION_FLAGS}) + message(STATUS "Creating fuzzer target: ${HARNESS_NAME}") +endforeach() diff --git a/tests/fuzzing/README.md b/tests/fuzzing/README.md index 89db1e283..2ee6b7736 100644 --- a/tests/fuzzing/README.md +++ b/tests/fuzzing/README.md @@ -2,83 +2,125 @@ ## Fuzzing -Fuzzing allows us to test how a program behaves when provided with invalid, unexpected, or random data as input. +Fuzzing allows us to test how a program behaves when provided with invalid, unexpected, or random +data as input. -Our fuzz target needs to implement `int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)`, +The fuzz target needs to implement `int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)`, which provides an array of random bytes that can be used to simulate a serialized buffer. If the application crashes, or a [sanitizer](https://github.com/google/sanitizers) detects any kind of access violation, the fuzzing process is stopped, a report regarding the vulnerability is shown, and the input that triggered the bug is written to disk under the name `crash-*`. -The vulnerable input file created can be passed as an argument to the fuzzer to triage the issue. +The vulnerable input file created can be passed as an argument to the fuzzer to triage the issue. ## Manual usage based on Ledger container ### Preparation -The fuzzer can run from the docker `ledger-app-builder-legacy`. You can download it from the `ghcr.io` docker repository: +The fuzzer can be run using the Docker image `ledger-app-dev-tools`. You can download it from the +`ghcr.io` docker repository: -```console -sudo docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest +```bash +docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest ``` -You can then enter this development environment by executing the following command from the repository root directory: +You can then enter this development environment by executing the following command from the +repository root directory: -```console -sudo docker run --rm -ti --user "$(id -u):$(id -g)" -v "$(realpath .):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest +```bash +docker run --rm -ti -v "$(realpath .):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest ``` -### Compilation +### Writing your Harness -Once in the container, go into the `tests/fuzzing` folder to compile the fuzzer: +When writing your harness, keep the following points in mind: -```console -cd tests/fuzzing +- An SDK's interface for compilation is provided via the target `secure_sdk` in CMakeLists.txt +- If you are running it for the first time, consider using the script `local_run` from inside the + Docker container using the flag build=1, if you need to manually + add/remove macros you can then do it using the files macros/add_macros.txt or + macros/exclude_macros.txt and rerunning it, or directly change the macros/generated/macros.txt. +- A typical harness looks like this: -# cmake initialization -cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -DCMAKE_C_COMPILER=/usr/bin/clang -DSANITIZER=[address|memory] -B build -S . + ```C + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (sigsetjmp(fuzz_exit_jump_ctx.jmp_buf, 1)) return 0; -# Fuzzer compilation -cmake --build build -``` + ### harness code ### -### Run + return 0; + } + ``` + + This allows a return point when the `os_sched_exit()` function is mocked. -```console -./build/fuzzer -max_len=8192 +- To provide an SDK interface, we automatically generate syscall mock functions located in + `SECURE_SDK_PATH/fuzzing/mock/generated/generated_syscalls.c`, if you need a more specific mock, + you can define it in `APP_PATH/fuzzing/mock` with the same name and without the WEAK attribute. + +### Compile and run the fuzzer from the container + +Once inside the container, navigate to the `tests/fuzzing` folder to compile the fuzzer: + +```bash +cd tests/fuzzing +export BOLOS_SDK=/opt/flex-secure-sdk/ +apt install -y libbsd-dev + +${BOLOS_SDK}/fuzzing/local_run.sh --build=1 \ + --j=4 \ + --BOLOS_SDK=${BOLOS_SDK} \ + --fuzzer=build/fuzz_dispatcher \ + --run-fuzzer=1 \ + --compute-coverage=1 ``` -If you want to do a fuzzing campain on more than one core and compute the coverage results, you can use the `local_run.sh` script within the container (it'll only run the address and UB sanitizers). +### About local_run.sh + +| Parameter | Type | Description | +| :--------------------- | :------------------ | :------------------------------------------------------------------- | +| `--BOLOS_SDK` | `PATH TO BOLOS SDK` | **Required**. Path to the BOLOS SDK | +| `--build` | `bool` | **Optional**. Whether to build the project (default: 0) | +| `--fuzzer` | `PATH` | **Required**. Path to the fuzzer binary | +| `--compute-coverage` | `bool` | **Optional**. Whether to compute coverage after fuzzing (default: 0) | +| `--run-fuzzer` | `bool` | **Optional**. Whether to run or not the fuzzer (default: 0) | +| `--run-crash` | `FILENAME` | **Optional**. Run the on a specific crash input file (default: 0) | +| `--sanitizer` | `address or memory` | **Optional**. Compile with sanitizer (default: address) | +| `--j` | `int` | **Optional**. N-parallel jobs for build and fuzzing (default: 1) | +| `--help` | | **Optional**. Display help message | + +### Visualizing code coverage + +After running your fuzzer, if `--compute-coverage=1` the coverage will be available in your browser. ## Full usage based on `clusterfuzzlite` container Exactly the same context as the CI, directly using the `clusterfuzzlite` environment. -More info can be found here: - +More info can be found here: ### Preparation The principle is to build the container, and run it to perform the fuzzing. -> **Note**: The container contains a copy of the sources (they are not cloned), -> which means the `docker build` command must be re-executed after each code modification. +> **Note**: The container contains a copy of the sources (they are not cloned), which means the +> `docker build` command must be re-executed after each code modification. -```console +```bash # Prepare directory tree mkdir tests/fuzzing/{corpus,out} # Container generation -docker build -t app-ethereum --file .clusterfuzzlite/Dockerfile . +docker build -t fuzz-ethereum --file .clusterfuzzlite/Dockerfile . ``` ### Compilation -```console -docker run --rm --privileged -e FUZZING_LANGUAGE=c -v "$(realpath .)/tests/fuzzing/out:/out" -ti app-ethereum +```bash +docker run --rm --privileged -e FUZZING_LANGUAGE=c -v "$(realpath .)/tests/fuzzing/out:/out" -ti fuzz-ethereum ``` ### Run -```console +```bash docker run --rm --privileged -e FUZZING_ENGINE=libfuzzer -e RUN_FUZZER_MODE=interactive -v "$(realpath .)/tests/fuzzing/corpus:/tmp/fuzz_corpus" -v "$(realpath .)/tests/fuzzing/out:/out" -ti gcr.io/oss-fuzz-base/base-runner run_fuzzer fuzzer ``` diff --git a/tests/fuzzing/harness/fuzz_calldata.c b/tests/fuzzing/harness/fuzz_calldata.c new file mode 100644 index 000000000..258655190 --- /dev/null +++ b/tests/fuzzing/harness/fuzz_calldata.c @@ -0,0 +1,49 @@ +#include "fuzz_utils.h" + +#include "calldata.h" + +static s_calldata *g_calldata = NULL; + +int fuzzCalldata(const uint8_t *data, size_t size) { + while (size > 0) { + switch (data[0]) { + case 'I': + data++; + size--; + if (g_calldata != NULL) { + calldata_delete(g_calldata); + } + g_calldata = calldata_init(500, NULL); + break; + case 'W': + size--; + data++; + if (size < 1 || size < data[0] + 1) return 0; + calldata_append(g_calldata, data + 1, data[0]); + size -= (1 + data[0]); + data += 1 + data[0]; + break; + case 'R': + size--; + data++; + if (size < 1) return 0; + calldata_get_chunk(g_calldata, data[0]); + size--; + data++; + break; + default: + return 0; + } + } + return 0; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + + // Run the harness + fuzzCalldata(data, size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_eip712.c b/tests/fuzzing/harness/fuzz_eip712.c new file mode 100644 index 000000000..2eccad693 --- /dev/null +++ b/tests/fuzzing/harness/fuzz_eip712.c @@ -0,0 +1,56 @@ +#include "fuzz_utils.h" + +int fuzzEIP712(const uint8_t *data, size_t size) { + if (eip712_context_init() == false) return 0; + size_t len = 0; + uint8_t p1; + uint8_t p2; + unsigned int flags; + + if (size < 2) goto eip712_end; + p2 = *(data++); + len = *(data++); + size -= 2; + if (size < len) goto eip712_end; + handle_eip712_struct_def(p2, data, len); + size -= len; + + if (size < 3) goto eip712_end; + p1 = *(data++); + p2 = *(data++); + len = *(data++); + size -= 3; + if (size < len) goto eip712_end; + handle_eip712_filtering(p1, p2, data, len, &flags); + size -= len; + + if (size < 3) goto eip712_end; + p1 = *(data++); + p2 = *(data++); + len = *(data++); + size -= 3; + if (size < len) goto eip712_end; + handle_eip712_struct_impl(p1, p2, data, len, &flags); + size -= len; + + if (size < 1) goto eip712_end; + len = *(data++); + size -= 1; + if (size < len) goto eip712_end; + handle_eip712_sign(data, len, &flags); + +eip712_end: + eip712_context_deinit(); + return 0; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + reset_app_context(); + + // Run the harness + fuzzEIP712(data, size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_eip7702.c b/tests/fuzzing/harness/fuzz_eip7702.c new file mode 100644 index 000000000..46cd9decf --- /dev/null +++ b/tests/fuzzing/harness/fuzz_eip7702.c @@ -0,0 +1,28 @@ +#include "fuzz_utils.h" + +int fuzzEIP7702(const uint8_t *data, size_t size) { + size_t offset = 0; + size_t len = 0; + uint8_t p1; + unsigned int flags; + + while (size - offset > 3) { + if (data[offset++] == 0) break; + p1 = data[offset++]; + len = data[offset++]; + if (size - offset < len) return 0; + if (handleSignEIP7702Authorization(p1, data + offset, len, &flags) != SWO_SUCCESS) return 0; + offset += len; + } + return 0; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + + // Run the harness + fuzzEIP7702(data, size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_generic_parser.c b/tests/fuzzing/harness/fuzz_generic_parser.c new file mode 100644 index 000000000..a01b607dc --- /dev/null +++ b/tests/fuzzing/harness/fuzz_generic_parser.c @@ -0,0 +1,63 @@ +#include "fuzz_utils.h" + +#include "gtp_field.h" +#include "gtp_tx_info.h" +#include "enum_value.h" + +// Fuzzing harness interface +typedef int (*harness)(const uint8_t *data, size_t size); + +int fuzzGenericParserFieldCmd(const uint8_t *data, size_t size) { + s_field field = {0}; + s_field_ctx ctx = {0}; + ctx.field = &field; + + if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_field_struct, &ctx)) return 0; + + if (!verify_field_struct(&ctx)) return 0; + + return format_field(&field); +} + +int fuzzGenericParserTxInfoCmd(const uint8_t *data, size_t size) { + s_tx_info tx_info = {0}; + s_tx_info_ctx ctx = {0}; + ctx.tx_info = &tx_info; + + cx_sha256_init(&ctx.struct_hash); + if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_tx_info_struct, &ctx)) return 0; + + return verify_tx_info_struct(&ctx); +} + +int fuzzGenericParserEnumCmd(const uint8_t *data, size_t size) { + s_enum_value_ctx ctx = {0}; + + cx_sha256_init(&ctx.struct_hash); + if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_enum_value_struct, &ctx)) return 0; + + return verify_enum_value_struct(&ctx); +} + +// Array of fuzzing harness functions +harness harnesses[] = { + fuzzGenericParserFieldCmd, + fuzzGenericParserTxInfoCmd, + fuzzGenericParserEnumCmd, +}; + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + uint8_t target; + init_fuzzing_environment(); + + // Determine which harness function to call based on the first byte of data + if (size < 1) return 0; + target = data[0]; + if (target >= sizeof(harnesses) / sizeof(harnesses[0])) return 0; + + // Call the selected harness function with the remaining data (which can be of size 0) + harnesses[target](++data, --size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_networks.c b/tests/fuzzing/harness/fuzz_networks.c new file mode 100644 index 000000000..f1fa76b16 --- /dev/null +++ b/tests/fuzzing/harness/fuzz_networks.c @@ -0,0 +1,30 @@ +#include "fuzz_utils.h" + +int fuzzDynamicNetworks(const uint8_t *data, size_t size) { + size_t offset = 0; + size_t len = 0; + uint8_t p1; + uint8_t p2; + unsigned int tx; + + while (size - offset > 4) { + if (data[offset++] == 0) break; + p1 = data[offset++]; + p2 = data[offset++]; + len = data[offset++]; + if (size - offset < len) return 0; + if (handle_network_info(p1, p2, data + offset, len, &tx) != SWO_SUCCESS) return 0; + offset += len; + } + return 0; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + + // Run the harness + fuzzDynamicNetworks(data, size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_nft.c b/tests/fuzzing/harness/fuzz_nft.c new file mode 100644 index 000000000..0648c1087 --- /dev/null +++ b/tests/fuzzing/harness/fuzz_nft.c @@ -0,0 +1,16 @@ +#include "fuzz_utils.h" + +int fuzzNFTInfo(const uint8_t *data, size_t size) { + unsigned int tx; + return handleProvideNFTInformation(data, size, &tx) != SWO_SUCCESS; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + + // Run the harness + fuzzNFTInfo(data, size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_proxy.c b/tests/fuzzing/harness/fuzz_proxy.c new file mode 100644 index 000000000..952ea720a --- /dev/null +++ b/tests/fuzzing/harness/fuzz_proxy.c @@ -0,0 +1,16 @@ +#include "fuzz_utils.h" + +int fuzzProxyInfo(const uint8_t *data, size_t size) { + if (size < 1) return 0; + return handle_proxy_info(data[0], 0, size - 1, data + 1); +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + + // Run the harness + fuzzProxyInfo(data, size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_safe.c b/tests/fuzzing/harness/fuzz_safe.c new file mode 100644 index 000000000..5e43670b1 --- /dev/null +++ b/tests/fuzzing/harness/fuzz_safe.c @@ -0,0 +1,42 @@ +#include "fuzz_utils.h" + +#include "safe_descriptor.h" + +int fuzzSafeCmd(const uint8_t *data, size_t size) { + handle_safe_tlv_payload(data, size); + return 0; +} + +int fuzzSignerCmd(const uint8_t *data, size_t size) { + if (size < 3) return 0; + safe_descriptor_t desc = { + .address = "AAAAAAAAAAAAAAAAAAAA", + .threshold = data[0] % 100, + .signers_count = data[1] % 100, + .role = data[2] % 2, + }; + SAFE_DESC = &desc; + handle_signer_tlv_payload(data + 3, size - 3); + SAFE_DESC = NULL; + return 0; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + uint8_t target; + init_fuzzing_environment(); + SAFE_DESC = NULL; + + // Determine which harness function to call based on the first byte of data + if (size < 1) return 0; + target = data[0]; + data++; + size--; + if (target & 0x01) { + fuzzSafeCmd(data, size); + } else { + fuzzSignerCmd(data, size); + } + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_trusted_names.c b/tests/fuzzing/harness/fuzz_trusted_names.c new file mode 100644 index 000000000..98a2efc97 --- /dev/null +++ b/tests/fuzzing/harness/fuzz_trusted_names.c @@ -0,0 +1,27 @@ +#include "fuzz_utils.h" + +int fuzzTrustedNames(const uint8_t *data, size_t size) { + size_t offset = 0; + size_t len = 0; + uint8_t p1; + + while (size - offset > 3) { + if (data[offset++] == 0) break; + p1 = data[offset++]; + len = data[offset++]; + if (size - offset < len) return 0; + if (handle_trusted_name(p1, data + offset, len) != SWO_SUCCESS) return 0; + offset += len; + } + return 0; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + + // Run the harness + fuzzTrustedNames(data, size); + + return 0; +} diff --git a/tests/fuzzing/harness/fuzz_tx_check.c b/tests/fuzzing/harness/fuzz_tx_check.c new file mode 100644 index 000000000..c6c874ba7 --- /dev/null +++ b/tests/fuzzing/harness/fuzz_tx_check.c @@ -0,0 +1,22 @@ +#include "fuzz_utils.h" + +int fuzzTxCheck(const uint8_t *data, size_t size) { + unsigned int flags; + if (size < 2) return 0; + + if (handle_tx_simulation(data[0], data[1], data + 2, size - 2, &flags) != SWO_SUCCESS) return 0; + + get_tx_simulation_risk_str(); + get_tx_simulation_category_str(); + return 0; +} + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + init_fuzzing_environment(); + + // Run the harness + fuzzTxCheck(data, size); + + return 0; +} diff --git a/tests/fuzzing/local_run.sh b/tests/fuzzing/local_run.sh deleted file mode 100755 index 0c02af315..000000000 --- a/tests/fuzzing/local_run.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -# Clean -rm -rf build - -# Build the fuzzer -cmake -B build -S . -DCMAKE_C_COMPILER=/usr/bin/clang -DSANITIZER=address -cmake --build build - -if ! [ -f ./build/fuzzer ]; then - echo "Build failed, please check the output above." - exit 1 -fi - -# Create the corpus directory if it doesn't exist -if ! [ -d ./corpus ]; then - mkdir corpus -fi - -# Run the fuzzer on half CPU cores -ncpus=$(nproc) -jobs=$(($ncpus/2)) -echo "The fuzzer will start very soon, press Ctrl-C when you want to stop it and compute the coverage" -./build/fuzzer -max_len=8192 -jobs="$jobs" ./corpus - - -read -p "Would you like to compute coverage (y/n)? " -n 1 -r -echo -if [[ $REPLY =~ ^[Nn]$ ]] -then - exit 0 -fi - -# Remove previous artifacts -rm default.profdata default.profraw - -# Run profiling on the corpus -./build/fuzzer -max_len=8192 -runs=0 ./corpus - -# Compute coverage -llvm-profdata merge -sparse *.profraw -o default.profdata -llvm-cov show build/fuzzer -instr-profile=default.profdata -format=html -ignore-filename-regex='ethereum-plugin-sdk\/|secure-sdk\/' > report.html -llvm-cov report build/fuzzer -instr-profile=default.profdata -ignore-filename-regex='ethereum-plugin-sdk\/|secure-sdk\/' diff --git a/tests/fuzzing/macros/add_macros.txt b/tests/fuzzing/macros/add_macros.txt new file mode 100644 index 000000000..90d80c5fc --- /dev/null +++ b/tests/fuzzing/macros/add_macros.txt @@ -0,0 +1,2 @@ +HAVE_SHA512 +HAVE_CHALLENGE_NO_CHECK diff --git a/tests/fuzzing/macros/exclude_macros.txt b/tests/fuzzing/macros/exclude_macros.txt new file mode 100644 index 000000000..19d309f67 --- /dev/null +++ b/tests/fuzzing/macros/exclude_macros.txt @@ -0,0 +1,3 @@ +HAVE_SHA512_WITH_BLOCK_ALT_METHOD +PRINTF(...)= +HAVE_BOLOS_APP_STACK_CANARY diff --git a/tests/fuzzing/mock/mock.c b/tests/fuzzing/mock/mock.c new file mode 100644 index 000000000..1cbe45e17 --- /dev/null +++ b/tests/fuzzing/mock/mock.c @@ -0,0 +1,63 @@ +#include +#include + +#include "cx_errors.h" +#include "cx_sha256.h" +#include "cx_sha3.h" +#include "buffer.h" +#include "lcx_ecfp.h" +#include "mem_alloc.h" + +#include "bip32_utils.h" + +/** MemorySanitizer does not wrap explicit_bzero https://github.com/google/sanitizers/issues/1507 + * which results in false positives when running MemorySanitizer. + */ +void memset_s(void *buffer, char c, size_t n) { + if (buffer == NULL) return; + + volatile char *ptr = buffer; + while (n--) *ptr++ = c; +} + +void app_quit(void) { +} +void app_main(void) { +} + +uint16_t io_seproxyhal_send_status(uint16_t sw, uint32_t tx, bool reset, bool idle) { + UNUSED(sw); + UNUSED(tx); + UNUSED(reset); + UNUSED(idle); + return 0; +} + +const uint8_t *parseBip32(const uint8_t *dataBuffer, uint8_t *dataLength, bip32_path_t *bip32) { + UNUSED(dataBuffer); + UNUSED(dataLength); + UNUSED(bip32); + return NULL; +} + +mem_ctx_t mem_init(void *heap_start, size_t heap_size) { + (void) heap_size; + return heap_start; +} + +void *mem_alloc(mem_ctx_t ctx, size_t nb_bytes) { + (void) ctx; + return malloc(nb_bytes); +} + +void mem_free(mem_ctx_t ctx, void *ptr) { + (void) ctx; + free(ptr); +} + +// APPs expect a specific length +cx_err_t cx_ecdomain_parameters_length(cx_curve_t cv, size_t *length) { + (void) cv; + *length = (size_t) 32; + return 0x00000000; +} diff --git a/tests/fuzzing/mock/net_icons.gen.h b/tests/fuzzing/mock/net_icons.gen.h new file mode 100644 index 000000000..f394ea48c --- /dev/null +++ b/tests/fuzzing/mock/net_icons.gen.h @@ -0,0 +1,18 @@ +/* + * Copied from file generated by tools/gen_networks.py + */ + +#ifndef NETWORK_ICONS_GENERATED_H_ +#define NETWORK_ICONS_GENERATED_H_ + +#include +#include "nbgl_types.h" + +typedef struct { + uint64_t chain_id; + const nbgl_icon_details_t *icon; +} network_icon_t; + +extern const network_icon_t g_network_icons[10]; + +#endif // NETWORK_ICONS_GENERATED_H_ diff --git a/tests/fuzzing/src/fuzz_utils.c b/tests/fuzzing/src/fuzz_utils.c new file mode 100644 index 000000000..5791e24f3 --- /dev/null +++ b/tests/fuzzing/src/fuzz_utils.c @@ -0,0 +1,62 @@ +#include "fuzz_utils.h" + +#include "caller_api.h" +#include "net_icons.gen.h" + +// Global state required by the app features +cx_sha3_t global_sha3 = {0}; +cx_sha3_t sha3 = {0}; +tmpContent_t tmpContent = {0}; +txContext_t txContext = {0}; +txContent_t txContent = {0}; +dataContext_t dataContext = {0}; +tmpCtx_t tmpCtx = {0}; +strings_t strings = {0}; +caller_app_t *caller_app = NULL; +const chain_config_t *chainConfig = NULL; + +const network_icon_t g_network_icons[10] = {0}; + +uint16_t apdu_response_code = 0; +pluginType_t pluginType = 0; +uint32_t eth2WithdrawalIndex = 0; +uint8_t appState = 0; + +// Mock the storage to enable wanted features +const internalStorage_t N_storage_real = { + .tx_check_enable = true, + .tx_check_opt_in = true, + .eip7702_enable = true, +}; + +chain_config_t config = { + .coinName = "FUZZ", + .chainId = 0x42, +}; + +void reset_app_context(void) { + gcs_cleanup(); + clear_safe_account(); + ui_all_cleanup(); +} + +void init_fuzzing_environment(void) { + // Clear global structures to ensure a clean state for each fuzzing iteration + explicit_bzero(&global_sha3, sizeof(global_sha3)); + explicit_bzero(&sha3, sizeof(sha3)); + explicit_bzero(&tmpContent, sizeof(tmpContent_t)); + explicit_bzero(&txContext, sizeof(txContext_t)); + explicit_bzero(&txContent, sizeof(txContent_t)); + explicit_bzero(&dataContext, sizeof(dataContext_t)); + explicit_bzero(&tmpCtx, sizeof(tmpCtx_t)); + explicit_bzero(&strings, sizeof(strings_t)); + + explicit_bzero(&G_io_apdu_buffer, OS_IO_SEPH_BUFFER_SIZE + 1); + + chainConfig = &config; + txContext.content = &txContent; + txContext.sha3 = &sha3; + pluginType = PLUGIN_TYPE_EXTERNAL; + eth2WithdrawalIndex = 0; + appState = APP_STATE_IDLE; +} diff --git a/tests/fuzzing/src/fuzz_utils.h b/tests/fuzzing/src/fuzz_utils.h new file mode 100644 index 000000000..9d2f639c4 --- /dev/null +++ b/tests/fuzzing/src/fuzz_utils.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +#include "shared_context.h" +#include "nbgl_use_case.h" +#include "status_words.h" + +extern void reset_app_context(void); +extern void init_fuzzing_environment(void); diff --git a/tests/fuzzing/src/fuzzer.c b/tests/fuzzing/src/fuzzer.c deleted file mode 100644 index 65fe4ea59..000000000 --- a/tests/fuzzing/src/fuzzer.c +++ /dev/null @@ -1,327 +0,0 @@ -#include -#include - -#include "cmd_trusted_name.h" - -#include "cmd_network_info.h" - -#include "cmd_get_tx_simulation.h" - -#include "cmd_proxy_info.h" - -#include "cmd_field.h" -#include "cmd_tx_info.h" -#include "cmd_enum_value.h" - -#include "cmd_safe_account.h" - -#include "gtp_field.h" -#include "gtp_tx_info.h" -#include "enum_value.h" -#include "tx_ctx.h" - -#include "auth_7702.h" -#include "commands_7702.h" - -#include "safe_descriptor.h" -#include "signer_descriptor.h" - -#include "commands_712.h" -#include "context_712.h" -#include "filtering.h" - -#include "shared_context.h" -#include "tlv.h" -#include "apdu_constants.h" -#include "nbgl_use_case.h" -#include "manage_asset_info.h" -#include "ui_utils.h" - -// Fuzzing harness interface -typedef int (*harness)(const uint8_t *data, size_t size); - -// Global state required by the app features -cx_sha3_t global_sha3; -cx_sha3_t sha3; -unsigned char G_io_apdu_buffer[260]; -tmpContent_t tmpContent; -txContext_t txContext; -txContent_t txContent; -chain_config_t config = { - .coinName = "FUZZ", - .chainId = 0x42, -}; -const chain_config_t *chainConfig = &config; -uint8_t appState; -tmpCtx_t tmpCtx; -strings_t strings; -nbgl_warning_t warning; -uint16_t apdu_response_code; - -// Mock the storage to enable wanted features -const internalStorage_t N_storage_real = { - .tx_check_enable = true, - .tx_check_opt_in = true, - .eip7702_enable = true, -}; - -void reset_app_context(void) { - appState = APP_STATE_IDLE; - memset((uint8_t *) &tmpCtx, 0, sizeof(tmpCtx)); - forget_known_assets(); - if (txContext.store_calldata) { - gcs_cleanup(); - } - memset((uint8_t *) &txContext, 0, sizeof(txContext)); - memset((uint8_t *) &tmpContent, 0, sizeof(tmpContent)); - clear_safe_account(); - ui_all_cleanup(); -} - -int fuzzGenericParserFieldCmd(const uint8_t *data, size_t size) { - s_field field = {0}; - s_field_ctx ctx = {0}; - ctx.field = &field; - - if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_field_struct, &ctx)) return 0; - - if (!verify_field_struct(&ctx)) return 0; - - return format_field(&field); -} - -int fuzzGenericParserTxInfoCmd(const uint8_t *data, size_t size) { - s_tx_info tx_info = {0}; - s_tx_info_ctx ctx = {0}; - ctx.tx_info = &tx_info; - - if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_tx_info_struct, &ctx)) return 0; - - return verify_tx_info_struct(&ctx); -} - -int fuzzGenericParserEnumCmd(const uint8_t *data, size_t size) { - s_enum_value_ctx ctx = {0}; - - if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_enum_value_struct, &ctx)) return 0; - - return verify_enum_value_struct(&ctx); -} - -int fuzzDynamicNetworks(const uint8_t *data, size_t size) { - size_t offset = 0; - size_t len = 0; - uint8_t p1; - uint8_t p2; - unsigned int tx; - - while (size - offset > 4) { - if (data[offset++] == 0) break; - p1 = data[offset++]; - p2 = data[offset++]; - len = data[offset++]; - if (size - offset < len) return 0; - if (handle_network_info(p1, p2, data + offset, len, &tx) != SWO_SUCCESS) return 0; - offset += len; - } - return 0; -} - -int fuzzTrustedNames(const uint8_t *data, size_t size) { - size_t offset = 0; - size_t len = 0; - uint8_t p1; - - while (size - offset > 3) { - if (data[offset++] == 0) break; - p1 = data[offset++]; - len = data[offset++]; - if (size - offset < len) return 0; - if (handle_trusted_name(p1, data + offset, len) != SWO_SUCCESS) return 0; - offset += len; - } - return 0; -} - -int fuzzNFTInfo(const uint8_t *data, size_t size) { - unsigned int tx; - return handleProvideNFTInformation(data, size, &tx) != SWO_SUCCESS; -} - -int fuzzProxyInfo(const uint8_t *data, size_t size) { - if (size < 1) return 0; - return handle_proxy_info(data[0], 0, size - 1, data + 1); -} - -int fuzzTxSimulation(const uint8_t *data, size_t size) { - unsigned int flags; - if (size < 2) return 0; - - if (handle_tx_simulation(data[0], data[1], data + 2, size - 2, &flags) != SWO_SUCCESS) return 0; - - get_tx_simulation_risk_str(); - get_tx_simulation_category_str(); - return 0; -} - -static s_calldata *g_calldata = NULL; - -int fuzzCalldata(const uint8_t *data, size_t size) { - while (size > 0) { - switch (data[0]) { - case 'I': - data++; - size--; - if (g_calldata != NULL) { - calldata_delete(g_calldata); - } - g_calldata = calldata_init(500, NULL); - break; - case 'W': - size--; - data++; - if (size < 1 || size < data[0] + 1) return 0; - calldata_append(g_calldata, data + 1, data[0]); - size -= (1 + data[0]); - data += 1 + data[0]; - break; - case 'R': - size--; - data++; - if (size < 1) return 0; - calldata_get_chunk(g_calldata, data[0]); - size--; - data++; - break; - default: - return 0; - } - } - return 0; -} - -int fuzzEIP7702(const uint8_t *data, size_t size) { - size_t offset = 0; - size_t len = 0; - uint8_t p1; - unsigned int flags; - - while (size - offset > 3) { - if (data[offset++] == 0) break; - p1 = data[offset++]; - len = data[offset++]; - if (size - offset < len) return 0; - if (handleSignEIP7702Authorization(p1, data + offset, len, &flags) != SWO_SUCCESS) return 0; - offset += len; - } - return 0; -} - -int fuzzSafeCmd(const uint8_t *data, size_t size) { - handle_safe_tlv_payload(data, size); - return 0; -} - -int fuzzSignerCmd(const uint8_t *data, size_t size) { - if (size < 3) return 0; - safe_descriptor_t desc = { - .address = "AAAAAAAAAAAAAAAAAAAA", - .threshold = data[0] % 100, - .signers_count = data[1] % 100, - .role = data[2] % 2, - }; - SAFE_DESC = &desc; - handle_signer_tlv_payload(data + 3, size - 3); - SAFE_DESC = NULL; - return 0; -} - -int fuzzEIP712(const uint8_t *data, size_t size) { - if (eip712_context_init() == false) return 0; - size_t len = 0; - uint8_t p1; - uint8_t p2; - unsigned int flags; - - if (size < 2) goto eip712_end; - p2 = *(data++); - len = *(data++); - size -= 2; - if (size < len) goto eip712_end; - handle_eip712_struct_def(p2, data, len); - size -= len; - - if (size < 3) goto eip712_end; - p1 = *(data++); - p2 = *(data++); - len = *(data++); - size -= 3; - if (size < len) goto eip712_end; - handle_eip712_filtering(p1, p2, data, len, &flags); - size -= len; - - if (size < 3) goto eip712_end; - p1 = *(data++); - p2 = *(data++); - len = *(data++); - size -= 3; - if (size < len) goto eip712_end; - handle_eip712_struct_impl(p1, p2, data, len, &flags); - size -= len; - - if (size < 1) goto eip712_end; - len = *(data++); - size -= 1; - if (size < len) goto eip712_end; - handle_eip712_sign(data, len, &flags); - -eip712_end: - eip712_context_deinit(); - return 0; -} - -// Array of fuzzing harness functions -harness harnesses[] = { - fuzzGenericParserFieldCmd, - fuzzGenericParserTxInfoCmd, - fuzzGenericParserEnumCmd, - fuzzDynamicNetworks, - fuzzTrustedNames, - fuzzNFTInfo, - fuzzProxyInfo, - fuzzTxSimulation, - fuzzCalldata, - fuzzEIP7702, - fuzzSafeCmd, - fuzzSignerCmd, - fuzzEIP712, -}; - -/* Main fuzzing handler called by libfuzzer */ -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - // Clear global structures to ensure a clean state for each fuzzing iteration - explicit_bzero(&tmpContent, sizeof(tmpContent_t)); - explicit_bzero(&txContext, sizeof(txContext_t)); - explicit_bzero(&txContent, sizeof(txContent_t)); - explicit_bzero(&tmpCtx, sizeof(tmpCtx_t)); - explicit_bzero(&strings, sizeof(strings_t)); - explicit_bzero(&G_io_apdu_buffer, 260); - explicit_bzero(&sha3, sizeof(sha3)); - explicit_bzero(&global_sha3, sizeof(global_sha3)); - SAFE_DESC = NULL; - - uint8_t target; - - txContext.content = &txContent; - txContext.sha3 = &sha3; - - // Determine which harness function to call based on the first byte of data - if (size < 1) return 0; - target = data[0]; - if (target >= sizeof(harnesses) / sizeof(harnesses[0])) return 0; - - // Call the selected harness function with the remaining data (which can be of size 0) - harnesses[target](++data, --size); - - return 0; -} diff --git a/tests/fuzzing/src/glyphs.h b/tests/fuzzing/src/glyphs.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/fuzzing/src/mock.c b/tests/fuzzing/src/mock.c deleted file mode 100644 index 7da4eaf30..000000000 --- a/tests/fuzzing/src/mock.c +++ /dev/null @@ -1,355 +0,0 @@ -#include -#include - -#include "cx_errors.h" -#include "cx_sha256.h" -#include "cx_sha3.h" -#include "buffer.h" -#include "lcx_ecfp.h" -#include "mem_alloc.h" - -#include "bip32_utils.h" - -/** MemorySanitizer does not wrap explicit_bzero https://github.com/google/sanitizers/issues/1507 - * which results in false positives when running MemorySanitizer. - */ -void memset_s(void *buffer, char c, size_t n) { - if (buffer == NULL) return; - - volatile char *ptr = buffer; - while (n--) *ptr++ = c; -} - -size_t strlcpy(char *dst, const char *src, size_t size) { - const char *s = src; - size_t n = size; - - if (n != 0) { - while (--n != 0) { - if ((*dst++ = *s++) == '\0') { - break; - } - } - } - - if (n == 0) { - if (size != 0) { - *dst = '\0'; - } - while (*s++) - ; - } - - return (s - src - 1); -} - -size_t strlcat(char *dst, const char *src, size_t size) { - char *d = dst; - const char *s = src; - size_t n = size; - size_t dsize; - - while (n-- != 0 && *d != '\0') { - d++; - } - dsize = d - dst; - n = size - dsize; - - if (n == 0) { - return (dsize + strlen(s)); - } - - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return (dsize + (s - src)); -} - -cx_err_t cx_sha256_init_no_throw(cx_sha256_t *hash) { - memset_s(hash, 0, sizeof(cx_sha256_t)); - return CX_OK; -} - -cx_err_t cx_sha3_init_no_throw(cx_sha3_t *hash PLENGTH(sizeof(cx_sha3_t)), size_t size) { - UNUSED(size); - memset_s(hash, 0, sizeof(cx_sha3_t)); - return CX_OK; -} - -cx_err_t cx_hash_no_throw(cx_hash_t *hash, - uint32_t mode, - const uint8_t *in, - size_t len, - uint8_t *out, - size_t out_len) { - UNUSED(hash); - UNUSED(mode); - memset_s(out, 0, out_len); // let's initialize the buffer - // if arrays are not empty, read the last element of in and write it in the last element of out - if (len > 0 && out_len > 0) out[out_len - 1] = in[len - 1]; - return CX_OK; -} - -void assert_exit(bool confirm) { - UNUSED(confirm); - exit(1); -} - -cx_err_t cx_keccak_256_hash_iovec(const cx_iovec_t *iovec, - size_t iovec_len, - uint8_t digest[static CX_KECCAK_256_SIZE]) { - UNUSED(iovec); - UNUSED(iovec_len); - memset_s(digest, 0, CX_SHA256_SIZE); - return CX_OK; -} - -cx_err_t cx_sha256_hash_iovec(const cx_iovec_t *iovec, - size_t iovec_len, - uint8_t digest[static CX_SHA256_SIZE]) { - UNUSED(iovec); - UNUSED(iovec_len); - memset_s(digest, 0, CX_SHA256_SIZE); - return CX_OK; -} - -int check_signature_with_pubkey(const char *tag, - uint8_t *buffer, - const uint8_t bufLen, - const uint8_t *PubKey, - const uint8_t keyLen, - const uint8_t keyUsageExp, - uint8_t *signature, - const uint8_t sigLen) { - UNUSED(tag); - UNUSED(buffer); - UNUSED(bufLen); - UNUSED(PubKey); - UNUSED(keyUsageExp); - UNUSED(keyLen); - UNUSED(signature); - UNUSED(sigLen); - return CX_OK; -} - -void *pic(void *addr) { - return addr; -} - -cx_err_t cx_math_mult_no_throw(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len) { - UNUSED(r); - UNUSED(a); - UNUSED(b); - UNUSED(len); - return CX_OK; -} - -void cx_rng_no_throw(uint8_t *buffer, size_t len) { - memset_s(buffer, 0, len); -} - -uint16_t get_public_key(uint8_t *out, uint8_t outLength) { - memset_s(out, 0, outLength); - return 0; -} - -uint16_t get_public_key_string(bip32_path_t *bip32, - uint8_t *pubKey, - char *address, - uint8_t *chainCode, - uint64_t chainId) { - UNUSED(bip32); - UNUSED(pubKey); - UNUSED(chainCode); - UNUSED(chainId); - memset_s(address, 0, 10); - return 0; -} - -void ui_gcs_cleanup(void) { -} - -size_t cx_hash_sha256(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { - memset_s(out, 0, out_len); // let's initialize the buffer - // if arrays are not empty, read the last element of in and write it in the last element of out - if (in_len > 0 && out_len > 0) out[out_len - 1] = in[in_len - 1]; - return CX_OK; -} - -typedef unsigned char bolos_task_status_t; - -void os_sched_exit(__attribute__((unused)) bolos_task_status_t exit_code) { - return; -} - -int io_send_response_buffers(const buffer_t *rdatalist, size_t count, uint16_t sw) { - UNUSED(rdatalist); - UNUSED(count); - UNUSED(sw); - return 0; -} - -uint16_t io_seproxyhal_send_status(uint16_t sw, uint32_t tx, bool reset, bool idle) { - UNUSED(sw); - UNUSED(tx); - UNUSED(reset); - UNUSED(idle); - return 0; -} - -uint32_t os_pki_get_info(uint8_t *key_usage, - uint8_t *trusted_name, - size_t *trusted_name_len, - cx_ecfp_384_public_key_t *public_key) { - UNUSED(key_usage); - UNUSED(trusted_name_len); - UNUSED(public_key); - memcpy(trusted_name, "trusted name", sizeof("trusted name")); - return 0; -} - -void ui_tx_simulation_opt_in(bool response_expected) { - UNUSED(response_expected); -} - -void ui_error_no_7702(void) { -} - -void ui_error_no_7702_whitelist(void) { -} - -void ui_sign_7702_auth(void) { -} - -void ui_sign_7702_revocation(void) { -} - -void ui_display_safe_account(void) { -} - -cx_err_t cx_keccak_init_no_throw(cx_sha3_t *hash PLENGTH(sizeof(cx_sha3_t)), size_t size) { - UNUSED(size); - memset_s(hash, 0, sizeof(cx_sha3_t)); - return CX_OK; -} - -cx_err_t bip32_derive_with_seed_get_pubkey_256(unsigned int derivation_mode, - cx_curve_t curve, - const uint32_t *path, - size_t path_len, - uint8_t raw_pubkey[static 65], - uint8_t *chain_code, - cx_md_t hashID, - unsigned char *seed, - size_t seed_len) { - UNUSED(derivation_mode); - UNUSED(curve); - UNUSED(path); - UNUSED(path_len); - UNUSED(chain_code); - UNUSED(hashID); - UNUSED(seed); - UNUSED(seed_len); - memset(raw_pubkey, 0, 65); - return CX_OK; -} - -cx_err_t bip32_derive_with_seed_ecdsa_sign_rs_hash_256(unsigned int derivation_mode, - cx_curve_t curve, - const uint32_t *path, - size_t path_len, - uint32_t sign_mode, - cx_md_t hashID, - const uint8_t *hash, - size_t hash_len, - uint8_t sig_r[static 32], - uint8_t sig_s[static 32], - uint32_t *info, - unsigned char *seed, - size_t seed_len) { - UNUSED(derivation_mode); - UNUSED(curve); - UNUSED(path); - UNUSED(path_len); - UNUSED(sign_mode); - UNUSED(hashID); - UNUSED(hash); - UNUSED(hash_len); - UNUSED(info); - UNUSED(seed); - UNUSED(seed_len); - memset(sig_r, 0, 32); - memset(sig_s, 0, 32); - return CX_OK; -} - -// Duplicate from main.c... -const uint8_t *parseBip32(const uint8_t *dataBuffer, uint8_t *dataLength, bip32_path_t *bip32) { - if (*dataLength < 1) { - PRINTF("Invalid data\n"); - return NULL; - } - - bip32->length = *dataBuffer; - - dataBuffer++; - (*dataLength)--; - - if (*dataLength < sizeof(uint32_t) * (bip32->length)) { - PRINTF("Invalid data\n"); - return NULL; - } - - if (bip32_path_read(dataBuffer, (size_t) dataLength, bip32->path, (size_t) bip32->length) == - false) { - PRINTF("Invalid Path data\n"); - return NULL; - } - dataBuffer += bip32->length * sizeof(uint32_t); - *dataLength -= bip32->length * sizeof(uint32_t); - - return dataBuffer; -} - -mem_ctx_t mem_init(void *heap_start, size_t heap_size) { - (void) heap_size; - return heap_start; -} - -void *mem_alloc(mem_ctx_t ctx, size_t nb_bytes) { - (void) ctx; - return malloc(nb_bytes); -} - -void mem_free(mem_ctx_t ctx, void *ptr) { - (void) ctx; - free(ptr); -} - -void ui_idle(void) { -} - -void ui_error_blind_signing(void) { -} - -void ui_sign_712(void) { -} - -void io_seproxyhal_io_heartbeat(void) { -} - -uint32_t cx_crc32_update(uint32_t crc_state, const void *buf, size_t len) { - crc_state = ((char *) buf)[len]; - return crc_state; -} - -cx_err_t cx_sha224_init_no_throw(cx_sha256_t *hash) { - memset(hash, 0, sizeof(cx_sha256_t)); - return CX_OK; -}