diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build-data/cc/gcc.txt | 2 | ||||
-rw-r--r-- | src/tls/c_hello.cpp | 31 | ||||
-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 | 9 | ||||
-rw-r--r-- | src/tls/tls_extensions.h | 35 | ||||
-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 | 30 | ||||
-rw-r--r-- | src/tls/tls_reader.h | 9 | ||||
-rw-r--r-- | src/tls/tls_server.cpp | 1 | ||||
-rw-r--r-- | src/tls/tls_session.cpp | 118 | ||||
-rw-r--r-- | src/tls/tls_session.h | 26 |
14 files changed, 288 insertions, 22 deletions
diff --git a/src/build-data/cc/gcc.txt b/src/build-data/cc/gcc.txt index 3de85099c..6173f6271 100644 --- a/src/build-data/cc/gcc.txt +++ b/src/build-data/cc/gcc.txt @@ -13,7 +13,7 @@ add_lib_option -l lang_flags "-D_REENTRANT -Wno-long-long" warning_flags "-W -Wall" -maintainer_warning_flags "-Werror -Weffc++ -Wall -Wextra -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wold-style-cast" +maintainer_warning_flags "-Werror -Wall -Wextra -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wold-style-cast" lib_opt_flags "-O3" check_opt_flags "-O2" diff --git a/src/tls/c_hello.cpp b/src/tls/c_hello.cpp index 35389f37b..df76c748f 100644 --- a/src/tls/c_hello.cpp +++ b/src/tls/c_hello.cpp @@ -71,7 +71,8 @@ Client_Hello::Client_Hello(Record_Writer& writer, m_next_protocol(next_protocol), m_fragment_size(0), m_secure_renegotiation(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(); @@ -86,7 +87,7 @@ Client_Hello::Client_Hello(Record_Writer& writer, } /* -* Create a new Client Hello message +* Create a new Client Hello message (session resumption case) */ Client_Hello::Client_Hello(Record_Writer& writer, Handshake_Hash& hash, @@ -100,7 +101,8 @@ Client_Hello::Client_Hello(Record_Writer& writer, m_srp_identifier(session.srp_identifier()), m_next_protocol(next_protocol), m_fragment_size(session.fragment_size()), - m_secure_renegotiation(session.secure_renegotiation()) + m_secure_renegotiation(session.secure_renegotiation()), + m_supports_session_ticket(true) { m_suites.push_back(session.ciphersuite_code()); m_comp_methods.push_back(session.compression_method()); @@ -110,10 +112,14 @@ Client_Hello::Client_Hello(Record_Writer& writer, hash.update(writer.send(*this)); } +/* +* Read a counterparty client hello +*/ Client_Hello::Client_Hello(const MemoryRegion<byte>& buf, Handshake_Type type) { m_next_protocol = false; m_secure_renegotiation = false; + m_supports_session_ticket = false; m_fragment_size = 0; if(type == CLIENT_HELLO) @@ -153,6 +159,7 @@ MemoryVector<byte> Client_Hello::serialize() const extensions.add(new Server_Name_Indicator(m_hostname)); extensions.add(new SRP_Identifier(m_srp_identifier)); extensions.add(new Supported_Elliptic_Curves(m_supported_curves)); + extensions.add(new Session_Ticket()); if(m_version >= Protocol_Version::TLS_V12) extensions.add(new Signature_Algorithms(m_supported_algos)); @@ -302,6 +309,24 @@ void Client_Hello::deserialize(const MemoryRegion<byte>& buf) } } + if(Maximum_Fragment_Length* frag = extensions.get<Maximum_Fragment_Length>()) + { + m_fragment_size = frag->fragment_size(); + } + + if(Session_Ticket* ticket = extensions.get<Session_Ticket>()) + { + m_supports_session_ticket = true; + m_session_ticket = ticket->contents(); + } + + if(Renegotation_Extension* reneg = extensions.get<Renegotation_Extension>()) + { + // checked by TLS_Client / TLS_Server as they know the handshake state + m_secure_renegotiation = true; + m_renegotiation_info = reneg->renegotiation_info(); + } + if(value_exists(m_suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) { /* 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 5d345cc9b..ae236d0c5 100644 --- a/src/tls/tls_extensions.cpp +++ b/src/tls/tls_extensions.cpp @@ -42,6 +42,9 @@ Extension* make_extension(TLS_Data_Reader& reader, case TLSEXT_NEXT_PROTOCOL: return new Next_Protocol_Notification(reader, size); + case TLSEXT_SESSION_TICKET: + return new Session_Ticket(reader, size); + default: return 0; // not known } @@ -501,6 +504,12 @@ 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_extensions.h b/src/tls/tls_extensions.h index 180216b8b..c2b9091db 100644 --- a/src/tls/tls_extensions.h +++ b/src/tls/tls_extensions.h @@ -1,6 +1,6 @@ /* * TLS Extensions -* (C) 2011 Jack Lloyd +* (C) 2011-2012 Jack Lloyd * * Released under the terms of the Botan license */ @@ -210,6 +210,39 @@ class Next_Protocol_Notification : public Extension std::vector<std::string> m_protocols; }; +class Session_Ticket : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SESSION_TICKET; } + + Handshake_Extension_Type type() const { return static_type(); } + + const MemoryVector<byte>& contents() const { return m_ticket; } + + /** + * Create empty extension, used by both client and server + */ + Session_Ticket() {} + + /** + * Extension with ticket, used by client + */ + Session_Ticket(const MemoryRegion<byte>& session_ticket) : + m_ticket(session_ticket) {} + + /** + * Deserialize a session ticket + */ + Session_Ticket(const TLS_Data_Reader& reader, u16bit extension_size); + + MemoryVector<byte> serialize() const { return m_ticket; } + + bool empty() const { return false; } + private: + MemoryVector<byte> m_ticket; + }; + /** * Supported Elliptic Curves Extension (RFC 4492) */ 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 513fdad70..019dafa69 100644 --- a/src/tls/tls_messages.h +++ b/src/tls/tls_messages.h @@ -108,6 +108,11 @@ class Client_Hello : public Handshake_Message size_t fragment_size() const { return m_fragment_size; } + bool supports_session_ticket() const { return m_supports_session_ticket; } + + const MemoryRegion<byte>& session_ticket() const + { return m_session_ticket; } + Client_Hello(Record_Writer& writer, Handshake_Hash& hash, const Policy& policy, @@ -145,6 +150,9 @@ class Client_Hello : public Handshake_Message std::vector<std::pair<std::string, std::string> > m_supported_algos; std::vector<std::string> m_supported_curves; + + bool m_supports_session_ticket; + MemoryVector<byte> m_session_ticket; }; /** @@ -170,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; } @@ -219,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; }; @@ -454,6 +465,25 @@ class Next_Protocol : public Handshake_Message std::string m_protocol; }; +class New_Session_Ticket : public Handshake_Message + { + public: + Handshake_Type type() const { return NEW_SESSION_TICKET; } + + u32bit ticket_lifetime_hint() const { return m_ticket_lifetime_hint; } + const MemoryVector<byte>& ticket() const { return m_ticket; } + + 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); + private: + MemoryVector<byte> serialize() const; + + u32bit m_ticket_lifetime_hint; + MemoryVector<byte> m_ticket; + }; + } } diff --git a/src/tls/tls_reader.h b/src/tls/tls_reader.h index 162f691aa..f6b0d4088 100644 --- a/src/tls/tls_reader.h +++ b/src/tls/tls_reader.h @@ -50,6 +50,15 @@ class TLS_Data_Reader offset += bytes; } + u16bit get_u32bit() + { + assert_at_least(4); + u16bit result = make_u32bit(buf[offset ], buf[offset+1], + buf[offset+2], buf[offset+3]); + offset += 4; + return result; + } + u16bit get_u16bit() { assert_at_least(2); 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 7a32c33d8..a9eb8f95c 100644 --- a/src/tls/tls_session.cpp +++ b/src/tls/tls_session.cpp @@ -1,6 +1,6 @@ /* * TLS Session State -* (C) 2011 Jack Lloyd +* (C) 2011-2012 Jack Lloyd * * Released under the terms of the Botan license */ @@ -11,6 +11,9 @@ #include <botan/asn1_str.h> #include <botan/pem.h> #include <botan/time.h> +#include <botan/lookup.h> +#include <botan/loadstor.h> +#include <memory> namespace Botan { @@ -25,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), @@ -42,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) { byte side_code = 0; @@ -56,10 +68,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) @@ -86,13 +99,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; @@ -102,10 +108,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)) @@ -124,6 +131,97 @@ 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, + RandomNumberGenerator& rng) + { + std::auto_ptr<KDF> kdf(get_kdf("KDF2(SHA-256)")); + + SymmetricKey aes_key = kdf->derive_key(32, master_key.bits_of(), + "tls.session.cipher-key"); + + SymmetricKey hmac_key = kdf->derive_key(32, master_key.bits_of(), + "tls.session.hmac-key"); + + InitializationVector aes_iv(rng, 16); + + std::auto_ptr<MessageAuthenticationCode> mac(get_mac("HMAC(SHA-256)")); + mac->set_key(hmac_key); + + Pipe pipe(get_cipher("AES-256/CBC", aes_key, aes_iv, ENCRYPTION)); + pipe.process_msg(this->DER_encode()); + MemoryVector<byte> ctext = pipe.read_all(0); + + MemoryVector<byte> out(8); + store_be(ENCRYPTED_SESSION_MAGIC, &out[0]); + out += aes_iv.bits_of(); + out += ctext; + + mac->update(out); + + out += mac->final(); + return out; + } + +Session Session::decrypt(const MemoryRegion<byte>& buf, + const SymmetricKey& master_key) + { + try + { + /* + 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(), + "tls.session.mac-key"); + + std::auto_ptr<MessageAuthenticationCode> mac(get_mac("HMAC(SHA-256)")); + mac->set_key(hmac_key); + + mac->update(&buf[0], buf.size() - 32); + 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 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(), + "tls.session.cipher-key"); + + InitializationVector aes_iv(&buf[8], 16); + + Pipe pipe(get_cipher("AES-256/CBC", aes_key, aes_iv, DECRYPTION)); + pipe.process_msg(&buf[8+16], buf.size() - (32 + 8 + 16)); + SecureVector<byte> ber = pipe.read_all(); + + return Session(&ber[0], ber.size()); + } + catch(std::exception& e) + { + 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 96b6d6daf..ab11154c9 100644 --- a/src/tls/tls_session.h +++ b/src/tls/tls_session.h @@ -1,6 +1,6 @@ /* * TLS Session -* (C) 2011 Jack Lloyd +* (C) 2011-2012 Jack Lloyd * * Released under the terms of the Botan license */ @@ -13,6 +13,7 @@ #include <botan/tls_ciphersuite.h> #include <botan/tls_magic.h> #include <botan/secmem.h> +#include <botan/symkey.h> namespace Botan { @@ -50,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 = ""); @@ -71,6 +73,20 @@ class BOTAN_DLL Session SecureVector<byte> DER_encode() const; /** + * Encrypt a session (useful for serialization or session tickets) + */ + MemoryVector<byte> encrypt(const SymmetricKey& key, + 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); + + /** * Encode this session data for storage * @warning if the master secret is compromised so is the * session traffic @@ -146,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; |