/* * Copyright 2022 The Tongsuo 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://github.com/Tongsuo-Project/Tongsuo/blob/master/LICENSE.txt */ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "helpers/ssltestlib.h" #include "testutil.h" #include "testutil/output.h" #include "internal/nelem.h" #include "internal/ktls.h" #include "../ssl/ssl_local.h" #include "filterprov.h" #undef OSSL_NO_USABLE_TLS1_3 #if defined(OPENSSL_NO_TLS1_3) \ || (defined(OPENSSL_NO_EC) && defined(OPENSSL_NO_DH)) # define OSSL_NO_USABLE_TLS1_3 #endif static char *cert = NULL; static char *privkey = NULL; #if !defined(OPENSSL_NO_QUIC) && !defined(OSSL_NO_USABLE_TLS1_3) static SSL_SESSION *clientpsk = NULL; static SSL_SESSION *serverpsk = NULL; static const char *pskid = "Identity"; static const char *srvid; static char *certsdir = NULL; static uint8_t default_quic_early_data_ctx[2] = {1, 2}; static int use_session_cb_cnt = 0; static int find_session_cb_cnt = 0; static int use_session_cb(SSL *ssl, const EVP_MD *md, const unsigned char **id, size_t *idlen, SSL_SESSION **sess); static int find_session_cb(SSL *ssl, const unsigned char *identity, size_t identity_len, SSL_SESSION **sess); static SSL_SESSION *create_a_psk(SSL *ssl); static int use_session_cb(SSL *ssl, const EVP_MD *md, const unsigned char **id, size_t *idlen, SSL_SESSION **sess) { switch (++use_session_cb_cnt) { case 1: /* The first call should always have a NULL md */ if (md != NULL) return 0; break; case 2: /* The second call should always have an md */ if (md == NULL) return 0; break; default: /* We should only be called a maximum of twice */ return 0; } if (clientpsk != NULL) SSL_SESSION_up_ref(clientpsk); *sess = clientpsk; *id = (const unsigned char *)pskid; *idlen = strlen(pskid); return 1; } static int find_session_cb(SSL *ssl, const unsigned char *identity, size_t identity_len, SSL_SESSION **sess) { find_session_cb_cnt++; /* We should only ever be called a maximum of twice per connection */ if (find_session_cb_cnt > 2) return 0; if (serverpsk == NULL) return 0; /* Identity should match that set by the client */ if (strlen(srvid) != identity_len || strncmp(srvid, (const char *)identity, identity_len) != 0) { /* No PSK found, continue but without a PSK */ *sess = NULL; return 1; } SSL_SESSION_up_ref(serverpsk); *sess = serverpsk; return 1; } # define TLS13_AES_256_GCM_SHA384_BYTES ((const unsigned char *)"\x13\x02") static SSL_SESSION *create_a_psk(SSL *ssl) { const SSL_CIPHER *cipher = NULL; const unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f }; SSL_SESSION *sess = NULL; cipher = SSL_CIPHER_find(ssl, TLS13_AES_256_GCM_SHA384_BYTES); sess = SSL_SESSION_new(); if (!TEST_ptr(sess) || !TEST_ptr(cipher) || !TEST_true(SSL_SESSION_set1_master_key(sess, key, sizeof(key))) || !TEST_true(SSL_SESSION_set_cipher(sess, cipher)) || !TEST_true( SSL_SESSION_set_protocol_version(sess, TLS1_3_VERSION))) { SSL_SESSION_free(sess); return NULL; } return sess; } static int test_quic_set_read_secret(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len) { test_printf_stderr("test_quic_set_read_secret() %s, lvl=%d, len=%zd\n", ssl->server ? "server" : "client", level, secret_len); return 1; } static int test_quic_set_write_secret(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len) { test_printf_stderr("test_quic_set_write_secret() %s, lvl=%d, len=%zd\n", ssl->server ? "server" : "client", level, secret_len); return 1; } static int test_quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, const uint8_t *data, size_t len) { SSL *peer = (SSL*)SSL_get_app_data(ssl); TEST_info("quic_add_handshake_data() %s, lvl=%d, *data=0x%02X, len=%zd\n", ssl->server ? "server" : "client", level, (int)*data, len); if (!TEST_ptr(peer)) return 0; /* We're called with what is locally written; this gives it to the peer */ if (!TEST_true(SSL_provide_quic_data(peer, level, data, len))) { ERR_print_errors_fp(stderr); return 0; } return 1; } static int test_quic_flush_flight(SSL *ssl) { test_printf_stderr("quic_flush_flight() %s\n", ssl->server ? "server" : "client"); return 1; } static int test_quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) { test_printf_stderr("quic_send_alert() %s, lvl=%d, alert=%d\n", ssl->server ? "server" : "client", level, alert); return 1; } static SSL_QUIC_METHOD quic_method = { test_quic_set_read_secret, test_quic_set_write_secret, test_quic_add_handshake_data, test_quic_flush_flight, test_quic_send_alert, }; static int test_quic_api_set_versions(SSL *ssl, int ver) { SSL_set_quic_transport_version(ssl, ver); return 1; } static int test_quic_api_version(int clnt, int srvr) { SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; int testresult = 0; static const char *server_str = "SERVER"; static const char *client_str = "CLIENT"; const uint8_t *peer_str; size_t peer_str_len; TEST_info("original clnt=0x%X, srvr=0x%X\n", clnt, srvr); if (!TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(), TLS1_3_VERSION, 0, &sctx, &cctx, cert, privkey)) || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) || !TEST_true(SSL_CTX_set_quic_method(cctx, &quic_method)) || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) || !TEST_true(SSL_set_quic_transport_params(serverssl, (unsigned char*)server_str, strlen(server_str)+1)) || !TEST_true(SSL_set_quic_transport_params(clientssl, (unsigned char*)client_str, strlen(client_str)+1)) || !TEST_true(SSL_set_app_data(serverssl, clientssl)) || !TEST_true(SSL_set_app_data(clientssl, serverssl)) || !TEST_true(test_quic_api_set_versions(clientssl, clnt)) || !TEST_true(test_quic_api_set_versions(serverssl, srvr)) || !TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) || !TEST_true(SSL_version(serverssl) == TLS1_3_VERSION) || !TEST_true(SSL_version(clientssl) == TLS1_3_VERSION) || !(TEST_int_eq(SSL_quic_read_level(clientssl), ssl_encryption_application)) || !(TEST_int_eq(SSL_quic_read_level(serverssl), ssl_encryption_application)) || !(TEST_int_eq(SSL_quic_write_level(clientssl), ssl_encryption_application)) || !(TEST_int_eq(SSL_quic_write_level(serverssl), ssl_encryption_application))) goto end; SSL_get_peer_quic_transport_params(serverssl, &peer_str, &peer_str_len); if (!TEST_mem_eq(peer_str, peer_str_len, client_str, strlen(client_str)+1)) goto end; SSL_get_peer_quic_transport_params(clientssl, &peer_str, &peer_str_len); if (!TEST_mem_eq(peer_str, peer_str_len, server_str, strlen(server_str)+1)) goto end; /* Deal with two NewSessionTickets */ if (!TEST_true(SSL_process_quic_post_handshake(clientssl))) goto end; /* Dummy handshake call should succeed */ if (!TEST_true(SSL_do_handshake(clientssl))) goto end; /* Test that we (correctly) fail to send KeyUpdate */ if (!TEST_true(SSL_key_update(clientssl, SSL_KEY_UPDATE_NOT_REQUESTED)) || !TEST_int_le(SSL_do_handshake(clientssl), 0)) goto end; if (!TEST_true(SSL_key_update(serverssl, SSL_KEY_UPDATE_NOT_REQUESTED)) || !TEST_int_le(SSL_do_handshake(serverssl), 0)) goto end; TEST_info("original clnt=0x%X, srvr=0x%X\n", clnt, srvr); if (srvr == 0 && clnt == 0) srvr = clnt = TLSEXT_TYPE_quic_transport_parameters; else if (srvr == 0) srvr = clnt; else if (clnt == 0) clnt = srvr; TEST_info("expected clnt=0x%X, srvr=0x%X\n", clnt, srvr); if (!TEST_int_eq(SSL_get_peer_quic_transport_version(serverssl), clnt)) goto end; if (!TEST_int_eq(SSL_get_peer_quic_transport_version(clientssl), srvr)) goto end; testresult = 1; end: SSL_free(serverssl); SSL_free(clientssl); SSL_CTX_free(sctx); SSL_CTX_free(cctx); return testresult; } static int test_quic_api(int tst) { SSL_CTX *sctx = NULL; SSL *serverssl = NULL; int testresult = 0; static int clnt_params[] = { 0, TLSEXT_TYPE_quic_transport_parameters_draft, TLSEXT_TYPE_quic_transport_parameters, 0, TLSEXT_TYPE_quic_transport_parameters_draft, TLSEXT_TYPE_quic_transport_parameters, 0, TLSEXT_TYPE_quic_transport_parameters_draft, TLSEXT_TYPE_quic_transport_parameters }; static int srvr_params[] = { 0, 0, 0, TLSEXT_TYPE_quic_transport_parameters_draft, TLSEXT_TYPE_quic_transport_parameters_draft, TLSEXT_TYPE_quic_transport_parameters_draft, TLSEXT_TYPE_quic_transport_parameters, TLSEXT_TYPE_quic_transport_parameters, TLSEXT_TYPE_quic_transport_parameters }; static int results[] = { 1, 1, 1, 1, 1, 0, 1, 0, 1 }; /* Failure cases: * test 6/[5] clnt = parameters, srvr = draft * test 8/[7] clnt = draft, srvr = parameters */ if (!TEST_ptr(sctx = SSL_CTX_new(TLS_server_method())) || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) || !TEST_ptr(sctx->quic_method) || !TEST_ptr(serverssl = SSL_new(sctx)) || !TEST_true(SSL_IS_QUIC(serverssl)) || !TEST_true(SSL_set_quic_method(serverssl, NULL)) || !TEST_false(SSL_IS_QUIC(serverssl)) || !TEST_true(SSL_set_quic_method(serverssl, &quic_method)) || !TEST_true(SSL_IS_QUIC(serverssl))) goto end; if (!TEST_int_eq(test_quic_api_version(clnt_params[tst], srvr_params[tst]), results[tst])) goto end; testresult = 1; end: SSL_CTX_free(sctx); sctx = NULL; SSL_free(serverssl); serverssl = NULL; return testresult; } /* * Helper method to setup objects for QUIC early data test. Caller * frees objects on error. */ static int quic_setupearly_data_test(SSL_CTX **cctx, SSL_CTX **sctx, SSL **clientssl, SSL **serverssl, SSL_SESSION **sess, int idx) { static const char *server_str = "SERVER"; static const char *client_str = "CLIENT"; if (*sctx == NULL && (!TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(), TLS1_3_VERSION, 0, sctx, cctx, cert, privkey)) || !TEST_true(SSL_CTX_set_quic_method(*sctx, &quic_method)) || !TEST_true(SSL_CTX_set_quic_method(*cctx, &quic_method)) || !TEST_true(SSL_CTX_set_max_early_data(*sctx, 0xffffffffu)))) return 0; if (idx == 1) { /* When idx == 1 we repeat the tests with read_ahead set */ SSL_CTX_set_read_ahead(*cctx, 1); SSL_CTX_set_read_ahead(*sctx, 1); } else if (idx == 2) { /* When idx == 2 we are doing early_data with a PSK. Set up callbacks */ SSL_CTX_set_psk_use_session_callback(*cctx, use_session_cb); SSL_CTX_set_psk_find_session_callback(*sctx, find_session_cb); use_session_cb_cnt = 0; find_session_cb_cnt = 0; srvid = pskid; } if (!TEST_true(create_ssl_objects(*sctx, *cctx, serverssl, clientssl, NULL, NULL)) || !TEST_true(SSL_set_quic_transport_params(*serverssl, (unsigned char*)server_str, strlen(server_str)+1)) || !TEST_true(SSL_set_quic_transport_params(*clientssl, (unsigned char*)client_str, strlen(client_str)+1)) || !TEST_true(SSL_set_quic_early_data_context(*serverssl, default_quic_early_data_ctx, sizeof(default_quic_early_data_ctx))) || !TEST_true(SSL_set_app_data(*serverssl, *clientssl)) || !TEST_true(SSL_set_app_data(*clientssl, *serverssl))) return 0; /* * For one of the run throughs (doesn't matter which one), we'll try sending * some SNI data in the initial ClientHello. This will be ignored (because * there is no SNI cb set up by the server), so it should not impact * early_data. */ if (idx == 1 && !TEST_true(SSL_set_tlsext_host_name(*clientssl, "localhost"))) return 0; if (idx == 2) { clientpsk = create_a_psk(*clientssl); if (!TEST_ptr(clientpsk) || !TEST_true(SSL_SESSION_set_max_early_data(clientpsk, 0xffffffffu)) || !TEST_true(SSL_SESSION_up_ref(clientpsk))) { SSL_SESSION_free(clientpsk); clientpsk = NULL; return 0; } if ((*serverssl)->quic_early_data_context) { clientpsk->quic_early_data_context = OPENSSL_memdup((*serverssl)->quic_early_data_context, (*serverssl)->quic_early_data_context_len); if (!TEST_ptr(clientpsk->quic_early_data_context)) { SSL_SESSION_free(clientpsk); clientpsk = NULL; return 0; } clientpsk->quic_early_data_context_len = (*serverssl)->quic_early_data_context_len; } serverpsk = clientpsk; if (sess != NULL) { if (!TEST_true(SSL_SESSION_up_ref(clientpsk))) { SSL_SESSION_free(clientpsk); SSL_SESSION_free(serverpsk); clientpsk = serverpsk = NULL; return 0; } *sess = clientpsk; } SSL_set_quic_early_data_enabled(*serverssl, 1); SSL_set_quic_early_data_enabled(*clientssl, 1); return 1; } if (sess == NULL) return 1; if (!TEST_true(create_ssl_connection(*serverssl, *clientssl, SSL_ERROR_NONE))) return 0; /* Deal with two NewSessionTickets */ if (!TEST_true(SSL_process_quic_post_handshake(*clientssl))) return 0; *sess = SSL_get1_session(*clientssl); SSL_shutdown(*clientssl); SSL_shutdown(*serverssl); SSL_free(*serverssl); SSL_free(*clientssl); *serverssl = *clientssl = NULL; if (!TEST_true(create_ssl_objects(*sctx, *cctx, serverssl, clientssl, NULL, NULL)) || !TEST_true(SSL_set_session(*clientssl, *sess)) || !TEST_true(SSL_set_quic_transport_params(*serverssl, (unsigned char*)server_str, strlen(server_str)+1)) || !TEST_true(SSL_set_quic_transport_params(*clientssl, (unsigned char*)client_str, strlen(client_str)+1)) || !TEST_true(SSL_set_app_data(*serverssl, *clientssl)) || !TEST_true(SSL_set_app_data(*clientssl, *serverssl))) return 0; SSL_set_quic_early_data_enabled(*serverssl, 1); SSL_set_quic_early_data_enabled(*clientssl, 1); return 1; } /* * TEST 0: quic early data * TEST 1: quic early data with read_ahead set * TEST 2: quic early data, client use session cb, server find session cb * TEST 3: quic early data context not compatible, reject early data */ static int test_quic_early_data(int tst) { SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; int testresult = 0; SSL_SESSION *sess = NULL; uint8_t quic_early_data_ctx[2] = {8, 8}; if (!TEST_true(quic_setupearly_data_test(&cctx, &sctx, &clientssl, &serverssl, &sess, tst))) goto end; if (tst < 3) { if (!TEST_true(SSL_set_quic_early_data_context(serverssl, default_quic_early_data_ctx, sizeof(default_quic_early_data_ctx)))) goto end; } else { if (!TEST_true(SSL_set_quic_early_data_context(serverssl, quic_early_data_ctx, sizeof(quic_early_data_ctx)))) goto end; } if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))) goto end; if (tst < 3) { if (!TEST_int_eq(SSL_get_early_data_status(serverssl), SSL_EARLY_DATA_ACCEPTED)) goto end; } else { if (!TEST_int_eq(SSL_get_early_data_status(serverssl), SSL_EARLY_DATA_REJECTED)) goto end; } testresult = 1; end: SSL_SESSION_free(sess); SSL_SESSION_free(clientpsk); SSL_SESSION_free(serverpsk); clientpsk = serverpsk = NULL; SSL_free(serverssl); SSL_free(clientssl); SSL_CTX_free(sctx); SSL_CTX_free(cctx); return testresult; } #endif OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n") int setup_tests(void) { if (!test_skip_common_options()) { TEST_error("Error parsing test options\n"); return 0; } #if !defined(OPENSSL_NO_QUIC) && !defined(OSSL_NO_USABLE_TLS1_3) if (!TEST_ptr(certsdir = test_get_argument(0))) return 0; cert = test_mk_file_path(certsdir, "servercert.pem"); if (cert == NULL) return 0; privkey = test_mk_file_path(certsdir, "serverkey.pem"); if (privkey == NULL) { OPENSSL_free(cert); return 0; } ADD_ALL_TESTS(test_quic_api, 9); ADD_ALL_TESTS(test_quic_early_data, 4); #endif return 1; } void cleanup_tests(void) { OPENSSL_free(cert); OPENSSL_free(privkey); bio_s_mempacket_test_free(); bio_s_always_retry_free(); }