Skip to content

Commit

Permalink
allow creating and verifying Schnorr sign-to-contract commitments
Browse files Browse the repository at this point in the history
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. Similarly, the tweak hash tag is
"s2c/schnorr/point".

Co-authored-by: Jonas Nick <jonasd.nick@gmail.com>
  • Loading branch information
benma and jonasnick committed Sep 12, 2022
1 parent a903875 commit c1904b1
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 54 deletions.
44 changes: 35 additions & 9 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,26 +125,36 @@ SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_fun
* setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT.
*
* Members:
* magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
* and has no other function than making sure the object is
* initialized.
* 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.
* magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
* and has no other function than making sure the object is
* initialized.
* 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.
* s2c_opening: pointer to an secp256k1_schnorrsig_s2c_opening structure which can be
* NULL but is required to be not NULL if this signature creates
* a sign-to-contract commitment (i.e. the `s2c_data32` argument
* is not NULL).
* s2c_data32: pointer to a 32-byte data to create an optional
* sign-to-contract commitment to if not NULL (can be NULL).
*/
typedef struct {
unsigned char magic[4];
secp256k1_nonce_function_hardened noncefp;
void* ndata;
secp256k1_schnorrsig_s2c_opening* s2c_opening;
const unsigned char* s2c_data32;
} secp256k1_schnorrsig_extraparams;

#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c }
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\
SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\
NULL,\
NULL,\
NULL,\
NULL\
}

Expand Down Expand Up @@ -231,6 +241,22 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
const secp256k1_xonly_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);

/** Verify a sign-to-contract commitment.
*
* Returns: 1: the signature contains a commitment to data32
* 0: incorrect opening
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig64: the signature containing the sign-to-contract commitment (cannot be NULL)
* data32: the 32-byte data that was committed to (cannot be NULL)
* opening: pointer to the opening created during signing (cannot be NULL)
*/
SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit(
const secp256k1_context* ctx,
const unsigned char *sig64,
const unsigned char *data32,
const secp256k1_schnorrsig_s2c_opening *opening
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

#ifdef __cplusplus
}
#endif
Expand Down
85 changes: 82 additions & 3 deletions src/modules/schnorrsig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

static unsigned char s2c_opening_magic[8] = "\x5d\x05\x20\xb8\xb7\xf2\xb1\x68";

static const unsigned char s2c_data_tag[16] = "s2c/schnorr/data";
static const unsigned char s2c_point_tag[17] = "s2c/schnorr/point";

static void secp256k1_schnorrsig_s2c_opening_init(secp256k1_schnorrsig_s2c_opening *opening) {
memcpy(opening->magic, s2c_opening_magic, 8);
opening->nonce_is_negated = 0;
Expand Down Expand Up @@ -183,23 +186,31 @@ static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned c
secp256k1_scalar_set_b32(e, buf, NULL);
}

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) {
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_schnorrsig_s2c_opening *s2c_opening, const unsigned char *s2c_data32) {
secp256k1_scalar sk;
secp256k1_scalar e;
secp256k1_scalar k;
secp256k1_gej rj;
secp256k1_ge pk;
secp256k1_ge r;
secp256k1_sha256 sha;
unsigned char buf[32] = { 0 };
unsigned char pk_buf[32];
unsigned char seckey[32];
unsigned char noncedata[32];
int ret = 1;

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

if (noncefp == NULL) {
noncefp = secp256k1_nonce_function_bip340;
Expand All @@ -215,6 +226,25 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi

secp256k1_scalar_get_b32(seckey, &sk);
secp256k1_fe_get_b32(pk_buf, &pk.x);

if (s2c_data32 != NULL) {
/* Provide s2c_data32 and ndata (if not NULL) to the the nonce function
* as additional data to derive the nonce from. If both pointers are
* not NULL, they need to be hashed to get the nonce data 32 bytes.
* Even if only s2c_data32 is not NULL, it's hashed because it should
* be possible to derive nonces even if only a SHA256 commitment to the
* data is known. This is for example important in the anti nonce
* sidechannel protocol.
*/
secp256k1_sha256_initialize_tagged(&sha, s2c_data_tag, sizeof(s2c_data_tag));
secp256k1_sha256_write(&sha, s2c_data32, 32);
if (ndata != NULL) {
secp256k1_sha256_write(&sha, ndata, 32);
}
secp256k1_sha256_finalize(&sha, noncedata);
ndata = &noncedata;
}

ret &= !!noncefp(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata);
secp256k1_scalar_set_b32(&k, buf, NULL);
ret &= !secp256k1_scalar_is_zero(&k);
Expand All @@ -223,12 +253,27 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
secp256k1_ge_set_gej(&r, &rj);

if (s2c_opening != NULL) {
secp256k1_schnorrsig_s2c_opening_init(s2c_opening);
if (s2c_data32 != NULL) {
secp256k1_sha256_initialize_tagged(&sha, s2c_point_tag, sizeof(s2c_point_tag));
/* Create sign-to-contract commitment */
secp256k1_pubkey_save(&s2c_opening->original_pubnonce, &r);
secp256k1_ec_commit_seckey(&k, &r, &sha, s2c_data32, 32);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
secp256k1_ge_set_gej(&r, &rj);
}
}

/* We declassify r to allow using it as a branch point. This is fine
* because r is not a secret. */
secp256k1_declassify(ctx, &r, sizeof(r));
secp256k1_fe_normalize_var(&r.y);
if (secp256k1_fe_is_odd(&r.y)) {
secp256k1_scalar_negate(&k, &k);
if (s2c_opening != NULL) {
s2c_opening->nonce_is_negated = 1;
}
}
secp256k1_fe_normalize_var(&r.x);
secp256k1_fe_get_b32(&sig64[0], &r.x);
Expand All @@ -248,7 +293,7 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi

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

int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
Expand All @@ -258,6 +303,8 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
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) {
secp256k1_nonce_function_hardened noncefp = NULL;
void *ndata = NULL;
secp256k1_schnorrsig_s2c_opening *s2c_opening = NULL;
const unsigned char *s2c_data32 = NULL;
VERIFY_CHECK(ctx != NULL);

if (extraparams != NULL) {
Expand All @@ -266,8 +313,10 @@ int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char
sizeof(extraparams->magic)) == 0);
noncefp = extraparams->noncefp;
ndata = extraparams->ndata;
s2c_opening = extraparams->s2c_opening;
s2c_data32 = extraparams->s2c_data32;
}
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata);
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata, s2c_opening, s2c_data32);
}

int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) {
Expand Down Expand Up @@ -318,4 +367,34 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha
secp256k1_fe_equal_var(&rx, &r.x);
}

int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *data32, const secp256k1_schnorrsig_s2c_opening *opening) {
secp256k1_fe rx;
secp256k1_ge original_r;
secp256k1_ge r;
secp256k1_sha256 sha;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sig64 != NULL);
ARG_CHECK(data32 != NULL);
ARG_CHECK(opening != NULL);
ARG_CHECK(secp256k1_schnorrsig_s2c_commit_is_init(opening));

if (!secp256k1_fe_set_b32(&rx, &sig64[0])) {
return 0;
}
if (!secp256k1_ge_set_xo_var(&r, &rx, 0)) {
return 0;
}
if (opening->nonce_is_negated) {
secp256k1_ge_neg(&r, &r);
}

if (!secp256k1_pubkey_load(ctx, &original_r, &opening->original_pubnonce)) {
return 0;
}

secp256k1_sha256_initialize_tagged(&sha, s2c_point_tag, sizeof(s2c_point_tag));
return secp256k1_ec_commit_verify(&r, &original_r, &sha, data32, 32);
}

#endif
Loading

0 comments on commit c1904b1

Please sign in to comment.