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