Merge pull request #115 from elichai/external-callbacks

Updating secp256k1 and linking with external callbacks
This commit is contained in:
Andrew Poelstra 2019-07-04 18:49:01 +00:00 committed by GitHub
commit d710e5885b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1329 additions and 454 deletions

3
.gitignore vendored
View File

@ -4,3 +4,6 @@ Cargo.lock
#IntelliJ project files #IntelliJ project files
.idea .idea
*.iml *.iml
CMakeLists.txt
cmake-build-debug

View File

@ -52,7 +52,9 @@ fn main() {
.define("USE_NUM_NONE", Some("1")) .define("USE_NUM_NONE", Some("1"))
.define("USE_FIELD_INV_BUILTIN", Some("1")) .define("USE_FIELD_INV_BUILTIN", Some("1"))
.define("USE_SCALAR_INV_BUILTIN", Some("1")) .define("USE_SCALAR_INV_BUILTIN", Some("1"))
.define("ENABLE_MODULE_ECDH", Some("1")); .define("ENABLE_MODULE_ECDH", Some("1"))
.define("USE_EXTERNAL_DEFAULT_CALLBACKS", Some("1"))
.define("ECMULT_WINDOW_SIZE", Some("15")); // This is the default in the configure file (`auto`)
#[cfg(feature = "endomorphism")] #[cfg(feature = "endomorphism")]
base_config.define("USE_ENDOMORPHISM", Some("1")); base_config.define("USE_ENDOMORPHISM", Some("1"));

View File

@ -1,5 +1,5 @@
language: c language: c
sudo: false os: linux
addons: addons:
apt: apt:
packages: libgmp-dev packages: libgmp-dev
@ -66,4 +66,3 @@ script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux

View File

@ -8,6 +8,7 @@ else
JNI_LIB = JNI_LIB =
endif endif
include_HEADERS = include/secp256k1.h include_HEADERS = include/secp256k1.h
include_HEADERS += include/secp256k1_preallocated.h
noinst_HEADERS = noinst_HEADERS =
noinst_HEADERS += src/scalar.h noinst_HEADERS += src/scalar.h
noinst_HEADERS += src/scalar_4x64.h noinst_HEADERS += src/scalar_4x64.h
@ -114,7 +115,7 @@ exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDE
if !ENABLE_COVERAGE if !ENABLE_COVERAGE
exhaustive_tests_CPPFLAGS += -DVERIFY exhaustive_tests_CPPFLAGS += -DVERIFY
endif endif
exhaustive_tests_LDADD = $(SECP_LIBS) exhaustive_tests_LDADD = $(SECP_LIBS) $(COMMON_LIB)
exhaustive_tests_LDFLAGS = -static exhaustive_tests_LDFLAGS = -static
TESTS += exhaustive_tests TESTS += exhaustive_tests
endif endif
@ -151,7 +152,6 @@ endif
if USE_ECMULT_STATIC_PRECOMPUTATION if USE_ECMULT_STATIC_PRECOMPUTATION
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
gen_context_OBJECTS = gen_context.o gen_context_OBJECTS = gen_context.o
gen_context_BIN = gen_context$(BUILD_EXEEXT) gen_context_BIN = gen_context$(BUILD_EXEEXT)
@ -159,7 +159,7 @@ gen_%.o: src/gen_%.c
$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@ $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
$(gen_context_BIN): $(gen_context_OBJECTS) $(gen_context_BIN): $(gen_context_OBJECTS)
$(CC_FOR_BUILD) $^ -o $@ $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $^ -o $@
$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h $(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h
$(tests_OBJECTS): src/ecmult_static_context.h $(tests_OBJECTS): src/ecmult_static_context.h

View File

@ -85,42 +85,42 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
]) ])
AC_ARG_ENABLE(benchmark, AC_ARG_ENABLE(benchmark,
AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is yes)]), AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]),
[use_benchmark=$enableval], [use_benchmark=$enableval],
[use_benchmark=yes]) [use_benchmark=yes])
AC_ARG_ENABLE(coverage, AC_ARG_ENABLE(coverage,
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]),
[enable_coverage=$enableval], [enable_coverage=$enableval],
[enable_coverage=no]) [enable_coverage=no])
AC_ARG_ENABLE(tests, AC_ARG_ENABLE(tests,
AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]),
[use_tests=$enableval], [use_tests=$enableval],
[use_tests=yes]) [use_tests=yes])
AC_ARG_ENABLE(openssl_tests, AC_ARG_ENABLE(openssl_tests,
AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]), AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests [default=auto]]),
[enable_openssl_tests=$enableval], [enable_openssl_tests=$enableval],
[enable_openssl_tests=auto]) [enable_openssl_tests=auto])
AC_ARG_ENABLE(experimental, AC_ARG_ENABLE(experimental,
AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]),
[use_experimental=$enableval], [use_experimental=$enableval],
[use_experimental=no]) [use_experimental=no])
AC_ARG_ENABLE(exhaustive_tests, AC_ARG_ENABLE(exhaustive_tests,
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]), AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]),
[use_exhaustive_tests=$enableval], [use_exhaustive_tests=$enableval],
[use_exhaustive_tests=yes]) [use_exhaustive_tests=yes])
AC_ARG_ENABLE(endomorphism, AC_ARG_ENABLE(endomorphism,
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), AS_HELP_STRING([--enable-endomorphism],[enable endomorphism [default=no]]),
[use_endomorphism=$enableval], [use_endomorphism=$enableval],
[use_endomorphism=no]) [use_endomorphism=no])
AC_ARG_ENABLE(ecmult_static_precomputation, AC_ARG_ENABLE(ecmult_static_precomputation,
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]), AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing [default=auto]]),
[use_ecmult_static_precomputation=$enableval], [use_ecmult_static_precomputation=$enableval],
[use_ecmult_static_precomputation=auto]) [use_ecmult_static_precomputation=auto])
@ -130,65 +130,100 @@ AC_ARG_ENABLE(module_ecdh,
[enable_module_ecdh=no]) [enable_module_ecdh=no])
AC_ARG_ENABLE(module_recovery, AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]), AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]),
[enable_module_recovery=$enableval], [enable_module_recovery=$enableval],
[enable_module_recovery=no]) [enable_module_recovery=no])
AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions (default is no)]),
[use_external_default_callbacks=$enableval],
[use_external_default_callbacks=no])
AC_ARG_ENABLE(jni, AC_ARG_ENABLE(jni,
AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is no)]), AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni [default=no]]),
[use_jni=$enableval], [use_jni=$enableval],
[use_jni=no]) [use_jni=no])
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) [finite field implementation to use [default=auto]])],[req_field=$withval], [req_field=auto])
AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto], AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto]) [bignum implementation to use [default=auto]])],[req_bignum=$withval], [req_bignum=auto])
AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto], AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto]) [scalar implementation to use [default=auto]])],[req_scalar=$withval], [req_scalar=auto])
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto] AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto],
[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto]) [assembly optimizations to use (experimental: arm) [default=auto]])],[req_asm=$withval], [req_asm=auto])
AC_ARG_WITH([ecmult-window], [AS_HELP_STRING([--with-ecmult-window=SIZE|auto],
[window size for ecmult precomputation for verification, specified as integer in range [2..24].]
[Larger values result in possibly better performance at the cost of an exponentially larger precomputed table.]
[The table will store 2^(SIZE-2) * 64 bytes of data but can be larger in memory due to platform-specific padding and alignment.]
[If the endomorphism optimization is enabled, two tables of this size are used instead of only one.]
["auto" is a reasonable setting for desktop machines (currently 15). [default=auto]]
)],
[req_ecmult_window=$withval], [req_ecmult_window=auto])
AC_CHECK_TYPES([__int128]) AC_CHECK_TYPES([__int128])
AC_MSG_CHECKING([for __builtin_expect])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
[ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ],
[ AC_MSG_RESULT([no])
])
if test x"$enable_coverage" = x"yes"; then if test x"$enable_coverage" = x"yes"; then
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code]) AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
CFLAGS="$CFLAGS -O0 --coverage" CFLAGS="$CFLAGS -O0 --coverage"
LDFLAGS="--coverage" LDFLAGS="$LDFLAGS --coverage"
else else
CFLAGS="$CFLAGS -O3" CFLAGS="$CFLAGS -O3"
fi fi
if test x"$use_ecmult_static_precomputation" != x"no"; then if test x"$use_ecmult_static_precomputation" != x"no"; then
# Temporarily switch to an environment for the native compiler
save_cross_compiling=$cross_compiling save_cross_compiling=$cross_compiling
cross_compiling=no cross_compiling=no
TEMP_CC="$CC" SAVE_CC="$CC"
CC="$CC_FOR_BUILD" CC="$CC_FOR_BUILD"
AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}]) SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS_FOR_BUILD"
SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS_FOR_BUILD"
SAVE_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS_FOR_BUILD"
warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $warn_CFLAGS_FOR_BUILD"
AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
[ AC_MSG_RESULT([yes]) ],
[ AC_MSG_RESULT([no])
CFLAGS="$saved_CFLAGS"
])
AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
AC_RUN_IFELSE( AC_RUN_IFELSE(
[AC_LANG_PROGRAM([], [return 0])], [AC_LANG_PROGRAM([], [])],
[working_native_cc=yes], [working_native_cc=yes],
[working_native_cc=no],[dnl]) [working_native_cc=no],[dnl])
CC="$TEMP_CC"
CFLAGS_FOR_BUILD="$CFLAGS"
# Restore the environment
cross_compiling=$save_cross_compiling cross_compiling=$save_cross_compiling
CC="$SAVE_CC"
CFLAGS="$SAVE_CFLAGS"
CPPFLAGS="$SAVE_CPPFLAGS"
LDFLAGS="$SAVE_LDFLAGS"
if test x"$working_native_cc" = x"no"; then if test x"$working_native_cc" = x"no"; then
AC_MSG_RESULT([no])
set_precomp=no set_precomp=no
m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
if test x"$use_ecmult_static_precomputation" = x"yes"; then if test x"$use_ecmult_static_precomputation" = x"yes"; then
AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
else else
AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) AC_MSG_WARN([Disabling statically generated ecmult table because the native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
fi fi
else else
AC_MSG_RESULT([ok]) AC_MSG_RESULT([yes])
set_precomp=yes set_precomp=yes
fi fi
else else
@ -366,6 +401,28 @@ case $set_scalar in
;; ;;
esac esac
#set ecmult window size
if test x"$req_ecmult_window" = x"auto"; then
set_ecmult_window=15
else
set_ecmult_window=$req_ecmult_window
fi
error_window_size=['window size for ecmult precomputation not an integer in range [2..24] or "auto"']
case $set_ecmult_window in
''|*[[!0-9]]*)
# no valid integer
AC_MSG_ERROR($error_window_size)
;;
*)
if test "$set_ecmult_window" -lt 2 -o "$set_ecmult_window" -gt 24 ; then
# not in range
AC_MSG_ERROR($error_window_size)
fi
AC_DEFINE_UNQUOTED(ECMULT_WINDOW_SIZE, $set_ecmult_window, [Set window size for ecmult precomputation])
;;
esac
if test x"$use_tests" = x"yes"; then if test x"$use_tests" = x"yes"; then
SECP_OPENSSL_CHECK SECP_OPENSSL_CHECK
if test x"$has_openssl_ec" = x"yes"; then if test x"$has_openssl_ec" = x"yes"; then
@ -441,17 +498,9 @@ if test x"$use_external_asm" = x"yes"; then
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
fi fi
AC_MSG_NOTICE([Using static precomputation: $set_precomp]) if test x"$use_external_default_callbacks" = x"yes"; then
AC_MSG_NOTICE([Using assembly optimizations: $set_asm]) AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used])
AC_MSG_NOTICE([Using field implementation: $set_field]) fi
AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
AC_MSG_NOTICE([Building benchmarks: $use_benchmark])
AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
AC_MSG_NOTICE([Using jni: $use_jni])
if test x"$enable_experimental" = x"yes"; then if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******]) AC_MSG_NOTICE([******])
@ -482,7 +531,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) 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_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) AM_CONDITIONAL([USE_JNI], [test x"$use_jni" = x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
@ -492,3 +541,26 @@ unset PKG_CONFIG_PATH
PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP"
AC_OUTPUT AC_OUTPUT
echo
echo "Build Options:"
echo " with endomorphism = $use_endomorphism"
echo " with ecmult precomp = $set_precomp"
echo " with external callbacks = $use_external_default_callbacks"
echo " with jni = $use_jni"
echo " with benchmarks = $use_benchmark"
echo " with coverage = $enable_coverage"
echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
echo
echo " asm = $set_asm"
echo " bignum = $set_bignum"
echo " field = $set_field"
echo " scalar = $set_scalar"
echo " ecmult window size = $set_ecmult_window"
echo
echo " CC = $CC"
echo " CFLAGS = $CFLAGS"
echo " CPPFLAGS = $CPPFLAGS"
echo " LDFLAGS = $LDFLAGS"
echo

View File

@ -33,9 +33,10 @@ extern "C" {
* verification). * verification).
* *
* A constructed context can safely be used from multiple threads * A constructed context can safely be used from multiple threads
* simultaneously, but API call that take a non-const pointer to a context * simultaneously, but API calls that take a non-const pointer to a context
* need exclusive access to it. In particular this is the case for * need exclusive access to it. In particular this is the case for
* secp256k1_context_destroy and secp256k1_context_randomize. * secp256k1_context_destroy, secp256k1_context_preallocated_destroy,
* and secp256k1_context_randomize.
* *
* Regarding randomization, either do it once at creation time (in which case * Regarding randomization, either do it once at creation time (in which case
* you do not need any locking for the other calls), or use a read-write lock. * you do not need any locking for the other calls), or use a read-write lock.
@ -163,7 +164,8 @@ typedef int (*secp256k1_nonce_function)(
#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) #define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) #define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
/** Flags to pass to secp256k1_context_create. */ /** Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and
* secp256k1_context_preallocated_create. */
#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) #define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) #define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) #define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
@ -186,7 +188,11 @@ typedef int (*secp256k1_nonce_function)(
*/ */
SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp; SECP256K1_API extern const secp256k1_context *secp256k1_context_no_precomp;
/** Create a secp256k1 context object. /** Create a secp256k1 context object (in dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most once for every call of this function. If you need to avoid dynamic
* memory allocation entirely, see the functions in secp256k1_preallocated.h.
* *
* Returns: a newly created context object. * Returns: a newly created context object.
* In: flags: which parts of the context to initialize. * In: flags: which parts of the context to initialize.
@ -197,7 +203,11 @@ SECP256K1_API secp256k1_context* secp256k1_context_create(
unsigned int flags unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT; ) SECP256K1_WARN_UNUSED_RESULT;
/** Copies a secp256k1 context object. /** Copy a secp256k1 context object (into dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most once for every call of this function. If you need to avoid dynamic
* memory allocation entirely, see the functions in secp256k1_preallocated.h.
* *
* Returns: a newly created context object. * Returns: a newly created context object.
* Args: ctx: an existing context to copy (cannot be NULL) * Args: ctx: an existing context to copy (cannot be NULL)
@ -206,10 +216,18 @@ SECP256K1_API secp256k1_context* secp256k1_context_clone(
const secp256k1_context* ctx const secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; ) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Destroy a secp256k1 context object. /** Destroy a secp256k1 context object (created in dynamically allocated memory).
* *
* The context pointer may not be used afterwards. * The context pointer may not be used afterwards.
* Args: ctx: an existing context to destroy (cannot be NULL) *
* The context to destroy must have been created using secp256k1_context_create
* or secp256k1_context_clone. If the context has instead been created using
* secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone, the
* behaviour is undefined. In that case, secp256k1_context_preallocated_destroy must
* be used instead.
*
* Args: ctx: an existing context to destroy, constructed using
* secp256k1_context_create or secp256k1_context_clone
*/ */
SECP256K1_API void secp256k1_context_destroy( SECP256K1_API void secp256k1_context_destroy(
secp256k1_context* ctx secp256k1_context* ctx
@ -229,11 +247,28 @@ SECP256K1_API void secp256k1_context_destroy(
* to cause a crash, though its return value and output arguments are * to cause a crash, though its return value and output arguments are
* undefined. * undefined.
* *
* When this function has not been called (or called with fn==NULL), then the
* default handler will be used. The library provides a default handler which
* writes the message to stderr and calls abort. This default handler can be
* replaced at link time if the preprocessor macro
* USE_EXTERNAL_DEFAULT_CALLBACKS is defined, which is the case if the build
* has been configured with --enable-external-default-callbacks. Then the
* following two symbols must be provided to link against:
* - void secp256k1_default_illegal_callback_fn(const char* message, void* data);
* - void secp256k1_default_error_callback_fn(const char* message, void* data);
* The library can call these default handlers even before a proper callback data
* pointer could have been set using secp256k1_context_set_illegal_callback or
* secp256k1_context_set_illegal_callback, e.g., when the creation of a context
* fails. In this case, the corresponding default handler will be called with
* the data pointer argument set to NULL.
*
* Args: ctx: an existing context object (cannot be NULL) * Args: ctx: an existing context object (cannot be NULL)
* In: fun: a pointer to a function to call when an illegal argument is * In: fun: a pointer to a function to call when an illegal argument is
* passed to the API, taking a message and an opaque pointer * passed to the API, taking a message and an opaque pointer.
* (NULL restores a default handler that calls abort). * (NULL restores the default handler.)
* data: the opaque pointer to pass to fun above. * data: the opaque pointer to pass to fun above.
*
* See also secp256k1_context_set_error_callback.
*/ */
SECP256K1_API void secp256k1_context_set_illegal_callback( SECP256K1_API void secp256k1_context_set_illegal_callback(
secp256k1_context* ctx, secp256k1_context* ctx,
@ -253,9 +288,12 @@ SECP256K1_API void secp256k1_context_set_illegal_callback(
* *
* Args: ctx: an existing context object (cannot be NULL) * Args: ctx: an existing context object (cannot be NULL)
* In: fun: a pointer to a function to call when an internal error occurs, * In: fun: a pointer to a function to call when an internal error occurs,
* taking a message and an opaque pointer (NULL restores a default * taking a message and an opaque pointer (NULL restores the
* handler that calls abort). * default handler, see secp256k1_context_set_illegal_callback
* for details).
* data: the opaque pointer to pass to fun above. * data: the opaque pointer to pass to fun above.
*
* See also secp256k1_context_set_illegal_callback.
*/ */
SECP256K1_API void secp256k1_context_set_error_callback( SECP256K1_API void secp256k1_context_set_error_callback(
secp256k1_context* ctx, secp256k1_context* ctx,
@ -267,21 +305,24 @@ SECP256K1_API void secp256k1_context_set_error_callback(
* *
* Returns: a newly created scratch space. * Returns: a newly created scratch space.
* Args: ctx: an existing context object (cannot be NULL) * Args: ctx: an existing context object (cannot be NULL)
* In: max_size: maximum amount of memory to allocate * In: size: amount of memory to be available as scratch space. Some extra
* (<100 bytes) will be allocated for extra accounting.
*/ */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
const secp256k1_context* ctx, const secp256k1_context* ctx,
size_t max_size size_t size
) SECP256K1_ARG_NONNULL(1); ) SECP256K1_ARG_NONNULL(1);
/** Destroy a secp256k1 scratch space. /** Destroy a secp256k1 scratch space.
* *
* The pointer may not be used afterwards. * The pointer may not be used afterwards.
* Args: scratch: space to destroy * Args: ctx: a secp256k1 context object.
* scratch: space to destroy
*/ */
SECP256K1_API void secp256k1_scratch_space_destroy( SECP256K1_API void secp256k1_scratch_space_destroy(
const secp256k1_context* ctx,
secp256k1_scratch_space* scratch secp256k1_scratch_space* scratch
); ) SECP256K1_ARG_NONNULL(1);
/** Parse a variable-length public key into the pubkey object. /** Parse a variable-length public key into the pubkey object.
* *
@ -615,7 +656,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/** Updates the context randomization to protect against side-channel leakage. /** Updates the context randomization to protect against side-channel leakage.
* Returns: 1: randomization successfully updated * Returns: 1: randomization successfully updated or nothing to randomize
* 0: error * 0: error
* Args: ctx: pointer to a context object (cannot be NULL) * Args: ctx: pointer to a context object (cannot be NULL)
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state) * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state)
@ -630,8 +671,14 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
* that it does not affect function results, but shields against attacks which * that it does not affect function results, but shields against attacks which
* rely on any input-dependent behaviour. * rely on any input-dependent behaviour.
* *
* This function has currently an effect only on contexts initialized for signing
* because randomization is currently used only for signing. However, this is not
* guaranteed and may change in the future. It is safe to call this function on
* contexts not initialized for signing; then it will have no effect and return 1.
*
* You should call this after secp256k1_context_create or * You should call this after secp256k1_context_create or
* secp256k1_context_clone, and may call this repeatedly afterwards. * secp256k1_context_clone (and secp256k1_context_preallocated_create or
* secp256k1_context_clone, resp.), and you may call this repeatedly afterwards.
*/ */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
secp256k1_context* ctx, secp256k1_context* ctx,

View File

@ -0,0 +1,128 @@
#ifndef SECP256K1_PREALLOCATED_H
#define SECP256K1_PREALLOCATED_H
#include "secp256k1.h"
#ifdef __cplusplus
extern "C" {
#endif
/* The module provided by this header file is intended for settings in which it
* is not possible or desirable to rely on dynamic memory allocation. It provides
* functions for creating, cloning, and destroying secp256k1 context objects in a
* contiguous fixed-size block of memory provided by the caller.
*
* Context objects created by functions in this module can be used like contexts
* objects created by functions in secp256k1.h, i.e., they can be passed to any
* API function that excepts a context object (see secp256k1.h for details). The
* only exception is that context objects created by functions in this module
* must be destroyed using secp256k1_context_preallocated_destroy (in this
* module) instead of secp256k1_context_destroy (in secp256k1.h).
*
* It is guaranteed that functions in by this module will not call malloc or its
* friends realloc, calloc, and free.
*/
/** Determine the memory size of a secp256k1 context object to be created in
* caller-provided memory.
*
* The purpose of this function is to determine how much memory must be provided
* to secp256k1_context_preallocated_create.
*
* Returns: the required size of the caller-provided memory block
* In: flags: which parts of the context to initialize.
*/
SECP256K1_API size_t secp256k1_context_preallocated_size(
unsigned int flags
) SECP256K1_WARN_UNUSED_RESULT;
/** Create a secp256k1 context object in caller-provided memory.
*
* The caller must provide a pointer to a rewritable contiguous block of memory
* of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
* aligned to hold an object of any type.
*
* The block of memory is exclusively owned by the created context object during
* the lifetime of this context object, which begins with the call to this
* function and ends when a call to secp256k1_context_preallocated_destroy
* (which destroys the context object again) returns. During the lifetime of the
* context object, the caller is obligated not to access this block of memory,
* i.e., the caller may not read or write the memory, e.g., by copying the memory
* contents to a different location or trying to create a second context object
* in the memory. In simpler words, the prealloc pointer (or any pointer derived
* from it) should not be used during the lifetime of the context object.
*
* Returns: a newly created context object.
* In: prealloc: a pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above (cannot be NULL)
* flags: which parts of the context to initialize.
*
* See also secp256k1_context_randomize (in secp256k1.h)
* and secp256k1_context_preallocated_destroy.
*/
SECP256K1_API secp256k1_context* secp256k1_context_preallocated_create(
void* prealloc,
unsigned int flags
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Determine the memory size of a secp256k1 context object to be copied into
* caller-provided memory.
*
* Returns: the required size of the caller-provided memory block.
* In: ctx: an existing context to copy (cannot be NULL)
*/
SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
const secp256k1_context* ctx
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;
/** Copy a secp256k1 context object into caller-provided memory.
*
* The caller must provide a pointer to a rewritable contiguous block of memory
* of size at least secp256k1_context_preallocated_size(flags) bytes, suitably
* aligned to hold an object of any type.
*
* The block of memory is exclusively owned by the created context object during
* the lifetime of this context object, see the description of
* secp256k1_context_preallocated_create for details.
*
* Returns: a newly created context object.
* Args: ctx: an existing context to copy (cannot be NULL)
* In: prealloc: a pointer to a rewritable contiguous block of memory of
* size at least secp256k1_context_preallocated_size(flags)
* bytes, as detailed above (cannot be NULL)
*/
SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone(
const secp256k1_context* ctx,
void* prealloc
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_WARN_UNUSED_RESULT;
/** Destroy a secp256k1 context object that has been created in
* caller-provided memory.
*
* The context pointer may not be used afterwards.
*
* The context to destroy must have been created using
* secp256k1_context_preallocated_create or secp256k1_context_preallocated_clone.
* If the context has instead been created using secp256k1_context_create or
* secp256k1_context_clone, the behaviour is undefined. In that case,
* secp256k1_context_destroy must be used instead.
*
* If required, it is the responsibility of the caller to deallocate the block
* of memory properly after this function returns, e.g., by calling free on the
* preallocated pointer given to secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone.
*
* Args: ctx: an existing context to destroy, constructed using
* secp256k1_context_preallocated_create or
* secp256k1_context_preallocated_clone (cannot be NULL)
*/
SECP256K1_API void secp256k1_context_preallocated_destroy(
secp256k1_context* ctx
);
#ifdef __cplusplus
}
#endif
#endif /* SECP256K1_PREALLOCATED_H */

View File

@ -8,6 +8,6 @@ Description: Optimized C library for EC operations on curve secp256k1
URL: https://github.com/bitcoin-core/secp256k1 URL: https://github.com/bitcoin-core/secp256k1
Version: @PACKAGE_VERSION@ Version: @PACKAGE_VERSION@
Cflags: -I${includedir} Cflags: -I${includedir}
Libs.private: @SECP_LIBS@
Libs: -L${libdir} -lsecp256k1 Libs: -L${libdir} -lsecp256k1
Libs.private: @SECP_LIBS@

View File

@ -16,15 +16,9 @@ Note:
*/ */
.syntax unified .syntax unified
.arch armv7-a
@ eabi attributes - see readelf -A @ eabi attributes - see readelf -A
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes
.eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no
.eabi_attribute 10, 0 @ Tag_FP_arch = none
.eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
.eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed
.eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
.text .text
@ Field constants @ Field constants

View File

@ -10,7 +10,10 @@
#ifdef USE_BASIC_CONFIG #ifdef USE_BASIC_CONFIG
#undef USE_ASM_X86_64 #undef USE_ASM_X86_64
#undef USE_ECMULT_STATIC_PRECOMPUTATION
#undef USE_ENDOMORPHISM #undef USE_ENDOMORPHISM
#undef USE_EXTERNAL_ASM
#undef USE_EXTERNAL_DEFAULT_CALLBACKS
#undef USE_FIELD_10X26 #undef USE_FIELD_10X26
#undef USE_FIELD_5X52 #undef USE_FIELD_5X52
#undef USE_FIELD_INV_BUILTIN #undef USE_FIELD_INV_BUILTIN
@ -27,6 +30,7 @@
#define USE_SCALAR_INV_BUILTIN 1 #define USE_SCALAR_INV_BUILTIN 1
#define USE_FIELD_10X26 1 #define USE_FIELD_10X26 1
#define USE_SCALAR_8X32 1 #define USE_SCALAR_8X32 1
#define ECMULT_WINDOW_SIZE 15
#endif /* USE_BASIC_CONFIG */ #endif /* USE_BASIC_CONFIG */

View File

@ -64,7 +64,7 @@ static void bench_ecmult(void* arg) {
size_t iter; size_t iter;
for (iter = 0; iter < iters; ++iter) { for (iter = 0; iter < iters; ++iter) {
data->ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g); data->ecmult_multi(&data->ctx->error_callback, &data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
data->offset1 = (data->offset1 + count) % POINTS; data->offset1 = (data->offset1 + count) % POINTS;
data->offset2 = (data->offset2 + count - 1) % POINTS; data->offset2 = (data->offset2 + count - 1) % POINTS;
} }
@ -139,6 +139,11 @@ int main(int argc, char **argv) {
secp256k1_gej* pubkeys_gej; secp256k1_gej* pubkeys_gej;
size_t scratch_size; size_t scratch_size;
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
data.ecmult_multi = secp256k1_ecmult_multi_var;
if (argc > 1) { if (argc > 1) {
if(have_flag(argc, argv, "pippenger_wnaf")) { if(have_flag(argc, argv, "pippenger_wnaf")) {
printf("Using pippenger_wnaf:\n"); printf("Using pippenger_wnaf:\n");
@ -146,15 +151,19 @@ int main(int argc, char **argv) {
} else if(have_flag(argc, argv, "strauss_wnaf")) { } else if(have_flag(argc, argv, "strauss_wnaf")) {
printf("Using strauss_wnaf:\n"); printf("Using strauss_wnaf:\n");
data.ecmult_multi = secp256k1_ecmult_strauss_batch_single; data.ecmult_multi = secp256k1_ecmult_strauss_batch_single;
} else if(have_flag(argc, argv, "simple")) {
printf("Using simple algorithm:\n");
data.ecmult_multi = secp256k1_ecmult_multi_var;
secp256k1_scratch_space_destroy(data.ctx, data.scratch);
data.scratch = NULL;
} else {
fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]);
fprintf(stderr, "Use 'pippenger_wnaf', 'strauss_wnaf', 'simple' or no argument to benchmark a combined algorithm.\n");
return 1;
} }
} else {
data.ecmult_multi = secp256k1_ecmult_multi_var;
} }
/* Allocate stuff */ /* Allocate stuff */
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS); data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS); data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS); data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);
@ -172,7 +181,7 @@ int main(int argc, char **argv) {
secp256k1_scalar_add(&data.seckeys[i], &data.seckeys[i - 1], &data.seckeys[i - 1]); secp256k1_scalar_add(&data.seckeys[i], &data.seckeys[i - 1], &data.seckeys[i - 1]);
} }
} }
secp256k1_ge_set_all_gej_var(data.pubkeys, pubkeys_gej, POINTS, &data.ctx->error_callback); secp256k1_ge_set_all_gej_var(data.pubkeys, pubkeys_gej, POINTS);
free(pubkeys_gej); free(pubkeys_gej);
for (i = 1; i <= 8; ++i) { for (i = 1; i <= 8; ++i) {
@ -184,8 +193,10 @@ int main(int argc, char **argv) {
run_test(&data, i << p, 1); run_test(&data, i << p, 1);
} }
} }
if (data.scratch != NULL) {
secp256k1_scratch_space_destroy(data.ctx, data.scratch);
}
secp256k1_context_destroy(data.ctx); secp256k1_context_destroy(data.ctx);
secp256k1_scratch_space_destroy(data.scratch);
free(data.scalars); free(data.scalars);
free(data.pubkeys); free(data.pubkeys);
free(data.seckeys); free(data.seckeys);

View File

@ -184,9 +184,11 @@ void bench_field_inverse_var(void* arg) {
void bench_field_sqrt(void* arg) { void bench_field_sqrt(void* arg) {
int i; int i;
bench_inv *data = (bench_inv*)arg; bench_inv *data = (bench_inv*)arg;
secp256k1_fe t;
for (i = 0; i < 20000; i++) { for (i = 0; i < 20000; i++) {
secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); t = data->fe_x;
secp256k1_fe_sqrt(&data->fe_x, &t);
secp256k1_fe_add(&data->fe_x, &data->fe_y); secp256k1_fe_add(&data->fe_x, &data->fe_y);
} }
} }
@ -251,7 +253,7 @@ void bench_wnaf_const(void* arg) {
bench_inv *data = (bench_inv*)arg; bench_inv *data = (bench_inv*)arg;
for (i = 0; i < 20000; i++) { for (i = 0; i < 20000; i++) {
secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A, 256); secp256k1_wnaf_const(data->wnaf, &data->scalar_x, WINDOW_A, 256);
secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y);
} }
} }

View File

@ -18,7 +18,7 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char
if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) { if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) {
secp256k1_fe x; secp256k1_fe x;
return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD);
} else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { } else if (size == 65 && (pub[0] == SECP256K1_TAG_PUBKEY_UNCOMPRESSED || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) {
secp256k1_fe x, y; secp256k1_fe x, y;
if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) {
return 0; return 0;

View File

@ -20,10 +20,10 @@ typedef struct {
#endif #endif
} secp256k1_ecmult_context; } secp256k1_ecmult_context;
static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx);
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb); static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc);
static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src);
const secp256k1_ecmult_context *src, const secp256k1_callback *cb);
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx);
static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx);
@ -37,11 +37,12 @@ typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge
* Chooses the right algorithm for a given number of points and scratch space * Chooses the right algorithm for a given number of points and scratch space
* size. Resets and overwrites the given scratch space. If the points do not * size. Resets and overwrites the given scratch space. If the points do not
* fit in the scratch space the algorithm is repeatedly run with batches of * fit in the scratch space the algorithm is repeatedly run with batches of
* points. * points. If no scratch space is given then a simple algorithm is used that
* simply multiplies the points with the corresponding scalars and adds them up.
* Returns: 1 on success (including when inp_g_sc is NULL and n is 0) * Returns: 1 on success (including when inp_g_sc is NULL and n is 0)
* 0 if there is not enough scratch space for a single point or * 0 if there is not enough scratch space for a single point or
* callback returns 0 * callback returns 0
*/ */
static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
#endif /* SECP256K1_ECMULT_H */ #endif /* SECP256K1_ECMULT_H */

View File

@ -48,7 +48,7 @@
* *
* Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335
*/ */
static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) { static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w, int size) {
int global_sign; int global_sign;
int skew = 0; int skew = 0;
int word = 0; int word = 0;
@ -59,8 +59,12 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
int flip; int flip;
int bit; int bit;
secp256k1_scalar neg_s; secp256k1_scalar s;
int not_neg_one; int not_neg_one;
VERIFY_CHECK(w > 0);
VERIFY_CHECK(size > 0);
/* Note that we cannot handle even numbers by negating them to be odd, as is /* Note that we cannot handle even numbers by negating them to be odd, as is
* done in other implementations, since if our scalars were specified to have * done in other implementations, since if our scalars were specified to have
* width < 256 for performance reasons, their negations would have width 256 * width < 256 for performance reasons, their negations would have width 256
@ -75,12 +79,13 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
* {1, 2} we want to add to the scalar when ensuring that it's odd. Further * {1, 2} we want to add to the scalar when ensuring that it's odd. Further
* complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and
* we need to special-case it in this logic. */ * we need to special-case it in this logic. */
flip = secp256k1_scalar_is_high(&s); flip = secp256k1_scalar_is_high(scalar);
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
bit = flip ^ !secp256k1_scalar_is_even(&s); bit = flip ^ !secp256k1_scalar_is_even(scalar);
/* We check for negative one, since adding 2 to it will cause an overflow */ /* We check for negative one, since adding 2 to it will cause an overflow */
secp256k1_scalar_negate(&neg_s, &s); secp256k1_scalar_negate(&s, scalar);
not_neg_one = !secp256k1_scalar_is_one(&neg_s); not_neg_one = !secp256k1_scalar_is_one(&s);
s = *scalar;
secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); secp256k1_scalar_cadd_bit(&s, bit, not_neg_one);
/* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects
* that we added two to it and flipped it. In fact for -1 these operations are * that we added two to it and flipped it. In fact for -1 these operations are
@ -93,7 +98,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
/* 4 */ /* 4 */
u_last = secp256k1_scalar_shr_int(&s, w); u_last = secp256k1_scalar_shr_int(&s, w);
while (word * w < size) { do {
int sign; int sign;
int even; int even;
@ -109,7 +114,7 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size)
wnaf[word++] = u_last * global_sign; wnaf[word++] = u_last * global_sign;
u_last = u; u_last = u;
} } while (word * w < size);
wnaf[word] = u * global_sign; wnaf[word] = u * global_sign;
VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); VERIFY_CHECK(secp256k1_scalar_is_zero(&s));
@ -132,7 +137,6 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
int i; int i;
secp256k1_scalar sc = *scalar;
/* build wnaf representation for q. */ /* build wnaf representation for q. */
int rsize = size; int rsize = size;
@ -140,13 +144,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
if (size > 128) { if (size > 128) {
rsize = 128; rsize = 128;
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); secp256k1_scalar_split_lambda(&q_1, &q_lam, scalar);
skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128); skew_1 = secp256k1_wnaf_const(wnaf_1, &q_1, WINDOW_A - 1, 128);
skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128); skew_lam = secp256k1_wnaf_const(wnaf_lam, &q_lam, WINDOW_A - 1, 128);
} else } else
#endif #endif
{ {
skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size); skew_1 = secp256k1_wnaf_const(wnaf_1, scalar, WINDOW_A - 1, size);
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
skew_lam = 0; skew_lam = 0;
#endif #endif

View File

@ -28,10 +28,10 @@ typedef struct {
secp256k1_gej initial; secp256k1_gej initial;
} secp256k1_ecmult_gen_context; } secp256k1_ecmult_gen_context;
static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx);
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb); static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, void **prealloc);
static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context* src);
const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb);
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx); static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx);
static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx); static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx);

View File

@ -7,6 +7,7 @@
#ifndef SECP256K1_ECMULT_GEN_IMPL_H #ifndef SECP256K1_ECMULT_GEN_IMPL_H
#define SECP256K1_ECMULT_GEN_IMPL_H #define SECP256K1_ECMULT_GEN_IMPL_H
#include "util.h"
#include "scalar.h" #include "scalar.h"
#include "group.h" #include "group.h"
#include "ecmult_gen.h" #include "ecmult_gen.h"
@ -14,23 +15,32 @@
#ifdef USE_ECMULT_STATIC_PRECOMPUTATION #ifdef USE_ECMULT_STATIC_PRECOMPUTATION
#include "ecmult_static_context.h" #include "ecmult_static_context.h"
#endif #endif
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = ROUND_TO_ALIGN(sizeof(*((secp256k1_ecmult_gen_context*) NULL)->prec));
#else
static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE = 0;
#endif
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) { static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) {
ctx->prec = NULL; ctx->prec = NULL;
} }
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) { static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, void **prealloc) {
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION #ifndef USE_ECMULT_STATIC_PRECOMPUTATION
secp256k1_ge prec[1024]; secp256k1_ge prec[1024];
secp256k1_gej gj; secp256k1_gej gj;
secp256k1_gej nums_gej; secp256k1_gej nums_gej;
int i, j; int i, j;
size_t const prealloc_size = SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
void* const base = *prealloc;
#endif #endif
if (ctx->prec != NULL) { if (ctx->prec != NULL) {
return; return;
} }
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION #ifndef USE_ECMULT_STATIC_PRECOMPUTATION
ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec)); ctx->prec = (secp256k1_ge_storage (*)[64][16])manual_alloc(prealloc, prealloc_size, base, prealloc_size);
/* get the generator */ /* get the generator */
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
@ -77,7 +87,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
} }
} }
secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb); secp256k1_ge_set_all_gej_var(prec, precj, 1024);
} }
for (j = 0; j < 64; j++) { for (j = 0; j < 64; j++) {
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
@ -85,7 +95,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
} }
} }
#else #else
(void)cb; (void)prealloc;
ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context;
#endif #endif
secp256k1_ecmult_gen_blind(ctx, NULL); secp256k1_ecmult_gen_blind(ctx, NULL);
@ -95,27 +105,18 @@ static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_cont
return ctx->prec != NULL; return ctx->prec != NULL;
} }
static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context *src) {
const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) {
if (src->prec == NULL) {
dst->prec = NULL;
} else {
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION #ifndef USE_ECMULT_STATIC_PRECOMPUTATION
dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); if (src->prec != NULL) {
memcpy(dst->prec, src->prec, sizeof(*dst->prec)); /* We cast to void* first to suppress a -Wcast-align warning. */
#else dst->prec = (secp256k1_ge_storage (*)[64][16])(void*)((unsigned char*)dst + ((unsigned char*)src->prec - (unsigned char*)src));
(void)cb;
dst->prec = src->prec;
#endif
dst->initial = src->initial;
dst->blind = src->blind;
} }
#else
(void)dst, (void)src;
#endif
} }
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
#ifndef USE_ECMULT_STATIC_PRECOMPUTATION
free(ctx->prec);
#endif
secp256k1_scalar_clear(&ctx->blind); secp256k1_scalar_clear(&ctx->blind);
secp256k1_gej_clear(&ctx->initial); secp256k1_gej_clear(&ctx->initial);
ctx->prec = NULL; ctx->prec = NULL;

View File

@ -10,6 +10,7 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include "util.h"
#include "group.h" #include "group.h"
#include "scalar.h" #include "scalar.h"
#include "ecmult.h" #include "ecmult.h"
@ -30,16 +31,32 @@
# endif # endif
#else #else
/* optimal for 128-bit and 256-bit exponents. */ /* optimal for 128-bit and 256-bit exponents. */
#define WINDOW_A 5 # define WINDOW_A 5
/** larger numbers may result in slightly better performance, at the cost of /** Larger values for ECMULT_WINDOW_SIZE result in possibly better
exponentially larger precomputed tables. */ * performance at the cost of an exponentially larger precomputed
#ifdef USE_ENDOMORPHISM * table. The exact table size is
/** Two tables for window size 15: 1.375 MiB. */ * (1 << (WINDOW_G - 2)) * sizeof(secp256k1_ge_storage) bytes,
#define WINDOW_G 15 * where sizeof(secp256k1_ge_storage) is typically 64 bytes but can
#else * be larger due to platform-specific padding and alignment.
/** One table for window size 16: 1.375 MiB. */ * If the endomorphism optimization is enabled (USE_ENDOMORMPHSIM)
#define WINDOW_G 16 * two tables of this size are used instead of only one.
*/
# define WINDOW_G ECMULT_WINDOW_SIZE
#endif #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
* not tested.
*
* The following limitations are known, and there are probably more:
* If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect
* because the size of the memory object that we allocate (in bytes)
* will not fit in a size_t.
* If WINDOW_G > 31 and int has 32 bits, then the code is incorrect
* because certain expressions will overflow.
*/
#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24
# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
#endif #endif
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
@ -137,24 +154,135 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p
secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
} }
static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) { static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp256k1_ge_storage *pre, const secp256k1_gej *a) {
secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n); secp256k1_gej d;
secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n); secp256k1_ge d_ge, p_ge;
secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n); secp256k1_gej pj;
secp256k1_fe zi;
secp256k1_fe zr;
secp256k1_fe dx_over_dz_squared;
int i; int i;
/* Compute the odd multiples in Jacobian form. */ VERIFY_CHECK(!a->infinity);
secp256k1_ecmult_odd_multiples_table(n, prej, zr, a);
/* Convert them in batch to affine coordinates. */ secp256k1_gej_double_var(&d, a, NULL);
secp256k1_ge_set_table_gej_var(prea, prej, zr, n);
/* Convert them to compact storage form. */ /* First, we perform all the additions in an isomorphic curve obtained by multiplying
for (i = 0; i < n; i++) { * all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use
secp256k1_ge_to_storage(&pre[i], &prea[i]); * `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store
* the resulting y-coordinate and the z-ratio, since we only have enough memory to
* store two field elements. These are sufficient to efficiently undo the isomorphism
* and recompute all the `x`s.
*/
d_ge.x = d.x;
d_ge.y = d.y;
d_ge.infinity = 0;
secp256k1_ge_set_gej_zinv(&p_ge, a, &d.z);
pj.x = p_ge.x;
pj.y = p_ge.y;
pj.z = a->z;
pj.infinity = 0;
for (i = 0; i < (n - 1); i++) {
secp256k1_fe_normalize_var(&pj.y);
secp256k1_fe_to_storage(&pre[i].y, &pj.y);
secp256k1_gej_add_ge_var(&pj, &pj, &d_ge, &zr);
secp256k1_fe_normalize_var(&zr);
secp256k1_fe_to_storage(&pre[i].x, &zr);
} }
free(prea); /* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */
free(prej); secp256k1_fe_mul(&zi, &pj.z, &d.z);
free(zr); secp256k1_fe_inv_var(&zi, &zi);
/* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so
* that we can combine it with the saved z-ratios to compute the other zs
* without any more inversions. */
secp256k1_ge_set_gej_zinv(&p_ge, &pj, &zi);
secp256k1_ge_to_storage(&pre[n - 1], &p_ge);
/* Compute the actual x-coordinate of D, which will be needed below. */
secp256k1_fe_mul(&d.z, &zi, &pj.z); /* d.z = 1/d.z */
secp256k1_fe_sqr(&dx_over_dz_squared, &d.z);
secp256k1_fe_mul(&dx_over_dz_squared, &dx_over_dz_squared, &d.x);
/* Going into the second loop, we have set `pre[n-1]` to its final affine
* form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We
* have `zi = (p.z * d.z)^-1`, where
*
* `p.z` is the z-coordinate of the point on the isomorphic curve
* which was ultimately assigned to `pre[n-1]`.
* `d.z` is the multiplier that must be applied to all z-coordinates
* to move from our isomorphic curve back to secp256k1; so the
* product `p.z * d.z` is the z-coordinate of the secp256k1
* point assigned to `pre[n-1]`.
*
* All subsequent inverse-z-coordinates can be obtained by multiplying this
* factor by successive z-ratios, which is much more efficient than directly
* computing each one.
*
* Importantly, these inverse-zs will be coordinates of points on secp256k1,
* while our other stored values come from computations on the isomorphic
* curve. So in the below loop, we will take care not to actually use `zi`
* or any derived values until we're back on secp256k1.
*/
i = n - 1;
while (i > 0) {
secp256k1_fe zi2, zi3;
const secp256k1_fe *rzr;
i--;
secp256k1_ge_from_storage(&p_ge, &pre[i]);
/* For each remaining point, we extract the z-ratio from the stored
* x-coordinate, compute its z^-1 from that, and compute the full
* point from that. */
rzr = &p_ge.x;
secp256k1_fe_mul(&zi, &zi, rzr);
secp256k1_fe_sqr(&zi2, &zi);
secp256k1_fe_mul(&zi3, &zi2, &zi);
/* To compute the actual x-coordinate, we use the stored z ratio and
* y-coordinate, which we obtained from `secp256k1_gej_add_ge_var`
* in the loop above, as well as the inverse of the square of its
* z-coordinate. We store the latter in the `zi2` variable, which is
* computed iteratively starting from the overall Z inverse then
* multiplying by each z-ratio in turn.
*
* Denoting the z-ratio as `rzr`, we observe that it is equal to `h`
* from the inside of the above `gej_add_ge_var` call. This satisfies
*
* rzr = d_x * z^2 - x * d_z^2
*
* where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)`
* are Jacobian coordinates of our desired point -- except both are on
* the isomorphic curve that we were using when we called `gej_add_ge_var`.
* To get back to secp256k1, we must multiply both `z`s by `d_z`, or
* equivalently divide both `x`s by `d_z^2`. Our equation then becomes
*
* rzr = d_x * z^2 / d_z^2 - x
*
* (The left-hand-side, being a ratio of z-coordinates, is unaffected
* by the isomorphism.)
*
* Rearranging to solve for `x`, we have
*
* x = d_x * z^2 / d_z^2 - rzr
*
* But what we actually want is the affine coordinate `X = x/z^2`,
* which will satisfy
*
* X = d_x / d_z^2 - rzr / z^2
* = dx_over_dz_squared - rzr * zi2
*/
secp256k1_fe_mul(&p_ge.x, rzr, &zi2);
secp256k1_fe_negate(&p_ge.x, &p_ge.x, 1);
secp256k1_fe_add(&p_ge.x, &dx_over_dz_squared);
/* y is stored_y/z^3, as we expect */
secp256k1_fe_mul(&p_ge.y, &p_ge.y, &zi3);
/* Store */
secp256k1_ge_to_storage(&pre[i], &p_ge);
}
} }
/** The following two macro retrieves a particular odd multiple from a table /** The following two macro retrieves a particular odd multiple from a table
@ -166,7 +294,8 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
if ((n) > 0) { \ if ((n) > 0) { \
*(r) = (pre)[((n)-1)/2]; \ *(r) = (pre)[((n)-1)/2]; \
} else { \ } else { \
secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \ *(r) = (pre)[(-(n)-1)/2]; \
secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
} \ } \
} while(0) } while(0)
@ -178,10 +307,17 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \
} else { \ } else { \
secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \
secp256k1_ge_neg((r), (r)); \ secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
} \ } \
} while(0) } while(0)
static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE =
ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
#ifdef USE_ENDOMORPHISM
+ ROUND_TO_ALIGN(sizeof((*((secp256k1_ecmult_context*) NULL)->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G))
#endif
;
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
ctx->pre_g = NULL; ctx->pre_g = NULL;
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
@ -189,8 +325,10 @@ static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) {
#endif #endif
} }
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc) {
secp256k1_gej gj; secp256k1_gej gj;
void* const base = *prealloc;
size_t const prealloc_size = SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
if (ctx->pre_g != NULL) { if (ctx->pre_g != NULL) {
return; return;
@ -199,44 +337,44 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
/* get the generator */ /* get the generator */
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); {
size_t size = sizeof((*ctx->pre_g)[0]) * ((size_t)ECMULT_TABLE_SIZE(WINDOW_G));
/* check for overflow */
VERIFY_CHECK(size / sizeof((*ctx->pre_g)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
ctx->pre_g = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
}
/* precompute the tables with odd multiples */ /* precompute the tables with odd multiples */
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb); secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj);
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
{ {
secp256k1_gej g_128j; secp256k1_gej g_128j;
int i; int i;
ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); size_t size = sizeof((*ctx->pre_g_128)[0]) * ((size_t) ECMULT_TABLE_SIZE(WINDOW_G));
/* check for overflow */
VERIFY_CHECK(size / sizeof((*ctx->pre_g_128)[0]) == ((size_t)ECMULT_TABLE_SIZE(WINDOW_G)));
ctx->pre_g_128 = (secp256k1_ge_storage (*)[])manual_alloc(prealloc, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G), base, prealloc_size);
/* calculate 2^128*generator */ /* calculate 2^128*generator */
g_128j = gj; g_128j = gj;
for (i = 0; i < 128; i++) { for (i = 0; i < 128; i++) {
secp256k1_gej_double_var(&g_128j, &g_128j, NULL); secp256k1_gej_double_var(&g_128j, &g_128j, NULL);
} }
secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb); secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j);
} }
#endif #endif
} }
static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src) {
const secp256k1_ecmult_context *src, const secp256k1_callback *cb) { if (src->pre_g != NULL) {
if (src->pre_g == NULL) { /* We cast to void* first to suppress a -Wcast-align warning. */
dst->pre_g = NULL; dst->pre_g = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g) - (unsigned char*)src));
} else {
size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
memcpy(dst->pre_g, src->pre_g, size);
} }
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
if (src->pre_g_128 == NULL) { if (src->pre_g_128 != NULL) {
dst->pre_g_128 = NULL; dst->pre_g_128 = (secp256k1_ge_storage (*)[])(void*)((unsigned char*)dst + ((unsigned char*)(src->pre_g_128) - (unsigned char*)src));
} else {
size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G);
dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size);
memcpy(dst->pre_g_128, src->pre_g_128, size);
} }
#endif #endif
} }
@ -246,10 +384,6 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx
} }
static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) {
free(ctx->pre_g);
#ifdef USE_ENDOMORPHISM
free(ctx->pre_g_128);
#endif
secp256k1_ecmult_context_init(ctx); secp256k1_ecmult_context_init(ctx);
} }
@ -306,7 +440,7 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
CHECK(carry == 0); CHECK(carry == 0);
while (bit < 256) { while (bit < 256) {
CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0);
} }
#endif #endif
return last_set_bit + 1; return last_set_bit + 1;
} }
@ -514,52 +648,55 @@ static size_t secp256k1_strauss_scratch_size(size_t n_points) {
return n_points*point_size; return n_points*point_size;
} }
static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
secp256k1_gej* points; secp256k1_gej* points;
secp256k1_scalar* scalars; secp256k1_scalar* scalars;
struct secp256k1_strauss_state state; struct secp256k1_strauss_state state;
size_t i; size_t i;
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
secp256k1_gej_set_infinity(r); secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n_points == 0) { if (inp_g_sc == NULL && n_points == 0) {
return 1; return 1;
} }
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) { points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
return 0; scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
} state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej)); state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A); state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
#else #else
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
#endif #endif
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
for (i = 0; i < n_points; i++) { for (i = 0; i < n_points; i++) {
secp256k1_ge point; secp256k1_ge point;
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) { if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
secp256k1_scratch_deallocate_frame(scratch); secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0; return 0;
} }
secp256k1_gej_set_ge(&points[i], &point); secp256k1_gej_set_ge(&points[i], &point);
} }
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc); secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
secp256k1_scratch_deallocate_frame(scratch); secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1; return 1;
} }
/* Wrapper for secp256k1_ecmult_multi_func interface */ /* Wrapper for secp256k1_ecmult_multi_func interface */
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0); return secp256k1_ecmult_strauss_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
} }
static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) { static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
} }
/** Convert a number to WNAF notation. /** Convert a number to WNAF notation.
@ -848,10 +985,11 @@ static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_windo
size_t entries = n_points + 1; size_t entries = n_points + 1;
#endif #endif
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int); size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
return ((1<<bucket_window) * sizeof(secp256k1_gej) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size); return (sizeof(secp256k1_gej) << bucket_window) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size;
} }
static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
/* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch /* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
* sizes. The reason for +1 is that we add the G scalar to the list of * sizes. The reason for +1 is that we add the G scalar to the list of
* other scalars. */ * other scalars. */
@ -876,15 +1014,21 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
} }
bucket_window = secp256k1_pippenger_bucket_window(n_points); bucket_window = secp256k1_pippenger_bucket_window(n_points);
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) { points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points));
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars));
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space));
if (points == NULL || scalars == NULL || state_space == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0;
}
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps));
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<<bucket_window) * sizeof(*buckets));
if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0; return 0;
} }
points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps));
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, (1<<bucket_window) * sizeof(*buckets));
if (inp_g_sc != NULL) { if (inp_g_sc != NULL) {
scalars[0] = *inp_g_sc; scalars[0] = *inp_g_sc;
@ -898,7 +1042,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
while (point_idx < n_points) { while (point_idx < n_points) {
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) { if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
secp256k1_scratch_deallocate_frame(scratch); secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 0; return 0;
} }
idx++; idx++;
@ -922,13 +1066,13 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
for(i = 0; i < 1<<bucket_window; i++) { for(i = 0; i < 1<<bucket_window; i++) {
secp256k1_gej_clear(&buckets[i]); secp256k1_gej_clear(&buckets[i]);
} }
secp256k1_scratch_deallocate_frame(scratch); secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
return 1; return 1;
} }
/* Wrapper for secp256k1_ecmult_multi_func interface */ /* Wrapper for secp256k1_ecmult_multi_func interface */
static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_pippenger_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0); return secp256k1_ecmult_pippenger_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
} }
/** /**
@ -936,8 +1080,8 @@ static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_contex
* a given scratch space. The function ensures that fewer points may also be * a given scratch space. The function ensures that fewer points may also be
* used. * used.
*/ */
static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) { static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
size_t max_alloc = secp256k1_scratch_max_allocation(scratch, PIPPENGER_SCRATCH_OBJECTS); size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS);
int bucket_window; int bucket_window;
size_t res = 0; size_t res = 0;
@ -951,7 +1095,7 @@ static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
#ifdef USE_ENDOMORPHISM #ifdef USE_ENDOMORPHISM
entry_size = 2*entry_size; entry_size = 2*entry_size;
#endif #endif
space_overhead = ((1<<bucket_window) * sizeof(secp256k1_gej) + entry_size + sizeof(struct secp256k1_pippenger_state)); space_overhead = (sizeof(secp256k1_gej) << bucket_window) + entry_size + sizeof(struct secp256k1_pippenger_state);
if (space_overhead > max_alloc) { if (space_overhead > max_alloc) {
break; break;
} }
@ -972,12 +1116,58 @@ static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) {
return res; return res;
} }
typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t); /* Computes ecmult_multi by simply multiplying and adding each point. Does not
static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { * require a scratch space */
static int secp256k1_ecmult_multi_simple_var(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) {
size_t point_idx;
secp256k1_scalar szero;
secp256k1_gej tmpj;
secp256k1_scalar_set_int(&szero, 0);
secp256k1_gej_set_infinity(r);
secp256k1_gej_set_infinity(&tmpj);
/* r = inp_g_sc*G */
secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc);
for (point_idx = 0; point_idx < n_points; point_idx++) {
secp256k1_ge point;
secp256k1_gej pointj;
secp256k1_scalar scalar;
if (!cb(&scalar, &point, point_idx, cbdata)) {
return 0;
}
/* r += scalar*point */
secp256k1_gej_set_ge(&pointj, &point);
secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL);
secp256k1_gej_add_var(r, r, &tmpj, NULL);
}
return 1;
}
/* Compute the number of batches and the batch size given the maximum batch size and the
* total number of points */
static int secp256k1_ecmult_multi_batch_size_helper(size_t *n_batches, size_t *n_batch_points, size_t max_n_batch_points, size_t n) {
if (max_n_batch_points == 0) {
return 0;
}
if (max_n_batch_points > ECMULT_MAX_POINTS_PER_BATCH) {
max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
}
if (n == 0) {
*n_batches = 0;
*n_batch_points = 0;
return 1;
}
/* Compute ceil(n/max_n_batch_points) and ceil(n/n_batches) */
*n_batches = 1 + (n - 1) / max_n_batch_points;
*n_batch_points = 1 + (n - 1) / *n_batches;
return 1;
}
typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
static int secp256k1_ecmult_multi_var(const secp256k1_callback* error_callback, const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
size_t i; size_t i;
int (*f)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t); int (*f)(const secp256k1_callback* error_callback, const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t);
size_t max_points;
size_t n_batches; size_t n_batches;
size_t n_batch_points; size_t n_batch_points;
@ -990,32 +1180,30 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2
secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc); secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc);
return 1; return 1;
} }
if (scratch == NULL) {
max_points = secp256k1_pippenger_max_points(scratch); return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
if (max_points == 0) {
return 0;
} else if (max_points > ECMULT_MAX_POINTS_PER_BATCH) {
max_points = ECMULT_MAX_POINTS_PER_BATCH;
} }
n_batches = (n+max_points-1)/max_points;
n_batch_points = (n+n_batches-1)/n_batches;
/* Compute the batch sizes for Pippenger's algorithm given a scratch space. If it's greater than
* a threshold use Pippenger's algorithm. Otherwise use Strauss' algorithm.
* As a first step check if there's enough space for Pippenger's algo (which requires less space
* than Strauss' algo) and if not, use the simple algorithm. */
if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_pippenger_max_points(error_callback, scratch), n)) {
return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
}
if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) { if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) {
f = secp256k1_ecmult_pippenger_batch; f = secp256k1_ecmult_pippenger_batch;
} else { } else {
max_points = secp256k1_strauss_max_points(scratch); if (!secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, secp256k1_strauss_max_points(error_callback, scratch), n)) {
if (max_points == 0) { return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n);
return 0;
} }
n_batches = (n+max_points-1)/max_points;
n_batch_points = (n+n_batches-1)/n_batches;
f = secp256k1_ecmult_strauss_batch; f = secp256k1_ecmult_strauss_batch;
} }
for(i = 0; i < n_batches; i++) { for(i = 0; i < n_batches; i++) {
size_t nbp = n < n_batch_points ? n : n_batch_points; size_t nbp = n < n_batch_points ? n : n_batch_points;
size_t offset = n_batch_points*i; size_t offset = n_batch_points*i;
secp256k1_gej tmp; secp256k1_gej tmp;
if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) { if (!f(error_callback, ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
return 0; return 0;
} }
secp256k1_gej_add_var(r, r, &tmp, NULL); secp256k1_gej_add_var(r, r, &tmp, NULL);

View File

@ -10,7 +10,9 @@
#include <stdint.h> #include <stdint.h>
typedef struct { typedef struct {
/* X = sum(i=0..9, elem[i]*2^26) mod n */ /* X = sum(i=0..9, n[i]*2^(i*26)) mod p
* where p = 2^256 - 0x1000003D1
*/
uint32_t n[10]; uint32_t n[10];
#ifdef VERIFY #ifdef VERIFY
int magnitude; int magnitude;

View File

@ -8,7 +8,6 @@
#define SECP256K1_FIELD_REPR_IMPL_H #define SECP256K1_FIELD_REPR_IMPL_H
#include "util.h" #include "util.h"
#include "num.h"
#include "field.h" #include "field.h"
#ifdef VERIFY #ifdef VERIFY
@ -486,7 +485,8 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t
VERIFY_BITS(b[9], 26); VERIFY_BITS(b[9], 26);
/** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n.
* px is a shorthand for sum(a[i]*b[x-i], i=0..x). * for 0 <= x <= 9, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
* for 9 <= x <= 18, px is a shorthand for sum(a[i]*b[x-i], i=(x-9)..9)
* Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0].
*/ */
@ -1069,6 +1069,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
secp256k1_fe_verify(a); secp256k1_fe_verify(a);
secp256k1_fe_verify(b); secp256k1_fe_verify(b);
VERIFY_CHECK(r != b); VERIFY_CHECK(r != b);
VERIFY_CHECK(a != b);
#endif #endif
secp256k1_fe_mul_inner(r->n, a->n, b->n); secp256k1_fe_mul_inner(r->n, a->n, b->n);
#ifdef VERIFY #ifdef VERIFY

View File

@ -10,7 +10,9 @@
#include <stdint.h> #include <stdint.h>
typedef struct { typedef struct {
/* X = sum(i=0..4, elem[i]*2^52) mod n */ /* X = sum(i=0..4, n[i]*2^(i*52)) mod p
* where p = 2^256 - 0x1000003D1
*/
uint64_t n[5]; uint64_t n[5];
#ifdef VERIFY #ifdef VERIFY
int magnitude; int magnitude;

View File

@ -12,7 +12,6 @@
#endif #endif
#include "util.h" #include "util.h"
#include "num.h"
#include "field.h" #include "field.h"
#if defined(USE_ASM_X86_64) #if defined(USE_ASM_X86_64)
@ -422,6 +421,7 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
secp256k1_fe_verify(a); secp256k1_fe_verify(a);
secp256k1_fe_verify(b); secp256k1_fe_verify(b);
VERIFY_CHECK(r != b); VERIFY_CHECK(r != b);
VERIFY_CHECK(a != b);
#endif #endif
secp256k1_fe_mul_inner(r->n, a->n, b->n); secp256k1_fe_mul_inner(r->n, a->n, b->n);
#ifdef VERIFY #ifdef VERIFY

View File

@ -32,9 +32,11 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t
VERIFY_BITS(b[3], 56); VERIFY_BITS(b[3], 56);
VERIFY_BITS(b[4], 52); VERIFY_BITS(b[4], 52);
VERIFY_CHECK(r != b); VERIFY_CHECK(r != b);
VERIFY_CHECK(a != b);
/* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. /* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n.
* px is a shorthand for sum(a[i]*b[x-i], i=0..x). * for 0 <= x <= 4, px is a shorthand for sum(a[i]*b[x-i], i=0..x).
* for 4 <= x <= 8, px is a shorthand for sum(a[i]*b[x-i], i=(x-4)..4)
* Note that [x 0 0 0 0 0] = [x*R]. * Note that [x 0 0 0 0 0] = [x*R].
*/ */

View File

@ -12,6 +12,7 @@
#endif #endif
#include "util.h" #include "util.h"
#include "num.h"
#if defined(USE_FIELD_10X26) #if defined(USE_FIELD_10X26)
#include "field_10x26_impl.h" #include "field_10x26_impl.h"
@ -48,6 +49,8 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
int j; int j;
VERIFY_CHECK(r != a);
/** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in
* { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: * { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block:
* 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] * 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]

View File

@ -8,6 +8,7 @@
#include "basic-config.h" #include "basic-config.h"
#include "include/secp256k1.h" #include "include/secp256k1.h"
#include "util.h"
#include "field_impl.h" #include "field_impl.h"
#include "scalar_impl.h" #include "scalar_impl.h"
#include "group_impl.h" #include "group_impl.h"
@ -26,6 +27,7 @@ static const secp256k1_callback default_error_callback = {
int main(int argc, char **argv) { int main(int argc, char **argv) {
secp256k1_ecmult_gen_context ctx; secp256k1_ecmult_gen_context ctx;
void *prealloc, *base;
int inner; int inner;
int outer; int outer;
FILE* fp; FILE* fp;
@ -38,15 +40,17 @@ int main(int argc, char **argv) {
fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n"); fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n");
return -1; return -1;
} }
fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n");
fprintf(fp, "#include \"src/group.h\"\n"); fprintf(fp, "#include \"src/group.h\"\n");
fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n");
fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n");
base = checked_malloc(&default_error_callback, SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE);
prealloc = base;
secp256k1_ecmult_gen_context_init(&ctx); secp256k1_ecmult_gen_context_init(&ctx);
secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback); secp256k1_ecmult_gen_context_build(&ctx, &prealloc);
for(outer = 0; outer != 64; outer++) { for(outer = 0; outer != 64; outer++) {
fprintf(fp,"{\n"); fprintf(fp,"{\n");
for(inner = 0; inner != 16; inner++) { for(inner = 0; inner != 16; inner++) {
@ -65,10 +69,11 @@ int main(int argc, char **argv) {
} }
fprintf(fp,"};\n"); fprintf(fp,"};\n");
secp256k1_ecmult_gen_context_clear(&ctx); secp256k1_ecmult_gen_context_clear(&ctx);
free(base);
fprintf(fp, "#undef SC\n"); fprintf(fp, "#undef SC\n");
fprintf(fp, "#endif\n"); fprintf(fp, "#endif\n");
fclose(fp); fclose(fp);
return 0; return 0;
} }

View File

@ -65,12 +65,7 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ /** Set a batch of group elements equal to the inputs given in jacobian coordinates */
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb); static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
/** Set a batch of group elements equal to the inputs given in jacobian
* coordinates (with known z-ratios). zr must contain the known z-ratios such
* that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */
static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len);
/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to /** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
* the same global z "denominator". zr must contain the known z-ratios such * the same global z "denominator". zr must contain the known z-ratios such

View File

@ -38,22 +38,22 @@
*/ */
#if defined(EXHAUSTIVE_TEST_ORDER) #if defined(EXHAUSTIVE_TEST_ORDER)
# if EXHAUSTIVE_TEST_ORDER == 199 # if EXHAUSTIVE_TEST_ORDER == 199
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069, 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18, 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868, 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
); );
const int CURVE_B = 4; static const int CURVE_B = 4;
# elif EXHAUSTIVE_TEST_ORDER == 13 # elif EXHAUSTIVE_TEST_ORDER == 13
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0, 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15, 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e, 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
); );
const int CURVE_B = 2; static const int CURVE_B = 2;
# else # else
# error No known generator for the specified exhaustive test group order. # error No known generator for the specified exhaustive test group order.
# endif # endif
@ -68,7 +68,7 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
); );
const int CURVE_B = 7; static const int CURVE_B = 7;
#endif #endif
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
@ -126,46 +126,43 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
r->y = a->y; r->y = a->y;
} }
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) { static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) {
secp256k1_fe *az; secp256k1_fe u;
secp256k1_fe *azi;
size_t i; size_t i;
size_t count = 0; size_t last_i = SIZE_MAX;
az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (!a[i].infinity) { if (!a[i].infinity) {
az[count++] = a[i].z; /* Use destination's x coordinates as scratch space */
if (last_i == SIZE_MAX) {
r[i].x = a[i].z;
} else {
secp256k1_fe_mul(&r[i].x, &r[last_i].x, &a[i].z);
}
last_i = i;
} }
} }
if (last_i == SIZE_MAX) {
return;
}
secp256k1_fe_inv_var(&u, &r[last_i].x);
azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); i = last_i;
secp256k1_fe_inv_all_var(azi, az, count); while (i > 0) {
free(az); i--;
if (!a[i].infinity) {
secp256k1_fe_mul(&r[last_i].x, &r[i].x, &u);
secp256k1_fe_mul(&u, &u, &a[last_i].z);
last_i = i;
}
}
VERIFY_CHECK(!a[last_i].infinity);
r[last_i].x = u;
count = 0;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
r[i].infinity = a[i].infinity; r[i].infinity = a[i].infinity;
if (!a[i].infinity) { if (!a[i].infinity) {
secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]); secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x);
}
}
free(azi);
}
static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) {
size_t i = len - 1;
secp256k1_fe zi;
if (len > 0) {
/* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */
secp256k1_fe_inv(&zi, &a[i].z);
secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi);
/* Work out way backwards, using the z-ratios to scale the x/y values. */
while (i > 0) {
secp256k1_fe_mul(&zi, &zi, &zr[i]);
i--;
secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi);
} }
} }
} }
@ -178,6 +175,8 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
/* The z of the final point gives us the "global Z" for the table. */ /* The z of the final point gives us the "global Z" for the table. */
r[i].x = a[i].x; r[i].x = a[i].x;
r[i].y = a[i].y; r[i].y = a[i].y;
/* Ensure all y values are in weak normal form for fast negation of points */
secp256k1_fe_normalize_weak(&r[i].y);
*globalz = a[i].z; *globalz = a[i].z;
r[i].infinity = 0; r[i].infinity = 0;
zs = zr[i]; zs = zr[i];

View File

@ -376,7 +376,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* extract m6 */ /* extract m6 */
"movq %%r8, %q6\n" "movq %%r8, %q6\n"
: "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6)
: "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) : "S"(l), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc");
/* Reduce 385 bits into 258. */ /* Reduce 385 bits into 258. */
@ -455,7 +455,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* extract p4 */ /* extract p4 */
"movq %%r9, %q4\n" "movq %%r9, %q4\n"
: "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4) : "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4)
: "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc");
/* Reduce 258 bits into 256. */ /* Reduce 258 bits into 256. */
@ -501,7 +501,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
/* Extract c */ /* Extract c */
"movq %%r9, %q0\n" "movq %%r9, %q0\n"
: "=g"(c) : "=g"(c)
: "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "i"(SECP256K1_N_C_0), "i"(SECP256K1_N_C_1)
: "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); : "rax", "rdx", "r8", "r9", "r10", "cc", "memory");
#else #else
uint128_t c; uint128_t c;

View File

@ -7,33 +7,36 @@
#ifndef _SECP256K1_SCRATCH_ #ifndef _SECP256K1_SCRATCH_
#define _SECP256K1_SCRATCH_ #define _SECP256K1_SCRATCH_
#define SECP256K1_SCRATCH_MAX_FRAMES 5
/* The typedef is used internally; the struct name is used in the public API /* The typedef is used internally; the struct name is used in the public API
* (where it is exposed as a different typedef) */ * (where it is exposed as a different typedef) */
typedef struct secp256k1_scratch_space_struct { typedef struct secp256k1_scratch_space_struct {
void *data[SECP256K1_SCRATCH_MAX_FRAMES]; /** guard against interpreting this object as other types */
size_t offset[SECP256K1_SCRATCH_MAX_FRAMES]; unsigned char magic[8];
size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES]; /** actual allocated data */
size_t frame; void *data;
/** amount that has been allocated (i.e. `data + offset` is the next
* available pointer) */
size_t alloc_size;
/** maximum size available to allocate */
size_t max_size; size_t max_size;
const secp256k1_callback* error_callback;
} secp256k1_scratch; } secp256k1_scratch;
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size); static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size);
static void secp256k1_scratch_destroy(secp256k1_scratch* scratch); static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch);
/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */ /** Returns an opaque object used to "checkpoint" a scratch space. Used
static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects); * with `secp256k1_scratch_apply_checkpoint` to undo allocations. */
static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch);
/** Deallocates a stack frame */ /** Applies a check point received from `secp256k1_scratch_checkpoint`,
static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch); * undoing all allocations since that point. */
static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint);
/** Returns the maximum allocation the scratch space will allow */ /** Returns the maximum allocation the scratch space will allow */
static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects); static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t n_objects);
/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */ /** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */
static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n); static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n);
#endif #endif

View File

@ -7,78 +7,80 @@
#ifndef _SECP256K1_SCRATCH_IMPL_H_ #ifndef _SECP256K1_SCRATCH_IMPL_H_
#define _SECP256K1_SCRATCH_IMPL_H_ #define _SECP256K1_SCRATCH_IMPL_H_
#include "util.h"
#include "scratch.h" #include "scratch.h"
/* Using 16 bytes alignment because common architectures never have alignment static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t size) {
* requirements above 8 for any of the types we care about. In addition we const size_t base_alloc = ((sizeof(secp256k1_scratch) + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
* leave some room because currently we don't care about a few bytes. void *alloc = checked_malloc(error_callback, base_alloc + size);
* TODO: Determine this at configure time. */ secp256k1_scratch* ret = (secp256k1_scratch *)alloc;
#define ALIGNMENT 16
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) {
secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret));
if (ret != NULL) { if (ret != NULL) {
memset(ret, 0, sizeof(*ret)); memset(ret, 0, sizeof(*ret));
ret->max_size = max_size; memcpy(ret->magic, "scratch", 8);
ret->error_callback = error_callback; ret->data = (void *) ((char *) alloc + base_alloc);
ret->max_size = size;
} }
return ret; return ret;
} }
static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) { static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
if (scratch != NULL) { if (scratch != NULL) {
VERIFY_CHECK(scratch->frame == 0); VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */
if (memcmp(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return;
}
memset(scratch->magic, 0, sizeof(scratch->magic));
free(scratch); free(scratch);
} }
} }
static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) { static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) {
size_t i = 0; if (memcmp(scratch->magic, "scratch", 8) != 0) {
size_t allocated = 0; secp256k1_callback_call(error_callback, "invalid scratch space");
for (i = 0; i < scratch->frame; i++) {
allocated += scratch->frame_size[i];
}
if (scratch->max_size - allocated <= objects * ALIGNMENT) {
return 0; return 0;
} }
return scratch->max_size - allocated - objects * ALIGNMENT; return scratch->alloc_size;
} }
static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) { static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) {
VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES); if (memcmp(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return;
}
if (checkpoint > scratch->alloc_size) {
secp256k1_callback_call(error_callback, "invalid checkpoint");
return;
}
scratch->alloc_size = checkpoint;
}
if (n <= secp256k1_scratch_max_allocation(scratch, objects)) { static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
n += objects * ALIGNMENT; if (memcmp(scratch->magic, "scratch", 8) != 0) {
scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n); secp256k1_callback_call(error_callback, "invalid scratch space");
if (scratch->data[scratch->frame] == NULL) {
return 0;
}
scratch->frame_size[scratch->frame] = n;
scratch->offset[scratch->frame] = 0;
scratch->frame++;
return 1;
} else {
return 0; return 0;
} }
if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) {
return 0;
}
return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1);
} }
static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) { static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) {
VERIFY_CHECK(scratch->frame > 0);
scratch->frame -= 1;
free(scratch->data[scratch->frame]);
}
static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
void *ret; void *ret;
size_t frame = scratch->frame - 1; size = ROUND_TO_ALIGN(size);
size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) { if (memcmp(scratch->magic, "scratch", 8) != 0) {
secp256k1_callback_call(error_callback, "invalid scratch space");
return NULL; return NULL;
} }
ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]);
if (size > scratch->max_size - scratch->alloc_size) {
return NULL;
}
ret = (void *) ((char *) scratch->data + scratch->alloc_size);
memset(ret, 0, size); memset(ret, 0, size);
scratch->offset[frame] += size; scratch->alloc_size += size;
return ret; return ret;
} }

View File

@ -5,6 +5,7 @@
**********************************************************************/ **********************************************************************/
#include "include/secp256k1.h" #include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
#include "util.h" #include "util.h"
#include "num_impl.h" #include "num_impl.h"
@ -26,28 +27,39 @@
} \ } \
} while(0) } while(0)
static void default_illegal_callback_fn(const char* str, void* data) { #define ARG_CHECK_NO_RETURN(cond) do { \
if (EXPECT(!(cond), 0)) { \
secp256k1_callback_call(&ctx->illegal_callback, #cond); \
} \
} while(0)
#ifndef USE_EXTERNAL_DEFAULT_CALLBACKS
#include <stdlib.h>
#include <stdio.h>
static void secp256k1_default_illegal_callback_fn(const char* str, void* data) {
(void)data; (void)data;
fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str);
abort(); abort();
} }
static void secp256k1_default_error_callback_fn(const char* str, void* data) {
static const secp256k1_callback default_illegal_callback = {
default_illegal_callback_fn,
NULL
};
static void default_error_callback_fn(const char* str, void* data) {
(void)data; (void)data;
fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str);
abort(); abort();
} }
#else
void secp256k1_default_illegal_callback_fn(const char* str, void* data);
void secp256k1_default_error_callback_fn(const char* str, void* data);
#endif
static const secp256k1_callback default_error_callback = { static const secp256k1_callback default_illegal_callback = {
default_error_callback_fn, secp256k1_default_illegal_callback_fn,
NULL NULL
}; };
static const secp256k1_callback default_error_callback = {
secp256k1_default_error_callback_fn,
NULL
};
struct secp256k1_context_struct { struct secp256k1_context_struct {
secp256k1_ecmult_context ecmult_ctx; secp256k1_ecmult_context ecmult_ctx;
@ -59,20 +71,55 @@ struct secp256k1_context_struct {
static const secp256k1_context secp256k1_context_no_precomp_ = { static const secp256k1_context secp256k1_context_no_precomp_ = {
{ 0 }, { 0 },
{ 0 }, { 0 },
{ default_illegal_callback_fn, 0 }, { secp256k1_default_illegal_callback_fn, 0 },
{ default_error_callback_fn, 0 } { secp256k1_default_error_callback_fn, 0 }
}; };
const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_; const secp256k1_context *secp256k1_context_no_precomp = &secp256k1_context_no_precomp_;
secp256k1_context* secp256k1_context_create(unsigned int flags) { size_t secp256k1_context_preallocated_size(unsigned int flags) {
secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context)); size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
secp256k1_callback_call(&default_illegal_callback,
"Invalid flags");
return 0;
}
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
}
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
}
return ret;
}
size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) {
size_t ret = ROUND_TO_ALIGN(sizeof(secp256k1_context));
VERIFY_CHECK(ctx != NULL);
if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) {
ret += SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
}
if (secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)) {
ret += SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
}
return ret;
}
secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) {
void* const base = prealloc;
size_t prealloc_size;
secp256k1_context* ret;
VERIFY_CHECK(prealloc != NULL);
prealloc_size = secp256k1_context_preallocated_size(flags);
ret = (secp256k1_context*)manual_alloc(&prealloc, sizeof(secp256k1_context), base, prealloc_size);
ret->illegal_callback = default_illegal_callback; ret->illegal_callback = default_illegal_callback;
ret->error_callback = default_error_callback; ret->error_callback = default_error_callback;
if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
secp256k1_callback_call(&ret->illegal_callback, secp256k1_callback_call(&ret->illegal_callback,
"Invalid flags"); "Invalid flags");
free(ret);
return NULL; return NULL;
} }
@ -80,47 +127,79 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) {
secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx);
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &prealloc);
} }
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); secp256k1_ecmult_context_build(&ret->ecmult_ctx, &prealloc);
} }
return (secp256k1_context*) ret;
}
secp256k1_context* secp256k1_context_create(unsigned int flags) {
size_t const prealloc_size = secp256k1_context_preallocated_size(flags);
secp256k1_context* ctx = (secp256k1_context*)checked_malloc(&default_error_callback, prealloc_size);
if (EXPECT(secp256k1_context_preallocated_create(ctx, flags) == NULL, 0)) {
free(ctx);
return NULL;
}
return ctx;
}
secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context* ctx, void* prealloc) {
size_t prealloc_size;
secp256k1_context* ret;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(prealloc != NULL);
prealloc_size = secp256k1_context_preallocated_clone_size(ctx);
ret = (secp256k1_context*)prealloc;
memcpy(ret, ctx, prealloc_size);
secp256k1_ecmult_gen_context_finalize_memcpy(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx);
secp256k1_ecmult_context_finalize_memcpy(&ret->ecmult_ctx, &ctx->ecmult_ctx);
return ret; return ret;
} }
secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) {
secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context)); secp256k1_context* ret;
ret->illegal_callback = ctx->illegal_callback; size_t prealloc_size;
ret->error_callback = ctx->error_callback;
secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); VERIFY_CHECK(ctx != NULL);
secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); prealloc_size = secp256k1_context_preallocated_clone_size(ctx);
ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size);
ret = secp256k1_context_preallocated_clone(ctx, ret);
return ret; return ret;
} }
void secp256k1_context_destroy(secp256k1_context* ctx) { void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) {
CHECK(ctx != secp256k1_context_no_precomp); ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (ctx != NULL) { if (ctx != NULL) {
secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); secp256k1_ecmult_context_clear(&ctx->ecmult_ctx);
secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx);
}
}
void secp256k1_context_destroy(secp256k1_context* ctx) {
if (ctx != NULL) {
secp256k1_context_preallocated_destroy(ctx);
free(ctx); free(ctx);
} }
} }
void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
CHECK(ctx != secp256k1_context_no_precomp); ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (fun == NULL) { if (fun == NULL) {
fun = default_illegal_callback_fn; fun = secp256k1_default_illegal_callback_fn;
} }
ctx->illegal_callback.fn = fun; ctx->illegal_callback.fn = fun;
ctx->illegal_callback.data = data; ctx->illegal_callback.data = data;
} }
void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
CHECK(ctx != secp256k1_context_no_precomp); ARG_CHECK_NO_RETURN(ctx != secp256k1_context_no_precomp);
if (fun == NULL) { if (fun == NULL) {
fun = default_error_callback_fn; fun = secp256k1_default_error_callback_fn;
} }
ctx->error_callback.fn = fun; ctx->error_callback.fn = fun;
ctx->error_callback.data = data; ctx->error_callback.data = data;
@ -131,8 +210,9 @@ secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context*
return secp256k1_scratch_create(&ctx->error_callback, max_size); return secp256k1_scratch_create(&ctx->error_callback, max_size);
} }
void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) { void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scratch_space* scratch) {
secp256k1_scratch_destroy(scratch); VERIFY_CHECK(ctx != NULL);
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
} }
static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
@ -457,6 +537,7 @@ int secp256k1_ec_privkey_negate(const secp256k1_context* ctx, unsigned char *sec
secp256k1_scalar_negate(&sec, &sec); secp256k1_scalar_negate(&sec, &sec);
secp256k1_scalar_get_b32(seckey, &sec); secp256k1_scalar_get_b32(seckey, &sec);
secp256k1_scalar_clear(&sec);
return 1; return 1;
} }
@ -570,9 +651,9 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) {
VERIFY_CHECK(ctx != NULL); VERIFY_CHECK(ctx != NULL);
CHECK(ctx != secp256k1_context_no_precomp); if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) {
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); }
return 1; return 1;
} }

View File

@ -16,6 +16,7 @@
#include "secp256k1.c" #include "secp256k1.c"
#include "include/secp256k1.h" #include "include/secp256k1.h"
#include "include/secp256k1_preallocated.h"
#include "testrand_impl.h" #include "testrand_impl.h"
#ifdef ENABLE_OPENSSL_TESTS #ifdef ENABLE_OPENSSL_TESTS
@ -137,23 +138,47 @@ void random_scalar_order(secp256k1_scalar *num) {
} while(1); } while(1);
} }
void run_context_tests(void) { void run_context_tests(int use_prealloc) {
secp256k1_pubkey pubkey; secp256k1_pubkey pubkey;
secp256k1_pubkey zero_pubkey; secp256k1_pubkey zero_pubkey;
secp256k1_ecdsa_signature sig; secp256k1_ecdsa_signature sig;
unsigned char ctmp[32]; unsigned char ctmp[32];
int32_t ecount; int32_t ecount;
int32_t ecount2; int32_t ecount2;
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_context *none;
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); secp256k1_context *sign;
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); secp256k1_context *vrfy;
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); secp256k1_context *both;
void *none_prealloc = NULL;
void *sign_prealloc = NULL;
void *vrfy_prealloc = NULL;
void *both_prealloc = NULL;
secp256k1_gej pubj; secp256k1_gej pubj;
secp256k1_ge pub; secp256k1_ge pub;
secp256k1_scalar msg, key, nonce; secp256k1_scalar msg, key, nonce;
secp256k1_scalar sigr, sigs; secp256k1_scalar sigr, sigs;
if (use_prealloc) {
none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN));
vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY));
both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY));
CHECK(none_prealloc != NULL);
CHECK(sign_prealloc != NULL);
CHECK(vrfy_prealloc != NULL);
CHECK(both_prealloc != NULL);
none = secp256k1_context_preallocated_create(none_prealloc, SECP256K1_CONTEXT_NONE);
sign = secp256k1_context_preallocated_create(sign_prealloc, SECP256K1_CONTEXT_SIGN);
vrfy = secp256k1_context_preallocated_create(vrfy_prealloc, SECP256K1_CONTEXT_VERIFY);
both = secp256k1_context_preallocated_create(both_prealloc, SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
} else {
none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
}
memset(&zero_pubkey, 0, sizeof(zero_pubkey)); memset(&zero_pubkey, 0, sizeof(zero_pubkey));
ecount = 0; ecount = 0;
@ -163,14 +188,57 @@ void run_context_tests(void) {
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL); secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL);
CHECK(vrfy->error_callback.fn != sign->error_callback.fn); CHECK(vrfy->error_callback.fn != sign->error_callback.fn);
/* check if sizes for cloning are consistent */
CHECK(secp256k1_context_preallocated_clone_size(none) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
CHECK(secp256k1_context_preallocated_clone_size(sign) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN));
CHECK(secp256k1_context_preallocated_clone_size(vrfy) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY));
CHECK(secp256k1_context_preallocated_clone_size(both) == secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY));
/*** clone and destroy all of them to make sure cloning was complete ***/ /*** clone and destroy all of them to make sure cloning was complete ***/
{ {
secp256k1_context *ctx_tmp; secp256k1_context *ctx_tmp;
ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp); if (use_prealloc) {
ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp); /* clone into a non-preallocated context and then again into a new preallocated one. */
ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp); ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp);
ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp); free(none_prealloc); none_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(none_prealloc != NULL);
ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, none_prealloc); secp256k1_context_destroy(ctx_tmp);
ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp);
free(sign_prealloc); sign_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(sign_prealloc != NULL);
ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, sign_prealloc); secp256k1_context_destroy(ctx_tmp);
ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp);
free(vrfy_prealloc); vrfy_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(vrfy_prealloc != NULL);
ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, vrfy_prealloc); secp256k1_context_destroy(ctx_tmp);
ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp);
free(both_prealloc); both_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(both_prealloc != NULL);
ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, both_prealloc); secp256k1_context_destroy(ctx_tmp);
} else {
/* clone into a preallocated context and then again into a new non-preallocated one. */
void *prealloc_tmp;
prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(prealloc_tmp != NULL);
ctx_tmp = none; none = secp256k1_context_preallocated_clone(none, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_preallocated_destroy(ctx_tmp);
free(prealloc_tmp);
prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN)); CHECK(prealloc_tmp != NULL);
ctx_tmp = sign; sign = secp256k1_context_preallocated_clone(sign, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_preallocated_destroy(ctx_tmp);
free(prealloc_tmp);
prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL);
ctx_tmp = vrfy; vrfy = secp256k1_context_preallocated_clone(vrfy, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_preallocated_destroy(ctx_tmp);
free(prealloc_tmp);
prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)); CHECK(prealloc_tmp != NULL);
ctx_tmp = both; both = secp256k1_context_preallocated_clone(both, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_preallocated_destroy(ctx_tmp);
free(prealloc_tmp);
}
} }
/* Verify that the error callback makes it across the clone. */ /* Verify that the error callback makes it across the clone. */
@ -218,17 +286,17 @@ void run_context_tests(void) {
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1); CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1);
CHECK(ecount == 3); CHECK(ecount == 3);
CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0); CHECK(secp256k1_context_randomize(vrfy, ctmp) == 1);
CHECK(ecount == 4); CHECK(ecount == 3);
CHECK(secp256k1_context_randomize(vrfy, NULL) == 1);
CHECK(ecount == 3);
CHECK(secp256k1_context_randomize(sign, ctmp) == 1);
CHECK(ecount2 == 14);
CHECK(secp256k1_context_randomize(sign, NULL) == 1); CHECK(secp256k1_context_randomize(sign, NULL) == 1);
CHECK(ecount2 == 14); CHECK(ecount2 == 14);
secp256k1_context_set_illegal_callback(vrfy, NULL, NULL); secp256k1_context_set_illegal_callback(vrfy, NULL, NULL);
secp256k1_context_set_illegal_callback(sign, NULL, NULL); secp256k1_context_set_illegal_callback(sign, NULL, NULL);
/* This shouldn't leak memory, due to already-set tests. */
secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL);
secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL);
/* obtain a working nonce */ /* obtain a working nonce */
do { do {
random_scalar_order_test(&nonce); random_scalar_order_test(&nonce);
@ -243,49 +311,95 @@ void run_context_tests(void) {
CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg)); CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg));
/* cleanup */ /* cleanup */
secp256k1_context_destroy(none); if (use_prealloc) {
secp256k1_context_destroy(sign); secp256k1_context_preallocated_destroy(none);
secp256k1_context_destroy(vrfy); secp256k1_context_preallocated_destroy(sign);
secp256k1_context_destroy(both); secp256k1_context_preallocated_destroy(vrfy);
secp256k1_context_preallocated_destroy(both);
free(none_prealloc);
free(sign_prealloc);
free(vrfy_prealloc);
free(both_prealloc);
} else {
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(vrfy);
secp256k1_context_destroy(both);
}
/* Defined as no-op. */ /* Defined as no-op. */
secp256k1_context_destroy(NULL); secp256k1_context_destroy(NULL);
secp256k1_context_preallocated_destroy(NULL);
} }
void run_scratch_tests(void) { void run_scratch_tests(void) {
const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
int32_t ecount = 0; int32_t ecount = 0;
size_t checkpoint;
size_t checkpoint_2;
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_scratch_space *scratch; secp256k1_scratch_space *scratch;
secp256k1_scratch_space local_scratch;
/* Test public API */ /* Test public API */
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
scratch = secp256k1_scratch_space_create(none, 1000); scratch = secp256k1_scratch_space_create(none, 1000);
CHECK(scratch != NULL); CHECK(scratch != NULL);
CHECK(ecount == 0); CHECK(ecount == 0);
/* Test internal API */ /* Test internal API */
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000); CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1));
CHECK(scratch->alloc_size == 0);
CHECK(scratch->alloc_size % ALIGNMENT == 0);
/* Allocating 500 bytes with no frame fails */ /* Allocating 500 bytes succeeds */
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); checkpoint = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
CHECK(scratch->alloc_size != 0);
CHECK(scratch->alloc_size % ALIGNMENT == 0);
/* ...but pushing a new stack frame does affect the max allocation */ /* Allocating another 500 bytes fails */
CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1)); CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */ CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL); CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); CHECK(scratch->alloc_size != 0);
CHECK(scratch->alloc_size % ALIGNMENT == 0);
CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0); /* ...but it succeeds once we apply the checkpoint to undo it */
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
CHECK(scratch->alloc_size == 0);
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
CHECK(scratch->alloc_size != 0);
/* ...and this effect is undone by popping the frame */ /* try to apply a bad checkpoint */
secp256k1_scratch_deallocate_frame(scratch); checkpoint_2 = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000); secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL); CHECK(ecount == 0);
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */
CHECK(ecount == 1);
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */
CHECK(ecount == 2);
/* try to use badly initialized scratch space */
secp256k1_scratch_space_destroy(none, scratch);
memset(&local_scratch, 0, sizeof(local_scratch));
scratch = &local_scratch;
CHECK(!secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0));
CHECK(ecount == 3);
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
CHECK(ecount == 4);
secp256k1_scratch_space_destroy(none, scratch);
CHECK(ecount == 5);
/* cleanup */ /* cleanup */
secp256k1_scratch_space_destroy(scratch); secp256k1_scratch_space_destroy(none, NULL); /* no-op */
secp256k1_context_destroy(none); secp256k1_context_destroy(none);
} }
@ -2095,7 +2209,6 @@ void test_ge(void) {
/* Test batch gej -> ge conversion with and without known z ratios. */ /* Test batch gej -> ge conversion with and without known z ratios. */
{ {
secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe)); secp256k1_fe *zr = (secp256k1_fe *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_fe));
secp256k1_ge *ge_set_table = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&ctx->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge));
for (i = 0; i < 4 * runs + 1; i++) { for (i = 0; i < 4 * runs + 1; i++) {
/* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */ /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */
@ -2103,20 +2216,33 @@ void test_ge(void) {
secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z); secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z);
} }
} }
secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1); secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1);
secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback);
for (i = 0; i < 4 * runs + 1; i++) { for (i = 0; i < 4 * runs + 1; i++) {
secp256k1_fe s; secp256k1_fe s;
random_fe_non_zero(&s); random_fe_non_zero(&s);
secp256k1_gej_rescale(&gej[i], &s); secp256k1_gej_rescale(&gej[i], &s);
ge_equals_gej(&ge_set_table[i], &gej[i]);
ge_equals_gej(&ge_set_all[i], &gej[i]); ge_equals_gej(&ge_set_all[i], &gej[i]);
} }
free(ge_set_table);
free(ge_set_all); free(ge_set_all);
free(zr); free(zr);
} }
/* Test batch gej -> ge conversion with many infinities. */
for (i = 0; i < 4 * runs + 1; i++) {
random_group_element_test(&ge[i]);
/* randomly set half the points to infinitiy */
if(secp256k1_fe_is_odd(&ge[i].x)) {
secp256k1_ge_set_infinity(&ge[i]);
}
secp256k1_gej_set_ge(&gej[i], &ge[i]);
}
/* batch invert */
secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1);
/* check result */
for (i = 0; i < 4 * runs + 1; i++) {
ge_equals_gej(&ge[i], &gej[i]);
}
free(ge); free(ge);
free(gej); free(gej);
free(zinv); free(zinv);
@ -2556,14 +2682,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_gej r; secp256k1_gej r;
secp256k1_gej r2; secp256k1_gej r2;
ecmult_multi_data data; ecmult_multi_data data;
secp256k1_scratch *scratch_empty;
data.sc = sc; data.sc = sc;
data.pt = pt; data.pt = pt;
secp256k1_scalar_set_int(&szero, 0); secp256k1_scalar_set_int(&szero, 0);
/* No points to multiply */ /* No points to multiply */
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
/* Check 1- and 2-point multiplies against ecmult */ /* Check 1- and 2-point multiplies against ecmult */
for (ncount = 0; ncount < count; ncount++) { for (ncount = 0; ncount < count; ncount++) {
@ -2579,36 +2704,31 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
/* only G scalar */ /* only G scalar */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]); secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0));
secp256k1_gej_neg(&r2, &r2); secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
/* 1-point */ /* 1-point */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero); secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1));
secp256k1_gej_neg(&r2, &r2); secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
/* Try to multiply 1 point, but scratch space is empty */
scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
secp256k1_scratch_destroy(scratch_empty);
/* Try to multiply 1 point, but callback returns false */ /* Try to multiply 1 point, but callback returns false */
CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1)); CHECK(!ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1));
/* 2-point */ /* 2-point */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]); secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2));
secp256k1_gej_neg(&r2, &r2); secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
/* 2-point with G scalar */ /* 2-point with G scalar */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]); secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1));
secp256k1_gej_neg(&r2, &r2); secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
@ -2625,7 +2745,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
random_scalar_order(&sc[i]); random_scalar_order(&sc[i]);
secp256k1_ge_set_infinity(&pt[i]); secp256k1_ge_set_infinity(&pt[i]);
} }
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
} }
@ -2635,7 +2755,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
pt[i] = ptg; pt[i] = ptg;
secp256k1_scalar_set_int(&sc[i], 0); secp256k1_scalar_set_int(&sc[i], 0);
} }
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
} }
@ -2648,7 +2768,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
pt[2 * i + 1] = ptg; pt[2 * i + 1] = ptg;
} }
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
random_scalar_order(&sc[0]); random_scalar_order(&sc[0]);
@ -2661,7 +2781,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]); secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]);
} }
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j])); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
} }
@ -2676,7 +2796,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_scalar_negate(&sc[i], &sc[i]); secp256k1_scalar_negate(&sc[i], &sc[i]);
} }
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
} }
@ -2695,7 +2815,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
} }
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero); secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_gej_neg(&r2, &r2); secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
@ -2718,7 +2838,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_gej_set_ge(&p0j, &pt[0]); secp256k1_gej_set_ge(&p0j, &pt[0]);
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero); secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_gej_neg(&r2, &r2); secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
@ -2731,13 +2851,13 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
} }
secp256k1_scalar_clear(&sc[0]); secp256k1_scalar_clear(&sc[0]);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_scalar_clear(&sc[1]); secp256k1_scalar_clear(&sc[1]);
secp256k1_scalar_clear(&sc[2]); secp256k1_scalar_clear(&sc[2]);
secp256k1_scalar_clear(&sc[3]); secp256k1_scalar_clear(&sc[3]);
secp256k1_scalar_clear(&sc[4]); secp256k1_scalar_clear(&sc[4]);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5));
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
/* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */ /* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */
@ -2782,7 +2902,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
secp256k1_scalar_add(&tmp1, &tmp1, &tmp2); secp256k1_scalar_add(&tmp1, &tmp1, &tmp2);
secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero); secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2)); CHECK(ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2));
secp256k1_gej_neg(&expected, &expected); secp256k1_gej_neg(&expected, &expected);
secp256k1_gej_add_var(&actual, &actual, &expected, NULL); secp256k1_gej_add_var(&actual, &actual, &expected, NULL);
CHECK(secp256k1_gej_is_infinity(&actual)); CHECK(secp256k1_gej_is_infinity(&actual));
@ -2793,6 +2913,24 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
} }
} }
void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
secp256k1_scalar szero;
secp256k1_scalar sc[32];
secp256k1_ge pt[32];
secp256k1_gej r;
ecmult_multi_data data;
secp256k1_scratch *scratch_empty;
data.sc = sc;
data.pt = pt;
secp256k1_scalar_set_int(&szero, 0);
/* Try to multiply 1 point, but scratch space is empty.*/
scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
CHECK(!ecmult_multi(&ctx->error_callback, &ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
secp256k1_scratch_destroy(&ctx->error_callback, scratch_empty);
}
void test_secp256k1_pippenger_bucket_window_inv(void) { void test_secp256k1_pippenger_bucket_window_inv(void) {
int i; int i;
@ -2823,21 +2961,75 @@ void test_ecmult_multi_pippenger_max_points(void) {
int bucket_window = 0; int bucket_window = 0;
for(; scratch_size < max_size; scratch_size+=256) { for(; scratch_size < max_size; scratch_size+=256) {
size_t i;
size_t total_alloc;
size_t checkpoint;
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size); scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
CHECK(scratch != NULL); CHECK(scratch != NULL);
n_points_supported = secp256k1_pippenger_max_points(scratch); checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch);
n_points_supported = secp256k1_pippenger_max_points(&ctx->error_callback, scratch);
if (n_points_supported == 0) { if (n_points_supported == 0) {
secp256k1_scratch_destroy(scratch); secp256k1_scratch_destroy(&ctx->error_callback, scratch);
continue; continue;
} }
bucket_window = secp256k1_pippenger_bucket_window(n_points_supported); bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS)); /* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */
secp256k1_scratch_deallocate_frame(scratch); total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window);
secp256k1_scratch_destroy(scratch); for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) {
CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 1));
total_alloc--;
}
CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, total_alloc));
secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint);
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
} }
CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW); CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);
} }
void test_ecmult_multi_batch_size_helper(void) {
size_t n_batches, n_batch_points, max_n_batch_points, n;
max_n_batch_points = 0;
n = 1;
CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 0);
max_n_batch_points = 1;
n = 0;
CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
CHECK(n_batches == 0);
CHECK(n_batch_points == 0);
max_n_batch_points = 2;
n = 5;
CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
CHECK(n_batches == 3);
CHECK(n_batch_points == 2);
max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH;
n = ECMULT_MAX_POINTS_PER_BATCH;
CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
CHECK(n_batches == 1);
CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH);
max_n_batch_points = ECMULT_MAX_POINTS_PER_BATCH + 1;
n = ECMULT_MAX_POINTS_PER_BATCH + 1;
CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
CHECK(n_batches == 2);
CHECK(n_batch_points == ECMULT_MAX_POINTS_PER_BATCH/2 + 1);
max_n_batch_points = 1;
n = SIZE_MAX;
CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
CHECK(n_batches == SIZE_MAX);
CHECK(n_batch_points == 1);
max_n_batch_points = 2;
n = SIZE_MAX;
CHECK(secp256k1_ecmult_multi_batch_size_helper(&n_batches, &n_batch_points, max_n_batch_points, n) == 1);
CHECK(n_batches == SIZE_MAX/2 + 1);
CHECK(n_batch_points == 2);
}
/** /**
* Run secp256k1_ecmult_multi_var with num points and a scratch space restricted to * Run secp256k1_ecmult_multi_var with num points and a scratch space restricted to
* 1 <= i <= num points. * 1 <= i <= num points.
@ -2872,19 +3064,25 @@ void test_ecmult_multi_batching(void) {
} }
data.sc = sc; data.sc = sc;
data.pt = pt; data.pt = pt;
secp256k1_gej_neg(&r2, &r2);
/* Test with empty scratch space */ /* Test with empty scratch space. It should compute the correct result using
* ecmult_mult_simple algorithm which doesn't require a scratch space. */
scratch = secp256k1_scratch_create(&ctx->error_callback, 0); scratch = secp256k1_scratch_create(&ctx->error_callback, 0);
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1)); CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
secp256k1_scratch_destroy(scratch); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
/* Test with space for 1 point in pippenger. That's not enough because /* Test with space for 1 point in pippenger. That's not enough because
* ecmult_multi selects strauss which requires more memory. */ * ecmult_multi selects strauss which requires more memory. It should
* therefore select the simple algorithm. */
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT); scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1)); CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
secp256k1_scratch_destroy(scratch); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
secp256k1_gej_neg(&r2, &r2);
for(i = 1; i <= n_points; i++) { for(i = 1; i <= n_points; i++) {
if (i > ECMULT_PIPPENGER_THRESHOLD) { if (i > ECMULT_PIPPENGER_THRESHOLD) {
int bucket_window = secp256k1_pippenger_bucket_window(i); int bucket_window = secp256k1_pippenger_bucket_window(i);
@ -2894,10 +3092,10 @@ void test_ecmult_multi_batching(void) {
size_t scratch_size = secp256k1_strauss_scratch_size(i); size_t scratch_size = secp256k1_strauss_scratch_size(i);
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
} }
CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points)); CHECK(secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
secp256k1_gej_add_var(&r, &r, &r2, NULL); secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r)); CHECK(secp256k1_gej_is_infinity(&r));
secp256k1_scratch_destroy(scratch); secp256k1_scratch_destroy(&ctx->error_callback, scratch);
} }
free(sc); free(sc);
free(pt); free(pt);
@ -2910,15 +3108,19 @@ void run_ecmult_multi_tests(void) {
test_ecmult_multi_pippenger_max_points(); test_ecmult_multi_pippenger_max_points();
scratch = secp256k1_scratch_create(&ctx->error_callback, 819200); scratch = secp256k1_scratch_create(&ctx->error_callback, 819200);
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
test_ecmult_multi(NULL, secp256k1_ecmult_multi_var);
test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single); test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single);
test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single);
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single); test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
secp256k1_scratch_destroy(scratch); test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single);
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
/* Run test_ecmult_multi with space for exactly one point */ /* Run test_ecmult_multi with space for exactly one point */
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT); scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
secp256k1_scratch_destroy(scratch); secp256k1_scratch_destroy(&ctx->error_callback, scratch);
test_ecmult_multi_batch_size_helper();
test_ecmult_multi_batching(); test_ecmult_multi_batching();
} }
@ -2988,7 +3190,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
} }
bits = 128; bits = 128;
#endif #endif
skew = secp256k1_wnaf_const(wnaf, num, w, bits); skew = secp256k1_wnaf_const(wnaf, &num, w, bits);
for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) { for (i = WNAF_SIZE_BITS(bits, w); i >= 0; --i) {
secp256k1_scalar t; secp256k1_scalar t;
@ -4978,8 +5180,9 @@ int main(int argc, char **argv) {
} }
} else { } else {
FILE *frand = fopen("/dev/urandom", "r"); FILE *frand = fopen("/dev/urandom", "r");
if ((frand == NULL) || fread(&seed16, sizeof(seed16), 1, frand) != sizeof(seed16)) { if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) {
uint64_t t = time(NULL) * (uint64_t)1337; uint64_t t = time(NULL) * (uint64_t)1337;
fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n");
seed16[0] ^= t; seed16[0] ^= t;
seed16[1] ^= t >> 8; seed16[1] ^= t >> 8;
seed16[2] ^= t >> 16; seed16[2] ^= t >> 16;
@ -4999,7 +5202,8 @@ int main(int argc, char **argv) {
printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]);
/* initialize */ /* initialize */
run_context_tests(); run_context_tests(0);
run_context_tests(1);
run_scratch_tests(); run_scratch_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (secp256k1_rand_bits(1)) { if (secp256k1_rand_bits(1)) {

View File

@ -212,14 +212,14 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_
data.pt[0] = group[x]; data.pt[0] = group[x];
data.pt[1] = group[y]; data.pt[1] = group[y];
secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2); secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp); ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp);
} }
} }
} }
} }
} }
secp256k1_scratch_destroy(scratch); secp256k1_scratch_destroy(&ctx->error_callback, scratch);
} }
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {

View File

@ -36,7 +36,7 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
} while(0) } while(0)
#endif #endif
#ifdef HAVE_BUILTIN_EXPECT #if SECP256K1_GNUC_PREREQ(3, 0)
#define EXPECT(x,c) __builtin_expect((x),(c)) #define EXPECT(x,c) __builtin_expect((x),(c))
#else #else
#define EXPECT(x,c) (x) #define EXPECT(x,c) (x)
@ -84,6 +84,47 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void
return ret; return ret;
} }
#if defined(__BIGGEST_ALIGNMENT__)
#define ALIGNMENT __BIGGEST_ALIGNMENT__
#else
/* Using 16 bytes alignment because common architectures never have alignment
* requirements above 8 for any of the types we care about. In addition we
* leave some room because currently we don't care about a few bytes. */
#define ALIGNMENT 16
#endif
#define ROUND_TO_ALIGN(size) (((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT)
/* Assume there is a contiguous memory object with bounds [base, base + max_size)
* of which the memory range [base, *prealloc_ptr) is already allocated for usage,
* where *prealloc_ptr is an aligned pointer. In that setting, this functions
* reserves the subobject [*prealloc_ptr, *prealloc_ptr + alloc_size) of
* alloc_size bytes by increasing *prealloc_ptr accordingly, taking into account
* alignment requirements.
*
* The function returns an aligned pointer to the newly allocated subobject.
*
* This is useful for manual memory management: if we're simply given a block
* [base, base + max_size), the caller can use this function to allocate memory
* in this block and keep track of the current allocation state with *prealloc_ptr.
*
* It is VERIFY_CHECKed that there is enough space left in the memory object and
* *prealloc_ptr is aligned relative to base.
*/
static SECP256K1_INLINE void *manual_alloc(void** prealloc_ptr, size_t alloc_size, void* base, size_t max_size) {
size_t aligned_alloc_size = ROUND_TO_ALIGN(alloc_size);
void* ret;
VERIFY_CHECK(prealloc_ptr != NULL);
VERIFY_CHECK(*prealloc_ptr != NULL);
VERIFY_CHECK(base != NULL);
VERIFY_CHECK((unsigned char*)*prealloc_ptr >= (unsigned char*)base);
VERIFY_CHECK(((unsigned char*)*prealloc_ptr - (unsigned char*)base) % ALIGNMENT == 0);
VERIFY_CHECK((unsigned char*)*prealloc_ptr - (unsigned char*)base + aligned_alloc_size <= max_size);
ret = *prealloc_ptr;
*((unsigned char**)prealloc_ptr) += aligned_alloc_size;
return ret;
}
/* Macro for restrict, when available and not in a VERIFY build. */ /* Macro for restrict, when available and not in a VERIFY build. */
#if defined(SECP256K1_BUILD) && defined(VERIFY) #if defined(SECP256K1_BUILD) && defined(VERIFY)
# define SECP256K1_RESTRICT # define SECP256K1_RESTRICT

View File

@ -255,6 +255,66 @@ extern "C" {
) -> c_int; ) -> c_int;
} }
#[no_mangle]
/// **This function is an override for the C function, this is the an edited version of the original description:**
///
/// A callback function to be called when an illegal argument is passed to
/// an API call. It will only trigger for violations that are mentioned
/// explicitly in the header. **This will cause a panic**.
///
/// The philosophy is that these shouldn't be dealt with through a
/// specific return value, as calling code should not have branches to deal with
/// the case that this code itself is broken.
///
/// On the other hand, during debug stage, one would want to be informed about
/// such mistakes, and the default (crashing) may be inadvisable.
/// When this callback is triggered, the API function called is guaranteed not
/// to cause a crash, though its return value and output arguments are
/// undefined.
///
/// See also secp256k1_default_error_callback_fn.
///
pub unsafe extern "C" fn secp256k1_default_illegal_callback_fn(message: *const c_char, _data: *mut c_void) {
use core::{str, slice};
let msg_slice = slice::from_raw_parts(message as *const u8, strlen(message));
let msg = str::from_utf8_unchecked(msg_slice);
panic!("[libsecp256k1] illegal argument. {}", msg);
}
#[no_mangle]
/// **This function is an override for the C function, this is the an edited version of the original description:**
///
/// A callback function to be called when an internal consistency check
/// fails. **This will cause a panic**.
///
/// This can only trigger in case of a hardware failure, miscompilation,
/// memory corruption, serious bug in the library, or other error would can
/// otherwise result in undefined behaviour. It will not trigger due to mere
/// incorrect usage of the API (see secp256k1_default_illegal_callback_fn
/// for that). After this callback returns, anything may happen, including
/// crashing.
///
/// See also secp256k1_default_illegal_callback_fn.
///
pub unsafe extern "C" fn secp256k1_default_error_callback_fn(message: *const c_char, _data: *mut c_void) {
use core::{str, slice};
let msg_slice = slice::from_raw_parts(message as *const u8, strlen(message));
let msg = str::from_utf8_unchecked(msg_slice);
panic!("[libsecp256k1] internal consistency check failed {}", msg);
}
unsafe fn strlen(mut str_ptr: *const c_char) -> usize {
let mut ctr = 0;
while *str_ptr != '\0' as c_char {
ctr += 1;
str_ptr = str_ptr.offset(1);
}
ctr
}
#[cfg(feature = "fuzztarget")] #[cfg(feature = "fuzztarget")]
mod fuzz_dummy { mod fuzz_dummy {
extern crate std; extern crate std;
@ -602,3 +662,18 @@ mod fuzz_dummy {
} }
#[cfg(feature = "fuzztarget")] #[cfg(feature = "fuzztarget")]
pub use self::fuzz_dummy::*; pub use self::fuzz_dummy::*;
#[cfg(test)]
mod tests {
use std::ffi::CString;
use super::strlen;
#[test]
fn test_strlen() {
let orig = "test strlen \t \n";
let test = CString::new(orig).unwrap();
assert_eq!(orig.len(), unsafe {strlen(test.as_ptr())});
}
}

View File

@ -5,6 +5,10 @@ pub type c_int = i32;
pub type c_uchar = u8; pub type c_uchar = u8;
pub type c_uint = u32; pub type c_uint = u32;
/// This might not match C's `c_char` exactly.
/// The way we use it makes it fine either way but this type shouldn't be used outside of the library.
pub type c_char = i8;
/// This is an exact copy of https://doc.rust-lang.org/core/ffi/enum.c_void.html /// This is an exact copy of https://doc.rust-lang.org/core/ffi/enum.c_void.html
/// It should be Equivalent to C's void type when used as a pointer. /// It should be Equivalent to C's void type when used as a pointer.
/// ///