LCOV - code coverage report
Current view: top level - src/test/fuzz - crypto_chacha20.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 95 95 100.0 %
Date: 2023-10-05 15:40:34 Functions: 18 18 100.0 %
Branches: 96 162 59.3 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2020-2021 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 <crypto/chacha20.h>
       6                 :            : #include <test/fuzz/FuzzedDataProvider.h>
       7                 :            : #include <test/fuzz/fuzz.h>
       8                 :            : #include <test/fuzz/util.h>
       9                 :            : #include <test/util/xoroshiro128plusplus.h>
      10                 :            : 
      11                 :            : #include <array>
      12                 :            : #include <cstddef>
      13                 :            : #include <cstdint>
      14                 :            : #include <vector>
      15                 :            : 
      16         [ -  + ]:        452 : FUZZ_TARGET(crypto_chacha20)
      17                 :            : {
      18                 :        106 :     FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
      19                 :            : 
      20                 :        106 :     const auto key = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, ChaCha20::KEYLEN);
      21         [ +  - ]:        106 :     ChaCha20 chacha20{key};
      22                 :            : 
      23   [ +  -  +  +  :      13667 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
                   +  + ]
      24         [ +  - ]:      13561 :         CallOneOf(
      25   [ +  -  +  -  :        692 :             fuzzed_data_provider,
             +  -  +  - ]
      26                 :      14491 :             [&] {
      27                 :        930 :                 auto key = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, ChaCha20::KEYLEN);
      28         [ +  - ]:        930 :                 chacha20.SetKey(key);
      29                 :        930 :             },
      30                 :      14468 :             [&] {
      31                 :       1814 :                 chacha20.Seek(
      32                 :        907 :                     {
      33                 :        907 :                         fuzzed_data_provider.ConsumeIntegral<uint32_t>(),
      34                 :        907 :                         fuzzed_data_provider.ConsumeIntegral<uint64_t>()
      35                 :        907 :                     }, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
      36                 :        907 :             },
      37                 :      21180 :             [&] {
      38         [ +  - ]:       7619 :                 std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
      39                 :       7619 :                 chacha20.Keystream(MakeWritableByteSpan(output));
      40                 :       7619 :             },
      41                 :      17666 :             [&] {
      42         [ +  - ]:       4105 :                 std::vector<std::byte> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
      43                 :       4105 :                 const auto input = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, output.size());
      44   [ +  -  +  - ]:       4105 :                 chacha20.Crypt(input, output);
      45                 :       4105 :             });
      46                 :      13561 :     }
      47                 :        106 : }
      48                 :            : 
      49                 :            : namespace
      50                 :            : {
      51                 :            : 
      52                 :            : /** Fuzzer that invokes ChaCha20::Crypt() or ChaCha20::Keystream multiple times:
      53                 :            :     once for a large block at once, and then the same data in chunks, comparing
      54                 :            :     the outcome.
      55                 :            : 
      56                 :            :     If UseCrypt, seeded Xoroshiro128++ output is used as input to Crypt().
      57                 :            :     If not, Keystream() is used directly, or sequences of 0x00 are encrypted.
      58                 :            : */
      59                 :            : template<bool UseCrypt>
      60                 :        187 : void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
      61                 :            : {
      62                 :            :     // Determine key, iv, start position, length.
      63                 :        187 :     auto key_bytes = ConsumeFixedLengthByteVector<std::byte>(provider, ChaCha20::KEYLEN);
      64                 :        187 :     uint64_t iv = provider.ConsumeIntegral<uint64_t>();
      65                 :        187 :     uint32_t iv_prefix = provider.ConsumeIntegral<uint32_t>();
      66                 :        187 :     uint64_t total_bytes = provider.ConsumeIntegralInRange<uint64_t>(0, 1000000);
      67                 :            :     /* ~x = 2^BITS - 1 - x, so ~(total_bytes >> 6) is the maximal seek position. */
      68                 :        187 :     uint32_t seek = provider.ConsumeIntegralInRange<uint32_t>(0, ~(uint32_t)(total_bytes >> 6));
      69                 :            : 
      70                 :            :     // Initialize two ChaCha20 ciphers, with the same key/iv/position.
      71   [ +  -  +  - ]:        187 :     ChaCha20 crypt1(key_bytes);
      72   [ +  -  +  - ]:        187 :     ChaCha20 crypt2(key_bytes);
      73   [ +  -  +  - ]:        187 :     crypt1.Seek({iv_prefix, iv}, seek);
      74   [ +  -  +  - ]:        187 :     crypt2.Seek({iv_prefix, iv}, seek);
      75                 :            : 
      76                 :            :     // Construct vectors with data.
      77                 :        187 :     std::vector<std::byte> data1, data2;
      78   [ +  -  +  - ]:        187 :     data1.resize(total_bytes);
      79   [ +  -  +  - ]:        187 :     data2.resize(total_bytes);
      80                 :            : 
      81                 :            :     // If using Crypt(), initialize data1 and data2 with the same Xoroshiro128++ based
      82                 :            :     // stream.
      83                 :            :     if constexpr (UseCrypt) {
      84                 :         85 :         uint64_t seed = provider.ConsumeIntegral<uint64_t>();
      85                 :         85 :         XoRoShiRo128PlusPlus rng(seed);
      86                 :         85 :         uint64_t bytes = 0;
      87         [ +  + ]:    2450656 :         while (bytes < (total_bytes & ~uint64_t{7})) {
      88                 :    2450571 :             uint64_t val = rng();
      89   [ +  -  +  - ]:    2450571 :             WriteLE64(UCharCast(data1.data() + bytes), val);
      90   [ +  -  +  - ]:    2450571 :             WriteLE64(UCharCast(data2.data() + bytes), val);
      91                 :    2450571 :             bytes += 8;
      92                 :            :         }
      93         [ +  + ]:         85 :         if (bytes < total_bytes) {
      94                 :            :             std::byte valbytes[8];
      95                 :         54 :             uint64_t val = rng();
      96   [ +  -  +  - ]:         54 :             WriteLE64(UCharCast(valbytes), val);
      97         [ +  - ]:         54 :             std::copy(valbytes, valbytes + (total_bytes - bytes), data1.data() + bytes);
      98         [ +  - ]:         54 :             std::copy(valbytes, valbytes + (total_bytes - bytes), data2.data() + bytes);
      99                 :         54 :         }
     100                 :            :     }
     101                 :            : 
     102                 :            :     // Whether UseCrypt is used or not, the two byte arrays must match.
     103   [ +  -  +  -  :        187 :     assert(data1 == data2);
             +  -  +  - ]
     104                 :            : 
     105                 :            :     // Encrypt data1, the whole array at once.
     106                 :            :     if constexpr (UseCrypt) {
     107   [ +  -  +  - ]:         85 :         crypt1.Crypt(data1, data1);
     108                 :            :     } else {
     109         [ +  - ]:        102 :         crypt1.Keystream(data1);
     110                 :            :     }
     111                 :            : 
     112                 :            :     // Encrypt data2, in at most 256 chunks.
     113                 :        187 :     uint64_t bytes2 = 0;
     114                 :        187 :     int iter = 0;
     115                 :      30917 :     while (true) {
     116   [ +  +  +  +  :      30917 :         bool is_last = (iter == 255) || (bytes2 == total_bytes) || provider.ConsumeBool();
          +  -  +  +  +  
                +  +  - ]
     117                 :      30917 :         ++iter;
     118                 :            :         // Determine how many bytes to encrypt in this chunk: a fuzzer-determined
     119                 :            :         // amount for all but the last chunk (which processes all remaining bytes).
     120   [ +  +  +  + ]:      30917 :         uint64_t now = is_last ? total_bytes - bytes2 :
     121                 :      30730 :             provider.ConsumeIntegralInRange<uint64_t>(0, total_bytes - bytes2);
     122                 :            :         // For each chunk, consider using Crypt() even when UseCrypt is false.
     123                 :            :         // This tests that Keystream() has the same behavior as Crypt() applied
     124                 :            :         // to 0x00 input bytes.
     125   [ +  -  +  + ]:      16627 :         if (UseCrypt || provider.ConsumeBool()) {
     126   [ +  -  +  -  :      15228 :             crypt2.Crypt(Span{data2}.subspan(bytes2, now), Span{data2}.subspan(bytes2, now));
             +  -  +  - ]
     127                 :        938 :         } else {
     128         [ +  - ]:      15689 :             crypt2.Keystream(Span{data2}.subspan(bytes2, now));
     129                 :            :         }
     130                 :      30917 :         bytes2 += now;
     131   [ +  +  +  + ]:      30917 :         if (is_last) break;
     132                 :            :     }
     133                 :            :     // We should have processed everything now.
     134   [ +  -  +  - ]:        187 :     assert(bytes2 == total_bytes);
     135                 :            :     // And the result should match.
     136   [ +  -  +  -  :        187 :     assert(data1 == data2);
             +  -  +  - ]
     137                 :        187 : }
     138                 :            : 
     139                 :            : } // namespace
     140                 :            : 
     141         [ -  + ]:        431 : FUZZ_TARGET(chacha20_split_crypt)
     142                 :            : {
     143                 :         85 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
     144                 :         85 :     ChaCha20SplitFuzz<true>(provider);
     145                 :         85 : }
     146                 :            : 
     147         [ -  + ]:        448 : FUZZ_TARGET(chacha20_split_keystream)
     148                 :            : {
     149                 :        102 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
     150                 :        102 :     ChaCha20SplitFuzz<false>(provider);
     151                 :        102 : }
     152                 :            : 
     153         [ -  + ]:        420 : FUZZ_TARGET(crypto_fschacha20)
     154                 :            : {
     155                 :         74 :     FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
     156                 :            : 
     157                 :         74 :     auto key = fuzzed_data_provider.ConsumeBytes<std::byte>(FSChaCha20::KEYLEN);
     158         [ +  - ]:         74 :     key.resize(FSChaCha20::KEYLEN);
     159                 :            : 
     160   [ +  -  +  - ]:         74 :     auto fsc20 = FSChaCha20{key, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 1024)};
     161                 :            : 
     162   [ +  -  +  +  :       4025 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
                   +  + ]
     163                 :            :     {
     164   [ +  -  +  - ]:       3951 :         auto input = fuzzed_data_provider.ConsumeBytes<std::byte>(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096));
     165                 :       3951 :         std::vector<std::byte> output;
     166         [ +  - ]:       3951 :         output.resize(input.size());
     167   [ +  -  +  - ]:       3951 :         fsc20.Crypt(input, output);
     168                 :       3951 :     }
     169                 :         74 : }

Generated by: LCOV version 1.14