Skip to content

Commit 25899bf

Browse files
committed
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.
1 parent d69625b commit 25899bf

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

include/secp256k1_schnorrsig.h

+63
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ typedef struct {
3838
int nonce_is_negated;
3939
} secp256k1_schnorrsig_s2c_opening;
4040

41+
/** The signer commitment in the anti-exfil protocol is the original public nonce. */
42+
typedef secp256k1_pubkey secp256k1_schnorrsig_anti_exfil_signer_commitment;
43+
4144
/** Parse a sign-to-contract opening.
4245
*
4346
* Returns: 1 if the opening was fully valid.
@@ -257,6 +260,66 @@ SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit(
257260
const secp256k1_schnorrsig_s2c_opening *opening
258261
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
259262

263+
264+
/** Create the initial host commitment to `rho`. Part of the Anti-Exfil Protocol.
265+
*
266+
* Returns 1 on success, 0 on failure.
267+
* Args: ctx: pointer to a context object (cannot be NULL)
268+
* Out: rand_commitment32: pointer to 32-byte array to store the returned commitment (cannot be NULL)
269+
* In: rand32: the 32-byte randomness to commit to (cannot be NULL). It must come from
270+
* a cryptographically secure RNG. As per the protocol, this value must not
271+
* be revealed to the client until after the host has received the client
272+
* commitment.
273+
*/
274+
SECP256K1_API int secp256k1_schnorrsig_anti_exfil_host_commit(
275+
const secp256k1_context* ctx,
276+
unsigned char* rand_commitment32,
277+
const unsigned char* rand32
278+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
279+
280+
/** Compute signer's original nonce. Part of the Anti-Exfil Protocol.
281+
*
282+
* Returns 1 on success, 0 on failure.
283+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
284+
* Out: signer_commitment: where the signer's public nonce will be placed. (cannot be NULL)
285+
* In: msg: the message to be signed (cannot be NULL)
286+
* msglen: length of the message
287+
* keypair: pointer to an initialized keypair (cannot be NULL).
288+
* rand_commitment32: the 32-byte randomness commitment from the host (cannot be NULL)
289+
*/
290+
SECP256K1_API int secp256k1_schnorrsig_anti_exfil_signer_commit(
291+
const secp256k1_context* ctx,
292+
secp256k1_schnorrsig_anti_exfil_signer_commitment* signer_commitment,
293+
const unsigned char *msg,
294+
size_t msglen,
295+
const secp256k1_keypair *keypair,
296+
const unsigned char* rand_commitment32
297+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
298+
299+
/** Verify a signature was correctly constructed using the Anti-Exfil Protocol.
300+
*
301+
* Returns: 1: the signature is valid and contains a commitment to host_data32
302+
* 0: failure
303+
* Args: ctx: a secp256k1 context object, initialized for verification.
304+
* In: sig64: pointer to the 64-byte signature to verify.
305+
* msg: the message being verified. Can only be NULL if msglen is 0.
306+
* msglen: length of the message
307+
* pubkey: pointer to an x-only public key to verify with (cannot be NULL)
308+
* host_data32: the 32-byte data provided by the host (cannot be NULL)
309+
* signer_commitment: signer commitment produced by `secp256k1_schnorrsig_anti_exfil_signer_commit()`.
310+
* opening: the s2c opening provided by the signer (cannot be NULL)
311+
*/
312+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_anti_exfil_host_verify(
313+
const secp256k1_context* ctx,
314+
const unsigned char *sig64,
315+
const unsigned char *msg,
316+
size_t msglen,
317+
const secp256k1_xonly_pubkey *pubkey,
318+
const unsigned char *host_data32,
319+
const secp256k1_schnorrsig_anti_exfil_signer_commitment *signer_commitment,
320+
const secp256k1_schnorrsig_s2c_opening *opening
321+
) 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);
322+
260323
#ifdef __cplusplus
261324
}
262325
#endif

src/modules/schnorrsig/main_impl.h

+67
Original file line numberDiff line numberDiff line change
@@ -397,4 +397,71 @@ int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const u
397397
return secp256k1_ec_commit_verify(&r, &original_r, &sha, data32, 32);
398398
}
399399

400+
int secp256k1_schnorrsig_anti_exfil_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) {
401+
secp256k1_sha256 sha;
402+
403+
VERIFY_CHECK(ctx != NULL);
404+
ARG_CHECK(rand_commitment32 != NULL);
405+
ARG_CHECK(rand32 != NULL);
406+
407+
secp256k1_sha256_initialize_tagged(&sha, s2c_data_tag, sizeof(s2c_data_tag));
408+
secp256k1_sha256_write(&sha, rand32, 32);
409+
secp256k1_sha256_finalize(&sha, rand_commitment32);
410+
411+
return 1;
412+
}
413+
414+
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) {
415+
secp256k1_scalar sk;
416+
secp256k1_scalar k;
417+
secp256k1_gej rj;
418+
secp256k1_ge pk;
419+
secp256k1_ge r;
420+
unsigned char buf[32] = { 0 };
421+
unsigned char pk_buf[32];
422+
unsigned char seckey[32];
423+
int ret = 1;
424+
425+
VERIFY_CHECK(ctx != NULL);
426+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
427+
ARG_CHECK(msg != NULL || msglen == 0);
428+
ARG_CHECK(keypair != NULL);
429+
430+
ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair);
431+
/* Because we are signing for a x-only pubkey, the secret key is negated
432+
* before signing if the point corresponding to the secret key does not
433+
* have an even Y. */
434+
if (secp256k1_fe_is_odd(&pk.y)) {
435+
secp256k1_scalar_negate(&sk, &sk);
436+
}
437+
438+
secp256k1_scalar_get_b32(seckey, &sk);
439+
secp256k1_fe_get_b32(pk_buf, &pk.x);
440+
441+
ret &= !!secp256k1_nonce_function_bip340(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), (void*)rand_commitment32);
442+
secp256k1_scalar_set_b32(&k, buf, NULL);
443+
ret &= !secp256k1_scalar_is_zero(&k);
444+
secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);
445+
446+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
447+
secp256k1_ge_set_gej(&r, &rj);
448+
449+
secp256k1_pubkey_save(signer_commitment, &r);
450+
451+
return ret;
452+
}
453+
454+
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) {
455+
/* Verify that the signer commitment matches the opening made when signing */
456+
if (secp256k1_ec_pubkey_cmp(ctx, signer_commitment, &opening->original_pubnonce)) {
457+
return 0;
458+
}
459+
/* Verify signature */
460+
if (!secp256k1_schnorrsig_verify(ctx, sig64, msg, msglen, pubkey)) {
461+
return 0;
462+
}
463+
/* Verify that the host nonce contribution was committed to */
464+
return secp256k1_schnorrsig_verify_s2c_commit(ctx, sig64, host_data32, opening);
465+
}
466+
400467
#endif

src/modules/schnorrsig/tests_impl.h

+45
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,50 @@ void test_s2c_opening(void) {
10551055
} while(i < count);
10561056
}
10571057

1058+
/* Uses the s2c primitives to perform the anti-exfil protocol */
1059+
void test_s2c_anti_exfil(void) {
1060+
unsigned char sk[32];
1061+
secp256k1_xonly_pubkey pk;
1062+
secp256k1_keypair keypair;
1063+
unsigned char host_msg[32];
1064+
unsigned char host_commitment[32];
1065+
unsigned char host_nonce_contribution[32];
1066+
secp256k1_schnorrsig_s2c_opening s2c_opening;
1067+
secp256k1_schnorrsig_anti_exfil_signer_commitment signer_commitment;
1068+
unsigned char sig[64];
1069+
/* Generate a random key, message. */
1070+
{
1071+
secp256k1_testrand256(sk);
1072+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
1073+
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
1074+
secp256k1_testrand256_test(host_msg);
1075+
secp256k1_testrand256_test(host_nonce_contribution);
1076+
}
1077+
1078+
/* Protocol step 1. */
1079+
/* Make host commitment. */
1080+
CHECK(secp256k1_schnorrsig_anti_exfil_host_commit(ctx, host_commitment, host_nonce_contribution) == 1);
1081+
1082+
/* Protocol step 2. */
1083+
/* Make signer commitment */
1084+
CHECK(secp256k1_schnorrsig_anti_exfil_signer_commit(ctx, &signer_commitment, host_msg, sizeof(host_msg), &keypair, host_commitment) == 1);
1085+
1086+
/* Protocol step 3: host_nonce_contribution send to signer to be used in step 4. */
1087+
1088+
/* Protocol step 4. */
1089+
/* Sign with host nonce contribution */
1090+
{
1091+
secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT;
1092+
extraparams.s2c_opening = &s2c_opening;
1093+
extraparams.s2c_data32 = host_nonce_contribution;
1094+
CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, host_msg, sizeof(host_msg), &keypair, &extraparams) == 1);
1095+
}
1096+
1097+
/* Protocol step 5. */
1098+
/* Verify commitment and signature */
1099+
CHECK(secp256k1_schnorrsig_anti_exfil_host_verify(ctx, sig, host_msg, sizeof(host_msg), &pk, host_nonce_contribution, &signer_commitment, &s2c_opening) == 1);
1100+
}
1101+
10581102
void run_schnorrsig_tests(void) {
10591103
int i;
10601104
run_nonce_function_bip340_tests();
@@ -1071,6 +1115,7 @@ void run_schnorrsig_tests(void) {
10711115
}
10721116
test_schnorrsig_taproot();
10731117
test_s2c_opening();
1118+
test_s2c_anti_exfil();
10741119
}
10751120

10761121
#endif

0 commit comments

Comments
 (0)