aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls/tls_channel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tls/tls_channel.cpp')
-rw-r--r--src/tls/tls_channel.cpp668
1 files changed, 0 insertions, 668 deletions
diff --git a/src/tls/tls_channel.cpp b/src/tls/tls_channel.cpp
deleted file mode 100644
index 8ed876b3f..000000000
--- a/src/tls/tls_channel.cpp
+++ /dev/null
@@ -1,668 +0,0 @@
-/*
-* TLS Channels
-* (C) 2011-2012 Jack Lloyd
-*
-* Released under the terms of the Botan license
-*/
-
-#include <botan/tls_channel.h>
-#include <botan/internal/tls_handshake_state.h>
-#include <botan/internal/tls_messages.h>
-#include <botan/internal/tls_heartbeats.h>
-#include <botan/internal/tls_record.h>
-#include <botan/internal/tls_seq_numbers.h>
-#include <botan/internal/rounding.h>
-#include <botan/internal/stl_util.h>
-#include <botan/loadstor.h>
-
-namespace Botan {
-
-namespace TLS {
-
-Channel::Channel(std::function<void (const byte[], size_t)> output_fn,
- std::function<void (const byte[], size_t)> data_cb,
- std::function<void (Alert, const byte[], size_t)> alert_cb,
- std::function<bool (const Session&)> handshake_cb,
- Session_Manager& session_manager,
- RandomNumberGenerator& rng,
- size_t reserved_io_buffer_size) :
- m_handshake_cb(handshake_cb),
- m_data_cb(data_cb),
- m_alert_cb(alert_cb),
- m_output_fn(output_fn),
- m_rng(rng),
- m_session_manager(session_manager)
- {
- m_writebuf.reserve(reserved_io_buffer_size);
- m_readbuf.reserve(reserved_io_buffer_size);
- }
-
-void Channel::reset_state()
- {
- m_active_state.reset();
- m_pending_state.reset();
- m_readbuf.clear();
- m_write_cipher_states.clear();
- m_read_cipher_states.clear();
- }
-
-Channel::~Channel()
- {
- // So unique_ptr destructors run correctly
- }
-
-Connection_Sequence_Numbers& Channel::sequence_numbers() const
- {
- BOTAN_ASSERT(m_sequence_numbers, "Have a sequence numbers object");
- return *m_sequence_numbers;
- }
-
-std::shared_ptr<Connection_Cipher_State> Channel::read_cipher_state_epoch(u16bit epoch) const
- {
- auto i = m_read_cipher_states.find(epoch);
-
- BOTAN_ASSERT(i != m_read_cipher_states.end(),
- "Have a cipher state for the specified epoch");
-
- return i->second;
- }
-
-std::shared_ptr<Connection_Cipher_State> Channel::write_cipher_state_epoch(u16bit epoch) const
- {
- auto i = m_write_cipher_states.find(epoch);
-
- BOTAN_ASSERT(i != m_write_cipher_states.end(),
- "Have a cipher state for the specified epoch");
-
- return i->second;
- }
-
-std::vector<X509_Certificate> Channel::peer_cert_chain() const
- {
- if(auto active = active_state())
- return get_peer_cert_chain(*active);
- return std::vector<X509_Certificate>();
- }
-
-Handshake_State& Channel::create_handshake_state(Protocol_Version version)
- {
- if(pending_state())
- throw Internal_Error("create_handshake_state called during handshake");
-
- if(auto active = active_state())
- {
- Protocol_Version active_version = active->version();
-
- if(active_version.is_datagram_protocol() != version.is_datagram_protocol())
- throw std::runtime_error("Active state using version " +
- active_version.to_string() +
- " cannot change to " +
- version.to_string() +
- " in pending");
- }
-
- if(!m_sequence_numbers)
- {
- if(version.is_datagram_protocol())
- m_sequence_numbers.reset(new Datagram_Sequence_Numbers);
- else
- m_sequence_numbers.reset(new Stream_Sequence_Numbers);
- }
-
- std::unique_ptr<Handshake_IO> io;
- if(version.is_datagram_protocol())
- io.reset(new Datagram_Handshake_IO(
- sequence_numbers(),
- std::bind(&Channel::send_record_under_epoch, this,
- std::placeholders::_1,
- std::placeholders::_2,
- std::placeholders::_3)));
- else
- io.reset(new Stream_Handshake_IO(
- std::bind(&Channel::send_record, this,
- std::placeholders::_1,
- std::placeholders::_2)));
-
- m_pending_state.reset(new_handshake_state(io.release()));
-
- if(auto active = active_state())
- m_pending_state->set_version(active->version());
-
- return *m_pending_state.get();
- }
-
-void Channel::renegotiate(bool force_full_renegotiation)
- {
- if(pending_state()) // currently in handshake?
- return;
-
- if(auto active = active_state())
- initiate_handshake(create_handshake_state(active->version()),
- force_full_renegotiation);
- else
- throw std::runtime_error("Cannot renegotiate on inactive connection");
- }
-
-size_t Channel::maximum_fragment_size() const
- {
- // should we be caching this value?
-
- if(auto pending = pending_state())
- if(auto server_hello = pending->server_hello())
- if(size_t frag = server_hello->fragment_size())
- return frag;
-
- if(auto active = active_state())
- if(size_t frag = active->server_hello()->fragment_size())
- return frag;
-
- return MAX_PLAINTEXT_SIZE;
- }
-
-void Channel::change_cipher_spec_reader(Connection_Side side)
- {
- auto pending = pending_state();
-
- BOTAN_ASSERT(pending && pending->server_hello(),
- "Have received server hello");
-
- if(pending->server_hello()->compression_method() != NO_COMPRESSION)
- throw Internal_Error("Negotiated unknown compression algorithm");
-
- sequence_numbers().new_read_cipher_state();
-
- const u16bit epoch = sequence_numbers().current_read_epoch();
-
- BOTAN_ASSERT(m_read_cipher_states.count(epoch) == 0,
- "No read cipher state currently set for next epoch");
-
- // flip side as we are reading
- std::shared_ptr<Connection_Cipher_State> read_state(
- new Connection_Cipher_State(pending->version(),
- (side == CLIENT) ? SERVER : CLIENT,
- false,
- pending->ciphersuite(),
- pending->session_keys()));
-
- m_read_cipher_states[epoch] = read_state;
- }
-
-void Channel::change_cipher_spec_writer(Connection_Side side)
- {
- auto pending = pending_state();
-
- BOTAN_ASSERT(pending && pending->server_hello(),
- "Have received server hello");
-
- if(pending->server_hello()->compression_method() != NO_COMPRESSION)
- throw Internal_Error("Negotiated unknown compression algorithm");
-
- sequence_numbers().new_write_cipher_state();
-
- const u16bit epoch = sequence_numbers().current_write_epoch();
-
- BOTAN_ASSERT(m_write_cipher_states.count(epoch) == 0,
- "No write cipher state currently set for next epoch");
-
- std::shared_ptr<Connection_Cipher_State> write_state(
- new Connection_Cipher_State(pending->version(),
- side,
- true,
- pending->ciphersuite(),
- pending->session_keys()));
-
- m_write_cipher_states[epoch] = write_state;
- }
-
-bool Channel::is_active() const
- {
- return (active_state() != nullptr);
- }
-
-bool Channel::is_closed() const
- {
- if(active_state() || pending_state())
- return false;
-
- /*
- * If no active or pending state, then either we had a connection
- * and it has been closed, or we are a server which has never
- * received a connection. This case is detectable by also lacking
- * m_sequence_numbers
- */
- return (m_sequence_numbers != nullptr);
- }
-
-void Channel::activate_session()
- {
- std::swap(m_active_state, m_pending_state);
- m_pending_state.reset();
-
- if(m_active_state->version().is_datagram_protocol())
- {
- // FIXME, remove old states when we are sure not needed anymore
- }
- else
- {
- // TLS is easy just remove all but the current state
- auto current_epoch = sequence_numbers().current_write_epoch();
-
- const auto not_current_epoch =
- [current_epoch](u16bit epoch) { return (epoch != current_epoch); };
-
- map_remove_if(not_current_epoch, m_write_cipher_states);
- map_remove_if(not_current_epoch, m_read_cipher_states);
- }
- }
-
-bool Channel::peer_supports_heartbeats() const
- {
- if(auto active = active_state())
- return active->server_hello()->supports_heartbeats();
- return false;
- }
-
-bool Channel::heartbeat_sending_allowed() const
- {
- if(auto active = active_state())
- return active->server_hello()->peer_can_send_heartbeats();
- return false;
- }
-
-size_t Channel::received_data(const std::vector<byte>& buf)
- {
- return this->received_data(&buf[0], buf.size());
- }
-
-size_t Channel::received_data(const byte input[], size_t input_size)
- {
- const auto get_cipherstate = [this](u16bit epoch)
- { return this->read_cipher_state_epoch(epoch).get(); };
-
- const size_t max_fragment_size = maximum_fragment_size();
-
- try
- {
- while(!is_closed() && input_size)
- {
- secure_vector<byte> record;
- u64bit record_sequence = 0;
- Record_Type record_type = NO_RECORD;
- Protocol_Version record_version;
-
- size_t consumed = 0;
-
- const size_t needed =
- read_record(m_readbuf,
- input,
- input_size,
- consumed,
- record,
- &record_sequence,
- &record_version,
- &record_type,
- m_sequence_numbers.get(),
- get_cipherstate);
-
- BOTAN_ASSERT(consumed <= input_size,
- "Record reader consumed sane amount");
-
- input += consumed;
- input_size -= consumed;
-
- BOTAN_ASSERT(input_size == 0 || needed == 0,
- "Got a full record or consumed all input");
-
- if(input_size == 0 && needed != 0)
- return needed; // need more data to complete record
-
- if(record.size() > max_fragment_size)
- throw TLS_Exception(Alert::RECORD_OVERFLOW,
- "Plaintext record is too large");
-
- if(record_type == HANDSHAKE || record_type == CHANGE_CIPHER_SPEC)
- {
- if(!m_pending_state)
- {
- create_handshake_state(record_version);
- if(record_version.is_datagram_protocol())
- sequence_numbers().read_accept(record_sequence);
- }
-
- 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);
- }
- }
- else if(record_type == HEARTBEAT && peer_supports_heartbeats())
- {
- if(!active_state())
- throw Unexpected_Message("Heartbeat sent before handshake done");
-
- Heartbeat_Message heartbeat(unlock(record));
-
- const std::vector<byte>& payload = heartbeat.payload();
-
- if(heartbeat.is_request())
- {
- if(!pending_state())
- {
- Heartbeat_Message response(Heartbeat_Message::RESPONSE,
- &payload[0], payload.size());
-
- send_record(HEARTBEAT, response.contents());
- }
- }
- else
- {
- m_alert_cb(Alert(Alert::HEARTBEAT_PAYLOAD), &payload[0], payload.size());
- }
- }
- 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[0], record.size());
- }
- 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;
- }
- }
- else
- throw Unexpected_Message("Unexpected record type " +
- std::to_string(record_type) +
- " from counterparty");
- }
-
- return 0; // on a record boundary
- }
- catch(TLS_Exception& e)
- {
- send_fatal_alert(e.type());
- throw;
- }
- catch(Integrity_Failure& e)
- {
- send_fatal_alert(Alert::BAD_RECORD_MAC);
- throw;
- }
- catch(Decoding_Error& e)
- {
- send_fatal_alert(Alert::DECODE_ERROR);
- throw;
- }
- catch(...)
- {
- send_fatal_alert(Alert::INTERNAL_ERROR);
- throw;
- }
- }
-
-void Channel::heartbeat(const byte payload[], size_t payload_size)
- {
- if(heartbeat_sending_allowed())
- {
- Heartbeat_Message heartbeat(Heartbeat_Message::REQUEST,
- payload, payload_size);
-
- send_record(HEARTBEAT, heartbeat.contents());
- }
- }
-
-void Channel::write_record(Connection_Cipher_State* cipher_state,
- byte record_type, const byte input[], size_t length)
- {
- BOTAN_ASSERT(m_pending_state || m_active_state,
- "Some connection state exists");
-
- Protocol_Version record_version =
- (m_pending_state) ? (m_pending_state->version()) : (m_active_state->version());
-
- TLS::write_record(m_writebuf,
- record_type,
- input,
- length,
- record_version,
- sequence_numbers().next_write_sequence(),
- cipher_state,
- m_rng);
-
- m_output_fn(&m_writebuf[0], m_writebuf.size());
- }
-
-void Channel::send_record_array(u16bit epoch, byte type, const byte input[], size_t length)
- {
- if(length == 0)
- return;
-
- /*
- * If using CBC mode without an explicit IV (SSL v3 or TLS v1.0),
- * send a single byte of plaintext to randomize the (implicit) IV of
- * the following main block. If using a stream cipher, or TLS v1.1
- * or higher, this isn't necessary.
- *
- * An empty record also works but apparently some implementations do
- * not like this (https://bugzilla.mozilla.org/show_bug.cgi?id=665814)
- *
- * See http://www.openssl.org/~bodo/tls-cbc.txt for background.
- */
-
- auto cipher_state = write_cipher_state_epoch(epoch);
-
- if(type == APPLICATION_DATA && cipher_state->cbc_without_explicit_iv())
- {
- write_record(cipher_state.get(), type, &input[0], 1);
- input += 1;
- length -= 1;
- }
-
- const size_t max_fragment_size = maximum_fragment_size();
-
- while(length)
- {
- const size_t sending = std::min(length, max_fragment_size);
- write_record(cipher_state.get(), type, &input[0], sending);
-
- input += sending;
- length -= sending;
- }
- }
-
-void Channel::send_record(byte record_type, const std::vector<byte>& record)
- {
- send_record_array(sequence_numbers().current_write_epoch(),
- record_type, &record[0], record.size());
- }
-
-void Channel::send_record_under_epoch(u16bit epoch, byte record_type,
- const std::vector<byte>& record)
- {
- send_record_array(epoch, record_type, &record[0], record.size());
- }
-
-void Channel::send(const byte buf[], size_t buf_size)
- {
- if(!is_active())
- throw std::runtime_error("Data cannot be sent on inactive TLS connection");
-
- send_record_array(sequence_numbers().current_write_epoch(),
- APPLICATION_DATA, buf, buf_size);
- }
-
-void Channel::send(const std::string& string)
- {
- this->send(reinterpret_cast<const byte*>(string.c_str()), string.size());
- }
-
-void Channel::send_alert(const Alert& alert)
- {
- if(alert.is_valid() && !is_closed())
- {
- try
- {
- send_record(ALERT, alert.serialize());
- }
- catch(...) { /* swallow it */ }
- }
-
- if(alert.type() == Alert::NO_RENEGOTIATION)
- m_pending_state.reset();
-
- if(alert.is_fatal())
- if(auto active = active_state())
- m_session_manager.remove_entry(active->server_hello()->session_id());
-
- if(alert.type() == Alert::CLOSE_NOTIFY || alert.is_fatal())
- reset_state();
- }
-
-void Channel::secure_renegotiation_check(const Client_Hello* client_hello)
- {
- const bool secure_renegotiation = client_hello->secure_renegotiation();
-
- if(auto active = active_state())
- {
- const bool active_sr = active->client_hello()->secure_renegotiation();
-
- if(active_sr != secure_renegotiation)
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
- "Client changed its mind about secure renegotiation");
- }
-
- if(secure_renegotiation)
- {
- const std::vector<byte>& data = client_hello->renegotiation_info();
-
- if(data != secure_renegotiation_data_for_client_hello())
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
- "Client sent bad values for secure renegotiation");
- }
- }
-
-void Channel::secure_renegotiation_check(const Server_Hello* server_hello)
- {
- const bool secure_renegotiation = server_hello->secure_renegotiation();
-
- if(auto active = active_state())
- {
- const bool active_sr = active->client_hello()->secure_renegotiation();
-
- if(active_sr != secure_renegotiation)
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
- "Server changed its mind about secure renegotiation");
- }
-
- if(secure_renegotiation)
- {
- const std::vector<byte>& data = server_hello->renegotiation_info();
-
- if(data != secure_renegotiation_data_for_server_hello())
- throw TLS_Exception(Alert::HANDSHAKE_FAILURE,
- "Server sent bad values for secure renegotiation");
- }
- }
-
-std::vector<byte> Channel::secure_renegotiation_data_for_client_hello() const
- {
- if(auto active = active_state())
- return active->client_finished()->verify_data();
- return std::vector<byte>();
- }
-
-std::vector<byte> Channel::secure_renegotiation_data_for_server_hello() const
- {
- if(auto active = active_state())
- {
- std::vector<byte> buf = active->client_finished()->verify_data();
- buf += active->server_finished()->verify_data();
- return buf;
- }
-
- return std::vector<byte>();
- }
-
-bool Channel::secure_renegotiation_supported() const
- {
- if(auto active = active_state())
- return active->server_hello()->secure_renegotiation();
-
- if(auto pending = pending_state())
- if(auto hello = pending->server_hello())
- return hello->secure_renegotiation();
-
- return false;
- }
-
-SymmetricKey Channel::key_material_export(const std::string& label,
- const std::string& context,
- size_t length) const
- {
- if(auto active = active_state())
- {
- std::unique_ptr<KDF> prf(active->protocol_specific_prf());
-
- const secure_vector<byte>& master_secret =
- active->session_keys().master_secret();
-
- std::vector<byte> salt;
- salt += to_byte_vector(label);
- salt += active->client_hello()->random();
- salt += active->server_hello()->random();
-
- if(context != "")
- {
- size_t context_size = context.length();
- if(context_size > 0xFFFF)
- throw std::runtime_error("key_material_export context is too long");
- salt.push_back(get_byte<u16bit>(0, context_size));
- salt.push_back(get_byte<u16bit>(1, context_size));
- salt += to_byte_vector(context);
- }
-
- return prf->derive_key(length, master_secret, salt);
- }
- else
- throw std::runtime_error("Channel::key_material_export connection not active");
- }
-
-}
-
-}
-