Skip to content
Snippets Groups Projects
quic_s_timer.c 9.93 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * 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>
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    #include <openssl/err.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
    
    // Server (NGINX)
    
    const char *hostname = "10.0.0.1";
    // const char *hostname = "localhost";
    
    const char *port = "8443";
    
    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;
        }
    
    
        // 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)
    
        int sock = -1;
    
        /*
         * 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;
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
    SSL *do_tls_handshake(SSL_CTX *ssl_ctx, BIO_ADDRINFO *addr_info, double *time_taken)
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    {
    
        // NOTE maybe also use ipv6
    
        BIO *conn = create_socket_bio_helper(hostname, port, AF_INET, addr_info);
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
        SSL *ssl = new_ssl_builder(ssl_ctx);
        if (!ssl)
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
        {
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
        }
    
    
        SSL_set_bio(ssl, conn, conn);
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
        struct timespec start, finish;
    
        // The total other time than the handshake in the hot path takes on average around 0.05 to 0.06 ms
    
        /* ok, lets connect */
    
        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;
        }
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
    #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;
    }
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
    int main(int argc, char *argv[])
    {
        int ret = -1;
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
        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);
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
        BIO_ADDRINFO *addr_info = lookup_address();
        if (!addr_info)
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
        {
    
            fprintf(stderr, "Could not lookup the address.\n");
            ERR_print_errors_fp(stderr);
            return 1;
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
        }
    
    
        SSL_CTX *ssl_ctx = new_ssl_ctx_builder(kex_alg);
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
        // Start experiments
        SSL *ssl = NULL;
        size_t measurements = 0;
        double *handshake_times_ms = malloc(measurements_to_make * sizeof(*handshake_times_ms));
    
        int error_count = 0;
    
        while (measurements < measurements_to_make)
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
        {
    
            double time_taken = -1.0;
            ssl = do_tls_handshake(ssl_ctx, addr_info, &time_taken);
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
            {
    
                /* 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 */
    
                error_count += 1;
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
            }
    
    
            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;
    
        printf("%d;", error_count);
    
        for (size_t i = 0; i < measurements - 1; i++)
        {
            printf("%f,", handshake_times_ms[i]);
        }
        printf("%f", handshake_times_ms[measurements - 1]);
    
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
        ret = 0;
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
    
    
    ossl_error:
        fprintf(stderr, "Unrecoverable OpenSSL error.\n");
        ERR_print_errors_fp(stderr);
    end:
    
        SSL_CTX_free(ssl_ctx);
        BIO_ADDRINFO_free(addr_info);
    
    Bartolomeo Berend Müller's avatar
    tmp
    Bartolomeo Berend Müller committed
        return ret;