Coverage Report

Created: 2026-04-29 19:21

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