Skip to content

Commit e46f81a

Browse files
committed
ElligatorSwift
1 parent d556a9d commit e46f81a

12 files changed

+909
-3
lines changed

.cirrus.yml

+12-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ env:
1818
ECDH: no
1919
RECOVERY: no
2020
SCHNORRSIG: no
21+
ELLSWIFT: no
2122
### test options
2223
SECP256K1_TEST_ITERS:
2324
BENCH: yes
@@ -67,11 +68,11 @@ task:
6768
<< : *LINUX_CONTAINER
6869
matrix: &ENV_MATRIX
6970
- env: {WIDEMUL: int64, RECOVERY: yes}
70-
- env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes}
71+
- env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes, ELLSWIFT: yes}
7172
- env: {WIDEMUL: int128}
72-
- env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes}
73+
- env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes, ELLSWIFT: yes}
7374
- env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes}
74-
- env: {WIDEMUL: int128, ASM: x86_64}
75+
- env: {WIDEMUL: int128, ASM: x86_64 , ELLSWIFT: yes}
7576
- env: { RECOVERY: yes, SCHNORRSIG: yes}
7677
- env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no}
7778
- env: {CPPFLAGS: -DDETERMINISTIC}
@@ -178,6 +179,7 @@ task:
178179
ECDH: yes
179180
RECOVERY: yes
180181
SCHNORRSIG: yes
182+
ELLSWIFT: yes
181183
CTIMETEST: no
182184
<< : *MERGE_BASE
183185
test_script:
@@ -197,6 +199,7 @@ task:
197199
ECDH: yes
198200
RECOVERY: yes
199201
SCHNORRSIG: yes
202+
ELLSWIFT: yes
200203
CTIMETEST: no
201204
matrix:
202205
- env: {}
@@ -217,6 +220,7 @@ task:
217220
ECDH: yes
218221
RECOVERY: yes
219222
SCHNORRSIG: yes
223+
ELLSWIFT: yes
220224
CTIMETEST: no
221225
<< : *MERGE_BASE
222226
test_script:
@@ -234,6 +238,7 @@ task:
234238
ECDH: yes
235239
RECOVERY: yes
236240
SCHNORRSIG: yes
241+
ELLSWIFT: yes
237242
CTIMETEST: no
238243
<< : *MERGE_BASE
239244
test_script:
@@ -271,6 +276,8 @@ task:
271276
RECOVERY: yes
272277
EXPERIMENTAL: yes
273278
SCHNORRSIG: yes
279+
ELLSWIFT: yes
280+
ELLSWIFT: yes
274281
CTIMETEST: no
275282
# Set non-essential options that affect the CLI messages here.
276283
# (They depend on the user's taste, so we don't want to set them automatically in configure.ac.)
@@ -304,6 +311,7 @@ task:
304311
ECDH: yes
305312
RECOVERY: yes
306313
SCHNORRSIG: yes
314+
ELLSWIFT: yes
307315
CTIMETEST: no
308316
matrix:
309317
- name: "Valgrind (memcheck)"
@@ -352,6 +360,7 @@ task:
352360
ECDH: yes
353361
RECOVERY: yes
354362
SCHNORRSIG: yes
363+
ELLSWIFT: yes
355364
<< : *MERGE_BASE
356365
test_script:
357366
- ./ci/cirrus.sh

Makefile.am

+4
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,7 @@ endif
227227
if ENABLE_MODULE_SCHNORRSIG
228228
include src/modules/schnorrsig/Makefile.am.include
229229
endif
230+
231+
if ENABLE_MODULE_ELLSWIFT
232+
include src/modules/ellswift/Makefile.am.include
233+
endif

ci/cirrus.sh

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ $WRAPPER_CMD --version || true
2828
--with-ecmult-window="$ECMULTWINDOW" \
2929
--with-ecmult-gen-precision="$ECMULTGENPRECISION" \
3030
--enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
31+
--enable-module-ellswift="$ELLSWIFT" \
3132
--enable-module-schnorrsig="$SCHNORRSIG" \
3233
--enable-examples="$EXAMPLES" \
3334
--with-valgrind="$WITH_VALGRIND" \

configure.ac

+11
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ AC_ARG_ENABLE(module_schnorrsig,
170170
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=no]]), [],
171171
[SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])])
172172

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+
173178
AC_ARG_ENABLE(external_default_callbacks,
174179
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
175180
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
@@ -362,6 +367,10 @@ if test x"$enable_module_schnorrsig" = x"yes"; then
362367
enable_module_extrakeys=yes
363368
fi
364369

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+
365374
# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig
366375
# module to set enable_module_extrakeys=yes
367376
if test x"$enable_module_extrakeys" = x"yes"; then
@@ -407,6 +416,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
407416
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
408417
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
409418
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"])
410420
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"])
411421
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
412422
AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"])
@@ -427,6 +437,7 @@ echo " module ecdh = $enable_module_ecdh"
427437
echo " module recovery = $enable_module_recovery"
428438
echo " module extrakeys = $enable_module_extrakeys"
429439
echo " module schnorrsig = $enable_module_schnorrsig"
440+
echo " module ellswift = $enable_module_ellswift"
430441
echo
431442
echo " asm = $set_asm"
432443
echo " ecmult window size = $set_ecmult_window"

include/secp256k1_ellswift.h

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#ifndef SECP256K1_ELLSWIFT_H
2+
#define SECP256K1_ELLSWIFT_H
3+
4+
#include "secp256k1.h"
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
/* This module provides an implementation of ElligatorSwift as well as
11+
* a version of x-only ECDH using it.
12+
*
13+
* ElligatorSwift is described in https://eprint.iacr.org/2022/759 by
14+
* Chavez-Saab, Rodriguez-Henriquez, and Tibouchi. It permits encoding
15+
* public keys in 64-byte objects which are indistinguishable from
16+
* uniformly random.
17+
*
18+
* Let f be the function from pairs of field elements to point X coordinates,
19+
* defined as follows (all operations modulo p = 2^256 - 2^32 - 977)
20+
* f(u,t):
21+
* - Let C = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852,
22+
* a square root of -3.
23+
* - If u=0, set u=1 instead.
24+
* - If t=0, set t=1 instead.
25+
* - If u^3 + t^2 + 7 = 0, multiply t by 2.
26+
* - Let p = u^3 + t^2 + 7
27+
* - Let m = u^3 - t^2 + 7
28+
* - Let v = (C * m / p - 1) * u / 2
29+
* - Let w = p / (C * t * u)
30+
* - Let x1 = v
31+
* - Let x2 = -u - v
32+
* - Let x3 = u + w^2
33+
* - Return the first of [x3,x2,x1] that is an X coordinate on the curve
34+
* (at least one of them is, for any inputs u and t).
35+
*
36+
* Then an ElligatorSwift encoding of x consists of the 32-byte big-endian
37+
* encodings of field elements u and t concatenated, where f(u,t) = x.
38+
* The encoding algorithm is described in the paper, and effectively picks a
39+
* uniformly random pair (u,t) among those which encode x.
40+
*
41+
* If the Y coordinate is relevant, it is given the same parity as t.
42+
*
43+
* Changes w.r.t. the the paper:
44+
* - The u=0, t=0, and u^3+t^2+7=0 conditions result in decoding to the point
45+
* at infinity in the paper. Here they are remapped to finite points.
46+
* - The paper uses an additional encoding bit for the parity of y. Here the
47+
* parity of t is used (negating t does not affect the decoded x coordinate,
48+
* so this is possible).
49+
*/
50+
51+
/** A pointer to a function used for hashing the shared X coordinate along
52+
* with the encoded public keys to a uniform shared secret.
53+
*
54+
* Returns: 1 if a shared secret was was successfully computed.
55+
* 0 will cause secp256k1_ellswift_xdh to fail and return 0.
56+
* Other return values are not allowed, and the behaviour of
57+
* secp256k1_ellswift_xdh is undefined for other return values.
58+
* Out: output: pointer to an array to be filled by the function
59+
* In: x32: pointer to the 32-byte serialized X coordinate
60+
* of the resulting shared point
61+
* ours64: pointer to the 64-byte encoded public key we sent
62+
* to the other party
63+
* theirs64: pointer to the 64-byte encoded public key we received
64+
* from the other party
65+
* data: arbitrary data pointer that is passed through
66+
*/
67+
typedef int (*secp256k1_ellswift_xdh_hash_function)(
68+
unsigned char *output,
69+
const unsigned char *x32,
70+
const unsigned char *ours64,
71+
const unsigned char *theirs64,
72+
void *data
73+
);
74+
75+
/** An implementation of an secp256k1_ellswift_xdh_hash_function which uses
76+
* SHA256(key1 || key2 || x32), where (key1, key2) = sorted([ours64, theirs64]), and
77+
* ignores data. The sorting is lexicographic. */
78+
SECP256K1_API extern const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_sha256;
79+
80+
/** A default secp256k1_ellswift_xdh_hash_function, currently secp256k1_ellswift_xdh_hash_function_sha256. */
81+
SECP256K1_API extern const secp256k1_ellswift_xdh_hash_function secp256k1_ellswift_xdh_hash_function_default;
82+
83+
/* Construct a 64-byte ElligatorSwift encoding of a given pubkey.
84+
*
85+
* Returns: 1 when pubkey is valid.
86+
* Args: ctx: pointer to a context object
87+
* Out: ell64: pointer to a 64-byte array to be filled
88+
* In: pubkey: a pointer to a secp256k1_pubkey containing an
89+
* initialized public key
90+
* rnd32: pointer to 32 bytes of entropy (must be unpredictable)
91+
*
92+
* This function runs in variable time.
93+
*/
94+
SECP256K1_API int secp256k1_ellswift_encode(
95+
const secp256k1_context* ctx,
96+
unsigned char *ell64,
97+
const secp256k1_pubkey *pubkey,
98+
const unsigned char *rnd32
99+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
100+
101+
/** Decode a 64-bytes ElligatorSwift encoded public key.
102+
*
103+
* Returns: always 1
104+
* Args: ctx: pointer to a context object
105+
* Out: pubkey: pointer to a secp256k1_pubkey that will be filled
106+
* In: ell64: pointer to a 64-byte array to decode
107+
*
108+
* This function runs in variable time.
109+
*/
110+
SECP256K1_API int secp256k1_ellswift_decode(
111+
const secp256k1_context* ctx,
112+
secp256k1_pubkey *pubkey,
113+
const unsigned char *ell64
114+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
115+
116+
/** Compute an ElligatorSwift public key for a secret key.
117+
*
118+
* Returns: 1: secret was valid, public key was stored.
119+
* 0: secret was invalid, try again.
120+
* Args: ctx: pointer to a context object, initialized for signing.
121+
* Out: ell64: pointer to a 64-byte area to receive the ElligatorSwift public key
122+
* In: seckey32: pointer to a 32-byte secret key.
123+
* auxrand32: (optional) pointer to 32 bytes of additional randomness
124+
*
125+
* Constant time in seckey and auxrand32, but not in the resulting public key.
126+
*
127+
* This function can be used instead of calling secp256k1_ec_pubkey_create followed
128+
* by secp256k1_ellswift_encode. It is safer, as it can use the secret key as
129+
* entropy for the encoding. That means that if the secret key itself is
130+
* unpredictable, no additional auxrand32 is needed to achieve indistinguishability
131+
* of the encoding.
132+
*/
133+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_create(
134+
const secp256k1_context* ctx,
135+
unsigned char *ell64,
136+
const unsigned char *seckey32,
137+
const unsigned char *auxrand32
138+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
139+
140+
/** Given a private key, and ElligatorSwift public keys sent in both directions,
141+
* compute a shared secret using x-only Diffie-Hellman.
142+
*
143+
* Returns: 1: shared secret was succesfully computed
144+
* 0: secret was invalid or hashfp returned 0
145+
* Args: ctx: pointer to a context object.
146+
* Out: output: pointer to an array to be filled by hashfp.
147+
* In: theirs64: a pointer to the 64-byte ElligatorSquare public key received from the other party.
148+
* ours64: a pointer to the 64-byte ElligatorSquare public key sent to the other party.
149+
* seckey32: a pointer to the 32-byte private key corresponding to ours64.
150+
* hashfp: pointer to a hash function. If NULL,
151+
* secp256k1_elswift_xdh_hash_function_default is used
152+
* (in which case, 32 bytes will be written to output).
153+
* data: arbitrary data pointer that is passed through to hashfp
154+
* (ignored for secp256k1_ellswift_xdh_hash_function_default).
155+
*
156+
* Constant time in seckey32.
157+
*
158+
* This function is more efficient than decoding the public keys, and performing ECDH on them.
159+
*/
160+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ellswift_xdh(
161+
const secp256k1_context* ctx,
162+
unsigned char *output,
163+
const unsigned char* theirs64,
164+
const unsigned char* ours64,
165+
const unsigned char* seckey32,
166+
secp256k1_ellswift_xdh_hash_function hashfp,
167+
void *data
168+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
169+
170+
171+
#ifdef __cplusplus
172+
}
173+
#endif
174+
175+
#endif /* SECP256K1_ELLSWIFT_H */

src/bench.c

+9
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ static void bench_keygen_run(void* arg, int iters) {
149149
# include "modules/schnorrsig/bench_impl.h"
150150
#endif
151151

152+
#ifdef ENABLE_MODULE_ELLSWIFT
153+
# include "modules/ellswift/bench_impl.h"
154+
#endif
155+
152156
int main(int argc, char** argv) {
153157
int i;
154158
secp256k1_pubkey pubkey;
@@ -247,5 +251,10 @@ int main(int argc, char** argv) {
247251
run_schnorrsig_bench(iters, argc, argv);
248252
#endif
249253

254+
#ifdef ENABLE_MODULE_ELLSWIFT
255+
/* ElligatorSwift benchmarks */
256+
run_ellswift_bench(iters, argc, argv);
257+
#endif
258+
250259
return 0;
251260
}
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
include_HEADERS += include/secp256k1_ellswift.h
2+
noinst_HEADERS += src/modules/ellswift/bench_impl.h
3+
noinst_HEADERS += src/modules/ellswift/main_impl.h
4+
noinst_HEADERS += src/modules/ellswift/tests_impl.h

0 commit comments

Comments
 (0)