← All libraries
Crypto · C
How to fuzz libsodium
libsodium trades footprint for correctness — fuzzing validates those guarantees hold.
libsodium is designed to be misuse-resistant, but its internal MAC verification, key derivation functions, and base64/hex codecs still process attacker-supplied byte strings. Fuzzing verifies that the API's length-check preconditions are uniformly enforced and that no code path exists to bypass constant-time comparisons.
Common bug classes
- •Heap buffer overflow in base64 decode length miscalculation
- •Out-of-bounds read in hex-to-binary conversion on odd-length input
- •Integer overflow in secret-stream chunk length arithmetic
- •Incorrect MAC length check allowing authentication bypass
- •Uninitialised-memory read in generichash state finalisation
Recommended setup
Fuzzers
- → AFL++
- → libFuzzer
Sanitizers
- → ASan
- → UBSan
- → MSan
Harness scaffold
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <sodium.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (sodium_init() < 0) return 0;
/* Fuzz secretstream open */
if (size < crypto_secretstream_xchacha20poly1305_HEADERBYTES) return 0;
crypto_secretstream_xchacha20poly1305_state st;
unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES];
memset(key, 0x42, sizeof(key));
crypto_secretstream_xchacha20poly1305_init_pull(&st, data, key);
if (size > crypto_secretstream_xchacha20poly1305_HEADERBYTES) {
const uint8_t *msg = data + crypto_secretstream_xchacha20poly1305_HEADERBYTES;
size_t mlen = size - crypto_secretstream_xchacha20poly1305_HEADERBYTES;
uint8_t *out = malloc(mlen);
if (out) {
unsigned long long outlen; unsigned char tag;
crypto_secretstream_xchacha20poly1305_pull(
&st, out, &outlen, &tag, msg, mlen, NULL, 0);
free(out);
}
}
return 0;
}Save this as fuzz_target.cc, build with your compiler + sanitizer flags, and you have a working starting point.
Push the harness above + a Dockerfile. First month 50% off.