LCOV - code coverage report
Current view: top level - src - musig.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 20.3 % 177 36
Test Date: 2026-06-05 07:17:20 Functions: 31.2 % 16 5
Branches: 8.7 % 242 21

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2024-present The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       5                 :             : #include <musig.h>
       6                 :             : #include <key.h>
       7                 :             : #include <random.h>
       8                 :             : #include <support/allocators/secure.h>
       9                 :             : 
      10                 :             : #include <secp256k1_musig.h>
      11                 :             : 
      12                 :             : //! MuSig2 chaincode as defined by BIP 328
      13                 :             : using namespace util::hex_literals;
      14                 :             : constexpr uint256 MUSIG_CHAINCODE{
      15                 :             :     // Use immediate lambda to work around GCC-14 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117966
      16                 :             :     []() consteval { return uint256{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8}; }(),
      17                 :             : };
      18                 :             : 
      19                 :         130 : static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
      20                 :             : {
      21         [ +  + ]:         130 :     if (pubkeys.empty()) {
      22                 :             :         return false;
      23                 :             :     }
      24                 :             : 
      25                 :             :     // Parse the pubkeys
      26                 :         129 :     std::vector<secp256k1_pubkey> secp_pubkeys;
      27                 :         129 :     std::vector<const secp256k1_pubkey*> pubkey_ptrs;
      28         [ +  + ]:         479 :     for (const CPubKey& pubkey : pubkeys) {
      29   [ +  -  +  -  :         351 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) {
                   +  + ]
      30                 :             :             return false;
      31                 :             :         }
      32                 :             :     }
      33   [ -  +  +  - ]:         128 :     pubkey_ptrs.reserve(secp_pubkeys.size());
      34         [ +  + ]:         478 :     for (const secp256k1_pubkey& p : secp_pubkeys) {
      35         [ +  - ]:         350 :         pubkey_ptrs.push_back(&p);
      36                 :             :     }
      37                 :             : 
      38                 :             :     // Aggregate the pubkey
      39   [ -  +  +  -  :         128 :     if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) {
                   -  + ]
      40                 :           0 :         return false;
      41                 :             :     }
      42                 :             :     return true;
      43                 :         129 : }
      44                 :             : 
      45                 :         128 : static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
      46                 :             : {
      47                 :             :     // Get the plain aggregated pubkey
      48                 :         128 :     secp256k1_pubkey agg_pubkey;
      49         [ -  + ]:         128 :     if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) {
      50                 :           0 :         return std::nullopt;
      51                 :             :     }
      52                 :             : 
      53                 :             :     // Turn into CPubKey
      54                 :         128 :     unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE];
      55                 :         128 :     size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE;
      56                 :         128 :     secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED);
      57                 :         128 :     return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);
      58                 :             : }
      59                 :             : 
      60                 :         130 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate)
      61                 :             : {
      62         [ +  + ]:         130 :     if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {
      63                 :           2 :         return std::nullopt;
      64                 :             :     }
      65                 :         128 :     std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
      66         [ -  + ]:         128 :     if (!agg_key.has_value()) return std::nullopt;
      67   [ -  +  -  - ]:         128 :     if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt;
      68                 :         128 :     return agg_key;
      69                 :             : }
      70                 :             : 
      71                 :         130 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
      72                 :             : {
      73                 :         130 :     secp256k1_musig_keyagg_cache keyagg_cache;
      74                 :         130 :     return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt);
      75                 :             : }
      76                 :             : 
      77                 :          27 : CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey)
      78                 :             : {
      79                 :          27 :     CExtPubKey extpub;
      80                 :          27 :     extpub.nDepth = 0;
      81                 :          27 :     std::memset(extpub.vchFingerprint, 0, 4);
      82                 :          27 :     extpub.nChild = 0;
      83                 :          27 :     extpub.chaincode = MUSIG_CHAINCODE;
      84                 :          27 :     extpub.pubkey = pubkey;
      85                 :          27 :     return extpub;
      86                 :             : }
      87                 :             : 
      88                 :           0 : class MuSig2SecNonceImpl
      89                 :             : {
      90                 :             : private:
      91                 :             :     //! The actual secnonce itself
      92                 :             :     secure_unique_ptr<secp256k1_musig_secnonce> m_nonce;
      93                 :             : 
      94                 :             : public:
      95         [ #  # ]:           0 :     MuSig2SecNonceImpl() : m_nonce{make_secure_unique<secp256k1_musig_secnonce>()} {}
      96                 :             : 
      97                 :             :     // Delete copy constructors
      98                 :             :     MuSig2SecNonceImpl(const MuSig2SecNonceImpl&) = delete;
      99                 :             :     MuSig2SecNonceImpl& operator=(const MuSig2SecNonceImpl&) = delete;
     100                 :             : 
     101                 :           0 :     secp256k1_musig_secnonce* Get() const { return m_nonce.get(); }
     102                 :           0 :     void Invalidate() { m_nonce.reset(); }
     103                 :           0 :     bool IsValid() { return m_nonce != nullptr; }
     104                 :             : };
     105                 :             : 
     106                 :           0 : MuSig2SecNonce::MuSig2SecNonce() : m_impl{std::make_unique<MuSig2SecNonceImpl>()} {}
     107                 :             : 
     108                 :           0 : MuSig2SecNonce::MuSig2SecNonce(MuSig2SecNonce&&) noexcept = default;
     109                 :           0 : MuSig2SecNonce& MuSig2SecNonce::operator=(MuSig2SecNonce&&) noexcept = default;
     110                 :             : 
     111                 :           0 : MuSig2SecNonce::~MuSig2SecNonce() = default;
     112                 :             : 
     113                 :           0 : secp256k1_musig_secnonce* MuSig2SecNonce::Get() const
     114                 :             : {
     115                 :           0 :     return m_impl->Get();
     116                 :             : }
     117                 :             : 
     118                 :           0 : void MuSig2SecNonce::Invalidate()
     119                 :             : {
     120                 :           0 :     return m_impl->Invalidate();
     121                 :             : }
     122                 :             : 
     123                 :           0 : bool MuSig2SecNonce::IsValid()
     124                 :             : {
     125                 :           0 :     return m_impl->IsValid();
     126                 :             : }
     127                 :             : 
     128                 :           0 : uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash, const std::vector<uint8_t>& pubnonce)
     129                 :             : {
     130                 :           0 :     HashWriter hasher;
     131                 :           0 :     hasher << script_pubkey << part_pubkey << sighash << pubnonce;
     132                 :           0 :     return hasher.GetSHA256();
     133                 :             : }
     134                 :             : 
     135                 :           0 : std::vector<uint8_t> CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector<CPubKey>& pubkeys)
     136                 :             : {
     137                 :             :     // Get the keyagg cache and aggregate pubkey
     138                 :           0 :     secp256k1_musig_keyagg_cache keyagg_cache;
     139         [ #  # ]:           0 :     if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return {};
     140                 :             : 
     141                 :             :     // Parse participant pubkey
     142                 :           0 :     CPubKey our_pubkey = our_seckey.GetPubKey();
     143                 :           0 :     secp256k1_pubkey pubkey;
     144         [ #  # ]:           0 :     if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &pubkey, our_pubkey.data(), our_pubkey.size())) {
     145                 :           0 :         return {};
     146                 :             :     }
     147                 :             : 
     148                 :             :     // Generate randomness for nonce
     149                 :           0 :     uint256 rand;
     150                 :           0 :     GetStrongRandBytes(rand);
     151                 :             : 
     152                 :             :     // Generate nonce
     153                 :           0 :     secp256k1_musig_pubnonce pubnonce;
     154   [ #  #  #  # ]:           0 :     if (!secp256k1_musig_nonce_gen(GetSecp256k1SignContext(), secnonce.Get(), &pubnonce, rand.data(), UCharCast(our_seckey.begin()), &pubkey, sighash.data(), &keyagg_cache, nullptr)) {
     155                 :           0 :         return {};
     156                 :             :     }
     157                 :             : 
     158                 :             :     // Serialize pubnonce
     159                 :           0 :     std::vector<uint8_t> out;
     160         [ #  # ]:           0 :     out.resize(MUSIG2_PUBNONCE_SIZE);
     161   [ #  #  #  # ]:           0 :     if (!secp256k1_musig_pubnonce_serialize(secp256k1_context_static, out.data(), &pubnonce)) {
     162                 :           0 :         return {};
     163                 :             :     }
     164                 :             : 
     165                 :           0 :     return out;
     166                 :           0 : }
     167                 :             : 
     168                 :           0 : std::optional<uint256> CreateMuSig2PartialSig(const uint256& sighash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector<CPubKey>& pubkeys, const std::map<CPubKey, std::vector<uint8_t>>& pubnonces, MuSig2SecNonce& secnonce, const std::vector<std::pair<uint256, bool>>& tweaks)
     169                 :             : {
     170                 :           0 :     secp256k1_keypair keypair;
     171   [ #  #  #  # ]:           0 :     if (!secp256k1_keypair_create(GetSecp256k1SignContext(), &keypair, UCharCast(our_seckey.begin()))) return std::nullopt;
     172                 :             : 
     173                 :             :     // Get the keyagg cache and aggregate pubkey
     174                 :           0 :     secp256k1_musig_keyagg_cache keyagg_cache;
     175         [ #  # ]:           0 :     if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt;
     176                 :             : 
     177                 :             :     // Check that there are enough pubnonces
     178   [ #  #  #  # ]:           0 :     if (pubnonces.size() != pubkeys.size()) return std::nullopt;
     179                 :             : 
     180                 :             :     // Parse the pubnonces
     181                 :           0 :     std::vector<std::pair<secp256k1_pubkey, secp256k1_musig_pubnonce>> signers_data;
     182                 :           0 :     std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
     183                 :           0 :     std::optional<size_t> our_pubkey_idx;
     184         [ #  # ]:           0 :     CPubKey our_pubkey = our_seckey.GetPubKey();
     185         [ #  # ]:           0 :     for (const CPubKey& part_pk : pubkeys) {
     186                 :           0 :         const auto& pn_it = pubnonces.find(part_pk);
     187         [ #  # ]:           0 :         if (pn_it == pubnonces.end()) return std::nullopt;
     188         [ #  # ]:           0 :         const std::vector<uint8_t> pubnonce = pn_it->second;
     189   [ #  #  #  # ]:           0 :         if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
     190         [ #  # ]:           0 :         if (part_pk == our_pubkey) {
     191         [ #  # ]:           0 :             our_pubkey_idx = signers_data.size();
     192                 :             :         }
     193                 :             : 
     194   [ #  #  #  # ]:           0 :         auto& [secp_pk, secp_pn] = signers_data.emplace_back();
     195                 :             : 
     196   [ #  #  #  # ]:           0 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) {
     197                 :           0 :             return std::nullopt;
     198                 :             :         }
     199                 :             : 
     200   [ #  #  #  # ]:           0 :         if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
     201                 :           0 :             return std::nullopt;
     202                 :             :         }
     203                 :           0 :     }
     204         [ #  # ]:           0 :     if (our_pubkey_idx == std::nullopt) {
     205                 :           0 :         return std::nullopt;
     206                 :             :     }
     207   [ #  #  #  # ]:           0 :     pubnonce_ptrs.reserve(signers_data.size());
     208   [ #  #  #  # ]:           0 :     for (auto& [_, pn] : signers_data) {
     209         [ #  # ]:           0 :         pubnonce_ptrs.push_back(&pn);
     210                 :             :     }
     211                 :             : 
     212                 :             :     // Aggregate nonces
     213                 :           0 :     secp256k1_musig_aggnonce aggnonce;
     214   [ #  #  #  #  :           0 :     if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) {
                   #  # ]
     215                 :           0 :         return std::nullopt;
     216                 :             :     }
     217                 :             : 
     218                 :             :     // Apply tweaks
     219   [ #  #  #  # ]:           0 :     for (const auto& [tweak, xonly] : tweaks) {
     220         [ #  # ]:           0 :         if (xonly) {
     221   [ #  #  #  # ]:           0 :             if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     222                 :           0 :                 return std::nullopt;
     223                 :             :             }
     224   [ #  #  #  # ]:           0 :         } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     225                 :           0 :             return std::nullopt;
     226                 :             :         }
     227                 :             :     }
     228                 :             : 
     229                 :             :     // Create musig_session
     230                 :           0 :     secp256k1_musig_session session;
     231   [ #  #  #  # ]:           0 :     if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) {
     232                 :           0 :         return std::nullopt;
     233                 :             :     }
     234                 :             : 
     235                 :             :     // Create partial signature
     236                 :           0 :     secp256k1_musig_partial_sig psig;
     237   [ #  #  #  #  :           0 :     if (!secp256k1_musig_partial_sign(secp256k1_context_static, &psig, secnonce.Get(), &keypair, &keyagg_cache, &session)) {
                   #  # ]
     238                 :           0 :         return std::nullopt;
     239                 :             :     }
     240                 :             :     // The secnonce must be deleted after signing to prevent nonce reuse.
     241         [ #  # ]:           0 :     secnonce.Invalidate();
     242                 :             : 
     243                 :             :     // Verify partial signature
     244   [ #  #  #  #  :           0 :     if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &psig, &(signers_data.at(*our_pubkey_idx).second), &(signers_data.at(*our_pubkey_idx).first), &keyagg_cache, &session)) {
             #  #  #  # ]
     245                 :           0 :         return std::nullopt;
     246                 :             :     }
     247                 :             : 
     248                 :             :     // Serialize
     249                 :           0 :     uint256 sig;
     250   [ #  #  #  # ]:           0 :     if (!secp256k1_musig_partial_sig_serialize(secp256k1_context_static, sig.data(), &psig)) {
     251                 :           0 :         return std::nullopt;
     252                 :             :     }
     253                 :             : 
     254                 :           0 :     return sig;
     255                 :           0 : }
     256                 :             : 
     257                 :           0 : std::optional<std::vector<uint8_t>> CreateMuSig2AggregateSig(const std::vector<CPubKey>& part_pubkeys, const CPubKey& aggregate_pubkey, const std::vector<std::pair<uint256, bool>>& tweaks, const uint256& sighash, const std::map<CPubKey, std::vector<uint8_t>>& pubnonces, const std::map<CPubKey, uint256>& partial_sigs)
     258                 :             : {
     259   [ #  #  #  # ]:           0 :     if (!part_pubkeys.size()) return std::nullopt;
     260                 :             : 
     261                 :             :     // Get the keyagg cache and aggregate pubkey
     262                 :           0 :     secp256k1_musig_keyagg_cache keyagg_cache;
     263         [ #  # ]:           0 :     if (!MuSig2AggregatePubkeys(part_pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt;
     264                 :             : 
     265                 :             :     // Check if enough pubnonces and partial sigs
     266   [ #  #  #  # ]:           0 :     if (pubnonces.size() != part_pubkeys.size()) return std::nullopt;
     267         [ #  # ]:           0 :     if (partial_sigs.size() != part_pubkeys.size()) return std::nullopt;
     268                 :             : 
     269                 :             :     // Parse the pubnonces and partial sigs
     270                 :           0 :     std::vector<std::tuple<secp256k1_pubkey, secp256k1_musig_pubnonce, secp256k1_musig_partial_sig>> signers_data;
     271                 :           0 :     std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
     272                 :           0 :     std::vector<const secp256k1_musig_partial_sig*> partial_sig_ptrs;
     273         [ #  # ]:           0 :     for (const CPubKey& part_pk : part_pubkeys) {
     274                 :           0 :         const auto& pn_it = pubnonces.find(part_pk);
     275         [ #  # ]:           0 :         if (pn_it == pubnonces.end()) return std::nullopt;
     276         [ #  # ]:           0 :         const std::vector<uint8_t> pubnonce = pn_it->second;
     277   [ #  #  #  # ]:           0 :         if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
     278                 :           0 :         const auto& it = partial_sigs.find(part_pk);
     279         [ #  # ]:           0 :         if (it == partial_sigs.end()) return std::nullopt;
     280         [ #  # ]:           0 :         const uint256& partial_sig = it->second;
     281                 :             : 
     282   [ #  #  #  # ]:           0 :         auto& [secp_pk, secp_pn, secp_ps] = signers_data.emplace_back();
     283                 :             : 
     284   [ #  #  #  # ]:           0 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) {
     285                 :           0 :             return std::nullopt;
     286                 :             :         }
     287                 :             : 
     288   [ #  #  #  # ]:           0 :         if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
     289                 :           0 :             return std::nullopt;
     290                 :             :         }
     291                 :             : 
     292   [ #  #  #  # ]:           0 :         if (!secp256k1_musig_partial_sig_parse(secp256k1_context_static, &secp_ps, partial_sig.data())) {
     293                 :           0 :             return std::nullopt;
     294                 :             :         }
     295                 :           0 :     }
     296   [ #  #  #  # ]:           0 :     pubnonce_ptrs.reserve(signers_data.size());
     297   [ #  #  #  # ]:           0 :     partial_sig_ptrs.reserve(signers_data.size());
     298   [ #  #  #  # ]:           0 :     for (auto& [_, pn, ps] : signers_data) {
     299         [ #  # ]:           0 :         pubnonce_ptrs.push_back(&pn);
     300         [ #  # ]:           0 :         partial_sig_ptrs.push_back(&ps);
     301                 :             :     }
     302                 :             : 
     303                 :             :     // Aggregate nonces
     304                 :           0 :     secp256k1_musig_aggnonce aggnonce;
     305   [ #  #  #  #  :           0 :     if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) {
                   #  # ]
     306                 :           0 :         return std::nullopt;
     307                 :             :     }
     308                 :             : 
     309                 :             :     // Apply tweaks
     310   [ #  #  #  # ]:           0 :     for (const auto& [tweak, xonly] : tweaks) {
     311         [ #  # ]:           0 :         if (xonly) {
     312   [ #  #  #  # ]:           0 :             if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     313                 :           0 :                 return std::nullopt;
     314                 :             :             }
     315   [ #  #  #  # ]:           0 :         } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     316                 :           0 :             return std::nullopt;
     317                 :             :         }
     318                 :             :     }
     319                 :             : 
     320                 :             :     // Create musig_session
     321                 :           0 :     secp256k1_musig_session session;
     322   [ #  #  #  # ]:           0 :     if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) {
     323                 :           0 :         return std::nullopt;
     324                 :             :     }
     325                 :             : 
     326                 :             :     // Verify partial sigs
     327   [ #  #  #  # ]:           0 :     for (const auto& [pk, pb, ps] : signers_data) {
     328   [ #  #  #  # ]:           0 :         if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &ps, &pb, &pk, &keyagg_cache, &session)) {
     329                 :           0 :             return std::nullopt;
     330                 :             :         }
     331                 :             :     }
     332                 :             : 
     333                 :             :     // Aggregate partial sigs
     334                 :           0 :     std::vector<uint8_t> sig;
     335         [ #  # ]:           0 :     sig.resize(64);
     336   [ #  #  #  #  :           0 :     if (!secp256k1_musig_partial_sig_agg(secp256k1_context_static, sig.data(), &session, partial_sig_ptrs.data(), partial_sig_ptrs.size())) {
                   #  # ]
     337                 :           0 :         return std::nullopt;
     338                 :             :     }
     339                 :             : 
     340                 :           0 :     return sig;
     341                 :           0 : }
        

Generated by: LCOV version 2.0-1