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