From 57a17929fc0056efb5436a6001597d656591e1ad Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 26 Oct 2021 15:52:10 +0000 Subject: [PATCH] musig: add ordinary and xonly tweaking to the example --- examples/musig.c | 68 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index 7cd664af..1106d874 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -52,14 +52,51 @@ int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_s return 1; } +/* Tweak the pubkey corresponding to the provided keyagg cache, update the cache + * and return the tweaked aggregate pk. */ +int tweak(const secp256k1_context* ctx, secp256k1_xonly_pubkey *agg_pk, secp256k1_musig_keyagg_cache *cache) { + secp256k1_pubkey output_pk; + unsigned char ordinary_tweak[32] = "this could be a BIP32 tweak...."; + unsigned char xonly_tweak[32] = "this could be a taproot tweak.."; + + + /* Ordinary tweaking which, for example, allows deriving multiple child + * public keys from a single aggregate key using BIP32 */ + if (!secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, cache, ordinary_tweak)) { + return 0; + } + /* Note that we did not provided an output_pk argument, because the + * resulting pk is also saved in the cache and so if one is just interested + * in signing the output_pk argument is unnecessary. On the other hand, if + * one is not interested in signing, the same output_pk can be obtained by + * calling `secp256k1_musig_pubkey_get` right after key aggregation to get + * the full pubkey and then call `secp256k1_ec_pubkey_tweak_add`. */ + + /* Xonly tweaking which, for example, allows creating taproot commitments */ + if (!secp256k1_musig_pubkey_xonly_tweak_add(ctx, &output_pk, cache, xonly_tweak)) { + return 0; + } + /* Note that if we wouldn't care about signing, we can arrive at the same + * output_pk by providing the untweaked public key to + * `secp256k1_xonly_pubkey_tweak_add` (after converting it to an xonly pubkey + * if necessary with `secp256k1_xonly_pubkey_from_pubkey`). */ + + /* Now we convert the output_pk to an xonly pubkey to allow to later verify + * the Schnorr signature against it. For this purpose we can ignore the + * `pk_parity` output argument; we would need it if we would have to open + * the taproot commitment. */ + if (!secp256k1_xonly_pubkey_from_pubkey(ctx, agg_pk, NULL, &output_pk)) { + return 0; + } + return 1; +} + /* Sign a message hash with the given key pairs and store the result in sig */ -int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, unsigned char *sig64) { +int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const secp256k1_musig_keyagg_cache *cache, const unsigned char *msg32, unsigned char *sig64) { int i; - const secp256k1_xonly_pubkey *pubkeys[N_SIGNERS]; const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS]; const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS]; /* The same for all signers */ - secp256k1_musig_keyagg_cache cache; secp256k1_musig_session session; for (i = 0; i < N_SIGNERS; i++) { @@ -86,7 +123,6 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, msg32, NULL, NULL)) { return 0; } - pubkeys[i] = &signer[i].pubkey; pubnonces[i] = &signer[i].pubnonce; } /* Communication round 1: A production system would exchange public nonces @@ -94,21 +130,18 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st for (i = 0; i < N_SIGNERS; i++) { secp256k1_musig_aggnonce agg_pubnonce; - /* Create aggregate pubkey, aggregate nonce and initialize signer data */ - if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, &cache, pubkeys, N_SIGNERS)) { - return 0; - } + /* Create aggregate nonce and initialize the session */ if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) { return 0; } - if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, &cache, NULL)) { + if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, cache, NULL)) { return 0; } /* partial_sign will clear the secnonce by setting it to 0. That's because * you must _never_ reuse the secnonce (or use the same session_id to * create a secnonce). If you do, you effectively reuse the nonce and * leak the secret key. */ - if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, &cache, &session)) { + if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, cache, &session)) { return 0; } partial_sigs[i] = &signer[i].partial_sig; @@ -127,7 +160,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st * fine to first verify the aggregate sig, and only verify the individual * sigs if it does not work. */ - if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, &cache, &session)) { + if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, cache, &session)) { return 0; } } @@ -141,6 +174,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st struct signer signers[N_SIGNERS]; const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS]; secp256k1_xonly_pubkey agg_pk; + secp256k1_musig_keyagg_cache cache; unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; unsigned char sig[64]; @@ -156,13 +190,21 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st } printf("ok\n"); printf("Combining public keys..."); - if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, NULL, pubkeys_ptr, N_SIGNERS)) { + /* If you just want to aggregate and not sign the cache can be NULL */ + if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &cache, pubkeys_ptr, N_SIGNERS)) { + printf("FAILED\n"); + return 1; + } + printf("ok\n"); + printf("Tweaking................"); + /* Optionally tweak the aggregate key */ + if (!tweak(ctx, &agg_pk, &cache)) { printf("FAILED\n"); return 1; } printf("ok\n"); printf("Signing message........."); - if (!sign(ctx, signer_secrets, signers, msg, sig)) { + if (!sign(ctx, signer_secrets, signers, &cache, msg, sig)) { printf("FAILED\n"); return 1; }