#include <crypto/chacha20.h>
#include <random.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <vector>
FUZZ_TARGET(crypto_chacha20)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
const auto key = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, ChaCha20::KEYLEN);
ChaCha20 chacha20{key};
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
CallOneOf(
fuzzed_data_provider,
[&] {
auto key = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, ChaCha20::KEYLEN);
chacha20.SetKey(key);
},
[&] {
ChaCha20::Nonce96 nonce{
fuzzed_data_provider.ConsumeIntegral<uint32_t>(),
fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
chacha20.Seek(nonce, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
},
[&] {
std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
chacha20.Keystream(MakeWritableByteSpan(output));
},
[&] {
std::vector<std::byte> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
const auto input = ConsumeFixedLengthByteVector<std::byte>(fuzzed_data_provider, output.size());
chacha20.Crypt(input, output);
});
}
}
namespace
{
template<bool UseCrypt>
void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
{
auto key_bytes = ConsumeFixedLengthByteVector<std::byte>(provider, ChaCha20::KEYLEN);
uint64_t iv = provider.ConsumeIntegral<uint64_t>();
uint32_t iv_prefix = provider.ConsumeIntegral<uint32_t>();
uint64_t total_bytes = provider.ConsumeIntegralInRange<uint64_t>(0, 1000000);
uint32_t seek = provider.ConsumeIntegralInRange<uint32_t>(0, ~(uint32_t)(total_bytes >> 6));
ChaCha20 crypt1(key_bytes);
ChaCha20 crypt2(key_bytes);
crypt1.Seek({iv_prefix, iv}, seek);
crypt2.Seek({iv_prefix, iv}, seek);
std::vector<std::byte> data1, data2;
data1.resize(total_bytes);
data2.resize(total_bytes);
if constexpr (UseCrypt) {
InsecureRandomContext(provider.ConsumeIntegral<uint64_t>()).fillrand(data1);
std::copy(data1.begin(), data1.end(), data2.begin());
}
assert(data1 == data2);
if constexpr (UseCrypt) {
crypt1.Crypt(data1, data1);
} else {
crypt1.Keystream(data1);
}
uint64_t bytes2 = 0;
int iter = 0;
while (true) {
bool is_last = (iter == 255) || (bytes2 == total_bytes) || provider.ConsumeBool();
++iter;
uint64_t now = is_last ? total_bytes - bytes2 :
provider.ConsumeIntegralInRange<uint64_t>(0, total_bytes - bytes2);
if (UseCrypt || provider.ConsumeBool()) {
crypt2.Crypt(std::span{data2}.subspan(bytes2, now), std::span{data2}.subspan(bytes2, now));
} else {
crypt2.Keystream(std::span{data2}.subspan(bytes2, now));
}
bytes2 += now;
if (is_last) break;
}
assert(bytes2 == total_bytes);
assert(data1 == data2);
}
}
FUZZ_TARGET(chacha20_split_crypt)
{
FuzzedDataProvider provider{buffer.data(), buffer.size()};
ChaCha20SplitFuzz<true>(provider);
}
FUZZ_TARGET(chacha20_split_keystream)
{
FuzzedDataProvider provider{buffer.data(), buffer.size()};
ChaCha20SplitFuzz<false>(provider);
}
FUZZ_TARGET(crypto_fschacha20)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
auto key = fuzzed_data_provider.ConsumeBytes<std::byte>(FSChaCha20::KEYLEN);
key.resize(FSChaCha20::KEYLEN);
auto fsc20 = FSChaCha20{key, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 1024)};
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
{
auto input = fuzzed_data_provider.ConsumeBytes<std::byte>(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096));
std::vector<std::byte> output;
output.resize(input.size());
fsc20.Crypt(input, output);
}
}