diff --git a/.github/actions/run-in-docker-action/action.yml b/.github/actions/run-in-docker-action/action.yml index 74933686a..5d46ca1b0 100644 --- a/.github/actions/run-in-docker-action/action.yml +++ b/.github/actions/run-in-docker-action/action.yml @@ -4,37 +4,35 @@ inputs: dockerfile: description: 'A Dockerfile that defines an image' required: true - tag: - description: 'A tag of an image' - required: true + scope: + description: 'A cached image scope' + required: false + default: ${{ runner.arch }} command: description: 'A command to run in a container' - required: false - default: ./ci/ci.sh + required: true runs: using: "composite" steps: - uses: docker/setup-buildx-action@v3 - - uses: docker/build-push-action@v5 + - uses: docker/build-push-action@v6 id: main_builder continue-on-error: true with: context: . file: ${{ inputs.dockerfile }} - tags: ${{ inputs.tag }} load: true - cache-from: type=gha + cache-from: type=gha,scope=${{ inputs.scope }} - - uses: docker/build-push-action@v5 + - uses: docker/build-push-action@v6 id: retry_builder if: steps.main_builder.outcome == 'failure' with: context: . file: ${{ inputs.dockerfile }} - tags: ${{ inputs.tag }} load: true - cache-from: type=gha + cache-from: type=gha,scope=${{ inputs.scope }} - # Workaround for https://github.com/google/sanitizers/issues/1614 . # The underlying issue has been fixed in clang 18.1.3. @@ -47,7 +45,8 @@ runs: $(echo '${{ toJSON(env) }}' | jq -r 'keys[] | "--env \(.) "') \ --volume ${{ github.workspace }}:${{ github.workspace }} \ --workdir ${{ github.workspace }} \ - ${{ inputs.tag }} bash -c " + $(docker images -q | head -n1) \ + bash -c " git config --global --add safe.directory ${{ github.workspace }} ${{ inputs.command }} " diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 690a95ca7..b1142202d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,12 +74,11 @@ jobs: network=host - name: Build container - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: file: ./ci/linux-debian.Dockerfile - tags: ${{ matrix.arch }}-debian-image - cache-from: type=gha - cache-to: type=gha,mode=min + cache-from: type=gha,scope=${{ runner.arch }} + cache-to: type=gha,scope=${{ runner.arch }},mode=min x86_64-debian: name: "x86_64: Linux (Debian stable)" @@ -115,17 +114,20 @@ jobs: CC: ${{ matrix.cc }} steps: - - name: Checkout - uses: actions/checkout@v4 + - &CHECKOUT + name: Checkout + uses: actions/checkout@v5 - - name: CI script + - &CI_SCRIPT_IN_DOCKER + name: CI script env: ${{ matrix.configuration.env_vars }} uses: ./.github/actions/run-in-docker-action with: dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image + command: ./ci/ci.sh - - name: Print logs + - &PRINT_LOGS + name: Print logs uses: ./.github/actions/print-logs if: ${{ !cancelled() }} @@ -137,6 +139,8 @@ jobs: strategy: fail-fast: false matrix: + configuration: + - env_vars: {} cc: - 'i686-linux-gnu-gcc' - 'clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include' @@ -160,24 +164,20 @@ jobs: CC: ${{ matrix.cc }} steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS s390x_debian: name: "s390x (big-endian): Linux (Debian stable, QEMU)" runs-on: ubuntu-latest needs: docker_cache + strategy: + matrix: + configuration: + - env_vars: {} + env: WRAPPER_CMD: 'qemu-s390x' SECP256K1_TEST_ITERS: 16 @@ -200,19 +200,9 @@ jobs: CTIMETESTS: 'no' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} - + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS arm32_debian: name: "ARM32: Linux (Debian stable, QEMU)" @@ -248,19 +238,9 @@ jobs: CTIMETESTS: 'no' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - env: ${{ matrix.configuration.env_vars }} - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS arm64-debian: name: "arm64: Linux (Debian stable)" @@ -290,6 +270,8 @@ jobs: strategy: fail-fast: false matrix: + configuration: + - env_vars: {} cc: - 'gcc' - 'clang' @@ -297,24 +279,20 @@ jobs: - 'clang-snapshot' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: arm64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS ppc64le_debian: name: "ppc64le: Linux (Debian stable, QEMU)" runs-on: ubuntu-latest needs: docker_cache + strategy: + matrix: + configuration: + - env_vars: {} + env: WRAPPER_CMD: 'qemu-ppc64le' SECP256K1_TEST_ITERS: 16 @@ -337,51 +315,35 @@ jobs: CTIMETESTS: 'no' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} - + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS valgrind_debian: - name: "Valgrind ${{ matrix.binary_arch }} (memcheck)" - runs-on: ${{ matrix.runner }} + name: "Valgrind ${{ matrix.configuration.binary_arch }} (memcheck)" + runs-on: ${{ matrix.configuration.runner }} needs: docker_cache strategy: fail-fast: false matrix: - include: - - docker_arch: x64 - runner: ubuntu-latest + configuration: + - runner: ubuntu-latest binary_arch: x64 env_vars: { CC: 'clang', ASM: 'auto' } - - docker_arch: x64 - runner: ubuntu-latest + - runner: ubuntu-latest binary_arch: i686 env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'auto' } - - docker_arch: arm64 - runner: ubuntu-24.04-arm + - runner: ubuntu-24.04-arm binary_arch: arm64 env_vars: { CC: 'clang', ASM: 'auto' } - - docker_arch: x64 - runner: ubuntu-latest + - runner: ubuntu-latest binary_arch: x64 env_vars: { CC: 'clang', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 } - - docker_arch: x64 - runner: ubuntu-latest + - runner: ubuntu-latest binary_arch: i686 env_vars: { CC: 'i686-linux-gnu-gcc', HOST: 'i686-linux-gnu', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 } - - docker_arch: arm64 - runner: ubuntu-24.04-arm + - runner: ubuntu-24.04-arm binary_arch: arm64 env_vars: { CC: 'clang', ASM: 'no', ECMULTGENKB: 2, ECMULTWINDOW: 2 } @@ -407,19 +369,9 @@ jobs: SECP256K1_TEST_ITERS: 2 steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - env: ${{ matrix.env_vars }} - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: ${{ matrix.docker_arch }}-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS sanitizers_debian: name: "UBSan, ASan, LSan" @@ -459,19 +411,9 @@ jobs: SYMBOL_CHECK: 'no' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - env: ${{ matrix.configuration.env_vars }} - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS msan_debian: name: "MSan" @@ -495,6 +437,9 @@ jobs: # when ctime_tests when enabled. CFLAGS: '-fsanitize=memory -fsanitize-recover=memory -fsanitize-memory-param-retval -g' CTIMETESTS: 'no' + cc: + - 'clang' + - 'clang-snapshot' env: ECDH: 'yes' @@ -511,27 +456,16 @@ jobs: ECDSAADAPTOR: 'yes' BPPP: 'yes' SCHNORRSIG_HALFAGG: 'yes' - CC: 'clang' + CC: ${{ matrix.cc }} SECP256K1_TEST_ITERS: 32 ASM: 'no' WITH_VALGRIND: 'no' SYMBOL_CHECK: 'no' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - env: ${{ matrix.configuration.env_vars }} - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} - + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS mingw_debian: name: ${{ matrix.configuration.job_name }} @@ -569,19 +503,9 @@ jobs: HOST: 'i686-w64-mingw32' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - env: ${{ matrix.configuration.env_vars }} - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS x86_64-macos-native: name: "x86_64: macOS Sequoia, Valgrind" @@ -609,8 +533,7 @@ jobs: - BUILD: 'distcheck' steps: - - name: Checkout - uses: actions/checkout@v4 + - *CHECKOUT - name: Install Homebrew packages run: | @@ -620,7 +543,8 @@ jobs: - name: Install and cache Valgrind uses: ./.github/actions/install-homebrew-valgrind - - name: CI script + - &CI_SCRIPT_ON_HOST + name: CI script env: ${{ matrix.env_vars }} run: ./ci/ci.sh @@ -634,9 +558,7 @@ jobs: python3 -m pip install lief python3 ./tools/symbol-check.py .libs/libsecp256k1.dylib - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *PRINT_LOGS arm64-macos-native: name: "ARM64: macOS Sonoma" @@ -665,17 +587,14 @@ jobs: - BUILD: 'distcheck' steps: - - name: Checkout - uses: actions/checkout@v4 + - *CHECKOUT - name: Install Homebrew packages run: | brew install --quiet automake libtool gcc ln -s $(brew --prefix gcc)/bin/gcc-?? /usr/local/bin/gcc - - name: CI script - env: ${{ matrix.env_vars }} - run: ./ci/ci.sh + - *CI_SCRIPT_ON_HOST - name: Symbol check env: @@ -687,10 +606,7 @@ jobs: python3 -m pip install lief python3 ./tools/symbol-check.py .libs/libsecp256k1.dylib - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} - + - *PRINT_LOGS win64-native: name: ${{ matrix.configuration.job_name }} @@ -725,8 +641,7 @@ jobs: cpp_flags: '/DSECP256K1_MSVC_MULH_TEST_OVERRIDE' steps: - - name: Checkout - uses: actions/checkout@v4 + - *CHECKOUT - name: Generate buildsystem run: cmake -E env CFLAGS="/WX ${{ matrix.configuration.cpp_flags }}" cmake -B build -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON ${{ matrix.configuration.cmake_options }} @@ -761,8 +676,7 @@ jobs: runs-on: windows-2022 steps: - - name: Checkout - uses: actions/checkout@v4 + - *CHECKOUT - name: Add cl.exe to PATH uses: ilammy/msvc-dev-cmd@v1 @@ -776,6 +690,11 @@ jobs: runs-on: ubuntu-latest needs: docker_cache + strategy: + matrix: + configuration: + - env_vars: {} + env: CC: 'g++' CFLAGS: '-fpermissive -g' @@ -797,18 +716,9 @@ jobs: SCHNORRSIG_HALFAGG: 'yes' steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: CI script - uses: ./.github/actions/run-in-docker-action - with: - dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image - - - name: Print logs - uses: ./.github/actions/print-logs - if: ${{ !cancelled() }} + - *CHECKOUT + - *CI_SCRIPT_IN_DOCKER + - *PRINT_LOGS cxx_headers_debian: name: "C++ (public headers)" @@ -816,14 +726,12 @@ jobs: needs: docker_cache steps: - - name: Checkout - uses: actions/checkout@v4 + - *CHECKOUT - name: CI script uses: ./.github/actions/run-in-docker-action with: dockerfile: ./ci/linux-debian.Dockerfile - tag: x64-debian-image command: | g++ -Werror include/*.h clang -Werror -x c++-header include/*.h @@ -836,8 +744,7 @@ jobs: options: --user root steps: - - name: Checkout - uses: actions/checkout@v4 + - *CHECKOUT - name: CI script run: | @@ -848,8 +755,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 + - *CHECKOUT - run: ./autogen.sh && ./configure --enable-dev-mode && make distcheck diff --git a/Makefile.am b/Makefile.am index cb96350ef..4191a57f5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,7 +47,10 @@ noinst_HEADERS += src/precomputed_ecmult.h noinst_HEADERS += src/precomputed_ecmult_gen.h noinst_HEADERS += src/assumptions.h noinst_HEADERS += src/checkmem.h +noinst_HEADERS += src/tests_common.h noinst_HEADERS += src/testutil.h +noinst_HEADERS += src/unit_test.h +noinst_HEADERS += src/unit_test.c noinst_HEADERS += src/util.h noinst_HEADERS += src/util_local_visibility.h noinst_HEADERS += src/int128.h @@ -122,7 +125,7 @@ if USE_TESTS TESTS += noverify_tests noinst_PROGRAMS += noverify_tests noverify_tests_SOURCES = src/tests.c -noverify_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) +noverify_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES) $(TEST_DEFINES) noverify_tests_LDADD = $(COMMON_LIB) $(PRECOMPUTED_LIB) noverify_tests_LDFLAGS = -static if !ENABLE_COVERAGE diff --git a/build-aux/m4/bitcoin_secp.m4 b/build-aux/m4/bitcoin_secp.m4 index 048267fa6..1428d4d9b 100644 --- a/build-aux/m4/bitcoin_secp.m4 +++ b/build-aux/m4/bitcoin_secp.m4 @@ -3,7 +3,7 @@ AC_DEFUN([SECP_X86_64_ASM_CHECK],[ AC_MSG_CHECKING(for x86_64 assembly availability) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ - uint64_t a = 11, tmp; + uint64_t a = 11, tmp = 0; __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); ]])], [has_x86_64_asm=yes], [has_x86_64_asm=no]) AC_MSG_RESULT([$has_x86_64_asm]) diff --git a/ci/ci.sh b/ci/ci.sh index c0252493a..15916b79e 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -53,22 +53,6 @@ if [ -n "$WRAPPER_CMD" ]; then $WRAPPER_CMD --version fi -# Workaround for https://bugs.kde.org/show_bug.cgi?id=452758 (fixed in valgrind 3.20.0). -case "${CC:-undefined}" in - clang*) - if [ "$CTIMETESTS" = "yes" ] && [ "$WITH_VALGRIND" = "yes" ] - then - export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4" - else - case "$WRAPPER_CMD" in - valgrind*) - export CFLAGS="${CFLAGS:+$CFLAGS }-gdwarf-4" - ;; - esac - fi - ;; -esac - ./autogen.sh ./configure \ diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile index 1093288ec..a862f1b17 100644 --- a/ci/linux-debian.Dockerfile +++ b/ci/linux-debian.Dockerfile @@ -21,7 +21,7 @@ RUN dpkg --add-architecture i386 && \ # dpkg-dev: to make pkg-config work in cross-builds # llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces -RUN apt-get update && apt-get install --no-install-recommends -y \ +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ git ca-certificates \ make automake libtool pkg-config dpkg-dev valgrind qemu-user \ gcc clang llvm libclang-rt-dev libc6-dbg \ @@ -34,14 +34,15 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ gcc-mingw-w64-i686-win32 wine32 \ python3-full && \ if ! ( dpkg --print-architecture | grep --quiet "arm64" ) ; then \ - apt-get install --no-install-recommends -y \ + DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 ;\ fi && \ apt-get clean && rm -rf /var/lib/apt/lists/* # Build and install gcc snapshot ARG GCC_SNAPSHOT_MAJOR=16 -RUN apt-get update && apt-get install --no-install-recommends -y wget libgmp-dev libmpfr-dev libmpc-dev flex && \ +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ + wget libgmp-dev libmpfr-dev libmpc-dev flex && \ mkdir gcc && cd gcc && \ wget --progress=dot:giga --https-only --recursive --accept '*.tar.xz' --level 1 --no-directories "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}" && \ wget "https://gcc.gnu.org/pub/gcc/snapshots/LATEST-${GCC_SNAPSHOT_MAJOR}/sha512.sum" && \ @@ -62,7 +63,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y wget libgmp-dev # Install clang snapshot, see https://apt.llvm.org/ RUN \ # Setup GPG keys of LLVM repository - apt-get update && apt-get install --no-install-recommends -y wget && \ + apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y wget && \ wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \ # Add repository for this Debian release . /etc/os-release && echo "deb http://apt.llvm.org/${VERSION_CODENAME} llvm-toolchain-${VERSION_CODENAME} main" >> /etc/apt/sources.list && \ @@ -73,7 +74,7 @@ RUN \ # Determine the version number of the LLVM development branch LLVM_VERSION=$(apt-cache search --names-only '^clang-[0-9]+$' | sort -V | tail -1 | cut -f1 -d" " | cut -f2 -d"-" ) && \ # Install - apt-get install --no-install-recommends -y "clang-${LLVM_VERSION}" && \ + DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y "clang-${LLVM_VERSION}" "libclang-rt-${LLVM_VERSION}-dev" && \ # Create symlink ln -s "/usr/bin/clang-${LLVM_VERSION}" /usr/bin/clang-snapshot && \ # Clean up diff --git a/cmake/CheckX86_64Assembly.cmake b/cmake/CheckX86_64Assembly.cmake index ae82cd476..ca18919e0 100644 --- a/cmake/CheckX86_64Assembly.cmake +++ b/cmake/CheckX86_64Assembly.cmake @@ -4,10 +4,11 @@ function(check_x86_64_assembly) check_c_source_compiles(" #include - int main() + int main(void) { - uint64_t a = 11, tmp; + uint64_t a = 11, tmp = 0; __asm__ __volatile__(\"movq $0x100000000,%1; mulq %%rsi\" : \"+a\"(a) : \"S\"(tmp) : \"cc\", \"%rdx\"); + return 0; } " HAVE_X86_64_ASM) set(HAVE_X86_64_ASM ${HAVE_X86_64_ASM} PARENT_SCOPE) diff --git a/configure.ac b/configure.ac index be77c8477..99ef0884f 100644 --- a/configure.ac +++ b/configure.ac @@ -579,6 +579,14 @@ if test x"$enable_experimental" = x"no"; then fi fi +# Check for concurrency support (tests only) +if test "x$enable_tests" != x"no"; then + AC_CHECK_HEADERS([sys/types.h sys/wait.h unistd.h]) + AS_IF([test "x$ac_cv_header_sys_types_h" = xyes && test "x$ac_cv_header_sys_wait_h" = xyes && + test "x$ac_cv_header_unistd_h" = xyes], [TEST_DEFINES="-DSUPPORTS_CONCURRENCY=1"], TEST_DEFINES="") + AC_SUBST(TEST_DEFINES) +fi + ### ### Generate output ### diff --git a/include/secp256k1.h b/include/secp256k1.h index f55a6ab97..b0a13b144 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -261,7 +261,7 @@ SECP256K1_DEPRECATED("Use secp256k1_context_static instead"); * secp256k1_context_create (or secp256k1_context_preallocated_create), which will * take care of performing the self tests. * - * If the tests fail, this function will call the default error handler to abort the + * If the tests fail, this function will call the default error callback to abort the * program (see secp256k1_context_set_error_callback). */ SECP256K1_API void secp256k1_selftest(void); @@ -334,36 +334,37 @@ SECP256K1_API void secp256k1_context_destroy( * an API call. It will only trigger for violations that are mentioned * explicitly in the header. * - * The philosophy is that these shouldn't be dealt with through a - * specific return value, as calling code should not have branches to deal with - * the case that this code itself is broken. + * The philosophy is that these shouldn't be dealt with through a specific + * return value, as calling code should not have branches to deal with the case + * that this code itself is broken. * * On the other hand, during debug stage, one would want to be informed about - * such mistakes, and the default (crashing) may be inadvisable. - * When this callback is triggered, the API function called is guaranteed not - * to cause a crash, though its return value and output arguments are - * undefined. - * - * When this function has not been called (or called with fn==NULL), then the - * default handler will be used. The library provides a default handler which - * writes the message to stderr and calls abort. This default handler can be + * such mistakes, and the default (crashing) may be inadvisable. Should this + * callback return instead of crashing, the return value and output arguments + * of the API function call are undefined. Moreover, the same API call may + * trigger the callback again in this case. + * + * When this function has not been called (or called with fun==NULL), then the + * default callback will be used. The library provides a default callback which + * writes the message to stderr and calls abort. This default callback can be * replaced at link time if the preprocessor macro * USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build * has been configured with --enable-external-default-callbacks. Then the * following two symbols must be provided to link against: * - void secp256k1_default_illegal_callback_fn(const char *message, void *data); * - void secp256k1_default_error_callback_fn(const char *message, void *data); - * The library can call these default handlers even before a proper callback data + * The library may call a default callback even before a proper callback data * pointer could have been set using secp256k1_context_set_illegal_callback or * secp256k1_context_set_error_callback, e.g., when the creation of a context - * fails. In this case, the corresponding default handler will be called with + * fails. In this case, the corresponding default callback will be called with * the data pointer argument set to NULL. * * Args: ctx: pointer to a context object. * In: fun: pointer to a function to call when an illegal argument is * passed to the API, taking a message and an opaque pointer. - * (NULL restores the default handler.) - * data: the opaque pointer to pass to fun above, must be NULL for the default handler. + * (NULL restores the default callback.) + * data: the opaque pointer to pass to fun above, must be NULL for the + * default callback. * * See also secp256k1_context_set_error_callback. */ @@ -380,8 +381,8 @@ SECP256K1_API void secp256k1_context_set_illegal_callback( * to abort the program. * * This can only trigger in case of a hardware failure, miscompilation, - * memory corruption, serious bug in the library, or other error would can - * otherwise result in undefined behaviour. It will not trigger due to mere + * memory corruption, serious bug in the library, or other error that would + * result in undefined behaviour. It will not trigger due to mere * incorrect usage of the API (see secp256k1_context_set_illegal_callback * for that). After this callback returns, anything may happen, including * crashing. @@ -389,9 +390,10 @@ SECP256K1_API void secp256k1_context_set_illegal_callback( * Args: ctx: pointer to a context object. * In: fun: pointer to a function to call when an internal error occurs, * taking a message and an opaque pointer (NULL restores the - * default handler, see secp256k1_context_set_illegal_callback + * default callback, see secp256k1_context_set_illegal_callback * for details). - * data: the opaque pointer to pass to fun above, must be NULL for the default handler. + * data: the opaque pointer to pass to fun above, must be NULL for the + * default callback. * * See also secp256k1_context_set_illegal_callback. */ diff --git a/include/secp256k1_recovery.h b/include/secp256k1_recovery.h index 93a2e4ccb..2430f9939 100644 --- a/include/secp256k1_recovery.h +++ b/include/secp256k1_recovery.h @@ -92,7 +92,17 @@ SECP256K1_API int secp256k1_ecdsa_sign_recoverable( /** Recover an ECDSA public key from a signature. * - * Returns: 1: public key successfully recovered (which guarantees a correct signature). + * Successful public key recovery guarantees that the signature, after normalization, + * passes `secp256k1_ecdsa_verify`. Thus, explicit verification is not necessary. + * + * However, a recoverable signature that successfully passes `secp256k1_ecdsa_recover`, + * when converted to a non-recoverable signature (using + * `secp256k1_ecdsa_recoverable_signature_convert`), is not guaranteed to be + * normalized and thus not guaranteed to pass `secp256k1_ecdsa_verify`. If a + * normalized signature is required, call `secp256k1_ecdsa_signature_normalize` + * after `secp256k1_ecdsa_recoverable_signature_convert`. + * + * Returns: 1: public key successfully recovered * 0: otherwise. * Args: ctx: pointer to a context object. * Out: pubkey: pointer to the recovered public key. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 52587ada7..cf371e9e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -186,15 +186,27 @@ if(SECP256K1_BUILD_BENCHMARK) endif() if(SECP256K1_BUILD_TESTS) + include(CheckIncludeFile) + check_include_file(sys/types.h HAVE_SYS_TYPES_H) + check_include_file(sys/wait.h HAVE_SYS_WAIT_H) + check_include_file(unistd.h HAVE_UNISTD_H) + + set(TEST_DEFINITIONS "") + if(HAVE_SYS_TYPES_H AND HAVE_SYS_WAIT_H AND HAVE_UNISTD_H) + list(APPEND TEST_DEFINITIONS SUPPORTS_CONCURRENCY=1) + endif() + add_executable(noverify_tests tests.c) target_link_libraries(noverify_tests secp256k1_precomputed secp256k1_asm) + target_compile_definitions(noverify_tests PRIVATE ${TEST_DEFINITIONS}) add_test(NAME secp256k1_noverify_tests COMMAND noverify_tests) if(NOT CMAKE_BUILD_TYPE STREQUAL "Coverage") add_executable(tests tests.c) - target_compile_definitions(tests PRIVATE VERIFY) + target_compile_definitions(tests PRIVATE VERIFY ${TEST_DEFINITIONS}) target_link_libraries(tests secp256k1_precomputed secp256k1_asm) add_test(NAME secp256k1_tests COMMAND tests) endif() + unset(TEST_DEFINITIONS) endif() if(SECP256K1_BUILD_EXHAUSTIVE_TESTS) diff --git a/src/bench.h b/src/bench.h index 232fb35fc..4e8e961b6 100644 --- a/src/bench.h +++ b/src/bench.h @@ -12,27 +12,7 @@ #include #include -#if (defined(_MSC_VER) && _MSC_VER >= 1900) -# include -#else -# include -#endif - -static int64_t gettime_i64(void) { -#if (defined(_MSC_VER) && _MSC_VER >= 1900) - /* C11 way to get wallclock time */ - struct timespec tv; - if (!timespec_get(&tv, TIME_UTC)) { - fputs("timespec_get failed!", stderr); - exit(EXIT_FAILURE); - } - return (int64_t)tv.tv_nsec / 1000 + (int64_t)tv.tv_sec * 1000000LL; -#else - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL; -#endif -} +#include "tests_common.h" #define FP_EXP (6) #define FP_MULT (1000000LL) diff --git a/src/checkmem.h b/src/checkmem.h index 7e333ce5f..08eae47d7 100644 --- a/src/checkmem.h +++ b/src/checkmem.h @@ -48,7 +48,17 @@ # if __has_feature(memory_sanitizer) # include # define SECP256K1_CHECKMEM_ENABLED 1 -# define SECP256K1_CHECKMEM_UNDEFINE(p, len) __msan_allocated_memory((p), (len)) +# if defined(__clang__) && ((__clang_major__ == 21 && __clang_minor__ >= 1) || __clang_major__ >= 22) +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) do { \ + /* Work around https://github.com/llvm/llvm-project/issues/160094 */ \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wuninitialized-const-pointer\"") \ + __msan_allocated_memory((p), (len)); \ + _Pragma("clang diagnostic pop") \ + } while(0) +# else +# define SECP256K1_CHECKMEM_UNDEFINE(p, len) __msan_allocated_memory((p), (len)) +# endif # define SECP256K1_CHECKMEM_DEFINE(p, len) __msan_unpoison((p), (len)) # define SECP256K1_CHECKMEM_MSAN_DEFINE(p, len) __msan_unpoison((p), (len)) # define SECP256K1_CHECKMEM_CHECK(p, len) __msan_check_mem_is_initialized((p), (len)) diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index b943fe2ab..2159eed5e 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -277,8 +277,8 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 /* Cleanup. */ secp256k1_fe_clear(&neg); secp256k1_ge_clear(&add); - secp256k1_memclear(&adds, sizeof(adds)); - secp256k1_memclear(&recoded, sizeof(recoded)); + secp256k1_memclear_explicit(&adds, sizeof(adds)); + secp256k1_memclear_explicit(&recoded, sizeof(recoded)); } /* Setup blinding values for secp256k1_ecmult_gen. */ @@ -310,7 +310,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const VERIFY_CHECK(seed32 != NULL); memcpy(keydata + 32, seed32, 32); secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64); - secp256k1_memclear(keydata, sizeof(keydata)); + secp256k1_memclear_explicit(keydata, sizeof(keydata)); /* Compute projective blinding factor (cannot be 0). */ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); @@ -331,7 +331,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_ge_set_gej(&ctx->ge_offset, &gb); /* Clean up. */ - secp256k1_memclear(nonce32, sizeof(nonce32)); + secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); secp256k1_scalar_clear(&b); secp256k1_gej_clear(&gb); secp256k1_fe_clear(&f); diff --git a/src/field_impl.h b/src/field_impl.h index 896507a3a..7aa7de431 100644 --- a/src/field_impl.h +++ b/src/field_impl.h @@ -19,7 +19,7 @@ #endif SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { - secp256k1_memclear(a, sizeof(secp256k1_fe)); + secp256k1_memclear_explicit(a, sizeof(secp256k1_fe)); } SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { diff --git a/src/group_impl.h b/src/group_impl.h index 326c67ac1..5dc9d1248 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -337,11 +337,11 @@ static void secp256k1_ge_set_infinity(secp256k1_ge *r) { } static void secp256k1_gej_clear(secp256k1_gej *r) { - secp256k1_memclear(r, sizeof(secp256k1_gej)); + secp256k1_memclear_explicit(r, sizeof(secp256k1_gej)); } static void secp256k1_ge_clear(secp256k1_ge *r) { - secp256k1_memclear(r, sizeof(secp256k1_ge)); + secp256k1_memclear_explicit(r, sizeof(secp256k1_ge)); } static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { diff --git a/src/hash_impl.h b/src/hash_impl.h index 1065acd64..434191777 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -172,7 +172,7 @@ static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const uns } static void secp256k1_sha256_clear(secp256k1_sha256 *hash) { - secp256k1_memclear(hash, sizeof(*hash)); + secp256k1_memclear_explicit(hash, sizeof(*hash)); } static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { @@ -200,7 +200,7 @@ static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const rkey[n] ^= 0x5c ^ 0x36; } secp256k1_sha256_write(&hash->inner, rkey, sizeof(rkey)); - secp256k1_memclear(rkey, sizeof(rkey)); + secp256k1_memclear_explicit(rkey, sizeof(rkey)); } static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size) { @@ -211,12 +211,12 @@ static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned unsigned char temp[32]; secp256k1_sha256_finalize(&hash->inner, temp); secp256k1_sha256_write(&hash->outer, temp, 32); - secp256k1_memclear(temp, sizeof(temp)); + secp256k1_memclear_explicit(temp, sizeof(temp)); secp256k1_sha256_finalize(&hash->outer, out32); } static void secp256k1_hmac_sha256_clear(secp256k1_hmac_sha256 *hash) { - secp256k1_memclear(hash, sizeof(*hash)); + secp256k1_memclear_explicit(hash, sizeof(*hash)); } static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) { @@ -285,7 +285,7 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 } static void secp256k1_rfc6979_hmac_sha256_clear(secp256k1_rfc6979_hmac_sha256 *rng) { - secp256k1_memclear(rng, sizeof(*rng)); + secp256k1_memclear_explicit(rng, sizeof(*rng)); } #undef Round diff --git a/src/modules/bppp/tests_impl.h b/src/modules/bppp/tests_impl.h index 6764694cb..85d1c1ca7 100644 --- a/src/modules/bppp/tests_impl.h +++ b/src/modules/bppp/tests_impl.h @@ -15,6 +15,7 @@ #include "bppp_transcript_impl.h" #include "test_vectors/verify.h" #include "test_vectors/prove.h" +#include "../../unit_test.h" static void test_bppp_generators_api(void) { secp256k1_bppp_generators *gens; @@ -649,15 +650,7 @@ static void norm_arg_prove_vectors(void) { #undef IDX_TO_TEST -static void run_bppp_tests(void) { - test_log_exp(); - test_norm_util_helpers(); - test_serialize_two_points(); - test_bppp_generators_api(); - test_bppp_generators_fixed(); - test_bppp_tagged_hash(); - - norm_arg_verify_zero_len(); +static void norm_arg_test_all(void) { norm_arg_test(1, 1); norm_arg_test(1, 64); norm_arg_test(64, 1); @@ -665,9 +658,20 @@ static void run_bppp_tests(void) { norm_arg_test(32, 64); norm_arg_test(64, 32); norm_arg_test(64, 64); - - norm_arg_verify_vectors(); - norm_arg_prove_vectors(); } +/* --- Test registry --- */ +static const struct tf_test_entry tests_bppp[] = { + CASE1(test_log_exp), + CASE1(test_norm_util_helpers), + CASE1(test_serialize_two_points), + CASE1(test_bppp_generators_api), + CASE1(test_bppp_generators_fixed), + CASE1(test_bppp_tagged_hash), + CASE1(norm_arg_verify_zero_len), + CASE1(norm_arg_test_all), + CASE1(norm_arg_verify_vectors), + CASE1(norm_arg_prove_vectors), +}; + #endif diff --git a/src/modules/ecdh/bench_impl.h b/src/modules/ecdh/bench_impl.h index c23aaa94d..8924e1fab 100644 --- a/src/modules/ecdh/bench_impl.h +++ b/src/modules/ecdh/bench_impl.h @@ -10,7 +10,7 @@ #include "../../../include/secp256k1_ecdh.h" typedef struct { - secp256k1_context *ctx; + const secp256k1_context *ctx; secp256k1_pubkey point; unsigned char scalar[32]; } bench_ecdh_data; @@ -46,12 +46,9 @@ static void run_ecdh_bench(int iters, int argc, char** argv) { bench_ecdh_data data; int d = argc == 1; - /* create a context with no capabilities */ - data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); + data.ctx = secp256k1_context_static; if (d || have_flag(argc, argv, "ecdh")) run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, iters); - - secp256k1_context_destroy(data.ctx); } #endif /* SECP256K1_MODULE_ECDH_BENCH_H */ diff --git a/src/modules/ecdh/main_impl.h b/src/modules/ecdh/main_impl.h index 842b5359e..9f2dfdd56 100644 --- a/src/modules/ecdh/main_impl.h +++ b/src/modules/ecdh/main_impl.h @@ -62,8 +62,8 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const se ret = hashfp(output, x, y, data); - secp256k1_memclear(x, sizeof(x)); - secp256k1_memclear(y, sizeof(y)); + secp256k1_memclear_explicit(x, sizeof(x)); + secp256k1_memclear_explicit(y, sizeof(y)); secp256k1_scalar_clear(&s); secp256k1_ge_clear(&pt); secp256k1_gej_clear(&res); diff --git a/src/modules/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h index 6888f18c6..cb1d953d2 100644 --- a/src/modules/ecdh/tests_impl.h +++ b/src/modules/ecdh/tests_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_MODULE_ECDH_TESTS_H #define SECP256K1_MODULE_ECDH_TESTS_H +#include "../../unit_test.h" + static int ecdh_hash_function_test_xpassthru(unsigned char *output, const unsigned char *x, const unsigned char *y, void *data) { (void)y; (void)data; @@ -90,12 +92,7 @@ static void test_ecdh_generator_basepoint(void) { static void test_bad_scalar(void) { unsigned char s_zero[32] = { 0 }; - unsigned char s_overflow[32] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, - 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 - }; + unsigned char s_overflow[32] = { 0 }; unsigned char s_rand[32] = { 0 }; unsigned char output[32]; secp256k1_scalar rand; @@ -107,6 +104,7 @@ static void test_bad_scalar(void) { CHECK(secp256k1_ec_pubkey_create(CTX, &point, s_rand) == 1); /* Try to multiply it by bad values */ + memcpy(s_overflow, secp256k1_group_order_bytes, 32); CHECK(secp256k1_ecdh(CTX, output, &point, s_zero, NULL, NULL) == 0); CHECK(secp256k1_ecdh(CTX, output, &point, s_overflow, NULL, NULL) == 0); /* ...and a good one */ @@ -182,12 +180,13 @@ static void test_ecdh_wycheproof(void) { } } -static void run_ecdh_tests(void) { - test_ecdh_api(); - test_ecdh_generator_basepoint(); - test_bad_scalar(); - test_result_basepoint(); - test_ecdh_wycheproof(); -} +/* --- Test registry --- */ +static const struct tf_test_entry tests_ecdh[] = { + CASE1(test_ecdh_api), + CASE1(test_ecdh_generator_basepoint), + CASE1(test_bad_scalar), + CASE1(test_result_basepoint), + CASE1(test_ecdh_wycheproof), +}; #endif /* SECP256K1_MODULE_ECDH_TESTS_H */ diff --git a/src/modules/ecdsa_adaptor/tests_impl.h b/src/modules/ecdsa_adaptor/tests_impl.h index a33602238..e2293adc6 100644 --- a/src/modules/ecdsa_adaptor/tests_impl.h +++ b/src/modules/ecdsa_adaptor/tests_impl.h @@ -2,6 +2,7 @@ #define SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H #include "../../../include/secp256k1_ecdsa_adaptor.h" +#include "../../unit_test.h" static void rand_scalar(secp256k1_scalar *scalar) { unsigned char buf32[32]; @@ -27,7 +28,7 @@ static void dleq_nonce_bitflip(unsigned char **args, size_t n_flip, size_t n_byt CHECK(secp256k1_scalar_eq(&k1, &k2) == 0); } -static void dleq_tests(void) { +static void dleq_tests_internal(void) { secp256k1_scalar s, e, sk, k; secp256k1_ge gen2, p1, p2; unsigned char *args[5]; @@ -850,7 +851,7 @@ static void test_ecdsa_adaptor_api(void) { CHECK_ILLEGAL(CTX, secp256k1_ecdsa_adaptor_recover(CTX, deckey, &sig, asig, &zero_pk)); } -static void adaptor_tests(void) { +static void adaptor_tests_internal(void) { unsigned char seckey[32]; secp256k1_pubkey pubkey; unsigned char msg[32]; @@ -1050,7 +1051,7 @@ static void adaptor_tests(void) { } } -static void multi_hop_lock_tests(void) { +static void multi_hop_lock_tests_internal(void) { unsigned char seckey_a[32]; unsigned char seckey_b[32]; unsigned char pop[32]; @@ -1124,21 +1125,18 @@ static void multi_hop_lock_tests(void) { CHECK(secp256k1_memcmp_var(buf, pop, 32) == 0); } -static void run_ecdsa_adaptor_tests(void) { - int i; - run_nonce_function_ecdsa_adaptor_tests(); - - test_ecdsa_adaptor_api(); - test_ecdsa_adaptor_spec_vectors(); - for (i = 0; i < COUNT; i++) { - dleq_tests(); - } - for (i = 0; i < COUNT; i++) { - adaptor_tests(); - } - for (i = 0; i < COUNT; i++) { - multi_hop_lock_tests(); - } -} +/* --- Test registry --- */ +REPEAT_TEST(dleq_tests) +REPEAT_TEST(adaptor_tests) +REPEAT_TEST(multi_hop_lock_tests) + +static const struct tf_test_entry tests_ecdsa_adaptor[] = { + CASE1(run_nonce_function_ecdsa_adaptor_tests), + CASE1(test_ecdsa_adaptor_api), + CASE1(test_ecdsa_adaptor_spec_vectors), + CASE1(dleq_tests), + CASE1(adaptor_tests), + CASE1(multi_hop_lock_tests), +}; #endif /* SECP256K1_MODULE_ECDSA_ADAPTOR_TESTS_H */ diff --git a/src/modules/ecdsa_s2c/main_impl.h b/src/modules/ecdsa_s2c/main_impl.h index 471ae8fb1..59a4cdfa6 100644 --- a/src/modules/ecdsa_s2c/main_impl.h +++ b/src/modules/ecdsa_s2c/main_impl.h @@ -182,7 +182,7 @@ int secp256k1_ecdsa_anti_exfil_signer_commit(const secp256k1_context* ctx, secp2 secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); secp256k1_ge_set_gej(&r, &rj); secp256k1_ecdsa_s2c_opening_save(opening, &r); - secp256k1_memclear(nonce32, 32); + secp256k1_memclear_explicit(nonce32, 32); secp256k1_scalar_clear(&k); return 1; } diff --git a/src/modules/ecdsa_s2c/tests_impl.h b/src/modules/ecdsa_s2c/tests_impl.h index f84443f31..d13e51647 100644 --- a/src/modules/ecdsa_s2c/tests_impl.h +++ b/src/modules/ecdsa_s2c/tests_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_ECDSA_S2C_TESTS_H #include "../../../include/secp256k1_ecdsa_s2c.h" +#include "../../unit_test.h" static void test_ecdsa_s2c_tagged_hash(void) { unsigned char tag_data[] = {'s', '2', 'c', '/', 'e', 'c', 'd', 's', 'a', '/', 'd', 'a', 't', 'a'}; @@ -323,15 +324,15 @@ static void test_ecdsa_anti_exfil(void) { } } -static void run_ecdsa_s2c_tests(void) { - run_s2c_opening_test(); - test_ecdsa_s2c_tagged_hash(); - test_ecdsa_s2c_api(); - test_ecdsa_s2c_fixed_vectors(); - test_ecdsa_s2c_sign_verify(); - - test_ecdsa_anti_exfil_signer_commit(); - test_ecdsa_anti_exfil(); -} +/* --- Test registry --- */ +static const struct tf_test_entry tests_ecdsa_s2c[] = { + CASE1(run_s2c_opening_test), + CASE1(test_ecdsa_s2c_tagged_hash), + CASE1(test_ecdsa_s2c_api), + CASE1(test_ecdsa_s2c_fixed_vectors), + CASE1(test_ecdsa_s2c_sign_verify), + CASE1(test_ecdsa_anti_exfil_signer_commit), + CASE1(test_ecdsa_anti_exfil) +}; #endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */ diff --git a/src/modules/ellswift/main_impl.h b/src/modules/ellswift/main_impl.h index 745a96913..0d13f7342 100644 --- a/src/modules/ellswift/main_impl.h +++ b/src/modules/ellswift/main_impl.h @@ -582,7 +582,7 @@ int secp256k1_ellswift_xdh(const secp256k1_context *ctx, unsigned char *output, /* Invoke hasher */ ret = hashfp(output, sx, ell_a64, ell_b64, data); - secp256k1_memclear(sx, sizeof(sx)); + secp256k1_memclear_explicit(sx, sizeof(sx)); secp256k1_fe_clear(&px); secp256k1_scalar_clear(&s); diff --git a/src/modules/ellswift/tests_impl.h b/src/modules/ellswift/tests_impl.h index b90fd0ab8..63c36e7ff 100644 --- a/src/modules/ellswift/tests_impl.h +++ b/src/modules/ellswift/tests_impl.h @@ -7,6 +7,7 @@ #define SECP256K1_MODULE_ELLSWIFT_TESTS_H #include "../../../include/secp256k1_ellswift.h" +#include "../../unit_test.h" struct ellswift_xswiftec_inv_test { int enc_bitmap; @@ -433,4 +434,10 @@ void run_ellswift_tests(void) { } } +/* --- Test registry --- */ +/* TODO: subdivide test in cases */ +static const struct tf_test_entry tests_ellswift[] = { + CASE(ellswift_tests), +}; + #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index ab4ef4a74..abebd1106 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_EXTRAKEYS_TESTS_H #include "../../../include/secp256k1_extrakeys.h" +#include "../../unit_test.h" static void test_xonly_pubkey(void) { secp256k1_pubkey pk; @@ -467,17 +468,17 @@ static void test_keypair_add(void) { } } -static void run_extrakeys_tests(void) { +/* --- Test registry --- */ +static const struct tf_test_entry tests_extrakeys[] = { /* xonly key test cases */ - test_xonly_pubkey(); - test_xonly_pubkey_tweak(); - test_xonly_pubkey_tweak_check(); - test_xonly_pubkey_tweak_recursive(); - test_xonly_pubkey_comparison(); - + CASE1(test_xonly_pubkey), + CASE1(test_xonly_pubkey_tweak), + CASE1(test_xonly_pubkey_tweak_check), + CASE1(test_xonly_pubkey_tweak_recursive), + CASE1(test_xonly_pubkey_comparison), /* keypair tests */ - test_keypair(); - test_keypair_add(); -} + CASE1(test_keypair), + CASE1(test_keypair_add), +}; #endif diff --git a/src/modules/generator/pedersen_impl.h b/src/modules/generator/pedersen_impl.h index f5526eb6a..7d2a8a275 100644 --- a/src/modules/generator/pedersen_impl.h +++ b/src/modules/generator/pedersen_impl.h @@ -28,7 +28,7 @@ static void secp256k1_pedersen_scalar_set_u64(secp256k1_scalar *sec, uint64_t va value <<= 8; } secp256k1_scalar_set_b32(sec, data, NULL); - secp256k1_memclear(data, 32); + secp256k1_memclear_explicit(data, 32); } static void secp256k1_pedersen_ecmult_small(secp256k1_gej *r, uint64_t gn, const secp256k1_ge* genp) { diff --git a/src/modules/generator/tests_impl.h b/src/modules/generator/tests_impl.h index bd8a0d928..c9f60c0f6 100644 --- a/src/modules/generator/tests_impl.h +++ b/src/modules/generator/tests_impl.h @@ -14,6 +14,7 @@ #include "../../scalar.h" #include "../../testrand.h" #include "../../util.h" +#include "../../unit_test.h" #include "../../../include/secp256k1_generator.h" @@ -228,7 +229,7 @@ static void test_pedersen_api(void) { CHECK_ILLEGAL(CTX, secp256k1_pedersen_blind_generator_blind_sum(CTX, &val, &blind_ptr, NULL, 1, 0)); } -static void test_pedersen(void) { +static void test_pedersen_internal(void) { secp256k1_pedersen_commitment commits[19]; const secp256k1_pedersen_commitment *cptr[19]; unsigned char blinds[32*19]; @@ -310,19 +311,17 @@ static void test_pedersen_commitment_fixed_vector(void) { CHECK(!secp256k1_pedersen_commitment_parse(CTX, &parse, result)); } - -static void run_generator_tests(void) { - int i; - - test_shallue_van_de_woestijne(); - test_generator_fixed_vector(); - test_generator_api(); - test_generator_generate(); - test_pedersen_api(); - test_pedersen_commitment_fixed_vector(); - for (i = 0; i < COUNT / 2 + 1; i++) { - test_pedersen(); - } -} +/* --- Test registry --- */ +REPEAT_TEST(test_pedersen) + +static const struct tf_test_entry tests_generator[] = { + CASE1(test_shallue_van_de_woestijne), + CASE1(test_generator_fixed_vector), + CASE1(test_generator_api), + CASE1(test_generator_generate), + CASE1(test_pedersen), + CASE1(test_pedersen_api), + CASE1(test_pedersen_commitment_fixed_vector), +}; #endif diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 8e18c151a..d698442ea 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -385,10 +385,10 @@ static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned c secp256k1_scalar_set_b32(&k[i], buf, NULL); /* Attempt to erase secret data */ - secp256k1_memclear(buf, sizeof(buf)); + secp256k1_memclear_explicit(buf, sizeof(buf)); secp256k1_sha256_clear(&sha_tmp); } - secp256k1_memclear(rand, sizeof(rand)); + secp256k1_memclear_explicit(rand, sizeof(rand)); secp256k1_sha256_clear(&sha); } @@ -518,7 +518,7 @@ int secp256k1_musig_nonce_gen_counter(const secp256k1_context* ctx, secp256k1_mu if (!secp256k1_musig_nonce_gen_internal(ctx, secnonce, pubnonce, buf, seckey, &pubkey, msg32, keyagg_cache, extra_input32)) { return 0; } - secp256k1_memclear(seckey, sizeof(seckey)); + secp256k1_memclear_explicit(seckey, sizeof(seckey)); return 1; } @@ -691,7 +691,7 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p ret = secp256k1_musig_secnonce_load(ctx, k, &pk, secnonce); /* Set nonce to zero to avoid nonce reuse. This will cause subsequent calls * of this function to fail */ - memset(secnonce, 0, sizeof(*secnonce)); + secp256k1_memzero_explicit(secnonce, sizeof(*secnonce)); if (!ret) { secp256k1_musig_partial_sign_clear(&sk, k); return 0; diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index a9a4f275d..49ff03935 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -20,6 +20,7 @@ #include "../../group.h" #include "../../hash.h" #include "../../util.h" +#include "../../unit_test.h" #include "vectors.h" @@ -36,7 +37,7 @@ static int create_keypair_and_pk(secp256k1_keypair *keypair, secp256k1_pubkey *p /* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig aggregate, sign, verify * test. */ -static void musig_simple_test(void) { +static void musig_simple_test_internal(void) { unsigned char sk[2][32]; secp256k1_keypair keypair[2]; secp256k1_musig_pubnonce pubnonce[2]; @@ -591,7 +592,7 @@ static void musig_nonce_test(void) { } } -static void scriptless_atomic_swap(void) { +static void scriptless_atomic_swap_internal(void) { /* Throughout this test "a" and "b" refer to two hypothetical blockchains, * while the indices 0 and 1 refer to the two signers. Here signer 0 is * sending a-coins to signer 1, while signer 1 is sending b-coins to signer @@ -777,7 +778,7 @@ static void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const /* Create aggregate public key P[0], tweak multiple times (using xonly and * plain tweaking) and test signing. */ -static void musig_tweak_test(void) { +static void musig_tweak_test_internal(void) { unsigned char sk[2][32]; secp256k1_pubkey pk[2]; const secp256k1_pubkey *pk_ptr[2]; @@ -1262,29 +1263,26 @@ static void musig_test_static_nonce_gen_counter(void) { CHECK(secp256k1_memcmp_var(pubnonce66, expected_pubnonce, sizeof(pubnonce66)) == 0); } -static void run_musig_tests(void) { - int i; - - for (i = 0; i < COUNT; i++) { - musig_simple_test(); - } - musig_api_tests(); - musig_nonce_test(); - for (i = 0; i < COUNT; i++) { - /* Run multiple times to ensure that pk and nonce have different y - * parities */ - scriptless_atomic_swap(); - musig_tweak_test(); - } - sha256_tag_test(); - musig_test_vectors_keyagg(); - musig_test_vectors_noncegen(); - musig_test_vectors_nonceagg(); - musig_test_vectors_signverify(); - musig_test_vectors_tweak(); - musig_test_vectors_sigagg(); - - musig_test_static_nonce_gen_counter(); -} +/* --- Test registry --- */ +REPEAT_TEST(musig_simple_test) +/* Run multiple times to ensure that pk and nonce have different y parities */ +REPEAT_TEST(scriptless_atomic_swap) +REPEAT_TEST(musig_tweak_test) + +static const struct tf_test_entry tests_musig[] = { + CASE1(musig_simple_test), + CASE1(musig_api_tests), + CASE1(musig_nonce_test), + CASE1(scriptless_atomic_swap), + CASE1(musig_tweak_test), + CASE1(sha256_tag_test), + CASE1(musig_test_vectors_keyagg), + CASE1(musig_test_vectors_noncegen), + CASE1(musig_test_vectors_nonceagg), + CASE1(musig_test_vectors_signverify), + CASE1(musig_test_vectors_tweak), + CASE1(musig_test_vectors_sigagg), + CASE1(musig_test_static_nonce_gen_counter), +}; #endif diff --git a/src/modules/rangeproof/borromean_impl.h b/src/modules/rangeproof/borromean_impl.h index 3ca861084..4906ce85f 100644 --- a/src/modules/rangeproof/borromean_impl.h +++ b/src/modules/rangeproof/borromean_impl.h @@ -192,7 +192,7 @@ int secp256k1_borromean_sign(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar_clear(&ens); secp256k1_ge_clear(&rge); secp256k1_gej_clear(&rgej); - secp256k1_memclear(tmp, 33); + secp256k1_memclear_explicit(tmp, 33); return 1; } diff --git a/src/modules/rangeproof/rangeproof_impl.h b/src/modules/rangeproof/rangeproof_impl.h index fc12ad4f4..476da5eb0 100644 --- a/src/modules/rangeproof/rangeproof_impl.h +++ b/src/modules/rangeproof/rangeproof_impl.h @@ -107,7 +107,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec, secp256k1_rfc6979_hmac_sha256_finalize(&rng); secp256k1_rfc6979_hmac_sha256_clear(&rng); secp256k1_scalar_clear(&acc); - secp256k1_memclear(tmp, 32); + secp256k1_memclear_explicit(tmp, 32); return ret; } @@ -270,7 +270,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len, genp)) { return 0; } - secp256k1_memclear(prep, 4096); + secp256k1_memclear_explicit(prep, 4096); for (i = 0; i < rings; i++) { /* Sign will overwrite the non-forged signature, move that random value into the nonce. */ k[i] = s[i * 4 + secidx[i]]; @@ -332,7 +332,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul } VERIFY_CHECK(len <= *plen); *plen = len; - secp256k1_memclear(prep, 4096); + secp256k1_memclear_explicit(prep, 4096); return 1; } @@ -473,7 +473,7 @@ SECP256K1_INLINE static int secp256k1_rangeproof_rewind_inner(secp256k1_scalar * } } *mlen = offset; - secp256k1_memclear(prep, 4096); + secp256k1_memclear_explicit(prep, 4096); for (i = 0; i < 128; i++) { secp256k1_scalar_clear(&s_orig[i]); } diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index e0686f904..74d9f3fdb 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -13,10 +13,11 @@ #include "../../scalar.h" #include "../../testrand.h" #include "../../util.h" +#include "../../unit_test.h" #include "../../../include/secp256k1_rangeproof.h" -static void test_rangeproof_api(void) { +static void test_rangeproof_api_internal(void) { unsigned char proof[5134]; unsigned char blind[32]; secp256k1_pedersen_commitment commit; @@ -121,7 +122,7 @@ static void test_rangeproof_api(void) { CHECK(secp256k1_rangeproof_max_size(CTX, UINT64_MAX, 0) == 5134); } -static void test_borromean(void) { +static void test_borromean_internal(void) { unsigned char e0[32]; secp256k1_scalar s[64]; secp256k1_gej pubs[64]; @@ -1346,24 +1347,25 @@ static void test_rangeproof_fixed_vectors_reproducible(void) { } } -static void run_rangeproof_tests(void) { - int i; - for (i = 0; i < COUNT; i++) { - test_rangeproof_api(); - } - +static void test_single_value_proof_all(void) { test_single_value_proof(0); test_single_value_proof(12345678); test_single_value_proof(UINT64_MAX); - - test_rangeproof_fixed_vectors(); - test_rangeproof_fixed_vectors_reproducible(); - for (i = 0; i < COUNT / 2 + 1; i++) { - test_borromean(); - } - test_rangeproof(); - test_rangeproof_null_blinder(); - test_multiple_generators(); } +/* --- Test registry --- */ +REPEAT_TEST(test_rangeproof_api) +REPEAT_TEST(test_borromean) + +static const struct tf_test_entry tests_rangeproof[] = { + CASE1(test_rangeproof_api), + CASE1(test_single_value_proof_all), + CASE1(test_rangeproof_fixed_vectors), + CASE1(test_rangeproof_fixed_vectors_reproducible), + CASE1(test_borromean), + CASE1(test_rangeproof), + CASE1(test_rangeproof_null_blinder), + CASE1(test_multiple_generators), +}; + #endif diff --git a/src/modules/recovery/tests_impl.h b/src/modules/recovery/tests_impl.h index 7a28a3ce6..09554a242 100644 --- a/src/modules/recovery/tests_impl.h +++ b/src/modules/recovery/tests_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_MODULE_RECOVERY_TESTS_H #define SECP256K1_MODULE_RECOVERY_TESTS_H +#include "../../unit_test.h" + static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { (void) msg32; (void) key32; @@ -28,7 +30,7 @@ static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned c return testrand_bits(1); } -static void test_ecdsa_recovery_api(void) { +static void test_ecdsa_recovery_api_internal(void) { /* Setup contexts that just count errors */ secp256k1_pubkey pubkey; secp256k1_pubkey recpubkey; @@ -92,7 +94,7 @@ static void test_ecdsa_recovery_api(void) { CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(CTX, &recsig, sig, recid) == 0); } -static void test_ecdsa_recovery_end_to_end(void) { +static void test_ecdsa_recovery_end_to_end_internal(void) { unsigned char extra[32] = {0x00}; unsigned char privkey[32]; unsigned char message[32]; @@ -324,15 +326,14 @@ static void test_ecdsa_recovery_edge_cases(void) { } } -static void run_recovery_tests(void) { - int i; - for (i = 0; i < COUNT; i++) { - test_ecdsa_recovery_api(); - } - for (i = 0; i < 64*COUNT; i++) { - test_ecdsa_recovery_end_to_end(); - } - test_ecdsa_recovery_edge_cases(); -} +/* --- Test registry --- */ +REPEAT_TEST(test_ecdsa_recovery_api) +REPEAT_TEST_MULT(test_ecdsa_recovery_end_to_end, 64) + +static const struct tf_test_entry tests_recovery[] = { + CASE1(test_ecdsa_recovery_api), + CASE1(test_ecdsa_recovery_end_to_end), + CASE1(test_ecdsa_recovery_edge_cases) +}; #endif /* SECP256K1_MODULE_RECOVERY_TESTS_H */ diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 13d924491..b410b19ec 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -94,7 +94,7 @@ static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *ms secp256k1_sha256_write(&sha, msg, msglen); secp256k1_sha256_finalize(&sha, nonce32); secp256k1_sha256_clear(&sha); - secp256k1_memclear(masked_key, sizeof(masked_key)); + secp256k1_memclear_explicit(masked_key, sizeof(masked_key)); return 1; } @@ -190,8 +190,8 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi secp256k1_memczero(sig64, 64, !ret); secp256k1_scalar_clear(&k); secp256k1_scalar_clear(&sk); - secp256k1_memclear(seckey, sizeof(seckey)); - secp256k1_memclear(nonce32, sizeof(nonce32)); + secp256k1_memclear_explicit(seckey, sizeof(seckey)); + secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); secp256k1_gej_clear(&rj); return ret; diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 5abbeefe0..9a1b15f0b 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_SCHNORRSIG_TESTS_H #include "../../../include/secp256k1_schnorrsig.h" +#include "../../unit_test.h" /* Checks that a bit flip in the n_flip-th argument (that has n_bytes many * bytes) changes the hash function @@ -802,7 +803,7 @@ static int nonce_function_overflowing(unsigned char *nonce32, const unsigned cha return 1; } -static void test_schnorrsig_sign(void) { +static void test_schnorrsig_sign_internal(void) { unsigned char sk[32]; secp256k1_xonly_pubkey pk; secp256k1_keypair keypair; @@ -852,7 +853,7 @@ static void test_schnorrsig_sign(void) { /* Creates N_SIGS valid signatures and verifies them with verify and * verify_batch (TODO). Then flips some bits and checks that verification now * fails. */ -static void test_schnorrsig_sign_verify(void) { +static void test_schnorrsig_sign_verify_internal(void) { unsigned char sk[32]; unsigned char msg[N_SIGS][32]; unsigned char sig[N_SIGS][64]; @@ -965,18 +966,18 @@ static void test_schnorrsig_taproot(void) { CHECK(secp256k1_xonly_pubkey_tweak_add_check(CTX, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); } -static void run_schnorrsig_tests(void) { - int i; - run_nonce_function_bip340_tests(); - - test_schnorrsig_api(); - test_schnorrsig_sha256_tagged(); - test_schnorrsig_bip_vectors(); - for (i = 0; i < COUNT; i++) { - test_schnorrsig_sign(); - test_schnorrsig_sign_verify(); - } - test_schnorrsig_taproot(); -} +/* --- Test registry --- */ +REPEAT_TEST(test_schnorrsig_sign) +REPEAT_TEST(test_schnorrsig_sign_verify) + +static const struct tf_test_entry tests_schnorrsig[] = { + CASE(nonce_function_bip340_tests), + CASE1(test_schnorrsig_api), + CASE1(test_schnorrsig_sha256_tagged), + CASE1(test_schnorrsig_bip_vectors), + CASE1(test_schnorrsig_sign), + CASE1(test_schnorrsig_sign_verify), + CASE1(test_schnorrsig_taproot), +}; #endif diff --git a/src/modules/schnorrsig_halfagg/tests_impl.h b/src/modules/schnorrsig_halfagg/tests_impl.h index b93999b64..29d39b2c8 100644 --- a/src/modules/schnorrsig_halfagg/tests_impl.h +++ b/src/modules/schnorrsig_halfagg/tests_impl.h @@ -2,6 +2,7 @@ #define SECP256K1_MODULE_SCHNORRSIG_HALFAGG_TESTS_H #include "../../../include/secp256k1_schnorrsig_halfagg.h" +#include "../../unit_test.h" #define N_MAX 50 @@ -34,7 +35,7 @@ void test_schnorrsig_aggregate_input_helper(secp256k1_xonly_pubkey *pubkeys, uns * aggregate some of them in one shot, and then * aggregate the others incrementally to the already aggregated ones. * The aggregate signature should verify after both steps. */ -void test_schnorrsig_aggregate(void) { +void test_schnorrsig_aggregate_internal(void) { secp256k1_xonly_pubkey pubkeys[N_MAX]; unsigned char msgs32[N_MAX*32]; unsigned char sigs64[N_MAX*64]; @@ -165,7 +166,7 @@ void test_schnorrsig_aggverify_spec_vectors(void) { } } -static void test_schnorrsig_aggregate_api(void) { +static void test_schnorrsig_aggregate_api_internal(void) { size_t n = testrand_int(N_MAX + 1); size_t n_initial = testrand_int(n + 1); size_t n_new = n - n_initial; @@ -241,7 +242,7 @@ static void test_schnorrsig_aggregate_api(void) { /* In this test, we make sure that trivial attempts to break * the security of verification do not work. */ -static void test_schnorrsig_aggregate_unforge(void) { +static void test_schnorrsig_aggregate_unforge_internal(void) { secp256k1_xonly_pubkey pubkeys[N_MAX]; unsigned char msgs32[N_MAX*32]; unsigned char sigs64[N_MAX*64]; @@ -297,7 +298,7 @@ static void test_schnorrsig_aggregate_unforge(void) { /* In this test, we make sure that the algorithms properly reject * for overflowing and non parseable values. */ -static void test_schnorrsig_aggregate_overflow(void) { +static void test_schnorrsig_aggregate_overflow_internal(void) { secp256k1_xonly_pubkey pubkeys[N_MAX]; unsigned char msgs32[N_MAX*32]; unsigned char sigs64[N_MAX*64]; @@ -317,19 +318,20 @@ static void test_schnorrsig_aggregate_overflow(void) { } } -static void run_schnorrsig_halfagg_tests(void) { - int i; +/* --- Test registry --- */ +REPEAT_TEST(test_schnorrsig_aggregate) +REPEAT_TEST(test_schnorrsig_aggregate_api) +REPEAT_TEST(test_schnorrsig_aggregate_unforge) +REPEAT_TEST(test_schnorrsig_aggregate_overflow) - test_schnorrsig_sha256_tagged_aggregate(); - test_schnorrsig_aggverify_spec_vectors(); - - for (i = 0; i < COUNT; i++) { - test_schnorrsig_aggregate(); - test_schnorrsig_aggregate_api(); - test_schnorrsig_aggregate_unforge(); - test_schnorrsig_aggregate_overflow(); - } -} +static const struct tf_test_entry tests_schnorrsig_halfagg[] = { + CASE1(test_schnorrsig_sha256_tagged_aggregate), + CASE1(test_schnorrsig_aggverify_spec_vectors), + CASE1(test_schnorrsig_aggregate), + CASE1(test_schnorrsig_aggregate_api), + CASE1(test_schnorrsig_aggregate_unforge), + CASE1(test_schnorrsig_aggregate_overflow), +}; #undef N_MAX diff --git a/src/modules/surjection/surjection_impl.h b/src/modules/surjection/surjection_impl.h index cc0ad3009..0776e4c82 100644 --- a/src/modules/surjection/surjection_impl.h +++ b/src/modules/surjection/surjection_impl.h @@ -55,11 +55,11 @@ SECP256K1_INLINE static int secp256k1_surjection_genrand(secp256k1_scalar *s, si secp256k1_sha256_clear(&sha256_en); secp256k1_scalar_set_b32(&s[i], sec_input, &overflow); if (overflow == 1) { - secp256k1_memclear(sec_input, 32); + secp256k1_memclear_explicit(sec_input, 32); return 0; } } - secp256k1_memclear(sec_input, 32); + secp256k1_memclear_explicit(sec_input, 32); return 1; } diff --git a/src/modules/surjection/tests_impl.h b/src/modules/surjection/tests_impl.h index e609d1708..731e2fd4b 100644 --- a/src/modules/surjection/tests_impl.h +++ b/src/modules/surjection/tests_impl.h @@ -9,6 +9,7 @@ #include "../../testrand.h" #include "../../group.h" +#include "../../unit_test.h" #include "../../../include/secp256k1_generator.h" #include "../../../include/secp256k1_rangeproof.h" #include "../../../include/secp256k1_surjectionproof.h" @@ -627,22 +628,29 @@ static void test_fixed_vectors(void) { CHECK(!secp256k1_surjectionproof_parse(CTX, &proof, bad, total5_used3_len)); } -static void run_surjection_tests(void) { - test_surjectionproof_api(); - test_input_eq_output(); - test_fixed_vectors(); - +static void test_input_selection_all(void) { test_input_selection(0); test_input_selection(1); test_input_selection(5); test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); +} - test_input_selection_distribution(); +static void test_gen_verify_all(void) { test_gen_verify(10, 3); test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS); - test_no_used_inputs_verify(); - test_bad_serialize(); - test_bad_parse(); } +/* --- Test registry --- */ +static const struct tf_test_entry tests_surjection[] = { + CASE1(test_surjectionproof_api), + CASE1(test_input_eq_output), + CASE1(test_fixed_vectors), + CASE1(test_input_selection_all), + CASE1(test_input_selection_distribution), + CASE1(test_gen_verify_all), + CASE1(test_no_used_inputs_verify), + CASE1(test_bad_serialize), + CASE1(test_bad_parse), +}; + #endif diff --git a/src/modules/whitelist/main_impl.h b/src/modules/whitelist/main_impl.h index ce94d23a7..301d24766 100644 --- a/src/modules/whitelist/main_impl.h +++ b/src/modules/whitelist/main_impl.h @@ -54,7 +54,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s break; } secp256k1_scalar_set_b32(&non, nonce32, &overflow); - secp256k1_memclear(nonce32, 32); + secp256k1_memclear_explicit(nonce32, 32); if (overflow || secp256k1_scalar_is_zero(&non)) { count++; continue; @@ -80,7 +80,7 @@ int secp256k1_whitelist_sign(const secp256k1_context* ctx, secp256k1_whitelist_s break; } } - secp256k1_memclear(seckey32, 32); + secp256k1_memclear_explicit(seckey32, 32); } /* Actually sign */ if (ret) { diff --git a/src/modules/whitelist/tests_impl.h b/src/modules/whitelist/tests_impl.h index 38f91e185..9cbb8287d 100644 --- a/src/modules/whitelist/tests_impl.h +++ b/src/modules/whitelist/tests_impl.h @@ -8,6 +8,7 @@ #define SECP256K1_MODULE_WHITELIST_TESTS_H #include "../../../include/secp256k1_whitelist.h" +#include "../../unit_test.h" static void test_whitelist_end_to_end_internal(const unsigned char *summed_seckey, const unsigned char *online_seckey, const secp256k1_pubkey *online_pubkeys, const secp256k1_pubkey *offline_pubkeys, const secp256k1_pubkey *sub_pubkey, const size_t signer_i, const size_t n_keys) { unsigned char serialized[32 + 4 + 32 * SECP256K1_WHITELIST_MAX_N_KEYS] = {0}; @@ -148,16 +149,20 @@ static void test_whitelist_bad_serialize(void) { CHECK(secp256k1_whitelist_signature_serialize(CTX, serialized, &serialized_len, &sig) == 0); } -static void run_whitelist_tests(void) { - int i; - test_whitelist_bad_parse(); - test_whitelist_bad_serialize(); - for (i = 0; i < COUNT; i++) { - test_whitelist_end_to_end(1, 1); - test_whitelist_end_to_end(10, 1); - test_whitelist_end_to_end(50, 1); - test_whitelist_end_to_end(SECP256K1_WHITELIST_MAX_N_KEYS, 0); - } +static void test_whitelist_end_to_end_all_internal(void) { + test_whitelist_end_to_end(1, 1); + test_whitelist_end_to_end(10, 1); + test_whitelist_end_to_end(50, 1); + test_whitelist_end_to_end(SECP256K1_WHITELIST_MAX_N_KEYS, 0); } +/* --- Test registry --- */ +REPEAT_TEST(test_whitelist_end_to_end_all) + +static const struct tf_test_entry tests_whitelist[] = { + CASE1(test_whitelist_bad_parse), + CASE1(test_whitelist_bad_serialize), + CASE1(test_whitelist_end_to_end_all), +}; + #endif diff --git a/src/scalar_impl.h b/src/scalar_impl.h index 0232a8c22..9965c2bab 100644 --- a/src/scalar_impl.h +++ b/src/scalar_impl.h @@ -28,7 +28,7 @@ static const secp256k1_scalar secp256k1_scalar_one = SECP256K1_SCALAR_CONST(0, 0 static const secp256k1_scalar secp256k1_scalar_zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { - secp256k1_memclear(r, sizeof(secp256k1_scalar)); + secp256k1_memclear_explicit(r, sizeof(secp256k1_scalar)); } static int secp256k1_scalar_set_b32_seckey(secp256k1_scalar *r, const unsigned char *bin) { diff --git a/src/secp256k1.c b/src/secp256k1.c index 7deb93a46..e20ac29d5 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -520,7 +520,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m } secp256k1_rfc6979_hmac_sha256_finalize(&rng); - secp256k1_memclear(keydata, sizeof(keydata)); + secp256k1_memclear_explicit(keydata, sizeof(keydata)); secp256k1_rfc6979_hmac_sha256_clear(&rng); return 1; } @@ -600,7 +600,7 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc * seckey. As a result is_sec_valid is included in ret only after ret was * used as a branching variable. */ ret &= is_sec_valid; - secp256k1_memclear(nonce32, sizeof(nonce32)); + secp256k1_memclear_explicit(nonce32, sizeof(nonce32)); secp256k1_scalar_clear(&msg); secp256k1_scalar_clear(&non); secp256k1_scalar_clear(&sec); diff --git a/src/tests.c b/src/tests.c index 1c7ee4227..7c1a03f8f 100644 --- a/src/tests.c +++ b/src/tests.c @@ -25,6 +25,8 @@ #include "checkmem.h" #include "testutil.h" #include "util.h" +#include "unit_test.h" +#include "unit_test.c" #include "../contrib/lax_der_parsing.c" #include "../contrib/lax_der_privatekey_parsing.c" @@ -37,7 +39,6 @@ #define CONDITIONAL_TEST(cnt, nam) if (COUNT < (cnt)) { printf("Skipping %s (iteration count too low)\n", nam); } else -static int COUNT = 16; static secp256k1_context *CTX = NULL; static secp256k1_context *STATIC_CTX = NULL; @@ -273,6 +274,12 @@ static void run_static_context_tests(int use_prealloc) { } } +static void run_all_static_context_tests(void) +{ + run_static_context_tests(0); + run_static_context_tests(1); +} + static void run_proper_context_tests(int use_prealloc) { int32_t dummy = 0; secp256k1_context *my_ctx, *my_ctx_fresh; @@ -395,6 +402,12 @@ static void run_proper_context_tests(int use_prealloc) { secp256k1_context_preallocated_destroy(NULL); } +static void run_all_proper_context_tests(void) +{ + run_proper_context_tests(0); + run_proper_context_tests(1); +} + static void run_scratch_tests(void) { const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; @@ -6198,12 +6211,7 @@ static void run_ec_pubkey_parse_test(void) { } static void run_eckey_edge_case_test(void) { - const unsigned char orderc[32] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, - 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 - }; + const unsigned char *orderc = secp256k1_group_order_bytes; const unsigned char zeros[sizeof(secp256k1_pubkey)] = {0x00}; unsigned char ctmp[33]; unsigned char ctmp2[33]; @@ -6517,13 +6525,7 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char return 1; } if (counter < 5) { - static const unsigned char order[] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, - 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, - 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 - }; - memcpy(nonce32, order, 32); + memcpy(nonce32, secp256k1_group_order_bytes, 32); if (counter == 4) { nonce32[31]++; } @@ -7541,12 +7543,7 @@ static void test_ecdsa_edge_cases(void) { /* Privkey export where pubkey is the point at infinity. */ { unsigned char privkey[300]; - unsigned char seckey[32] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, - 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, - }; + const unsigned char *seckey = secp256k1_group_order_bytes; size_t outlen = 300; CHECK(!ec_privkey_export_der(CTX, privkey, &outlen, seckey, 0)); outlen = 300; @@ -7860,217 +7857,202 @@ static void run_cmov_tests(void) { ge_storage_cmov_test(); } -int main(int argc, char **argv) { - /* Disable buffering for stdout to improve reliability of getting - * diagnostic information. Happens right at the start of main because - * setbuf must be used before any other operation on the stream. */ - setbuf(stdout, NULL); - /* Also disable buffering for stderr because it's not guaranteed that it's - * unbuffered on all systems. */ - setbuf(stderr, NULL); - - /* find iteration count */ - if (argc > 1) { - COUNT = strtol(argv[1], NULL, 0); - } else { - const char* env = getenv("SECP256K1_TEST_ITERS"); - if (env && strlen(env) > 0) { - COUNT = strtol(env, NULL, 0); - } - } - if (COUNT <= 0) { - fputs("An iteration count of 0 or less is not allowed.\n", stderr); - return EXIT_FAILURE; - } - printf("test count = %i\n", COUNT); - - /* run test RNG tests (must run before we really initialize the test RNG) */ - run_xoshiro256pp_tests(); +/* --------------------------------------------------------- */ +/* Test Registry */ +/* --------------------------------------------------------- */ - /* find random seed */ - testrand_init(argc > 2 ? argv[2] : NULL); - - /*** Setup test environment ***/ +/* --- Special test cases that must run before RNG initialization --- */ +static const struct tf_test_entry tests_no_rng[] = { + CASE(xoshiro256pp_tests), +}; +static const struct tf_test_module registry_modules_no_rng = MAKE_TEST_MODULE(no_rng); + +/* --- Standard test cases start here --- */ +static const struct tf_test_entry tests_general[] = { + CASE(selftest_tests), + CASE(all_proper_context_tests), + CASE(all_static_context_tests), + CASE(deprecated_context_flags_test), + CASE(scratch_tests), +}; - /* Create a global context available to all tests */ - CTX = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - /* Randomize the context only with probability 15/16 - to make sure we test without context randomization from time to time. - TODO Reconsider this when recalibrating the tests. */ - if (testrand_bits(4)) { - unsigned char rand32[32]; - testrand256(rand32); - CHECK(secp256k1_context_randomize(CTX, rand32)); - } - /* Make a writable copy of secp256k1_context_static in order to test the effect of API functions - that write to the context. The API does not support cloning the static context, so we use - memcpy instead. The user is not supposed to copy a context but we should still ensure that - the API functions handle copies of the static context gracefully. */ - STATIC_CTX = malloc(sizeof(*secp256k1_context_static)); - CHECK(STATIC_CTX != NULL); - memcpy(STATIC_CTX, secp256k1_context_static, sizeof(secp256k1_context)); - CHECK(!secp256k1_context_is_proper(STATIC_CTX)); +static const struct tf_test_entry tests_integer[] = { +#ifdef SECP256K1_WIDEMUL_INT128 + CASE(int128_tests), +#endif + CASE(ctz_tests), + CASE(modinv_tests), + CASE(inverse_tests), +}; - /*** Run actual tests ***/ +static const struct tf_test_entry tests_hash[] = { + CASE(sha256_known_output_tests), + CASE(sha256_counter_tests), + CASE(hmac_sha256_tests), + CASE(rfc6979_hmac_sha256_tests), + CASE(tagged_sha256_tests), +}; - /* selftest tests */ - run_selftest_tests(); +static const struct tf_test_entry tests_scalar[] = { + CASE(scalar_tests), +}; - /* context tests */ - run_proper_context_tests(0); run_proper_context_tests(1); - run_static_context_tests(0); run_static_context_tests(1); - run_deprecated_context_flags_test(); +static const struct tf_test_entry tests_field[] = { + CASE(field_half), + CASE(field_misc), + CASE(field_convert), + CASE(field_be32_overflow), + CASE(fe_mul), + CASE(sqr), + CASE(sqrt), +}; - /* scratch tests */ - run_scratch_tests(); +static const struct tf_test_entry tests_group[] = { + CASE(ge), + CASE(gej), + CASE(group_decompress), +}; - /* util tests */ - run_util_tests(); +static const struct tf_test_entry tests_ecmult[] = { + CASE(ecmult_pre_g), + CASE(wnaf), + CASE(point_times_order), + CASE(ecmult_near_split_bound), + CASE(ecmult_chain), + CASE(ecmult_constants), + CASE(ecmult_gen_blind), + CASE(ecmult_const_tests), + CASE(ecmult_multi_tests), + CASE(ec_combine), +}; - /* integer arithmetic tests */ -#ifdef SECP256K1_WIDEMUL_INT128 - run_int128_tests(); -#endif - run_ctz_tests(); - run_modinv_tests(); - run_inverse_tests(); - - /* sorting tests */ - run_hsort_tests(); - - /* hash tests */ - run_sha256_known_output_tests(); - run_sha256_counter_tests(); - run_hmac_sha256_tests(); - run_rfc6979_hmac_sha256_tests(); - run_tagged_sha256_tests(); - - /* scalar tests */ - run_scalar_tests(); - - /* field tests */ - run_field_half(); - run_field_misc(); - run_field_convert(); - run_field_be32_overflow(); - run_fe_mul(); - run_sqr(); - run_sqrt(); - - /* group tests */ - run_ge(); - run_gej(); - run_group_decompress(); - - /* ecmult tests */ - run_ecmult_pre_g(); - run_wnaf(); - run_point_times_order(); - run_ecmult_near_split_bound(); - run_ecmult_chain(); - run_ecmult_constants(); - run_ecmult_gen_blind(); - run_ecmult_const_tests(); - run_ecmult_multi_tests(); - run_ec_combine(); - run_ec_commit(); - - /* endomorphism tests */ - run_endomorphism_tests(); - - /* EC point parser test */ - run_ec_pubkey_parse_test(); - - /* EC key edge cases */ - run_eckey_edge_case_test(); - - /* EC key arithmetic test */ - run_eckey_negate_test(); +static const struct tf_test_entry tests_ec[] = { + CASE(endomorphism_tests), + CASE(ec_pubkey_parse_test), + CASE(eckey_edge_case_test), + CASE(eckey_negate_test), +}; -#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG - run_schnorrsig_halfagg_tests(); -#endif +static const struct tf_test_entry tests_ecdsa[] = { + CASE(ec_illegal_argument_tests), + CASE(pubkey_comparison), + CASE(pubkey_sort), + CASE(random_pubkeys), + CASE(ecdsa_der_parse), + CASE(ecdsa_sign_verify), + CASE(ecdsa_end_to_end), + CASE(ecdsa_edge_cases), + CASE(ecdsa_wycheproof), +}; -#ifdef ENABLE_MODULE_BPPP - run_bppp_tests(); -#endif +static const struct tf_test_entry tests_utils[] = { + CASE(hsort_tests), + CASE(secp256k1_memczero_test), + CASE(secp256k1_is_zero_array_test), + CASE(secp256k1_byteorder_tests), + CASE(cmov_tests), +}; +/* Register test modules */ +static const struct tf_test_module registry_modules[] = { + MAKE_TEST_MODULE(general), + MAKE_TEST_MODULE(integer), + MAKE_TEST_MODULE(hash), + MAKE_TEST_MODULE(scalar), + MAKE_TEST_MODULE(field), + MAKE_TEST_MODULE(group), + MAKE_TEST_MODULE(ecmult), + MAKE_TEST_MODULE(ec), #ifdef ENABLE_MODULE_ECDH - /* ecdh tests */ - run_ecdh_tests(); + MAKE_TEST_MODULE(ecdh), #endif - - /* ecdsa tests */ - run_ec_illegal_argument_tests(); - run_pubkey_comparison(); - run_pubkey_sort(); - run_random_pubkeys(); - run_ecdsa_der_parse(); - run_ecdsa_sign_verify(); - run_ecdsa_end_to_end(); - run_ecdsa_edge_cases(); - run_ecdsa_wycheproof(); - + MAKE_TEST_MODULE(ecdsa), #ifdef ENABLE_MODULE_RECOVERY /* ECDSA pubkey recovery tests */ - run_recovery_tests(); -#endif - -#ifdef ENABLE_MODULE_GENERATOR - run_generator_tests(); -#endif - -#ifdef ENABLE_MODULE_RANGEPROOF - run_rangeproof_tests(); + MAKE_TEST_MODULE(recovery), #endif - -#ifdef ENABLE_MODULE_WHITELIST - /* Key whitelisting tests */ - run_whitelist_tests(); -#endif - -#ifdef ENABLE_MODULE_SURJECTIONPROOF - run_surjection_tests(); -#endif - #ifdef ENABLE_MODULE_EXTRAKEYS - run_extrakeys_tests(); + MAKE_TEST_MODULE(extrakeys), #endif - #ifdef ENABLE_MODULE_SCHNORRSIG - run_schnorrsig_tests(); + MAKE_TEST_MODULE(schnorrsig), #endif - #ifdef ENABLE_MODULE_MUSIG - run_musig_tests(); + MAKE_TEST_MODULE(musig), #endif - #ifdef ENABLE_MODULE_ELLSWIFT - run_ellswift_tests(); + MAKE_TEST_MODULE(ellswift), #endif - -#ifdef ENABLE_MODULE_ECDSA_S2C - /* ECDSA sign to contract */ - run_ecdsa_s2c_tests(); + /* --- ZKP-SPECIFIC MODULES --- */ +#ifdef ENABLE_MODULE_SCHNORRSIG_HALFAGG + MAKE_TEST_MODULE(schnorrsig_halfagg), +#endif +#ifdef ENABLE_MODULE_BPPP + MAKE_TEST_MODULE(bppp), +#endif +#ifdef ENABLE_MODULE_GENERATOR + MAKE_TEST_MODULE(generator), +#endif +#ifdef ENABLE_MODULE_RANGEPROOF + MAKE_TEST_MODULE(rangeproof), +#endif +#ifdef ENABLE_MODULE_WHITELIST + MAKE_TEST_MODULE(whitelist), +#endif +#ifdef ENABLE_MODULE_SURJECTIONPROOF + MAKE_TEST_MODULE(surjection), #endif - #ifdef ENABLE_MODULE_ECDSA_ADAPTOR - run_ecdsa_adaptor_tests(); + MAKE_TEST_MODULE(ecdsa_adaptor), #endif +#ifdef ENABLE_MODULE_ECDSA_S2C + MAKE_TEST_MODULE(ecdsa_s2c), +#endif + MAKE_TEST_MODULE(utils), +}; - /* util tests */ - run_secp256k1_memczero_test(); - run_secp256k1_is_zero_array_test(); - run_secp256k1_byteorder_tests(); - - run_cmov_tests(); +/* Setup test environment */ +static int setup(void) { + /* Create a global context available to all tests */ + CTX = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + /* Randomize the context only with probability 15/16 + to make sure we test without context randomization from time to time. + TODO Reconsider this when recalibrating the tests. */ + if (testrand_bits(4)) { + unsigned char rand32[32]; + testrand256(rand32); + CHECK(secp256k1_context_randomize(CTX, rand32)); + } + /* Make a writable copy of secp256k1_context_static in order to test the effect of API functions + that write to the context. The API does not support cloning the static context, so we use + memcpy instead. The user is not supposed to copy a context but we should still ensure that + the API functions handle copies of the static context gracefully. */ + STATIC_CTX = malloc(sizeof(*secp256k1_context_static)); + CHECK(STATIC_CTX != NULL); + memcpy(STATIC_CTX, secp256k1_context_static, sizeof(secp256k1_context)); + CHECK(!secp256k1_context_is_proper(STATIC_CTX)); + return 0; +} - /*** Tear down test environment ***/ +/* Shutdown test environment */ +static int teardown(void) { free(STATIC_CTX); secp256k1_context_destroy(CTX); + return 0; +} - testrand_finish(); +int main(int argc, char **argv) { + struct tf_framework tf = {0}; + tf.registry_modules = registry_modules; + tf.num_modules = sizeof(registry_modules) / sizeof(registry_modules[0]); + tf.registry_no_rng = ®istry_modules_no_rng; + + /* Add context creation/destruction functions */ + tf.fn_setup = setup; + tf.fn_teardown = teardown; - printf("no problems found\n"); - return EXIT_SUCCESS; + /* Init and run framework */ + if (tf_init(&tf, argc, argv) != 0) return EXIT_FAILURE; + return tf_run(&tf); } + diff --git a/src/tests_common.h b/src/tests_common.h new file mode 100644 index 000000000..a341633bb --- /dev/null +++ b/src/tests_common.h @@ -0,0 +1,42 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_TESTS_COMMON_H +#define SECP256K1_TESTS_COMMON_H + +/*********************************************************************** + * Test Support Utilities + * + * This file provides general-purpose functions for tests and benchmark + * programs. Unlike testutil.h, this file is not linked to the library, + * allowing each program to choose whether to run against the production + * API or access library internals directly. + ***********************************************************************/ + +#include + +#if (defined(_MSC_VER) && _MSC_VER >= 1900) +# include +#else +# include +#endif + +static int64_t gettime_i64(void) { +#if (defined(_MSC_VER) && _MSC_VER >= 1900) + /* C11 way to get wallclock time */ + struct timespec tv; + if (!timespec_get(&tv, TIME_UTC)) { + fputs("timespec_get failed!", stderr); + exit(EXIT_FAILURE); + } + return (int64_t)tv.tv_nsec / 1000 + (int64_t)tv.tv_sec * 1000000LL; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_usec + (int64_t)tv.tv_sec * 1000000LL; +#endif +} + +#endif /* SECP256K1_TESTS_COMMON_H */ diff --git a/src/testutil.h b/src/testutil.h index 64b3bb41c..480f6a1a0 100644 --- a/src/testutil.h +++ b/src/testutil.h @@ -11,6 +11,14 @@ #include "testrand.h" #include "util.h" +/* group order of the secp256k1 curve in 32-byte big endian representation */ +static const unsigned char secp256k1_group_order_bytes[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 +}; + static void testutil_random_fe(secp256k1_fe *x) { unsigned char bin[32]; do { diff --git a/src/unit_test.c b/src/unit_test.c new file mode 100644 index 000000000..a1858a117 --- /dev/null +++ b/src/unit_test.c @@ -0,0 +1,479 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#include +#include +#include + +#if defined(SUPPORTS_CONCURRENCY) +#include +#include +#include +#endif + +#include "unit_test.h" +#include "testrand.h" +#include "tests_common.h" + +#define UNUSED(x) (void)(x) + +/* Number of times certain tests will run */ +int COUNT = 16; + +static int parse_jobs_count(const char* key, const char* value, struct tf_framework* tf); +static int parse_iterations(const char* key, const char* value, struct tf_framework* tf); +static int parse_seed(const char* key, const char* value, struct tf_framework* tf); +static int parse_target(const char* key, const char* value, struct tf_framework* tf); +static int parse_logging(const char* key, const char* value, struct tf_framework* tf); + +/* Mapping table: key -> handler */ +typedef int (*ArgHandler)(const char* key, const char* value, struct tf_framework* tf); +struct ArgMap { + const char* key; + ArgHandler handler; +}; + +/* + * Main entry point for handling command-line arguments. + * + * Developers should extend this map whenever new command-line + * options are introduced. Each new argument should be validated, + * converted to the appropriate type, and stored in 'tf->args' struct. + */ +static struct ArgMap arg_map[] = { + { "t", parse_target }, { "target", parse_target }, + { "j", parse_jobs_count }, { "jobs", parse_jobs_count }, + { "i", parse_iterations }, { "iterations", parse_iterations }, + { "seed", parse_seed }, + { "log", parse_logging }, + { NULL, NULL } /* sentinel */ +}; + +/* Display options that are not printed elsewhere */ +static void print_args(const struct tf_args* args) { + printf("iterations = %d\n", COUNT); + printf("jobs = %d. %s execution.\n", args->num_processes, args->num_processes > 1 ? "Parallel" : "Sequential"); +} + +/* Main entry point for reading environment variables */ +static int read_env(struct tf_framework* tf) { + const char* env_iter = getenv("SECP256K1_TEST_ITERS"); + if (env_iter && strlen(env_iter) > 0) { + return parse_iterations("i", env_iter, tf); + } + return 0; +} + +static int parse_arg(const char* key, const char* value, struct tf_framework* tf) { + int i; + for (i = 0; arg_map[i].key != NULL; i++) { + if (strcmp(key, arg_map[i].key) == 0) { + return arg_map[i].handler(key, value, tf); + } + } + /* Unknown key: report just so typos don't silently pass. */ + fprintf(stderr, "Unknown argument '-%s=%s'\n", key, value); + return -1; +} + +static void help(void) { + printf("Usage: ./tests [options]\n\n"); + printf("Run the test suite for the project with optional configuration.\n\n"); + printf("Options:\n"); + printf(" --help, -h Show this help message\n"); + printf(" --list_tests, -l Display list of all available tests and modules\n"); + printf(" --jobs=, -j= Number of parallel worker processes (default: 0 = sequential)\n"); + printf(" --iterations=, -i= Number of iterations for each test (default: 16)\n"); + printf(" --seed= Set a specific RNG seed (default: random)\n"); + printf(" --target=, -t= Run a specific test (can be provided multiple times)\n"); + printf(" --target=, -t= Run all tests within a specific module (can be provided multiple times)\n"); + printf(" --log=<0|1> Enable or disable test execution logging (default: 0 = disabled)\n"); + printf("\n"); + printf("Notes:\n"); + printf(" - All arguments must be provided in the form '--key=value', '-key=value' or '-k=value'.\n"); + printf(" - Single or double dashes are allowed for multi character options.\n"); + printf(" - Unknown arguments are reported but ignored.\n"); + printf(" - Sequential execution occurs if -jobs=0 or unspecified.\n"); + printf(" - Iterations and seed can also be passed as positional arguments before any other argument for backward compatibility.\n"); +} + +/* Print all tests in registry */ +static void print_test_list(struct tf_framework* tf) { + int m, t, total = 0; + printf("\nAvailable tests (%d modules):\n", tf->num_modules); + printf("========================================\n"); + for (m = 0; m < tf->num_modules; m++) { + const struct tf_test_module* mod = &tf->registry_modules[m]; + printf("Module: %s (%d tests)\n", mod->name, mod->size); + for (t = 0; t < mod->size; t++) { + printf("\t[%3d] %s\n", total + 1, mod->data[t].name); + total++; + } + printf("----------------------------------------\n"); + } + printf("\nRun specific module: ./tests -t=\n"); + printf("Run specific test: ./tests -t=\n\n"); +} + +static int parse_jobs_count(const char* key, const char* value, struct tf_framework* tf) { + char* ptr_val; + long val = strtol(value, &ptr_val, 10); /* base 10 */ + if (*ptr_val != '\0') { + fprintf(stderr, "Invalid number for -%s=%s\n", key, value); + return -1; + } + if (val < 0 || val > MAX_SUBPROCESSES) { + fprintf(stderr, "Arg '-%s' out of range: '%ld'. Range: 0..%d\n", key, val, MAX_SUBPROCESSES); + return -1; + } + tf->args.num_processes = (int) val; + return 0; +} + +static int parse_iterations(const char* key, const char* value, struct tf_framework* tf) { + UNUSED(key); UNUSED(tf); + if (!value) return 0; + COUNT = (int) strtol(value, NULL, 0); + if (COUNT <= 0) { + fputs("An iteration count of 0 or less is not allowed.\n", stderr); + return -1; + } + return 0; +} + +static int parse_seed(const char* key, const char* value, struct tf_framework* tf) { + UNUSED(key); + tf->args.custom_seed = (!value || strcmp(value, "NULL") == 0) ? NULL : value; + return 0; +} + +static int parse_logging(const char* key, const char* value, struct tf_framework* tf) { + UNUSED(key); + tf->args.logging = value && strcmp(value, "1") == 0; + return 0; +} + +/* Strip up to two leading dashes */ +static const char* normalize_key(const char* arg, const char** err_msg) { + const char* key; + if (!arg || arg[0] != '-') { + *err_msg = "missing initial dash"; + return NULL; + } + /* single-dash short option */ + if (arg[1] != '-') return arg + 1; + + /* double-dash checks now */ + if (arg[2] == '\0') { + *err_msg = "missing option name after double dash"; + return NULL; + } + + if (arg[2] == '-') { + *err_msg = "too many leading dashes"; + return NULL; + } + + key = arg + 2; + if (key[1] == '\0') { + *err_msg = "short option cannot use double dash"; + return NULL; + } + return key; +} + +static int parse_target(const char* key, const char* value, struct tf_framework* tf) { + int group, idx; + const struct tf_test_entry* entry; + UNUSED(key); + /* Find test index in the registry */ + for (group = 0; group < tf->num_modules; group++) { + const struct tf_test_module* module = &tf->registry_modules[group]; + int add_all = strcmp(value, module->name) == 0; /* select all from module */ + for (idx = 0; idx < module->size; idx++) { + entry = &module->data[idx]; + if (add_all || strcmp(value, entry->name) == 0) { + if (tf->args.targets.size >= MAX_ARGS) { + fprintf(stderr, "Too many -target args (max: %d)\n", MAX_ARGS); + return -1; + } + tf->args.targets.slots[tf->args.targets.size++] = entry; + /* Matched a single test, we're done */ + if (!add_all) return 0; + } + } + /* If add_all was true, we added all tests in the module, so return */ + if (add_all) return 0; + } + fprintf(stderr, "Error: target '%s' not found (missing or module disabled).\n" + "Run program with -list_tests option to display available tests and modules.\n", value); + return -1; +} + +/* Read args: all must be in the form -key=value, --key=value or -key=value */ +static int read_args(int argc, char** argv, int start, struct tf_framework* tf) { + int i; + const char* key; + const char* value; + char* eq; + const char* err_msg = "unknown error"; + for (i = start; i < argc; i++) { + char* raw_arg = argv[i]; + if (!raw_arg || raw_arg[0] != '-') { + fprintf(stderr, "Invalid arg '%s': must start with '-'\n", raw_arg ? raw_arg : "(null)"); + return -1; + } + + key = normalize_key(raw_arg, &err_msg); + if (!key || *key == '\0') { + fprintf(stderr, "Invalid arg '%s': %s. Must be -k=value or --key=value\n", raw_arg, err_msg); + return -1; + } + + eq = strchr(raw_arg, '='); + if (!eq || eq == raw_arg + 1) { + /* Allowed options without value */ + if (strcmp(key, "h") == 0 || strcmp(key, "help") == 0) { + tf->args.help = 1; + return 0; + } + if (strcmp(key, "l") == 0 || strcmp(key, "list_tests") == 0) { + tf->args.list_tests = 1; + return 0; + } + fprintf(stderr, "Invalid arg '%s': must be -k=value or --key=value\n", raw_arg); + return -1; + } + + *eq = '\0'; /* split key and value */ + value = eq + 1; + if (!value || *value == '\0') { /* value is empty */ + fprintf(stderr, "Invalid arg '%s': value cannot be empty\n", raw_arg); + return -1; + } + + if (parse_arg(key, value, tf) != 0) return -1; + } + return 0; +} + +static void run_test_log(const struct tf_test_entry* t) { + int64_t start_time = gettime_i64(); + printf("Running %s..\n", t->name); + t->func(); + printf("Test %s PASSED (%.3f sec)\n", t->name, (double)(gettime_i64() - start_time) / 1000000); +} + +static void run_test(const struct tf_test_entry* t) { t->func(); } + +/* Process tests in sequential order */ +static int run_sequential(struct tf_framework* tf) { + int it; + for (it = 0; it < tf->args.targets.size; it++) { + tf->fn_run_test(tf->args.targets.slots[it]); + } + return EXIT_SUCCESS; +} + +#if defined(SUPPORTS_CONCURRENCY) +static const int MAX_TARGETS = 255; + +/* Process tests in parallel */ +static int run_concurrent(struct tf_framework* tf) { + /* Sub-processes info */ + pid_t workers[MAX_SUBPROCESSES]; + int pipefd[2]; + int status = EXIT_SUCCESS; + int it; /* loop iterator */ + unsigned char idx; /* test index */ + + if (tf->args.targets.size > MAX_TARGETS) { + fprintf(stderr, "Internal Error: the number of targets (%d) exceeds the maximum supported (%d). " + "If you need more, extend 'run_concurrent()' to handle additional targets.\n", + tf->args.targets.size, MAX_TARGETS); + exit(EXIT_FAILURE); + } + + + if (pipe(pipefd) != 0) { + perror("Error during pipe setup"); + return EXIT_FAILURE; + } + + /* Launch worker processes */ + for (it = 0; it < tf->args.num_processes; it++) { + pid_t pid = fork(); + if (pid < 0) { + perror("Error during process fork"); + return EXIT_FAILURE; + } + if (pid == 0) { + /* Child worker: read jobs from the shared pipe */ + close(pipefd[1]); /* children never write */ + while (read(pipefd[0], &idx, sizeof(idx)) == sizeof(idx)) { + tf->fn_run_test(tf->args.targets.slots[(int)idx]); + } + _exit(EXIT_SUCCESS); /* finish child process */ + } else { + /* Parent: save worker pid */ + workers[it] = pid; + } + } + + /* Parent: write all tasks into the pipe */ + close(pipefd[0]); /* close read end */ + for (it = 0; it < tf->args.targets.size; it++) { + idx = (unsigned char)it; + if (write(pipefd[1], &idx, sizeof(idx)) == -1) { + perror("Error during workload distribution"); + close(pipefd[1]); + return EXIT_FAILURE; + } + } + /* Close write end to signal EOF */ + close(pipefd[1]); + /* Wait for all workers */ + for (it = 0; it < tf->args.num_processes; it++) { + int ret = 0; + if (waitpid(workers[it], &ret, 0) == -1 || ret != 0) { + status = EXIT_FAILURE; + } + } + + return status; +} +#endif + +static int tf_init(struct tf_framework* tf, int argc, char** argv) +{ + /* Caller must set the registry and its size before calling tf_init */ + if (tf->registry_modules == NULL || tf->num_modules <= 0) { + fprintf(stderr, "Error: tests registry not provided or empty\n"); + return EXIT_FAILURE; + } + + /* Initialize command-line options */ + tf->args.num_processes = 0; + tf->args.custom_seed = NULL; + tf->args.help = 0; + tf->args.targets.size = 0; + tf->args.list_tests = 0; + tf->args.logging = 0; + + /* Disable buffering for stdout to improve reliability of getting + * diagnostic information. Happens right at the start of main because + * setbuf must be used before any other operation on the stream. */ + setbuf(stdout, NULL); + /* Also disable buffering for stderr because it's not guaranteed that it's + * unbuffered on all systems. */ + setbuf(stderr, NULL); + + /* Parse env args */ + if (read_env(tf) != 0) return EXIT_FAILURE; + + /* Parse command-line args */ + if (argc > 1) { + int named_arg_start = 1; /* index to begin processing named arguments */ + if (argc - 1 > MAX_ARGS) { /* first arg is always the binary path */ + fprintf(stderr, "Too many command-line arguments (max: %d)\n", MAX_ARGS); + return EXIT_FAILURE; + } + + /* Compatibility Note: The first two args were the number of iterations and the seed. */ + /* If provided, parse them and adjust the starting index for named arguments accordingly. */ + if (argv[1][0] != '-') { + int has_seed = argc > 2 && argv[2][0] != '-'; + if (parse_iterations("i", argv[1], tf) != 0) return EXIT_FAILURE; + if (has_seed) parse_seed("seed", argv[2], tf); + named_arg_start = has_seed ? 3 : 2; + } + if (read_args(argc, argv, named_arg_start, tf) != 0) { + return EXIT_FAILURE; + } + + if (tf->args.help) { + help(); + exit(EXIT_SUCCESS); + } + + if (tf->args.list_tests) { + print_test_list(tf); + exit(EXIT_SUCCESS); + } + } + + tf->fn_run_test = tf->args.logging ? run_test_log : run_test; + return EXIT_SUCCESS; +} + +static int tf_run(struct tf_framework* tf) { + /* Process exit status */ + int status; + /* Whether to run all tests */ + int run_all; + /* Loop iterator */ + int it; + /* Initial test time */ + int64_t start_time = gettime_i64(); + /* Verify 'tf_init' has been called */ + if (!tf->fn_run_test) { + fprintf(stderr, "Error: No test runner set. You must call 'tf_init' first to initialize the framework " + "or manually assign 'fn_run_test' before calling 'tf_run'.\n"); + return EXIT_FAILURE; + } + + /* Populate targets with all tests if none were explicitly specified */ + run_all = tf->args.targets.size == 0; + if (run_all) { + int group, idx; + for (group = 0; group < tf->num_modules; group++) { + const struct tf_test_module* module = &tf->registry_modules[group]; + for (idx = 0; idx < module->size; idx++) { + if (tf->args.targets.size >= MAX_ARGS) { + fprintf(stderr, "Internal Error: Number of tests (%d) exceeds MAX_ARGS (%d). " + "Increase MAX_ARGS to accommodate all tests.\n", tf->args.targets.size, MAX_ARGS); + return EXIT_FAILURE; + } + tf->args.targets.slots[tf->args.targets.size++] = &module->data[idx]; + } + } + } + + if (!tf->args.logging) printf("Tests running silently. Use '-log=1' to enable detailed logging\n"); + + /* Log configuration */ + print_args(&tf->args); + + /* Run test RNG tests (must run before we really initialize the test RNG) */ + /* Note: currently, these tests are executed sequentially because there */ + /* is really only one test. */ + for (it = 0; tf->registry_no_rng && it < tf->registry_no_rng->size; it++) { + if (run_all) { /* future: support filtering */ + tf->fn_run_test(&tf->registry_no_rng->data[it]); + } + } + + /* Initialize test RNG and library contexts */ + testrand_init(tf->args.custom_seed); + if (tf->fn_setup && tf->fn_setup() != 0) return EXIT_FAILURE; + + /* Check whether to process tests sequentially or concurrently */ + if (tf->args.num_processes <= 1) { + status = run_sequential(tf); + } else { +#if defined(SUPPORTS_CONCURRENCY) + status = run_concurrent(tf); +#else + fputs("Parallel execution not supported on your system. Running sequentially...\n", stderr); + status = run_sequential(tf); +#endif + } + + /* Print accumulated time */ + printf("Total execution time: %.3f seconds\n", (double)(gettime_i64() - start_time) / 1000000); + if (tf->fn_teardown && tf->fn_teardown() != 0) return EXIT_FAILURE; + + return status; +} diff --git a/src/unit_test.h b/src/unit_test.h new file mode 100644 index 000000000..bf301e539 --- /dev/null +++ b/src/unit_test.h @@ -0,0 +1,145 @@ +/*********************************************************************** + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_UNIT_TEST_H +#define SECP256K1_UNIT_TEST_H + +/* --------------------------------------------------------- */ +/* Configurable constants */ +/* --------------------------------------------------------- */ + +/* Maximum number of command-line arguments. + * Must be at least as large as the total number of tests + * to allow specifying all tests individually. */ +#define MAX_ARGS 150 +/* Maximum number of parallel jobs */ +#define MAX_SUBPROCESSES 16 + +/* --------------------------------------------------------- */ +/* Test Framework Registry Macros */ +/* --------------------------------------------------------- */ + +#define CASE(name) { #name, run_##name } +#define CASE1(name) { #name, name } + +#define MAKE_TEST_MODULE(name) { \ + #name, \ + tests_##name, \ + sizeof(tests_##name) / sizeof(tests_##name[0]) \ +} + +/* Macro to wrap a test internal function with a COUNT loop (iterations number) */ +#define REPEAT_TEST(fn) REPEAT_TEST_MULT(fn, 1) +#define REPEAT_TEST_MULT(fn, multiplier) \ + static void fn(void) { \ + int i; \ + int repeat = COUNT * (multiplier); \ + for (i = 0; i < repeat; i++) \ + fn##_internal(); \ + } + + + +/* --------------------------------------------------------- */ +/* Test Framework API */ +/* --------------------------------------------------------- */ + +typedef void (*test_fn)(void); + +struct tf_test_entry { + const char* name; + test_fn func; +}; + +struct tf_test_module { + const char* name; + const struct tf_test_entry* data; + int size; +}; + +typedef int (*setup_ctx_fn)(void); +typedef int (*teardown_fn)(void); +typedef void (*run_test_fn)(const struct tf_test_entry*); + +struct tf_targets { + /* Target tests indexes */ + const struct tf_test_entry* slots[MAX_ARGS]; + /* Next available slot */ + int size; +}; + +/* --- Command-line args --- */ +struct tf_args { + /* 0 => sequential; 1..MAX_SUBPROCESSES => parallel workers */ + int num_processes; + /* Specific RNG seed */ + const char* custom_seed; + /* Whether to print the help msg */ + int help; + /* Whether to print the tests list msg */ + int list_tests; + /* Target tests indexes */ + struct tf_targets targets; + /* Enable test execution logging */ + int logging; +}; + +/* --------------------------------------------------------- */ +/* Public API */ +/* --------------------------------------------------------- */ + +struct tf_framework { + /* Command-line args */ + struct tf_args args; + /* Test modules registry */ + const struct tf_test_module* registry_modules; + /* Num of modules */ + int num_modules; + /* Registry for tests that require no RNG init */ + const struct tf_test_module* registry_no_rng; + /* Specific context setup and teardown functions */ + setup_ctx_fn fn_setup; + teardown_fn fn_teardown; + /* Test runner function (can be customized) */ + run_test_fn fn_run_test; +}; + +/* + * Initialize the test framework. + * + * Must be called before tf_run() and before any output is performed to + * stdout or stderr, because this function disables buffering on both + * streams to ensure reliable diagnostic output. + * + * Parses command-line arguments and configures the framework context. + * The caller must initialize the following members of 'tf' before calling: + * - tf->registry_modules + * - tf->num_modules + * + * Side effects: + * - stdout and stderr are set to unbuffered mode via setbuf(). + * This allows immediate flushing of diagnostic messages but may + * affect performance for other output operations. + * + * Returns: + * EXIT_SUCCESS (0) on success, + * EXIT_FAILURE (non-zero) on error. + */ +static int tf_init(struct tf_framework* tf, int argc, char** argv); + +/* + * Run tests based on the provided test framework context. + * + * This function uses the configuration stored in the tf_framework + * (targets, number of processes, iteration count, etc.) to determine + * which tests to execute and how to execute them. + * + * Returns: + * EXIT_SUCCESS (0) if all tests passed, + * EXIT_FAILURE (non-zero) otherwise. + */ +static int tf_run(struct tf_framework* tf); + +#endif /* SECP256K1_UNIT_TEST_H */ diff --git a/src/util.h b/src/util.h index be1366f65..d1fd78438 100644 --- a/src/util.h +++ b/src/util.h @@ -245,8 +245,8 @@ static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { } } -/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */ -static SECP256K1_INLINE void secp256k1_memclear(void *ptr, size_t len) { +/* Zeroes memory to prevent leaking sensitive info. Won't be optimized out. */ +static SECP256K1_INLINE void secp256k1_memzero_explicit(void *ptr, size_t len) { #if defined(_MSC_VER) /* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */ SecureZeroMemory(ptr, len); @@ -268,6 +268,19 @@ static SECP256K1_INLINE void secp256k1_memclear(void *ptr, size_t len) { void *(*volatile const volatile_memset)(void *, int, size_t) = memset; volatile_memset(ptr, 0, len); #endif +} + +/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. + * The state of the memory after this call is unspecified so callers must not + * make any assumptions about its contents. + * + * In VERIFY builds, it has the side effect of marking the memory as undefined. + * This helps to detect use-after-clear bugs where code incorrectly reads from + * cleansed memory during testing. + */ +static SECP256K1_INLINE void secp256k1_memclear_explicit(void *ptr, size_t len) { + /* The current implementation zeroes, but callers must not rely on this */ + secp256k1_memzero_explicit(ptr, len); #ifdef VERIFY SECP256K1_CHECKMEM_UNDEFINE(ptr, len); #endif @@ -303,7 +316,7 @@ static SECP256K1_INLINE int secp256k1_is_zero_array(const unsigned char *s, size } ret = (acc == 0); /* acc may contain secret values. Try to explicitly clear it. */ - secp256k1_memclear(&acc, sizeof(acc)); + secp256k1_memclear_explicit(&acc, sizeof(acc)); return ret; }