musig.cpp
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 <support/allocators/secure.h> 7 8 #include <secp256k1_musig.h> 9 10 static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache) 11 { 12 // Parse the pubkeys 13 std::vector<secp256k1_pubkey> secp_pubkeys; 14 std::vector<const secp256k1_pubkey*> pubkey_ptrs; 15 for (const CPubKey& pubkey : pubkeys) { 16 if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) { 17 return false; 18 } 19 } 20 pubkey_ptrs.reserve(secp_pubkeys.size()); 21 for (const secp256k1_pubkey& p : secp_pubkeys) { 22 pubkey_ptrs.push_back(&p); 23 } 24 25 // Aggregate the pubkey 26 if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) { 27 return false; 28 } 29 return true; 30 } 31 32 static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache) 33 { 34 // Get the plain aggregated pubkey 35 secp256k1_pubkey agg_pubkey; 36 if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) { 37 return std::nullopt; 38 } 39 40 // Turn into CPubKey 41 unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE]; 42 size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE; 43 secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED); 44 return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len); 45 } 46 47 std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate) 48 { 49 if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) { 50 return std::nullopt; 51 } 52 std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache); 53 if (!agg_key.has_value()) return std::nullopt; 54 if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt; 55 return agg_key; 56 } 57 58 std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys) 59 { 60 secp256k1_musig_keyagg_cache keyagg_cache; 61 return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt); 62 } 63 64 CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey) 65 { 66 CExtPubKey extpub; 67 extpub.nDepth = 0; 68 std::memset(extpub.vchFingerprint, 0, 4); 69 extpub.nChild = 0; 70 extpub.chaincode = MUSIG_CHAINCODE; 71 extpub.pubkey = pubkey; 72 return extpub; 73 } 74 75 class MuSig2SecNonceImpl 76 { 77 private: 78 //! The actual secnonce itself 79 secure_unique_ptr<secp256k1_musig_secnonce> m_nonce; 80 81 public: 82 MuSig2SecNonceImpl() : m_nonce{make_secure_unique<secp256k1_musig_secnonce>()} {} 83 84 // Delete copy constructors 85 MuSig2SecNonceImpl(const MuSig2SecNonceImpl&) = delete; 86 MuSig2SecNonceImpl& operator=(const MuSig2SecNonceImpl&) = delete; 87 88 secp256k1_musig_secnonce* Get() const { return m_nonce.get(); } 89 void Invalidate() { m_nonce.reset(); } 90 bool IsValid() { return m_nonce != nullptr; } 91 }; 92 93 MuSig2SecNonce::MuSig2SecNonce() : m_impl{std::make_unique<MuSig2SecNonceImpl>()} {} 94 95 MuSig2SecNonce::MuSig2SecNonce(MuSig2SecNonce&&) noexcept = default; 96 MuSig2SecNonce& MuSig2SecNonce::operator=(MuSig2SecNonce&&) noexcept = default; 97 98 MuSig2SecNonce::~MuSig2SecNonce() = default; 99 100 secp256k1_musig_secnonce* MuSig2SecNonce::Get() const 101 { 102 return m_impl->Get(); 103 } 104 105 void MuSig2SecNonce::Invalidate() 106 { 107 return m_impl->Invalidate(); 108 } 109 110 bool MuSig2SecNonce::IsValid() 111 { 112 return m_impl->IsValid(); 113 } 114 115 uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash) 116 { 117 HashWriter hasher; 118 hasher << script_pubkey << part_pubkey << sighash; 119 return hasher.GetSHA256(); 120 } 121 122 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) 123 { 124 if (!part_pubkeys.size()) return std::nullopt; 125 126 // Get the keyagg cache and aggregate pubkey 127 secp256k1_musig_keyagg_cache keyagg_cache; 128 if (!MuSig2AggregatePubkeys(part_pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt; 129 130 // Check if enough pubnonces and partial sigs 131 if (pubnonces.size() != part_pubkeys.size()) return std::nullopt; 132 if (partial_sigs.size() != part_pubkeys.size()) return std::nullopt; 133 134 // Parse the pubnonces and partial sigs 135 std::vector<std::tuple<secp256k1_pubkey, secp256k1_musig_pubnonce, secp256k1_musig_partial_sig>> signers_data; 136 std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs; 137 std::vector<const secp256k1_musig_partial_sig*> partial_sig_ptrs; 138 for (const CPubKey& part_pk : part_pubkeys) { 139 const auto& pn_it = pubnonces.find(part_pk); 140 if (pn_it == pubnonces.end()) return std::nullopt; 141 const std::vector<uint8_t> pubnonce = pn_it->second; 142 if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt; 143 const auto& it = partial_sigs.find(part_pk); 144 if (it == partial_sigs.end()) return std::nullopt; 145 const uint256& partial_sig = it->second; 146 147 auto& [secp_pk, secp_pn, secp_ps] = signers_data.emplace_back(); 148 149 if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) { 150 return std::nullopt; 151 } 152 153 if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) { 154 return std::nullopt; 155 } 156 157 if (!secp256k1_musig_partial_sig_parse(secp256k1_context_static, &secp_ps, partial_sig.data())) { 158 return std::nullopt; 159 } 160 } 161 pubnonce_ptrs.reserve(signers_data.size()); 162 partial_sig_ptrs.reserve(signers_data.size()); 163 for (auto& [_, pn, ps] : signers_data) { 164 pubnonce_ptrs.push_back(&pn); 165 partial_sig_ptrs.push_back(&ps); 166 } 167 168 // Aggregate nonces 169 secp256k1_musig_aggnonce aggnonce; 170 if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) { 171 return std::nullopt; 172 } 173 174 // Apply tweaks 175 for (const auto& [tweak, xonly] : tweaks) { 176 if (xonly) { 177 if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) { 178 return std::nullopt; 179 } 180 } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) { 181 return std::nullopt; 182 } 183 } 184 185 // Create musig_session 186 secp256k1_musig_session session; 187 if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) { 188 return std::nullopt; 189 } 190 191 // Verify partial sigs 192 for (const auto& [pk, pb, ps] : signers_data) { 193 if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &ps, &pb, &pk, &keyagg_cache, &session)) { 194 return std::nullopt; 195 } 196 } 197 198 // Aggregate partial sigs 199 std::vector<uint8_t> sig; 200 sig.resize(64); 201 if (!secp256k1_musig_partial_sig_agg(secp256k1_context_static, sig.data(), &session, partial_sig_ptrs.data(), partial_sig_ptrs.size())) { 202 return std::nullopt; 203 } 204 205 return sig; 206 }