diff options
author | lloyd <[email protected]> | 2012-04-25 13:48:08 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2012-04-25 13:48:08 +0000 |
commit | b72a44475d06263e1492f8913310b5f29515cba6 (patch) | |
tree | 680752dbd43999cea16851b9c196046d9e5fbd7f /src/tls | |
parent | edca5f211722ea6b9d99b8b5fce4603a1b9b422d (diff) | |
parent | f14a9fdee7902ba1a4c962cfbabe29d5146e7c55 (diff) |
propagate from branch 'net.randombit.botan.tls-state-machine' (head a4741cd07f50a9e1b29b0dd97c6fb8697c038ade)
to branch 'net.randombit.botan.cxx11' (head 116e5ff139c07000be431e07d3472cc8f3919b91)
Diffstat (limited to 'src/tls')
36 files changed, 1264 insertions, 788 deletions
diff --git a/src/tls/c_hello.cpp b/src/tls/c_hello.cpp index b08e1abe2..056a7550f 100644 --- a/src/tls/c_hello.cpp +++ b/src/tls/c_hello.cpp @@ -11,19 +11,20 @@ #include <botan/internal/tls_extensions.h> #include <botan/tls_record.h> #include <botan/internal/stl_util.h> -#include <chrono> +#include <botan/time.h> namespace Botan { namespace TLS { +enum { + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF +}; + MemoryVector<byte> make_hello_random(RandomNumberGenerator& rng) { MemoryVector<byte> buf(32); - - const u32bit time32 = static_cast<u32bit>( - std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())); - + const u32bit time32 = system_time(); store_be(time32, buf); rng.randomize(&buf[4], buf.size() - 4); return buf; @@ -67,7 +68,7 @@ Client_Hello::Client_Hello(Record_Writer& writer, const std::string& srp_identifier) : m_version(policy.pref_version()), m_random(make_hello_random(rng)), - m_suites(policy.ciphersuite_list((srp_identifier != ""))), + m_suites(ciphersuite_list(policy, (srp_identifier != ""))), m_comp_methods(policy.compression()), m_hostname(hostname), m_srp_identifier(srp_identifier), @@ -76,7 +77,9 @@ Client_Hello::Client_Hello(Record_Writer& writer, m_secure_renegotiation(true), m_renegotiation_info(reneg_info), m_supported_curves(policy.allowed_ecc_curves()), - m_supports_session_ticket(true) + m_supports_session_ticket(true), + m_supports_heartbeats(true), + m_peer_can_send_heartbeats(true) { std::vector<std::string> hashes = policy.allowed_hashes(); std::vector<std::string> sigs = policy.allowed_signature_methods(); @@ -95,21 +98,25 @@ Client_Hello::Client_Hello(Record_Writer& writer, Handshake_Hash& hash, const Policy& policy, RandomNumberGenerator& rng, + const MemoryRegion<byte>& reneg_info, const Session& session, bool next_protocol) : m_version(session.version()), m_session_id(session.session_id()), m_random(make_hello_random(rng)), - m_suites(policy.ciphersuite_list(session.srp_identifier() != "")), + m_suites(ciphersuite_list(policy, (session.srp_identifier() != ""))), m_comp_methods(policy.compression()), m_hostname(session.sni_hostname()), m_srp_identifier(session.srp_identifier()), m_next_protocol(next_protocol), m_fragment_size(session.fragment_size()), m_secure_renegotiation(session.secure_renegotiation()), + m_renegotiation_info(reneg_info), m_supported_curves(policy.allowed_ecc_curves()), m_supports_session_ticket(true), - m_session_ticket(session.session_ticket()) + m_session_ticket(session.session_ticket()), + m_supports_heartbeats(true), + m_peer_can_send_heartbeats(true) { if(!value_exists(m_suites, session.ciphersuite_code())) m_suites.push_back(session.ciphersuite_code()); @@ -135,6 +142,8 @@ Client_Hello::Client_Hello(const MemoryRegion<byte>& buf, Handshake_Type type) m_next_protocol = false; m_secure_renegotiation = false; m_supports_session_ticket = false; + m_supports_heartbeats = false; + m_peer_can_send_heartbeats = false; m_fragment_size = 0; if(type == CLIENT_HELLO) @@ -167,28 +176,23 @@ MemoryVector<byte> Client_Hello::serialize() const Extensions extensions; - // Initial handshake - if(m_renegotiation_info.empty()) - { + if(m_secure_renegotiation) extensions.add(new Renegotation_Extension(m_renegotiation_info)); - extensions.add(new Server_Name_Indicator(m_hostname)); - extensions.add(new SRP_Identifier(m_srp_identifier)); - extensions.add(new Supported_Elliptic_Curves(m_supported_curves)); - if(m_version >= Protocol_Version::TLS_V12) - extensions.add(new Signature_Algorithms(m_supported_algos)); + extensions.add(new Session_Ticket(m_session_ticket)); - if(m_next_protocol) - extensions.add(new Next_Protocol_Notification()); + extensions.add(new Server_Name_Indicator(m_hostname)); + extensions.add(new SRP_Identifier(m_srp_identifier)); - extensions.add(new Session_Ticket(m_session_ticket)); - } - else - { - // renegotiation - extensions.add(new Renegotation_Extension(m_renegotiation_info)); - extensions.add(new Session_Ticket(m_session_ticket)); - } + extensions.add(new Supported_Elliptic_Curves(m_supported_curves)); + + if(m_version >= Protocol_Version::TLS_V12) + extensions.add(new Signature_Algorithms(m_supported_algos)); + + extensions.add(new Heartbeat_Support_Indicator(true)); + + if(m_renegotiation_info.empty() && m_next_protocol) + extensions.add(new Next_Protocol_Notification()); buf += extensions.serialize(); @@ -337,6 +341,12 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf) m_session_ticket = ticket->contents(); } + if(Heartbeat_Support_Indicator* hb = extensions.get<Heartbeat_Support_Indicator>()) + { + m_supports_heartbeats = true; + m_peer_can_send_heartbeats = hb->peer_allowed_to_send(); + } + if(Renegotation_Extension* reneg = extensions.get<Renegotation_Extension>()) { // checked by TLS_Client / TLS_Server as they know the handshake state diff --git a/src/tls/c_kex.cpp b/src/tls/c_kex.cpp index 0e7eba23a..13925a482 100644 --- a/src/tls/c_kex.cpp +++ b/src/tls/c_kex.cpp @@ -15,6 +15,7 @@ #include <botan/dh.h> #include <botan/ecdh.h> #include <botan/rsa.h> +#include <botan/srp6.h> #include <botan/rng.h> #include <botan/loadstor.h> #include <memory> @@ -50,6 +51,7 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, Handshake_State* state, Credentials_Manager& creds, const std::vector<X509_Certificate>& peer_certs, + const std::string& hostname, RandomNumberGenerator& rng) { const std::string kex_algo = state->suite.kex_algo(); @@ -147,8 +149,7 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, const std::string name = Supported_Elliptic_Curves::curve_id_to_name(curve_id); if(name == "") - throw Decoding_Error("Server sent unknown named curve " + - std::to_string(curve_id)); + throw Decoding_Error("Server sent unknown named curve " + to_string(curve_id)); EC_Group group(name); @@ -172,6 +173,33 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, append_tls_length_value(key_material, priv_key.public_value(), 1); } + else if(kex_algo == "SRP_SHA") + { + const BigInt N = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + const BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + MemoryVector<byte> salt = reader.get_range<byte>(1, 1, 255); + const BigInt B = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + + const std::string srp_group = srp6_group_identifier(N, g); + + const std::string srp_identifier = + creds.srp_identifier("tls-client", hostname); + + const std::string srp_password = + creds.srp_password("tls-client", hostname, srp_identifier); + + std::pair<BigInt, SymmetricKey> srp_vals = + srp6_client_agree(srp_identifier, + srp_password, + srp_group, + "SHA-1", + salt, + B, + rng); + + append_tls_length_value(key_material, BigInt::encode(srp_vals.first), 2); + pre_master = srp_vals.second.bits_of(); + } else { throw Internal_Error("Client_Key_Exchange: Unknown kex " + @@ -188,7 +216,7 @@ Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, if(peer_certs.empty()) throw Internal_Error("No certificate and no server key exchange"); - std::unique_ptr<Public_Key> pub_key(peer_certs[0].subject_public_key()); + std::auto_ptr<Public_Key> pub_key(peer_certs[0].subject_public_key()); if(const RSA_PublicKey* rsa_pub = dynamic_cast<const RSA_PublicKey*>(pub_key.get())) { @@ -302,6 +330,12 @@ Client_Key_Exchange::Client_Key_Exchange(const MemoryRegion<byte>& contents, append_tls_length_value(pre_master, zeros, 2); append_tls_length_value(pre_master, psk.bits_of(), 2); } + else if(kex_algo == "SRP_SHA") + { + SRP6_Server_Session& srp = state->server_kex->server_srp_params(); + + pre_master = srp.step2(BigInt::decode(reader.get_range<byte>(2, 0, 65535))).bits_of(); + } else if(kex_algo == "DH" || kex_algo == "DHE_PSK" || kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") { diff --git a/src/tls/cert_req.cpp b/src/tls/cert_req.cpp index df70cc43d..1b686c1c4 100644 --- a/src/tls/cert_req.cpp +++ b/src/tls/cert_req.cpp @@ -122,7 +122,7 @@ Certificate_Req::Certificate_Req(const MemoryRegion<byte>& buf, m_supported_algos.push_back(std::make_pair("SHA-1", "ECDSA")); } - u16bit purported_size = reader.get_u16bit(); + const u16bit purported_size = reader.get_u16bit(); if(reader.remaining_bytes() != purported_size) throw Decoding_Error("Inconsistent length in certificate request"); @@ -153,18 +153,20 @@ MemoryVector<byte> Certificate_Req::serialize() const append_tls_length_value(buf, cert_types, 1); if(!m_supported_algos.empty()) - { buf += Signature_Algorithms(m_supported_algos).serialize(); - } + + MemoryVector<byte> encoded_names; for(size_t i = 0; i != names.size(); ++i) { DER_Encoder encoder; encoder.encode(names[i]); - append_tls_length_value(buf, encoder.get_contents(), 2); + append_tls_length_value(encoded_names, encoder.get_contents(), 2); } + append_tls_length_value(buf, encoded_names, 2); + return buf; } diff --git a/src/tls/info.txt b/src/tls/info.txt index b19eedb20..3407aba32 100644 --- a/src/tls/info.txt +++ b/src/tls/info.txt @@ -1,5 +1,7 @@ define TLS +load_on auto + <comment> The TLS code is complex, new, and not yet reviewed, there may be serious bugs or security issues. @@ -8,6 +10,7 @@ serious bugs or security issues. <header:public> tls_alert.h tls_channel.h +tls_ciphersuite.h tls_client.h tls_exceptn.h tls_magic.h @@ -16,7 +19,6 @@ tls_record.h tls_server.h tls_session.h tls_session_manager.h -tls_ciphersuite.h tls_version.h </header:public> @@ -25,13 +27,13 @@ tls_extensions.h tls_handshake_hash.h tls_handshake_reader.h tls_handshake_state.h +tls_heartbeats.h tls_messages.h tls_reader.h tls_session_key.h </header:internal> <source> -tls_alert.cpp c_hello.cpp c_kex.cpp cert_req.cpp @@ -44,18 +46,21 @@ rec_wri.cpp s_hello.cpp s_kex.cpp session_ticket.cpp +tls_alert.cpp tls_channel.cpp +tls_ciphersuite.cpp tls_client.cpp tls_extensions.cpp tls_handshake_hash.cpp tls_handshake_reader.cpp tls_handshake_state.cpp +tls_heartbeats.cpp tls_policy.cpp tls_server.cpp tls_session.cpp tls_session_key.cpp tls_session_manager.cpp -tls_ciphersuite.cpp +tls_suite_info.cpp tls_version.cpp </source> @@ -63,6 +68,8 @@ tls_version.cpp aes arc4 asn1 +camellia +cbc credentials des dh @@ -80,6 +87,7 @@ prf_tls rng rsa seed +srp6 sha1 sha2_32 ssl3mac diff --git a/src/tls/rec_read.cpp b/src/tls/rec_read.cpp index bd6ae5af9..5d46ec1fa 100644 --- a/src/tls/rec_read.cpp +++ b/src/tls/rec_read.cpp @@ -216,10 +216,11 @@ size_t Record_Reader::add_input(const byte input_array[], size_t input_sz, if(m_readbuf[0] != CHANGE_CIPHER_SPEC && m_readbuf[0] != ALERT && m_readbuf[0] != HANDSHAKE && - m_readbuf[0] != APPLICATION_DATA) + m_readbuf[0] != APPLICATION_DATA && + m_readbuf[0] != HEARTBEAT) { throw Unexpected_Message( - "Unknown record type " + std::to_string(m_readbuf[0]) + " from counterparty"); + "Unknown record type " + to_string(m_readbuf[0]) + " from counterparty"); } const size_t record_len = make_u16bit(m_readbuf[3], m_readbuf[4]); diff --git a/src/tls/s_hello.cpp b/src/tls/s_hello.cpp index 7da9fdc57..1244dd2d8 100644 --- a/src/tls/s_hello.cpp +++ b/src/tls/s_hello.cpp @@ -21,47 +21,6 @@ namespace TLS { */ Server_Hello::Server_Hello(Record_Writer& writer, Handshake_Hash& hash, - Protocol_Version version, - const Client_Hello& c_hello, - const std::vector<std::string>& available_cert_types, - const Policy& policy, - bool have_session_ticket_key, - bool client_has_secure_renegotiation, - const MemoryRegion<byte>& reneg_info, - bool client_has_npn, - const std::vector<std::string>& next_protocols, - RandomNumberGenerator& rng) : - s_version(version), - m_session_id(rng.random_vec(32)), - s_random(make_hello_random(rng)), - m_fragment_size(c_hello.fragment_size()), - m_secure_renegotiation(client_has_secure_renegotiation), - m_renegotiation_info(reneg_info), - m_next_protocol(client_has_npn), - m_next_protocols(next_protocols), - m_supports_session_ticket(have_session_ticket_key && - c_hello.supports_session_ticket()) - { - suite = policy.choose_suite( - c_hello.ciphersuites(), - available_cert_types, - policy.choose_curve(c_hello.supported_ecc_curves()) != "", - false); - - if(suite == 0) - throw TLS_Exception(Alert::HANDSHAKE_FAILURE, - "Can't agree on a ciphersuite with client"); - - comp_method = policy.choose_compression(c_hello.compression_methods()); - - hash.update(writer.send(*this)); - } - -/* -* Create a new Server Hello message -*/ -Server_Hello::Server_Hello(Record_Writer& writer, - Handshake_Hash& hash, const MemoryRegion<byte>& session_id, Protocol_Version ver, u16bit ciphersuite, @@ -69,21 +28,24 @@ Server_Hello::Server_Hello(Record_Writer& writer, size_t max_fragment_size, bool client_has_secure_renegotiation, const MemoryRegion<byte>& reneg_info, - bool client_supports_session_tickets, + bool offer_session_ticket, bool client_has_npn, const std::vector<std::string>& next_protocols, + bool client_has_heartbeat, RandomNumberGenerator& rng) : - s_version(ver), + m_version(ver), m_session_id(session_id), - s_random(make_hello_random(rng)), - suite(ciphersuite), - comp_method(compression), + m_random(make_hello_random(rng)), + m_ciphersuite(ciphersuite), + m_comp_method(compression), m_fragment_size(max_fragment_size), m_secure_renegotiation(client_has_secure_renegotiation), m_renegotiation_info(reneg_info), m_next_protocol(client_has_npn), m_next_protocols(next_protocols), - m_supports_session_ticket(client_supports_session_tickets) + m_supports_session_ticket(offer_session_ticket), + m_supports_heartbeats(client_has_heartbeat), + m_peer_can_send_heartbeats(true) { hash.update(writer.send(*this)); } @@ -105,24 +67,24 @@ Server_Hello::Server_Hello(const MemoryRegion<byte>& buf) const byte major_version = reader.get_byte(); const byte minor_version = reader.get_byte(); - s_version = Protocol_Version(major_version, minor_version); + m_version = Protocol_Version(major_version, minor_version); - if(s_version != Protocol_Version::SSL_V3 && - s_version != Protocol_Version::TLS_V10 && - s_version != Protocol_Version::TLS_V11 && - s_version != Protocol_Version::TLS_V12) + if(m_version != Protocol_Version::SSL_V3 && + m_version != Protocol_Version::TLS_V10 && + m_version != Protocol_Version::TLS_V11 && + m_version != Protocol_Version::TLS_V12) { throw TLS_Exception(Alert::PROTOCOL_VERSION, "Server_Hello: Unsupported server version"); } - s_random = reader.get_fixed<byte>(32); + m_random = reader.get_fixed<byte>(32); m_session_id = reader.get_range<byte>(1, 0, 32); - suite = reader.get_u16bit(); + m_ciphersuite = reader.get_u16bit(); - comp_method = reader.get_byte(); + m_comp_method = reader.get_byte(); Extensions extensions(reader); @@ -145,6 +107,12 @@ Server_Hello::Server_Hello(const MemoryRegion<byte>& buf) throw Decoding_Error("TLS server sent non-empty session ticket extension"); m_supports_session_ticket = true; } + + if(Heartbeat_Support_Indicator* hb = extensions.get<Heartbeat_Support_Indicator>()) + { + m_supports_heartbeats = true; + m_peer_can_send_heartbeats = hb->peer_allowed_to_send(); + } } /* @@ -154,19 +122,22 @@ MemoryVector<byte> Server_Hello::serialize() const { MemoryVector<byte> buf; - buf.push_back(s_version.major_version()); - buf.push_back(s_version.minor_version()); - buf += s_random; + buf.push_back(m_version.major_version()); + buf.push_back(m_version.minor_version()); + buf += m_random; append_tls_length_value(buf, m_session_id, 1); - buf.push_back(get_byte(0, suite)); - buf.push_back(get_byte(1, suite)); + buf.push_back(get_byte(0, m_ciphersuite)); + buf.push_back(get_byte(1, m_ciphersuite)); - buf.push_back(comp_method); + buf.push_back(m_comp_method); Extensions extensions; + if(m_supports_heartbeats) + extensions.add(new Heartbeat_Support_Indicator(m_peer_can_send_heartbeats)); + if(m_secure_renegotiation) extensions.add(new Renegotation_Extension(m_renegotiation_info)); diff --git a/src/tls/s_kex.cpp b/src/tls/s_kex.cpp index 0890cac49..95518aa32 100644 --- a/src/tls/s_kex.cpp +++ b/src/tls/s_kex.cpp @@ -16,6 +16,7 @@ #include <botan/dh.h> #include <botan/ecdh.h> #include <botan/rsa.h> +#include <botan/srp6.h> #include <botan/oids.h> #include <memory> @@ -32,22 +33,22 @@ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, Credentials_Manager& creds, RandomNumberGenerator& rng, const Private_Key* signing_key) : - m_kex_key(0) + m_kex_key(0), m_srp_params(0) { + const std::string hostname = state->client_hello->sni_hostname(); const std::string kex_algo = state->suite.kex_algo(); if(kex_algo == "PSK" || kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") { std::string identity_hint = - creds.psk_identity_hint("tls-server", - state->client_hello->sni_hostname()); + creds.psk_identity_hint("tls-server", hostname); append_tls_length_value(m_params, identity_hint, 2); } if(kex_algo == "DH" || kex_algo == "DHE_PSK") { - std::unique_ptr<DH_PrivateKey> dh(new DH_PrivateKey(rng, policy.dh_group())); + std::auto_ptr<DH_PrivateKey> dh(new DH_PrivateKey(rng, policy.dh_group())); append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_p()), 2); append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_g()), 2); @@ -70,7 +71,7 @@ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, EC_Group ec_group(curve_name); - std::unique_ptr<ECDH_PrivateKey> ecdh(new ECDH_PrivateKey(rng, ec_group)); + std::auto_ptr<ECDH_PrivateKey> ecdh(new ECDH_PrivateKey(rng, ec_group)); const std::string ecdh_domain_oid = ecdh->domain().get_oid(); const std::string domain = OIDS::lookup(OID(ecdh_domain_oid)); @@ -88,6 +89,35 @@ Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, m_kex_key = ecdh.release(); } + else if(kex_algo == "SRP_SHA") + { + const std::string srp_identifier = state->client_hello->srp_identifier(); + + std::string group_id; + BigInt v; + MemoryVector<byte> salt; + + const bool found = creds.srp_verifier("tls-server", hostname, + srp_identifier, + group_id, v, salt, + policy.hide_unknown_users()); + + if(!found) + throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, + "Unknown SRP user " + srp_identifier); + + m_srp_params = new SRP6_Server_Session; + + BigInt B = m_srp_params->step1(v, group_id, + "SHA-1", rng); + + DL_Group group(group_id); + + append_tls_length_value(m_params, BigInt::encode(group.get_p()), 2); + append_tls_length_value(m_params, BigInt::encode(group.get_g()), 2); + append_tls_length_value(m_params, salt, 1); + append_tls_length_value(m_params, BigInt::encode(B), 2); + } else if(kex_algo != "PSK") throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_algo); @@ -116,7 +146,7 @@ Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion<byte>& buf, const std::string& kex_algo, const std::string& sig_algo, Protocol_Version version) : - m_kex_key(0) + m_kex_key(0), m_srp_params(0) { if(buf.size() < 6) throw Decoding_Error("Server_Key_Exchange: Packet corrupted"); @@ -160,13 +190,27 @@ Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion<byte>& buf, if(name == "") throw Decoding_Error("Server_Key_Exchange: Server sent unknown named curve " + - std::to_string(curve_id)); + to_string(curve_id)); m_params.push_back(curve_type); m_params.push_back(get_byte(0, curve_id)); m_params.push_back(get_byte(1, curve_id)); append_tls_length_value(m_params, ecdh_key, 1); } + else if(kex_algo == "SRP_SHA") + { + // 2 bigints (N,g) then salt, then server B + + const BigInt N = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + const BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + MemoryVector<byte> salt = reader.get_range<byte>(1, 1, 255); + const BigInt B = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + + append_tls_length_value(m_params, BigInt::encode(N), 2); + append_tls_length_value(m_params, BigInt::encode(g), 2); + append_tls_length_value(m_params, salt, 1); + append_tls_length_value(m_params, BigInt::encode(B), 2); + } else if(kex_algo != "PSK") throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + kex_algo); @@ -182,6 +226,12 @@ Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion<byte>& buf, } } +Server_Key_Exchange::~Server_Key_Exchange() + { + delete m_kex_key; + delete m_srp_params; + } + /** * Serialize a Server Key Exchange message @@ -211,7 +261,7 @@ MemoryVector<byte> Server_Key_Exchange::serialize() const bool Server_Key_Exchange::verify(const X509_Certificate& cert, Handshake_State* state) const { - std::unique_ptr<Public_Key> key(cert.subject_public_key()); + std::auto_ptr<Public_Key> key(cert.subject_public_key()); std::pair<std::string, Signature_Format> format = state->understand_sig_format(key.get(), m_hash_algo, m_sig_algo, false); @@ -230,6 +280,15 @@ const Private_Key& Server_Key_Exchange::server_kex_key() const BOTAN_ASSERT(m_kex_key, "Key is non-NULL"); return *m_kex_key; } + +// Only valid for SRP negotiation +SRP6_Server_Session& Server_Key_Exchange::server_srp_params() + { + BOTAN_ASSERT(m_srp_params, "SRP params are non-NULL"); + return *m_srp_params; + } +} + } } diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp index 175adad6c..aa9385d70 100644 --- a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp +++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp @@ -9,9 +9,9 @@ #include <botan/internal/assert.h> #include <botan/lookup.h> #include <botan/hex.h> +#include <botan/time.h> #include <botan/loadstor.h> #include <memory> -#include <chrono> #include <sqlite3.h> @@ -86,6 +86,38 @@ class sqlite3_statement {} } + std::pair<const byte*, size_t> get_blob(int column) + { + BOTAN_ASSERT(sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB, + "Return value is a blob"); + + const void* session_blob = sqlite3_column_blob(m_stmt, column); + const int session_blob_size = sqlite3_column_bytes(m_stmt, column); + + BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative"); + + return std::make_pair(static_cast<const byte*>(session_blob), + static_cast<size_t>(session_blob_size)); + } + + size_t get_size_t(int column) + { + BOTAN_ASSERT(sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, + "Return count is an integer"); + + const int sessions_int = sqlite3_column_int(m_stmt, column); + + BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative"); + + return static_cast<size_t>(sessions_int); + } + + void spin() + { + while(sqlite3_step(m_stmt) == SQLITE_ROW) + {} + } + int step() { return sqlite3_step(m_stmt); @@ -146,7 +178,7 @@ Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase, RandomNumberGenerator& rng, const std::string& db_filename, size_t max_sessions, - std::chrono::seconds session_lifetime) : + u32bit session_lifetime) : m_rng(rng), m_max_sessions(max_sessions), m_session_lifetime(session_lifetime) @@ -309,7 +341,7 @@ void Session_Manager_SQLite::save(const Session& session) " values(?1, ?2, ?3, ?4, ?5)"); stmt.bind(1, hex_encode(session.session_id())); - stmt.bind(2, std::chrono::system_clock::to_time_t(session.start_time())); + stmt.bind(2, session.start_time()); stmt.bind(3, session.sni_hostname()); stmt.bind(4, 0); stmt.bind(5, session.encrypt(m_session_key, m_rng)); @@ -323,8 +355,7 @@ void Session_Manager_SQLite::prune_session_cache() { sqlite3_statement remove_expired(m_db, "delete from tls_sessions where session_start <= ?1"); - remove_expired.bind(1, std::chrono::system_clock::to_time_t( - std::chrono::system_clock::now() - m_session_lifetime)); + remove_expired.bind(1, system_time() - m_session_lifetime); remove_expired.spin(); diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h index 1d12b71cd..57e5a58f6 100644 --- a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h +++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h @@ -5,8 +5,8 @@ * Released under the terms of the Botan license */ -#ifndef TLS_SQLITE_SESSION_MANAGER_H__ -#define TLS_SQLITE_SESSION_MANAGER_H__ +#ifndef BOTAN_TLS_SQLITE_SESSION_MANAGER_H__ +#define BOTAN_TLS_SQLITE_SESSION_MANAGER_H__ #include <botan/tls_session_manager.h> #include <botan/rng.h> @@ -36,7 +36,7 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager RandomNumberGenerator& rng, const std::string& db_filename, size_t max_sessions = 1000, - std::chrono::seconds session_lifetime = std::chrono::seconds(7200)); + u32bit session_lifetime = 7200); ~Session_Manager_SQLite(); @@ -49,6 +49,8 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager void remove_entry(const MemoryRegion<byte>& session_id); void save(const Session& session_data); + + u32bit session_lifetime() const { return m_session_lifetime; } private: Session_Manager_SQLite(const Session_Manager_SQLite&); Session_Manager_SQLite& operator=(const Session_Manager_SQLite&); @@ -58,7 +60,7 @@ class BOTAN_DLL Session_Manager_SQLite : public Session_Manager SymmetricKey m_session_key; RandomNumberGenerator& m_rng; size_t m_max_sessions; - std::chrono::seconds m_session_lifetime; + u32bit m_session_lifetime; class sqlite3* m_db; }; diff --git a/src/tls/tls_alert.cpp b/src/tls/tls_alert.cpp index 30c51d4c8..9b37a282f 100644 --- a/src/tls/tls_alert.cpp +++ b/src/tls/tls_alert.cpp @@ -90,14 +90,22 @@ std::string Alert::type_string() const case UNSUPPORTED_EXTENSION: return "unsupported_extension"; + case CERTIFICATE_UNOBTAINABLE: + return "certificate_unobtainable"; case UNRECOGNIZED_NAME: return "unrecognized_name"; - + case BAD_CERTIFICATE_STATUS_RESPONSE: + return "bad_certificate_status_response"; + case BAD_CERTIFICATE_HASH_VALUE: + return "bad_certificate_hash_value"; case UNKNOWN_PSK_IDENTITY: return "unknown_psk_identity"; case NULL_ALERT: - return ""; + return "none"; + + case HEARTBEAT_PAYLOAD: + return "heartbeat_payload"; } /* @@ -106,7 +114,7 @@ std::string Alert::type_string() const * compiler can warn us that it is not included in the switch * statement. */ - return "unrecognized_alert_" + std::to_string(type()); + return "unrecognized_alert_" + to_string(type()); } diff --git a/src/tls/tls_alert.h b/src/tls/tls_alert.h index 0446a8c30..3dfff3d29 100644 --- a/src/tls/tls_alert.h +++ b/src/tls/tls_alert.h @@ -22,37 +22,40 @@ class BOTAN_DLL Alert { public: enum Type { - CLOSE_NOTIFY = 0, - UNEXPECTED_MESSAGE = 10, - BAD_RECORD_MAC = 20, - DECRYPTION_FAILED = 21, - RECORD_OVERFLOW = 22, - DECOMPRESSION_FAILURE = 30, - HANDSHAKE_FAILURE = 40, - NO_CERTIFICATE = 41, // SSLv3 only - BAD_CERTIFICATE = 42, - UNSUPPORTED_CERTIFICATE = 43, - CERTIFICATE_REVOKED = 44, - CERTIFICATE_EXPIRED = 45, - CERTIFICATE_UNKNOWN = 46, - ILLEGAL_PARAMETER = 47, - UNKNOWN_CA = 48, - ACCESS_DENIED = 49, - DECODE_ERROR = 50, - DECRYPT_ERROR = 51, - EXPORT_RESTRICTION = 60, - PROTOCOL_VERSION = 70, - INSUFFICIENT_SECURITY = 71, - INTERNAL_ERROR = 80, - USER_CANCELED = 90, - NO_RENEGOTIATION = 100, - - UNSUPPORTED_EXTENSION = 110, - UNRECOGNIZED_NAME = 112, - - UNKNOWN_PSK_IDENTITY = 115, - - NULL_ALERT = 255 + CLOSE_NOTIFY = 0, + UNEXPECTED_MESSAGE = 10, + BAD_RECORD_MAC = 20, + DECRYPTION_FAILED = 21, + RECORD_OVERFLOW = 22, + DECOMPRESSION_FAILURE = 30, + HANDSHAKE_FAILURE = 40, + NO_CERTIFICATE = 41, // SSLv3 only + BAD_CERTIFICATE = 42, + UNSUPPORTED_CERTIFICATE = 43, + CERTIFICATE_REVOKED = 44, + CERTIFICATE_EXPIRED = 45, + CERTIFICATE_UNKNOWN = 46, + ILLEGAL_PARAMETER = 47, + UNKNOWN_CA = 48, + ACCESS_DENIED = 49, + DECODE_ERROR = 50, + DECRYPT_ERROR = 51, + EXPORT_RESTRICTION = 60, + PROTOCOL_VERSION = 70, + INSUFFICIENT_SECURITY = 71, + INTERNAL_ERROR = 80, + USER_CANCELED = 90, + NO_RENEGOTIATION = 100, + UNSUPPORTED_EXTENSION = 110, + CERTIFICATE_UNOBTAINABLE = 111, + UNRECOGNIZED_NAME = 112, + BAD_CERTIFICATE_STATUS_RESPONSE = 113, + BAD_CERTIFICATE_HASH_VALUE = 114, + UNKNOWN_PSK_IDENTITY = 115, + + NULL_ALERT = 255, + + HEARTBEAT_PAYLOAD = 256 }; /** diff --git a/src/tls/tls_channel.cpp b/src/tls/tls_channel.cpp index 736b37654..86d9db8bd 100644 --- a/src/tls/tls_channel.cpp +++ b/src/tls/tls_channel.cpp @@ -8,6 +8,7 @@ #include <botan/tls_channel.h> #include <botan/internal/tls_handshake_state.h> #include <botan/internal/tls_messages.h> +#include <botan/internal/tls_heartbeats.h> #include <botan/internal/assert.h> #include <botan/loadstor.h> @@ -15,15 +16,17 @@ namespace Botan { namespace TLS { -Channel::Channel(std::function<void (const byte[], size_t)> socket_output_fn, - std::function<void (const byte[], size_t, Alert)> proc_fn, - std::function<bool (const Session&)> handshake_complete) : +Channel::Channel(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete) : proc_fn(proc_fn), handshake_fn(handshake_complete), writer(socket_output_fn), state(0), handshake_completed(false), - connection_closed(false) + connection_closed(false), + m_peer_supports_heartbeats(false), + m_heartbeat_sending_allowed(false) { } @@ -56,7 +59,30 @@ size_t Channel::received_data(const byte buf[], size_t buf_size) if(buf_size == 0 && needed != 0) return needed; // need more data to complete record - if(rec_type == APPLICATION_DATA) + if(rec_type == HANDSHAKE || rec_type == CHANGE_CIPHER_SPEC) + { + read_handshake(rec_type, record); + } + else if(rec_type == HEARTBEAT && m_peer_supports_heartbeats) + { + Heartbeat_Message heartbeat(record); + + const MemoryRegion<byte>& payload = heartbeat.payload(); + + if(heartbeat.is_request() && !state) + { + Heartbeat_Message response(Heartbeat_Message::RESPONSE, + payload, payload.size()); + + writer.send(HEARTBEAT, response.contents()); + } + else + { + // pass up to the application + proc_fn(&payload[0], payload.size(), Alert(Alert::HEARTBEAT_PAYLOAD)); + } + } + else if(rec_type == APPLICATION_DATA) { if(handshake_completed) { @@ -73,10 +99,6 @@ size_t Channel::received_data(const byte buf[], size_t buf_size) throw Unexpected_Message("Application data before handshake done"); } } - else if(rec_type == HANDSHAKE || rec_type == CHANGE_CIPHER_SPEC) - { - read_handshake(rec_type, record); - } else if(rec_type == ALERT) { Alert alert_msg(record); @@ -106,7 +128,7 @@ size_t Channel::received_data(const byte buf[], size_t buf_size) } else throw Unexpected_Message("Unknown TLS message type " + - std::to_string(rec_type) + " received"); + to_string(rec_type) + " received"); } return 0; // on a record boundary @@ -178,6 +200,20 @@ void Channel::read_handshake(byte rec_type, } } +void Channel::heartbeat(const byte payload[], size_t payload_size) + { + if(!is_active()) + throw std::runtime_error("Heartbeat cannot be sent on inactive TLS connection"); + + if(m_heartbeat_sending_allowed) + { + Heartbeat_Message heartbeat(Heartbeat_Message::REQUEST, + payload, payload_size); + + writer.send(HEARTBEAT, heartbeat.contents()); + } + } + void Channel::send(const byte buf[], size_t buf_size) { if(!is_active()) @@ -287,3 +323,5 @@ void Channel::Secure_Renegotiation_State::update(Finished* client_finished, } } + +} diff --git a/src/tls/tls_channel.h b/src/tls/tls_channel.h index 9ab89e5f7..257745d80 100644 --- a/src/tls/tls_channel.h +++ b/src/tls/tls_channel.h @@ -54,17 +54,31 @@ class BOTAN_DLL Channel /** * Attempt to renegotiate the session + * @param force_full_renegotiation if true, require a full renegotiation, + * otherwise allow session resumption */ - virtual void renegotiate() = 0; + virtual void renegotiate(bool force_full_renegotiation) = 0; + + /** + * Attempt to send a heartbeat message (if negotiated with counterparty) + * @param payload will be echoed back + * @param countents_size size of payload in bytes + */ + void heartbeat(const byte payload[], size_t payload_size); + + /** + * Attempt to send a heartbeat message (if negotiated with counterparty) + */ + void heartbeat() { heartbeat(0, 0); } /** * @return certificate chain of the peer (may be empty) */ std::vector<X509_Certificate> peer_cert_chain() const { return peer_certs; } - Channel(std::function<void (const byte[], size_t)> socket_output_fn, - std::function<void (const byte[], size_t, Alert)> proc_fn, - std::function<bool (const Session&)> handshake_complete); + Channel(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete); virtual ~Channel(); protected: @@ -85,8 +99,8 @@ class BOTAN_DLL Channel virtual void alert_notify(const Alert& alert) = 0; - std::function<void (const byte[], size_t, Alert)> proc_fn; - std::function<bool (const Session&)> handshake_fn; + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn; + std::tr1::function<bool (const Session&)> handshake_fn; Record_Writer writer; Record_Reader reader; @@ -130,6 +144,8 @@ class BOTAN_DLL Channel bool handshake_completed; bool connection_closed; + bool m_peer_supports_heartbeats; + bool m_heartbeat_sending_allowed; }; } diff --git a/src/tls/tls_ciphersuite.cpp b/src/tls/tls_ciphersuite.cpp index 89daaf679..798df0186 100644 --- a/src/tls/tls_ciphersuite.cpp +++ b/src/tls/tls_ciphersuite.cpp @@ -1,12 +1,11 @@ /* -* TLS Cipher Suites +* TLS Cipher Suite * (C) 2004-2010,2012 Jack Lloyd * * Released under the terms of the Botan license */ #include <botan/tls_ciphersuite.h> -#include <botan/tls_magic.h> #include <botan/parsing.h> #include <sstream> #include <stdexcept> @@ -15,246 +14,6 @@ namespace Botan { namespace TLS { -/** -* Convert an SSL/TLS ciphersuite to algorithm fields -*/ -Ciphersuite Ciphersuite::by_id(u16bit suite) - { - switch(static_cast<Ciphersuite_Code>(suite)) - { - // RSA ciphersuites - - case TLS_RSA_WITH_AES_128_CBC_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "AES-128", 16); - - case TLS_RSA_WITH_AES_256_CBC_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "AES-256", 32); - - case TLS_RSA_WITH_AES_128_CBC_SHA256: - return Ciphersuite("RSA", "RSA", "SHA-256", "AES-128", 16); - - case TLS_RSA_WITH_AES_256_CBC_SHA256: - return Ciphersuite("RSA", "RSA", "SHA-256", "AES-256", 32); - - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "3DES", 24); - - case TLS_RSA_WITH_RC4_128_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "ARC4", 16); - - case TLS_RSA_WITH_RC4_128_MD5: - return Ciphersuite("RSA", "RSA", "MD5", "ARC4", 16); - - case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "Camellia", 16); - - case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "Camellia", 32); - - case TLS_RSA_WITH_SEED_CBC_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "SEED", 16); - -#if defined(BOTAN_HAS_IDEA) - case TLS_RSA_WITH_IDEA_CBC_SHA: - return Ciphersuite("RSA", "RSA", "SHA-1", "IDEA", 16); -#endif - - // DH/DSS ciphersuites - - case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: - return Ciphersuite("DSA", "DH", "SHA-1", "AES-128", 16); - - case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: - return Ciphersuite("DSA", "DH", "SHA-1", "AES-256", 32); - - case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: - return Ciphersuite("DSA", "DH", "SHA-256", "AES-128", 16); - - case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: - return Ciphersuite("DSA", "DH", "SHA-256", "AES-256", 32); - - case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("DSA", "DH", "SHA-1", "3DES", 24); - - case TLS_DHE_DSS_WITH_RC4_128_SHA: - return Ciphersuite("DSA", "DH", "SHA-1", "ARC4", 16); - - case TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: - return Ciphersuite("DSA", "DH", "SHA-1", "Camellia", 16); - - case TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: - return Ciphersuite("DSA", "DH", "SHA-1", "Camellia", 32); - - case TLS_DHE_DSS_WITH_SEED_CBC_SHA: - return Ciphersuite("DSA", "DH", "SHA-1", "SEED", 16); - - // DH/RSA ciphersuites - - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: - return Ciphersuite("RSA", "DH", "SHA-1", "AES-128", 16); - - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: - return Ciphersuite("RSA", "DH", "SHA-1", "AES-256", 32); - - case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: - return Ciphersuite("RSA", "DH", "SHA-256", "AES-128", 16); - - case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: - return Ciphersuite("RSA", "DH", "SHA-256", "AES-256", 32); - - case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("RSA", "DH", "SHA-1", "3DES", 24); - - case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: - return Ciphersuite("RSA", "DH", "SHA-1", "Camellia", 16); - - case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: - return Ciphersuite("RSA", "DH", "SHA-1", "Camellia", 32); - - case TLS_DHE_RSA_WITH_SEED_CBC_SHA: - return Ciphersuite("RSA", "DH", "SHA-1", "SEED", 16); - - // ECDH/RSA ciphersuites - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: - return Ciphersuite("RSA", "ECDH", "SHA-1", "AES-128", 16); - - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: - return Ciphersuite("RSA", "ECDH", "SHA-1", "AES-256", 32); - - case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: - return Ciphersuite("RSA", "ECDH", "SHA-256", "AES-128", 16); - - case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: - return Ciphersuite("RSA", "ECDH", "SHA-384", "AES-256", 32); - - case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("RSA", "ECDH", "SHA-1", "3DES", 24); - - case TLS_ECDHE_RSA_WITH_RC4_128_SHA: - return Ciphersuite("RSA", "ECDH", "SHA-1", "ARC4", 16); - - // ECDH/ECDSA ciphersuites - - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: - return Ciphersuite("ECDSA", "ECDH", "SHA-1", "AES-128", 16); - - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: - return Ciphersuite("ECDSA", "ECDH", "SHA-1", "AES-256", 32); - - case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: - return Ciphersuite("ECDSA", "ECDH", "SHA-256", "AES-128", 16); - - case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: - return Ciphersuite("ECDSA", "ECDH", "SHA-384", "AES-256", 32); - - case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: - return Ciphersuite("ECDSA", "ECDH", "SHA-1", "ARC4", 16); - - case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("ECDSA", "ECDH", "SHA-1", "3DES", 24); - - // PSK ciphersuites - - case TLS_PSK_WITH_RC4_128_SHA: - return Ciphersuite("", "PSK", "SHA-1", "ARC4", 16); - - case TLS_PSK_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("", "PSK", "SHA-1", "3DES", 24); - - case TLS_PSK_WITH_AES_128_CBC_SHA: - return Ciphersuite("", "PSK", "SHA-1", "AES-128", 16); - - case TLS_PSK_WITH_AES_128_CBC_SHA256: - return Ciphersuite("", "PSK", "SHA-256", "AES-128", 16); - - case TLS_PSK_WITH_AES_256_CBC_SHA: - return Ciphersuite("", "PSK", "SHA-1", "AES-256", 32); - - case TLS_PSK_WITH_AES_256_CBC_SHA384: - return Ciphersuite("", "PSK", "SHA-384", "AES-256", 32); - - // PSK+DH ciphersuites - - case TLS_DHE_PSK_WITH_RC4_128_SHA: - return Ciphersuite("", "DHE_PSK", "SHA-1", "ARC4", 16); - - case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("", "DHE_PSK", "SHA-1", "3DES", 24); - - case TLS_DHE_PSK_WITH_AES_128_CBC_SHA: - return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-128", 16); - - case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: - return Ciphersuite("", "DHE_PSK", "SHA-256", "AES-128", 16); - - case TLS_DHE_PSK_WITH_AES_256_CBC_SHA: - return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-256", 32); - - case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: - return Ciphersuite("", "DHE_PSK", "SHA-384", "AES-256", 32); - - // PSK+ECDH ciphersuites - - case TLS_ECDHE_PSK_WITH_RC4_128_SHA: - return Ciphersuite("", "ECDHE_PSK", "SHA-1", "ARC4", 16); - - case TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("", "ECDHE_PSK", "SHA-1", "3DES", 24); - - case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: - return Ciphersuite("", "ECDHE_PSK", "SHA-1", "AES-128", 16); - - case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: - return Ciphersuite("", "ECDHE_PSK", "SHA-256", "AES-128", 16); - - case TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: - return Ciphersuite("", "ECDHE_PSK", "SHA-1", "AES-256", 32); - - case TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: - return Ciphersuite("", "ECDHE_PSK", "SHA-384", "AES-256", 32); - - // SRP ciphersuites - - case TLS_SRP_SHA_WITH_AES_128_CBC_SHA: - return Ciphersuite("", "SRP", "SHA-1", "AES-128", 16); - - case TLS_SRP_SHA_WITH_AES_256_CBC_SHA: - return Ciphersuite("", "SRP", "SHA-1", "AES-256", 32); - - case TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("", "SRP", "SHA-1", "3DES", 24); - - // SRP/RSA ciphersuites - - case TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: - return Ciphersuite("RSA", "SRP", "SHA-1", "AES-128", 16); - - case TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: - return Ciphersuite("RSA", "SRP", "SHA-1", "AES-256", 32); - - case TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("RSA", "SRP", "SHA-1", "3DES", 24); - - // SRP/DSA ciphersuites - - case TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: - return Ciphersuite("DSA", "SRP", "SHA-1", "AES-128", 16); - - case TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: - return Ciphersuite("DSA", "SRP", "SHA-1", "AES-256", 32); - - case TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: - return Ciphersuite("DSA", "SRP", "SHA-1", "3DES", 24); - - // Signaling ciphersuite values - - case TLS_EMPTY_RENEGOTIATION_INFO_SCSV: - return Ciphersuite(); - } - - return Ciphersuite(); // some unknown ciphersuite - } - Ciphersuite Ciphersuite::by_name(const std::string& name) { for(size_t i = 0; i != 65536; ++i) @@ -271,6 +30,18 @@ Ciphersuite Ciphersuite::by_name(const std::string& name) return Ciphersuite(); // some unknown ciphersuite } +bool Ciphersuite::psk_ciphersuite() const + { + return (kex_algo() == "PSK" || + kex_algo() == "DHE_PSK" || + kex_algo() == "ECDHE_PSK"); + } + +bool Ciphersuite::ecc_ciphersuite() const + { + return (kex_algo() == "ECDH" || sig_algo() == "ECDSA"); + } + std::string Ciphersuite::to_string() const { if(m_cipher_keylen == 0) @@ -286,8 +57,6 @@ std::string Ciphersuite::to_string() const out << "DHE"; else if(kex_algo() == "ECDH") out << "ECDHE"; - else if(kex_algo() == "SRP") - out << "SRP_SHA"; else out << kex_algo(); @@ -310,7 +79,7 @@ std::string Ciphersuite::to_string() const if(cipher_algo() == "3DES") out << "3DES_EDE"; else if(cipher_algo() == "Camellia") - out << "CAMELLIA_" << std::to_string(8*cipher_keylen()); + out << "CAMELLIA_" << Botan::to_string(8*cipher_keylen()); else out << replace_char(cipher_algo(), '-', '_'); @@ -329,18 +98,7 @@ std::string Ciphersuite::to_string() const return out.str(); } -Ciphersuite::Ciphersuite(const std::string& sig_algo, - const std::string& kex_algo, - const std::string& mac_algo, - const std::string& cipher_algo, - size_t cipher_algo_keylen) : - m_sig_algo(sig_algo), - m_kex_algo(kex_algo), - m_mac_algo(mac_algo), - m_cipher_algo(cipher_algo), - m_cipher_keylen(cipher_algo_keylen) - { - } +} } diff --git a/src/tls/tls_ciphersuite.h b/src/tls/tls_ciphersuite.h index e5d8c967b..dcb4b6a6f 100644 --- a/src/tls/tls_ciphersuite.h +++ b/src/tls/tls_ciphersuite.h @@ -21,6 +21,9 @@ namespace TLS { class BOTAN_DLL Ciphersuite { public: + /** + * Convert an SSL/TLS ciphersuite to algorithm fields + */ static Ciphersuite by_id(u16bit suite); static Ciphersuite by_name(const std::string& name); @@ -30,6 +33,9 @@ class BOTAN_DLL Ciphersuite */ std::string to_string() const; + bool psk_ciphersuite() const; + bool ecc_ciphersuite() const; + std::string kex_algo() const { return m_kex_algo; } std::string sig_algo() const { return m_sig_algo; } @@ -46,7 +52,15 @@ class BOTAN_DLL Ciphersuite const std::string& kex_algo, const std::string& mac_algo, const std::string& cipher_algo, - size_t cipher_algo_keylen); + size_t cipher_algo_keylen) : + m_sig_algo(sig_algo), + m_kex_algo(kex_algo), + m_mac_algo(mac_algo), + m_cipher_algo(cipher_algo), + m_cipher_keylen(cipher_algo_keylen) + { + } + private: std::string m_sig_algo, m_kex_algo, m_mac_algo, m_cipher_algo; size_t m_cipher_keylen; diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp index 77e87e9bc..63d0ee148 100644 --- a/src/tls/tls_client.cpp +++ b/src/tls/tls_client.cpp @@ -18,20 +18,21 @@ namespace TLS { /* * TLS Client Constructor */ -Client::Client(std::function<void (const byte[], size_t)> output_fn, - std::function<void (const byte[], size_t, Alert)> proc_fn, - std::function<bool (const Session&)> handshake_fn, +Client::Client(std::tr1::function<void (const byte[], size_t)> output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_fn, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, const std::string& hostname, - std::function<std::string (std::vector<std::string>)> next_protocol) : + std::tr1::function<std::string (std::vector<std::string>)> next_protocol) : Channel(output_fn, proc_fn, handshake_fn), policy(policy), rng(rng), session_manager(session_manager), - creds(creds) + creds(creds), + m_hostname(hostname) { writer.set_version(Protocol_Version::SSL_V3); @@ -56,6 +57,7 @@ Client::Client(std::function<void (const byte[], size_t)> output_fn, state->hash, policy, rng, + secure_renegotiation.for_client_hello(), session_info, send_npn_request); @@ -83,16 +85,42 @@ Client::Client(std::function<void (const byte[], size_t)> output_fn, /* * Send a new client hello to renegotiate */ -void Client::renegotiate() +void Client::renegotiate(bool force_full_renegotiation) { - if(state) - return; // currently in handshake + if(state && state->client_hello) + return; // currently in active handshake + delete state; state = new Handshake_State(new Stream_Handshake_Reader); + state->set_expected_next(SERVER_HELLO); - state->client_hello = new Client_Hello(writer, state->hash, policy, rng, - secure_renegotiation.for_client_hello()); + if(!force_full_renegotiation) + { + Session session_info; + if(session_manager.load_from_host_info(m_hostname, 0, session_info)) + { + state->client_hello = new Client_Hello( + writer, + state->hash, + policy, + rng, + secure_renegotiation.for_client_hello(), + session_info); + + state->resume_master_secret = session_info.master_secret(); + } + } + + if(!state->client_hello) + { + state->client_hello = new Client_Hello( + writer, + state->hash, + policy, + rng, + secure_renegotiation.for_client_hello()); + } secure_renegotiation.update(state->client_hello); } @@ -136,11 +164,7 @@ void Client::process_handshake_msg(Handshake_Type type, return; } - state->client_hello = new Client_Hello(writer, state->hash, policy, rng, - secure_renegotiation.for_client_hello()); - secure_renegotiation.update(state->client_hello); - - state->set_expected_next(SERVER_HELLO); + renegotiate(false); return; } @@ -188,6 +212,9 @@ void Client::process_handshake_msg(Handshake_Type type, secure_renegotiation.update(state->server_hello); + m_peer_supports_heartbeats = state->server_hello->supports_heartbeats(); + m_heartbeat_sending_allowed = state->server_hello->peer_can_send_heartbeats(); + state->suite = Ciphersuite::by_id(state->server_hello->ciphersuite()); const bool server_returned_same_session_id = @@ -241,8 +268,8 @@ void Client::process_handshake_msg(Handshake_Type type, ever sent. The server may or may not send a server kex, depending on if it has an identity hint for us. - DHE_PSK always sends a server key exchange for the DH - exchange portion. + (EC)DHE_PSK always sends a server key exchange for the + DH exchange portion. */ state->set_expected_next(SERVER_KEX); @@ -280,15 +307,14 @@ void Client::process_handshake_msg(Handshake_Type type, try { - const std::string hostname = state->client_hello->sni_hostname(); - creds.verify_certificate_chain("tls-client", hostname, peer_certs); + creds.verify_certificate_chain("tls-client", m_hostname, peer_certs); } catch(std::exception& e) { throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); } - std::unique_ptr<Public_Key> peer_key(peer_certs[0].subject_public_key()); + std::auto_ptr<Public_Key> peer_key(peer_certs[0].subject_public_key()); if(peer_key->algo_name() != state->suite.sig_algo()) throw TLS_Exception(Alert::ILLEGAL_PARAMETER, @@ -330,7 +356,7 @@ void Client::process_handshake_msg(Handshake_Type type, std::vector<X509_Certificate> client_certs = creds.cert_chain(types, "tls-client", - state->client_hello->sni_hostname()); + m_hostname); state->client_certs = new Certificate(writer, state->hash, @@ -342,6 +368,7 @@ void Client::process_handshake_msg(Handshake_Type type, state, creds, peer_certs, + m_hostname, rng); state->keys = Session_Keys(state, @@ -354,7 +381,7 @@ void Client::process_handshake_msg(Handshake_Type type, Private_Key* private_key = creds.private_key_for(state->client_certs->cert_chain()[0], "tls-client", - state->client_hello->sni_hostname()); + m_hostname); state->client_verify = new Certificate_Verify(writer, state, @@ -437,7 +464,7 @@ void Client::process_handshake_msg(Handshake_Type type, state->server_hello->fragment_size(), peer_certs, session_ticket, - state->client_hello->sni_hostname(), + m_hostname, "" ); diff --git a/src/tls/tls_client.h b/src/tls/tls_client.h index cd972fa28..4efe2a2df 100644 --- a/src/tls/tls_client.h +++ b/src/tls/tls_client.h @@ -42,18 +42,18 @@ class BOTAN_DLL Client : public Channel * called with the list of protocols the server advertised; * the client should return the protocol it would like to use. */ - Client(std::function<void (const byte[], size_t)> socket_output_fn, - std::function<void (const byte[], size_t, Alert)> proc_fn, - std::function<bool (const Session&)> handshake_complete, + Client(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, const std::string& servername = "", - std::function<std::string (std::vector<std::string>)> next_protocol = - std::function<std::string (std::vector<std::string>)>()); + std::tr1::function<std::string (std::vector<std::string>)> next_protocol = + std::tr1::function<std::string (std::vector<std::string>)>()); - void renegotiate(); + void renegotiate(bool force_full_renegotiation); private: void process_handshake_msg(Handshake_Type type, const MemoryRegion<byte>& contents); @@ -64,6 +64,7 @@ class BOTAN_DLL Client : public Channel RandomNumberGenerator& rng; Session_Manager& session_manager; Credentials_Manager& creds; + const std::string m_hostname; }; } diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp index c0de24bfe..f1361bbb9 100644 --- a/src/tls/tls_extensions.cpp +++ b/src/tls/tls_extensions.cpp @@ -42,6 +42,9 @@ Extension* make_extension(TLS_Data_Reader& reader, case TLSEXT_NEXT_PROTOCOL: return new Next_Protocol_Notification(reader, size); + case TLSEXT_HEARTBEAT_SUPPORT: + return new Heartbeat_Support_Indicator(reader, size); + case TLSEXT_SESSION_TICKET: return new Session_Ticket(reader, size); @@ -242,7 +245,7 @@ Maximum_Fragment_Length::Maximum_Fragment_Length(size_t max_fragment) else if(max_fragment == 4096) val = 4; else - throw std::invalid_argument("Bad setting " + std::to_string(max_fragment) + + throw std::invalid_argument("Bad setting " + to_string(max_fragment) + " for maximum fragment size"); } @@ -468,11 +471,16 @@ MemoryVector<byte> Signature_Algorithms::serialize() const for(size_t i = 0; i != m_supported_algos.size(); ++i) { - if(m_supported_algos[i].second == "") - continue; + try + { + const byte hash_code = hash_algo_code(m_supported_algos[i].first); + const byte sig_code = sig_algo_code(m_supported_algos[i].second); - buf.push_back(hash_algo_code(m_supported_algos[i].first)); - buf.push_back(sig_algo_code(m_supported_algos[i].second)); + buf.push_back(hash_code); + buf.push_back(sig_code); + } + catch(...) + {} } buf[0] = get_byte<u16bit>(0, buf.size()-2); diff --git a/src/tls/tls_extensions.h b/src/tls/tls_extensions.h index 6a97d2560..3fe3f7399 100644 --- a/src/tls/tls_extensions.h +++ b/src/tls/tls_extensions.h @@ -32,6 +32,7 @@ enum Handshake_Extension_Type { TLSEXT_EC_POINT_FORMATS = 11, TLSEXT_SRP_IDENTIFIER = 12, TLSEXT_SIGNATURE_ALGORITHMS = 13, + TLSEXT_HEARTBEAT_SUPPORT = 15, TLSEXT_SESSION_TICKET = 35, @@ -309,6 +310,32 @@ class Signature_Algorithms : public Extension }; /** +* Heartbeat Extension (RFC 6520) +*/ +class Heartbeat_Support_Indicator : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_HEARTBEAT_SUPPORT; } + + Handshake_Extension_Type type() const { return static_type(); } + + bool peer_allowed_to_send() const { return m_peer_allowed_to_send; } + + MemoryVector<byte> serialize() const; + + bool empty() const { return false; } + + Heartbeat_Support_Indicator(bool peer_allowed_to_send) : + m_peer_allowed_to_send(peer_allowed_to_send) {} + + Heartbeat_Support_Indicator(TLS_Data_Reader& reader, u16bit extension_size); + + private: + bool m_peer_allowed_to_send; + }; + +/** * Represents a block of extensions in a hello message */ class Extensions diff --git a/src/tls/tls_handshake_hash.cpp b/src/tls/tls_handshake_hash.cpp index a9bd8dccf..d0c74136b 100644 --- a/src/tls/tls_handshake_hash.cpp +++ b/src/tls/tls_handshake_hash.cpp @@ -35,7 +35,7 @@ SecureVector<byte> Handshake_Hash::final(Protocol_Version version, { Algorithm_Factory& af = global_state().algorithm_factory(); - std::unique_ptr<HashFunction> hash; + std::auto_ptr<HashFunction> hash; if(version == Protocol_Version::TLS_V10 || version == Protocol_Version::TLS_V11) { @@ -43,7 +43,7 @@ SecureVector<byte> Handshake_Hash::final(Protocol_Version version, } else if(version == Protocol_Version::TLS_V12) { - if(mac_algo == "SHA-1" || mac_algo == "SHA-256") + if(mac_algo == "MD5" || mac_algo == "SHA-1" || mac_algo == "SHA-256") hash.reset(af.make_hash_function("SHA-256")); else hash.reset(af.make_hash_function(mac_algo)); @@ -65,8 +65,8 @@ SecureVector<byte> Handshake_Hash::final_ssl3(const MemoryRegion<byte>& secret) Algorithm_Factory& af = global_state().algorithm_factory(); - std::unique_ptr<HashFunction> md5(af.make_hash_function("MD5")); - std::unique_ptr<HashFunction> sha1(af.make_hash_function("SHA-1")); + std::auto_ptr<HashFunction> md5(af.make_hash_function("MD5")); + std::auto_ptr<HashFunction> sha1(af.make_hash_function("SHA-1")); md5->update(data); sha1->update(data); diff --git a/src/tls/tls_handshake_state.cpp b/src/tls/tls_handshake_state.cpp index b34d8616d..1a55305e3 100644 --- a/src/tls/tls_handshake_state.cpp +++ b/src/tls/tls_handshake_state.cpp @@ -68,7 +68,7 @@ u32bit bitmask_for_handshake_type(Handshake_Type type) return 0; default: - throw Internal_Error("Unknown handshake type " + std::to_string(type)); + throw Internal_Error("Unknown handshake type " + to_string(type)); } return 0; @@ -104,6 +104,8 @@ Handshake_State::Handshake_State(Handshake_Reader* reader) hand_expecting_mask = 0; hand_received_mask = 0; + + allow_session_resumption = true; } void Handshake_State::set_version(const Protocol_Version& version) @@ -121,8 +123,8 @@ void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg) if(!ok) throw Unexpected_Message("Unexpected state transition in handshake, got " + - std::to_string(handshake_msg) + " mask is " + - std::to_string(hand_expecting_mask)); + to_string(handshake_msg) + " mask is " + + to_string(hand_expecting_mask)); /* We don't know what to expect next, so force a call to set_expected_next; if it doesn't happen, the next transition @@ -143,6 +145,14 @@ bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const return (hand_received_mask & mask); } +std::string Handshake_State::srp_identifier() const + { + if(suite.valid() && suite.kex_algo() == "SRP_SHA") + return client_hello->srp_identifier(); + + return ""; + } + const MemoryRegion<byte>& Handshake_State::session_ticket() const { if(new_session_ticket && !new_session_ticket->ticket().empty()) @@ -163,8 +173,12 @@ KDF* Handshake_State::protocol_specific_prf() } else if(version() == Protocol_Version::TLS_V12) { - if(suite.mac_algo() == "SHA-1" || suite.mac_algo() == "SHA-256") + if(suite.mac_algo() == "MD5" || + suite.mac_algo() == "SHA-1" || + suite.mac_algo() == "SHA-256") + { return get_kdf("TLS-12-PRF(SHA-256)"); + } return get_kdf("TLS-12-PRF(" + suite.mac_algo() + ")"); } diff --git a/src/tls/tls_handshake_state.h b/src/tls/tls_handshake_state.h index d2f64c43b..ec4c2fea8 100644 --- a/src/tls/tls_handshake_state.h +++ b/src/tls/tls_handshake_state.h @@ -15,7 +15,20 @@ #include <botan/pubkey.h> #include <utility> -#include <functional> + +#if defined(BOTAN_USE_STD_TR1) + +#if defined(BOTAN_BUILD_COMPILER_IS_MSVC) + #include <functional> +#else + #include <tr1/functional> +#endif + +#elif defined(BOTAN_USE_BOOST_TR1) + #include <boost/tr1/functional.hpp> +#else + #error "No TR1 library defined for use" +#endif namespace Botan { @@ -51,6 +64,8 @@ class Handshake_State std::string& sig_algo, bool for_client_auth); + std::string srp_identifier() const; + KDF* protocol_specific_prf(); Protocol_Version version() const { return m_version; } @@ -86,10 +101,15 @@ class Handshake_State */ SecureVector<byte> resume_master_secret; + /* + * + */ + bool allow_session_resumption; + /** * Used by client using NPN */ - std::function<std::string (std::vector<std::string>)> client_npn_cb; + std::tr1::function<std::string (std::vector<std::string>)> client_npn_cb; Handshake_Reader* handshake_reader() { return m_handshake_reader; } private: diff --git a/src/tls/tls_heartbeats.cpp b/src/tls/tls_heartbeats.cpp new file mode 100644 index 000000000..a77d23534 --- /dev/null +++ b/src/tls/tls_heartbeats.cpp @@ -0,0 +1,78 @@ +/* +* TLS Heartbeats +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_heartbeats.h> +#include <botan/internal/tls_extensions.h> +#include <botan/internal/tls_reader.h> +#include <botan/tls_exceptn.h> + +namespace Botan { + +namespace TLS { + +Heartbeat_Message::Heartbeat_Message(const MemoryRegion<byte>& buf) + { + TLS_Data_Reader reader(buf); + + const byte type = reader.get_byte(); + + if(type != 1 && type != 2) + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, + "Unknown heartbeat message type"); + + m_type = static_cast<Type>(type); + + m_payload = reader.get_range<byte>(2, 0, 16*1024); + + // padding follows and is ignored + } + +Heartbeat_Message::Heartbeat_Message(Type type, + const byte payload[], + size_t payload_len) : + m_type(type), + m_payload(payload, payload_len) + { + } + +MemoryVector<byte> Heartbeat_Message::contents() const + { + MemoryVector<byte> send_buf(3 + m_payload.size() + 16); + send_buf[0] = m_type; + send_buf[1] = get_byte<u16bit>(0, m_payload.size()); + send_buf[2] = get_byte<u16bit>(1, m_payload.size()); + copy_mem(&send_buf[3], &m_payload[0], m_payload.size()); + // leave padding as all zeros + + return send_buf; + } + +MemoryVector<byte> Heartbeat_Support_Indicator::serialize() const + { + MemoryVector<byte> heartbeat(1); + heartbeat[0] = (m_peer_allowed_to_send ? 1 : 2); + return heartbeat; + } + +Heartbeat_Support_Indicator::Heartbeat_Support_Indicator(TLS_Data_Reader& reader, + u16bit extension_size) + { + if(extension_size != 1) + throw Decoding_Error("Strange size for heartbeat extension"); + + const byte code = reader.get_byte(); + + if(code != 1 && code != 2) + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, + "Unknown heartbeat code " + to_string(code)); + + m_peer_allowed_to_send = (code == 1); + } + +} + +} diff --git a/src/tls/tls_heartbeats.h b/src/tls/tls_heartbeats.h new file mode 100644 index 000000000..4fa49501b --- /dev/null +++ b/src/tls/tls_heartbeats.h @@ -0,0 +1,40 @@ +/* +* TLS Heartbeats +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_HEARTBEATS_H__ +#define BOTAN_TLS_HEARTBEATS_H__ + +#include <botan/secmem.h> + +namespace Botan { + +namespace TLS { + +class Heartbeat_Message + { + public: + enum Type { REQUEST = 1, RESPONSE = 2 }; + + MemoryVector<byte> contents() const; + + const MemoryRegion<byte>& payload() const { return m_payload; } + + bool is_request() const { return m_type == REQUEST; } + + Heartbeat_Message(const MemoryRegion<byte>& buf); + + Heartbeat_Message(Type type, const byte payload[], size_t payload_len); + private: + Type m_type; + MemoryVector<byte> m_payload; + }; + +} + +} + +#endif diff --git a/src/tls/tls_magic.h b/src/tls/tls_magic.h index 0e45407d3..2972321c9 100644 --- a/src/tls/tls_magic.h +++ b/src/tls/tls_magic.h @@ -32,7 +32,8 @@ enum Record_Type { CHANGE_CIPHER_SPEC = 20, ALERT = 21, HANDSHAKE = 22, - APPLICATION_DATA = 23 + APPLICATION_DATA = 23, + HEARTBEAT = 24, }; enum Handshake_Type { @@ -56,90 +57,6 @@ enum Handshake_Type { HANDSHAKE_NONE = 255 // Null value }; -enum Ciphersuite_Code { - TLS_RSA_WITH_RC4_128_MD5 = 0x0004, - TLS_RSA_WITH_RC4_128_SHA = 0x0005, - - TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A, - TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F, - TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035, - TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C, - TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D, - TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041, - TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084, - TLS_RSA_WITH_SEED_CBC_SHA = 0x0096, - TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007, - - TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038, - TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040, - TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A, - TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044, - TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087, - TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099, - TLS_DHE_DSS_WITH_RC4_128_SHA = 0x0066, - - TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039, - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067, - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B, - TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045, - TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088, - TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A, - - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007, - TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024, - - TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, - - TLS_PSK_WITH_RC4_128_SHA = 0x008A, - TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B, - TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, - TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D, - TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, - TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF, - - TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E, - TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F, - TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091, - TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2, - TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3, - - TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033, - TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034, - TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035, - TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036, - TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037, - TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038, - - TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A, - TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D, - TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020, - - TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C, - TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F, - TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022, - - TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B, - TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E, - TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021, - - /* signalling values that cannot be negotiated */ - TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF -}; - enum Compression_Method { NO_COMPRESSION = 0x00, DEFLATE_COMPRESSION = 0x01 diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h index 2f8af5fd2..d9146dda1 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -21,6 +21,7 @@ namespace Botan { class Credentials_Manager; +class SRP6_Server_Session; namespace TLS { @@ -72,15 +73,10 @@ class Client_Hello : public Handshake_Message { public: Handshake_Type type() const { return CLIENT_HELLO; } + Protocol_Version version() const { return m_version; } - const MemoryVector<byte>& session_id() const { return m_session_id; } - std::vector<byte> session_id_vector() const - { - std::vector<byte> v; - v.insert(v.begin(), &m_session_id[0], &m_session_id[m_session_id.size()]); - return v; - } + const MemoryVector<byte>& session_id() const { return m_session_id; } const std::vector<std::pair<std::string, std::string> >& supported_algos() const { return m_supported_algos; } @@ -113,6 +109,10 @@ class Client_Hello : public Handshake_Message const MemoryRegion<byte>& session_ticket() const { return m_session_ticket; } + bool supports_heartbeats() const { return m_supports_heartbeats; } + + bool peer_can_send_heartbeats() const { return m_peer_can_send_heartbeats; } + Client_Hello(Record_Writer& writer, Handshake_Hash& hash, const Policy& policy, @@ -126,6 +126,7 @@ class Client_Hello : public Handshake_Message Handshake_Hash& hash, const Policy& policy, RandomNumberGenerator& rng, + const MemoryRegion<byte>& reneg_info, const Session& resumed_session, bool next_protocol = false); @@ -154,6 +155,9 @@ class Client_Hello : public Handshake_Message bool m_supports_session_ticket; MemoryVector<byte> m_session_ticket; + + bool m_supports_heartbeats; + bool m_peer_can_send_heartbeats; }; /** @@ -163,17 +167,16 @@ class Server_Hello : public Handshake_Message { public: Handshake_Type type() const { return SERVER_HELLO; } - Protocol_Version version() { return s_version; } + + Protocol_Version version() { return m_version; } + + const MemoryVector<byte>& random() const { return m_random; } + const MemoryVector<byte>& session_id() const { return m_session_id; } - u16bit ciphersuite() const { return suite; } - byte compression_method() const { return comp_method; } - std::vector<byte> session_id_vector() const - { - std::vector<byte> v; - v.insert(v.begin(), &m_session_id[0], &m_session_id[m_session_id.size()]); - return v; - } + u16bit ciphersuite() const { return m_ciphersuite; } + + byte compression_method() const { return m_comp_method; } bool secure_renegotiation() const { return m_secure_renegotiation; } @@ -189,20 +192,9 @@ class Server_Hello : public Handshake_Message const MemoryVector<byte>& renegotiation_info() { return m_renegotiation_info; } - const MemoryVector<byte>& random() const { return s_random; } + bool supports_heartbeats() const { return m_supports_heartbeats; } - Server_Hello(Record_Writer& writer, - Handshake_Hash& hash, - Protocol_Version version, - const Client_Hello& other, - const std::vector<std::string>& available_cert_types, - const Policy& policies, - bool have_session_ticket_key, - bool client_has_secure_renegotiation, - const MemoryRegion<byte>& reneg_info, - bool client_has_npn, - const std::vector<std::string>& next_protocols, - RandomNumberGenerator& rng); + bool peer_can_send_heartbeats() const { return m_peer_can_send_heartbeats; } Server_Hello(Record_Writer& writer, Handshake_Hash& hash, @@ -213,19 +205,20 @@ class Server_Hello : public Handshake_Message size_t max_fragment_size, bool client_has_secure_renegotiation, const MemoryRegion<byte>& reneg_info, - bool client_supports_session_tickets, + bool offer_session_ticket, bool client_has_npn, const std::vector<std::string>& next_protocols, + bool client_has_heartbeat, RandomNumberGenerator& rng); Server_Hello(const MemoryRegion<byte>& buf); private: MemoryVector<byte> serialize() const; - Protocol_Version s_version; - MemoryVector<byte> m_session_id, s_random; - u16bit suite; - byte comp_method; + Protocol_Version m_version; + MemoryVector<byte> m_session_id, m_random; + u16bit m_ciphersuite; + byte m_comp_method; size_t m_fragment_size; bool m_secure_renegotiation; @@ -234,6 +227,9 @@ class Server_Hello : public Handshake_Message bool m_next_protocol; std::vector<std::string> m_next_protocols; bool m_supports_session_ticket; + + bool m_supports_heartbeats; + bool m_peer_can_send_heartbeats; }; /** @@ -251,6 +247,7 @@ class Client_Key_Exchange : public Handshake_Message Handshake_State* state, Credentials_Manager& creds, const std::vector<X509_Certificate>& peer_certs, + const std::string& hostname, RandomNumberGenerator& rng); Client_Key_Exchange(const MemoryRegion<byte>& buf, @@ -408,6 +405,9 @@ class Server_Key_Exchange : public Handshake_Message // Only valid for certain kex types const Private_Key& server_kex_key() const; + // Only valid for SRP negotiation + SRP6_Server_Session& server_srp_params(); + Server_Key_Exchange(Record_Writer& writer, Handshake_State* state, const Policy& policy, @@ -420,11 +420,12 @@ class Server_Key_Exchange : public Handshake_Message const std::string& sig_alg, Protocol_Version version); - ~Server_Key_Exchange() { delete m_kex_key; } + ~Server_Key_Exchange(); private: MemoryVector<byte> serialize() const; Private_Key* m_kex_key; + SRP6_Server_Session* m_srp_params; MemoryVector<byte> m_params; @@ -479,7 +480,7 @@ class New_Session_Ticket : public Handshake_Message New_Session_Ticket(Record_Writer& writer, Handshake_Hash& hash, const MemoryRegion<byte>& ticket, - u32bit lifetime = 0); + u32bit lifetime); New_Session_Ticket(Record_Writer& writer, Handshake_Hash& hash); diff --git a/src/tls/tls_policy.cpp b/src/tls/tls_policy.cpp index 1ab55f7c6..f240bebac 100644 --- a/src/tls/tls_policy.cpp +++ b/src/tls/tls_policy.cpp @@ -11,8 +11,6 @@ #include <botan/tls_exceptn.h> #include <botan/internal/stl_util.h> -#include <assert.h> - namespace Botan { namespace TLS { @@ -25,8 +23,8 @@ std::vector<std::string> Policy::allowed_ciphers() const allowed.push_back("AES-128"); allowed.push_back("3DES"); allowed.push_back("ARC4"); - - // Note that Camellia, SEED and IDEA are not included by default + //allowed.push_back("Camellia"); + //allowed.push_back("SEED"); return allowed; } @@ -40,7 +38,7 @@ std::vector<std::string> Policy::allowed_hashes() const allowed.push_back("SHA-256"); allowed.push_back("SHA-224"); allowed.push_back("SHA-1"); - // Note that MD5 is not included by default + //allowed.push_back("MD5"); return allowed; } @@ -49,13 +47,14 @@ std::vector<std::string> Policy::allowed_key_exchange_methods() const { std::vector<std::string> allowed; - //allowed.push_back("SRP"); + allowed.push_back("SRP_SHA"); //allowed.push_back("ECDHE_PSK"); //allowed.push_back("DHE_PSK"); //allowed.push_back("PSK"); + allowed.push_back("ECDH"); allowed.push_back("DH"); - allowed.push_back("RSA"); // RSA via server cert + allowed.push_back("RSA"); return allowed; } @@ -67,7 +66,7 @@ std::vector<std::string> Policy::allowed_signature_methods() const allowed.push_back("ECDSA"); allowed.push_back("RSA"); allowed.push_back("DSA"); - allowed.push_back(""); + //allowed.push_back(""); return allowed; } @@ -89,6 +88,40 @@ std::vector<std::string> Policy::allowed_ecc_curves() const return curves; } +/* +* Choose an ECC curve to use +*/ +std::string Policy::choose_curve(const std::vector<std::string>& curve_names) const + { + const std::vector<std::string> our_curves = allowed_ecc_curves(); + + for(size_t i = 0; i != our_curves.size(); ++i) + if(value_exists(curve_names, our_curves[i])) + return our_curves[i]; + + return ""; // no shared curve + } + +DL_Group Policy::dh_group() const + { + return DL_Group("modp/ietf/2048"); + } + +/* +* Return allowed compression algorithms +*/ +std::vector<byte> Policy::compression() const + { + std::vector<byte> algs; + algs.push_back(NO_COMPRESSION); + return algs; + } + +u32bit Policy::session_ticket_lifetime() const + { + return 86400; // 1 day + } + Protocol_Version Policy::min_version() const { return Protocol_Version::SSL_V3; @@ -173,21 +206,13 @@ class Ciphersuite_Preference_Ordering } -std::vector<u16bit> Policy::ciphersuite_list(bool have_srp) const +std::vector<u16bit> ciphersuite_list(const Policy& policy, + bool have_srp) { - std::vector<std::string> ciphers = allowed_ciphers(); - std::vector<std::string> hashes = allowed_hashes(); - std::vector<std::string> kex = allowed_key_exchange_methods(); - std::vector<std::string> sigs = allowed_signature_methods(); - - if(!have_srp) - { - std::vector<std::string>::iterator i = - std::find(kex.begin(), kex.end(), "SRP"); - - if(i != kex.end()) - kex.erase(i); - } + const std::vector<std::string> ciphers = policy.allowed_ciphers(); + const std::vector<std::string> hashes = policy.allowed_hashes(); + const std::vector<std::string> kex = policy.allowed_key_exchange_methods(); + const std::vector<std::string> sigs = policy.allowed_signature_methods(); Ciphersuite_Preference_Ordering order(ciphers, hashes, kex, sigs); @@ -201,13 +226,27 @@ std::vector<u16bit> Policy::ciphersuite_list(bool have_srp) const if(!suite.valid()) continue; // not a ciphersuite we know, skip - if(value_exists(ciphers, suite.cipher_algo()) && - value_exists(hashes, suite.mac_algo()) && - value_exists(kex, suite.kex_algo()) && - value_exists(sigs, suite.sig_algo())) + if(!have_srp && suite.kex_algo() == "SRP_SHA") + continue; + + if(!value_exists(kex, suite.kex_algo())) + continue; // unsupported key exchange + + if(!value_exists(ciphers, suite.cipher_algo())) + continue; // unsupported cipher + + if(!value_exists(hashes, suite.mac_algo())) + continue; // unsupported MAC algo + + if(!value_exists(sigs, suite.sig_algo())) { - ciphersuites[suite] = i; + // allow if it's an empty sig algo and we want to use PSK + if(suite.sig_algo() != "" || !suite.psk_ciphersuite()) + continue; } + + // OK, allow it: + ciphersuites[suite] = i; } std::vector<u16bit> ciphersuite_codes; @@ -221,79 +260,6 @@ std::vector<u16bit> Policy::ciphersuite_list(bool have_srp) const return ciphersuite_codes; } -/* -* Return allowed compression algorithms -*/ -std::vector<byte> Policy::compression() const - { - std::vector<byte> algs; - algs.push_back(NO_COMPRESSION); - return algs; - } - -/* -* Choose an ECC curve to use -*/ -std::string Policy::choose_curve(const std::vector<std::string>& curve_names) const - { - std::vector<std::string> our_curves = allowed_ecc_curves(); - - for(size_t i = 0; i != our_curves.size(); ++i) - if(value_exists(curve_names, our_curves[i])) - return our_curves[i]; - - return ""; // no shared curve - } - -/* -* Choose which ciphersuite to use -*/ -u16bit Policy::choose_suite(const std::vector<u16bit>& client_suites, - const std::vector<std::string>& available_cert_types, - bool have_shared_ecc_curve, - bool have_srp) const - { - std::vector<u16bit> ciphersuites = ciphersuite_list(have_srp); - - for(size_t i = 0; i != ciphersuites.size(); ++i) - { - const u16bit suite_id = ciphersuites[i]; - Ciphersuite suite = Ciphersuite::by_id(suite_id); - - if(!have_shared_ecc_curve) - { - if(suite.kex_algo() == "ECDH" || suite.sig_algo() == "ECDSA") - continue; - } - - if(suite.sig_algo() != "" && - !value_exists(available_cert_types, suite.sig_algo())) - { - continue; - } - - if(value_exists(client_suites, suite_id)) - return suite_id; - } - - return 0; // no shared cipersuite - } - -/* -* Choose which compression algorithm to use -*/ -byte Policy::choose_compression(const std::vector<byte>& c_comp) const - { - std::vector<byte> s_comp = compression(); - - for(size_t i = 0; i != s_comp.size(); ++i) - for(size_t j = 0; j != c_comp.size(); ++j) - if(s_comp[i] == c_comp[j]) - return s_comp[i]; - - return NO_COMPRESSION; - } - } } diff --git a/src/tls/tls_policy.h b/src/tls/tls_policy.h index f53b9bab6..c3a0fc29e 100644 --- a/src/tls/tls_policy.h +++ b/src/tls/tls_policy.h @@ -82,19 +82,26 @@ class BOTAN_DLL Policy /** * Return the group to use for ephemeral Diffie-Hellman key agreement */ - virtual DL_Group dh_group() const { return DL_Group("modp/ietf/1536"); } + virtual DL_Group dh_group() 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 * identifier value will be used and the protocol allowed to - * proceed, causing the login to eventually fail without + * proceed, causing the handshake to eventually fail without * revealing that the username does not exist on this system. */ virtual bool hide_unknown_users() const { return false; } /** + * Return the allowed lifetime of a session ticket. If 0, session + * tickets do not expire until the session ticket key rolls over. + * Expired session tickets cannot be used to resume a session. + */ + virtual u32bit session_ticket_lifetime() const; + + /** * @return the minimum version that we are willing to negotiate */ virtual Protocol_Version min_version() const; @@ -104,21 +111,15 @@ class BOTAN_DLL Policy */ virtual Protocol_Version pref_version() const; - /** - * Return allowed ciphersuites, in order of preference - */ - std::vector<u16bit> ciphersuite_list(bool have_srp) const; - - u16bit choose_suite(const std::vector<u16bit>& client_suites, - const std::vector<std::string>& available_cert_types, - bool have_shared_ecc_curve, - bool have_srp) const; - - byte choose_compression(const std::vector<byte>& client_algos) const; - virtual ~Policy() {} }; +/** +* Return allowed ciphersuites, in order of preference +*/ +std::vector<u16bit> ciphersuite_list(const Policy& policy, + bool have_srp); + } } diff --git a/src/tls/tls_record.h b/src/tls/tls_record.h index b966e3c72..680ec8f7b 100644 --- a/src/tls/tls_record.h +++ b/src/tls/tls_record.h @@ -22,6 +22,16 @@ namespace Botan { namespace TLS { +#elif defined(BOTAN_USE_BOOST_TR1) + #include <boost/tr1/functional.hpp> +#else + #error "No TR1 library defined for use" +#endif + +namespace Botan { + +namespace TLS { + class Session_Keys; /** @@ -33,6 +43,9 @@ class BOTAN_DLL Record_Writer void send(byte type, const byte input[], size_t length); void send(byte type, byte val) { send(type, &val, 1); } + void send(byte type, const MemoryRegion<byte>& input) + { send(type, &input[0], input.size()); } + MemoryVector<byte> send(class Handshake_Message& msg); void send_alert(const Alert& alert); @@ -48,7 +61,7 @@ class BOTAN_DLL Record_Writer void set_maximum_fragment_size(size_t max_fragment); - Record_Writer(std::function<void (const byte[], size_t)> output_fn); + Record_Writer(std::tr1::function<void (const byte[], size_t)> output_fn); ~Record_Writer() { delete m_mac; } private: @@ -57,7 +70,7 @@ class BOTAN_DLL Record_Writer void send_record(byte type, const byte input[], size_t length); - std::function<void (const byte[], size_t)> m_output_fn; + std::tr1::function<void (const byte[], size_t)> m_output_fn; MemoryVector<byte> m_writebuf; diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp index 3b4a526cc..9da4ca3b8 100644 --- a/src/tls/tls_server.cpp +++ b/src/tls/tls_server.cpp @@ -21,7 +21,8 @@ namespace { bool check_for_resume(Session& session_info, Session_Manager& session_manager, Credentials_Manager& credentials, - Client_Hello* client_hello) + Client_Hello* client_hello, + u32bit session_ticket_lifetime) { const MemoryVector<byte>& client_session_id = client_hello->session_id(); const MemoryVector<byte>& session_ticket = client_hello->session_ticket(); @@ -43,6 +44,10 @@ bool check_for_resume(Session& session_info, session_info = Session::decrypt( session_ticket, credentials.psk("tls-server", "session-ticket", "")); + + if(session_ticket_lifetime && + session_info.session_age() > session_ticket_lifetime) + return false; // ticket has expired } catch(...) { @@ -81,6 +86,79 @@ bool check_for_resume(Session& session_info, return true; } +/* +* Choose which ciphersuite to use +*/ +u16bit choose_ciphersuite( + const Policy& policy, + Credentials_Manager& creds, + const std::map<std::string, std::vector<X509_Certificate> >& cert_chains, + const Client_Hello* client_hello) + { + const bool have_srp = creds.attempt_srp("tls-server", + client_hello->sni_hostname()); + + const std::vector<u16bit> client_suites = client_hello->ciphersuites(); + const std::vector<u16bit> server_suites = ciphersuite_list(policy, have_srp); + + if(server_suites.empty()) + throw Internal_Error("Policy forbids us from negotiating any ciphersuite"); + + const bool have_shared_ecc_curve = + (policy.choose_curve(client_hello->supported_ecc_curves()) != ""); + + // Ordering by our preferences rather than by clients + for(size_t i = 0; i != server_suites.size(); ++i) + { + const u16bit suite_id = server_suites[i]; + + if(!value_exists(client_suites, suite_id)) + continue; + + Ciphersuite suite = Ciphersuite::by_id(suite_id); + + if(!have_shared_ecc_curve && suite.ecc_ciphersuite()) + continue; + + if(cert_chains.count(suite.sig_algo()) == 0) + continue; + + /* + The client may offer SRP cipher suites in the hello message but + omit the SRP extension. If the server would like to select an + SRP cipher suite in this case, the server SHOULD return a fatal + "unknown_psk_identity" alert immediately after processing the + client hello message. + - RFC 5054 section 2.5.1.2 + */ + if(suite.kex_algo() == "SRP_SHA" && client_hello->srp_identifier() == "") + throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, + "Client wanted SRP but did not send username"); + + return suite_id; + } + + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Can't agree on a ciphersuite with client"); + } + + +/* +* Choose which compression algorithm to use +*/ +byte choose_compression(const Policy& policy, + const std::vector<byte>& c_comp) + { + std::vector<byte> s_comp = policy.compression(); + + for(size_t i = 0; i != s_comp.size(); ++i) + for(size_t j = 0; j != c_comp.size(); ++j) + if(s_comp[i] == c_comp[j]) + return s_comp[i]; + + return NO_COMPRESSION; + } + std::map<std::string, std::vector<X509_Certificate> > get_server_certs(const std::string& hostname, Credentials_Manager& creds) @@ -106,9 +184,9 @@ get_server_certs(const std::string& hostname, /* * TLS Server Constructor */ -Server::Server(std::function<void (const byte[], size_t)> output_fn, - std::function<void (const byte[], size_t, Alert)> proc_fn, - std::function<bool (const Session&)> handshake_fn, +Server::Server(std::tr1::function<void (const byte[], size_t)> output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_fn, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, @@ -126,12 +204,14 @@ Server::Server(std::function<void (const byte[], size_t)> output_fn, /* * Send a hello request to the client */ -void Server::renegotiate() +void Server::renegotiate(bool force_full_renegotiation) { if(state) return; // currently in handshake state = new Handshake_State(new Stream_Handshake_Reader); + + state->allow_session_resumption = !force_full_renegotiation; state->set_expected_next(CLIENT_HELLO); Hello_Request hello_req(writer); } @@ -193,7 +273,8 @@ void Server::process_handshake_msg(Handshake_Type type, { state->client_hello = new Client_Hello(contents, type); - m_hostname = state->client_hello->sni_hostname(); + if(state->client_hello->sni_hostname() != "") + m_hostname = state->client_hello->sni_hostname(); Protocol_Version client_version = state->client_hello->version(); @@ -208,14 +289,20 @@ void Server::process_handshake_msg(Handshake_Type type, secure_renegotiation.update(state->client_hello); + m_peer_supports_heartbeats = state->client_hello->supports_heartbeats(); + m_heartbeat_sending_allowed = state->client_hello->peer_can_send_heartbeats(); + writer.set_version(state->version()); reader.set_version(state->version()); Session session_info; - const bool resuming = check_for_resume(session_info, - session_manager, - creds, - state->client_hello); + const bool resuming = + state->allow_session_resumption && + check_for_resume(session_info, + session_manager, + creds, + state->client_hello, + policy.session_ticket_lifetime()); bool have_session_ticket_key = false; @@ -240,11 +327,16 @@ void Server::process_handshake_msg(Handshake_Type type, session_info.fragment_size(), secure_renegotiation.supported(), secure_renegotiation.for_server_hello(), - state->client_hello->supports_session_ticket() && have_session_ticket_key, + (state->client_hello->supports_session_ticket() && + state->client_hello->session_ticket().empty() && + have_session_ticket_key), state->client_hello->next_protocol_notification(), m_possible_protocols, + state->client_hello->supports_heartbeats(), rng); + secure_renegotiation.update(state->server_hello); + if(session_info.fragment_size()) { reader.set_maximum_fragment_size(session_info.fragment_size()); @@ -257,13 +349,12 @@ void Server::process_handshake_msg(Handshake_Type type, if(!handshake_fn(session_info)) { - if(state->server_hello->supports_session_ticket()) + session_manager.remove_entry(session_info.session_id()); + + if(state->server_hello->supports_session_ticket()) // send an empty ticket state->new_session_ticket = new New_Session_Ticket(writer, state->hash); - else - session_manager.remove_entry(session_info.session_id()); } - // FIXME: should only send a new ticket if we need too (eg old session) if(state->server_hello->supports_session_ticket() && !state->new_session_ticket) { try @@ -272,7 +363,8 @@ void Server::process_handshake_msg(Handshake_Type type, state->new_session_ticket = new New_Session_Ticket(writer, state->hash, - session_info.encrypt(ticket_key, rng)); + session_info.encrypt(ticket_key, rng), + policy.session_ticket_lifetime()); } catch(...) {} @@ -301,29 +393,24 @@ void Server::process_handshake_msg(Handshake_Type type, cert_chains = get_server_certs("", creds); } - std::vector<std::string> available_cert_types; - - for(std::map<std::string, std::vector<X509_Certificate> >::const_iterator i = cert_chains.begin(); - i != cert_chains.end(); ++i) - { - if(!i->second.empty()) - available_cert_types.push_back(i->first); - } - state->server_hello = new Server_Hello( writer, state->hash, + rng.random_vec(32), // new session ID state->version(), - *(state->client_hello), - available_cert_types, - policy, - have_session_ticket_key, + choose_ciphersuite(policy, creds, cert_chains, state->client_hello), + choose_compression(policy, state->client_hello->compression_methods()), + state->client_hello->fragment_size(), secure_renegotiation.supported(), secure_renegotiation.for_server_hello(), + state->client_hello->supports_session_ticket() && have_session_ticket_key, state->client_hello->next_protocol_notification(), m_possible_protocols, + state->client_hello->supports_heartbeats(), rng); + secure_renegotiation.update(state->server_hello); + if(state->client_hello->fragment_size()) { reader.set_maximum_fragment_size(state->client_hello->fragment_size()); @@ -381,8 +468,6 @@ void Server::process_handshake_msg(Handshake_Type type, state->set_expected_next(CERTIFICATE); } - secure_renegotiation.update(state->server_hello); - /* * If the client doesn't have a cert they want to use they are * allowed to send either an empty cert message or proceed @@ -397,11 +482,6 @@ void Server::process_handshake_msg(Handshake_Type type, { state->client_certs = new Certificate(contents); - // Is this allowed by the protocol? - if(state->client_certs->count() > 1) - throw TLS_Exception(Alert::CERTIFICATE_UNKNOWN, - "Client sent more than one certificate"); - state->set_expected_next(CLIENT_KEX); } else if(type == CLIENT_KEX) @@ -419,11 +499,10 @@ void Server::process_handshake_msg(Handshake_Type type, { state->client_verify = new Certificate_Verify(contents, state->version()); - const std::vector<X509_Certificate>& client_certs = - state->client_certs->cert_chain(); + peer_certs = state->client_certs->cert_chain(); const bool sig_valid = - state->client_verify->verify(client_certs[0], state); + state->client_verify->verify(peer_certs[0], state); state->hash.update(type, contents); @@ -437,7 +516,7 @@ void Server::process_handshake_msg(Handshake_Type type, try { - creds.verify_certificate_chain("tls-server", "", client_certs); + creds.verify_certificate_chain("tls-server", "", peer_certs); } catch(std::exception& e) { @@ -492,7 +571,7 @@ void Server::process_handshake_msg(Handshake_Type type, peer_certs, MemoryVector<byte>(), m_hostname, - "" + state->srp_identifier() ); if(handshake_fn(session_info)) @@ -505,7 +584,8 @@ void Server::process_handshake_msg(Handshake_Type type, state->new_session_ticket = new New_Session_Ticket(writer, state->hash, - session_info.encrypt(ticket_key, rng)); + session_info.encrypt(ticket_key, rng), + policy.session_ticket_lifetime()); } catch(...) {} } @@ -522,9 +602,6 @@ void Server::process_handshake_msg(Handshake_Type type, state->server_hello->compression_method()); state->server_finished = new Finished(writer, state, SERVER); - - if(state->client_certs && state->client_verify) - peer_certs = state->client_certs->cert_chain(); } secure_renegotiation.update(state->client_finished, diff --git a/src/tls/tls_server.h b/src/tls/tls_server.h index a90982066..6ade91afc 100644 --- a/src/tls/tls_server.h +++ b/src/tls/tls_server.h @@ -26,9 +26,9 @@ class BOTAN_DLL Server : public Channel /** * Server initialization */ - Server(std::function<void (const byte[], size_t)> socket_output_fn, - std::function<void (const byte[], size_t, Alert)> proc_fn, - std::function<bool (const Session&)> handshake_complete, + Server(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, @@ -36,7 +36,7 @@ class BOTAN_DLL Server : public Channel const std::vector<std::string>& protocols = std::vector<std::string>()); - void renegotiate(); + void renegotiate(bool force_full_renegotiation); /** * Return the server name indicator, if sent by the client diff --git a/src/tls/tls_session.cpp b/src/tls/tls_session.cpp index cec1c87ae..0e8bf3051 100644 --- a/src/tls/tls_session.cpp +++ b/src/tls/tls_session.cpp @@ -10,6 +10,7 @@ #include <botan/ber_dec.h> #include <botan/asn1_str.h> #include <botan/pem.h> +#include <botan/time.h> #include <botan/lookup.h> #include <botan/loadstor.h> #include <memory> @@ -30,7 +31,7 @@ Session::Session(const MemoryRegion<byte>& session_identifier, const MemoryRegion<byte>& ticket, const std::string& sni_hostname, const std::string& srp_identifier) : - m_start_time(std::chrono::system_clock::now()), + m_start_time(system_time()), m_identifier(session_identifier), m_session_ticket(ticket), m_master_secret(master_secret), @@ -63,13 +64,11 @@ Session::Session(const byte ber[], size_t ber_len) MemoryVector<byte> peer_cert_bits; - size_t start_time = 0; - BER_Decoder(ber, ber_len) .start_cons(SEQUENCE) .decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION), "Unknown version in session structure") - .decode_integer_type(start_time) + .decode_integer_type(m_start_time) .decode_integer_type(major_version) .decode_integer_type(minor_version) .decode(m_identifier, OCTET_STRING) @@ -87,7 +86,6 @@ Session::Session(const byte ber[], size_t ber_len) .verify_end(); m_version = Protocol_Version(major_version, minor_version); - m_start_time = std::chrono::system_clock::from_time_t(start_time); m_sni_hostname = sni_hostname_str.value(); m_srp_identifier = srp_identifier_str.value(); m_connection_side = static_cast<Connection_Side>(side_code); @@ -110,7 +108,7 @@ SecureVector<byte> Session::DER_encode() const return DER_Encoder() .start_cons(SEQUENCE) .encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION)) - .encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time))) + .encode(static_cast<size_t>(m_start_time)) .encode(static_cast<size_t>(m_version.major_version())) .encode(static_cast<size_t>(m_version.minor_version())) .encode(m_identifier, OCTET_STRING) @@ -133,6 +131,11 @@ std::string Session::PEM_encode() const return PEM_Code::encode(this->DER_encode(), "SSL SESSION"); } +u32bit Session::session_age() const + { + return (system_time() - m_start_time); + } + namespace { const u32bit SESSION_CRYPTO_MAGIC = 0x571B0E4E; @@ -152,7 +155,7 @@ MemoryVector<byte> Session::encrypt(const SymmetricKey& master_key, RandomNumberGenerator& rng) const { - std::unique_ptr<KDF> kdf(get_kdf(SESSION_CRYPTO_KDF)); + std::auto_ptr<KDF> kdf(get_kdf(SESSION_CRYPTO_KDF)); SymmetricKey cipher_key = kdf->derive_key(CIPHER_KEY_LENGTH, @@ -166,7 +169,7 @@ Session::encrypt(const SymmetricKey& master_key, InitializationVector cipher_iv(rng, 16); - std::unique_ptr<MessageAuthenticationCode> mac(get_mac(SESSION_CRYPTO_MAC)); + std::auto_ptr<MessageAuthenticationCode> mac(get_mac(SESSION_CRYPTO_MAC)); mac->set_key(mac_key); Pipe pipe(get_cipher(SESSION_CRYPTO_CIPHER, cipher_key, cipher_iv, ENCRYPTION)); @@ -200,14 +203,14 @@ Session Session::decrypt(const byte buf[], size_t buf_len, if(load_be<u32bit>(buf, 0) != SESSION_CRYPTO_MAGIC) throw Decoding_Error("Unknown header value in encrypted session"); - std::unique_ptr<KDF> kdf(get_kdf(SESSION_CRYPTO_KDF)); + std::auto_ptr<KDF> kdf(get_kdf(SESSION_CRYPTO_KDF)); SymmetricKey mac_key = kdf->derive_key(MAC_KEY_LENGTH, master_key.bits_of(), "tls.session.mac-key"); - std::unique_ptr<MessageAuthenticationCode> mac(get_mac(SESSION_CRYPTO_MAC)); + std::auto_ptr<MessageAuthenticationCode> mac(get_mac(SESSION_CRYPTO_MAC)); mac->set_key(mac_key); mac->update(&buf[0], buf_len - MAC_OUTPUT_LENGTH); diff --git a/src/tls/tls_session.h b/src/tls/tls_session.h index 8fc048c75..290ee6dcc 100644 --- a/src/tls/tls_session.h +++ b/src/tls/tls_session.h @@ -5,8 +5,8 @@ * Released under the terms of the Botan license */ -#ifndef TLS_SESSION_STATE_H__ -#define TLS_SESSION_STATE_H__ +#ifndef BOTAN_TLS_SESSION_STATE_H__ +#define BOTAN_TLS_SESSION_STATE_H__ #include <botan/x509cert.h> #include <botan/tls_version.h> @@ -14,7 +14,6 @@ #include <botan/tls_magic.h> #include <botan/secmem.h> #include <botan/symkey.h> -#include <chrono> namespace Botan { @@ -31,7 +30,7 @@ class BOTAN_DLL Session * Uninitialized session */ Session() : - m_start_time(std::chrono::system_clock::time_point::min()), + m_start_time(0), m_version(), m_ciphersuite(0), m_compression_method(0), @@ -175,8 +174,12 @@ class BOTAN_DLL Session /** * Get the time this session began (seconds since Epoch) */ - std::chrono::system_clock::time_point start_time() const - { return m_start_time; } + u64bit start_time() const { return m_start_time; } + + /** + * Return how long this session has existed (in seconds) + */ + u32bit session_age() const; /** * Return the session ticket the server gave us @@ -186,7 +189,7 @@ class BOTAN_DLL Session private: enum { TLS_SESSION_PARAM_STRUCT_VERSION = 0x2994e300 }; - std::chrono::system_clock::time_point m_start_time; + u64bit m_start_time; MemoryVector<byte> m_identifier; MemoryVector<byte> m_session_ticket; // only used by client side diff --git a/src/tls/tls_session_manager.cpp b/src/tls/tls_session_manager.cpp index 4c6bc1a47..69823e8bd 100644 --- a/src/tls/tls_session_manager.cpp +++ b/src/tls/tls_session_manager.cpp @@ -20,7 +20,7 @@ bool Session_Manager_In_Memory::load_from_session_str( auto i = sessions.find(session_str); - if(i == sessions.end()) + if(i == m_sessions.end()) return false; // if session has expired, remove it @@ -28,7 +28,7 @@ bool Session_Manager_In_Memory::load_from_session_str( if(i->second.start_time() + session_lifetime < now) { - sessions.erase(i); + m_sessions.erase(i); return false; } @@ -75,8 +75,8 @@ void Session_Manager_In_Memory::remove_entry( auto i = sessions.find(hex_encode(session_id)); - if(i != sessions.end()) - sessions.erase(i); + if(i != m_sessions.end()) + m_sessions.erase(i); } void Session_Manager_In_Memory::save(const Session& session) @@ -89,8 +89,8 @@ void Session_Manager_In_Memory::save(const Session& session) This removes randomly based on ordering of session ids. Instead, remove oldest first? */ - while(sessions.size() >= max_sessions) - sessions.erase(sessions.begin()); + while(m_sessions.size() >= m_max_sessions) + m_sessions.erase(m_sessions.begin()); } const std::string session_id_str = hex_encode(session.session_id()); diff --git a/src/tls/tls_session_manager.h b/src/tls/tls_session_manager.h index 4152f2392..8a4f31b78 100644 --- a/src/tls/tls_session_manager.h +++ b/src/tls/tls_session_manager.h @@ -5,8 +5,8 @@ * Released under the terms of the Botan license */ -#ifndef TLS_SESSION_MANAGER_H__ -#define TLS_SESSION_MANAGER_H__ +#ifndef BOTAN_TLS_SESSION_MANAGER_H__ +#define BOTAN_TLS_SESSION_MANAGER_H__ #include <botan/tls_session.h> #include <mutex> @@ -65,6 +65,13 @@ class BOTAN_DLL Session_Manager */ virtual void save(const Session& session) = 0; + /** + * Return the allowed lifetime of a session; beyond this time, + * sessions are not resumed. Returns 0 if unknown/no explicit + * expiration policy. + */ + virtual u32bit session_lifetime() const = 0; + virtual ~Session_Manager() {} }; @@ -82,9 +89,9 @@ class BOTAN_DLL Session_Manager_In_Memory : public Session_Manager * seconds have elapsed from initial handshake. */ Session_Manager_In_Memory(size_t max_sessions = 1000, - std::chrono::seconds session_lifetime = std::chrono::seconds(7200)) : - max_sessions(max_sessions), - session_lifetime(session_lifetime) + u32bit session_lifetime = 7200) : + m_max_sessions(max_sessions), + m_session_lifetime(session_lifetime) {} bool load_from_session_id(const MemoryRegion<byte>& session_id, @@ -97,17 +104,18 @@ class BOTAN_DLL Session_Manager_In_Memory : public Session_Manager void save(const Session& session_data); + u32bit session_lifetime() const { return m_session_lifetime; } + private: bool load_from_session_str(const std::string& session_str, Session& session); - std::mutex mutex; + size_t m_max_sessions; - size_t max_sessions; - std::chrono::seconds session_lifetime; + u32bit m_session_lifetime; - std::map<std::string, Session> sessions; // hex(session_id) -> session - std::map<std::string, std::string> host_sessions; + std::map<std::string, Session> m_sessions; // hex(session_id) -> session + std::map<std::string, std::string> m_host_sessions; }; } diff --git a/src/tls/tls_suite_info.cpp b/src/tls/tls_suite_info.cpp new file mode 100644 index 000000000..997bda428 --- /dev/null +++ b/src/tls/tls_suite_info.cpp @@ -0,0 +1,317 @@ +/* +* TLS Cipher Suite +* (C) 2004-2010,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_ciphersuite.h> + +namespace Botan { + +namespace TLS { + +Ciphersuite Ciphersuite::by_id(u16bit suite) + { + // Automatically generated by a Python script from the IANA values + + switch(suite) + { + case 0x0013: // DHE_DSS_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "3DES", 24); + + case 0x0032: // DHE_DSS_WITH_AES_128_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "AES-128", 16); + + case 0x0040: // DHE_DSS_WITH_AES_128_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "AES-128", 16); + + case 0x0038: // DHE_DSS_WITH_AES_256_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "AES-256", 32); + + case 0x006A: // DHE_DSS_WITH_AES_256_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "AES-256", 32); + + case 0x0044: // DHE_DSS_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "Camellia-128", 16); + + case 0x00BD: // DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "Camellia-128", 16); + + case 0x0087: // DHE_DSS_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "Camellia-256", 32); + + case 0x00C3: // DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "Camellia-256", 32); + + case 0x0066: // DHE_DSS_WITH_RC4_128_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "ARC4", 16); + + case 0x0099: // DHE_DSS_WITH_SEED_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "SEED", 16); + + case 0x008F: // DHE_PSK_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "3DES", 24); + + case 0x0090: // DHE_PSK_WITH_AES_128_CBC_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-128", 16); + + case 0x00B2: // DHE_PSK_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "DHE_PSK", "SHA-256", "AES-128", 16); + + case 0x0091: // DHE_PSK_WITH_AES_256_CBC_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-256", 32); + + case 0x00B3: // DHE_PSK_WITH_AES_256_CBC_SHA384 + return Ciphersuite("", "DHE_PSK", "SHA-384", "AES-256", 32); + + case 0xC096: // DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "DHE_PSK", "SHA-256", "Camellia-128", 16); + + case 0xC097: // DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("", "DHE_PSK", "SHA-384", "Camellia-256", 32); + + case 0x008E: // DHE_PSK_WITH_RC4_128_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "ARC4", 16); + + case 0x0016: // DHE_RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "3DES", 24); + + case 0x0033: // DHE_RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "AES-128", 16); + + case 0x0067: // DHE_RSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "AES-128", 16); + + case 0x0039: // DHE_RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "AES-256", 32); + + case 0x006B: // DHE_RSA_WITH_AES_256_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "AES-256", 32); + + case 0x0045: // DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "Camellia-128", 16); + + case 0x00BE: // DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "Camellia-128", 16); + + case 0x0088: // DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "Camellia-256", 32); + + case 0x00C4: // DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "Camellia-256", 32); + + case 0x009A: // DHE_RSA_WITH_SEED_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "SEED", 16); + + case 0x001B: // DH_anon_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "3DES", 24); + + case 0x0034: // DH_anon_WITH_AES_128_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "AES-128", 16); + + case 0x006C: // DH_anon_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "AES-128", 16); + + case 0x003A: // DH_anon_WITH_AES_256_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "AES-256", 32); + + case 0x006D: // DH_anon_WITH_AES_256_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "AES-256", 32); + + case 0x0046: // DH_anon_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "Camellia-128", 16); + + case 0x00BF: // DH_anon_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "Camellia-128", 16); + + case 0x0089: // DH_anon_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "Camellia-256", 32); + + case 0x00C5: // DH_anon_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "Camellia-256", 32); + + case 0x0018: // DH_anon_WITH_RC4_128_MD5 + return Ciphersuite("", "DH", "MD5", "ARC4", 16); + + case 0x009B: // DH_anon_WITH_SEED_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "SEED", 16); + + case 0xC008: // ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "3DES", 24); + + case 0xC009: // ECDHE_ECDSA_WITH_AES_128_CBC_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "AES-128", 16); + + case 0xC023: // ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("ECDSA", "ECDH", "SHA-256", "AES-128", 16); + + case 0xC00A: // ECDHE_ECDSA_WITH_AES_256_CBC_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "AES-256", 32); + + case 0xC024: // ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + return Ciphersuite("ECDSA", "ECDH", "SHA-384", "AES-256", 32); + + case 0xC072: // ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("ECDSA", "ECDH", "SHA-256", "Camellia-128", 16); + + case 0xC073: // ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("ECDSA", "ECDH", "SHA-384", "Camellia-256", 32); + + case 0xC007: // ECDHE_ECDSA_WITH_RC4_128_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "ARC4", 16); + + case 0xC034: // ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "3DES", 24); + + case 0xC035: // ECDHE_PSK_WITH_AES_128_CBC_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "AES-128", 16); + + case 0xC037: // ECDHE_PSK_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "ECDHE_PSK", "SHA-256", "AES-128", 16); + + case 0xC036: // ECDHE_PSK_WITH_AES_256_CBC_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "AES-256", 32); + + case 0xC038: // ECDHE_PSK_WITH_AES_256_CBC_SHA384 + return Ciphersuite("", "ECDHE_PSK", "SHA-384", "AES-256", 32); + + case 0xC09A: // ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "ECDHE_PSK", "SHA-256", "Camellia-128", 16); + + case 0xC09B: // ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("", "ECDHE_PSK", "SHA-384", "Camellia-256", 32); + + case 0xC033: // ECDHE_PSK_WITH_RC4_128_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "ARC4", 16); + + case 0xC012: // ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "3DES", 24); + + case 0xC013: // ECDHE_RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "AES-128", 16); + + case 0xC027: // ECDHE_RSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("RSA", "ECDH", "SHA-256", "AES-128", 16); + + case 0xC014: // ECDHE_RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "AES-256", 32); + + case 0xC028: // ECDHE_RSA_WITH_AES_256_CBC_SHA384 + return Ciphersuite("RSA", "ECDH", "SHA-384", "AES-256", 32); + + case 0xC076: // ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("RSA", "ECDH", "SHA-256", "Camellia-128", 16); + + case 0xC077: // ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("RSA", "ECDH", "SHA-384", "Camellia-256", 32); + + case 0xC011: // ECDHE_RSA_WITH_RC4_128_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "ARC4", 16); + + case 0xC017: // ECDH_anon_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "ECDH", "SHA-1", "3DES", 24); + + case 0xC018: // ECDH_anon_WITH_AES_128_CBC_SHA + return Ciphersuite("", "ECDH", "SHA-1", "AES-128", 16); + + case 0xC019: // ECDH_anon_WITH_AES_256_CBC_SHA + return Ciphersuite("", "ECDH", "SHA-1", "AES-256", 32); + + case 0xC016: // ECDH_anon_WITH_RC4_128_SHA + return Ciphersuite("", "ECDH", "SHA-1", "ARC4", 16); + + case 0x008B: // PSK_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "PSK", "SHA-1", "3DES", 24); + + case 0x008C: // PSK_WITH_AES_128_CBC_SHA + return Ciphersuite("", "PSK", "SHA-1", "AES-128", 16); + + case 0x00AE: // PSK_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "PSK", "SHA-256", "AES-128", 16); + + case 0x008D: // PSK_WITH_AES_256_CBC_SHA + return Ciphersuite("", "PSK", "SHA-1", "AES-256", 32); + + case 0x00AF: // PSK_WITH_AES_256_CBC_SHA384 + return Ciphersuite("", "PSK", "SHA-384", "AES-256", 32); + + case 0xC094: // PSK_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "PSK", "SHA-256", "Camellia-128", 16); + + case 0xC095: // PSK_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("", "PSK", "SHA-384", "Camellia-256", 32); + + case 0x008A: // PSK_WITH_RC4_128_SHA + return Ciphersuite("", "PSK", "SHA-1", "ARC4", 16); + + case 0x000A: // RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "3DES", 24); + + case 0x002F: // RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "AES-128", 16); + + case 0x003C: // RSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "AES-128", 16); + + case 0x0035: // RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "AES-256", 32); + + case 0x003D: // RSA_WITH_AES_256_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "AES-256", 32); + + case 0x0041: // RSA_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "Camellia-128", 16); + + case 0x00BA: // RSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "Camellia-128", 16); + + case 0x0084: // RSA_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "Camellia-256", 32); + + case 0x00C0: // RSA_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "Camellia-256", 32); + + case 0x0004: // RSA_WITH_RC4_128_MD5 + return Ciphersuite("RSA", "RSA", "MD5", "ARC4", 16); + + case 0x0005: // RSA_WITH_RC4_128_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "ARC4", 16); + + case 0x0096: // RSA_WITH_SEED_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "SEED", 16); + + case 0xC01C: // SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("DSA", "SRP_SHA", "SHA-1", "3DES", 24); + + case 0xC01F: // SRP_SHA_DSS_WITH_AES_128_CBC_SHA + return Ciphersuite("DSA", "SRP_SHA", "SHA-1", "AES-128", 16); + + case 0xC022: // SRP_SHA_DSS_WITH_AES_256_CBC_SHA + return Ciphersuite("DSA", "SRP_SHA", "SHA-1", "AES-256", 32); + + case 0xC01B: // SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "SRP_SHA", "SHA-1", "3DES", 24); + + case 0xC01E: // SRP_SHA_RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "SRP_SHA", "SHA-1", "AES-128", 16); + + case 0xC021: // SRP_SHA_RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "SRP_SHA", "SHA-1", "AES-256", 32); + + case 0xC01A: // SRP_SHA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "SRP_SHA", "SHA-1", "3DES", 24); + + case 0xC01D: // SRP_SHA_WITH_AES_128_CBC_SHA + return Ciphersuite("", "SRP_SHA", "SHA-1", "AES-128", 16); + + case 0xC020: // SRP_SHA_WITH_AES_256_CBC_SHA + return Ciphersuite("", "SRP_SHA", "SHA-1", "AES-256", 32); + } + + return Ciphersuite(); // some unknown ciphersuite + } + +} + +} |