diff options
author | Jack Lloyd <[email protected]> | 2016-02-28 02:43:57 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-03-20 09:38:17 -0400 |
commit | ada363473a9491a3b07e3bb6fa2b5fd9f12aec98 (patch) | |
tree | 0dc7eefb24c3d9983e45dd6e2e7f0876179c8c11 /src/lib/pubkey | |
parent | f70a9de37d22282d8cca465632efd0044ab9008c (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.cpp | 8 | ||||
-rw-r--r-- | src/lib/pubkey/dlies/dlies.h | 3 | ||||
-rw-r--r-- | src/lib/pubkey/pk_ops.cpp | 8 | ||||
-rw-r--r-- | src/lib/pubkey/pk_ops.h | 4 | ||||
-rw-r--r-- | src/lib/pubkey/pk_ops_impl.h | 3 | ||||
-rw-r--r-- | src/lib/pubkey/pubkey.cpp | 85 | ||||
-rw-r--r-- | src/lib/pubkey/pubkey.h | 58 |
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; }; |