key.cpp raw

   1  // Copyright (c) 2020-2022 The Limenka 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          assert(GetAllDestinationsForKey(pubkey).size() == 3);
 108      }
 109  
 110      {
 111          DataStream data_stream{};
 112          pubkey.Serialize(data_stream);
 113  
 114          CPubKey pubkey_deserialized;
 115          pubkey_deserialized.Unserialize(data_stream);
 116          assert(pubkey_deserialized == pubkey);
 117      }
 118  
 119      {
 120          const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey);
 121          assert(!tx_pubkey_script.IsPayToScriptHash());
 122          assert(!tx_pubkey_script.IsPayToWitnessScriptHash());
 123          assert(!tx_pubkey_script.IsPushOnly());
 124          assert(!tx_pubkey_script.IsUnspendable());
 125          assert(tx_pubkey_script.HasValidOps());
 126          assert(tx_pubkey_script.size() == 35);
 127  
 128          const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey});
 129          assert(!tx_multisig_script.IsPayToScriptHash());
 130          assert(!tx_multisig_script.IsPayToWitnessScriptHash());
 131          assert(!tx_multisig_script.IsPushOnly());
 132          assert(!tx_multisig_script.IsUnspendable());
 133          assert(tx_multisig_script.HasValidOps());
 134          assert(tx_multisig_script.size() == 37);
 135  
 136          FillableSigningProvider fillable_signing_provider;
 137          assert(!IsSegWitOutput(fillable_signing_provider, tx_pubkey_script));
 138          assert(!IsSegWitOutput(fillable_signing_provider, tx_multisig_script));
 139          assert(fillable_signing_provider.GetKeys().size() == 0);
 140          assert(!fillable_signing_provider.HaveKey(pubkey.GetID()));
 141  
 142          const bool ok_add_key = fillable_signing_provider.AddKey(key);
 143          assert(ok_add_key);
 144          assert(fillable_signing_provider.HaveKey(pubkey.GetID()));
 145  
 146          FillableSigningProvider fillable_signing_provider_pub;
 147          assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
 148  
 149          const bool ok_add_key_pubkey = fillable_signing_provider_pub.AddKeyPubKey(key, pubkey);
 150          assert(ok_add_key_pubkey);
 151          assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
 152  
 153          TxoutType which_type_tx_pubkey;
 154          const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey);
 155          assert(is_standard_tx_pubkey);
 156          assert(which_type_tx_pubkey == TxoutType::PUBKEY);
 157  
 158          TxoutType which_type_tx_multisig;
 159          const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, std::nullopt, which_type_tx_multisig);
 160          assert(is_standard_tx_multisig);
 161          assert(which_type_tx_multisig == TxoutType::MULTISIG);
 162  
 163          std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
 164          const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
 165          assert(outtype_tx_pubkey == TxoutType::PUBKEY);
 166          assert(v_solutions_ret_tx_pubkey.size() == 1);
 167          assert(v_solutions_ret_tx_pubkey[0].size() == 33);
 168  
 169          std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
 170          const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
 171          assert(outtype_tx_multisig == TxoutType::MULTISIG);
 172          assert(v_solutions_ret_tx_multisig.size() == 3);
 173          assert(v_solutions_ret_tx_multisig[0].size() == 1);
 174          assert(v_solutions_ret_tx_multisig[1].size() == 33);
 175          assert(v_solutions_ret_tx_multisig[2].size() == 1);
 176  
 177          OutputType output_type{};
 178          const CTxDestination tx_destination = GetDestinationForKey(pubkey, output_type);
 179          assert(output_type == OutputType::LEGACY);
 180          assert(IsValidDestination(tx_destination));
 181          assert(PKHash{pubkey} == *std::get_if<PKHash>(&tx_destination));
 182  
 183          const CScript script_for_destination = GetScriptForDestination(tx_destination);
 184          assert(script_for_destination.size() == 25);
 185  
 186          const std::string destination_address = EncodeDestination(tx_destination);
 187          assert(DecodeDestination(destination_address) == tx_destination);
 188  
 189          const CPubKey pubkey_from_address_string = AddrToPubKey(fillable_signing_provider, destination_address);
 190          assert(pubkey_from_address_string == pubkey);
 191  
 192          CKeyID key_id = pubkey.GetID();
 193          assert(!key_id.IsNull());
 194          assert(key_id == CKeyID{key_id});
 195          assert(key_id == GetKeyForDestination(fillable_signing_provider, tx_destination));
 196  
 197          CPubKey pubkey_out;
 198          const bool ok_get_pubkey = fillable_signing_provider.GetPubKey(key_id, pubkey_out);
 199          assert(ok_get_pubkey);
 200  
 201          CKey key_out;
 202          const bool ok_get_key = fillable_signing_provider.GetKey(key_id, key_out);
 203          assert(ok_get_key);
 204          assert(fillable_signing_provider.GetKeys().size() == 1);
 205          assert(fillable_signing_provider.HaveKey(key_id));
 206  
 207          KeyOriginInfo key_origin_info;
 208          const bool ok_get_key_origin = fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info);
 209          assert(!ok_get_key_origin);
 210      }
 211  
 212      {
 213          const std::vector<unsigned char> vch_pubkey{pubkey.begin(), pubkey.end()};
 214          assert(CPubKey::ValidSize(vch_pubkey));
 215          assert(!CPubKey::ValidSize({pubkey.begin(), pubkey.begin() + pubkey.size() - 1}));
 216  
 217          const CPubKey pubkey_ctor_1{vch_pubkey};
 218          assert(pubkey == pubkey_ctor_1);
 219  
 220          const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()};
 221          assert(pubkey == pubkey_ctor_2);
 222  
 223          CPubKey pubkey_set;
 224          pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end());
 225          assert(pubkey == pubkey_set);
 226      }
 227  
 228      {
 229          const CPubKey invalid_pubkey{};
 230          assert(!invalid_pubkey.IsValid());
 231          assert(!invalid_pubkey.IsFullyValid());
 232          assert(!(pubkey == invalid_pubkey));
 233          assert(pubkey != invalid_pubkey);
 234          assert(pubkey < invalid_pubkey);
 235      }
 236  
 237      {
 238          // Cover CPubKey's operator[](unsigned int pos)
 239          unsigned int sum = 0;
 240          for (size_t i = 0; i < pubkey.size(); ++i) {
 241              sum += pubkey[i];
 242          }
 243          assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum);
 244      }
 245  
 246      {
 247          CPubKey decompressed_pubkey = pubkey;
 248          assert(decompressed_pubkey.IsCompressed());
 249  
 250          const bool ok = decompressed_pubkey.Decompress();
 251          assert(ok);
 252          assert(!decompressed_pubkey.IsCompressed());
 253          assert(decompressed_pubkey.size() == 65);
 254      }
 255  
 256      {
 257          std::vector<unsigned char> vch_sig;
 258          const bool ok = key.Sign(random_uint256, vch_sig, false);
 259          assert(ok);
 260          assert(pubkey.Verify(random_uint256, vch_sig));
 261          assert(CPubKey::CheckLowS(vch_sig));
 262  
 263          const std::vector<unsigned char> vch_invalid_sig{vch_sig.begin(), vch_sig.begin() + vch_sig.size() - 1};
 264          assert(!pubkey.Verify(random_uint256, vch_invalid_sig));
 265          assert(!CPubKey::CheckLowS(vch_invalid_sig));
 266      }
 267  
 268      {
 269          std::vector<unsigned char> vch_compact_sig;
 270          const bool ok_sign_compact = key.SignCompact(random_uint256, vch_compact_sig);
 271          assert(ok_sign_compact);
 272  
 273          CPubKey recover_pubkey;
 274          const bool ok_recover_compact = recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig);
 275          assert(ok_recover_compact);
 276          assert(recover_pubkey == pubkey);
 277      }
 278  
 279      {
 280          CPubKey child_pubkey;
 281          ChainCode child_chaincode;
 282          const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
 283          assert(ok);
 284          assert(child_pubkey != pubkey);
 285          assert(child_pubkey.IsCompressed());
 286          assert(child_pubkey.IsFullyValid());
 287          assert(child_pubkey.IsValid());
 288          assert(child_pubkey.size() == 33);
 289          assert(child_chaincode != random_uint256);
 290      }
 291  
 292      const CPrivKey priv_key = key.GetPrivKey();
 293  
 294      {
 295          for (const bool skip_check : {true, false}) {
 296              CKey loaded_key;
 297              const bool ok = loaded_key.Load(priv_key, pubkey, skip_check);
 298              assert(ok);
 299              assert(key == loaded_key);
 300          }
 301      }
 302  }
 303  
 304  FUZZ_TARGET(ellswift_roundtrip, .init = initialize_key)
 305  {
 306      FuzzedDataProvider fdp{buffer.data(), buffer.size()};
 307  
 308      CKey key = ConsumePrivateKey(fdp, /*compressed=*/true);
 309      if (!key.IsValid()) return;
 310  
 311      auto ent32 = fdp.ConsumeBytes<std::byte>(32);
 312      ent32.resize(32);
 313  
 314      auto encoded_ellswift = key.EllSwiftCreate(ent32);
 315      auto decoded_pubkey = encoded_ellswift.Decode();
 316  
 317      uint256 hash{ConsumeUInt256(fdp)};
 318      std::vector<unsigned char> sig;
 319      key.Sign(hash, sig);
 320      assert(decoded_pubkey.Verify(hash, sig));
 321  }
 322  
 323  FUZZ_TARGET(bip324_ecdh, .init = initialize_key)
 324  {
 325      FuzzedDataProvider fdp{buffer.data(), buffer.size()};
 326  
 327      // We generate private key, k1.
 328      CKey k1 = ConsumePrivateKey(fdp, /*compressed=*/true);
 329      if (!k1.IsValid()) return;
 330  
 331      // They generate private key, k2.
 332      CKey k2 = ConsumePrivateKey(fdp, /*compressed=*/true);
 333      if (!k2.IsValid()) return;
 334  
 335      // We construct an ellswift encoding for our key, k1_ellswift.
 336      auto ent32_1 = fdp.ConsumeBytes<std::byte>(32);
 337      ent32_1.resize(32);
 338      auto k1_ellswift = k1.EllSwiftCreate(ent32_1);
 339  
 340      // They construct an ellswift encoding for their key, k2_ellswift.
 341      auto ent32_2 = fdp.ConsumeBytes<std::byte>(32);
 342      ent32_2.resize(32);
 343      auto k2_ellswift = k2.EllSwiftCreate(ent32_2);
 344  
 345      // They construct another (possibly distinct) ellswift encoding for their key, k2_ellswift_bad.
 346      auto ent32_2_bad = fdp.ConsumeBytes<std::byte>(32);
 347      ent32_2_bad.resize(32);
 348      auto k2_ellswift_bad = k2.EllSwiftCreate(ent32_2_bad);
 349      assert((ent32_2_bad == ent32_2) == (k2_ellswift_bad == k2_ellswift));
 350  
 351      // Determine who is who.
 352      bool initiating = fdp.ConsumeBool();
 353  
 354      // We compute our shared secret using our key and their public key.
 355      auto ecdh_secret_1 = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, initiating);
 356      // They compute their shared secret using their key and our public key.
 357      auto ecdh_secret_2 = k2.ComputeBIP324ECDHSecret(k1_ellswift, k2_ellswift, !initiating);
 358      // Those must match, as everyone is behaving correctly.
 359      assert(ecdh_secret_1 == ecdh_secret_2);
 360  
 361      if (k1_ellswift != k2_ellswift) {
 362          // Unless the two keys are exactly identical, acting as the wrong party breaks things.
 363          auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, !initiating);
 364          assert(ecdh_secret_bad != ecdh_secret_1);
 365      }
 366  
 367      if (k2_ellswift_bad != k2_ellswift) {
 368          // Unless both encodings created by them are identical, using the second one breaks things.
 369          auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift_bad, k1_ellswift, initiating);
 370          assert(ecdh_secret_bad != ecdh_secret_1);
 371      }
 372  }
 373