LCOV - code coverage report
Current view: top level - src - musig.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 85.8 % 176 151
Test Date: 2026-05-17 07:22:50 Functions: 93.8 % 16 15
Branches: 57.1 % 240 137

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

Generated by: LCOV version 2.0-1