aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-08-31 10:31:58 -0400
committerJack Lloyd <[email protected]>2016-08-31 10:31:58 -0400
commit148262088c117ba849efc42432f2d2510ce25349 (patch)
tree663bf183b9ce54f08530f319fd4b491473514f61
parent5e946f93e8e751d2104f58583d4f209ca631aff1 (diff)
parentee60a29088fc6dd712c1651af1e7f56a26f40d63 (diff)
Merge GH #567/GH #457 TLS refactoring and Callbacks interface
-rw-r--r--doc/manual/tls.rst174
-rw-r--r--src/cli/tls_client.cpp38
-rw-r--r--src/cli/tls_proxy.cpp18
-rw-r--r--src/lib/cert/x509/x509_ca.h1
-rw-r--r--src/lib/math/bigint/big_ops2.cpp6
-rw-r--r--src/lib/math/bigint/big_ops3.cpp5
-rw-r--r--src/lib/math/ec_gfp/curve_gfp.cpp19
-rw-r--r--src/lib/math/mp/mp_core.h13
-rw-r--r--src/lib/math/mp/mp_karat.cpp58
-rw-r--r--src/lib/math/mp/mp_monty.cpp25
-rw-r--r--src/lib/math/numbertheory/mp_numth.cpp16
-rw-r--r--src/lib/math/numbertheory/powm_mnt.cpp28
-rw-r--r--src/lib/pubkey/curve25519/donna.cpp76
-rw-r--r--src/lib/tls/info.txt1
-rw-r--r--src/lib/tls/msg_client_hello.cpp21
-rw-r--r--src/lib/tls/msg_server_hello.cpp19
-rw-r--r--src/lib/tls/tls_blocking.cpp15
-rw-r--r--src/lib/tls/tls_blocking.h5
-rw-r--r--src/lib/tls/tls_callbacks.h205
-rw-r--r--src/lib/tls/tls_channel.cpp260
-rw-r--r--src/lib/tls/tls_channel.h40
-rw-r--r--src/lib/tls/tls_client.cpp53
-rw-r--r--src/lib/tls/tls_client.h53
-rw-r--r--src/lib/tls/tls_extensions.h2
-rw-r--r--src/lib/tls/tls_handshake_msg.h4
-rw-r--r--src/lib/tls/tls_handshake_state.cpp10
-rw-r--r--src/lib/tls/tls_handshake_state.h14
-rw-r--r--src/lib/tls/tls_messages.h64
-rw-r--r--src/lib/tls/tls_record.cpp153
-rw-r--r--src/lib/tls/tls_record.h87
-rw-r--r--src/lib/tls/tls_server.cpp823
-rw-r--r--src/lib/tls/tls_server.h73
-rw-r--r--src/tests/unit_tls.cpp596
33 files changed, 1829 insertions, 1146 deletions
diff --git a/doc/manual/tls.rst b/doc/manual/tls.rst
index a10a4280c..6c1ca42f2 100644
--- a/doc/manual/tls.rst
+++ b/doc/manual/tls.rst
@@ -22,44 +22,62 @@ instance, by reading from a network socket) it passes that information
to TLS using :cpp:func:`TLS::Channel::received_data`. If the data
passed in results in some change in the state, such as a handshake
completing, or some data or an alert being received from the other
-side, then a user provided callback will be invoked. If the reader is
-familiar with OpenSSL's BIO layer, it might be analagous to saying the
-only way of interacting with Botan's TLS is via a `BIO_mem` I/O
+side, then the appropriate user provided callback will be invoked.
+
+If the reader is familiar with OpenSSL's BIO layer, it might be analagous
+to saying the only way of interacting with Botan's TLS is via a `BIO_mem` I/O
abstraction. This makes the library completely agnostic to how you
write your network layer, be it blocking sockets, libevent, asio, a
-message queue, etc.
+message queue, lwIP on RTOS, some carrier pidgeons, etc.
-The callbacks for TLS have the signatures
+Starting in 1.11.31, the application callbacks are encapsulated as the class
+``TLS::Callbacks`` with the following members. The first four (``tls_emit_data``,
+``tls_record_received``, ``tls_alert``, and ``tls_session_established``) are
+mandatory for using TLS, all others are optional and provide additional
+information about the connection.
- .. cpp:function:: void output_fn(const byte data[], size_t data_len)
+ .. cpp:function:: void tls_emit_data(const byte data[], size_t data_len)
- TLS requests that all bytes of *data* be queued up to send to the
- counterparty. After this function returns, *data* will be
- overwritten, so a copy of the input must be made if the callback
+ Mandatory. The TLS stack requests that all bytes of *data* be queued up to send to the
+ counterparty. After this function returns, the buffer containing *data* will
+ be overwritten, so a copy of the input must be made if the callback
cannot send the data immediately.
- .. cpp:function:: void data_cb(const byte data[], size_t data_len)
+ As an example you could ``send`` to perform a blocking write on a socket,
+ or append the data to a queue managed by your application, and initiate
+ an asyncronous write.
+
+ For TLS all writes must occur *in the order requested*.
+ For DTLS this ordering is not strictly required, but is still recommended.
+
+ .. cpp:function:: void tls_record_received(uint64_t rec_no, const byte data[], size_t data_len)
+
+ Mandatory. Called once for each application_data record which is received, with the
+ matching (TLS level) record sequence number.
+
+ Currently empty records are ignored and do not instigate a callback,
+ but this may change in a future release.
+
+ As with ``tls_emit_data``, the array will be overwritten sometime after
+ the callback returns, so a copy should be made if needed.
- Called whenever application data is received from the other side
- of the connection, in which case *data* and *data_len* specify
- the data received. This array will be overwritten sometime after
- the callback returns, so again a copy should be made if need be.
+ For TLS the record number will always increase.
- .. cpp:function:: void alert_cb(Alert alert, const byte data[], size_t data_len)
+ For DTLS, it is possible to receive records with the `rec_no` field
+ field out of order or repeated. It is even possible (from a malicious or
+ faulty peer) to receive multiple copies of a single record with differing plaintexts.
- Called when an alert is received. Normally, data is null and
- data_len is 0, as most alerts have no associated data. However,
- if TLS heartbeats (see :rfc:`6520`) were negotiated, and we
- initiated a heartbeat, then if/when the other party responds,
- ``alert_cb`` will be called with whatever data was included in
- the heartbeat response (if any) along with a psuedo-alert value
- of ``HEARTBEAT_PAYLOAD``.
+ .. cpp:function:: void tls_alert(Alert alert)
- .. cpp:function:: bool handshake_cb(const TLS::Session& session)
+ Mandatory. Called when an alert is received from the peer. Note that alerts
+ received before the handshake is complete are not authenticated and
+ could have been inserted by a MITM attacker.
+
+ .. cpp:function:: bool tls_session_established(const TLS::Session& session)
- Called whenever a negotiation completes. This can happen more
- than once on any connection. The *session* parameter provides
- information about the session which was established.
+ Mandatory. Called whenever a negotiation completes. This can happen more
+ than once on any connection, if renegotiation occurs. The *session* parameter
+ provides information about the session which was just established.
If this function returns false, the session will not be cached
for later resumption.
@@ -68,8 +86,29 @@ The callbacks for TLS have the signatures
exception which will send a close message to the counterparty and
reset the connection state.
-You can of course use tools like ``std::bind`` to bind additional
-parameters to your callback functions.
+ .. cpp:function:: std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos)
+
+ Optional. Called by the server when a client includes a list of protocols in the ALPN extension.
+ The server then choose which protocol to use, or "" to disable sending any ALPN response.
+ The default implementation returns the empty string all of the time, effectively disabling
+ ALPN responses.
+
+ .. cpp:function:: void tls_inspect_handshake_msg(const Handshake_Message&)
+
+ This callback is optional, and can be used to inspect all handshake messages
+ while the session establishment occurs.
+
+ .. cpp:function:: void tls_log_debug(const char*)
+
+ This callback is for exerimental purposes and currently unused. It may be
+ removed or modified in a future release.
+
+Versions from 1.11.0 to 1.11.30 did not have ``TLS::Callbacks` and instead
+used independent std::functions to pass the various callback functions.
+This interface is currently still included but is deprecated and will be removed
+in a future release. For the documentation for this interface, please check
+the docs for 1.11.30. This version of the manual only documents the new interface
+added in 1.11.31.
TLS Channels
----------------------------------------
@@ -80,16 +119,6 @@ available:
.. cpp:class:: TLS::Channel
- .. cpp:type:: std::function<void (const byte[], size_t)> output_fn
-
- .. cpp:type:: std::function<void (const byte[], size_t)> data_cb
-
- .. cpp:type:: std::function<void (Alert, const byte[], size_t)> alert_cb
-
- .. cpp:type:: std::function<bool (const Session&)> handshake_cb
-
- Typedefs used in the code for the functions described above
-
.. cpp:function:: size_t received_data(const byte buf[], size_t buf_size)
.. cpp:function:: size_t received_data(const std::vector<byte>& buf)
@@ -194,10 +223,7 @@ TLS Clients
.. cpp:class:: TLS::Client
.. cpp:function:: Client( \
- output_fn out, \
- data_cb app_data_cb, \
- alert_cb alert_cb, \
- handshake_cb hs_cb, \
+ Callbacks& callbacks,
Session_Manager& session_manager, \
Credentials_Manager& creds, \
const Policy& policy, \
@@ -211,29 +237,8 @@ TLS Clients
Initialize a new TLS client. The constructor will immediately
initiate a new session.
- The *output_fn* callback will be called with output that
- should be sent to the counterparty. For instance this will be
- called immediately from the constructor after the client hello
- message is constructed. An implementation of *output_fn* is
- allowed to defer the write (for instance if writing when the
- callback occurs would block), but should eventually write the data
- to the counterparty *in order*.
-
- The *data_cb* will be called with data sent by the counterparty
- after it has been processed. The byte array and size_t represent
- the plaintext value and size.
-
- The *alert_cb* will be called when a protocol alert is received,
- commonly with a close alert during connection teardown.
-
- The *handshake_cb* function is called when a handshake
- (either initial or renegotiation) is completed. The return value of
- the callback specifies if the session should be cached for later
- resumption. If the function for some reason desires to prevent the
- connection from completing, it should throw an exception
- (preferably a TLS::Exception, which can provide more specific alert
- information to the counterparty). The :cpp:class:`TLS::Session`
- provides information about the session that was just established.
+ The *callbacks* parameter specifies the various application callbacks
+ which pertain to this particular client connection.
The *session_manager* is an interface for storing TLS sessions,
which allows for session resumption upon reconnecting to a server.
@@ -285,34 +290,30 @@ TLS Servers
.. cpp:class:: TLS::Server
.. cpp:function:: Server( \
- output_fn output, \
- data_cb data_cb, \
- alert_cb alert_cb, \
- handshake_cb handshake_cb, \
+ 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 \
)
-The first 8 arguments as well as the final argument
+The first 5 arguments as well as the final argument
*reserved_io_buffer_size*, are treated similiarly to the :ref:`client
<tls_client>`.
-The (optional) argument, *proto_chooser*, is a function called if the
-client sent the ALPN extension to negotiate an application
-protocol. In that case, the function should choose a protocol to use
-and return it. Alternately it can throw an exception to abort the
-exchange; the ALPN specification says that if this occurs the alert
-should be of type `NO_APPLICATION_PROTOCOL`.
+If a client sends the ALPN extension, the ``callbacks`` function
+``tls_server_choose_app_protocol`` will be called and the result
+sent back to the client. If the empty string is returned, the server
+will not send an ALPN response. The function can also throw an exception
+to abort the handshake entirely, the ALPN specification says that if this
+occurs the alert should be of type `NO_APPLICATION_PROTOCOL`.
The optional argument *is_datagram* specifies if this is a TLS or DTLS
server; unlike clients, which know what type of protocol (TLS vs DTLS)
they are negotiating from the start via the *offer_version*, servers
-would not until they actually received a hello without this parameter.
+would not until they actually received a client hello.
Code for a TLS server using asio is in `src/cli/tls_proxy.cpp`.
@@ -516,7 +517,7 @@ policy settings from a file.
Values without an explicit mode use old-style CBC with HMAC encryption.
Default value: "AES-256/GCM", "AES-128/GCM", "ChaCha20Poly1305",
- "AES-256/CCM", "AES-128/CCM", "AES-256/CCM-8", "AES-128/CCM-8",
+ "AES-256/CCM", "AES-128/CCM", "AES-256/CCM(8)", "AES-128/CCM(8)",
"AES-256", "AES-128"
Also allowed: "Camellia-256/GCM", "Camellia-128/GCM",
@@ -529,15 +530,18 @@ policy settings from a file.
.. note::
- The current ChaCha20Poly1305 ciphersuites are non-standard but
- as of 2015 were implemented and deployed by Google and
- elsewhere. Support will be changed to using IETF standard
- ChaCha20Poly1305 ciphersuites when those are defined.
+ Before 1.11.30 only the non-standard ChaCha20Poly1305 ciphersuite
+ was implemented. The RFC 7905 ciphersuites are supported in 1.11.30
+ onwards.
.. note::
Support for the broken RC4 cipher was removed in 1.11.17
+ .. note::
+
+ SEED and 3DES are deprecated and will be removed in a future release.
+
.. cpp:function:: std::vector<std::string> allowed_macs() const
Returns the list of algorithms we are willing to use for
@@ -577,6 +581,10 @@ policy settings from a file.
Also allowed (disabled by default): "" (meaning anonymous)
+ .. note::
+
+ DSA authentication is deprecated and will be removed in a future release.
+
.. cpp:function:: std::vector<std::string> allowed_ecc_curves() const
Return a list of ECC curves we are willing to use, in order of preference.
diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp
index 6af2f56f8..082daf4ac 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)
*/
@@ -35,7 +36,7 @@
namespace Botan_CLI {
-class TLS_Client final : public Command
+class TLS_Client final : public Command, public Botan::TLS::Callbacks
{
public:
TLS_Client() : Command("tls_client host --port=443 --print-certs --policy= "
@@ -98,15 +99,10 @@ class TLS_Client final : public Command
const std::vector<std::string> protocols_to_offer = Botan::split_on("next-protocols", ',');
- int sockfd = connect_to_host(host, port, use_tcp);
+ m_sockfd = connect_to_host(host, port, use_tcp);
using namespace std::placeholders;
- auto socket_write =
- use_tcp ?
- std::bind(stream_socket_write, sockfd, _1, _2) :
- std::bind(dgram_socket_write, sockfd, _1, _2);
-
auto version = policy->latest_supported_version(!use_tcp);
if(flag_set("tls1.0"))
@@ -118,10 +114,7 @@ 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(*this,
*session_mgr,
creds,
*policy,
@@ -136,7 +129,7 @@ class TLS_Client final : public Command
{
fd_set readfds;
FD_ZERO(&readfds);
- FD_SET(sockfd, &readfds);
+ FD_SET(m_sockfd, &readfds);
if(client.is_active())
{
@@ -152,13 +145,13 @@ class TLS_Client final : public Command
struct timeval timeout = { 1, 0 };
- ::select(sockfd + 1, &readfds, nullptr, nullptr, &timeout);
+ ::select(m_sockfd + 1, &readfds, nullptr, nullptr, &timeout);
- if(FD_ISSET(sockfd, &readfds))
+ if(FD_ISSET(m_sockfd, &readfds))
{
uint8_t buf[4*1024] = { 0 };
- ssize_t got = ::read(sockfd, buf, sizeof(buf));
+ ssize_t got = ::read(m_sockfd, buf, sizeof(buf));
if(got == 0)
{
@@ -216,7 +209,7 @@ class TLS_Client final : public Command
}
}
- ::close(sockfd);
+ ::close(m_sockfd);
}
private:
@@ -256,7 +249,7 @@ class TLS_Client final : public Command
return fd;
}
- bool handshake_complete(const Botan::TLS::Session& session)
+ bool tls_session_established(const Botan::TLS::Session& session) override
{
output() << "Handshake complete, " << session.version().to_string()
<< " using " << session.ciphersuite().to_string() << "\n";
@@ -290,13 +283,13 @@ class TLS_Client final : public Command
throw CLI_Error("Socket write failed errno=" + std::to_string(errno));
}
- static void stream_socket_write(int sockfd, const uint8_t buf[], size_t length)
+ void tls_emit_data(const uint8_t buf[], size_t length) override
{
size_t offset = 0;
while(length)
{
- ssize_t sent = ::send(sockfd, (const char*)buf + offset,
+ ssize_t sent = ::send(m_sockfd, (const char*)buf + offset,
length, MSG_NOSIGNAL);
if(sent == -1)
@@ -312,16 +305,19 @@ class TLS_Client final : public Command
}
}
- void alert_received(Botan::TLS::Alert alert, const uint8_t [], size_t )
+ void tls_alert(Botan::TLS::Alert alert)
{
output() << "Alert: " << alert.type_string() << "\n";
}
- void process_data(const uint8_t buf[], size_t buf_size)
+ void tls_record_received(uint64_t seq_no, const uint8_t buf[], size_t buf_size)
{
for(size_t i = 0; i != buf_size; ++i)
output() << buf[i];
}
+
+ private:
+ int m_sockfd;
};
BOTAN_REGISTER_COMMAND("tls_client", TLS_Client);
diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp
index 2929e473d..5140654de 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)
*/
@@ -59,7 +60,7 @@ void log_text_message(const char* where, const uint8_t buf[], size_t buf_len)
//std::cout << where << ' ' << std::string(c, c + buf_len) << std::endl;
}
-class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_session>
+class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_session>, public Botan::TLS::Callbacks
{
public:
enum { readbuf_size = 4 * 1024 };
@@ -111,10 +112,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
m_server_endpoints(endpoints),
m_client_socket(io),
m_server_socket(io),
- m_tls(boost::bind(&tls_proxy_session::tls_proxy_write_to_client, this, _1, _2),
- boost::bind(&tls_proxy_session::tls_client_write_to_proxy, this, _1, _2),
- boost::bind(&tls_proxy_session::tls_alert_cb, this, _1, _2, _3),
- boost::bind(&tls_proxy_session::tls_handshake_complete, this, _1),
+ m_tls(*this,
session_manager,
credentials,
policy,
@@ -167,7 +165,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
{
m_client_socket.close();
}
- tls_proxy_write_to_client(nullptr, 0); // initiate another write if needed
+ tls_emit_data(nullptr, 0); // initiate another write if needed
}
void handle_server_write_completion(const boost::system::error_code& error)
@@ -183,13 +181,13 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
proxy_write_to_server(nullptr, 0); // initiate another write if needed
}
- void tls_client_write_to_proxy(const uint8_t buf[], size_t buf_len)
+ void tls_record_received(uint64_t /*rec_no*/, const uint8_t buf[], size_t buf_len) override
{
// Immediately bounce message to server
proxy_write_to_server(buf, buf_len);
}
- void tls_proxy_write_to_client(const uint8_t buf[], size_t buf_len)
+ void tls_emit_data(const uint8_t buf[], size_t buf_len) override
{
if(buf_len > 0)
m_p2c_pending.insert(m_p2c_pending.end(), buf, buf + buf_len);
@@ -268,7 +266,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
boost::asio::placeholders::bytes_transferred)));
}
- bool tls_handshake_complete(const Botan::TLS::Session& session)
+ bool tls_session_established(const Botan::TLS::Session& session) override
{
//std::cout << "Handshake from client complete" << std::endl;
@@ -292,7 +290,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
return true;
}
- void tls_alert_cb(Botan::TLS::Alert alert, const uint8_t[], size_t)
+ void tls_alert(Botan::TLS::Alert alert) override
{
if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
{
diff --git a/src/lib/cert/x509/x509_ca.h b/src/lib/cert/x509/x509_ca.h
index 6ea51cd06..ba3724f5e 100644
--- a/src/lib/cert/x509/x509_ca.h
+++ b/src/lib/cert/x509/x509_ca.h
@@ -22,7 +22,6 @@ namespace Botan {
class BOTAN_DLL X509_CA
{
public:
-
/**
* Sign a PKCS#10 Request.
* @param req the request to sign
diff --git a/src/lib/math/bigint/big_ops2.cpp b/src/lib/math/bigint/big_ops2.cpp
index 9a3408247..6e234f036 100644
--- a/src/lib/math/bigint/big_ops2.cpp
+++ b/src/lib/math/bigint/big_ops2.cpp
@@ -1,6 +1,7 @@
/*
* BigInt Assignment Operators
* (C) 1999-2007 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -118,10 +119,7 @@ BigInt& BigInt::operator*=(const BigInt& y)
secure_vector<word> z(data(), data() + x_sw);
secure_vector<word> workspace(size());
-
- bigint_mul(mutable_data(), size(), workspace.data(),
- z.data(), z.size(), x_sw,
- y.data(), y.size(), y_sw);
+ bigint_mul(*this, BigInt(*this), y, workspace.data());
}
return (*this);
diff --git a/src/lib/math/bigint/big_ops3.cpp b/src/lib/math/bigint/big_ops3.cpp
index 6cf837020..24927b4fc 100644
--- a/src/lib/math/bigint/big_ops3.cpp
+++ b/src/lib/math/bigint/big_ops3.cpp
@@ -1,6 +1,7 @@
/*
* BigInt Binary Operators
* (C) 1999-2007 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -93,9 +94,7 @@ BigInt operator*(const BigInt& x, const BigInt& y)
else if(x_sw && y_sw)
{
secure_vector<word> workspace(z.size());
- bigint_mul(z.mutable_data(), z.size(), workspace.data(),
- x.data(), x.size(), x_sw,
- y.data(), y.size(), y_sw);
+ bigint_mul(z, x, y, workspace.data());
}
if(x_sw && y_sw && x.sign() != y.sign())
diff --git a/src/lib/math/ec_gfp/curve_gfp.cpp b/src/lib/math/ec_gfp/curve_gfp.cpp
index 9bf2191c6..96593e601 100644
--- a/src/lib/math/ec_gfp/curve_gfp.cpp
+++ b/src/lib/math/ec_gfp/curve_gfp.cpp
@@ -1,6 +1,7 @@
/*
* Elliptic curves over GF(p) Montgomery Representation
* (C) 2014,2015 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -80,20 +81,14 @@ void CurveGFp_Montgomery::curve_mul(BigInt& z, const BigInt& x, const BigInt& y,
return;
}
- const size_t x_sw = x.sig_words();
- const size_t y_sw = y.sig_words();
-
const size_t output_size = 2*m_p_words + 1;
ws.resize(2*(m_p_words+2));
z.grow_to(output_size);
z.clear();
- bigint_monty_mul(z.mutable_data(), output_size,
- x.data(), x.size(), x_sw,
- y.data(), y.size(), y_sw,
- m_p.data(), m_p_words, m_p_dash,
- ws.data());
+ bigint_monty_mul(z, x, y, m_p.data(), m_p_words, m_p_dash, ws.data());
+
}
void CurveGFp_Montgomery::curve_sqr(BigInt& z, const BigInt& x,
@@ -115,9 +110,7 @@ void CurveGFp_Montgomery::curve_sqr(BigInt& z, const BigInt& x,
z.grow_to(output_size);
z.clear();
- bigint_monty_sqr(z.mutable_data(), output_size,
- x.data(), x.size(), x_sw,
- m_p.data(), m_p_words, m_p_dash,
+ bigint_monty_sqr(z, x, m_p.data(), m_p_words, m_p_dash,
ws.data());
}
@@ -174,9 +167,7 @@ void CurveGFp_NIST::curve_mul(BigInt& z, const BigInt& x, const BigInt& y,
z.grow_to(output_size);
z.clear();
- bigint_mul(z.mutable_data(), output_size, ws.data(),
- x.data(), x.size(), x.sig_words(),
- y.data(), y.size(), y.sig_words());
+ bigint_mul(z, x, y, ws.data());
this->redc(z, ws);
}
diff --git a/src/lib/math/mp/mp_core.h b/src/lib/math/mp/mp_core.h
index 73f13742c..c4ce005ba 100644
--- a/src/lib/math/mp/mp_core.h
+++ b/src/lib/math/mp/mp_core.h
@@ -2,6 +2,7 @@
* MPI Algorithms
* (C) 1999-2010 Jack Lloyd
* 2006 Luca Piccarreta
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -9,6 +10,7 @@
#ifndef BOTAN_MP_CORE_OPS_H__
#define BOTAN_MP_CORE_OPS_H__
+#include <botan/bigint.h>
#include <botan/mp_types.h>
namespace Botan {
@@ -134,17 +136,14 @@ void bigint_monty_redc(word z[],
/*
* Montgomery Multiplication
*/
-void bigint_monty_mul(word z[], size_t z_size,
- const word x[], size_t x_size, size_t x_sw,
- const word y[], size_t y_size, size_t y_sw,
+void bigint_monty_mul(BigInt& z, const BigInt& x, const BigInt& y,
const word p[], size_t p_size, word p_dash,
word workspace[]);
/*
* Montgomery Squaring
*/
-void bigint_monty_sqr(word z[], size_t z_size,
- const word x[], size_t x_size, size_t x_sw,
+void bigint_monty_sqr(BigInt& z, const BigInt& x,
const word p[], size_t p_size, word p_dash,
word workspace[]);
@@ -182,9 +181,7 @@ void bigint_comba_sqr16(word out[32], const word in[16]);
/*
* High Level Multiplication/Squaring Interfaces
*/
-void bigint_mul(word z[], size_t z_size, word workspace[],
- const word x[], size_t x_size, size_t x_sw,
- const word y[], size_t y_size, size_t y_sw);
+void bigint_mul(BigInt& z, const BigInt& x, const BigInt& y, word workspace[]);
void bigint_sqr(word z[], size_t z_size, word workspace[],
const word x[], size_t x_size, size_t x_sw);
diff --git a/src/lib/math/mp/mp_karat.cpp b/src/lib/math/mp/mp_karat.cpp
index 9135fdd6a..7a763e2a9 100644
--- a/src/lib/math/mp/mp_karat.cpp
+++ b/src/lib/math/mp/mp_karat.cpp
@@ -1,6 +1,7 @@
/*
* Multiplication and Squaring
* (C) 1999-2010 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -252,60 +253,55 @@ size_t karatsuba_size(size_t z_size, size_t x_size, size_t x_sw)
/*
* Multiplication Algorithm Dispatcher
*/
-void bigint_mul(word z[], size_t z_size, word workspace[],
- const word x[], size_t x_size, size_t x_sw,
- const word y[], size_t y_size, size_t y_sw)
+void bigint_mul(BigInt& z, const BigInt& x, const BigInt& y, word workspace[])
{
- // checking that z_size >= x_sw + y_sw without overflow
- BOTAN_ASSERT(z_size > x_sw && z_size > y_sw && z_size-x_sw >= y_sw, "Output size is sufficient");
-
- if(x_sw == 1)
+ if(x.sig_words() == 1)
{
- bigint_linmul3(z, y, y_sw, x[0]);
+ bigint_linmul3(z.mutable_data(), y.data(), y.sig_words(), x.data()[0]);
}
- else if(y_sw == 1)
+ else if(y.sig_words() == 1)
{
- bigint_linmul3(z, x, x_sw, y[0]);
+ bigint_linmul3(z.mutable_data(), x.data(), x.sig_words(), y.data()[0]);
}
- else if(x_sw <= 4 && x_size >= 4 &&
- y_sw <= 4 && y_size >= 4 && z_size >= 8)
+ else if(x.sig_words() <= 4 && x.size() >= 4 &&
+ y.sig_words() <= 4 && y.size() >= 4 && z.size() >= 8)
{
- bigint_comba_mul4(z, x, y);
+ bigint_comba_mul4(z.mutable_data(), x.data(), y.data());
}
- else if(x_sw <= 6 && x_size >= 6 &&
- y_sw <= 6 && y_size >= 6 && z_size >= 12)
+ else if(x.sig_words() <= 6 && x.size() >= 6 &&
+ y.sig_words() <= 6 && y.size() >= 6 && z.size() >= 12)
{
- bigint_comba_mul6(z, x, y);
+ bigint_comba_mul6(z.mutable_data(), x.data(), y.data());
}
- else if(x_sw <= 8 && x_size >= 8 &&
- y_sw <= 8 && y_size >= 8 && z_size >= 16)
+ else if(x.sig_words() <= 8 && x.size() >= 8 &&
+ y.sig_words() <= 8 && y.size() >= 8 && z.size() >= 16)
{
- bigint_comba_mul8(z, x, y);
+ bigint_comba_mul8(z.mutable_data(), x.data(), y.data());
}
- else if(x_sw <= 9 && x_size >= 9 &&
- y_sw <= 9 && y_size >= 9 && z_size >= 18)
+ else if(x.sig_words() <= 9 && x.size() >= 9 &&
+ y.sig_words() <= 9 && y.size() >= 9 && z.size() >= 18)
{
- bigint_comba_mul9(z, x, y);
+ bigint_comba_mul9(z.mutable_data(), x.data(), y.data());
}
- else if(x_sw <= 16 && x_size >= 16 &&
- y_sw <= 16 && y_size >= 16 && z_size >= 32)
+ else if(x.sig_words() <= 16 && x.size() >= 16 &&
+ y.sig_words() <= 16 && y.size() >= 16 && z.size() >= 32)
{
- bigint_comba_mul16(z, x, y);
+ bigint_comba_mul16(z.mutable_data(), x.data(), y.data());
}
- else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD ||
- y_sw < KARATSUBA_MULTIPLY_THRESHOLD ||
+ else if(x.sig_words() < KARATSUBA_MULTIPLY_THRESHOLD ||
+ y.sig_words() < KARATSUBA_MULTIPLY_THRESHOLD ||
!workspace)
{
- basecase_mul(z, x, x_sw, y, y_sw);
+ basecase_mul(z.mutable_data(), x.data(), x.sig_words(), y.data(), y.sig_words());
}
else
{
- const size_t N = karatsuba_size(z_size, x_size, x_sw, y_size, y_sw);
+ const size_t N = karatsuba_size(z.size(), x.size(), x.sig_words(), y.size(), y.sig_words());
if(N)
- karatsuba_mul(z, x, y, N, workspace);
+ karatsuba_mul(z.mutable_data(), x.data(), y.data(), N, workspace);
else
- basecase_mul(z, x, x_sw, y, y_sw);
+ basecase_mul(z.mutable_data(), x.data(), x.sig_words(), y.data(), y.sig_words());
}
}
diff --git a/src/lib/math/mp/mp_monty.cpp b/src/lib/math/mp/mp_monty.cpp
index 7e427b540..88b5de715 100644
--- a/src/lib/math/mp/mp_monty.cpp
+++ b/src/lib/math/mp/mp_monty.cpp
@@ -2,10 +2,12 @@
* Montgomery Reduction
* (C) 1999-2011 Jack Lloyd
* 2006 Luca Piccarreta
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
+#include <botan/bigint.h>
#include <botan/internal/mp_core.h>
#include <botan/internal/mp_madd.h>
#include <botan/internal/mp_asmi.h>
@@ -92,30 +94,25 @@ void bigint_monty_redc(word z[],
BOTAN_ASSERT(borrow == 0 || borrow == 1, "Expected borrow");
}
-void bigint_monty_mul(word z[], size_t z_size,
- const word x[], size_t x_size, size_t x_sw,
- const word y[], size_t y_size, size_t y_sw,
+void bigint_monty_mul(BigInt& z, const BigInt& x, const BigInt& y,
const word p[], size_t p_size, word p_dash,
word ws[])
{
- bigint_mul(&z[0], z_size, &ws[0],
- &x[0], x_size, x_sw,
- &y[0], y_size, y_sw);
+ bigint_mul(z, x, y, &ws[0]);
- bigint_monty_redc(&z[0],
+ bigint_monty_redc(z.mutable_data(),
&p[0], p_size, p_dash,
&ws[0]);
+
}
-void bigint_monty_sqr(word z[], size_t z_size,
- const word x[], size_t x_size, size_t x_sw,
- const word p[], size_t p_size, word p_dash,
- word ws[])
+void bigint_monty_sqr(BigInt& z, const BigInt& x, const word p[],
+ size_t p_size, word p_dash, word ws[])
{
- bigint_sqr(&z[0], z_size, &ws[0],
- &x[0], x_size, x_sw);
+ bigint_sqr(z.mutable_data(), z.size(), &ws[0],
+ x.data(), x.size(), x.sig_words());
- bigint_monty_redc(&z[0],
+ bigint_monty_redc(z.mutable_data(),
&p[0], p_size, p_dash,
&ws[0]);
}
diff --git a/src/lib/math/numbertheory/mp_numth.cpp b/src/lib/math/numbertheory/mp_numth.cpp
index 3373b9ee7..d78d21128 100644
--- a/src/lib/math/numbertheory/mp_numth.cpp
+++ b/src/lib/math/numbertheory/mp_numth.cpp
@@ -1,6 +1,7 @@
/*
* Fused and Important MP Algorithms
* (C) 1999-2007 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -40,20 +41,13 @@ BigInt mul_add(const BigInt& a, const BigInt& b, const BigInt& c)
if(a.sign() != b.sign())
sign = BigInt::Negative;
- const size_t a_sw = a.sig_words();
- const size_t b_sw = b.sig_words();
- const size_t c_sw = c.sig_words();
-
- BigInt r(sign, std::max(a.size() + b.size(), c_sw) + 1);
+ BigInt r(sign, std::max(a.size() + b.size(), c.sig_words()) + 1);
secure_vector<word> workspace(r.size());
- bigint_mul(r.mutable_data(), r.size(),
- workspace.data(),
- a.data(), a.size(), a_sw,
- b.data(), b.size(), b_sw);
+ bigint_mul(r, a, b, workspace.data());
- const size_t r_size = std::max(r.sig_words(), c_sw);
- bigint_add2(r.mutable_data(), r_size, c.data(), c_sw);
+ const size_t r_size = std::max(r.sig_words(), c.sig_words());
+ bigint_add2(r.mutable_data(), r_size, c.data(), c.sig_words());
return r;
}
diff --git a/src/lib/math/numbertheory/powm_mnt.cpp b/src/lib/math/numbertheory/powm_mnt.cpp
index 5c441db3a..572f0de98 100644
--- a/src/lib/math/numbertheory/powm_mnt.cpp
+++ b/src/lib/math/numbertheory/powm_mnt.cpp
@@ -1,6 +1,7 @@
/*
* Montgomery Exponentiation
* (C) 1999-2010,2012 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -8,6 +9,7 @@
#include <botan/internal/def_powm.h>
#include <botan/numthry.h>
#include <botan/internal/mp_core.h>
+#include <iostream>
namespace Botan {
@@ -34,36 +36,26 @@ void Montgomery_Exponentiator::set_base(const BigInt& base)
m_g[0] = 1;
- bigint_monty_mul(z.mutable_data(), z.size(),
- m_g[0].data(), m_g[0].size(), m_g[0].sig_words(),
- m_R2_mod.data(), m_R2_mod.size(), m_R2_mod.sig_words(),
+ bigint_monty_mul(z, m_g[0], m_R2_mod,
m_modulus.data(), m_mod_words, m_mod_prime,
workspace.data());
-
m_g[0] = z;
m_g[1] = (base >= m_modulus) ? (base % m_modulus) : base;
- bigint_monty_mul(z.mutable_data(), z.size(),
- m_g[1].data(), m_g[1].size(), m_g[1].sig_words(),
- m_R2_mod.data(), m_R2_mod.size(), m_R2_mod.sig_words(),
+ bigint_monty_mul(z, m_g[1], m_R2_mod,
m_modulus.data(), m_mod_words, m_mod_prime,
workspace.data());
m_g[1] = z;
const BigInt& x = m_g[1];
- const size_t x_sig = x.sig_words();
for(size_t i = 2; i != m_g.size(); ++i)
{
const BigInt& y = m_g[i-1];
- const size_t y_sig = y.sig_words();
- bigint_monty_mul(z.mutable_data(), z.size(),
- x.data(), x.size(), x_sig,
- y.data(), y.size(), y_sig,
- m_modulus.data(), m_mod_words, m_mod_prime,
+ bigint_monty_mul(z, x, y, m_modulus.data(), m_mod_words, m_mod_prime,
workspace.data());
m_g[i] = z;
@@ -82,15 +74,13 @@ BigInt Montgomery_Exponentiator::execute() const
const size_t z_size = 2*(m_mod_words + 1);
BigInt z(BigInt::Positive, z_size);
- secure_vector<word> workspace(z_size);
+ secure_vector<word> workspace(z.size());
for(size_t i = exp_nibbles; i > 0; --i)
{
for(size_t k = 0; k != m_window_bits; ++k)
{
- bigint_monty_sqr(z.mutable_data(), z_size,
- x.data(), x.size(), x.sig_words(),
- m_modulus.data(), m_mod_words, m_mod_prime,
+ bigint_monty_sqr(z, x, m_modulus.data(), m_mod_words, m_mod_prime,
workspace.data());
x = z;
@@ -100,9 +90,7 @@ BigInt Montgomery_Exponentiator::execute() const
const BigInt& y = m_g[nibble];
- bigint_monty_mul(z.mutable_data(), z_size,
- x.data(), x.size(), x.sig_words(),
- y.data(), y.size(), y.sig_words(),
+ bigint_monty_mul(z, x, y,
m_modulus.data(), m_mod_words, m_mod_prime,
workspace.data());
diff --git a/src/lib/pubkey/curve25519/donna.cpp b/src/lib/pubkey/curve25519/donna.cpp
index 9b28e412c..a0e4d249f 100644
--- a/src/lib/pubkey/curve25519/donna.cpp
+++ b/src/lib/pubkey/curve25519/donna.cpp
@@ -39,6 +39,26 @@ typedef byte u8;
typedef u64bit limb;
typedef limb felem[5];
+typedef struct
+ {
+ limb* x;
+ limb* z;
+ } fmonty_pair_t;
+
+typedef struct
+ {
+ fmonty_pair_t q;
+ fmonty_pair_t q_dash;
+ const limb* q_minus_q_dash;
+ } fmonty_in_t;
+
+typedef struct
+ {
+ fmonty_pair_t two_q;
+ fmonty_pair_t q_plus_q_dash;
+ } fmonty_out_t;
+
+
#if !defined(BOTAN_TARGET_HAS_NATIVE_UINT128)
typedef donna128 uint128_t;
#endif
@@ -273,44 +293,41 @@ fcontract(u8 *output, const felem input) {
/* Input: Q, Q', Q-Q'
* Output: 2Q, Q+Q'
*
- * x2 z3: long form
- * x3 z3: long form
- * x z: short form, destroyed
- * xprime zprime: short form, destroyed
- * qmqp: short form, preserved
+ * result.two_q (2*Q): long form
+ * result.q_plus_q_dash (Q + Q): long form
+ * in.q: short form, destroyed
+ * in.q_dash: short form, destroyed
+ * in.q_minus_q_dash: short form, preserved
*/
static void
-fmonty(limb *x2, limb *z2, /* output 2Q */
- limb *x3, limb *z3, /* output Q + Q' */
- limb *x, limb *z, /* input Q */
- limb *xprime, limb *zprime, /* input Q' */
- const limb *qmqp /* input Q - Q' */) {
+fmonty(fmonty_out_t& result, fmonty_in_t& in)
+{
limb origx[5], origxprime[5], zzz[5], xx[5], zz[5], xxprime[5],
- zzprime[5], zzzprime[5];
+ zzprime[5], zzzprime[5];
- copy_mem(origx, x, 5);
- fsum(x, z);
- fdifference_backwards(z, origx); // does x - z
+ copy_mem(origx, in.q.x, 5);
+ fsum(in.q.x, in.q.z);
+ fdifference_backwards(in.q.z, origx); // does x - z
- copy_mem(origxprime, xprime, 5);
- fsum(xprime, zprime);
- fdifference_backwards(zprime, origxprime);
- fmul(xxprime, xprime, z);
- fmul(zzprime, x, zprime);
+ copy_mem(origxprime, in.q_dash.x, 5);
+ fsum(in.q_dash.x, in.q_dash.z);
+ fdifference_backwards(in.q_dash.z, origxprime);
+ fmul(xxprime, in.q_dash.x, in.q.z);
+ fmul(zzprime, in.q.x, in.q_dash.z);
copy_mem(origxprime, xxprime, 5);
fsum(xxprime, zzprime);
fdifference_backwards(zzprime, origxprime);
- fsquare_times(x3, xxprime, 1);
+ fsquare_times(result.q_plus_q_dash.x, xxprime, 1);
fsquare_times(zzzprime, zzprime, 1);
- fmul(z3, zzzprime, qmqp);
+ fmul(result.q_plus_q_dash.z, zzzprime, in.q_minus_q_dash);
- fsquare_times(xx, x, 1);
- fsquare_times(zz, z, 1);
- fmul(x2, xx, zz);
+ fsquare_times(xx, in.q.x, 1);
+ fsquare_times(zz, in.q.z, 1);
+ fmul(result.two_q.x, xx, zz);
fdifference_backwards(zz, xx); // does zz = xx - zz
fscalar_product(zzz, zz, 121665);
fsum(zzz, xx);
- fmul(z2, zz, zzz);
+ fmul(result.two_q.z, zz, zzz);
}
// -----------------------------------------------------------------------------
@@ -356,11 +373,10 @@ cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
swap_conditional(nqx, nqpqx, bit);
swap_conditional(nqz, nqpqz, bit);
- fmonty(nqx2, nqz2,
- nqpqx2, nqpqz2,
- nqx, nqz,
- nqpqx, nqpqz,
- q);
+
+ fmonty_out_t result { nqx2, nqz2, nqpqx2, nqpqz2 };
+ fmonty_in_t in { nqx, nqz, nqpqx, nqpqz, q };
+ fmonty(result, in);
swap_conditional(nqx2, nqpqx2, bit);
swap_conditional(nqz2, nqpqz2, bit);
diff --git a/src/lib/tls/info.txt b/src/lib/tls/info.txt
index b26179226..667726318 100644
--- a/src/lib/tls/info.txt
+++ b/src/lib/tls/info.txt
@@ -6,6 +6,7 @@ load_on auto
credentials_manager.h
tls_alert.h
tls_blocking.h
+tls_callbacks.h
tls_channel.h
tls_ciphersuite.h
tls_client.h
diff --git a/src/lib/tls/msg_client_hello.cpp b/src/lib/tls/msg_client_hello.cpp
index 23807215f..41a6f5c02 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)
*/
@@ -68,22 +69,20 @@ std::vector<byte> Hello_Request::serialize() const
*/
Client_Hello::Client_Hello(Handshake_IO& io,
Handshake_Hash& hash,
- Protocol_Version version,
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 +97,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,10 +105,10 @@ 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));
diff --git a/src/lib/tls/msg_server_hello.cpp b/src/lib/tls/msg_server_hello.cpp
index f8d0c63c7..f32625508 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)
*/
@@ -23,17 +24,13 @@ Server_Hello::Server_Hello(Handshake_IO& io,
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 +38,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())
diff --git a/src/lib/tls/tls_blocking.cpp b/src/lib/tls/tls_blocking.cpp
index a1867b6b5..34ebc6c91 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)
*/
@@ -23,10 +24,13 @@ Blocking_Client::Blocking_Client(read_fn reader,
const Protocol_Version& offer_version,
const std::vector<std::string>& next) :
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_callbacks(new TLS::Compat_Callbacks(
+ writer,
+ std::bind(&Blocking_Client::data_cb, this, _1, _2),
+ std::function<void (Alert)>(std::bind(&Blocking_Client::alert_cb, this, _1)),
+ std::bind(&Blocking_Client::handshake_cb, this, _1)
+ )),
+ m_channel(*m_callbacks.get(),
session_manager,
creds,
policy,
@@ -35,6 +39,7 @@ Blocking_Client::Blocking_Client(read_fn reader,
offer_version,
next)
{
+ printf("hi\n");
}
bool Blocking_Client::handshake_cb(const Session& session)
@@ -42,7 +47,7 @@ bool Blocking_Client::handshake_cb(const Session& session)
return this->handshake_complete(session);
}
-void Blocking_Client::alert_cb(const Alert& alert, const byte[], size_t)
+void Blocking_Client::alert_cb(const Alert& alert)
{
this->alert_notification(alert);
}
diff --git a/src/lib/tls/tls_blocking.h b/src/lib/tls/tls_blocking.h
index 00e65cbaf..0f2986710 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)
*/
@@ -32,6 +33,7 @@ class BOTAN_DLL Blocking_Client
typedef std::function<size_t (byte[], size_t)> read_fn;
typedef std::function<void (const byte[], size_t)> write_fn;
+ BOTAN_DEPRECATED("Use the regular TLS::Client interface")
Blocking_Client(read_fn reader,
write_fn writer,
Session_Manager& session_manager,
@@ -89,9 +91,10 @@ class BOTAN_DLL Blocking_Client
void data_cb(const byte data[], size_t data_len);
- void alert_cb(const Alert& alert, const byte data[], size_t data_len);
+ void alert_cb(const Alert& alert);
read_fn m_read;
+ std::unique_ptr<Compat_Callbacks> m_callbacks;
TLS::Client m_channel;
secure_vector<byte> m_plaintext;
};
diff --git a/src/lib/tls/tls_callbacks.h b/src/lib/tls/tls_callbacks.h
new file mode 100644
index 000000000..d7a68da31
--- /dev/null
+++ b/src/lib/tls/tls_callbacks.h
@@ -0,0 +1,205 @@
+/*
+* TLS Callbacks
+* (C) 2016 Matthias Gierlings
+* 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_TLS_CALLBACKS_H__
+#define BOTAN_TLS_CALLBACKS_H__
+
+#include <botan/tls_session.h>
+#include <botan/tls_alert.h>
+namespace Botan {
+
+namespace TLS {
+
+class Handshake_Message;
+
+/**
+* Encapsulates the callbacks that a TLS channel will make which are due to
+* channel specific operations.
+*/
+class BOTAN_DLL Callbacks
+ {
+ public:
+ virtual ~Callbacks() {}
+
+ /**
+ * Mandatory callback: output function
+ * The channel will call this with data which needs to be sent to the peer
+ * (eg, over a socket or some other form of IPC). The array will be overwritten
+ * when the function returns so a copy must be made if the data cannot be
+ * sent immediately.
+ *
+ * @param data the vector of data to send
+ *
+ * @param size the number of bytes to send
+ */
+ virtual void tls_emit_data(const uint8_t data[], size_t size) = 0;
+
+ /**
+ * Mandatory callback: process application data
+ * Called when application data record is received from the peer.
+ * Again the array is overwritten immediately after the function returns.
+ *
+ * @param seq_no the underlying TLS/DTLS record sequence number
+ *
+ * @param data the vector containing the received record
+ *
+ * @param size the length of the received record, in bytes
+ */
+ virtual void tls_record_received(u64bit seq_no, const uint8_t data[], size_t size) = 0;
+
+ /**
+ * Mandary callback: alert received
+ * Called when an alert is received from the peer
+ * If fatal, the connection is closing. If not fatal, the connection may
+ * still be closing (depending on the error and the peer).
+ *
+ * @param alert the source of the alert
+ */
+ virtual void tls_alert(Alert alert) = 0;
+
+ /**
+ * Mandatory callback: session established
+ * Called when a session is established. Throw an exception to abort
+ * the connection.
+ *
+ * @param session the session descriptor
+ *
+ * @return return false to prevent the session from being cached,
+ * return true to cache the session in the configured session manager
+ */
+ virtual bool tls_session_established(const Session& session) = 0;
+
+ /**
+ * Optional callback: inspect handshake message
+ * Throw an exception to abort the handshake.
+ *
+ * @param message the handshake message
+ */
+ virtual void tls_inspect_handshake_msg(const Handshake_Message& message) {}
+
+ /**
+ * Optional callback for server: choose ALPN protocol
+ * ALPN (RFC 7301) works by the client sending a list of application
+ * protocols it is willing to negotiate. The server then selects which
+ * protocol to use, which is not necessarily even on the list that
+ * the client sent.
+ *
+ * @param client_protos the vector of protocols the client is willing to negotiate
+ *
+ * @return the protocol selected by the server, which need not be on the
+ * list that the client sent; if this is the empty string, the server ignores the
+ * client ALPN extension
+ */
+ virtual std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos)
+ {
+ return "";
+ }
+
+ /**
+ * Optional callback: debug logging. (not currently used)
+ */
+ virtual bool tls_log_debug(const char*) { return false; }
+ };
+
+/**
+* TLS::Callbacks using std::function for compatability with the old API signatures.
+* This type is only provided for backward compatibility.
+* New implementations should derive from TLS::Callbacks instead.
+*/
+class BOTAN_DLL Compat_Callbacks final : public 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;
+ typedef std::function<std::string (std::vector<std::string>)> next_protocol_fn;
+
+ /**
+ * @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
+ */
+ BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).")
+ Compat_Callbacks(output_fn out, data_cb app_data_cb, alert_cb alert_cb,
+ handshake_cb hs_cb, handshake_msg_cb hs_msg_cb = nullptr,
+ next_protocol_fn next_proto = nullptr)
+ : m_output_function(out), m_app_data_cb(app_data_cb),
+ m_alert_cb(std::bind(alert_cb, std::placeholders::_1, nullptr, 0)),
+ m_hs_cb(hs_cb), m_hs_msg_cb(hs_msg_cb), m_next_proto(next_proto) {}
+
+ BOTAN_DEPRECATED("Use TLS::Callbacks (virtual interface).")
+ Compat_Callbacks(output_fn out, data_cb app_data_cb,
+ std::function<void (Alert)> alert_cb,
+ handshake_cb hs_cb,
+ handshake_msg_cb hs_msg_cb = nullptr,
+ next_protocol_fn next_proto = nullptr)
+ : 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), m_next_proto(next_proto) {}
+
+ void tls_emit_data(const byte data[], size_t size) override
+ {
+ BOTAN_ASSERT(m_output_function != nullptr,
+ "Invalid TLS output function callback.");
+ m_output_function(data, size);
+ }
+
+ void tls_record_received(u64bit /*seq_no*/, const byte data[], size_t size) override
+ {
+ BOTAN_ASSERT(m_app_data_cb != nullptr,
+ "Invalid TLS app data callback.");
+ m_app_data_cb(data, size);
+ }
+
+ void tls_alert(Alert alert) override
+ {
+ BOTAN_ASSERT(m_alert_cb != nullptr,
+ "Invalid TLS alert callback.");
+ m_alert_cb(alert);
+ }
+
+ bool tls_session_established(const Session& session) override
+ {
+ BOTAN_ASSERT(m_hs_cb != nullptr,
+ "Invalid TLS handshake callback.");
+ return m_hs_cb(session);
+ }
+
+ std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos) override
+ {
+ if(m_next_proto != nullptr) { return m_next_proto(client_protos); }
+ return "";
+ }
+
+ void tls_inspect_handshake_msg(const Handshake_Message& hmsg) override
+ {
+ // The handshake message callback is optional so we can
+ // not assume it has been set.
+ if(m_hs_msg_cb != nullptr) { m_hs_msg_cb(hmsg); }
+ }
+
+ private:
+ const output_fn m_output_function;
+ const data_cb m_app_data_cb;
+ const std::function<void (Alert)> m_alert_cb;
+ const handshake_cb m_hs_cb;
+ const handshake_msg_cb m_hs_msg_cb;
+ const next_protocol_fn m_next_proto;
+ };
+
+}
+
+}
+
+#endif
diff --git a/src/lib/tls/tls_channel.cpp b/src/lib/tls/tls_channel.cpp
index f445eef99..5f882af64 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
+* (C) 2011,2012,2014,2015,2016 Jack Lloyd
+* 2016 Matthias Gierlings
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -18,32 +19,51 @@ 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(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)
{
+ init(reserved_io_buffer_size);
+ }
+
+Channel::Channel(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,
+ RandomNumberGenerator& rng,
+ const Policy& policy,
+ bool is_datagram,
+ size_t io_buf_sz) :
+ m_is_datagram(is_datagram),
+ m_compat_callbacks(new Compat_Callbacks(out, app_data_cb, alert_cb, hs_cb, hs_msg_cb)),
+ m_callbacks(*m_compat_callbacks.get()),
+ m_session_manager(session_manager),
+ m_policy(policy),
+ m_rng(rng)
+ {
+ init(io_buf_sz);
+ }
+
+void Channel::init(size_t io_buf_sz)
+ {
/* epoch 0 is plaintext, thus null cipher state */
m_write_cipher_states[0] = nullptr;
m_read_cipher_states[0] = nullptr;
- m_writebuf.reserve(reserved_io_buffer_size);
- m_readbuf.reserve(reserved_io_buffer_size);
+ m_writebuf.reserve(io_buf_sz);
+ m_readbuf.reserve(io_buf_sz);
}
void Channel::reset_state()
@@ -263,23 +283,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 +314,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_sequence, 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 +360,108 @@ size_t Channel::received_data(const byte input[], size_t input_size)
}
}
+void Channel::process_handshake_ccs(const 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(u64bit seq_no, const 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)
+ callbacks().tls_record_received(seq_no, record.data(), record.size());
+ }
+
+void Channel::process_alert(const secure_vector<byte>& record)
+ {
+ Alert alert_msg(record);
+
+ if(alert_msg.type() == Alert::NO_RENEGOTIATION)
+ m_pending_state.reset();
+
+ callbacks().tls_alert(alert_msg);
+
+ 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 +470,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());
+ callbacks().tls_emit_data(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..073af760f 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)
*/
@@ -12,6 +13,7 @@
#include <botan/tls_session.h>
#include <botan/tls_alert.h>
#include <botan/tls_session_manager.h>
+#include <botan/tls_callbacks.h>
#include <botan/x509cert.h>
#include <vector>
#include <string>
@@ -37,7 +39,20 @@ class BOTAN_DLL Channel
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;
+ static size_t IO_BUF_DEFAULT_SIZE;
+ Channel(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ RandomNumberGenerator& rng,
+ const Policy& policy,
+ bool is_datagram,
+ size_t io_buf_sz = IO_BUF_DEFAULT_SIZE);
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ */
+ BOTAN_DEPRECATED("Use TLS::Channel(TLS::Callbacks ...)")
Channel(output_fn out,
data_cb app_data_cb,
alert_cb alert_cb,
@@ -47,7 +62,7 @@ class BOTAN_DLL Channel
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,10 +215,12 @@ 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 callbacks().tls_session_established(session); }
- handshake_msg_cb get_handshake_msg_cb() const { return m_handshake_msg_cb; }
+ Callbacks& callbacks() const { return m_callbacks; }
private:
+ void init(size_t io_buf_sze);
+
void send_record(byte record_type, const std::vector<byte>& record);
void send_record_under_epoch(u16bit epoch, byte record_type,
@@ -227,14 +244,21 @@ 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(const secure_vector<byte>& record,
+ u64bit record_sequence,
+ Record_Type record_type,
+ Protocol_Version record_version);
+
+ void process_application_data(u64bit req_no, const secure_vector<byte>& record);
+
+ void process_alert(const 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;
+ std::unique_ptr<Compat_Callbacks> m_compat_callbacks;
+ Callbacks& m_callbacks;
/* external state */
Session_Manager& m_session_manager;
diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp
index 301c77c6b..f77521c03 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)
*/
@@ -23,7 +24,7 @@ class Client_Handshake_State : public Handshake_State
public:
// using Handshake_State::Handshake_State;
- Client_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State(io, cb) {}
+ Client_Handshake_State(Handshake_IO* io, Callbacks& cb) : Handshake_State(io, cb) {}
const Public_Key& get_server_public_Key() const
{
@@ -42,6 +43,23 @@ class Client_Handshake_State : public Handshake_State
/*
* TLS Client Constructor
*/
+Client::Client(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,
+ size_t io_buf_sz) :
+ Channel(callbacks, session_manager, rng, policy, offer_version.is_datagram_protocol(),
+ io_buf_sz),
+ m_creds(creds),
+ m_info(info)
+ {
+ init(offer_version, next_protos);
+ }
+
Client::Client(output_fn output_fn,
data_cb proc_cb,
alert_cb alert_cb,
@@ -59,10 +77,7 @@ Client::Client(output_fn output_fn,
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);
+ init(offer_version, next_protos);
}
Client::Client(output_fn output_fn,
@@ -82,15 +97,22 @@ Client::Client(output_fn output_fn,
m_creds(creds),
m_info(info)
{
+ init(offer_version, next_protos);
+ }
+
+void Client::init(const Protocol_Version& protocol_version,
+ const std::vector<std::string>& next_protocols)
+ {
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(protocol_version);
+ send_client_hello(state, false, protocol_version,
+ srp_identifier, next_protocols);
}
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, callbacks());
}
std::vector<X509_Certificate>
@@ -145,16 +167,15 @@ void Client::send_client_hello(Handshake_State& state_base,
if(!state.client_hello()) // not resuming
{
+ Client_Hello::Settings client_settings(version, m_info.hostname(), srp_identifier);
state.client_hello(new Client_Hello(
state.handshake_io(),
state.hash(),
- version,
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 +440,9 @@ 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)
- );
+ state.client_certs(new Certificate(state.handshake_io(),
+ state.hash(),
+ client_certs));
}
state.client_kex(
diff --git a/src/lib/tls/tls_client.h b/src/lib/tls/tls_client.h
index 45a741878..09af053af 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)
*/
@@ -22,9 +23,49 @@ namespace TLS {
class BOTAN_DLL Client final : public Channel
{
public:
+
/**
* Set up a new TLS client session
*
+ * @param callbacks contains a set of callback function references
+ * required by the TLS client.
+ *
+ * @param session_manager manages session state
+ *
+ * @param creds manages application/user credentials
+ *
+ * @param policy specifies other connection policy information
+ *
+ * @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 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(Callbacks& callbacks,
+ 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 = TLS::Client::IO_BUF_DEFAULT_SIZE
+ );
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new code.
+ *
+ * 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
@@ -52,7 +93,7 @@ class BOTAN_DLL Client final : public Channel
* be preallocated for the read and write buffers. Smaller
* values just mean reallocations and copies are more likely.
*/
-
+ BOTAN_DEPRECATED("Use TLS::Client(TLS::Callbacks ...)")
Client(output_fn out,
data_cb app_data_cb,
alert_cb alert_cb,
@@ -64,9 +105,14 @@ class BOTAN_DLL Client final : public Channel
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
+ size_t reserved_io_buffer_size = TLS::Client::IO_BUF_DEFAULT_SIZE
);
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ */
+ BOTAN_DEPRECATED("Use TLS::Client(TLS::Callbacks ...)")
Client(output_fn out,
data_cb app_data_cb,
alert_cb alert_cb,
@@ -83,6 +129,9 @@ class BOTAN_DLL Client final : public Channel
const std::string& application_protocol() const { return m_application_protocol; }
private:
+ void init(const Protocol_Version& protocol_version,
+ const std::vector<std::string>& next_protocols);
+
std::vector<X509_Certificate>
get_peer_cert_chain(const Handshake_State& state) const override;
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..618ae8d76 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)
*/
@@ -16,6 +17,9 @@ namespace Botan {
namespace TLS {
+class Handshake_IO;
+class Handshake_Hash;
+
/**
* TLS Handshake Message Base Class
*/
diff --git a/src/lib/tls/tls_handshake_state.cpp b/src/lib/tls/tls_handshake_state.cpp
index afc32ba87..71cacdabd 100644
--- a/src/lib/tls/tls_handshake_state.cpp
+++ b/src/lib/tls/tls_handshake_state.cpp
@@ -8,6 +8,7 @@
#include <botan/internal/tls_handshake_state.h>
#include <botan/internal/tls_messages.h>
#include <botan/internal/tls_record.h>
+#include <botan/tls_callbacks.h>
namespace Botan {
@@ -174,8 +175,8 @@ std::string handshake_mask_to_string(u32bit mask)
/*
* Initialize the SSL/TLS Handshake State
*/
-Handshake_State::Handshake_State(Handshake_IO* io, handshake_msg_cb cb) :
- m_msg_callback(cb),
+Handshake_State::Handshake_State(Handshake_IO* io, Callbacks& cb) :
+ m_callbacks(cb),
m_handshake_io(io),
m_version(m_handshake_io->initial_record_version())
{
@@ -183,6 +184,11 @@ Handshake_State::Handshake_State(Handshake_IO* io, handshake_msg_cb cb) :
Handshake_State::~Handshake_State() {}
+void Handshake_State::note_message(const Handshake_Message& msg)
+ {
+ m_callbacks.tls_inspect_handshake_msg(msg);
+ }
+
void Handshake_State::hello_verify_request(const Hello_Verify_Request& hello_verify)
{
note_message(hello_verify);
diff --git a/src/lib/tls/tls_handshake_state.h b/src/lib/tls/tls_handshake_state.h
index 2943a8637..bdec10d14 100644
--- a/src/lib/tls/tls_handshake_state.h
+++ b/src/lib/tls/tls_handshake_state.h
@@ -24,6 +24,7 @@ class KDF;
namespace TLS {
+class Callbacks;
class Policy;
class Hello_Verify_Request;
@@ -45,9 +46,7 @@ class Finished;
class Handshake_State
{
public:
- typedef std::function<void (const Handshake_Message&)> handshake_msg_cb;
-
- Handshake_State(Handshake_IO* io, handshake_msg_cb cb);
+ Handshake_State(Handshake_IO* io, Callbacks& callbacks);
virtual ~Handshake_State();
@@ -164,15 +163,10 @@ class Handshake_State
const Handshake_Hash& hash() const { return m_handshake_hash; }
- void note_message(const Handshake_Message& msg)
- {
- if(m_msg_callback)
- m_msg_callback(msg);
- }
-
+ void note_message(const Handshake_Message& msg);
private:
- handshake_msg_cb m_msg_callback;
+ Callbacks& m_callbacks;
std::unique_ptr<Handshake_IO> m_handshake_io;
diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h
index 3bee89e13..47ff7d3d8 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; }
@@ -162,13 +183,11 @@ class Client_Hello final : public Handshake_Message
Client_Hello(Handshake_IO& io,
Handshake_Hash& hash,
- Protocol_Version version,
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,
@@ -199,6 +218,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; }
@@ -262,12 +310,8 @@ class Server_Hello final : public Handshake_Message
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,
diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp
index eacf313a8..e028c43a0 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],
- readbuf[DTLS_HEADER_SIZE-1]);
+ 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..c16b919b5 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,80 @@ 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; }
+
+ 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 +178,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 +192,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..2e546eab1 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,30 +10,41 @@
#include <botan/internal/tls_handshake_state.h>
#include <botan/internal/tls_messages.h>
#include <botan/internal/stl_util.h>
+#include <botan/tls_magic.h>
namespace Botan {
namespace TLS {
-namespace {
-
class Server_Handshake_State : public Handshake_State
{
public:
- // using Handshake_State::Handshake_State;
+ Server_Handshake_State(Handshake_IO* io, Callbacks& 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; }
- Server_Handshake_State(Handshake_IO* io, handshake_msg_cb cb) : Handshake_State(io, cb) {}
+ private:
// Used by the server only, in case of RSA key exchange. Not owned
- Private_Key* server_rsa_kex_key = nullptr;
+ 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 allow_session_resumption = true;
+ bool m_allow_session_resumption = true;
};
+namespace {
+
bool check_for_resume(Session& session_info,
Session_Manager& session_manager,
Credentials_Manager& credentials,
@@ -225,6 +237,19 @@ get_server_certs(const std::string& hostname,
/*
* TLS Server Constructor
*/
+Server::Server(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool is_datagram,
+ size_t io_buf_sz) :
+ Channel(callbacks, session_manager, rng, policy,
+ is_datagram, io_buf_sz),
+ m_creds(creds)
+ {
+ }
+
Server::Server(output_fn output,
data_cb data_cb,
alert_cb alert_cb,
@@ -236,13 +261,15 @@ 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),
+ 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,
@@ -263,8 +290,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()));
+ std::unique_ptr<Handshake_State> state(new Server_Handshake_State(io, callbacks()));
state->set_expected_next(CLIENT_HELLO);
return state.release();
@@ -284,441 +310,516 @@ 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(...) {}
+
+ m_next_protocol = "";
+ if(pending_state.client_hello()->supports_alpn())
+ {
+ m_next_protocol = callbacks().tls_server_choose_app_protocol(pending_state.client_hello()->next_protocols());
- if(state.client_hello()->sent_fallback_scsv())
+ // if the callback return was empty, fall back to the (deprecated) std::function
+ if(m_next_protocol.empty() && m_choose_next_protocol)
{
- if(latest_supported > client_version)
- throw TLS_Exception(Alert::INAPPROPRIATE_FALLBACK,
- "Client signalled fallback SCSV, possible attack");
+ 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))
- {
- 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())
- );
- }
- }
+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 );
+}
- if(state.server_hello()->supports_session_ticket() && !state.new_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())
- );
- }
- }
+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 );
- state.handshake_io().send(Change_Cipher_Spec());
+ pending_state.client_finished ( new Finished ( contents ) );
- change_cipher_spec_writer(SERVER);
+ if ( !pending_state.client_finished()->verify ( pending_state, CLIENT ) )
+ throw TLS_Exception ( Alert::DECRYPT_ERROR,
+ "Finished message didn't verify" );
- 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;
+ if ( !pending_state.server_finished() )
+ {
+ // already sent finished if resuming, so this is a new session
- const std::string sni_hostname = state.client_hello()->sni_hostname();
+ pending_state.hash().update ( pending_state.handshake_io().format ( contents, type ) );
- cert_chains = get_server_certs(sni_hostname, m_creds);
+ Session session_info(
+ pending_state.server_hello()->session_id(),
+ pending_state.session_keys().master_secret(),
+ pending_state.server_hello()->version(),
+ pending_state.server_hello()->ciphersuite(),
+ pending_state.server_hello()->compression_method(),
+ SERVER,
+ pending_state.server_hello()->supports_extended_master_secret(),
+ get_peer_cert_chain ( pending_state ),
+ std::vector<byte>(),
+ Server_Information(pending_state.client_hello()->sni_hostname()),
+ pending_state.srp_identifier(),
+ pending_state.server_hello()->srtp_profile()
+ );
- if(sni_hostname != "" && cert_chains.empty())
+ if ( save_session ( session_info ) )
{
- 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));
+ 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 );
}
- 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)
+ if ( !pending_state.new_session_ticket() &&
+ pending_state.server_hello()->supports_session_ticket() )
+ {
+ pending_state.new_session_ticket (
+ new New_Session_Ticket ( pending_state.handshake_io(), pending_state.hash() )
);
+ }
- secure_renegotiation_check(state.server_hello());
+ pending_state.handshake_io().send ( Change_Cipher_Spec() );
- const std::string sig_algo = state.ciphersuite().sig_algo();
- const std::string kex_algo = state.ciphersuite().kex_algo();
+ change_cipher_spec_writer ( SERVER );
- if(sig_algo != "")
- {
- BOTAN_ASSERT(!cert_chains[sig_algo].empty(),
- "Attempting to send empty certificate chain");
+ pending_state.server_finished ( new Finished ( pending_state.handshake_io(), pending_state, SERVER ) );
+ }
- state.server_certs(new Certificate(state.handshake_io(),
- state.hash(),
- cert_chains[sig_algo]));
- }
+ activate_session();
- Private_Key* private_key = nullptr;
+}
- if(kex_algo == "RSA" || sig_algo != "")
- {
- private_key = m_creds.private_key_for(
- state.server_certs()->cert_chain()[0],
- "tls-server",
- sni_hostname);
+/*
+* 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);
- if(!private_key)
- throw Internal_Error("No private key located for associated server cert");
- }
+ /*
+ * 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));
+ }
- if(kex_algo == "RSA")
- {
- state.server_rsa_kex_key = private_key;
- }
- else
- {
- state.server_kex(new Server_Key_Exchange(state.handshake_io(),
- state, policy(),
- m_creds, rng(), private_key));
- }
+ switch(type)
+ {
+ case CLIENT_HELLO:
+ this->process_client_hello_msg(active_state, state, contents);
+ break;
- auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
+ case CERTIFICATE:
+ this->process_certificate_msg(state, contents);
+ break;
- std::vector<X509_DN> client_auth_CAs;
+ case CLIENT_KEX:
+ this->process_client_key_exchange_msg(state, contents);
+ break;
+
+ case CERTIFICATE_VERIFY:
+ this->process_certificate_verify_msg(state, type, contents);
+ break;
+
+ case HANDSHAKE_CCS:
+ this->process_change_cipher_spec_msg(state);
+ break;
+
+ case FINISHED:
+ this->process_finished_msg(state, type, contents);
+ break;
+
+ default:
+ throw Unexpected_Message("Unknown handshake message received");
+ break;
+ }
+ }
- for(auto store : trusted_CAs)
+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);
+
+ pending_state.server_hello(new Server_Hello(
+ pending_state.handshake_io(),
+ pending_state.hash(),
+ 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(pending_state.server_hello()->supports_session_ticket()) // send an empty ticket
{
- auto subjects = store->all_subjects();
- client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(),
+ pending_state.hash())
+ );
}
+ }
- if(!client_auth_CAs.empty() && state.ciphersuite().sig_algo() != "")
+ if(pending_state.server_hello()->supports_session_ticket() && !pending_state.new_session_ticket())
+ {
+ try
{
- state.cert_req(
- new Certificate_Req(state.handshake_io(), state.hash(),
- policy(), client_auth_CAs, state.version()));
+ const SymmetricKey ticket_key = m_creds.psk("tls-server", "session-ticket", "");
- state.set_expected_next(CERTIFICATE);
+ 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(...) {}
- /*
- * 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);
-
- state.server_hello_done(new Server_Hello_Done(state.handshake_io(), state.hash()));
+ if(!pending_state.new_session_ticket())
+ {
+ pending_state.new_session_ticket(
+ new New_Session_Ticket(pending_state.handshake_io(), pending_state.hash())
+ );
+ }
}
- }
- else if(type == CERTIFICATE)
- {
- state.client_certs(new Certificate(contents));
- state.set_expected_next(CLIENT_KEX);
- }
- 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);
+ pending_state.handshake_io().send(Change_Cipher_Spec());
- state.client_kex(
- new Client_Key_Exchange(contents, state,
- state.server_rsa_kex_key,
- m_creds, policy(), rng())
- );
+ change_cipher_spec_writer(SERVER);
- state.compute_session_keys();
- }
- else if(type == CERTIFICATE_VERIFY)
- {
- state.client_verify(new Certificate_Verify(contents, state.version()));
+ pending_state.server_finished(new Finished(pending_state.handshake_io(), pending_state, SERVER));
+ pending_state.set_expected_next(HANDSHAKE_CCS);
+ }
+
+void Server::session_create(Server_Handshake_State& pending_state,
+ bool have_session_ticket_key)
+ {
+ std::map<std::string, std::vector<X509_Certificate> > cert_chains;
- const std::vector<X509_Certificate>& client_certs =
- state.client_certs()->cert_chain();
+ const std::string sni_hostname = pending_state.client_hello()->sni_hostname();
- const bool sig_valid =
- state.client_verify()->verify(client_certs[0], state, policy());
+ cert_chains = get_server_certs(sni_hostname, m_creds);
- state.hash().update(state.handshake_io().format(contents, type));
+ if(sni_hostname != "" && cert_chains.empty())
+ {
+ cert_chains = get_server_certs("", m_creds);
/*
- * 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, ..."
+ * 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(!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());
- }
-
- state.set_expected_next(HANDSHAKE_CCS);
+ if(!cert_chains.empty())
+ send_alert(Alert(Alert::UNRECOGNIZED_NAME));
}
- else if(type == HANDSHAKE_CCS)
+
+ 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);
+
+ pending_state.server_hello(new Server_Hello(
+ pending_state.handshake_io(),
+ pending_state.hash(),
+ 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.set_expected_next(FINISHED);
- change_cipher_spec_reader(SERVER);
+ BOTAN_ASSERT(!cert_chains[sig_algo].empty(),
+ "Attempting to send empty certificate chain");
+
+ pending_state.server_certs(new Certificate(pending_state.handshake_io(),
+ pending_state.hash(),
+ cert_chains[sig_algo]));
}
- else if(type == FINISHED)
- {
- state.set_expected_next(HANDSHAKE_NONE);
- state.client_finished(new Finished(contents));
+ Private_Key* private_key = nullptr;
- if(!state.client_finished()->verify(state, CLIENT))
- throw TLS_Exception(Alert::DECRYPT_ERROR,
- "Finished message didn't verify");
+ if(kex_algo == "RSA" || sig_algo != "")
+ {
+ private_key = m_creds.private_key_for(
+ pending_state.server_certs()->cert_chain()[0],
+ "tls-server",
+ sni_hostname);
- if(!state.server_finished())
- {
- // already sent finished if resuming, so this is a new session
+ if(!private_key)
+ throw Internal_Error("No private key located for associated server cert");
+ }
- state.hash().update(state.handshake_io().format(contents, type));
+ if(kex_algo == "RSA")
+ {
+ pending_state.set_server_rsa_kex_key(private_key);
+ }
+ else
+ {
- 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()
- );
+ pending_state.server_kex(new Server_Key_Exchange(pending_state.handshake_io(),
+ pending_state, policy(),
+ m_creds, rng(), private_key));
+ }
- 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);
- }
+ auto trusted_CAs = m_creds.trusted_certificate_authorities("tls-server", sni_hostname);
- if(!state.new_session_ticket() &&
- state.server_hello()->supports_session_ticket())
- {
- state.new_session_ticket(
- new New_Session_Ticket(state.handshake_io(), state.hash())
- );
- }
+ std::vector<X509_DN> client_auth_CAs;
- state.handshake_io().send(Change_Cipher_Spec());
+ for(auto store : trusted_CAs)
+ {
+ auto subjects = store->all_subjects();
+ client_auth_CAs.insert(client_auth_CAs.end(), subjects.begin(), subjects.end());
+ }
- change_cipher_spec_writer(SERVER);
+ if(!client_auth_CAs.empty() && pending_state.ciphersuite().sig_algo() != "")
+ {
+ pending_state.cert_req(
+ new Certificate_Req(pending_state.handshake_io(),
+ pending_state.hash(),
+ policy(),
+ client_auth_CAs,
+ pending_state.version()));
+
+ 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..051eda445 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)
*/
@@ -12,10 +13,13 @@
#include <botan/credentials_manager.h>
#include <vector>
+
namespace Botan {
namespace TLS {
+class Server_Handshake_State;
+
/**
* TLS Server
*/
@@ -26,7 +30,39 @@ class BOTAN_DLL Server final : public Channel
/**
* Server initialization
+ *
+ * @param callbacks contains a set of callback function references
+ * required by the TLS client.
+ *
+ * @param session_manager manages session state
+ *
+ * @param creds manages application/user credentials
+ *
+ * @param policy specifies other connection policy information
+ *
+ * @param rng a random number generator
+ *
+ * @param is_datagram set to true if this server should expect DTLS
+ * connections. Otherwise TLS connections are expected.
+ *
+ * @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.
*/
+ Server(Callbacks& callbacks,
+ Session_Manager& session_manager,
+ Credentials_Manager& creds,
+ const Policy& policy,
+ RandomNumberGenerator& rng,
+ bool is_datagram = false,
+ size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE
+ );
+
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ */
+ BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)")
Server(output_fn output,
data_cb data_cb,
alert_cb alert_cb,
@@ -37,9 +73,14 @@ class BOTAN_DLL Server final : public Channel
RandomNumberGenerator& rng,
next_protocol_fn next_proto = next_protocol_fn(),
bool is_datagram = false,
- size_t reserved_io_buffer_size = 16*1024
+ size_t reserved_io_buffer_size = TLS::Server::IO_BUF_DEFAULT_SIZE
);
+ /**
+ * DEPRECATED. This constructor is only provided for backward
+ * compatibility and should not be used in new implementations.
+ */
+ BOTAN_DEPRECATED("Use TLS::Server(TLS::Callbacks ...)")
Server(output_fn output,
data_cb data_cb,
alert_cb alert_cb,
@@ -73,12 +114,40 @@ 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;
+ std::string m_next_protocol;
+ // Set by deprecated constructor, Server calls both this fn and Callbacks version
next_protocol_fn m_choose_next_protocol;
- std::string m_next_protocol;
};
}
diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp
index f125bfcb5..f0caa4aef 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)
*/
@@ -161,7 +162,11 @@ std::function<void (const byte[], size_t)> queue_inserter(std::vector<byte>& q)
return [&](const byte buf[], size_t sz) { q.insert(q.end(), buf, buf + sz); };
}
-void print_alert(Botan::TLS::Alert, const byte[], size_t)
+void print_alert(Botan::TLS::Alert)
+ {
+ }
+
+void alert_cb_with_data(Botan::TLS::Alert, const byte[], size_t)
{
}
@@ -218,164 +223,216 @@ 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,
+ std::unique_ptr<Botan::TLS::Callbacks> server_cb(new Botan::TLS::Compat_Callbacks(
+ queue_inserter(s2c_traffic),
+ queue_inserter(server_recv),
+ std::function<void (Botan::TLS::Alert, const byte[], size_t)>(alert_cb_with_data),
+ handshake_complete,
+ nullptr,
+ next_protocol_chooser));
+
+ // TLS::Server object constructed by new constructor using virtual callback interface.
+ std::unique_ptr<Botan::TLS::Server> server(
+ new Botan::TLS::Server(*server_cb,
server_sessions,
creds,
policy,
rng,
- next_protocol_chooser,
- false);
+ false));
- Botan::TLS::Client client(queue_inserter(c2s_traffic),
- queue_inserter(client_recv),
- print_alert,
- handshake_complete,
+ std::unique_ptr<Botan::TLS::Callbacks> client_cb(new Botan::TLS::Compat_Callbacks(
+ queue_inserter(c2s_traffic),
+ queue_inserter(client_recv),
+ std::function<void (Botan::TLS::Alert, const byte[], size_t)>(alert_cb_with_data),
+ handshake_complete));
+
+ // TLS::Client object constructed by new constructor using virtual callback interface.
+ std::unique_ptr<Botan::TLS::Client> client(
+ new Botan::TLS::Client(*client_cb,
client_sessions,
creds,
policy,
rng,
Botan::TLS::Server_Information("server.example.com"),
offer_version,
- protocols_offered);
+ protocols_offered));
size_t rounds = 0;
- while(true)
+ // Test TLS using both new and legacy constructors.
+ for(size_t ctor_sel = 0; ctor_sel < 2; ctor_sel++)
{
- ++rounds;
-
- if(rounds > 25)
+ if(ctor_sel == 1)
{
- if(r <= 2)
- result.test_failure("Still here after many rounds, deadlock?");
- break;
+ c2s_traffic.clear();
+ s2c_traffic.clear();
+ server_recv.clear();
+ client_recv.clear();
+ client_sent.clear();
+ server_sent.clear();
+
+ // TLS::Server object constructed by legacy constructor.
+ server.reset(
+ new Botan::TLS::Server(queue_inserter(s2c_traffic),
+ queue_inserter(server_recv),
+ alert_cb_with_data,
+ handshake_complete,
+ server_sessions,
+ creds,
+ policy,
+ rng,
+ next_protocol_chooser,
+ false));
+
+ // TLS::Client object constructed by legacy constructor.
+ client.reset(
+ new Botan::TLS::Client(queue_inserter(c2s_traffic),
+ queue_inserter(client_recv),
+ alert_cb_with_data,
+ handshake_complete,
+ client_sessions,
+ creds,
+ policy,
+ rng,
+ Botan::TLS::Server_Information("server.example.com"),
+ offer_version,
+ protocols_offered));
}
- if(handshake_done && (client.is_closed() || server.is_closed()))
- break;
-
- if(client.is_active() && client_sent.empty())
+ while(true)
{
- // Choose a len between 1 and 511
- const size_t c_len = 1 + rng.next_byte() + rng.next_byte();
- client_sent = unlock(rng.random_vec(c_len));
+ ++rounds;
- // TODO send in several records
- client.send(client_sent);
- }
+ if(rounds > 25)
+ {
+ if(r <= 2)
+ result.test_failure("Still here after many rounds, deadlock?");
+ break;
+ }
- if(server.is_active() && server_sent.empty())
- {
- result.test_eq("server protocol", server.next_protocol(), "test/3");
+ if(handshake_done && (client->is_closed() || server->is_closed()))
+ break;
- const size_t s_len = 1 + rng.next_byte() + rng.next_byte();
- server_sent = unlock(rng.random_vec(s_len));
- server.send(server_sent);
- }
+ if(client->is_active() && client_sent.empty())
+ {
+ // Choose a len between 1 and 511
+ const size_t c_len = 1 + rng.next_byte() + rng.next_byte();
+ client_sent = unlock(rng.random_vec(c_len));
- const bool corrupt_client_data = (r == 3);
- const bool corrupt_server_data = (r == 4);
+ // TODO send in several records
+ client->send(client_sent);
+ }
- if(c2s_traffic.size() > 0)
- {
- /*
- * Use this as a temp value to hold the queues as otherwise they
- * might end up appending more in response to messages during the
- * handshake.
- */
- std::vector<byte> input;
- std::swap(c2s_traffic, input);
-
- if(corrupt_server_data)
+ if(server->is_active() && server_sent.empty())
{
- input = Test::mutate_vec(input, true);
- size_t needed = server.received_data(input.data(), input.size());
-
- size_t total_consumed = needed;
+ result.test_eq("server->protocol", server->next_protocol(), "test/3");
- while(needed > 0 &&
- result.test_lt("Never requesting more than max protocol len", needed, 18*1024) &&
- result.test_lt("Total requested is readonable", total_consumed, 128*1024))
- {
- input.resize(needed);
- Test::rng().randomize(input.data(), input.size());
- needed = server.received_data(input.data(), input.size());
- total_consumed += needed;
- }
+ const size_t s_len = 1 + rng.next_byte() + rng.next_byte();
+ server_sent = unlock(rng.random_vec(s_len));
+ server->send(server_sent);
}
- else
+
+ const bool corrupt_client_data = (r == 3);
+ const bool corrupt_server_data = (r == 4);
+
+ if(c2s_traffic.size() > 0)
{
- size_t needed = server.received_data(input.data(), input.size());
- result.test_eq("full packet received", needed, 0);
- }
+ /*
+ * Use this as a temp value to hold the queues as otherwise they
+ * might end up appending more in response to messages during the
+ * handshake.
+ */
+ std::vector<byte> input;
+ std::swap(c2s_traffic, input);
+
+ if(corrupt_server_data)
+ {
+ input = Test::mutate_vec(input, true);
+ size_t needed = server->received_data(input.data(), input.size());
- continue;
- }
+ size_t total_consumed = needed;
- if(s2c_traffic.size() > 0)
- {
- std::vector<byte> input;
- std::swap(s2c_traffic, input);
+ while(needed > 0 &&
+ result.test_lt("Never requesting more than max protocol len", needed, 18*1024) &&
+ result.test_lt("Total requested is readonable", total_consumed, 128*1024))
+ {
+ input.resize(needed);
+ Test::rng().randomize(input.data(), input.size());
+ needed = server->received_data(input.data(), input.size());
+ total_consumed += needed;
+ }
+ }
+ else
+ {
+ size_t needed = server->received_data(input.data(), input.size());
+ result.test_eq("full packet received", needed, 0);
+ }
+
+ continue;
+ }
- if(corrupt_client_data)
+ if(s2c_traffic.size() > 0)
{
- input = Test::mutate_vec(input, true);
- size_t needed = client.received_data(input.data(), input.size());
+ std::vector<byte> input;
+ std::swap(s2c_traffic, input);
- size_t total_consumed = 0;
+ if(corrupt_client_data)
+ {
+ input = Test::mutate_vec(input, true);
+ size_t needed = client->received_data(input.data(), input.size());
- while(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024))
+ size_t total_consumed = 0;
+
+ while(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024))
+ {
+ input.resize(needed);
+ Test::rng().randomize(input.data(), input.size());
+ needed = client->received_data(input.data(), input.size());
+ total_consumed += needed;
+ }
+ }
+ else
{
- input.resize(needed);
- Test::rng().randomize(input.data(), input.size());
- needed = client.received_data(input.data(), input.size());
- total_consumed += needed;
+ size_t needed = client->received_data(input.data(), input.size());
+ result.test_eq("full packet received", needed, 0);
}
+
+ continue;
}
- else
+
+ if(client_recv.size())
{
- size_t needed = client.received_data(input.data(), input.size());
- result.test_eq("full packet received", needed, 0);
+ result.test_eq("client recv", client_recv, server_sent);
}
- continue;
- }
-
- if(client_recv.size())
- {
- result.test_eq("client recv", client_recv, server_sent);
- }
-
- if(server_recv.size())
- {
- result.test_eq("server recv", server_recv, client_sent);
- }
+ if(server_recv.size())
+ {
+ result.test_eq("server->recv", server_recv, client_sent);
+ }
- if(r > 2)
- {
- if(client_recv.size() && server_recv.size())
+ if(r > 2)
{
- result.test_failure("Negotiated in the face of data corruption " + std::to_string(r));
+ if(client_recv.size() && server_recv.size())
+ {
+ result.test_failure("Negotiated in the face of data corruption " + std::to_string(r));
+ }
}
- }
- if(client.is_closed() && server.is_closed())
- break;
+ if(client->is_closed() && server->is_closed())
+ break;
- if(server_recv.size() && client_recv.size())
- {
- Botan::SymmetricKey client_key = client.key_material_export("label", "context", 32);
- Botan::SymmetricKey server_key = server.key_material_export("label", "context", 32);
+ if(server_recv.size() && client_recv.size())
+ {
+ Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
+ Botan::SymmetricKey server_key = server->key_material_export("label", "context", 32);
- result.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
+ result.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of());
- if(r % 2 == 0)
- client.close();
- else
- server.close();
+ if(r % 2 == 0)
+ client->close();
+ else
+ server->close();
+ }
}
}
}
@@ -444,183 +501,234 @@ 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,
- server_sessions,
- creds,
- policy,
- rng,
- next_protocol_chooser,
- true);
-
- Botan::TLS::Client client(queue_inserter(c2s_traffic),
- queue_inserter(client_recv),
- print_alert,
- handshake_complete,
+ std::unique_ptr<Botan::TLS::Callbacks> server_cb(new Botan::TLS::Compat_Callbacks(
+ queue_inserter(s2c_traffic),
+ queue_inserter(server_recv),
+ std::function<void (Botan::TLS::Alert)>(print_alert),
+ handshake_complete,
+ nullptr,
+ next_protocol_chooser));
+
+ std::unique_ptr<Botan::TLS::Callbacks> client_cb(new Botan::TLS::Compat_Callbacks(
+ queue_inserter(c2s_traffic),
+ queue_inserter(client_recv),
+ std::function<void (Botan::TLS::Alert)>(print_alert),
+ handshake_complete));
+
+ // TLS::Server object constructed by new constructor using virtual callback interface.
+ std::unique_ptr<Botan::TLS::Server> server(
+ new Botan::TLS::Server(*server_cb,
+ server_sessions,
+ creds,
+ policy,
+ rng,
+ true));
+
+ // TLS::Client object constructed by new constructor using virtual callback interface.
+ std::unique_ptr<Botan::TLS::Client> client(
+ new Botan::TLS::Client(*client_cb,
client_sessions,
creds,
policy,
rng,
Botan::TLS::Server_Information("server.example.com"),
offer_version,
- protocols_offered);
+ protocols_offered));
size_t rounds = 0;
- while(true)
+ // Test DTLS using both new and legacy constructors.
+ for(size_t ctor_sel = 0; ctor_sel < 2; ctor_sel++)
{
- // TODO: client and server should be in different threads
- std::this_thread::sleep_for(std::chrono::milliseconds(rng.next_byte() % 2));
- ++rounds;
-
- if(rounds > 100)
+ if(ctor_sel == 1)
{
- result.test_failure("Still here after many rounds");
- break;
+ c2s_traffic.clear();
+ s2c_traffic.clear();
+ server_recv.clear();
+ client_recv.clear();
+ client_sent.clear();
+ server_sent.clear();
+ // TLS::Server object constructed by legacy constructor.
+ server.reset(
+ new Botan::TLS::Server(queue_inserter(s2c_traffic),
+ queue_inserter(server_recv),
+ alert_cb_with_data,
+ handshake_complete,
+ server_sessions,
+ creds,
+ policy,
+ rng,
+ next_protocol_chooser,
+ true));
+
+ // TLS::Client object constructed by legacy constructor.
+ client.reset(
+ new Botan::TLS::Client(queue_inserter(c2s_traffic),
+ queue_inserter(client_recv),
+ alert_cb_with_data,
+ handshake_complete,
+ client_sessions,
+ creds,
+ policy,
+ rng,
+ Botan::TLS::Server_Information("server.example.com"),
+ offer_version,
+ protocols_offered));
}
- if(handshake_done && (client.is_closed() || server.is_closed()))
- break;
-
- if(client.is_active() && client_sent.empty())
+ while(true)
{
- // Choose a len between 1 and 511 and send random chunks:
- const size_t c_len = 1 + rng.next_byte() + rng.next_byte();
- client_sent = unlock(rng.random_vec(c_len));
+ // TODO: client and server should be in different threads
+ std::this_thread::sleep_for(std::chrono::milliseconds(rng.next_byte() % 2));
+ ++rounds;
- // TODO send multiple parts
- client.send(client_sent);
- }
-
- if(server.is_active() && server_sent.empty())
- {
- result.test_eq("server ALPN", server.next_protocol(), "test/3");
-
- const size_t s_len = 1 + rng.next_byte() + rng.next_byte();
- server_sent = unlock(rng.random_vec(s_len));
- server.send(server_sent);
- }
+ if(rounds > 100)
+ {
+ result.test_failure("Still here after many rounds");
+ break;
+ }
- const bool corrupt_client_data = (r == 3 && rng.next_byte() % 3 <= 1 && rounds < 10);
- const bool corrupt_server_data = (r == 4 && rng.next_byte() % 3 <= 1 && rounds < 10);
+ if(handshake_done && (client->is_closed() || server->is_closed()))
+ break;
- if(c2s_traffic.size() > 0)
- {
- /*
- * Use this as a temp value to hold the queues as otherwise they
- * might end up appending more in response to messages during the
- * handshake.
- */
- std::vector<byte> input;
- std::swap(c2s_traffic, input);
-
- if(corrupt_server_data)
+ if(client->is_active() && client_sent.empty())
{
- try
- {
- input = Test::mutate_vec(input, true);
- size_t needed = server.received_data(input.data(), input.size());
+ // Choose a len between 1 and 511 and send random chunks:
+ const size_t c_len = 1 + rng.next_byte() + rng.next_byte();
+ client_sent = unlock(rng.random_vec(c_len));
- if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024))
- {
- input.resize(needed);
- Test::rng().randomize(input.data(), input.size());
- client.received_data(input.data(), input.size());
- }
- }
- catch(std::exception&)
- {
- result.test_note("corruption caused server exception");
- }
+ // TODO send multiple parts
+ client->send(client_sent);
}
- else
+
+ if(server->is_active() && server_sent.empty())
{
- try
- {
- size_t needed = server.received_data(input.data(), input.size());
- result.test_eq("full packet received", needed, 0);
- }
- catch(std::exception& e)
- {
- result.test_failure("server error", e.what());
- }
- }
+ result.test_eq("server ALPN", server->next_protocol(), "test/3");
- continue;
- }
+ const size_t s_len = 1 + rng.next_byte() + rng.next_byte();
+ server_sent = unlock(rng.random_vec(s_len));
+ server->send(server_sent);
+ }
- if(s2c_traffic.size() > 0)
- {
- std::vector<byte> input;
- std::swap(s2c_traffic, input);
+ const bool corrupt_client_data = (r == 3 && rng.next_byte() % 3 <= 1 && rounds < 10);
+ const bool corrupt_server_data = (r == 4 && rng.next_byte() % 3 <= 1 && rounds < 10);
- if(corrupt_client_data)
+ if(c2s_traffic.size() > 0)
{
- try
+ /*
+ * Use this as a temp value to hold the queues as otherwise they
+ * might end up appending more in response to messages during the
+ * handshake.
+ */
+ std::vector<byte> input;
+ std::swap(c2s_traffic, input);
+
+ if(corrupt_server_data)
{
- input = Test::mutate_vec(input, true);
- size_t needed = client.received_data(input.data(), input.size());
-
- if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024))
+ try
{
- input.resize(needed);
- Test::rng().randomize(input.data(), input.size());
- client.received_data(input.data(), input.size());
+ input = Test::mutate_vec(input, true);
+ size_t needed = server->received_data(input.data(), input.size());
+
+ if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024))
+ {
+ input.resize(needed);
+ Test::rng().randomize(input.data(), input.size());
+ client->received_data(input.data(), input.size());
+ }
+ }
+ catch(std::exception&)
+ {
+ result.test_note("corruption caused server exception");
}
}
- catch(std::exception&)
+ else
{
- result.test_note("corruption caused client exception");
+ try
+ {
+ size_t needed = server->received_data(input.data(), input.size());
+ result.test_eq("full packet received", needed, 0);
+ }
+ catch(std::exception& e)
+ {
+ result.test_failure("server error", e.what());
+ }
}
+
+ continue;
}
- else
+
+ if(s2c_traffic.size() > 0)
{
- try
+ std::vector<byte> input;
+ std::swap(s2c_traffic, input);
+
+ if(corrupt_client_data)
{
- size_t needed = client.received_data(input.data(), input.size());
- result.test_eq("full packet received", needed, 0);
+ try
+ {
+ input = Test::mutate_vec(input, true);
+ size_t needed = client->received_data(input.data(), input.size());
+
+ if(needed > 0 && result.test_lt("Never requesting more than max protocol len", needed, 18*1024))
+ {
+ input.resize(needed);
+ Test::rng().randomize(input.data(), input.size());
+ client->received_data(input.data(), input.size());
+ }
+ }
+ catch(std::exception&)
+ {
+ result.test_note("corruption caused client exception");
+ }
}
- catch(std::exception& e)
+ else
{
- result.test_failure("client error", e.what());
+ try
+ {
+ size_t needed = client->received_data(input.data(), input.size());
+ result.test_eq("full packet received", needed, 0);
+ }
+ catch(std::exception& e)
+ {
+ result.test_failure("client error", e.what());
+ }
}
- }
- continue;
- }
+ continue;
+ }
- // If we corrupted a DTLS application message, resend it:
- if(client.is_active() && corrupt_client_data && server_recv.empty())
- client.send(client_sent);
- if(server.is_active() && corrupt_server_data && client_recv.empty())
- server.send(server_sent);
+ // If we corrupted a DTLS application message, resend it:
+ if(client->is_active() && corrupt_client_data && server_recv.empty())
+ client->send(client_sent);
+ if(server->is_active() && corrupt_server_data && client_recv.empty())
+ server->send(server_sent);
- if(client_recv.size())
- {
- result.test_eq("client recv", client_recv, server_sent);
- }
+ if(client_recv.size())
+ {
+ result.test_eq("client recv", client_recv, server_sent);
+ }
- if(server_recv.size())
- {
- result.test_eq("server recv", server_recv, client_sent);
- }
+ if(server_recv.size())
+ {
+ result.test_eq("server recv", server_recv, client_sent);
+ }
- if(client.is_closed() && server.is_closed())
- break;
+ if(client->is_closed() && server->is_closed())
+ break;
- if(server_recv.size() && client_recv.size())
- {
- Botan::SymmetricKey client_key = client.key_material_export("label", "context", 32);
- Botan::SymmetricKey server_key = server.key_material_export("label", "context", 32);
+ if(server_recv.size() && client_recv.size())
+ {
+ Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32);
+ Botan::SymmetricKey server_key = server->key_material_export("label", "context", 32);
- result.test_eq("key material export", client_key.bits_of(), server_key.bits_of());
+ result.test_eq("key material export", client_key.bits_of(), server_key.bits_of());
- if(r % 2 == 0)
- client.close();
- else
- server.close();
+ if(r % 2 == 0)
+ client->close();
+ else
+ server->close();
+ }
}
}
}