SSL_SET_QUIC_TLS_CBS(3ossl)

πŸ’– SSL_SET_QUIC_TLS_CBS πŸ’–

okay so bestie, this is the SSL_set_quic_tls_cbs(3ossl) page β€” like, manual section 3ossl, OpenSSL girlies rise up 🫢. It's all about using the OpenSSL TLS implementation for a third party QUIC implementation. Yes you read that right, we're doing QUIC energy today πŸ”₯


✨ NAME ✨

OSSL_FUNC_SSL_QUIC_TLS_crypto_send_fn, OSSL_FUNC_SSL_QUIC_TLS_crypto_recv_rcd_fn, OSSL_FUNC_SSL_QUIC_TLS_crypto_release_rcd_fn, OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn, OSSL_FUNC_SSL_QUIC_TLS_got_transport_params_fn, OSSL_FUNC_SSL_QUIC_TLS_alert_fn, SSL_set_quic_tls_cbs, SSL_set_quic_tls_transport_params, SSL_set_quic_tls_early_data_enabled β€” Use the OpenSSL TLS implementation for a third party QUIC implementation πŸ’…


🌸 SYNOPSIS 🌸

#include <openssl/ssl.h>

/* QUIC TLS callbacks available via an OSSL_DISPATCH table */

/* Function id: OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND */
typedef int (*OSSL_FUNC_SSL_QUIC_TLS_crypto_send_fn)(SSL *s,
                                                     const unsigned char *buf,
                                                     size_t buf_len,
                                                     size_t *consumed,
                                                     void *arg);

/* Function id: OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD */
typedef int (*OSSL_FUNC_SSL_QUIC_TLS_crypto_recv_rcd_fn)(SSL *s,
                                                   const unsigned char **buf,
                                                   size_t *bytes_read,
                                                   void *arg);

/* Function id: OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD */
typedef int (*OSSL_FUNC_SSL_QUIC_TLS_crypto_release_rcd_fn)(SSL *,
                                                            size_t bytes_read,
                                                            void *arg);

/* Function id: OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET */
typedef int (*OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn)(SSL *s,
                                                   uint32_t prot_level,
                                                   int direction,
                                                   const unsigned char *secret,
                                                   size_t secret_len,
                                                   void *arg);

/* Function id: OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS */
typedef int (*OSSL_FUNC_SSL_QUIC_TLS_got_transport_params_fn)(SSL *s,
                                                   const unsigned char *params,
                                                   size_t params_len,
                                                   void *arg);

/* Function id: OSSL_FUNC_SSL_QUIC_TLS_ALERT */
typedef int (*OSSL_FUNC_SSL_QUIC_TLS_alert_fn)(SSL *s,
                                               unsigned char alert_code,
                                               void *arg);

int SSL_set_quic_tls_cbs(SSL *s, const OSSL_DISPATCH *qtdis, void *arg);
int SSL_set_quic_tls_transport_params(SSL *s,
                                      const unsigned char *params,
                                      size_t params_len);
int SSL_set_quic_tls_early_data_enabled(SSL *s, int enabled);

πŸ‘‘ DESCRIPTION πŸ‘‘

SSL_set_quic_tls_cbs() is the moment, fr fr. It swaps out the standard TLS record layer for a custom record layer, so a third party QUIC implementation can do its thing πŸ¦‹. For the given SSL object s, you hand over a whole set of callbacks in an OSSL_DISPATCH table via qtdis. And that arg parameter? It gets passed right back to you as an argument whenever the various callbacks fire. cute, right?

The callbacks up there get invoked, as needed, by SSL_do_handshake() and SSL_read() (and yes that includes SSL_read_ex, SSL_peek, SSL_peek_ex β€” the whole clique πŸ’…). Once the SSL handshake is complete, the QUIC stack must arrange to call one of the SSL_read() variants whenever a post-handshake CRYPTO frame is received. And ngl, the number of bytes requested may be zero. slay.

An OSSL_DISPATCH table should consist of an array of OSSL_DISPATCH entries where each entry is a function id, and a function pointer. The array should be terminated with an empty entry (i.e. a 0 function id, and a NULL function pointer). Don't forget the terminator bestie, it's giving "semicolon you forgot" energy πŸ’€

Calling the SSL_set_quic_tls_cbs() function will switch off the SSL_OP_ENABLE_MIDDLEBOX_COMPAT option (if set). See SSL_set_options(3). Additionally the minimum TLS protocol version will be set to TLS1_3_VERSION. And heads up: it is an error to call this function with anything other than a TLS connection SSL object. periodt.


πŸŽ€ the callbacks, one by one πŸŽ€

  • The OSSL_FUNC_SSL_QUIC_TLS_crypto_send_fn callback (function id OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND) is called when CRYPTO frame data should be sent to the peer. The data to be sent is supplied in the buffer buf which is of length buf_len. The callback may choose to consume less data than was supplied in the buffer. On successful completion of the callback the consumed parameter should be populated with the amount of data that the callback consumed. This should be less than or equal to the value in buf_len. CRYPTO data should be sent using the most recent write encryption level set via the OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn callback (if it has been called). πŸ’Œ
  • The OSSL_FUNC_SSL_QUIC_TLS_crypto_recv_rcd_fn callback (function id OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD) is used to receive CRYPTO frame data from the peer. When OpenSSL wants to read data from the peer this callback is called. The callback should populate *buf with a pointer to a buffer containing CRYPTO data that has been received from the peer. The size of the buffer should be populated in *bytes_read. The buffer should remain valid until OpenSSL calls the OSSL_FUNC_SSL_QUIC_TLS_crypto_release_rcd_fn callback. CRYPTO frame data is assumed to have been decrypted using the most recent read protection level set via the yield_secret_cb callback (if it has been called). πŸ™ˆ
  • The OSSL_FUNC_SSL_QUIC_TLS_crypto_release_rcd_fn callback (function id OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD) is called when data previously read via OSSL_FUNC_SSL_QUIC_TLS_crypto_recv_rcd_fn is no longer required. The bytes_read argument is always equal to the size of the buffer previously provided in the crypto_receive_rcd_cb callback. Only one record at a time will ever be read by OpenSSL. one at a time, she's monogamous πŸ’…
  • The OSSL_FUNC_SSL_QUIC_TLS_yield_secret_fn callback (function id OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET) is called when a new secret has been established. The prot_level argument identities the TLS protection level and will be one of OSSL_RECORD_PROTECTION_LEVEL_NONE, OSSL_RECORD_PROTECTION_LEVEL_EARLY, OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE or OSSL_RECORD_PROTECTION_LEVEL_APPLICATION. The direction will either be 0 (for the read secret) or 1 (for the write secret). The secret itself will be in the buffer pointed to by secret and the buffer will be of length secret_len. πŸ”
  • The OSSL_FUNC_SSL_QUIC_TLS_got_transport_params_fn callback (function id OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS) is called when transport parameters have been received from the peer. The parameters are held in the params buffer which is of length params_len. πŸ“¬
  • The OSSL_FUNC_SSL_QUIC_TLS_alert_fn callback (function id OSSL_FUNC_SSL_QUIC_TLS_ALERT) is called when OpenSSL is attempting to send an alert to the peer. The code for the alert is supplied in alert_code. 🚨

🌈 the other two functions 🌈

SSL_set_quic_tls_transport_params() is used to set the transport parameters to be sent by this endpoint. The parameters are in the params buffer which should be of length params_len. The buffer containing the parameters should remain valid until after the parameters have been sent. This function must have been called by the time the transport parameters need to be sent. For a client this will be before the connection has been initiated. For a server this might typically occur during the got_transport_params_cb. ⏰

SSL_set_quic_tls_early_data_enabled() is used to enable the 0-RTT feature for a third party QUIC implementation. iconic behaviour honestly ✨


⭐ RETURN VALUES ⭐

These functions return 1 on success and 0 on failure.

All of the callbacks should also return 1 on success and 0 on failure. And listen β€” a failure response is fatal to the connection. like, no coming back from that one 😭


🧚 EXAMPLES 🧚

A call to SSL_set_quic_tls_cbs() might look something like the following, given suitable definitions of the various callback functions:

const OSSL_DISPATCH qtdis[] = {
    {OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND, (void (*)(void))crypto_send_cb},
    {OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD,
        (void (*)(void))crypto_recv_rcd_cb},
    {OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD,
        (void (*)(void))crypto_release_rcd_cb},
    {OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET,
        (void (*)(void))yield_secret_cb},
    {OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS,
        (void (*)(void))got_transport_params_cb},
    {OSSL_FUNC_SSL_QUIC_TLS_ALERT, (void (*)(void))alert_cb},
    {0, NULL}
 };

if (!SSL_set_quic_tls_cbs(ssl, qtdis, NULL))
    goto err;

see how the last entry is {0, NULL}? that's the terminator bestie, she's load-bearing πŸ’–


πŸ¦‹ HISTORY πŸ¦‹

These functions were added in OpenSSL 3.5. so they're new new, not vintage πŸ’…


Copyright 2024-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>.


and that's the whole page bestie, you ate πŸ’…βœ¨

this was version 3.5.9, dated 2026-09-29 β€” go forth and QUIC, ily πŸ«ΆπŸŽ€

πŸ“¦ where this page comes from