Skip to content

Commit 07138bb

Browse files
committed
schnorrsig: add versioned parameter struct for sign_custom
This allows adding customization options in the future without breaking the API
1 parent d12ca15 commit 07138bb

File tree

3 files changed

+80
-13
lines changed

3 files changed

+80
-13
lines changed

include/secp256k1_schnorrsig.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ typedef int (*secp256k1_nonce_function_hardened)(
6060
*/
6161
SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
6262

63+
/** Data structure that holds additional arguments for schnorrsig signing.
64+
*/
65+
typedef struct {
66+
unsigned char magic[4];
67+
size_t size;
68+
secp256k1_nonce_function_hardened noncefp;
69+
void* ndata;
70+
} secp256k1_schnorrsig_config;
71+
72+
#define SECP256K1_SCHNORRSIG_CONFIG_MAGIC "\xda\x6f\xb3\x8c"
73+
#define SECP256K1_SCHNORRSIG_CONFIG_INIT { SECP256K1_SCHNORRSIG_CONFIG_MAGIC,\
74+
sizeof(secp256k1_schnorrsig_config),\
75+
NULL,\
76+
NULL };
77+
6378
/** Create a Schnorr signature.
6479
*
6580
* Does _not_ strictly follow BIP-340 because it does not verify the resulting
@@ -88,26 +103,26 @@ SECP256K1_API int secp256k1_schnorrsig_sign(
88103

89104
/** Create a Schnorr signature with a more flexible API.
90105
*
91-
* Same arguments as secp256k1_schnorrsig_sign except that it misses aux_rand32
92-
* and instead allows allows providing a different nonce derivation function
93-
* with its own data argument.
106+
* Same arguments as secp256k1_schnorrsig_sign except that it accepts a pointer
107+
* to a config object that allows customizing signing by passing additional
108+
* arguments.
94109
*
95110
* In: noncefp: pointer to a nonce generation function. If NULL,
96111
* secp256k1_nonce_function_bip340 is used
97112
* ndata: pointer to arbitrary data used by the nonce generation function
98113
* (can be NULL). If it is non-NULL and
99114
* secp256k1_nonce_function_bip340 is used, then ndata must be a
100115
* pointer to 32-byte auxiliary randomness as per BIP-340.
116+
* config: pointer to a config object.
101117
*/
102118
SECP256K1_API int secp256k1_schnorrsig_sign_custom(
103119
const secp256k1_context* ctx,
104120
unsigned char *sig64,
105121
const unsigned char *msg,
106122
size_t msg_len,
107123
const secp256k1_keypair *keypair,
108-
secp256k1_nonce_function_hardened noncefp,
109-
void *ndata
110-
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
124+
secp256k1_schnorrsig_config *config
125+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
111126

112127

113128
/** Verify a Schnorr signature.

src/modules/schnorrsig/main_impl.h

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,7 @@ static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned c
124124
secp256k1_scalar_set_b32(e, buf, NULL);
125125
}
126126

127-
128-
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) {
129-
return secp256k1_schnorrsig_sign_custom(ctx, sig64, msg, msg_len, keypair, NULL, aux_rand32);
130-
}
131-
132-
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) {
127+
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) {
133128
secp256k1_scalar sk;
134129
secp256k1_scalar e;
135130
secp256k1_scalar k;
@@ -192,6 +187,59 @@ int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char
192187
return ret;
193188
}
194189

190+
191+
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) {
192+
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msg_len, keypair, secp256k1_nonce_function_bip340, aux_rand32);
193+
}
194+
195+
/* This implements versioned structs which are used as function parameters with
196+
* forward and backward compatibility (see also
197+
* https://lkml.org/lkml/2015/7/30/117). This function resembles the linux
198+
* kernel function copy_struct_from_user
199+
* (https://github.com/torvalds/linux/blob/0593c1b4598a77b5f835b278cde0ab71e2578588/include/linux/uaccess.h#L298).
200+
*/
201+
static int copy_versioned_struct(void *dst, size_t dstsize, const void *src, size_t srcsize)
202+
{
203+
size_t size = dstsize < srcsize ? dstsize : srcsize; /* min */
204+
size_t rest = (dstsize > srcsize ? dstsize : srcsize) /* max */
205+
- size;
206+
207+
/* Deal with trailing bytes. */
208+
if (srcsize < dstsize) {
209+
memset((unsigned char*)dst + size, 0, rest);
210+
} else if (srcsize > dstsize) {
211+
size_t i;
212+
for (i = 0; i < rest; i++) {
213+
if (*((unsigned char *)src + size + i) != 0) {
214+
return 0;
215+
}
216+
}
217+
}
218+
/* Copy the interoperable parts of the struct. */
219+
memcpy(dst, src, size);
220+
return 1;
221+
}
222+
223+
static int copy_versioned_config(secp256k1_schnorrsig_config *config, secp256k1_schnorrsig_config *uconfig) {
224+
memset(config, 0, sizeof(*config));
225+
return copy_versioned_struct(config, sizeof(*config), uconfig, config->size);
226+
}
227+
228+
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) {
229+
secp256k1_schnorrsig_config config;
230+
231+
VERIFY_CHECK(ctx != NULL);
232+
ARG_CHECK(uconfig != NULL);
233+
ARG_CHECK(secp256k1_memcmp_var(uconfig->magic,
234+
SECP256K1_SCHNORRSIG_CONFIG_MAGIC,
235+
sizeof(uconfig->magic)) == 0);
236+
237+
if (!copy_versioned_config(&config, uconfig)) {
238+
return 0;
239+
}
240+
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msg_len, keypair, config.noncefp, config.ndata);
241+
}
242+
195243
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) {
196244
secp256k1_scalar s;
197245
secp256k1_scalar e;

src/modules/schnorrsig/tests_exhaustive_impl.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, cons
139139
static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsigned char (*xonly_pubkey_bytes)[32], const secp256k1_keypair* keypairs, const int* parities) {
140140
int d, k;
141141
uint64_t iter = 0;
142+
secp256k1_schnorrsig_config config = SECP256K1_SCHNORRSIG_CONFIG_INIT;
143+
142144
/* Loop over keys. */
143145
for (d = 1; d < EXHAUSTIVE_TEST_ORDER; ++d) {
144146
int actual_d = d;
@@ -151,6 +153,8 @@ static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsign
151153
unsigned char sig64[64];
152154
int actual_k = k;
153155
if (skip_section(&iter)) continue;
156+
config.noncefp = secp256k1_hardened_nonce_function_smallint;
157+
config.ndata = &k;
154158
if (parities[k - 1]) actual_k = EXHAUSTIVE_TEST_ORDER - k;
155159
/* Generate random messages until all challenges have been tried. */
156160
while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
@@ -163,7 +167,7 @@ static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsign
163167
unsigned char expected_s_bytes[32];
164168
secp256k1_scalar_get_b32(expected_s_bytes, &expected_s);
165169
/* Invoke the real function to construct a signature. */
166-
CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig64, msg32, sizeof(msg32), &keypairs[d - 1], secp256k1_hardened_nonce_function_smallint, &k));
170+
CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig64, msg32, sizeof(msg32), &keypairs[d - 1], &config));
167171
/* The first 32 bytes must match the xonly pubkey for the specified k. */
168172
CHECK(secp256k1_memcmp_var(sig64, xonly_pubkey_bytes[k - 1], 32) == 0);
169173
/* The last 32 bytes must match the expected s value. */

0 commit comments

Comments
 (0)