aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cli/tls_client.cpp17
-rw-r--r--src/cli/tls_proxy.cpp1
-rw-r--r--src/cli/tls_server.cpp10
-rw-r--r--src/lib/cert/x509/x509_ca.cpp19
-rw-r--r--src/lib/cert/x509/x509_ca.h24
-rw-r--r--src/lib/cert/x509/x509self.cpp11
-rw-r--r--src/lib/tls/info.txt1
-rw-r--r--src/lib/tls/msg_cert_req.cpp6
-rw-r--r--src/lib/tls/msg_certificate.cpp6
-rw-r--r--src/lib/tls/msg_client_hello.cpp31
-rw-r--r--src/lib/tls/msg_server_hello.cpp29
-rw-r--r--src/lib/tls/tls_blocking.cpp19
-rw-r--r--src/lib/tls/tls_blocking.h5
-rw-r--r--src/lib/tls/tls_channel.cpp229
-rw-r--r--src/lib/tls/tls_channel.h81
-rw-r--r--src/lib/tls/tls_ciphersuite.h37
-rw-r--r--src/lib/tls/tls_client.cpp87
-rw-r--r--src/lib/tls/tls_client.h95
-rw-r--r--src/lib/tls/tls_extensions.h2
-rw-r--r--src/lib/tls/tls_handshake_msg.h14
-rw-r--r--src/lib/tls/tls_messages.h83
-rw-r--r--src/lib/tls/tls_record.cpp151
-rw-r--r--src/lib/tls/tls_record.h91
-rw-r--r--src/lib/tls/tls_server.cpp822
-rw-r--r--src/lib/tls/tls_server.h52
-rw-r--r--src/lib/tls/tls_server_handshake_state.h47
-rw-r--r--src/lib/tls/tls_session.cpp58
-rw-r--r--src/lib/tls/tls_session.h118
-rw-r--r--src/tests/unit_tls.cpp52
29 files changed, 1256 insertions, 942 deletions
diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp
index 6af2f56f8..7db8bacf8 100644
--- a/src/cli/tls_client.cpp
+++ b/src/cli/tls_client.cpp
@@ -1,5 +1,6 @@
/*
* (C) 2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -118,17 +119,19 @@ class TLS_Client final : public Command
version = Botan::TLS::Protocol_Version::TLS_V11;
}
- Botan::TLS::Client client(socket_write,
- std::bind(&TLS_Client::process_data, this, _1, _2),
- std::bind(&TLS_Client::alert_received, this, _1, _2, _3),
- std::bind(&TLS_Client::handshake_complete, this, _1),
+ Botan::TLS::Client client(Botan::TLS::Client::Callbacks(
+ socket_write,
+ std::bind(&TLS_Client::process_data, this, _1, _2),
+ std::bind(&TLS_Client::alert_received, this, _1, _2, _3),
+ std::bind(&TLS_Client::handshake_complete, this, _1)),
*session_mgr,
creds,
*policy,
rng(),
- Botan::TLS::Server_Information(host, port),
- version,
- protocols_to_offer);
+ Botan::TLS::Client::Properties(
+ Botan::TLS::Server_Information(host, port),
+ version,
+ protocols_to_offer));
bool first_active = true;
diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp
index 2929e473d..f5f666cf1 100644
--- a/src/cli/tls_proxy.cpp
+++ b/src/cli/tls_proxy.cpp
@@ -1,6 +1,7 @@
/*
* TLS Server Proxy
* (C) 2014,2015 Jack Lloyd
+* (C) 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp
index 2496f5508..335f36c97 100644
--- a/src/cli/tls_server.cpp
+++ b/src/cli/tls_server.cpp
@@ -1,6 +1,7 @@
/*
* TLS echo server using BSD sockets
* (C) 2014 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -106,10 +107,11 @@ class TLS_Server final : public Command
}
};
- Botan::TLS::Server server(socket_write,
- proc_fn,
- std::bind(&TLS_Server::alert_received, this, _1, _2, _3),
- std::bind(&TLS_Server::handshake_complete, this, _1),
+ Botan::TLS::Server server(Botan::TLS::Server::Callbacks(
+ socket_write,
+ proc_fn,
+ std::bind(&TLS_Server::alert_received, this, _1, _2, _3),
+ std::bind(&TLS_Server::handshake_complete, this, _1)),
session_manager,
creds,
policy,
diff --git a/src/lib/cert/x509/x509_ca.cpp b/src/lib/cert/x509/x509_ca.cpp
index 147fdd6ad..3f7af77f5 100644
--- a/src/lib/cert/x509/x509_ca.cpp
+++ b/src/lib/cert/x509/x509_ca.cpp
@@ -78,8 +78,10 @@ X509_Certificate X509_CA::sign_request(const PKCS10_Request& req,
return make_cert(m_signer, rng, m_ca_sig_algo,
req.raw_public_key(),
- not_before, not_after,
- m_cert.subject_dn(), req.subject_dn(),
+ Certificate_Properties(not_before,
+ not_after,
+ m_cert.subject_dn(),
+ req.subject_dn()),
extensions);
}
@@ -90,10 +92,7 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer,
RandomNumberGenerator& rng,
const AlgorithmIdentifier& sig_algo,
const std::vector<byte>& pub_key,
- const X509_Time& not_before,
- const X509_Time& not_after,
- const X509_DN& issuer_dn,
- const X509_DN& subject_dn,
+ const Certificate_Properties properties,
const Extensions& extensions)
{
const size_t X509_CERT_VERSION = 3;
@@ -112,14 +111,14 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer,
.encode(serial_no)
.encode(sig_algo)
- .encode(issuer_dn)
+ .encode(properties.get_issuer_dn())
.start_cons(SEQUENCE)
- .encode(not_before)
- .encode(not_after)
+ .encode(properties.get_not_before())
+ .encode(properties.get_not_after())
.end_cons()
- .encode(subject_dn)
+ .encode(properties.get_subject_dn())
.raw_bytes(pub_key)
.start_explicit(3)
diff --git a/src/lib/cert/x509/x509_ca.h b/src/lib/cert/x509/x509_ca.h
index 6ea51cd06..8cedb9db9 100644
--- a/src/lib/cert/x509/x509_ca.h
+++ b/src/lib/cert/x509/x509_ca.h
@@ -22,6 +22,25 @@ namespace Botan {
class BOTAN_DLL X509_CA
{
public:
+ class Certificate_Properties
+ {
+ public:
+ Certificate_Properties(X509_Time not_before, X509_Time not_after,
+ X509_DN issuer_dn, X509_DN subject_dn)
+ : m_not_before(not_before), m_not_after(not_after),
+ m_issuer_dn(issuer_dn), m_subject_dn(subject_dn) {}
+
+ const X509_Time& get_not_before() const { return m_not_before; }
+ const X509_Time& get_not_after() const { return m_not_after; }
+ const X509_DN& get_issuer_dn() const { return m_issuer_dn; }
+ const X509_DN& get_subject_dn() const { return m_subject_dn; }
+
+ private:
+ X509_Time m_not_before;
+ X509_Time m_not_after;
+ X509_DN m_issuer_dn;
+ X509_DN m_subject_dn;
+ };
/**
* Sign a PKCS#10 Request.
@@ -82,10 +101,7 @@ class BOTAN_DLL X509_CA
RandomNumberGenerator& rng,
const AlgorithmIdentifier& sig_algo,
const std::vector<byte>& pub_key,
- const X509_Time& not_before,
- const X509_Time& not_after,
- const X509_DN& issuer_dn,
- const X509_DN& subject_dn,
+ const Certificate_Properties properties,
const Extensions& extensions);
/**
diff --git a/src/lib/cert/x509/x509self.cpp b/src/lib/cert/x509/x509self.cpp
index 8b9aeda09..62f9fc370 100644
--- a/src/lib/cert/x509/x509self.cpp
+++ b/src/lib/cert/x509/x509self.cpp
@@ -75,9 +75,14 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts,
extensions.add(
new Cert_Extension::Extended_Key_Usage(opts.ex_constraints));
- return X509_CA::make_cert(signer.get(), rng, sig_algo, pub_key,
- opts.start, opts.end,
- subject_dn, subject_dn,
+ return X509_CA::make_cert(signer.get(),
+ rng,
+ sig_algo,
+ pub_key,
+ X509_CA::Certificate_Properties(opts.start,
+ opts.end,
+ subject_dn,
+ subject_dn),
extensions);
}
diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt
index a43d5619a..de15a65e5 100644
--- a/src/lib/tls/info.txt
+++ b/src/lib/tls/info.txt
@@ -29,6 +29,7 @@ tls_messages.h
tls_reader.h
tls_record.h
tls_seq_numbers.h
+tls_server_handshake_state.h
tls_session_key.h
</header:internal>
diff --git a/src/lib/tls/msg_cert_req.cpp b/src/lib/tls/msg_cert_req.cpp
index 4fd528148..569a5aa63 100644
--- a/src/lib/tls/msg_cert_req.cpp
+++ b/src/lib/tls/msg_cert_req.cpp
@@ -1,6 +1,7 @@
/*
* Certificate Request Message
* (C) 2004-2006,2012 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -51,8 +52,7 @@ byte cert_type_name_to_code(const std::string& name)
/**
* Create a new Certificate Request message
*/
-Certificate_Req::Certificate_Req(Handshake_IO& io,
- Handshake_Hash& hash,
+Certificate_Req::Certificate_Req(Handshake_Info& hs_info,
const Policy& policy,
const std::vector<X509_DN>& ca_certs,
Protocol_Version version) :
@@ -69,7 +69,7 @@ Certificate_Req::Certificate_Req(Handshake_IO& io,
m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j]));
}
- hash.update(io.send(*this));
+ hs_info.get_hash().update(hs_info.get_io().send(*this));
}
/**
diff --git a/src/lib/tls/msg_certificate.cpp b/src/lib/tls/msg_certificate.cpp
index 32e3e17f0..a622d8573 100644
--- a/src/lib/tls/msg_certificate.cpp
+++ b/src/lib/tls/msg_certificate.cpp
@@ -1,6 +1,7 @@
/*
* Certificate Message
* (C) 2004-2006,2012 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -20,12 +21,11 @@ namespace TLS {
/**
* Create a new Certificate message
*/
-Certificate::Certificate(Handshake_IO& io,
- Handshake_Hash& hash,
+Certificate::Certificate(Handshake_Info& hs_info,
const std::vector<X509_Certificate>& cert_list) :
m_certs(cert_list)
{
- hash.update(io.send(*this));
+ hs_info.get_hash().update(hs_info.get_io().send(*this));
}
/**
diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp
index 23807215f..9c080b9a5 100644
--- a/src/lib/tls/msg_client_hello.cpp
+++ b/src/lib/tls/msg_client_hello.cpp
@@ -1,6 +1,7 @@
/*
* TLS Hello Request and Client Hello Messages
* (C) 2004-2011,2015,2016 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -66,24 +67,21 @@ std::vector<byte> Hello_Request::serialize() const
/*
* Create a new Client Hello message
*/
-Client_Hello::Client_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
- Protocol_Version version,
+Client_Hello::Client_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& reneg_info,
- const std::vector<std::string>& next_protocols,
- const std::string& hostname,
- const std::string& srp_identifier) :
- m_version(version),
+ const Client_Hello::Settings& client_settings,
+ const std::vector<std::string>& next_protocols) :
+ m_version(client_settings.protocol_version()),
m_random(make_hello_random(rng, policy)),
- m_suites(policy.ciphersuite_list(m_version, (srp_identifier != ""))),
+ m_suites(policy.ciphersuite_list(m_version,
+ client_settings.srp_identifier() != "")),
m_comp_methods(policy.compression())
{
m_extensions.add(new Extended_Master_Secret);
m_extensions.add(new Renegotiation_Extension(reneg_info));
-
- m_extensions.add(new Server_Name_Indicator(hostname));
+ m_extensions.add(new Server_Name_Indicator(client_settings.hostname()));
m_extensions.add(new Session_Ticket());
m_extensions.add(new Supported_Elliptic_Curves(policy.allowed_ecc_curves()));
@@ -98,7 +96,7 @@ Client_Hello::Client_Hello(Handshake_IO& io,
m_extensions.add(new Application_Layer_Protocol_Notification(next_protocols));
#if defined(BOTAN_HAS_SRP6)
- m_extensions.add(new SRP_Identifier(srp_identifier));
+ m_extensions.add(new SRP_Identifier(client_settings.srp_identifier()));
#else
if(!srp_identifier.empty())
{
@@ -106,20 +104,19 @@ Client_Hello::Client_Hello(Handshake_IO& io,
}
#endif
- BOTAN_ASSERT(policy.acceptable_protocol_version(version),
+ BOTAN_ASSERT(policy.acceptable_protocol_version(client_settings.protocol_version()),
"Our policy accepts the version we are offering");
- if(policy.send_fallback_scsv(version))
+ if(policy.send_fallback_scsv(client_settings.protocol_version()))
m_suites.push_back(TLS_FALLBACK_SCSV);
- hash.update(io.send(*this));
+ hs_info.get_hash().update(hs_info.get_io().send(*this));
}
/*
* Create a new Client Hello message (session resumption case)
*/
-Client_Hello::Client_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
+Client_Hello::Client_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& reneg_info,
@@ -165,7 +162,7 @@ Client_Hello::Client_Hello(Handshake_IO& io,
}
#endif
- hash.update(io.send(*this));
+ hs_info.get_hash().update(hs_info.get_io().send(*this));
}
void Client_Hello::update_hello_cookie(const Hello_Verify_Request& hello_verify)
diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp
index f8d0c63c7..2c80ed59a 100644
--- a/src/lib/tls/msg_server_hello.cpp
+++ b/src/lib/tls/msg_server_hello.cpp
@@ -1,6 +1,7 @@
/*
* TLS Server Hello and Server Hello Done
* (C) 2004-2011,2015,2016 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -17,23 +18,18 @@ namespace Botan {
namespace TLS {
// New session case
-Server_Hello::Server_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
+Server_Hello::Server_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& reneg_info,
const Client_Hello& client_hello,
- const std::vector<byte>& new_session_id,
- Protocol_Version new_session_version,
- u16bit ciphersuite,
- byte compression,
- bool offer_session_ticket,
- const std::string& next_protocol) :
- m_version(new_session_version),
- m_session_id(new_session_id),
+ const Server_Hello::Settings& server_settings,
+ const std::string next_protocol) :
+ m_version(server_settings.protocol_version()),
+ m_session_id(server_settings.session_id()),
m_random(make_hello_random(rng, policy)),
- m_ciphersuite(ciphersuite),
- m_comp_method(compression)
+ m_ciphersuite(server_settings.ciphersuite()),
+ m_comp_method(server_settings.compression())
{
if(client_hello.supports_extended_master_secret())
m_extensions.add(new Extended_Master_Secret);
@@ -41,7 +37,7 @@ Server_Hello::Server_Hello(Handshake_IO& io,
if(client_hello.secure_renegotiation())
m_extensions.add(new Renegotiation_Extension(reneg_info));
- if(client_hello.supports_session_ticket() && offer_session_ticket)
+ if(client_hello.supports_session_ticket() && server_settings.offer_session_ticket())
m_extensions.add(new Session_Ticket());
if(!next_protocol.empty() && client_hello.supports_alpn())
@@ -68,12 +64,11 @@ Server_Hello::Server_Hello(Handshake_IO& io,
}
}
- hash.update(io.send(*this));
+ hs_info.get_hash().update(hs_info.get_io().send(*this));
}
// Resuming
-Server_Hello::Server_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
+Server_Hello::Server_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& reneg_info,
@@ -99,7 +94,7 @@ Server_Hello::Server_Hello(Handshake_IO& io,
if(!next_protocol.empty() && client_hello.supports_alpn())
m_extensions.add(new Application_Layer_Protocol_Notification(next_protocol));
- hash.update(io.send(*this));
+ hs_info.get_hash().update(hs_info.get_io().send(*this));
}
/*
diff --git a/src/lib/tls/tls_blocking.cpp b/src/lib/tls/tls_blocking.cpp
index a1867b6b5..14e04693d 100644
--- a/src/lib/tls/tls_blocking.cpp
+++ b/src/lib/tls/tls_blocking.cpp
@@ -1,6 +1,7 @@
/*
* TLS Blocking API
* (C) 2013 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -19,21 +20,19 @@ Blocking_Client::Blocking_Client(read_fn reader,
Credentials_Manager& creds,
const Policy& policy,
RandomNumberGenerator& rng,
- const Server_Information& server_info,
- const Protocol_Version& offer_version,
- const std::vector<std::string>& next) :
+ TLS::Client::Properties& properties) :
m_read(reader),
- m_channel(writer,
- std::bind(&Blocking_Client::data_cb, this, _1, _2),
- std::bind(&Blocking_Client::alert_cb, this, _1, _2, _3),
- std::bind(&Blocking_Client::handshake_cb, this, _1),
+ m_channel(TLS::Client::Callbacks(
+ writer,
+ std::bind(&Blocking_Client::data_cb, this, _1, _2),
+ std::bind(&Blocking_Client::alert_cb, this, _1, _2, _3),
+ std::bind(&Blocking_Client::handshake_cb, this, _1)
+ ),
session_manager,
creds,
policy,
rng,
- server_info,
- offer_version,
- next)
+ properties)
{
}
diff --git a/src/lib/tls/tls_blocking.h b/src/lib/tls/tls_blocking.h
index 00e65cbaf..47c5c7483 100644
--- a/src/lib/tls/tls_blocking.h
+++ b/src/lib/tls/tls_blocking.h
@@ -1,6 +1,7 @@
/*
* TLS Blocking API
* (C) 2013 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -38,9 +39,7 @@ class BOTAN_DLL Blocking_Client
Credentials_Manager& creds,
const Policy& policy,
RandomNumberGenerator& rng,
- const Server_Information& server_info = Server_Information(),
- const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
- const std::vector<std::string>& next_protos = {});
+ TLS::Client::Properties& properties);
/**
* Completes full handshake then returns
diff --git a/src/lib/tls/tls_channel.cpp b/src/lib/tls/tls_channel.cpp
index f445eef99..2c7e80feb 100644
--- a/src/lib/tls/tls_channel.cpp
+++ b/src/lib/tls/tls_channel.cpp
@@ -1,6 +1,7 @@
/*
* TLS Channels
* (C) 2011,2012,2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -18,22 +19,16 @@ namespace Botan {
namespace TLS {
-Channel::Channel(output_fn output_fn,
- data_cb data_cb,
- alert_cb alert_cb,
- handshake_cb handshake_cb,
- handshake_msg_cb handshake_msg_cb,
+size_t TLS::Channel::IO_BUF_DEFAULT_SIZE = 10*1024;
+
+Channel::Channel(const Callbacks& callbacks,
Session_Manager& session_manager,
RandomNumberGenerator& rng,
const Policy& policy,
bool is_datagram,
size_t reserved_io_buffer_size) :
m_is_datagram(is_datagram),
- m_data_cb(data_cb),
- m_alert_cb(alert_cb),
- m_output_fn(output_fn),
- m_handshake_cb(handshake_cb),
- m_handshake_msg_cb(handshake_msg_cb),
+ m_callbacks(callbacks),
m_session_manager(session_manager),
m_policy(policy),
m_rng(rng)
@@ -263,23 +258,19 @@ size_t Channel::received_data(const byte input[], size_t input_size)
{
while(!is_closed() && input_size)
{
- secure_vector<byte> record;
+ secure_vector<byte> record_data;
u64bit record_sequence = 0;
Record_Type record_type = NO_RECORD;
Protocol_Version record_version;
size_t consumed = 0;
+ Record_Raw_Input raw_input(input, input_size, consumed, m_is_datagram);
+ Record record(record_data, &record_sequence, &record_version, &record_type);
const size_t needed =
read_record(m_readbuf,
- input,
- input_size,
- m_is_datagram,
- consumed,
+ raw_input,
record,
- &record_sequence,
- &record_version,
- &record_type,
m_sequence_numbers.get(),
std::bind(&TLS::Channel::read_cipher_state_epoch, this,
std::placeholders::_1));
@@ -298,105 +289,21 @@ size_t Channel::received_data(const byte input[], size_t input_size)
if(input_size == 0 && needed != 0)
return needed; // need more data to complete record
- if(record.size() > MAX_PLAINTEXT_SIZE)
+ if(record_data.size() > MAX_PLAINTEXT_SIZE)
throw TLS_Exception(Alert::RECORD_OVERFLOW,
"TLS plaintext record is larger than allowed maximum");
if(record_type == HANDSHAKE || record_type == CHANGE_CIPHER_SPEC)
{
- if(!m_pending_state)
- {
- // No pending handshake, possibly new:
- if(record_version.is_datagram_protocol())
- {
- if(m_sequence_numbers)
- {
- /*
- * Might be a peer retransmit under epoch - 1 in which
- * case we must retransmit last flight
- */
- sequence_numbers().read_accept(record_sequence);
-
- const u16bit epoch = record_sequence >> 48;
-
- if(epoch == sequence_numbers().current_read_epoch())
- {
- create_handshake_state(record_version);
- }
- else if(epoch == sequence_numbers().current_read_epoch() - 1)
- {
- BOTAN_ASSERT(m_active_state, "Have active state here");
- m_active_state->handshake_io().add_record(unlock(record),
- record_type,
- record_sequence);
- }
- }
- else if(record_sequence == 0)
- {
- create_handshake_state(record_version);
- }
- }
- else
- {
- create_handshake_state(record_version);
- }
- }
-
- // May have been created in above conditional
- if(m_pending_state)
- {
- 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);
- }
- }
+ process_handshake_ccs(record_data, record_sequence, record_type, record_version);
}
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.data(), record.size());
+ process_application_data(record_data);
}
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;
- }
+ process_alert(record_data);
}
else if(record_type != NO_RECORD)
throw Unexpected_Message("Unexpected record type " +
@@ -428,6 +335,108 @@ size_t Channel::received_data(const byte input[], size_t input_size)
}
}
+void Channel::process_handshake_ccs(secure_vector<byte>& record,
+ u64bit& record_sequence,
+ Record_Type& record_type,
+ Protocol_Version& record_version)
+ {
+ if(!m_pending_state)
+ {
+ // No pending handshake, possibly new:
+ if(record_version.is_datagram_protocol())
+ {
+ if(m_sequence_numbers)
+ {
+ /*
+ * Might be a peer retransmit under epoch - 1 in which
+ * case we must retransmit last flight
+ */
+ sequence_numbers().read_accept(record_sequence);
+
+ const u16bit epoch = record_sequence >> 48;
+
+ if(epoch == sequence_numbers().current_read_epoch())
+ {
+ create_handshake_state(record_version);
+ }
+ else if(epoch == sequence_numbers().current_read_epoch() - 1)
+ {
+ BOTAN_ASSERT(m_active_state, "Have active state here");
+ m_active_state->handshake_io().add_record(unlock(record),
+ record_type,
+ record_sequence);
+ }
+ }
+ else if(record_sequence == 0)
+ {
+ create_handshake_state(record_version);
+ }
+ }
+ else
+ {
+ create_handshake_state(record_version);
+ }
+ }
+
+ // May have been created in above conditional
+ if(m_pending_state)
+ {
+ 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);
+ }
+ }
+ }
+
+void Channel::process_application_data(secure_vector<byte>& record)
+ {
+ 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_callbacks.app_data()(record.data(), record.size());
+ }
+
+void Channel::process_alert(secure_vector<byte>& record)
+ {
+ Alert alert_msg(record);
+
+ if(alert_msg.type() == Alert::NO_RENEGOTIATION)
+ m_pending_state.reset();
+
+ m_callbacks.alert()(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();
+ }
+ }
+
+
void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch,
byte record_type, const byte input[], size_t length)
{
@@ -436,16 +445,16 @@ void Channel::write_record(Connection_Cipher_State* cipher_state, u16bit epoch,
Protocol_Version record_version =
(m_pending_state) ? (m_pending_state->version()) : (m_active_state->version());
+ Record_Message record_message(record_type, 0, input, length);
+
TLS::write_record(m_writebuf,
- record_type,
- input,
- length,
+ record_message,
record_version,
sequence_numbers().next_write_sequence(epoch),
cipher_state,
m_rng);
- m_output_fn(m_writebuf.data(), m_writebuf.size());
+ m_callbacks.out_fn()(m_writebuf.data(), m_writebuf.size());
}
void Channel::send_record_array(u16bit epoch, byte type, const byte input[], size_t length)
diff --git a/src/lib/tls/tls_channel.h b/src/lib/tls/tls_channel.h
index e0219c242..c9ea8edde 100644
--- a/src/lib/tls/tls_channel.h
+++ b/src/lib/tls/tls_channel.h
@@ -1,6 +1,7 @@
/*
* TLS Channel
* (C) 2011,2012,2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -32,22 +33,58 @@ class Handshake_Message;
class BOTAN_DLL Channel
{
public:
- typedef std::function<void (const byte[], size_t)> output_fn;
- typedef std::function<void (const byte[], size_t)> data_cb;
- typedef std::function<void (Alert, const byte[], size_t)> alert_cb;
- typedef std::function<bool (const Session&)> handshake_cb;
- typedef std::function<void (const Handshake_Message&)> handshake_msg_cb;
-
- Channel(output_fn out,
- data_cb app_data_cb,
- alert_cb alert_cb,
- handshake_cb hs_cb,
- handshake_msg_cb hs_msg_cb,
+ static size_t IO_BUF_DEFAULT_SIZE;
+
+ class Callbacks
+ {
+ public:
+ typedef std::function<void (const byte[], size_t)> output_fn;
+ typedef std::function<void (const byte[], size_t)> data_cb;
+ typedef std::function<void (Alert, const byte[], size_t)> alert_cb;
+ typedef std::function<bool (const Session&)> handshake_cb;
+ typedef std::function<void (const Handshake_Message&)> handshake_msg_cb;
+ /**
+ * Encapsulates a set of callback functions required by a TLS Channel.
+ * @param output_fn is called with data for the outbound socket
+ *
+ * @param app_data_cb is called when new application data is received
+ *
+ * @param alert_cb is called when a TLS alert is received
+ *
+ * @param handshake_cb is called when a handshake is completed
+ */
+ Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb,
+ handshake_cb hs_cb)
+ : m_output_function(out), m_app_data_cb(app_data_cb),
+ m_alert_cb(alert_cb), m_hs_cb(hs_cb), m_hs_msg_cb() {}
+
+ Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb,
+ handshake_cb hs_cb, handshake_msg_cb hs_msg_cb)
+ : m_output_function(out), m_app_data_cb(app_data_cb),
+ m_alert_cb(alert_cb), m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb) {}
+
+ const output_fn& out_fn() const { return m_output_function; }
+ const data_cb& app_data() const { return m_app_data_cb; }
+ const alert_cb& alert() const { return m_alert_cb; }
+ const handshake_cb& handshake() const { return m_hs_cb; }
+ const handshake_msg_cb& handshake_msg() const { return m_hs_msg_cb; }
+
+ private:
+ const output_fn m_output_function;
+ const data_cb m_app_data_cb;
+ const alert_cb m_alert_cb;
+ const handshake_cb m_hs_cb;
+ const handshake_msg_cb m_hs_msg_cb;
+ };
+
+
+
+ Channel(const Callbacks& callbacks,
Session_Manager& session_manager,
RandomNumberGenerator& rng,
const Policy& policy,
bool is_datagram,
- size_t io_buf_sz = 16*1024);
+ size_t io_buf_sz = IO_BUF_DEFAULT_SIZE);
Channel(const Channel&) = delete;
@@ -200,9 +237,9 @@ class BOTAN_DLL Channel
const Policy& policy() const { return m_policy; }
- bool save_session(const Session& session) const { return m_handshake_cb(session); }
+ bool save_session(const Session& session) const { return m_callbacks.handshake()(session); }
- handshake_msg_cb get_handshake_msg_cb() const { return m_handshake_msg_cb; }
+ Callbacks get_callbacks() const { return m_callbacks; }
private:
void send_record(byte record_type, const std::vector<byte>& record);
@@ -227,14 +264,20 @@ class BOTAN_DLL Channel
const Handshake_State* pending_state() const { return m_pending_state.get(); }
+ /* methods to handle incoming traffic through Channel::receive_data. */
+ void process_handshake_ccs(secure_vector<byte>& record,
+ u64bit& record_sequence,
+ Record_Type& record_type,
+ Protocol_Version& record_version);
+
+ void process_application_data(secure_vector<byte>& record);
+
+ void process_alert(secure_vector<byte>& record);
+
bool m_is_datagram;
/* callbacks */
- data_cb m_data_cb;
- alert_cb m_alert_cb;
- output_fn m_output_fn;
- handshake_cb m_handshake_cb;
- handshake_msg_cb m_handshake_msg_cb;
+ Callbacks m_callbacks;
/* external state */
Session_Manager& m_session_manager;
diff --git a/src/lib/tls/tls_ciphersuite.h b/src/lib/tls/tls_ciphersuite.h
index 1f646cc7e..71596897c 100644
--- a/src/lib/tls/tls_ciphersuite.h
+++ b/src/lib/tls/tls_ciphersuite.h
@@ -1,6 +1,7 @@
/*
* TLS Cipher Suites
* (C) 2004-2011,2012 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -116,31 +117,17 @@ class BOTAN_DLL Ciphersuite
private:
-
- Ciphersuite(u16bit ciphersuite_code,
- const char* iana_id,
- const char* sig_algo,
- const char* kex_algo,
- const char* cipher_algo,
- size_t cipher_keylen,
- size_t nonce_bytes_from_handshake,
- size_t nonce_bytes_from_record,
- const char* mac_algo,
- size_t mac_keylen,
- const char* prf_algo) :
- m_ciphersuite_code(ciphersuite_code),
- m_iana_id(iana_id),
- m_sig_algo(sig_algo),
- m_kex_algo(kex_algo),
- m_prf_algo(prf_algo),
- m_cipher_algo(cipher_algo),
- m_mac_algo(mac_algo),
- m_cipher_keylen(cipher_keylen),
- m_nonce_bytes_from_handshake(nonce_bytes_from_handshake),
- m_nonce_bytes_from_record(nonce_bytes_from_record),
- m_mac_keylen(mac_keylen)
- {
- }
+ Ciphersuite(u16bit ciphersuite_code,
+ const char* iana_id,
+ const char* sig_algo,
+ const char* kex_algo,
+ const char* cipher_algo,
+ size_t cipher_keylen,
+ size_t nonce_bytes_from_handshake,
+ size_t nonce_bytes_from_record,
+ const char* mac_algo,
+ size_t mac_keylen,
+ const char* prf_algo = "");
u16bit m_ciphersuite_code = 0;
diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp
index 301c77c6b..ff4b20bbf 100644
--- a/src/lib/tls/tls_client.cpp
+++ b/src/lib/tls/tls_client.cpp
@@ -1,6 +1,7 @@
/*
* TLS Client
* (C) 2004-2011,2012,2015,2016 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -42,55 +43,28 @@ class Client_Handshake_State : public Handshake_State
/*
* TLS Client Constructor
*/
-Client::Client(output_fn output_fn,
- data_cb proc_cb,
- alert_cb alert_cb,
- handshake_cb handshake_cb,
+Client::Client(const Callbacks& callbacks,
Session_Manager& session_manager,
Credentials_Manager& creds,
const Policy& policy,
RandomNumberGenerator& rng,
- const Server_Information& info,
- const Protocol_Version& offer_version,
- const std::vector<std::string>& next_protos,
+ Properties properties,
size_t io_buf_sz) :
- Channel(output_fn, proc_cb, alert_cb, handshake_cb, Channel::handshake_msg_cb(),
- session_manager, rng, policy, offer_version.is_datagram_protocol(), io_buf_sz),
+ Channel(callbacks, session_manager, rng, policy, properties.get_protocol_version().is_datagram_protocol(),
+ io_buf_sz),
m_creds(creds),
- m_info(info)
+ m_info(properties.get_server_info())
{
const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname());
- Handshake_State& state = create_handshake_state(offer_version);
- send_client_hello(state, false, offer_version, srp_identifier, next_protos);
- }
-
-Client::Client(output_fn output_fn,
- data_cb proc_cb,
- alert_cb alert_cb,
- handshake_cb handshake_cb,
- handshake_msg_cb hs_msg_cb,
- Session_Manager& session_manager,
- Credentials_Manager& creds,
- const Policy& policy,
- RandomNumberGenerator& rng,
- const Server_Information& info,
- const Protocol_Version& offer_version,
- const std::vector<std::string>& next_protos) :
- Channel(output_fn, proc_cb, alert_cb, handshake_cb, hs_msg_cb,
- session_manager, rng, policy, offer_version.is_datagram_protocol()),
- m_creds(creds),
- m_info(info)
- {
- const std::string srp_identifier = m_creds.srp_identifier("tls-client", m_info.hostname());
-
- Handshake_State& state = create_handshake_state(offer_version);
- send_client_hello(state, false, offer_version, srp_identifier, next_protos);
+ Handshake_State& state = create_handshake_state(properties.get_protocol_version());
+ send_client_hello(state, false, properties.get_protocol_version(),
+ srp_identifier, properties.get_next_protocol_versions());
}
Handshake_State* Client::new_handshake_state(Handshake_IO* io)
{
- return new Client_Handshake_State(io, get_handshake_msg_cb());
+ return new Client_Handshake_State(io, get_callbacks().handshake_msg());
}
std::vector<X509_Certificate>
@@ -129,9 +103,9 @@ void Client::send_client_hello(Handshake_State& state_base,
{
if(srp_identifier == "" || session_info.srp_identifier() == srp_identifier)
{
+ Client_Hello::Handshake_Info hs_info(state.handshake_io(), state.hash());
state.client_hello(new Client_Hello(
- state.handshake_io(),
- state.hash(),
+ hs_info,
policy(),
rng(),
secure_renegotiation_data_for_client_hello(),
@@ -145,16 +119,16 @@ void Client::send_client_hello(Handshake_State& state_base,
if(!state.client_hello()) // not resuming
{
+ Client_Hello::Handshake_Info hs_info(state.handshake_io(), state.hash());
+
+ Client_Hello::Settings client_settings(version, m_info.hostname(), srp_identifier);
state.client_hello(new Client_Hello(
- state.handshake_io(),
- state.hash(),
- version,
+ hs_info,
policy(),
rng(),
secure_renegotiation_data_for_client_hello(),
- next_protocols,
- m_info.hostname(),
- srp_identifier));
+ client_settings,
+ next_protocols));
}
secure_renegotiation_check(state.client_hello());
@@ -419,11 +393,10 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
"tls-client",
m_info.hostname());
- state.client_certs(
- new Certificate(state.handshake_io(),
- state.hash(),
- client_certs)
- );
+ Certificate::Handshake_Info hs_info(state.handshake_io(),
+ state.hash());
+
+ state.client_certs(new Certificate(hs_info, client_certs));
}
state.client_kex(
@@ -502,20 +475,22 @@ void Client::process_handshake_msg(const Handshake_State* active_state,
if(session_id.empty() && !session_ticket.empty())
session_id = make_hello_random(rng(), policy());
+ Session::Properties session_properties(
+ m_info,
+ "",
+ state.server_hello()->srtp_profile(),
+ state.server_hello()->version(),
+ state.server_hello()->ciphersuite(),
+ state.server_hello()->compression_method());
+
Session session_info(
session_id,
state.session_keys().master_secret(),
- state.server_hello()->version(),
- state.server_hello()->ciphersuite(),
- state.server_hello()->compression_method(),
CLIENT,
state.server_hello()->supports_extended_master_secret(),
get_peer_cert_chain(state),
session_ticket,
- m_info,
- "",
- state.server_hello()->srtp_profile()
- );
+ session_properties);
const bool should_save = save_session(session_info);
diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h
index 45a741878..e80739010 100644
--- a/src/lib/tls/tls_client.h
+++ b/src/lib/tls/tls_client.h
@@ -1,6 +1,7 @@
/*
* TLS Client
* (C) 2004-2011 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -25,13 +26,8 @@ class BOTAN_DLL Client final : public Channel
/**
* Set up a new TLS client session
*
- * @param output_fn is called with data for the outbound socket
- *
- * @param app_data_cb is called when new application data is received
- *
- * @param alert_cb is called when a TLS alert is received
- *
- * @param handshake_cb is called when a handshake is completed
+ * @param callbacks contains a set of callback function references
+ * required by the TLS client.
*
* @param session_manager manages session state
*
@@ -41,44 +37,65 @@ class BOTAN_DLL Client final : public Channel
*
* @param rng a random number generator
*
- * @param server_info is identifying information about the TLS server
- *
- * @param offer_version specifies which version we will offer
- * to the TLS server.
- *
- * @param next_protocols specifies protocols to advertise with ALPN
+ * @param properties holds server information and protocol related
+ * properties.
*
* @param reserved_io_buffer_size This many bytes of memory will
* be preallocated for the read and write buffers. Smaller
* values just mean reallocations and copies are more likely.
*/
- Client(output_fn out,
- data_cb app_data_cb,
- alert_cb alert_cb,
- handshake_cb hs_cb,
- Session_Manager& session_manager,
- Credentials_Manager& creds,
- const Policy& policy,
- RandomNumberGenerator& rng,
- const Server_Information& server_info = Server_Information(),
- const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
- const std::vector<std::string>& next_protocols = {},
- size_t reserved_io_buffer_size = 16*1024
- );
-
- Client(output_fn out,
- data_cb app_data_cb,
- alert_cb alert_cb,
- handshake_cb hs_cb,
- handshake_msg_cb hs_msg_cb,
- Session_Manager& session_manager,
- Credentials_Manager& creds,
- const Policy& policy,
- RandomNumberGenerator& rng,
- const Server_Information& server_info = Server_Information(),
- const Protocol_Version& offer_version = Protocol_Version::latest_tls_version(),
- const std::vector<std::string>& next_protocols = {}
+ class Properties
+ {
+ /**
+ * Stores TLS Client properties.
+ *
+ * @param server_info is identifying information about the TLS server
+ *
+ * @param protocol_version specifies which version we will offer
+ * to the TLS server.
+ *
+ * @param next_protocols specifies protocols to advertise with ALPN
+ */
+
+ public:
+ Properties(const Server_Information& server_info
+ = Server_Information(),
+ const Protocol_Version protocol_version
+ = Protocol_Version::latest_tls_version(),
+ const std::vector<std::string>& next_versions
+ = {})
+ : m_server_info(server_info),
+ m_protocol_version(protocol_version),
+ m_next_protocol_versions(next_versions) {}
+
+ const Server_Information& get_server_info()
+ {
+ return m_server_info;
+ }
+
+ const Protocol_Version& get_protocol_version()
+ {
+ return m_protocol_version;
+ }
+
+ const std::vector<std::string>& get_next_protocol_versions()
+ {
+ return m_next_protocol_versions;
+ }
+
+ private:
+ const Server_Information& m_server_info;
+ const Protocol_Version m_protocol_version;
+ const std::vector<std::string>& m_next_protocol_versions;
+ };
+ Client(const Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ Properties properties,
+ size_t reserved_io_buffer_size = 16*1024
);
const std::string& application_protocol() const { return m_application_protocol; }
diff --git a/src/lib/tls/tls_extensions.h b/src/lib/tls/tls_extensions.h
index a5aac0020..cfde0067c 100644
--- a/src/lib/tls/tls_extensions.h
+++ b/src/lib/tls/tls_extensions.h
@@ -1,6 +1,7 @@
/*
* TLS Extensions
* (C) 2011,2012,2016 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -34,7 +35,6 @@ enum Handshake_Extension_Type {
TLSEXT_SRP_IDENTIFIER = 12,
TLSEXT_SIGNATURE_ALGORITHMS = 13,
TLSEXT_USE_SRTP = 14,
- TLSEXT_HEARTBEAT_SUPPORT = 15,
TLSEXT_ALPN = 16,
TLSEXT_EXTENDED_MASTER_SECRET = 23,
diff --git a/src/lib/tls/tls_handshake_msg.h b/src/lib/tls/tls_handshake_msg.h
index 7e527abf4..c53f73a3c 100644
--- a/src/lib/tls/tls_handshake_msg.h
+++ b/src/lib/tls/tls_handshake_msg.h
@@ -1,6 +1,7 @@
/*
* TLS Handshake Message
* (C) 2012 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -22,6 +23,19 @@ namespace TLS {
class BOTAN_DLL Handshake_Message
{
public:
+ class Handshake_Info
+ {
+ public:
+ Handshake_Info(Handshake_IO& io, Handshake_Hash& hash)
+ : m_io(io), m_hash(hash) {};
+
+ Handshake_IO& get_io() { return m_io; };
+ Handshake_Hash& get_hash() {return m_hash; };
+
+ private:
+ Handshake_IO& m_io;
+ Handshake_Hash& m_hash;
+ };
std::string type_string() const;
virtual Handshake_Type type() const = 0;
diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h
index 3bee89e13..c6f8f9944 100644
--- a/src/lib/tls/tls_messages.h
+++ b/src/lib/tls/tls_messages.h
@@ -1,6 +1,7 @@
/*
* TLS Messages
* (C) 2004-2011,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -61,6 +62,26 @@ class Hello_Verify_Request final : public Handshake_Message
class Client_Hello final : public Handshake_Message
{
public:
+ class Settings
+ {
+ public:
+ Settings(const Protocol_Version version,
+ const std::string& hostname = "",
+ const std::string& srp_identifier = "")
+ : m_new_session_version(version),
+ m_hostname(hostname),
+ m_srp_identifier(srp_identifier) {};
+
+ const Protocol_Version protocol_version() const { return m_new_session_version; };
+ const std::string& hostname() const { return m_hostname; };
+ const std::string& srp_identifier() const { return m_srp_identifier; }
+
+ private:
+ const Protocol_Version m_new_session_version;
+ const std::string m_hostname;
+ const std::string m_srp_identifier;
+ };
+
Handshake_Type type() const override { return CLIENT_HELLO; }
Protocol_Version version() const { return m_version; }
@@ -160,18 +181,14 @@ class Client_Hello final : public Handshake_Message
std::set<Handshake_Extension_Type> extension_types() const
{ return m_extensions.extension_types(); }
- Client_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
- Protocol_Version version,
+ Client_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& reneg_info,
- const std::vector<std::string>& next_protocols,
- const std::string& hostname = "",
- const std::string& srp_identifier = "");
+ const Client_Hello::Settings& client_settings,
+ const std::vector<std::string>& next_protocols);
- Client_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
+ Client_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& reneg_info,
@@ -199,6 +216,35 @@ class Client_Hello final : public Handshake_Message
class Server_Hello final : public Handshake_Message
{
public:
+ class Settings
+ {
+ public:
+ Settings(const std::vector<byte> new_session_id,
+ Protocol_Version new_session_version,
+ u16bit ciphersuite,
+ byte compression,
+ bool offer_session_ticket)
+ : m_new_session_id(new_session_id),
+ m_new_session_version(new_session_version),
+ m_ciphersuite(ciphersuite),
+ m_compression(compression),
+ m_offer_session_ticket(offer_session_ticket) {};
+
+ const std::vector<byte>& session_id() const { return m_new_session_id; };
+ Protocol_Version protocol_version() const { return m_new_session_version; };
+ u16bit ciphersuite() const { return m_ciphersuite; };
+ byte compression() const { return m_compression; }
+ bool offer_session_ticket() const { return m_offer_session_ticket; }
+
+ private:
+ const std::vector<byte> m_new_session_id;
+ Protocol_Version m_new_session_version;
+ u16bit m_ciphersuite;
+ byte m_compression;
+ bool m_offer_session_ticket;
+ };
+
+
Handshake_Type type() const override { return SERVER_HELLO; }
Protocol_Version version() const { return m_version; }
@@ -256,21 +302,15 @@ class Server_Hello final : public Handshake_Message
std::set<Handshake_Extension_Type> extension_types() const
{ return m_extensions.extension_types(); }
- Server_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
+ Server_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& secure_reneg_info,
const Client_Hello& client_hello,
- const std::vector<byte>& new_session_id,
- Protocol_Version new_session_version,
- u16bit ciphersuite,
- byte compression,
- bool offer_session_ticket,
- const std::string& next_protocol);
+ const Server_Hello::Settings& settings,
+ const std::string next_protocol);
- Server_Hello(Handshake_IO& io,
- Handshake_Hash& hash,
+ Server_Hello(Handshake_Info& hs_info,
const Policy& policy,
RandomNumberGenerator& rng,
const std::vector<byte>& secure_reneg_info,
@@ -301,7 +341,6 @@ class Client_Key_Exchange final : public Handshake_Message
const secure_vector<byte>& pre_master_secret() const
{ return m_pre_master; }
-
Client_Key_Exchange(Handshake_IO& io,
Handshake_State& state,
const Policy& policy,
@@ -337,8 +376,7 @@ class Certificate final : public Handshake_Message
size_t count() const { return m_certs.size(); }
bool empty() const { return m_certs.empty(); }
- Certificate(Handshake_IO& io,
- Handshake_Hash& hash,
+ Certificate(Handshake_Info& hs_info,
const std::vector<X509_Certificate>& certs);
explicit Certificate(const std::vector<byte>& buf);
@@ -364,8 +402,7 @@ class Certificate_Req final : public Handshake_Message
std::vector<std::pair<std::string, std::string> > supported_algos() const
{ return m_supported_algos; }
- Certificate_Req(Handshake_IO& io,
- Handshake_Hash& hash,
+ Certificate_Req(Handshake_Info& hs_info,
const Policy& policy,
const std::vector<X509_DN>& allowed_cas,
Protocol_Version version);
diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp
index eacf313a8..5fda1fbb4 100644
--- a/src/lib/tls/tls_record.cpp
+++ b/src/lib/tls/tls_record.cpp
@@ -1,6 +1,7 @@
/*
* TLS Record Handling
* (C) 2012,2013,2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -152,7 +153,7 @@ Connection_Cipher_State::format_ad(u64bit msg_sequence,
}
void write_record(secure_vector<byte>& output,
- byte msg_type, const byte msg[], size_t msg_length,
+ Record_Message msg,
Protocol_Version version,
u64bit seq,
Connection_Cipher_State* cs,
@@ -160,7 +161,7 @@ void write_record(secure_vector<byte>& output,
{
output.clear();
- output.push_back(msg_type);
+ output.push_back(msg.get_type());
output.push_back(version.major_version());
output.push_back(version.minor_version());
@@ -172,17 +173,17 @@ void write_record(secure_vector<byte>& output,
if(!cs) // initial unencrypted handshake records
{
- output.push_back(get_byte(0, static_cast<u16bit>(msg_length)));
- output.push_back(get_byte(1, static_cast<u16bit>(msg_length)));
+ output.push_back(get_byte<u16bit>(0, static_cast<u16bit>(msg.get_size())));
+ output.push_back(get_byte<u16bit>(1, static_cast<u16bit>(msg.get_size())));
- output.insert(output.end(), msg, msg + msg_length);
+ output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size());
return;
}
if(AEAD_Mode* aead = cs->aead())
{
- const size_t ctext_size = aead->output_length(msg_length);
+ const size_t ctext_size = aead->output_length(msg.get_size());
const std::vector<byte> nonce = cs->aead_nonce(seq);
@@ -193,17 +194,16 @@ void write_record(secure_vector<byte>& output,
output.push_back(get_byte(0, static_cast<u16bit>(rec_size)));
output.push_back(get_byte(1, static_cast<u16bit>(rec_size)));
- aead->set_ad(cs->format_ad(seq, msg_type, version, static_cast<u16bit>(msg_length)));
+ aead->set_ad(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size())));
if(cs->nonce_bytes_from_record() > 0)
{
output += std::make_pair(&nonce[cs->nonce_bytes_from_handshake()], cs->nonce_bytes_from_record());
}
-
BOTAN_ASSERT(aead->start(nonce).empty(), "AEAD doesn't return anything from start");
const size_t offset = output.size();
- output += std::make_pair(msg, msg_length);
+ output += std::make_pair(msg.get_data(), msg.get_size());
aead->finish(output, offset);
BOTAN_ASSERT(output.size() == offset + ctext_size, "Expected size");
@@ -213,16 +213,16 @@ void write_record(secure_vector<byte>& output,
return;
}
- cs->mac()->update(cs->format_ad(seq, msg_type, version, static_cast<u16bit>(msg_length)));
+ cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size())));
- cs->mac()->update(msg, msg_length);
+ cs->mac()->update(msg.get_data(), msg.get_size());
const size_t block_size = cs->block_size();
const size_t iv_size = cs->iv_size();
const size_t mac_size = cs->mac_size();
const size_t buf_size = round_up(
- iv_size + msg_length + mac_size + (block_size ? 1 : 0),
+ iv_size + msg.get_size() + mac_size + (block_size ? 1 : 0),
block_size);
if(buf_size > MAX_CIPHERTEXT_SIZE)
@@ -239,7 +239,7 @@ void write_record(secure_vector<byte>& output,
rng.randomize(&output[output.size() - iv_size], iv_size);
}
- output.insert(output.end(), msg, msg + msg_length);
+ output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size());
output.resize(output.size() + mac_size);
cs->mac()->final(&output[output.size() - mac_size]);
@@ -247,7 +247,7 @@ void write_record(secure_vector<byte>& output,
if(block_size)
{
const size_t pad_val =
- buf_size - (iv_size + msg_length + mac_size + 1);
+ buf_size - (iv_size + msg.get_size() + mac_size + 1);
for(size_t i = 0; i != pad_val + 1; ++i)
output.push_back(static_cast<byte>(pad_val));
@@ -461,65 +461,58 @@ void decrypt_record(secure_vector<byte>& output,
}
size_t read_tls_record(secure_vector<byte>& readbuf,
- const byte input[],
- size_t input_sz,
- size_t& consumed,
- secure_vector<byte>& record,
- u64bit* record_sequence,
- Protocol_Version* record_version,
- Record_Type* record_type,
+ Record_Raw_Input& raw_input,
+ Record& rec,
Connection_Sequence_Numbers* sequence_numbers,
get_cipherstate_fn get_cipherstate)
{
- consumed = 0;
-
if(readbuf.size() < TLS_HEADER_SIZE) // header incomplete?
{
if(size_t needed = fill_buffer_to(readbuf,
- input, input_sz, consumed,
+ raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(),
TLS_HEADER_SIZE))
return needed;
BOTAN_ASSERT_EQUAL(readbuf.size(), TLS_HEADER_SIZE, "Have an entire header");
}
- *record_version = Protocol_Version(readbuf[1], readbuf[2]);
+ *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]);
- BOTAN_ASSERT(!record_version->is_datagram_protocol(), "Expected TLS");
+ BOTAN_ASSERT(!rec.get_protocol_version()->is_datagram_protocol(), "Expected TLS");
- const size_t record_len = make_u16bit(readbuf[TLS_HEADER_SIZE-2],
+ const size_t record_size = make_u16bit(readbuf[TLS_HEADER_SIZE-2],
readbuf[TLS_HEADER_SIZE-1]);
- if(record_len > MAX_CIPHERTEXT_SIZE)
+ if(record_size > MAX_CIPHERTEXT_SIZE)
throw TLS_Exception(Alert::RECORD_OVERFLOW,
"Received a record that exceeds maximum size");
- if(record_len == 0)
+ if(record_size == 0)
throw TLS_Exception(Alert::DECODE_ERROR,
"Received a completely empty record");
if(size_t needed = fill_buffer_to(readbuf,
- input, input_sz, consumed,
- TLS_HEADER_SIZE + record_len))
+ raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(),
+ TLS_HEADER_SIZE + record_size))
return needed;
- BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_len,
+ BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_size,
readbuf.size(),
"Have the full record");
- *record_type = static_cast<Record_Type>(readbuf[0]);
+ *rec.get_type() = static_cast<Record_Type>(readbuf[0]);
u16bit epoch = 0;
if(sequence_numbers)
{
- *record_sequence = sequence_numbers->next_read_sequence();
+ *rec.get_sequence() = sequence_numbers->next_read_sequence();
epoch = sequence_numbers->current_read_epoch();
}
else
{
// server initial handshake case
- *record_sequence = 0;
+ *rec.get_sequence() = 0;
epoch = 0;
}
@@ -527,7 +520,7 @@ size_t read_tls_record(secure_vector<byte>& readbuf,
if(epoch == 0) // Unencrypted initial handshake
{
- record.assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_len);
+ rec.get_data().assign(readbuf.begin() + TLS_HEADER_SIZE, readbuf.begin() + TLS_HEADER_SIZE + record_size);
readbuf.clear();
return 0; // got a full record
}
@@ -537,37 +530,30 @@ size_t read_tls_record(secure_vector<byte>& readbuf,
BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
- decrypt_record(record,
+ decrypt_record(rec.get_data(),
record_contents,
- record_len,
- *record_sequence,
- *record_version,
- *record_type,
+ record_size,
+ *rec.get_sequence(),
+ *rec.get_protocol_version(),
+ *rec.get_type(),
*cs);
if(sequence_numbers)
- sequence_numbers->read_accept(*record_sequence);
+ sequence_numbers->read_accept(*rec.get_sequence());
readbuf.clear();
return 0;
}
size_t read_dtls_record(secure_vector<byte>& readbuf,
- const byte input[],
- size_t input_sz,
- size_t& consumed,
- secure_vector<byte>& record,
- u64bit* record_sequence,
- Protocol_Version* record_version,
- Record_Type* record_type,
+ Record_Raw_Input& raw_input,
+ Record& rec,
Connection_Sequence_Numbers* sequence_numbers,
get_cipherstate_fn get_cipherstate)
{
- consumed = 0;
-
if(readbuf.size() < DTLS_HEADER_SIZE) // header incomplete?
{
- if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE))
+ if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE))
{
readbuf.clear();
return 0;
@@ -576,38 +562,35 @@ size_t read_dtls_record(secure_vector<byte>& readbuf,
BOTAN_ASSERT_EQUAL(readbuf.size(), DTLS_HEADER_SIZE, "Have an entire header");
}
- *record_version = Protocol_Version(readbuf[1], readbuf[2]);
+ *rec.get_protocol_version() = Protocol_Version(readbuf[1], readbuf[2]);
- BOTAN_ASSERT(record_version->is_datagram_protocol(), "Expected DTLS");
+ BOTAN_ASSERT(rec.get_protocol_version()->is_datagram_protocol(), "Expected DTLS");
- const size_t record_len = make_u16bit(readbuf[DTLS_HEADER_SIZE-2],
+ const size_t record_size = make_u16bit(readbuf[DTLS_HEADER_SIZE-2],
readbuf[DTLS_HEADER_SIZE-1]);
- // Invalid packet:
- if(record_len == 0 || record_len > MAX_CIPHERTEXT_SIZE)
- {
- readbuf.clear();
- return 0;
- }
+ if(record_size > MAX_CIPHERTEXT_SIZE)
+ throw TLS_Exception(Alert::RECORD_OVERFLOW,
+ "Got message that exceeds maximum size");
- if(fill_buffer_to(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE + record_len))
+ if(fill_buffer_to(readbuf, raw_input.get_data(), raw_input.get_size(), raw_input.get_consumed(), DTLS_HEADER_SIZE + record_size))
{
// Truncated packet?
readbuf.clear();
return 0;
}
- BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_len, readbuf.size(),
+ BOTAN_ASSERT_EQUAL(static_cast<size_t>(DTLS_HEADER_SIZE) + record_size, readbuf.size(),
"Have the full record");
- *record_type = static_cast<Record_Type>(readbuf[0]);
+ *rec.get_type() = static_cast<Record_Type>(readbuf[0]);
u16bit epoch = 0;
- *record_sequence = load_be<u64bit>(&readbuf[3], 0);
- epoch = (*record_sequence >> 48);
+ *rec.get_sequence() = load_be<u64bit>(&readbuf[3], 0);
+ epoch = (*rec.get_sequence() >> 48);
- if(sequence_numbers && sequence_numbers->already_seen(*record_sequence))
+ if(sequence_numbers && sequence_numbers->already_seen(*rec.get_sequence()))
{
readbuf.clear();
return 0;
@@ -617,7 +600,7 @@ size_t read_dtls_record(secure_vector<byte>& readbuf,
if(epoch == 0) // Unencrypted initial handshake
{
- record.assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_len);
+ rec.get_data().assign(readbuf.begin() + DTLS_HEADER_SIZE, readbuf.begin() + DTLS_HEADER_SIZE + record_size);
readbuf.clear();
return 0; // got a full record
}
@@ -629,23 +612,23 @@ size_t read_dtls_record(secure_vector<byte>& readbuf,
BOTAN_ASSERT(cs, "Have cipherstate for this epoch");
- decrypt_record(record,
+ decrypt_record(rec.get_data(),
record_contents,
- record_len,
- *record_sequence,
- *record_version,
- *record_type,
+ record_size,
+ *rec.get_sequence(),
+ *rec.get_protocol_version(),
+ *rec.get_type(),
*cs);
}
catch(std::exception)
{
readbuf.clear();
- *record_type = NO_RECORD;
+ *rec.get_type() = NO_RECORD;
return 0;
}
if(sequence_numbers)
- sequence_numbers->read_accept(*record_sequence);
+ sequence_numbers->read_accept(*rec.get_sequence());
readbuf.clear();
return 0;
@@ -654,24 +637,16 @@ size_t read_dtls_record(secure_vector<byte>& readbuf,
}
size_t read_record(secure_vector<byte>& readbuf,
- const byte input[],
- size_t input_sz,
- bool is_datagram,
- size_t& consumed,
- secure_vector<byte>& record,
- u64bit* record_sequence,
- Protocol_Version* record_version,
- Record_Type* record_type,
+ Record_Raw_Input& raw_input,
+ Record& rec,
Connection_Sequence_Numbers* sequence_numbers,
get_cipherstate_fn get_cipherstate)
{
- if(is_datagram)
- return read_dtls_record(readbuf, input, input_sz, consumed,
- record, record_sequence, record_version, record_type,
+ if(raw_input.is_datagram())
+ return read_dtls_record(readbuf, raw_input, rec,
sequence_numbers, get_cipherstate);
else
- return read_tls_record(readbuf, input, input_sz, consumed,
- record, record_sequence, record_version, record_type,
+ return read_tls_record(readbuf, raw_input, rec,
sequence_numbers, get_cipherstate);
}
diff --git a/src/lib/tls/tls_record.h b/src/lib/tls/tls_record.h
index e3b0b9b58..53e447af6 100644
--- a/src/lib/tls/tls_record.h
+++ b/src/lib/tls/tls_record.h
@@ -1,6 +1,7 @@
/*
* TLS Record Handling
* (C) 2004-2012 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -90,6 +91,84 @@ class Connection_Cipher_State
size_t m_iv_size = 0;
};
+class Record
+ {
+ public:
+ Record(secure_vector<byte>& data,
+ u64bit* sequence,
+ Protocol_Version* protocol_version,
+ Record_Type* type)
+ : m_data(data), m_sequence(sequence), m_protocol_version(protocol_version),
+ m_type(type), m_size(data.size()) {};
+
+ secure_vector<byte>& get_data() { return m_data; }
+ void set_data(secure_vector<byte> data)
+ {
+ m_data.swap(data);
+ }
+
+ Protocol_Version* get_protocol_version() { return m_protocol_version; }
+
+ u64bit* get_sequence() { return m_sequence; }
+
+ Record_Type* get_type() { return m_type; }
+
+ size_t& get_size() { return m_size; }
+
+ private:
+ secure_vector<byte>& m_data;
+ u64bit* m_sequence;
+ Protocol_Version* m_protocol_version;
+ Record_Type* m_type;
+ size_t m_size;
+ };
+
+class Record_Message
+ {
+ public:
+ Record_Message(const byte* data, size_t size)
+ : m_type(0), m_sequence(0), m_data(data), m_size(size) {};
+ Record_Message(byte type, u64bit sequence, const byte* data, size_t size)
+ : m_type(type), m_sequence(sequence), m_data(data),
+ m_size(size) {};
+
+ byte& get_type() { return m_type; };
+ u64bit& get_sequence() { return m_sequence; };
+ const byte* get_data() { return m_data; };
+ size_t& get_size() { return m_size; };
+
+ private:
+ byte m_type;
+ u64bit m_sequence;
+ const byte* m_data;
+ size_t m_size;
+};
+
+class Record_Raw_Input
+ {
+ public:
+ Record_Raw_Input(const byte* data, size_t size, size_t& consumed,
+ bool is_datagram)
+ : m_data(data), m_size(size), m_consumed(consumed),
+ m_is_datagram(is_datagram) {};
+
+ const byte*& get_data() { return m_data; };
+
+ size_t& get_size() { return m_size; };
+
+ size_t& get_consumed() { return m_consumed; };
+ void set_consumed(size_t consumed) { m_consumed = consumed; }
+
+ bool is_datagram() { return m_is_datagram; };
+
+ private:
+ const byte* m_data;
+ size_t m_size;
+ size_t& m_consumed;
+ bool m_is_datagram;
+ };
+
+
/**
* Create a TLS record
* @param write_buffer the output record is placed here
@@ -103,7 +182,7 @@ class Connection_Cipher_State
* @return number of bytes written to write_buffer
*/
void write_record(secure_vector<byte>& write_buffer,
- byte msg_type, const byte msg[], size_t msg_length,
+ Record_Message rec_msg,
Protocol_Version version,
u64bit msg_sequence,
Connection_Cipher_State* cipherstate,
@@ -117,14 +196,8 @@ typedef std::function<std::shared_ptr<Connection_Cipher_State> (u16bit)> get_cip
* @return zero if full message, else number of bytes still needed
*/
size_t read_record(secure_vector<byte>& read_buffer,
- const byte input[],
- size_t input_length,
- bool is_datagram,
- size_t& input_consumed,
- secure_vector<byte>& record,
- u64bit* record_sequence,
- Protocol_Version* record_version,
- Record_Type* record_type,
+ Record_Raw_Input& raw_input,
+ Record& rec,
Connection_Sequence_Numbers* sequence_numbers,
get_cipherstate_fn get_cipherstate);
diff --git a/src/lib/tls/tls_server.cpp b/src/lib/tls/tls_server.cpp
index 41b14ae08..de1e8668d 100644
--- a/src/lib/tls/tls_server.cpp
+++ b/src/lib/tls/tls_server.cpp
@@ -1,6 +1,7 @@
/*
* TLS Server
* (C) 2004-2011,2012,2016 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -9,6 +10,7 @@
#include <botan/internal/tls_handshake_state.h>
#include <botan/internal/tls_messages.h>
#include <botan/internal/stl_util.h>
+#include "tls_magic.h"
namespace Botan {
@@ -16,22 +18,7 @@ namespace TLS {
namespace {
-class Server_Handshake_State : public Handshake_State
- {
- public:
- // using Handshake_State::Handshake_State;
-
- Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State(io, cb) {}
- // Used by the server only, in case of RSA key exchange. Not owned
- Private_Key* server_rsa_kex_key = nullptr;
-
- /*
- * Used by the server to know if resumption should be allowed on
- * a server-initiated renegotiation
- */
- bool allow_session_resumption = true;
- };
bool check_for_resume(Session& session_info,
Session_Manager& session_manager,
@@ -225,10 +212,7 @@ get_server_certs(const std::string& hostname,
/*
* TLS Server Constructor
*/
-Server::Server(output_fn output,
- data_cb data_cb,
- alert_cb alert_cb,
- handshake_cb handshake_cb,
+Server::Server(const Callbacks& callbacks,
Session_Manager& session_manager,
Credentials_Manager& creds,
const Policy& policy,
@@ -236,26 +220,8 @@ Server::Server(output_fn output,
next_protocol_fn next_proto,
bool is_datagram,
size_t io_buf_sz) :
- Channel(output, data_cb, alert_cb, handshake_cb, Channel::handshake_msg_cb(),
- session_manager, rng, policy, is_datagram, io_buf_sz),
- m_creds(creds),
- m_choose_next_protocol(next_proto)
- {
- }
-
-Server::Server(output_fn output,
- data_cb data_cb,
- alert_cb alert_cb,
- handshake_cb handshake_cb,
- handshake_msg_cb hs_msg_cb,
- Session_Manager& session_manager,
- Credentials_Manager& creds,
- const Policy& policy,
- RandomNumberGenerator& rng,
- next_protocol_fn next_proto,
- bool is_datagram) :
- Channel(output, data_cb, alert_cb, handshake_cb, hs_msg_cb,
- session_manager, rng, policy, is_datagram),
+ Channel(callbacks, session_manager, rng, policy,
+ is_datagram, io_buf_sz),
m_creds(creds),
m_choose_next_protocol(next_proto)
{
@@ -264,7 +230,7 @@ Server::Server(output_fn output,
Handshake_State* Server::new_handshake_state(Handshake_IO* io)
{
std::unique_ptr<Handshake_State> state(
- new Server_Handshake_State(io, get_handshake_msg_cb()));
+ new Server_Handshake_State(io, get_callbacks().handshake_msg()));
state->set_expected_next(CLIENT_HELLO);
return state.release();
@@ -284,441 +250,513 @@ Server::get_peer_cert_chain(const Handshake_State& state) const
void Server::initiate_handshake(Handshake_State& state,
bool force_full_renegotiation)
{
- dynamic_cast<Server_Handshake_State&>(state).allow_session_resumption =
- !force_full_renegotiation;
+ dynamic_cast<Server_Handshake_State&>(state).
+ set_allow_session_resumption(!force_full_renegotiation);
Hello_Request hello_req(state.handshake_io());
}
/*
-* Process a handshake message
+* Process a CLIENT HELLO Message
*/
-void Server::process_handshake_msg(const Handshake_State* active_state,
- Handshake_State& state_base,
- Handshake_Type type,
- const std::vector<byte>& contents)
- {
- Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base);
-
- state.confirm_transition_to(type);
-
- /*
- * The change cipher spec message isn't technically a handshake
- * message so it's not included in the hash. The finished and
- * certificate verify messages are verified based on the current
- * state of the hash *before* this message so we delay adding them
- * to the hash computation until we've processed them below.
- */
- if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY)
+void Server::process_client_hello_msg(const Handshake_State* active_state,
+ Server_Handshake_State& pending_state,
+ const std::vector<byte>& contents)
+{
+ const bool initial_handshake = !active_state;
+
+ if(!policy().allow_insecure_renegotiation() &&
+ !(initial_handshake || secure_renegotiation_supported()))
{
- state.hash().update(state.handshake_io().format(contents, type));
+ send_warning_alert(Alert::NO_RENEGOTIATION);
+ return;
}
- if(type == CLIENT_HELLO)
+ pending_state.client_hello(new Client_Hello(contents));
+ const Protocol_Version client_version = pending_state.client_hello()->version();
+
+ Protocol_Version negotiated_version;
+
+ const Protocol_Version latest_supported =
+ policy().latest_supported_version(client_version.is_datagram_protocol());
+
+ if((initial_handshake && client_version.known_version()) ||
+ (!initial_handshake && client_version == active_state->version()))
{
- const bool initial_handshake = !active_state;
+ /*
+ Common cases: new client hello with some known version, or a
+ renegotiation using the same version as previously
+ negotiated.
+ */
- if(!policy().allow_insecure_renegotiation() &&
- !(initial_handshake || secure_renegotiation_supported()))
+ negotiated_version = client_version;
+ }
+ else if(!initial_handshake && (client_version != active_state->version()))
+ {
+ /*
+ * If this is a renegotiation, and the client has offered a
+ * later version than what it initially negotiated, negotiate
+ * the old version. This matches OpenSSL's behavior. If the
+ * client is offering a version earlier than what it initially
+ * negotiated, reject as a probable attack.
+ */
+ if(active_state->version() > client_version)
{
- send_warning_alert(Alert::NO_RENEGOTIATION);
- return;
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Client negotiated " +
+ active_state->version().to_string() +
+ " then renegotiated with " +
+ client_version.to_string());
}
+ else
+ negotiated_version = active_state->version();
+ }
+ else
+ {
+ /*
+ New negotiation using a version we don't know. Offer them the
+ best we currently know and support
+ */
+ negotiated_version = latest_supported;
+ }
- state.client_hello(new Client_Hello(contents));
+ if(!policy().acceptable_protocol_version(negotiated_version))
+ {
+ throw TLS_Exception(Alert::PROTOCOL_VERSION,
+ "Client version " + negotiated_version.to_string() +
+ " is unacceptable by policy");
+ }
- const Protocol_Version client_version = state.client_hello()->version();
+ if(pending_state.client_hello()->sent_fallback_scsv())
+ {
+ if(latest_supported > client_version)
+ throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
+ "Client signalled fallback SCSV, possible attack");
+ }
- Protocol_Version negotiated_version;
+ secure_renegotiation_check(pending_state.client_hello());
- const Protocol_Version latest_supported =
- policy().latest_supported_version(client_version.is_datagram_protocol());
+ pending_state.set_version(negotiated_version);
- if((initial_handshake && client_version.known_version()) ||
- (!initial_handshake && client_version == active_state->version()))
- {
- /*
- Common cases: new client hello with some known version, or a
- renegotiation using the same version as previously
- negotiated.
- */
+ Session session_info;
+ const bool resuming =
+ pending_state.allow_session_resumption() &&
+ check_for_resume(session_info,
+ session_manager(),
+ m_creds,
+ pending_state.client_hello(),
+ std::chrono::seconds(policy().session_ticket_lifetime()));
- negotiated_version = client_version;
- }
- else if(!initial_handshake && (client_version != active_state->version()))
- {
- /*
- * If this is a renegotiation, and the client has offered a
- * later version than what it initially negotiated, negotiate
- * the old version. This matches OpenSSL's behavior. If the
- * client is offering a version earlier than what it initially
- * negotiated, reject as a probable attack.
- */
- if(active_state->version() > client_version)
- {
- throw TLS_Exception(Alert::PROTOCOL_VERSION,
- "Client negotiated " +
- active_state->version().to_string() +
- " then renegotiated with " +
- client_version.to_string());
- }
- else
- negotiated_version = active_state->version();
- }
- else
- {
- /*
- New negotiation using a version we don't know. Offer them the
- best we currently know and support
- */
- negotiated_version = latest_supported;
- }
+ bool have_session_ticket_key = false;
- if(!policy().acceptable_protocol_version(negotiated_version))
- {
- throw TLS_Exception(Alert::PROTOCOL_VERSION,
- "Client version " + negotiated_version.to_string() +
- " is unacceptable by policy");
- }
+ try
+ {
+ have_session_ticket_key =
+ m_creds.psk("tls-server", "session-ticket", "").length() > 0;
+ }
+ catch(...) {}
- if(state.client_hello()->sent_fallback_scsv())
- {
- if(latest_supported > client_version)
- throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
- "Client signalled fallback SCSV, possible attack");
- }
+ m_next_protocol = "";
+ if(m_choose_next_protocol && pending_state.client_hello()->supports_alpn())
+ m_next_protocol = m_choose_next_protocol(pending_state.client_hello()->next_protocols());
- secure_renegotiation_check(state.client_hello());
+ if(resuming)
+ {
+ this->session_resume(pending_state, have_session_ticket_key, session_info);
+ }
+ else // new session
+ {
+ this->session_create(pending_state, have_session_ticket_key);
+ }
+}
- state.set_version(negotiated_version);
+void Server::process_certificate_msg(Server_Handshake_State& pending_state,
+ const std::vector<byte>& contents)
+{
+ pending_state.client_certs(new Certificate(contents));
+ pending_state.set_expected_next(CLIENT_KEX);
+}
- Session session_info;
- const bool resuming =
- state.allow_session_resumption &&
- check_for_resume(session_info,
- session_manager(),
- m_creds,
- state.client_hello(),
- std::chrono::seconds(policy().session_ticket_lifetime()));
+void Server::process_client_key_exchange_msg(Server_Handshake_State& pending_state,
+ const std::vector<byte>& contents)
+{
+ if(pending_state.received_handshake_msg(CERTIFICATE) && !pending_state.client_certs()->empty())
+ pending_state.set_expected_next(CERTIFICATE_VERIFY);
+ else
+ pending_state.set_expected_next(HANDSHAKE_CCS);
- bool have_session_ticket_key = false;
+ pending_state.client_kex(
+ new Client_Key_Exchange(contents, pending_state,
+ pending_state.server_rsa_kex_key(),
+ m_creds, policy(), rng())
+ );
- try
- {
- have_session_ticket_key =
- m_creds.psk("tls-server", "session-ticket", "").length() > 0;
- }
- catch(...) {}
+ pending_state.compute_session_keys();
+}
- m_next_protocol = "";
- if(m_choose_next_protocol && state.client_hello()->supports_alpn())
- m_next_protocol = m_choose_next_protocol(state.client_hello()->next_protocols());
+void Server::process_change_cipher_spec_msg(Server_Handshake_State& pending_state)
+{
+ pending_state.set_expected_next(FINISHED);
+ change_cipher_spec_reader(SERVER);
+}
- if(resuming)
- {
- // Only offer a resuming client a new ticket if they didn't send one this time,
- // ie, resumed via server-side resumption. TODO: also send one if expiring soon?
-
- const bool offer_new_session_ticket =
- (state.client_hello()->supports_session_ticket() &&
- state.client_hello()->session_ticket().empty() &&
- have_session_ticket_key);
-
- state.server_hello(new Server_Hello(
- state.handshake_io(),
- state.hash(),
- policy(),
- rng(),
- secure_renegotiation_data_for_server_hello(),
- *state.client_hello(),
- session_info,
- offer_new_session_ticket,
- m_next_protocol
- ));
-
- secure_renegotiation_check(state.server_hello());
-
- state.compute_session_keys(session_info.master_secret());
-
- if(!save_session(session_info))
+void Server::process_certificate_verify_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<byte>& contents)
+{
+ pending_state.client_verify ( new Certificate_Verify ( contents, pending_state.version() ) );
+
+ const std::vector<X509_Certificate>& client_certs =
+ pending_state.client_certs()->cert_chain();
+
+ const bool sig_valid =
+ pending_state.client_verify()->verify ( client_certs[0], pending_state, policy() );
+
+ pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) );
+
+ /*
+ * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for
+ * "A handshake cryptographic operation failed, including being
+ * unable to correctly verify a signature, ..."
+ */
+ if ( !sig_valid )
+ throw TLS_Exception ( Alert::DECRYPT_ERROR, "Client cert verify failed" );
+
+ try
+ {
+ m_creds.verify_certificate_chain ( "tls-server", "", client_certs );
+ }
+ catch ( std::exception& e )
+ {
+ throw TLS_Exception ( Alert::BAD_CERTIFICATE, e.what() );
+ }
+
+ pending_state.set_expected_next ( HANDSHAKE_CCS );
+}
+
+void Server::process_finished_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<byte>& contents)
+{
+ pending_state.set_expected_next ( HANDSHAKE_NONE );
+
+ pending_state.client_finished ( new Finished ( contents ) );
+
+ if ( !pending_state.client_finished()->verify ( pending_state, CLIENT ) )
+ throw TLS_Exception ( Alert::DECRYPT_ERROR,
+ "Finished message didn't verify" );
+
+ if ( !pending_state.server_finished() )
+ {
+ // already sent finished if resuming, so this is a new session
+
+ pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) );
+
+ Session::Properties session_properties(
+ Server_Information(pending_state.client_hello()->sni_hostname()),
+ "",
+ pending_state.server_hello()->srtp_profile(),
+ pending_state.server_hello()->version(),
+ pending_state.server_hello()->ciphersuite(),
+ pending_state.server_hello()->compression_method());
+
+
+ Session session_info(
+ pending_state.server_hello()->session_id(),
+ pending_state.session_keys().master_secret(),
+ SERVER,
+ pending_state.server_hello()->supports_extended_master_secret(),
+ get_peer_cert_chain ( pending_state ),
+ std::vector<byte>(),
+ session_properties);
+
+ if ( save_session ( session_info ) )
{
- session_manager().remove_entry(session_info.session_id());
-
- if(state.server_hello()->supports_session_ticket()) // send an empty ticket
- {
- state.new_session_ticket(
- new New_Session_Ticket(state.handshake_io(),
- state.hash())
- );
- }
+ if ( pending_state.server_hello()->supports_session_ticket() )
+ {
+ try
+ {
+ const SymmetricKey ticket_key = m_creds.psk ( "tls-server", "session-ticket", "" );
+
+ pending_state.new_session_ticket (
+ new New_Session_Ticket ( pending_state.handshake_io(),
+ pending_state.hash(),
+ session_info.encrypt ( ticket_key, rng() ),
+ policy().session_ticket_lifetime() )
+ );
+ }
+ catch ( ... ) {}
+ }
+ else
+ session_manager().save ( session_info );
}
- if(state.server_hello()->supports_session_ticket() && !state.new_session_ticket())
+ if ( !pending_state.new_session_ticket() &&
+ pending_state.server_hello()->supports_session_ticket() )
{
- try
- {
- const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
-
- state.new_session_ticket(
- new New_Session_Ticket(state.handshake_io(),
- state.hash(),
- session_info.encrypt(ticket_key, rng()),
- policy().session_ticket_lifetime())
- );
- }
- catch(...) {}
-
- if(!state.new_session_ticket())
- {
- state.new_session_ticket(
- new New_Session_Ticket(state.handshake_io(), state.hash())
- );
- }
+ pending_state.new_session_ticket (
+ new New_Session_Ticket ( pending_state.handshake_io(), pending_state.hash() )
+ );
}
- state.handshake_io().send(Change_Cipher_Spec());
+ pending_state.handshake_io().send ( Change_Cipher_Spec() );
- change_cipher_spec_writer(SERVER);
+ change_cipher_spec_writer ( SERVER );
- state.server_finished(new Finished(state.handshake_io(), state, SERVER));
- state.set_expected_next(HANDSHAKE_CCS);
- }
- else // new session
- {
- std::map<std::string, std::vector<X509_Certificate> > cert_chains;
+ pending_state.server_finished ( new Finished ( pending_state.handshake_io(), pending_state, SERVER ) );
+ }
- const std::string sni_hostname = state.client_hello()->sni_hostname();
+ activate_session();
- cert_chains = get_server_certs(sni_hostname, m_creds);
+}
- if(sni_hostname != "" && cert_chains.empty())
- {
- cert_chains = get_server_certs("", m_creds);
-
- /*
- * Only send the unrecognized_name alert if we couldn't
- * find any certs for the requested name but did find at
- * least one cert to use in general. That avoids sending an
- * unrecognized_name when a server is configured for purely
- * anonymous operation.
- */
- if(!cert_chains.empty())
- send_alert(Alert(Alert::UNRECOGNIZED_NAME));
- }
+/*
+* Process a handshake message
+*/
+void Server::process_handshake_msg(const Handshake_State* active_state,
+ Handshake_State& state_base,
+ Handshake_Type type,
+ const std::vector<byte>& contents)
+ {
+ Server_Handshake_State& state = dynamic_cast<Server_Handshake_State&>(state_base);
+ state.confirm_transition_to(type);
- state.server_hello(new Server_Hello(
- state.handshake_io(),
- state.hash(),
- policy(),
- rng(),
- secure_renegotiation_data_for_server_hello(),
- *state.client_hello(),
- make_hello_random(rng(), policy()), // new session ID
- state.version(),
- choose_ciphersuite(policy(), state.version(), m_creds, cert_chains, state.client_hello()),
- choose_compression(policy(), state.client_hello()->compression_methods()),
- have_session_ticket_key,
- m_next_protocol)
- );
+ /*
+ * The change cipher spec message isn't technically a handshake
+ * message so it's not included in the hash. The finished and
+ * certificate verify messages are verified based on the current
+ * state of the hash *before* this message so we delay adding them
+ * to the hash computation until we've processed them below.
+ */
+ if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY)
+ {
+ state.hash().update(state.handshake_io().format(contents, type));
+ }
- secure_renegotiation_check(state.server_hello());
+ switch(type)
+ {
+ case CLIENT_HELLO:
+ this->process_client_hello_msg(active_state, state, contents);
+ break;
- const std::string sig_algo = state.ciphersuite().sig_algo();
- const std::string kex_algo = state.ciphersuite().kex_algo();
+ case CERTIFICATE:
+ this->process_certificate_msg(state, contents);
+ break;
- if(sig_algo != "")
- {
- BOTAN_ASSERT(!cert_chains[sig_algo].empty(),
- "Attempting to send empty certificate chain");
+ case CLIENT_KEX:
+ this->process_client_key_exchange_msg(state, contents);
+ break;
- state.server_certs(new Certificate(state.handshake_io(),
- state.hash(),
- cert_chains[sig_algo]));
- }
+ case CERTIFICATE_VERIFY:
+ this->process_certificate_verify_msg(state, type, contents);
+ break;
- Private_Key* private_key = nullptr;
+ case HANDSHAKE_CCS:
+ this->process_change_cipher_spec_msg(state);
+ break;
- if(kex_algo == "RSA" || sig_algo != "")
- {
- private_key = m_creds.private_key_for(
- state.server_certs()->cert_chain()[0],
- "tls-server",
- sni_hostname);
+ case FINISHED:
+ this->process_finished_msg(state, type, contents);
+ break;
+
+ default:
+ throw Unexpected_Message("Unknown handshake message received");
+ break;
+ }
+ }
+
+void Server::session_resume(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key,
+ Session& session_info)
+ {
+ // Only offer a resuming client a new ticket if they didn't send one this time,
+ // ie, resumed via server-side resumption. TODO: also send one if expiring soon?
+
+ const bool offer_new_session_ticket =
+ (pending_state.client_hello()->supports_session_ticket() &&
+ pending_state.client_hello()->session_ticket().empty() &&
+ have_session_ticket_key);
+
+ Server_Hello::Handshake_Info hs_info(pending_state.handshake_io(),
+ pending_state.hash());
+ pending_state.server_hello(new Server_Hello(
+ hs_info,
+ policy(),
+ rng(),
+ secure_renegotiation_data_for_server_hello(),
+ *pending_state.client_hello(),
+ session_info,
+ offer_new_session_ticket,
+ m_next_protocol
+ ));
+
+ secure_renegotiation_check(pending_state.server_hello());
+
+ pending_state.compute_session_keys(session_info.master_secret());
+
+ if(!save_session(session_info))
+ {
+ session_manager().remove_entry(session_info.session_id());
- if(!private_key)
- throw Internal_Error("No private key located for associated server cert");
+ if(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash())
+ );
}
+ }
- if(kex_algo == "RSA")
+ if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket())
+ {
+ try
{
- state.server_rsa_kex_key = private_key;
+ const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
+
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash(),
+ session_info.encrypt(ticket_key, rng()),
+ policy().session_ticket_lifetime())
+ );
}
- else
+ catch(...) {}
+
+ if(!pending_state.new_session_ticket())
{
- state.server_kex(new Server_Key_Exchange(state.handshake_io(),
- state, policy(),
- m_creds, rng(), private_key));
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash())
+ );
}
+ }
- auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+ pending_state.handshake_io().send(Change_Cipher_Spec());
- std::vector<X509_DN> client_auth_CAs;
+ change_cipher_spec_writer(SERVER);
- for(auto store : trusted_CAs)
- {
- auto subjects = store->all_subjects();
- client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
- }
+ pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+ }
- if(!client_auth_CAs.empty() && state.ciphersuite().sig_algo() != "")
- {
- state.cert_req(
- new Certificate_Req(state.handshake_io(), state.hash(),
- policy(), client_auth_CAs, state.version()));
+void Server::session_create(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key)
+ {
+ std::map<std::string, std::vector<X509_Certificate> > cert_chains;
- state.set_expected_next(CERTIFICATE);
- }
+ const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
- /*
- * If the client doesn't have a cert they want to use they are
- * allowed to send either an empty cert message or proceed
- * directly to the client key exchange, so allow either case.
- */
- state.set_expected_next(CLIENT_KEX);
+ cert_chains = get_server_certs(sni_hostname, m_creds);
- state.server_hello_done(new Server_Hello_Done(state.handshake_io(), state.hash()));
- }
- }
- else if(type == CERTIFICATE)
+ if(sni_hostname != "" && cert_chains.empty())
{
- state.client_certs(new Certificate(contents));
+ cert_chains = get_server_certs("", m_creds);
- state.set_expected_next(CLIENT_KEX);
+ /*
+ * Only send the unrecognized_name alert if we couldn't
+ * find any certs for the requested name but did find at
+ * least one cert to use in general. That avoids sending an
+ * unrecognized_name when a server is configured for purely
+ * anonymous operation.
+ */
+ if(!cert_chains.empty())
+ send_alert(Alert(Alert::UNRECOGNIZED_NAME));
}
- else if(type == CLIENT_KEX)
- {
- if(state.received_handshake_msg(CERTIFICATE) && !state.client_certs()->empty())
- state.set_expected_next(CERTIFICATE_VERIFY);
- else
- state.set_expected_next(HANDSHAKE_CCS);
-
- state.client_kex(
- new Client_Key_Exchange(contents, state,
- state.server_rsa_kex_key,
- m_creds, policy(), rng())
- );
- state.compute_session_keys();
- }
- else if(type == CERTIFICATE_VERIFY)
+ Server_Hello::Settings srv_settings(
+ make_hello_random(rng(), policy()), // new session ID
+ pending_state.version(),
+ choose_ciphersuite(policy(),
+ pending_state.version(),
+ m_creds,
+ cert_chains,
+ pending_state.client_hello()),
+ choose_compression(policy(),
+ pending_state.client_hello()->compression_methods()),
+ have_session_ticket_key);
+
+ Server_Hello::Handshake_Info hs_info(pending_state.handshake_io(),
+ pending_state.hash());
+ pending_state.server_hello(new Server_Hello(
+ hs_info,
+ policy(),
+ rng(),
+ secure_renegotiation_data_for_server_hello(),
+ *pending_state.client_hello(),
+ srv_settings,
+ m_next_protocol)
+ );
+
+ secure_renegotiation_check(pending_state.server_hello());
+
+ const std::string sig_algo = pending_state.ciphersuite().sig_algo();
+ const std::string kex_algo = pending_state.ciphersuite().kex_algo();
+
+ if(sig_algo != "")
{
- state.client_verify(new Certificate_Verify(contents, state.version()));
+ BOTAN_ASSERT(!cert_chains[sig_algo].empty(),
+ "Attempting to send empty certificate chain");
- const std::vector<X509_Certificate>& client_certs =
- state.client_certs()->cert_chain();
+ Certificate::Handshake_Info hs_info(pending_state.handshake_io(),
+ pending_state.hash());
- const bool sig_valid =
- state.client_verify()->verify(client_certs[0], state, policy());
+ pending_state.server_certs(new Certificate(hs_info, cert_chains[sig_algo]));
+ }
- state.hash().update(state.handshake_io().format(contents, type));
+ Private_Key* private_key = nullptr;
- /*
- * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for
- * "A handshake cryptographic operation failed, including being
- * unable to correctly verify a signature, ..."
- */
- if(!sig_valid)
- throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed");
-
- try
- {
- m_creds.verify_certificate_chain("tls-server", "", client_certs);
- }
- catch(std::exception& e)
- {
- throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what());
- }
+ if(kex_algo == "RSA" || sig_algo != "")
+ {
+ private_key = m_creds.private_key_for(
+ pending_state.server_certs()->cert_chain()[0],
+ "tls-server",
+ sni_hostname);
- state.set_expected_next(HANDSHAKE_CCS);
+ if(!private_key)
+ throw Internal_Error("No private key located for associated server cert");
}
- else if(type == HANDSHAKE_CCS)
+
+ if(kex_algo == "RSA")
{
- state.set_expected_next(FINISHED);
- change_cipher_spec_reader(SERVER);
+ pending_state.set_server_rsa_kex_key(private_key);
}
- else if(type == FINISHED)
+ else
{
- state.set_expected_next(HANDSHAKE_NONE);
-
- state.client_finished(new Finished(contents));
- if(!state.client_finished()->verify(state, CLIENT))
- throw TLS_Exception(Alert::DECRYPT_ERROR,
- "Finished message didn't verify");
+ pending_state.server_kex(new Server_Key_Exchange(pending_state.handshake_io(),
+ pending_state, policy(),
+ m_creds, rng(), private_key));
+ }
- if(!state.server_finished())
- {
- // already sent finished if resuming, so this is a new session
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
- state.hash().update(state.handshake_io().format(contents, type));
+ std::vector<X509_DN> client_auth_CAs;
- Session session_info(
- state.server_hello()->session_id(),
- state.session_keys().master_secret(),
- state.server_hello()->version(),
- state.server_hello()->ciphersuite(),
- state.server_hello()->compression_method(),
- SERVER,
- state.server_hello()->supports_extended_master_secret(),
- get_peer_cert_chain(state),
- std::vector<byte>(),
- Server_Information(state.client_hello()->sni_hostname()),
- state.srp_identifier(),
- state.server_hello()->srtp_profile()
- );
-
- if(save_session(session_info))
- {
- if(state.server_hello()->supports_session_ticket())
- {
- try
- {
- const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
-
- state.new_session_ticket(
- new New_Session_Ticket(state.handshake_io(),
- state.hash(),
- session_info.encrypt(ticket_key, rng()),
- policy().session_ticket_lifetime())
- );
- }
- catch(...) {}
- }
- else
- session_manager().save(session_info);
- }
-
- if(!state.new_session_ticket() &&
- state.server_hello()->supports_session_ticket())
- {
- state.new_session_ticket(
- new New_Session_Ticket(state.handshake_io(), state.hash())
- );
- }
+ for(auto store : trusted_CAs)
+ {
+ auto subjects = store->all_subjects();
+ client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
+ }
- state.handshake_io().send(Change_Cipher_Spec());
+ if(!client_auth_CAs.empty() && pending_state.ciphersuite().sig_algo() != "")
+ {
+ Certificate_Req::Handshake_Info hs_info(pending_state.handshake_io(),
+ pending_state.hash());
+ pending_state.cert_req(
+ new Certificate_Req(hs_info, policy(), client_auth_CAs,
+ pending_state.version()));
- change_cipher_spec_writer(SERVER);
+ pending_state.set_expected_next(CERTIFICATE);
+ }
- state.server_finished(new Finished(state.handshake_io(), state, SERVER));
- }
+ /*
+ * If the client doesn't have a cert they want to use they are
+ * allowed to send either an empty cert message or proceed
+ * directly to the client key exchange, so allow either case.
+ */
+ pending_state.set_expected_next(CLIENT_KEX);
- activate_session();
- }
- else
- throw Unexpected_Message("Unknown handshake message received");
+ pending_state.server_hello_done(new Server_Hello_Done(pending_state.handshake_io(), pending_state.hash()));
}
-
}
}
diff --git a/src/lib/tls/tls_server.h b/src/lib/tls/tls_server.h
index 5ea2a1318..72834785e 100644
--- a/src/lib/tls/tls_server.h
+++ b/src/lib/tls/tls_server.h
@@ -1,6 +1,7 @@
/*
* TLS Server
* (C) 2004-2011 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -10,8 +11,11 @@
#include <botan/tls_channel.h>
#include <botan/credentials_manager.h>
+#include <botan/internal/tls_server_handshake_state.h>
#include <vector>
+
+
namespace Botan {
namespace TLS {
@@ -27,32 +31,15 @@ class BOTAN_DLL Server final : public Channel
/**
* Server initialization
*/
- Server(output_fn output,
- data_cb data_cb,
- alert_cb alert_cb,
- handshake_cb handshake_cb,
+ Server(const Callbacks& callbacks,
Session_Manager& session_manager,
Credentials_Manager& creds,
const Policy& policy,
RandomNumberGenerator& rng,
next_protocol_fn next_proto = next_protocol_fn(),
bool is_datagram = false,
- size_t reserved_io_buffer_size = 16*1024
- );
-
- Server(output_fn output,
- data_cb data_cb,
- alert_cb alert_cb,
- handshake_cb handshake_cb,
- handshake_msg_cb hs_msg_cb,
- Session_Manager& session_manager,
- Credentials_Manager& creds,
- const Policy& policy,
- RandomNumberGenerator& rng,
- next_protocol_fn next_proto = next_protocol_fn(),
- bool is_datagram = false
+ size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE
);
-
/**
* Return the protocol notification set by the client (using the
* NPN extension) for this connection, if any. This value is not
@@ -73,6 +60,33 @@ class BOTAN_DLL Server final : public Channel
Handshake_Type type,
const std::vector<byte>& contents) override;
+ void process_client_hello_msg(const Handshake_State* active_state,
+ Server_Handshake_State& pending_state,
+ const std::vector<byte>& contents);
+
+ void process_certificate_msg(Server_Handshake_State& pending_state,
+ const std::vector<byte>& contents);
+
+ void process_client_key_exchange_msg(Server_Handshake_State& pending_state,
+ const std::vector<byte>& contents);
+
+ void process_change_cipher_spec_msg(Server_Handshake_State& pending_state);
+
+ void process_certificate_verify_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<byte>& contents);
+
+ void process_finished_msg(Server_Handshake_State& pending_state,
+ Handshake_Type type,
+ const std::vector<byte>& contents);
+
+ void session_resume(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key,
+ Session& session_info);
+
+ void session_create(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key);
+
Handshake_State* new_handshake_state(Handshake_IO* io) override;
Credentials_Manager& m_creds;
diff --git a/src/lib/tls/tls_server_handshake_state.h b/src/lib/tls/tls_server_handshake_state.h
new file mode 100644
index 000000000..281dd82df
--- /dev/null
+++ b/src/lib/tls/tls_server_handshake_state.h
@@ -0,0 +1,47 @@
+/*
+* TLS Server
+* (C) 2004-2011,2012,2016 Jack Lloyd
+* 2016 Matthias Gierlings
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__
+#define BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__
+
+#include <botan/internal/tls_handshake_state.h>
+namespace Botan {
+
+namespace TLS {
+
+class Server_Handshake_State : public Handshake_State
+ {
+ public:
+ Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb)
+ : Handshake_State(io, cb) {}
+
+ Private_Key* server_rsa_kex_key() { return m_server_rsa_kex_key; }
+ void set_server_rsa_kex_key(Private_Key* key)
+ { m_server_rsa_kex_key = key; }
+
+ bool allow_session_resumption() const
+ { return m_allow_session_resumption; }
+ void set_allow_session_resumption(bool allow_session_resumption)
+ { m_allow_session_resumption = allow_session_resumption; }
+
+
+ private:
+ // Used by the server only, in case of RSA key exchange. Not owned
+ Private_Key* m_server_rsa_kex_key = nullptr;
+
+ /*
+ * Used by the server to know if resumption should be allowed on
+ * a server-initiated renegotiation
+ */
+ bool m_allow_session_resumption = true;
+ };
+
+}
+
+}
+#endif //BOTAN_TLS_SERVER_HANDSHAKE_STATE_H__
diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp
index 18c9b357c..bcbac10af 100644
--- a/src/lib/tls/tls_session.cpp
+++ b/src/lib/tls/tls_session.cpp
@@ -1,6 +1,7 @@
/*
* TLS Session State
* (C) 2011-2012,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -19,29 +20,19 @@ namespace TLS {
Session::Session(const std::vector<byte>& session_identifier,
const secure_vector<byte>& master_secret,
- Protocol_Version version,
- u16bit ciphersuite,
- byte compression_method,
Connection_Side side,
bool extended_master_secret,
const std::vector<X509_Certificate>& certs,
const std::vector<byte>& ticket,
- const Server_Information& server_info,
- const std::string& srp_identifier,
- u16bit srtp_profile) :
+ Properties properties) :
m_start_time(std::chrono::system_clock::now()),
m_identifier(session_identifier),
m_session_ticket(ticket),
m_master_secret(master_secret),
- m_version(version),
- m_ciphersuite(ciphersuite),
- m_compression_method(compression_method),
m_connection_side(side),
- m_srtp_profile(srtp_profile),
m_extended_master_secret(extended_master_secret),
m_peer_certs(certs),
- m_server_info(server_info),
- m_srp_identifier(srp_identifier)
+ m_properties(properties)
{
}
@@ -69,6 +60,9 @@ Session::Session(const byte ber[], size_t ber_len)
size_t srtp_profile = 0;
size_t fragment_size = 0;
+ u16bit cs = m_properties.get_ciphersuite();
+ byte compr = compression_method();
+
BER_Decoder(ber, ber_len)
.start_cons(SEQUENCE)
.decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION),
@@ -78,10 +72,9 @@ Session::Session(const byte ber[], size_t ber_len)
.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(cs)
+ .decode_integer_type(compr)
.decode_integer_type(side_code)
- .decode_integer_type(fragment_size)
.decode(m_extended_master_secret)
.decode(m_master_secret, OCTET_STRING)
.decode(peer_cert_bits, OCTET_STRING)
@@ -103,16 +96,17 @@ Session::Session(const byte ber[], size_t ber_len)
" no longer supported");
}
- m_version = Protocol_Version(major_version, minor_version);
+ m_properties.set_ciphersuite(cs);
+ m_properties.set_compression_method(compr);
+ m_properties.set_protocol_version(Protocol_Version(major_version, minor_version));
m_start_time = std::chrono::system_clock::from_time_t(start_time);
m_connection_side = static_cast<Connection_Side>(side_code);
- m_srtp_profile = static_cast<u16bit>(srtp_profile);
-
- m_server_info = Server_Information(server_hostname.value(),
- server_service.value(),
- static_cast<u16bit>(server_port));
-
- m_srp_identifier = srp_identifier_str.value();
+ m_properties.set_srtp_profile(static_cast<u16bit>(srtp_profile));
+ m_properties.set_server_info(
+ Server_Information(server_hostname.value(),
+ server_service.value(),
+ static_cast<u16bit>(server_port)));
+ m_properties.set_srp_identifier(srp_identifier_str.value());
if(!peer_cert_bits.empty())
{
@@ -133,22 +127,22 @@ secure_vector<byte> Session::DER_encode() const
.start_cons(SEQUENCE)
.encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION))
.encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time)))
- .encode(static_cast<size_t>(m_version.major_version()))
- .encode(static_cast<size_t>(m_version.minor_version()))
+ .encode(static_cast<size_t>(version().major_version()))
+ .encode(static_cast<size_t>(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>(ciphersuite_code()))
+ .encode(static_cast<size_t>(compression_method()))
.encode(static_cast<size_t>(m_connection_side))
.encode(static_cast<size_t>(/*old fragment size*/0))
.encode(m_extended_master_secret)
.encode(m_master_secret, OCTET_STRING)
.encode(peer_cert_bits, OCTET_STRING)
- .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING))
- .encode(ASN1_String(m_server_info.service(), UTF8_STRING))
- .encode(static_cast<size_t>(m_server_info.port()))
- .encode(ASN1_String(m_srp_identifier, UTF8_STRING))
- .encode(static_cast<size_t>(m_srtp_profile))
+ .encode(ASN1_String(m_properties.get_server_info().hostname(), UTF8_STRING))
+ .encode(ASN1_String(m_properties.get_server_info().service(), UTF8_STRING))
+ .encode(static_cast<size_t>(m_properties.get_server_info().port()))
+ .encode(ASN1_String(srp_identifier(), UTF8_STRING))
+ .encode(static_cast<size_t>(dtls_srtp_profile()))
.end_cons()
.get_contents();
}
diff --git a/src/lib/tls/tls_session.h b/src/lib/tls/tls_session.h
index 8ca646cf2..600aa0a10 100644
--- a/src/lib/tls/tls_session.h
+++ b/src/lib/tls/tls_session.h
@@ -1,6 +1,7 @@
/*
* TLS Session
* (C) 2011-2012,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -27,35 +28,106 @@ namespace TLS {
class BOTAN_DLL Session
{
public:
+ class Properties
+ {
+ public:
+ Properties() : m_srtp_profile(0), m_protocol_version(),
+ m_ciphersuite(), m_compression_method(0) {}
+
+ Properties(const Server_Information& server_info,
+ const std::string& srp_identifier,
+ u16bit srtp_profile,
+ Protocol_Version protocol_version,
+ u16bit ciphersuite,
+ byte compression_method)
+ : m_server_info(server_info),
+ m_srp_identifier(srp_identifier),
+ m_srtp_profile(srtp_profile),
+ m_protocol_version(protocol_version),
+ m_ciphersuite(ciphersuite),
+ m_compression_method(compression_method) {}
+
+ const Server_Information& get_server_info() const
+ {
+ return m_server_info;
+ }
+
+ void set_server_info(Server_Information server_info)
+ {
+ m_server_info = server_info;
+ }
+
+ const std::string& get_srp_identifier() const
+ {
+ return m_srp_identifier;
+ }
+
+ void set_srp_identifier(const std::string& srp_identifier)
+ {
+ m_srp_identifier = srp_identifier;
+ }
+
+ u16bit get_srtp_profile() const { return m_srtp_profile; }
+ void set_srtp_profile(u16bit srtp_profile)
+ {
+ m_srtp_profile = srtp_profile;
+ }
+
+ Protocol_Version get_protocol_version() const
+ {
+ return m_protocol_version;
+ }
+
+ void set_protocol_version(Protocol_Version protocol_version)
+ {
+ m_protocol_version = protocol_version;
+ }
+
+ u16bit get_ciphersuite() const { return m_ciphersuite; }
+
+ void set_ciphersuite(u16bit ciphersuite)
+ {
+ m_ciphersuite = ciphersuite;
+ }
+
+ byte get_compression_method() const
+ {
+ return m_compression_method;
+ }
+
+ void set_compression_method(byte compression_method)
+ {
+ m_compression_method = compression_method;
+ }
+
+ private:
+ Server_Information m_server_info;
+ std::string m_srp_identifier;
+ u16bit m_srtp_profile;
+ Protocol_Version m_protocol_version;
+ u16bit m_ciphersuite;
+ byte m_compression_method;
+ };
/**
* Uninitialized session
*/
Session() :
m_start_time(std::chrono::system_clock::time_point::min()),
- m_version(),
- m_ciphersuite(0),
- m_compression_method(0),
m_connection_side(static_cast<Connection_Side>(0)),
- m_srtp_profile(0),
- m_extended_master_secret(false)
- {}
+ m_extended_master_secret(false),
+ m_properties() {}
/**
* New session (sets session start time)
*/
Session(const std::vector<byte>& session_id,
const secure_vector<byte>& master_secret,
- Protocol_Version version,
- u16bit ciphersuite,
- byte compression_method,
Connection_Side side,
bool supports_extended_master_secret,
const std::vector<X509_Certificate>& peer_certs,
const std::vector<byte>& session_ticket,
- const Server_Information& server_info,
- const std::string& srp_identifier,
- u16bit srtp_profile);
+ Properties properties);
/**
* Load a session from DER representation (created by DER_encode)
@@ -112,22 +184,22 @@ class BOTAN_DLL Session
/**
* Get the version of the saved session
*/
- Protocol_Version version() const { return m_version; }
+ Protocol_Version version() const { return m_properties.get_protocol_version(); }
/**
* Get the ciphersuite code of the saved session
*/
- u16bit ciphersuite_code() const { return m_ciphersuite; }
+ u16bit ciphersuite_code() const { return m_properties.get_ciphersuite(); }
/**
* Get the ciphersuite info of the saved session
*/
- Ciphersuite ciphersuite() const { return Ciphersuite::by_id(m_ciphersuite); }
+ Ciphersuite ciphersuite() const { return Ciphersuite::by_id(ciphersuite_code()); }
/**
* Get the compression method used in the saved session
*/
- byte compression_method() const { return m_compression_method; }
+ byte compression_method() const { return m_properties.get_compression_method(); }
/**
* Get which side of the connection the resumed session we are/were
@@ -138,7 +210,7 @@ class BOTAN_DLL Session
/**
* Get the SRP identity (if sent by the client in the initial handshake)
*/
- const std::string& srp_identifier() const { return m_srp_identifier; }
+ const std::string& srp_identifier() const { return m_properties.get_srp_identifier(); }
/**
* Get the saved master secret
@@ -153,7 +225,7 @@ class BOTAN_DLL Session
/**
* Get the negotiated DTLS-SRTP algorithm (RFC 5764)
*/
- u16bit dtls_srtp_profile() const { return m_srtp_profile; }
+ u16bit dtls_srtp_profile() const { return m_properties.get_srtp_profile(); }
bool supports_extended_master_secret() const { return m_extended_master_secret; }
@@ -177,7 +249,7 @@ class BOTAN_DLL Session
*/
const std::vector<byte>& session_ticket() const { return m_session_ticket; }
- const Server_Information& server_info() const { return m_server_info; }
+ const Server_Information& server_info() const { return m_properties.get_server_info(); }
private:
enum { TLS_SESSION_PARAM_STRUCT_VERSION = 20160103 };
@@ -188,16 +260,10 @@ class BOTAN_DLL Session
std::vector<byte> m_session_ticket; // only used by client side
secure_vector<byte> m_master_secret;
- Protocol_Version m_version;
- u16bit m_ciphersuite;
- byte m_compression_method;
Connection_Side m_connection_side;
- u16bit m_srtp_profile;
bool m_extended_master_secret;
-
std::vector<X509_Certificate> m_peer_certs;
- Server_Information m_server_info; // optional
- std::string m_srp_identifier; // optional
+ Properties m_properties;
};
}
diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp
index f125bfcb5..6e6ac29a0 100644
--- a/src/tests/unit_tls.cpp
+++ b/src/tests/unit_tls.cpp
@@ -1,5 +1,6 @@
/*
* (C) 2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -218,10 +219,12 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
{
std::vector<byte> c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent;
- Botan::TLS::Server server(queue_inserter(s2c_traffic),
- queue_inserter(server_recv),
- print_alert,
- handshake_complete,
+
+ Botan::TLS::Server server(Botan::TLS::Server::Callbacks(
+ queue_inserter(s2c_traffic),
+ queue_inserter(server_recv),
+ print_alert,
+ handshake_complete),
server_sessions,
creds,
policy,
@@ -229,17 +232,19 @@ Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version,
next_protocol_chooser,
false);
- Botan::TLS::Client client(queue_inserter(c2s_traffic),
- queue_inserter(client_recv),
- print_alert,
- handshake_complete,
+ Botan::TLS::Client::Callbacks client_callbacks(queue_inserter(c2s_traffic),
+ queue_inserter(client_recv),
+ print_alert,
+ handshake_complete);
+ Botan::TLS::Client client(client_callbacks,
client_sessions,
creds,
policy,
rng,
- Botan::TLS::Server_Information("server.example.com"),
- offer_version,
- protocols_offered);
+ Botan::TLS::Client::Properties(
+ Botan::TLS::Server_Information("server.example.com"),
+ offer_version,
+ protocols_offered));
size_t rounds = 0;
@@ -444,10 +449,11 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
{
std::vector<byte> c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent;
- Botan::TLS::Server server(queue_inserter(s2c_traffic),
- queue_inserter(server_recv),
- print_alert,
- handshake_complete,
+ Botan::TLS::Server::Callbacks server_callbacks(queue_inserter(s2c_traffic),
+ queue_inserter(server_recv),
+ print_alert,
+ handshake_complete);
+ Botan::TLS::Server server(server_callbacks,
server_sessions,
creds,
policy,
@@ -455,17 +461,19 @@ Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version,
next_protocol_chooser,
true);
- Botan::TLS::Client client(queue_inserter(c2s_traffic),
- queue_inserter(client_recv),
- print_alert,
- handshake_complete,
+ Botan::TLS::Client::Callbacks client_callbacks(queue_inserter(c2s_traffic),
+ queue_inserter(client_recv),
+ print_alert,
+ handshake_complete);
+ Botan::TLS::Client client(client_callbacks,
client_sessions,
creds,
policy,
rng,
- Botan::TLS::Server_Information("server.example.com"),
- offer_version,
- protocols_offered);
+ Botan::TLS::Client::Properties(
+ Botan::TLS::Server_Information("server.example.com"),
+ offer_version,
+ protocols_offered));
size_t rounds = 0;