Newer
Older
* Copyright 1995-2018 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
*/
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#define NS_IN_MS 1000000.0
#define MS_IN_S 1000
const char *hostname = "10.0.0.1";
// const char *hostname = "localhost";
const char *sni_hostname = "localhost";
const char *SSL_VERIFY_LOCATION = "../tmp/.local/nginx/conf/CA.crt";
/* Don't forget to free: BIO_ADDRINFO_free()*/
BIO_ADDRINFO *lookup_address()
/*
* Lookup IP address info for the server.
*/
BIO_ADDRINFO *res;
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, AF_INET, SOCK_DGRAM, 0,
&res))
return NULL;
return res;
/* Helper function to create a new SSL context with all options */
static SSL_CTX *new_ssl_ctx_builder(const char *kex_alg)
SSL_CTX *ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method());
if (!ssl_ctx)
{
printf("Failed to create the SSL_CTX\n");
goto ossl_error;
}
// Automatic retries after TLS session events like renegotiations or heartbeats
// SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); // will be retried anyway
// SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_COMPRESSION); // has no effect
const char *ciphersuites = "TLS_AES_256_GCM_SHA384";
if (SSL_CTX_set_ciphersuites(ssl_ctx, ciphersuites) != 1)
{
fprintf(stderr, "Could not set the ciphersuite.\n");
goto ossl_error;
}
if (SSL_CTX_set1_groups_list(ssl_ctx, kex_alg) != 1)
{
fprintf(stderr, "Could not set the group/kem algorithm.\n");
goto ossl_error;
}
Bartolomeo Berend Müller
committed
// Add as many signature algorithms as needed to be comparable with cquiche_s_timer
// -> so that both use 114/115 byte padding for x25519_mlkem512
// :p256_falconpadded512:rsa3072_falconpadded512:falcon1024:p521_falcon1024:falconpadded1024:p521_falconpadded1024
// :sphincssha2128fsimple:p256_sphincssha2128fsimple:rsa3072_sphincssha2128fsimple:sphincssha2128ssimple
// :p256_sphincssha2128ssimple:rsa3072_sphincssha2128ssimple:sphincssha2192fsimple:p384_sphincssha2192fsimple
// :sphincsshake128fsimple:p256_sphincsshake128fsimple:rsa3072_sphincsshake128fsimple:mayo1:p256_mayo1:mayo2
// :p256_mayo2:mayo3:p384_mayo3:mayo5:p521_mayo5:CROSSrsdp128balanced
const char *sigalgs = "ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512"
":RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512"
":dilithium2:p256_dilithium2:rsa3072_dilithium2:dilithium3:p384_dilithium3:dilithium5:p521_dilithium5"
":mldsa44:p256_mldsa44"; //:rsa3072_mldsa44:mldsa44_pss2048:mldsa44_rsa2048:mldsa44_ed25519:mldsa44_p256:mldsa44_bp256"
// ":mldsa65:p384_mldsa65:mldsa65_pss3072:mldsa65_rsa3072:mldsa65_p256:mldsa65_bp256:mldsa65_ed25519"
// ":mldsa87:p521_mldsa87:mldsa87_p384:mldsa87_bp384:mldsa87_ed448"
// ":falcon512:p256_falcon512:rsa3072_falcon512:falconpadded512";
if (SSL_CTX_set1_sigalgs_list(ssl_ctx, sigalgs) != 1) {
fprintf(stderr, "Could not set the signature algorithms.\n");
goto ossl_error;
}
if (SSL_CTX_load_verify_locations(ssl_ctx, SSL_VERIFY_LOCATION, 0) != 1)
{
fprintf(stderr, "Could not load the verify location.\n");
goto ossl_error;
}
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
return ssl_ctx;
ossl_error:
SSL_CTX_free(ssl_ctx);
return NULL;
/* Helper function to create a BIO connected to the server */
static BIO *create_socket_bio_helper(const char *hostname, const char *port,
int family, BIO_ADDRINFO *addr_info)
/*
* Create a UDP socket. We could equally use non-OpenSSL calls such
* as "socket" here for this and the subsequent connect and close
* functions. But for portability reasons and also so that we get
* errors on the OpenSSL stack in the event of a failure we use
* OpenSSL's versions of these functions.
*/
sock = BIO_socket(BIO_ADDRINFO_family(addr_info), SOCK_DGRAM, 0, 0);
if (sock == -1)
return NULL;
/* Connect the socket to the server's address */
if (!BIO_connect(sock, BIO_ADDRINFO_address(addr_info), 0))
BIO_closesocket(sock);
sock = -1;
return NULL;
}
/* Set to nonblocking mode */
if (!BIO_socket_nbio(sock, 1))
{
BIO_closesocket(sock);
sock = -1;
return NULL;
/* If sock is -1 then we've been unable to connect to the server */
if (sock == -1)
return NULL;
/* Create a BIO to wrap the socket */
BIO *bio = BIO_new(BIO_s_datagram());
if (bio == NULL)
{
BIO_closesocket(sock);
return NULL;
}
/*
* Associate the newly created BIO with the underlying socket. By
* passing BIO_CLOSE here the socket will be automatically closed when
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
* case you must close the socket explicitly when it is no longer
* needed.
*/
BIO_set_fd(bio, sock, BIO_CLOSE);
return bio;
SSL *new_ssl_builder(SSL_CTX *ssl_ctx)
SSL *ssl = SSL_new(ssl_ctx);
/*
* Tell the server during the handshake which hostname we are attempting
* to connect to in case the server supports multiple hosts.
*/
if (!SSL_set_tlsext_host_name(ssl, sni_hostname))
printf("Failed to set the SNI sni_hostname\n");
SSL_free(ssl);
return NULL;
}
/*
* Ensure we check during certificate verification that the server has
* supplied a certificate for the hostname that we were expecting.
* Virtually all clients should do this unless you really know what you
* are doing.
*/
if (!SSL_set1_host(ssl, sni_hostname))
printf("Failed to set the certificate verification sni_hostname");
SSL_free(ssl);
return NULL;
}
/* SSL_set_alpn_protos returns 0 for success! */
unsigned char alpn[] = {2, 'h', '3'};
if (SSL_set_alpn_protos(ssl, alpn, sizeof(alpn)) != 0)
{
printf("Failed to set the ALPN for the connection\n");
SSL_free(ssl);
return NULL;
}
return ssl;
SSL *do_tls_handshake(SSL_CTX *ssl_ctx, BIO_ADDRINFO *addr_info, double *time_taken)
BIO *conn = create_socket_bio_helper(hostname, port, AF_INET, addr_info);
if (!conn)
{
return 0;
}
SSL *ssl = new_ssl_builder(ssl_ctx);
if (!ssl)
// The total other time than the handshake in the hot path takes on average around 0.05 to 0.06 ms
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
int ret = SSL_connect(ssl);
clock_gettime(CLOCK_MONOTONIC_RAW, &finish);
*time_taken = ((finish.tv_sec - start.tv_sec) * MS_IN_S) + ((finish.tv_nsec - start.tv_nsec) / NS_IN_MS);
if (ret <= 0) {
ERR_print_errors_fp(stderr);
SSL_free(ssl);
return 0;
}
#if defined(SOL_SOCKET) && defined(SO_LINGER)
{
struct linger no_linger = {.l_onoff = 1, .l_linger = 0};
int fd = SSL_get_fd(ssl);
if (fd >= 0)
{
(void)setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&no_linger,
sizeof(no_linger));
}
}
#endif
return ssl;
}
int main(int argc, char *argv[])
{
int ret = -1;
if (argc != 3)
{
fprintf(stderr, "Wrong number of arguments.\n");
fprintf(stderr, "Usage: ./quic_s_timer GROUP N\n");
return 1;
}
const char *kex_alg = argv[1];
const size_t measurements_to_make = strtol(argv[2], 0, 10);
BIO_ADDRINFO *addr_info = lookup_address();
if (!addr_info)
fprintf(stderr, "Could not lookup the address.\n");
ERR_print_errors_fp(stderr);
return 1;
SSL_CTX *ssl_ctx = new_ssl_ctx_builder(kex_alg);
// Start experiments
SSL *ssl = NULL;
size_t measurements = 0;
double *handshake_times_ms = malloc(measurements_to_make * sizeof(*handshake_times_ms));
while (measurements < measurements_to_make)
double time_taken = -1.0;
ssl = do_tls_handshake(ssl_ctx, addr_info, &time_taken);
/* Retry since at high packet loss rates,
* the connect() syscall fails sometimes.
* Non-retryable errors are caught by manual
* inspection of logs, which has sufficed
* for our purposes */
char buf[160]; // nginx answers with 9 bytes of http3
size_t readbytes;
SSL_read_ex(ssl, buf, sizeof(buf), &readbytes);
// this performs a non-rfc compliant close, which is sufficient if the close packet is not dropped
SSL_shutdown_ex(ssl, SSL_SHUTDOWN_FLAG_RAPID, NULL, 0); // send close packet
ret = BIO_closesocket(SSL_get_fd(ssl));
if (ret == -1)
goto ossl_error;
SSL_free(ssl); // This also frees the associated BIO
handshake_times_ms[measurements] = time_taken;
for (size_t i = 0; i < measurements - 1; i++)
{
printf("%f,", handshake_times_ms[i]);
}
printf("%f", handshake_times_ms[measurements - 1]);
ossl_error:
fprintf(stderr, "Unrecoverable OpenSSL error.\n");
ERR_print_errors_fp(stderr);
end:
free(handshake_times_ms);
SSL_CTX_free(ssl_ctx);
BIO_ADDRINFO_free(addr_info);