Skip to content

Commit b26da2a

Browse files
benmajonasnick
andcommitted
allow creating and verifying Schnorr sign-to-contract commitments
Adapted from bitcoin-core#589. The data is hashed using a tagged hash with the "s2c/schnorr/data" tag, which is consistent with the data hashing done in the ECDSA version of sign-to-contract (in ElementsProject/secp256k1-zkp), where the "s2c/ecdsa/data" tag is used. Co-authored-by: Jonas Nick <[email protected]>
1 parent 6daba26 commit b26da2a

File tree

3 files changed

+329
-54
lines changed

3 files changed

+329
-54
lines changed

include/secp256k1_schnorrsig.h

+35-9
Original file line numberDiff line numberDiff line change
@@ -125,26 +125,36 @@ SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_fun
125125
* setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT.
126126
*
127127
* Members:
128-
* magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
129-
* and has no other function than making sure the object is
130-
* initialized.
131-
* noncefp: pointer to a nonce generation function. If NULL,
132-
* secp256k1_nonce_function_bip340 is used
133-
* ndata: pointer to arbitrary data used by the nonce generation function
134-
* (can be NULL). If it is non-NULL and
135-
* secp256k1_nonce_function_bip340 is used, then ndata must be a
136-
* pointer to 32-byte auxiliary randomness as per BIP-340.
128+
* magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
129+
* and has no other function than making sure the object is
130+
* initialized.
131+
* noncefp: pointer to a nonce generation function. If NULL,
132+
* secp256k1_nonce_function_bip340 is used
133+
* ndata: pointer to arbitrary data used by the nonce generation function
134+
* (can be NULL). If it is non-NULL and
135+
* secp256k1_nonce_function_bip340 is used, then ndata must be a
136+
* pointer to 32-byte auxiliary randomness as per BIP-340.
137+
* s2c_opening: pointer to an secp256k1_schnorr_s2c_opening structure which can be
138+
* NULL but is required to be not NULL if this signature creates
139+
* a sign-to-contract commitment (i.e. the `s2c_data32` argument
140+
* is not NULL).
141+
* s2c_data32: pointer to a 32-byte data to create an optional
142+
* sign-to-contract commitment to if not NULL (can be NULL).
137143
*/
138144
typedef struct {
139145
unsigned char magic[4];
140146
secp256k1_nonce_function_hardened noncefp;
141147
void* ndata;
148+
secp256k1_schnorr_s2c_opening* s2c_opening;
149+
const unsigned char* s2c_data32;
142150
} secp256k1_schnorrsig_extraparams;
143151

144152
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c }
145153
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\
146154
SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\
147155
NULL,\
156+
NULL,\
157+
NULL,\
148158
NULL\
149159
}
150160

@@ -231,6 +241,22 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
231241
const secp256k1_xonly_pubkey *pubkey
232242
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
233243

244+
/** Verify a sign-to-contract commitment.
245+
*
246+
* Returns: 1: the signature contains a commitment to data32
247+
* 0: incorrect opening
248+
* Args: ctx: a secp256k1 context object, initialized for verification.
249+
* In: sig64: the signature containing the sign-to-contract commitment (cannot be NULL)
250+
* data32: the 32-byte data that was committed to (cannot be NULL)
251+
* opening: pointer to the opening created during signing (cannot be NULL)
252+
*/
253+
SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit(
254+
const secp256k1_context* ctx,
255+
const unsigned char *sig64,
256+
const unsigned char *data32,
257+
const secp256k1_schnorr_s2c_opening *opening
258+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
259+
234260
#ifdef __cplusplus
235261
}
236262
#endif

src/modules/schnorrsig/main_impl.h

+81-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
static uint64_t s2c_opening_magic = 0x5d0520b8b7f2b168ULL;
1515

16+
static const unsigned char s2c_data_tag[16] = "s2c/schnorr/data";
17+
1618
static void secp256k1_schnorr_s2c_opening_init(secp256k1_schnorr_s2c_opening *opening) {
1719
opening->magic = s2c_opening_magic;
1820
opening->nonce_is_negated = 0;
@@ -183,23 +185,31 @@ static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned c
183185
secp256k1_scalar_set_b32(e, buf, NULL);
184186
}
185187

186-
static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
188+
static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata, secp256k1_schnorr_s2c_opening *s2c_opening, const unsigned char *s2c_data32) {
187189
secp256k1_scalar sk;
188190
secp256k1_scalar e;
189191
secp256k1_scalar k;
190192
secp256k1_gej rj;
191193
secp256k1_ge pk;
192194
secp256k1_ge r;
195+
secp256k1_sha256 sha;
193196
unsigned char buf[32] = { 0 };
194197
unsigned char pk_buf[32];
195198
unsigned char seckey[32];
199+
unsigned char noncedata[32];
196200
int ret = 1;
197201

198202
VERIFY_CHECK(ctx != NULL);
199203
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
200204
ARG_CHECK(sig64 != NULL);
201205
ARG_CHECK(msg != NULL || msglen == 0);
202206
ARG_CHECK(keypair != NULL);
207+
/* sign-to-contract commitments only work with the default nonce function,
208+
* because we need to ensure that s2c_data is actually hashed into the nonce and
209+
* not just ignored because otherwise this could result in nonce reuse. */
210+
ARG_CHECK(s2c_data32 == NULL || (noncefp == NULL || noncefp == secp256k1_nonce_function_bip340));
211+
/* s2c_opening and s2c_data32 should be either both non-NULL or both NULL. */
212+
ARG_CHECK((s2c_opening != NULL) == (s2c_data32 != NULL));
203213

204214
if (noncefp == NULL) {
205215
noncefp = secp256k1_nonce_function_bip340;
@@ -215,6 +225,25 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi
215225

216226
secp256k1_scalar_get_b32(seckey, &sk);
217227
secp256k1_fe_get_b32(pk_buf, &pk.x);
228+
229+
if (s2c_data32 != NULL) {
230+
/* Provide s2c_data32 and ndata (if not NULL) to the the nonce function
231+
* as additional data to derive the nonce from. If both pointers are
232+
* not NULL, they need to be hashed to get the nonce data 32 bytes.
233+
* Even if only s2c_data32 is not NULL, it's hashed because it should
234+
* be possible to derive nonces even if only a SHA256 commitment to the
235+
* data is known. This is for example important in the anti nonce
236+
* sidechannel protocol.
237+
*/
238+
secp256k1_sha256_initialize(&sha);
239+
secp256k1_sha256_write(&sha, s2c_data32, 32);
240+
if (ndata != NULL) {
241+
secp256k1_sha256_write(&sha, ndata, 32);
242+
}
243+
secp256k1_sha256_finalize(&sha, noncedata);
244+
ndata = &noncedata;
245+
}
246+
218247
ret &= !!noncefp(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata);
219248
secp256k1_scalar_set_b32(&k, buf, NULL);
220249
ret &= !secp256k1_scalar_is_zero(&k);
@@ -223,12 +252,27 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi
223252
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
224253
secp256k1_ge_set_gej(&r, &rj);
225254

255+
if (s2c_opening != NULL) {
256+
secp256k1_schnorr_s2c_opening_init(s2c_opening);
257+
if (s2c_data32 != NULL) {
258+
secp256k1_sha256_initialize_tagged(&sha, s2c_data_tag, sizeof(s2c_data_tag));
259+
/* Create sign-to-contract commitment */
260+
secp256k1_pubkey_save(&s2c_opening->original_pubnonce, &r);
261+
secp256k1_ec_commit_seckey(&k, &r, &sha, s2c_data32, 32);
262+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
263+
secp256k1_ge_set_gej(&r, &rj);
264+
}
265+
}
266+
226267
/* We declassify r to allow using it as a branch point. This is fine
227268
* because r is not a secret. */
228269
secp256k1_declassify(ctx, &r, sizeof(r));
229270
secp256k1_fe_normalize_var(&r.y);
230271
if (secp256k1_fe_is_odd(&r.y)) {
231272
secp256k1_scalar_negate(&k, &k);
273+
if (s2c_opening != NULL) {
274+
s2c_opening->nonce_is_negated = 1;
275+
}
232276
}
233277
secp256k1_fe_normalize_var(&r.x);
234278
secp256k1_fe_get_b32(&sig64[0], &r.x);
@@ -248,7 +292,7 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi
248292

249293
int secp256k1_schnorrsig_sign32(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
250294
/* We cast away const from the passed aux_rand32 argument since we know the default nonce function does not modify it. */
251-
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, (unsigned char*)aux_rand32);
295+
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, (unsigned char*)aux_rand32, NULL, NULL);
252296
}
253297

254298
int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
@@ -258,6 +302,8 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
258302
int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_schnorrsig_extraparams *extraparams) {
259303
secp256k1_nonce_function_hardened noncefp = NULL;
260304
void *ndata = NULL;
305+
secp256k1_schnorr_s2c_opening *s2c_opening = NULL;
306+
const unsigned char *s2c_data32 = NULL;
261307
VERIFY_CHECK(ctx != NULL);
262308

263309
if (extraparams != NULL) {
@@ -266,8 +312,10 @@ int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char
266312
sizeof(extraparams->magic)) == 0);
267313
noncefp = extraparams->noncefp;
268314
ndata = extraparams->ndata;
315+
s2c_opening = extraparams->s2c_opening;
316+
s2c_data32 = extraparams->s2c_data32;
269317
}
270-
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata);
318+
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata, s2c_opening, s2c_data32);
271319
}
272320

273321
int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) {
@@ -318,4 +366,34 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha
318366
secp256k1_fe_equal_var(&rx, &r.x);
319367
}
320368

369+
int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *data32, const secp256k1_schnorr_s2c_opening *opening) {
370+
secp256k1_fe rx;
371+
secp256k1_ge original_r;
372+
secp256k1_ge r;
373+
secp256k1_sha256 sha;
374+
375+
VERIFY_CHECK(ctx != NULL);
376+
ARG_CHECK(sig64 != NULL);
377+
ARG_CHECK(data32 != NULL);
378+
ARG_CHECK(opening != NULL);
379+
ARG_CHECK(secp256k1_s2c_commit_is_init(opening));
380+
381+
if (!secp256k1_fe_set_b32(&rx, &sig64[0])) {
382+
return 0;
383+
}
384+
if (!secp256k1_ge_set_xo_var(&r, &rx, 0)) {
385+
return 0;
386+
}
387+
if (opening->nonce_is_negated) {
388+
secp256k1_ge_neg(&r, &r);
389+
}
390+
391+
if (!secp256k1_pubkey_load(ctx, &original_r, &opening->original_pubnonce)) {
392+
return 0;
393+
}
394+
395+
secp256k1_sha256_initialize_tagged(&sha, s2c_data_tag, sizeof(s2c_data_tag));
396+
return secp256k1_ec_commit_verify(&r, &original_r, &sha, data32, 32);
397+
}
398+
321399
#endif

0 commit comments

Comments
 (0)