aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-08-31 17:11:49 -0400
committerJack Lloyd <[email protected]>2016-08-31 17:11:49 -0400
commitde94c3778d91fa329f83eeb93efb1b7eb6a35f13 (patch)
tree6852910cc5d8ece21f2da14f70b03ec127b73062 /src
parent47ec0534ebeb3e4035ff6d9866c726501ad2bc0c (diff)
parentdfab07a7bc00dc00f98ab86c70d536306073f34f (diff)
Merge GH #578/#492: TLS EtM extension and new policy toggles
Diffstat (limited to 'src')
-rw-r--r--src/cli/tls_server.cpp23
-rw-r--r--src/lib/tls/msg_cert_verify.cpp2
-rw-r--r--src/lib/tls/msg_certificate.cpp2
-rw-r--r--src/lib/tls/msg_client_hello.cpp6
-rw-r--r--src/lib/tls/msg_client_kex.cpp11
-rw-r--r--src/lib/tls/msg_server_hello.cpp14
-rw-r--r--src/lib/tls/msg_server_kex.cpp2
-rw-r--r--src/lib/tls/tls_channel.cpp6
-rw-r--r--src/lib/tls/tls_ciphersuite.cpp7
-rw-r--r--src/lib/tls/tls_ciphersuite.h5
-rw-r--r--src/lib/tls/tls_client.cpp3
-rw-r--r--src/lib/tls/tls_extensions.cpp16
-rw-r--r--src/lib/tls/tls_extensions.h22
-rw-r--r--src/lib/tls/tls_messages.h12
-rw-r--r--src/lib/tls/tls_policy.cpp103
-rw-r--r--src/lib/tls/tls_policy.h121
-rw-r--r--src/lib/tls/tls_record.cpp268
-rw-r--r--src/lib/tls/tls_record.h7
-rw-r--r--src/lib/tls/tls_server.cpp17
-rw-r--r--src/lib/tls/tls_session.cpp4
-rw-r--r--src/lib/tls/tls_session.h9
-rw-r--r--src/tests/unit_tls.cpp69
22 files changed, 597 insertions, 132 deletions
diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp
index 2496f5508..7fc38cf31 100644
--- a/src/cli/tls_server.cpp
+++ b/src/cli/tls_server.cpp
@@ -33,7 +33,7 @@ namespace Botan_CLI {
class TLS_Server final : public Command
{
public:
- TLS_Server() : Command("tls_server cert key --port=443 --type=tcp") {}
+ TLS_Server() : Command("tls_server cert key --port=443 --type=tcp --policy=") {}
void go() override
{
@@ -47,7 +47,24 @@ class TLS_Server final : public Command
const bool is_tcp = (transport == "tcp");
- Botan::TLS::Policy policy; // TODO read policy from file
+ std::unique_ptr<Botan::TLS::Policy> policy;
+ const std::string policy_file = get_arg("policy");
+ std::filebuf fb;
+ if(policy_file.size() > 0)
+ {
+ std::ifstream policy_stream(policy_file);
+ if(!policy_stream.good())
+ {
+ error_output() << "Failed reading policy file\n";
+ return;
+ }
+ policy.reset(new Botan::TLS::Text_Policy(policy_stream));
+ }
+
+ if(!policy)
+ {
+ policy.reset(new Botan::TLS::Policy);
+ }
Botan::TLS::Session_Manager_In_Memory session_manager(rng()); // TODO sqlite3
@@ -112,7 +129,7 @@ class TLS_Server final : public Command
std::bind(&TLS_Server::handshake_complete, this, _1),
session_manager,
creds,
- policy,
+ *policy,
rng(),
protocol_chooser,
!is_tcp);
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
diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp
index f0caa4aef..33c6c245e 100644
--- a/src/tests/unit_tls.cpp
+++ b/src/tests/unit_tls.cpp
@@ -172,7 +172,8 @@ void alert_cb_with_data(Botan::TLS::Alert, const byte[], size_t)
Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
Botan::Credentials_Manager& creds,
- Botan::TLS::Policy& policy)
+ Botan::TLS::Policy& client_policy,
+ Botan::TLS::Policy& server_policy )
{
Botan::RandomNumberGenerator& rng = Test::rng();
@@ -236,7 +237,7 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
new Botan::TLS::Server(*server_cb,
server_sessions,
creds,
- policy,
+ server_policy,
rng,
false));
@@ -251,7 +252,7 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
new Botan::TLS::Client(*client_cb,
client_sessions,
creds,
- policy,
+ client_policy,
rng,
Botan::TLS::Server_Information("server.example.com"),
offer_version,
@@ -279,7 +280,7 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
handshake_complete,
server_sessions,
creds,
- policy,
+ server_policy,
rng,
next_protocol_chooser,
false));
@@ -292,7 +293,7 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
handshake_complete,
client_sessions,
creds,
- policy,
+ server_policy,
rng,
Botan::TLS::Server_Information("server.example.com"),
offer_version,
@@ -454,9 +455,17 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
return result;
}
+Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
+ Botan::Credentials_Manager& creds,
+ Botan::TLS::Policy& policy )
+ {
+ return test_tls_handshake(offer_version, creds, policy, policy);
+ }
+
Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
Botan::Credentials_Manager& creds,
- Botan::TLS::Policy& policy)
+ Botan::TLS::Policy& client_policy,
+ Botan::TLS::Policy& server_policy )
{
BOTAN_ASSERT(offer_version.is_datagram_protocol(), "Test is for datagram version");
@@ -518,18 +527,18 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
// TLS::Server object constructed by new constructor using virtual callback interface.
std::unique_ptr<Botan::TLS::Server> server(
new Botan::TLS::Server(*server_cb,
- server_sessions,
- creds,
- policy,
- rng,
- true));
+ server_sessions,
+ creds,
+ server_policy,
+ rng,
+ true));
// TLS::Client object constructed by new constructor using virtual callback interface.
std::unique_ptr<Botan::TLS::Client> client(
new Botan::TLS::Client(*client_cb,
client_sessions,
creds,
- policy,
+ client_policy,
rng,
Botan::TLS::Server_Information("server.example.com"),
offer_version,
@@ -556,7 +565,7 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
handshake_complete,
server_sessions,
creds,
- policy,
+ server_policy,
rng,
next_protocol_chooser,
true));
@@ -569,7 +578,7 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
handshake_complete,
client_sessions,
creds,
- policy,
+ client_policy,
rng,
Botan::TLS::Server_Information("server.example.com"),
offer_version,
@@ -593,11 +602,9 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
if(client->is_active() && client_sent.empty())
{
- // Choose a len between 1 and 511 and send random chunks:
+ // Choose a len between 1 and 511, todo use random chunks
const size_t c_len = 1 + rng.next_byte() + rng.next_byte();
client_sent = unlock(rng.random_vec(c_len));
-
- // TODO send multiple parts
client->send(client_sent);
}
@@ -749,6 +756,13 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
return result;
}
+Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
+ Botan::Credentials_Manager& creds,
+ Botan::TLS::Policy& policy)
+ {
+ return test_dtls_handshake(offer_version, creds, policy, policy);
+ }
+
class Test_Policy : public Botan::TLS::Text_Policy
{
public:
@@ -758,6 +772,8 @@ class Test_Policy : public Botan::TLS::Text_Policy
size_t dtls_initial_timeout() const override { return 1; }
size_t dtls_maximum_timeout() const override { return 8; }
+
+ size_t minimum_rsa_bits() const { return 1024; }
};
@@ -827,6 +843,25 @@ class TLS_Unit_Tests : public Test
results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds, policy));
results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V12, *basic_creds, policy));
+ policy.set("negotiate_encrypt_then_mac", "false");
+ policy.set("key_exchange_methods", "ECDH");
+ policy.set("ciphers", "AES-128");
+ Test_Policy server_policy;
+ server_policy.set("key_exchange_methods", "ECDH");
+ server_policy.set("ciphers", "AES-128");
+ server_policy.set("negotiate_encrypt_then_mac", "true");
+ results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V10, *basic_creds, policy, server_policy));
+ results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V11, *basic_creds, policy, server_policy));
+ results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds, policy, server_policy));
+ results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V10, *basic_creds, policy, server_policy));
+ results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V12, *basic_creds, policy, server_policy));
+
+ policy.set("negotiate_encrypt_then_mac", "true");
+ policy.set("ciphers", "AES-128/GCM");
+ server_policy.set("ciphers", "AES-128/GCM");
+ results.push_back(test_tls_handshake(Botan::TLS::Protocol_Version::TLS_V12, *basic_creds, policy, server_policy));
+ results.push_back(test_dtls_handshake(Botan::TLS::Protocol_Version::DTLS_V12, *basic_creds, policy, server_policy));
+
return results;
}