diff options
author | Jack Lloyd <[email protected]> | 2018-09-22 12:27:05 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2018-09-22 12:30:10 -0400 |
commit | a2cd27f00034a274099eca17ed87e8462959a2a3 (patch) | |
tree | 09ef4424e4de0085c57b3208b303ba647615648b /src | |
parent | 24c05a24f58f67e36f616c6c266fec32bf90e4a4 (diff) |
Add a fuzzer for OAEP unpadding
This tests the delim scanning section which must be const time.
Diffstat (limited to 'src')
-rw-r--r-- | src/fuzzer/oaep.cpp | 102 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_oaep/oaep.cpp | 38 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_oaep/oaep.h | 5 |
3 files changed, 135 insertions, 10 deletions
diff --git a/src/fuzzer/oaep.cpp b/src/fuzzer/oaep.cpp new file mode 100644 index 000000000..7b482edb8 --- /dev/null +++ b/src/fuzzer/oaep.cpp @@ -0,0 +1,102 @@ +/* +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ +#include "fuzzers.h" + +#include <botan/oaep.h> +#include <botan/hex.h> + +namespace { + +Botan::secure_vector<uint8_t> +ref_oaep_unpad(uint8_t& valid_mask, + const uint8_t in[], size_t len, + const Botan::secure_vector<uint8_t>& Phash) + { + const size_t hlen = Phash.size(); + + if(len < 2*hlen + 1) + { + return Botan::secure_vector<uint8_t>(); + } + + for(size_t i = hlen; i != 2*hlen; ++i) + { + if(in[i] != Phash[i-hlen]) + { + return Botan::secure_vector<uint8_t>(); + } + } + + for(size_t i = 2*hlen; i != len; ++i) + { + if(in[i] != 0x00 && in[i] != 0x01) + { + return Botan::secure_vector<uint8_t>(); + } + + if(in[i] == 0x01) + { + valid_mask = 0xFF; + return Botan::secure_vector<uint8_t>(in + i + 1, in + len); + } + } + + return Botan::secure_vector<uint8_t>(); + } + +inline bool all_zeros(const Botan::secure_vector<uint8_t>& v) + { + for(size_t i = 0; i != v.size(); ++i) + { + if(v[i] != 0) + return false; + } + return true; + } + +} + +void fuzz(const uint8_t in[], size_t len) + { + const Botan::secure_vector<uint8_t> Phash = { 1, 2, 3, 4 }; + + uint8_t lib_valid_mask = 0; + const Botan::secure_vector<uint8_t> lib_output = Botan::oaep_find_delim(lib_valid_mask, in, len, Phash); + FUZZER_ASSERT_TRUE(lib_valid_mask == 0 || lib_valid_mask == 0xFF); + + uint8_t ref_valid_mask = 0; + const Botan::secure_vector<uint8_t> ref_output = ref_oaep_unpad(ref_valid_mask, in, len, Phash); + FUZZER_ASSERT_TRUE(ref_valid_mask == 0 || ref_valid_mask == 0xFF); + + if(ref_valid_mask == 0xFF && lib_valid_mask == 0x00) + { + FUZZER_WRITE_AND_CRASH("Ref accepted but library rejected, output " << Botan::hex_encode(ref_output) << "\n"); + } + else if(ref_valid_mask == 0x00 && lib_valid_mask == 0xFF) + { + FUZZER_WRITE_AND_CRASH("Lib accepted but ref rejected, output = " << Botan::hex_encode(lib_output) << "\n"); + } + + if(ref_valid_mask == 0x00) + { + FUZZER_ASSERT_TRUE(all_zeros(ref_output)); + } + + if(lib_valid_mask == 0x00) + { + FUZZER_ASSERT_TRUE(all_zeros(lib_output)); + } + + if(ref_valid_mask && lib_valid_mask) + { + if(ref_output != lib_output) + { + FUZZER_WRITE_AND_CRASH("Ref and lib both accepted but produced different output:" + << " ref = " << Botan::hex_encode(ref_output) + << " lib = " << Botan::hex_encode(lib_output)); + } + } + } diff --git a/src/lib/pk_pad/eme_oaep/oaep.cpp b/src/lib/pk_pad/eme_oaep/oaep.cpp index f528dd134..398202bd1 100644 --- a/src/lib/pk_pad/eme_oaep/oaep.cpp +++ b/src/lib/pk_pad/eme_oaep/oaep.cpp @@ -50,7 +50,7 @@ secure_vector<uint8_t> OAEP::pad(const uint8_t in[], size_t in_length, * OAEP Unpad Operation */ secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask, - const uint8_t in[], size_t in_length) const + const uint8_t in[], size_t in_length) const { /* Must be careful about error messages here; if an attacker can @@ -61,8 +61,8 @@ secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask, Also have to be careful about timing attacks! Pointed out by Falko Strenzke. - - According to the standard (Section 7.1.1), the encryptor always + + According to the standard (Section 7.1.1), the encryptor always creates a message as follows: i. Concatenate a single octet with hexadecimal value 0x00, maskedSeed, and maskedDB to form an encoded message EM of @@ -73,10 +73,8 @@ secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask, */ uint8_t skip_first = CT::is_zero<uint8_t>(in[0]) & 0x01; - - secure_vector<uint8_t> input(in + skip_first, in + in_length); - CT::poison(input.data(), input.size()); + secure_vector<uint8_t> input(in + skip_first, in + in_length); const size_t hlen = m_Phash.size(); @@ -88,11 +86,29 @@ secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask, input.data(), hlen, &input[hlen], input.size() - hlen); + return oaep_find_delim(valid_mask, input.data(), input.size(), m_Phash); + } + +secure_vector<uint8_t> +oaep_find_delim(uint8_t& valid_mask, + const uint8_t input[], size_t input_len, + const secure_vector<uint8_t>& Phash) + { + const size_t hlen = Phash.size(); + + // Too short to be valid, reject immediately + if(input_len < 1 + 2*hlen) + { + return secure_vector<uint8_t>(); + } + + CT::poison(input, input_len); + size_t delim_idx = 2 * hlen; uint8_t waiting_for_delim = 0xFF; uint8_t bad_input = 0; - for(size_t i = delim_idx; i < input.size(); ++i) + for(size_t i = delim_idx; i < input_len; ++i) { const uint8_t zero_m = CT::is_zero<uint8_t>(input[i]); const uint8_t one_m = CT::is_equal<uint8_t>(input[i], 1); @@ -108,15 +124,17 @@ secure_vector<uint8_t> OAEP::unpad(uint8_t& valid_mask, // If we never saw any non-zero byte, then it's not valid input bad_input |= waiting_for_delim; - bad_input |= CT::is_equal<uint8_t>(constant_time_compare(&input[hlen], m_Phash.data(), hlen), false); + bad_input |= CT::is_equal<uint8_t>(constant_time_compare(&input[hlen], Phash.data(), hlen), false); + + delim_idx &= ~CT::expand_mask<size_t>(bad_input); - CT::unpoison(input.data(), input.size()); + CT::unpoison(input, input_len); CT::unpoison(&bad_input, 1); CT::unpoison(&delim_idx, 1); valid_mask = ~bad_input; - secure_vector<uint8_t> output(input.begin() + delim_idx + 1, input.end()); + secure_vector<uint8_t> output(input + delim_idx + 1, input + input_len); CT::cond_zero_mem(bad_input, output.data(), output.size()); return output; diff --git a/src/lib/pk_pad/eme_oaep/oaep.h b/src/lib/pk_pad/eme_oaep/oaep.h index 461d24f86..4bb12d27e 100644 --- a/src/lib/pk_pad/eme_oaep/oaep.h +++ b/src/lib/pk_pad/eme_oaep/oaep.h @@ -50,6 +50,11 @@ class BOTAN_PUBLIC_API(2,0) OAEP final : public EME std::unique_ptr<HashFunction> m_mgf1_hash; }; +secure_vector<uint8_t> +BOTAN_TEST_API oaep_find_delim(uint8_t& valid_mask, + const uint8_t input[], size_t input_len, + const secure_vector<uint8_t>& Phash); + } #endif |