gateway2/Tongsuo-8.4.0/test/quicapitest.c
2026-07-11 13:40:57 +08:00

602 lines
21 KiB
C

/*
* 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 <stdio.h>
#include <string.h>
#include <openssl/opensslconf.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/ocsp.h>
#include <openssl/srp.h>
#include <openssl/txt_db.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/core_names.h>
#include <openssl/core_dispatch.h>
#include <openssl/provider.h>
#include <openssl/param_build.h>
#include <openssl/x509v3.h>
#include <openssl/dh.h>
#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();
}