Browse Source

Squashed 'src/secp256k1/' changes from 6c527ec..7a49cac

7a49cac Merge #410: Add string.h include to ecmult_impl
0bbd5d4 Add string.h include to ecmult_impl
c5b32e1 Merge #405: Make secp256k1_fe_sqrt constant time
926836a Make secp256k1_fe_sqrt constant time
e2a8e92 Merge #404: Replace 3M + 4S doubling formula with 2M + 5S one
8ec49d8 Add note about 2M + 5S doubling formula
5a91bd7 Merge #400: A couple minor cleanups
ac01378 build: add -DSECP256K1_BUILD to benchmark_internal build flags
a6c6f99 Remove a bunch of unused stdlib #includes
65285a6 Merge #403: configure: add flag to disable OpenSSL tests
a9b2a5d configure: add flag to disable OpenSSL tests
b340123 Merge #402: Add support for testing quadratic residues
e6e9805 Add function for testing quadratic residue field/group elements.
efd953a Add Jacobi symbol test via GMP
fa36a0d Merge #401: ecmult_const: unify endomorphism and non-endomorphism skew cases
c6191fd ecmult_const: unify endomorphism and non-endomorphism skew cases
0b3e618 Merge #378: .gitignore build-aux cleanup
6042217 Merge #384: JNI: align shared files copyright/comments to bitcoinj's
24ad20f Merge #399: build: verify that the native compiler works for static precomp
b3be852 Merge #398: Test whether ECDH and Schnorr are enabled for JNI
aa0b1fd build: verify that the native compiler works for static precomp
eee808d Test whether ECDH and Schnorr are enabled for JNI
7b0fb18 Merge #366: ARM assembly implementation of field_10x26 inner (rebase of #173)
001f176 ARM assembly implementation of field_10x26 inner
0172be9 Merge #397: Small fixes for sha256
3f8b78e Fix undefs in hash_impl.h
2ab4695 Fix state size in sha256 struct
6875b01 Merge #386: Add some missing `VERIFY_CHECK(ctx != NULL)`
2c52b5d Merge #389: Cast pointers through uintptr_t under JNI
43097a4 Merge #390: Update bitcoin-core GitHub links
31c9c12 Merge #391: JNI: Only call ecdsa_verify if its inputs parsed correctly
1cb2302 Merge #392: Add testcase which hits additional branch in secp256k1_scalar_sqr
d2ee340 Merge #388: bench_ecdh: fix call to secp256k1_context_create
093a497 Add testcase which hits additional branch in secp256k1_scalar_sqr
a40c701 JNI: Only call ecdsa_verify if its inputs parsed correctly
faa2a11 Update bitcoin-core GitHub links
47b9e78 Cast pointers through uintptr_t under JNI
f36f9c6 bench_ecdh: fix call to secp256k1_context_create
bcc4881 Add some missing `VERIFY_CHECK(ctx != NULL)` for functions that use `ARG_CHECK`
6ceea2c align shared files copyright/comments to bitcoinj's
70141a8 Update .gitignore
7b549b1 Merge #373: build: fix x86_64 asm detection for some compilers
bc7c93c Merge #374: Add note about y=0 being possible on one of the sextic twists
e457018 Merge #364: JNI rebased
86e2d07 JNI library: cleanup, removed unimplemented code
3093576a JNI library
bd2895f Merge pull request #371
e72e93a Add note about y=0 being possible on one of the sextic twists
3f8fdfb build: fix x86_64 asm detection for some compilers
e5a9047 [Trivial] Remove double semicolons
c18b869 Merge pull request #360
3026daa Merge pull request #302
03d4611 Add sage verification script for the group laws
a965937 Merge pull request #361
83221ec Add experimental features to configure
5d4c5a3 Prevent damage_array in the signature test from going out of bounds.
419bf7f Merge pull request #356
03d84a4 Benchmark against OpenSSL verification

git-subtree-dir: src/secp256k1
git-subtree-split: 7a49cacd39
tags/v0.15.1
Wladimir J. van der Laan 4 years ago
parent
commit
b2135359b3

+ 13
- 6
.gitignore View File

@@ -25,17 +25,24 @@ config.status
libtool
.deps/
.dirstamp
build-aux/
*.lo
*.o
*~
src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
m4/libtool.m4
m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/lt~obsolete.m4
build-aux/config.guess
build-aux/config.sub
build-aux/depcomp
build-aux/install-sh
build-aux/ltmain.sh
build-aux/m4/libtool.m4
build-aux/m4/lt~obsolete.m4
build-aux/m4/ltoptions.m4
build-aux/m4/ltsugar.m4
build-aux/m4/ltversion.m4
build-aux/missing
build-aux/compile
build-aux/test-driver
src/stamp-h1
libsecp256k1.pc

+ 13
- 6
.travis.yml View File

@@ -6,26 +6,31 @@ addons:
compiler:
- clang
- gcc
cache:
directories:
- src/java/guava/
env:
global:
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no EXPERIMENTAL=no
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
matrix:
- SCALAR=32bit RECOVERY=yes
- SCALAR=32bit FIELD=32bit ECDH=yes
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
- SCALAR=64bit
- FIELD=64bit RECOVERY=yes
- FIELD=64bit ENDOMORPHISM=yes
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- FIELD=64bit ASM=x86_64
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- FIELD=32bit SCHNORR=yes
- FIELD=32bit SCHNORR=yes EXPERIMENTAL=yes
- FIELD=32bit ENDOMORPHISM=yes
- BIGNUM=no
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
- EXTRAFLAGS=CFLAGS=-O0
- BUILD=check-java ECDH=yes SCHNORR=yes EXPERIMENTAL=yes
matrix:
fast_finish: true
include:
@@ -55,9 +60,11 @@ matrix:
packages:
- gcc-multilib
- libgmp-dev:i386
before_install: mkdir -p `dirname $GUAVA_JAR`
install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi
before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $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-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux

+ 62
- 10
Makefile.am View File

@@ -1,6 +1,12 @@
ACLOCAL_AMFLAGS = -I build-aux/m4

lib_LTLIBRARIES = libsecp256k1.la
if USE_JNI
JNI_LIB = libsecp256k1_jni.la
noinst_LTLIBRARIES = $(JNI_LIB)
else
JNI_LIB =
endif
include_HEADERS = include/secp256k1.h
noinst_HEADERS =
noinst_HEADERS += src/scalar.h
@@ -32,6 +38,7 @@ noinst_HEADERS += src/field_5x52_impl.h
noinst_HEADERS += src/field_5x52_int128_impl.h
noinst_HEADERS += src/field_5x52_asm_impl.h
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
@@ -45,35 +52,80 @@ noinst_HEADERS += contrib/lax_der_parsing.c
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
noinst_HEADERS += contrib/lax_der_privatekey_parsing.c

if USE_EXTERNAL_ASM
COMMON_LIB = libsecp256k1_common.la
noinst_LTLIBRARIES = $(COMMON_LIB)
else
COMMON_LIB =
endif

pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsecp256k1.pc

if USE_EXTERNAL_ASM
if USE_ASM_ARM
libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s
endif
endif

libsecp256k1_la_SOURCES = src/secp256k1.c
libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
libsecp256k1_la_LIBADD = $(SECP_LIBS)
libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB)

libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c
libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)

noinst_PROGRAMS =
if USE_BENCHMARK
noinst_PROGRAMS += bench_verify bench_sign bench_internal
bench_verify_SOURCES = src/bench_verify.c
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_sign_SOURCES = src/bench_sign.c
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
bench_internal_LDADD = $(SECP_LIBS)
bench_internal_CPPFLAGS = $(SECP_INCLUDES)
bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
endif

if USE_TESTS
noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c
tests_CPPFLAGS = -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS)
tests_CPPFLAGS = -DSECP256K1_BUILD -DVERIFY -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
tests_LDFLAGS = -static
TESTS = tests
endif

JAVAROOT=src/java
JAVAORG=org/bitcoin
JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
JAVA_FILES= \
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
$(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java

if USE_JNI

$(JAVA_GUAVA):
@echo Guava is missing. Fetch it via: \
wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@)
@false

.stamp-java: $(JAVA_FILES)
@echo Compiling $^
$(AM_V_at)$(CLASSPATH_ENV) javac $^
@touch $@

if USE_TESTS

check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
$(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test

endif
endif

if USE_ECMULT_STATIC_PRECOMPUTATION
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
@@ -93,10 +145,10 @@ $(bench_internal_OBJECTS): src/ecmult_static_context.h
src/ecmult_static_context.h: $(gen_context_BIN)
./$(gen_context_BIN)

CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
endif

EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)

if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include

+ 1
- 1
README.md View File

@@ -1,7 +1,7 @@
libsecp256k1
============

[![Build Status](https://travis-ci.org/bitcoin/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin/secp256k1)
[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1)

Optimized C library for EC operations on curve secp256k1.


+ 140
- 0
build-aux/m4/ax_jni_include_dir.m4 View File

@@ -0,0 +1,140 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_JNI_INCLUDE_DIR
#
# DESCRIPTION
#
# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
# programs using the JNI interface.
#
# JNI include directories are usually in the Java distribution. This is
# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
# that order. When this macro completes, a list of directories is left in
# the variable JNI_INCLUDE_DIRS.
#
# Example usage follows:
#
# AX_JNI_INCLUDE_DIR
#
# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
# do
# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
# done
#
# If you want to force a specific compiler:
#
# - at the configure.in level, set JAVAC=yourcompiler before calling
# AX_JNI_INCLUDE_DIR
#
# - at the configure level, setenv JAVAC
#
# Note: This macro can work with the autoconf M4 macros for Java programs.
# This particular macro is not part of the original set of macros.
#
# LICENSE
#
# Copyright (c) 2008 Don Anderson <dda@sleepycat.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.

#serial 10

AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
AC_DEFUN([AX_JNI_INCLUDE_DIR],[

JNI_INCLUDE_DIRS=""

if test "x$JAVA_HOME" != x; then
_JTOPDIR="$JAVA_HOME"
else
if test "x$JAVAC" = x; then
JAVAC=javac
fi
AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
if test "x$_ACJNI_JAVAC" = xno; then
AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
fi
_ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
_JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
fi

case "$host_os" in
darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
_JINC="$_JTOPDIR/Headers";;
*) _JINC="$_JTOPDIR/include";;
esac
_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
_AS_ECHO_LOG([_JINC=$_JINC])

# On Mac OS X 10.6.4, jni.h is a symlink:
# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
# -> ../../CurrentJDK/Headers/jni.h.

AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
[
if test -f "$_JINC/jni.h"; then
ac_cv_jni_header_path="$_JINC"
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
else
_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
if test -f "$_JTOPDIR/include/jni.h"; then
ac_cv_jni_header_path="$_JTOPDIR/include"
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
else
ac_cv_jni_header_path=none
fi
fi
])



# get the likely subdirectories for system specific java includes
case "$host_os" in
bsdi*) _JNI_INC_SUBDIRS="bsdos";;
darwin*) _JNI_INC_SUBDIRS="darwin";;
freebsd*) _JNI_INC_SUBDIRS="freebsd";;
linux*) _JNI_INC_SUBDIRS="linux genunix";;
osf*) _JNI_INC_SUBDIRS="alpha";;
solaris*) _JNI_INC_SUBDIRS="solaris";;
mingw*) _JNI_INC_SUBDIRS="win32";;
cygwin*) _JNI_INC_SUBDIRS="win32";;
*) _JNI_INC_SUBDIRS="genunix";;
esac

if test "x$ac_cv_jni_header_path" != "xnone"; then
# add any subdirectories that are present
for JINCSUBDIR in $_JNI_INC_SUBDIRS
do
if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
fi
done
fi
])

# _ACJNI_FOLLOW_SYMLINKS <path>
# Follows symbolic links on <path>,
# finally setting variable _ACJNI_FOLLOWED
# ----------------------------------------
AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
# find the include directory relative to the javac executable
_cur="$1"
while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
AC_MSG_CHECKING([symlink for $_cur])
_slink=`ls -ld "$_cur" | sed 's/.* -> //'`
case "$_slink" in
/*) _cur="$_slink";;
# 'X' avoids triggering unwanted echo options.
*) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
esac
AC_MSG_RESULT([$_cur])
done
_ACJNI_FOLLOWED="$_cur"
])# _ACJNI

+ 2
- 2
build-aux/m4/bitcoin_secp.m4 View File

@@ -3,13 +3,13 @@ AC_DEFUN([SECP_INT128_CHECK],[
has_int128=$ac_cv_type___int128
])

dnl
dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
AC_MSG_CHECKING(for x86_64 assembly availability)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <stdint.h>]],[[
uint64_t a = 11, tmp;
__asm__ __volatile__("movq $0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
__asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
]])],[has_64bit_asm=yes],[has_64bit_asm=no])
AC_MSG_RESULT([$has_64bit_asm])
])

+ 130
- 16
configure.ac View File

@@ -29,6 +29,7 @@ AC_PROG_CC_C89
if test x"$ac_cv_prog_cc_c89" = x"no"; then
AC_MSG_ERROR([c89 compiler support required])
fi
AM_PROG_AS

case $host_os in
*darwin*)
@@ -93,23 +94,33 @@ AC_ARG_ENABLE(tests,
[use_tests=$enableval],
[use_tests=yes])

AC_ARG_ENABLE(openssl_tests,
AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]),
[enable_openssl_tests=$enableval],
[enable_openssl_tests=auto])

AC_ARG_ENABLE(experimental,
AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]),
[use_experimental=$enableval],
[use_experimental=no])

AC_ARG_ENABLE(endomorphism,
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
[use_endomorphism=$enableval],
[use_endomorphism=no])
AC_ARG_ENABLE(ecmult_static_precomputation,
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]),
[use_ecmult_static_precomputation=$enableval],
[use_ecmult_static_precomputation=yes])
[use_ecmult_static_precomputation=auto])

AC_ARG_ENABLE(module_ecdh,
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (default is no)]),
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]),
[enable_module_ecdh=$enableval],
[enable_module_ecdh=no])

AC_ARG_ENABLE(module_schnorr,
AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]),
AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (experimental)]),
[enable_module_schnorr=$enableval],
[enable_module_schnorr=no])

@@ -118,6 +129,11 @@ AC_ARG_ENABLE(module_recovery,
[enable_module_recovery=$enableval],
[enable_module_recovery=no])

AC_ARG_ENABLE(jni,
AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
[use_jni=$enableval],
[use_jni=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])

@@ -127,8 +143,8 @@ AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|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])

AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|no|auto]
[Specify assembly optimizations to use. Default is auto])],[req_asm=$withval], [req_asm=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])

AC_CHECK_TYPES([__int128])

@@ -138,6 +154,34 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
[ AC_MSG_RESULT([no])
])

if test x"$use_ecmult_static_precomputation" != x"no"; then
save_cross_compiling=$cross_compiling
cross_compiling=no
TEMP_CC="$CC"
CC="$CC_FOR_BUILD"
AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
AC_RUN_IFELSE(
[AC_LANG_PROGRAM([], [return 0])],
[working_native_cc=yes],
[working_native_cc=no],[dnl])
CC="$TEMP_CC"
cross_compiling=$save_cross_compiling

if test x"$working_native_cc" = x"no"; then
set_precomp=no
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])
else
AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
fi
else
AC_MSG_RESULT([ok])
set_precomp=yes
fi
else
set_precomp=no
fi

if test x"$req_asm" = x"auto"; then
SECP_64BIT_ASM_CHECK
if test x"$has_64bit_asm" = x"yes"; then
@@ -155,6 +199,8 @@ else
AC_MSG_ERROR([x86_64 assembly optimization requested but not available])
fi
;;
arm)
;;
no)
;;
*)
@@ -247,10 +293,15 @@ else
fi

# select assembly optimization
use_external_asm=no

case $set_asm in
x86_64)
AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations])
;;
arm)
use_external_asm=yes
;;
no)
;;
*)
@@ -305,16 +356,51 @@ esac
if test x"$use_tests" = x"yes"; then
SECP_OPENSSL_CHECK
if test x"$has_openssl_ec" = x"yes"; then
AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
SECP_TEST_LIBS="$CRYPTO_LIBS"

case $host in
*mingw*)
SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
;;
esac
if test x"$enable_openssl_tests" != x"no"; then
AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
SECP_TEST_LIBS="$CRYPTO_LIBS"

case $host in
*mingw*)
SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
;;
esac
fi
else
if test x"$enable_openssl_tests" = x"yes"; then
AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available])
fi
fi
else
if test x"$enable_openssl_tests" = x"yes"; then
AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled])
fi
fi

if test x"$use_jni" != x"no"; then
AX_JNI_INCLUDE_DIR
have_jni_dependencies=yes
if test x"$enable_module_schnorr" = x"no"; then
have_jni_dependencies=no
fi
if test x"$enable_module_ecdh" = x"no"; then
have_jni_dependencies=no
fi
if test "x$JNI_INCLUDE_DIRS" = "x"; then
have_jni_dependencies=no
fi
if test "x$have_jni_dependencies" = "xno"; then
if test x"$use_jni" = x"yes"; then
AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and Schnorr and try again.])
fi
AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
use_jni=no
else
use_jni=yes
for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR"
done
fi
fi

@@ -345,18 +431,43 @@ fi

AC_C_BIGENDIAN()

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])
fi

AC_MSG_NOTICE([Using static precomputation: $set_precomp])
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
AC_MSG_NOTICE([Using field implementation: $set_field])
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 ECDH module: $enable_module_ecdh])

AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
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
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_schnorr" = x"yes"; then
AC_MSG_ERROR([Schnorr signature module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi
if test x"$set_asm" = x"arm"; then
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
fi
fi

AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
AC_SUBST(JNI_INCLUDES)
AC_SUBST(SECP_INCLUDES)
AC_SUBST(SECP_LIBS)
AC_SUBST(SECP_TEST_LIBS)
@@ -367,6 +478,9 @@ AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_pr
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = 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_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])

dnl make sure nothing new is exported so that we don't break the cache
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"

+ 1
- 1
libsecp256k1.pc.in View File

@@ -5,7 +5,7 @@ includedir=@includedir@

Name: libsecp256k1
Description: Optimized C library for EC operations on curve secp256k1
URL: https://github.com/bitcoin/secp256k1
URL: https://github.com/bitcoin-core/secp256k1
Version: @PACKAGE_VERSION@
Cflags: -I${includedir}
Libs.private: @SECP_LIBS@

+ 322
- 0
sage/group_prover.sage View File

@@ -0,0 +1,322 @@
# This code supports verifying group implementations which have branches
# or conditional statements (like cmovs), by allowing each execution path
# to independently set assumptions on input or intermediary variables.
#
# The general approach is:
# * A constraint is a tuple of two sets of of symbolic expressions:
# the first of which are required to evaluate to zero, the second of which
# are required to evaluate to nonzero.
# - A constraint is said to be conflicting if any of its nonzero expressions
# is in the ideal with basis the zero expressions (in other words: when the
# zero expressions imply that one of the nonzero expressions are zero).
# * There is a list of laws that describe the intended behaviour, including
# laws for addition and doubling. Each law is called with the symbolic point
# coordinates as arguments, and returns:
# - A constraint describing the assumptions under which it is applicable,
# called "assumeLaw"
# - A constraint describing the requirements of the law, called "require"
# * Implementations are transliterated into functions that operate as well on
# algebraic input points, and are called once per combination of branches
# exectured. Each execution returns:
# - A constraint describing the assumptions this implementation requires
# (such as Z1=1), called "assumeFormula"
# - A constraint describing the assumptions this specific branch requires,
# but which is by construction guaranteed to cover the entire space by
# merging the results from all branches, called "assumeBranch"
# - The result of the computation
# * All combinations of laws with implementation branches are tried, and:
# - If the combination of assumeLaw, assumeFormula, and assumeBranch results
# in a conflict, it means this law does not apply to this branch, and it is
# skipped.
# - For others, we try to prove the require constraints hold, assuming the
# information in assumeLaw + assumeFormula + assumeBranch, and if this does
# not succeed, we fail.
# + To prove an expression is zero, we check whether it belongs to the
# ideal with the assumed zero expressions as basis. This test is exact.
# + To prove an expression is nonzero, we check whether each of its
# factors is contained in the set of nonzero assumptions' factors.
# This test is not exact, so various combinations of original and
# reduced expressions' factors are tried.
# - If we succeed, we print out the assumptions from assumeFormula that
# weren't implied by assumeLaw already. Those from assumeBranch are skipped,
# as we assume that all constraints in it are complementary with each other.
#
# Based on the sage verification scripts used in the Explicit-Formulas Database
# by Tanja Lange and others, see http://hyperelliptic.org/EFD

class fastfrac:
"""Fractions over rings."""

def __init__(self,R,top,bot=1):
"""Construct a fractional, given a ring, a numerator, and denominator."""
self.R = R
if parent(top) == ZZ or parent(top) == R:
self.top = R(top)
self.bot = R(bot)
elif top.__class__ == fastfrac:
self.top = top.top
self.bot = top.bot * bot
else:
self.top = R(numerator(top))
self.bot = R(denominator(top)) * bot

def iszero(self,I):
"""Return whether this fraction is zero given an ideal."""
return self.top in I and self.bot not in I

def reduce(self,assumeZero):
zero = self.R.ideal(map(numerator, assumeZero))
return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))

def __add__(self,other):
"""Add two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top + self.bot * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot)
return NotImplemented

def __sub__(self,other):
"""Subtract two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top - self.bot * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot)
return NotImplemented

def __neg__(self):
"""Return the negation of a fraction."""
return fastfrac(self.R,-self.top,self.bot)

def __mul__(self,other):
"""Multiply two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.top,self.bot * other.bot)
return NotImplemented

def __rmul__(self,other):
"""Multiply something else with a fraction."""
return self.__mul__(other)

def __div__(self,other):
"""Divide two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top,self.bot * other)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot,self.bot * other.top)
return NotImplemented

def __pow__(self,other):
"""Compute a power of a fraction."""
if parent(other) == ZZ:
if other < 0:
# Negative powers require flipping top and bottom
return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other))
else:
return fastfrac(self.R,self.top ^ other,self.bot ^ other)
return NotImplemented

def __str__(self):
return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))"
def __repr__(self):
return "%s" % self

def numerator(self):
return self.top

class constraints:
"""A set of constraints, consisting of zero and nonzero expressions.

Constraints can either be used to express knowledge or a requirement.

Both the fields zero and nonzero are maps from expressions to description
strings. The expressions that are the keys in zero are required to be zero,
and the expressions that are the keys in nonzero are required to be nonzero.

Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in
nonzero could be multiplied into a single key. This is often much less
efficient to work with though, so we keep them separate inside the
constraints. This allows higher-level code to do fast checks on the individual
nonzero elements, or combine them if needed for stronger checks.

We can't multiply the different zero elements, as it would suffice for one of
the factors to be zero, instead of all of them. Instead, the zero elements are
typically combined into an ideal first.
"""

def __init__(self, **kwargs):
if 'zero' in kwargs:
self.zero = dict(kwargs['zero'])
else:
self.zero = dict()
if 'nonzero' in kwargs:
self.nonzero = dict(kwargs['nonzero'])
else:
self.nonzero = dict()

def negate(self):
return constraints(zero=self.nonzero, nonzero=self.zero)

def __add__(self, other):
zero = self.zero.copy()
zero.update(other.zero)
nonzero = self.nonzero.copy()
nonzero.update(other.nonzero)
return constraints(zero=zero, nonzero=nonzero)

def __str__(self):
return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero)

def __repr__(self):
return "%s" % self


def conflicts(R, con):
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
zero = R.ideal(map(numerator, con.zero))
if 1 in zero:
return True
# First a cheap check whether any of the individual nonzero terms conflict on
# their own.
for nonzero in con.nonzero:
if nonzero.iszero(zero):
return True
# It can be the case that entries in the nonzero set do not individually
# conflict with the zero set, but their combination does. For example, knowing
# that either x or y is zero is equivalent to having x*y in the zero set.
# Having x or y individually in the nonzero set is not a conflict, but both
# simultaneously is, so that is the right thing to check for.
if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero):
return True
return False


def get_nonzero_set(R, assume):
"""Calculate a simple set of nonzero expressions"""
zero = R.ideal(map(numerator, assume.zero))
nonzero = set()
for nz in map(numerator, assume.nonzero):
for (f,n) in nz.factor():
nonzero.add(f)
rnz = zero.reduce(nz)
for (f,n) in rnz.factor():
nonzero.add(f)
return nonzero


def prove_nonzero(R, exprs, assume):
"""Check whether an expression is provably nonzero, given assumptions"""
zero = R.ideal(map(numerator, assume.zero))
nonzero = get_nonzero_set(R, assume)
expl = set()
ok = True
for expr in exprs:
if numerator(expr) in zero:
return (False, [exprs[expr]])
allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
for (f, n) in allexprs.factor():
if f not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for (f, n) in zero.reduce(numerator(allexprs)).factor():
if f not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for expr in exprs:
for (f,n) in numerator(expr).factor():
if f not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for expr in exprs:
for (f,n) in zero.reduce(numerator(expr)).factor():
if f not in nonzero:
expl.add(exprs[expr])
if expl:
return (False, list(expl))
else:
return (True, None)


def prove_zero(R, exprs, assume):
"""Check whether all of the passed expressions are provably zero, given assumptions"""
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
if not r:
return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
zero = R.ideal(map(numerator, assume.zero))
nonzero = prod(x for x in assume.nonzero)
expl = []
for expr in exprs:
if not expr.iszero(zero):
expl.append(exprs[expr])
if not expl:
return (True, None)
return (False, expl)


def describe_extra(R, assume, assumeExtra):
"""Describe what assumptions are added, given existing assumptions"""
zerox = assume.zero.copy()
zerox.update(assumeExtra.zero)
zero = R.ideal(map(numerator, assume.zero))
zeroextra = R.ideal(map(numerator, zerox))
nonzero = get_nonzero_set(R, assume)
ret = set()
# Iterate over the extra zero expressions
for base in assumeExtra.zero:
if base not in zero:
add = []
for (f, n) in numerator(base).factor():
if f not in nonzero:
add += ["%s" % f]
if add:
ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
# Iterate over the extra nonzero expressions
for nz in assumeExtra.nonzero:
nzr = zeroextra.reduce(numerator(nz))
if nzr not in zeroextra:
for (f,n) in nzr.factor():
if zeroextra.reduce(f) not in nonzero:
ret.add("%s != 0" % zeroextra.reduce(f))
return ", ".join(x for x in ret)


def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require):
"""Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions"""
assume = assumeLaw + assumeAssert + assumeBranch

if conflicts(R, assume):
# This formula does not apply
return None

describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)

ok, msg = prove_zero(R, require.zero, assume)
if not ok:
return "FAIL, %s fails (assuming %s)" % (str(msg), describe)

res, expl = prove_nonzero(R, require.nonzero, assume)
if not res:
return "FAIL, %s fails (assuming %s)" % (str(expl), describe)

if describe != "":
return "OK (assuming %s)" % describe
else:
return "OK"


def concrete_verify(c):
for k in c.zero:
if k != 0:
return (False, c.zero[k])
for k in c.nonzero:
if k == 0:
return (False, c.nonzero[k])
return (True, None)

+ 306
- 0
sage/secp256k1.sage View File

@@ -0,0 +1,306 @@
# Test libsecp256k1' group operation implementations using prover.sage

import sys

load("group_prover.sage")
load("weierstrass_prover.sage")

def formula_secp256k1_gej_double_var(a):
"""libsecp256k1's secp256k1_gej_double_var, used by various addition functions"""
rz = a.Z * a.Y
rz = rz * 2
t1 = a.X^2
t1 = t1 * 3
t2 = t1^2
t3 = a.Y^2
t3 = t3 * 2
t4 = t3^2
t4 = t4 * 2
t3 = t3 * a.X
rx = t3
rx = rx * 4
rx = -rx
rx = rx + t2
t2 = -t2
t3 = t3 * 6
t3 = t3 + t2
ry = t1 * t3
t2 = -t4
ry = ry + t2
return jacobianpoint(rx, ry, rz)

def formula_secp256k1_gej_add_var(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_var"""
if branch == 0:
return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
if branch == 1:
return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
z22 = b.Z^2
z12 = a.Z^2
u1 = a.X * z22
u2 = b.X * z12
s1 = a.Y * z22
s1 = s1 * b.Z
s2 = b.Y * z12
s2 = s2 * a.Z
h = -u1
h = h + u2
i = -s1
i = i + s2
if branch == 2:
r = formula_secp256k1_gej_double_var(a)
return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r)
if branch == 3:
return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity())
i2 = i^2
h2 = h^2
h3 = h2 * h
h = h * b.Z
rz = a.Z * h
t = u1 * h2
rx = t
rx = rx * 2
rx = rx + h3
rx = -rx
rx = rx + i2
ry = -rx
ry = ry + t
ry = ry * i
h3 = h3 * s1
h3 = -h3
ry = ry + h3
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))

def formula_secp256k1_gej_add_ge_var(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1"""
if branch == 0:
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
if branch == 1:
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
z12 = a.Z^2
u1 = a.X
u2 = b.X * z12
s1 = a.Y
s2 = b.Y * z12
s2 = s2 * a.Z
h = -u1
h = h + u2
i = -s1
i = i + s2
if (branch == 2):
r = formula_secp256k1_gej_double_var(a)
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
if (branch == 3):
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
i2 = i^2
h2 = h^2
h3 = h * h2
rz = a.Z * h
t = u1 * h2
rx = t
rx = rx * 2
rx = rx + h3
rx = -rx
rx = rx + i2
ry = -rx
ry = ry + t
ry = ry * i
h3 = h3 * s1
h3 = -h3
ry = ry + h3
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))

def formula_secp256k1_gej_add_zinv_var(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_zinv_var"""
bzinv = b.Z^(-1)
if branch == 0:
return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a)
if branch == 1:
bzinv2 = bzinv^2
bzinv3 = bzinv2 * bzinv
rx = b.X * bzinv2
ry = b.Y * bzinv3
rz = 1
return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz))
azz = a.Z * bzinv
z12 = azz^2
u1 = a.X
u2 = b.X * z12
s1 = a.Y
s2 = b.Y * z12
s2 = s2 * azz
h = -u1
h = h + u2
i = -s1
i = i + s2
if branch == 2:
r = formula_secp256k1_gej_double_var(a)
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
if branch == 3:
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
i2 = i^2
h2 = h^2
h3 = h * h2
rz = a.Z
rz = rz * h
t = u1 * h2
rx = t
rx = rx * 2
rx = rx + h3
rx = -rx
rx = rx + i2
ry = -rx
ry = ry + t
ry = ry * i
h3 = h3 * s1
h3 = -h3
ry = ry + h3
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))

def formula_secp256k1_gej_add_ge(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_ge"""
zeroes = {}
nonzeroes = {}
a_infinity = False
if (branch & 4) != 0:
nonzeroes.update({a.Infinity : 'a_infinite'})
a_infinity = True
else:
zeroes.update({a.Infinity : 'a_finite'})
zz = a.Z^2
u1 = a.X
u2 = b.X * zz
s1 = a.Y
s2 = b.Y * zz
s2 = s2 * a.Z
t = u1
t = t + u2
m = s1
m = m + s2
rr = t^2
m_alt = -u2
tt = u1 * m_alt
rr = rr + tt
degenerate = (branch & 3) == 3
if (branch & 1) != 0:
zeroes.update({m : 'm_zero'})
else:
nonzeroes.update({m : 'm_nonzero'})
if (branch & 2) != 0:
zeroes.update({rr : 'rr_zero'})
else:
nonzeroes.update({rr : 'rr_nonzero'})
rr_alt = s1
rr_alt = rr_alt * 2
m_alt = m_alt + u1
if not degenerate:
rr_alt = rr
m_alt = m
n = m_alt^2
q = n * t
n = n^2
if degenerate:
n = m
t = rr_alt^2
rz = a.Z * m_alt
infinity = False
if (branch & 8) != 0:
if not a_infinity:
infinity = True
zeroes.update({rz : 'r.z=0'})
else:
nonzeroes.update({rz : 'r.z!=0'})
rz = rz * 2
q = -q
t = t + q
rx = t
t = t * 2
t = t + q
t = t * rr_alt
t = t + n
ry = -t
rx = rx * 4
ry = ry * 4
if a_infinity:
rx = b.X
ry = b.Y
rz = 1
if infinity:
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity())
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz))

def formula_secp256k1_gej_add_ge_old(branch, a, b):
"""libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx"""
a_infinity = (branch & 1) != 0
zero = {}
nonzero = {}
if a_infinity:
nonzero.update({a.Infinity : 'a_infinite'})
else:
zero.update({a.Infinity : 'a_finite'})
zz = a.Z^2
u1 = a.X
u2 = b.X * zz
s1 = a.Y
s2 = b.Y * zz
s2 = s2 * a.Z
z = a.Z
t = u1
t = t + u2
m = s1
m = m + s2
n = m^2
q = n * t
n = n^2
rr = t^2
t = u1 * u2
t = -t
rr = rr + t
t = rr^2
rz = m * z
infinity = False
if (branch & 2) != 0:
if not a_infinity:
infinity = True
else:
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity())
zero.update({rz : 'r.z=0'})
else:
nonzero.update({rz : 'r.z!=0'})
rz = rz * (0 if a_infinity else 2)
rx = t
q = -q
rx = rx + q
q = q * 3
t = t * 2
t = t + q
t = t * rr
t = t + n
ry = -t
rx = rx * (0 if a_infinity else 4)
ry = ry * (0 if a_infinity else 4)
t = b.X
t = t * (1 if a_infinity else 0)
rx = rx + t
t = b.Y
t = t * (1 if a_infinity else 0)
ry = ry + t
t = (1 if a_infinity else 0)
rz = rz + t
if infinity:
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity())
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz))

if __name__ == "__main__":
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)

if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive":
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)

+ 264
- 0
sage/weierstrass_prover.sage View File

@@ -0,0 +1,264 @@
# Prover implementation for Weierstrass curves of the form
# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws
# operating on affine and Jacobian coordinates, including the point at infinity
# represented by a 4th variable in coordinates.

load("group_prover.sage")


class affinepoint:
def __init__(self, x, y, infinity=0):
self.x = x
self.y = y
self.infinity = infinity
def __str__(self):
return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity)


class jacobianpoint:
def __init__(self, x, y, z, infinity=0):
self.X = x
self.Y = y
self.Z = z
self.Infinity = infinity
def __str__(self):
return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity)


def point_at_infinity():
return jacobianpoint(1, 1, 1, 1)


def negate(p):
if p.__class__ == affinepoint:
return affinepoint(p.x, -p.y)
if p.__class__ == jacobianpoint:
return jacobianpoint(p.X, -p.Y, p.Z)
assert(False)


def on_weierstrass_curve(A, B, p):
"""Return a set of zero-expressions for an affine point to be on the curve"""
return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'})


def tangential_to_weierstrass_curve(A, B, p12, p3):
"""Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)"""
return constraints(zero={
(p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve'
})


def colinear(p1, p2, p3):
"""Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear"""
return constraints(zero={
(p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1',
(p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2',
(p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3'
})


def good_affine_point(p):
return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'})


def good_jacobian_point(p):
return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'})


def good_point(p):
return constraints(nonzero={p.Z^6 : 'nonzero_X'})


def finite(p, *affine_fns):
con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'})
if p.Z != 0:
return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con)
else:
return con

def infinite(p):
return constraints(nonzero={p.Infinity : 'infinite_point'})


def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC):
"""Check whether the passed set of coordinates is a valid Jacobian add, given assumptions"""
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
on_weierstrass_curve(A, B, pb) +
finite(pA) +
finite(pB) +
constraints(nonzero={pa.x - pb.x : 'different_x'}))
require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
colinear(pa, pb, negate(pc))))
return (assumeLaw, require)


def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC):
"""Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions"""
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
on_weierstrass_curve(A, B, pb) +
finite(pA) +
finite(pB) +
constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'}))
require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
tangential_to_weierstrass_curve(A, B, pa, negate(pc))))
return (assumeLaw, require)


def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
on_weierstrass_curve(A, B, pb) +
finite(pA) +
finite(pB) +
constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'}))
require = infinite(pC)
return (assumeLaw, require)


def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pb) +
infinite(pA) +
finite(pB))
require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'}))
return (assumeLaw, require)


def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
infinite(pB) +
finite(pA))
require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'}))
return (assumeLaw, require)


def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
infinite(pA) +
infinite(pB))
require = infinite(pC)
return (assumeLaw, require)


laws_jacobian_weierstrass = {
'add': law_jacobian_weierstrass_add,
'double': law_jacobian_weierstrass_double,
'add_opposite': law_jacobian_weierstrass_add_opposites,
'add_infinite_a': law_jacobian_weierstrass_add_infinite_a,
'add_infinite_b': law_jacobian_weierstrass_add_infinite_b,
'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab
}


def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
"""Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
F = Integers(p)
print "Formula %s on Z%i:" % (name, p)
points = []
for x in xrange(0, p):
for y in xrange(0, p):
point = affinepoint(F(x), F(y))
r, e = concrete_verify(on_weierstrass_curve(A, B, point))
if r:
points.append(point)

for za in xrange(1, p):
for zb in xrange(1, p):
for pa in points:
for pb in points:
for ia in xrange(2):
for ib in xrange(2):
pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
for branch in xrange(0, branches):
assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
pC.X = F(pC.X)
pC.Y = F(pC.Y)
pC.Z = F(pC.Z)
pC.Infinity = F(pC.Infinity)
r, e = concrete_verify(assumeAssert + assumeBranch)
if r:
match = False
for key in laws_jacobian_weierstrass:
assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC)
r, e = concrete_verify(assumeLaw)
if r:
if match:
print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)
else:
match = True
r, e = concrete_verify(require)
if not r:
print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)
print


def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
assumeLaw, require = f(A, B, pa, pb, pA, pB, pC)
return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require)

def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
"""Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically"""
R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
lift = lambda x: fastfrac(R,x)
ax = lift(ax)
ay = lift(ay)
Az = lift(Az)
bx = lift(bx)
by = lift(by)
Bz = lift(Bz)
Ai = lift(Ai)
Bi = lift(Bi)

pa = affinepoint(ax, ay, Ai)
pb = affinepoint(bx, by, Bi)
pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai)
pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi)

res = {}

for key in laws_jacobian_weierstrass:
res[key] = []

print ("Formula " + name + ":")
count = 0
for branch in xrange(branches):
assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
pC.X = lift(pC.X)
pC.Y = lift(pC.Y)
pC.Z = lift(pC.Z)
pC.Infinity = lift(pC.Infinity)

for key in laws_jacobian_weierstrass:
res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))

for key in res:
print " %s:" % key
val = res[key]
for x in val:
if x[0] is not None:
print " branch %i: %s" % (x[1], x[0])

print

+ 919
- 0
src/asm/field_10x26_arm.s View File

@@ -0,0 +1,919 @@
@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm:
/**********************************************************************
* Copyright (c) 2014 Wladimir J. van der Laan *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
/*
ARM implementation of field_10x26 inner loops.

Note:

- To avoid unnecessary loads and make use of available registers, two
'passes' have every time been interleaved, with the odd passes accumulating c' and d'
which will be added to c and d respectively in the the even passes

*/

.syntax unified
.arch armv7-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 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
.eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed
.eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
.text

@ Field constants
.set field_R0, 0x3d10
.set field_R1, 0x400
.set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff

.align 2
.global secp256k1_fe_mul_inner
.type secp256k1_fe_mul_inner, %function
@ Arguments:
@ r0 r Restrict: can overlap with a, not with b
@ r1 a
@ r2 b
@ Stack (total 4+10*4 = 44)
@ sp + #0 saved 'r' pointer
@ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
secp256k1_fe_mul_inner:
stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
sub sp, sp, #48 @ frame=44 + alignment
str r0, [sp, #0] @ save result address, we need it only at the end

/******************************************
* Main computation code.
******************************************

Allocation:
r0,r14,r7,r8 scratch
r1 a (pointer)
r2 b (pointer)
r3:r4 c
r5:r6 d
r11:r12 c'
r9:r10 d'

Note: do not write to r[] here, it may overlap with a[]
*/

/* A - interleaved with B */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #9*4] @ b[9]
ldr r0, [r1, #1*4] @ a[1]
umull r5, r6, r7, r8 @ d = a[0] * b[9]
ldr r14, [r2, #8*4] @ b[8]
umull r9, r10, r0, r8 @ d' = a[1] * b[9]
ldr r7, [r1, #2*4] @ a[2]
umlal r5, r6, r0, r14 @ d += a[1] * b[8]
ldr r8, [r2, #7*4] @ b[7]
umlal r9, r10, r7, r14 @ d' += a[2] * b[8]
ldr r0, [r1, #3*4] @ a[3]
umlal r5, r6, r7, r8 @ d += a[2] * b[7]
ldr r14, [r2, #6*4] @ b[6]
umlal r9, r10, r0, r8 @ d' += a[3] * b[7]
ldr r7, [r1, #4*4] @ a[4]
umlal r5, r6, r0, r14 @ d += a[3] * b[6]
ldr r8, [r2, #5*4] @ b[5]
umlal r9, r10, r7, r14 @ d' += a[4] * b[6]
ldr r0, [r1, #5*4] @ a[5]
umlal r5, r6, r7, r8 @ d += a[4] * b[5]
ldr r14, [r2, #4*4] @ b[4]
umlal r9, r10, r0, r8 @ d' += a[5] * b[5]
ldr r7, [r1, #6*4] @ a[6]
umlal r5, r6, r0, r14 @ d += a[5] * b[4]
ldr r8, [r2, #3*4] @ b[3]
umlal r9, r10, r7, r14 @ d' += a[6] * b[4]
ldr r0, [r1, #7*4] @ a[7]
umlal r5, r6, r7, r8 @ d += a[6] * b[3]
ldr r14, [r2, #2*4] @ b[2]
umlal r9, r10, r0, r8 @ d' += a[7] * b[3]
ldr r7, [r1, #8*4] @ a[8]
umlal r5, r6, r0, r14 @ d += a[7] * b[2]
ldr r8, [r2, #1*4] @ b[1]
umlal r9, r10, r7, r14 @ d' += a[8] * b[2]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r8 @ d += a[8] * b[1]
ldr r14, [r2, #0*4] @ b[0]
umlal r9, r10, r0, r8 @ d' += a[9] * b[1]
ldr r7, [r1, #0*4] @ a[0]
umlal r5, r6, r0, r14 @ d += a[9] * b[0]
@ r7,r14 used in B

bic r0, r5, field_not_M @ t9 = d & M
str r0, [sp, #4 + 4*9]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26

/* B */
umull r3, r4, r7, r14 @ c = a[0] * b[0]
adds r5, r5, r9 @ d += d'
adc r6, r6, r10

bic r0, r5, field_not_M @ u0 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u0 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t0 = c & M
str r14, [sp, #4 + 0*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u0 * R1
umlal r3, r4, r0, r14

/* C - interleaved with D */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #2*4] @ b[2]
ldr r14, [r2, #1*4] @ b[1]
umull r11, r12, r7, r8 @ c' = a[0] * b[2]
ldr r0, [r1, #1*4] @ a[1]
umlal r3, r4, r7, r14 @ c += a[0] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r11, r12, r0, r14 @ c' += a[1] * b[1]
ldr r7, [r1, #2*4] @ a[2]
umlal r3, r4, r0, r8 @ c += a[1] * b[0]
ldr r14, [r2, #9*4] @ b[9]
umlal r11, r12, r7, r8 @ c' += a[2] * b[0]
ldr r0, [r1, #3*4] @ a[3]
umlal r5, r6, r7, r14 @ d += a[2] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umull r9, r10, r0, r14 @ d' = a[3] * b[9]
ldr r7, [r1, #4*4] @ a[4]
umlal r5, r6, r0, r8 @ d += a[3] * b[8]
ldr r14, [r2, #7*4] @ b[7]
umlal r9, r10, r7, r8 @ d' += a[4] * b[8]
ldr r0, [r1, #5*4] @ a[5]
umlal r5, r6, r7, r14 @ d += a[4] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r9, r10, r0, r14 @ d' += a[5] * b[7]
ldr r7, [r1, #6*4] @ a[6]
umlal r5, r6, r0, r8 @ d += a[5] * b[6]
ldr r14, [r2, #5*4] @ b[5]
umlal r9, r10, r7, r8 @ d' += a[6] * b[6]
ldr r0, [r1, #7*4] @ a[7]
umlal r5, r6, r7, r14 @ d += a[6] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r9, r10, r0, r14 @ d' += a[7] * b[5]
ldr r7, [r1, #8*4] @ a[8]
umlal r5, r6, r0, r8 @ d += a[7] * b[4]
ldr r14, [r2, #3*4] @ b[3]
umlal r9, r10, r7, r8 @ d' += a[8] * b[4]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r14 @ d += a[8] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r9, r10, r0, r14 @ d' += a[9] * b[3]
umlal r5, r6, r0, r8 @ d += a[9] * b[2]

bic r0, r5, field_not_M @ u1 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u1 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t1 = c & M
str r14, [sp, #4 + 1*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u1 * R1
umlal r3, r4, r0, r14

/* D */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10

bic r0, r5, field_not_M @ u2 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u2 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t2 = c & M
str r14, [sp, #4 + 2*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u2 * R1
umlal r3, r4, r0, r14

/* E - interleaved with F */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #4*4] @ b[4]
umull r11, r12, r7, r8 @ c' = a[0] * b[4]
ldr r8, [r2, #3*4] @ b[3]
umlal r3, r4, r7, r8 @ c += a[0] * b[3]
ldr r7, [r1, #1*4] @ a[1]
umlal r11, r12, r7, r8 @ c' += a[1] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r3, r4, r7, r8 @ c += a[1] * b[2]
ldr r7, [r1, #2*4] @ a[2]
umlal r11, r12, r7, r8 @ c' += a[2] * b[2]
ldr r8, [r2, #1*4] @ b[1]
umlal r3, r4, r7, r8 @ c += a[2] * b[1]
ldr r7, [r1, #3*4] @ a[3]
umlal r11, r12, r7, r8 @ c' += a[3] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r3, r4, r7, r8 @ c += a[3] * b[0]
ldr r7, [r1, #4*4] @ a[4]
umlal r11, r12, r7, r8 @ c' += a[4] * b[0]
ldr r8, [r2, #9*4] @ b[9]
umlal r5, r6, r7, r8 @ d += a[4] * b[9]
ldr r7, [r1, #5*4] @ a[5]
umull r9, r10, r7, r8 @ d' = a[5] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umlal r5, r6, r7, r8 @ d += a[5] * b[8]
ldr r7, [r1, #6*4] @ a[6]
umlal r9, r10, r7, r8 @ d' += a[6] * b[8]
ldr r8, [r2, #7*4] @ b[7]
umlal r5, r6, r7, r8 @ d += a[6] * b[7]
ldr r7, [r1, #7*4] @ a[7]
umlal r9, r10, r7, r8 @ d' += a[7] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r5, r6, r7, r8 @ d += a[7] * b[6]
ldr r7, [r1, #8*4] @ a[8]
umlal r9, r10, r7, r8 @ d' += a[8] * b[6]
ldr r8, [r2, #5*4] @ b[5]
umlal r5, r6, r7, r8 @ d += a[8] * b[5]
ldr r7, [r1, #9*4] @ a[9]
umlal r9, r10, r7, r8 @ d' += a[9] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r5, r6, r7, r8 @ d += a[9] * b[4]

bic r0, r5, field_not_M @ u3 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u3 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t3 = c & M
str r14, [sp, #4 + 3*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u3 * R1
umlal r3, r4, r0, r14

/* F */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10

bic r0, r5, field_not_M @ u4 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u4 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t4 = c & M
str r14, [sp, #4 + 4*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u4 * R1
umlal r3, r4, r0, r14

/* G - interleaved with H */
ldr r7, [r1, #0*4] @ a[0]
ldr r8, [r2, #6*4] @ b[6]
ldr r14, [r2, #5*4] @ b[5]
umull r11, r12, r7, r8 @ c' = a[0] * b[6]
ldr r0, [r1, #1*4] @ a[1]
umlal r3, r4, r7, r14 @ c += a[0] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r11, r12, r0, r14 @ c' += a[1] * b[5]
ldr r7, [r1, #2*4] @ a[2]
umlal r3, r4, r0, r8 @ c += a[1] * b[4]
ldr r14, [r2, #3*4] @ b[3]
umlal r11, r12, r7, r8 @ c' += a[2] * b[4]
ldr r0, [r1, #3*4] @ a[3]
umlal r3, r4, r7, r14 @ c += a[2] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r11, r12, r0, r14 @ c' += a[3] * b[3]
ldr r7, [r1, #4*4] @ a[4]
umlal r3, r4, r0, r8 @ c += a[3] * b[2]
ldr r14, [r2, #1*4] @ b[1]
umlal r11, r12, r7, r8 @ c' += a[4] * b[2]
ldr r0, [r1, #5*4] @ a[5]
umlal r3, r4, r7, r14 @ c += a[4] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r11, r12, r0, r14 @ c' += a[5] * b[1]
ldr r7, [r1, #6*4] @ a[6]
umlal r3, r4, r0, r8 @ c += a[5] * b[0]
ldr r14, [r2, #9*4] @ b[9]
umlal r11, r12, r7, r8 @ c' += a[6] * b[0]
ldr r0, [r1, #7*4] @ a[7]
umlal r5, r6, r7, r14 @ d += a[6] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umull r9, r10, r0, r14 @ d' = a[7] * b[9]
ldr r7, [r1, #8*4] @ a[8]
umlal r5, r6, r0, r8 @ d += a[7] * b[8]
ldr r14, [r2, #7*4] @ b[7]
umlal r9, r10, r7, r8 @ d' += a[8] * b[8]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r14 @ d += a[8] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r9, r10, r0, r14 @ d' += a[9] * b[7]
umlal r5, r6, r0, r8 @ d += a[9] * b[6]

bic r0, r5, field_not_M @ u5 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u5 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t5 = c & M
str r14, [sp, #4 + 5*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u5 * R1
umlal r3, r4, r0, r14

/* H */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10

bic r0, r5, field_not_M @ u6 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u6 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t6 = c & M
str r14, [sp, #4 + 6*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u6 * R1
umlal r3, r4, r0, r14

/* I - interleaved with J */
ldr r8, [r2, #8*4] @ b[8]
ldr r7, [r1, #0*4] @ a[0]
ldr r14, [r2, #7*4] @ b[7]
umull r11, r12, r7, r8 @ c' = a[0] * b[8]
ldr r0, [r1, #1*4] @ a[1]
umlal r3, r4, r7, r14 @ c += a[0] * b[7]
ldr r8, [r2, #6*4] @ b[6]
umlal r11, r12, r0, r14 @ c' += a[1] * b[7]
ldr r7, [r1, #2*4] @ a[2]
umlal r3, r4, r0, r8 @ c += a[1] * b[6]
ldr r14, [r2, #5*4] @ b[5]
umlal r11, r12, r7, r8 @ c' += a[2] * b[6]
ldr r0, [r1, #3*4] @ a[3]
umlal r3, r4, r7, r14 @ c += a[2] * b[5]
ldr r8, [r2, #4*4] @ b[4]
umlal r11, r12, r0, r14 @ c' += a[3] * b[5]
ldr r7, [r1, #4*4] @ a[4]
umlal r3, r4, r0, r8 @ c += a[3] * b[4]
ldr r14, [r2, #3*4] @ b[3]
umlal r11, r12, r7, r8 @ c' += a[4] * b[4]
ldr r0, [r1, #5*4] @ a[5]
umlal r3, r4, r7, r14 @ c += a[4] * b[3]
ldr r8, [r2, #2*4] @ b[2]
umlal r11, r12, r0, r14 @ c' += a[5] * b[3]
ldr r7, [r1, #6*4] @ a[6]
umlal r3, r4, r0, r8 @ c += a[5] * b[2]
ldr r14, [r2, #1*4] @ b[1]
umlal r11, r12, r7, r8 @ c' += a[6] * b[2]
ldr r0, [r1, #7*4] @ a[7]
umlal r3, r4, r7, r14 @ c += a[6] * b[1]
ldr r8, [r2, #0*4] @ b[0]
umlal r11, r12, r0, r14 @ c' += a[7] * b[1]
ldr r7, [r1, #8*4] @ a[8]
umlal r3, r4, r0, r8 @ c += a[7] * b[0]
ldr r14, [r2, #9*4] @ b[9]
umlal r11, r12, r7, r8 @ c' += a[8] * b[0]
ldr r0, [r1, #9*4] @ a[9]
umlal r5, r6, r7, r14 @ d += a[8] * b[9]
ldr r8, [r2, #8*4] @ b[8]
umull r9, r10, r0, r14 @ d' = a[9] * b[9]
umlal r5, r6, r0, r8 @ d += a[9] * b[8]

bic r0, r5, field_not_M @ u7 = d & M
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u7 * R0
umlal r3, r4, r0, r14

bic r14, r3, field_not_M @ t7 = c & M
str r14, [sp, #4 + 7*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u7 * R1
umlal r3, r4, r0, r14

/* J */
adds r3, r3, r11 @ c += c'
adc r4, r4, r12
adds r5, r5, r9 @ d += d'
adc r6, r6, r10

bic r0, r5, field_not_M @ u8 = d & M
str r0, [sp, #4 + 8*4]
mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R0 @ c += u8 * R0
umlal r3, r4, r0, r14

/******************************************
* compute and write back result
******************************************
Allocation:
r0 r
r3:r4 c
r5:r6 d
r7 t0
r8 t1
r9 t2
r11 u8
r12 t9
r1,r2,r10,r14 scratch

Note: do not read from a[] after here, it may overlap with r[]
*/
ldr r0, [sp, #0]
add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
add r1, r0, #3*4
stmia r1, {r2,r7,r8,r9,r10}

bic r2, r3, field_not_M @ r[8] = c & M
str r2, [r0, #8*4]
mov r3, r3, lsr #26 @ c >>= 26
orr r3, r3, r4, asl #6
mov r4, r4, lsr #26
mov r14, field_R1 @ c += u8 * R1
umlal r3, r4, r11, r14
movw r14, field_R0 @ c += d * R0
umlal r3, r4, r5, r14
adds r3, r3, r12 @ c += t9
adc r4, r4, #0

add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
ldmia r1, {r7,r8,r9}

ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
str r2, [r0, #9*4]
mov r3, r3, lsr #22 @ c >>= 22
orr r3, r3, r4, asl #10
mov r4, r4, lsr #22
movw r14, field_R1 << 4 @ c += d * (R1 << 4)
umlal r3, r4, r5, r14

movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
adds r5, r5, r7 @ d.lo += t0
mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
adc r6, r6, 0 @ d.hi += carry

bic r2, r5, field_not_M @ r[0] = d & M
str r2, [r0, #0*4]

mov r5, r5, lsr #26 @ d >>= 26
orr r5, r5, r6, asl #6
mov r6, r6, lsr #26
movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
adds r5, r5, r8 @ d.lo += t1
adc r6, r6, #0 @ d.hi += carry
adds r5, r5, r1 @ d.lo += tmp.lo
mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
adc r6, r6, r2 @ d.hi += carry + tmp.hi

bic r2, r5, field_not_M @ r[1] = d & M
str r2, [r0, #1*4]
mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
orr r5, r5, r6, asl #6

add r5, r5, r9 @ d += t2
str r5, [r0, #2*4] @ r[2] = d

add sp, sp, #48
ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
.size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner

.align 2
.global secp256k1_fe_sqr_inner
.type secp256k1_fe_sqr_inner, %function
@ Arguments:
@ r0 r Can overlap with a
@ r1 a
@ Stack (total 4+10*4 = 44)
@ sp + #0 saved 'r' pointer
@ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
secp256k1_fe_sqr_inner:
stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
sub sp, sp, #48 @ frame=44 + alignment
str r0, [sp, #0] @ save result address, we need it only at the end
/******************************************
* Main computation code.
******************************************

Allocation:
r0,r14,r2,r7,r8 scratch
r1 a (pointer)
r3:r4 c
r5:r6 d
r11:r12 c'
r9:r10 d'

Note: do not write to r[] here, it may overlap with a[]
*/
/* A interleaved with B */
ldr r0, [r1, #1*4] @ a[1]*2
ldr r7, [r1, #0*4] @ a[0]
mov r0, r0, asl #1
ldr r14, [r1, #9*4] @ a[9]
umull r3, r4, r7, r7 @ c = a[0] * a[0]
ldr r8, [r1, #8*4] @ a[8]
mov r7, r7, asl #1
umull r5, r6, r7, r14 @ d = a[0]*2 * a[9]
ldr r7, [r1, #2*4] @ a[2]*2
umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9]
ldr r14, [r1, #7*4] @ a[7]
umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8]
mov r7, r7, asl #1
ldr r0, [r1, #3*4] @ a[3]*2
umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8]
ldr r8, [r1, #6*4] @ a[6]
umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7]
mov r0, r0, asl #1
ldr r7, [r1, #4*4] @ a[4]*2
umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7]