diff options
author | Jack Lloyd <[email protected]> | 2016-10-30 23:39:21 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-10-30 23:39:21 -0400 |
commit | 6582fe8638ab6c37551691a6a0196b22977a6e2e (patch) | |
tree | 152f0e76e76349f916b17c900bb7b39b5fcecb8f | |
parent | 76a0cff3b0200660ef678bbdaf3762e45c27bccd (diff) |
Import fuzzer drivers
Originally from https://github.com/randombit/botan-fuzzers but
merging to the main tree (without the corpus files, since I suspect
the corpus files in that repo are not useful anymore)
Adds --unsafe-fuzzer-mode which can be used to selectively disable
cryptographic checks which get in the way of fuzzer testing. This
setting is reflected in build.h and in the version string. Right
now it doesn't actually disable anything.
30 files changed, 1104 insertions, 4 deletions
diff --git a/configure.py b/configure.py index a860c9664..51ca56a8f 100755 --- a/configure.py +++ b/configure.py @@ -381,6 +381,9 @@ def process_command_line(args): mods_group.add_option('--minimized-build', action='store_true', dest='no_autoload', help='minimize build') + mods_group.add_option('--unsafe-fuzzer-mode', action='store_true', + help='disable checks for fuzz testing') + # Should be derived from info.txt but this runs too early third_party = ['boost', 'bzip2', 'lzma', 'openssl', 'sqlite3', 'zlib', 'tpm', 'pkcs11'] @@ -1362,6 +1365,8 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): 'include_files': makefile_list(build_config.public_headers), + 'unsafe_fuzzer_mode_define': '' if not options.unsafe_fuzzer_mode else '#define BOTAN_UNSAFE_FUZZER_MODE', + 'ar_command': cc.ar_command or osinfo.ar_command, 'ranlib_command': osinfo.ranlib_command(), 'install_cmd_exec': osinfo.install_cmd_exec, @@ -1417,13 +1422,13 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): else: vars['libname'] = 'botan' - vars["header_in"] = process_template('src/build-data/makefile/header.in', vars) + vars["header_in"] = process_template(os.path.join(options.makefile_dir, 'header.in'), vars) if vars["makefile_style"] == "gmake": - vars["gmake_commands_in"] = process_template('src/build-data/makefile/gmake_commands.in', vars) - vars["gmake_dso_in"] = process_template('src/build-data/makefile/gmake_dso.in', vars) \ + vars["gmake_commands_in"] = process_template(os.path.join(options.makefile_dir, 'gmake_commands.in'), vars) + vars["gmake_dso_in"] = process_template(os.path.join(options.makefile_dir, 'gmake_dso.in'), vars) \ if options.build_shared_lib else '' - vars["gmake_coverage_in"] = process_template('src/build-data/makefile/gmake_coverage.in', vars) \ + vars["gmake_coverage_in"] = process_template(os.path.join(options.makefile_dir, 'gmake_coverage.in'), vars) \ if options.with_coverage else '' return vars diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in index c5912e4ed..41685cfc4 100644 --- a/src/build-data/buildh.in +++ b/src/build-data/buildh.in @@ -21,6 +21,8 @@ #define BOTAN_VERSION_VC_REVISION "%{version_vc_rev}" #define BOTAN_DISTRIBUTION_INFO "%{distribution_info}" +%{unsafe_fuzzer_mode_define} + #define BOTAN_INSTALL_PREFIX R"(%{prefix})" #define BOTAN_INSTALL_HEADER_DIR "%{includedir}/botan-%{version_major}.%{version_minor}" diff --git a/src/extra_tests/fuzzers/.gitignore b/src/extra_tests/fuzzers/.gitignore new file mode 100644 index 000000000..f7ce206a2 --- /dev/null +++ b/src/extra_tests/fuzzers/.gitignore @@ -0,0 +1,10 @@ +*.log + +/libFuzzer +libFuzzer.a + +/output +/botan +/bin +callgrind.out.* +/corpus/ diff --git a/src/extra_tests/fuzzers/GNUmakefile b/src/extra_tests/fuzzers/GNUmakefile new file mode 100644 index 000000000..75b302bee --- /dev/null +++ b/src/extra_tests/fuzzers/GNUmakefile @@ -0,0 +1,69 @@ + +FUZZERS=$(patsubst jigs/%.cpp,%,$(wildcard jigs/*.cpp)) + +BOTAN_DIR=botan + +AFL_SAN_FLAGS=-fsanitize=address,undefined -fno-sanitize-recover=undefined +CLANG_SAN_FLAGS=-fsanitize=address,undefined -fno-sanitize-recover=undefined -fsanitize-coverage=edge,indirect-calls,8bit-counters + +SHARED_FLAGS=-O3 -g -std=c++11 -pthread +LIBFUZZER_FLAGS=-DUSE_LLVM_FUZZER -I$(BOTAN_DIR)/llvm/build/include $(SHARED_FLAGS) $(CLANG_SAN_FLAGS) +AFL_FLAGS=-I$(BOTAN_DIR)/afl/build/include $(SHARED_FLAGS) + +LIBFUZZER_LIBS=$(BOTAN_DIR)/llvm/libbotan-1.11.a libFuzzer.a +AFL_LIBS=$(BOTAN_DIR)/afl/libbotan-1.11.a + +#AFL_CXX=AFL_USE_ASAN=1 afl-g++ -m32 +AFL_CXX=afl-clang-fast++ +CLANG_CXX=clang++ + +LIBFUZZER_PROGS=$(patsubst %,bin/llvm_fuzz_%,$(FUZZERS)) +AFL_PROGS=$(patsubst %,bin/afl_fuzz_%,$(FUZZERS)) + +all: afl_progs libfuzzer_progs + +afl_progs: $(AFL_PROGS) + +libfuzzer_progs: $(LIBFUZZER_PROGS) + +bin/llvm_fuzz_%: jigs/%.cpp $(LIBFUZZER_LIBS) + $(CLANG_CXX) $(LIBFUZZER_FLAGS) -DUSE_LLVM_FUZZER $< $(LIBFUZZER_LIBS) -o $@ + +bin/afl_fuzz_%: jigs/%.cpp $(AFL_LIBS) + $(AFL_CXX) $(AFL_FLAGS) $< $(AFL_LIBS) -o $@ + +# libFuzzer default is max_len 64 this sets 140 but allows override via args= + +run_llvm_%: bin/llvm_fuzz_% + $(eval FUZZER = $(subst bin/llvm_fuzz_,,$<)) + mkdir -p output/$(FUZZER)/llvm/queue + mkdir -p output/$(FUZZER)/llvm/outputs + $< -max_len=140 -artifact_prefix=output/$(FUZZER)/llvm/outputs/ output/$(FUZZER)/llvm/queue corpus/$(FUZZER) $(args) + +run_afl_%: bin/afl_fuzz_% + $(eval FUZZER = $(subst bin/afl_fuzz_,,$<)) + mkdir -p output/$(FUZZER)/afl + afl-fuzz $(args) -o output/$(FUZZER)/afl -i corpus/$(FUZZER) $< + +cmin_%: bin/afl_fuzz_% + $(eval FUZZER = $(subst bin/afl_fuzz_,,$<)) + rm -rf cmin-dir + mv corpus/$(FUZZER) cmin-dir + -cp -n output/$(FUZZER)/afl/queue/* cmin-dir + -cp -n output/$(FUZZER)/llvm/queue/* cmin-dir + afl-cmin -i cmin-dir -o corpus/$(FUZZER) $< + rm -rf cmin-dir + +clean: + rm -f $(LIBFUZZER_PROGS) $(AFL_PROGS) + +libFuzzer.a: libFuzzer + cd libFuzzer && clang -c -g -O2 -std=c++11 *.cpp + ar cr libFuzzer.a libFuzzer/*.o + +setup: + svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer libFuzzer + +update: + cd botan && git pull + svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer libFuzzer diff --git a/src/extra_tests/fuzzers/jigs/bn_sqr.cpp b/src/extra_tests/fuzzers/jigs/bn_sqr.cpp new file mode 100644 index 000000000..2bc5ebe33 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/bn_sqr.cpp @@ -0,0 +1,21 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" + +#include <botan/bigint.h> +#include <botan/numthry.h> + +void fuzz(const uint8_t in[], size_t len) + { + Botan::BigInt x = Botan::BigInt::decode(in, len); + + Botan::BigInt x_sqr = square(x); + Botan::BigInt x_mul = x * x; + + FUZZER_ASSERT_EQUAL(x_sqr, x_mul); + } + diff --git a/src/extra_tests/fuzzers/jigs/cert.cpp b/src/extra_tests/fuzzers/jigs/cert.cpp new file mode 100644 index 000000000..2c13551e2 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/cert.cpp @@ -0,0 +1,18 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" + +#include <botan/x509cert.h> + +void fuzz(const uint8_t in[], size_t len) + { + try + { + DataSource_Memory input(in, len); + X509_Certificate cert(input); + } + catch(Botan::Exception& e) { } + } diff --git a/src/extra_tests/fuzzers/jigs/crl.cpp b/src/extra_tests/fuzzers/jigs/crl.cpp new file mode 100644 index 000000000..be61ae131 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/crl.cpp @@ -0,0 +1,18 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" + +#include <botan/x509_crl.h> + +void fuzz(const uint8_t in[], size_t len) + { + try + { + DataSource_Memory input(in, len); + X509_CRL crl(input); + } + catch(Botan::Exception& e) { } + } diff --git a/src/extra_tests/fuzzers/jigs/divide.cpp b/src/extra_tests/fuzzers/jigs/divide.cpp new file mode 100644 index 000000000..fba68b9a6 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/divide.cpp @@ -0,0 +1,26 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include <botan/divide.h> + +void fuzz(const uint8_t in[], size_t len) + { + const BigInt x = BigInt::decode(in, len / 2); + const BigInt y = BigInt::decode(in + len / 2, len / 2); + + if(y == 0) + return; + + BigInt q, r; + Botan::divide(x, y, q, r); + + FUZZER_ASSERT_TRUE(r < y); + + BigInt z = q*y + r; + + FUZZER_ASSERT_EQUAL(z, x); + } + diff --git a/src/extra_tests/fuzzers/jigs/driver.h b/src/extra_tests/fuzzers/jigs/driver.h new file mode 100644 index 000000000..530cc80b7 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/driver.h @@ -0,0 +1,87 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef FUZZER_DRIVER_H_ +#define FUZZER_DRIVER_H_ + +#include <stdint.h> +#include <iostream> +#include <vector> +#include <stdlib.h> // for setenv +#include <botan/exceptn.h> + +using namespace Botan; + +void fuzz(const uint8_t in[], size_t len); + +void fuzzer_init() + { + /* + * This disables the mlock pool, as overwrites within the pool are + * opaque to ASan or other instrumentation. + */ + ::setenv("BOTAN_MLOCK_POOL_SIZE", "0", 1); + } + +#if defined(USE_LLVM_FUZZER) + +// Called by main() in libFuzzer +extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len) + { + fuzz(in, len); + return 0; + } + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + fuzzer_init(); + return 0; +} + +#else + +// Read stdin for AFL + +int main(int argc, char* argv[]) + { + const size_t max_read = 4096; + + fuzzer_init(); + +#if defined(__AFL_LOOP) + while(__AFL_LOOP(1000)) +#endif + { + std::vector<uint8_t> buf(max_read); + std::cin.read((char*)buf.data(), buf.size()); + size_t got = std::cin.gcount(); + + buf.resize(got); + buf.shrink_to_fit(); + + fuzz(buf.data(), got); + } + } + +#endif + +#endif + +// Some helpers for the fuzzer jigs + +#define FUZZER_ASSERT_EQUAL(x, y) do { \ + if(x != y) { \ + std::cerr << #x << " = " << x << " !=\n" << #y << " = " << y \ + << " at " << __LINE__ << ":" << __FILE__ << std::endl; \ + abort(); \ +} } while(0) + +#define FUZZER_ASSERT_TRUE(e) \ + do { \ + if(!(e)) { \ + std::cerr << "Expression " << #e << " was false at " \ + << __LINE__ << ":" << __FILE__ << std::endl; \ + abort(); \ + } } while(0) diff --git a/src/extra_tests/fuzzers/jigs/ecc_bp256.cpp b/src/extra_tests/fuzzers/jigs/ecc_bp256.cpp new file mode 100644 index 000000000..5b979b03f --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/ecc_bp256.cpp @@ -0,0 +1,13 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include "ecc_helper.h" + +void fuzz(const uint8_t in[], size_t len) + { + static EC_Group bp256("brainpool256r1"); + return check_ecc_math(bp256, in, len); + } diff --git a/src/extra_tests/fuzzers/jigs/ecc_helper.h b/src/extra_tests/fuzzers/jigs/ecc_helper.h new file mode 100644 index 000000000..9848e6a9e --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/ecc_helper.h @@ -0,0 +1,74 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#ifndef ECC_HELPERS_H__ +#define ECC_HELPERS_H__ + +#include "driver.h" +#include <botan/curve_gfp.h> +#include <botan/ec_group.h> +#include <botan/reducer.h> +#include <botan/system_rng.h> + +void check_redc(std::function<void (BigInt&, secure_vector<word>&)> redc_fn, + const Modular_Reducer& redc, + const BigInt& prime, + const BigInt& x) + { + const Botan::BigInt v1 = x % prime; + const Botan::BigInt v2 = redc.reduce(x); + + Botan::secure_vector<Botan::word> ws; + Botan::BigInt v3 = x; + redc_fn(v3, ws); + + FUZZER_ASSERT_EQUAL(v1, v2); + FUZZER_ASSERT_EQUAL(v2, v3); + } + +inline std::ostream& operator<<(std::ostream& o, const PointGFp& point) + { + o << point.get_affine_x() << "," << point.get_affine_y(); + return o; + } + +void check_ecc_math(const EC_Group& group, const uint8_t in[], size_t len) + { + const size_t hlen = len / 2; + const BigInt a = BigInt::decode(in, hlen); + const BigInt b = BigInt::decode(in + hlen, len - hlen); + + const Botan::PointGFp& base_point = group.get_base_point(); + const Botan::BigInt& group_order = group.get_order(); + + const Botan::BigInt c = a + b; + + const Botan::PointGFp P = base_point * a; + const Botan::PointGFp Q = base_point * b; + const Botan::PointGFp R = base_point * c; + + const Botan::PointGFp A1 = P + Q; + const Botan::PointGFp A2 = Q + P; + + if(a == 31337 && b == 31337) + abort(); + + FUZZER_ASSERT_EQUAL(A1, R); + FUZZER_ASSERT_EQUAL(A2, R); + + Botan::Blinded_Point_Multiply blind(base_point, group_order, 4); + + const Botan::PointGFp P1 = blind.blinded_multiply(a, system_rng()); + const Botan::PointGFp Q1 = blind.blinded_multiply(b, system_rng()); + const Botan::PointGFp R1 = blind.blinded_multiply(c, system_rng()); + + const Botan::PointGFp S1 = P1 + Q1; + const Botan::PointGFp S2 = Q1 + P1; + + FUZZER_ASSERT_EQUAL(S1, R1); + FUZZER_ASSERT_EQUAL(S2, R1); + } + +#endif diff --git a/src/extra_tests/fuzzers/jigs/ecc_p256.cpp b/src/extra_tests/fuzzers/jigs/ecc_p256.cpp new file mode 100644 index 000000000..429925ba1 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/ecc_p256.cpp @@ -0,0 +1,13 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include "ecc_helper.h" + +void fuzz(const uint8_t in[], size_t len) + { + static EC_Group p256("secp256r1"); + return check_ecc_math(p256, in, len); + } diff --git a/src/extra_tests/fuzzers/jigs/ecc_p384.cpp b/src/extra_tests/fuzzers/jigs/ecc_p384.cpp new file mode 100644 index 000000000..0441b6dcf --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/ecc_p384.cpp @@ -0,0 +1,13 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include "ecc_helper.h" + +void fuzz(const uint8_t in[], size_t len) + { + static EC_Group p384("secp384r1"); + return check_ecc_math(p384, in, len); + } diff --git a/src/extra_tests/fuzzers/jigs/ecc_p521.cpp b/src/extra_tests/fuzzers/jigs/ecc_p521.cpp new file mode 100644 index 000000000..cc998137c --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/ecc_p521.cpp @@ -0,0 +1,13 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include "ecc_helper.h" + +void fuzz(const uint8_t in[], size_t len) + { + static EC_Group p521("secp521r1"); + return check_ecc_math(p521, in, len); + } diff --git a/src/extra_tests/fuzzers/jigs/invert.cpp b/src/extra_tests/fuzzers/jigs/invert.cpp new file mode 100644 index 000000000..fcda5770e --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/invert.cpp @@ -0,0 +1,81 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include <botan/numthry.h> + +BigInt inverse_mod_ref(const BigInt& n, const BigInt& mod) + { + if(n == 0) + return 0; + + BigInt u = mod, v = n; + BigInt B = 0, D = 1; + + while(u.is_nonzero()) + { + const size_t u_zero_bits = low_zero_bits(u); + u >>= u_zero_bits; + for(size_t i = 0; i != u_zero_bits; ++i) + { + //B.cond_sub(B.is_odd(), mod); + if(B.is_odd()) + { B -= mod; } + B >>= 1; + } + + const size_t v_zero_bits = low_zero_bits(v); + v >>= v_zero_bits; + for(size_t i = 0; i != v_zero_bits; ++i) + { + if(D.is_odd()) + { D -= mod; } + D >>= 1; + } + + if(u >= v) { u -= v; B -= D; } + else { v -= u; D -= B; } + } + + if(v != 1) + return 0; // no modular inverse + + while(D.is_negative()) D += mod; + while(D >= mod) D -= mod; + + return D; + } + + +void fuzz(const uint8_t in[], size_t len) + { + const BigInt x = BigInt::decode(in, len / 2); + BigInt mod = BigInt::decode(in + len / 2, len / 2); + + mod.set_bit(0); + + if(mod < 3 || x >= mod) + return; + + BigInt ref = inverse_mod_ref(x, mod); + BigInt ct = ct_inverse_mod_odd_modulus(x, mod); + //BigInt mon = normalized_montgomery_inverse(x, mod); + + if(ref != ct) + { + std::cout << "X = " << x << "\n"; + std::cout << "P = " << mod << "\n"; + std::cout << "GCD = " << gcd(x, mod) << "\n"; + std::cout << "Ref = " << ref << "\n"; + std::cout << "CT = " << ct << "\n"; + //std::cout << "Mon = " << mon << "\n"; + + std::cout << "RefCheck = " << (ref*ref)%mod << "\n"; + std::cout << "CTCheck = " << (ct*ct)%mod << "\n"; + //std::cout << "MonCheck = " << (mon*mon)%mod << "\n"; + abort(); + } + } + diff --git a/src/extra_tests/fuzzers/jigs/os2ecp.cpp b/src/extra_tests/fuzzers/jigs/os2ecp.cpp new file mode 100644 index 000000000..2b939eb5b --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/os2ecp.cpp @@ -0,0 +1,37 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include <botan/ec_group.h> +#include <botan/point_gfp.h> + +void check_os2ecp(const EC_Group& group, const uint8_t in[], size_t len) + { + try + { + PointGFp point = OS2ECP(in, len, group.get_curve()); + } + catch(Botan::Exception& e) {} + } + +void fuzz(const uint8_t in[], size_t len) + { + static EC_Group p192("secp192r1"); + static EC_Group p224("secp224r1"); + static EC_Group p256("secp256r1"); + static EC_Group p384("secp384r1"); + static EC_Group p521("secp521r1"); + static EC_Group bp256("brainpool256r1"); + static EC_Group bp512("brainpool512r1"); + + check_os2ecp(p192, in, len); + check_os2ecp(p224, in, len); + check_os2ecp(p256, in, len); + check_os2ecp(p384, in, len); + check_os2ecp(p521, in, len); + check_os2ecp(p521, in, len); + check_os2ecp(bp256, in, len); + check_os2ecp(bp512, in, len); + } diff --git a/src/extra_tests/fuzzers/jigs/pkcs1.cpp b/src/extra_tests/fuzzers/jigs/pkcs1.cpp new file mode 100644 index 000000000..889308f0e --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/pkcs1.cpp @@ -0,0 +1,68 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" + +#include <botan/eme_pkcs.h> +#include <botan/hex.h> + +secure_vector<byte> simple_pkcs1_unpad(const byte in[], size_t len) + { + if(len < 10) + throw Botan::Decoding_Error("bad len"); + + if(in[0] != 2) + throw Botan::Decoding_Error("bad field"); + + for(size_t i = 1; i < len; ++i) + { + if(in[i] == 0) + { + if(i < 9) + throw Botan::Decoding_Error("insufficient padding bytes"); + return secure_vector<byte>(in + i + 1, in + len); + } + } + + throw Botan::Decoding_Error("delim not found"); + } + +void fuzz(const uint8_t in[], size_t len) + { + static EME_PKCS1v15 pkcs1; + + secure_vector<byte> lib_result, ref_result; + bool lib_rejected = false, ref_rejected = false; + + try + { + byte valid_mask = 0; + secure_vector<byte> decoded = ((EME*)&pkcs1)->unpad(valid_mask, in, len); + + if(valid_mask == 0) + lib_rejected = false; + else if(valid_mask == 0xFF) + lib_rejected = true; + else + abort(); + } + catch(Botan::Decoding_Error&) { lib_rejected = true; } + + try + { + ref_result = simple_pkcs1_unpad(in, len); + } + catch(Botan::Decoding_Error&) { ref_rejected = true; } + + FUZZER_ASSERT_EQUAL(lib_rejected, ref_rejected); + + if(lib_result != ref_result) + { + std::cerr << hex_encode(lib_result) << " != ref \n" + << hex_encode(ref_result) << std::endl; + abort(); + } + + } diff --git a/src/extra_tests/fuzzers/jigs/pkcs8.cpp b/src/extra_tests/fuzzers/jigs/pkcs8.cpp new file mode 100644 index 000000000..69e2c193f --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/pkcs8.cpp @@ -0,0 +1,20 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" + +#include <botan/pkcs8.h> +#include <botan/system_rng.h> + +void fuzz(const uint8_t in[], size_t len) + { + try + { + System_RNG rng; + DataSource_Memory input(in, len); + std::unique_ptr<Private_Key> key(PKCS8::load_key(input, rng)); + } + catch(Botan::Exception& e) { } + } diff --git a/src/extra_tests/fuzzers/jigs/redc_p192.cpp b/src/extra_tests/fuzzers/jigs/redc_p192.cpp new file mode 100644 index 000000000..a27e6a37b --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/redc_p192.cpp @@ -0,0 +1,23 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include "ecc_helper.h" +#include <botan/curve_nistp.h> + +void fuzz(const uint8_t in[], size_t len) + { + static const BigInt& prime = Botan::prime_p192(); + static const BigInt prime_2 = prime * prime; + static Botan::Modular_Reducer prime_redc(prime); + + Botan::BigInt x = Botan::BigInt::decode(in, len); + + if(x < prime_2) + { + check_redc(Botan::redc_p192, prime_redc, prime, x); + } + } diff --git a/src/extra_tests/fuzzers/jigs/redc_p224.cpp b/src/extra_tests/fuzzers/jigs/redc_p224.cpp new file mode 100644 index 000000000..637d9e6fd --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/redc_p224.cpp @@ -0,0 +1,23 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include "ecc_helper.h" +#include <botan/curve_nistp.h> + +void fuzz(const uint8_t in[], size_t len) + { + static const BigInt& prime = Botan::prime_p224(); + static const BigInt prime_2 = prime * prime; + static Botan::Modular_Reducer prime_redc(prime); + + Botan::BigInt x = Botan::BigInt::decode(in, len); + + if(x < prime_2) + { + check_redc(Botan::redc_p224, prime_redc, prime, x); + } + } diff --git a/src/extra_tests/fuzzers/jigs/redc_p256.cpp b/src/extra_tests/fuzzers/jigs/redc_p256.cpp new file mode 100644 index 000000000..86f0910a0 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/redc_p256.cpp @@ -0,0 +1,23 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include "ecc_helper.h" +#include <botan/curve_nistp.h> + +void fuzz(const uint8_t in[], size_t len) + { + static const BigInt& prime = Botan::prime_p256(); + static const BigInt prime_2 = prime * prime; + static Botan::Modular_Reducer prime_redc(prime); + + Botan::BigInt x = Botan::BigInt::decode(in, len); + + if(x < prime_2) + { + check_redc(Botan::redc_p256, prime_redc, prime, x); + } + } diff --git a/src/extra_tests/fuzzers/jigs/redc_p384.cpp b/src/extra_tests/fuzzers/jigs/redc_p384.cpp new file mode 100644 index 000000000..9a9ac896a --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/redc_p384.cpp @@ -0,0 +1,23 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include "ecc_helper.h" +#include <botan/curve_nistp.h> + +void fuzz(const uint8_t in[], size_t len) + { + static const BigInt& prime = Botan::prime_p384(); + static const BigInt prime_2 = prime * prime; + static Botan::Modular_Reducer prime_redc(prime); + + Botan::BigInt x = Botan::BigInt::decode(in, len); + + if(x < prime_2) + { + check_redc(Botan::redc_p384, prime_redc, prime, x); + } + } diff --git a/src/extra_tests/fuzzers/jigs/redc_p521.cpp b/src/extra_tests/fuzzers/jigs/redc_p521.cpp new file mode 100644 index 000000000..fe164217d --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/redc_p521.cpp @@ -0,0 +1,23 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include "ecc_helper.h" +#include <botan/curve_nistp.h> + +void fuzz(const uint8_t in[], size_t len) + { + static const BigInt& prime = Botan::prime_p521(); + static const BigInt prime_2 = prime * prime; + static Botan::Modular_Reducer prime_redc(prime); + + Botan::BigInt x = Botan::BigInt::decode(in, len); + + if(x < prime_2) + { + check_redc(Botan::redc_p521, prime_redc, prime, x); + } + } diff --git a/src/extra_tests/fuzzers/jigs/ressol.cpp b/src/extra_tests/fuzzers/jigs/ressol.cpp new file mode 100644 index 000000000..011194866 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/ressol.cpp @@ -0,0 +1,50 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include <botan/numthry.h> +#include <botan/system_rng.h> + +void fuzz(const uint8_t in[], size_t len) + { + if(len % 2 != 0) + return; + + const BigInt a = BigInt::decode(in, len / 2); + const BigInt n = BigInt::decode(in + len / 2, len / 2); + + try { + BigInt a_sqrt = ressol(a, n); + + if(a_sqrt > 0) + { + /* + * If n is not prime then the result of ressol will be bogus. But + * this function is exposed to untrusted inputs (via OS2ECP) so + * should not hang or crash even with composite modulus. + * If the result is incorrect, check if n is a prime: if it is + * then z != a is a bug. + */ + BigInt z = (a_sqrt * a_sqrt) % n; + BigInt a_redc = a % n; + if(z != a_redc) + { + if(is_prime(n, system_rng(), 64)) + { + std::cout << "A = " << a << "\n"; + std::cout << "Ressol = " << a_sqrt << "\n"; + std::cout << "N = " << n << "\n"; + std::cout << "Z = " << z << "\n"; + abort(); + } + } + } + } + catch(Botan::Exception& e) {} + + return; + } + diff --git a/src/extra_tests/fuzzers/jigs/tls_client.cpp b/src/extra_tests/fuzzers/jigs/tls_client.cpp new file mode 100644 index 000000000..e0fd039c9 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/tls_client.cpp @@ -0,0 +1,85 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include <botan/tls_client.h> +#include <botan/system_rng.h> + +class Fuzzer_TLS_Client_Creds : public Credentials_Manager + { + public: + void verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector<X509_Certificate>& cert_chain) override + { + try + { + Credentials_Manager::verify_certificate_chain(type, + purported_hostname, + cert_chain); + } + catch(std::exception& e) {} + } + + std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; } + std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; } + SymmetricKey psk(const std::string&, const std::string&, const std::string&) override + { + return SymmetricKey("AABBCCDDEEFF00112233445566778899"); + } + }; + +void fuzz(const uint8_t in[], size_t len) + { + if(len == 0) + return; + + auto dev_null = [](const byte[], size_t) {}; + + auto ignore_alerts = [](TLS::Alert, const byte[], size_t) {}; + auto ignore_hs = [](const TLS::Session&) { abort(); return true; }; + + Botan::System_RNG rng; + TLS::Session_Manager_Noop session_manager; + TLS::Policy policy; + TLS::Protocol_Version client_offer = TLS::Protocol_Version::TLS_V12; + TLS::Server_Information info("server.name", 443); + const std::vector<std::string> protocols_to_offer = { "fuzz/1.0", "http/1.1", "bunny/1.21.3" }; + Fuzzer_TLS_Client_Creds creds; + + TLS::Client client(dev_null, + dev_null, + ignore_alerts, + ignore_hs, + session_manager, + creds, + policy, + rng, + info, + client_offer, + protocols_to_offer); + + try + { + while(len > 0) + { + const size_t write_len = in[0]; + const size_t left = len - 1; + + const size_t consumed = std::min(left, write_len); + + client.received_data(in + 1, consumed); + + in += consumed + 1; + len -= consumed + 1; + } + } + catch(std::exception& e) + { + } + + } + diff --git a/src/extra_tests/fuzzers/jigs/tls_client_hello.cpp b/src/extra_tests/fuzzers/jigs/tls_client_hello.cpp new file mode 100644 index 000000000..5705dca91 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/tls_client_hello.cpp @@ -0,0 +1,21 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "driver.h" +#include <botan/internal/tls_messages.h> + +void fuzz(const uint8_t in[], size_t len) + { + try + { + std::vector<uint8_t> v(in, in + len); + Botan::TLS::Client_Hello ch(v); + + printf("%s\n", ch.version().to_string().c_str()); + if(ch.version() == Botan::TLS::Protocol_Version::TLS_V12) + abort(); + } + catch(Botan::Exception& e) {printf("%s\n", e.what()); } + } diff --git a/src/extra_tests/fuzzers/jigs/tls_server.cpp b/src/extra_tests/fuzzers/jigs/tls_server.cpp new file mode 100644 index 000000000..510f7f7b7 --- /dev/null +++ b/src/extra_tests/fuzzers/jigs/tls_server.cpp @@ -0,0 +1,179 @@ +/* +* (C) 2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "driver.h" +#include <botan/tls_server.h> +#include <botan/system_rng.h> + +const char* fixed_rsa_key = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCe6qqpMQVJ7zCJ\n" + "oSnpxia0yO6M7Ie3FGqPcd0DzueC+kWPvuHQ+PpP5vfO6qqRaDVII37PFX5NUZQm\n" + "GK/rAm7spjIHTCMgqSZ8pN13LU8m1gDwIdu9al16LXN9zZjB67uLlFn2trtLi234\n" + "i0cnyeF8IC0cz7tgCOzMSVEBcqJjkdgGrZ3WUgOXecVm2lXVrYlEiaSxFp4VOE9k\n" + "RFeVrELCjmNtc4hRd1yJsF+vObCtvyqGYQE1Qcb0MVSQDBHMkiUVmO6zuW7td5ef\n" + "O/1OyntQJGyVa+SnWbkSLCybta2J7MreHENrF5GA0K1KL140SNRHeWifRMuNQua7\n" + "qmKXMBTFAgMBAAECggEAIk3fxyQI0zvpy1vZ01ft1QqmzA7nAPNMSWi33/GS8iga\n" + "SfxXfKeySPs/tQ/dAARxs//NiOBH4mLgyxR7LQzaawU5OXALCSraXv+ruuUx990s\n" + "WKnGaG4EfbJAAwEVn47Gbkv425P4fEc91vAhzQn8PbIoatbAyOtESpjs/pYDTeC/\n" + "mnJId8gqO90cqyRECEMjk9sQ8iEjWPlik4ayGlUVbeeMu6/pJ9F8IZEgkLZiNDAB\n" + "4anmOFaT7EmqUjI4IlcaqfbbXyDXlvWUYukidEss+CNvPuqbQHBDnpFVvBxdDR2N\n" + "Uj2D5Xd5blcIe2/+1IVRnznjoQ5zvutzb7ThBmMehQKBgQDOITKG0ht2kXLxjVoR\n" + "r/pVpx+f3hs3H7wE0+vrLHoQgkVjpMWXQ47YuZTT9rCOOYNI2cMoH2D27t1j78/B\n" + "9kGYABUVpvQQ+6amqJDI1eYI6e68TPueEDjeALfSCdmPNiI3lZZrCIK9XLpkoy8K\n" + "tGYBRRJ+JJxjj1zPXj9SGshPgwKBgQDFXUtoxY3mCStH3+0b1qxGG9r1L5goHEmd\n" + "Am8WBYDheNpL0VqPNzouhuM/ZWMGyyAs/py6aLATe+qhR1uX5vn7LVZwjCSONZ4j\n" + "7ieEEUh1BHetPI1oI5PxgokRYfVuckotqVseanI/536Er3Yf2FXNQ1/ceVp9WykX\n" + "3mYTKMhQFwKBgQDKakcXpZNaZ5IcKdZcsBZ/rdGcR5sqEnursf9lvRNQytwg8Vkn\n" + "JSxNHlBLpV/TCh8lltHRwJ6TXhUBYij+KzhWbx5FWOErHDOWTMmArqtp7W6GcoJT\n" + "wVJWjxXzp8CApYQMWVSQXpckJL7UvHohZO0WKiHyxTjde5aD++TqV2qEyQKBgBbD\n" + "jvoTpy08K4DLxCZs2Uvw1I1pIuylbpwsdrGciuP2s38BM6fHH+/T4Qwj3osfDKQD\n" + "7gHWJ1Dn/wUBHQBlRLoC3bB3iZPZfVb5lhc2gxv0GvWhQVIcoGi/vJ2DpfJKPmIL\n" + "4ZWdg3X5dm9JaZ98rVDSj5D3ckd5J0E4hp95GbmbAoGBAJJHM4O9lx60tIjw9Sf/\n" + "QmKWyUk0NLnt8DcgRMW7fVxtzPNDy9DBKGIkDdWZ2s+ForICA3C9WSxBC1EOEHGG\n" + "xkg2xKt66CeutGroP6M191mHQrRClt1VbEYzQFX21BCk5kig9i/BURyoTHtFiV+t\n" + "kbf4VLg8Vk9u/R3RU1HsYWhe\n" + "-----END PRIVATE KEY-----\n"; + +const char* fixed_rsa_cert = + "-----BEGIN CERTIFICATE-----\n" + "MIIDUDCCAjgCCQD7pIb1ZsoafjANBgkqhkiG9w0BAQsFADBqMQswCQYDVQQGEwJW\n" + "VDEQMA4GA1UECAwHVmVybW9udDEWMBQGA1UEBwwNVGhlIEludGVybmV0czEUMBIG\n" + "A1UECgwLTWFuZ29zIFIgVXMxGzAZBgNVBAMMEnNlcnZlci5leGFtcGxlLmNvbTAe\n" + "Fw0xNjAxMDYxNzQ3MjNaFw0yNjAxMDMxNzQ3MjNaMGoxCzAJBgNVBAYTAlZUMRAw\n" + "DgYDVQQIDAdWZXJtb250MRYwFAYDVQQHDA1UaGUgSW50ZXJuZXRzMRQwEgYDVQQK\n" + "DAtNYW5nb3MgUiBVczEbMBkGA1UEAwwSc2VydmVyLmV4YW1wbGUuY29tMIIBIjAN\n" + "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnuqqqTEFSe8wiaEp6cYmtMjujOyH\n" + "txRqj3HdA87ngvpFj77h0Pj6T+b3zuqqkWg1SCN+zxV+TVGUJhiv6wJu7KYyB0wj\n" + "IKkmfKTddy1PJtYA8CHbvWpdei1zfc2Yweu7i5RZ9ra7S4tt+ItHJ8nhfCAtHM+7\n" + "YAjszElRAXKiY5HYBq2d1lIDl3nFZtpV1a2JRImksRaeFThPZERXlaxCwo5jbXOI\n" + "UXdcibBfrzmwrb8qhmEBNUHG9DFUkAwRzJIlFZjus7lu7XeXnzv9Tsp7UCRslWvk\n" + "p1m5Eiwsm7WtiezK3hxDaxeRgNCtSi9eNEjUR3lon0TLjULmu6pilzAUxQIDAQAB\n" + "MA0GCSqGSIb3DQEBCwUAA4IBAQA1eZGc/4V7z/E/6eG0hVkzoAZeuTcSP7WqBSx+\n" + "OP2yh0163UYjoa6nehmkKYQQ9PbYPZGzIcl+dBFyYzy6jcp0NdtzpWnTFrjl4rMq\n" + "akcQ1D0LTYjJXVP9G/vF/SvatOFeVTnQmLlLt/a8ZtRUINqejeZZPzH8ifzFW6tu\n" + "mlhTVIEKyPHpxClh5Y3ubw/mZYygekFTqMkTx3FwJxKU8J6rYGZxanWAODUIvCUo\n" + "Fxer1qC5Love3uWl3vXPLEZWZdORnExSRByzz2immBP2vX4zYZoeZRhTQ9ae1TIV\n" + "Dk02a/1AOJZdZReDbgXhlqaUx5pk/rzo4mDzvu5HSCeXmClz\n" + "-----END CERTIFICATE-----\n"; + +class Fuzzer_TLS_Server_Creds : public Credentials_Manager + { + public: + Fuzzer_TLS_Server_Creds() + { + DataSource_Memory cert_in(fixed_rsa_cert); + DataSource_Memory key_in(fixed_rsa_key); + + m_rsa_cert.reset(new Botan::X509_Certificate(cert_in)); + //m_rsa_key.reset(Botan::PKCS8::load_key(key_in, Botan::system_rng())); + } + + void verify_certificate_chain(const std::string& type, + const std::string& purported_hostname, + const std::vector<X509_Certificate>& cert_chain) override + { + try + { + Credentials_Manager::verify_certificate_chain(type, + purported_hostname, + cert_chain); + } + catch(std::exception& e) {} + } + + std::vector<Botan::X509_Certificate> cert_chain( + const std::vector<std::string>& algos, + const std::string& type, + const std::string& hostname) override + { + std::vector<Botan::X509_Certificate> v; + + for(auto algo : algos) + { + if(algo == "RSA") + { + v.push_back(*m_rsa_cert); + break; + } + } + + return v; + } + + Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert, + const std::string& /*type*/, + const std::string& /*context*/) override + { + return m_rsa_key.get(); + } + + std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; } + std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; } + SymmetricKey psk(const std::string&, const std::string&, const std::string&) override + { + return SymmetricKey("AABBCCDDEEFF00112233445566778899"); + } + private: + std::unique_ptr<Botan::X509_Certificate> m_rsa_cert; + std::unique_ptr<Botan::Private_Key> m_rsa_key; + }; + +void fuzz(const uint8_t in[], size_t len) + { + if(len == 0) + return; + + auto dev_null = [](const byte[], size_t) {}; + + auto ignore_alerts = [](TLS::Alert, const byte[], size_t) {}; + auto ignore_hs = [](const TLS::Session&) { return true; }; + + Botan::System_RNG rng; + TLS::Session_Manager_Noop session_manager; + TLS::Policy policy; + TLS::Server_Information info("server.name", 443); + Fuzzer_TLS_Server_Creds creds; + + auto next_proto_fn = [](const std::vector<std::string>& protos) -> std::string { + if(protos.size() > 1) + return protos[0]; + else + return "fuzzed"; + }; + + const bool is_datagram = (len % 2 == 0); + + TLS::Server server(dev_null, + dev_null, + ignore_alerts, + ignore_hs, + session_manager, + creds, + policy, + rng, + next_proto_fn, + is_datagram); + + try + { + while(len > 0) + { + const size_t write_len = in[0]; + const size_t left = len - 1; + + const size_t consumed = std::min(left, write_len); + + server.received_data(in + 1, consumed); + + in += consumed + 1; + len -= consumed + 1; + } + } + catch(std::exception& e) + { + } + } diff --git a/src/extra_tests/fuzzers/readme.txt b/src/extra_tests/fuzzers/readme.txt new file mode 100644 index 000000000..f10982508 --- /dev/null +++ b/src/extra_tests/fuzzers/readme.txt @@ -0,0 +1,43 @@ + +The code in this directory is for testing various message decoders and +math functions using the fuzzers AFL (http://lcamtuf.coredump.cx/afl/) +and libFuzzer (http://llvm.org/docs/LibFuzzer.html). + +Run setup.sh to set up builds for both fuzzers + +To add a new fuzzer, create a new file in jigs/, include "driver.h", +and implement the function with the signature + +void fuzz(const uint8_t buf[], size_t len); + +This function should abort or crash if something is incorrect. + +Run it with + +make run_{llvm,afl}_{what} + +like in + +make run_llvm_crl +make run_afl_tls_client + +You can pass args to the fuzzer process using args= + +make args=-max_len=4000 run_llvm_tls_client + +The fuzzer entry point assumes no more than 4K of input. The base +libFuzzer default max len is 64 bytes, the makefile sets it to 140 as +default. + +Use + +make cmin_redc_p384 + +to run afl-cmin to minimize and merge the LLVM and AFL outputs back to +the corpus directory. + +TODO: + +- KLEE (https://klee.github.io) +- DFSan (http://clang.llvm.org/docs/DataFlowSanitizer.html) +- More jigs diff --git a/src/extra_tests/fuzzers/setup.sh b/src/extra_tests/fuzzers/setup.sh new file mode 100755 index 000000000..deda2ba25 --- /dev/null +++ b/src/extra_tests/fuzzers/setup.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +mkdir bin +mkdir output +mkdir corpus + +CFG_FLAGS="--with-debug-info --unsafe-fuzzer-mode --minimized-build --enable-modules=tls,chacha20poly1305,ocb,ccm,system_rng,auto_rng" + +# Just need the static lib, not CLI or tests + +../../../configure.py $CFG_FLAGS --with-build-dir=afl --cc=clang --cc-bin='afl-clang-fast++' +make -f afl/Makefile afl/libbotan-1.11.a -j2 + +CLANG_COV_FLAGS="-fsanitize=address,undefined -fsanitize-coverage=edge,indirect-calls,8bit-counters -fno-sanitize-recover=undefined" +../../../configure.py $CFG_FLAGS --with-build-dir=llvm --cc=clang "--cc-abi-flags=$CLANG_COV_FLAGS" +make -f llvm/Makefile llvm/libbotan-1.11.a -j2 diff --git a/src/lib/utils/version.cpp b/src/lib/utils/version.cpp index 8e14cc62f..16ac3a18d 100644 --- a/src/lib/utils/version.cpp +++ b/src/lib/utils/version.cpp @@ -38,6 +38,9 @@ const char* version_cstr() return "Botan " STR(BOTAN_VERSION_MAJOR) "." STR(BOTAN_VERSION_MINOR) "." STR(BOTAN_VERSION_PATCH) " (" +if defined(BOTAN_UNSAFE_FUZZER_MODE) + "UNSAFE FUZZER MODE BUILD" +#endif BOTAN_VERSION_RELEASE_TYPE #if (BOTAN_VERSION_DATESTAMP != 0) ", dated " STR(BOTAN_VERSION_DATESTAMP) |