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