#include <bip324.h>
#include <chainparams.h>
#include <random.h>
#include <span.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <algorithm>
#include <cstdint>
#include <vector>
namespace {
void Initialize()
{
static ECC_Context ecc_context{};
SelectParams(ChainType::MAIN);
}
}
FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
{
FuzzedDataProvider provider(buffer.data(), buffer.size());
CKey init_key = ConsumePrivateKey(provider, true);
if (!init_key.IsValid()) return;
auto init_ent = provider.ConsumeBytes<std::byte>(32);
init_ent.resize(32);
CKey resp_key = ConsumePrivateKey(provider, true);
if (!resp_key.IsValid()) return;
auto resp_ent = provider.ConsumeBytes<std::byte>(32);
resp_ent.resize(32);
BIP324Cipher initiator(init_key, init_ent);
assert(!initiator);
BIP324Cipher responder(resp_key, resp_ent);
assert(!responder);
initiator.Initialize(responder.GetOurPubKey(), true);
assert(initiator);
responder.Initialize(initiator.GetOurPubKey(), false);
assert(responder);
InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
assert(std::ranges::equal(initiator.GetSessionID(), responder.GetSessionID()));
assert(std::ranges::equal(initiator.GetSendGarbageTerminator(), responder.GetReceiveGarbageTerminator()));
assert(std::ranges::equal(initiator.GetReceiveGarbageTerminator(), responder.GetSendGarbageTerminator()));
LIMITED_WHILE(provider.remaining_bytes(), 1000) {
unsigned mode = provider.ConsumeIntegral<uint8_t>();
bool ignore = mode & 1;
bool from_init = mode & 2;
bool damage = mode & 4;
unsigned aad_length_bits = 4 * ((mode >> 3) & 3);
unsigned aad_length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << aad_length_bits) - 1);
unsigned length_bits = 2 * ((mode >> 5) & 7);
unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
auto aad = rng.randbytes<std::byte>(aad_length);
auto contents = rng.randbytes<std::byte>(length);
auto& sender{from_init ? initiator : responder};
auto& receiver{from_init ? responder : initiator};
std::vector<std::byte> ciphertext(length + initiator.EXPANSION);
sender.Encrypt(contents, aad, ignore, ciphertext);
if (damage) {
unsigned damage_bit = provider.ConsumeIntegralInRange<unsigned>(0,
(ciphertext.size() + aad.size()) * 8U - 1U);
unsigned damage_pos = damage_bit >> 3;
std::byte damage_val{(uint8_t)(1U << (damage_bit & 7))};
if (damage_pos >= ciphertext.size()) {
aad[damage_pos - ciphertext.size()] ^= damage_val;
} else {
ciphertext[damage_pos] ^= damage_val;
}
}
uint32_t dec_length = receiver.DecryptLength(std::span{ciphertext}.first(initiator.LENGTH_LEN));
if (!damage) {
assert(dec_length == length);
} else {
if (dec_length > 16384 + length) break;
ciphertext.resize(dec_length + initiator.EXPANSION);
}
std::vector<std::byte> decrypt(dec_length);
bool dec_ignore{false};
bool ok = receiver.Decrypt(std::span{ciphertext}.subspan(initiator.LENGTH_LEN), aad, dec_ignore, decrypt);
assert(!ok == damage);
if (!ok) break;
assert(ignore == dec_ignore);
assert(decrypt == contents);
}
}