aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2018-09-22 12:27:05 -0400
committerJack Lloyd <[email protected]>2018-09-22 12:30:10 -0400
commita2cd27f00034a274099eca17ed87e8462959a2a3 (patch)
tree09ef4424e4de0085c57b3208b303ba647615648b /src
parent24c05a24f58f67e36f616c6c266fec32bf90e4a4 (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.cpp102
-rw-r--r--src/lib/pk_pad/eme_oaep/oaep.cpp38
-rw-r--r--src/lib/pk_pad/eme_oaep/oaep.h5
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