aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/tls
diff options
context:
space:
mode:
authorRené Meusel <[email protected]>2022-04-04 14:44:05 +0200
committerRené Meusel <[email protected]>2022-04-04 15:12:38 +0200
commitd95869cab3dce4835ac66e06c09ff0266fe5ab94 (patch)
tree1934c6f4bda866ee4a41f5656b8a2a791db0e335 /src/lib/tls
parent3b3965ffc688196314fe8ae5ff26084421fc549d (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.txt1
-rw-r--r--src/lib/tls/tls_handshake_state.cpp140
-rw-r--r--src/lib/tls/tls_handshake_state.h4
-rw-r--r--src/lib/tls/tls_handshake_transitions.cpp176
-rw-r--r--src/lib/tls/tls_handshake_transitions.h66
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