Skip to content

Commit

Permalink
schnorrsig: add versioned parameter struct for sign_custom
Browse files Browse the repository at this point in the history
This allows adding customization options in the future without
breaking the API
  • Loading branch information
jonasnick committed Jan 8, 2021
1 parent d12ca15 commit 07138bb
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 13 deletions.
27 changes: 21 additions & 6 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ typedef int (*secp256k1_nonce_function_hardened)(
*/
SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;

/** Data structure that holds additional arguments for schnorrsig signing.
*/
typedef struct {
unsigned char magic[4];
size_t size;
secp256k1_nonce_function_hardened noncefp;
void* ndata;
} secp256k1_schnorrsig_config;

#define SECP256K1_SCHNORRSIG_CONFIG_MAGIC "\xda\x6f\xb3\x8c"
#define SECP256K1_SCHNORRSIG_CONFIG_INIT { SECP256K1_SCHNORRSIG_CONFIG_MAGIC,\
sizeof(secp256k1_schnorrsig_config),\
NULL,\
NULL };

/** Create a Schnorr signature.
*
* Does _not_ strictly follow BIP-340 because it does not verify the resulting
Expand Down Expand Up @@ -88,26 +103,26 @@ SECP256K1_API int secp256k1_schnorrsig_sign(

/** Create a Schnorr signature with a more flexible API.
*
* Same arguments as secp256k1_schnorrsig_sign except that it misses aux_rand32
* and instead allows allows providing a different nonce derivation function
* with its own data argument.
* Same arguments as secp256k1_schnorrsig_sign except that it accepts a pointer
* to a config object that allows customizing signing by passing additional
* arguments.
*
* In: noncefp: pointer to a nonce generation function. If NULL,
* secp256k1_nonce_function_bip340 is used
* ndata: pointer to arbitrary data used by the nonce generation function
* (can be NULL). If it is non-NULL and
* secp256k1_nonce_function_bip340 is used, then ndata must be a
* pointer to 32-byte auxiliary randomness as per BIP-340.
* config: pointer to a config object.
*/
SECP256K1_API int secp256k1_schnorrsig_sign_custom(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char *msg,
size_t msg_len,
const secp256k1_keypair *keypair,
secp256k1_nonce_function_hardened noncefp,
void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
secp256k1_schnorrsig_config *config
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);


/** Verify a Schnorr signature.
Expand Down
60 changes: 54 additions & 6 deletions src/modules/schnorrsig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,7 @@ static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned c
secp256k1_scalar_set_b32(e, buf, NULL);
}


int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msg_len, const secp256k1_keypair *keypair, unsigned char *aux_rand32) {
return secp256k1_schnorrsig_sign_custom(ctx, sig64, msg, msg_len, keypair, NULL, aux_rand32);
}

int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msg_len, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msg_len, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
secp256k1_scalar sk;
secp256k1_scalar e;
secp256k1_scalar k;
Expand Down Expand Up @@ -192,6 +187,59 @@ int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char
return ret;
}


int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msg_len, const secp256k1_keypair *keypair, unsigned char *aux_rand32) {
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msg_len, keypair, secp256k1_nonce_function_bip340, aux_rand32);
}

/* This implements versioned structs which are used as function parameters with
* forward and backward compatibility (see also
* https://lkml.org/lkml/2015/7/30/117). This function resembles the linux
* kernel function copy_struct_from_user
* (https://github.com/torvalds/linux/blob/0593c1b4598a77b5f835b278cde0ab71e2578588/include/linux/uaccess.h#L298).
*/
static int copy_versioned_struct(void *dst, size_t dstsize, const void *src, size_t srcsize)
{
size_t size = dstsize < srcsize ? dstsize : srcsize; /* min */
size_t rest = (dstsize > srcsize ? dstsize : srcsize) /* max */
- size;

/* Deal with trailing bytes. */
if (srcsize < dstsize) {
memset((unsigned char*)dst + size, 0, rest);
} else if (srcsize > dstsize) {
size_t i;
for (i = 0; i < rest; i++) {
if (*((unsigned char *)src + size + i) != 0) {
return 0;
}
}
}
/* Copy the interoperable parts of the struct. */
memcpy(dst, src, size);
return 1;
}

static int copy_versioned_config(secp256k1_schnorrsig_config *config, secp256k1_schnorrsig_config *uconfig) {
memset(config, 0, sizeof(*config));
return copy_versioned_struct(config, sizeof(*config), uconfig, config->size);
}

int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msg_len, const secp256k1_keypair *keypair, secp256k1_schnorrsig_config *uconfig) {
secp256k1_schnorrsig_config config;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(uconfig != NULL);
ARG_CHECK(secp256k1_memcmp_var(uconfig->magic,
SECP256K1_SCHNORRSIG_CONFIG_MAGIC,
sizeof(uconfig->magic)) == 0);

if (!copy_versioned_config(&config, uconfig)) {
return 0;
}
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msg_len, keypair, config.noncefp, config.ndata);
}

int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msg_len, const secp256k1_xonly_pubkey *pubkey) {
secp256k1_scalar s;
secp256k1_scalar e;
Expand Down
6 changes: 5 additions & 1 deletion src/modules/schnorrsig/tests_exhaustive_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, cons
static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsigned char (*xonly_pubkey_bytes)[32], const secp256k1_keypair* keypairs, const int* parities) {
int d, k;
uint64_t iter = 0;
secp256k1_schnorrsig_config config = SECP256K1_SCHNORRSIG_CONFIG_INIT;

/* Loop over keys. */
for (d = 1; d < EXHAUSTIVE_TEST_ORDER; ++d) {
int actual_d = d;
Expand All @@ -151,6 +153,8 @@ static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsign
unsigned char sig64[64];
int actual_k = k;
if (skip_section(&iter)) continue;
config.noncefp = secp256k1_hardened_nonce_function_smallint;
config.ndata = &k;
if (parities[k - 1]) actual_k = EXHAUSTIVE_TEST_ORDER - k;
/* Generate random messages until all challenges have been tried. */
while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
Expand All @@ -163,7 +167,7 @@ static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsign
unsigned char expected_s_bytes[32];
secp256k1_scalar_get_b32(expected_s_bytes, &expected_s);
/* Invoke the real function to construct a signature. */
CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig64, msg32, sizeof(msg32), &keypairs[d - 1], secp256k1_hardened_nonce_function_smallint, &k));
CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig64, msg32, sizeof(msg32), &keypairs[d - 1], &config));
/* The first 32 bytes must match the xonly pubkey for the specified k. */
CHECK(secp256k1_memcmp_var(sig64, xonly_pubkey_bytes[k - 1], 32) == 0);
/* The last 32 bytes must match the expected s value. */
Expand Down

0 comments on commit 07138bb

Please sign in to comment.