Skip to content

Commit fcd4abf

Browse files
committed
Elligator Squared module
This adds a module with an implementation of the Elligator Squared algorithm for encoding/decoding public keys in uniformly random byte arrays.
1 parent 7e1bbef commit fcd4abf

File tree

10 files changed

+656
-0
lines changed

10 files changed

+656
-0
lines changed

Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,7 @@ endif
228228
if ENABLE_MODULE_SCHNORRSIG
229229
include src/modules/schnorrsig/Makefile.am.include
230230
endif
231+
232+
if ENABLE_MODULE_ELLSQ
233+
include src/modules/ellsq/Makefile.am.include
234+
endif

configure.ac

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ AC_ARG_ENABLE(module_schnorrsig,
156156
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=no]]), [],
157157
[SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])])
158158

159+
AC_ARG_ENABLE(module_ellsq,
160+
AS_HELP_STRING([--enable-module-ellsq],[enable Elligator^2 module (experimental)]),
161+
[enable_module_ellsq=$enableval],
162+
[enable_module_ellsq=no])
163+
159164
AC_ARG_ENABLE(external_default_callbacks,
160165
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [],
161166
[SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])])
@@ -352,6 +357,10 @@ if test x"$enable_module_extrakeys" = x"yes"; then
352357
AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
353358
fi
354359

360+
if test x"$enable_module_ellsq" = x"yes"; then
361+
AC_DEFINE(ENABLE_MODULE_ELLSQ, 1, [Define this symbol to enable the Elligator^2 module])
362+
fi
363+
355364
if test x"$enable_external_default_callbacks" = x"yes"; then
356365
AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used])
357366
fi
@@ -391,6 +400,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
391400
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
392401
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
393402
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
403+
AM_CONDITIONAL([ENABLE_MODULE_ELLSQ], [test x"$enable_module_ellsq" = x"yes"])
394404
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"])
395405
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
396406
AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"])
@@ -411,6 +421,7 @@ echo " module ecdh = $enable_module_ecdh"
411421
echo " module recovery = $enable_module_recovery"
412422
echo " module extrakeys = $enable_module_extrakeys"
413423
echo " module schnorrsig = $enable_module_schnorrsig"
424+
echo " module ellsq = $enable_module_ellsq"
414425
echo
415426
echo " asm = $set_asm"
416427
echo " ecmult window size = $set_ecmult_window"

include/secp256k1_ellsq.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#ifndef SECP256K1_ELLSQ_H
2+
#define SECP256K1_ELLSQ_H
3+
4+
#include "secp256k1.h"
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
/* This module provides an implementation of the Elligator Squared encoding
11+
* for secp256k1 public keys. Given a uniformly random public key, this
12+
* produces a 64-byte encoding that is indistinguishable from uniformly
13+
* random bytes.
14+
*
15+
* Elligator Squared is described in https://eprint.iacr.org/2014/043.pdf by
16+
* Mehdi Tibouchi. The mapping function used is described in
17+
* https://www.di.ens.fr/~fouque/pub/latincrypt12.pdf by Fouque and Tibouchi.
18+
*
19+
* Let f be the function from field elements to curve points, defined as
20+
* follows:
21+
* f(t):
22+
* - Let c = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852
23+
* - Let x1 = (c - 1)/2 - c*t^2 / (t^2 + 8) (mod p)
24+
* - Let x2 = (-c - 1)/2 + c*t^2 / (t^2 + 8) (mod p)
25+
* - Let x3 = 1 - (t^2 + 8)^2 / (3*t^2) (mod p)
26+
* - Let x be the first of [x1,x2,x3] that is an X coordinate on the curve
27+
* (at least one of them is, for any field element t).
28+
* - Let y be the the corresponding Y coordinate to x, with the same parity
29+
* as t (even if t is even, odd if t is odd).
30+
* - Return the curve point with coordinates (x, y).
31+
*
32+
* Then an Elligator Squared encoding of P consists of the 32-byte big-endian
33+
* encodings of field elements u1 and u2 concatenated, where f(u1)+f(u2) = P.
34+
* The encoding algorithm is described in the paper, and effectively picks a
35+
* uniformly random pair (u1,u2) among those which encode P.
36+
*
37+
* To make the encoding able to deal with all inputs, if f(u1)+f(u2) is the
38+
* point at infinity, the decoding is defined to be f(u1) instead.
39+
*/
40+
41+
/* Construct a 64-byte Elligator Squared encoding of a given pubkey.
42+
*
43+
* Returns: 1 when pubkey is valid.
44+
* Args: ctx: pointer to a context object
45+
* Out: ell64: pointer to a 64-byte array to be filled
46+
* In: rnd32: pointer to 32 bytes of entropy (must be unpredictable)
47+
* pubkey: a pointer to a secp256k1_pubkey containing an
48+
* initialized public key
49+
*
50+
* This function runs in variable time.
51+
*/
52+
SECP256K1_API int secp256k1_ellsq_encode(
53+
const secp256k1_context* ctx,
54+
unsigned char *ell64,
55+
const unsigned char *rnd32,
56+
const secp256k1_pubkey *pubkey
57+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
58+
59+
/** Decode a 64-bytes Elligator Squared encoded public key.
60+
*
61+
* Returns: always 1
62+
* Args: ctx: pointer to a context object
63+
* Out: pubkey: pointer to a secp256k1_pubkey that will be filled
64+
* In: ell64: pointer to a 64-byte array to decode
65+
*
66+
* This function runs in variable time.
67+
*/
68+
SECP256K1_API int secp256k1_ellsq_decode(
69+
const secp256k1_context* ctx,
70+
secp256k1_pubkey *pubkey,
71+
const unsigned char *ell64
72+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
73+
74+
#ifdef __cplusplus
75+
}
76+
#endif
77+
78+
#endif /* SECP256K1_ELLSQ_H */

src/bench.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ static void bench_sign_run(void* arg, int iters) {
133133
# include "modules/schnorrsig/bench_impl.h"
134134
#endif
135135

136+
#ifdef ENABLE_MODULE_ELLSQ
137+
# include "modules/ellsq/bench_impl.h"
138+
#endif
139+
136140
int main(int argc, char** argv) {
137141
int i;
138142
secp256k1_pubkey pubkey;
@@ -230,5 +234,10 @@ int main(int argc, char** argv) {
230234
run_schnorrsig_bench(iters, argc, argv);
231235
#endif
232236

237+
#ifdef ENABLE_MODULE_ELLSQ
238+
/* Elligator squared signature benchmarks */
239+
run_ellsq_bench(iters, argc, argv);
240+
#endif
241+
233242
return 0;
234243
}

src/modules/ellsq/Makefile.am.include

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
include_HEADERS += include/secp256k1_ellsq.h
2+
noinst_HEADERS += src/modules/ellsq/bench_impl.h
3+
noinst_HEADERS += src/modules/ellsq/main_impl.h
4+
noinst_HEADERS += src/modules/ellsq/tests_impl.h

src/modules/ellsq/bench_impl.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/***********************************************************************
2+
* Copyright (c) 2021 Pieter Wuille *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
5+
***********************************************************************/
6+
7+
#ifndef SECP256K1_MODULE_ELLSQ_BENCH_H
8+
#define SECP256K1_MODULE_ELLSQ_BENCH_H
9+
10+
#include "../include/secp256k1_ellsq.h"
11+
12+
typedef struct {
13+
secp256k1_context *ctx;
14+
secp256k1_pubkey point;
15+
unsigned char rnd64[64];
16+
} bench_ellsq_data;
17+
18+
static void bench_ellsq_setup(void* arg) {
19+
bench_ellsq_data *data = (bench_ellsq_data*)arg;
20+
const unsigned char point[] = {
21+
0x03,
22+
0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06,
23+
0xc2, 0x37, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd,
24+
0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb,
25+
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
26+
};
27+
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1);
28+
}
29+
30+
static void bench_ellsq_encode(void* arg, int iters) {
31+
int i;
32+
bench_ellsq_data *data = (bench_ellsq_data*)arg;
33+
34+
for (i = 0; i < iters; i++) {
35+
data->rnd64[29] ^= 145;
36+
CHECK(secp256k1_ellsq_encode(data->ctx, data->rnd64, data->rnd64 + 16, &data->point) == 1);
37+
}
38+
}
39+
40+
static void bench_ellsq_decode(void* arg, int iters) {
41+
int i;
42+
secp256k1_pubkey out;
43+
bench_ellsq_data *data = (bench_ellsq_data*)arg;
44+
45+
for (i = 0; i < iters; i++) {
46+
data->rnd64[13] ^= 247;
47+
data->rnd64[47] ^= 113;
48+
CHECK(secp256k1_ellsq_decode(data->ctx, &out, data->rnd64) == 1);
49+
memcpy(data->rnd64, &out.data, 64);
50+
}
51+
}
52+
53+
void run_ellsq_bench(int iters, int argc, char** argv) {
54+
bench_ellsq_data data;
55+
int d = argc == 1;
56+
57+
/* create a context with no capabilities */
58+
data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
59+
memset(data.rnd64, 11, sizeof(data.rnd64));
60+
61+
if (d || have_flag(argc, argv, "ellsq") || have_flag(argc, argv, "encode") || have_flag(argc, argv, "ellsq_encode")) run_benchmark("ellsq_encode", bench_ellsq_encode, bench_ellsq_setup, NULL, &data, 10, iters);
62+
if (d || have_flag(argc, argv, "ellsq") || have_flag(argc, argv, "decode") || have_flag(argc, argv, "ellsq_decode")) run_benchmark("ellsq_decode", bench_ellsq_decode, bench_ellsq_setup, NULL, &data, 10, iters);
63+
64+
secp256k1_context_destroy(data.ctx);
65+
}
66+
67+
#endif /* SECP256K1_MODULE_ELLSQ_BENCH_H */

0 commit comments

Comments
 (0)