src/tls/xqc_tls.c (936 lines of code) (raw):
/**
* @copyright Copyright (c) 2022, Alibaba Group Holding Limited
*/
#include "xqc_tls.h"
#include "xqc_tls_ctx.h"
#include "xqc_ssl_if.h"
#include "xqc_ssl_cbs.h"
#include "xqc_crypto.h"
#include "src/common/xqc_common.h"
#include <openssl/rand.h>
#include <openssl/hmac.h>
typedef enum xqc_tls_flag_e {
/* initial state */
XQC_TLS_FLAG_NONE = 0,
/* received peer's transport parameter. this is used for transport parameter callback */
XQC_TLS_FLAG_TRANSPORT_PARAM_RCVD = 1 << 0,
/*
* handshake completed flag. this is set when ssl library report handshake completion,
* and is used for process crypto data
*/
XQC_TLS_FLAG_HSK_COMPLETED = 1 << 1,
} xqc_tls_flag_t;
typedef struct xqc_tls_s {
/* tls context */
xqc_tls_ctx_t *ctx;
/* SSL handler */
SSL *ssl;
/* tls type. instance of client and server got different behaviour */
xqc_tls_type_t type;
/* cert verify config */
uint8_t cert_verify_flag;
/* no crypto */
xqc_bool_t no_crypto;
/* crypto context */
xqc_crypto_t *crypto[XQC_ENC_LEV_MAX];
/* log handler */
xqc_log_t *log;
/* callback functions and user_data */
xqc_tls_callbacks_t *cbs;
void *user_data;
/* whether client used resumption. aka, whether client inputs session ticket */
xqc_bool_t resumption;
/* tls state flag */
xqc_tls_flag_t flag;
/* quic version. used to decide protection salt */
xqc_proto_version_t version;
xqc_bool_t key_update_confirmed;
} xqc_tls_t;
/* quic method callback functions for ssl library */
SSL_QUIC_METHOD xqc_ssl_quic_method;
xqc_bool_t
xqc_tls_check_session_ticket_timeout(SSL_SESSION *session)
{
uint32_t now = (uint32_t)time(NULL);
uint32_t session_time = SSL_get_time(session);
if (session_time > now) {
return XQC_FALSE;
}
uint32_t agesec = now - session_time;
uint64_t session_timeout = SSL_SESSION_get_timeout(session);
if (session_timeout < agesec) {
return XQC_FALSE;
}
/* session is still available */
return XQC_TRUE;
}
xqc_int_t
xqc_tls_cli_set_session_data(xqc_tls_t *tls, char *session_data, size_t session_data_len)
{
xqc_int_t ret = XQC_OK;
int ssl_ret;
SSL *ssl = tls->ssl;
BIO *bio = BIO_new_mem_buf(session_data, session_data_len);
if (bio == NULL) {
xqc_log(tls->log, XQC_LOG_DEBUG, "|new mem buf error|%s",
ERR_error_string(ERR_get_error(), NULL));
return -XQC_TLS_INTERNAL;
}
SSL_SESSION *session = PEM_read_bio_SSL_SESSION(bio, NULL, 0, NULL);
if (session == NULL) {
ret = -XQC_TLS_INTERNAL;
xqc_log(tls->log, XQC_LOG_DEBUG, "|read session ticket info error|%s",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
if (!xqc_tls_check_session_ticket_timeout(session)) {
ret = -XQC_TLS_INVALID_ARGUMENT;
xqc_log(tls->log, XQC_LOG_DEBUG, "|check session timeout failed|%s",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
ssl_ret = SSL_set_session(ssl, session);
if (ssl_ret != XQC_SSL_SUCCESS) {
ret = -XQC_TLS_INTERNAL;
xqc_log(tls->log, XQC_LOG_ERROR, "|set session error|%s",
ERR_error_string(ERR_get_error(), NULL));
goto end;
}
end:
if (bio) {
BIO_free(bio);
}
if (session) {
SSL_SESSION_free(session);
}
return ret;
}
/* only for client */
xqc_int_t
xqc_tls_set_alpn(SSL *ssl, const char *alpn)
{
if (NULL == alpn) {
return -XQC_TLS_INVALID_ARGUMENT;
}
size_t alpn_len = strlen(alpn);
if (alpn_len >= 128) {
return -XQC_TLS_INVALID_ARGUMENT;
}
/*
* ALPN protocol is a series of non-empty, 8-bit length-prefixed strings,
* the length is one byte more than input alpn string.
*/
size_t protos_len = alpn_len + 1;
uint8_t *p_alpn = xqc_malloc(protos_len + 1);
if (p_alpn == NULL) {
return -XQC_TLS_NOBUF;
}
/*
* copy the alpn string len into first byte, copy the alpn string into
* the rest bytes, set the last byte with '\0'
*/
p_alpn[0] = alpn_len;
memcpy(&p_alpn[1], alpn, alpn_len);
p_alpn[protos_len] = '\0';
SSL_set_alpn_protos(ssl, p_alpn, protos_len);
xqc_free(p_alpn);
return XQC_OK;
}
xqc_int_t
xqc_tls_init_client_ssl(xqc_tls_t *tls, xqc_tls_config_t *cfg)
{
xqc_int_t ret = XQC_OK;
char *hostname = NULL;
SSL *ssl = tls->ssl;
/* configure ssl as client */
SSL_set_connect_state(ssl);
/* If remote host is NULL, send "localhost" as SNI. */
if (NULL == cfg->hostname || 0 == strlen(cfg->hostname)) {
hostname = "localhost";
} else {
hostname = cfg->hostname;
}
SSL_set_tlsext_host_name(ssl, hostname);
/*
* set alpn in ClientHello. for client, xquic set alpn for every ssl instance. while server set
* the alpn select callback function while initializing tls context.
*/
ret = xqc_tls_set_alpn(ssl, cfg->alpn);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|xqc_create_client_ssl|set alpn error|");
goto end;
}
/* set session data and enable early data */
if (cfg->session_ticket && cfg->session_ticket_len > 0) {
if (xqc_tls_cli_set_session_data(tls, cfg->session_ticket,
cfg->session_ticket_len) == XQC_OK)
{
tls->resumption = XQC_TRUE;
xqc_ssl_enable_max_early_data(ssl);
}
}
/* set verify if flag set */
if (cfg->cert_verify_flag & XQC_TLS_CERT_FLAG_NEED_VERIFY) {
if (X509_VERIFY_PARAM_set1_host(SSL_get0_param(ssl), hostname,
strlen(hostname)) != XQC_SSL_SUCCESS)
{
/* hostname set failed need log */
xqc_log(tls->log, XQC_LOG_DEBUG, "|certificate verify set hostname failed|");
ret = -XQC_TLS_INTERNAL;
goto end;
}
SSL_set_verify(ssl, SSL_VERIFY_PEER, xqc_ssl_cert_verify_cb);
}
end:
return ret;
}
xqc_int_t
xqc_tls_init_server_ssl(xqc_tls_t *tls, xqc_tls_config_t *cfg)
{
xqc_int_t ret = XQC_OK;
SSL *ssl = tls->ssl;
/* configure ssl as server */
SSL_set_accept_state(ssl);
/* enable early data and set context */
xqc_ssl_enable_max_early_data(ssl);
SSL_set_quic_early_data_context(ssl, (const uint8_t *)XQC_EARLY_DATA_CONTEXT,
XQC_EARLY_DATA_CONTEXT_LEN);
return ret;
}
xqc_int_t
xqc_tls_create_ssl(xqc_tls_t *tls, xqc_tls_config_t *cfg)
{
xqc_int_t ret = XQC_OK;
int ssl_ret;
/* create ssl instance */
SSL *ssl = SSL_new(xqc_tls_ctx_get_ssl_ctx(tls->ctx));
if (ssl == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|SSL_new return null|%s|",
ERR_error_string(ERR_get_error(), NULL));
ret = -XQC_TLS_INTERNAL;
goto end;
}
tls->ssl = ssl;
/*
* make tls the app data of ssl instance, which will be used in callback
* functions defined in xqc_ssl_cbs.h
*/
ssl_ret = SSL_set_app_data(ssl, tls);
if (ssl_ret != XQC_SSL_SUCCESS) {
xqc_log(tls->log, XQC_LOG_ERROR, "|ssl set app data error|%s|",
ERR_error_string(ERR_get_error(), NULL));
ret = -XQC_TLS_INTERNAL;
goto end;
}
ssl_ret = SSL_set_quic_method(ssl, &xqc_ssl_quic_method);
if (ssl_ret != XQC_SSL_SUCCESS) {
xqc_log(tls->log, XQC_LOG_ERROR, "|ssl set quic method error|",
ERR_error_string(ERR_get_error(), NULL));
ret = -XQC_TLS_INTERNAL;
goto end;
}
/* set local transport parameter */
ssl_ret = SSL_set_quic_transport_params(tls->ssl, cfg->trans_params, cfg->trans_params_len);
if (ssl_ret != XQC_SSL_SUCCESS) {
xqc_log(tls->log, XQC_LOG_ERROR, "|set transport params error|%s|",
ERR_error_string(ERR_get_error(), NULL));
ret = -XQC_TLS_INTERNAL;
goto end;
}
/* the difference of initialization between client and server */
if (tls->type == XQC_TLS_TYPE_SERVER) {
ret = xqc_tls_init_server_ssl(tls, cfg);
} else {
ret = xqc_tls_init_client_ssl(tls, cfg);
}
end:
return ret;
}
xqc_int_t
xqc_tls_update_tp(xqc_tls_t *tls, uint8_t *tp_buf, size_t tp_len)
{
xqc_int_t ret = XQC_OK;
int ssl_ret;
/* set local transport parameter */
ssl_ret = SSL_set_quic_transport_params(tls->ssl, tp_buf, tp_len);
if (ssl_ret != XQC_SSL_SUCCESS) {
xqc_log(tls->log, XQC_LOG_ERROR, "|set transport params error|%s|",
ERR_error_string(ERR_get_error(), NULL));
ret = -XQC_TLS_INTERNAL;
}
return ret;
}
xqc_tls_t *
xqc_tls_create(xqc_tls_ctx_t *ctx, xqc_tls_config_t *cfg, xqc_log_t *log, void *user_data)
{
xqc_int_t ret;
xqc_tls_t *tls = xqc_calloc(1, sizeof(xqc_tls_t));
if (NULL == tls) {
return NULL;
}
/* set upper layer callback functions */
xqc_tls_ctx_get_tls_callbacks(ctx, &tls->cbs);
tls->type = xqc_tls_ctx_get_type(ctx);
tls->ctx = ctx;
tls->log = log;
tls->user_data = user_data;
tls->cert_verify_flag = cfg->cert_verify_flag;
tls->no_crypto = cfg->no_crypto_flag;
tls->key_update_confirmed = XQC_TRUE;
/* init ssl with input config */
ret = xqc_tls_create_ssl(tls, cfg);
if (XQC_OK != ret) {
goto fail;
}
return tls;
fail:
xqc_tls_destroy(tls);
return NULL;
}
/* try to get transport parameter bytes from ssl and notify to Transport layer */
void
xqc_tls_process_trans_param(xqc_tls_t *tls)
{
const uint8_t *peer_tp;
size_t tp_len = 0;
if (tls->flag & XQC_TLS_FLAG_TRANSPORT_PARAM_RCVD) {
/* transport parameter already known, need no more process */
return;
}
/* get buffer */
SSL_get_peer_quic_transport_params(tls->ssl, &peer_tp, &tp_len);
if (tp_len <= 0) {
return;
}
/* callback to Transport layer */
if (tls->cbs->tp_cb) {
tls->cbs->tp_cb(peer_tp, tp_len, tls->user_data);
}
tls->flag |= XQC_TLS_FLAG_TRANSPORT_PARAM_RCVD;
}
xqc_int_t
xqc_tls_do_handshake(xqc_tls_t *tls)
{
xqc_ssl_handshake_res_t res = xqc_ssl_do_handshake(tls->ssl, tls->user_data, tls->log);
xqc_log(tls->log, XQC_LOG_DEBUG, "|TLS handshake|ret:%d|", res);
if (res == XQC_SSL_HSK_RES_FAIL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|TLS handshake error:%s|",
ERR_error_string(ERR_get_error(), NULL));
return -XQC_TLS_INTERNAL;
}
if (res == XQC_SSL_HSK_RES_WAIT) {
return XQC_OK;
}
/* SSL_do_handshake returns 1 when handshake has completed or False Started */
tls->flag |= XQC_TLS_FLAG_HSK_COMPLETED;
if (tls->cbs->hsk_completed_cb) {
tls->cbs->hsk_completed_cb(tls->user_data);
}
return XQC_OK;
}
xqc_int_t
xqc_tls_derive_and_install_initial_keys(xqc_tls_t *tls, const xqc_cid_t *odcid)
{
xqc_int_t ret;
xqc_crypto_t *init_crypto = tls->crypto[XQC_ENC_LEV_INIT];
/* initial secret for packet protection client/server */
uint8_t cli_initial_secret[INITIAL_SECRET_MAX_LEN] = {0};
uint8_t svr_initial_secret[INITIAL_SECRET_MAX_LEN] = {0};
/* derive initial key */
ret = xqc_crypto_derive_initial_secret(cli_initial_secret, INITIAL_SECRET_MAX_LEN,
svr_initial_secret, INITIAL_SECRET_MAX_LEN,
odcid, xqc_crypto_initial_salt[tls->version],
strlen(xqc_crypto_initial_salt[tls->version]));
if (XQC_OK != ret) {
xqc_log(tls->log, XQC_LOG_ERROR, "|derive initial secret error|ret:%d", ret);
return ret;
}
/* install initial rx/tx keys */
if (tls->type == XQC_TLS_TYPE_CLIENT) {
/* client use client initial secret to derive tx key */
ret = xqc_crypto_derive_keys(init_crypto, cli_initial_secret, INITIAL_SECRET_MAX_LEN,
XQC_KEY_TYPE_TX_WRITE);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|client install initial write key error");
return ret;
}
/* client use server initial secret to derive rx key */
ret = xqc_crypto_derive_keys(init_crypto, svr_initial_secret, INITIAL_SECRET_MAX_LEN,
XQC_KEY_TYPE_RX_READ);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|client install initial read key error");
return ret;
}
} else {
/* server use server initial secret to derive tx key */
ret = xqc_crypto_derive_keys(init_crypto, svr_initial_secret, INITIAL_SECRET_MAX_LEN,
XQC_KEY_TYPE_TX_WRITE);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|server install initial write key error");
return ret;
}
/* server use client initial secret to derive rx key */
ret = xqc_crypto_derive_keys(init_crypto, cli_initial_secret, INITIAL_SECRET_MAX_LEN,
XQC_KEY_TYPE_RX_READ);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|client install initial read key error");
return ret;
}
}
return XQC_OK;
}
xqc_int_t
xqc_tls_init_client(xqc_tls_t *tls, const xqc_cid_t *odcid)
{
xqc_int_t ret = xqc_tls_derive_and_install_initial_keys(tls, odcid);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|derive initial keys error|ret:%d", ret);
return ret;
}
/* do handshake to generate ClientHello */
return xqc_tls_do_handshake(tls);
}
xqc_int_t
xqc_tls_init_server(xqc_tls_t *tls, const xqc_cid_t *odcid)
{
return xqc_tls_derive_and_install_initial_keys(tls, odcid);
}
xqc_int_t
xqc_tls_init(xqc_tls_t *tls, xqc_proto_version_t version, const xqc_cid_t *odcid)
{
tls->version = version;
/* set the quic_transport_parameters extension codepoint */
SSL_set_quic_use_legacy_codepoint(tls->ssl, tls->version != XQC_VERSION_V1);
/* create init level crypto */
tls->crypto[XQC_ENC_LEV_INIT] = xqc_crypto_create(XQC_TLS13_AES_128_GCM_SHA256, tls->log);
if (NULL == tls->crypto[XQC_ENC_LEV_INIT]) {
xqc_log(tls->log, XQC_LOG_ERROR, "|create init level crypto error|");
return -XQC_TLS_NOMEM;
}
/* derive and install initial keys */
if (tls->type == XQC_TLS_TYPE_SERVER) {
return xqc_tls_init_server(tls, odcid);
} else {
return xqc_tls_init_client(tls, odcid);
}
return XQC_OK;
}
xqc_int_t
xqc_tls_reset_initial(xqc_tls_t *tls, xqc_proto_version_t version, const xqc_cid_t *odcid)
{
tls->version = version;
if (tls->crypto[XQC_ENC_LEV_INIT] == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|tls instance is not inited");
return -XQC_TLS_INVALID_STATE;
}
return xqc_tls_derive_and_install_initial_keys(tls, odcid);
}
void
xqc_tls_destroy(xqc_tls_t *tls)
{
if (tls) {
if (tls->ssl) {
SSL_free(tls->ssl);
}
for (xqc_encrypt_level_t lv = 0; lv < XQC_ENC_LEV_MAX; lv++) {
xqc_crypto_destroy(tls->crypto[lv]);
}
xqc_free(tls);
}
}
xqc_int_t
xqc_tls_process_crypto_data(xqc_tls_t *tls, xqc_encrypt_level_t level,
const uint8_t *crypto_data, size_t data_len)
{
SSL *ssl = tls->ssl;
int ret;
int err;
xqc_log(tls->log, XQC_LOG_DEBUG, "|xqc_tls_process_crypto_data|level:%d|%zu|", level, data_len);
if (SSL_provide_quic_data(ssl, (enum ssl_encryption_level_t)level, crypto_data, data_len)
!= XQC_SSL_SUCCESS)
{
xqc_log(tls->log, XQC_LOG_ERROR, "|SSL_provide_quic_data failed|level:%d|%s|",
level, ERR_error_string(ERR_get_error(), NULL));
return -XQC_TLS_INTERNAL;
}
if (!(tls->flag & XQC_TLS_FLAG_HSK_COMPLETED)) {
/* handshake not completed, continue handshake */
if (xqc_tls_do_handshake(tls) != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|xqc_do_handshake failed |");
return -XQC_TLS_DO_HANDSHAKE_ERROR;
}
} else {
/* handshake finished, process NewSessionTicket */
ret = SSL_process_quic_post_handshake(ssl);
if (ret != XQC_SSL_SUCCESS) {
err = SSL_get_error(ssl, ret);
switch (err) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return XQC_OK;
case SSL_ERROR_SSL:
case SSL_ERROR_ZERO_RETURN:
default:
xqc_log(tls->log, XQC_LOG_ERROR, "|SSL_process_quic_post_handshake failed|%s",
ERR_error_string(ERR_get_error(), NULL));
return -XQC_TLS_POST_HANDSHAKE_ERROR;
}
}
}
return XQC_OK;
}
xqc_int_t
xqc_tls_encrypt_header(xqc_tls_t *tls, xqc_encrypt_level_t level,
xqc_pkt_type_t pkt_type, uint8_t *header, uint8_t *pktno, uint8_t *end)
{
xqc_crypto_t *crypto = tls->crypto[level];
if (crypto == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|crypto not initialized|level:%d|", level);
return -XQC_TLS_INVALID_STATE;
}
return xqc_crypto_encrypt_header(crypto, pkt_type, header, pktno, end);
}
xqc_int_t
xqc_tls_encrypt_payload(xqc_tls_t *tls, xqc_encrypt_level_t level,
uint64_t pktno, uint32_t path_id,
uint8_t *header, size_t header_len, uint8_t *payload, size_t payload_len,
uint8_t *dst, size_t dst_cap, size_t *dst_len)
{
xqc_crypto_t *crypto = tls->crypto[level];
if (crypto == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|crypto not initialized|level:%d|", level);
return -XQC_TLS_INVALID_STATE;
}
xqc_uint_t key_phase = 0;
if (level == XQC_ENC_LEV_1RTT) {
key_phase = XQC_PACKET_SHORT_HEADER_KEY_PHASE(header);
if (key_phase >= XQC_KEY_PHASE_CNT) {
xqc_log(tls->log, XQC_LOG_ERROR, "|illegal key phase|key_phase:%ui|", key_phase);
return -XQC_TLS_INVALID_STATE;
}
}
return xqc_crypto_encrypt_payload(crypto, pktno, key_phase, path_id,
header, header_len, payload, payload_len,
dst, dst_cap, dst_len);
}
xqc_int_t
xqc_tls_decrypt_header(xqc_tls_t *tls, xqc_encrypt_level_t level,
xqc_pkt_type_t pkt_type, uint8_t *header, uint8_t *pktno, uint8_t *end)
{
xqc_crypto_t *crypto = tls->crypto[level];
if (crypto == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|crypto not initialized|level:%d|", level);
return -XQC_TLS_INVALID_STATE;
}
return xqc_crypto_decrypt_header(crypto, pkt_type, header, pktno, end);
}
xqc_int_t
xqc_tls_decrypt_payload(xqc_tls_t *tls, xqc_encrypt_level_t level,
uint64_t pktno, uint32_t path_id,
uint8_t *header, size_t header_len, uint8_t *payload, size_t payload_len,
uint8_t *dst, size_t dst_cap, size_t *dst_len)
{
xqc_crypto_t *crypto = tls->crypto[level];
if (crypto == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|crypto not initialized|level:%d|", level);
return -XQC_TLS_INVALID_STATE;
}
xqc_uint_t key_phase = 0;
if (level == XQC_ENC_LEV_1RTT) {
key_phase = XQC_PACKET_SHORT_HEADER_KEY_PHASE(header);
if (key_phase >= XQC_KEY_PHASE_CNT) {
xqc_log(tls->log, XQC_LOG_ERROR, "|illegal key phase|key_phase:%ui|", key_phase);
return -XQC_TLS_INVALID_STATE;
}
}
return xqc_crypto_decrypt_payload(crypto, pktno, key_phase, path_id,
header, header_len, payload, payload_len,
dst, dst_cap, dst_len);
}
xqc_bool_t
xqc_tls_is_key_ready(xqc_tls_t *tls, xqc_encrypt_level_t level, xqc_key_type_t key_type)
{
if (NULL == tls->crypto[level]) {
return XQC_FALSE;
}
return xqc_crypto_is_key_ready(tls->crypto[level], key_type);
}
uint32_t
xqc_tls_get_cipher_id(SSL *ssl, const SSL_CIPHER *cipher, xqc_encrypt_level_t level, xqc_bool_t no_crypto)
{
if (no_crypto == XQC_TRUE
&& (level == XQC_ENC_LEV_0RTT || level == XQC_ENC_LEV_1RTT))
{
return NID_undef;
}
if (cipher != NULL) {
return SSL_CIPHER_get_id(cipher);
}
return SSL_CIPHER_get_id(SSL_get_current_cipher(ssl));
}
xqc_tls_early_data_accept_t
xqc_tls_is_early_data_accepted(xqc_tls_t *tls)
{
/* client set no session ticket */
if (tls->type == XQC_TLS_TYPE_CLIENT && !tls->resumption) {
return XQC_TLS_NO_EARLY_DATA;
}
return xqc_ssl_is_early_data_accepted(tls->ssl)
? XQC_TLS_EARLY_DATA_ACCEPT : XQC_TLS_EARLY_DATA_REJECT;
}
xqc_bool_t
xqc_tls_is_ready_to_send_early_data(xqc_tls_t *tls)
{
if (tls->resumption == XQC_FALSE) {
/* server will always be false */
return XQC_FALSE;
}
/* ready to send when 0rtt tx key is ready */
return xqc_tls_is_key_ready(tls, XQC_ENC_LEV_0RTT, XQC_KEY_TYPE_TX_WRITE);
}
ssize_t
xqc_tls_aead_tag_len(xqc_tls_t *tls, xqc_encrypt_level_t level)
{
xqc_crypto_t *crypto = tls->crypto[level];
if (crypto == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|crypto not initialized|level:%d|", level);
return -XQC_TLS_INVALID_STATE;
}
return xqc_crypto_aead_tag_len(crypto);
}
void
xqc_tls_set_no_crypto(xqc_tls_t *tls)
{
tls->no_crypto = XQC_TRUE;
}
void
xqc_tls_set_1rtt_key_phase(xqc_tls_t *tls, xqc_uint_t key_phase)
{
tls->crypto[XQC_ENC_LEV_1RTT]->key_phase = key_phase;
}
xqc_bool_t
xqc_tls_is_key_update_confirmed(xqc_tls_t *tls)
{
return tls->key_update_confirmed;
}
xqc_int_t
xqc_tls_update_1rtt_keys(xqc_tls_t *tls, xqc_key_type_t type)
{
xqc_crypto_t *crypto = tls->crypto[XQC_ENC_LEV_1RTT];
if (crypto == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|invalid state|1rtt crypto is null|");
return -XQC_TLS_UPDATE_KEY_ERROR;
}
xqc_int_t ret = xqc_crypto_derive_updated_keys(crypto, type);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|derive write keys error|");
return -XQC_TLS_UPDATE_KEY_ERROR;
}
if (type == XQC_KEY_TYPE_RX_READ) {
tls->key_update_confirmed = XQC_FALSE;
} else if (type == XQC_KEY_TYPE_TX_WRITE) {
tls->key_update_confirmed = XQC_TRUE;
}
return XQC_OK;
}
void
xqc_tls_discard_old_1rtt_keys(xqc_tls_t *tls)
{
xqc_crypto_discard_old_keys(tls->crypto[XQC_ENC_LEV_1RTT]);
}
xqc_int_t
xqc_tls_cal_retry_integrity_tag(xqc_tls_t *tls,
uint8_t *retry_pseudo_packet, size_t retry_pseudo_packet_len,
uint8_t *dst, size_t dst_cap, size_t *dst_len)
{
xqc_int_t ret = XQC_OK;
xqc_crypto_t *crypto = xqc_crypto_create(XQC_TLS13_AES_128_GCM_SHA256, tls->log);
if (crypto == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|create retry crypto error|");
return -XQC_TLS_NOMEM;
}
xqc_proto_version_t ver = tls->version;
ret = xqc_crypto_aead_encrypt(crypto, "", 0,
xqc_crypto_retry_key[ver], strlen(xqc_crypto_retry_key[ver]),
xqc_crypto_retry_nonce[ver], strlen(xqc_crypto_retry_nonce[ver]),
retry_pseudo_packet, retry_pseudo_packet_len,
dst, dst_cap, dst_len);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|calculate retry integrity tag error|");
}
xqc_crypto_destroy(crypto);
return ret;
}
/**
* ============================================================================
* callback functions to upper layer
* ============================================================================
*/
void
xqc_ssl_keylog_cb(const SSL *ssl, const char *line)
{
xqc_tls_t *tls = (xqc_tls_t *)SSL_get_app_data(ssl);
if (tls->cbs->keylog_cb) {
tls->cbs->keylog_cb(line, tls->user_data);
}
}
int
xqc_ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
xqc_tls_t *tls = (xqc_tls_t *)SSL_get_app_data(ssl);
/* get configured alpn_list */
xqc_engine_ssl_config_t *cfg = NULL;
xqc_tls_ctx_get_cfg(tls->ctx, &cfg);
/* get alpn list */
uint8_t *alpn_list = NULL;
size_t alpn_list_len = 0;
xqc_tls_ctx_get_alpn_list(tls->ctx, &alpn_list, &alpn_list_len);
/* select alp */
if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_list, alpn_list_len, in, inlen)
!= OPENSSL_NPN_NEGOTIATED)
{
xqc_log(tls->log, XQC_LOG_ERROR, "|select proto error|in:%*s",
(size_t)inlen, in);
return SSL_TLSEXT_ERR_NOACK;
}
/* notify alpn selection to upper layer */
const unsigned char *alpn = (uint8_t *)(*out);
size_t alpn_len = *outlen;
xqc_int_t ret = tls->cbs->alpn_select_cb(alpn, alpn_len, tls->user_data);
if (XQC_OK != ret) {
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
xqc_log_event(tls->log, TRA_ALPN_INFORMATION, alpn_list, alpn_list_len, in,
inlen, alpn, alpn_len);
return SSL_TLSEXT_ERR_OK;
}
int
xqc_ssl_session_ticket_key_cb(SSL *ssl, uint8_t *key_name, uint8_t *iv,
EVP_CIPHER_CTX *cipher_ctx, HMAC_CTX *hmac_ctx, int encrypt)
{
size_t size = 0;
const EVP_CIPHER *cipher = NULL;
const EVP_MD *digest = EVP_sha256();
xqc_tls_t *tls = (xqc_tls_t *)SSL_get_app_data(ssl);
/* get session ticket key */
xqc_ssl_session_ticket_key_t *key = NULL;
xqc_tls_ctx_get_session_ticket_key(tls->ctx, &key);
if (NULL == key) {
xqc_log(tls->log, XQC_LOG_ERROR, "|get session ticket key failed|");
return -1;
}
if (encrypt == 1) {
/* encrypt session ticket, returns 1 on success and -1 on error */
if (key->size == 48) {
cipher = EVP_aes_128_cbc();
size = 16;
} else {
cipher = EVP_aes_256_cbc();
size = 32;
}
if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
xqc_log(tls->log, XQC_LOG_ERROR, "|RAND_bytes() failed|");
return -1;
}
if (EVP_EncryptInit_ex(cipher_ctx, cipher, NULL, key->aes_key, iv) != 1) {
xqc_log(tls->log, XQC_LOG_ERROR, "|EVP_EncryptInit_ex() failed|");
return -1;
}
if (HMAC_Init_ex(hmac_ctx, key->hmac_key, size, digest, NULL) != 1) {
xqc_log(tls->log, XQC_LOG_ERROR, "|HMAC_Init_ex() failed|");
return -1;
}
memcpy(key_name, key->name, 16);
} else {
/*
* decrypt session ticket, returns -1 to abort the handshake,
* 0 if decrypting the ticket failed, and 1 or 2 on success
*/
if (memcmp(key_name, key->name, 16) != 0) {
xqc_log(tls->log, XQC_LOG_ERROR, "|ssl session ticket decrypt, key name not match|");
return -1;
}
if (key->size == 48) {
cipher = EVP_aes_128_cbc();
size = 16;
} else {
cipher = EVP_aes_256_cbc();
size = 32;
}
if (HMAC_Init_ex(hmac_ctx, key->hmac_key, size, digest, NULL) != 1) {
xqc_log(tls->log, XQC_LOG_ERROR, "|HMAC_Init_ex() failed|");
return 0;
}
if (EVP_DecryptInit_ex(cipher_ctx, cipher, NULL, key->aes_key, iv) != 1) {
xqc_log(tls->log, XQC_LOG_ERROR, "|EVP_DecryptInit_ex() failed|");
return 0;
}
}
return 1;
}
int
xqc_ssl_new_session_cb(SSL *ssl, SSL_SESSION *session)
{
xqc_tls_t *tls = (xqc_tls_t *)SSL_get_app_data(ssl);
/* check if early data is enabled */
if (!xqc_ssl_session_is_early_data_enabled(session)) {
xqc_log(tls->log, XQC_LOG_ERROR, "|early data is not enabled|");
goto end;
}
if (tls->cbs->session_cb != NULL) {
char *data = NULL;
BIO *bio = BIO_new(BIO_s_mem());
if (bio == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|save new session error|");
goto end;
}
PEM_write_bio_SSL_SESSION(bio, session);
size_t data_len = BIO_get_mem_data(bio, &data);
if (data_len == 0 || data == NULL) {
xqc_log(tls->log, XQC_LOG_ERROR, "|save new session error|");
} else {
/* callback to upper layer */
tls->cbs->session_cb(data, data_len, tls->user_data);
}
BIO_free(bio);
}
end:
/* return one for taking ownership and zero otherwise */
return 0;
}
int
xqc_ssl_cert_verify_cb(int ok, X509_STORE_CTX *store_ctx)
{
int verify_res = XQC_SSL_SUCCESS;
size_t certs_array_len = 0;
unsigned char *certs_array[XQC_MAX_VERIFY_DEPTH] = {0};
size_t certs_len[XQC_MAX_VERIFY_DEPTH] = {0};
if (ok == XQC_SSL_SUCCESS) {
return XQC_SSL_SUCCESS;
}
SSL *ssl = X509_STORE_CTX_get_ex_data(store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
if (ssl == NULL) {
return XQC_SSL_FAIL;
}
xqc_tls_t *tls = (xqc_tls_t *)SSL_get_app_data(ssl);
if (tls == NULL) {
return XQC_SSL_FAIL;
}
int err_code = X509_STORE_CTX_get_error(store_ctx);
if (err_code != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
&& err_code != X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
&& !((tls->cert_verify_flag & XQC_TLS_CERT_FLAG_ALLOW_SELF_SIGNED) != 0
&& XQC_TLS_SELF_SIGNED_CERT(err_code)))
{
xqc_log(tls->log, XQC_LOG_ERROR, "|certificate verify failed with err_code:%d|", err_code);
if (tls->cbs->error_cb) {
tls->cbs->error_cb(err_code, tls->user_data);
}
return XQC_SSL_FAIL;
}
/* get certs array */
xqc_int_t ret = xqc_ssl_get_certs_array(ssl, store_ctx, certs_array, XQC_MAX_VERIFY_DEPTH,
&certs_array_len, certs_len);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|get cert array error|%d|", ret);
if (tls->cbs->error_cb) {
tls->cbs->error_cb(err_code, tls->user_data);
}
verify_res = XQC_SSL_FAIL;
goto end;
}
/* callback to upper layer */
if (tls->cbs->cert_verify_cb != NULL) {
if (tls->cbs->cert_verify_cb((const unsigned char **)certs_array, certs_len,
certs_array_len, tls->user_data) != XQC_OK)
{
verify_res = XQC_SSL_FAIL;
} else {
verify_res = XQC_SSL_SUCCESS;
}
}
end:
xqc_ssl_free_certs_array(certs_array, certs_array_len);
return verify_res;
}
int
xqc_ssl_cert_cb(SSL *ssl, void *arg)
{
int ssl_ret = XQC_SSL_SUCCESS;
xqc_int_t ret = XQC_OK;
const char *hostname = NULL;
STACK_OF(X509) *chain = NULL;
X509 *crt = NULL;
EVP_PKEY *key = NULL;
xqc_tls_t *tls = (xqc_tls_t *)SSL_get_app_data(ssl);
if (tls == NULL) {
return XQC_SSL_FAIL;
}
hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (NULL == hostname) {
xqc_log(tls->log, XQC_LOG_ERROR, "|hostname is NULL");
return XQC_SSL_FAIL;
}
/* callback to upper layer to get SSL_CTX */
if (tls->cbs->cert_cb) {
/* if error returned by upper layer, use default ssl_ctx */
ret = tls->cbs->cert_cb(hostname, (void **)&chain, (void **)&crt,
(void **)&key, tls->user_data);
if (XQC_OK != ret) {
xqc_log(tls->log, XQC_LOG_INFO, "|cert cb error|sni:%s|ret:%d",
hostname, ret);
goto end;
}
/* if no cert or key returned, use default ssl_ctx */
if (NULL == crt || NULL == key) {
xqc_log(tls->log, XQC_LOG_INFO, "|cert chain or key empty|sni:%s",
hostname);
goto end;
}
ssl_ret = SSL_set1_chain(ssl, chain);
if (ssl_ret != XQC_SSL_SUCCESS) {
xqc_log(tls->log, XQC_LOG_ERROR,
"|set chain error|sni:%s|ret:%d", hostname, ssl_ret);
return XQC_SSL_FAIL;
}
ssl_ret = SSL_use_certificate(ssl, crt);
if (ssl_ret != XQC_SSL_SUCCESS) {
xqc_log(tls->log, XQC_LOG_ERROR,
"|set certificate error|sni:%s|ret:%d", hostname, ssl_ret);
return XQC_SSL_FAIL;
}
ssl_ret = SSL_use_PrivateKey(ssl, key);
if (ssl_ret != XQC_SSL_SUCCESS) {
xqc_log(tls->log, XQC_LOG_ERROR,
"|set key error|sni:%s|ret:%d", hostname, ssl_ret);
return XQC_SSL_FAIL;
}
}
end:
return XQC_SSL_SUCCESS;
}
void
xqc_tls_get_selected_alpn(xqc_tls_t *tls, const char **out_alpn,
size_t *out_len)
{
if (NULL == tls) {
return;
}
SSL_get0_alpn_selected(tls->ssl, (const uint8_t **)out_alpn,
(unsigned *)out_len);
}
void *
xqc_tls_get_ssl(xqc_tls_t *tls)
{
if (!tls) {
return NULL;
}
return tls->ssl;
}
/**
* ============================================================================
* quic method callback functions
* ============================================================================
*/
int
xqc_tls_set_read_secret(SSL *ssl, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
{
xqc_int_t ret;
xqc_tls_t *tls = SSL_get_app_data(ssl);
/* try to process transport parameter if ssl parsed the quic_transport_params extension */
xqc_tls_process_trans_param(tls);
/* create crypto instance if not created */
if (NULL == tls->crypto[level]) {
tls->crypto[level] = xqc_crypto_create(
xqc_tls_get_cipher_id(ssl, cipher, (xqc_encrypt_level_t)level, tls->no_crypto), tls->log);
if (NULL == tls->crypto[level]) {
xqc_log(tls->log, XQC_LOG_ERROR, "|create crypto error");
return XQC_SSL_FAIL;
}
}
/* save application traffic secret */
if (level == ssl_encryption_application) {
ret = xqc_crypto_save_application_traffic_secret_0(tls->crypto[level], secret,
secret_len, XQC_KEY_TYPE_RX_READ);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR,
"|save application traffic secret error|level:%d|ret:%d", level, ret);
return XQC_SSL_FAIL;
}
}
/* derive and install read key */
xqc_crypto_t *crypto = tls->crypto[level];
ret = xqc_crypto_derive_keys(crypto, secret, secret_len, XQC_KEY_TYPE_RX_READ);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|install write key error|level:%d|ret:%d", level, ret);
return XQC_SSL_FAIL;
}
return XQC_SSL_SUCCESS;
}
int
xqc_tls_set_write_secret(SSL *ssl, enum ssl_encryption_level_t level,
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
{
xqc_int_t ret;
xqc_tls_t *tls = SSL_get_app_data(ssl);
/* try to process transport parameter if ssl parsed the quic_transport_params extension */
xqc_tls_process_trans_param(tls);
/* create crypto instance if not created */
if (NULL == tls->crypto[level]) {
tls->crypto[level] = xqc_crypto_create(
xqc_tls_get_cipher_id(ssl, cipher, (xqc_encrypt_level_t)level, tls->no_crypto), tls->log);
if (NULL == tls->crypto[level]) {
xqc_log(tls->log, XQC_LOG_ERROR, "|create crypto error");
return XQC_SSL_FAIL;
}
}
/* save application traffic secret */
if (level == ssl_encryption_application) {
ret = xqc_crypto_save_application_traffic_secret_0(tls->crypto[level], secret,
secret_len, XQC_KEY_TYPE_TX_WRITE);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR,
"|save application traffic secret error|level:%d|ret:%d", level, ret);
return XQC_SSL_FAIL;
}
}
/* derive and install write key */
xqc_crypto_t *crypto = tls->crypto[level];
ret = xqc_crypto_derive_keys(crypto, secret, secret_len, XQC_KEY_TYPE_TX_WRITE);
if (ret != XQC_OK) {
xqc_log(tls->log, XQC_LOG_ERROR, "|install write key error|level:%d|ret:%d", level, ret);
return XQC_SSL_FAIL;
}
return XQC_SSL_SUCCESS;
}
int
xqc_tls_add_handshake_data(SSL *ssl, enum ssl_encryption_level_t level,
const uint8_t *data, size_t len)
{
xqc_tls_t *tls = SSL_get_app_data(ssl);
/* notify tls handshake data to upper layer */
if (tls->cbs->crypto_data_cb) {
if (tls->cbs->crypto_data_cb((xqc_encrypt_level_t)level, data, len,
tls->user_data) != XQC_OK)
{
xqc_log(tls->log, XQC_LOG_ERROR, "|crypto_data_cb error|");
return XQC_SSL_FAIL;
}
}
return XQC_SSL_SUCCESS;
}
int
xqc_tls_flush_flight(SSL *ssl)
{
return XQC_SSL_SUCCESS;
}
int
xqc_tls_send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert)
{
xqc_tls_t *tls = SSL_get_app_data(ssl);
xqc_log(tls->log, XQC_LOG_ERROR, "|ssl alert|level:%d|alert:%d|error:%s",
level, alert, ERR_error_string(ERR_get_error(), NULL));
/* callback to upper layer. */
if (tls->cbs->error_cb) {
tls->cbs->error_cb(alert, tls->user_data);
}
return XQC_SSL_SUCCESS;
}
SSL_QUIC_METHOD xqc_ssl_quic_method = {
.set_read_secret = xqc_tls_set_read_secret,
.set_write_secret = xqc_tls_set_write_secret,
.add_handshake_data = xqc_tls_add_handshake_data,
.flush_flight = xqc_tls_flush_flight,
.send_alert = xqc_tls_send_alert,
};