aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-02-28 02:43:57 -0500
committerJack Lloyd <[email protected]>2016-03-20 09:38:17 -0400
commitada363473a9491a3b07e3bb6fa2b5fd9f12aec98 (patch)
tree0dc7eefb24c3d9983e45dd6e2e7f0876179c8c11
parentf70a9de37d22282d8cca465632efd0044ab9008c (diff)
Add PK_Decryptor::decrypt_or_random
Performs content checks on the value (expected length, expected bytes) and in constant time returns either the decrypted value or a random value.
-rw-r--r--src/lib/pk_pad/eme.cpp25
-rw-r--r--src/lib/pk_pad/eme.h55
-rw-r--r--src/lib/pk_pad/eme_oaep/oaep.cpp13
-rw-r--r--src/lib/pk_pad/eme_oaep/oaep.h12
-rw-r--r--src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp15
-rw-r--r--src/lib/pk_pad/eme_pkcs1/eme_pkcs.h6
-rw-r--r--src/lib/pk_pad/eme_raw/eme_raw.cpp4
-rw-r--r--src/lib/pk_pad/eme_raw/eme_raw.h5
-rw-r--r--src/lib/pubkey/dlies/dlies.cpp8
-rw-r--r--src/lib/pubkey/dlies/dlies.h3
-rw-r--r--src/lib/pubkey/pk_ops.cpp8
-rw-r--r--src/lib/pubkey/pk_ops.h4
-rw-r--r--src/lib/pubkey/pk_ops_impl.h3
-rw-r--r--src/lib/pubkey/pubkey.cpp85
-rw-r--r--src/lib/pubkey/pubkey.h58
-rw-r--r--src/lib/tls/msg_client_kex.cpp52
-rw-r--r--src/lib/utils/ct_utils.h26
17 files changed, 253 insertions, 129 deletions
diff --git a/src/lib/pk_pad/eme.cpp b/src/lib/pk_pad/eme.cpp
index 623c3777e..b36ed6e67 100644
--- a/src/lib/pk_pad/eme.cpp
+++ b/src/lib/pk_pad/eme.cpp
@@ -57,8 +57,8 @@ EME* get_eme(const std::string& algo_spec)
* Encode a message
*/
secure_vector<byte> EME::encode(const byte msg[], size_t msg_len,
- size_t key_bits,
- RandomNumberGenerator& rng) const
+ size_t key_bits,
+ RandomNumberGenerator& rng) const
{
return pad(msg, msg_len, key_bits, rng);
}
@@ -67,28 +67,11 @@ secure_vector<byte> EME::encode(const byte msg[], size_t msg_len,
* Encode a message
*/
secure_vector<byte> EME::encode(const secure_vector<byte>& msg,
- size_t key_bits,
- RandomNumberGenerator& rng) const
+ size_t key_bits,
+ RandomNumberGenerator& rng) const
{
return pad(msg.data(), msg.size(), key_bits, rng);
}
-/*
-* Decode a message
-*/
-secure_vector<byte> EME::decode(const byte msg[], size_t msg_len,
- size_t key_bits) const
- {
- return unpad(msg, msg_len, key_bits);
- }
-
-/*
-* Decode a message
-*/
-secure_vector<byte> EME::decode(const secure_vector<byte>& msg,
- size_t key_bits) const
- {
- return unpad(msg.data(), msg.size(), key_bits);
- }
}
diff --git a/src/lib/pk_pad/eme.h b/src/lib/pk_pad/eme.h
index 7318ec480..f4c85da70 100644
--- a/src/lib/pk_pad/eme.h
+++ b/src/lib/pk_pad/eme.h
@@ -22,6 +22,8 @@ class BOTAN_DLL EME
public:
typedef SCAN_Name Spec;
+ virtual ~EME() = default;
+
/**
* Return the maximum input size in bytes we can support
* @param keybits the size of the key in bits
@@ -38,9 +40,9 @@ class BOTAN_DLL EME
* @return encoded plaintext
*/
secure_vector<byte> encode(const byte in[],
- size_t in_length,
- size_t key_length,
- RandomNumberGenerator& rng) const;
+ size_t in_length,
+ size_t key_length,
+ RandomNumberGenerator& rng) const;
/**
* Encode an input
@@ -50,31 +52,21 @@ class BOTAN_DLL EME
* @return encoded plaintext
*/
secure_vector<byte> encode(const secure_vector<byte>& in,
- size_t key_length,
- RandomNumberGenerator& rng) const;
+ size_t key_length,
+ RandomNumberGenerator& rng) const;
/**
* Decode an input
+ * @param valid_mask written to specifies if output is valid
* @param in the encoded plaintext
- * @param in_length length of encoded plaintext in bytes
- * @param key_length length of the key in bits
- * @return plaintext
+ * @param in_len length of encoded plaintext in bytes
+ * @return bytes of out[] written to along with
+ * validity mask (0xFF if valid, else 0x00)
*/
- secure_vector<byte> decode(const byte in[],
- size_t in_length,
- size_t key_length) const;
-
- /**
- * Decode an input
- * @param in the encoded plaintext
- * @param key_length length of the key in bits
- * @return plaintext
- */
- secure_vector<byte> decode(const secure_vector<byte>& in,
- size_t key_length) const;
-
- virtual ~EME() {}
- private:
+ virtual secure_vector<byte> unpad(byte& valid_mask,
+ const byte in[],
+ size_t in_len,
+ size_t key_length) const = 0;
/**
* Encode an input
* @param in the plaintext
@@ -84,20 +76,9 @@ class BOTAN_DLL EME
* @return encoded plaintext
*/
virtual secure_vector<byte> pad(const byte in[],
- size_t in_length,
- size_t key_length,
- RandomNumberGenerator& rng) const = 0;
-
- /**
- * Decode an input
- * @param in the encoded plaintext
- * @param in_length length of encoded plaintext in bytes
- * @param key_length length of the key in bits
- * @return plaintext
- */
- virtual secure_vector<byte> unpad(const byte in[],
- size_t in_length,
- size_t key_length) const = 0;
+ size_t in_length,
+ size_t key_length,
+ RandomNumberGenerator& rng) const = 0;
};
/**
diff --git a/src/lib/pk_pad/eme_oaep/oaep.cpp b/src/lib/pk_pad/eme_oaep/oaep.cpp
index 370a9fe45..894368e2d 100644
--- a/src/lib/pk_pad/eme_oaep/oaep.cpp
+++ b/src/lib/pk_pad/eme_oaep/oaep.cpp
@@ -60,7 +60,8 @@ secure_vector<byte> OAEP::pad(const byte in[], size_t in_length,
/*
* OAEP Unpad Operation
*/
-secure_vector<byte> OAEP::unpad(const byte in[], size_t in_length,
+secure_vector<byte> OAEP::unpad(byte& valid_mask,
+ const byte in[], size_t in_length,
size_t key_length) const
{
/*
@@ -116,16 +117,18 @@ secure_vector<byte> OAEP::unpad(const byte in[], size_t in_length,
// If we never saw any non-zero byte, then it's not valid input
bad_input |= waiting_for_delim;
- bad_input |= CT::expand_mask<byte>(!same_mem(&input[hlen], m_Phash.data(), hlen));
+ bad_input |= CT::is_equal<byte>(same_mem(&input[hlen], m_Phash.data(), hlen), false);
CT::unpoison(input.data(), input.size());
CT::unpoison(&bad_input, 1);
CT::unpoison(&delim_idx, 1);
- if(bad_input)
- throw Decoding_Error("Invalid OAEP encoding");
+ valid_mask = ~bad_input;
- return secure_vector<byte>(input.begin() + delim_idx + 1, input.end());
+ secure_vector<byte> output(input.begin() + delim_idx + 1, input.end());
+ 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 22d009f5f..dce706613 100644
--- a/src/lib/pk_pad/eme_oaep/oaep.h
+++ b/src/lib/pk_pad/eme_oaep/oaep.h
@@ -29,9 +29,15 @@ class BOTAN_DLL OAEP final : public EME
*/
OAEP(HashFunction* hash, const std::string& P = "");
private:
- secure_vector<byte> pad(const byte[], size_t, size_t,
- RandomNumberGenerator&) const override;
- secure_vector<byte> unpad(const byte[], size_t, size_t) const override;
+ secure_vector<byte> pad(const byte in[],
+ size_t in_length,
+ size_t key_length,
+ RandomNumberGenerator& rng) const override;
+
+ secure_vector<byte> unpad(byte& valid_mask,
+ const byte in[],
+ size_t in_len,
+ size_t key_length) const override;
secure_vector<byte> m_Phash;
std::unique_ptr<HashFunction> m_hash;
diff --git a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
index 5ff288db2..4780fe43b 100644
--- a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
+++ b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp
@@ -37,7 +37,8 @@ secure_vector<byte> EME_PKCS1v15::pad(const byte in[], size_t inlen,
/*
* PKCS1 Unpad Operation
*/
-secure_vector<byte> EME_PKCS1v15::unpad(const byte in[], size_t inlen,
+secure_vector<byte> EME_PKCS1v15::unpad(byte& valid_mask,
+ const byte in[], size_t inlen,
size_t key_len) const
{
if(inlen != key_len / 8 || inlen < 10)
@@ -64,13 +65,13 @@ secure_vector<byte> EME_PKCS1v15::unpad(const byte in[], size_t inlen,
bad_input_m |= ~seen_zero_m;
CT::unpoison(in, inlen);
- CT::unpoison(&bad_input_m, 1);
- CT::unpoison(&delim_idx, 1);
+ CT::unpoison(bad_input_m);
+ CT::unpoison(delim_idx);
- if(bad_input_m)
- throw Decoding_Error("Invalid PKCS #1 v1.5 encryption padding");
-
- return secure_vector<byte>(&in[delim_idx + 1], &in[inlen]);
+ secure_vector<byte> output(&in[delim_idx + 1], &in[inlen]);
+ CT::cond_zero_mem(bad_input_m, output.data(), output.size());
+ valid_mask = ~bad_input_m;
+ return output;
}
/*
diff --git a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h
index 148ab7e20..d5f8879d6 100644
--- a/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h
+++ b/src/lib/pk_pad/eme_pkcs1/eme_pkcs.h
@@ -22,7 +22,11 @@ class BOTAN_DLL EME_PKCS1v15 final : public EME
private:
secure_vector<byte> pad(const byte[], size_t, size_t,
RandomNumberGenerator&) const override;
- secure_vector<byte> unpad(const byte[], size_t, size_t) const override;
+
+ secure_vector<byte> unpad(byte& valid_mask,
+ const byte in[],
+ size_t in_len,
+ size_t key_length) const override;
};
}
diff --git a/src/lib/pk_pad/eme_raw/eme_raw.cpp b/src/lib/pk_pad/eme_raw/eme_raw.cpp
index 78b670b65..5c5dd6e40 100644
--- a/src/lib/pk_pad/eme_raw/eme_raw.cpp
+++ b/src/lib/pk_pad/eme_raw/eme_raw.cpp
@@ -18,9 +18,11 @@ secure_vector<byte> EME_Raw::pad(const byte in[], size_t in_length,
return secure_vector<byte>(in, in + in_length);
}
-secure_vector<byte> EME_Raw::unpad(const byte in[], size_t in_length,
+secure_vector<byte> EME_Raw::unpad(byte& valid_mask,
+ const byte in[], size_t in_length,
size_t) const
{
+ valid_mask = 0xFF;
return secure_vector<byte>(in, in + in_length);
}
diff --git a/src/lib/pk_pad/eme_raw/eme_raw.h b/src/lib/pk_pad/eme_raw/eme_raw.h
index ae57587a3..60d23323c 100644
--- a/src/lib/pk_pad/eme_raw/eme_raw.h
+++ b/src/lib/pk_pad/eme_raw/eme_raw.h
@@ -21,7 +21,10 @@ class BOTAN_DLL EME_Raw final : public EME
secure_vector<byte> pad(const byte[], size_t, size_t,
RandomNumberGenerator&) const override;
- secure_vector<byte> unpad(const byte[], size_t, size_t) const override;
+ secure_vector<byte> unpad(byte& valid_mask,
+ const byte in[],
+ size_t in_len,
+ size_t key_length) const override;
};
}
diff --git a/src/lib/pubkey/dlies/dlies.cpp b/src/lib/pubkey/dlies/dlies.cpp
index ba890ac3d..2c98966b0 100644
--- a/src/lib/pubkey/dlies/dlies.cpp
+++ b/src/lib/pubkey/dlies/dlies.cpp
@@ -6,6 +6,7 @@
*/
#include <botan/dlies.h>
+#include <botan/internal/ct_utils.h>
namespace Botan {
@@ -97,7 +98,8 @@ DLIES_Decryptor::DLIES_Decryptor(const PK_Key_Agreement_Key& key,
/*
* DLIES Decryption
*/
-secure_vector<byte> DLIES_Decryptor::dec(const byte msg[], size_t length) const
+secure_vector<byte> DLIES_Decryptor::do_decrypt(byte& valid_mask,
+ const byte msg[], size_t length) const
{
if(length < m_my_key.size() + m_mac->output_length())
throw Decoding_Error("DLIES decryption: ciphertext is too short");
@@ -124,8 +126,8 @@ secure_vector<byte> DLIES_Decryptor::dec(const byte msg[], size_t length) const
for(size_t j = 0; j != 8; ++j)
m_mac->update(0);
secure_vector<byte> T2 = m_mac->final();
- if(T != T2)
- throw Decoding_Error("DLIES: message authentication failed");
+
+ valid_mask = CT::expand_mask<byte>(same_mem(T.data(), T2.data(), T.size()));
xor_buf(C, K.data() + m_mac_keylen, C.size());
diff --git a/src/lib/pubkey/dlies/dlies.h b/src/lib/pubkey/dlies/dlies.h
index dd8838a28..10471048d 100644
--- a/src/lib/pubkey/dlies/dlies.h
+++ b/src/lib/pubkey/dlies/dlies.h
@@ -52,7 +52,8 @@ class BOTAN_DLL DLIES_Decryptor : public PK_Decryptor
size_t mac_key_len = 20);
private:
- secure_vector<byte> dec(const byte[], size_t) const override;
+ secure_vector<byte> do_decrypt(byte& valid_mask,
+ const byte in[], size_t in_len) const override;
std::vector<byte> m_my_key;
diff --git a/src/lib/pubkey/pk_ops.cpp b/src/lib/pubkey/pk_ops.cpp
index 81b087894..37c31777d 100644
--- a/src/lib/pubkey/pk_ops.cpp
+++ b/src/lib/pubkey/pk_ops.cpp
@@ -54,9 +54,13 @@ size_t PK_Ops::Decryption_with_EME::max_input_bits() const
return m_eme->maximum_input_size(max_raw_input_bits());
}
-secure_vector<byte> PK_Ops::Decryption_with_EME::decrypt(const byte msg[], size_t length)
+secure_vector<byte>
+PK_Ops::Decryption_with_EME::decrypt(byte& valid_mask,
+ const byte ciphertext[],
+ size_t ciphertext_len)
{
- return m_eme->decode(raw_decrypt(msg, length), max_raw_input_bits());
+ const secure_vector<byte> raw = raw_decrypt(ciphertext, ciphertext_len);
+ return m_eme->unpad(valid_mask, raw.data(), raw.size(), max_raw_input_bits());
}
PK_Ops::Key_Agreement_with_KDF::Key_Agreement_with_KDF(const std::string& kdf)
diff --git a/src/lib/pubkey/pk_ops.h b/src/lib/pubkey/pk_ops.h
index 6fc21ea4a..712b8c457 100644
--- a/src/lib/pubkey/pk_ops.h
+++ b/src/lib/pubkey/pk_ops.h
@@ -68,7 +68,9 @@ class BOTAN_DLL Decryption
virtual size_t max_input_bits() const = 0;
- virtual secure_vector<byte> decrypt(const byte msg[], size_t msg_len) = 0;
+ virtual secure_vector<byte> decrypt(byte& valid_mask,
+ const byte ciphertext[],
+ size_t ciphertext_len) = 0;
virtual ~Decryption() {}
};
diff --git a/src/lib/pubkey/pk_ops_impl.h b/src/lib/pubkey/pk_ops_impl.h
index 9be65cf21..81637a81c 100644
--- a/src/lib/pubkey/pk_ops_impl.h
+++ b/src/lib/pubkey/pk_ops_impl.h
@@ -37,7 +37,8 @@ class Decryption_with_EME : public Decryption
public:
size_t max_input_bits() const override;
- secure_vector<byte> decrypt(const byte msg[], size_t msg_len) override;
+ secure_vector<byte> decrypt(byte& valid_mask,
+ const byte msg[], size_t msg_len) override;
~Decryption_with_EME();
protected:
diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp
index d3b711f1e..86665ed93 100644
--- a/src/lib/pubkey/pubkey.cpp
+++ b/src/lib/pubkey/pubkey.cpp
@@ -5,10 +5,11 @@
*/
#include <botan/pubkey.h>
-#include <botan/internal/algo_registry.h>
#include <botan/der_enc.h>
#include <botan/ber_dec.h>
#include <botan/bigint.h>
+#include <botan/internal/algo_registry.h>
+#include <botan/internal/ct_utils.h>
namespace Botan {
@@ -30,6 +31,83 @@ T* get_pk_op(const std::string& what, const Key& key, const std::string& pad,
}
+secure_vector<byte> PK_Decryptor::decrypt(const byte in[], size_t length) const
+ {
+ byte valid_mask = 0;
+
+ secure_vector<byte> decoded = do_decrypt(valid_mask, in, length);
+
+ if(valid_mask == 0)
+ throw Decoding_Error("Invalid public key ciphertext, cannot decrypt");
+
+ return decoded;
+ }
+
+secure_vector<byte>
+PK_Decryptor::decrypt_or_random(const byte in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng,
+ const byte required_content_bytes[],
+ const byte required_content_offsets[],
+ size_t required_contents_length) const
+ {
+ const secure_vector<byte> fake_pms = rng.random_vec(expected_pt_len);
+ //secure_vector<byte> decoded(expected_pt_len);
+
+ CT::poison(in, length);
+
+ byte valid_mask = 0;
+ secure_vector<byte> decoded = do_decrypt(valid_mask, in, length);
+
+ valid_mask &= CT::is_equal(decoded.size(), expected_pt_len);
+
+ // fixme
+ decoded.resize(expected_pt_len);
+
+ for(size_t i = 0; i != required_contents_length; ++i)
+ {
+ /*
+ These values are chosen by the application and for TLS are constants,
+ so this early failure via assert is fine since we know 0,1 < 48
+
+ If there is a protocol that has content checks on the key where
+ the expected offsets are controllable by the attacker this could
+ still leak.
+
+ Alternately could always reduce the offset modulo the length?
+ */
+
+ const byte exp = required_content_bytes[i];
+ const byte off = required_content_offsets[i];
+
+ BOTAN_ASSERT(off < expected_pt_len, "Offset in range of plaintext");
+
+ valid_mask &= CT::is_equal(decoded[off], exp);
+ }
+
+ CT::conditional_copy_mem(valid_mask,
+ /*output*/decoded.data(),
+ /*from0*/decoded.data(),
+ /*from1*/fake_pms.data(),
+ expected_pt_len);
+
+ CT::unpoison(in, length);
+ CT::unpoison(decoded.data(), decoded.size());
+
+ return decoded;
+ }
+
+secure_vector<byte>
+PK_Decryptor::decrypt_or_random(const byte in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng) const
+ {
+ return decrypt_or_random(in, length, expected_pt_len, rng,
+ nullptr, nullptr, 0);
+ }
+
PK_Encryptor_EME::PK_Encryptor_EME(const Public_Key& key,
const std::string& padding,
const std::string& provider)
@@ -54,9 +132,10 @@ PK_Decryptor_EME::PK_Decryptor_EME(const Private_Key& key, const std::string& pa
m_op.reset(get_pk_op<PK_Ops::Decryption>("Decryption", key, padding, provider));
}
-secure_vector<byte> PK_Decryptor_EME::dec(const byte msg[], size_t length) const
+secure_vector<byte> PK_Decryptor_EME::do_decrypt(byte& valid_mask,
+ const byte in[], size_t in_len) const
{
- return m_op->decrypt(msg, length);
+ return m_op->decrypt(valid_mask, in, in_len);
}
PK_KEM_Encryptor::PK_KEM_Encryptor(const Public_Key& key,
diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h
index bfcde2190..26cbb1790 100644
--- a/src/lib/pubkey/pubkey.h
+++ b/src/lib/pubkey/pubkey.h
@@ -81,35 +81,71 @@ class BOTAN_DLL PK_Decryptor
{
public:
/**
- * Decrypt a ciphertext.
+ * Decrypt a ciphertext, throwing an exception if the input
+ * seems to be invalid (eg due to an accidental or malicious
+ * error in the ciphertext).
+ *
* @param in the ciphertext as a byte array
* @param length the length of the above byte array
* @return decrypted message
*/
- secure_vector<byte> decrypt(const byte in[], size_t length) const
- {
- return dec(in, length);
- }
+ secure_vector<byte> decrypt(const byte in[], size_t length) const;
/**
- * Decrypt a ciphertext.
+ * Same as above, but taking a vector
* @param in the ciphertext
* @return decrypted message
*/
template<typename Alloc>
secure_vector<byte> decrypt(const std::vector<byte, Alloc>& in) const
{
- return dec(in.data(), in.size());
+ return decrypt(in.data(), in.size());
}
+ /**
+ * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
+ * invalid padding) or is not the expected length, instead
+ * returns a random string of the expected length. Use to avoid
+ * oracle attacks, especially against PKCS #1 v1.5 decryption.
+ */
+ secure_vector<byte>
+ decrypt_or_random(const byte in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng) const;
+
+ /**
+ * Decrypt a ciphertext. If the ciphertext is invalid (eg due to
+ * invalid padding) or is not the expected length, instead
+ * returns a random string of the expected length. Use to avoid
+ * oracle attacks, especially against PKCS #1 v1.5 decryption.
+ *
+ * Additionally checks (also in const time) that:
+ * contents[required_content_offsets[i]] == required_content_bytes[i]
+ * for 0 <= i < required_contents
+ *
+ * Used for example in TLS, which encodes the client version in
+ * the content bytes: if there is any timing variation the version
+ * check can be used as an oracle to recover the key.
+ */
+ secure_vector<byte>
+ decrypt_or_random(const byte in[],
+ size_t length,
+ size_t expected_pt_len,
+ RandomNumberGenerator& rng,
+ const byte required_content_bytes[],
+ const byte required_content_offsets[],
+ size_t required_contents) const;
+
PK_Decryptor() {}
- virtual ~PK_Decryptor() {}
+ virtual ~PK_Decryptor() = default;
PK_Decryptor(const PK_Decryptor&) = delete;
PK_Decryptor& operator=(const PK_Decryptor&) = delete;
private:
- virtual secure_vector<byte> dec(const byte[], size_t) const = 0;
+ virtual secure_vector<byte> do_decrypt(byte& valid_mask,
+ const byte in[], size_t in_len) const = 0;
};
/**
@@ -436,7 +472,9 @@ class BOTAN_DLL PK_Decryptor_EME : public PK_Decryptor
const std::string& eme,
const std::string& provider = "");
private:
- secure_vector<byte> dec(const byte[], size_t) const override;
+ secure_vector<byte> do_decrypt(byte& valid_mask,
+ const byte in[],
+ size_t in_len) const override;
std::unique_ptr<PK_Ops::Decryption> m_op;
};
diff --git a/src/lib/tls/msg_client_kex.cpp b/src/lib/tls/msg_client_kex.cpp
index d7689df45..77e9795f4 100644
--- a/src/lib/tls/msg_client_kex.cpp
+++ b/src/lib/tls/msg_client_kex.cpp
@@ -270,41 +270,33 @@ Client_Key_Exchange::Client_Key_Exchange(const std::vector<byte>& contents,
if(!dynamic_cast<const RSA_PrivateKey*>(server_rsa_kex_key))
throw Internal_Error("Expected RSA key but got " + server_rsa_kex_key->algo_name());
+ TLS_Data_Reader reader("ClientKeyExchange", contents);
+ const std::vector<byte> encrypted_pre_master = reader.get_range<byte>(2, 0, 65535);
+
PK_Decryptor_EME decryptor(*server_rsa_kex_key, "PKCS1v15");
- Protocol_Version client_version = state.client_hello()->version();
+ const byte client_major = state.client_hello()->version().major_version();
+ const byte client_minor = state.client_hello()->version().minor_version();
/*
- * This is used as the pre-master if RSA decryption fails.
- * Otherwise we can be used as an oracle. See Bleichenbacher
- * "Chosen Ciphertext Attacks against Protocols Based on RSA
- * Encryption Standard PKCS #1", Crypto 98
- *
- * Create it here instead if in the catch clause as otherwise we
- * expose a timing channel WRT the generation of the fake value.
- * Some timing channel likely remains due to exception handling
- * and the like.
+ * PK_Decryptor::decrypt_or_random will return a random value if
+ * either the length does not match the expected value or if the
+ * version number embedded in the PMS does not match the one sent
+ * in the client hello.
*/
- secure_vector<byte> fake_pre_master = rng.random_vec(48);
- fake_pre_master[0] = client_version.major_version();
- fake_pre_master[1] = client_version.minor_version();
-
- try
- {
- TLS_Data_Reader reader("ClientKeyExchange", contents);
- m_pre_master = decryptor.decrypt(reader.get_range<byte>(2, 0, 65535));
-
- if(m_pre_master.size() != 48 ||
- client_version.major_version() != m_pre_master[0] ||
- client_version.minor_version() != m_pre_master[1])
- {
- throw Decoding_Error("Client_Key_Exchange: Secret corrupted");
- }
- }
- catch(...)
- {
- m_pre_master = fake_pre_master;
- }
+ const size_t expected_plaintext_size = 48;
+ const size_t expected_content_size = 2;
+ const byte expected_content_bytes[expected_content_size] = { client_major, client_minor };
+ const byte expected_content_pos[expected_content_size] = { 0, 1 };
+
+ m_pre_master =
+ decryptor.decrypt_or_random(encrypted_pre_master.data(),
+ encrypted_pre_master.size(),
+ expected_plaintext_size,
+ rng,
+ expected_content_bytes,
+ expected_content_pos,
+ expected_content_size);
}
else
{
diff --git a/src/lib/utils/ct_utils.h b/src/lib/utils/ct_utils.h
index 401a53e86..ec055374a 100644
--- a/src/lib/utils/ct_utils.h
+++ b/src/lib/utils/ct_utils.h
@@ -100,6 +100,12 @@ inline T select(T mask, T from0, T from1)
return (from0 & mask) | (from1 & ~mask);
}
+template<typename PredT, typename ValT>
+inline ValT val_or_zero(PredT pred_val, ValT val)
+ {
+ return select(CT::expand_mask<ValT>(pred_val), val, static_cast<ValT>(0));
+ }
+
template<typename T>
inline T is_zero(T x)
{
@@ -127,12 +133,28 @@ inline void conditional_copy_mem(T value,
T* to,
const T* from0,
const T* from1,
- size_t bytes)
+ size_t elems)
{
const T mask = CT::expand_mask(value);
- for(size_t i = 0; i != bytes; ++i)
+ for(size_t i = 0; i != elems; ++i)
+ {
to[i] = CT::select(mask, from0[i], from1[i]);
+ }
+ }
+
+template<typename T>
+inline void cond_zero_mem(T cond,
+ T* array,
+ size_t elems)
+ {
+ const T mask = CT::expand_mask(cond);
+ const T zero(0);
+
+ for(size_t i = 0; i != elems; ++i)
+ {
+ array[i] = CT::select(mask, zero, array[i]);
+ }
}
template<typename T>