LCOV - code coverage report
Current view: top level - src/test/fuzz - crypto_chacha20poly1305.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 100.0 % 109 109
Test Date: 2024-12-04 04:00:22 Functions: 100.0 % 5 5
Branches: 75.0 % 72 54

             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/chacha20poly1305.h>
       6                 :             : #include <random.h>
       7                 :             : #include <span.h>
       8                 :             : #include <test/fuzz/FuzzedDataProvider.h>
       9                 :             : #include <test/fuzz/fuzz.h>
      10                 :             : #include <test/fuzz/util.h>
      11                 :             : 
      12                 :             : #include <cstddef>
      13                 :             : #include <cstdint>
      14                 :             : #include <vector>
      15                 :             : 
      16                 :       11733 : constexpr static inline void crypt_till_rekey(FSChaCha20Poly1305& aead, int rekey_interval, bool encrypt)
      17                 :             : {
      18         [ +  + ]:     2839296 :     for (int i = 0; i < rekey_interval; ++i) {
      19                 :     2827563 :         std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
      20         [ +  + ]:     2827563 :         if (encrypt) {
      21                 :      942521 :             aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag);
      22                 :             :         } else {
      23                 :     1885042 :             aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0));
      24                 :             :         }
      25                 :             :     }
      26                 :       11733 : }
      27                 :             : 
      28         [ +  - ]:         786 : FUZZ_TARGET(crypto_aeadchacha20poly1305)
      29                 :             : {
      30                 :         168 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
      31                 :             : 
      32                 :         168 :     auto key = provider.ConsumeBytes<std::byte>(32);
      33         [ +  - ]:         168 :     key.resize(32);
      34                 :         168 :     AEADChaCha20Poly1305 aead(key);
      35                 :             : 
      36                 :             :     // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
      37                 :             :     // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
      38                 :             :     // reading the actual data for those from the fuzzer input (which would need large amounts of
      39                 :             :     // data).
      40                 :         168 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
      41                 :             : 
      42   [ +  +  +  + ]:        6191 :     LIMITED_WHILE(provider.ConsumeBool(), 100)
      43                 :             :     {
      44                 :             :         // Mode:
      45                 :             :         // - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.
      46                 :             :         // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
      47                 :             :         // - Bit 3-4: controls the maximum aad length (max 511 bytes)
      48                 :             :         // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
      49                 :        6084 :         unsigned mode = provider.ConsumeIntegral<uint8_t>();
      50                 :        6084 :         bool use_splits = mode & 1;
      51                 :        6084 :         bool damage = mode & 4;
      52                 :        6084 :         unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
      53                 :        6084 :         unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
      54                 :        6084 :         unsigned length_bits = 2 * ((mode >> 5) & 7);
      55                 :        6084 :         unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
      56                 :             :         // Generate aad and content.
      57                 :        6084 :         auto aad = rng.randbytes<std::byte>(aad_length);
      58                 :        6084 :         auto plain = rng.randbytes<std::byte>(length);
      59         [ +  - ]:        6084 :         std::vector<std::byte> cipher(length + AEADChaCha20Poly1305::EXPANSION);
      60                 :             :         // Generate nonce
      61         [ +  + ]:        6084 :         AEADChaCha20Poly1305::Nonce96 nonce = {(uint32_t)rng(), rng()};
      62                 :             : 
      63         [ +  + ]:        6084 :         if (use_splits && length > 0) {
      64                 :        4334 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
      65                 :        4334 :             aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, nonce, cipher);
      66                 :             :         } else {
      67                 :        1750 :             aead.Encrypt(plain, aad, nonce, cipher);
      68                 :             :         }
      69                 :             : 
      70                 :             :         // Test Keystream output
      71         [ +  - ]:        6084 :         std::vector<std::byte> keystream(length);
      72                 :        6084 :         aead.Keystream(nonce, keystream);
      73         [ +  + ]:     9816952 :         for (size_t i = 0; i < length; ++i) {
      74         [ -  + ]:     9810868 :             assert((plain[i] ^ keystream[i]) == cipher[i]);
      75                 :             :         }
      76                 :             : 
      77         [ +  - ]:        6084 :         std::vector<std::byte> decrypted_contents(length);
      78                 :        6084 :         bool ok{false};
      79                 :             : 
      80                 :             :         // damage the key
      81                 :        6084 :         unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
      82                 :        6084 :         std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
      83         [ +  - ]:        6084 :         std::vector<std::byte> bad_key = key;
      84                 :        6084 :         bad_key[key_position] ^= damage_val;
      85                 :             : 
      86                 :        6084 :         AEADChaCha20Poly1305 bad_aead(bad_key);
      87                 :        6084 :         ok = bad_aead.Decrypt(cipher, aad, nonce, decrypted_contents);
      88         [ -  + ]:        6084 :         assert(!ok);
      89                 :             : 
      90                 :             :         // Optionally damage 1 bit in either the cipher (corresponding to a change in transit)
      91                 :             :         // or the aad (to make sure that decryption will fail if the AAD mismatches).
      92         [ +  + ]:        6084 :         if (damage) {
      93                 :          61 :             unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
      94                 :          61 :             unsigned damage_pos = damage_bit >> 3;
      95                 :          61 :             std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
      96         [ +  + ]:          61 :             if (damage_pos >= cipher.size()) {
      97                 :           7 :                 aad[damage_pos - cipher.size()] ^= damage_val;
      98                 :             :             } else {
      99                 :          54 :                 cipher[damage_pos] ^= damage_val;
     100                 :             :             }
     101                 :             :         }
     102                 :             : 
     103         [ +  + ]:        6084 :         if (use_splits && length > 0) {
     104                 :        4334 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
     105                 :        4334 :             ok = aead.Decrypt(cipher, aad, nonce, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
     106                 :             :         } else {
     107                 :        1750 :             ok = aead.Decrypt(cipher, aad, nonce, decrypted_contents);
     108                 :             :         }
     109                 :             : 
     110                 :             :         // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
     111         [ -  + ]:        6084 :         assert(!ok == damage);
     112         [ +  + ]:        6084 :         if (!ok) break;
     113         [ -  + ]:        6023 :         assert(decrypted_contents == plain);
     114                 :        6084 :     }
     115                 :         168 : }
     116                 :             : 
     117         [ +  - ]:         560 : FUZZ_TARGET(crypto_fschacha20poly1305)
     118                 :             : {
     119                 :         148 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
     120                 :             : 
     121                 :         148 :     uint32_t rekey_interval = provider.ConsumeIntegralInRange<size_t>(32, 512);
     122                 :         148 :     auto key = provider.ConsumeBytes<std::byte>(32);
     123         [ +  - ]:         148 :     key.resize(32);
     124                 :         148 :     FSChaCha20Poly1305 enc_aead(key, rekey_interval);
     125                 :         148 :     FSChaCha20Poly1305 dec_aead(key, rekey_interval);
     126                 :             : 
     127                 :             :     // Initialize RNG deterministically, to generate contents and AAD. We assume that there are no
     128                 :             :     // (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
     129                 :             :     // reading the actual data for those from the fuzzer input (which would need large amounts of
     130                 :             :     // data).
     131                 :         148 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
     132                 :             : 
     133   [ +  +  +  + ]:        4012 :     LIMITED_WHILE(provider.ConsumeBool(), 100)
     134                 :             :     {
     135                 :             :         // Mode:
     136                 :             :         // - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix.
     137                 :             :         // - Bit 2: whether this ciphertext will be corrupted (making it the last sent one)
     138                 :             :         // - Bit 3-4: controls the maximum aad length (max 511 bytes)
     139                 :             :         // - Bit 5-7: controls the maximum content length (max 16383 bytes, for performance reasons)
     140                 :        3911 :         unsigned mode = provider.ConsumeIntegral<uint8_t>();
     141                 :        3911 :         bool use_splits = mode & 1;
     142                 :        3911 :         bool damage = mode & 4;
     143                 :        3911 :         unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
     144                 :        3911 :         unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
     145                 :        3911 :         unsigned length_bits = 2 * ((mode >> 5) & 7);
     146                 :        3911 :         unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
     147                 :             :         // Generate aad and content.
     148                 :        3911 :         auto aad = rng.randbytes<std::byte>(aad_length);
     149                 :        3911 :         auto plain = rng.randbytes<std::byte>(length);
     150         [ +  - ]:        3911 :         std::vector<std::byte> cipher(length + FSChaCha20Poly1305::EXPANSION);
     151                 :             : 
     152                 :        3911 :         crypt_till_rekey(enc_aead, rekey_interval, true);
     153         [ +  + ]:        3911 :         if (use_splits && length > 0) {
     154                 :        2950 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
     155                 :        2950 :             enc_aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, cipher);
     156                 :             :         } else {
     157                 :         961 :             enc_aead.Encrypt(plain, aad, cipher);
     158                 :             :         }
     159                 :             : 
     160         [ +  - ]:        3911 :         std::vector<std::byte> decrypted_contents(length);
     161                 :        3911 :         bool ok{false};
     162                 :             : 
     163                 :             :         // damage the key
     164                 :        3911 :         unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
     165                 :        3911 :         std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
     166         [ +  - ]:        3911 :         std::vector<std::byte> bad_key = key;
     167                 :        3911 :         bad_key[key_position] ^= damage_val;
     168                 :             : 
     169                 :        3911 :         FSChaCha20Poly1305 bad_fs_aead(bad_key, rekey_interval);
     170                 :        3911 :         crypt_till_rekey(bad_fs_aead, rekey_interval, false);
     171                 :        3911 :         ok = bad_fs_aead.Decrypt(cipher, aad, decrypted_contents);
     172         [ -  + ]:        3911 :         assert(!ok);
     173                 :             : 
     174                 :             :         // Optionally damage 1 bit in either the cipher (corresponding to a change in transit)
     175                 :             :         // or the aad (to make sure that decryption will fail if the AAD mismatches).
     176         [ +  + ]:        3911 :         if (damage) {
     177                 :          47 :             unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
     178                 :          47 :             unsigned damage_pos = damage_bit >> 3;
     179                 :          47 :             std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
     180         [ +  + ]:          47 :             if (damage_pos >= cipher.size()) {
     181                 :           3 :                 aad[damage_pos - cipher.size()] ^= damage_val;
     182                 :             :             } else {
     183                 :          44 :                 cipher[damage_pos] ^= damage_val;
     184                 :             :             }
     185                 :             :         }
     186                 :             : 
     187                 :        3911 :         crypt_till_rekey(dec_aead, rekey_interval, false);
     188         [ +  + ]:        3911 :         if (use_splits && length > 0) {
     189                 :        2950 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
     190                 :        2950 :             ok = dec_aead.Decrypt(cipher, aad, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
     191                 :             :         } else {
     192                 :         961 :             ok = dec_aead.Decrypt(cipher, aad, decrypted_contents);
     193                 :             :         }
     194                 :             : 
     195                 :             :         // Decryption *must* fail if the packet was damaged, and succeed if it wasn't.
     196         [ -  + ]:        3911 :         assert(!ok == damage);
     197         [ +  + ]:        3911 :         if (!ok) break;
     198         [ -  + ]:        3864 :         assert(decrypted_contents == plain);
     199                 :        3911 :     }
     200                 :         148 : }
        

Generated by: LCOV version 2.0-1