diff options
29 files changed, 1256 insertions, 942 deletions
diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index 6af2f56f8..7db8bacf8 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -118,17 +119,19 @@ class TLS_Client final : public Command version = Botan::TLS::Protocol_Version::TLS_V11; } - Botan::TLS::Client client(socket_write, - std::bind(&TLS_Client::process_data, this, _1, _2), - std::bind(&TLS_Client::alert_received, this, _1, _2, _3), - std::bind(&TLS_Client::handshake_complete, this, _1), + Botan::TLS::Client client(Botan::TLS::Client::Callbacks( + socket_write, + std::bind(&TLS_Client::process_data, this, _1, _2), + std::bind(&TLS_Client::alert_received, this, _1, _2, _3), + std::bind(&TLS_Client::handshake_complete, this, _1)), *session_mgr, creds, *policy, rng(), - Botan::TLS::Server_Information(host, port), - version, - protocols_to_offer); + Botan::TLS::Client::Properties( + Botan::TLS::Server_Information(host, port), + version, + protocols_to_offer)); bool first_active = true; diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp index 2929e473d..f5f666cf1 100644 --- a/src/cli/tls_proxy.cpp +++ b/src/cli/tls_proxy.cpp @@ -1,6 +1,7 @@ /* * TLS Server Proxy * (C) 2014,2015 Jack Lloyd +* (C) 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp index 2496f5508..335f36c97 100644 --- a/src/cli/tls_server.cpp +++ b/src/cli/tls_server.cpp @@ -1,6 +1,7 @@ /* * TLS echo server using BSD sockets * (C) 2014 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -106,10 +107,11 @@ class TLS_Server final : public Command } }; - Botan::TLS::Server server(socket_write, - proc_fn, - std::bind(&TLS_Server::alert_received, this, _1, _2, _3), - std::bind(&TLS_Server::handshake_complete, this, _1), + Botan::TLS::Server server(Botan::TLS::Server::Callbacks( + socket_write, + proc_fn, + std::bind(&TLS_Server::alert_received, this, _1, _2, _3), + std::bind(&TLS_Server::handshake_complete, this, _1)), session_manager, creds, policy, diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp index 147fdd6ad..3f7af77f5 100644 --- a/src/lib/cert/x509/x509_ca.cpp +++ b/src/lib/cert/x509/x509_ca.cpp @@ -78,8 +78,10 @@ X509_Certificate X509_CA::sign_request(const PKCS10_Request& req, return make_cert(m_signer, rng, m_ca_sig_algo, req.raw_public_key(), - not_before, not_after, - m_cert.subject_dn(), req.subject_dn(), + Certificate_Properties(not_before, + not_after, + m_cert.subject_dn(), + req.subject_dn()), extensions); } @@ -90,10 +92,7 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer, RandomNumberGenerator& rng, const AlgorithmIdentifier& sig_algo, const std::vector<byte>& pub_key, - const X509_Time& not_before, - const X509_Time& not_after, - const X509_DN& issuer_dn, - const X509_DN& subject_dn, + const Certificate_Properties properties, const Extensions& extensions) { const size_t X509_CERT_VERSION = 3; @@ -112,14 +111,14 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer, .encode(serial_no) .encode(sig_algo) - .encode(issuer_dn) + .encode(properties.get_issuer_dn()) .start_cons(SEQUENCE) - .encode(not_before) - .encode(not_after) + .encode(properties.get_not_before()) + .encode(properties.get_not_after()) .end_cons() - .encode(subject_dn) + .encode(properties.get_subject_dn()) .raw_bytes(pub_key) .start_explicit(3) diff --git a/src/lib/cert/x509/x509_ca.h b/src/lib/cert/x509/x509_ca.h index 6ea51cd06..8cedb9db9 100644 --- a/src/lib/cert/x509/x509_ca.h +++ b/src/lib/cert/x509/x509_ca.h @@ -22,6 +22,25 @@ namespace Botan { class BOTAN_DLL X509_CA { public: + class Certificate_Properties + { + public: + Certificate_Properties(X509_Time not_before, X509_Time not_after, + X509_DN issuer_dn, X509_DN subject_dn) + : m_not_before(not_before), m_not_after(not_after), + m_issuer_dn(issuer_dn), m_subject_dn(subject_dn) {} + + const X509_Time& get_not_before() const { return m_not_before; } + const X509_Time& get_not_after() const { return m_not_after; } + const X509_DN& get_issuer_dn() const { return m_issuer_dn; } + const X509_DN& get_subject_dn() const { return m_subject_dn; } + + private: + X509_Time m_not_before; + X509_Time m_not_after; + X509_DN m_issuer_dn; + X509_DN m_subject_dn; + }; /** * Sign a PKCS#10 Request. @@ -82,10 +101,7 @@ class BOTAN_DLL X509_CA RandomNumberGenerator& rng, const AlgorithmIdentifier& sig_algo, const std::vector<byte>& pub_key, - const X509_Time& not_before, - const X509_Time& not_after, - const X509_DN& issuer_dn, - const X509_DN& subject_dn, + const Certificate_Properties properties, const Extensions& extensions); /** diff --git a/src/lib/cert/x509/x509self.cpp b/src/lib/cert/x509/x509self.cpp index 8b9aeda09..62f9fc370 100644 --- a/src/lib/cert/x509/x509self.cpp +++ b/src/lib/cert/x509/x509self.cpp @@ -75,9 +75,14 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, extensions.add( new Cert_Extension::Extended_Key_Usage(opts.ex_constraints)); - return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key, - opts.start, opts.end, - subject_dn, subject_dn, + return X509_CA::make_cert(signer.get(), + rng, + sig_algo, + pub_key, + X509_CA::Certificate_Properties(opts.start, + opts.end, + subject_dn, + subject_dn), extensions); } diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index a43d5619a..de15a65e5 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -29,6 +29,7 @@ tls_messages.h tls_reader.h tls_record.h tls_seq_numbers.h +tls_server_handshake_state.h tls_session_key.h </header:internal> diff --git a/src/lib/tls/msg_cert_req.cpp b/src/lib/tls/msg_cert_req.cpp index 4fd528148..569a5aa63 100644 --- a/src/lib/tls/msg_cert_req.cpp +++ b/src/lib/tls/msg_cert_req.cpp @@ -1,6 +1,7 @@ /* * Certificate Request Message * (C) 2004-2006,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -51,8 +52,7 @@ byte cert_type_name_to_code(const std::string& name) /** * Create a new Certificate Request message */ -Certificate_Req::Certificate_Req(Handshake_IO& io, - Handshake_Hash& hash, +Certificate_Req::Certificate_Req(Handshake_Info& hs_info, const Policy& policy, const std::vector<X509_DN>& ca_certs, Protocol_Version version) : @@ -69,7 +69,7 @@ Certificate_Req::Certificate_Req(Handshake_IO& io, m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j])); } - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /** diff --git a/src/lib/tls/msg_certificate.cpp b/src/lib/tls/msg_certificate.cpp index 32e3e17f0..a622d8573 100644 --- a/src/lib/tls/msg_certificate.cpp +++ b/src/lib/tls/msg_certificate.cpp @@ -1,6 +1,7 @@ /* * Certificate Message * (C) 2004-2006,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -20,12 +21,11 @@ namespace TLS { /** * Create a new Certificate message */ -Certificate::Certificate(Handshake_IO& io, - Handshake_Hash& hash, +Certificate::Certificate(Handshake_Info& hs_info, const std::vector<X509_Certificate>& cert_list) : m_certs(cert_list) { - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /** diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp index 23807215f..9c080b9a5 100644 --- a/src/lib/tls/msg_client_hello.cpp +++ b/src/lib/tls/msg_client_hello.cpp @@ -1,6 +1,7 @@ /* * TLS Hello Request and Client Hello Messages * (C) 2004-2011,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -66,24 +67,21 @@ std::vector<byte> Hello_Request::serialize() const /* * Create a new Client Hello message */ -Client_Hello::Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - Protocol_Version version, +Client_Hello::Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, - const std::vector<std::string>& next_protocols, - const std::string& hostname, - const std::string& srp_identifier) : - m_version(version), + const Client_Hello::Settings& client_settings, + const std::vector<std::string>& next_protocols) : + m_version(client_settings.protocol_version()), m_random(make_hello_random(rng, policy)), - m_suites(policy.ciphersuite_list(m_version, (srp_identifier != ""))), + m_suites(policy.ciphersuite_list(m_version, + client_settings.srp_identifier() != "")), m_comp_methods(policy.compression()) { m_extensions.add(new Extended_Master_Secret); m_extensions.add(new Renegotiation_Extension(reneg_info)); - - m_extensions.add(new Server_Name_Indicator(hostname)); + m_extensions.add(new Server_Name_Indicator(client_settings.hostname())); m_extensions.add(new Session_Ticket()); m_extensions.add(new Supported_Elliptic_Curves(policy.allowed_ecc_curves())); @@ -98,7 +96,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols)); #if defined(BOTAN_HAS_SRP6) - m_extensions.add(new SRP_Identifier(srp_identifier)); + m_extensions.add(new SRP_Identifier(client_settings.srp_identifier())); #else if(!srp_identifier.empty()) { @@ -106,20 +104,19 @@ Client_Hello::Client_Hello(Handshake_IO& io, } #endif - BOTAN_ASSERT(policy.acceptable_protocol_version(version), + BOTAN_ASSERT(policy.acceptable_protocol_version(client_settings.protocol_version()), "Our policy accepts the version we are offering"); - if(policy.send_fallback_scsv(version)) + if(policy.send_fallback_scsv(client_settings.protocol_version())) m_suites.push_back(TLS_FALLBACK_SCSV); - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /* * Create a new Client Hello message (session resumption case) */ -Client_Hello::Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, +Client_Hello::Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, @@ -165,7 +162,7 @@ Client_Hello::Client_Hello(Handshake_IO& io, } #endif - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } void Client_Hello::update_hello_cookie(const Hello_Verify_Request& hello_verify) diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp index f8d0c63c7..2c80ed59a 100644 --- a/src/lib/tls/msg_server_hello.cpp +++ b/src/lib/tls/msg_server_hello.cpp @@ -1,6 +1,7 @@ /* * TLS Server Hello and Server Hello Done * (C) 2004-2011,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -17,23 +18,18 @@ namespace Botan { namespace TLS { // New session case -Server_Hello::Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, +Server_Hello::Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, const Client_Hello& client_hello, - const std::vector<byte>& new_session_id, - Protocol_Version new_session_version, - u16bit ciphersuite, - byte compression, - bool offer_session_ticket, - const std::string& next_protocol) : - m_version(new_session_version), - m_session_id(new_session_id), + const Server_Hello::Settings& server_settings, + const std::string next_protocol) : + m_version(server_settings.protocol_version()), + m_session_id(server_settings.session_id()), m_random(make_hello_random(rng, policy)), - m_ciphersuite(ciphersuite), - m_comp_method(compression) + m_ciphersuite(server_settings.ciphersuite()), + m_comp_method(server_settings.compression()) { if(client_hello.supports_extended_master_secret()) m_extensions.add(new Extended_Master_Secret); @@ -41,7 +37,7 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(client_hello.secure_renegotiation()) m_extensions.add(new Renegotiation_Extension(reneg_info)); - if(client_hello.supports_session_ticket() && offer_session_ticket) + if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket()) m_extensions.add(new Session_Ticket()); if(!next_protocol.empty() && client_hello.supports_alpn()) @@ -68,12 +64,11 @@ Server_Hello::Server_Hello(Handshake_IO& io, } } - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } // Resuming -Server_Hello::Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, +Server_Hello::Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, @@ -99,7 +94,7 @@ Server_Hello::Server_Hello(Handshake_IO& io, if(!next_protocol.empty() && client_hello.supports_alpn()) m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol)); - hash.update(io.send(*this)); + hs_info.get_hash().update(hs_info.get_io().send(*this)); } /* diff --git a/src/lib/tls/tls_blocking.cpp b/src/lib/tls/tls_blocking.cpp index a1867b6b5..14e04693d 100644 --- a/src/lib/tls/tls_blocking.cpp +++ b/src/lib/tls/tls_blocking.cpp @@ -1,6 +1,7 @@ /* * TLS Blocking API * (C) 2013 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -19,21 +20,19 @@ Blocking_Client::Blocking_Client(read_fn reader, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - const Server_Information& server_info, - const Protocol_Version& offer_version, - const std::vector<std::string>& next) : + TLS::Client::Properties& properties) : m_read(reader), - m_channel(writer, - std::bind(&Blocking_Client::data_cb, this, _1, _2), - std::bind(&Blocking_Client::alert_cb, this, _1, _2, _3), - std::bind(&Blocking_Client::handshake_cb, this, _1), + m_channel(TLS::Client::Callbacks( + writer, + std::bind(&Blocking_Client::data_cb, this, _1, _2), + std::bind(&Blocking_Client::alert_cb, this, _1, _2, _3), + std::bind(&Blocking_Client::handshake_cb, this, _1) + ), session_manager, creds, policy, rng, - server_info, - offer_version, - next) + properties) { } diff --git a/src/lib/tls/tls_blocking.h b/src/lib/tls/tls_blocking.h index 00e65cbaf..47c5c7483 100644 --- a/src/lib/tls/tls_blocking.h +++ b/src/lib/tls/tls_blocking.h @@ -1,6 +1,7 @@ /* * TLS Blocking API * (C) 2013 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -38,9 +39,7 @@ class BOTAN_DLL Blocking_Client Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - const Server_Information& server_info = Server_Information(), - const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(), - const std::vector<std::string>& next_protos = {}); + TLS::Client::Properties& properties); /** * Completes full handshake then returns diff --git a/src/lib/tls/tls_channel.cpp b/src/lib/tls/tls_channel.cpp index f445eef99..2c7e80feb 100644 --- a/src/lib/tls/tls_channel.cpp +++ b/src/lib/tls/tls_channel.cpp @@ -1,6 +1,7 @@ /* * TLS Channels * (C) 2011,2012,2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -18,22 +19,16 @@ namespace Botan { namespace TLS { -Channel::Channel(output_fn output_fn, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb handshake_msg_cb, +size_t TLS::Channel::IO_BUF_DEFAULT_SIZE = 10*1024; + +Channel::Channel(const Callbacks& callbacks, Session_Manager& session_manager, RandomNumberGenerator& rng, const Policy& policy, bool is_datagram, size_t reserved_io_buffer_size) : m_is_datagram(is_datagram), - m_data_cb(data_cb), - m_alert_cb(alert_cb), - m_output_fn(output_fn), - m_handshake_cb(handshake_cb), - m_handshake_msg_cb(handshake_msg_cb), + m_callbacks(callbacks), m_session_manager(session_manager), m_policy(policy), m_rng(rng) @@ -263,23 +258,19 @@ size_t Channel::received_data(const byte input[], size_t input_size) { while(!is_closed() && input_size) { - secure_vector<byte> record; + secure_vector<byte> record_data; u64bit record_sequence = 0; Record_Type record_type = NO_RECORD; Protocol_Version record_version; size_t consumed = 0; + Record_Raw_Input raw_input(input, input_size, consumed, m_is_datagram); + Record record(record_data, &record_sequence, &record_version, &record_type); const size_t needed = read_record(m_readbuf, - input, - input_size, - m_is_datagram, - consumed, + raw_input, record, - &record_sequence, - &record_version, - &record_type, m_sequence_numbers.get(), std::bind(&TLS::Channel::read_cipher_state_epoch, this, std::placeholders::_1)); @@ -298,105 +289,21 @@ size_t Channel::received_data(const byte input[], size_t input_size) if(input_size == 0 && needed != 0) return needed; // need more data to complete record - if(record.size() > MAX_PLAINTEXT_SIZE) + if(record_data.size() > MAX_PLAINTEXT_SIZE) throw TLS_Exception(Alert::RECORD_OVERFLOW, "TLS plaintext record is larger than allowed maximum"); if(record_type == HANDSHAKE || record_type == CHANGE_CIPHER_SPEC) { - if(!m_pending_state) - { - // No pending handshake, possibly new: - if(record_version.is_datagram_protocol()) - { - if(m_sequence_numbers) - { - /* - * Might be a peer retransmit under epoch - 1 in which - * case we must retransmit last flight - */ - sequence_numbers().read_accept(record_sequence); - - const u16bit epoch = record_sequence >> 48; - - if(epoch == sequence_numbers().current_read_epoch()) - { - create_handshake_state(record_version); - } - else if(epoch == sequence_numbers().current_read_epoch() - 1) - { - BOTAN_ASSERT(m_active_state, "Have active state here"); - m_active_state->handshake_io().add_record(unlock(record), - record_type, - record_sequence); - } - } - else if(record_sequence == 0) - { - create_handshake_state(record_version); - } - } - else - { - create_handshake_state(record_version); - } - } - - // May have been created in above conditional - if(m_pending_state) - { - m_pending_state->handshake_io().add_record(unlock(record), - record_type, - record_sequence); - - while(auto pending = m_pending_state.get()) - { - auto msg = pending->get_next_handshake_msg(); - - if(msg.first == HANDSHAKE_NONE) // no full handshake yet - break; - - process_handshake_msg(active_state(), *pending, - msg.first, msg.second); - } - } + process_handshake_ccs(record_data, record_sequence, record_type, record_version); } else if(record_type == APPLICATION_DATA) { - if(!active_state()) - throw Unexpected_Message("Application data before handshake done"); - - /* - * OpenSSL among others sends empty records in versions - * before TLS v1.1 in order to randomize the IV of the - * following record. Avoid spurious callbacks. - */ - if(record.size() > 0) - m_data_cb(record.data(), record.size()); + process_application_data(record_data); } else if(record_type == ALERT) { - Alert alert_msg(record); - - if(alert_msg.type() == Alert::NO_RENEGOTIATION) - m_pending_state.reset(); - - m_alert_cb(alert_msg, nullptr, 0); - - if(alert_msg.is_fatal()) - { - if(auto active = active_state()) - m_session_manager.remove_entry(active->server_hello()->session_id()); - } - - if(alert_msg.type() == Alert::CLOSE_NOTIFY) - send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind - - if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal()) - { - reset_state(); - return 0; - } + process_alert(record_data); } else if(record_type != NO_RECORD) throw Unexpected_Message("Unexpected record type " + @@ -428,6 +335,108 @@ size_t Channel::received_data(const byte input[], size_t input_size) } } +void Channel::process_handshake_ccs(secure_vector<byte>& record, + u64bit& record_sequence, + Record_Type& record_type, + Protocol_Version& record_version) + { + if(!m_pending_state) + { + // No pending handshake, possibly new: + if(record_version.is_datagram_protocol()) + { + if(m_sequence_numbers) + { + /* + * Might be a peer retransmit under epoch - 1 in which + * case we must retransmit last flight + */ + sequence_numbers().read_accept(record_sequence); + + const u16bit epoch = record_sequence >> 48; + + if(epoch == sequence_numbers().current_read_epoch()) + { + create_handshake_state(record_version); + } + else if(epoch == sequence_numbers().current_read_epoch() - 1) + { + BOTAN_ASSERT(m_active_state, "Have active state here"); + m_active_state->handshake_io().add_record(unlock(record), + record_type, + record_sequence); + } + } + else if(record_sequence == 0) + { + create_handshake_state(record_version); + } + } + else + { + create_handshake_state(record_version); + } + } + + // May have been created in above conditional + if(m_pending_state) + { + m_pending_state->handshake_io().add_record(unlock(record), + record_type, + record_sequence); + + while(auto pending = m_pending_state.get()) + { + auto msg = pending->get_next_handshake_msg(); + + if(msg.first == HANDSHAKE_NONE) // no full handshake yet + break; + + process_handshake_msg(active_state(), *pending, + msg.first, msg.second); + } + } + } + +void Channel::process_application_data(secure_vector<byte>& record) + { + if(!active_state()) + throw Unexpected_Message("Application data before handshake done"); + + /* + * OpenSSL among others sends empty records in versions + * before TLS v1.1 in order to randomize the IV of the + * following record. Avoid spurious callbacks. + */ + if(record.size() > 0) + m_callbacks.app_data()(record.data(), record.size()); + } + +void Channel::process_alert(secure_vector<byte>& record) + { + Alert alert_msg(record); + + if(alert_msg.type() == Alert::NO_RENEGOTIATION) + m_pending_state.reset(); + + m_callbacks.alert()(alert_msg, nullptr, 0); + + if(alert_msg.is_fatal()) + { + if(auto active = active_state()) + m_session_manager.remove_entry(active->server_hello()->session_id()); + } + + if(alert_msg.type() == Alert::CLOSE_NOTIFY) + send_warning_alert(Alert::CLOSE_NOTIFY); // reply in kind + + if(alert_msg.type() == Alert::CLOSE_NOTIFY || alert_msg.is_fatal()) + { + reset_state(); + } + } + + void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch, byte record_type, const byte input[], size_t length) { @@ -436,16 +445,16 @@ void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch, Protocol_Version record_version = (m_pending_state) ? (m_pending_state->version()) : (m_active_state->version()); + Record_Message record_message(record_type, 0, input, length); + TLS::write_record(m_writebuf, - record_type, - input, - length, + record_message, record_version, sequence_numbers().next_write_sequence(epoch), cipher_state, m_rng); - m_output_fn(m_writebuf.data(), m_writebuf.size()); + m_callbacks.out_fn()(m_writebuf.data(), m_writebuf.size()); } void Channel::send_record_array(u16bit epoch, byte type, const byte input[], size_t length) diff --git a/src/lib/tls/tls_channel.h b/src/lib/tls/tls_channel.h index e0219c242..c9ea8edde 100644 --- a/src/lib/tls/tls_channel.h +++ b/src/lib/tls/tls_channel.h @@ -1,6 +1,7 @@ /* * TLS Channel * (C) 2011,2012,2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -32,22 +33,58 @@ class Handshake_Message; class BOTAN_DLL Channel { public: - typedef std::function<void (const byte[], size_t)> output_fn; - typedef std::function<void (const byte[], size_t)> data_cb; - typedef std::function<void (Alert, const byte[], size_t)> alert_cb; - typedef std::function<bool (const Session&)> handshake_cb; - typedef std::function<void (const Handshake_Message&)> handshake_msg_cb; - - Channel(output_fn out, - data_cb app_data_cb, - alert_cb alert_cb, - handshake_cb hs_cb, - handshake_msg_cb hs_msg_cb, + static size_t IO_BUF_DEFAULT_SIZE; + + class Callbacks + { + public: + typedef std::function<void (const byte[], size_t)> output_fn; + typedef std::function<void (const byte[], size_t)> data_cb; + typedef std::function<void (Alert, const byte[], size_t)> alert_cb; + typedef std::function<bool (const Session&)> handshake_cb; + typedef std::function<void (const Handshake_Message&)> handshake_msg_cb; + /** + * Encapsulates a set of callback functions required by a TLS Channel. + * @param output_fn is called with data for the outbound socket + * + * @param app_data_cb is called when new application data is received + * + * @param alert_cb is called when a TLS alert is received + * + * @param handshake_cb is called when a handshake is completed + */ + Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb, + handshake_cb hs_cb) + : m_output_function(out), m_app_data_cb(app_data_cb), + m_alert_cb(alert_cb), m_hs_cb(hs_cb), m_hs_msg_cb() {} + + Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb, + handshake_cb hs_cb, handshake_msg_cb hs_msg_cb) + : m_output_function(out), m_app_data_cb(app_data_cb), + m_alert_cb(alert_cb), m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb) {} + + const output_fn& out_fn() const { return m_output_function; } + const data_cb& app_data() const { return m_app_data_cb; } + const alert_cb& alert() const { return m_alert_cb; } + const handshake_cb& handshake() const { return m_hs_cb; } + const handshake_msg_cb& handshake_msg() const { return m_hs_msg_cb; } + + private: + const output_fn m_output_function; + const data_cb m_app_data_cb; + const alert_cb m_alert_cb; + const handshake_cb m_hs_cb; + const handshake_msg_cb m_hs_msg_cb; + }; + + + + Channel(const Callbacks& callbacks, Session_Manager& session_manager, RandomNumberGenerator& rng, const Policy& policy, bool is_datagram, - size_t io_buf_sz = 16*1024); + size_t io_buf_sz = IO_BUF_DEFAULT_SIZE); Channel(const Channel&) = delete; @@ -200,9 +237,9 @@ class BOTAN_DLL Channel const Policy& policy() const { return m_policy; } - bool save_session(const Session& session) const { return m_handshake_cb(session); } + bool save_session(const Session& session) const { return m_callbacks.handshake()(session); } - handshake_msg_cb get_handshake_msg_cb() const { return m_handshake_msg_cb; } + Callbacks get_callbacks() const { return m_callbacks; } private: void send_record(byte record_type, const std::vector<byte>& record); @@ -227,14 +264,20 @@ class BOTAN_DLL Channel const Handshake_State* pending_state() const { return m_pending_state.get(); } + /* methods to handle incoming traffic through Channel::receive_data. */ + void process_handshake_ccs(secure_vector<byte>& record, + u64bit& record_sequence, + Record_Type& record_type, + Protocol_Version& record_version); + + void process_application_data(secure_vector<byte>& record); + + void process_alert(secure_vector<byte>& record); + bool m_is_datagram; /* callbacks */ - data_cb m_data_cb; - alert_cb m_alert_cb; - output_fn m_output_fn; - handshake_cb m_handshake_cb; - handshake_msg_cb m_handshake_msg_cb; + Callbacks m_callbacks; /* external state */ Session_Manager& m_session_manager; diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h index 1f646cc7e..71596897c 100644 --- a/src/lib/tls/tls_ciphersuite.h +++ b/src/lib/tls/tls_ciphersuite.h @@ -1,6 +1,7 @@ /* * TLS Cipher Suites * (C) 2004-2011,2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -116,31 +117,17 @@ class BOTAN_DLL Ciphersuite private: - - Ciphersuite(u16bit ciphersuite_code, - const char* iana_id, - const char* sig_algo, - const char* kex_algo, - const char* cipher_algo, - size_t cipher_keylen, - size_t nonce_bytes_from_handshake, - size_t nonce_bytes_from_record, - const char* mac_algo, - size_t mac_keylen, - const char* prf_algo) : - m_ciphersuite_code(ciphersuite_code), - m_iana_id(iana_id), - m_sig_algo(sig_algo), - m_kex_algo(kex_algo), - m_prf_algo(prf_algo), - m_cipher_algo(cipher_algo), - m_mac_algo(mac_algo), - m_cipher_keylen(cipher_keylen), - m_nonce_bytes_from_handshake(nonce_bytes_from_handshake), - m_nonce_bytes_from_record(nonce_bytes_from_record), - m_mac_keylen(mac_keylen) - { - } + Ciphersuite(u16bit ciphersuite_code, + const char* iana_id, + const char* sig_algo, + const char* kex_algo, + const char* cipher_algo, + size_t cipher_keylen, + size_t nonce_bytes_from_handshake, + size_t nonce_bytes_from_record, + const char* mac_algo, + size_t mac_keylen, + const char* prf_algo = ""); u16bit m_ciphersuite_code = 0; diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index 301c77c6b..ff4b20bbf 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -1,6 +1,7 @@ /* * TLS Client * (C) 2004-2011,2012,2015,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -42,55 +43,28 @@ class Client_Handshake_State : public Handshake_State /* * TLS Client Constructor */ -Client::Client(output_fn output_fn, - data_cb proc_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, +Client::Client(const Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, - const Server_Information& info, - const Protocol_Version& offer_version, - const std::vector<std::string>& next_protos, + Properties properties, size_t io_buf_sz) : - Channel(output_fn, proc_cb, alert_cb, handshake_cb, Channel::handshake_msg_cb(), - session_manager, rng, policy, offer_version.is_datagram_protocol(), io_buf_sz), + Channel(callbacks, session_manager, rng, policy, properties.get_protocol_version().is_datagram_protocol(), + io_buf_sz), m_creds(creds), - m_info(info) + m_info(properties.get_server_info()) { const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname()); - Handshake_State& state = create_handshake_state(offer_version); - send_client_hello(state, false, offer_version, srp_identifier, next_protos); - } - -Client::Client(output_fn output_fn, - data_cb proc_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - const Server_Information& info, - const Protocol_Version& offer_version, - const std::vector<std::string>& next_protos) : - Channel(output_fn, proc_cb, alert_cb, handshake_cb, hs_msg_cb, - session_manager, rng, policy, offer_version.is_datagram_protocol()), - m_creds(creds), - m_info(info) - { - const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname()); - - Handshake_State& state = create_handshake_state(offer_version); - send_client_hello(state, false, offer_version, srp_identifier, next_protos); + Handshake_State& state = create_handshake_state(properties.get_protocol_version()); + send_client_hello(state, false, properties.get_protocol_version(), + srp_identifier, properties.get_next_protocol_versions()); } Handshake_State* Client::new_handshake_state(Handshake_IO* io) { - return new Client_Handshake_State(io, get_handshake_msg_cb()); + return new Client_Handshake_State(io, get_callbacks().handshake_msg()); } std::vector<X509_Certificate> @@ -129,9 +103,9 @@ void Client::send_client_hello(Handshake_State& state_base, { if(srp_identifier == "" || session_info.srp_identifier() == srp_identifier) { + Client_Hello::Handshake_Info hs_info(state.handshake_io(), state.hash()); state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), + hs_info, policy(), rng(), secure_renegotiation_data_for_client_hello(), @@ -145,16 +119,16 @@ void Client::send_client_hello(Handshake_State& state_base, if(!state.client_hello()) // not resuming { + Client_Hello::Handshake_Info hs_info(state.handshake_io(), state.hash()); + + Client_Hello::Settings client_settings(version, m_info.hostname(), srp_identifier); state.client_hello(new Client_Hello( - state.handshake_io(), - state.hash(), - version, + hs_info, policy(), rng(), secure_renegotiation_data_for_client_hello(), - next_protocols, - m_info.hostname(), - srp_identifier)); + client_settings, + next_protocols)); } secure_renegotiation_check(state.client_hello()); @@ -419,11 +393,10 @@ void Client::process_handshake_msg(const Handshake_State* active_state, "tls-client", m_info.hostname()); - state.client_certs( - new Certificate(state.handshake_io(), - state.hash(), - client_certs) - ); + Certificate::Handshake_Info hs_info(state.handshake_io(), + state.hash()); + + state.client_certs(new Certificate(hs_info, client_certs)); } state.client_kex( @@ -502,20 +475,22 @@ void Client::process_handshake_msg(const Handshake_State* active_state, if(session_id.empty() && !session_ticket.empty()) session_id = make_hello_random(rng(), policy()); + Session::Properties session_properties( + m_info, + "", + state.server_hello()->srtp_profile(), + state.server_hello()->version(), + state.server_hello()->ciphersuite(), + state.server_hello()->compression_method()); + Session session_info( session_id, state.session_keys().master_secret(), - state.server_hello()->version(), - state.server_hello()->ciphersuite(), - state.server_hello()->compression_method(), CLIENT, state.server_hello()->supports_extended_master_secret(), get_peer_cert_chain(state), session_ticket, - m_info, - "", - state.server_hello()->srtp_profile() - ); + session_properties); const bool should_save = save_session(session_info); diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h index 45a741878..e80739010 100644 --- a/src/lib/tls/tls_client.h +++ b/src/lib/tls/tls_client.h @@ -1,6 +1,7 @@ /* * TLS Client * (C) 2004-2011 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -25,13 +26,8 @@ class BOTAN_DLL Client final : public Channel /** * Set up a new TLS client session * - * @param output_fn is called with data for the outbound socket - * - * @param app_data_cb is called when new application data is received - * - * @param alert_cb is called when a TLS alert is received - * - * @param handshake_cb is called when a handshake is completed + * @param callbacks contains a set of callback function references + * required by the TLS client. * * @param session_manager manages session state * @@ -41,44 +37,65 @@ class BOTAN_DLL Client final : public Channel * * @param rng a random number generator * - * @param server_info is identifying information about the TLS server - * - * @param offer_version specifies which version we will offer - * to the TLS server. - * - * @param next_protocols specifies protocols to advertise with ALPN + * @param properties holds server information and protocol related + * properties. * * @param reserved_io_buffer_size This many bytes of memory will * be preallocated for the read and write buffers. Smaller * values just mean reallocations and copies are more likely. */ - Client(output_fn out, - data_cb app_data_cb, - alert_cb alert_cb, - handshake_cb hs_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - const Server_Information& server_info = Server_Information(), - const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(), - const std::vector<std::string>& next_protocols = {}, - size_t reserved_io_buffer_size = 16*1024 - ); - - Client(output_fn out, - data_cb app_data_cb, - alert_cb alert_cb, - handshake_cb hs_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - const Server_Information& server_info = Server_Information(), - const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(), - const std::vector<std::string>& next_protocols = {} + class Properties + { + /** + * Stores TLS Client properties. + * + * @param server_info is identifying information about the TLS server + * + * @param protocol_version specifies which version we will offer + * to the TLS server. + * + * @param next_protocols specifies protocols to advertise with ALPN + */ + + public: + Properties(const Server_Information& server_info + = Server_Information(), + const Protocol_Version protocol_version + = Protocol_Version::latest_tls_version(), + const std::vector<std::string>& next_versions + = {}) + : m_server_info(server_info), + m_protocol_version(protocol_version), + m_next_protocol_versions(next_versions) {} + + const Server_Information& get_server_info() + { + return m_server_info; + } + + const Protocol_Version& get_protocol_version() + { + return m_protocol_version; + } + + const std::vector<std::string>& get_next_protocol_versions() + { + return m_next_protocol_versions; + } + + private: + const Server_Information& m_server_info; + const Protocol_Version m_protocol_version; + const std::vector<std::string>& m_next_protocol_versions; + }; + Client(const Callbacks& callbacks, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + Properties properties, + size_t reserved_io_buffer_size = 16*1024 ); const std::string& application_protocol() const { return m_application_protocol; } diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h index a5aac0020..cfde0067c 100644 --- a/src/lib/tls/tls_extensions.h +++ b/src/lib/tls/tls_extensions.h @@ -1,6 +1,7 @@ /* * TLS Extensions * (C) 2011,2012,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -34,7 +35,6 @@ enum Handshake_Extension_Type { TLSEXT_SRP_IDENTIFIER = 12, TLSEXT_SIGNATURE_ALGORITHMS = 13, TLSEXT_USE_SRTP = 14, - TLSEXT_HEARTBEAT_SUPPORT = 15, TLSEXT_ALPN = 16, TLSEXT_EXTENDED_MASTER_SECRET = 23, diff --git a/src/lib/tls/tls_handshake_msg.h b/src/lib/tls/tls_handshake_msg.h index 7e527abf4..c53f73a3c 100644 --- a/src/lib/tls/tls_handshake_msg.h +++ b/src/lib/tls/tls_handshake_msg.h @@ -1,6 +1,7 @@ /* * TLS Handshake Message * (C) 2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -22,6 +23,19 @@ namespace TLS { class BOTAN_DLL Handshake_Message { public: + class Handshake_Info + { + public: + Handshake_Info(Handshake_IO& io, Handshake_Hash& hash) + : m_io(io), m_hash(hash) {}; + + Handshake_IO& get_io() { return m_io; }; + Handshake_Hash& get_hash() {return m_hash; }; + + private: + Handshake_IO& m_io; + Handshake_Hash& m_hash; + }; std::string type_string() const; virtual Handshake_Type type() const = 0; diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 3bee89e13..c6f8f9944 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -1,6 +1,7 @@ /* * TLS Messages * (C) 2004-2011,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -61,6 +62,26 @@ class Hello_Verify_Request final : public Handshake_Message class Client_Hello final : public Handshake_Message { public: + class Settings + { + public: + Settings(const Protocol_Version version, + const std::string& hostname = "", + const std::string& srp_identifier = "") + : m_new_session_version(version), + m_hostname(hostname), + m_srp_identifier(srp_identifier) {}; + + const Protocol_Version protocol_version() const { return m_new_session_version; }; + const std::string& hostname() const { return m_hostname; }; + const std::string& srp_identifier() const { return m_srp_identifier; } + + private: + const Protocol_Version m_new_session_version; + const std::string m_hostname; + const std::string m_srp_identifier; + }; + Handshake_Type type() const override { return CLIENT_HELLO; } Protocol_Version version() const { return m_version; } @@ -160,18 +181,14 @@ class Client_Hello final : public Handshake_Message std::set<Handshake_Extension_Type> extension_types() const { return m_extensions.extension_types(); } - Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, - Protocol_Version version, + Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, - const std::vector<std::string>& next_protocols, - const std::string& hostname = "", - const std::string& srp_identifier = ""); + const Client_Hello::Settings& client_settings, + const std::vector<std::string>& next_protocols); - Client_Hello(Handshake_IO& io, - Handshake_Hash& hash, + Client_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& reneg_info, @@ -199,6 +216,35 @@ class Client_Hello final : public Handshake_Message class Server_Hello final : public Handshake_Message { public: + class Settings + { + public: + Settings(const std::vector<byte> new_session_id, + Protocol_Version new_session_version, + u16bit ciphersuite, + byte compression, + bool offer_session_ticket) + : m_new_session_id(new_session_id), + m_new_session_version(new_session_version), + m_ciphersuite(ciphersuite), + m_compression(compression), + m_offer_session_ticket(offer_session_ticket) {}; + + const std::vector<byte>& session_id() const { return m_new_session_id; }; + Protocol_Version protocol_version() const { return m_new_session_version; }; + u16bit ciphersuite() const { return m_ciphersuite; }; + byte compression() const { return m_compression; } + bool offer_session_ticket() const { return m_offer_session_ticket; } + + private: + const std::vector<byte> m_new_session_id; + Protocol_Version m_new_session_version; + u16bit m_ciphersuite; + byte m_compression; + bool m_offer_session_ticket; + }; + + Handshake_Type type() const override { return SERVER_HELLO; } Protocol_Version version() const { return m_version; } @@ -256,21 +302,15 @@ class Server_Hello final : public Handshake_Message std::set<Handshake_Extension_Type> extension_types() const { return m_extensions.extension_types(); } - Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, + Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& secure_reneg_info, const Client_Hello& client_hello, - const std::vector<byte>& new_session_id, - Protocol_Version new_session_version, - u16bit ciphersuite, - byte compression, - bool offer_session_ticket, - const std::string& next_protocol); + const Server_Hello::Settings& settings, + const std::string next_protocol); - Server_Hello(Handshake_IO& io, - Handshake_Hash& hash, + Server_Hello(Handshake_Info& hs_info, const Policy& policy, RandomNumberGenerator& rng, const std::vector<byte>& secure_reneg_info, @@ -301,7 +341,6 @@ class Client_Key_Exchange final : public Handshake_Message const secure_vector<byte>& pre_master_secret() const { return m_pre_master; } - Client_Key_Exchange(Handshake_IO& io, Handshake_State& state, const Policy& policy, @@ -337,8 +376,7 @@ class Certificate final : public Handshake_Message size_t count() const { return m_certs.size(); } bool empty() const { return m_certs.empty(); } - Certificate(Handshake_IO& io, - Handshake_Hash& hash, + Certificate(Handshake_Info& hs_info, const std::vector<X509_Certificate>& certs); explicit Certificate(const std::vector<byte>& buf); @@ -364,8 +402,7 @@ class Certificate_Req final : public Handshake_Message std::vector<std::pair<std::string, std::string> > supported_algos() const { return m_supported_algos; } - Certificate_Req(Handshake_IO& io, - Handshake_Hash& hash, + Certificate_Req(Handshake_Info& hs_info, const Policy& policy, const std::vector<X509_DN>& allowed_cas, Protocol_Version version); diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp index eacf313a8..5fda1fbb4 100644 --- a/src/lib/tls/tls_record.cpp +++ b/src/lib/tls/tls_record.cpp @@ -1,6 +1,7 @@ /* * TLS Record Handling * (C) 2012,2013,2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -152,7 +153,7 @@ Connection_Cipher_State::format_ad(u64bit msg_sequence, } void write_record(secure_vector<byte>& output, - byte msg_type, const byte msg[], size_t msg_length, + Record_Message msg, Protocol_Version version, u64bit seq, Connection_Cipher_State* cs, @@ -160,7 +161,7 @@ void write_record(secure_vector<byte>& output, { output.clear(); - output.push_back(msg_type); + output.push_back(msg.get_type()); output.push_back(version.major_version()); output.push_back(version.minor_version()); @@ -172,17 +173,17 @@ void write_record(secure_vector<byte>& output, if(!cs) // initial unencrypted handshake records { - output.push_back(get_byte(0, static_cast<u16bit>(msg_length))); - output.push_back(get_byte(1, static_cast<u16bit>(msg_length))); + output.push_back(get_byte<u16bit>(0, static_cast<u16bit>(msg.get_size()))); + output.push_back(get_byte<u16bit>(1, static_cast<u16bit>(msg.get_size()))); - output.insert(output.end(), msg, msg + msg_length); + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); return; } if(AEAD_Mode* aead = cs->aead()) { - const size_t ctext_size = aead->output_length(msg_length); + const size_t ctext_size = aead->output_length(msg.get_size()); const std::vector<byte> nonce = cs->aead_nonce(seq); @@ -193,17 +194,16 @@ void write_record(secure_vector<byte>& output, output.push_back(get_byte(0, static_cast<u16bit>(rec_size))); output.push_back(get_byte(1, static_cast<u16bit>(rec_size))); - aead->set_ad(cs->format_ad(seq, msg_type, version, static_cast<u16bit>(msg_length))); + aead->set_ad(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size()))); if(cs->nonce_bytes_from_record() > 0) { output += std::make_pair(&nonce[cs->nonce_bytes_from_handshake()], cs->nonce_bytes_from_record()); } - BOTAN_ASSERT(aead->start(nonce).empty(), "AEAD doesn't return anything from start"); const size_t offset = output.size(); - output += std::make_pair(msg, msg_length); + output += std::make_pair(msg.get_data(), msg.get_size()); aead->finish(output, offset); BOTAN_ASSERT(output.size() == offset + ctext_size, "Expected size"); @@ -213,16 +213,16 @@ void write_record(secure_vector<byte>& output, return; } - cs->mac()->update(cs->format_ad(seq, msg_type, version, static_cast<u16bit>(msg_length))); + cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size()))); - cs->mac()->update(msg, msg_length); + cs->mac()->update(msg.get_data(), msg.get_size()); const size_t block_size = cs->block_size(); const size_t iv_size = cs->iv_size(); const size_t mac_size = cs->mac_size(); const size_t buf_size = round_up( - iv_size + msg_length + mac_size + (block_size ? 1 : 0), + iv_size + msg.get_size() + mac_size + (block_size ? 1 : 0), block_size); if(buf_size > MAX_CIPHERTEXT_SIZE) @@ -239,7 +239,7 @@ void write_record(secure_vector<byte>& output, rng.randomize(&output[output.size() - iv_size], iv_size); } - output.insert(output.end(), msg, msg + msg_length); + output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size()); output.resize(output.size() + mac_size); cs->mac()->final(&output[output.size() - mac_size]); @@ -247,7 +247,7 @@ void write_record(secure_vector<byte>& output, if(block_size) { const size_t pad_val = - buf_size - (iv_size + msg_length + mac_size + 1); + buf_size - (iv_size + msg.get_size() + mac_size + 1); for(size_t i = 0; i != pad_val + 1; ++i) output.push_back(static_cast<byte>(pad_val)); @@ -461,65 +461,58 @@ void decrypt_record(secure_vector<byte>& output, } size_t read_tls_record(secure_vector<byte>& readbuf, - const byte input[], - size_t input_sz, - size_t& consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - consumed = 0; - if(readbuf.size() < TLS_HEADER_SIZE) // header incomplete? { if(size_t needed = fill_buffer_to(readbuf, - input, input_sz, consumed, + raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), TLS_HEADER_SIZE)) return needed; BOTAN_ASSERT_EQUAL(readbuf.size(), TLS_HEADER_SIZE, "Have an entire header"); } - *record_version = Protocol_Version(readbuf[1], readbuf[2]); + *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]); - BOTAN_ASSERT(!record_version->is_datagram_protocol(), "Expected TLS"); + BOTAN_ASSERT(!rec.get_protocol_version()->is_datagram_protocol(), "Expected TLS"); - const size_t record_len = make_u16bit(readbuf[TLS_HEADER_SIZE-2], + const size_t record_size = make_u16bit(readbuf[TLS_HEADER_SIZE-2], readbuf[TLS_HEADER_SIZE-1]); - if(record_len > MAX_CIPHERTEXT_SIZE) + if(record_size > MAX_CIPHERTEXT_SIZE) throw TLS_Exception(Alert::RECORD_OVERFLOW, "Received a record that exceeds maximum size"); - if(record_len == 0) + if(record_size == 0) throw TLS_Exception(Alert::DECODE_ERROR, "Received a completely empty record"); if(size_t needed = fill_buffer_to(readbuf, - input, input_sz, consumed, - TLS_HEADER_SIZE + record_len)) + raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), + TLS_HEADER_SIZE + record_size)) return needed; - BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_len, + BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record"); - *record_type = static_cast<Record_Type>(readbuf[0]); + *rec.get_type() = static_cast<Record_Type>(readbuf[0]); u16bit epoch = 0; if(sequence_numbers) { - *record_sequence = sequence_numbers->next_read_sequence(); + *rec.get_sequence() = sequence_numbers->next_read_sequence(); epoch = sequence_numbers->current_read_epoch(); } else { // server initial handshake case - *record_sequence = 0; + *rec.get_sequence() = 0; epoch = 0; } @@ -527,7 +520,7 @@ size_t read_tls_record(secure_vector<byte>& readbuf, if(epoch == 0) // Unencrypted initial handshake { - record.assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_len); + rec.get_data().assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_size); readbuf.clear(); return 0; // got a full record } @@ -537,37 +530,30 @@ size_t read_tls_record(secure_vector<byte>& readbuf, BOTAN_ASSERT(cs, "Have cipherstate for this epoch"); - decrypt_record(record, + decrypt_record(rec.get_data(), record_contents, - record_len, - *record_sequence, - *record_version, - *record_type, + record_size, + *rec.get_sequence(), + *rec.get_protocol_version(), + *rec.get_type(), *cs); if(sequence_numbers) - sequence_numbers->read_accept(*record_sequence); + sequence_numbers->read_accept(*rec.get_sequence()); readbuf.clear(); return 0; } size_t read_dtls_record(secure_vector<byte>& readbuf, - const byte input[], - size_t input_sz, - size_t& consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - consumed = 0; - if(readbuf.size() < DTLS_HEADER_SIZE) // header incomplete? { - if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE)) + if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE)) { readbuf.clear(); return 0; @@ -576,38 +562,35 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, BOTAN_ASSERT_EQUAL(readbuf.size(), DTLS_HEADER_SIZE, "Have an entire header"); } - *record_version = Protocol_Version(readbuf[1], readbuf[2]); + *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]); - BOTAN_ASSERT(record_version->is_datagram_protocol(), "Expected DTLS"); + BOTAN_ASSERT(rec.get_protocol_version()->is_datagram_protocol(), "Expected DTLS"); - const size_t record_len = make_u16bit(readbuf[DTLS_HEADER_SIZE-2], + const size_t record_size = make_u16bit(readbuf[DTLS_HEADER_SIZE-2], readbuf[DTLS_HEADER_SIZE-1]); - // Invalid packet: - if(record_len == 0 || record_len > MAX_CIPHERTEXT_SIZE) - { - readbuf.clear(); - return 0; - } + if(record_size > MAX_CIPHERTEXT_SIZE) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "Got message that exceeds maximum size"); - if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE + record_len)) + if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE + record_size)) { // Truncated packet? readbuf.clear(); return 0; } - BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_len, readbuf.size(), + BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_size, readbuf.size(), "Have the full record"); - *record_type = static_cast<Record_Type>(readbuf[0]); + *rec.get_type() = static_cast<Record_Type>(readbuf[0]); u16bit epoch = 0; - *record_sequence = load_be<u64bit>(&readbuf[3], 0); - epoch = (*record_sequence >> 48); + *rec.get_sequence() = load_be<u64bit>(&readbuf[3], 0); + epoch = (*rec.get_sequence() >> 48); - if(sequence_numbers && sequence_numbers->already_seen(*record_sequence)) + if(sequence_numbers && sequence_numbers->already_seen(*rec.get_sequence())) { readbuf.clear(); return 0; @@ -617,7 +600,7 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, if(epoch == 0) // Unencrypted initial handshake { - record.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_len); + rec.get_data().assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size); readbuf.clear(); return 0; // got a full record } @@ -629,23 +612,23 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, BOTAN_ASSERT(cs, "Have cipherstate for this epoch"); - decrypt_record(record, + decrypt_record(rec.get_data(), record_contents, - record_len, - *record_sequence, - *record_version, - *record_type, + record_size, + *rec.get_sequence(), + *rec.get_protocol_version(), + *rec.get_type(), *cs); } catch(std::exception) { readbuf.clear(); - *record_type = NO_RECORD; + *rec.get_type() = NO_RECORD; return 0; } if(sequence_numbers) - sequence_numbers->read_accept(*record_sequence); + sequence_numbers->read_accept(*rec.get_sequence()); readbuf.clear(); return 0; @@ -654,24 +637,16 @@ size_t read_dtls_record(secure_vector<byte>& readbuf, } size_t read_record(secure_vector<byte>& readbuf, - const byte input[], - size_t input_sz, - bool is_datagram, - size_t& consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate) { - if(is_datagram) - return read_dtls_record(readbuf, input, input_sz, consumed, - record, record_sequence, record_version, record_type, + if(raw_input.is_datagram()) + return read_dtls_record(readbuf, raw_input, rec, sequence_numbers, get_cipherstate); else - return read_tls_record(readbuf, input, input_sz, consumed, - record, record_sequence, record_version, record_type, + return read_tls_record(readbuf, raw_input, rec, sequence_numbers, get_cipherstate); } diff --git a/src/lib/tls/tls_record.h b/src/lib/tls/tls_record.h index e3b0b9b58..53e447af6 100644 --- a/src/lib/tls/tls_record.h +++ b/src/lib/tls/tls_record.h @@ -1,6 +1,7 @@ /* * TLS Record Handling * (C) 2004-2012 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -90,6 +91,84 @@ class Connection_Cipher_State size_t m_iv_size = 0; }; +class Record + { + public: + Record(secure_vector<byte>& data, + u64bit* sequence, + Protocol_Version* protocol_version, + Record_Type* type) + : m_data(data), m_sequence(sequence), m_protocol_version(protocol_version), + m_type(type), m_size(data.size()) {}; + + secure_vector<byte>& get_data() { return m_data; } + void set_data(secure_vector<byte> data) + { + m_data.swap(data); + } + + Protocol_Version* get_protocol_version() { return m_protocol_version; } + + u64bit* get_sequence() { return m_sequence; } + + Record_Type* get_type() { return m_type; } + + size_t& get_size() { return m_size; } + + private: + secure_vector<byte>& m_data; + u64bit* m_sequence; + Protocol_Version* m_protocol_version; + Record_Type* m_type; + size_t m_size; + }; + +class Record_Message + { + public: + Record_Message(const byte* data, size_t size) + : m_type(0), m_sequence(0), m_data(data), m_size(size) {}; + Record_Message(byte type, u64bit sequence, const byte* data, size_t size) + : m_type(type), m_sequence(sequence), m_data(data), + m_size(size) {}; + + byte& get_type() { return m_type; }; + u64bit& get_sequence() { return m_sequence; }; + const byte* get_data() { return m_data; }; + size_t& get_size() { return m_size; }; + + private: + byte m_type; + u64bit m_sequence; + const byte* m_data; + size_t m_size; +}; + +class Record_Raw_Input + { + public: + Record_Raw_Input(const byte* data, size_t size, size_t& consumed, + bool is_datagram) + : m_data(data), m_size(size), m_consumed(consumed), + m_is_datagram(is_datagram) {}; + + const byte*& get_data() { return m_data; }; + + size_t& get_size() { return m_size; }; + + size_t& get_consumed() { return m_consumed; }; + void set_consumed(size_t consumed) { m_consumed = consumed; } + + bool is_datagram() { return m_is_datagram; }; + + private: + const byte* m_data; + size_t m_size; + size_t& m_consumed; + bool m_is_datagram; + }; + + /** * Create a TLS record * @param write_buffer the output record is placed here @@ -103,7 +182,7 @@ class Connection_Cipher_State * @return number of bytes written to write_buffer */ void write_record(secure_vector<byte>& write_buffer, - byte msg_type, const byte msg[], size_t msg_length, + Record_Message rec_msg, Protocol_Version version, u64bit msg_sequence, Connection_Cipher_State* cipherstate, @@ -117,14 +196,8 @@ typedef std::function<std::shared_ptr<Connection_Cipher_State> (u16bit)> get_cip * @return zero if full message, else number of bytes still needed */ size_t read_record(secure_vector<byte>& read_buffer, - const byte input[], - size_t input_length, - bool is_datagram, - size_t& input_consumed, - secure_vector<byte>& record, - u64bit* record_sequence, - Protocol_Version* record_version, - Record_Type* record_type, + Record_Raw_Input& raw_input, + Record& rec, Connection_Sequence_Numbers* sequence_numbers, get_cipherstate_fn get_cipherstate); diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp index 41b14ae08..de1e8668d 100644 --- a/src/lib/tls/tls_server.cpp +++ b/src/lib/tls/tls_server.cpp @@ -1,6 +1,7 @@ /* * TLS Server * (C) 2004-2011,2012,2016 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,6 +10,7 @@ #include <botan/internal/tls_handshake_state.h> #include <botan/internal/tls_messages.h> #include <botan/internal/stl_util.h> +#include "tls_magic.h" namespace Botan { @@ -16,22 +18,7 @@ namespace TLS { namespace { -class Server_Handshake_State : public Handshake_State - { - public: - // using Handshake_State::Handshake_State; - - Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State(io, cb) {} - // Used by the server only, in case of RSA key exchange. Not owned - Private_Key* server_rsa_kex_key = nullptr; - - /* - * Used by the server to know if resumption should be allowed on - * a server-initiated renegotiation - */ - bool allow_session_resumption = true; - }; bool check_for_resume(Session& session_info, Session_Manager& session_manager, @@ -225,10 +212,7 @@ get_server_certs(const std::string& hostname, /* * TLS Server Constructor */ -Server::Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, +Server::Server(const Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, @@ -236,26 +220,8 @@ Server::Server(output_fn output, next_protocol_fn next_proto, bool is_datagram, size_t io_buf_sz) : - Channel(output, data_cb, alert_cb, handshake_cb, Channel::handshake_msg_cb(), - session_manager, rng, policy, is_datagram, io_buf_sz), - m_creds(creds), - m_choose_next_protocol(next_proto) - { - } - -Server::Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - next_protocol_fn next_proto, - bool is_datagram) : - Channel(output, data_cb, alert_cb, handshake_cb, hs_msg_cb, - session_manager, rng, policy, is_datagram), + Channel(callbacks, session_manager, rng, policy, + is_datagram, io_buf_sz), m_creds(creds), m_choose_next_protocol(next_proto) { @@ -264,7 +230,7 @@ Server::Server(output_fn output, Handshake_State* Server::new_handshake_state(Handshake_IO* io) { std::unique_ptr<Handshake_State> state( - new Server_Handshake_State(io, get_handshake_msg_cb())); + new Server_Handshake_State(io, get_callbacks().handshake_msg())); state->set_expected_next(CLIENT_HELLO); return state.release(); @@ -284,441 +250,513 @@ Server::get_peer_cert_chain(const Handshake_State& state) const void Server::initiate_handshake(Handshake_State& state, bool force_full_renegotiation) { - dynamic_cast<Server_Handshake_State&>(state).allow_session_resumption = - !force_full_renegotiation; + dynamic_cast<Server_Handshake_State&>(state). + set_allow_session_resumption(!force_full_renegotiation); Hello_Request hello_req(state.handshake_io()); } /* -* Process a handshake message +* Process a CLIENT HELLO Message */ -void Server::process_handshake_msg(const Handshake_State* active_state, - Handshake_State& state_base, - Handshake_Type type, - const std::vector<byte>& contents) - { - Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base); - - state.confirm_transition_to(type); - - /* - * The change cipher spec message isn't technically a handshake - * message so it's not included in the hash. The finished and - * certificate verify messages are verified based on the current - * state of the hash *before* this message so we delay adding them - * to the hash computation until we've processed them below. - */ - if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY) +void Server::process_client_hello_msg(const Handshake_State* active_state, + Server_Handshake_State& pending_state, + const std::vector<byte>& contents) +{ + const bool initial_handshake = !active_state; + + if(!policy().allow_insecure_renegotiation() && + !(initial_handshake || secure_renegotiation_supported())) { - state.hash().update(state.handshake_io().format(contents, type)); + send_warning_alert(Alert::NO_RENEGOTIATION); + return; } - if(type == CLIENT_HELLO) + pending_state.client_hello(new Client_Hello(contents)); + const Protocol_Version client_version = pending_state.client_hello()->version(); + + Protocol_Version negotiated_version; + + const Protocol_Version latest_supported = + policy().latest_supported_version(client_version.is_datagram_protocol()); + + if((initial_handshake && client_version.known_version()) || + (!initial_handshake && client_version == active_state->version())) { - const bool initial_handshake = !active_state; + /* + Common cases: new client hello with some known version, or a + renegotiation using the same version as previously + negotiated. + */ - if(!policy().allow_insecure_renegotiation() && - !(initial_handshake || secure_renegotiation_supported())) + negotiated_version = client_version; + } + else if(!initial_handshake && (client_version != active_state->version())) + { + /* + * If this is a renegotiation, and the client has offered a + * later version than what it initially negotiated, negotiate + * the old version. This matches OpenSSL's behavior. If the + * client is offering a version earlier than what it initially + * negotiated, reject as a probable attack. + */ + if(active_state->version() > client_version) { - send_warning_alert(Alert::NO_RENEGOTIATION); - return; + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client negotiated " + + active_state->version().to_string() + + " then renegotiated with " + + client_version.to_string()); } + else + negotiated_version = active_state->version(); + } + else + { + /* + New negotiation using a version we don't know. Offer them the + best we currently know and support + */ + negotiated_version = latest_supported; + } - state.client_hello(new Client_Hello(contents)); + if(!policy().acceptable_protocol_version(negotiated_version)) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client version " + negotiated_version.to_string() + + " is unacceptable by policy"); + } - const Protocol_Version client_version = state.client_hello()->version(); + if(pending_state.client_hello()->sent_fallback_scsv()) + { + if(latest_supported > client_version) + throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK, + "Client signalled fallback SCSV, possible attack"); + } - Protocol_Version negotiated_version; + secure_renegotiation_check(pending_state.client_hello()); - const Protocol_Version latest_supported = - policy().latest_supported_version(client_version.is_datagram_protocol()); + pending_state.set_version(negotiated_version); - if((initial_handshake && client_version.known_version()) || - (!initial_handshake && client_version == active_state->version())) - { - /* - Common cases: new client hello with some known version, or a - renegotiation using the same version as previously - negotiated. - */ + Session session_info; + const bool resuming = + pending_state.allow_session_resumption() && + check_for_resume(session_info, + session_manager(), + m_creds, + pending_state.client_hello(), + std::chrono::seconds(policy().session_ticket_lifetime())); - negotiated_version = client_version; - } - else if(!initial_handshake && (client_version != active_state->version())) - { - /* - * If this is a renegotiation, and the client has offered a - * later version than what it initially negotiated, negotiate - * the old version. This matches OpenSSL's behavior. If the - * client is offering a version earlier than what it initially - * negotiated, reject as a probable attack. - */ - if(active_state->version() > client_version) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Client negotiated " + - active_state->version().to_string() + - " then renegotiated with " + - client_version.to_string()); - } - else - negotiated_version = active_state->version(); - } - else - { - /* - New negotiation using a version we don't know. Offer them the - best we currently know and support - */ - negotiated_version = latest_supported; - } + bool have_session_ticket_key = false; - if(!policy().acceptable_protocol_version(negotiated_version)) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Client version " + negotiated_version.to_string() + - " is unacceptable by policy"); - } + try + { + have_session_ticket_key = + m_creds.psk("tls-server", "session-ticket", "").length() > 0; + } + catch(...) {} - if(state.client_hello()->sent_fallback_scsv()) - { - if(latest_supported > client_version) - throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK, - "Client signalled fallback SCSV, possible attack"); - } + m_next_protocol = ""; + if(m_choose_next_protocol && pending_state.client_hello()->supports_alpn()) + m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols()); - secure_renegotiation_check(state.client_hello()); + if(resuming) + { + this->session_resume(pending_state, have_session_ticket_key, session_info); + } + else // new session + { + this->session_create(pending_state, have_session_ticket_key); + } +} - state.set_version(negotiated_version); +void Server::process_certificate_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& contents) +{ + pending_state.client_certs(new Certificate(contents)); + pending_state.set_expected_next(CLIENT_KEX); +} - Session session_info; - const bool resuming = - state.allow_session_resumption && - check_for_resume(session_info, - session_manager(), - m_creds, - state.client_hello(), - std::chrono::seconds(policy().session_ticket_lifetime())); +void Server::process_client_key_exchange_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& contents) +{ + if(pending_state.received_handshake_msg(CERTIFICATE) && !pending_state.client_certs()->empty()) + pending_state.set_expected_next(CERTIFICATE_VERIFY); + else + pending_state.set_expected_next(HANDSHAKE_CCS); - bool have_session_ticket_key = false; + pending_state.client_kex( + new Client_Key_Exchange(contents, pending_state, + pending_state.server_rsa_kex_key(), + m_creds, policy(), rng()) + ); - try - { - have_session_ticket_key = - m_creds.psk("tls-server", "session-ticket", "").length() > 0; - } - catch(...) {} + pending_state.compute_session_keys(); +} - m_next_protocol = ""; - if(m_choose_next_protocol && state.client_hello()->supports_alpn()) - m_next_protocol = m_choose_next_protocol(state.client_hello()->next_protocols()); +void Server::process_change_cipher_spec_msg(Server_Handshake_State& pending_state) +{ + pending_state.set_expected_next(FINISHED); + change_cipher_spec_reader(SERVER); +} - if(resuming) - { - // Only offer a resuming client a new ticket if they didn't send one this time, - // ie, resumed via server-side resumption. TODO: also send one if expiring soon? - - const bool offer_new_session_ticket = - (state.client_hello()->supports_session_ticket() && - state.client_hello()->session_ticket().empty() && - have_session_ticket_key); - - state.server_hello(new Server_Hello( - state.handshake_io(), - state.hash(), - policy(), - rng(), - secure_renegotiation_data_for_server_hello(), - *state.client_hello(), - session_info, - offer_new_session_ticket, - m_next_protocol - )); - - secure_renegotiation_check(state.server_hello()); - - state.compute_session_keys(session_info.master_secret()); - - if(!save_session(session_info)) +void Server::process_certificate_verify_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector<byte>& contents) +{ + pending_state.client_verify ( new Certificate_Verify ( contents, pending_state.version() ) ); + + const std::vector<X509_Certificate>& client_certs = + pending_state.client_certs()->cert_chain(); + + const bool sig_valid = + pending_state.client_verify()->verify ( client_certs[0], pending_state, policy() ); + + pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) ); + + /* + * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for + * "A handshake cryptographic operation failed, including being + * unable to correctly verify a signature, ..." + */ + if ( !sig_valid ) + throw TLS_Exception ( Alert::DECRYPT_ERROR, "Client cert verify failed" ); + + try + { + m_creds.verify_certificate_chain ( "tls-server", "", client_certs ); + } + catch ( std::exception& e ) + { + throw TLS_Exception ( Alert::BAD_CERTIFICATE, e.what() ); + } + + pending_state.set_expected_next ( HANDSHAKE_CCS ); +} + +void Server::process_finished_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector<byte>& contents) +{ + pending_state.set_expected_next ( HANDSHAKE_NONE ); + + pending_state.client_finished ( new Finished ( contents ) ); + + if ( !pending_state.client_finished()->verify ( pending_state, CLIENT ) ) + throw TLS_Exception ( Alert::DECRYPT_ERROR, + "Finished message didn't verify" ); + + if ( !pending_state.server_finished() ) + { + // already sent finished if resuming, so this is a new session + + pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) ); + + Session::Properties session_properties( + Server_Information(pending_state.client_hello()->sni_hostname()), + "", + pending_state.server_hello()->srtp_profile(), + pending_state.server_hello()->version(), + pending_state.server_hello()->ciphersuite(), + pending_state.server_hello()->compression_method()); + + + Session session_info( + pending_state.server_hello()->session_id(), + pending_state.session_keys().master_secret(), + SERVER, + pending_state.server_hello()->supports_extended_master_secret(), + get_peer_cert_chain ( pending_state ), + std::vector<byte>(), + session_properties); + + if ( save_session ( session_info ) ) { - 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(state.handshake_io(), - state.hash()) - ); - } + if ( pending_state.server_hello()->supports_session_ticket() ) + { + try + { + const SymmetricKey ticket_key = m_creds.psk ( "tls-server", "session-ticket", "" ); + + pending_state.new_session_ticket ( + new New_Session_Ticket ( pending_state.handshake_io(), + pending_state.hash(), + session_info.encrypt ( ticket_key, rng() ), + policy().session_ticket_lifetime() ) + ); + } + catch ( ... ) {} + } + else + session_manager().save ( session_info ); } - if(state.server_hello()->supports_session_ticket() && !state.new_session_ticket()) + if ( !pending_state.new_session_ticket() && + pending_state.server_hello()->supports_session_ticket() ) { - try - { - const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); - - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash(), - session_info.encrypt(ticket_key, rng()), - policy().session_ticket_lifetime()) - ); - } - catch(...) {} - - if(!state.new_session_ticket()) - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), state.hash()) - ); - } + pending_state.new_session_ticket ( + new New_Session_Ticket ( pending_state.handshake_io(), pending_state.hash() ) + ); } - state.handshake_io().send(Change_Cipher_Spec()); + pending_state.handshake_io().send ( Change_Cipher_Spec() ); - change_cipher_spec_writer(SERVER); + change_cipher_spec_writer ( SERVER ); - state.server_finished(new Finished(state.handshake_io(), state, SERVER)); - state.set_expected_next(HANDSHAKE_CCS); - } - else // new session - { - std::map<std::string, std::vector<X509_Certificate> > cert_chains; + pending_state.server_finished ( new Finished ( pending_state.handshake_io(), pending_state, SERVER ) ); + } - const std::string sni_hostname = state.client_hello()->sni_hostname(); + activate_session(); - cert_chains = get_server_certs(sni_hostname, m_creds); +} - if(sni_hostname != "" && cert_chains.empty()) - { - cert_chains = get_server_certs("", m_creds); - - /* - * Only send the unrecognized_name alert if we couldn't - * find any certs for the requested name but did find at - * least one cert to use in general. That avoids sending an - * unrecognized_name when a server is configured for purely - * anonymous operation. - */ - if(!cert_chains.empty()) - send_alert(Alert(Alert::UNRECOGNIZED_NAME)); - } +/* +* Process a handshake message +*/ +void Server::process_handshake_msg(const Handshake_State* active_state, + Handshake_State& state_base, + Handshake_Type type, + const std::vector<byte>& contents) + { + Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base); + state.confirm_transition_to(type); - state.server_hello(new Server_Hello( - state.handshake_io(), - state.hash(), - policy(), - rng(), - secure_renegotiation_data_for_server_hello(), - *state.client_hello(), - make_hello_random(rng(), policy()), // new session ID - state.version(), - choose_ciphersuite(policy(), state.version(), m_creds, cert_chains, state.client_hello()), - choose_compression(policy(), state.client_hello()->compression_methods()), - have_session_ticket_key, - m_next_protocol) - ); + /* + * The change cipher spec message isn't technically a handshake + * message so it's not included in the hash. The finished and + * certificate verify messages are verified based on the current + * state of the hash *before* this message so we delay adding them + * to the hash computation until we've processed them below. + */ + if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY) + { + state.hash().update(state.handshake_io().format(contents, type)); + } - secure_renegotiation_check(state.server_hello()); + switch(type) + { + case CLIENT_HELLO: + this->process_client_hello_msg(active_state, state, contents); + break; - const std::string sig_algo = state.ciphersuite().sig_algo(); - const std::string kex_algo = state.ciphersuite().kex_algo(); + case CERTIFICATE: + this->process_certificate_msg(state, contents); + break; - if(sig_algo != "") - { - BOTAN_ASSERT(!cert_chains[sig_algo].empty(), - "Attempting to send empty certificate chain"); + case CLIENT_KEX: + this->process_client_key_exchange_msg(state, contents); + break; - state.server_certs(new Certificate(state.handshake_io(), - state.hash(), - cert_chains[sig_algo])); - } + case CERTIFICATE_VERIFY: + this->process_certificate_verify_msg(state, type, contents); + break; - Private_Key* private_key = nullptr; + case HANDSHAKE_CCS: + this->process_change_cipher_spec_msg(state); + break; - if(kex_algo == "RSA" || sig_algo != "") - { - private_key = m_creds.private_key_for( - state.server_certs()->cert_chain()[0], - "tls-server", - sni_hostname); + case FINISHED: + this->process_finished_msg(state, type, contents); + break; + + default: + throw Unexpected_Message("Unknown handshake message received"); + break; + } + } + +void Server::session_resume(Server_Handshake_State& pending_state, + bool have_session_ticket_key, + Session& session_info) + { + // Only offer a resuming client a new ticket if they didn't send one this time, + // ie, resumed via server-side resumption. TODO: also send one if expiring soon? + + const bool offer_new_session_ticket = + (pending_state.client_hello()->supports_session_ticket() && + pending_state.client_hello()->session_ticket().empty() && + have_session_ticket_key); + + Server_Hello::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); + pending_state.server_hello(new Server_Hello( + hs_info, + policy(), + rng(), + secure_renegotiation_data_for_server_hello(), + *pending_state.client_hello(), + session_info, + offer_new_session_ticket, + m_next_protocol + )); + + secure_renegotiation_check(pending_state.server_hello()); + + pending_state.compute_session_keys(session_info.master_secret()); + + if(!save_session(session_info)) + { + session_manager().remove_entry(session_info.session_id()); - if(!private_key) - throw Internal_Error("No private key located for associated server cert"); + if(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket + { + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), + pending_state.hash()) + ); } + } - if(kex_algo == "RSA") + if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket()) + { + try { - state.server_rsa_kex_key = private_key; + const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); + + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), + pending_state.hash(), + session_info.encrypt(ticket_key, rng()), + policy().session_ticket_lifetime()) + ); } - else + catch(...) {} + + if(!pending_state.new_session_ticket()) { - state.server_kex(new Server_Key_Exchange(state.handshake_io(), - state, policy(), - m_creds, rng(), private_key)); + pending_state.new_session_ticket( + new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash()) + ); } + } - auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname); + pending_state.handshake_io().send(Change_Cipher_Spec()); - std::vector<X509_DN> client_auth_CAs; + change_cipher_spec_writer(SERVER); - for(auto store : trusted_CAs) - { - auto subjects = store->all_subjects(); - client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end()); - } + pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER)); + pending_state.set_expected_next(HANDSHAKE_CCS); + } - if(!client_auth_CAs.empty() && state.ciphersuite().sig_algo() != "") - { - state.cert_req( - new Certificate_Req(state.handshake_io(), state.hash(), - policy(), client_auth_CAs, state.version())); +void Server::session_create(Server_Handshake_State& pending_state, + bool have_session_ticket_key) + { + std::map<std::string, std::vector<X509_Certificate> > cert_chains; - state.set_expected_next(CERTIFICATE); - } + const std::string sni_hostname = pending_state.client_hello()->sni_hostname(); - /* - * If the client doesn't have a cert they want to use they are - * allowed to send either an empty cert message or proceed - * directly to the client key exchange, so allow either case. - */ - state.set_expected_next(CLIENT_KEX); + cert_chains = get_server_certs(sni_hostname, m_creds); - state.server_hello_done(new Server_Hello_Done(state.handshake_io(), state.hash())); - } - } - else if(type == CERTIFICATE) + if(sni_hostname != "" && cert_chains.empty()) { - state.client_certs(new Certificate(contents)); + cert_chains = get_server_certs("", m_creds); - state.set_expected_next(CLIENT_KEX); + /* + * Only send the unrecognized_name alert if we couldn't + * find any certs for the requested name but did find at + * least one cert to use in general. That avoids sending an + * unrecognized_name when a server is configured for purely + * anonymous operation. + */ + if(!cert_chains.empty()) + send_alert(Alert(Alert::UNRECOGNIZED_NAME)); } - else if(type == CLIENT_KEX) - { - if(state.received_handshake_msg(CERTIFICATE) && !state.client_certs()->empty()) - state.set_expected_next(CERTIFICATE_VERIFY); - else - state.set_expected_next(HANDSHAKE_CCS); - - state.client_kex( - new Client_Key_Exchange(contents, state, - state.server_rsa_kex_key, - m_creds, policy(), rng()) - ); - state.compute_session_keys(); - } - else if(type == CERTIFICATE_VERIFY) + Server_Hello::Settings srv_settings( + make_hello_random(rng(), policy()), // new session ID + pending_state.version(), + choose_ciphersuite(policy(), + pending_state.version(), + m_creds, + cert_chains, + pending_state.client_hello()), + choose_compression(policy(), + pending_state.client_hello()->compression_methods()), + have_session_ticket_key); + + Server_Hello::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); + pending_state.server_hello(new Server_Hello( + hs_info, + policy(), + rng(), + secure_renegotiation_data_for_server_hello(), + *pending_state.client_hello(), + srv_settings, + m_next_protocol) + ); + + secure_renegotiation_check(pending_state.server_hello()); + + const std::string sig_algo = pending_state.ciphersuite().sig_algo(); + const std::string kex_algo = pending_state.ciphersuite().kex_algo(); + + if(sig_algo != "") { - state.client_verify(new Certificate_Verify(contents, state.version())); + BOTAN_ASSERT(!cert_chains[sig_algo].empty(), + "Attempting to send empty certificate chain"); - const std::vector<X509_Certificate>& client_certs = - state.client_certs()->cert_chain(); + Certificate::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); - const bool sig_valid = - state.client_verify()->verify(client_certs[0], state, policy()); + pending_state.server_certs(new Certificate(hs_info, cert_chains[sig_algo])); + } - state.hash().update(state.handshake_io().format(contents, type)); + Private_Key* private_key = nullptr; - /* - * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for - * "A handshake cryptographic operation failed, including being - * unable to correctly verify a signature, ..." - */ - if(!sig_valid) - throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed"); - - try - { - m_creds.verify_certificate_chain("tls-server", "", client_certs); - } - catch(std::exception& e) - { - throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); - } + if(kex_algo == "RSA" || sig_algo != "") + { + private_key = m_creds.private_key_for( + pending_state.server_certs()->cert_chain()[0], + "tls-server", + sni_hostname); - state.set_expected_next(HANDSHAKE_CCS); + if(!private_key) + throw Internal_Error("No private key located for associated server cert"); } - else if(type == HANDSHAKE_CCS) + + if(kex_algo == "RSA") { - state.set_expected_next(FINISHED); - change_cipher_spec_reader(SERVER); + pending_state.set_server_rsa_kex_key(private_key); } - else if(type == FINISHED) + else { - state.set_expected_next(HANDSHAKE_NONE); - - state.client_finished(new Finished(contents)); - if(!state.client_finished()->verify(state, CLIENT)) - throw TLS_Exception(Alert::DECRYPT_ERROR, - "Finished message didn't verify"); + pending_state.server_kex(new Server_Key_Exchange(pending_state.handshake_io(), + pending_state, policy(), + m_creds, rng(), private_key)); + } - if(!state.server_finished()) - { - // already sent finished if resuming, so this is a new session + auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname); - state.hash().update(state.handshake_io().format(contents, type)); + std::vector<X509_DN> client_auth_CAs; - Session session_info( - state.server_hello()->session_id(), - state.session_keys().master_secret(), - state.server_hello()->version(), - state.server_hello()->ciphersuite(), - state.server_hello()->compression_method(), - SERVER, - state.server_hello()->supports_extended_master_secret(), - get_peer_cert_chain(state), - std::vector<byte>(), - Server_Information(state.client_hello()->sni_hostname()), - state.srp_identifier(), - state.server_hello()->srtp_profile() - ); - - if(save_session(session_info)) - { - if(state.server_hello()->supports_session_ticket()) - { - try - { - const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", ""); - - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), - state.hash(), - session_info.encrypt(ticket_key, rng()), - policy().session_ticket_lifetime()) - ); - } - catch(...) {} - } - else - session_manager().save(session_info); - } - - if(!state.new_session_ticket() && - state.server_hello()->supports_session_ticket()) - { - state.new_session_ticket( - new New_Session_Ticket(state.handshake_io(), state.hash()) - ); - } + for(auto store : trusted_CAs) + { + auto subjects = store->all_subjects(); + client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end()); + } - state.handshake_io().send(Change_Cipher_Spec()); + if(!client_auth_CAs.empty() && pending_state.ciphersuite().sig_algo() != "") + { + Certificate_Req::Handshake_Info hs_info(pending_state.handshake_io(), + pending_state.hash()); + pending_state.cert_req( + new Certificate_Req(hs_info, policy(), client_auth_CAs, + pending_state.version())); - change_cipher_spec_writer(SERVER); + pending_state.set_expected_next(CERTIFICATE); + } - state.server_finished(new Finished(state.handshake_io(), state, SERVER)); - } + /* + * If the client doesn't have a cert they want to use they are + * allowed to send either an empty cert message or proceed + * directly to the client key exchange, so allow either case. + */ + pending_state.set_expected_next(CLIENT_KEX); - activate_session(); - } - else - throw Unexpected_Message("Unknown handshake message received"); + pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash())); } - } } diff --git a/src/lib/tls/tls_server.h b/src/lib/tls/tls_server.h index 5ea2a1318..72834785e 100644 --- a/src/lib/tls/tls_server.h +++ b/src/lib/tls/tls_server.h @@ -1,6 +1,7 @@ /* * TLS Server * (C) 2004-2011 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,8 +11,11 @@ #include <botan/tls_channel.h> #include <botan/credentials_manager.h> +#include <botan/internal/tls_server_handshake_state.h> #include <vector> + + namespace Botan { namespace TLS { @@ -27,32 +31,15 @@ class BOTAN_DLL Server final : public Channel /** * Server initialization */ - Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, + Server(const Callbacks& callbacks, Session_Manager& session_manager, Credentials_Manager& creds, const Policy& policy, RandomNumberGenerator& rng, next_protocol_fn next_proto = next_protocol_fn(), bool is_datagram = false, - size_t reserved_io_buffer_size = 16*1024 - ); - - Server(output_fn output, - data_cb data_cb, - alert_cb alert_cb, - handshake_cb handshake_cb, - handshake_msg_cb hs_msg_cb, - Session_Manager& session_manager, - Credentials_Manager& creds, - const Policy& policy, - RandomNumberGenerator& rng, - next_protocol_fn next_proto = next_protocol_fn(), - bool is_datagram = false + size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE ); - /** * Return the protocol notification set by the client (using the * NPN extension) for this connection, if any. This value is not @@ -73,6 +60,33 @@ class BOTAN_DLL Server final : public Channel Handshake_Type type, const std::vector<byte>& contents) override; + void process_client_hello_msg(const Handshake_State* active_state, + Server_Handshake_State& pending_state, + const std::vector<byte>& contents); + + void process_certificate_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& contents); + + void process_client_key_exchange_msg(Server_Handshake_State& pending_state, + const std::vector<byte>& contents); + + void process_change_cipher_spec_msg(Server_Handshake_State& pending_state); + + void process_certificate_verify_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector<byte>& contents); + + void process_finished_msg(Server_Handshake_State& pending_state, + Handshake_Type type, + const std::vector<byte>& contents); + + void session_resume(Server_Handshake_State& pending_state, + bool have_session_ticket_key, + Session& session_info); + + void session_create(Server_Handshake_State& pending_state, + bool have_session_ticket_key); + Handshake_State* new_handshake_state(Handshake_IO* io) override; Credentials_Manager& m_creds; diff --git a/src/lib/tls/tls_server_handshake_state.h b/src/lib/tls/tls_server_handshake_state.h new file mode 100644 index 000000000..281dd82df --- /dev/null +++ b/src/lib/tls/tls_server_handshake_state.h @@ -0,0 +1,47 @@ +/* +* TLS Server +* (C) 2004-2011,2012,2016 Jack Lloyd +* 2016 Matthias Gierlings +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__ +#define BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__ + +#include <botan/internal/tls_handshake_state.h> +namespace Botan { + +namespace TLS { + +class Server_Handshake_State : public Handshake_State + { + public: + Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) + : Handshake_State(io, cb) {} + + Private_Key* server_rsa_kex_key() { return m_server_rsa_kex_key; } + void set_server_rsa_kex_key(Private_Key* key) + { m_server_rsa_kex_key = key; } + + bool allow_session_resumption() const + { return m_allow_session_resumption; } + void set_allow_session_resumption(bool allow_session_resumption) + { m_allow_session_resumption = allow_session_resumption; } + + + private: + // Used by the server only, in case of RSA key exchange. Not owned + Private_Key* m_server_rsa_kex_key = nullptr; + + /* + * Used by the server to know if resumption should be allowed on + * a server-initiated renegotiation + */ + bool m_allow_session_resumption = true; + }; + +} + +} +#endif //BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__ diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp index 18c9b357c..bcbac10af 100644 --- a/src/lib/tls/tls_session.cpp +++ b/src/lib/tls/tls_session.cpp @@ -1,6 +1,7 @@ /* * TLS Session State * (C) 2011-2012,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -19,29 +20,19 @@ namespace TLS { Session::Session(const std::vector<byte>& session_identifier, const secure_vector<byte>& master_secret, - Protocol_Version version, - u16bit ciphersuite, - byte compression_method, Connection_Side side, bool extended_master_secret, const std::vector<X509_Certificate>& certs, const std::vector<byte>& ticket, - const Server_Information& server_info, - const std::string& srp_identifier, - u16bit srtp_profile) : + Properties properties) : m_start_time(std::chrono::system_clock::now()), m_identifier(session_identifier), m_session_ticket(ticket), m_master_secret(master_secret), - m_version(version), - m_ciphersuite(ciphersuite), - m_compression_method(compression_method), m_connection_side(side), - m_srtp_profile(srtp_profile), m_extended_master_secret(extended_master_secret), m_peer_certs(certs), - m_server_info(server_info), - m_srp_identifier(srp_identifier) + m_properties(properties) { } @@ -69,6 +60,9 @@ Session::Session(const byte ber[], size_t ber_len) size_t srtp_profile = 0; size_t fragment_size = 0; + u16bit cs = m_properties.get_ciphersuite(); + byte compr = compression_method(); + BER_Decoder(ber, ber_len) .start_cons(SEQUENCE) .decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION), @@ -78,10 +72,9 @@ Session::Session(const byte ber[], size_t ber_len) .decode_integer_type(minor_version) .decode(m_identifier, OCTET_STRING) .decode(m_session_ticket, OCTET_STRING) - .decode_integer_type(m_ciphersuite) - .decode_integer_type(m_compression_method) + .decode_integer_type(cs) + .decode_integer_type(compr) .decode_integer_type(side_code) - .decode_integer_type(fragment_size) .decode(m_extended_master_secret) .decode(m_master_secret, OCTET_STRING) .decode(peer_cert_bits, OCTET_STRING) @@ -103,16 +96,17 @@ Session::Session(const byte ber[], size_t ber_len) " no longer supported"); } - m_version = Protocol_Version(major_version, minor_version); + m_properties.set_ciphersuite(cs); + m_properties.set_compression_method(compr); + m_properties.set_protocol_version(Protocol_Version(major_version, minor_version)); m_start_time = std::chrono::system_clock::from_time_t(start_time); m_connection_side = static_cast<Connection_Side>(side_code); - m_srtp_profile = static_cast<u16bit>(srtp_profile); - - m_server_info = Server_Information(server_hostname.value(), - server_service.value(), - static_cast<u16bit>(server_port)); - - m_srp_identifier = srp_identifier_str.value(); + m_properties.set_srtp_profile(static_cast<u16bit>(srtp_profile)); + m_properties.set_server_info( + Server_Information(server_hostname.value(), + server_service.value(), + static_cast<u16bit>(server_port))); + m_properties.set_srp_identifier(srp_identifier_str.value()); if(!peer_cert_bits.empty()) { @@ -133,22 +127,22 @@ secure_vector<byte> Session::DER_encode() const .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_version.major_version())) - .encode(static_cast<size_t>(m_version.minor_version())) + .encode(static_cast<size_t>(version().major_version())) + .encode(static_cast<size_t>(version().minor_version())) .encode(m_identifier, OCTET_STRING) .encode(m_session_ticket, OCTET_STRING) - .encode(static_cast<size_t>(m_ciphersuite)) - .encode(static_cast<size_t>(m_compression_method)) + .encode(static_cast<size_t>(ciphersuite_code())) + .encode(static_cast<size_t>(compression_method())) .encode(static_cast<size_t>(m_connection_side)) .encode(static_cast<size_t>(/*old fragment size*/0)) .encode(m_extended_master_secret) .encode(m_master_secret, OCTET_STRING) .encode(peer_cert_bits, OCTET_STRING) - .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING)) - .encode(ASN1_String(m_server_info.service(), UTF8_STRING)) - .encode(static_cast<size_t>(m_server_info.port())) - .encode(ASN1_String(m_srp_identifier, UTF8_STRING)) - .encode(static_cast<size_t>(m_srtp_profile)) + .encode(ASN1_String(m_properties.get_server_info().hostname(), UTF8_STRING)) + .encode(ASN1_String(m_properties.get_server_info().service(), UTF8_STRING)) + .encode(static_cast<size_t>(m_properties.get_server_info().port())) + .encode(ASN1_String(srp_identifier(), UTF8_STRING)) + .encode(static_cast<size_t>(dtls_srtp_profile())) .end_cons() .get_contents(); } diff --git a/src/lib/tls/tls_session.h b/src/lib/tls/tls_session.h index 8ca646cf2..600aa0a10 100644 --- a/src/lib/tls/tls_session.h +++ b/src/lib/tls/tls_session.h @@ -1,6 +1,7 @@ /* * TLS Session * (C) 2011-2012,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -27,35 +28,106 @@ namespace TLS { class BOTAN_DLL Session { public: + class Properties + { + public: + Properties() : m_srtp_profile(0), m_protocol_version(), + m_ciphersuite(), m_compression_method(0) {} + + Properties(const Server_Information& server_info, + const std::string& srp_identifier, + u16bit srtp_profile, + Protocol_Version protocol_version, + u16bit ciphersuite, + byte compression_method) + : m_server_info(server_info), + m_srp_identifier(srp_identifier), + m_srtp_profile(srtp_profile), + m_protocol_version(protocol_version), + m_ciphersuite(ciphersuite), + m_compression_method(compression_method) {} + + const Server_Information& get_server_info() const + { + return m_server_info; + } + + void set_server_info(Server_Information server_info) + { + m_server_info = server_info; + } + + const std::string& get_srp_identifier() const + { + return m_srp_identifier; + } + + void set_srp_identifier(const std::string& srp_identifier) + { + m_srp_identifier = srp_identifier; + } + + u16bit get_srtp_profile() const { return m_srtp_profile; } + void set_srtp_profile(u16bit srtp_profile) + { + m_srtp_profile = srtp_profile; + } + + Protocol_Version get_protocol_version() const + { + return m_protocol_version; + } + + void set_protocol_version(Protocol_Version protocol_version) + { + m_protocol_version = protocol_version; + } + + u16bit get_ciphersuite() const { return m_ciphersuite; } + + void set_ciphersuite(u16bit ciphersuite) + { + m_ciphersuite = ciphersuite; + } + + byte get_compression_method() const + { + return m_compression_method; + } + + void set_compression_method(byte compression_method) + { + m_compression_method = compression_method; + } + + private: + Server_Information m_server_info; + std::string m_srp_identifier; + u16bit m_srtp_profile; + Protocol_Version m_protocol_version; + u16bit m_ciphersuite; + byte m_compression_method; + }; /** * Uninitialized session */ Session() : m_start_time(std::chrono::system_clock::time_point::min()), - m_version(), - m_ciphersuite(0), - m_compression_method(0), m_connection_side(static_cast<Connection_Side>(0)), - m_srtp_profile(0), - m_extended_master_secret(false) - {} + m_extended_master_secret(false), + m_properties() {} /** * New session (sets session start time) */ Session(const std::vector<byte>& session_id, const secure_vector<byte>& master_secret, - Protocol_Version version, - u16bit ciphersuite, - byte compression_method, Connection_Side side, bool supports_extended_master_secret, const std::vector<X509_Certificate>& peer_certs, const std::vector<byte>& session_ticket, - const Server_Information& server_info, - const std::string& srp_identifier, - u16bit srtp_profile); + Properties properties); /** * Load a session from DER representation (created by DER_encode) @@ -112,22 +184,22 @@ class BOTAN_DLL Session /** * Get the version of the saved session */ - Protocol_Version version() const { return m_version; } + Protocol_Version version() const { return m_properties.get_protocol_version(); } /** * Get the ciphersuite code of the saved session */ - u16bit ciphersuite_code() const { return m_ciphersuite; } + u16bit ciphersuite_code() const { return m_properties.get_ciphersuite(); } /** * Get the ciphersuite info of the saved session */ - Ciphersuite ciphersuite() const { return Ciphersuite::by_id(m_ciphersuite); } + Ciphersuite ciphersuite() const { return Ciphersuite::by_id(ciphersuite_code()); } /** * Get the compression method used in the saved session */ - byte compression_method() const { return m_compression_method; } + byte compression_method() const { return m_properties.get_compression_method(); } /** * Get which side of the connection the resumed session we are/were @@ -138,7 +210,7 @@ class BOTAN_DLL Session /** * Get the SRP identity (if sent by the client in the initial handshake) */ - const std::string& srp_identifier() const { return m_srp_identifier; } + const std::string& srp_identifier() const { return m_properties.get_srp_identifier(); } /** * Get the saved master secret @@ -153,7 +225,7 @@ class BOTAN_DLL Session /** * Get the negotiated DTLS-SRTP algorithm (RFC 5764) */ - u16bit dtls_srtp_profile() const { return m_srtp_profile; } + u16bit dtls_srtp_profile() const { return m_properties.get_srtp_profile(); } bool supports_extended_master_secret() const { return m_extended_master_secret; } @@ -177,7 +249,7 @@ class BOTAN_DLL Session */ const std::vector<byte>& session_ticket() const { return m_session_ticket; } - const Server_Information& server_info() const { return m_server_info; } + const Server_Information& server_info() const { return m_properties.get_server_info(); } private: enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160103 }; @@ -188,16 +260,10 @@ class BOTAN_DLL Session std::vector<byte> m_session_ticket; // only used by client side secure_vector<byte> m_master_secret; - Protocol_Version m_version; - u16bit m_ciphersuite; - byte m_compression_method; Connection_Side m_connection_side; - u16bit m_srtp_profile; bool m_extended_master_secret; - std::vector<X509_Certificate> m_peer_certs; - Server_Information m_server_info; // optional - std::string m_srp_identifier; // optional + Properties m_properties; }; } diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index f125bfcb5..6e6ac29a0 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -1,5 +1,6 @@ /* * (C) 2014,2015 Jack Lloyd +* 2016 Matthias Gierlings * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -218,10 +219,12 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, { std::vector<byte> c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent; - Botan::TLS::Server server(queue_inserter(s2c_traffic), - queue_inserter(server_recv), - print_alert, - handshake_complete, + + Botan::TLS::Server server(Botan::TLS::Server::Callbacks( + queue_inserter(s2c_traffic), + queue_inserter(server_recv), + print_alert, + handshake_complete), server_sessions, creds, policy, @@ -229,17 +232,19 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, next_protocol_chooser, false); - Botan::TLS::Client client(queue_inserter(c2s_traffic), - queue_inserter(client_recv), - print_alert, - handshake_complete, + Botan::TLS::Client::Callbacks client_callbacks(queue_inserter(c2s_traffic), + queue_inserter(client_recv), + print_alert, + handshake_complete); + Botan::TLS::Client client(client_callbacks, client_sessions, creds, policy, rng, - Botan::TLS::Server_Information("server.example.com"), - offer_version, - protocols_offered); + Botan::TLS::Client::Properties( + Botan::TLS::Server_Information("server.example.com"), + offer_version, + protocols_offered)); size_t rounds = 0; @@ -444,10 +449,11 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, { std::vector<byte> c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent; - Botan::TLS::Server server(queue_inserter(s2c_traffic), - queue_inserter(server_recv), - print_alert, - handshake_complete, + Botan::TLS::Server::Callbacks server_callbacks(queue_inserter(s2c_traffic), + queue_inserter(server_recv), + print_alert, + handshake_complete); + Botan::TLS::Server server(server_callbacks, server_sessions, creds, policy, @@ -455,17 +461,19 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, next_protocol_chooser, true); - Botan::TLS::Client client(queue_inserter(c2s_traffic), - queue_inserter(client_recv), - print_alert, - handshake_complete, + Botan::TLS::Client::Callbacks client_callbacks(queue_inserter(c2s_traffic), + queue_inserter(client_recv), + print_alert, + handshake_complete); + Botan::TLS::Client client(client_callbacks, client_sessions, creds, policy, rng, - Botan::TLS::Server_Information("server.example.com"), - offer_version, - protocols_offered); + Botan::TLS::Client::Properties( + Botan::TLS::Server_Information("server.example.com"), + offer_version, + protocols_offered)); size_t rounds = 0; |