diff --git a/CMakeLists.txt b/CMakeLists.txt index 257d302a4..4042a9e72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ # SPDX-License-Identifier: MIT cmake_minimum_required (VERSION 3.15) +include(CMakeDependentOption) + # option() honors normal variables. # see: https://cmake.org/cmake/help/git-stage/policy/CMP0077.html if(POLICY CMP0077) @@ -27,6 +29,11 @@ option(OQS_LIBJADE_BUILD "Enable formally verified implementation of supported a option(OQS_PERMIT_UNSUPPORTED_ARCHITECTURE "Permit compilation on an an unsupported architecture." OFF) option(OQS_STRICT_WARNINGS "Enable all compiler warnings." OFF) option(OQS_EMBEDDED_BUILD "Compile liboqs for an Embedded environment without a full standard library." OFF) +# Libfuzzer isn't supported on gcc +if('${CMAKE_CXX_COMPILER_ID}' STREQUAL 'Clang') + option(OQS_BUILD_FUZZ_TESTS "Build fuzz test suite" OFF) +endif() + set(OQS_OPT_TARGET auto CACHE STRING "The target microarchitecture for optimization.") @@ -39,6 +46,16 @@ set(OQS_COMPILE_BUILD_TARGET "${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_HOST_SYSTEM}") set(OQS_MINIMAL_GCC_VERSION "7.1.0") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# Determine the flags for fuzzing. Use OSS-Fuzz's configuration if available, otherwise fall back to defaults. +if(DEFINED ENV{LIB_FUZZING_ENGINE}) + set(FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE}) + set(FUZZING_COMPILE_FLAGS "") + set(FUZZING_LINK_FLAGS "${FUZZING_ENGINE}") +else() + set(FUZZING_COMPILE_FLAGS "-fsanitize=fuzzer,address") + set(FUZZING_LINK_FLAGS "-fsanitize=fuzzer,address") +endif() + # heuristic check to see whether we're running on a RaspberryPi if(EXISTS "/opt/vc/include/bcm_host.h") add_definitions( -DOQS_USE_RASPBERRY_PI ) diff --git a/shell.nix b/shell.nix new file mode 100644 index 000000000..7410186c4 --- /dev/null +++ b/shell.nix @@ -0,0 +1,21 @@ +let + pkgs = import {}; +in + with pkgs; + stdenv.mkDerivation { + name = "clang-env-with-nightly-rust"; + buildInputs = [ + clang + llvmPackages.libclang + openssl + cmake + ninja + ]; + shellHook = '' + export LIBCLANG_PATH="${pkgs.llvmPackages.libclang}/lib"; + export CC=clang; + export CXX=clang++; + export CFLAGS=-fsanitize=fuzzer-no-link,address + export LDFLAGS=-fsanitize=address + ''; + } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index eb297a804..19fbec728 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -88,6 +88,15 @@ set(KEM_TESTS example_kem kat_kem test_kem test_kem_mem speed_kem vectors_kem) add_executable(example_sig example_sig.c) target_link_libraries(example_sig PRIVATE ${TEST_DEPS}) +if(OQS_BUILD_FUZZ_TESTS) + add_executable(fuzz_test_sig fuzz_test_sig.c) + target_link_libraries(fuzz_test_sig PRIVATE ${TEST_DEPS}) + set_target_properties(fuzz_test_sig PROPERTIES + COMPILE_FLAGS "${FUZZING_COMPILE_FLAGS}" + LINK_FLAGS "${FUZZING_LINK_FLAGS}" + ) +endif() + # Stateful SIG API tests add_executable(example_sig_stfl example_sig_stfl.c) target_link_libraries(example_sig_stfl PRIVATE ${TEST_DEPS}) diff --git a/tests/fuzz_test_sig.c b/tests/fuzz_test_sig.c new file mode 100644 index 000000000..be570cb9e --- /dev/null +++ b/tests/fuzz_test_sig.c @@ -0,0 +1,97 @@ +/* + * fuzz_test_sig.c + * + * Minimal fuzz test for liboqs. + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include + +#include + +void cleanup_heap(uint8_t *public_key, uint8_t *secret_key, + uint8_t *signature, + OQS_SIG *sig); + +static OQS_STATUS fuzz_dilithium_2(const uint8_t *message, size_t message_len) { + +#ifdef OQS_ENABLE_SIG_dilithium_2 + + OQS_SIG *sig = NULL; + uint8_t *public_key = NULL; + uint8_t *secret_key = NULL; + uint8_t *signature = NULL; + size_t signature_len; + OQS_STATUS rc; + + sig = OQS_SIG_new(OQS_SIG_alg_dilithium_2); + if (sig == NULL) { + printf("[example_heap] OQS_SIG_alg_dilithium_2 was not enabled at compile-time.\n"); + return OQS_ERROR; + } + + public_key = malloc(sig->length_public_key); + secret_key = malloc(sig->length_secret_key); + signature = malloc(sig->length_signature); + if ((public_key == NULL) || (secret_key == NULL) || (message == NULL) || (signature == NULL)) { + fprintf(stderr, "ERROR: malloc failed!\n"); + cleanup_heap(public_key, secret_key, signature, sig); + return OQS_ERROR; + } + + rc = OQS_SIG_keypair(sig, public_key, secret_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_SIG_keypair failed!\n"); + cleanup_heap(public_key, secret_key, signature, sig); + return OQS_ERROR; + } + rc = OQS_SIG_sign(sig, signature, &signature_len, message, message_len, secret_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_SIG_sign failed!\n"); + cleanup_heap(public_key, secret_key, signature, sig); + return OQS_ERROR; + } + rc = OQS_SIG_verify(sig, message, message_len, signature, signature_len, public_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_SIG_verify failed!\n"); + cleanup_heap(public_key, secret_key, signature, sig); + exit(1); + } + + cleanup_heap(public_key, secret_key, signature, sig); + return OQS_SUCCESS; // success +#else + + printf("[example_heap] OQS_SIG_dilithium_2 was not enabled at compile-time.\n"); + return OQS_SUCCESS; + +#endif +} + +void cleanup_heap(uint8_t *public_key, uint8_t *secret_key, + uint8_t *signature, + OQS_SIG *sig) { + if (sig != NULL) { + OQS_MEM_secure_free(secret_key, sig->length_secret_key); + } + OQS_MEM_insecure_free(public_key); + OQS_MEM_insecure_free(signature); + OQS_SIG_free(sig); +} + +int LLVMFuzzerTestOneInput(const char* data, size_t size) { + OQS_init(); + if (OQS_ERROR == fuzz_dilithium_2((const uint8_t*)data, size)) { + // If we get an error prune testcase from corpus. + return -1; + } + OQS_destroy(); + return 0; +} + +