From ea28c7a9dfeaf83d64cc8cbe0f33769fb2ef9c0c Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Sun, 11 Sep 2022 19:20:10 +0200 Subject: [PATCH] add Schnorr anti-exfil functions 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. --- include/secp256k1_schnorrsig.h | 63 +++++++++++++++++++++++++++ src/modules/schnorrsig/main_impl.h | 67 +++++++++++++++++++++++++++++ src/modules/schnorrsig/tests_impl.h | 45 +++++++++++++++++++ 3 files changed, 175 insertions(+) diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 937fc7a44a..c1c0fa92a0 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -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. @@ -257,6 +260,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 diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 62652cbe8c..31df2fba2a 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -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 diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 04b98ef1e9..05eb2a743b 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -1055,6 +1055,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. */ + { + secp256k1_testrand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + secp256k1_testrand256_test(host_msg); + secp256k1_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(); @@ -1071,6 +1115,7 @@ void run_schnorrsig_tests(void) { } test_schnorrsig_taproot(); test_s2c_opening(); + test_s2c_anti_exfil(); } #endif