Coverage Report

Created: 2026-07-14 18:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 <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
static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
17
0
{
18
0
    if (pubkeys.empty()) {
  Branch (18:9): [True: 0, False: 0]
19
0
        return false;
20
0
    }
21
22
    // Parse the pubkeys
23
0
    std::vector<secp256k1_pubkey> secp_pubkeys;
24
0
    std::vector<const secp256k1_pubkey*> pubkey_ptrs;
25
0
    for (const CPubKey& pubkey : pubkeys) {
  Branch (25:32): [True: 0, False: 0]
26
0
        if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) {
  Branch (26:13): [True: 0, False: 0]
27
0
            return false;
28
0
        }
29
0
    }
30
0
    pubkey_ptrs.reserve(secp_pubkeys.size());
31
0
    for (const secp256k1_pubkey& p : secp_pubkeys) {
  Branch (31:36): [True: 0, False: 0]
32
0
        pubkey_ptrs.push_back(&p);
33
0
    }
34
35
    // Aggregate the pubkey
36
0
    if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) {
  Branch (36:9): [True: 0, False: 0]
37
0
        return false;
38
0
    }
39
0
    return true;
40
0
}
41
42
static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
43
0
{
44
    // Get the plain aggregated pubkey
45
0
    secp256k1_pubkey agg_pubkey;
46
0
    if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) {
  Branch (46:9): [True: 0, False: 0]
47
0
        return std::nullopt;
48
0
    }
49
50
    // Turn into CPubKey
51
0
    unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE];
52
0
    size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE;
53
0
    secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED);
54
0
    return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);
55
0
}
56
57
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate)
58
0
{
59
0
    if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {
  Branch (59:9): [True: 0, False: 0]
60
0
        return std::nullopt;
61
0
    }
62
0
    std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
63
0
    if (!agg_key.has_value()) return std::nullopt;
  Branch (63:9): [True: 0, False: 0]
64
0
    if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt;
  Branch (64:9): [True: 0, False: 0]
  Branch (64:43): [True: 0, False: 0]
65
0
    return agg_key;
66
0
}
67
68
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
69
0
{
70
0
    secp256k1_musig_keyagg_cache keyagg_cache;
71
0
    return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt);
72
0
}
73
74
CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey)
75
0
{
76
0
    CExtPubKey extpub;
77
0
    extpub.nDepth = 0;
78
0
    std::memset(extpub.vchFingerprint, 0, 4);
79
0
    extpub.nChild = 0;
80
0
    extpub.chaincode = MUSIG_CHAINCODE;
81
0
    extpub.pubkey = pubkey;
82
0
    return extpub;
83
0
}
84
85
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
secp256k1_musig_secnonce* MuSig2SecNonce::Get() const
111
0
{
112
0
    return m_impl->Get();
113
0
}
114
115
void MuSig2SecNonce::Invalidate()
116
0
{
117
0
    return m_impl->Invalidate();
118
0
}
119
120
bool MuSig2SecNonce::IsValid()
121
0
{
122
0
    return m_impl->IsValid();
123
0
}
124
125
uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash, const std::vector<uint8_t>& pubnonce)
126
0
{
127
0
    HashWriter hasher;
128
0
    hasher << script_pubkey << part_pubkey << sighash << pubnonce;
129
0
    return hasher.GetSHA256();
130
0
}
131
132
std::vector<uint8_t> CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector<CPubKey>& pubkeys)
133
0
{
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 {};
  Branch (136:9): [True: 0, False: 0]
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())) {
  Branch (141:9): [True: 0, False: 0]
142
0
        return {};
143
0
    }
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)) {
  Branch (151:9): [True: 0, False: 0]
152
0
        return {};
153
0
    }
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)) {
  Branch (158:9): [True: 0, False: 0]
159
0
        return {};
160
0
    }
161
162
0
    return out;
163
0
}
164
165
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
0
{
167
0
    secp256k1_keypair keypair;
168
0
    if (!secp256k1_keypair_create(GetSecp256k1SignContext(), &keypair, UCharCast(our_seckey.begin()))) return std::nullopt;
  Branch (168:9): [True: 0, False: 0]
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;
  Branch (172:9): [True: 0, False: 0]
173
174
    // Check that there are enough pubnonces
175
0
    if (pubnonces.size() != pubkeys.size()) return std::nullopt;
  Branch (175:9): [True: 0, False: 0]
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) {
  Branch (182:33): [True: 0, False: 0]
183
0
        const auto& pn_it = pubnonces.find(part_pk);
184
0
        if (pn_it == pubnonces.end()) return std::nullopt;
  Branch (184:13): [True: 0, False: 0]
185
0
        const std::vector<uint8_t> pubnonce = pn_it->second;
186
0
        if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
  Branch (186:13): [True: 0, False: 0]
187
0
        if (part_pk == our_pubkey) {
  Branch (187:13): [True: 0, False: 0]
188
0
            our_pubkey_idx = signers_data.size();
189
0
        }
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())) {
  Branch (193:13): [True: 0, False: 0]
194
0
            return std::nullopt;
195
0
        }
196
197
0
        if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
  Branch (197:13): [True: 0, False: 0]
198
0
            return std::nullopt;
199
0
        }
200
0
    }
201
0
    if (our_pubkey_idx == std::nullopt) {
  Branch (201:9): [True: 0, False: 0]
202
0
        return std::nullopt;
203
0
    }
204
0
    pubnonce_ptrs.reserve(signers_data.size());
205
0
    for (auto& [_, pn] : signers_data) {
  Branch (205:24): [True: 0, False: 0]
206
0
        pubnonce_ptrs.push_back(&pn);
207
0
    }
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())) {
  Branch (211:9): [True: 0, False: 0]
212
0
        return std::nullopt;
213
0
    }
214
215
    // Apply tweaks
216
0
    for (const auto& [tweak, xonly] : tweaks) {
  Branch (216:37): [True: 0, False: 0]
217
0
        if (xonly) {
  Branch (217:13): [True: 0, False: 0]
218
0
            if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
  Branch (218:17): [True: 0, False: 0]
219
0
                return std::nullopt;
220
0
            }
221
0
        } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
  Branch (221:20): [True: 0, False: 0]
222
0
            return std::nullopt;
223
0
        }
224
0
    }
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)) {
  Branch (228:9): [True: 0, False: 0]
229
0
        return std::nullopt;
230
0
    }
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)) {
  Branch (234:9): [True: 0, False: 0]
235
0
        return std::nullopt;
236
0
    }
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)) {
  Branch (241:9): [True: 0, False: 0]
242
0
        return std::nullopt;
243
0
    }
244
245
    // Serialize
246
0
    uint256 sig;
247
0
    if (!secp256k1_musig_partial_sig_serialize(secp256k1_context_static, sig.data(), &psig)) {
  Branch (247:9): [True: 0, False: 0]
248
0
        return std::nullopt;
249
0
    }
250
251
0
    return sig;
252
0
}
253
254
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
0
{
256
0
    if (!part_pubkeys.size()) return std::nullopt;
  Branch (256:9): [True: 0, False: 0]
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;
  Branch (260:9): [True: 0, False: 0]
261
262
    // Check if enough pubnonces and partial sigs
263
0
    if (pubnonces.size() != part_pubkeys.size()) return std::nullopt;
  Branch (263:9): [True: 0, False: 0]
264
0
    if (partial_sigs.size() != part_pubkeys.size()) return std::nullopt;
  Branch (264:9): [True: 0, False: 0]
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) {
  Branch (270:33): [True: 0, False: 0]
271
0
        const auto& pn_it = pubnonces.find(part_pk);
272
0
        if (pn_it == pubnonces.end()) return std::nullopt;
  Branch (272:13): [True: 0, False: 0]
273
0
        const std::vector<uint8_t> pubnonce = pn_it->second;
274
0
        if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
  Branch (274:13): [True: 0, False: 0]
275
0
        const auto& it = partial_sigs.find(part_pk);
276
0
        if (it == partial_sigs.end()) return std::nullopt;
  Branch (276:13): [True: 0, False: 0]
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())) {
  Branch (281:13): [True: 0, False: 0]
282
0
            return std::nullopt;
283
0
        }
284
285
0
        if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
  Branch (285:13): [True: 0, False: 0]
286
0
            return std::nullopt;
287
0
        }
288
289
0
        if (!secp256k1_musig_partial_sig_parse(secp256k1_context_static, &secp_ps, partial_sig.data())) {
  Branch (289:13): [True: 0, False: 0]
290
0
            return std::nullopt;
291
0
        }
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) {
  Branch (295:28): [True: 0, False: 0]
296
0
        pubnonce_ptrs.push_back(&pn);
297
0
        partial_sig_ptrs.push_back(&ps);
298
0
    }
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())) {
  Branch (302:9): [True: 0, False: 0]
303
0
        return std::nullopt;
304
0
    }
305
306
    // Apply tweaks
307
0
    for (const auto& [tweak, xonly] : tweaks) {
  Branch (307:37): [True: 0, False: 0]
308
0
        if (xonly) {
  Branch (308:13): [True: 0, False: 0]
309
0
            if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
  Branch (309:17): [True: 0, False: 0]
310
0
                return std::nullopt;
311
0
            }
312
0
        } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
  Branch (312:20): [True: 0, False: 0]
313
0
            return std::nullopt;
314
0
        }
315
0
    }
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)) {
  Branch (319:9): [True: 0, False: 0]
320
0
        return std::nullopt;
321
0
    }
322
323
    // Verify partial sigs
324
0
    for (const auto& [pk, pb, ps] : signers_data) {
  Branch (324:35): [True: 0, False: 0]
325
0
        if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &ps, &pb, &pk, &keyagg_cache, &session)) {
  Branch (325:13): [True: 0, False: 0]
326
0
            return std::nullopt;
327
0
        }
328
0
    }
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())) {
  Branch (333:9): [True: 0, False: 0]
334
0
        return std::nullopt;
335
0
    }
336
337
0
    return sig;
338
0
}