/ src / test / fuzz / bip324.cpp
bip324.cpp
  1  // Copyright (c) 2023 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 <bip324.h>
  6  #include <chainparams.h>
  7  #include <span.h>
  8  #include <test/fuzz/FuzzedDataProvider.h>
  9  #include <test/fuzz/fuzz.h>
 10  #include <test/fuzz/util.h>
 11  #include <test/util/xoroshiro128plusplus.h>
 12  
 13  #include <cstdint>
 14  #include <vector>
 15  
 16  namespace {
 17  
 18  void Initialize()
 19  {
 20      ECC_Start();
 21      SelectParams(ChainType::MAIN);
 22  }
 23  
 24  }  // namespace
 25  
 26  FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
 27  {
 28      // Test that BIP324Cipher's encryption and decryption agree.
 29  
 30      // Load keys from fuzzer.
 31      FuzzedDataProvider provider(buffer.data(), buffer.size());
 32      // Initiator key
 33      CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true);
 34      if (!init_key.IsValid()) return;
 35      // Initiator entropy
 36      auto init_ent = provider.ConsumeBytes<std::byte>(32);
 37      init_ent.resize(32);
 38      // Responder key
 39      CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true);
 40      if (!resp_key.IsValid()) return;
 41      // Responder entropy
 42      auto resp_ent = provider.ConsumeBytes<std::byte>(32);
 43      resp_ent.resize(32);
 44  
 45      // Initialize ciphers by exchanging public keys.
 46      BIP324Cipher initiator(init_key, init_ent);
 47      assert(!initiator);
 48      BIP324Cipher responder(resp_key, resp_ent);
 49      assert(!responder);
 50      initiator.Initialize(responder.GetOurPubKey(), true);
 51      assert(initiator);
 52      responder.Initialize(initiator.GetOurPubKey(), false);
 53      assert(responder);
 54  
 55      // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
 56      // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
 57      // reading the actual data for those from the fuzzer input (which would need large amounts of
 58      // data).
 59      XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
 60  
 61      // Compare session IDs and garbage terminators.
 62      assert(initiator.GetSessionID() == responder.GetSessionID());
 63      assert(initiator.GetSendGarbageTerminator() == responder.GetReceiveGarbageTerminator());
 64      assert(initiator.GetReceiveGarbageTerminator() == responder.GetSendGarbageTerminator());
 65  
 66      LIMITED_WHILE(provider.remaining_bytes(), 1000) {
 67          // Mode:
 68          // - Bit 0: whether the ignore bit is set in message
 69          // - Bit 1: whether the responder (0) or initiator (1) sends
 70          // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
 71          // - Bit 3-4: controls the maximum aad length (max 4095 bytes)
 72          // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
 73          unsigned mode = provider.ConsumeIntegral<uint8_t>();
 74          bool ignore = mode & 1;
 75          bool from_init = mode & 2;
 76          bool damage = mode & 4;
 77          unsigned aad_length_bits = 4 * ((mode >> 3) & 3);
 78          unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
 79          unsigned length_bits = 2 * ((mode >> 5) & 7);
 80          unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
 81          // Generate aad and content.
 82          std::vector<std::byte> aad(aad_length);
 83          for (auto& val : aad) val = std::byte{(uint8_t)rng()};
 84          std::vector<std::byte> contents(length);
 85          for (auto& val : contents) val = std::byte{(uint8_t)rng()};
 86  
 87          // Pick sides.
 88          auto& sender{from_init ? initiator : responder};
 89          auto& receiver{from_init ? responder : initiator};
 90  
 91          // Encrypt
 92          std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
 93          sender.Encrypt(contents, aad, ignore, ciphertext);
 94  
 95          // Optionally damage 1 bit in either the ciphertext (corresponding to a change in transit)
 96          // or the aad (to make sure that decryption will fail if the AAD mismatches).
 97          if (damage) {
 98              unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
 99                  (ciphertext.size() + aad.size()) * 8U - 1U);
100              unsigned damage_pos = damage_bit >> 3;
101              std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
102              if (damage_pos >= ciphertext.size()) {
103                  aad[damage_pos - ciphertext.size()] ^= damage_val;
104              } else {
105                  ciphertext[damage_pos] ^= damage_val;
106              }
107          }
108  
109          // Decrypt length
110          uint32_t dec_length = receiver.DecryptLength(Span{ciphertext}.first(initiator.LENGTH_LEN));
111          if (!damage) {
112              assert(dec_length == length);
113          } else {
114              // For performance reasons, don't try to decode if length got increased too much.
115              if (dec_length > 16384 + length) break;
116              // Otherwise, just append zeros if dec_length > length.
117              ciphertext.resize(dec_length + initiator.EXPANSION);
118          }
119  
120          // Decrypt
121          std::vector<std::byte> decrypt(dec_length);
122          bool dec_ignore{false};
123          bool ok = receiver.Decrypt(Span{ciphertext}.subspan(initiator.LENGTH_LEN), aad, dec_ignore, decrypt);
124          // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
125          assert(!ok == damage);
126          if (!ok) break;
127          assert(ignore == dec_ignore);
128          assert(decrypt == contents);
129      }
130  }