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 | |
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.
-rw-r--r-- | src/lib/pk_pad/eme.cpp | 25 | ||||
-rw-r--r-- | src/lib/pk_pad/eme.h | 55 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_oaep/oaep.cpp | 13 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_oaep/oaep.h | 12 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_pkcs1/eme_pkcs.cpp | 15 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_pkcs1/eme_pkcs.h | 6 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_raw/eme_raw.cpp | 4 | ||||
-rw-r--r-- | src/lib/pk_pad/eme_raw/eme_raw.h | 5 | ||||
-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 | ||||
-rw-r--r-- | src/lib/tls/msg_client_kex.cpp | 52 | ||||
-rw-r--r-- | src/lib/utils/ct_utils.h | 26 |
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> |