diff --git a/examples/musig.c b/examples/musig.c index 7cd664af9..1106d8742 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; } diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index 17ddf7d20..45f4d32e0 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -223,16 +223,77 @@ SECP256K1_API int secp256k1_musig_pubkey_agg( size_t n_pubkeys ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(5); -/** Tweak an x-only public key in a given keyagg_cache by adding - * the generator multiplied with `tweak32` to it. +/** Obtain the aggregate public key from a keyagg_cache. + * + * This is only useful if you need the non-xonly public key, in particular for + * ordinary (non-xonly) tweaking or batch-verifying multiple key aggregations + * (not implemented). + * + * Returns: 0 if the arguments are invalid, 1 otherwise + * Args: ctx: pointer to a context object + * Out: agg_pk: the MuSig-aggregated public key. + * In: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by + * `musig_pubkey_agg` + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_get( + const secp256k1_context* ctx, + secp256k1_pubkey *agg_pk, + secp256k1_musig_keyagg_cache *keyagg_cache +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Apply ordinary "EC" tweaking to a public key in a given keyagg_cache by + * adding the generator multiplied with `tweak32` to it. This is useful for + * deriving child keys from an aggregate public key via BIP32. + * + * The tweaking method is the same as `secp256k1_ec_pubkey_tweak_add`. So after + * the following pseudocode buf and buf2 have identical contents (absent + * earlier failures). + * + * secp256k1_musig_pubkey_agg(..., keyagg_cache, pubkeys, ...) + * secp256k1_musig_pubkey_get(..., agg_pk, keyagg_cache) + * secp256k1_musig_pubkey_ec_tweak_add(..., output_pk, tweak32, keyagg_cache) + * secp256k1_ec_pubkey_serialize(..., buf, output_pk) + * secp256k1_ec_pubkey_tweak_add(..., agg_pk, tweak32) + * secp256k1_ec_pubkey_serialize(..., buf2, agg_pk) + * + * This function is required if you want to _sign_ for a tweaked aggregate key. + * On the other hand, if you are only computing a public key, but not intending + * to create a signature for it, you can just use + * `secp256k1_ec_pubkey_tweak_add`. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for verification + * Out: output_pubkey: pointer to a public key to store the result. Will be set + * to an invalid value if this function returns 0. If you + * do not need it, this arg can be NULL. + * In/Out: keyagg_cache: pointer to a `musig_keyagg_cache` struct initialized by + * `musig_pubkey_agg` + * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid + * according to `secp256k1_ec_seckey_verify`, this function + * returns 0. For uniformly random 32-byte arrays the + * chance of being invalid is negligible (around 1 in + * 2^128). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_ec_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *output_pubkey, + secp256k1_musig_keyagg_cache *keyagg_cache, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Apply x-only tweaking to a public key in a given keyagg_cache by adding the + * generator multiplied with `tweak32` to it. This is useful for creating + * Taproot outputs. * * The tweaking method is the same as `secp256k1_xonly_pubkey_tweak_add`. So in * the following pseudocode xonly_pubkey_tweak_add_check (absent earlier * failures) returns 1. * * secp256k1_musig_pubkey_agg(..., agg_pk, keyagg_cache, pubkeys, ...) - * secp256k1_musig_pubkey_tweak_add(..., output_pubkey, tweak32, keyagg_cache) - * secp256k1_xonly_pubkey_serialize(..., buf, output_pubkey) + * secp256k1_musig_pubkey_xonly_tweak_add(..., output_pk, tweak32, keyagg_cache) + * secp256k1_xonly_pubkey_serialize(..., buf, output_pk) * secp256k1_xonly_pubkey_tweak_add_check(..., buf, ..., agg_pk, tweak32) * * This function is required if you want to _sign_ for a tweaked aggregate key. @@ -255,7 +316,7 @@ SECP256K1_API int secp256k1_musig_pubkey_agg( * chance of being invalid is negligible (around 1 in * 2^128). */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add( +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_xonly_tweak_add( const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, diff --git a/src/modules/musig/keyagg_impl.h b/src/modules/musig/keyagg_impl.h index 5299edca8..c7f18b370 100644 --- a/src/modules/musig/keyagg_impl.h +++ b/src/modules/musig/keyagg_impl.h @@ -244,7 +244,21 @@ int secp256k1_musig_pubkey_agg(const secp256k1_context* ctx, secp256k1_scratch_s return 1; } -int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { +int secp256k1_musig_pubkey_get(const secp256k1_context* ctx, secp256k1_pubkey *agg_pk, secp256k1_musig_keyagg_cache *keyagg_cache) { + secp256k1_keyagg_cache_internal cache_i; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(agg_pk != NULL); + memset(agg_pk, 0, sizeof(*agg_pk)); + ARG_CHECK(keyagg_cache != NULL); + + if(!secp256k1_keyagg_cache_load(ctx, &cache_i, keyagg_cache)) { + return 0; + } + secp256k1_pubkey_save(agg_pk, &cache_i.pk); + return 1; +} + +static int secp256k1_musig_pubkey_tweak_add_internal(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32, int xonly) { secp256k1_keyagg_cache_internal cache_i; int overflow = 0; secp256k1_scalar tweak; @@ -263,7 +277,7 @@ int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pub if (overflow) { return 0; } - if (secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { + if (xonly && secp256k1_extrakeys_ge_even_y(&cache_i.pk)) { cache_i.internal_key_parity ^= 1; secp256k1_scalar_negate(&cache_i.tweak, &cache_i.tweak); } @@ -280,4 +294,12 @@ int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pub return 1; } +int secp256k1_musig_pubkey_ec_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { + return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 0); +} + +int secp256k1_musig_pubkey_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32) { + return secp256k1_musig_pubkey_tweak_add_internal(ctx, output_pubkey, keyagg_cache, tweak32, 1); +} + #endif diff --git a/src/modules/musig/session_impl.h b/src/modules/musig/session_impl.h index 7cdcebe3e..b85881c47 100644 --- a/src/modules/musig/session_impl.h +++ b/src/modules/musig/session_impl.h @@ -538,56 +538,65 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, secp256k1_musig_p * The following public keys arise as intermediate steps: * - P[i] is the i-th public key with corresponding secret key x[i] * P[i] := x[i]*G - * - P_agg is the aggregate public key - * P_agg := mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]| - * - P_tweak[i] is the tweaked public key after the i-th tweaking operation - * P_tweak[0] := P_agg - * P_tweak[i] := |P_tweak[i-1]| + t[i]*G for i = 1, ..., m + * - P_agg[0] is the aggregate public key + * P_agg[0] := mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]| + * - P_agg[i] for 1 <= i <= m is the tweaked public key after the i-th + * tweaking operation. There are two types of tweaking: x-only and ordinary + * "EC" tweaking. We define a boolean predicate xonly(i) that is true if + * the i-th tweaking operation is x-only tweaking and false otherwise + * (ordinary tweaking). + * Let + * P_agg[i] := f(i, P_agg[i-1]) + t[i]*G for i = 1, ..., m + * where f(i, X) := |X| if xonly(i) + * f(i, X) := X otherwise * * Note that our goal is to produce a partial signature corresponding to - * the final public key after m tweaking operations P_final = |P_tweak[m]|. + * the final public key after m tweaking operations P_final = |P_agg[m]|. * - * Define d[i], d_agg, and d_tweak[i] so that: + * Define d[i] for 0 <= i <= n-1 and d_agg[i] for 0 <= i <= m so that: * - |P[i]| = d[i]*P[i] - * - |P_agg| = d_agg*P_agg - * - |P_tweak[i]| = d_tweak[i]*P_tweak[i] + * - f(i+1, P_agg[i]) = d_agg[i]*P_agg[i] for 0 <= i <= m - 1 + * - |P_agg[m]| = d_agg[m]*P_agg[m] * - * In other words, d[i] = 1 if P[i] has even y coordinate, -1 otherwise; - * similarly for d_agg and d_tweak[i]. + * In other words, d[i] = 1 if P[i] has even y coordinate, -1 otherwise. + * For 0 <= i <= m-1, d_agg[i] is -1 if and only if xonly(i+1) is true and + * P_agg[i] has an odd Y coordinate. * - * The (xonly) final public key is P_final = |P_tweak[m]| - * = d_tweak[m]*P_tweak[m] - * = d_tweak[m]*(|P_tweak[m-1]| + t[m]*G) - * = d_tweak[m]*(d_tweak[m-1]*(|P_tweak[m-2]| + t[m-1]*G) + t[m]*G) - * = d_tweak[m]*...*d_tweak[1]*|P_agg| + (d_tweak[m]*t[m]+...+*d_tweak[1]*t[1])*G. - * To simplify the equation let us define - * t := d_tweak[m]*t[m]+...+*d_tweak[1]*t[1] - * d_tweak := d_tweak[m]*...*d_tweak[1]. + * The (x-only) final public key is P_final = |P_agg[m]| + * = d_agg[m]*P_agg[m] + * = d_agg[m]*(f(m, P_agg[m-1]) + t[m]*G) + * = d_agg[m]*(d_agg[m-1]*(f(m-1, P_agg[m-2]) + t[m-1]*G) + t[m]*G) + * = d_agg[m]*...*d_agg[0]*P_agg[0] + (d_agg[m]*t[m]+...+*d_agg[1]*t[1])*G. + * To simplify the equation let us define + * d_agg := d_agg[m]*...*d_agg[0]. + * t := d_agg[m]*t[m]+...+*d_agg[1]*t[1] if m > 0, otherwise t := 0 * Then we have * P_final - t*G - * = d_tweak*|P_agg| - * = d_tweak*d_agg*P_agg - * = d_tweak*d_agg*(mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]|) - * = d_tweak*d_agg*(d[0]*mu[0]*P[0] + ... + d[n-1]*mu[n-1]*P[n-1]) - * = sum((d_tweak*d_agg*d[i])*mu[i]*x[i])*G. + * = d_agg*P_agg[0] + * = d_agg*(mu[0]*|P[0]| + ... + mu[n-1]*|P[n-1]|) + * = d_agg*(d[0]*mu[0]*P[0] + ... + d[n-1]*mu[n-1]*P[n-1]) + * = sum((d_agg*d[i])*mu[i]*x[i])*G. * * Thus whether signer i should use the negated x[i] depends on the product - * d_tweak[m]*...*d_tweak[1]*d_agg*d[i]. In other words, negate if and only + * d_agg[m]*...*d_agg[1]*d_agg[0]*d[i]. In other words, negate if and only * if the following holds: - * (P[i] has odd y) XOR (P_agg has odd y) - * XOR (P_tweak[1] has odd y) XOR ... XOR (P_tweak[m] has odd y) + * (P[i] has odd y) XOR (xonly(1) and P_agg[0] has odd y) + * XOR (xonly(2) and P_agg[1] has odd y) + * XOR ... XOR (xonly(m) and P_agg[m-1] has odd y) + * XOR (P_agg[m] has odd y) * * Let us now look at how the terms in the equation correspond to the if * condition below for some values of m: - * m = 0: P_i has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * m = 0: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) + * P_agg[0] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) * cache_i.internal_key_parity = 0 - * m = 1: P_i has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg has odd y = cache_i.internal_key_parity - * P_tweak[1] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) - * m = 2: P_i has odd y = secp256k1_fe_is_odd(&pk.y) - * P_agg has odd y XOR P_tweak[1] has odd y = cache_i.internal_key_parity - * P_tweak[2] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * m = 1: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) + * xonly(1) and P_agg[0] has odd y = cache_i.internal_key_parity + * P_agg[1] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) + * m = 2: P[i] has odd y = secp256k1_fe_is_odd(&pk.y) + * (xonly(1) and P_agg[0] has odd y) + XOR (xonly(2) and P_agg[1] has odd y) = cache_i.internal_key_parity + * P_agg[2] has odd y = secp256k1_fe_is_odd(&cache_i.pk.y) * etc. */ if ((secp256k1_fe_is_odd(&pk.y) @@ -674,7 +683,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 /* When producing a partial signature, signer i uses a possibly * negated secret key: * - * sk[i] = (d_tweak*d_agg*d[i])*x[i] + * sk[i] = (d_agg*d[i])*x[i] * * to ensure that the aggregate signature will correspond to * an aggregate public key with even Y coordinate (see the @@ -698,14 +707,14 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 * The verifier doesn't have access to sk[i]*G, but can construct * it using the xonly public key |P[i]| as follows: * - * sk[i]*G = d_tweak*d_agg*d[i]*x[i]*G - * = d_tweak*d_agg*d[i]*P[i] - * = d_tweak*d_agg*|P[i]| + * sk[i]*G = d_agg*d[i]*x[i]*G + * = d_agg*d[i]*P[i] + * = d_agg*|P[i]| * - * The if condition below is true whenever d_tweak*d_agg is - * negative (again, see the explanation in musig_partial_sign). In - * this case, the verifier negates e which will have the same end - * result as negating |P[i]|, since they are multiplied later anyway. + * The if condition below is true whenever d_agg is negative (again, see the + * explanation in musig_partial_sign). In this case, the verifier negates e + * which will have the same end result as negating |P[i]|, since they are + * multiplied later anyway. */ if (secp256k1_fe_is_odd(&cache_i.pk.y) != cache_i.internal_key_parity) { diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 69a91c160..70512360b 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -140,6 +140,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { unsigned char aggnonce_ser[66]; unsigned char msg[32]; secp256k1_xonly_pubkey agg_pk; + secp256k1_pubkey full_agg_pk; secp256k1_musig_keyagg_cache keyagg_cache; secp256k1_musig_keyagg_cache invalid_keyagg_cache; secp256k1_musig_session session; @@ -243,38 +244,52 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_pubkey_agg(sign, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); CHECK(secp256k1_musig_pubkey_agg(vrfy, scratch, &agg_pk, &keyagg_cache, pk_ptr, 2) == 1); - /** Tweaking **/ + /* pubkey_get */ ecount = 0; + CHECK(secp256k1_musig_pubkey_get(none, &full_agg_pk, &keyagg_cache) == 1); + CHECK(secp256k1_musig_pubkey_get(none, NULL, &keyagg_cache) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubkey_get(none, &full_agg_pk, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_memcmp_var(&full_agg_pk, zeros68, sizeof(full_agg_pk)) == 0); + + /** Tweaking **/ { - secp256k1_pubkey tmp_output_pk; - secp256k1_musig_keyagg_cache tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - /* Reset keyagg_cache */ - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(none, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(sign, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, NULL, &tmp_keyagg_cache, tweak) == 1); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, NULL, tweak) == 0); - CHECK(ecount == 1); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, NULL) == 0); - CHECK(ecount == 2); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &tmp_keyagg_cache, max64) == 0); - CHECK(ecount == 2); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); - tmp_keyagg_cache = keyagg_cache; - /* Uninitialized keyagg_cache */ - CHECK(secp256k1_musig_pubkey_tweak_add(vrfy, &tmp_output_pk, &invalid_keyagg_cache, tweak) == 0); - CHECK(ecount == 3); - CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + int (*tweak_func[2]) (const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *tweak32); + tweak_func[0] = secp256k1_musig_pubkey_ec_tweak_add; + tweak_func[1] = secp256k1_musig_pubkey_xonly_tweak_add; + for (i = 0; i < 2; i++) { + secp256k1_pubkey tmp_output_pk; + secp256k1_musig_keyagg_cache tmp_keyagg_cache = keyagg_cache; + ecount = 0; + CHECK((*tweak_func[i])(ctx, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + /* Reset keyagg_cache */ + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(none, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(sign, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, NULL, &tmp_keyagg_cache, tweak) == 1); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, NULL, tweak) == 0); + CHECK(ecount == 1); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &tmp_keyagg_cache, max64) == 0); + CHECK(ecount == 2); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + tmp_keyagg_cache = keyagg_cache; + /* Uninitialized keyagg_cache */ + CHECK((*tweak_func[i])(vrfy, &tmp_output_pk, &invalid_keyagg_cache, tweak) == 0); + CHECK(ecount == 3); + CHECK(memcmp_and_randomize(tmp_output_pk.data, zeros68, sizeof(tmp_output_pk.data)) == 0); + } } /** Session creation **/ @@ -841,7 +856,8 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* agg_pk, const unsigne CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, sizeof(msg), agg_pk) == 1); } -/* Create aggregate public key P[0], tweak multiple times and test signing. */ +/* Create aggregate public key P[0], tweak multiple times (using xonly and + * ordinary tweaking) and test signing. */ void musig_tweak_test(secp256k1_scratch_space *scratch) { unsigned char sk[2][32]; secp256k1_xonly_pubkey pk[2]; @@ -861,22 +877,35 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) { /* Compute P0 = keyagg(pk0, pk1) and test signing for it */ CHECK(secp256k1_musig_pubkey_agg(ctx, scratch, &P_xonly[0], &keyagg_cache, pk_ptr, 2) == 1); musig_tweak_test_helper(&P_xonly[0], sk[0], sk[1], &keyagg_cache); + CHECK(secp256k1_musig_pubkey_get(ctx, &P[0], &keyagg_cache)); - /* Compute Pi = |Pj| + tweaki*G where where j = i-1 and try signing for - * that key. The function |.| normalizes the point to have an even - * X-coordinate. This results in ordinary "xonly-tweaking". */ + /* Compute Pi = f(Pj) + tweaki*G where where j = i-1 and try signing for + * that key. If xonly is set to true, the function f is normalizes the input + * point to have an even X-coordinate ("xonly-tweaking"). + * Otherwise, the function f is the identity function. */ for (i = 1; i < N_TWEAKS; i++) { unsigned char tweak[32]; int P_parity; - unsigned char P_serialized[32]; + int xonly = secp256k1_testrand_bits(1); secp256k1_testrand256(tweak); - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, &P[i], &keyagg_cache, tweak) == 1); + if (xonly) { + CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, &P[i], &keyagg_cache, tweak) == 1); + } else { + CHECK(secp256k1_musig_pubkey_ec_tweak_add(ctx, &P[i], &keyagg_cache, tweak) == 1); + } CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &P_xonly[i], &P_parity, &P[i])); - CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P_xonly[i])); /* Check that musig_pubkey_tweak_add produces same result as - * xonly_pubkey_tweak_add. */ - CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, P_serialized, P_parity, &P_xonly[i-1], tweak) == 1); + * xonly_pubkey_tweak_add or ec_pubkey_tweak_add. */ + if (xonly) { + unsigned char P_serialized[32]; + CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P_xonly[i])); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, P_serialized, P_parity, &P_xonly[i-1], tweak) == 1); + } else { + secp256k1_pubkey tmp_key = P[i-1]; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &tmp_key, tweak)); + CHECK(memcmp(&tmp_key, &P[i], sizeof(tmp_key)) == 0); + } /* Test signing for P[i] */ musig_tweak_test_helper(&P_xonly[i], sk[0], sk[1], &keyagg_cache); } @@ -1114,7 +1143,7 @@ void musig_test_vectors_noncegen(void) { } } -void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char *tweak, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) { +void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, int *fin_nonce_parity, unsigned char *sig, const unsigned char *secnonce_bytes, const unsigned char *agg_pubnonce_ser, const unsigned char *sk, const unsigned char *msg, const unsigned char *tweak, int xonly_tweak, const secp256k1_pubkey *adaptor, const unsigned char **pk_ser, int signer_pos) { secp256k1_keypair signer_keypair; secp256k1_musig_secnonce secnonce; secp256k1_xonly_pubkey pk[3]; @@ -1135,7 +1164,11 @@ void musig_test_vectors_sign_helper(secp256k1_musig_keyagg_cache *keyagg_cache, } CHECK(secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, keyagg_cache, pk_ptr, 3) == 1); if (tweak != NULL) { - CHECK(secp256k1_musig_pubkey_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + if (xonly_tweak) { + CHECK(secp256k1_musig_pubkey_xonly_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + } else { + CHECK(secp256k1_musig_pubkey_ec_tweak_add(ctx, NULL, keyagg_cache, tweak) == 1); + } } memcpy(&secnonce.data[0], secp256k1_musig_secnonce_magic, 4); memcpy(&secnonce.data[4], secnonce_bytes, sizeof(secnonce.data) - 4); @@ -1214,7 +1247,7 @@ void musig_test_vectors_sign(void) { 0x20, 0xA1, 0x81, 0x85, 0x5F, 0xD8, 0xBD, 0xB7, 0xF1, 0x27, 0xBB, 0x12, 0x40, 0x3B, 0x4D, 0x3B, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 0); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 0); /* TODO: remove when test vectors are not expected to change anymore */ /* int k, l; */ /* printf("const unsigned char sig_expected[32] = {\n"); */ @@ -1243,7 +1276,7 @@ void musig_test_vectors_sign(void) { 0x81, 0x38, 0xDA, 0xEC, 0x5C, 0xB2, 0x0A, 0x35, 0x7C, 0xEC, 0xA7, 0xC8, 0x42, 0x42, 0x95, 0xEA, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 1); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 1); /* Check that the description of the test vector is correct */ CHECK(musig_test_pk_parity(&keyagg_cache) == 0); CHECK(musig_test_is_second_pk(&keyagg_cache, sk)); @@ -1259,7 +1292,7 @@ void musig_test_vectors_sign(void) { 0xE6, 0xA7, 0xF7, 0xFB, 0xE1, 0x5C, 0xDC, 0xAF, 0xA4, 0xA3, 0xD1, 0xBC, 0xAA, 0xBC, 0x75, 0x17, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, NULL, pk, 2); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, NULL, pk, 2); /* Check that the description of the test vector is correct */ CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(fin_nonce_parity == 0); @@ -1267,7 +1300,7 @@ void musig_test_vectors_sign(void) { CHECK(memcmp(sig, sig_expected, 32) == 0); } { - /* This is a test that includes a public key tweak. */ + /* This is a test that includes an xonly public key tweak. */ const unsigned char sig_expected[32] = { 0x5E, 0x24, 0xC7, 0x49, 0x6B, 0x56, 0x5D, 0xEB, 0xC3, 0xB9, 0x63, 0x9E, 0x6F, 0x13, 0x04, 0xA2, @@ -1280,13 +1313,34 @@ void musig_test_vectors_sign(void) { 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, }; - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, NULL, pk, 2); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, 1, NULL, pk, 2); CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); CHECK(fin_nonce_parity == 1); CHECK(memcmp(sig, sig_expected, 32) == 0); } + { + /* This is a test that includes an ordinary public key tweak. */ + const unsigned char sig_expected[32] = { + 0x78, 0x40, 0x8D, 0xDC, 0xAB, 0x48, 0x13, 0xD1, + 0x39, 0x4C, 0x97, 0xD4, 0x93, 0xEF, 0x10, 0x84, + 0x19, 0x5C, 0x1D, 0x4B, 0x52, 0xE6, 0x3E, 0xCD, + 0x7B, 0xC5, 0x99, 0x16, 0x44, 0xE4, 0x4D, 0xDD, + }; + const unsigned char tweak[32] = { + 0xE8, 0xF7, 0x91, 0xFF, 0x92, 0x25, 0xA2, 0xAF, + 0x01, 0x02, 0xAF, 0xFF, 0x4A, 0x9A, 0x72, 0x3D, + 0x96, 0x12, 0xA6, 0x82, 0xA2, 0x5E, 0xBE, 0x79, + 0x80, 0x2B, 0x26, 0x3C, 0xDF, 0xCD, 0x83, 0xBB, + }; + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, tweak, 0, NULL, pk, 2); + + CHECK(musig_test_pk_parity(&keyagg_cache) == 1); + CHECK(!musig_test_is_second_pk(&keyagg_cache, sk)); + CHECK(fin_nonce_parity == 0); + CHECK(memcmp(sig, sig_expected, 32) == 0); + } { /* This is a test that includes an adaptor. */ const unsigned char sig_expected[32] = { @@ -1303,7 +1357,7 @@ void musig_test_vectors_sign(void) { }; secp256k1_pubkey pub_adaptor; CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor) == 1); - musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, &pub_adaptor, pk, 2); + musig_test_vectors_sign_helper(&keyagg_cache, &fin_nonce_parity, sig, secnonce, agg_pubnonce, sk, msg, NULL, 0, &pub_adaptor, pk, 2); CHECK(musig_test_pk_parity(&keyagg_cache) == 1); CHECK(!musig_test_is_second_pk(&keyagg_cache, sk));