LCOV - code coverage report
Current view: top level - src - musig.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 19.8 % 177 35
Test Date: 2026-06-11 22:35:12 Functions: 31.2 % 16 5
Branches: 7.4 % 242 18

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

Generated by: LCOV version 2.0-1