[feat] SSL RTT in both client and server statem. SSL_get_handshake_rtt makes it available

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/20248)
This commit is contained in:
Jairus Christensen 2023-02-27 09:36:15 -07:00 committed by Dr. David von Oheimb
parent fc570b2605
commit cee0628e0d
12 changed files with 245 additions and 2 deletions

View File

@ -25,6 +25,13 @@ OpenSSL 3.2
### Changes between 3.1 and 3.2 [xx XXX xxxx]
* TLS round-trip time calculation was added by a Brigham Young University
Capstone team partnering with Sandia National Laboratories. A new function
in ssl_lib titled SSL_get_handshake_rtt will calculate and retrieve this
value.
*Jairus Christensen*
* Added the "-quic" option to s_client to enable connectivity to QUIC servers.
QUIC requires the use of ALPN, so this must be specified via the "-alpn"
option. Use of the "advanced" s_client command command via the "-adv" option

View File

@ -2551,6 +2551,10 @@ DEPEND[html/man3/SSL_get_fd.html]=man3/SSL_get_fd.pod
GENERATE[html/man3/SSL_get_fd.html]=man3/SSL_get_fd.pod
DEPEND[man/man3/SSL_get_fd.3]=man3/SSL_get_fd.pod
GENERATE[man/man3/SSL_get_fd.3]=man3/SSL_get_fd.pod
DEPEND[html/man3/SSL_get_handshake_rtt.html]=man3/SSL_get_handshake_rtt.pod
GENERATE[html/man3/SSL_get_handshake_rtt.html]=man3/SSL_get_handshake_rtt.pod
DEPEND[man/man3/SSL_get_handshake_rtt.3]=man3/SSL_get_handshake_rtt.pod
GENERATE[man/man3/SSL_get_handshake_rtt.3]=man3/SSL_get_handshake_rtt.pod
DEPEND[html/man3/SSL_get_peer_cert_chain.html]=man3/SSL_get_peer_cert_chain.pod
GENERATE[html/man3/SSL_get_peer_cert_chain.html]=man3/SSL_get_peer_cert_chain.pod
DEPEND[man/man3/SSL_get_peer_cert_chain.3]=man3/SSL_get_peer_cert_chain.pod
@ -3533,6 +3537,7 @@ html/man3/SSL_get_error.html \
html/man3/SSL_get_event_timeout.html \
html/man3/SSL_get_extms_support.html \
html/man3/SSL_get_fd.html \
html/man3/SSL_get_handshake_rtt.html \
html/man3/SSL_get_peer_cert_chain.html \
html/man3/SSL_get_peer_certificate.html \
html/man3/SSL_get_peer_signature_nid.html \
@ -4169,6 +4174,7 @@ man/man3/SSL_get_error.3 \
man/man3/SSL_get_event_timeout.3 \
man/man3/SSL_get_extms_support.3 \
man/man3/SSL_get_fd.3 \
man/man3/SSL_get_handshake_rtt.3 \
man/man3/SSL_get_peer_cert_chain.3 \
man/man3/SSL_get_peer_certificate.3 \
man/man3/SSL_get_peer_signature_nid.3 \

View File

@ -0,0 +1,57 @@
=pod
=head1 NAME
SSL_get_handshake_rtt
- get round trip time for SSL Handshake
=head1 SYNOPSIS
#include <openssl/ssl.h>
int SSL_get_handshake_rtt(const SSL *s, uint64_t *rtt);
=head1 DESCRIPTION
SSL_get_handshake_rtt() retrieves the round-trip time (RTT) for I<ssl>.
This metric is represented in microseconds (us) as a uint64_t data type.
=head1 NOTES
This metric is created by taking two timestamps during the handshake and
providing the difference between these two times.
When acting as the server, one timestamp is taken when the server is finished
writing to the client. This is during the ServerFinished in TLS 1.3 and
ServerHelloDone in TLS 1.2. The other timestamp is taken when the server is
done reading the client's response. This is after the client has responded
with ClientFinished.
When acting as the client, one timestamp is taken when the client is finished
writing the ClientHello and early data (if any). The other is taken when
client is done reading the server's response. This is after ServerFinished in
TLS 1.3 and after ServerHelloDone in TLS 1.2.
In addition to network propagation delay and network stack overhead, this
metric includes processing time on both endpoints, as this is based on TLS
protocol-level messages and the TLS protocol is not designed to measure
network timings. In some cases the processing time can be significant,
especially when the processing includes asymmetric cryptographic operations.
=head1 RETURN VALUES
Returns 1 if the TLS handshake RTT is successfully retrieved.
Returns 0 if the TLS handshake RTT cannot be determined yet.
Returns -1 if, while retrieving the TLS handshake RTT, an error occurs.
=head1 COPYRIGHT
Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
in the file LICENSE in the source distribution or at
L<https://www.openssl.org/source/license.html>.
=cut

View File

@ -1948,6 +1948,7 @@ __owur int SSL_get_early_data_status(const SSL *s);
__owur int SSL_get_error(const SSL *s, int ret_code);
__owur const char *SSL_get_version(const SSL *s);
__owur int SSL_get_handshake_rtt(const SSL *s, uint64_t *rtt);
/* This sets the 'default' SSL version that SSL_new() will create */
# ifndef OPENSSL_NO_DEPRECATED_3_0

View File

@ -4747,6 +4747,21 @@ const char *SSL_get_version(const SSL *s)
return ssl_protocol_to_string(sc->version);
}
__owur int SSL_get_handshake_rtt(const SSL *s, uint64_t *rtt)
{
const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);
if (sc == NULL)
return -1;
if (sc->ts_msg_write.t <= 0 || sc->ts_msg_read.t <= 0)
return 0; /* data not (yet) available */
if (sc->ts_msg_read.t < sc->ts_msg_write.t)
return -1;
*rtt = ossl_time2us(ossl_time_subtract(sc->ts_msg_read, sc->ts_msg_write));
return 1;
}
static int dup_ca_names(STACK_OF(X509_NAME) **dst, STACK_OF(X509_NAME) *src)
{
STACK_OF(X509_NAME) *sk;

View File

@ -1249,6 +1249,9 @@ struct ssl_connection_st {
int quiet_shutdown;
/* we have shut things down, 0x01 sent, 0x02 for received */
int shutdown;
/* Timestamps used to calculate the handshake RTT */
OSSL_TIME ts_msg_write;
OSSL_TIME ts_msg_read;
/* where we are */
OSSL_STATEM statem;
SSL_EARLY_DATA_STATE early_data_state;

View File

@ -484,6 +484,8 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
st->hand_state = TLS_ST_CW_COMP_CERT;
else
st->hand_state = TLS_ST_CW_CERT;
s->ts_msg_read = ossl_time_now();
return WRITE_TRAN_CONTINUE;
case TLS_ST_PENDING_EARLY_DATA_END:
@ -584,6 +586,7 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL_CONNECTION *s)
* No transition at the end of writing because we don't know what
* we will be sent
*/
s->ts_msg_write = ossl_time_now();
return WRITE_TRAN_FINISHED;
case TLS_ST_CR_SRVR_HELLO:
@ -600,6 +603,7 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL_CONNECTION *s)
return WRITE_TRAN_CONTINUE;
case TLS_ST_EARLY_DATA:
s->ts_msg_write = ossl_time_now();
return WRITE_TRAN_FINISHED;
case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
@ -607,6 +611,7 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL_CONNECTION *s)
return WRITE_TRAN_CONTINUE;
case TLS_ST_CR_SRVR_DONE:
s->ts_msg_read = ossl_time_now();
if (s->s3.tmp.cert_req)
st->hand_state = TLS_ST_CW_CERT;
else

View File

@ -547,12 +547,14 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s)
case TLS_ST_SW_FINISHED:
st->hand_state = TLS_ST_EARLY_DATA;
s->ts_msg_write = ossl_time_now();
return WRITE_TRAN_CONTINUE;
case TLS_ST_EARLY_DATA:
return WRITE_TRAN_FINISHED;
case TLS_ST_SR_FINISHED:
s->ts_msg_read = ossl_time_now();
/*
* Technically we have finished the handshake at this point, but we're
* going to remain "in_init" for now and write out any session tickets
@ -702,9 +704,11 @@ WRITE_TRAN ossl_statem_server_write_transition(SSL_CONNECTION *s)
return WRITE_TRAN_CONTINUE;
case TLS_ST_SW_SRVR_DONE:
s->ts_msg_write = ossl_time_now();
return WRITE_TRAN_FINISHED;
case TLS_ST_SR_FINISHED:
s->ts_msg_read = ossl_time_now();
if (s->hit) {
st->hand_state = TLS_ST_OK;
return WRITE_TRAN_CONTINUE;

View File

@ -50,7 +50,7 @@ IF[{- !$disabled{tests} -}]
dtlsv1listentest ct_test threadstest afalgtest d2i_test \
ssl_test_ctx_test ssl_test x509aux cipherlist_test asynciotest \
bio_callback_test bio_memleak_test bio_core_test bio_dgram_test param_build_test \
bioprinttest sslapitest dtlstest sslcorrupttest \
bioprinttest sslapitest ssl_handshake_rtt_test dtlstest sslcorrupttest \
bio_enc_test pkey_meth_test pkey_meth_kdf_test evp_kdf_test uitest \
cipherbytes_test threadstest_fips threadpool_test \
asn1_encode_test asn1_decode_test asn1_string_table_test \
@ -494,6 +494,10 @@ IF[{- !$disabled{tests} -}]
INCLUDE[sslapitest]=../include ../apps/include ..
DEPEND[sslapitest]=../libcrypto ../libssl libtestutil.a
SOURCE[ssl_handshake_rtt_test]=ssl_handshake_rtt_test.c helpers/ssltestlib.c
INCLUDE[ssl_handshake_rtt_test]=../include ../apps/include ..
DEPEND[ssl_handshake_rtt_test]=../libcrypto.a ../libssl.a libtestutil.a
SOURCE[rpktest]=rpktest.c helpers/ssltestlib.c
INCLUDE[rpktest]=../include ../apps/include ..
DEPEND[rpktest]=../libcrypto ../libssl libtestutil.a

View File

@ -33,7 +33,7 @@ my $provconfnew = bldtop_file("test", "temp.cnf");
plan skip_all => "No TLS/SSL protocols are supported by this OpenSSL build"
if alldisabled(grep { $_ ne "ssl3" } available_protocols("tls"));
plan tests => 3;
plan tests => 4;
(undef, my $tmpfilename) = tempfile();
@ -140,4 +140,6 @@ SKIP: {
unlink $provconfnew;
}
ok(run(test(["ssl_handshake_rtt_test"])),"running ssl_handshake_rtt_test");
unlink $tmpfilename;

View File

@ -0,0 +1,138 @@
/*
* Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* We need access to the deprecated low level HMAC APIs for legacy purposes
* when the deprecated calls are not hidden
*/
#ifndef OPENSSL_NO_DEPRECATED_3_0
# define OPENSSL_SUPPRESS_DEPRECATED
#endif
#include <stdio.h>
#include <string.h>
#include <openssl/opensslconf.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/engine.h>
#include "helpers/ssltestlib.h"
#include "testutil.h"
#include "testutil/output.h"
#include "internal/ktls.h"
#include "../ssl/ssl_local.h"
#include "../ssl/statem/statem_local.h"
static OSSL_LIB_CTX *libctx = NULL;
static char *cert = NULL;
static char *privkey = NULL;
/*
* Test 0: Clientside handshake RTT (TLSv1.2)
* Test 1: Serverside handshake RTT (TLSv1.2)
* Test 2: Clientside handshake RTT (TLSv1.3)
* Test 3: Serverside handshake RTT (TLSv1.3)
* Test 4: Clientside handshake RTT with Early Data (TLSv1.3)
*/
static int test_handshake_rtt(int tst)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int testresult = 0;
SSL_CONNECTION *s = NULL;
OSSL_STATEM *st = NULL;
uint64_t rtt;
#ifdef OPENSSL_NO_TLS1_2
if (tst <= 1)
return 1;
#endif
#ifdef OSSL_NO_USABLE_TLS1_3
if (tst >= 2)
return 1;
#endif
if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
TLS_client_method(),
TLS1_VERSION,
(tst <= 1) ? TLS1_2_VERSION
: TLS1_3_VERSION,
&sctx, &cctx, cert, privkey))
|| !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
NULL, NULL)))
goto end;
s = SSL_CONNECTION_FROM_SSL(tst % 2 == 0 ? clientssl : serverssl);
if (!TEST_ptr(s) || !TEST_ptr(st = &s->statem))
return 0;
/* implicitly set handshake rtt with a delay */
switch (tst) {
case 0:
st->hand_state = TLS_ST_CW_CLNT_HELLO;
ossl_statem_client_write_transition(s);
OSSL_sleep(1);
st->hand_state = TLS_ST_CR_SRVR_DONE;
ossl_statem_client_write_transition(s);
break;
case 1:
st->hand_state = TLS_ST_SW_SRVR_DONE;
ossl_statem_server_write_transition(s);
OSSL_sleep(1);
st->hand_state = TLS_ST_SR_FINISHED;
ossl_statem_server_write_transition(s);
break;
case 2:
st->hand_state = TLS_ST_CW_CLNT_HELLO;
ossl_statem_client_write_transition(s);
OSSL_sleep(1);
st->hand_state = TLS_ST_CR_SRVR_DONE;
ossl_statem_client_write_transition(s);
break;
case 3:
st->hand_state = TLS_ST_SW_SRVR_DONE;
ossl_statem_server_write_transition(s);
OSSL_sleep(1);
st->hand_state = TLS_ST_SR_FINISHED;
ossl_statem_server_write_transition(s);
break;
case 4:
st->hand_state = TLS_ST_EARLY_DATA;
ossl_statem_client_write_transition(s);
OSSL_sleep(1);
st->hand_state = TLS_ST_CR_SRVR_DONE;
ossl_statem_client_write_transition(s);
break;
}
if (!TEST_int_gt(SSL_get_handshake_rtt(SSL_CONNECTION_GET_SSL(s), &rtt), 0))
goto end;
/* 1 millisec is the absolute minimum it could be given the delay */
if (!TEST_uint64_t_ge(rtt, 1000))
goto end;
testresult = 1;
end:
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
return testresult;
}
int setup_tests(void)
{
ADD_ALL_TESTS(test_handshake_rtt, 5);
return 1;
}

View File

@ -558,6 +558,7 @@ SSL_add_expected_rpk ? 3_2_0 EXIST::FUNCTION:
d2i_SSL_SESSION_ex ? 3_2_0 EXIST::FUNCTION:
SSL_is_tls ? 3_2_0 EXIST::FUNCTION:
SSL_is_quic ? 3_2_0 EXIST::FUNCTION:
SSL_get_handshake_rtt ? 3_2_0 EXIST::FUNCTION:
SSL_new_stream ? 3_2_0 EXIST::FUNCTION:
SSL_get0_connection ? 3_2_0 EXIST::FUNCTION:
SSL_is_connection ? 3_2_0 EXIST::FUNCTION: