aboutsummaryrefslogtreecommitdiff
path: root/crypto/openssl/ssl/quic/quic_tls.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssl/ssl/quic/quic_tls.c')
-rw-r--r--crypto/openssl/ssl/quic/quic_tls.c949
1 files changed, 949 insertions, 0 deletions
diff --git a/crypto/openssl/ssl/quic/quic_tls.c b/crypto/openssl/ssl/quic/quic_tls.c
new file mode 100644
index 000000000000..ccc053203d4f
--- /dev/null
+++ b/crypto/openssl/ssl/quic/quic_tls.c
@@ -0,0 +1,949 @@
+/*
+ * Copyright 2022-2025 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 <openssl/ssl.h>
+#include "internal/recordmethod.h"
+#include "internal/quic_tls.h"
+#include "../ssl_local.h"
+#include "internal/quic_record_util.h"
+#include "internal/quic_error.h"
+#include "internal/quic_types.h"
+#include "internal/ssl_unwrap.h"
+
+#define QUIC_TLS_FATAL(rl, ad, err) \
+ do { \
+ if ((rl) != NULL) (rl)->alert = (ad); \
+ ERR_raise(ERR_LIB_SSL, (err)); \
+ if ((rl) != NULL) (rl)->qtls->inerror = 1; \
+ } while(0)
+
+struct quic_tls_st {
+ QUIC_TLS_ARGS args;
+
+ /*
+ * Transport parameters which client should send. Buffer lifetime must
+ * exceed the lifetime of the QUIC_TLS object.
+ */
+ const unsigned char *local_transport_params;
+ size_t local_transport_params_len;
+
+ ERR_STATE *error_state;
+
+ /*
+ * QUIC error code (usually in the TLS Alert-mapped CRYPTO_ERR range). Valid
+ * only if inerror is 1.
+ */
+ uint64_t error_code;
+
+ /*
+ * Error message with static storage duration. Valid only if inerror is 1.
+ * Should be suitable for encapsulation in a CONNECTION_CLOSE frame.
+ */
+ const char *error_msg;
+
+ /* Whether our SSL object for TLS has been configured for use in QUIC */
+ unsigned int configured : 1;
+
+ /* Set if we have hit any error state */
+ unsigned int inerror : 1;
+
+ /* Set if the handshake has completed */
+ unsigned int complete : 1;
+
+ /* Set if we have consumed the local transport parameters yet. */
+ unsigned int local_transport_params_consumed : 1;
+};
+
+struct ossl_record_layer_st {
+ QUIC_TLS *qtls;
+
+ /* Protection level */
+ int level;
+
+ /* Only used for retry flags */
+ BIO *dummybio;
+
+ /* Number of bytes written so far if we are part way through a write */
+ size_t written;
+
+ /* If we are part way through a write, a copy of the template */
+ OSSL_RECORD_TEMPLATE template;
+
+ /*
+ * If we hit an error, what alert code should be used
+ */
+ int alert;
+
+ /* Amount of crypto stream data we read in the last call to quic_read_record */
+ size_t recread;
+
+ /* Amount of crypto stream data read but not yet released */
+ size_t recunreleased;
+
+ /* Callbacks */
+ OSSL_FUNC_rlayer_msg_callback_fn *msg_callback;
+ void *cbarg;
+};
+
+static int quic_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio);
+static int quic_free(OSSL_RECORD_LAYER *r);
+
+static int
+quic_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
+ int role, int direction, int level, uint16_t epoch,
+ unsigned char *secret, size_t secretlen,
+ unsigned char *key, size_t keylen, unsigned char *iv,
+ size_t ivlen, unsigned char *mackey, size_t mackeylen,
+ const EVP_CIPHER *ciph, size_t taglen,
+ int mactype,
+ const EVP_MD *md, COMP_METHOD *comp,
+ const EVP_MD *kdfdigest, BIO *prev, BIO *transport,
+ BIO *next, BIO_ADDR *local, BIO_ADDR *peer,
+ const OSSL_PARAM *settings, const OSSL_PARAM *options,
+ const OSSL_DISPATCH *fns, void *cbarg, void *rlarg,
+ OSSL_RECORD_LAYER **retrl)
+{
+ OSSL_RECORD_LAYER *rl = OPENSSL_zalloc(sizeof(*rl));
+ int qdir;
+ uint32_t suite_id = 0;
+
+ if (rl == NULL) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ rl->qtls = (QUIC_TLS *)rlarg;
+ rl->level = level;
+ if (!quic_set1_bio(rl, transport)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ rl->cbarg = cbarg;
+ *retrl = rl;
+
+ if (fns != NULL) {
+ for (; fns->function_id != 0; fns++) {
+ switch (fns->function_id) {
+ break;
+ case OSSL_FUNC_RLAYER_MSG_CALLBACK:
+ rl->msg_callback = OSSL_FUNC_rlayer_msg_callback(fns);
+ break;
+ default:
+ /* Just ignore anything we don't understand */
+ break;
+ }
+ }
+ }
+
+ if (level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
+ return 1;
+
+ if (direction == OSSL_RECORD_DIRECTION_READ)
+ qdir = 0;
+ else
+ qdir = 1;
+
+ if (rl->qtls->args.ossl_quic) {
+#ifndef OPENSSL_NO_QUIC
+ /*
+ * We only look up the suite_id/MD for internal callers. Not used in the
+ * public API. We assume that a 3rd party QUIC stack will want to
+ * figure this out by itself (e.g. so that they could add new
+ * ciphersuites at a different pace to us)
+ */
+ if (EVP_CIPHER_is_a(ciph, "AES-128-GCM")) {
+ suite_id = QRL_SUITE_AES128GCM;
+ } else if (EVP_CIPHER_is_a(ciph, "AES-256-GCM")) {
+ suite_id = QRL_SUITE_AES256GCM;
+ } else if (EVP_CIPHER_is_a(ciph, "CHACHA20-POLY1305")) {
+ suite_id = QRL_SUITE_CHACHA20POLY1305;
+ } else {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, SSL_R_UNKNOWN_CIPHER_TYPE);
+ goto err;
+ }
+
+ /* We pass a ref to the md in a successful yield_secret_cb call */
+ /* TODO(QUIC FUTURE): This cast is horrible. We should try and remove it */
+ if (!EVP_MD_up_ref((EVP_MD *)kdfdigest)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+#else
+ if (!ossl_assert("Should not happen" == NULL))
+ goto err;
+#endif
+ } else {
+ kdfdigest = NULL;
+ }
+
+ if (!rl->qtls->args.yield_secret_cb(level, qdir, suite_id,
+ (EVP_MD *)kdfdigest, secret, secretlen,
+ rl->qtls->args.yield_secret_cb_arg)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ EVP_MD_free((EVP_MD *)kdfdigest);
+ goto err;
+ }
+
+ return 1;
+ err:
+ *retrl = NULL;
+ quic_free(rl);
+ return 0;
+}
+
+static int quic_free(OSSL_RECORD_LAYER *rl)
+{
+ if (rl == NULL)
+ return 1;
+
+ BIO_free(rl->dummybio);
+ OPENSSL_free(rl);
+ return 1;
+}
+
+static int quic_unprocessed_read_pending(OSSL_RECORD_LAYER *rl)
+{
+ /*
+ * Read ahead isn't really a thing for QUIC so we never have unprocessed
+ * data pending
+ */
+ return 0;
+}
+
+static int quic_processed_read_pending(OSSL_RECORD_LAYER *rl)
+{
+ /*
+ * This is currently only ever used by:
+ * - SSL_has_pending()
+ * - to check whether we have more records that we want to supply to the
+ * upper layers
+ *
+ * We only ever supply 1 record at a time to the upper layers, and
+ * SSL_has_pending() will go via the QUIC method not the TLS method so that
+ * use case doesn't apply here.
+ * Therefore we can ignore this for now and always return 0. We might
+ * eventually want to change this to check in the receive buffers to see if
+ * we have any more data pending.
+ */
+ return 0;
+}
+
+static size_t quic_get_max_records(OSSL_RECORD_LAYER *rl, uint8_t type,
+ size_t len,
+ size_t maxfrag, size_t *preffrag)
+{
+ return 1;
+}
+
+static int quic_write_records(OSSL_RECORD_LAYER *rl,
+ OSSL_RECORD_TEMPLATE *template,
+ size_t numtempl)
+{
+ size_t consumed;
+ unsigned char alert;
+
+ if (!ossl_assert(numtempl == 1)) {
+ /* How could this be? quic_get_max_records() always returns 1 */
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+
+ BIO_clear_retry_flags(rl->dummybio);
+
+ if (rl->msg_callback != NULL) {
+ unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];
+
+ /*
+ * For the purposes of the callback we "pretend" to be normal TLS,
+ * and manufacture a dummy record header
+ */
+ dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
+ ? template->type
+ : SSL3_RT_APPLICATION_DATA;
+ dummyrec[1] = (unsigned char)((template->version >> 8) & 0xff);
+ dummyrec[2] = (unsigned char)(template->version & 0xff);
+ /*
+ * We assume that buflen is always <= UINT16_MAX. Since this is
+ * generated by libssl itself we actually expect it to never
+ * exceed SSL3_RT_MAX_PLAIN_LENGTH - so it should be a safe
+ * assumption
+ */
+ dummyrec[3] = (unsigned char)((template->buflen >> 8) & 0xff);
+ dummyrec[4] = (unsigned char)(template->buflen & 0xff);
+
+ rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
+ SSL3_RT_HEADER_LENGTH, rl->cbarg);
+
+ if (rl->level != OSSL_RECORD_PROTECTION_LEVEL_NONE) {
+ rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE,
+ &template->type, 1, rl->cbarg);
+ }
+ }
+
+ switch (template->type) {
+ case SSL3_RT_ALERT:
+ if (template->buflen != 2) {
+ /*
+ * We assume that libssl always sends both bytes of an alert to
+ * us in one go, and never fragments it. If we ever get more
+ * or less bytes than exactly 2 then this is very unexpected.
+ */
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_VALUE);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+ /*
+ * Byte 0 is the alert level (we ignore it) and byte 1 is the alert
+ * description that we are actually interested in.
+ */
+ alert = template->buf[1];
+
+ if (!rl->qtls->args.alert_cb(rl->qtls->args.alert_cb_arg, alert)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+ break;
+
+ case SSL3_RT_HANDSHAKE:
+ /*
+ * We expect this to only fail on some fatal error (e.g. malloc
+ * failure)
+ */
+ if (!rl->qtls->args.crypto_send_cb(template->buf + rl->written,
+ template->buflen - rl->written,
+ &consumed,
+ rl->qtls->args.crypto_send_cb_arg)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+ /*
+ * We might have written less than we wanted to if we have filled the
+ * send stream buffer.
+ */
+ if (consumed + rl->written != template->buflen) {
+ if (!ossl_assert(consumed + rl->written < template->buflen)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+
+ /*
+ * We've not written everything we wanted to. Take a copy of the
+ * template, remember how much we wrote so far and signal a retry.
+ * The buffer supplied in the template is guaranteed to be the same
+ * on a retry for handshake data
+ */
+ rl->written += consumed;
+ rl->template = *template;
+ BIO_set_retry_write(rl->dummybio);
+
+ return OSSL_RECORD_RETURN_RETRY;
+ }
+ rl->written = 0;
+ break;
+
+ default:
+ /* Anything else is unexpected and an error */
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+
+ return OSSL_RECORD_RETURN_SUCCESS;
+}
+
+static int quic_retry_write_records(OSSL_RECORD_LAYER *rl)
+{
+ return quic_write_records(rl, &rl->template, 1);
+}
+
+static int quic_read_record(OSSL_RECORD_LAYER *rl, void **rechandle,
+ int *rversion, uint8_t *type, const unsigned char **data,
+ size_t *datalen, uint16_t *epoch,
+ unsigned char *seq_num)
+{
+ if (rl->recread != 0 || rl->recunreleased != 0)
+ return OSSL_RECORD_RETURN_FATAL;
+
+ BIO_clear_retry_flags(rl->dummybio);
+
+ if (!rl->qtls->args.crypto_recv_rcd_cb(data, datalen,
+ rl->qtls->args.crypto_recv_rcd_cb_arg)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+
+ if (*datalen == 0) {
+ BIO_set_retry_read(rl->dummybio);
+ return OSSL_RECORD_RETURN_RETRY;
+ }
+
+ *rechandle = rl;
+ *rversion = TLS1_3_VERSION;
+ *type = SSL3_RT_HANDSHAKE;
+ rl->recread = rl->recunreleased = *datalen;
+ /* epoch/seq_num are not relevant for TLS */
+
+ if (rl->msg_callback != NULL) {
+ unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];
+
+ /*
+ * For the purposes of the callback we "pretend" to be normal TLS,
+ * and manufacture a dummy record header
+ */
+ dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
+ ? SSL3_RT_HANDSHAKE
+ : SSL3_RT_APPLICATION_DATA;
+ dummyrec[1] = (unsigned char)((TLS1_2_VERSION >> 8) & 0xff);
+ dummyrec[2] = (unsigned char)(TLS1_2_VERSION & 0xff);
+ /*
+ * *datalen will always fit into 2 bytes because our original buffer
+ * size is less than that.
+ */
+ dummyrec[3] = (unsigned char)((*datalen >> 8) & 0xff);
+ dummyrec[4] = (unsigned char)(*datalen & 0xff);
+
+ rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
+ SSL3_RT_HEADER_LENGTH, rl->cbarg);
+ rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE, type, 1,
+ rl->cbarg);
+ }
+
+ return OSSL_RECORD_RETURN_SUCCESS;
+}
+
+static int quic_release_record(OSSL_RECORD_LAYER *rl, void *rechandle,
+ size_t length)
+{
+ if (!ossl_assert(rl->recread > 0)
+ || !ossl_assert(rl->recunreleased <= rl->recread)
+ || !ossl_assert(rl == rechandle)
+ || !ossl_assert(length <= rl->recunreleased)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+
+ if (rl->recunreleased == length) {
+ if (!rl->qtls->args.crypto_release_rcd_cb(rl->recread,
+ rl->qtls->args.crypto_release_rcd_cb_arg)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return OSSL_RECORD_RETURN_FATAL;
+ }
+ rl->recread = 0;
+ }
+ rl->recunreleased -= length;
+ return OSSL_RECORD_RETURN_SUCCESS;
+}
+
+static int quic_get_alert_code(OSSL_RECORD_LAYER *rl)
+{
+ return rl->alert;
+}
+
+static int quic_set_protocol_version(OSSL_RECORD_LAYER *rl, int version)
+{
+ /* We only support TLSv1.3, so its bad if we negotiate anything else */
+ if (!ossl_assert(version == TLS1_3_VERSION)) {
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void quic_set_plain_alerts(OSSL_RECORD_LAYER *rl, int allow)
+{
+ /* We don't care */
+}
+
+static void quic_set_first_handshake(OSSL_RECORD_LAYER *rl, int first)
+{
+ /* We don't care */
+}
+
+static void quic_set_max_pipelines(OSSL_RECORD_LAYER *rl, size_t max_pipelines)
+{
+ /* We don't care */
+}
+
+static void quic_get_state(OSSL_RECORD_LAYER *rl, const char **shortstr,
+ const char **longstr)
+{
+ /*
+ * According to the docs, valid read state strings are: "RH"/"read header",
+ * "RB"/"read body", and "unknown"/"unknown". We don't read records in quite
+ * that way, so we report every "normal" state as "read header". In the
+ * event of error then we report "unknown".
+ */
+
+ if (rl->qtls->inerror) {
+ if (shortstr != NULL)
+ *shortstr = "unknown";
+ if (longstr != NULL)
+ *longstr = "unknown";
+ } else {
+ if (shortstr != NULL)
+ *shortstr = "RH";
+ if (longstr != NULL)
+ *longstr = "read header";
+ }
+}
+
+static int quic_set_options(OSSL_RECORD_LAYER *rl, const OSSL_PARAM *options)
+{
+ /*
+ * We don't support any options yet - but we might do at some point so
+ * this could be useful.
+ */
+ return 1;
+}
+
+static const COMP_METHOD *quic_get_compression(OSSL_RECORD_LAYER *rl)
+{
+ /* We only support TLSv1.3 which doesn't have compression */
+ return NULL;
+}
+
+static void quic_set_max_frag_len(OSSL_RECORD_LAYER *rl, size_t max_frag_len)
+{
+ /* This really doesn't make any sense for QUIC. Ignore it */
+}
+
+static int quic_alloc_buffers(OSSL_RECORD_LAYER *rl)
+{
+ /*
+ * This is a hint only. We don't support it (yet), so just ignore the
+ * request
+ */
+ return 1;
+}
+
+static int quic_free_buffers(OSSL_RECORD_LAYER *rl)
+{
+ /*
+ * This is a hint only. We don't support it (yet), so just ignore the
+ * request
+ */
+ return 1;
+}
+
+static int quic_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio)
+{
+ if (bio != NULL && !BIO_up_ref(bio))
+ return 0;
+ BIO_free(rl->dummybio);
+ rl->dummybio = bio;
+
+ return 1;
+}
+
+/*
+ * Never called functions
+ *
+ * Due to the way we are configured and used we never expect any of the next set
+ * of functions to be called. Therefore we set them to always fail.
+ */
+
+static size_t quic_app_data_pending(OSSL_RECORD_LAYER *rl)
+{
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return (size_t)ossl_assert(0);
+}
+
+static size_t quic_get_max_record_overhead(OSSL_RECORD_LAYER *rl)
+{
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return (size_t)ossl_assert(0);
+}
+
+static int quic_increment_sequence_ctr(OSSL_RECORD_LAYER *rl)
+{
+ QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return ossl_assert(0);
+}
+
+/* End of never called functions */
+
+static const OSSL_RECORD_METHOD quic_tls_record_method = {
+ quic_new_record_layer,
+ quic_free,
+ quic_unprocessed_read_pending,
+ quic_processed_read_pending,
+ quic_app_data_pending, /* Never called */
+ quic_get_max_records,
+ quic_write_records,
+ quic_retry_write_records,
+ quic_read_record,
+ quic_release_record,
+ quic_get_alert_code,
+ quic_set1_bio,
+ quic_set_protocol_version,
+ quic_set_plain_alerts,
+ quic_set_first_handshake,
+ quic_set_max_pipelines,
+ NULL, /* set_in_init: Optional - we don't need it */
+ quic_get_state,
+ quic_set_options,
+ quic_get_compression,
+ quic_set_max_frag_len,
+ quic_get_max_record_overhead, /* Never called */
+ quic_increment_sequence_ctr, /* Never called */
+ quic_alloc_buffers,
+ quic_free_buffers
+};
+
+static int add_transport_params_cb(SSL *s, unsigned int ext_type,
+ unsigned int context,
+ const unsigned char **out, size_t *outlen,
+ X509 *x, size_t chainidx, int *al,
+ void *add_arg)
+{
+ QUIC_TLS *qtls = add_arg;
+
+ *out = qtls->local_transport_params;
+ *outlen = qtls->local_transport_params_len;
+ qtls->local_transport_params_consumed = 1;
+ return 1;
+}
+
+static void free_transport_params_cb(SSL *s, unsigned int ext_type,
+ unsigned int context,
+ const unsigned char *out,
+ void *add_arg)
+{
+}
+
+static int parse_transport_params_cb(SSL *s, unsigned int ext_type,
+ unsigned int context,
+ const unsigned char *in,
+ size_t inlen, X509 *x,
+ size_t chainidx,
+ int *al, void *parse_arg)
+{
+ QUIC_TLS *qtls = parse_arg;
+
+ return qtls->args.got_transport_params_cb(in, inlen,
+ qtls->args.got_transport_params_cb_arg);
+}
+
+QUIC_TLS *ossl_quic_tls_new(const QUIC_TLS_ARGS *args)
+{
+ QUIC_TLS *qtls;
+
+ if (args->crypto_send_cb == NULL
+ || args->crypto_recv_rcd_cb == NULL
+ || args->crypto_release_rcd_cb == NULL) {
+ ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
+ return NULL;
+ }
+
+ qtls = OPENSSL_zalloc(sizeof(*qtls));
+ if (qtls == NULL)
+ return NULL;
+
+ if (args->ossl_quic && (qtls->error_state = OSSL_ERR_STATE_new()) == NULL) {
+ OPENSSL_free(qtls);
+ return NULL;
+ }
+
+ qtls->args = *args;
+ return qtls;
+}
+
+void ossl_quic_tls_free(QUIC_TLS *qtls)
+{
+ if (qtls == NULL)
+ return;
+ OSSL_ERR_STATE_free(qtls->error_state);
+ OPENSSL_free(qtls);
+}
+
+static int raise_error(QUIC_TLS *qtls, uint64_t error_code,
+ const char *error_msg,
+ const char *src_file,
+ int src_line,
+ const char *src_func)
+{
+ /*
+ * When QTLS fails, add a "cover letter" error with information, potentially
+ * with any underlying libssl errors underneath it (but our cover error may
+ * be the only error in some cases). Then capture this into an ERR_STATE so
+ * we can report it later if need be when the QUIC_CHANNEL asks for it.
+ * For external QUIC TLS we just raise the error.
+ */
+ ERR_new();
+ ERR_set_debug(src_file, src_line, src_func);
+ ERR_set_error(ERR_LIB_SSL, SSL_R_QUIC_HANDSHAKE_LAYER_ERROR,
+ "handshake layer error, error code %llu (0x%llx) (\"%s\")",
+ error_code, error_code, error_msg);
+
+ if (qtls->args.ossl_quic) {
+ OSSL_ERR_STATE_save_to_mark(qtls->error_state);
+
+ /*
+ * We record the error information reported via the QUIC protocol
+ * separately.
+ */
+ qtls->error_code = error_code;
+ qtls->error_msg = error_msg;
+ qtls->inerror = 1;
+
+ ERR_pop_to_mark();
+ }
+ return 0;
+}
+
+#define RAISE_ERROR(qtls, error_code, error_msg) \
+ raise_error((qtls), (error_code), (error_msg), \
+ OPENSSL_FILE, OPENSSL_LINE, OPENSSL_FUNC)
+
+#ifndef OPENSSL_NO_QUIC
+# define RAISE_INTERNAL_ERROR(qtls) \
+ RAISE_ERROR((qtls), OSSL_QUIC_ERR_INTERNAL_ERROR, "internal error")
+#else
+# define RAISE_INTERNAL_ERROR(qtls) \
+ RAISE_ERROR((qtls), 0x01, "internal error")
+#endif
+
+int ossl_quic_tls_configure(QUIC_TLS *qtls)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s);
+ BIO *nullbio;
+
+ if (sc == NULL || !SSL_set_min_proto_version(qtls->args.s, TLS1_3_VERSION))
+ return RAISE_INTERNAL_ERROR(qtls);
+
+ nullbio = BIO_new(BIO_s_null());
+ if (nullbio == NULL)
+ return RAISE_INTERNAL_ERROR(qtls);
+
+ /*
+ * Our custom record layer doesn't use the BIO - but libssl generally
+ * expects one to be present.
+ */
+ SSL_set_bio(qtls->args.s, nullbio, nullbio);
+
+ SSL_clear_options(qtls->args.s, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
+ ossl_ssl_set_custom_record_layer(sc, &quic_tls_record_method, qtls);
+
+ if (!ossl_tls_add_custom_ext_intern(NULL, &sc->cert->custext,
+ qtls->args.is_server ? ENDPOINT_SERVER
+ : ENDPOINT_CLIENT,
+ TLSEXT_TYPE_quic_transport_parameters,
+ SSL_EXT_TLS1_3_ONLY
+ | SSL_EXT_CLIENT_HELLO
+ | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+ add_transport_params_cb,
+ free_transport_params_cb, qtls,
+ parse_transport_params_cb, qtls))
+ return 0;
+
+ sc->s3.flags |= TLS1_FLAGS_QUIC;
+
+ return 1;
+}
+
+#ifndef OPENSSL_NO_QUIC
+int ossl_quic_tls_tick(QUIC_TLS *qtls)
+{
+ int ret, err;
+ const unsigned char *alpn;
+ unsigned int alpnlen;
+
+ if (qtls->inerror)
+ return 0;
+
+ /*
+ * SSL_get_error does not truly know what the cause of an SSL_read failure
+ * is and to some extent guesses based on contextual information. In
+ * particular, if there is _any_ ERR on the error stack, SSL_ERROR_SSL or
+ * SSL_ERROR_SYSCALL will be returned no matter what and there is no
+ * possibility of SSL_ERROR_WANT_READ/WRITE being returned, even if that was
+ * the actual cause of the SSL_read() failure.
+ *
+ * This means that ordinarily, the below code might not work right if the
+ * application has any ERR on the error stack. In order to make this code
+ * perform correctly regardless of prior ERR state, we use a variant of
+ * SSL_get_error() which ignores the error stack. However, some ERRs are
+ * raised by SSL_read() and actually indicate that something has gone wrong
+ * during the call to SSL_read(). We therefore adopt a strategy of marking
+ * the ERR stack and seeing if any errors get appended during the call to
+ * SSL_read(). If they are, we assume SSL_read() has raised an error and
+ * that we should use normal SSL_get_error() handling.
+ *
+ * NOTE: Ensure all escape paths from this function call
+ * ERR_clear_to_mark(). The RAISE macros handle this in failure cases.
+ */
+ ERR_set_mark();
+
+ if (!qtls->configured) {
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s);
+ SSL_CTX *sctx;
+
+ if (sc == NULL)
+ return RAISE_INTERNAL_ERROR(qtls);
+ sctx = SSL_CONNECTION_GET_CTX(sc);
+
+ /*
+ * No matter how the user has configured us, there are certain
+ * requirements for QUIC-TLS that we enforce
+ */
+
+ /* ALPN is a requirement for QUIC and must be set */
+ if (qtls->args.is_server) {
+ if (sctx->ext.alpn_select_cb == NULL)
+ return RAISE_INTERNAL_ERROR(qtls);
+ } else {
+ if (sc->ext.alpn == NULL || sc->ext.alpn_len == 0)
+ return RAISE_ERROR(qtls, OSSL_QUIC_ERR_CRYPTO_NO_APP_PROTO,
+ "ALPN must be configured when using QUIC");
+ }
+
+ if (!ossl_quic_tls_configure(qtls))
+ return RAISE_INTERNAL_ERROR(qtls);
+
+ sc->s3.flags |= TLS1_FLAGS_QUIC_INTERNAL;
+
+ if (qtls->args.is_server)
+ SSL_set_accept_state(qtls->args.s);
+ else
+ SSL_set_connect_state(qtls->args.s);
+
+ qtls->configured = 1;
+ }
+
+ if (qtls->complete)
+ /*
+ * There should never be app data to read, but calling SSL_read() will
+ * ensure any post-handshake messages are processed.
+ */
+ ret = SSL_read(qtls->args.s, NULL, 0);
+ else
+ ret = SSL_do_handshake(qtls->args.s);
+
+ if (ret <= 0) {
+ err = ossl_ssl_get_error(qtls->args.s, ret,
+ /*check_err=*/ERR_count_to_mark() > 0);
+
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ case SSL_ERROR_WANT_RETRY_VERIFY:
+ ERR_pop_to_mark();
+ return 1;
+
+ default:
+ return RAISE_INTERNAL_ERROR(qtls);
+ }
+ }
+
+ if (!qtls->complete) {
+ /* Validate that we have ALPN */
+ SSL_get0_alpn_selected(qtls->args.s, &alpn, &alpnlen);
+ if (alpn == NULL || alpnlen == 0)
+ return RAISE_ERROR(qtls, OSSL_QUIC_ERR_CRYPTO_NO_APP_PROTO,
+ "no application protocol negotiated");
+
+ qtls->complete = 1;
+ ERR_pop_to_mark();
+ return qtls->args.handshake_complete_cb(qtls->args.handshake_complete_cb_arg);
+ }
+
+ ERR_pop_to_mark();
+ return 1;
+}
+#endif
+
+void ossl_quic_tls_clear(QUIC_TLS *qtls)
+{
+ if (qtls == NULL)
+ return;
+ qtls->local_transport_params_consumed = 0;
+}
+
+int ossl_quic_tls_set_transport_params(QUIC_TLS *qtls,
+ const unsigned char *transport_params,
+ size_t transport_params_len)
+{
+ if (qtls->local_transport_params_consumed)
+ return 0;
+
+ qtls->local_transport_params = transport_params;
+ qtls->local_transport_params_len = transport_params_len;
+ return 1;
+}
+
+int ossl_quic_tls_get_error(QUIC_TLS *qtls,
+ uint64_t *error_code,
+ const char **error_msg,
+ ERR_STATE **error_state)
+{
+ if (qtls->inerror) {
+ *error_code = qtls->error_code;
+ *error_msg = qtls->error_msg;
+ *error_state = qtls->error_state;
+ }
+
+ return qtls->inerror;
+}
+
+/*
+ * Returns true if the last handshake record message we processed was a
+ * CertificateRequest
+ */
+int ossl_quic_tls_is_cert_request(QUIC_TLS *qtls)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s);
+
+ if (sc == NULL)
+ return 0;
+
+ return sc->s3.tmp.message_type == SSL3_MT_CERTIFICATE_REQUEST;
+}
+
+/*
+ * Returns true if the last session associated with the connection has an
+ * invalid max_early_data value for QUIC.
+ */
+int ossl_quic_tls_has_bad_max_early_data(QUIC_TLS *qtls)
+{
+ uint32_t max_early_data = SSL_get0_session(qtls->args.s)->ext.max_early_data;
+
+ /*
+ * If max_early_data was present we always ensure a non-zero value is
+ * stored in the session for QUIC. Therefore if max_early_data == 0 here
+ * we can be confident that it was not present in the NewSessionTicket
+ */
+ return max_early_data != 0xffffffff && max_early_data != 0;
+}
+
+int ossl_quic_tls_set_early_data_enabled(QUIC_TLS *qtls, int enabled)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s);
+
+ if (sc == NULL || !SSL_IS_QUIC_HANDSHAKE(sc) || !SSL_in_before(qtls->args.s))
+ return 0;
+
+ if (!enabled) {
+ sc->max_early_data = 0;
+ sc->early_data_state = SSL_EARLY_DATA_NONE;
+ return 1;
+ }
+
+ if (sc->server) {
+ sc->max_early_data = 0xffffffff;
+ sc->early_data_state = SSL_EARLY_DATA_ACCEPTING;
+ return 1;
+ }
+
+ if ((sc->session == NULL || sc->session->ext.max_early_data != 0xffffffff)
+ && sc->psk_use_session_cb == NULL)
+ return 0;
+
+ sc->early_data_state = SSL_EARLY_DATA_CONNECTING;
+ return 1;
+}