aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/pubkey
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 /src/lib/pubkey
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.
Diffstat (limited to 'src/lib/pubkey')
-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
7 files changed, 148 insertions, 21 deletions
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;
};