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 % 120 120
Test Date: 2024-09-01 05:20:30 Functions: 100.0 % 5 5
Branches: 60.8 % 212 129

             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                 :        9816 : constexpr static inline void crypt_till_rekey(FSChaCha20Poly1305& aead, int rekey_interval, bool encrypt)
      17                 :             : {
      18         [ +  + ]:     2431803 :     for (int i = 0; i < rekey_interval; ++i) {
      19                 :     2421987 :         std::byte dummy_tag[FSChaCha20Poly1305::EXPANSION] = {{}};
      20         [ +  + ]:     2421987 :         if (encrypt) {
      21                 :      807329 :             aead.Encrypt(Span{dummy_tag}.first(0), Span{dummy_tag}.first(0), dummy_tag);
      22                 :      807329 :         } else {
      23                 :     1614658 :             aead.Decrypt(dummy_tag, Span{dummy_tag}.first(0), Span{dummy_tag}.first(0));
      24                 :             :         }
      25                 :     2421987 :     }
      26                 :        9816 : }
      27                 :             : 
      28   [ +  -  +  - ]:         150 : FUZZ_TARGET(crypto_aeadchacha20poly1305)
      29                 :             : {
      30                 :         147 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
      31                 :             : 
      32                 :         147 :     auto key = provider.ConsumeBytes<std::byte>(32);
      33         [ +  - ]:         147 :     key.resize(32);
      34         [ +  - ]:         147 :     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         [ +  - ]:         147 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
      41                 :             : 
      42   [ +  -  +  +  :        9542 :     LIMITED_WHILE(provider.ConsumeBool(), 10000)
                   +  + ]
      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         [ -  + ]:        9395 :         unsigned mode = provider.ConsumeIntegral<uint8_t>();
      50                 :        9395 :         bool use_splits = mode & 1;
      51                 :        9395 :         bool damage = mode & 4;
      52                 :        9395 :         unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
      53         [ -  + ]:        9395 :         unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
      54                 :        9395 :         unsigned length_bits = 2 * ((mode >> 5) & 7);
      55         [ -  + ]:        9395 :         unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
      56                 :             :         // Generate aad and content.
      57                 :        9395 :         auto aad = rng.randbytes<std::byte>(aad_length);
      58                 :        9395 :         auto plain = rng.randbytes<std::byte>(length);
      59         [ -  + ]:        9395 :         std::vector<std::byte> cipher(length + AEADChaCha20Poly1305::EXPANSION);
      60                 :             :         // Generate nonce
      61                 :        9395 :         AEADChaCha20Poly1305::Nonce96 nonce = {(uint32_t)rng(), rng()};
      62                 :             : 
      63   [ +  +  +  + ]:        9395 :         if (use_splits && length > 0) {
      64         [ -  + ]:        6293 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
      65   [ -  +  +  -  :        6293 :             aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, nonce, cipher);
             +  -  +  - ]
      66                 :        6293 :         } else {
      67   [ +  -  +  -  :        3102 :             aead.Encrypt(plain, aad, nonce, cipher);
                   +  - ]
      68                 :             :         }
      69                 :             : 
      70                 :             :         // Test Keystream output
      71         [ -  + ]:        9395 :         std::vector<std::byte> keystream(length);
      72         [ -  + ]:        9395 :         aead.Keystream(nonce, keystream);
      73         [ +  + ]:    17139247 :         for (size_t i = 0; i < length; ++i) {
      74         [ +  - ]:    17129852 :             assert((plain[i] ^ keystream[i]) == cipher[i]);
      75                 :    17129852 :         }
      76                 :             : 
      77         [ -  + ]:        9395 :         std::vector<std::byte> decrypted_contents(length);
      78                 :        9395 :         bool ok{false};
      79                 :             : 
      80                 :             :         // damage the key
      81         [ -  + ]:        9395 :         unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
      82                 :        9395 :         std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
      83         [ -  + ]:        9395 :         std::vector<std::byte> bad_key = key;
      84                 :        9395 :         bad_key[key_position] ^= damage_val;
      85                 :             : 
      86         [ -  + ]:        9395 :         AEADChaCha20Poly1305 bad_aead(bad_key);
      87   [ +  -  +  -  :        9395 :         ok = bad_aead.Decrypt(cipher, aad, nonce, decrypted_contents);
                   +  - ]
      88         [ +  - ]:        9395 :         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         [ +  + ]:        9395 :         if (damage) {
      93         [ +  - ]:          48 :             unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
      94                 :          48 :             unsigned damage_pos = damage_bit >> 3;
      95                 :          48 :             std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
      96         [ +  + ]:          48 :             if (damage_pos >= cipher.size()) {
      97                 :           2 :                 aad[damage_pos - cipher.size()] ^= damage_val;
      98                 :           2 :             } else {
      99                 :          46 :                 cipher[damage_pos] ^= damage_val;
     100                 :             :             }
     101                 :          48 :         }
     102                 :             : 
     103   [ +  +  +  + ]:        9395 :         if (use_splits && length > 0) {
     104         [ +  - ]:        6293 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
     105   [ +  -  +  -  :        6293 :             ok = aead.Decrypt(cipher, aad, nonce, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
             -  +  +  - ]
     106                 :        6293 :         } else {
     107   [ +  -  +  -  :        3102 :             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         [ +  - ]:        9395 :         assert(!ok == damage);
     112         [ +  + ]:        9395 :         if (!ok) break;
     113   [ +  -  +  - ]:        9347 :         assert(decrypted_contents == plain);
     114         [ +  + ]:        9395 :     }
     115                 :         147 : }
     116                 :             : 
     117   [ +  -  +  - ]:         146 : FUZZ_TARGET(crypto_fschacha20poly1305)
     118                 :             : {
     119                 :         143 :     FuzzedDataProvider provider{buffer.data(), buffer.size()};
     120                 :             : 
     121                 :         143 :     uint32_t rekey_interval = provider.ConsumeIntegralInRange<size_t>(32, 512);
     122                 :         143 :     auto key = provider.ConsumeBytes<std::byte>(32);
     123         [ +  - ]:         143 :     key.resize(32);
     124         [ +  - ]:         143 :     FSChaCha20Poly1305 enc_aead(key, rekey_interval);
     125         [ +  - ]:         143 :     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         [ +  - ]:         143 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
     132                 :             : 
     133   [ -  +  +  +  :        3415 :     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         [ -  + ]:        3272 :         unsigned mode = provider.ConsumeIntegral<uint8_t>();
     141                 :        3272 :         bool use_splits = mode & 1;
     142                 :        3272 :         bool damage = mode & 4;
     143                 :        3272 :         unsigned aad_length_bits = 3 * ((mode >> 3) & 3);
     144         [ -  + ]:        3272 :         unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
     145                 :        3272 :         unsigned length_bits = 2 * ((mode >> 5) & 7);
     146         [ -  + ]:        3272 :         unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
     147                 :             :         // Generate aad and content.
     148                 :        3272 :         auto aad = rng.randbytes<std::byte>(aad_length);
     149                 :        3272 :         auto plain = rng.randbytes<std::byte>(length);
     150         [ -  + ]:        3272 :         std::vector<std::byte> cipher(length + FSChaCha20Poly1305::EXPANSION);
     151                 :             : 
     152         [ +  - ]:        3272 :         crypt_till_rekey(enc_aead, rekey_interval, true);
     153   [ +  +  +  + ]:        3272 :         if (use_splits && length > 0) {
     154         [ -  + ]:        2554 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
     155   [ -  +  +  -  :        2554 :             enc_aead.Encrypt(Span{plain}.first(split_index), Span{plain}.subspan(split_index), aad, cipher);
             +  -  +  - ]
     156                 :        2554 :         } else {
     157   [ +  -  +  -  :         718 :             enc_aead.Encrypt(plain, aad, cipher);
                   +  - ]
     158                 :             :         }
     159                 :             : 
     160         [ -  + ]:        3272 :         std::vector<std::byte> decrypted_contents(length);
     161                 :        3272 :         bool ok{false};
     162                 :             : 
     163                 :             :         // damage the key
     164         [ -  + ]:        3272 :         unsigned key_position = provider.ConsumeIntegralInRange<unsigned>(0, 31);
     165                 :        3272 :         std::byte damage_val{(uint8_t)(1U << (key_position & 7))};
     166         [ -  + ]:        3272 :         std::vector<std::byte> bad_key = key;
     167                 :        3272 :         bad_key[key_position] ^= damage_val;
     168                 :             : 
     169         [ -  + ]:        3272 :         FSChaCha20Poly1305 bad_fs_aead(bad_key, rekey_interval);
     170         [ +  - ]:        3272 :         crypt_till_rekey(bad_fs_aead, rekey_interval, false);
     171   [ +  -  +  -  :        3272 :         ok = bad_fs_aead.Decrypt(cipher, aad, decrypted_contents);
                   +  - ]
     172         [ +  - ]:        3272 :         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         [ +  + ]:        3272 :         if (damage) {
     177         [ -  + ]:          44 :             unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0, (cipher.size() + aad.size()) * 8U - 1U);
     178                 :          44 :             unsigned damage_pos = damage_bit >> 3;
     179                 :          44 :             std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
     180         [ +  + ]:          44 :             if (damage_pos >= cipher.size()) {
     181                 :           2 :                 aad[damage_pos - cipher.size()] ^= damage_val;
     182                 :           2 :             } else {
     183                 :          42 :                 cipher[damage_pos] ^= damage_val;
     184                 :             :             }
     185                 :          44 :         }
     186                 :             : 
     187         [ +  - ]:        3272 :         crypt_till_rekey(dec_aead, rekey_interval, false);
     188   [ +  +  +  + ]:        3272 :         if (use_splits && length > 0) {
     189         [ +  - ]:        2554 :             size_t split_index = provider.ConsumeIntegralInRange<size_t>(1, length);
     190   [ +  -  +  -  :        2554 :             ok = dec_aead.Decrypt(cipher, aad, Span{decrypted_contents}.first(split_index), Span{decrypted_contents}.subspan(split_index));
             -  +  +  - ]
     191                 :        2554 :         } else {
     192   [ +  -  +  -  :         718 :             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         [ +  - ]:        3272 :         assert(!ok == damage);
     197         [ +  + ]:        3272 :         if (!ok) break;
     198   [ +  -  -  + ]:        3228 :         assert(decrypted_contents == plain);
     199         [ +  + ]:        3272 :     }
     200                 :         143 : }
        

Generated by: LCOV version 2.0-1