diff options
Diffstat (limited to 'src/tls')
-rw-r--r-- | src/tls/c_hello.cpp | 4 | ||||
-rw-r--r-- | src/tls/info.txt | 1 | ||||
-rw-r--r-- | src/tls/s_hello.cpp | 8 | ||||
-rw-r--r-- | src/tls/tls_client.cpp | 30 | ||||
-rw-r--r-- | src/tls/tls_extensions.cpp | 2 | ||||
-rw-r--r-- | src/tls/tls_handshake_state.cpp | 9 | ||||
-rw-r--r-- | src/tls/tls_handshake_state.h | 1 | ||||
-rw-r--r-- | src/tls/tls_messages.h | 22 | ||||
-rw-r--r-- | src/tls/tls_server.cpp | 1 | ||||
-rw-r--r-- | src/tls/tls_session.cpp | 88 | ||||
-rw-r--r-- | src/tls/tls_session.h | 18 |
11 files changed, 120 insertions, 64 deletions
diff --git a/src/tls/c_hello.cpp b/src/tls/c_hello.cpp index 712fde30c..df76c748f 100644 --- a/src/tls/c_hello.cpp +++ b/src/tls/c_hello.cpp @@ -71,8 +71,8 @@ Client_Hello::Client_Hello(Record_Writer& writer, m_next_protocol(next_protocol), m_fragment_size(0), m_secure_renegotiation(true), - m_supports_session_ticket(true), - m_renegotiation_info(reneg_info) + m_renegotiation_info(reneg_info), + m_supports_session_ticket(true) { std::vector<std::string> hashes = policy.allowed_hashes(); std::vector<std::string> sigs = policy.allowed_signature_methods(); diff --git a/src/tls/info.txt b/src/tls/info.txt index 822914a3d..9cc936471 100644 --- a/src/tls/info.txt +++ b/src/tls/info.txt @@ -45,6 +45,7 @@ rec_read.cpp rec_wri.cpp s_hello.cpp s_kex.cpp +session_ticket.cpp tls_channel.cpp tls_client.cpp tls_extensions.cpp diff --git a/src/tls/s_hello.cpp b/src/tls/s_hello.cpp index 9bcbdb5e9..3e30382f6 100644 --- a/src/tls/s_hello.cpp +++ b/src/tls/s_hello.cpp @@ -89,6 +89,7 @@ Server_Hello::Server_Hello(Record_Writer& writer, Server_Hello::Server_Hello(const MemoryRegion<byte>& buf) { m_secure_renegotiation = false; + m_supports_session_ticket = false; m_next_protocol = false; if(buf.size() < 38) @@ -132,6 +133,13 @@ Server_Hello::Server_Hello(const MemoryRegion<byte>& buf) m_next_protocols = npn->protocols(); m_next_protocol = true; } + + if(Session_Ticket* ticket = extensions.get<Session_Ticket>()) + { + if(!ticket->contents().empty()) + throw Decoding_Error("TLS server sent non-empty session ticket extension"); + m_supports_session_ticket = true; + } } /* diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp index 02e24a1c9..f6e016725 100644 --- a/src/tls/tls_client.cpp +++ b/src/tls/tls_client.cpp @@ -11,6 +11,9 @@ #include <botan/internal/stl_util.h> #include <memory> +#include <stdio.h> +#include <botan/hex.h> + namespace Botan { namespace TLS { @@ -173,6 +176,13 @@ void Client::process_handshake_msg(Handshake_Type type, "Server sent next protocol but we didn't request it"); } + if(state->server_hello->supports_session_ticket()) + { + if(!state->client_hello->supports_session_ticket()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server sent session ticket extension but we did not"); + } + state->set_version(state->server_hello->version()); writer.set_version(state->version()); @@ -199,7 +209,10 @@ void Client::process_handshake_msg(Handshake_Type type, state->resume_master_secret, true); - state->set_expected_next(HANDSHAKE_CCS); + if(state->server_hello->supports_session_ticket()) + state->set_expected_next(NEW_SESSION_TICKET); + else + state->set_expected_next(HANDSHAKE_CCS); } else { @@ -306,8 +319,6 @@ void Client::process_handshake_msg(Handshake_Type type, } else if(type == SERVER_HELLO_DONE) { - state->set_expected_next(HANDSHAKE_CCS); - state->server_hello_done = new Server_Hello_Done(contents); if(state->received_handshake_msg(CERTIFICATE_REQUEST)) @@ -364,6 +375,17 @@ void Client::process_handshake_msg(Handshake_Type type, } state->client_finished = new Finished(writer, state, CLIENT); + + if(state->server_hello->supports_session_ticket()) + state->set_expected_next(NEW_SESSION_TICKET); + else + state->set_expected_next(HANDSHAKE_CCS); + } + else if(type == NEW_SESSION_TICKET) + { + state->new_session_ticket = new New_Session_Ticket(contents); + + state->set_expected_next(HANDSHAKE_CCS); } else if(type == HANDSHAKE_CCS) { @@ -404,6 +426,8 @@ void Client::process_handshake_msg(Handshake_Type type, secure_renegotiation.supported(), state->server_hello->fragment_size(), peer_certs, + state->new_session_ticket ? state->new_session_ticket->ticket() : + MemoryVector<byte>(), state->client_hello->sni_hostname(), "" ); diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp index c1b62ef16..ae236d0c5 100644 --- a/src/tls/tls_extensions.cpp +++ b/src/tls/tls_extensions.cpp @@ -507,7 +507,7 @@ Signature_Algorithms::Signature_Algorithms(TLS_Data_Reader& reader, Session_Ticket::Session_Ticket(const TLS_Data_Reader& reader, u16bit extension_size) { - +#warning "can't read session tickets" } } diff --git a/src/tls/tls_handshake_state.cpp b/src/tls/tls_handshake_state.cpp index 2db97db0a..4a82a1641 100644 --- a/src/tls/tls_handshake_state.cpp +++ b/src/tls/tls_handshake_state.cpp @@ -54,12 +54,15 @@ u32bit bitmask_for_handshake_type(Handshake_Type type) case NEXT_PROTOCOL: return (1 << 9); - case HANDSHAKE_CCS: + case NEW_SESSION_TICKET: return (1 << 10); - case FINISHED: + case HANDSHAKE_CCS: return (1 << 11); + case FINISHED: + return (1 << 12); + // allow explicitly disabling new handshakes case HANDSHAKE_NONE: return 0; @@ -85,6 +88,7 @@ Handshake_State::Handshake_State(Handshake_Reader* reader) cert_req = 0; server_hello_done = 0; next_protocol = 0; + new_session_ticket = 0; client_certs = 0; client_kex = 0; @@ -287,6 +291,7 @@ Handshake_State::~Handshake_State() delete cert_req; delete server_hello_done; delete next_protocol; + delete new_session_ticket; delete client_certs; delete client_kex; diff --git a/src/tls/tls_handshake_state.h b/src/tls/tls_handshake_state.h index 206e19096..52bfd24b2 100644 --- a/src/tls/tls_handshake_state.h +++ b/src/tls/tls_handshake_state.h @@ -80,6 +80,7 @@ class Handshake_State class Certificate_Verify* client_verify; class Next_Protocol* next_protocol; + class New_Session_Ticket* new_session_ticket; class Finished* client_finished; class Finished* server_finished; diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h index baee610d9..019dafa69 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -178,6 +178,8 @@ class Server_Hello : public Handshake_Message bool next_protocol_notification() const { return m_next_protocol; } + bool supports_session_ticket() const { return m_supports_session_ticket; } + const std::vector<std::string>& next_protocols() const { return m_next_protocols; } @@ -227,6 +229,7 @@ class Server_Hello : public Handshake_Message MemoryVector<byte> m_renegotiation_info; bool m_next_protocol; + bool m_supports_session_ticket; std::vector<std::string> m_next_protocols; }; @@ -467,23 +470,18 @@ class New_Session_Ticket : public Handshake_Message public: Handshake_Type type() const { return NEW_SESSION_TICKET; } - static Session decrypt(const MemoryRegion<byte>& ctext, - const SymmetricKey& key, - const MemoryRegion<byte>& key_name); - - const MemoryVector<byte>& contents() const { return m_contents; } + u32bit ticket_lifetime_hint() const { return m_ticket_lifetime_hint; } + const MemoryVector<byte>& ticket() const { return m_ticket; } - New_Session_Ticket(const Session& session_info, - const SymmetricKey& key, - const MemoryRegion<byte>& key_name, - RandomNumberGenerator& rng); + New_Session_Ticket(const MemoryVector<byte>& ticket, u32bit lifetime = 0) : + m_ticket_lifetime_hint(lifetime), m_ticket(ticket) {} - New_Session_Ticket(const MemoryRegion<byte>& buf) { deserialize(buf); } + New_Session_Ticket(const MemoryRegion<byte>& buf); private: MemoryVector<byte> serialize() const; - void deserialize(const MemoryRegion<byte>&); - MemoryVector<byte> m_contents; + u32bit m_ticket_lifetime_hint; + MemoryVector<byte> m_ticket; }; } diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp index eacbc02e0..874aa064c 100644 --- a/src/tls/tls_server.cpp +++ b/src/tls/tls_server.cpp @@ -447,6 +447,7 @@ void Server::process_handshake_msg(Handshake_Type type, secure_renegotiation.supported(), state->server_hello->fragment_size(), peer_certs, + MemoryVector<byte>(), m_hostname, "" ); diff --git a/src/tls/tls_session.cpp b/src/tls/tls_session.cpp index ddf0661c6..3d890210f 100644 --- a/src/tls/tls_session.cpp +++ b/src/tls/tls_session.cpp @@ -12,6 +12,7 @@ #include <botan/pem.h> #include <botan/time.h> #include <botan/lookup.h> +#include <botan/loadstor.h> #include <memory> namespace Botan { @@ -27,10 +28,12 @@ Session::Session(const MemoryRegion<byte>& session_identifier, bool secure_renegotiation_supported, size_t fragment_size, const std::vector<X509_Certificate>& certs, + const MemoryRegion<byte>& ticket, const std::string& sni_hostname, const std::string& srp_identifier) : m_start_time(system_time()), m_identifier(session_identifier), + m_session_ticket(ticket), m_master_secret(master_secret), m_version(version), m_ciphersuite(ciphersuite), @@ -44,6 +47,13 @@ Session::Session(const MemoryRegion<byte>& session_identifier, { } +Session::Session(const std::string& pem) + { + SecureVector<byte> der = PEM_Code::decode_check_label(pem, "SSL SESSION"); + + *this = Session(&der[0], der.size()); + } + Session::Session(const byte ber[], size_t ber_len) { BER_Decoder decoder(ber, ber_len); @@ -60,10 +70,11 @@ Session::Session(const byte ber[], size_t ber_len) .start_cons(SEQUENCE) .decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION), "Unknown version in session structure") - .decode(m_identifier, OCTET_STRING) .decode_integer_type(m_start_time) .decode_integer_type(major_version) .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(side_code) @@ -90,13 +101,6 @@ Session::Session(const byte ber[], size_t ber_len) } } -Session::Session(const std::string& pem) - { - SecureVector<byte> der = PEM_Code::decode_check_label(pem, "SSL SESSION"); - - *this = Session(&der[0], der.size()); - } - SecureVector<byte> Session::DER_encode() const { MemoryVector<byte> peer_cert_bits; @@ -106,10 +110,11 @@ SecureVector<byte> Session::DER_encode() const return DER_Encoder() .start_cons(SEQUENCE) .encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION)) - .encode(m_identifier, OCTET_STRING) .encode(static_cast<size_t>(m_start_time)) .encode(static_cast<size_t>(m_version.major_version())) .encode(static_cast<size_t>(m_version.minor_version())) + .encode(m_identifier, OCTET_STRING) + .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>(m_connection_side)) @@ -128,25 +133,23 @@ std::string Session::PEM_encode() const return PEM_Code::encode(this->DER_encode(), "SSL SESSION"); } +namespace { + +const u64bit ENCRYPTED_SESSION_MAGIC = 0xACE4480800000000; + +} + MemoryVector<byte> Session::encrypt(const SymmetricKey& master_key, - const MemoryRegion<byte>& key_name, RandomNumberGenerator& rng) { - if(key_name.size() != 16) - throw Encoding_Error("Bad length " + to_string(key_name.size()) + - " for key_name in TLS_Session::encrypt"); - - if(master_key.length() == 0) - throw Decoding_Error("Session encryption key not set"); - std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)")); SymmetricKey aes_key = kdf->derive_key(32, master_key.bits_of(), - "session-ticket.cipher-key"); + "tls.session.cipher-key"); SymmetricKey hmac_key = kdf->derive_key(32, master_key.bits_of(), - "session-ticket.hmac-key"); + "tls.session.hmac-key"); InitializationVector aes_iv(rng, 16); @@ -157,8 +160,8 @@ Session::encrypt(const SymmetricKey& master_key, pipe.process_msg(this->DER_encode()); MemoryVector<byte> ctext = pipe.read_all(0); - MemoryVector<byte> out; - out += key_name; + MemoryVector<byte> out(8); + store_be(ENCRYPTED_SESSION_MAGIC, &out[0]); out += aes_iv.bits_of(); out += ctext; @@ -169,25 +172,24 @@ Session::encrypt(const SymmetricKey& master_key, } Session Session::decrypt(const MemoryRegion<byte>& buf, - const SymmetricKey& master_key, - const MemoryRegion<byte>& key_name) + const SymmetricKey& master_key) { try { - if(buf.size() < (16 + 16 + 32 + 1)) - throw Decoding_Error("Encrypted TLS_Session too short to be real"); - - if(master_key.length() == 0) - throw Decoding_Error("Session master_key not set"); - - if(key_name.size() != 16) - throw Decoding_Error("Bad length " + to_string(key_name.size()) + - " for key_name"); + /* + 8 bytes header + 16 bytes IV + 32 bytes MAC + 16 bytes per AES block * 4 blocks (absolute min amount due to + 48 bytes master secret) + */ + if(buf.size() < (8 + 16 + 32 + 4*16)) + throw Decoding_Error("Encrypted TLS session too short to be valid"); std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)")); SymmetricKey hmac_key = kdf->derive_key(32, master_key.bits_of(), - "session-ticket.hmac-key"); + "tls.session.mac-key"); std::auto_ptr<MessageAuthenticationCode> mac(get_mac("HMAC(SHA-256)")); mac->set_key(hmac_key); @@ -196,22 +198,28 @@ Session Session::decrypt(const MemoryRegion<byte>& buf, MemoryVector<byte> computed_mac = mac->final(); if(!same_mem(&buf[buf.size() - 32], &computed_mac[0], computed_mac.size())) - throw Decoding_Error("MAC verification failed"); + throw Decoding_Error("MAC verification failed for encrypted session"); + + const u64bit header = load_be<u64bit>(buf, 0); + + if(header != ENCRYPTED_SESSION_MAGIC) + throw Decoding_Error("Unknown header value in encrypted session"); SymmetricKey aes_key = kdf->derive_key(32, master_key.bits_of(), - "session-ticket.cipher-key"); + "tls.session.cipher-key"); - InitializationVector aes_iv(&buf[16], 16); + InitializationVector aes_iv(&buf[8], 16); Pipe pipe(get_cipher("AES-256/CBC", aes_key, aes_iv, DECRYPTION)); - pipe.process_msg(&buf[16], buf.size() - (16 + 32)); - MemoryVector<byte> ber = pipe.read_all(); + pipe.process_msg(&buf[8+16], buf.size() - (32 + 8 + 16)); + SecureVector<byte> ber = pipe.read_all(); return Session(&ber[0], ber.size()); } - catch(...) + catch(std::exception& e) { - throw Decoding_Error("Failed to decrypt encrypted session"); + throw Decoding_Error("Failed to decrypt encrypted session -" + + std::string(e.what())); } } diff --git a/src/tls/tls_session.h b/src/tls/tls_session.h index 64a83367e..ab11154c9 100644 --- a/src/tls/tls_session.h +++ b/src/tls/tls_session.h @@ -51,6 +51,7 @@ class BOTAN_DLL Session bool secure_renegotiation_supported, size_t fragment_size, const std::vector<X509_Certificate>& peer_certs, + const MemoryRegion<byte>& session_ticket, const std::string& sni_hostname = "", const std::string& srp_identifier = ""); @@ -75,12 +76,15 @@ class BOTAN_DLL Session * Encrypt a session (useful for serialization or session tickets) */ MemoryVector<byte> encrypt(const SymmetricKey& key, - const MemoryRegion<byte>& key_name, RandomNumberGenerator& rng); + /** + * Decrypt a session created by encrypt + * @param ctext the ciphertext returned by encrypt + * @param key the same key used by the encrypting side + */ static Session decrypt(const MemoryRegion<byte>& ctext, - const SymmetricKey& key, - const MemoryRegion<byte>& key_name); + const SymmetricKey& key); /** * Encode this session data for storage @@ -158,12 +162,18 @@ class BOTAN_DLL Session */ u64bit start_time() const { return m_start_time; } + /** + * Return the session ticket the server gave us + */ + const MemoryVector<byte>& session_ticket() const { return m_session_ticket; } + private: - enum { TLS_SESSION_PARAM_STRUCT_VERSION = 1 }; + enum { TLS_SESSION_PARAM_STRUCT_VERSION = 0x2994e300 }; u64bit m_start_time; MemoryVector<byte> m_identifier; + MemoryVector<byte> m_session_ticket; // only used by client side SecureVector<byte> m_master_secret; Protocol_Version m_version; |