Files
tinyaes/fuzz/fuzz_ecb.cpp
Brandon Lehmann b4df5d078a Fix ARM CE byte ordering, expand C/C++ API, and harden build
ARM backends: fix round key byte-swap on little-endian (vrev32q_u8),
rewrite decrypt to pre-process middle keys with InvMixColumns, fix
GHASH PMULL reflect and reduction ordering.

API: add nonce/IV-generating convenience overloads for CTR, CBC, and
GCM (library generates and prepends nonce, appends tag). Add C API
for IV/nonce generation. Rename error codes (TINYAES_OK, Result::Ok,
Result::AuthenticationFailed, etc.).

Build: add MinGW GCC AVX-512 debug alignment fix, harden bench/fuzz
CMake targets (warnings-as-errors, linker hardening), align with
tinysha CMake conventions. Add README.

Tests: expand coverage for nonce-generating API overloads, add NIST
GCM test vectors, improve fuzz target differential testing.
2026-02-24 21:59:23 -05:00

74 lines
2.3 KiB
C++

// Copyright (c) 2025-2026, Brandon Lehmann
// BSD 3-Clause License (see LICENSE)
#include "tinyaes/ecb.h"
#include "internal/aes_impl.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
// Differential: compare dispatched encrypt_block against portable
static void diff_encrypt_block(const uint8_t *key, size_t key_len, const uint8_t *plaintext, size_t pt_len)
{
int rounds = tinyaes::internal::aes_rounds(key_len);
if (rounds == 0)
return;
uint32_t rk[tinyaes::internal::AES_MAX_RK_WORDS];
// Portable key expansion + encrypt
tinyaes::internal::aes_key_expand_portable(key, key_len, rk);
for (size_t off = 0; off + 16 <= pt_len; off += 16)
{
uint8_t out_portable[16], out_dispatch[16];
tinyaes::internal::aes_encrypt_block_portable(rk, rounds, plaintext + off, out_portable);
tinyaes::internal::get_encrypt_block()(rk, rounds, plaintext + off, out_dispatch);
assert(std::memcmp(out_portable, out_dispatch, 16) == 0);
// Also verify decrypt roundtrip
uint8_t dec_portable[16], dec_dispatch[16];
tinyaes::internal::aes_decrypt_block_portable(rk, rounds, out_portable, dec_portable);
tinyaes::internal::get_decrypt_block()(rk, rounds, out_dispatch, dec_dispatch);
assert(std::memcmp(dec_portable, dec_dispatch, 16) == 0);
assert(std::memcmp(dec_portable, plaintext + off, 16) == 0);
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (size < 2)
return 0;
// First byte selects key size: 16, 24, or 32
static const size_t key_sizes[] = {16, 24, 32};
size_t key_len = key_sizes[data[0] % 3];
data++;
size--;
if (size < key_len + 16)
return 0;
std::vector<uint8_t> key(data, data + key_len);
size_t pt_len = ((size - key_len) / 16) * 16;
if (pt_len == 0)
return 0;
std::vector<uint8_t> plaintext(data + key_len, data + key_len + pt_len);
std::vector<uint8_t> ct, pt;
auto result = tinyaes::ecb_encrypt(key, plaintext, ct);
if (result != tinyaes::Result::Ok)
return 0;
result = tinyaes::ecb_decrypt(key, ct, pt);
assert(result == tinyaes::Result::Ok);
assert(pt == plaintext);
// Differential test: portable vs dispatched
diff_encrypt_block(data, key_len, data + key_len, pt_len);
return 0;
}