From 82ea67c607cde6187d7082429d27b927dc21c0c6 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Tue, 5 Aug 2025 12:38:29 -0700 Subject: [PATCH] musig: Add MuSig2AggregatePubkeys variant that validates the aggregate A common pattern that MuSig2 functions will use is to aggregate the pubkeys to get the keyagg_cache and then validate the aggregated pubkey against a provided aggregate pubkey. A variant of MuSig2AggregatePubkeys is added which does that. The functionality of GetMuSig2KeyAggCache and GetCPubKeyFromMuSig2KeyAggCache are included in MuSig2AggregatePubkeys (and used internally) so there is no expectation that callers will need these so they are made static. --- src/musig.cpp | 18 +++++++++++++----- src/musig.h | 9 ++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/musig.cpp b/src/musig.cpp index c361a7ea890..7ebb8e55258 100644 --- a/src/musig.cpp +++ b/src/musig.cpp @@ -7,7 +7,7 @@ #include -bool GetMuSig2KeyAggCache(const std::vector& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache) +static bool GetMuSig2KeyAggCache(const std::vector& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache) { // Parse the pubkeys std::vector secp_pubkeys; @@ -29,7 +29,7 @@ bool GetMuSig2KeyAggCache(const std::vector& pubkeys, secp256k1_musig_k return true; } -std::optional GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache) +static std::optional GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache) { // Get the plain aggregated pubkey secp256k1_pubkey agg_pubkey; @@ -44,13 +44,21 @@ std::optional GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_ca return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len); } -std::optional MuSig2AggregatePubkeys(const std::vector& pubkeys) +std::optional MuSig2AggregatePubkeys(const std::vector& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional& expected_aggregate) { - secp256k1_musig_keyagg_cache keyagg_cache; if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) { return std::nullopt; } - return GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache); + std::optional agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache); + if (!agg_key.has_value()) return std::nullopt; + if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt; + return agg_key; +} + +std::optional MuSig2AggregatePubkeys(const std::vector& pubkeys) +{ + secp256k1_musig_keyagg_cache keyagg_cache; + return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt); } CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey) diff --git a/src/musig.h b/src/musig.h index 40da1f10b97..bb469df4a9a 100644 --- a/src/musig.h +++ b/src/musig.h @@ -18,11 +18,10 @@ struct secp256k1_musig_secnonce; using namespace util::hex_literals; constexpr uint256 MUSIG_CHAINCODE{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8}; -//! Create a secp256k1_musig_keyagg_cache from the pubkeys in their current order. This is necessary for most MuSig2 operations -bool GetMuSig2KeyAggCache(const std::vector& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache); -//! Retrieve the full aggregate pubkey from the secp256k1_musig_keyagg_cache -std::optional GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& cache); -//! Compute the full aggregate pubkey from the given participant pubkeys in their current order +//! Compute the full aggregate pubkey from the given participant pubkeys in their current order. +//! Outputs the secp256k1_musig_keyagg_cache and validates that the computed aggregate pubkey matches an expected aggregate pubkey. +//! This is necessary for most MuSig2 operations. +std::optional MuSig2AggregatePubkeys(const std::vector& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional& expected_aggregate); std::optional MuSig2AggregatePubkeys(const std::vector& pubkeys); //! Construct the BIP 328 synthetic xpub for a pubkey