Skip to content

Commit

Permalink
Elligator Squared module
Browse files Browse the repository at this point in the history
This adds a module with an implementation of the Elligator Squared
algorithm for encoding/decoding public keys in uniformly random
byte arrays.
  • Loading branch information
sipa committed Nov 16, 2021
1 parent 53b7ce2 commit 07a4ef5
Show file tree
Hide file tree
Showing 10 changed files with 660 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,7 @@ endif
if ENABLE_MODULE_SCHNORRSIG
include src/modules/schnorrsig/Makefile.am.include
endif

if ENABLE_MODULE_ELLSQ
include src/modules/ellsq/Makefile.am.include
endif
15 changes: 15 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ AC_ARG_ENABLE(module_schnorrsig,
[enable_module_schnorrsig=$enableval],
[enable_module_schnorrsig=no])

AC_ARG_ENABLE(module_ellsq,
AS_HELP_STRING([--enable-module-ellsq],[enable Elligator^2 module (experimental)]),
[enable_module_ellsq=$enableval],
[enable_module_ellsq=no])

AC_ARG_ENABLE(external_default_callbacks,
AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]),
[use_external_default_callbacks=$enableval],
Expand Down Expand Up @@ -419,6 +424,10 @@ if test x"$enable_module_extrakeys" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module])
fi

if test x"$enable_module_ellsq" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ELLSQ, 1, [Define this symbol to enable the Elligator^2 module])
fi

if test x"$use_external_default_callbacks" = x"yes"; then
AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used])
fi
Expand All @@ -433,6 +442,7 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys])
AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
AC_MSG_NOTICE([Building Elligator^2 module: $enable_module_ellsq])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_extrakeys" = x"yes"; then
Expand All @@ -441,6 +451,9 @@ else
if test x"$enable_module_schnorrsig" = x"yes"; then
AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_ellsq" = x"yes"; then
AC_MSG_ERROR([Elligator^2 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
Expand All @@ -466,6 +479,7 @@ AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ELLSQ], [test x"$enable_module_ellsq" = 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"])

Expand All @@ -487,6 +501,7 @@ echo " module ecdh = $enable_module_ecdh"
echo " module recovery = $enable_module_recovery"
echo " module extrakeys = $enable_module_extrakeys"
echo " module schnorrsig = $enable_module_schnorrsig"
echo " module ellsq = $enable_module_ellsq"
echo
echo " asm = $set_asm"
echo " ecmult window size = $set_ecmult_window"
Expand Down
78 changes: 78 additions & 0 deletions include/secp256k1_ellsq.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#ifndef SECP256K1_ELLSQ_H
#define SECP256K1_ELLSQ_H

#include "secp256k1.h"

#ifdef __cplusplus
extern "C" {
#endif

/* This module provides an implementation of the Elligator Squared encoding
* for secp256k1 public keys. Given a uniformly random public key, this
* produces a 64-byte encoding that is indistinguishable from uniformly
* random bytes.
*
* Elligator Squared is described in https://eprint.iacr.org/2014/043.pdf by
* Mehdi Tibouchi. The mapping function used is described in
* https://www.di.ens.fr/~fouque/pub/latincrypt12.pdf by Fouque and Tibouchi.
*
* Let f be the function from field elements to curve points, defined as
* follows:
* f(t):
* - Let c = 0xa2d2ba93507f1df233770c2a797962cc61f6d15da14ecd47d8d27ae1cd5f852
* - Let x1 = (c - 1)/2 - c*t^2 / (t^2 + 8) (mod p)
* - Let x2 = (-c - 1)/2 + c*t^2 / (t^2 + 8) (mod p)
* - Let x3 = 1 - (t^2 + 8)^2 / (3*t^2) (mod p)
* - Let x be the first of [x1,x2,x3] that is an X coordinate on the curve
* (at least one of them is, for any field element t).
* - Let y be the the corresponding Y coordinate to x, with the same parity
* as t (even if t is even, odd if t is odd).
* - Return the curve point with coordinates (x, y).
*
* Then an Elligator Squared encoding of P consists of the 32-byte big-endian
* encodings of field elements u1 and u2 concatenated, where f(u1)+f(u2) = P.
* The encoding algorithm is described in the paper, and effectively picks a
* uniformly random pair (u1,u2) among those which encode P.
*
* To make the encoding able to deal with all inputs, if f(u1)+f(u2) is the
* point at infinity, the decoding is defined to be f(u1) instead.
*/

/* Construct a 64-byte Elligator Squared encoding of a given pubkey.
*
* Returns: 1 when pubkey is valid.
* Args: ctx: pointer to a context object
* Out: ell64: pointer to a 64-byte array to be filled
* In: rnd32: pointer to 32 bytes of entropy (must be unpredictable)
* pubkey: a pointer to a secp256k1_pubkey containing an
* initialized public key
*
* This function runs in variable time.
*/
SECP256K1_API int secp256k1_ellsq_encode(
const secp256k1_context* ctx,
unsigned char *ell64,
const unsigned char *rnd32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Decode a 64-bytes Elligator Squared encoded public key.
*
* Returns: always 1
* Args: ctx: pointer to a context object
* Out: pubkey: pointer to a secp256k1_pubkey that will be filled
* In: ell64: pointer to a 64-byte array to decode
*
* This function runs in variable time.
*/
SECP256K1_API int secp256k1_ellsq_decode(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const unsigned char *ell64
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

#ifdef __cplusplus
}
#endif

#endif /* SECP256K1_ELLSQ_H */
9 changes: 9 additions & 0 deletions src/bench.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ static void bench_sign_run(void* arg, int iters) {
# include "modules/schnorrsig/bench_impl.h"
#endif

#ifdef ENABLE_MODULE_ELLSQ
# include "modules/ellsq/bench_impl.h"
#endif

int main(int argc, char** argv) {
int i;
secp256k1_pubkey pubkey;
Expand Down Expand Up @@ -140,5 +144,10 @@ int main(int argc, char** argv) {
run_schnorrsig_bench(iters, argc, argv);
#endif

#ifdef ENABLE_MODULE_ELLSQ
/* Elligator squared signature benchmarks */
run_ellsq_bench(iters, argc, argv);
#endif

return 0;
}
4 changes: 4 additions & 0 deletions src/modules/ellsq/Makefile.am.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include_HEADERS += include/secp256k1_ellsq.h
noinst_HEADERS += src/modules/ellsq/bench_impl.h
noinst_HEADERS += src/modules/ellsq/main_impl.h
noinst_HEADERS += src/modules/ellsq/tests_impl.h
67 changes: 67 additions & 0 deletions src/modules/ellsq/bench_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/***********************************************************************
* Copyright (c) 2021 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/

#ifndef SECP256K1_MODULE_ELLSQ_BENCH_H
#define SECP256K1_MODULE_ELLSQ_BENCH_H

#include "../include/secp256k1_ellsq.h"

typedef struct {
secp256k1_context *ctx;
secp256k1_pubkey point;
unsigned char rnd64[64];
} bench_ellsq_data;

static void bench_ellsq_setup(void* arg) {
bench_ellsq_data *data = (bench_ellsq_data*)arg;
const unsigned char point[] = {
0x03,
0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06,
0xc2, 0x37, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd,
0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb,
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
};
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1);
}

static void bench_ellsq_encode(void* arg, int iters) {
int i;
bench_ellsq_data *data = (bench_ellsq_data*)arg;

for (i = 0; i < iters; i++) {
data->rnd64[29] ^= 145;
CHECK(secp256k1_ellsq_encode(data->ctx, data->rnd64, data->rnd64 + 16, &data->point) == 1);
}
}

static void bench_ellsq_decode(void* arg, int iters) {
int i;
secp256k1_pubkey out;
bench_ellsq_data *data = (bench_ellsq_data*)arg;

for (i = 0; i < iters; i++) {
data->rnd64[13] ^= 247;
data->rnd64[47] ^= 113;
CHECK(secp256k1_ellsq_decode(data->ctx, &out, data->rnd64) == 1);
memcpy(data->rnd64, &out.data, 64);
}
}

void run_ellsq_bench(int iters, int argc, char** argv) {
bench_ellsq_data data;
int d = argc == 1;

/* create a context with no capabilities */
data.ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
memset(data.rnd64, 11, sizeof(data.rnd64));

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);
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);

secp256k1_context_destroy(data.ctx);
}

#endif /* SECP256K1_MODULE_ELLSQ_BENCH_H */
Loading

0 comments on commit 07a4ef5

Please sign in to comment.