LCOV - code coverage report
Current view: top level - src - musig.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 85.9 % 177 152
Test Date: 2026-06-07 07:49:58 Functions: 93.8 % 16 15
Branches: 57.4 % 242 139

             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                 :         862 : static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
      20                 :             : {
      21         [ +  + ]:         862 :     if (pubkeys.empty()) {
      22                 :             :         return false;
      23                 :             :     }
      24                 :             : 
      25                 :             :     // Parse the pubkeys
      26                 :         861 :     std::vector<secp256k1_pubkey> secp_pubkeys;
      27                 :         861 :     std::vector<const secp256k1_pubkey*> pubkey_ptrs;
      28         [ +  + ]:        3284 :     for (const CPubKey& pubkey : pubkeys) {
      29   [ +  -  +  -  :        2424 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) {
                   +  + ]
      30                 :             :             return false;
      31                 :             :         }
      32                 :             :     }
      33   [ -  +  +  - ]:         860 :     pubkey_ptrs.reserve(secp_pubkeys.size());
      34         [ +  + ]:        3283 :     for (const secp256k1_pubkey& p : secp_pubkeys) {
      35         [ +  - ]:        2423 :         pubkey_ptrs.push_back(&p);
      36                 :             :     }
      37                 :             : 
      38                 :             :     // Aggregate the pubkey
      39   [ -  +  +  -  :         860 :     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                 :         861 : }
      44                 :             : 
      45                 :         860 : static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
      46                 :             : {
      47                 :             :     // Get the plain aggregated pubkey
      48                 :         860 :     secp256k1_pubkey agg_pubkey;
      49         [ -  + ]:         860 :     if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) {
      50                 :           0 :         return std::nullopt;
      51                 :             :     }
      52                 :             : 
      53                 :             :     // Turn into CPubKey
      54                 :         860 :     unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE];
      55                 :         860 :     size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE;
      56                 :         860 :     secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED);
      57                 :         860 :     return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);
      58                 :             : }
      59                 :             : 
      60                 :         862 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate)
      61                 :             : {
      62         [ +  + ]:         862 :     if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {
      63                 :           2 :         return std::nullopt;
      64                 :             :     }
      65                 :         860 :     std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
      66         [ -  + ]:         860 :     if (!agg_key.has_value()) return std::nullopt;
      67   [ +  +  -  + ]:         860 :     if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt;
      68                 :         860 :     return agg_key;
      69                 :             : }
      70                 :             : 
      71                 :         502 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
      72                 :             : {
      73                 :         502 :     secp256k1_musig_keyagg_cache keyagg_cache;
      74                 :         502 :     return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt);
      75                 :             : }
      76                 :             : 
      77                 :        2763 : CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey)
      78                 :             : {
      79                 :        2763 :     CExtPubKey extpub;
      80                 :        2763 :     extpub.nDepth = 0;
      81                 :        2763 :     std::memset(extpub.vchFingerprint, 0, 4);
      82                 :        2763 :     extpub.nChild = 0;
      83                 :        2763 :     extpub.chaincode = MUSIG_CHAINCODE;
      84                 :        2763 :     extpub.pubkey = pubkey;
      85                 :        2763 :     return extpub;
      86                 :             : }
      87                 :             : 
      88                 :         151 : class MuSig2SecNonceImpl
      89                 :             : {
      90                 :             : private:
      91                 :             :     //! The actual secnonce itself
      92                 :             :     secure_unique_ptr<secp256k1_musig_secnonce> m_nonce;
      93                 :             : 
      94                 :             : public:
      95         [ +  - ]:         151 :     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                 :         291 :     secp256k1_musig_secnonce* Get() const { return m_nonce.get(); }
     102                 :         140 :     void Invalidate() { m_nonce.reset(); }
     103                 :         280 :     bool IsValid() { return m_nonce != nullptr; }
     104                 :             : };
     105                 :             : 
     106                 :         151 : MuSig2SecNonce::MuSig2SecNonce() : m_impl{std::make_unique<MuSig2SecNonceImpl>()} {}
     107                 :             : 
     108                 :         151 : MuSig2SecNonce::MuSig2SecNonce(MuSig2SecNonce&&) noexcept = default;
     109                 :           0 : MuSig2SecNonce& MuSig2SecNonce::operator=(MuSig2SecNonce&&) noexcept = default;
     110                 :             : 
     111                 :         302 : MuSig2SecNonce::~MuSig2SecNonce() = default;
     112                 :             : 
     113                 :         291 : secp256k1_musig_secnonce* MuSig2SecNonce::Get() const
     114                 :             : {
     115                 :         291 :     return m_impl->Get();
     116                 :             : }
     117                 :             : 
     118                 :         140 : void MuSig2SecNonce::Invalidate()
     119                 :             : {
     120                 :         140 :     return m_impl->Invalidate();
     121                 :             : }
     122                 :             : 
     123                 :         280 : bool MuSig2SecNonce::IsValid()
     124                 :             : {
     125                 :         280 :     return m_impl->IsValid();
     126                 :             : }
     127                 :             : 
     128                 :         399 : uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash, const std::vector<uint8_t>& pubnonce)
     129                 :             : {
     130                 :         399 :     HashWriter hasher;
     131                 :         399 :     hasher << script_pubkey << part_pubkey << sighash << pubnonce;
     132                 :         399 :     return hasher.GetSHA256();
     133                 :             : }
     134                 :             : 
     135                 :         151 : 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                 :         151 :     secp256k1_musig_keyagg_cache keyagg_cache;
     139         [ -  + ]:         151 :     if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return {};
     140                 :             : 
     141                 :             :     // Parse participant pubkey
     142                 :         151 :     CPubKey our_pubkey = our_seckey.GetPubKey();
     143                 :         151 :     secp256k1_pubkey pubkey;
     144         [ -  + ]:         151 :     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                 :         151 :     uint256 rand;
     150                 :         151 :     GetStrongRandBytes(rand);
     151                 :             : 
     152                 :             :     // Generate nonce
     153                 :         151 :     secp256k1_musig_pubnonce pubnonce;
     154   [ +  -  -  + ]:         302 :     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                 :         151 :     std::vector<uint8_t> out;
     160         [ +  - ]:         151 :     out.resize(MUSIG2_PUBNONCE_SIZE);
     161   [ +  -  -  + ]:         151 :     if (!secp256k1_musig_pubnonce_serialize(secp256k1_context_static, out.data(), &pubnonce)) {
     162                 :           0 :         return {};
     163                 :             :     }
     164                 :             : 
     165                 :         151 :     return out;
     166                 :         151 : }
     167                 :             : 
     168                 :         140 : 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                 :         140 :     secp256k1_keypair keypair;
     171   [ +  -  -  + ]:         280 :     if (!secp256k1_keypair_create(GetSecp256k1SignContext(), &keypair, UCharCast(our_seckey.begin()))) return std::nullopt;
     172                 :             : 
     173                 :             :     // Get the keyagg cache and aggregate pubkey
     174                 :         140 :     secp256k1_musig_keyagg_cache keyagg_cache;
     175         [ -  + ]:         140 :     if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt;
     176                 :             : 
     177                 :             :     // Check that there are enough pubnonces
     178   [ -  +  -  + ]:         140 :     if (pubnonces.size() != pubkeys.size()) return std::nullopt;
     179                 :             : 
     180                 :             :     // Parse the pubnonces
     181                 :         140 :     std::vector<std::pair<secp256k1_pubkey, secp256k1_musig_pubnonce>> signers_data;
     182                 :         140 :     std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
     183                 :         140 :     std::optional<size_t> our_pubkey_idx;
     184         [ +  - ]:         140 :     CPubKey our_pubkey = our_seckey.GetPubKey();
     185         [ +  + ]:         536 :     for (const CPubKey& part_pk : pubkeys) {
     186                 :         396 :         const auto& pn_it = pubnonces.find(part_pk);
     187         [ -  + ]:         396 :         if (pn_it == pubnonces.end()) return std::nullopt;
     188         [ +  - ]:         396 :         const std::vector<uint8_t> pubnonce = pn_it->second;
     189   [ -  +  -  + ]:         396 :         if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
     190         [ +  + ]:         396 :         if (part_pk == our_pubkey) {
     191         [ -  + ]:         140 :             our_pubkey_idx = signers_data.size();
     192                 :             :         }
     193                 :             : 
     194   [ +  -  +  - ]:         396 :         auto& [secp_pk, secp_pn] = signers_data.emplace_back();
     195                 :             : 
     196   [ +  -  -  + ]:         396 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) {
     197                 :           0 :             return std::nullopt;
     198                 :             :         }
     199                 :             : 
     200   [ +  -  -  + ]:         396 :         if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
     201                 :           0 :             return std::nullopt;
     202                 :             :         }
     203                 :         396 :     }
     204         [ -  + ]:         140 :     if (our_pubkey_idx == std::nullopt) {
     205                 :           0 :         return std::nullopt;
     206                 :             :     }
     207   [ -  +  +  - ]:         140 :     pubnonce_ptrs.reserve(signers_data.size());
     208   [ +  -  +  + ]:         536 :     for (auto& [_, pn] : signers_data) {
     209         [ +  - ]:         396 :         pubnonce_ptrs.push_back(&pn);
     210                 :             :     }
     211                 :             : 
     212                 :             :     // Aggregate nonces
     213                 :         140 :     secp256k1_musig_aggnonce aggnonce;
     214   [ -  +  +  -  :         140 :     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   [ +  +  +  + ]:         356 :     for (const auto& [tweak, xonly] : tweaks) {
     220         [ +  + ]:         216 :         if (xonly) {
     221   [ +  -  -  + ]:          36 :             if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     222                 :           0 :                 return std::nullopt;
     223                 :             :             }
     224   [ +  -  -  + ]:         180 :         } 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                 :         140 :     secp256k1_musig_session session;
     231   [ +  -  -  + ]:         140 :     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                 :         140 :     secp256k1_musig_partial_sig psig;
     237   [ +  -  +  -  :         140 :     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         [ +  - ]:         140 :     secnonce.Invalidate();
     242                 :             : 
     243                 :             :     // Verify partial signature
     244   [ +  -  +  -  :         140 :     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                 :         140 :     uint256 sig;
     250   [ +  -  -  + ]:         140 :     if (!secp256k1_musig_partial_sig_serialize(secp256k1_context_static, sig.data(), &psig)) {
     251                 :           0 :         return std::nullopt;
     252                 :             :     }
     253                 :             : 
     254                 :         140 :     return sig;
     255                 :         140 : }
     256                 :             : 
     257                 :          69 : 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   [ -  +  -  + ]:          69 :     if (!part_pubkeys.size()) return std::nullopt;
     260                 :             : 
     261                 :             :     // Get the keyagg cache and aggregate pubkey
     262                 :          69 :     secp256k1_musig_keyagg_cache keyagg_cache;
     263         [ -  + ]:          69 :     if (!MuSig2AggregatePubkeys(part_pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt;
     264                 :             : 
     265                 :             :     // Check if enough pubnonces and partial sigs
     266   [ -  +  -  + ]:          69 :     if (pubnonces.size() != part_pubkeys.size()) return std::nullopt;
     267         [ -  + ]:          69 :     if (partial_sigs.size() != part_pubkeys.size()) return std::nullopt;
     268                 :             : 
     269                 :             :     // Parse the pubnonces and partial sigs
     270                 :          69 :     std::vector<std::tuple<secp256k1_pubkey, secp256k1_musig_pubnonce, secp256k1_musig_partial_sig>> signers_data;
     271                 :          69 :     std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
     272                 :          69 :     std::vector<const secp256k1_musig_partial_sig*> partial_sig_ptrs;
     273         [ +  + ]:         264 :     for (const CPubKey& part_pk : part_pubkeys) {
     274                 :         195 :         const auto& pn_it = pubnonces.find(part_pk);
     275         [ -  + ]:         195 :         if (pn_it == pubnonces.end()) return std::nullopt;
     276         [ +  - ]:         195 :         const std::vector<uint8_t> pubnonce = pn_it->second;
     277   [ -  +  -  + ]:         195 :         if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
     278                 :         195 :         const auto& it = partial_sigs.find(part_pk);
     279         [ -  + ]:         195 :         if (it == partial_sigs.end()) return std::nullopt;
     280         [ +  - ]:         195 :         const uint256& partial_sig = it->second;
     281                 :             : 
     282   [ +  -  +  - ]:         195 :         auto& [secp_pk, secp_pn, secp_ps] = signers_data.emplace_back();
     283                 :             : 
     284   [ +  -  -  + ]:         195 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) {
     285                 :           0 :             return std::nullopt;
     286                 :             :         }
     287                 :             : 
     288   [ +  -  -  + ]:         195 :         if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
     289                 :           0 :             return std::nullopt;
     290                 :             :         }
     291                 :             : 
     292   [ +  -  -  + ]:         195 :         if (!secp256k1_musig_partial_sig_parse(secp256k1_context_static, &secp_ps, partial_sig.data())) {
     293                 :           0 :             return std::nullopt;
     294                 :             :         }
     295                 :         195 :     }
     296   [ -  +  +  - ]:          69 :     pubnonce_ptrs.reserve(signers_data.size());
     297   [ -  +  +  - ]:          69 :     partial_sig_ptrs.reserve(signers_data.size());
     298   [ +  -  +  + ]:         264 :     for (auto& [_, pn, ps] : signers_data) {
     299         [ +  - ]:         195 :         pubnonce_ptrs.push_back(&pn);
     300         [ +  - ]:         195 :         partial_sig_ptrs.push_back(&ps);
     301                 :             :     }
     302                 :             : 
     303                 :             :     // Aggregate nonces
     304                 :          69 :     secp256k1_musig_aggnonce aggnonce;
     305   [ -  +  +  -  :          69 :     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   [ +  +  +  + ]:         177 :     for (const auto& [tweak, xonly] : tweaks) {
     311         [ +  + ]:         108 :         if (xonly) {
     312   [ +  -  -  + ]:          18 :             if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     313                 :           0 :                 return std::nullopt;
     314                 :             :             }
     315   [ +  -  -  + ]:          90 :         } 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                 :          69 :     secp256k1_musig_session session;
     322   [ +  -  -  + ]:          69 :     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   [ +  -  +  + ]:         264 :     for (const auto& [pk, pb, ps] : signers_data) {
     328   [ +  -  -  + ]:         195 :         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                 :          69 :     std::vector<uint8_t> sig;
     335         [ +  - ]:          69 :     sig.resize(64);
     336   [ -  +  +  -  :          69 :     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                 :          69 :     return sig;
     341                 :         138 : }
        

Generated by: LCOV version 2.0-1