diff options
author | René Meusel <[email protected]> | 2022-04-04 14:44:05 +0200 |
---|---|---|
committer | René Meusel <[email protected]> | 2022-04-04 15:12:38 +0200 |
commit | d95869cab3dce4835ac66e06c09ff0266fe5ab94 (patch) | |
tree | 1934c6f4bda866ee4a41f5656b8a2a791db0e335 /src/lib/tls | |
parent | 3b3965ffc688196314fe8ae5ff26084421fc549d (diff) |
Extract helper for handshake message transitions
The extracted class `Handshake_Transitions` will be used independently from the
TLS 1.2 `Handshake_State` in the upcoming TLS 1.3 code base.
Co-Authored-By: Hannes Rantzsch <[email protected]>
Diffstat (limited to 'src/lib/tls')
-rw-r--r-- | src/lib/tls/info.txt | 1 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_state.cpp | 140 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_state.h | 4 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_transitions.cpp | 176 | ||||
-rw-r--r-- | src/lib/tls/tls_handshake_transitions.h | 66 |
5 files changed, 249 insertions, 138 deletions
diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt index 3ed102e9c..2cebae346 100644 --- a/src/lib/tls/info.txt +++ b/src/lib/tls/info.txt @@ -27,6 +27,7 @@ tls_version.h tls_handshake_hash.h tls_handshake_io.h tls_handshake_state.h +tls_handshake_transitions.h tls_reader.h tls_record.h tls_seq_numbers.h diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp index 56c3b1b21..0fbce3750 100644 --- a/src/lib/tls/tls_handshake_state.cpp +++ b/src/lib/tls/tls_handshake_state.cpp @@ -76,104 +76,6 @@ const char* handshake_type_to_string(Handshake_Type type) "Unknown TLS handshake message type " + std::to_string(type)); } -namespace { - -uint32_t bitmask_for_handshake_type(Handshake_Type type) - { - switch(type) - { - case HELLO_VERIFY_REQUEST: - return (1 << 0); - - case HELLO_REQUEST: - return (1 << 1); - - case CLIENT_HELLO: - return (1 << 2); - - case SERVER_HELLO: - return (1 << 3); - - case CERTIFICATE: - return (1 << 4); - - case CERTIFICATE_URL: - return (1 << 5); - - case CERTIFICATE_STATUS: - return (1 << 6); - - case SERVER_KEX: - return (1 << 7); - - case CERTIFICATE_REQUEST: - return (1 << 8); - - case SERVER_HELLO_DONE: - return (1 << 9); - - case CERTIFICATE_VERIFY: - return (1 << 10); - - case CLIENT_KEX: - return (1 << 11); - - case NEW_SESSION_TICKET: - return (1 << 12); - - case HANDSHAKE_CCS: - return (1 << 13); - - case FINISHED: - return (1 << 14); - - // allow explicitly disabling new handshakes - case HANDSHAKE_NONE: - return 0; - } - - throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, - "Unknown TLS handshake message type " + std::to_string(type)); - } - -std::string handshake_mask_to_string(uint32_t mask, char combiner) - { - const Handshake_Type types[] = { - HELLO_VERIFY_REQUEST, - HELLO_REQUEST, - CLIENT_HELLO, - SERVER_HELLO, - CERTIFICATE, - CERTIFICATE_URL, - CERTIFICATE_STATUS, - SERVER_KEX, - CERTIFICATE_REQUEST, - SERVER_HELLO_DONE, - CERTIFICATE_VERIFY, - CLIENT_KEX, - NEW_SESSION_TICKET, - HANDSHAKE_CCS, - FINISHED - }; - - std::ostringstream o; - bool empty = true; - - for(auto&& t : types) - { - if(mask & bitmask_for_handshake_type(t)) - { - if(!empty) - o << combiner; - o << handshake_type_to_string(t); - empty = false; - } - } - - return o.str(); - } - -} /* * Initialize the SSL/TLS Handshake State @@ -313,57 +215,23 @@ void Handshake_State::compute_session_keys(const secure_vector<uint8_t>& resume_ void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg) { - const uint32_t mask = bitmask_for_handshake_type(handshake_msg); - - m_hand_received_mask |= mask; - - const bool ok = (m_hand_expecting_mask & mask) != 0; // overlap? - - if(!ok) - { - const uint32_t seen_so_far = m_hand_received_mask & ~mask; - - std::ostringstream msg; - - msg << "Unexpected state transition in handshake got a " << handshake_type_to_string(handshake_msg); - - if(m_hand_expecting_mask == 0) - msg << " not expecting messages"; - else - msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|'); - - if(seen_so_far != 0) - msg << " seen " << handshake_mask_to_string(seen_so_far, '+'); - - throw Unexpected_Message(msg.str()); - } - - /* We don't know what to expect next, so force a call to - set_expected_next; if it doesn't happen, the next transition - check will always fail which is what we want. - */ - m_hand_expecting_mask = 0; + m_transitions.confirm_transition_to(handshake_msg); } void Handshake_State::set_expected_next(Handshake_Type handshake_msg) { - m_hand_expecting_mask |= bitmask_for_handshake_type(handshake_msg); + m_transitions.set_expected_next(handshake_msg); } bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const { - const uint32_t mask = bitmask_for_handshake_type(handshake_msg); - - return (m_hand_received_mask & mask) != 0; + return m_transitions.received_handshake_msg(handshake_msg); } std::pair<Handshake_Type, std::vector<uint8_t>> Handshake_State::get_next_handshake_msg() { - const bool expecting_ccs = - (bitmask_for_handshake_type(HANDSHAKE_CCS) & m_hand_expecting_mask) != 0; - - return m_handshake_io->get_next_record(expecting_ccs); + return m_handshake_io->get_next_record(m_transitions.change_cipher_spec_expected()); } std::vector<uint8_t> Handshake_State::session_ticket() const diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls_handshake_state.h index 1cc22d029..ebd267677 100644 --- a/src/lib/tls/tls_handshake_state.h +++ b/src/lib/tls/tls_handshake_state.h @@ -11,6 +11,7 @@ #include <botan/internal/tls_handshake_hash.h> #include <botan/internal/tls_handshake_io.h> +#include <botan/internal/tls_handshake_transitions.h> #include <botan/internal/tls_session_key.h> #include <botan/tls_ciphersuite.h> #include <botan/tls_exceptn.h> @@ -176,8 +177,7 @@ class Handshake_State std::unique_ptr<Handshake_IO> m_handshake_io; - uint32_t m_hand_expecting_mask = 0; - uint32_t m_hand_received_mask = 0; + Handshake_Transitions m_transitions; Protocol_Version m_version; std::optional<Ciphersuite> m_ciphersuite; Session_Keys m_session_keys; diff --git a/src/lib/tls/tls_handshake_transitions.cpp b/src/lib/tls/tls_handshake_transitions.cpp new file mode 100644 index 000000000..9bf3611c1 --- /dev/null +++ b/src/lib/tls/tls_handshake_transitions.cpp @@ -0,0 +1,176 @@ +/* +* TLS Handshake State Transitions +* (C) 2004-2006,2011,2012 Jack Lloyd +* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/internal/tls_handshake_transitions.h> + +#include <botan/tls_exceptn.h> + +#include <sstream> + +namespace Botan::TLS { + +namespace { + +uint32_t bitmask_for_handshake_type(Handshake_Type type) + { + switch(type) + { + case HELLO_VERIFY_REQUEST: + return (1 << 0); + + case HELLO_REQUEST: + return (1 << 1); + + case CLIENT_HELLO: + return (1 << 2); + + case SERVER_HELLO: + return (1 << 3); + + case CERTIFICATE: + return (1 << 4); + + case CERTIFICATE_URL: + return (1 << 5); + + case CERTIFICATE_STATUS: + return (1 << 6); + + case SERVER_KEX: + return (1 << 7); + + case CERTIFICATE_REQUEST: + return (1 << 8); + + case SERVER_HELLO_DONE: + return (1 << 9); + + case CERTIFICATE_VERIFY: + return (1 << 10); + + case CLIENT_KEX: + return (1 << 11); + + case NEW_SESSION_TICKET: + return (1 << 12); + + case HANDSHAKE_CCS: + return (1 << 13); + + case FINISHED: + return (1 << 14); + + // allow explicitly disabling new handshakes + case HANDSHAKE_NONE: + return 0; + } + + throw TLS_Exception(Alert::UNEXPECTED_MESSAGE, + "Unknown TLS handshake message type " + std::to_string(type)); + } + +std::string handshake_mask_to_string(uint32_t mask, char combiner) + { + const Handshake_Type types[] = + { + HELLO_VERIFY_REQUEST, + HELLO_REQUEST, + CLIENT_HELLO, + SERVER_HELLO, + CERTIFICATE, + CERTIFICATE_URL, + CERTIFICATE_STATUS, + SERVER_KEX, + CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, + CERTIFICATE_VERIFY, + CLIENT_KEX, + NEW_SESSION_TICKET, + HANDSHAKE_CCS, + FINISHED + }; + + std::ostringstream o; + bool empty = true; + + for(auto&& t : types) + { + if(mask & bitmask_for_handshake_type(t)) + { + if(!empty) + { o << combiner; } + o << handshake_type_to_string(t); + empty = false; + } + } + + return o.str(); + } + +} + +bool Handshake_Transitions::received_handshake_msg(Handshake_Type msg_type) const + { + const uint32_t mask = bitmask_for_handshake_type(msg_type); + + return (m_hand_received_mask & mask) != 0; + } + +void Handshake_Transitions::confirm_transition_to(Handshake_Type msg_type) + { + const uint32_t mask = bitmask_for_handshake_type(msg_type); + + m_hand_received_mask |= mask; + + const bool ok = (m_hand_expecting_mask & mask) != 0; // overlap? + + if(!ok) + { + const uint32_t seen_so_far = m_hand_received_mask & ~mask; + + std::ostringstream msg; + + msg << "Unexpected state transition in handshake got a " << handshake_type_to_string(msg_type); + + if(m_hand_expecting_mask == 0) + { msg << " not expecting messages"; } + else + { msg << " expected " << handshake_mask_to_string(m_hand_expecting_mask, '|'); } + + if(seen_so_far != 0) + { msg << " seen " << handshake_mask_to_string(seen_so_far, '+'); } + + throw Unexpected_Message(msg.str()); + } + + /* We don't know what to expect next, so force a call to + set_expected_next; if it doesn't happen, the next transition + check will always fail which is what we want. + */ + m_hand_expecting_mask = 0; + } + +void Handshake_Transitions::set_expected_next(Handshake_Type msg_type) + { + m_hand_expecting_mask |= bitmask_for_handshake_type(msg_type); + } + +void Handshake_Transitions::set_expected_next(const std::vector<Handshake_Type>& msg_types) + { + for (const auto type : msg_types) + { + set_expected_next(type); + } + } + +bool Handshake_Transitions::change_cipher_spec_expected() const + { + return (bitmask_for_handshake_type(HANDSHAKE_CCS) & m_hand_expecting_mask) != 0; + } + +} diff --git a/src/lib/tls/tls_handshake_transitions.h b/src/lib/tls/tls_handshake_transitions.h new file mode 100644 index 000000000..019ec3ca5 --- /dev/null +++ b/src/lib/tls/tls_handshake_transitions.h @@ -0,0 +1,66 @@ +/* +* TLS Handshake State Transitions +* (C) 2004-2006,2011,2012 Jack Lloyd +* 2017 Harry Reimann, Rohde & Schwarz Cybersecurity +* 2022 René Meusel, Hannes Rantzsch - neXenio GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_TRANSITIONS_H_ +#define BOTAN_TLS_HANDSHAKE_TRANSITIONS_H_ + +#include <vector> + +#include <botan/tls_magic.h> + +namespace Botan::TLS { + +/** + * Manages the expectations for incoming handshake messages in both TLS 1.2 and 1.3. + * This does not bear any knowledge about the actual state machine but is a mere + * helper to implement state transition validation. + */ +class BOTAN_TEST_API Handshake_Transitions + { + public: + /** + * Return true iff we have received a particular message already + * @param msg_type the message type + */ + bool received_handshake_msg(Handshake_Type msg_type) const; + + /** + * Confirm that we were expecting this message type + * @param msg_type the message type + */ + void confirm_transition_to(Handshake_Type msg_type); + + /** + * Record that we are expecting a particular message type next + * @param msg_type the message type + */ + void set_expected_next(Handshake_Type msg_type); + + /** + * Record that we are expecting one of the enumerated message types next. + * Note that receiving any of the expected messages in `confirm_transition_to` + * resets _all_ the expectations. + * + * @param msg_types the message types + */ + void set_expected_next(const std::vector<Handshake_Type>& msg_types); + + /** + * Check whether a Change Cipher Spec must be expected + */ + bool change_cipher_spec_expected() const; + + private: + uint32_t m_hand_expecting_mask = 0; + uint32_t m_hand_received_mask = 0; + }; + +} + +#endif |