Skip to content

Commit

Permalink
add Schnorr anti-exfil functions
Browse files Browse the repository at this point in the history
These functions allow to perform the anti-exfil protocol. It is very
similar to the implementation of the same protocol for ECDSA in
ElementsProject/secp256k1-zkp.

The opening struct can't be use in
`secp256k1_schnorrsig_anti_exfil_signer_commit()` as it contains the
``nonce_is_negated` field, which can only be set correctly during
signing with s2c data. As a result, we must use the opening in the
commitment verification, so we also must check that the signer
commitment is the same as the one used during signing. The alternative
is to only compare the x-coordinate, in which case the opening struct
could skip `nonce_is_negated` and the struct could be reused in
`secp256k1_schnorrsig_anti_exfil_signer_commit()`, but it seems to
have a downside that it would prevent batch-verification of the
commitments.
  • Loading branch information
benma committed Oct 10, 2024
1 parent 76e225a commit 56b34e8
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 0 deletions.
63 changes: 63 additions & 0 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ typedef struct {
int nonce_is_negated;
} secp256k1_schnorrsig_s2c_opening;

/** The signer commitment in the anti-exfil protocol is the original public nonce. */
typedef secp256k1_pubkey secp256k1_schnorrsig_anti_exfil_signer_commitment;

/** Parse a sign-to-contract opening.
*
* Returns: 1 if the opening was fully valid.
Expand Down Expand Up @@ -265,6 +268,66 @@ SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit(
const secp256k1_schnorrsig_s2c_opening *opening
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);


/** Create the initial host commitment to `rho`. Part of the Anti-Exfil Protocol.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL)
* In: rand32: the 32-byte randomness to commit to (cannot be NULL). It must come from
* a cryptographically secure RNG. As per the protocol, this value must not
* be revealed to the client until after the host has received the client
* commitment.
*/
SECP256K1_API int secp256k1_schnorrsig_anti_exfil_host_commit(
const secp256k1_context* ctx,
unsigned char* rand_commitment32,
const unsigned char* rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Compute signer's original nonce. Part of the Anti-Exfil Protocol.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: signer_commitment: where the signer's public nonce will be placed. (cannot be NULL)
* In: msg: the message to be signed (cannot be NULL)
* msglen: length of the message
* keypair: pointer to an initialized keypair (cannot be NULL).
* rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL)
*/
SECP256K1_API int secp256k1_schnorrsig_anti_exfil_signer_commit(
const secp256k1_context* ctx,
secp256k1_schnorrsig_anti_exfil_signer_commitment* signer_commitment,
const unsigned char *msg,
size_t msglen,
const secp256k1_keypair *keypair,
const unsigned char* rand_commitment32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);

/** Verify a signature was correctly constructed using the Anti-Exfil Protocol.
*
* Returns: 1: the signature is valid and contains a commitment to host_data32
* 0: failure
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig64: pointer to the 64-byte signature to verify.
* msg: the message being verified. Can only be NULL if msglen is 0.
* msglen: length of the message
* pubkey: pointer to an x-only public key to verify with (cannot be NULL)
* host_data32: the 32-byte data provided by the host (cannot be NULL)
* signer_commitment: signer commitment produced by `secp256k1_schnorrsig_anti_exfil_signer_commit()`.
* opening: the s2c opening provided by the signer (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_anti_exfil_host_verify(
const secp256k1_context* ctx,
const unsigned char *sig64,
const unsigned char *msg,
size_t msglen,
const secp256k1_xonly_pubkey *pubkey,
const unsigned char *host_data32,
const secp256k1_schnorrsig_anti_exfil_signer_commitment *signer_commitment,
const secp256k1_schnorrsig_s2c_opening *opening
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);

#ifdef __cplusplus
}
#endif
Expand Down
67 changes: 67 additions & 0 deletions src/modules/schnorrsig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,4 +397,71 @@ int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const u
return secp256k1_ec_commit_verify(&r, &original_r, &sha, data32, 32);
}

int secp256k1_schnorrsig_anti_exfil_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) {
secp256k1_sha256 sha;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(rand_commitment32 != NULL);
ARG_CHECK(rand32 != NULL);

secp256k1_sha256_initialize_tagged(&sha, s2c_data_tag, sizeof(s2c_data_tag));
secp256k1_sha256_write(&sha, rand32, 32);
secp256k1_sha256_finalize(&sha, rand_commitment32);

return 1;
}

int secp256k1_schnorrsig_anti_exfil_signer_commit(const secp256k1_context* ctx, secp256k1_schnorrsig_anti_exfil_signer_commitment* signer_commitment, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, const unsigned char* rand_commitment32) {
secp256k1_scalar sk;
secp256k1_scalar k;
secp256k1_gej rj;
secp256k1_ge pk;
secp256k1_ge r;
unsigned char buf[32] = { 0 };
unsigned char pk_buf[32];
unsigned char seckey[32];
int ret = 1;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(msg != NULL || msglen == 0);
ARG_CHECK(keypair != NULL);

ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair);
/* Because we are signing for a x-only pubkey, the secret key is negated
* before signing if the point corresponding to the secret key does not
* have an even Y. */
if (secp256k1_fe_is_odd(&pk.y)) {
secp256k1_scalar_negate(&sk, &sk);
}

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

ret &= !!secp256k1_nonce_function_bip340(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), (void*)rand_commitment32);
secp256k1_scalar_set_b32(&k, buf, NULL);
ret &= !secp256k1_scalar_is_zero(&k);
secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);

secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
secp256k1_ge_set_gej(&r, &rj);

secp256k1_pubkey_save(signer_commitment, &r);

return ret;
}

int secp256k1_schnorrsig_anti_exfil_host_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_schnorrsig_anti_exfil_signer_commitment *signer_commitment, const secp256k1_schnorrsig_s2c_opening *opening) {
/* Verify that the signer commitment matches the opening made when signing */
if (secp256k1_ec_pubkey_cmp(ctx, signer_commitment, &opening->original_pubnonce)) {
return 0;
}
/* Verify signature */
if (!secp256k1_schnorrsig_verify(ctx, sig64, msg, msglen, pubkey)) {
return 0;
}
/* Verify that the host nonce contribution was committed to */
return secp256k1_schnorrsig_verify_s2c_commit(ctx, sig64, host_data32, opening);
}

#endif
45 changes: 45 additions & 0 deletions src/modules/schnorrsig/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,50 @@ void test_s2c_opening(void) {
} while(i < COUNT);
}

/* Uses the s2c primitives to perform the anti-exfil protocol */
void test_s2c_anti_exfil(void) {
unsigned char sk[32];
secp256k1_xonly_pubkey pk;
secp256k1_keypair keypair;
unsigned char host_msg[32];
unsigned char host_commitment[32];
unsigned char host_nonce_contribution[32];
secp256k1_schnorrsig_s2c_opening s2c_opening;
secp256k1_schnorrsig_anti_exfil_signer_commitment signer_commitment;
unsigned char sig[64];
/* Generate a random key, message. */
{
testrand256(sk);
CHECK(secp256k1_keypair_create(CTX, &keypair, sk));
CHECK(secp256k1_keypair_xonly_pub(CTX, &pk, NULL, &keypair));
testrand256_test(host_msg);
testrand256_test(host_nonce_contribution);
}

/* Protocol step 1. */
/* Make host commitment. */
CHECK(secp256k1_schnorrsig_anti_exfil_host_commit(CTX, host_commitment, host_nonce_contribution) == 1);

/* Protocol step 2. */
/* Make signer commitment */
CHECK(secp256k1_schnorrsig_anti_exfil_signer_commit(CTX, &signer_commitment, host_msg, sizeof(host_msg), &keypair, host_commitment) == 1);

/* Protocol step 3: host_nonce_contribution send to signer to be used in step 4. */

/* Protocol step 4. */
/* Sign with host nonce contribution */
{
secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT;
extraparams.s2c_opening = &s2c_opening;
extraparams.s2c_data32 = host_nonce_contribution;
CHECK(secp256k1_schnorrsig_sign_custom(CTX, sig, host_msg, sizeof(host_msg), &keypair, &extraparams) == 1);
}

/* Protocol step 5. */
/* Verify commitment and signature */
CHECK(secp256k1_schnorrsig_anti_exfil_host_verify(CTX, sig, host_msg, sizeof(host_msg), &pk, host_nonce_contribution, &signer_commitment, &s2c_opening) == 1);
}

void run_schnorrsig_tests(void) {
int i;
run_nonce_function_bip340_tests();
Expand All @@ -1129,6 +1173,7 @@ void run_schnorrsig_tests(void) {
}
test_schnorrsig_taproot();
test_s2c_opening();
test_s2c_anti_exfil();
}

#endif

0 comments on commit 56b34e8

Please sign in to comment.