From 5667aa958a96326c87b07e2d3acacac31fe18062 Mon Sep 17 00:00:00 2001 From: dhruv <856960+dhruv@users.noreply.github.com> Date: Fri, 12 Aug 2022 10:28:01 -0700 Subject: [PATCH] Squashed 'src/secp256k1/' changes from 44c2452fd3..34227ad260 34227ad260 ElligatorSwift a3bb417d62 Add benchmark for key generation cee0666204 Add x-only ecmult_const version for x=n/d b57ed9d701 doc: Describe Jacobi calculation in safegcd_implementation.md 7eedb96864 Native jacobi symbol algorithm 9f8a13dc8e Merge bitcoin-core/secp256k1#1128: configure: Remove pkgconfig macros again (reintroduced by mismerge) cabe085bb4 configure: Remove pkgconfig macros again (reintroduced by mismerge) 3efeb9da21 Merge bitcoin-core/secp256k1#1121: config: Set preprocessor defaults for ECMULT_* config values 6a873cc4a9 Merge bitcoin-core/secp256k1#1122: tests: Randomize the context with probability 15/16 instead of 1/4 17065f48ae tests: Randomize the context with probability 15/16 instead of 1/4 c27ae45144 config: Remove basic-config.h da6514a04a config: Introduce DEBUG_CONFIG macro for debug output of config 63a3565e97 Merge bitcoin-core/secp256k1#1120: ecmult_gen: Skip RNG when creating blinding if no seed is available d0cf55e13a config: Set preprocessor defaults for ECMULT_* config values 55f8bc99dc ecmult_gen: Improve comments about projective blinding 7a86955800 ecmult_gen: Simplify code (no observable change) 4cc0b1b669 ecmult_gen: Skip RNG when creating blinding if no seed is available af65d30cc8 Merge bitcoin-core/secp256k1#1116: build: Fix #include "..." paths to get rid of further -I arguments 40a3473a9d build: Fix #include "..." paths to get rid of further -I arguments 43756da819 Merge bitcoin-core/secp256k1#1115: Fix sepc256k1 -> secp256k1 typo in group.h 069aba8125 Fix sepc256k1 -> secp256k1 typo in group.h accadc94df Merge bitcoin-core/secp256k1#1114: `_scratch_destroy`: move `VERIFY_CHECK` after invalid scrach space check cd47033335 Merge bitcoin-core/secp256k1#1084: ci: Add MSVC builds 1827c9bf2b scratch_destroy: move VERIFY_CHECK after invalid scrach space check 49e2acd927 configure: Improve rationale for WERROR_CFLAGS 8dc4b03341 ci: Add a C++ job that compiles the public headers without -fpermissive 51f296a46c ci: Run persistent wineserver to speed up wine 3fb3269c22 ci: Add 32-bit MinGW64 build 9efc2e5221 ci: Add MSVC builds 2be6ba0fed configure: Convince autotools to work with MSVC's archiver lib.exe bd81f4140a schnorrsig bench: Suppress a stupid warning in MSVC 09f3d71c51 configure: Add a few CFLAGS for MSVC 3b4f3d0d46 build: Reject C++ compilers in the preprocessor 1cc0941414 configure: Don't abort if the compiler does not define __STDC__ cca8cbbac8 configure: Output message when checking for valgrind 1a6be5745f bench: Make benchmarks compile on MSVC git-subtree-dir: src/secp256k1 git-subtree-split: 34227ad26038c54a7b42c54bfd008121a3ca8fb5 --- .cirrus.yml | 80 +++- Makefile.am | 9 +- build-aux/m4/bitcoin_secp.m4 | 2 + ci/cirrus.sh | 14 + ci/linux-debian.Dockerfile | 31 +- configure.ac | 69 ++- doc/safegcd_implementation.md | 31 +- include/secp256k1_ellswift.h | 175 ++++++++ src/basic-config.h | 17 - src/bench.c | 26 ++ src/bench.h | 18 +- src/bench_internal.c | 12 + src/ecmult.h | 11 + src/ecmult_const.h | 19 + src/ecmult_const_impl.h | 54 +++ src/ecmult_gen.h | 12 + src/ecmult_gen_impl.h | 17 +- src/field.h | 3 + src/field_10x26_impl.h | 28 ++ src/field_5x52_impl.h | 28 ++ src/group.h | 2 +- src/modinv32.h | 4 + src/modinv32_impl.h | 174 +++++++- src/modinv64.h | 4 + src/modinv64_impl.h | 155 ++++++- src/modules/ecdh/bench_impl.h | 2 +- src/modules/ellswift/Makefile.am.include | 4 + src/modules/ellswift/bench_impl.h | 94 +++++ src/modules/ellswift/main_impl.h | 393 ++++++++++++++++++ src/modules/ellswift/tests_impl.h | 133 ++++++ src/modules/extrakeys/tests_exhaustive_impl.h | 2 +- src/modules/recovery/bench_impl.h | 2 +- src/modules/recovery/tests_exhaustive_impl.h | 2 +- src/modules/schnorrsig/bench_impl.h | 10 +- .../schnorrsig/tests_exhaustive_impl.h | 2 +- src/scratch_impl.h | 2 +- src/secp256k1.c | 15 + src/tests.c | 125 +++++- src/tests_exhaustive.c | 6 +- src/util.h | 5 + 40 files changed, 1683 insertions(+), 109 deletions(-) create mode 100644 include/secp256k1_ellswift.h delete mode 100644 src/basic-config.h create mode 100644 src/modules/ellswift/Makefile.am.include create mode 100644 src/modules/ellswift/bench_impl.h create mode 100644 src/modules/ellswift/main_impl.h create mode 100644 src/modules/ellswift/tests_impl.h diff --git a/.cirrus.yml b/.cirrus.yml index a2e7f36d1f..4e2429779e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -18,6 +18,7 @@ env: ECDH: no RECOVERY: no SCHNORRSIG: no + ELLSWIFT: no ### test options SECP256K1_TEST_ITERS: BENCH: yes @@ -67,11 +68,11 @@ task: << : *LINUX_CONTAINER matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes} - env: {WIDEMUL: int128} - - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes, ELLSWIFT: yes} - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ASM: x86_64} + - env: {WIDEMUL: int128, ASM: x86_64 , ELLSWIFT: yes} - env: { RECOVERY: yes, SCHNORRSIG: yes} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} @@ -178,6 +179,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -197,6 +199,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no matrix: - env: {} @@ -217,6 +220,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -234,6 +238,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no << : *MERGE_BASE test_script: @@ -241,17 +246,59 @@ task: << : *CAT_LOGS task: - name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)" << : *LINUX_CONTAINER env: - WRAPPER_CMD: wine64-stable - SECP256K1_TEST_ITERS: 16 - HOST: x86_64-w64-mingw32 + WRAPPER_CMD: wine + WITH_VALGRIND: no + ECDH: yes + RECOVERY: yes + SCHNORRSIG: yes + CTIMETEST: no + matrix: + - name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)" + env: + HOST: x86_64-w64-mingw32 + - name: "i686 (mingw32-w64): Windows (Debian stable, Wine)" + env: + HOST: i686-w64-mingw32 + << : *MERGE_BASE + test_script: + - ./ci/cirrus.sh + << : *CAT_LOGS + +task: + << : *LINUX_CONTAINER + env: + WRAPPER_CMD: wine + WERROR_CFLAGS: -WX WITH_VALGRIND: no ECDH: yes RECOVERY: yes + EXPERIMENTAL: yes SCHNORRSIG: yes + ELLSWIFT: yes + ELLSWIFT: yes CTIMETEST: no + # Set non-essential options that affect the CLI messages here. + # (They depend on the user's taste, so we don't want to set them automatically in configure.ac.) + CFLAGS: -nologo -diagnostics:caret + LDFLAGS: -XCClinker -nologo -XCClinker -diagnostics:caret + # Use a MinGW-w64 host to tell ./configure we're building for Windows. + # This will detect some MinGW-w64 tools but then make will need only + # the MSVC tools CC, AR and NM as specified below. + matrix: + - name: "x86_64 (MSVC): Windows (Debian stable, Wine)" + env: + HOST: x86_64-w64-mingw32 + CC: /opt/msvc/bin/x64/cl + AR: /opt/msvc/bin/x64/lib + NM: /opt/msvc/bin/x64/dumpbin -symbols -headers + - name: "i686 (MSVC): Windows (Debian stable, Wine)" + env: + HOST: i686-w64-mingw32 + CC: /opt/msvc/bin/x86/cl + AR: /opt/msvc/bin/x86/lib + NM: /opt/msvc/bin/x86/dumpbin -symbols -headers << : *MERGE_BASE test_script: - ./ci/cirrus.sh @@ -264,6 +311,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes CTIMETEST: no matrix: - name: "Valgrind (memcheck)" @@ -302,22 +350,30 @@ task: << : *CAT_LOGS task: - name: "C++ -fpermissive" + name: "C++ -fpermissive (entire project)" << : *LINUX_CONTAINER env: - # ./configure correctly errors out when given CC=g++. - # We hack around this by passing CC=g++ only to make. - CC: gcc - MAKEFLAGS: -j4 CC=g++ CFLAGS=-fpermissive\ -g + CC: g++ + CFLAGS: -fpermissive -g + CPPFLAGS: -DSECP256K1_CPLUSPLUS_TEST_OVERRIDE WERROR_CFLAGS: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + ELLSWIFT: yes << : *MERGE_BASE test_script: - ./ci/cirrus.sh << : *CAT_LOGS +task: + name: "C++ (public headers)" + << : *LINUX_CONTAINER + test_script: + - g++ -Werror include/*.h + - clang -Werror -x c++-header include/*.h + - /opt/msvc/bin/x64/cl.exe -c -WX -TP include/*.h + task: name: "sage prover" << : *LINUX_CONTAINER diff --git a/Makefile.am b/Makefile.am index 51c5960301..145baee617 100644 --- a/Makefile.am +++ b/Makefile.am @@ -58,7 +58,6 @@ noinst_HEADERS += src/hash_impl.h noinst_HEADERS += src/field.h noinst_HEADERS += src/field_impl.h noinst_HEADERS += src/bench.h -noinst_HEADERS += src/basic-config.h noinst_HEADERS += contrib/lax_der_parsing.h noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h @@ -87,7 +86,7 @@ endif endif libsecp256k1_la_SOURCES = src/secp256k1.c -libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) +libsecp256k1_la_CPPFLAGS = $(SECP_INCLUDES) libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB) libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE) @@ -112,7 +111,7 @@ TESTS = if USE_TESTS noinst_PROGRAMS += tests tests_SOURCES = src/tests.c -tests_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +tests_CPPFLAGS = $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) if VALGRIND_ENABLED tests_CPPFLAGS += -DVALGRIND noinst_PROGRAMS += valgrind_ctime_test @@ -228,3 +227,7 @@ endif if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif + +if ENABLE_MODULE_ELLSWIFT +include src/modules/ellswift/Makefile.am.include +endif diff --git a/build-aux/m4/bitcoin_secp.m4 b/build-aux/m4/bitcoin_secp.m4 index 9cb54de098..98be915b67 100644 --- a/build-aux/m4/bitcoin_secp.m4 +++ b/build-aux/m4/bitcoin_secp.m4 @@ -10,6 +10,7 @@ AC_MSG_RESULT([$has_64bit_asm]) ]) AC_DEFUN([SECP_VALGRIND_CHECK],[ +AC_MSG_CHECKING([for valgrind support]) if test x"$has_valgrind" != x"yes"; then CPPFLAGS_TEMP="$CPPFLAGS" CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS" @@ -21,6 +22,7 @@ if test x"$has_valgrind" != x"yes"; then #endif ]])], [has_valgrind=yes; AC_DEFINE(HAVE_VALGRIND,1,[Define this symbol if valgrind is installed, and it supports the host platform])]) fi +AC_MSG_RESULT($has_valgrind) ]) dnl SECP_TRY_APPEND_CFLAGS(flags, VAR) diff --git a/ci/cirrus.sh b/ci/cirrus.sh index b85f012d3f..8779b6fa52 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -5,10 +5,20 @@ set -x export LC_ALL=C +# Start persistent wineserver if necessary. +# This speeds up jobs with many invocations of wine (e.g., ./configure with MSVC) tremendously. +case "$WRAPPER_CMD" in + *wine*) + # This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards. + wineserver -p && wine hh.exe + ;; +esac + env >> test_env.log $CC -v || true valgrind --version || true +$WRAPPER_CMD --version || true ./autogen.sh @@ -18,6 +28,7 @@ valgrind --version || true --with-ecmult-window="$ECMULTWINDOW" \ --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-ellswift="$ELLSWIFT" \ --enable-module-schnorrsig="$SCHNORRSIG" \ --enable-examples="$EXAMPLES" \ --with-valgrind="$WITH_VALGRIND" \ @@ -63,6 +74,9 @@ then make precomp fi +# Shutdown wineserver again +wineserver -k || true + # Check that no repo files have been modified by the build. # (This fails for example if the precomp files need to be updated in the repo.) git diff --exit-code diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile index 5cccbb5565..a83a4e36db 100644 --- a/ci/linux-debian.Dockerfile +++ b/ci/linux-debian.Dockerfile @@ -1,15 +1,14 @@ FROM debian:stable -RUN dpkg --add-architecture i386 -RUN dpkg --add-architecture s390x -RUN dpkg --add-architecture armhf -RUN dpkg --add-architecture arm64 -RUN dpkg --add-architecture ppc64el -RUN apt-get update +RUN dpkg --add-architecture i386 && \ + dpkg --add-architecture s390x && \ + dpkg --add-architecture armhf && \ + dpkg --add-architecture arm64 && \ + dpkg --add-architecture ppc64el # dkpg-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 install --no-install-recommends --no-upgrade -y \ +RUN apt-get update && apt-get install --no-install-recommends -y \ git ca-certificates \ make automake libtool pkg-config dpkg-dev valgrind qemu-user \ gcc clang llvm libc6-dbg \ @@ -19,8 +18,20 @@ RUN apt-get install --no-install-recommends --no-upgrade -y \ gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \ gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \ - wine gcc-mingw-w64-x86-64 \ + gcc-mingw-w64-x86-64-win32 wine64 wine \ + gcc-mingw-w64-i686-win32 wine32 \ sagemath -# Run a dummy command in wine to make it set up configuration -RUN wine64-stable xcopy || true +WORKDIR /root +# The "wine" package provides a convience wrapper that we need +RUN apt-get update && apt-get install --no-install-recommends -y \ + git ca-certificates wine64 wine python3-simplejson python3-six msitools winbind procps && \ + git clone https://github.com/mstorsjo/msvc-wine && \ + mkdir /opt/msvc && \ + python3 msvc-wine/vsdownload.py --accept-license --dest /opt/msvc Microsoft.VisualStudio.Workload.VCTools && \ + msvc-wine/install.sh /opt/msvc + +# Initialize the wine environment. Wait until the wineserver process has +# exited before closing the session, to avoid corrupting the wine prefix. +RUN wine64 wineboot --init && \ + while (ps -A | grep wineserver) > /dev/null; do sleep 1; done diff --git a/configure.ac b/configure.ac index 2db59a8ff3..cf4019e06f 100644 --- a/configure.ac +++ b/configure.ac @@ -33,12 +33,14 @@ AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_PROG_CC -if test x"$ac_cv_prog_cc_c89" = x"no"; then - AC_MSG_ERROR([c89 compiler support required]) -fi AM_PROG_AS AM_PROG_AR +# Clear some cache variables as a workaround for a bug that appears due to a bad +# interaction between AM_PROG_AR and LT_INIT when combining MSVC's archiver lib.exe. +# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54421 +AS_UNSET(ac_cv_prog_AR) +AS_UNSET(ac_cv_prog_ac_ct_AR) LT_INIT([win32-dll]) build_windows=no @@ -87,23 +89,35 @@ esac # # TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues. AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [ - # Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will - # not error out if it gets unknown warning flags and the checks here will always succeed - # no matter if clang knows the flag or not. - SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS" - SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS) - - SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic. - SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic. - SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers - SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall. - SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions. - SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95 - SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0 - SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only - SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0 - - CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS" + # GCC and compatible (incl. clang) + if test "x$GCC" = "xyes"; then + # Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will + # not error out if it gets unknown warning flags and the checks here will always succeed + # no matter if clang knows the flag or not. + SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS" + SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS) + + SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic. + SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic. + SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers + SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall. + SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions. + SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95 + SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0 + SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only + SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0 + + CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS" + fi + + # MSVC + # Assume MSVC if we're building for Windows but not with GCC or compatible; + # libtool makes the same assumption internally. + # Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path. + if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then + SECP_TRY_APPEND_CFLAGS([-W2 -wd4146], $1) # Moderate warning level, disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned" + SECP_TRY_APPEND_CFLAGS([-external:anglebrackets -external:W0], $1) # Suppress warnings from #include <...> files + fi ]) SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) @@ -156,6 +170,11 @@ AC_ARG_ENABLE(module_schnorrsig, AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])]) +AC_ARG_ENABLE(module_ellswift, + AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module (experimental)]), + [enable_module_ellswift=$enableval], + [enable_module_ellswift=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) @@ -326,7 +345,9 @@ if test x"$enable_valgrind" = x"yes"; then SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS" fi -# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI) +# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI). +# We don't want to set the user variable CFLAGS in CI because this would disable +# autoconf's logic for setting default CFLAGS, which we would like to test in CI. SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS" ### @@ -346,6 +367,10 @@ if test x"$enable_module_schnorrsig" = x"yes"; then enable_module_extrakeys=yes fi +if test x"$enable_module_ellswift" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ELLSWIFT, 1, [Define this symbol to enable the ElligatorSwift module]) +fi + # Test if extrakeys is set after the schnorrsig module to allow the schnorrsig # module to set enable_module_extrakeys=yes if test x"$enable_module_extrakeys" = x"yes"; then @@ -391,6 +416,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) @@ -411,6 +437,7 @@ echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" echo " module schnorrsig = $enable_module_schnorrsig" +echo " module ellswift = $enable_module_ellswift" echo echo " asm = $set_asm" echo " ecmult window size = $set_ecmult_window" diff --git a/doc/safegcd_implementation.md b/doc/safegcd_implementation.md index 063aa8efae..c1cdd0cfe1 100644 --- a/doc/safegcd_implementation.md +++ b/doc/safegcd_implementation.md @@ -1,7 +1,7 @@ # The safegcd implementation in libsecp256k1 explained -This document explains the modular inverse implementation in the `src/modinv*.h` files. It is based -on the paper +This document explains the modular inverse and Jacobi symbol implementations in the `src/modinv*.h` files. +It is based on the paper ["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd) by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version. @@ -769,3 +769,30 @@ def modinv_var(M, Mi, x): d, e = update_de(d, e, t, M, Mi) return normalize(f, d, Mi) ``` + +## 8. From GCDs to Jacobi symbol + +We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we make corresponding updates to *j* using [properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties). In particular, we update *j* whenever we divide *g* by *2* or swap *f* and *g*; these updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied very quickly. Overall, this calculation is slightly simpler than the one for modular inverse because we no longer need to keep track of *d* and *e*. + +However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative values. We resolve this by using the following modified steps: + +```python + # Before + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g - f) // 2 + + # After + if delta > 0 and g & 1: + delta, f, g = 1 - delta, g, (g + f) // 2 +``` + +The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4 and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm will converge. The justification for posdivsteps is completely empirical: in practice, it appears that the vast majority of inputs converge to *f=g=gcd(f0, g0)* in a number of steps proportional to their logarithm. + +Note that: +- We require inputs to satisfy *gcd(x, M) = 1*. +- We need to update the termination condition from *g=0* to *f=1*. +- We deal with the case where *g=0* on input specially. + +We account for the possibility of nonconvergence by only performing a bounded number of posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not yet been found. + +The optimizations in sections 3-7 above are described in the context of the original divsteps, but in the C implementation we also adapt most of them (not including "avoiding modulus operations", since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate Jacobi symbols for secret data) to the posdivsteps version. diff --git a/include/secp256k1_ellswift.h b/include/secp256k1_ellswift.h new file mode 100644 index 0000000000..cda5bac2e3 --- /dev/null +++ b/include/secp256k1_ellswift.h @@ -0,0 +1,175 @@ +#ifndef SECP256K1_ELLSWIFT_H +#define SECP256K1_ELLSWIFT_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This module provides an implementation of ElligatorSwift as well as + * a version of x-only ECDH using it. + * + * ElligatorSwift is described in https://eprint.iacr.org/2022/759 by + * Chavez-Saab, Rodriguez-Henriquez, and Tibouchi. It permits encoding + * public keys in 64-byte objects which are indistinguishable from + * uniformly random. + * + * Let f be the function from pairs of field elements to point X coordinates, + * defined as follows (all operations modulo p = 2^256 - 2^32 - 977) + * f(u,t): + * - Let C = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852, + * a square root of -3. + * - If u=0, set u=1 instead. + * - If t=0, set t=1 instead. + * - If u^3 + t^2 + 7 = 0, multiply t by 2. + * - Let p = u^3 + t^2 + 7 + * - Let m = u^3 - t^2 + 7 + * - Let v = (C * m / p - 1) * u / 2 + * - Let w = p / (C * t * u) + * - Let x1 = v + * - Let x2 = -u - v + * - Let x3 = u + w^2 + * - Return the first of [x3,x2,x1] that is an X coordinate on the curve + * (at least one of them is, for any inputs u and t). + * + * Then an ElligatorSwift encoding of x consists of the 32-byte big-endian + * encodings of field elements u and t concatenated, where f(u,t) = x. + * The encoding algorithm is described in the paper, and effectively picks a + * uniformly random pair (u,t) among those which encode x. + * + * If the Y coordinate is relevant, it is given the same parity as t. + * + * Changes w.r.t. the the paper: + * - The u=0, t=0, and u^3+t^2+7=0 conditions result in decoding to the point + * at infinity in the paper. Here they are remapped to finite points. + * - The paper uses an additional encoding bit for the parity of y. Here the + * parity of t is used (negating t does not affect the decoded x coordinate, + * so this is possible). + */ + +/** A pointer to a function used for hashing the shared X coordinate along + * with the encoded public keys to a uniform shared secret. + * + * Returns: 1 if a shared secret was was successfully computed. + * 0 will cause secp256k1_ellswift_xdh to fail and return 0. + * Other return values are not allowed, and the behaviour of + * secp256k1_ellswift_xdh is undefined for other return values. + * Out: output: pointer to an array to be filled by the function + * In: x32: pointer to the 32-byte serialized X coordinate + * of the resulting shared point + * ours64: pointer to the 64-byte encoded public key we sent + * to the other party + * theirs64: pointer to the 64-byte encoded public key we received + * from the other party + * data: arbitrary data pointer that is passed through + */ +typedef int (*secp256k1_ellswift_xdh_hash_function)( + unsigned char *output, + const unsigned char *x32, + const unsigned char *ours64, + const unsigned char *theirs64, + void *data +); + +/** An implementation of an secp256k1_ellswift_xdh_hash_function which uses + * SHA256(key1 || key2 || x32), where (key1, key2) = sorted([ours64, theirs64]), and + * ignores data. The sorting is lexicographic. */ +SECP256K1_API extern const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_sha256; + +/** A default secp256k1_ellswift_xdh_hash_function, currently secp256k1_ellswift_xdh_hash_function_sha256. */ +SECP256K1_API extern const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_default; + +/* Construct a 64-byte ElligatorSwift encoding of a given pubkey. + * + * Returns: 1 when pubkey is valid. + * Args: ctx: pointer to a context object + * Out: ell64: pointer to a 64-byte array to be filled + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key + * rnd32: pointer to 32 bytes of entropy (must be unpredictable) + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_encode( + const secp256k1_context* ctx, + unsigned char *ell64, + const secp256k1_pubkey *pubkey, + const unsigned char *rnd32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Decode a 64-bytes ElligatorSwift encoded public key. + * + * Returns: always 1 + * Args: ctx: pointer to a context object + * Out: pubkey: pointer to a secp256k1_pubkey that will be filled + * In: ell64: pointer to a 64-byte array to decode + * + * This function runs in variable time. + */ +SECP256K1_API int secp256k1_ellswift_decode( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *ell64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Compute an ElligatorSwift public key for a secret key. + * + * Returns: 1: secret was valid, public key was stored. + * 0: secret was invalid, try again. + * Args: ctx: pointer to a context object, initialized for signing. + * Out: ell64: pointer to a 64-byte area to receive the ElligatorSwift public key + * In: seckey32: pointer to a 32-byte secret key. + * auxrand32: (optional) pointer to 32 bytes of additional randomness + * + * Constant time in seckey and auxrand32, but not in the resulting public key. + * + * This function can be used instead of calling secp256k1_ec_pubkey_create followed + * by secp256k1_ellswift_encode. It is safer, as it can use the secret key as + * entropy for the encoding. That means that if the secret key itself is + * unpredictable, no additional auxrand32 is needed to achieve indistinguishability + * of the encoding. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create( + const secp256k1_context* ctx, + unsigned char *ell64, + const unsigned char *seckey32, + const unsigned char *auxrand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Given a private key, and ElligatorSwift public keys sent in both directions, + * compute a shared secret using x-only Diffie-Hellman. + * + * Returns: 1: shared secret was succesfully computed + * 0: secret was invalid or hashfp returned 0 + * Args: ctx: pointer to a context object. + * Out: output: pointer to an array to be filled by hashfp. + * In: theirs64: a pointer to the 64-byte ElligatorSquare public key received from the other party. + * ours64: a pointer to the 64-byte ElligatorSquare public key sent to the other party. + * seckey32: a pointer to the 32-byte private key corresponding to ours64. + * hashfp: pointer to a hash function. If NULL, + * secp256k1_elswift_xdh_hash_function_default is used + * (in which case, 32 bytes will be written to output). + * data: arbitrary data pointer that is passed through to hashfp + * (ignored for secp256k1_ellswift_xdh_hash_function_default). + * + * Constant time in seckey32. + * + * This function is more efficient than decoding the public keys, and performing ECDH on them. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_xdh( + const secp256k1_context* ctx, + unsigned char *output, + const unsigned char* theirs64, + const unsigned char* ours64, + const unsigned char* seckey32, + secp256k1_ellswift_xdh_hash_function hashfp, + void *data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_ELLSWIFT_H */ diff --git a/src/basic-config.h b/src/basic-config.h deleted file mode 100644 index 6f7693cb8f..0000000000 --- a/src/basic-config.h +++ /dev/null @@ -1,17 +0,0 @@ -/*********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - ***********************************************************************/ - -#ifndef SECP256K1_BASIC_CONFIG_H -#define SECP256K1_BASIC_CONFIG_H - -#ifdef USE_BASIC_CONFIG - -#define ECMULT_WINDOW_SIZE 15 -#define ECMULT_GEN_PREC_BITS 4 - -#endif /* USE_BASIC_CONFIG */ - -#endif /* SECP256K1_BASIC_CONFIG_H */ diff --git a/src/bench.c b/src/bench.c index d5937b763f..68cb163b13 100644 --- a/src/bench.c +++ b/src/bench.c @@ -121,6 +121,22 @@ static void bench_sign_run(void* arg, int iters) { } } +static void bench_keygen_run(void* arg, int iters) { + int i; + bench_sign_data *data = (bench_sign_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char pub33[33]; + size_t len = 33; + secp256k1_pubkey pubkey; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pub33, &len, &pubkey, SECP256K1_EC_COMPRESSED)); + memcpy(data->key, pub33 + 1, 32); + data->key[17] ^= i; + } +} + + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/bench_impl.h" #endif @@ -133,6 +149,10 @@ static void bench_sign_run(void* arg, int iters) { # include "modules/schnorrsig/bench_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/bench_impl.h" +#endif + int main(int argc, char** argv) { int i; secp256k1_pubkey pubkey; @@ -212,6 +232,7 @@ int main(int argc, char** argv) { data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "ecdsa_sign")) run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ec") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ec_keygen")) run_benchmark("ec_keygen", bench_keygen_run, bench_sign_setup, NULL, &data, 10, iters); secp256k1_context_destroy(data.ctx); @@ -230,5 +251,10 @@ int main(int argc, char** argv) { run_schnorrsig_bench(iters, argc, argv); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + /* ElligatorSwift benchmarks */ + run_ellswift_bench(iters, argc, argv); +#endif + return 0; } diff --git a/src/bench.h b/src/bench.h index aa275fe919..611ba11f04 100644 --- a/src/bench.h +++ b/src/bench.h @@ -7,15 +7,31 @@ #ifndef SECP256K1_BENCH_H #define SECP256K1_BENCH_H +#include #include #include #include -#include "sys/time.h" + +#if (defined(_MSC_VER) && _MSC_VER >= 1900) +# include +#else +# include "sys/time.h" +#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(1); + } + 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 } #define FP_EXP (6) diff --git a/src/bench_internal.c b/src/bench_internal.c index 7eb3af28d7..27af24b1a0 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -218,6 +218,17 @@ void bench_field_sqrt(void* arg, int iters) { CHECK(j <= iters); } +void bench_field_jacobi_var(void* arg, int iters) { + int i, j = 0; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + j += secp256k1_fe_jacobi_var(&data->fe[0]); + secp256k1_fe_add(&data->fe[0], &data->fe[1]); + } + CHECK(j <= iters); +} + void bench_group_double_var(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -379,6 +390,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, iters*10); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "jacobi")) run_benchmark("field_jacobi_var", bench_field_jacobi_var, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, iters*10); diff --git a/src/ecmult.h b/src/ecmult.h index b47d8f494a..e28c602506 100644 --- a/src/ecmult.h +++ b/src/ecmult.h @@ -11,6 +11,17 @@ #include "scalar.h" #include "scratch.h" +#ifndef ECMULT_WINDOW_SIZE +# define ECMULT_WINDOW_SIZE 15 +# ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_MSG("ECMULT_WINDOW_SIZE undefined, assuming default value") +# endif +#endif + +#ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_DEF(ECMULT_WINDOW_SIZE) +#endif + /* Noone will ever need more than a window size of 24. The code might * be correct for larger values of ECMULT_WINDOW_SIZE but this is not * tested. diff --git a/src/ecmult_const.h b/src/ecmult_const.h index f891f3f306..aae902743b 100644 --- a/src/ecmult_const.h +++ b/src/ecmult_const.h @@ -18,4 +18,23 @@ */ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); +/** + * Same as secp256k1_ecmult_const, but takes in an x coordinate of the base point + * only, specified as fraction n/d. Only the x coordinate of the result is returned. + * + * If known_on_curve is 0, a verification is performed that n/d is a valid X + * coordinate, and 0 is returned if not. Otherwise, 1 is returned. + * + * d being NULL is interpreted as d=1. + * + * Constant time in the value of q, but not any other inputs. + */ +static int secp256k1_ecmult_const_xonly( + secp256k1_fe* r, + const secp256k1_fe *n, + const secp256k1_fe *d, + const secp256k1_scalar *q, + int bits, + int known_on_curve); + #endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h index 12dbcc6c5b..1940ee7f08 100644 --- a/src/ecmult_const_impl.h +++ b/src/ecmult_const_impl.h @@ -228,4 +228,58 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons secp256k1_fe_mul(&r->z, &r->z, &Z); } +static int secp256k1_ecmult_const_xonly(secp256k1_fe* r, const secp256k1_fe *n, const secp256k1_fe *d, const secp256k1_scalar *q, int bits, int known_on_curve) { + + /* This algorithm is a generalization of Peter Dettman's technique for + * avoiding the square root in a random-basepoint x-only multiplication + * on a Weierstrass curve: + * https://mailarchive.ietf.org/arch/msg/cfrg/7DyYY6gg32wDgHAhgSb6XxMDlJA/ + */ + secp256k1_fe g, i; + secp256k1_ge p; + secp256k1_gej rj; + + /* Compute g = (n^3 + B*d^3). */ + secp256k1_fe_sqr(&g, n); + secp256k1_fe_mul(&g, &g, n); + if (d) { + secp256k1_fe b; + secp256k1_fe_sqr(&b, d); + secp256k1_fe_mul(&b, &b, d); + secp256k1_fe_mul(&b, &b, &secp256k1_fe_const_b); + secp256k1_fe_add(&g, &b); + if (!known_on_curve) { + secp256k1_fe c; + secp256k1_fe_mul(&c, &g, d); + if (secp256k1_fe_jacobi_var(&c) < 0) return 0; + } + } else { + secp256k1_fe_add(&g, &secp256k1_fe_const_b); + if (!known_on_curve) { + if (secp256k1_fe_jacobi_var(&g) < 0) return 0; + } + } + + /* Compute base point P = (n*g, g^2), the effective affine version of + * (n*g, g^2, sqrt(d*g)), which has corresponding affine X coordinate + * n/d. */ + secp256k1_fe_mul(&p.x, &g, n); + secp256k1_fe_sqr(&p.y, &g); + p.infinity = 0; + + /* Perform x-only EC multiplication of P with q. */ + secp256k1_ecmult_const(&rj, &p, q, bits); + + /* The resulting (X, Y, Z) point on the effective-affine isomorphic curve + * corresponds to (X, Y, Z*sqrt(d*g)) on the secp256k1 curve. The affine + * version of that has X coordinate (X / (Z^2*d*g)). */ + secp256k1_fe_sqr(&i, &rj.z); + secp256k1_fe_mul(&i, &i, &g); + if (d) secp256k1_fe_mul(&i, &i, d); + secp256k1_fe_inv(&i, &i); + secp256k1_fe_mul(r, &rj.x, &i); + + return 1; +} + #endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h index f48f266461..a430e8d5d9 100644 --- a/src/ecmult_gen.h +++ b/src/ecmult_gen.h @@ -10,9 +10,21 @@ #include "scalar.h" #include "group.h" +#ifndef ECMULT_GEN_PREC_BITS +# define ECMULT_GEN_PREC_BITS 4 +# ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_MSG("ECMULT_GEN_PREC_BITS undefined, assuming default value") +# endif +#endif + +#ifdef DEBUG_CONFIG +# pragma message DEBUG_CONFIG_DEF(ECMULT_GEN_PREC_BITS) +#endif + #if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8 # error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8." #endif + #define ECMULT_GEN_PREC_G(bits) (1 << bits) #define ECMULT_GEN_PREC_N(bits) (256 / bits) diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 2c8a503acc..4f5ea9f3c0 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -88,31 +88,31 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char nonce32[32]; secp256k1_rfc6979_hmac_sha256 rng; int overflow; - unsigned char keydata[64] = {0}; + unsigned char keydata[64]; if (seed32 == NULL) { /* When seed is NULL, reset the initial point and blinding value. */ secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); secp256k1_gej_neg(&ctx->initial, &ctx->initial); secp256k1_scalar_set_int(&ctx->blind, 1); + return; } /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ - secp256k1_scalar_get_b32(nonce32, &ctx->blind); + secp256k1_scalar_get_b32(keydata, &ctx->blind); /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, * and guards against weak or adversarial seeds. This is a simpler and safer interface than * asking the caller for blinding values directly and expecting them to retry on failure. */ - memcpy(keydata, nonce32, 32); - if (seed32 != NULL) { - memcpy(keydata + 32, seed32, 32); - } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); + VERIFY_CHECK(seed32 != NULL); + memcpy(keydata + 32, seed32, 32); + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64); memset(keydata, 0, sizeof(keydata)); /* Accept unobservably small non-uniformity. */ secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); overflow = !secp256k1_fe_set_b32(&s, nonce32); overflow |= secp256k1_fe_is_zero(&s); secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow); - /* Randomize the projection to defend against multiplier sidechannels. */ + /* Randomize the projection to defend against multiplier sidechannels. + Do this before our own call to secp256k1_ecmult_gen below. */ secp256k1_gej_rescale(&ctx->initial, &s); secp256k1_fe_clear(&s); secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); @@ -121,6 +121,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b)); secp256k1_rfc6979_hmac_sha256_finalize(&rng); memset(nonce32, 0, 32); + /* The random projection in ctx->initial ensures that gb will have a random projection. */ secp256k1_ecmult_gen(ctx, &gb, &b); secp256k1_scalar_negate(&b, &b); ctx->blind = b; diff --git a/src/field.h b/src/field.h index 2584a494ee..c9bafeb481 100644 --- a/src/field.h +++ b/src/field.h @@ -139,4 +139,7 @@ static void secp256k1_fe_half(secp256k1_fe *r); * magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */ static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m); +/** Compute the Jacobi symbol of a / p. 0 if a=0; 1 if a square; -1 if a non-square. */ +static int secp256k1_fe_jacobi_var(const secp256k1_fe *a); + #endif /* SECP256K1_FIELD_H */ diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h index 21742bf6eb..61a86190c5 100644 --- a/src/field_10x26_impl.h +++ b/src/field_10x26_impl.h @@ -1364,4 +1364,32 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { VERIFY_CHECK(secp256k1_fe_normalizes_to_zero(r) == secp256k1_fe_normalizes_to_zero(&tmp)); } +static int secp256k1_fe_jacobi_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv32_signed30 s; + int ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed30(&s, &tmp); + ret = secp256k1_jacobi32_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (ret == -2) { + /* secp256k1_jacobi32_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input. */ + secp256k1_fe dummy; + ret = 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1; +#ifdef VERIFY + } else { + secp256k1_fe dummy; + if (secp256k1_fe_is_zero(&tmp)) { + VERIFY_CHECK(ret == 0); + } else { + VERIFY_CHECK(ret == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); + } +#endif + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h index 6bd202f587..26e89123a0 100644 --- a/src/field_5x52_impl.h +++ b/src/field_5x52_impl.h @@ -667,4 +667,32 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *x) { #endif } +static int secp256k1_fe_jacobi_var(const secp256k1_fe *x) { + secp256k1_fe tmp; + secp256k1_modinv64_signed62 s; + int ret; + + tmp = *x; + secp256k1_fe_normalize_var(&tmp); + secp256k1_fe_to_signed62(&s, &tmp); + ret = secp256k1_jacobi64_maybe_var(&s, &secp256k1_const_modinfo_fe); + if (ret == -2) { + /* secp256k1_jacobi64_maybe_var failed to compute the Jacobi symbol. Fall back + * to computing a square root. This should be extremely rare with random + * input. */ + secp256k1_fe dummy; + ret = 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1; +#ifdef VERIFY + } else { + secp256k1_fe dummy; + if (secp256k1_fe_is_zero(&tmp)) { + VERIFY_CHECK(ret == 0); + } else { + VERIFY_CHECK(ret == 2*secp256k1_fe_sqrt(&dummy, &tmp) - 1); + } +#endif + } + return ret; +} + #endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/group.h b/src/group.h index bb7dae1cf7..585457d93b 100644 --- a/src/group.h +++ b/src/group.h @@ -23,7 +23,7 @@ typedef struct { #define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} /** A group element of the secp256k1 curve, in jacobian coordinates. - * Note: For exhastive test mode, sepc256k1 is replaced by a small subgroup of a different curve. + * Note: For exhastive test mode, secp256k1 is replaced by a small subgroup of a different curve. */ typedef struct { secp256k1_fe x; /* actual X: x/z^2 */ diff --git a/src/modinv32.h b/src/modinv32.h index 0efdda9ab5..263bda20b8 100644 --- a/src/modinv32.h +++ b/src/modinv32.h @@ -39,4 +39,8 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 /* Same as secp256k1_modinv32_var, but constant time in x (not in the modulus). */ static void secp256k1_modinv32(secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); +/* Compute the Jacobi symbol for (x | modinfo->modulus). Either x must be 0, or x must be coprime with + * modulus. All limbs of x must be non-negative. Returns -2 if the result cannot be computed. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo); + #endif /* SECP256K1_MODINV32_H */ diff --git a/src/modinv32_impl.h b/src/modinv32_impl.h index 661c5fc04c..93bc576675 100644 --- a/src/modinv32_impl.h +++ b/src/modinv32_impl.h @@ -232,6 +232,21 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ return zeta; } +/* inv256[i] = -(2*i+1)^-1 (mod 256) */ +static const uint8_t secp256k1_modinv32_inv256[128] = { + 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, + 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, + 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, + 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, + 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, + 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, + 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, + 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, + 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, + 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, + 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 +}; + /* Compute the transition matrix and eta for 30 divsteps (variable time). * * Input: eta: initial eta @@ -243,21 +258,6 @@ static int32_t secp256k1_modinv32_divsteps_30(int32_t zeta, uint32_t f0, uint32_ * Implements the divsteps_n_matrix_var function from the explanation. */ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t) { - /* inv256[i] = -(2*i+1)^-1 (mod 256) */ - static const uint8_t inv256[128] = { - 0xFF, 0x55, 0x33, 0x49, 0xC7, 0x5D, 0x3B, 0x11, 0x0F, 0xE5, 0xC3, 0x59, - 0xD7, 0xED, 0xCB, 0x21, 0x1F, 0x75, 0x53, 0x69, 0xE7, 0x7D, 0x5B, 0x31, - 0x2F, 0x05, 0xE3, 0x79, 0xF7, 0x0D, 0xEB, 0x41, 0x3F, 0x95, 0x73, 0x89, - 0x07, 0x9D, 0x7B, 0x51, 0x4F, 0x25, 0x03, 0x99, 0x17, 0x2D, 0x0B, 0x61, - 0x5F, 0xB5, 0x93, 0xA9, 0x27, 0xBD, 0x9B, 0x71, 0x6F, 0x45, 0x23, 0xB9, - 0x37, 0x4D, 0x2B, 0x81, 0x7F, 0xD5, 0xB3, 0xC9, 0x47, 0xDD, 0xBB, 0x91, - 0x8F, 0x65, 0x43, 0xD9, 0x57, 0x6D, 0x4B, 0xA1, 0x9F, 0xF5, 0xD3, 0xE9, - 0x67, 0xFD, 0xDB, 0xB1, 0xAF, 0x85, 0x63, 0xF9, 0x77, 0x8D, 0x6B, 0xC1, - 0xBF, 0x15, 0xF3, 0x09, 0x87, 0x1D, 0xFB, 0xD1, 0xCF, 0xA5, 0x83, 0x19, - 0x97, 0xAD, 0x8B, 0xE1, 0xDF, 0x35, 0x13, 0x29, 0xA7, 0x3D, 0x1B, 0xF1, - 0xEF, 0xC5, 0xA3, 0x39, 0xB7, 0xCD, 0xAB, 0x01 - }; - /* Transformation matrix; see comments in secp256k1_modinv32_divsteps_30. */ uint32_t u = 1, v = 0, q = 0, r = 1; uint32_t f = f0, g = g0, m; @@ -297,7 +297,7 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint VERIFY_CHECK(limit > 0 && limit <= 30); m = (UINT32_MAX >> (32 - limit)) & 255U; /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ - w = (g * inv256[(f >> 1) & 127]) & m; + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; /* Do so. */ g += f * w; q += u * w; @@ -317,6 +317,83 @@ static int32_t secp256k1_modinv32_divsteps_30_var(int32_t eta, uint32_t f0, uint return eta; } +/* Compute the transition matrix and eta for 30 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^32 rather than 2^30, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + */ +static int32_t secp256k1_modinv32_posdivsteps_30_var(int32_t eta, uint32_t f0, uint32_t g0, secp256k1_modinv32_trans2x2 *t, int *jacp) { + /* Transformation matrix. */ + uint32_t u = 1, v = 0, q = 0, r = 1; + uint32_t f = f0, g = g0, m; + uint16_t w; + int i = 30, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz32_var(g | (UINT32_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 30 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (30 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (30 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint32_t tmp; + eta = -eta; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + } + /* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more + * than i can be cancelled out (as we'd be done before that point), and no more than eta+1 + * can be done as its sign will flip once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + /* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */ + VERIFY_CHECK(limit > 0 && limit <= 30); + m = (UINT32_MAX >> (32 - limit)) & 255U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */ + w = (g * secp256k1_modinv32_inv256[(f >> 1) & 127]) & m; + /* Do so. */ + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int32_t)u; + t->v = (int32_t)v; + t->q = (int32_t)q; + t->r = (int32_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 30 of them will have determinant 2^30 or -2^30. */ + VERIFY_CHECK((int64_t)t->u * t->r - (int64_t)t->v * t->q == ((int64_t)1) << 30 || + (int64_t)t->u * t->r - (int64_t)t->v * t->q == -(((int64_t)1) << 30)); + *jacp = jac; + return eta; +} + /* Compute (t/2^30) * [d, e] mod modulus, where t is a transition matrix for 30 divsteps. * * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range @@ -584,4 +661,69 @@ static void secp256k1_modinv32_var(secp256k1_modinv32_signed30 *x, const secp256 *x = d; } +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1, or x must be 0. */ +static int secp256k1_jacobi32_maybe_var(const secp256k1_modinv32_signed30 *x, const secp256k1_modinv32_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv32_signed30 f = modinfo->modulus; + secp256k1_modinv32_signed30 g = *x; + int j, len = 9; + int32_t eta = -1; /* eta = -delta; delta is initially 1 */ + int32_t cond, fn, gn; + int jac = 0; + int count; + + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0 && g.v[5] >= 0 && g.v[6] >= 0 && g.v[7] >= 0 && g.v[8] >= 0); + + /* The loop below does not converge for input g=0. Deal with this case specifically. */ + if (!(g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4] | g.v[5] | g.v[6] | g.v[7] | g.v[8])) return 0; + + /* Do up to 50 iterations of 30 posdivsteps (up to 1500 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (750, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY + for (count = 0; count < 25; ++count) { +#else + for (count = 0; count < 50; ++count) { +#endif + /* Compute transition matrix and new eta after 30 posdivsteps. */ + secp256k1_modinv32_trans2x2 t; + eta = secp256k1_modinv32_posdivsteps_30_var(eta, f.v[0] | ((uint32_t)f.v[1] << 30), g.v[0] | ((uint32_t)g.v[1] << 30), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + secp256k1_modinv32_update_fg_30_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int32_t)len - 2) >> 31; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv32_mul_cmp_30(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1500 iterations. Return -2, indicating unknown result. */ + return -2; +} + #endif /* SECP256K1_MODINV32_IMPL_H */ diff --git a/src/modinv64.h b/src/modinv64.h index da506dfa9f..e432fcbe8d 100644 --- a/src/modinv64.h +++ b/src/modinv64.h @@ -43,4 +43,8 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 /* Same as secp256k1_modinv64_var, but constant time in x (not in the modulus). */ static void secp256k1_modinv64(secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); +/* Compute the Jacobi symbol for (x | modinfo->modulus). Either x must be 0, or x must be coprime with + * modulus. All limbs of x must be non-negative. Returns -2 if the result cannot be computed. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo); + #endif /* SECP256K1_MODINV64_H */ diff --git a/src/modinv64_impl.h b/src/modinv64_impl.h index 0743a9c821..2d0d33d777 100644 --- a/src/modinv64_impl.h +++ b/src/modinv64_impl.h @@ -256,7 +256,7 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint tmp = v; v = r; r = -tmp; /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled * out (as we'd be done before that point), and no more than eta+1 can be done as its - * will flip again once that happens. */ + * sign will flip again once that happens. */ limit = ((int)eta + 1) > i ? i : ((int)eta + 1); VERIFY_CHECK(limit > 0 && limit <= 62); /* m is a mask for the bottom min(limit, 6) bits. */ @@ -294,6 +294,94 @@ static int64_t secp256k1_modinv64_divsteps_62_var(int64_t eta, uint64_t f0, uint return eta; } +/* Compute the transition matrix and eta for 62 posdivsteps (variable time, eta=-delta), and keeps track + * of the Jacobi symbol along the way. f0 and g0 must be f and g mod 2^64 rather than 2^62, because + * Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2). + * + * Input: eta: initial eta + * f0: bottom limb of initial f + * g0: bottom limb of initial g + * Output: t: transition matrix + * Return: final eta + */ +static int64_t secp256k1_modinv64_posdivsteps_62_var(int64_t eta, uint64_t f0, uint64_t g0, secp256k1_modinv64_trans2x2 *t, int *jacp) { + /* Transformation matrix; see comments in secp256k1_modinv64_divsteps_62. */ + uint64_t u = 1, v = 0, q = 0, r = 1; + uint64_t f = f0, g = g0, m; + uint32_t w; + int i = 62, limit, zeros; + int jac = *jacp; + + for (;;) { + /* Use a sentinel bit to count zeros only up to i. */ + zeros = secp256k1_ctz64_var(g | (UINT64_MAX << i)); + /* Perform zeros divsteps at once; they all just divide g by two. */ + g >>= zeros; + u <<= zeros; + v <<= zeros; + eta -= zeros; + i -= zeros; + /* Update the bottom bit of jac: when dividing g by an odd power of 2, + * if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */ + jac ^= (zeros & ((f >> 1) ^ (f >> 2))); + /* We're done once we've done 62 posdivsteps. */ + if (i == 0) break; + VERIFY_CHECK((f & 1) == 1); + VERIFY_CHECK((g & 1) == 1); + VERIFY_CHECK((u * f0 + v * g0) == f << (62 - i)); + VERIFY_CHECK((q * f0 + r * g0) == g << (62 - i)); + /* If eta is negative, negate it and replace f,g with g,f. */ + if (eta < 0) { + uint64_t tmp; + eta = -eta; + tmp = f; f = g; g = tmp; + tmp = u; u = q; q = tmp; + tmp = v; v = r; r = tmp; + /* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign + * if both f and g are 3 mod 4. */ + jac ^= ((f & g) >> 1); + /* Use a formula to cancel out up to 6 bits of g. Also, no more than i can be cancelled + * out (as we'd be done before that point), and no more than eta+1 can be done as its + * sign will flip again once that happens. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 6) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 63U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 6) + * bits. */ + w = (f * g * (f * f - 2)) & m; + } else { + /* In this branch, use a simpler formula that only lets us cancel up to 4 bits of g, as + * eta tends to be smaller here. */ + limit = ((int)eta + 1) > i ? i : ((int)eta + 1); + VERIFY_CHECK(limit > 0 && limit <= 62); + /* m is a mask for the bottom min(limit, 4) bits. */ + m = (UINT64_MAX >> (64 - limit)) & 15U; + /* Find what multiple of f must be added to g to cancel its bottom min(limit, 4) + * bits. */ + w = f + (((f + 1) & 4) << 1); + w = (-w * g) & m; + } + g += f * w; + q += u * w; + r += v * w; + VERIFY_CHECK((g & m) == 0); + } + /* Return data in t and return value. */ + t->u = (int64_t)u; + t->v = (int64_t)v; + t->q = (int64_t)q; + t->r = (int64_t)r; + /* The determinant of t must be a power of two. This guarantees that multiplication with t + * does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which + * will be divided out again). As each divstep's individual matrix has determinant 2 or -2, + * the aggregate of 62 of them will have determinant 2^62 or -2^62. */ + VERIFY_CHECK((int128_t)t->u * t->r - (int128_t)t->v * t->q == ((int128_t)1) << 62 || + (int128_t)t->u * t->r - (int128_t)t->v * t->q == -(((int128_t)1) << 62)); + *jacp = jac; + return eta; +} + /* Compute (t/2^62) * [d, e] mod modulus, where t is a transition matrix scaled by 2^62. * * On input and output, d and e are in range (-2*modulus,modulus). All output limbs will be in range @@ -590,4 +678,69 @@ static void secp256k1_modinv64_var(secp256k1_modinv64_signed62 *x, const secp256 *x = d; } +/* Compute the Jacobi symbol of x modulo modinfo->modulus (variable time). gcd(x,modulus) must be 1, or x must be 0. */ +static int secp256k1_jacobi64_maybe_var(const secp256k1_modinv64_signed62 *x, const secp256k1_modinv64_modinfo *modinfo) { + /* Start with f=modulus, g=x, eta=-1. */ + secp256k1_modinv64_signed62 f = modinfo->modulus; + secp256k1_modinv64_signed62 g = *x; + int j, len = 5; + int64_t eta = -1; /* eta = -delta; delta is initially 1 */ + int64_t cond, fn, gn; + int jac = 0; + int count; + + VERIFY_CHECK(g.v[0] >= 0 && g.v[1] >= 0 && g.v[2] >= 0 && g.v[3] >= 0 && g.v[4] >= 0); + + /* The loop below does not converge for input g=0. Deal with this case specifically. */ + if (!(g.v[0] | g.v[1] | g.v[2] | g.v[3] | g.v[4])) return 0; + + /* Do up to 25 iterations of 62 posdivsteps (up to 1550 steps; more is extremely rare) each until f=1. + * In VERIFY mode use a lower number of iterations (744, close to the median 756), so failure actually occurs. */ +#ifdef VERIFY + for (count = 0; count < 12; ++count) { +#else + for (count = 0; count < 25; ++count) { +#endif + /* Compute transition matrix and new eta after 62 posdivsteps. */ + secp256k1_modinv64_trans2x2 t; + eta = secp256k1_modinv64_posdivsteps_62_var(eta, f.v[0] | ((uint64_t)f.v[1] << 62), g.v[0] | ((uint64_t)g.v[1] << 62), &t, &jac); + /* Update f,g using that transition matrix. */ +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + secp256k1_modinv64_update_fg_62_var(len, &f, &g, &t); + /* If the bottom limb of f is 1, there is a chance that f=1. */ + if (f.v[0] == 1) { + cond = 0; + /* Check if the other limbs are also 0. */ + for (j = 1; j < len; ++j) { + cond |= f.v[j]; + } + /* If so, we're done. */ + if (cond == 0) return 1 - 2*(jac & 1); + } + + /* Determine if len>1 and limb (len-1) of both f and g is 0. */ + fn = f.v[len - 1]; + gn = g.v[len - 1]; + cond = ((int64_t)len - 2) >> 63; + cond |= fn; + cond |= gn; + /* If so, reduce length. */ + if (cond == 0) --len; +#ifdef VERIFY + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 0) > 0); /* f > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&f, len, &modinfo->modulus, 1) <= 0); /* f <= modulus */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 0) > 0); /* g > 0 */ + VERIFY_CHECK(secp256k1_modinv64_mul_cmp_62(&g, len, &modinfo->modulus, 1) < 0); /* g < modulus */ +#endif + } + + /* The loop failed to converge to f=g after 1550 iterations. Return -2, indicating unknown result. */ + return -2; +} + #endif /* SECP256K1_MODINV64_IMPL_H */ diff --git a/src/modules/ecdh/bench_impl.h b/src/modules/ecdh/bench_impl.h index 94d833462f..8df15bcf43 100644 --- a/src/modules/ecdh/bench_impl.h +++ b/src/modules/ecdh/bench_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_ECDH_BENCH_H #define SECP256K1_MODULE_ECDH_BENCH_H -#include "../include/secp256k1_ecdh.h" +#include "../../../include/secp256k1_ecdh.h" typedef struct { secp256k1_context *ctx; diff --git a/src/modules/ellswift/Makefile.am.include b/src/modules/ellswift/Makefile.am.include new file mode 100644 index 0000000000..e7efea2981 --- /dev/null +++ b/src/modules/ellswift/Makefile.am.include @@ -0,0 +1,4 @@ +include_HEADERS += include/secp256k1_ellswift.h +noinst_HEADERS += src/modules/ellswift/bench_impl.h +noinst_HEADERS += src/modules/ellswift/main_impl.h +noinst_HEADERS += src/modules/ellswift/tests_impl.h diff --git a/src/modules/ellswift/bench_impl.h b/src/modules/ellswift/bench_impl.h new file mode 100644 index 0000000000..1befd0a4a2 --- /dev/null +++ b/src/modules/ellswift/bench_impl.h @@ -0,0 +1,94 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_BENCH_H +#define SECP256K1_MODULE_ELLSWIFT_BENCH_H + +#include "../include/secp256k1_ellswift.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char rnd64[64]; +} bench_ellswift_data; + +static void bench_ellswift_setup(void* arg) { + bench_ellswift_data *data = (bench_ellswift_data*)arg; + static const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f + }; + memcpy(data->rnd64, point, 32); + memcpy(data->rnd64 + 32, point + 1, 32); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1); +} + +static void bench_ellswift_encode(void* arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[19] ^= 247; + data->rnd64[47] ^= 113; + CHECK(secp256k1_ellswift_encode(data->ctx, data->rnd64, &data->point, data->rnd64 + 16) == 1); + } +} + +static void bench_ellswift_create(void* arg, int iters) { + int i, j; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + unsigned char out64[64]; + CHECK(secp256k1_ellswift_create(data->ctx, out64, data->rnd64, data->rnd64 + 32) == 1); + for (j = 0; j < 64; j++) data->rnd64[j] ^= out64[j]; + } +} + +static void bench_ellswift_decode(void* arg, int iters) { + int i; + secp256k1_pubkey out; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[13] ^= 247; + data->rnd64[49] ^= 113; + CHECK(secp256k1_ellswift_decode(data->ctx, &out, data->rnd64) == 1); + memcpy(data->rnd64 + 16, &out.data, 32); + } +} + +static void bench_ellswift_xdh(void* arg, int iters) { + int i; + bench_ellswift_data *data = (bench_ellswift_data*)arg; + + for (i = 0; i < iters; i++) { + data->rnd64[13] ^= 247; + data->rnd64[49] ^= 113; + CHECK(secp256k1_ellswift_xdh(data->ctx, data->rnd64 + 16, data->rnd64, data->rnd64, data->rnd64 + 13, NULL, NULL) == 1); + } +} + +void run_ellswift_bench(int iters, int argc, char** argv) { + bench_ellswift_data data; + int d = argc == 1; + + /* create a context with signing capabilities */ + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + memset(data.rnd64, 11, sizeof(data.rnd64)); + + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "ellswift_encode")) run_benchmark("ellswift_encode", bench_ellswift_encode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellswift_decode")) run_benchmark("ellswift_decode", bench_ellswift_decode, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "create") || have_flag(argc, argv, "ellswift_create")) run_benchmark("ellswift_create", bench_ellswift_create, bench_ellswift_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "ellswift") || have_flag(argc, argv, "xdh") || have_flag(argc, argv, "ellswift_xdh")) run_benchmark("ellswift_xdh", bench_ellswift_xdh, bench_ellswift_setup, NULL, &data, 10, iters); + + secp256k1_context_destroy(data.ctx); +} + +#endif /* SECP256K1_MODULE_ellswift_BENCH_H */ diff --git a/src/modules/ellswift/main_impl.h b/src/modules/ellswift/main_impl.h new file mode 100644 index 0000000000..fb5a8ab856 --- /dev/null +++ b/src/modules/ellswift/main_impl.h @@ -0,0 +1,393 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_MAIN_H +#define SECP256K1_MODULE_ELLSWIFT_MAIN_H + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_ellswift.h" +#include "../../hash.h" + +/** c1 = the square root of -3 ((-3)**((p+1)/4)). */ +static const secp256k1_fe secp256k1_ellswift_c1 = SECP256K1_FE_CONST(0x0a2d2ba9, 0x3507f1df, 0x233770c2, 0xa797962c, 0xc61f6d15, 0xda14ecd4, 0x7d8d27ae, 0x1cd5f852); +/** c2 = -1/2 * (c1 - 1). */ +static const secp256k1_fe secp256k1_ellswift_c2 = SECP256K1_FE_CONST(0x7ae96a2b, 0x657c0710, 0x6e64479e, 0xac3434e9, 0x9cf04975, 0x12f58995, 0xc1396c28, 0x719501ef); + +/** Decode ElligatorSwift encoding (u, t) to a fraction xn/xd representing a curve X coordinate. */ +static void secp256k1_ellswift_fe2_to_gexfrac_var(secp256k1_fe* xn, secp256k1_fe* xd, const secp256k1_fe* u, const secp256k1_fe* t) { + secp256k1_fe v1 = *u, v2 = *t; + secp256k1_fe v3, v4, v5, v6, v7, v8; + secp256k1_fe_normalize_var(&v1); + secp256k1_fe_normalize_var(&v2); + if (secp256k1_fe_is_zero(&v1)) v1 = secp256k1_fe_one; + if (secp256k1_fe_is_zero(&v2)) v2 = secp256k1_fe_one; + secp256k1_fe_sqr(&v3, &v1); + secp256k1_fe_mul(&v3, &v3, &v1); + secp256k1_fe_add(&v3, &secp256k1_fe_const_b); + secp256k1_fe_sqr(&v4, &v2); + v5 = v3; + secp256k1_fe_add(&v5, &v4); + if (secp256k1_fe_normalizes_to_zero_var(&v5)) { + secp256k1_fe_add(&v2, &v2); + secp256k1_fe_sqr(&v4, &v2); + v5 = v3; + secp256k1_fe_add(&v5, &v4); + } + secp256k1_fe_mul(&v6, &v1, &secp256k1_ellswift_c1); + secp256k1_fe_negate(&v4, &v4, 1); + secp256k1_fe_add(&v4, &v3); + secp256k1_fe_mul(&v4, &v4, &v6); + secp256k1_fe_mul(&v2, &v2, &v6); + secp256k1_fe_sqr(&v2, &v2); + secp256k1_fe_sqr(&v8, &v5); + secp256k1_fe_mul(&v3, &v1, &v2); + secp256k1_fe_add(&v3, &v8); + secp256k1_fe_sqr(&v6, &v2); + secp256k1_fe_sqr(&v6, &v6); + secp256k1_fe_mul_int(&v6, 7); + secp256k1_fe_sqr(&v7, &v3); + secp256k1_fe_mul(&v7, &v7, &v3); + secp256k1_fe_mul(&v7, &v7, &v2); + secp256k1_fe_add(&v7, &v6); + if (secp256k1_fe_jacobi_var(&v7) >= 0) { + *xn = v3; + *xd = v2; + return; + } + secp256k1_fe_mul(&v1, &v1, &v5); + secp256k1_fe_add(&v1, &v4); + secp256k1_fe_half(&v1); + secp256k1_fe_negate(&v1, &v1, 3); + secp256k1_fe_sqr(&v6, &v8); + secp256k1_fe_mul_int(&v6, 7); + secp256k1_fe_sqr(&v7, &v1); + secp256k1_fe_mul(&v7, &v7, &v1); + secp256k1_fe_mul(&v7, &v7, &v5); + secp256k1_fe_add(&v7, &v6); + *xd = v5; + secp256k1_fe_inv_var(&v5, &v5); + if (secp256k1_fe_jacobi_var(&v7) >= 0) { + *xn = v1; + return; + } + secp256k1_fe_add(&v1, &v4); + *xn = v1; +} + +/** Decode ElligatorSwift encoding (u, t) to X coordinate. */ +static void secp256k1_ellswift_fe2_to_gex_var(secp256k1_fe* x, const secp256k1_fe* u, const secp256k1_fe* t) { + secp256k1_fe xn, xd; + secp256k1_ellswift_fe2_to_gexfrac_var(&xn, &xd, u, t); + secp256k1_fe_inv_var(&xd, &xd); + secp256k1_fe_mul(x, &xn, &xd); +} + +/** Decode ElligatorSwift encoding (u, t) to point P. */ +static void secp256k1_ellswift_fe2_to_ge_var(secp256k1_ge* p, const secp256k1_fe* u, const secp256k1_fe* t) { + secp256k1_fe x; + secp256k1_ellswift_fe2_to_gex_var(&x, u, t); + secp256k1_ge_set_xo_var(p, &x, secp256k1_fe_is_odd(t)); +} + +/* Try to complete an ElligatorSwift encoding (u, t) for X coordinate x, given u and x. + * + * There may be up to 8 distinct t values such that (u, t) decodes back to x, but also + * fewer, or none at all. Each such partial inverse can be accessed individually using a + * distinct input argument i (in range 0-7), and some or all of these may return failure. + * The following guarantees exist: + * - Given (x, u), no two distinct i values give the same successful result t. + * - Every successful result maps back to x through secp256k1_ellswift_fe2_to_gex_var. + * - Given (x, u), all t values that map back to x can be reached by combining the + * successful results from this function over all i values, with the exception of: + * - this function cannot be called with u=0 + * - no result with t=0 will be returned + * - no result for which u^3 + t^2 + 7 = 0 will be returned. + */ +static int secp256k1_ellswift_fegex_to_fe_var(secp256k1_fe* t, const secp256k1_fe* x, const secp256k1_fe* u, int i) { + secp256k1_fe xm = *x, um = *u; + secp256k1_fe g, s, w2, w; + secp256k1_fe_normalize_weak(&xm); + secp256k1_fe_normalize_weak(&um); + secp256k1_fe_sqr(&g, u); + secp256k1_fe_mul(&g, &g, u); + secp256k1_fe_add(&g, &secp256k1_fe_const_b); + if ((i & 2) == 0) { + secp256k1_fe o; + s = xm; + secp256k1_fe_add(&s, &um); + secp256k1_fe_sqr(&o, &s); + secp256k1_fe_mul(&o, &o, &s); + secp256k1_fe_negate(&o, &o, 1); + secp256k1_fe_add(&o, &secp256k1_fe_const_b); + if (secp256k1_fe_jacobi_var(&o) >= 0) return 0; + if (i & 1) { + secp256k1_fe_add(&xm, &um); + secp256k1_fe_negate(&xm, &xm, 2); + } + o = um; + secp256k1_fe_add(&o, &xm); + secp256k1_fe_sqr(&o, &o); + secp256k1_fe_negate(&o, &o, 1); + secp256k1_fe_mul(&w2, &um, &xm); + secp256k1_fe_add(&w2, &o); + secp256k1_fe_inv_var(&w2, &w2); + secp256k1_fe_mul(&w2, &w2, &g); + } else { + secp256k1_fe r2, r; + secp256k1_fe_negate(&w2, &um, 1); + secp256k1_fe_add(&w2, &xm); + if (secp256k1_fe_normalizes_to_zero_var(&w2)) return 0; + secp256k1_fe_normalize_weak(&g); + secp256k1_fe_mul_int(&g, 4); + secp256k1_fe_sqr(&r2, &um); + secp256k1_fe_mul_int(&r2, 3); + secp256k1_fe_mul(&r2, &r2, &w2); + secp256k1_fe_add(&r2, &g); + secp256k1_fe_mul(&r2, &r2, &w2); + secp256k1_fe_negate(&r2, &r2, 1); + if (!secp256k1_fe_sqrt(&r, &r2)) return 0; + if (i & 1) { + if (secp256k1_fe_normalizes_to_zero_var(&r)) return 0; + secp256k1_fe_negate(&r, &r, 1); + } + secp256k1_fe_inv_var(&xm, &w2); + secp256k1_fe_mul(&xm, &xm, &r); + secp256k1_fe_add(&xm, &um); + secp256k1_fe_half(&xm); + secp256k1_fe_negate(&xm, &xm, 2); + } + if (!secp256k1_fe_sqrt(&w, &w2)) return 0; + if (i & 4) secp256k1_fe_negate(&w, &w, 1); + secp256k1_fe_mul(&um, &um, &secp256k1_ellswift_c2); + secp256k1_fe_add(&um, &xm); + secp256k1_fe_mul(t, &w, &um); + return 1; +} + +/** Find an ElligatorSwift encoding (u, t) for X coordinate x. + * + * hasher is a SHA256 object which a incrementing 4-byte counter is added to to + * generate randomness for the rejection sampling in this function. Its size plus + * 4 (for the counter) plus 9 (for the SHA256 padding) must be a multiple of 64 + * for efficiency reasons. + */ +static void secp256k1_ellswift_gex_to_fe2_var(secp256k1_fe* u, secp256k1_fe* t, const secp256k1_fe* x, const secp256k1_sha256* hasher) { + /* Pool of 3-bit branch values. */ + unsigned char branch_hash[32]; + /* Number of 3-bit values in branch_hash left. */ + int branches_left = 0; + /* Field elements u and branch values are extracted from + * SHA256(hasher || cnt) for consecutive values of cnt. cnt==0 + * is first used to populate a pool of 64 4-bit branch values. The 64 cnt + * values that follow are used to generate field elements u. cnt==65 (and + * multiples thereof) are used to repopulate the pool and start over, if + * that were ever necessary. */ + uint32_t cnt = 0; + VERIFY_CHECK((hasher->bytes + 4 + 9) % 64 == 0); + while (1) { + int branch; + /* If the pool of branch values is empty, populate it. */ + if (branches_left == 0) { + secp256k1_sha256 hash = *hasher; + unsigned char buf4[4]; + buf4[0] = cnt; + buf4[1] = cnt >> 8; + buf4[2] = cnt >> 16; + buf4[3] = cnt >> 24; + ++cnt; + secp256k1_sha256_write(&hash, buf4, 4); + secp256k1_sha256_finalize(&hash, branch_hash); + branches_left = 64; + } + /* Take a 3-bit branch value from the branch pool (top bit is discarded). */ + --branches_left; + branch = (branch_hash[branches_left >> 1] >> ((branches_left & 1) << 2)) & 7; + /* Compute a new u value by hashing. */ + { + secp256k1_sha256 hash = *hasher; + unsigned char buf4[4]; + unsigned char u32[32]; + buf4[0] = cnt; + buf4[1] = cnt >> 8; + buf4[2] = cnt >> 16; + buf4[3] = cnt >> 24; + ++cnt; + secp256k1_sha256_write(&hash, buf4, 4); + secp256k1_sha256_finalize(&hash, u32); + if (!secp256k1_fe_set_b32(u, u32)) continue; + if (secp256k1_fe_is_zero(u)) continue; + } + /* Find a remainder t, and return it if found. */ + if (secp256k1_ellswift_fegex_to_fe_var(t, x, u, branch)) { + secp256k1_fe_normalize_var(t); + break; + } + } +} + +/** Find an ElligatorSwift encoding (u, t) for point P. */ +static void secp256k1_ellswift_ge_to_fe2_var(secp256k1_fe* u, secp256k1_fe* t, const secp256k1_ge* p, const secp256k1_sha256* hasher) { + secp256k1_ellswift_gex_to_fe2_var(u, t, &p->x, hasher); + if (secp256k1_fe_is_odd(t) != secp256k1_fe_is_odd(&p->y)) { + secp256k1_fe_negate(t, t, 1); + secp256k1_fe_normalize_var(t); + } +} + +int secp256k1_ellswift_encode(const secp256k1_context* ctx, unsigned char *ell64, const secp256k1_pubkey *pubkey, const unsigned char *rnd32) { + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(rnd32 != NULL); + + if (secp256k1_pubkey_load(ctx, &p, pubkey)) { + static const unsigned char PREFIX[128 - 9 - 4 - 32 - 33] = "secp256k1_ellswift_encode"; + secp256k1_fe u, t; + unsigned char p33[33]; + secp256k1_sha256 hash; + + /* Set up hasher state */ + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, PREFIX, sizeof(PREFIX)); + secp256k1_sha256_write(&hash, rnd32, 32); + secp256k1_fe_get_b32(p33, &p.x); + p33[32] = secp256k1_fe_is_odd(&p.y); + secp256k1_sha256_write(&hash, p33, sizeof(p33)); + VERIFY_CHECK(hash.bytes == 128 - 9 - 4); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_ge_to_fe2_var(&u, &t, &p, &hash); + secp256k1_fe_get_b32(ell64, &u); + secp256k1_fe_get_b32(ell64 + 32, &t); + return 1; + } + /* Only returned in case the provided pubkey is invalid. */ + return 0; +} + +int secp256k1_ellswift_create(const secp256k1_context* ctx, unsigned char *ell64, const unsigned char *seckey32, const unsigned char *rnd32) { + secp256k1_ge p; + secp256k1_fe u, t; + secp256k1_sha256 hash; + secp256k1_scalar seckey_scalar; + static const unsigned char PREFIX[32] = "secp256k1_ellswift_create"; + static const unsigned char ZERO[32] = {0}; + int ret = 0; + + /* Sanity check inputs. */ + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(ell64 != NULL); + memset(ell64, 0, 64); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey32 != NULL); + + /* Compute (affine) public key */ + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey32); + secp256k1_fe_normalize_var(&p.x); + secp256k1_fe_normalize_var(&p.y); + + /* Set up hasher state */ + secp256k1_sha256_initialize(&hash); + secp256k1_sha256_write(&hash, PREFIX, sizeof(PREFIX)); + secp256k1_sha256_write(&hash, seckey32, 32); + secp256k1_sha256_write(&hash, rnd32 ? rnd32 : ZERO, 32); + secp256k1_sha256_write(&hash, ZERO, 32 - 9 - 4); + + /* Compute ElligatorSwift encoding and construct output. */ + secp256k1_ellswift_ge_to_fe2_var(&u, &t, &p, &hash); + secp256k1_fe_get_b32(ell64, &u); + secp256k1_fe_get_b32(ell64 + 32, &t); + + secp256k1_memczero(ell64, 64, !ret); + secp256k1_scalar_clear(&seckey_scalar); + + return ret; +} + +int secp256k1_ellswift_decode(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *ell64) { + secp256k1_fe u, t; + secp256k1_ge p; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(ell64 != NULL); + + secp256k1_fe_set_b32(&u, ell64); + secp256k1_fe_normalize_var(&u); + secp256k1_fe_set_b32(&t, ell64 + 32); + secp256k1_fe_normalize_var(&t); + secp256k1_ellswift_fe2_to_ge_var(&p, &u, &t); + secp256k1_pubkey_save(pubkey, &p); + return 1; +} + +static int ellswift_xdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *ours64, const unsigned char *theirs64, void *data) { + secp256k1_sha256 sha; + + (void)data; + + secp256k1_sha256_initialize(&sha); + if (secp256k1_memcmp_var(ours64, theirs64, 64) <= 0) { + secp256k1_sha256_write(&sha, ours64, 64); + secp256k1_sha256_write(&sha, theirs64, 64); + } else { + secp256k1_sha256_write(&sha, theirs64, 64); + secp256k1_sha256_write(&sha, ours64, 64); + } + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_sha256 = ellswift_xdh_hash_function_sha256; +const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_default = ellswift_xdh_hash_function_sha256; + +int secp256k1_ellswift_xdh(const secp256k1_context* ctx, unsigned char *output, const unsigned char* theirs64, const unsigned char* ours64, const unsigned char* seckey32, secp256k1_ellswift_xdh_hash_function hashfp, void *data) { + int ret = 0; + int overflow; + secp256k1_scalar s; + secp256k1_fe xn, xd, px, u, t; + unsigned char sx[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(theirs64 != NULL); + ARG_CHECK(ours64 != NULL); + ARG_CHECK(seckey32 != NULL); + + if (hashfp == NULL) { + hashfp = secp256k1_ellswift_xdh_hash_function_default; + } + + /* Load remote public key (as fraction). */ + secp256k1_fe_set_b32(&u, theirs64); + secp256k1_fe_normalize_var(&u); + secp256k1_fe_set_b32(&t, theirs64 + 32); + secp256k1_fe_normalize_var(&t); + secp256k1_ellswift_fe2_to_gexfrac_var(&xn, &xd, &u, &t); + + /* Load private key (using one if invalid). */ + secp256k1_scalar_set_b32(&s, seckey32, &overflow); + overflow = secp256k1_scalar_is_zero(&s); + secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow); + + /* Compute shared X coordinate. */ + secp256k1_ecmult_const_xonly(&px, &xn, &xd, &s, 256, 1); + secp256k1_fe_normalize(&px); + secp256k1_fe_get_b32(sx, &px); + + /* Invoke hasher */ + ret = hashfp(output, sx, ours64, theirs64, data); + + memset(sx, 0, 32); + secp256k1_fe_clear(&px); + secp256k1_scalar_clear(&s); + + return !!ret & !overflow; +} + +#endif diff --git a/src/modules/ellswift/tests_impl.h b/src/modules/ellswift/tests_impl.h new file mode 100644 index 0000000000..5c4e968fed --- /dev/null +++ b/src/modules/ellswift/tests_impl.h @@ -0,0 +1,133 @@ +/*********************************************************************** + * Copyright (c) 2022 Pieter Wuile * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or https://www.opensource.org/licenses/mit-license.php.* + ***********************************************************************/ + +#ifndef SECP256K1_MODULE_ELLSWIFT_TESTS_H +#define SECP256K1_MODULE_ELLSWIFT_TESTS_H + +#include "../../../include/secp256k1_ellswift.h" + +/** This is a hasher for ellswift_xdh which just returns the shared X coordinate. + * + * This is generally a bad idea as it means changes to the encoding of the + * exchanged public keys do not affect the shared secret. However, it's used here + * in tests to be able to verify the X coordinate through other means. + */ +static int ellswift_xdh_hash_x32(unsigned char *output, const unsigned char *x32, const unsigned char *ours64, const unsigned char *theirs64, void *data) { + (void)ours64; + (void)theirs64; + (void)data; + memcpy(output, x32, 32); + return 1; +} + +void run_ellswift_tests(void) { + int i = 0; + /* Verify that secp256k1_ellswift_encode + decode roundtrips. */ + for (i = 0; i < 1000 * count; i++) { + unsigned char rnd32[32]; + unsigned char ell64[64]; + secp256k1_ge g, g2; + secp256k1_pubkey pubkey, pubkey2; + /* Generate random public key and random randomizer. */ + random_group_element_test(&g); + secp256k1_pubkey_save(&pubkey, &g); + secp256k1_testrand256(rnd32); + /* Convert the public key to ElligatorSwift and back. */ + secp256k1_ellswift_encode(ctx, ell64, &pubkey, rnd32); + secp256k1_ellswift_decode(ctx, &pubkey2, ell64); + secp256k1_pubkey_load(ctx, &g2, &pubkey2); + /* Compare with original. */ + ge_equals_ge(&g, &g2); + } + /* Verify the behavior of secp256k1_ellswift_create */ + for (i = 0; i < 400 * count; i++) { + unsigned char rnd32[32], sec32[32]; + secp256k1_scalar sec; + secp256k1_gej res; + secp256k1_ge dec; + secp256k1_pubkey pub; + unsigned char ell64[64]; + int ret; + /* Generate random secret key and random randomizer. */ + secp256k1_testrand256_test(rnd32); + random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Construct ElligatorSwift-encoded public keys for that key. */ + ret = secp256k1_ellswift_create(ctx, ell64, sec32, rnd32); + CHECK(ret); + /* Decode it, and compare with traditionally-computed public key. */ + secp256k1_ellswift_decode(ctx, &pub, ell64); + secp256k1_pubkey_load(ctx, &dec, &pub); + secp256k1_ecmult(&res, NULL, &secp256k1_scalar_zero, &sec); + ge_equals_gej(&dec, &res); + } + /* Verify that secp256k1_ellswift_xdh computes the right shared X coordinate. */ + for (i = 0; i < 800 * count; i++) { + unsigned char ell64[64], sec32[32], share32[32]; + secp256k1_scalar sec; + secp256k1_ge dec, res; + secp256k1_fe share_x; + secp256k1_gej decj, resj; + secp256k1_pubkey pub; + int ret; + /* Generate random secret key. */ + random_scalar_order_test(&sec); + secp256k1_scalar_get_b32(sec32, &sec); + /* Generate random ElligatorSwift encoding for the remote key and decode it. */ + secp256k1_testrand256_test(ell64); + secp256k1_testrand256_test(ell64 + 32); + secp256k1_ellswift_decode(ctx, &pub, ell64); + secp256k1_pubkey_load(ctx, &dec, &pub); + secp256k1_gej_set_ge(&decj, &dec); + /* Compute the X coordinate of seckey*pubkey using ellswift_xdh. Note that we + * pass ell64 as claimed (but incorrect) encoding for sec32 here; this works + * because the "hasher" function we use here ignores the ours64 argument. */ + ret = secp256k1_ellswift_xdh(ctx, share32, ell64, ell64, sec32, &ellswift_xdh_hash_x32, NULL); + CHECK(ret); + secp256k1_fe_set_b32(&share_x, share32); + /* Compute seckey*pubkey directly. */ + secp256k1_ecmult(&resj, &decj, &sec, NULL); + secp256k1_ge_set_gej(&res, &resj); + /* Compare. */ + CHECK(check_fe_equal(&res.x, &share_x)); + } + /* Verify the joint behavior of secp256k1_ellswift_xdh */ + for (i = 0; i < 200 * count; i++) { + unsigned char rnd32a[32], rnd32b[32], sec32a[32], sec32b[32]; + secp256k1_scalar seca, secb; + unsigned char ell64a[64], ell64b[64]; + unsigned char share32a[32], share32b[32]; + int ret; + /* Generate random secret keys and random randomizers. */ + secp256k1_testrand256_test(rnd32a); + secp256k1_testrand256_test(rnd32b); + random_scalar_order_test(&seca); + random_scalar_order_test(&secb); + secp256k1_scalar_get_b32(sec32a, &seca); + secp256k1_scalar_get_b32(sec32b, &secb); + /* Construct ElligatorSwift-encoded public keys for those keys. */ + ret = secp256k1_ellswift_create(ctx, ell64a, sec32a, rnd32a); + CHECK(ret); + ret = secp256k1_ellswift_create(ctx, ell64b, sec32b, rnd32b); + CHECK(ret); + /* Compute the shared secret both ways and compare with each other. */ + ret = secp256k1_ellswift_xdh(ctx, share32a, ell64a, ell64b, sec32b, NULL, NULL); + CHECK(ret); + ret = secp256k1_ellswift_xdh(ctx, share32b, ell64b, ell64a, sec32a, NULL, NULL); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32a, share32b, 32) == 0); + /* Verify that the shared secret doesn't match if a secret key or remote pubkey changes. */ + secp256k1_testrand_flip(ell64a, 64); + ret = secp256k1_ellswift_xdh(ctx, share32a, ell64a, ell64b, sec32b, NULL, NULL); + CHECK(ret); + CHECK(secp256k1_memcmp_var(share32a, share32b, 32) != 0); + secp256k1_testrand_flip(sec32a, 32); + ret = secp256k1_ellswift_xdh(ctx, share32a, ell64a, ell64b, sec32b, NULL, NULL); + CHECK(!ret || secp256k1_memcmp_var(share32a, share32b, 32) != 0); + } +} + +#endif diff --git a/src/modules/extrakeys/tests_exhaustive_impl.h b/src/modules/extrakeys/tests_exhaustive_impl.h index d4a2f5bdf4..5ecc90d50f 100644 --- a/src/modules/extrakeys/tests_exhaustive_impl.h +++ b/src/modules/extrakeys/tests_exhaustive_impl.h @@ -7,8 +7,8 @@ #ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H #define SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H -#include "src/modules/extrakeys/main_impl.h" #include "../../../include/secp256k1_extrakeys.h" +#include "main_impl.h" static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) { secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1]; diff --git a/src/modules/recovery/bench_impl.h b/src/modules/recovery/bench_impl.h index 4a9e886910..e1cf4924d3 100644 --- a/src/modules/recovery/bench_impl.h +++ b/src/modules/recovery/bench_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_RECOVERY_BENCH_H #define SECP256K1_MODULE_RECOVERY_BENCH_H -#include "../include/secp256k1_recovery.h" +#include "../../../include/secp256k1_recovery.h" typedef struct { secp256k1_context *ctx; diff --git a/src/modules/recovery/tests_exhaustive_impl.h b/src/modules/recovery/tests_exhaustive_impl.h index 590a972ed3..ed9386b6f8 100644 --- a/src/modules/recovery/tests_exhaustive_impl.h +++ b/src/modules/recovery/tests_exhaustive_impl.h @@ -7,7 +7,7 @@ #ifndef SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H #define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H -#include "src/modules/recovery/main_impl.h" +#include "main_impl.h" #include "../../../include/secp256k1_recovery.h" void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) { diff --git a/src/modules/schnorrsig/bench_impl.h b/src/modules/schnorrsig/bench_impl.h index 41f393c84d..84a172742f 100644 --- a/src/modules/schnorrsig/bench_impl.h +++ b/src/modules/schnorrsig/bench_impl.h @@ -91,10 +91,12 @@ void run_schnorrsig_bench(int iters, int argc, char** argv) { free((void *)data.msgs[i]); free((void *)data.sigs[i]); } - free(data.keypairs); - free(data.pk); - free(data.msgs); - free(data.sigs); + + /* Casting to (void *) avoids a stupid warning in MSVC. */ + free((void *)data.keypairs); + free((void *)data.pk); + free((void *)data.msgs); + free((void *)data.sigs); secp256k1_context_destroy(data.ctx); } diff --git a/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/modules/schnorrsig/tests_exhaustive_impl.h index d8df9dd2df..55f9028a63 100644 --- a/src/modules/schnorrsig/tests_exhaustive_impl.h +++ b/src/modules/schnorrsig/tests_exhaustive_impl.h @@ -8,7 +8,7 @@ #define SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H #include "../../../include/secp256k1_schnorrsig.h" -#include "src/modules/schnorrsig/main_impl.h" +#include "main_impl.h" static const unsigned char invalid_pubkey_bytes[][32] = { /* 0 */ diff --git a/src/scratch_impl.h b/src/scratch_impl.h index 688e18eb66..f71a20b963 100644 --- a/src/scratch_impl.h +++ b/src/scratch_impl.h @@ -25,11 +25,11 @@ static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* err static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) { if (scratch != NULL) { - VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */ if (secp256k1_memcmp_var(scratch->magic, "scratch", 8) != 0) { secp256k1_callback_call(error_callback, "invalid scratch space"); return; } + VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */ memset(scratch->magic, 0, sizeof(scratch->magic)); free(scratch); } diff --git a/src/secp256k1.c b/src/secp256k1.c index 8f34c35283..df9bd1e5d7 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -4,6 +4,17 @@ * file COPYING or https://www.opensource.org/licenses/mit-license.php.* ***********************************************************************/ +/* This is a C project. It should not be compiled with a C++ compiler, + * and we error out if we detect one. + * + * We still want to be able to test the project with a C++ compiler + * because it is still good to know if this will lead to real trouble, so + * there is a possibility to override the check. But be warned that + * compiling with a C++ compiler is not supported. */ +#if defined(__cplusplus) && !defined(SECP256K1_CPLUSPLUS_TEST_OVERRIDE) +#error Trying to compile a C project with a C++ compiler. +#endif + #define SECP256K1_BUILD #include "../include/secp256k1.h" @@ -765,3 +776,7 @@ int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, #ifdef ENABLE_MODULE_SCHNORRSIG # include "modules/schnorrsig/main_impl.h" #endif + +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index dd53173930..9494a2cdc9 100644 --- a/src/tests.c +++ b/src/tests.c @@ -942,12 +942,32 @@ void test_modinv32_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod uint16_to_signed30(&x, in); nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4] | x.v[5] | x.v[6] | x.v[7] | x.v[8]) != 0; uint16_to_signed30(&m.modulus, mod); - mutate_sign_signed30(&m.modulus); /* compute 1/modulus mod 2^30 */ m.modulus_inv30 = modinv2p64(m.modulus.v[0]) & 0x3fffffff; CHECK(((m.modulus_inv30 * m.modulus.v[0]) & 0x3fffffff) == 1); + /* Test secp256k1_jacobi32_maybe_var. */ + { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed30(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 0 or 1 (or uncomputable). */ + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == -2 || jac == nonzero); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed30(&x, sqr); + jac = secp256k1_jacobi32_maybe_var(&x, &m); + CHECK(jac == -2 || jac == (1 - (mod[0] & 2)) * nonzero); + } + + uint16_to_signed30(&x, in); + mutate_sign_signed30(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv32_var : secp256k1_modinv32)(&x, &m); @@ -1015,12 +1035,32 @@ void test_modinv64_uint16(uint16_t* out, const uint16_t* in, const uint16_t* mod uint16_to_signed62(&x, in); nonzero = (x.v[0] | x.v[1] | x.v[2] | x.v[3] | x.v[4]) != 0; uint16_to_signed62(&m.modulus, mod); - mutate_sign_signed62(&m.modulus); /* compute 1/modulus mod 2^62 */ m.modulus_inv62 = modinv2p64(m.modulus.v[0]) & M62; CHECK(((m.modulus_inv62 * m.modulus.v[0]) & M62) == 1); + /* Test secp256k1_jacobi64_maybe_var. */ + { + int jac; + uint16_t sqr[16], negone[16]; + mulmod256(sqr, in, in, mod); + uint16_to_signed62(&x, sqr); + /* Compute jacobi symbol of in^2, which must be 0 or 1 (or uncomputable). */ + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == -2 || jac == nonzero); + /* Then compute the jacobi symbol of -(in^2). x and -x have opposite + * jacobi symbols if and only if (mod % 4) == 3. */ + negone[0] = mod[0] - 1; + for (i = 1; i < 16; ++i) negone[i] = mod[i]; + mulmod256(sqr, sqr, negone, mod); + uint16_to_signed62(&x, sqr); + jac = secp256k1_jacobi64_maybe_var(&x, &m); + CHECK(jac == -2 || jac == (1 - (mod[0] & 2)) * nonzero); + } + + uint16_to_signed62(&x, in); + mutate_sign_signed62(&m.modulus); for (vartime = 0; vartime < 2; ++vartime) { /* compute inverse */ (vartime ? secp256k1_modinv64_var : secp256k1_modinv64)(&x, &m); @@ -2854,8 +2894,10 @@ void run_sqrt(void) { for (j = 0; j < count; j++) { random_fe(&x); secp256k1_fe_sqr(&s, &x); + CHECK(secp256k1_fe_jacobi_var(&s) == 1); test_sqrt(&s, &x); secp256k1_fe_negate(&t, &s, 1); + CHECK(secp256k1_fe_jacobi_var(&t) == -1); test_sqrt(&t, NULL); secp256k1_fe_mul(&t, &s, &ns); test_sqrt(&t, NULL); @@ -3986,6 +4028,68 @@ void ecmult_const_mult_zero_one(void) { ge_equals_ge(&res2, &point); } +void ecmult_const_mult_xonly(void) { + int i; + + /* Test correspondence between secp256k1_ecmult_const and secp256k1_ecmult_const_xonly. */ + for (i = 0; i < 2*count; ++i) { + secp256k1_ge base; + secp256k1_gej basej, resj; + secp256k1_fe n, d, resx, v; + secp256k1_scalar q; + int res; + /* Random base point. */ + random_group_element_test(&base); + /* Random scalar to multiply it with. */ + random_scalar_order_test(&q); + /* If i is odd, n=d*base.x for random non-zero d */ + if (i & 1) { + do { + random_field_element_test(&d); + } while (secp256k1_fe_normalizes_to_zero_var(&d)); + secp256k1_fe_mul(&n, &base.x, &d); + } else { + n = base.x; + } + /* Perform x-only multiplication. */ + res = secp256k1_ecmult_const_xonly(&resx, &n, (i & 1) ? &d : NULL, &q, 256, i & 2); + CHECK(res); + /* Perform normal multiplication. */ + secp256k1_gej_set_ge(&basej, &base); + secp256k1_ecmult(&resj, &basej, &q, NULL); + /* Check that resj's X coordinate corresponds with resx. */ + secp256k1_fe_sqr(&v, &resj.z); + secp256k1_fe_mul(&v, &v, &resx); + CHECK(check_fe_equal(&v, &resj.x)); + } + + /* Test that secp256k1_ecmult_const_xonly correctly rejects X coordinates not on curve. */ + for (i = 0; i < 2*count; ++i) { + secp256k1_fe x, n, d, c, r; + int res; + secp256k1_scalar q; + random_scalar_order_test(&q); + /* Generate random X coordinate not on the curve. */ + do { + random_field_element_test(&x); + secp256k1_fe_sqr(&c, &x); + secp256k1_fe_mul(&c, &c, &x); + secp256k1_fe_add(&c, &secp256k1_fe_const_b); + } while (secp256k1_fe_jacobi_var(&c) >= 0); + /* If i is odd, n=d*x for random non-zero d. */ + if (i & 1) { + do { + random_field_element_test(&d); + } while (secp256k1_fe_normalizes_to_zero_var(&d)); + secp256k1_fe_mul(&n, &x, &d); + } else { + n = x; + } + res = secp256k1_ecmult_const_xonly(&r, &n, (i & 1) ? &d : NULL, &q, 256, 0); + CHECK(res == 0); + } +} + void ecmult_const_chain_multiply(void) { /* Check known result (randomly generated test problem from sage) */ const secp256k1_scalar scalar = SECP256K1_SCALAR_CONST( @@ -4017,6 +4121,7 @@ void run_ecmult_const_tests(void) { ecmult_const_random_mult(); ecmult_const_commutativity(); ecmult_const_chain_multiply(); + ecmult_const_mult_xonly(); } typedef struct { @@ -6872,6 +6977,10 @@ void run_ecdsa_edge_cases(void) { # include "modules/schnorrsig/tests_impl.h" #endif +#ifdef ENABLE_MODULE_ELLSWIFT +# include "modules/ellswift/tests_impl.h" +#endif + void run_secp256k1_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -7086,11 +7195,15 @@ int main(int argc, char **argv) { run_context_tests(0); run_context_tests(1); run_scratch_tests(); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - if (secp256k1_testrand_bits(1)) { + /* 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 (secp256k1_testrand_bits(4)) { unsigned char rand32[32]; secp256k1_testrand256(rand32); - CHECK(secp256k1_context_randomize(ctx, secp256k1_testrand_bits(1) ? rand32 : NULL)); + CHECK(secp256k1_context_randomize(ctx, rand32)); } run_rand_bits(); @@ -7172,6 +7285,10 @@ int main(int argc, char **argv) { run_schnorrsig_tests(); #endif +#ifdef ENABLE_MODULE_ELLSWIFT + run_ellswift_tests(); +#endif + /* util tests */ run_secp256k1_memczero_test(); run_secp256k1_byteorder_tests(); diff --git a/src/tests_exhaustive.c b/src/tests_exhaustive.c index 6a4e2340f2..225bbddffc 100644 --- a/src/tests_exhaustive.c +++ b/src/tests_exhaustive.c @@ -342,15 +342,15 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou } #ifdef ENABLE_MODULE_RECOVERY -#include "src/modules/recovery/tests_exhaustive_impl.h" +#include "modules/recovery/tests_exhaustive_impl.h" #endif #ifdef ENABLE_MODULE_EXTRAKEYS -#include "src/modules/extrakeys/tests_exhaustive_impl.h" +#include "modules/extrakeys/tests_exhaustive_impl.h" #endif #ifdef ENABLE_MODULE_SCHNORRSIG -#include "src/modules/schnorrsig/tests_exhaustive_impl.h" +#include "modules/schnorrsig/tests_exhaustive_impl.h" #endif int main(int argc, char** argv) { diff --git a/src/util.h b/src/util.h index dac86bd77f..0921e34f16 100644 --- a/src/util.h +++ b/src/util.h @@ -16,6 +16,11 @@ #include #include +#define STR_(x) #x +#define STR(x) STR_(x) +#define DEBUG_CONFIG_MSG(x) "DEBUG_CONFIG: " x +#define DEBUG_CONFIG_DEF(x) DEBUG_CONFIG_MSG(#x "=" STR(x)) + typedef struct { void (*fn)(const char *text, void* data); const void* data;