Skip to content

Commit c354ccd

Browse files
committed
Squashed 'src/secp256k1/' changes from 44c2452fd3..74de18c6da
74de18c6da Normalizing field element t in secp256k1_ellswift_decode() 7638661ec2 ElligatorSwift 2a2c15843c Add benchmark for key generation e00770e788 Add x-only ecmult_const version for x=n/d 1e68243a94 doc: Describe Jacobi calculation in safegcd_implementation.md 10bb8bef02 Native jacobi symbol algorithm 9f8a13dc8e Merge bitcoin-core/secp256k1#1128: configure: Remove pkgconfig macros again (reintroduced by mismerge) cabe085bb4 configure: Remove pkgconfig macros again (reintroduced by mismerge) 3efeb9da21 Merge bitcoin-core/secp256k1#1121: config: Set preprocessor defaults for ECMULT_* config values 6a873cc4a9 Merge bitcoin-core/secp256k1#1122: tests: Randomize the context with probability 15/16 instead of 1/4 17065f48ae tests: Randomize the context with probability 15/16 instead of 1/4 c27ae45144 config: Remove basic-config.h da6514a04a config: Introduce DEBUG_CONFIG macro for debug output of config 63a3565e97 Merge bitcoin-core/secp256k1#1120: ecmult_gen: Skip RNG when creating blinding if no seed is available d0cf55e13a config: Set preprocessor defaults for ECMULT_* config values 55f8bc99dc ecmult_gen: Improve comments about projective blinding 7a86955800 ecmult_gen: Simplify code (no observable change) 4cc0b1b669 ecmult_gen: Skip RNG when creating blinding if no seed is available af65d30cc8 Merge bitcoin-core/secp256k1#1116: build: Fix #include "..." paths to get rid of further -I arguments 40a3473a9d build: Fix #include "..." paths to get rid of further -I arguments 43756da819 Merge bitcoin-core/secp256k1#1115: Fix sepc256k1 -> secp256k1 typo in group.h 069aba8125 Fix sepc256k1 -> secp256k1 typo in group.h accadc94df Merge bitcoin-core/secp256k1#1114: `_scratch_destroy`: move `VERIFY_CHECK` after invalid scrach space check cd47033335 Merge bitcoin-core/secp256k1#1084: ci: Add MSVC builds 1827c9bf2b scratch_destroy: move VERIFY_CHECK after invalid scrach space check 49e2acd927 configure: Improve rationale for WERROR_CFLAGS 8dc4b03341 ci: Add a C++ job that compiles the public headers without -fpermissive 51f296a46c ci: Run persistent wineserver to speed up wine 3fb3269c22 ci: Add 32-bit MinGW64 build 9efc2e5221 ci: Add MSVC builds 2be6ba0fed configure: Convince autotools to work with MSVC's archiver lib.exe bd81f4140a schnorrsig bench: Suppress a stupid warning in MSVC 09f3d71c51 configure: Add a few CFLAGS for MSVC 3b4f3d0d46 build: Reject C++ compilers in the preprocessor 1cc0941414 configure: Don't abort if the compiler does not define __STDC__ cca8cbbac8 configure: Output message when checking for valgrind 1a6be5745f bench: Make benchmarks compile on MSVC git-subtree-dir: src/secp256k1 git-subtree-split: 74de18c6da4bdc921916013dfb0e30ad758841ea
1 parent c41bfd1 commit c354ccd

40 files changed

+1667
-106
lines changed

.cirrus.yml

+56-9
Original file line numberDiff line numberDiff line change
@@ -241,17 +241,57 @@ task:
241241
<< : *CAT_LOGS
242242

243243
task:
244-
name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)"
245244
<< : *LINUX_CONTAINER
246245
env:
247-
WRAPPER_CMD: wine64-stable
248-
SECP256K1_TEST_ITERS: 16
249-
HOST: x86_64-w64-mingw32
246+
WRAPPER_CMD: wine
247+
WITH_VALGRIND: no
248+
ECDH: yes
249+
RECOVERY: yes
250+
SCHNORRSIG: yes
251+
CTIMETEST: no
252+
matrix:
253+
- name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)"
254+
env:
255+
HOST: x86_64-w64-mingw32
256+
- name: "i686 (mingw32-w64): Windows (Debian stable, Wine)"
257+
env:
258+
HOST: i686-w64-mingw32
259+
<< : *MERGE_BASE
260+
test_script:
261+
- ./ci/cirrus.sh
262+
<< : *CAT_LOGS
263+
264+
task:
265+
<< : *LINUX_CONTAINER
266+
env:
267+
WRAPPER_CMD: wine
268+
WERROR_CFLAGS: -WX
250269
WITH_VALGRIND: no
251270
ECDH: yes
252271
RECOVERY: yes
272+
EXPERIMENTAL: yes
253273
SCHNORRSIG: yes
254274
CTIMETEST: no
275+
# Set non-essential options that affect the CLI messages here.
276+
# (They depend on the user's taste, so we don't want to set them automatically in configure.ac.)
277+
CFLAGS: -nologo -diagnostics:caret
278+
LDFLAGS: -XCClinker -nologo -XCClinker -diagnostics:caret
279+
# Use a MinGW-w64 host to tell ./configure we're building for Windows.
280+
# This will detect some MinGW-w64 tools but then make will need only
281+
# the MSVC tools CC, AR and NM as specified below.
282+
matrix:
283+
- name: "x86_64 (MSVC): Windows (Debian stable, Wine)"
284+
env:
285+
HOST: x86_64-w64-mingw32
286+
CC: /opt/msvc/bin/x64/cl
287+
AR: /opt/msvc/bin/x64/lib
288+
NM: /opt/msvc/bin/x64/dumpbin -symbols -headers
289+
- name: "i686 (MSVC): Windows (Debian stable, Wine)"
290+
env:
291+
HOST: i686-w64-mingw32
292+
CC: /opt/msvc/bin/x86/cl
293+
AR: /opt/msvc/bin/x86/lib
294+
NM: /opt/msvc/bin/x86/dumpbin -symbols -headers
255295
<< : *MERGE_BASE
256296
test_script:
257297
- ./ci/cirrus.sh
@@ -302,13 +342,12 @@ task:
302342
<< : *CAT_LOGS
303343

304344
task:
305-
name: "C++ -fpermissive"
345+
name: "C++ -fpermissive (entire project)"
306346
<< : *LINUX_CONTAINER
307347
env:
308-
# ./configure correctly errors out when given CC=g++.
309-
# We hack around this by passing CC=g++ only to make.
310-
CC: gcc
311-
MAKEFLAGS: -j4 CC=g++ CFLAGS=-fpermissive\ -g
348+
CC: g++
349+
CFLAGS: -fpermissive -g
350+
CPPFLAGS: -DSECP256K1_CPLUSPLUS_TEST_OVERRIDE
312351
WERROR_CFLAGS:
313352
ECDH: yes
314353
RECOVERY: yes
@@ -318,6 +357,14 @@ task:
318357
- ./ci/cirrus.sh
319358
<< : *CAT_LOGS
320359

360+
task:
361+
name: "C++ (public headers)"
362+
<< : *LINUX_CONTAINER
363+
test_script:
364+
- g++ -Werror include/*.h
365+
- clang -Werror -x c++-header include/*.h
366+
- /opt/msvc/bin/x64/cl.exe -c -WX -TP include/*.h
367+
321368
task:
322369
name: "sage prover"
323370
<< : *LINUX_CONTAINER

Makefile.am

+6-3
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ noinst_HEADERS += src/hash_impl.h
5858
noinst_HEADERS += src/field.h
5959
noinst_HEADERS += src/field_impl.h
6060
noinst_HEADERS += src/bench.h
61-
noinst_HEADERS += src/basic-config.h
6261
noinst_HEADERS += contrib/lax_der_parsing.h
6362
noinst_HEADERS += contrib/lax_der_parsing.c
6463
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
@@ -87,7 +86,7 @@ endif
8786
endif
8887

8988
libsecp256k1_la_SOURCES = src/secp256k1.c
90-
libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
89+
libsecp256k1_la_CPPFLAGS = $(SECP_INCLUDES)
9190
libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB)
9291
libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE)
9392

@@ -112,7 +111,7 @@ TESTS =
112111
if USE_TESTS
113112
noinst_PROGRAMS += tests
114113
tests_SOURCES = src/tests.c
115-
tests_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
114+
tests_CPPFLAGS = $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
116115
if VALGRIND_ENABLED
117116
tests_CPPFLAGS += -DVALGRIND
118117
noinst_PROGRAMS += valgrind_ctime_test
@@ -228,3 +227,7 @@ endif
228227
if ENABLE_MODULE_SCHNORRSIG
229228
include src/modules/schnorrsig/Makefile.am.include
230229
endif
230+
231+
if ENABLE_MODULE_ELLSWIFT
232+
include src/modules/ellswift/Makefile.am.include
233+
endif

build-aux/m4/bitcoin_secp.m4

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ AC_MSG_RESULT([$has_64bit_asm])
1010
])
1111

1212
AC_DEFUN([SECP_VALGRIND_CHECK],[
13+
AC_MSG_CHECKING([for valgrind support])
1314
if test x"$has_valgrind" != x"yes"; then
1415
CPPFLAGS_TEMP="$CPPFLAGS"
1516
CPPFLAGS="$VALGRIND_CPPFLAGS $CPPFLAGS"
@@ -21,6 +22,7 @@ if test x"$has_valgrind" != x"yes"; then
2122
#endif
2223
]])], [has_valgrind=yes; AC_DEFINE(HAVE_VALGRIND,1,[Define this symbol if valgrind is installed, and it supports the host platform])])
2324
fi
25+
AC_MSG_RESULT($has_valgrind)
2426
])
2527

2628
dnl SECP_TRY_APPEND_CFLAGS(flags, VAR)

ci/cirrus.sh

+13
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,20 @@ set -x
55

66
export LC_ALL=C
77

8+
# Start persistent wineserver if necessary.
9+
# This speeds up jobs with many invocations of wine (e.g., ./configure with MSVC) tremendously.
10+
case "$WRAPPER_CMD" in
11+
*wine*)
12+
# This is apparently only reliable when we run a dummy command such as "hh.exe" afterwards.
13+
wineserver -p && wine hh.exe
14+
;;
15+
esac
16+
817
env >> test_env.log
918

1019
$CC -v || true
1120
valgrind --version || true
21+
$WRAPPER_CMD --version || true
1222

1323
./autogen.sh
1424

@@ -63,6 +73,9 @@ then
6373
make precomp
6474
fi
6575

76+
# Shutdown wineserver again
77+
wineserver -k || true
78+
6679
# Check that no repo files have been modified by the build.
6780
# (This fails for example if the precomp files need to be updated in the repo.)
6881
git diff --exit-code

ci/linux-debian.Dockerfile

+21-10
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
FROM debian:stable
22

3-
RUN dpkg --add-architecture i386
4-
RUN dpkg --add-architecture s390x
5-
RUN dpkg --add-architecture armhf
6-
RUN dpkg --add-architecture arm64
7-
RUN dpkg --add-architecture ppc64el
8-
RUN apt-get update
3+
RUN dpkg --add-architecture i386 && \
4+
dpkg --add-architecture s390x && \
5+
dpkg --add-architecture armhf && \
6+
dpkg --add-architecture arm64 && \
7+
dpkg --add-architecture ppc64el
98

109
# dkpg-dev: to make pkg-config work in cross-builds
1110
# llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces
12-
RUN apt-get install --no-install-recommends --no-upgrade -y \
11+
RUN apt-get update && apt-get install --no-install-recommends -y \
1312
git ca-certificates \
1413
make automake libtool pkg-config dpkg-dev valgrind qemu-user \
1514
gcc clang llvm libc6-dbg \
@@ -19,8 +18,20 @@ RUN apt-get install --no-install-recommends --no-upgrade -y \
1918
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \
2019
gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \
2120
gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \
22-
wine gcc-mingw-w64-x86-64 \
21+
gcc-mingw-w64-x86-64-win32 wine64 wine \
22+
gcc-mingw-w64-i686-win32 wine32 \
2323
sagemath
2424

25-
# Run a dummy command in wine to make it set up configuration
26-
RUN wine64-stable xcopy || true
25+
WORKDIR /root
26+
# The "wine" package provides a convience wrapper that we need
27+
RUN apt-get update && apt-get install --no-install-recommends -y \
28+
git ca-certificates wine64 wine python3-simplejson python3-six msitools winbind procps && \
29+
git clone https://github.com/mstorsjo/msvc-wine && \
30+
mkdir /opt/msvc && \
31+
python3 msvc-wine/vsdownload.py --accept-license --dest /opt/msvc Microsoft.VisualStudio.Workload.VCTools && \
32+
msvc-wine/install.sh /opt/msvc
33+
34+
# Initialize the wine environment. Wait until the wineserver process has
35+
# exited before closing the session, to avoid corrupting the wine prefix.
36+
RUN wine64 wineboot --init && \
37+
while (ps -A | grep wineserver) > /dev/null; do sleep 1; done

configure.ac

+48-21
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects])
3333
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
3434

3535
AC_PROG_CC
36-
if test x"$ac_cv_prog_cc_c89" = x"no"; then
37-
AC_MSG_ERROR([c89 compiler support required])
38-
fi
3936
AM_PROG_AS
4037
AM_PROG_AR
4138

39+
# Clear some cache variables as a workaround for a bug that appears due to a bad
40+
# interaction between AM_PROG_AR and LT_INIT when combining MSVC's archiver lib.exe.
41+
# https://debbugs.gnu.org/cgi/bugreport.cgi?bug=54421
42+
AS_UNSET(ac_cv_prog_AR)
43+
AS_UNSET(ac_cv_prog_ac_ct_AR)
4244
LT_INIT([win32-dll])
4345

4446
build_windows=no
@@ -87,23 +89,35 @@ esac
8789
#
8890
# TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues.
8991
AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [
90-
# Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will
91-
# not error out if it gets unknown warning flags and the checks here will always succeed
92-
# no matter if clang knows the flag or not.
93-
SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS"
94-
SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS)
95-
96-
SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
97-
SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
98-
SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers
99-
SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall.
100-
SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
101-
SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95
102-
SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0
103-
SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only
104-
SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0
105-
106-
CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS"
92+
# GCC and compatible (incl. clang)
93+
if test "x$GCC" = "xyes"; then
94+
# Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will
95+
# not error out if it gets unknown warning flags and the checks here will always succeed
96+
# no matter if clang knows the flag or not.
97+
SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS"
98+
SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS)
99+
100+
SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
101+
SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
102+
SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers
103+
SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall.
104+
SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
105+
SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95
106+
SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0
107+
SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only
108+
SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0
109+
110+
CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS"
111+
fi
112+
113+
# MSVC
114+
# Assume MSVC if we're building for Windows but not with GCC or compatible;
115+
# libtool makes the same assumption internally.
116+
# Note that "/opt" and "-opt" are equivalent for MSVC; we use "-opt" because "/opt" looks like a path.
117+
if test x"$GCC" != x"yes" && test x"$build_windows" = x"yes"; then
118+
SECP_TRY_APPEND_CFLAGS([-W2 -wd4146], $1) # Moderate warning level, disable warning C4146 "unary minus operator applied to unsigned type, result still unsigned"
119+
SECP_TRY_APPEND_CFLAGS([-external:anglebrackets -external:W0], $1) # Suppress warnings from #include <...> files
120+
fi
107121
])
108122
SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS)
109123

@@ -156,6 +170,11 @@ AC_ARG_ENABLE(module_schnorrsig,
156170
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=no]]), [],
157171
[SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])])
158172

173+
AC_ARG_ENABLE(module_ellswift,
174+
AS_HELP_STRING([--enable-module-ellswift],[enable ElligatorSwift module (experimental)]),
175+
[enable_module_ellswift=$enableval],
176+
[enable_module_ellswift=no])
177+
159178
AC_ARG_ENABLE(external_default_callbacks,
160179
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
161180
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
@@ -326,7 +345,9 @@ if test x"$enable_valgrind" = x"yes"; then
326345
SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS"
327346
fi
328347

329-
# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI)
348+
# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI).
349+
# We don't want to set the user variable CFLAGS in CI because this would disable
350+
# autoconf's logic for setting default CFLAGS, which we would like to test in CI.
330351
SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS"
331352

332353
###
@@ -346,6 +367,10 @@ if test x"$enable_module_schnorrsig" = x"yes"; then
346367
enable_module_extrakeys=yes
347368
fi
348369

370+
if test x"$enable_module_ellswift" = x"yes"; then
371+
AC_DEFINE(ENABLE_MODULE_ELLSWIFT, 1, [Define this symbol to enable the ElligatorSwift module])
372+
fi
373+
349374
# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig
350375
# module to set enable_module_extrakeys=yes
351376
if test x"$enable_module_extrakeys" = x"yes"; then
@@ -391,6 +416,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
391416
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
392417
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
393418
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
419+
AM_CONDITIONAL([ENABLE_MODULE_ELLSWIFT], [test x"$enable_module_ellswift" = x"yes"])
394420
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"])
395421
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
396422
AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"])
@@ -411,6 +437,7 @@ echo " module ecdh = $enable_module_ecdh"
411437
echo " module recovery = $enable_module_recovery"
412438
echo " module extrakeys = $enable_module_extrakeys"
413439
echo " module schnorrsig = $enable_module_schnorrsig"
440+
echo " module ellswift = $enable_module_ellswift"
414441
echo
415442
echo " asm = $set_asm"
416443
echo " ecmult window size = $set_ecmult_window"

doc/safegcd_implementation.md

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The safegcd implementation in libsecp256k1 explained
22

3-
This document explains the modular inverse implementation in the `src/modinv*.h` files. It is based
4-
on the paper
3+
This document explains the modular inverse and Jacobi symbol implementations in the `src/modinv*.h` files.
4+
It is based on the paper
55
["Fast constant-time gcd computation and modular inversion"](https://gcd.cr.yp.to/papers.html#safegcd)
66
by Daniel J. Bernstein and Bo-Yin Yang. The references below are for the Date: 2019.04.13 version.
77

@@ -769,3 +769,30 @@ def modinv_var(M, Mi, x):
769769
d, e = update_de(d, e, t, M, Mi)
770770
return normalize(f, d, Mi)
771771
```
772+
773+
## 8. From GCDs to Jacobi symbol
774+
775+
We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we make corresponding updates to *j* using [properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties). In particular, we update *j* whenever we divide *g* by *2* or swap *f* and *g*; these updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied very quickly. Overall, this calculation is slightly simpler than the one for modular inverse because we no longer need to keep track of *d* and *e*.
776+
777+
However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative values. We resolve this by using the following modified steps:
778+
779+
```python
780+
# Before
781+
if delta > 0 and g & 1:
782+
delta, f, g = 1 - delta, g, (g - f) // 2
783+
784+
# After
785+
if delta > 0 and g & 1:
786+
delta, f, g = 1 - delta, g, (g + f) // 2
787+
```
788+
789+
The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4 and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm will converge. The justification for posdivsteps is completely empirical: in practice, it appears that the vast majority of inputs converge to *f=g=gcd(f<sub>0</sub>, g<sub>0<sub>)* in a number of steps proportional to their logarithm.
790+
791+
Note that:
792+
- We require inputs to satisfy *gcd(x, M) = 1*.
793+
- We need to update the termination condition from *g=0* to *f=1*.
794+
- We deal with the case where *g=0* on input specially.
795+
796+
We account for the possibility of nonconvergence by only performing a bounded number of posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not yet been found.
797+
798+
The optimizations in sections 3-7 above are described in the context of the original divsteps, but in the C implementation we also adapt most of them (not including "avoiding modulus operations", since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate Jacobi symbols for secret data) to the posdivsteps version.

0 commit comments

Comments
 (0)