diff options
author | Jack Lloyd <[email protected]> | 2016-08-31 17:11:49 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-08-31 17:11:49 -0400 |
commit | de94c3778d91fa329f83eeb93efb1b7eb6a35f13 (patch) | |
tree | 6852910cc5d8ece21f2da14f70b03ec127b73062 /src/lib/tls | |
parent | 47ec0534ebeb3e4035ff6d9866c726501ad2bc0c (diff) | |
parent | dfab07a7bc00dc00f98ab86c70d536306073f34f (diff) |
Merge GH #578/#492: TLS EtM extension and new policy toggles
Diffstat (limited to 'src/lib/tls')
-rw-r--r-- | src/lib/tls/msg_cert_verify.cpp | 2 | ||||
-rw-r--r-- | src/lib/tls/msg_certificate.cpp | 2 | ||||
-rw-r--r-- | src/lib/tls/msg_client_hello.cpp | 6 | ||||
-rw-r--r-- | src/lib/tls/msg_client_kex.cpp | 11 | ||||
-rw-r--r-- | src/lib/tls/msg_server_hello.cpp | 14 | ||||
-rw-r--r-- | src/lib/tls/msg_server_kex.cpp | 2 | ||||
-rw-r--r-- | src/lib/tls/tls_channel.cpp | 6 | ||||
-rw-r--r-- | src/lib/tls/tls_ciphersuite.cpp | 7 | ||||
-rw-r--r-- | src/lib/tls/tls_ciphersuite.h | 5 | ||||
-rw-r--r-- | src/lib/tls/tls_client.cpp | 3 | ||||
-rw-r--r-- | src/lib/tls/tls_extensions.cpp | 16 | ||||
-rw-r--r-- | src/lib/tls/tls_extensions.h | 22 | ||||
-rw-r--r-- | src/lib/tls/tls_messages.h | 12 | ||||
-rw-r--r-- | src/lib/tls/tls_policy.cpp | 103 | ||||
-rw-r--r-- | src/lib/tls/tls_policy.h | 121 | ||||
-rw-r--r-- | src/lib/tls/tls_record.cpp | 268 | ||||
-rw-r--r-- | src/lib/tls/tls_record.h | 7 | ||||
-rw-r--r-- | src/lib/tls/tls_server.cpp | 17 | ||||
-rw-r--r-- | src/lib/tls/tls_session.cpp | 4 | ||||
-rw-r--r-- | src/lib/tls/tls_session.h | 9 |
20 files changed, 525 insertions, 112 deletions
diff --git a/src/lib/tls/msg_cert_verify.cpp b/src/lib/tls/msg_cert_verify.cpp index 2598255eb..6b59e703f 100644 --- a/src/lib/tls/msg_cert_verify.cpp +++ b/src/lib/tls/msg_cert_verify.cpp @@ -82,6 +82,8 @@ bool Certificate_Verify::verify(const X509_Certificate& cert, { std::unique_ptr<Public_Key> key(cert.subject_public_key()); + policy.check_peer_key_acceptable(*key); + std::pair<std::string, Signature_Format> format = state.parse_sig_format(*key.get(), m_hash_algo, m_sig_algo, true, policy); diff --git a/src/lib/tls/msg_certificate.cpp b/src/lib/tls/msg_certificate.cpp index 32e3e17f0..10ee7c95f 100644 --- a/src/lib/tls/msg_certificate.cpp +++ b/src/lib/tls/msg_certificate.cpp @@ -31,7 +31,7 @@ Certificate::Certificate(Handshake_IO& io, /** * Deserialize a Certificate message */ -Certificate::Certificate(const std::vector<byte>& buf) +Certificate::Certificate(const std::vector<byte>& buf, const Policy& /*policy_currently_unused*/) { if(buf.size() < 3) throw Decoding_Error("Certificate: Message malformed"); diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index f504786ef..69f9a5e11 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -95,6 +95,9 @@ Client_Hello::Client_Hello(Handshake_IO& io, if(reneg_info.empty() && !next_protocols.empty()) m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + if(policy.negotiate_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); #if defined(BOTAN_HAS_SRP6) m_extensions.add(new SRP_Identifier(client_settings.srp_identifier())); @@ -154,6 +157,9 @@ Client_Hello::Client_Hello(Handshake_IO& io, if(reneg_info.empty() && !next_protocols.empty()) m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); + + if(policy.negotiate_encrypt_then_mac()) + m_extensions.add(new Encrypt_then_MAC); #if defined(BOTAN_HAS_SRP6) m_extensions.add(new SRP_Identifier(session.srp_identifier())); diff --git a/src/lib/tls/msg_client_kex.cpp b/src/lib/tls/msg_client_kex.cpp index 77e9795f4..bc4d33d52 100644 --- a/src/lib/tls/msg_client_kex.cpp +++ b/src/lib/tls/msg_client_kex.cpp @@ -92,13 +92,6 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, if(reader.remaining_bytes()) throw Decoding_Error("Bad params size for DH key exchange"); - if(p.bits() < policy.minimum_dh_group_size()) - throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, - "Server sent DH group of " + - std::to_string(p.bits()) + - " bits, policy requires at least " + - std::to_string(policy.minimum_dh_group_size())); - /* * A basic check for key validity. As we do not know q here we * cannot check that Y is in the right subgroup. However since @@ -117,6 +110,8 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, DH_PublicKey counterparty_key(group, Y); + policy.check_peer_key_acceptable(counterparty_key); + DH_PrivateKey priv_key(rng, group); PK_Key_Agreement ka(priv_key, "Raw"); @@ -160,6 +155,8 @@ Client_Key_Exchange::Client_Key_Exchange(Handshake_IO& io, ECDH_PublicKey counterparty_key(group, OS2ECP(ecdh_key, group.get_curve())); + policy.check_peer_key_acceptable(counterparty_key); + ECDH_PrivateKey priv_key(rng, group); PK_Key_Agreement ka(priv_key, "Raw"); diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index f32625508..ebe8fb085 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -35,6 +35,13 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(client_hello.supports_extended_master_secret()) m_extensions.add(new Extended_Master_Secret); + if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) + { + Ciphersuite c = Ciphersuite::by_id(m_ciphersuite); + if(c.cbc_ciphersuite()) + m_extensions.add(new Encrypt_then_MAC); + } + if(client_hello.secure_renegotiation()) m_extensions.add(new Renegotiation_Extension(reneg_info)); @@ -87,6 +94,13 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(client_hello.supports_extended_master_secret()) m_extensions.add(new Extended_Master_Secret); + if(client_hello.supports_encrypt_then_mac() && policy.negotiate_encrypt_then_mac()) + { + Ciphersuite c = resumed_session.ciphersuite(); + if(c.cbc_ciphersuite()) + m_extensions.add(new Encrypt_then_MAC); + } + if(client_hello.secure_renegotiation()) m_extensions.add(new Renegotiation_Extension(reneg_info)); diff --git a/src/lib/tls/msg_server_kex.cpp b/src/lib/tls/msg_server_kex.cpp index 98e3ad1f0..10581fe45 100644 --- a/src/lib/tls/msg_server_kex.cpp +++ b/src/lib/tls/msg_server_kex.cpp @@ -236,6 +236,8 @@ bool Server_Key_Exchange::verify(const Public_Key& server_key, const Handshake_State& state, const Policy& policy) const { + policy.check_peer_key_acceptable(server_key); + std::pair<std::string, Signature_Format> format = state.parse_sig_format(server_key, m_hash_algo, m_sig_algo, false, policy); diff --git a/src/lib/tls/tls_channel.cpp b/src/lib/tls/tls_channel.cpp index a476b21bd..5e9207da7 100644 --- a/src/lib/tls/tls_channel.cpp +++ b/src/lib/tls/tls_channel.cpp @@ -215,7 +215,8 @@ void Channel::change_cipher_spec_reader(Connection_Side side) (side == CLIENT) ? SERVER : CLIENT, false, pending->ciphersuite(), - pending->session_keys())); + pending->session_keys(), + pending->server_hello()->supports_encrypt_then_mac())); m_read_cipher_states[epoch] = read_state; } @@ -242,7 +243,8 @@ void Channel::change_cipher_spec_writer(Connection_Side side) side, true, pending->ciphersuite(), - pending->session_keys())); + pending->session_keys(), + pending->server_hello()->supports_encrypt_then_mac())); m_write_cipher_states[epoch] = write_state; } diff --git a/src/lib/tls/tls_ciphersuite.cpp b/src/lib/tls/tls_ciphersuite.cpp index dc0c71278..9a52e0e0e 100644 --- a/src/lib/tls/tls_ciphersuite.cpp +++ b/src/lib/tls/tls_ciphersuite.cpp @@ -35,6 +35,13 @@ bool Ciphersuite::ecc_ciphersuite() const return (sig_algo() == "ECDSA" || kex_algo() == "ECDH" || kex_algo() == "ECDHE_PSK"); } +bool Ciphersuite::cbc_ciphersuite() const + { + return (cipher_algo() == "3DES" || cipher_algo() == "SEED" || + cipher_algo() == "AES-128" || cipher_algo() == "AES-256" || + cipher_algo() == "Camellia-128" || cipher_algo() == "Camellia-256"); + } + Ciphersuite Ciphersuite::by_id(u16bit suite) { const std::vector<Ciphersuite>& all_suites = all_known_ciphersuites(); diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h index 199e126b1..6708e3ca6 100644 --- a/src/lib/tls/tls_ciphersuite.h +++ b/src/lib/tls/tls_ciphersuite.h @@ -63,6 +63,11 @@ class BOTAN_DLL Ciphersuite bool ecc_ciphersuite() const; /** + * @return true if this suite uses a CBC cipher + */ + bool cbc_ciphersuite() const; + + /** * @return key exchange algorithm used by this ciphersuite */ std::string kex_algo() const { return m_kex_algo; } diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index f77521c03..0e72b9a28 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -373,7 +373,7 @@ void Client::process_handshake_msg(const Handshake_State* active_state, state.set_expected_next(SERVER_HELLO_DONE); } - state.server_certs(new Certificate(contents)); + state.server_certs(new Certificate(contents, policy())); const std::vector<X509_Certificate>& server_certs = state.server_certs()->cert_chain(); @@ -529,6 +529,7 @@ void Client::process_handshake_msg(const Handshake_State* active_state, state.server_hello()->compression_method(), CLIENT, state.server_hello()->supports_extended_master_secret(), + state.server_hello()->supports_encrypt_then_mac(), get_peer_cert_chain(state), session_ticket, m_info, diff --git a/src/lib/tls/tls_extensions.cpp b/src/lib/tls/tls_extensions.cpp index 3ea97203c..3dceb505a 100644 --- a/src/lib/tls/tls_extensions.cpp +++ b/src/lib/tls/tls_extensions.cpp @@ -1,6 +1,7 @@ /* * TLS Extensions * (C) 2011,2012,2015,2016 Jack Lloyd +* 2016 Juraj Somorovsky * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -47,6 +48,9 @@ Extension* make_extension(TLS_Data_Reader& reader, case TLSEXT_EXTENDED_MASTER_SECRET: return new Extended_Master_Secret(reader, size); + case TLSEXT_ENCRYPT_THEN_MAC: + return new Encrypt_then_MAC(reader, size); + case TLSEXT_SESSION_TICKET: return new Session_Ticket(reader, size); @@ -519,6 +523,18 @@ std::vector<byte> Extended_Master_Secret::serialize() const return std::vector<byte>(); } +Encrypt_then_MAC::Encrypt_then_MAC(TLS_Data_Reader&, + u16bit extension_size) + { + if(extension_size != 0) + throw Decoding_Error("Invalid encrypt_then_mac extension"); + } + +std::vector<byte> Encrypt_then_MAC::serialize() const + { + return std::vector<byte>(); + } + } } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index cfde0067c..dc69eec36 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -1,6 +1,7 @@ /* * TLS Extensions * (C) 2011,2012,2016 Jack Lloyd +* 2016 Juraj Somorovsky * 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) @@ -37,6 +38,7 @@ enum Handshake_Extension_Type { TLSEXT_USE_SRTP = 14, TLSEXT_ALPN = 16, + TLSEXT_ENCRYPT_THEN_MAC = 22, TLSEXT_EXTENDED_MASTER_SECRET = 23, TLSEXT_SESSION_TICKET = 35, @@ -341,6 +343,26 @@ class Extended_Master_Secret final : public Extension }; /** +* Encrypt-then-MAC Extension (RFC 7366) +*/ +class Encrypt_then_MAC final : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_ENCRYPT_THEN_MAC; } + + Handshake_Extension_Type type() const override { return static_type(); } + + std::vector<byte> serialize() const override; + + bool empty() const override { return false; } + + Encrypt_then_MAC() {} + + Encrypt_then_MAC(TLS_Data_Reader& reader, u16bit extension_size); + }; + +/** * Represents a block of extensions in a hello message */ class Extensions diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 47ff7d3d8..8ccb2fbff 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -162,6 +162,11 @@ class Client_Hello final : public Handshake_Message return m_extensions.has<Extended_Master_Secret>(); } + bool supports_encrypt_then_mac() const + { + return m_extensions.has<Encrypt_then_MAC>(); + } + std::vector<std::string> next_protocols() const { if(auto alpn = m_extensions.get<Application_Layer_Protocol_Notification>()) @@ -276,6 +281,11 @@ class Server_Hello final : public Handshake_Message return m_extensions.has<Extended_Master_Secret>(); } + bool supports_encrypt_then_mac() const + { + return m_extensions.has<Encrypt_then_MAC>(); + } + bool supports_session_ticket() const { return m_extensions.has<Session_Ticket>(); @@ -385,7 +395,7 @@ class Certificate final : public Handshake_Message Handshake_Hash& hash, const std::vector<X509_Certificate>& certs); - explicit Certificate(const std::vector<byte>& buf); + explicit Certificate(const std::vector<byte>& buf, const Policy &policy); private: std::vector<byte> serialize() const override; diff --git a/src/lib/tls/tls_policy.cpp b/src/lib/tls/tls_policy.cpp index 10b193215..592d4f572 100644 --- a/src/lib/tls/tls_policy.cpp +++ b/src/lib/tls/tls_policy.cpp @@ -1,6 +1,7 @@ /* * Policies for TLS * (C) 2004-2010,2012,2015,2016 Jack Lloyd +* 2016 Christian Mainka * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -119,14 +120,75 @@ std::string Policy::choose_curve(const std::vector<std::string>& curve_names) co std::string Policy::dh_group() const { + // We offer 2048 bit DH because we can return "modp/ietf/2048"; } size_t Policy::minimum_dh_group_size() const { + // Many servers still send 1024 bit return 1024; } +size_t Policy::minimum_ecdsa_group_size() const + { + // Here we are at the mercy of whatever the CA signed, but most certs should be 256 bit by now + return 256; + } + +size_t Policy::minimum_ecdh_group_size() const + { + // P-256 is smallest curve currently supplrted for TLS key exchange (after 1.11.29) + return 256; + } + +size_t Policy::minimum_rsa_bits() const + { + /* Default assumption is all end-entity certificates should + be at least 2048 bits these days. + + If you are connecting to arbitrary servers on the Internet + (ie as a web browser or SMTP client) you'll probably have to reduce this + to 1024 bits, or perhaps even lower. + */ + return 2048; + } + +void Policy::check_peer_key_acceptable(const Public_Key& public_key) const + { + const std::string algo_name = public_key.algo_name(); + + // FIXME this is not really the right way to do this + size_t keylength = public_key.max_input_bits(); + size_t expected_keylength = 0; + + if(algo_name == "RSA") + { + expected_keylength = minimum_rsa_bits(); + keylength += 1; // fixup for use of max_input_bits above + } + else if(algo_name == "DH") + { + expected_keylength = minimum_dh_group_size(); + } + else if(algo_name == "ECDH") + { + expected_keylength = minimum_ecdh_group_size(); + } + else if(algo_name == "ECDSA") + { + expected_keylength = minimum_ecdsa_group_size(); + } + // else some other algo, so leave expected_keylength as zero and the check is a no-op + + if(keylength < expected_keylength) + throw TLS_Exception(Alert::INSUFFICIENT_SECURITY, + "Peer sent " + + std::to_string(keylength) + " bit " + algo_name + " key" + ", policy requires at least " + + std::to_string(expected_keylength)); + } + /* * Return allowed compression algorithms */ @@ -147,10 +209,17 @@ bool Policy::send_fallback_scsv(Protocol_Version version) const bool Policy::acceptable_protocol_version(Protocol_Version version) const { - if(version.is_datagram_protocol()) - return (version >= Protocol_Version::DTLS_V12); - else - return (version >= Protocol_Version::TLS_V10); + // Uses boolean optimization: + // First check the current version (left part), then if it is allowed + // (right part) + // checks are ordered according to their probability + return ( + ( ( version == Protocol_Version::TLS_V12) && allow_tls12() ) || + ( ( version == Protocol_Version::TLS_V10) && allow_tls10() ) || + ( ( version == Protocol_Version::TLS_V11) && allow_tls11() ) || + ( ( version == Protocol_Version::DTLS_V12) && allow_dtls12() ) || + ( ( version == Protocol_Version::DTLS_V10) && allow_dtls10() ) + ); } Protocol_Version Policy::latest_supported_version(bool datagram) const @@ -168,9 +237,15 @@ bool Policy::acceptable_ciphersuite(const Ciphersuite&) const bool Policy::allow_server_initiated_renegotiation() const { return false; } bool Policy::allow_insecure_renegotiation() const { return false; } +bool Policy::allow_tls10() const { return true; } +bool Policy::allow_tls11() const { return true; } +bool Policy::allow_tls12() const { return true; } +bool Policy::allow_dtls10() const { return false; } +bool Policy::allow_dtls12() const { return true; } bool Policy::include_time_in_hello_random() const { return true; } bool Policy::hide_unknown_users() const { return false; } bool Policy::server_uses_own_ciphersuite_preferences() const { return true; } +bool Policy::negotiate_encrypt_then_mac() const { return true; } // 1 second initial timeout, 60 second max - see RFC 6347 sec 4.2.4.1 size_t Policy::dtls_initial_timeout() const { return 1*1000; } @@ -339,6 +414,11 @@ void print_bool(std::ostream& o, void Policy::print(std::ostream& o) const { + print_bool(o, "allow_tls10", allow_tls10()); + print_bool(o, "allow_tls11", allow_tls11()); + print_bool(o, "allow_tls12", allow_tls12()); + print_bool(o, "allow_dtls10", allow_dtls10()); + print_bool(o, "allow_dtls12", allow_dtls12()); print_vec(o, "ciphers", allowed_ciphers()); print_vec(o, "macs", allowed_macs()); print_vec(o, "signature_hashes", allowed_signature_hashes()); @@ -351,9 +431,12 @@ void Policy::print(std::ostream& o) const print_bool(o, "allow_server_initiated_renegotiation", allow_server_initiated_renegotiation()); print_bool(o, "hide_unknown_users", hide_unknown_users()); print_bool(o, "server_uses_own_ciphersuite_preferences", server_uses_own_ciphersuite_preferences()); + print_bool(o, "negotiate_encrypt_then_mac", negotiate_encrypt_then_mac()); o << "session_ticket_lifetime = " << session_ticket_lifetime() << '\n'; o << "dh_group = " << dh_group() << '\n'; o << "minimum_dh_group_size = " << minimum_dh_group_size() << '\n'; + o << "minimum_ecdh_group_size = " << minimum_ecdh_group_size() << '\n'; + o << "minimum_rsa_bits = " << minimum_rsa_bits() << '\n'; } std::vector<std::string> Strict_Policy::allowed_ciphers() const @@ -376,13 +459,11 @@ std::vector<std::string> Strict_Policy::allowed_key_exchange_methods() const return { "ECDH" }; } -bool Strict_Policy::acceptable_protocol_version(Protocol_Version version) const - { - if(version.is_datagram_protocol()) - return (version >= Protocol_Version::DTLS_V12); - else - return (version >= Protocol_Version::TLS_V12); - } +bool Strict_Policy::allow_tls10() const { return false; } +bool Strict_Policy::allow_tls11() const { return false; } +bool Strict_Policy::allow_tls12() const { return true; } +bool Strict_Policy::allow_dtls10() const { return false; } +bool Strict_Policy::allow_dtls12() const { return true; } } diff --git a/src/lib/tls/tls_policy.h b/src/lib/tls/tls_policy.h index 999ba2887..76e80ddde 100644 --- a/src/lib/tls/tls_policy.h +++ b/src/lib/tls/tls_policy.h @@ -101,15 +101,79 @@ class BOTAN_DLL Policy * Allow servers to initiate a new handshake */ virtual bool allow_server_initiated_renegotiation() const; + + /** + * Allow TLS v1.0 + */ + virtual bool allow_tls10() const; + + /** + * Allow TLS v1.1 + */ + virtual bool allow_tls11() const; + + /** + * Allow TLS v1.2 + */ + virtual bool allow_tls12() const; + + /** + * Allow DTLS v1.0 + */ + virtual bool allow_dtls10() const; + + /** + * Allow DTLS v1.2 + */ + virtual bool allow_dtls12() const; virtual std::string dh_group() const; /** * Return the minimum DH group size we're willing to use + * Default is currently 1024 (insecure), should be 2048 */ virtual size_t minimum_dh_group_size() const; + + /** + * For ECDSA authenticated ciphersuites, the smallest key size the + * client will accept. + * This policy is currently only enforced on the server by the client. + */ + virtual size_t minimum_ecdsa_group_size() const; + + /** + * Return the minimum ECDH group size we're willing to use + * for key exchange + * + * Default 256, allowing P-256 and larger + * P-256 is the smallest curve we will negotiate + */ + virtual size_t minimum_ecdh_group_size() const; + + /** + * Return the minimum bit size we're willing to accept for RSA + * key exchange or server signatures. + * + * It does not place any requirements on the size of any RSA signature(s) + * which were used to check the server certificate. This is only + * concerned with the server's public key. + * + * Default is 2048 which is smallest RSA key size still secure + * for medium term security. + */ + virtual size_t minimum_rsa_bits() const; /** + * Throw an exception if you don't like the peer's key. + * Default impl checks the key size against minimum_rsa_bits, minimum_ecdsa_group_size, + * or minimum_ecdh_group_size depending on the key's type. + * Override if you'd like to perform some other kind of test on + * (or logging of) the peer's keys. + */ + virtual void check_peer_key_acceptable(const Public_Key& public_key) const; + + /** * If this function returns false, unknown SRP/PSK identifiers * will be rejected with an unknown_psk_identifier alert as soon * as the non-existence is identified. Otherwise, a false @@ -168,6 +232,12 @@ class BOTAN_DLL Policy virtual bool server_uses_own_ciphersuite_preferences() const; /** + * Indicates whether the encrypt-then-MAC extension should be negotiated + * (RFC 7366) + */ + virtual bool negotiate_encrypt_then_mac() const; + + /** * Return allowed ciphersuites, in order of preference */ virtual std::vector<u16bit> ciphersuite_list(Protocol_Version version, @@ -207,9 +277,12 @@ class BOTAN_DLL NSA_Suite_B_128 : public Policy std::vector<std::string> allowed_ecc_curves() const override { return std::vector<std::string>({"secp256r1"}); } - - bool acceptable_protocol_version(Protocol_Version version) const override - { return version == Protocol_Version::TLS_V12; } + + bool allow_tls10() const override { return false; } + bool allow_tls11() const override { return false; } + bool allow_tls12() const override { return true; } + bool allow_dtls10() const override { return false; } + bool allow_dtls12() const override { return false; } }; /** @@ -220,9 +293,12 @@ class BOTAN_DLL Datagram_Policy : public Policy public: std::vector<std::string> allowed_macs() const override { return std::vector<std::string>({"AEAD"}); } - - bool acceptable_protocol_version(Protocol_Version version) const override - { return version == Protocol_Version::DTLS_V12; } + + bool allow_tls10() const override { return false; } + bool allow_tls11() const override { return false; } + bool allow_tls12() const override { return false; } + bool allow_dtls10() const override { return false; } + bool allow_dtls12() const override { return true; } }; /* @@ -243,7 +319,11 @@ class BOTAN_DLL Strict_Policy : public Policy std::vector<std::string> allowed_key_exchange_methods() const override; - bool acceptable_protocol_version(Protocol_Version version) const override; + bool allow_tls10() const override; + bool allow_tls11() const override; + bool allow_tls12() const override; + bool allow_dtls10() const override; + bool allow_dtls12() const override; }; class BOTAN_DLL Text_Policy : public Policy @@ -267,6 +347,21 @@ class BOTAN_DLL Text_Policy : public Policy std::vector<std::string> allowed_ecc_curves() const override { return get_list("ecc_curves", Policy::allowed_ecc_curves()); } + + bool allow_tls10() const override + { return get_bool("allow_tls10", Policy::allow_tls10()); } + + bool allow_tls11() const override + { return get_bool("allow_tls11", Policy::allow_tls11()); } + + bool allow_tls12() const override + { return get_bool("allow_tls12", Policy::allow_tls12()); } + + bool allow_dtls10() const override + { return get_bool("allow_dtls10", Policy::allow_dtls10()); } + + bool allow_dtls12() const override + { return get_bool("allow_dtls12", Policy::allow_dtls12()); } bool allow_insecure_renegotiation() const override { return get_bool("allow_insecure_renegotiation", Policy::allow_insecure_renegotiation()); } @@ -280,12 +375,24 @@ class BOTAN_DLL Text_Policy : public Policy bool server_uses_own_ciphersuite_preferences() const override { return get_bool("server_uses_own_ciphersuite_preferences", Policy::server_uses_own_ciphersuite_preferences()); } + bool negotiate_encrypt_then_mac() const override + { return get_bool("negotiate_encrypt_then_mac", Policy::negotiate_encrypt_then_mac()); } + std::string dh_group() const override { return get_str("dh_group", Policy::dh_group()); } + size_t minimum_ecdh_group_size() const override + { return get_len("minimum_ecdh_group_size", Policy::minimum_ecdh_group_size()); } + + size_t minimum_ecdsa_group_size() const override + { return get_len("minimum_ecdsa_group_size", Policy::minimum_ecdsa_group_size()); } + size_t minimum_dh_group_size() const override { return get_len("minimum_dh_group_size", Policy::minimum_dh_group_size()); } + size_t minimum_rsa_bits() const override + { return get_len("minimum_rsa_bits", Policy::minimum_rsa_bits()); } + bool hide_unknown_users() const override { return get_bool("hide_unknown_users", Policy::hide_unknown_users()); } diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp index e028c43a0..877b81b41 100644 --- a/src/lib/tls/tls_record.cpp +++ b/src/lib/tls/tls_record.cpp @@ -1,6 +1,7 @@ /* * TLS Record Handling -* (C) 2012,2013,2014,2015 Jack Lloyd +* (C) 2012,2013,2014,2015,2016 Jack Lloyd +* 2016 Juraj Somorovsky * 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) @@ -24,10 +25,12 @@ Connection_Cipher_State::Connection_Cipher_State(Protocol_Version version, Connection_Side side, bool our_side, const Ciphersuite& suite, - const Session_Keys& keys) : + const Session_Keys& keys, + bool uses_encrypt_then_mac) : m_start_time(std::chrono::system_clock::now()), m_nonce_bytes_from_handshake(suite.nonce_bytes_from_handshake()), - m_nonce_bytes_from_record(suite.nonce_bytes_from_record()) + m_nonce_bytes_from_record(suite.nonce_bytes_from_record()), + m_uses_encrypt_then_mac(uses_encrypt_then_mac) { SymmetricKey mac_key, cipher_key; InitializationVector iv; @@ -213,77 +216,151 @@ void write_record(secure_vector<byte>& output, return; } - cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size()))); - - cs->mac()->update(msg.get_data(), msg.get_size()); - const size_t block_size = cs->block_size(); const size_t iv_size = cs->iv_size(); const size_t mac_size = cs->mac_size(); - const size_t buf_size = round_up( - iv_size + msg.get_size() + mac_size + (block_size ? 1 : 0), - block_size); + if(!cs->uses_encrypt_then_mac()) + { + cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size()))); + cs->mac()->update(msg.get_data(), msg.get_size()); - if(buf_size > MAX_CIPHERTEXT_SIZE) - throw Internal_Error("Output record is larger than allowed by protocol"); + const size_t buf_size = round_up( + iv_size + msg.get_size() + mac_size + (block_size ? 1 : 0), + block_size); - output.push_back(get_byte(0, static_cast<u16bit>(buf_size))); - output.push_back(get_byte(1, static_cast<u16bit>(buf_size))); + if(buf_size > MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Output record is larger than allowed by protocol"); - const size_t header_size = output.size(); + output.push_back(get_byte(0, static_cast<u16bit>(buf_size))); + output.push_back(get_byte(1, static_cast<u16bit>(buf_size))); - if(iv_size) - { - output.resize(output.size() + iv_size); - rng.randomize(&output[output.size() - iv_size], iv_size); - } + const size_t header_size = output.size(); - output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); + if(iv_size) + { + output.resize(output.size() + iv_size); + rng.randomize(&output[output.size() - iv_size], iv_size); + } - output.resize(output.size() + mac_size); - cs->mac()->final(&output[output.size() - mac_size]); + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); - if(block_size) - { - const size_t pad_val = - buf_size - (iv_size + msg.get_size() + mac_size + 1); + output.resize(output.size() + mac_size); + cs->mac()->final(&output[output.size() - mac_size]); - for(size_t i = 0; i != pad_val + 1; ++i) - output.push_back(static_cast<byte>(pad_val)); - } + if(block_size) + { + const size_t pad_val = + buf_size - (iv_size + msg.get_size() + mac_size + 1); + + for(size_t i = 0; i != pad_val + 1; ++i) + output.push_back(static_cast<byte>(pad_val)); + } - if(buf_size > MAX_CIPHERTEXT_SIZE) - throw Internal_Error("Produced ciphertext larger than protocol allows"); + if(buf_size > MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Produced ciphertext larger than protocol allows"); - BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(), + BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(), "Output buffer is sized properly"); - if(BlockCipher* bc = cs->block_cipher()) - { - secure_vector<byte>& cbc_state = cs->cbc_state(); + if(BlockCipher* bc = cs->block_cipher()) + { + secure_vector<byte>& cbc_state = cs->cbc_state(); - BOTAN_ASSERT(buf_size % block_size == 0, + BOTAN_ASSERT(buf_size % block_size == 0, "Buffer is an even multiple of block size"); - byte* buf = &output[header_size]; + byte* buf = &output[header_size]; + + const size_t blocks = buf_size / block_size; + + xor_buf(buf, cbc_state.data(), block_size); + bc->encrypt(buf); + + for(size_t i = 1; i < blocks; ++i) + { + xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size); + bc->encrypt(&buf[block_size*i]); + } + + cbc_state.assign(&buf[block_size*(blocks-1)], + &buf[block_size*blocks]); + } + else + { + throw Internal_Error("NULL cipher not supported"); + } + } + else + { + const size_t enc_size = round_up( + iv_size + msg.get_size() + (block_size ? 1 : 0), + block_size); + + const size_t buf_size = enc_size + mac_size; - const size_t blocks = buf_size / block_size; + if(buf_size > MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Output record is larger than allowed by protocol"); - xor_buf(buf, cbc_state.data(), block_size); - bc->encrypt(buf); + output.push_back(get_byte<u16bit>(0, buf_size)); + output.push_back(get_byte<u16bit>(1, buf_size)); + + const size_t header_size = output.size(); - for(size_t i = 1; i < blocks; ++i) + if(iv_size) { - xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size); - bc->encrypt(&buf[block_size*i]); + output.resize(output.size() + iv_size); + rng.randomize(&output[output.size() - iv_size], iv_size); + } + + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); + + if(block_size) + { + const size_t pad_val = + enc_size - (iv_size + msg.get_size() + 1); + + for(size_t i = 0; i != pad_val + 1; ++i) + output.push_back(pad_val); } - cbc_state.assign(&buf[block_size*(blocks-1)], + if(BlockCipher* bc = cs->block_cipher()) + { + secure_vector<byte>& cbc_state = cs->cbc_state(); + + BOTAN_ASSERT( enc_size % block_size == 0, + "Buffer is an even multiple of block size"); + + byte* buf = &output[header_size]; + + const size_t blocks = enc_size / block_size; + + xor_buf(buf, cbc_state.data(), block_size); + bc->encrypt(buf); + + for(size_t i = 1; i < blocks; ++i) + { + xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size); + bc->encrypt(&buf[block_size*i]); + } + + cbc_state.assign(&buf[block_size*(blocks-1)], &buf[block_size*blocks]); + + cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, enc_size)); + cs->mac()->update(buf, enc_size); + + output.resize(output.size() + mac_size); + cs->mac()->final(&output[output.size() - mac_size]); + + BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(), + "Output buffer is sized properly"); + } + else + { + throw Internal_Error("NULL cipher not supported"); + } } - else - throw Internal_Error("NULL cipher not supported"); } namespace { @@ -409,53 +486,90 @@ void decrypt_record(secure_vector<byte>& output, const size_t mac_size = cs.mac_size(); const size_t iv_size = cs.iv_size(); - // This early exit does not leak info because all the values are public - if((record_len < mac_size + iv_size) || (record_len % cs.block_size() != 0)) - throw Decoding_Error("Record sent with invalid length"); + if(!cs.uses_encrypt_then_mac()) + { + // This early exit does not leak info because all the values are public + if((record_len < mac_size + iv_size) || (record_len % cs.block_size() != 0)) + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); - CT::poison(record_contents, record_len); + CT::poison(record_contents, record_len); - cbc_decrypt_record(record_contents, record_len, cs, *bc); + cbc_decrypt_record(record_contents, record_len, cs, *bc); - // 0 if padding was invalid, otherwise 1 + padding_bytes - u16bit pad_size = tls_padding_check(record_contents, record_len); + // 0 if padding was invalid, otherwise 1 + padding_bytes + u16bit pad_size = tls_padding_check(record_contents, record_len); - // This mask is zero if there is not enough room in the packet - const u16bit size_ok_mask = CT::is_lte<u16bit>(static_cast<u16bit>(mac_size + pad_size + iv_size), static_cast<u16bit>(record_len)); - pad_size &= size_ok_mask; + // This mask is zero if there is not enough room in the packet to get + // a valid MAC. We have to accept empty packets, since otherwise we + // are not compatible with the BEAST countermeasure (thus record_len+1). + const u16bit size_ok_mask = CT::is_lte<u16bit>(static_cast<u16bit>(mac_size + pad_size + iv_size), static_cast<u16bit>(record_len + 1)); + pad_size &= size_ok_mask; - CT::unpoison(record_contents, record_len); + CT::unpoison(record_contents, record_len); - /* - This is unpoisoned sooner than it should. The pad_size leaks to plaintext_length and - then to the timing channel in the MAC computation described in the Lucky 13 paper. - */ - CT::unpoison(pad_size); + /* + This is unpoisoned sooner than it should. The pad_size leaks to plaintext_length and + then to the timing channel in the MAC computation described in the Lucky 13 paper. + */ + CT::unpoison(pad_size); - const byte* plaintext_block = &record_contents[iv_size]; - const u16bit plaintext_length = static_cast<u16bit>(record_len - mac_size - iv_size - pad_size); + const byte* plaintext_block = &record_contents[iv_size]; + const u16bit plaintext_length = static_cast<u16bit>(record_len - mac_size - iv_size - pad_size); - cs.mac()->update(cs.format_ad(record_sequence, record_type, record_version, plaintext_length)); - cs.mac()->update(plaintext_block, plaintext_length); + cs.mac()->update(cs.format_ad(record_sequence, record_type, record_version, plaintext_length)); + cs.mac()->update(plaintext_block, plaintext_length); - std::vector<byte> mac_buf(mac_size); - cs.mac()->final(mac_buf.data()); + std::vector<byte> mac_buf(mac_size); + cs.mac()->final(mac_buf.data()); - const size_t mac_offset = record_len - (mac_size + pad_size); + const size_t mac_offset = record_len - (mac_size + pad_size); - const bool mac_ok = same_mem(&record_contents[mac_offset], mac_buf.data(), mac_size); + const bool mac_ok = same_mem(&record_contents[mac_offset], mac_buf.data(), mac_size); - const u16bit ok_mask = size_ok_mask & CT::expand_mask<u16bit>(mac_ok) & CT::expand_mask<u16bit>(pad_size); + const u16bit ok_mask = size_ok_mask & CT::expand_mask<u16bit>(mac_ok) & CT::expand_mask<u16bit>(pad_size); - CT::unpoison(ok_mask); + CT::unpoison(ok_mask); - if(ok_mask) - { - output.assign(plaintext_block, plaintext_block + plaintext_length); + if(ok_mask) + { + output.assign(plaintext_block, plaintext_block + plaintext_length); + } + else + { + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + } } else { - throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + const size_t enc_size = record_len - mac_size; + // This early exit does not leak info because all the values are public + if((record_len < mac_size + iv_size) || ( enc_size % cs.block_size() != 0)) + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + + cs.mac()->update(cs.format_ad(record_sequence, record_type, record_version, enc_size)); + cs.mac()->update(record_contents, enc_size); + + std::vector<byte> mac_buf(mac_size); + cs.mac()->final(mac_buf.data()); + + const size_t mac_offset = enc_size; + + const bool mac_ok = same_mem(&record_contents[mac_offset], mac_buf.data(), mac_size); + + if(!mac_ok) + { + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + } + + cbc_decrypt_record(record_contents, enc_size, cs, *bc); + + // 0 if padding was invalid, otherwise 1 + padding_bytes + u16bit pad_size = tls_padding_check(record_contents, enc_size); + + const byte* plaintext_block = &record_contents[iv_size]; + const u16bit plaintext_length = enc_size - iv_size - pad_size; + + output.assign(plaintext_block, plaintext_block + plaintext_length); } } } diff --git a/src/lib/tls/tls_record.h b/src/lib/tls/tls_record.h index c16b919b5..4420a9c66 100644 --- a/src/lib/tls/tls_record.h +++ b/src/lib/tls/tls_record.h @@ -39,7 +39,8 @@ class Connection_Cipher_State Connection_Side which_side, bool is_our_side, const Ciphersuite& suite, - const Session_Keys& keys); + const Session_Keys& keys, + bool uses_encrypt_then_mac); AEAD_Mode* aead() { return m_aead.get(); } @@ -67,6 +68,8 @@ class Connection_Cipher_State size_t nonce_bytes_from_handshake() const { return m_nonce_bytes_from_handshake; } + bool uses_encrypt_then_mac() const { return m_uses_encrypt_then_mac; } + bool cbc_without_explicit_iv() const { return (m_block_size > 0) && (m_iv_size == 0); } @@ -89,6 +92,8 @@ class Connection_Cipher_State size_t m_nonce_bytes_from_handshake; size_t m_nonce_bytes_from_record; size_t m_iv_size = 0; + + bool m_uses_encrypt_then_mac; }; class Record diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp index 2e546eab1..40aa18d27 100644 --- a/src/lib/tls/tls_server.cpp +++ b/src/lib/tls/tls_server.cpp @@ -130,6 +130,19 @@ bool check_for_resume(Session& session_info, } } + // Checking encrypt_then_mac on resume (RFC 7366 section 3.1) + if( !client_hello->supports_encrypt_then_mac() && session_info.supports_encrypt_then_mac()) + { + + /* + Client previously negotiated session with Encrypt-then-MAC, + but has now attempted to resume without the extension: abort + */ + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client resumed Encrypt-then-MAC session without sending extension"); + + } + return true; } @@ -441,7 +454,7 @@ void Server::process_client_hello_msg(const Handshake_State* active_state, void Server::process_certificate_msg(Server_Handshake_State& pending_state, const std::vector<byte>& contents) { - pending_state.client_certs(new Certificate(contents)); + pending_state.client_certs(new Certificate(contents, policy())); pending_state.set_expected_next(CLIENT_KEX); } @@ -528,6 +541,7 @@ void Server::process_finished_msg(Server_Handshake_State& pending_state, pending_state.server_hello()->compression_method(), SERVER, pending_state.server_hello()->supports_extended_master_secret(), + pending_state.server_hello()->supports_encrypt_then_mac(), get_peer_cert_chain ( pending_state ), std::vector<byte>(), Server_Information(pending_state.client_hello()->sni_hostname()), @@ -783,7 +797,6 @@ void Server::session_create(Server_Handshake_State& pending_state, } else { - pending_state.server_kex(new Server_Key_Exchange(pending_state.handshake_io(), pending_state, policy(), m_creds, rng(), private_key)); diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp index 18c9b357c..d6b52846f 100644 --- a/src/lib/tls/tls_session.cpp +++ b/src/lib/tls/tls_session.cpp @@ -24,6 +24,7 @@ Session::Session(const std::vector<byte>& session_identifier, byte compression_method, Connection_Side side, bool extended_master_secret, + bool encrypt_then_mac, const std::vector<X509_Certificate>& certs, const std::vector<byte>& ticket, const Server_Information& server_info, @@ -39,6 +40,7 @@ Session::Session(const std::vector<byte>& session_identifier, m_connection_side(side), m_srtp_profile(srtp_profile), m_extended_master_secret(extended_master_secret), + m_encrypt_then_mac(encrypt_then_mac), m_peer_certs(certs), m_server_info(server_info), m_srp_identifier(srp_identifier) @@ -83,6 +85,7 @@ Session::Session(const byte ber[], size_t ber_len) .decode_integer_type(side_code) .decode_integer_type(fragment_size) .decode(m_extended_master_secret) + .decode(m_encrypt_then_mac) .decode(m_master_secret, OCTET_STRING) .decode(peer_cert_bits, OCTET_STRING) .decode(server_hostname) @@ -142,6 +145,7 @@ secure_vector<byte> Session::DER_encode() const .encode(static_cast<size_t>(m_connection_side)) .encode(static_cast<size_t>(/*old fragment size*/0)) .encode(m_extended_master_secret) + .encode(m_encrypt_then_mac) .encode(m_master_secret, OCTET_STRING) .encode(peer_cert_bits, OCTET_STRING) .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING)) diff --git a/src/lib/tls/tls_session.h b/src/lib/tls/tls_session.h index 8ca646cf2..643b79ac6 100644 --- a/src/lib/tls/tls_session.h +++ b/src/lib/tls/tls_session.h @@ -38,7 +38,8 @@ class BOTAN_DLL Session m_compression_method(0), m_connection_side(static_cast<Connection_Side>(0)), m_srtp_profile(0), - m_extended_master_secret(false) + m_extended_master_secret(false), + m_encrypt_then_mac(false) {} /** @@ -51,6 +52,7 @@ class BOTAN_DLL Session byte compression_method, Connection_Side side, bool supports_extended_master_secret, + bool supports_encrypt_then_mac, const std::vector<X509_Certificate>& peer_certs, const std::vector<byte>& session_ticket, const Server_Information& server_info, @@ -157,6 +159,8 @@ class BOTAN_DLL Session bool supports_extended_master_secret() const { return m_extended_master_secret; } + bool supports_encrypt_then_mac() const { return m_encrypt_then_mac; } + /** * Return the certificate chain of the peer (possibly empty) */ @@ -180,7 +184,7 @@ class BOTAN_DLL Session const Server_Information& server_info() const { return m_server_info; } private: - enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160103 }; + enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160812}; std::chrono::system_clock::time_point m_start_time; @@ -194,6 +198,7 @@ class BOTAN_DLL Session Connection_Side m_connection_side; u16bit m_srtp_profile; bool m_extended_master_secret; + bool m_encrypt_then_mac; std::vector<X509_Certificate> m_peer_certs; Server_Information m_server_info; // optional |