Skip to content

Commit

Permalink
TLSv1.3 0-RTT support
Browse files Browse the repository at this point in the history
TLSv1.3 0-RTT test
  • Loading branch information
duke8253 committed May 29, 2019
1 parent 58e7e86 commit d196d59
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 18 deletions.
25 changes: 25 additions & 0 deletions build/crypto.m4
Expand Up @@ -225,3 +225,28 @@ AC_DEFUN([TS_CHECK_CRYPTO_SET_CIPHERSUITES], [
TS_ARG_ENABLE_VAR([use], [tls-set-ciphersuites])
AC_SUBST(use_tls_set_ciphersuites)
])

dnl
dnl Since OpenSSL 1.1.1
dnl
AC_DEFUN([TS_CHECK_EARLY_DATA], [
AC_MSG_CHECKING([OpenSSL early data support])
AC_TRY_RUN([
#include <openssl/opensslv.h>
int main() {
if (OPENSSL_VERSION_NUMBER >= 0x10101000L) {
return 0;
}
return 1;
}
],
[
AC_MSG_RESULT([yes])
has_tls_early_data=1
],
[
AC_MSG_RESULT([no])
has_tls_early_data=0
])
AC_SUBST(has_tls_early_data)
])
5 changes: 4 additions & 1 deletion configure.ac
Expand Up @@ -1197,6 +1197,9 @@ TS_CHECK_CRYPTO_OCSP
# Check for SSL_CTX_set_ciphersuites call
TS_CHECK_CRYPTO_SET_CIPHERSUITES

# Check for openssl early data support
TS_CHECK_EARLY_DATA

saved_LIBS="$LIBS"
TS_ADDTO([LIBS], ["$OPENSSL_LIBS"])

Expand Down Expand Up @@ -1233,7 +1236,7 @@ AC_CHECK_FUNC([EVP_MD_CTX_free], [],

LIBS="$saved_LIBS"

#
#
# Check OpenSSL version for JA3 Fingerprint
#
AC_MSG_CHECKING([for JA3 compatible OpenSSL version])
Expand Down
12 changes: 12 additions & 0 deletions doc/admin-guide/files/records.config.en.rst
Expand Up @@ -3442,6 +3442,18 @@ Client-Related Configuration
engines. This setting assumes an absolute path. An example config file is at
:ts:git:`contrib/openssl/load_engine.cnf`.

TLS v1.3 0-RTT Configuration
----------------------------

.. note::
TLS v1.3 must be enabled in order to utilize 0-RTT early data.

.. ts:cv:: CONFIG proxy.config.ssl.server.max_early_data INT 0
Specifies the maximum amount of early data in bytes that is permitted to be sent on a single connection.

Setting to ``0`` effectively disables 0-RTT.

OCSP Stapling Configuration
===========================

Expand Down
1 change: 1 addition & 0 deletions include/tscore/ink_config.h.in
Expand Up @@ -77,6 +77,7 @@
#define TS_USE_REMOTE_UNWINDING @use_remote_unwinding@
#define TS_USE_SSLV3_CLIENT @use_sslv3_client@
#define TS_USE_TLS_OCSP @use_tls_ocsp@
#define TS_HAS_TLS_EARLY_DATA @has_tls_early_data@

#define TS_HAS_SO_PEERCRED @has_so_peercred@

Expand Down
7 changes: 7 additions & 0 deletions iocore/net/P_SSLConfig.h
Expand Up @@ -40,6 +40,8 @@
#include "SSLSessionCache.h"
#include "YamlSNIConfig.h"

#include "P_SSLUtils.h"

struct SSLCertLookup;
struct ssl_ticket_key_block;
/////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -99,6 +101,11 @@ struct SSLConfigParams : public ConfigInfo {
char *server_groups_list;
char *client_groups_list;

#if TS_HAS_TLS_EARLY_DATA
static int server_max_early_data;
static int server_recv_max_early_data;
#endif

static int ssl_maxrecord;
static bool ssl_allow_client_renegotiation;

Expand Down
7 changes: 7 additions & 0 deletions iocore/net/P_SSLNetVConnection.h
Expand Up @@ -41,6 +41,7 @@
#include "P_EventSystem.h"
#include "P_UnixNetVConnection.h"
#include "P_UnixNet.h"
#include "P_SSLUtils.h"

// These are included here because older OpenSSL libraries don't have them.
// Don't copy these defines, or use their values directly, they are merely
Expand Down Expand Up @@ -383,6 +384,12 @@ class SSLNetVConnection : public UnixNetVConnection
bool protocol_mask_set = false;
unsigned long protocol_mask;

#if TS_HAS_TLS_EARLY_DATA
bool early_data_finish = false;
MIOBuffer *early_data_buf = nullptr;
IOBufferReader *early_data_reader = nullptr;
#endif

// Only applies during the VERIFY certificate hooks (client and server side)
// Means to give the plugin access to the data structure passed in during the underlying
// openssl callback so the plugin can make more detailed decisions about the
Expand Down
18 changes: 17 additions & 1 deletion iocore/net/SSLConfig.cc
Expand Up @@ -41,7 +41,6 @@
#include "HttpConfig.h"

#include "P_Net.h"
#include "P_SSLUtils.h"
#include "P_SSLClientUtils.h"
#include "P_SSLCertLookup.h"
#include "SSLDiags.h"
Expand All @@ -66,6 +65,11 @@ init_ssl_ctx_func SSLConfigParams::init_ssl_ctx_cb = nullptr;
load_ssl_file_func SSLConfigParams::load_ssl_file_cb = nullptr;
IpMap *SSLConfigParams::proxy_protocol_ipmap = nullptr;

#if TS_HAS_TLS_EARLY_DATA
int SSLConfigParams::server_max_early_data = 0;
int SSLConfigParams::server_recv_max_early_data = 16384;
#endif

int SSLConfigParams::async_handshake_enabled = 0;
char *SSLConfigParams::engine_conf_file = nullptr;

Expand Down Expand Up @@ -286,6 +290,18 @@ SSLConfigParams::initialize()
ssl_client_ctx_options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
#endif

#if TS_HAS_TLS_EARLY_DATA
REC_ReadConfigInteger(server_max_early_data, "proxy.config.ssl.server.max_early_data");

if (server_max_early_data < 0) {
Fatal("proxy.config.ssl.server.max_early_data must be a positive number.");
} else {
// According to OpenSSL the default value is 16384,
// we keep it unless "server_max_early_data" is higher.
server_recv_max_early_data = server_max_early_data <= 16384 ? 16384 : server_max_early_data;
}
#endif

REC_ReadConfigStringAlloc(serverCertChainFilename, "proxy.config.ssl.server.cert_chain.filename");
REC_ReadConfigStringAlloc(serverCertRelativePath, "proxy.config.ssl.server.cert.path");
set_paths_helper(serverCertRelativePath, nullptr, &serverCertPathOnly, nullptr);
Expand Down
30 changes: 22 additions & 8 deletions iocore/net/SSLNetVConnection.cc
Expand Up @@ -35,7 +35,6 @@

#include "P_Net.h"
#include "P_SSLNextProtocolSet.h"
#include "P_SSLUtils.h"
#include "P_SSLConfig.h"
#include "P_SSLClientUtils.h"
#include "P_SSLSNI.h"
Expand Down Expand Up @@ -945,6 +944,19 @@ SSLNetVConnection::free(EThread *t)

ats_free(tunnel_host);

#if TS_HAS_TLS_EARLY_DATA
if (early_data_reader != nullptr) {
early_data_reader->dealloc();
}

if (early_data_buf != nullptr) {
free_MIOBuffer(early_data_buf);
}

early_data_reader = nullptr;
early_data_buf = nullptr;
#endif

clear();
SET_CONTINUATION_HANDLER(this, (SSLNetVConnHandler)&SSLNetVConnection::startEvent);
ink_assert(con.fd == NO_FD);
Expand Down Expand Up @@ -1191,16 +1203,18 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)

#if TS_USE_TLS_ASYNC
if (SSLConfigParams::async_handshake_enabled) {
SSL_set_mode(ssl, SSL_MODE_ASYNC);
SSL_set_mode(this->ssl, SSL_MODE_ASYNC);
}
#endif
ssl_error_t ssl_error = SSLAccept(ssl);

ssl_error_t ssl_error = SSLAccept(this->ssl);

#if TS_USE_TLS_ASYNC
if (ssl_error == SSL_ERROR_WANT_ASYNC) {
size_t numfds;
OSSL_ASYNC_FD waitfd;
// Set up the epoll entry for the signalling
if (SSL_get_all_async_fds(ssl, &waitfd, &numfds) && numfds > 0) {
if (SSL_get_all_async_fds(this->ssl, &waitfd, &numfds) && numfds > 0) {
// Temporarily disable regular net
read_disable(nh, this);
this->ep.stop(); // Modify used in read_disable doesn't work for edge triggered epol
Expand All @@ -1213,7 +1227,7 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
}
} else if (SSLConfigParams::async_handshake_enabled) {
// Clean up the epoll entry for signalling
SSL_clear_mode(ssl, SSL_MODE_ASYNC);
SSL_clear_mode(this->ssl, SSL_MODE_ASYNC);
this->ep.stop();
// Reactivate the socket, ready to rock
PollDescriptor *pd = get_PollDescriptor(this_ethread());
Expand Down Expand Up @@ -1244,7 +1258,7 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
switch (ssl_error) {
case SSL_ERROR_NONE:
if (is_debug_tag_set("ssl")) {
X509 *cert = SSL_get_peer_certificate(ssl);
X509 *cert = SSL_get_peer_certificate(this->ssl);

Debug("ssl", "SSL server handshake completed successfully");
if (cert) {
Expand Down Expand Up @@ -1273,9 +1287,9 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
// is preferred since it is the server's preference. The server
// preference would not be meaningful if we let the client
// preference have priority.
SSL_get0_alpn_selected(ssl, &proto, &len);
SSL_get0_alpn_selected(this->ssl, &proto, &len);
if (len == 0) {
SSL_get0_next_proto_negotiated(ssl, &proto, &len);
SSL_get0_next_proto_negotiated(this->ssl, &proto, &len);
}

if (len) {
Expand Down
6 changes: 3 additions & 3 deletions iocore/net/SSLSessionTicket.cc
Expand Up @@ -82,7 +82,7 @@ ssl_callback_session_ticket(SSL *ssl, unsigned char *keyname, unsigned char *iv,
EVP_EncryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), nullptr, most_recent_key.aes_key, iv);
HMAC_Init_ex(hctx, most_recent_key.hmac_secret, sizeof(most_recent_key.hmac_secret), evp_md_func, nullptr);

Debug("ssl", "create ticket for a new session.");
Debug("ssl.session_ticket", "create ticket for a new session.");
SSL_INCREMENT_DYN_STAT(ssl_total_tickets_created_stat);
return 1;
} else if (enc == 0) {
Expand All @@ -91,7 +91,7 @@ ssl_callback_session_ticket(SSL *ssl, unsigned char *keyname, unsigned char *iv,
EVP_DecryptInit_ex(cipher_ctx, EVP_aes_128_cbc(), nullptr, keyblock->keys[i].aes_key, iv);
HMAC_Init_ex(hctx, keyblock->keys[i].hmac_secret, sizeof(keyblock->keys[i].hmac_secret), evp_md_func, nullptr);

Debug("ssl", "verify the ticket for an existing session.");
Debug("ssl.session_ticket", "verify the ticket for an existing session.");
// Increase the total number of decrypted tickets.
SSL_INCREMENT_DYN_STAT(ssl_total_tickets_verified_stat);

Expand All @@ -105,7 +105,7 @@ ssl_callback_session_ticket(SSL *ssl, unsigned char *keyname, unsigned char *iv,
}
}

Debug("ssl", "keyname is not consistent.");
Debug("ssl.session_ticket", "keyname is not consistent.");
SSL_INCREMENT_DYN_STAT(ssl_total_tickets_not_found_stat);
return 0;
}
Expand Down
4 changes: 4 additions & 0 deletions iocore/net/SSLStats.cc
Expand Up @@ -202,6 +202,10 @@ SSLInitializeStatistics()
RecRegisterRawStat(ssl_rsb, RECT_PROCESS, "proxy.process.ssl.ssl_ocsp_refresh_cert_failure", RECD_INT, RECP_PERSISTENT,
(int)ssl_ocsp_refresh_cert_failure_stat, RecRawStatSyncCount);

/* TLSv1.3 0-RTT stats */
RecRegisterRawStat(ssl_rsb, RECT_PROCESS, "proxy.process.ssl.early_data_received", RECD_INT, RECP_PERSISTENT,
(int)ssl_early_data_received_count, RecRawStatSyncCount);

// Get and register the SSL cipher stats. Note that we are using the default SSL context to obtain
// the cipher list. This means that the set of ciphers is fixed by the build configuration and not
// filtered by proxy.config.ssl.server.cipher_suite. This keeps the set of cipher suites stable across
Expand Down
1 change: 1 addition & 0 deletions iocore/net/SSLStats.h
Expand Up @@ -83,6 +83,7 @@ enum SSL_Stats {
ssl_session_cache_eviction,
ssl_session_cache_lock_contention,
ssl_session_cache_new_session,
ssl_early_data_received_count, // how many times we received early data

/* error stats */
ssl_error_want_write,
Expand Down

0 comments on commit d196d59

Please sign in to comment.