Branch data Line data Source code
1 : : // Copyright (c) 2020-2022 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 <chainparams.h>
6 : : #include <key.h>
7 : : #include <key_io.h>
8 : : #include <outputtype.h>
9 : : #include <policy/policy.h>
10 : : #include <pubkey.h>
11 : : #include <rpc/util.h>
12 : : #include <script/keyorigin.h>
13 : : #include <script/script.h>
14 : : #include <script/sign.h>
15 : : #include <script/signingprovider.h>
16 : : #include <script/solver.h>
17 : : #include <streams.h>
18 : : #include <test/fuzz/FuzzedDataProvider.h>
19 : : #include <test/fuzz/fuzz.h>
20 : : #include <test/fuzz/util.h>
21 : : #include <util/chaintype.h>
22 : : #include <util/strencodings.h>
23 : :
24 : : #include <array>
25 : : #include <cassert>
26 : : #include <cstddef>
27 : 2 : #include <cstdint>
28 : : #include <numeric>
29 : : #include <optional>
30 : : #include <string>
31 : : #include <vector>
32 : :
33 : 0 : void initialize_key()
34 : : {
35 : 0 : ECC_Start();
36 : 0 : SelectParams(ChainType::REGTEST);
37 : 0 : }
38 : :
39 [ + - ]: 4 : FUZZ_TARGET(key, .init = initialize_key)
40 : : {
41 : 0 : const CKey key = [&] {
42 : 0 : CKey k;
43 [ # # ]: 0 : k.Set(buffer.begin(), buffer.end(), true);
44 : 0 : return k;
45 [ # # ]: 0 : }();
46 [ # # ][ # # ]: 0 : if (!key.IsValid()) {
47 : 0 : return;
48 : : }
49 : :
50 : : {
51 [ # # ][ # # ]: 0 : assert(key.begin() + key.size() == key.end());
[ # # ][ # # ]
52 [ # # ][ # # ]: 0 : assert(key.IsCompressed());
53 [ # # ][ # # ]: 0 : assert(key.size() == 32);
54 [ # # ][ # # ]: 0 : assert(DecodeSecret(EncodeSecret(key)) == key);
[ # # ][ # # ]
55 : : }
56 : :
57 : : {
58 : 0 : CKey invalid_key;
59 [ # # ][ # # ]: 0 : assert(!(invalid_key == key));
60 [ # # ][ # # ]: 0 : assert(!invalid_key.IsCompressed());
61 [ # # ][ # # ]: 0 : assert(!invalid_key.IsValid());
62 [ # # ][ # # ]: 0 : assert(invalid_key.size() == 0);
63 : 0 : }
64 : :
65 : : {
66 : 0 : CKey uncompressed_key;
67 [ # # ]: 0 : uncompressed_key.Set(buffer.begin(), buffer.end(), false);
68 [ # # ][ # # ]: 0 : assert(!(uncompressed_key == key));
69 [ # # ][ # # ]: 0 : assert(!uncompressed_key.IsCompressed());
70 [ # # ][ # # ]: 0 : assert(key.size() == 32);
71 [ # # ][ # # ]: 0 : assert(uncompressed_key.begin() + uncompressed_key.size() == uncompressed_key.end());
[ # # ][ # # ]
72 [ # # ][ # # ]: 0 : assert(uncompressed_key.IsValid());
73 : 0 : }
74 : :
75 : : {
76 : 0 : CKey copied_key;
77 [ # # ][ # # ]: 0 : copied_key.Set(key.begin(), key.end(), key.IsCompressed());
[ # # ][ # # ]
78 [ # # ][ # # ]: 0 : assert(copied_key == key);
79 : 0 : }
80 : :
81 : : {
82 [ # # ]: 0 : CKey negated_key = key;
83 [ # # ]: 0 : negated_key.Negate();
84 [ # # ][ # # ]: 0 : assert(negated_key.IsValid());
85 [ # # ][ # # ]: 0 : assert(!(negated_key == key));
86 : :
87 [ # # ]: 0 : negated_key.Negate();
88 [ # # ][ # # ]: 0 : assert(negated_key == key);
89 : 0 : }
90 : :
91 [ # # ]: 0 : const uint256 random_uint256 = Hash(buffer);
92 : :
93 : : {
94 : 0 : CKey child_key;
95 [ # # ]: 0 : ChainCode child_chaincode;
96 [ # # ]: 0 : const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256);
97 [ # # ]: 0 : assert(ok);
98 [ # # ][ # # ]: 0 : assert(child_key.IsValid());
99 [ # # ][ # # ]: 0 : assert(!(child_key == key));
100 [ # # ][ # # ]: 0 : assert(child_chaincode != random_uint256);
101 : 0 : }
102 : :
103 [ # # ]: 0 : const CPubKey pubkey = key.GetPubKey();
104 : :
105 : : {
106 [ # # ][ # # ]: 0 : assert(pubkey.size() == 33);
107 [ # # ][ # # ]: 0 : assert(key.VerifyPubKey(pubkey));
108 [ # # ][ # # ]: 0 : assert(pubkey.GetHash() != random_uint256);
[ # # ]
109 [ # # ][ # # ]: 0 : assert(pubkey.begin() + pubkey.size() == pubkey.end());
[ # # ][ # # ]
110 [ # # ][ # # ]: 0 : assert(pubkey.data() == pubkey.begin());
[ # # ]
111 [ # # ][ # # ]: 0 : assert(pubkey.IsCompressed());
112 [ # # ][ # # ]: 0 : assert(pubkey.IsValid());
113 [ # # ][ # # ]: 0 : assert(pubkey.IsFullyValid());
114 [ # # ][ # # ]: 0 : assert(HexToPubKey(HexStr(pubkey)) == pubkey);
[ # # ][ # # ]
[ # # ]
115 [ # # ][ # # ]: 0 : assert(GetAllDestinationsForKey(pubkey).size() == 3);
116 : : }
117 : :
118 : : {
119 [ # # ]: 0 : DataStream data_stream{};
120 [ # # ]: 0 : pubkey.Serialize(data_stream);
121 : :
122 [ # # ]: 0 : CPubKey pubkey_deserialized;
123 [ # # ]: 0 : pubkey_deserialized.Unserialize(data_stream);
124 [ # # ][ # # ]: 0 : assert(pubkey_deserialized == pubkey);
125 : 0 : }
126 : :
127 : : {
128 [ # # ]: 0 : const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey);
129 [ # # ][ # # ]: 0 : assert(!tx_pubkey_script.IsPayToScriptHash());
130 [ # # ][ # # ]: 0 : assert(!tx_pubkey_script.IsPayToWitnessScriptHash());
131 [ # # ][ # # ]: 0 : assert(!tx_pubkey_script.IsPushOnly());
132 [ # # ][ # # ]: 0 : assert(!tx_pubkey_script.IsUnspendable());
133 [ # # ][ # # ]: 0 : assert(tx_pubkey_script.HasValidOps());
134 [ # # ][ # # ]: 0 : assert(tx_pubkey_script.size() == 35);
135 : :
136 [ # # ][ # # ]: 0 : const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey});
137 [ # # ][ # # ]: 0 : assert(!tx_multisig_script.IsPayToScriptHash());
138 [ # # ][ # # ]: 0 : assert(!tx_multisig_script.IsPayToWitnessScriptHash());
139 [ # # ][ # # ]: 0 : assert(!tx_multisig_script.IsPushOnly());
140 [ # # ][ # # ]: 0 : assert(!tx_multisig_script.IsUnspendable());
141 [ # # ][ # # ]: 0 : assert(tx_multisig_script.HasValidOps());
142 [ # # ][ # # ]: 0 : assert(tx_multisig_script.size() == 37);
143 : :
144 : 0 : FillableSigningProvider fillable_signing_provider;
145 [ # # ][ # # ]: 0 : assert(!IsSegWitOutput(fillable_signing_provider, tx_pubkey_script));
146 [ # # ][ # # ]: 0 : assert(!IsSegWitOutput(fillable_signing_provider, tx_multisig_script));
147 [ # # ][ # # ]: 0 : assert(fillable_signing_provider.GetKeys().size() == 0);
148 [ # # ][ # # ]: 0 : assert(!fillable_signing_provider.HaveKey(pubkey.GetID()));
[ # # ]
149 : :
150 [ # # ]: 0 : const bool ok_add_key = fillable_signing_provider.AddKey(key);
151 [ # # ]: 0 : assert(ok_add_key);
152 [ # # ][ # # ]: 0 : assert(fillable_signing_provider.HaveKey(pubkey.GetID()));
[ # # ]
153 : :
154 : 0 : FillableSigningProvider fillable_signing_provider_pub;
155 [ # # ][ # # ]: 0 : assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
[ # # ]
156 : :
157 [ # # ]: 0 : const bool ok_add_key_pubkey = fillable_signing_provider_pub.AddKeyPubKey(key, pubkey);
158 [ # # ]: 0 : assert(ok_add_key_pubkey);
159 [ # # ][ # # ]: 0 : assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
[ # # ]
160 : :
161 : : TxoutType which_type_tx_pubkey;
162 [ # # ]: 0 : const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey);
163 [ # # ]: 0 : assert(is_standard_tx_pubkey);
164 [ # # ]: 0 : assert(which_type_tx_pubkey == TxoutType::PUBKEY);
165 : :
166 : : TxoutType which_type_tx_multisig;
167 [ # # ]: 0 : const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, std::nullopt, which_type_tx_multisig);
168 [ # # ]: 0 : assert(is_standard_tx_multisig);
169 [ # # ]: 0 : assert(which_type_tx_multisig == TxoutType::MULTISIG);
170 : :
171 : 0 : std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
172 [ # # ]: 0 : const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
173 [ # # ]: 0 : assert(outtype_tx_pubkey == TxoutType::PUBKEY);
174 [ # # ]: 0 : assert(v_solutions_ret_tx_pubkey.size() == 1);
175 [ # # ]: 0 : assert(v_solutions_ret_tx_pubkey[0].size() == 33);
176 : :
177 : 0 : std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
178 [ # # ]: 0 : const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
179 [ # # ]: 0 : assert(outtype_tx_multisig == TxoutType::MULTISIG);
180 [ # # ]: 0 : assert(v_solutions_ret_tx_multisig.size() == 3);
181 [ # # ]: 0 : assert(v_solutions_ret_tx_multisig[0].size() == 1);
182 [ # # ]: 0 : assert(v_solutions_ret_tx_multisig[1].size() == 33);
183 [ # # ]: 0 : assert(v_solutions_ret_tx_multisig[2].size() == 1);
184 : :
185 : 0 : OutputType output_type{};
186 [ # # ]: 0 : const CTxDestination tx_destination = GetDestinationForKey(pubkey, output_type);
187 [ # # ]: 0 : assert(output_type == OutputType::LEGACY);
188 [ # # ][ # # ]: 0 : assert(IsValidDestination(tx_destination));
189 [ # # ][ # # ]: 0 : assert(PKHash{pubkey} == *std::get_if<PKHash>(&tx_destination));
190 : :
191 [ # # ]: 0 : const CScript script_for_destination = GetScriptForDestination(tx_destination);
192 [ # # ][ # # ]: 0 : assert(script_for_destination.size() == 25);
193 : :
194 [ # # ]: 0 : const std::string destination_address = EncodeDestination(tx_destination);
195 [ # # ][ # # ]: 0 : assert(DecodeDestination(destination_address) == tx_destination);
[ # # ]
196 : :
197 [ # # ]: 0 : const CPubKey pubkey_from_address_string = AddrToPubKey(fillable_signing_provider, destination_address);
198 [ # # ][ # # ]: 0 : assert(pubkey_from_address_string == pubkey);
199 : :
200 [ # # ]: 0 : CKeyID key_id = pubkey.GetID();
201 [ # # ][ # # ]: 0 : assert(!key_id.IsNull());
202 [ # # ][ # # ]: 0 : assert(key_id == CKeyID{key_id});
203 [ # # ][ # # ]: 0 : assert(key_id == GetKeyForDestination(fillable_signing_provider, tx_destination));
[ # # ]
204 : :
205 [ # # ]: 0 : CPubKey pubkey_out;
206 [ # # ]: 0 : const bool ok_get_pubkey = fillable_signing_provider.GetPubKey(key_id, pubkey_out);
207 [ # # ]: 0 : assert(ok_get_pubkey);
208 : :
209 : 0 : CKey key_out;
210 [ # # ]: 0 : const bool ok_get_key = fillable_signing_provider.GetKey(key_id, key_out);
211 [ # # ]: 0 : assert(ok_get_key);
212 [ # # ][ # # ]: 0 : assert(fillable_signing_provider.GetKeys().size() == 1);
213 [ # # ][ # # ]: 0 : assert(fillable_signing_provider.HaveKey(key_id));
214 : :
215 : 0 : KeyOriginInfo key_origin_info;
216 [ # # ]: 0 : const bool ok_get_key_origin = fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info);
217 [ # # ]: 0 : assert(!ok_get_key_origin);
218 : 0 : }
219 : :
220 : : {
221 [ # # ][ # # ]: 0 : const std::vector<unsigned char> vch_pubkey{pubkey.begin(), pubkey.end()};
[ # # ]
222 [ # # ][ # # ]: 0 : assert(CPubKey::ValidSize(vch_pubkey));
223 [ # # ][ # # ]: 0 : assert(!CPubKey::ValidSize({pubkey.begin(), pubkey.begin() + pubkey.size() - 1}));
[ # # ][ # # ]
[ # # ][ # # ]
224 : :
225 [ # # ][ # # ]: 0 : const CPubKey pubkey_ctor_1{vch_pubkey};
226 [ # # ][ # # ]: 0 : assert(pubkey == pubkey_ctor_1);
227 : :
228 [ # # ]: 0 : const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()};
229 [ # # ][ # # ]: 0 : assert(pubkey == pubkey_ctor_2);
230 : :
231 [ # # ]: 0 : CPubKey pubkey_set;
232 [ # # ]: 0 : pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end());
233 [ # # ][ # # ]: 0 : assert(pubkey == pubkey_set);
234 : 0 : }
235 : :
236 : : {
237 [ # # ]: 0 : const CPubKey invalid_pubkey{};
238 [ # # ][ # # ]: 0 : assert(!invalid_pubkey.IsValid());
239 [ # # ][ # # ]: 0 : assert(!invalid_pubkey.IsFullyValid());
240 [ # # ][ # # ]: 0 : assert(!(pubkey == invalid_pubkey));
241 [ # # ][ # # ]: 0 : assert(pubkey != invalid_pubkey);
242 [ # # ][ # # ]: 0 : assert(pubkey < invalid_pubkey);
243 : : }
244 : :
245 : : {
246 : : // Cover CPubKey's operator[](unsigned int pos)
247 : 0 : unsigned int sum = 0;
248 [ # # ][ # # ]: 0 : for (size_t i = 0; i < pubkey.size(); ++i) {
249 [ # # ]: 0 : sum += pubkey[i];
250 : 0 : }
251 [ # # ][ # # ]: 0 : assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum);
[ # # ][ # # ]
252 : : }
253 : :
254 : : {
255 : 0 : CPubKey decompressed_pubkey = pubkey;
256 [ # # ][ # # ]: 0 : assert(decompressed_pubkey.IsCompressed());
257 : :
258 [ # # ]: 0 : const bool ok = decompressed_pubkey.Decompress();
259 [ # # ]: 0 : assert(ok);
260 [ # # ][ # # ]: 0 : assert(!decompressed_pubkey.IsCompressed());
261 [ # # ][ # # ]: 0 : assert(decompressed_pubkey.size() == 65);
262 : : }
263 : :
264 : : {
265 : 0 : std::vector<unsigned char> vch_sig;
266 [ # # ]: 0 : const bool ok = key.Sign(random_uint256, vch_sig, false);
267 [ # # ]: 0 : assert(ok);
268 [ # # ][ # # ]: 0 : assert(pubkey.Verify(random_uint256, vch_sig));
269 [ # # ][ # # ]: 0 : assert(CPubKey::CheckLowS(vch_sig));
270 : :
271 [ # # ]: 0 : const std::vector<unsigned char> vch_invalid_sig{vch_sig.begin(), vch_sig.begin() + vch_sig.size() - 1};
272 [ # # ][ # # ]: 0 : assert(!pubkey.Verify(random_uint256, vch_invalid_sig));
273 [ # # ][ # # ]: 0 : assert(!CPubKey::CheckLowS(vch_invalid_sig));
274 : 0 : }
275 : :
276 : : {
277 : 0 : std::vector<unsigned char> vch_compact_sig;
278 [ # # ]: 0 : const bool ok_sign_compact = key.SignCompact(random_uint256, vch_compact_sig);
279 [ # # ]: 0 : assert(ok_sign_compact);
280 : :
281 [ # # ]: 0 : CPubKey recover_pubkey;
282 [ # # ]: 0 : const bool ok_recover_compact = recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig);
283 [ # # ]: 0 : assert(ok_recover_compact);
284 [ # # ][ # # ]: 0 : assert(recover_pubkey == pubkey);
285 : 0 : }
286 : :
287 : : {
288 [ # # ]: 0 : CPubKey child_pubkey;
289 [ # # ]: 0 : ChainCode child_chaincode;
290 [ # # ]: 0 : const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
291 [ # # ]: 0 : assert(ok);
292 [ # # ][ # # ]: 0 : assert(child_pubkey != pubkey);
293 [ # # ][ # # ]: 0 : assert(child_pubkey.IsCompressed());
294 [ # # ][ # # ]: 0 : assert(child_pubkey.IsFullyValid());
295 [ # # ][ # # ]: 0 : assert(child_pubkey.IsValid());
296 [ # # ][ # # ]: 0 : assert(child_pubkey.size() == 33);
297 [ # # ][ # # ]: 0 : assert(child_chaincode != random_uint256);
298 : : }
299 : :
300 [ # # ]: 0 : const CPrivKey priv_key = key.GetPrivKey();
301 : :
302 : : {
303 [ # # ]: 0 : for (const bool skip_check : {true, false}) {
304 : 0 : CKey loaded_key;
305 [ # # ]: 0 : const bool ok = loaded_key.Load(priv_key, pubkey, skip_check);
306 [ # # ]: 0 : assert(ok);
307 [ # # ][ # # ]: 0 : assert(key == loaded_key);
308 : 0 : }
309 : : }
310 [ # # ]: 0 : }
311 : :
312 [ + - ]: 4 : FUZZ_TARGET(ellswift_roundtrip, .init = initialize_key)
313 : : {
314 : 0 : FuzzedDataProvider fdp{buffer.data(), buffer.size()};
315 : :
316 : 0 : CKey key = ConsumePrivateKey(fdp, /*compressed=*/true);
317 [ # # ][ # # ]: 0 : if (!key.IsValid()) return;
318 : :
319 [ # # ]: 0 : auto ent32 = fdp.ConsumeBytes<std::byte>(32);
320 [ # # ]: 0 : ent32.resize(32);
321 : :
322 [ # # ][ # # ]: 0 : auto encoded_ellswift = key.EllSwiftCreate(ent32);
323 [ # # ]: 0 : auto decoded_pubkey = encoded_ellswift.Decode();
324 : :
325 [ # # ][ # # ]: 0 : assert(key.VerifyPubKey(decoded_pubkey));
326 [ # # ]: 0 : }
327 : :
328 [ + - ]: 4 : FUZZ_TARGET(bip324_ecdh, .init = initialize_key)
329 : : {
330 : 0 : FuzzedDataProvider fdp{buffer.data(), buffer.size()};
331 : :
332 : : // We generate private key, k1.
333 : 0 : CKey k1 = ConsumePrivateKey(fdp, /*compressed=*/true);
334 [ # # ][ # # ]: 0 : if (!k1.IsValid()) return;
335 : :
336 : : // They generate private key, k2.
337 : 0 : CKey k2 = ConsumePrivateKey(fdp, /*compressed=*/true);
338 [ # # ][ # # ]: 0 : if (!k2.IsValid()) return;
339 : :
340 : : // We construct an ellswift encoding for our key, k1_ellswift.
341 [ # # ]: 0 : auto ent32_1 = fdp.ConsumeBytes<std::byte>(32);
342 [ # # ]: 0 : ent32_1.resize(32);
343 [ # # ][ # # ]: 0 : auto k1_ellswift = k1.EllSwiftCreate(ent32_1);
344 : :
345 : : // They construct an ellswift encoding for their key, k2_ellswift.
346 [ # # ]: 0 : auto ent32_2 = fdp.ConsumeBytes<std::byte>(32);
347 [ # # ]: 0 : ent32_2.resize(32);
348 [ # # ][ # # ]: 0 : auto k2_ellswift = k2.EllSwiftCreate(ent32_2);
349 : :
350 : : // They construct another (possibly distinct) ellswift encoding for their key, k2_ellswift_bad.
351 [ # # ]: 0 : auto ent32_2_bad = fdp.ConsumeBytes<std::byte>(32);
352 [ # # ]: 0 : ent32_2_bad.resize(32);
353 [ # # ][ # # ]: 0 : auto k2_ellswift_bad = k2.EllSwiftCreate(ent32_2_bad);
354 [ # # ][ # # ]: 0 : assert((ent32_2_bad == ent32_2) == (k2_ellswift_bad == k2_ellswift));
[ # # ]
355 : :
356 : : // Determine who is who.
357 [ # # ]: 0 : bool initiating = fdp.ConsumeBool();
358 : :
359 : : // We compute our shared secret using our key and their public key.
360 [ # # ]: 0 : auto ecdh_secret_1 = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, initiating);
361 : : // They compute their shared secret using their key and our public key.
362 [ # # ]: 0 : auto ecdh_secret_2 = k2.ComputeBIP324ECDHSecret(k1_ellswift, k2_ellswift, !initiating);
363 : : // Those must match, as everyone is behaving correctly.
364 [ # # ][ # # ]: 0 : assert(ecdh_secret_1 == ecdh_secret_2);
365 : :
366 [ # # ][ # # ]: 0 : if (k1_ellswift != k2_ellswift) {
367 : : // Unless the two keys are exactly identical, acting as the wrong party breaks things.
368 [ # # ]: 0 : auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, !initiating);
369 [ # # ][ # # ]: 0 : assert(ecdh_secret_bad != ecdh_secret_1);
370 : 0 : }
371 : :
372 [ # # ][ # # ]: 0 : if (k2_ellswift_bad != k2_ellswift) {
373 : : // Unless both encodings created by them are identical, using the second one breaks things.
374 [ # # ]: 0 : auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift_bad, k1_ellswift, initiating);
375 [ # # ][ # # ]: 0 : assert(ecdh_secret_bad != ecdh_secret_1);
376 : 0 : }
377 [ # # ]: 0 : }
|