LCOV - code coverage report
Current view: top level - src/test/fuzz - bip324.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 1 68 1.5 %
Date: 2023-09-26 12:08:55 Functions: 2 4 50.0 %

          Line data    Source code
       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           0 : void Initialize()
      19             : {
      20           0 :     ECC_Start();
      21           0 :     SelectParams(ChainType::MAIN);
      22           0 : }
      23             : 
      24             : }  // namespace
      25             : 
      26           4 : FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
      27             : {
      28             :     // Test that BIP324Cipher's encryption and decryption agree.
      29             : 
      30             :     // Load keys from fuzzer.
      31           0 :     FuzzedDataProvider provider(buffer.data(), buffer.size());
      32             :     // Initiator key
      33           0 :     CKey init_key = ConsumePrivateKey(provider, /*compressed=*/true);
      34           0 :     if (!init_key.IsValid()) return;
      35             :     // Initiator entropy
      36           0 :     auto init_ent = provider.ConsumeBytes<std::byte>(32);
      37           0 :     init_ent.resize(32);
      38             :     // Responder key
      39           0 :     CKey resp_key = ConsumePrivateKey(provider, /*compressed=*/true);
      40           0 :     if (!resp_key.IsValid()) return;
      41             :     // Responder entropy
      42           0 :     auto resp_ent = provider.ConsumeBytes<std::byte>(32);
      43           0 :     resp_ent.resize(32);
      44             : 
      45             :     // Initialize ciphers by exchanging public keys.
      46           0 :     BIP324Cipher initiator(init_key, init_ent);
      47           0 :     assert(!initiator);
      48           0 :     BIP324Cipher responder(resp_key, resp_ent);
      49           0 :     assert(!responder);
      50           0 :     initiator.Initialize(responder.GetOurPubKey(), true);
      51           0 :     assert(initiator);
      52           0 :     responder.Initialize(initiator.GetOurPubKey(), false);
      53           0 :     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           0 :     XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
      60             : 
      61             :     // Compare session IDs and garbage terminators.
      62           0 :     assert(initiator.GetSessionID() == responder.GetSessionID());
      63           0 :     assert(initiator.GetSendGarbageTerminator() == responder.GetReceiveGarbageTerminator());
      64           0 :     assert(initiator.GetReceiveGarbageTerminator() == responder.GetSendGarbageTerminator());
      65             : 
      66           0 :     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           0 :         unsigned mode = provider.ConsumeIntegral<uint8_t>();
      74           0 :         bool ignore = mode & 1;
      75           0 :         bool from_init = mode & 2;
      76           0 :         bool damage = mode & 4;
      77           0 :         unsigned aad_length_bits = 4 * ((mode >> 3) & 3);
      78           0 :         unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
      79           0 :         unsigned length_bits = 2 * ((mode >> 5) & 7);
      80           0 :         unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
      81             :         // Generate aad and content.
      82           0 :         std::vector<std::byte> aad(aad_length);
      83           0 :         for (auto& val : aad) val = std::byte{(uint8_t)rng()};
      84           0 :         std::vector<std::byte> contents(length);
      85           0 :         for (auto& val : contents) val = std::byte{(uint8_t)rng()};
      86             : 
      87             :         // Pick sides.
      88           0 :         auto& sender{from_init ? initiator : responder};
      89           0 :         auto& receiver{from_init ? responder : initiator};
      90             : 
      91             :         // Encrypt
      92           0 :         std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
      93           0 :         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           0 :         if (damage) {
      98           0 :             unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
      99           0 :                 (ciphertext.size() + aad.size()) * 8U - 1U);
     100           0 :             unsigned damage_pos = damage_bit >> 3;
     101           0 :             std::byte damage_val{(uint8_t)(1U << (damage_bit & 3))};
     102           0 :             if (damage_pos >= ciphertext.size()) {
     103           0 :                 aad[damage_pos - ciphertext.size()] ^= damage_val;
     104           0 :             } else {
     105           0 :                 ciphertext[damage_pos] ^= damage_val;
     106             :             }
     107           0 :         }
     108             : 
     109             :         // Decrypt length
     110           0 :         uint32_t dec_length = receiver.DecryptLength(Span{ciphertext}.first(initiator.LENGTH_LEN));
     111           0 :         if (!damage) {
     112           0 :             assert(dec_length == length);
     113           0 :         } else {
     114             :             // For performance reasons, don't try to decode if length got increased too much.
     115           0 :             if (dec_length > 16384 + length) break;
     116             :             // Otherwise, just append zeros if dec_length > length.
     117           0 :             ciphertext.resize(dec_length + initiator.EXPANSION);
     118             :         }
     119             : 
     120             :         // Decrypt
     121           0 :         std::vector<std::byte> decrypt(dec_length);
     122           0 :         bool dec_ignore{false};
     123           0 :         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           0 :         assert(!ok == damage);
     126           0 :         if (!ok) break;
     127           0 :         assert(ignore == dec_ignore);
     128           0 :         assert(decrypt == contents);
     129           0 :     }
     130           0 : }

Generated by: LCOV version 1.14