QUIC_LCIDM *lcidm;
/* SRTM to register SRTs with. */
QUIC_SRTM *srtm;
- OSSL_QRX *qrx;
int is_server;
SSL *tls;
/* Whether to use qlog. */
int use_qlog;
- int is_tserver_ch;
-
/* Title to use for the qlog session, or NULL. */
const char *qlog_title;
} QUIC_CHANNEL_ARGS;
*/
QUIC_CHANNEL *ossl_quic_channel_alloc(const QUIC_CHANNEL_ARGS *args);
int ossl_quic_channel_init(QUIC_CHANNEL *ch);
-void ossl_quic_channel_bind_qrx(QUIC_CHANNEL *tserver_ch, OSSL_QRX *qrx);
/* No-op if ch is NULL. */
typedef struct quic_reactor_wait_ctx_st QUIC_REACTOR_WAIT_CTX;
typedef struct ossl_statm_st OSSL_STATM;
typedef struct quic_demux_st QUIC_DEMUX;
-typedef struct ossl_qrx_st OSSL_QRX;
typedef struct ossl_qrx_pkt_st OSSL_QRX_PKT;
typedef struct ossl_qtx_pkt_st OSSL_QTX_PKT;
typedef struct quic_tick_result_st QUIC_TICK_RESULT;
* QUIC Record Layer - RX
* ======================
*/
+typedef struct ossl_qrx_st OSSL_QRX;
typedef struct ossl_qrx_args_st {
OSSL_LIB_CTX *libctx;
* establish a new connection.
*/
void ossl_qrx_inject_urxe(OSSL_QRX *qrx, QUIC_URXE *e);
-int ossl_qrx_validate_initial_packet(OSSL_QRX *qrx, QUIC_URXE *urxe,
- const QUIC_CONN_ID *dcid);
/*
* Decryption of 1-RTT packets must be explicitly enabled by calling this
ossl_quic_tx_packetiser_set_ack_tx_cb(ch->txp, ch_on_txp_ack_tx, ch);
- /*
- * qrx does not exist yet, then we must be dealing with client channel
- * (QUIC connection initiator).
- * If qrx exists already, then we are dealing with server channel which
- * qrx gets created by port_default_packet_handler() before
- * port_default_packet_handler() accepts connection and creates channel
- * for it.
- * The exception here is tserver which always creates channel,
- * before the first packet is ever seen.
- */
- if (ch->qrx == NULL && ch->is_tserver_ch == 0) {
- /* we are regular client, create channel */
- qrx_args.libctx = ch->port->engine->libctx;
- qrx_args.demux = ch->port->demux;
- qrx_args.short_conn_id_len = rx_short_dcid_len;
- qrx_args.max_deferred = 32;
-
- if ((ch->qrx = ossl_qrx_new(&qrx_args)) == NULL)
- goto err;
- }
+ qrx_args.libctx = ch->port->engine->libctx;
+ qrx_args.demux = ch->port->demux;
+ qrx_args.short_conn_id_len = rx_short_dcid_len;
+ qrx_args.max_deferred = 32;
- if (ch->qrx != NULL) {
- /*
- * callbacks for channels associated with tserver's port
- * are set up later when we call ossl_quic_channel_bind_qrx()
- * in port_default_packet_handler()
- */
- if (!ossl_qrx_set_late_validation_cb(ch->qrx,
- rx_late_validate,
- ch))
- goto err;
+ if ((ch->qrx = ossl_qrx_new(&qrx_args)) == NULL)
+ goto err;
- if (!ossl_qrx_set_key_update_cb(ch->qrx,
- rxku_detected,
- ch))
- goto err;
- }
+ if (!ossl_qrx_set_late_validation_cb(ch->qrx,
+ rx_late_validate,
+ ch))
+ goto err;
+ if (!ossl_qrx_set_key_update_cb(ch->qrx,
+ rxku_detected,
+ ch))
+ goto err;
for (pn_space = QUIC_PN_SPACE_INITIAL; pn_space < QUIC_PN_SPACE_NUM; ++pn_space) {
ch->crypto_recv[pn_space] = ossl_quic_rstream_new(NULL, NULL, 0);
return ch_init(ch);
}
-void ossl_quic_channel_bind_qrx(QUIC_CHANNEL *tserver_ch, OSSL_QRX *qrx)
-{
- if (tserver_ch->qrx == NULL && tserver_ch->is_tserver_ch == 1) {
- tserver_ch->qrx = qrx;
- ossl_qrx_set_late_validation_cb(tserver_ch->qrx, rx_late_validate,
- tserver_ch);
- ossl_qrx_set_key_update_cb(tserver_ch->qrx, rxku_detected,
- tserver_ch);
- }
-}
-
QUIC_CHANNEL *ossl_quic_channel_alloc(const QUIC_CHANNEL_ARGS *args)
{
QUIC_CHANNEL *ch = NULL;
if ((ch = OPENSSL_zalloc(sizeof(*ch))) == NULL)
return NULL;
- ch->port = args->port;
- ch->is_server = args->is_server;
- ch->tls = args->tls;
- ch->lcidm = args->lcidm;
- ch->srtm = args->srtm;
- ch->qrx = args->qrx;
- ch->is_tserver_ch = args->is_tserver_ch;
+ ch->port = args->port;
+ ch->is_server = args->is_server;
+ ch->tls = args->tls;
+ ch->lcidm = args->lcidm;
+ ch->srtm = args->srtm;
#ifndef OPENSSL_NO_QLOG
ch->use_qlog = args->use_qlog;
size_t ossl_quic_channel_get_short_header_conn_id_len(QUIC_CHANNEL *ch)
{
- return ossl_quic_port_get_rx_short_dcid_len(ch->port);
+ return ossl_qrx_get_short_hdr_conn_id_len(ch->qrx);
}
QUIC_STREAM *ossl_quic_channel_get_stream_by_id(QUIC_CHANNEL *ch,
ossl_qtx_set_qlog_cb(ch->qtx, ch_get_qlog_cb, ch);
ossl_quic_tx_packetiser_set_qlog_cb(ch->txp, ch_get_qlog_cb, ch);
- /*
- * Plug in secrets for the Initial EL. secrets for QRX were created in
- * port_default_packet_handler() already.
- */
+ /* Plug in secrets for the Initial EL. */
if (!ossl_quic_provide_initial_secret(ch->port->engine->libctx,
ch->port->engine->propq,
&ch->init_dcid,
/*is_server=*/1,
- NULL, ch->qtx))
+ ch->qrx, ch->qtx))
return 0;
/* Register the peer ODCID in the LCIDM. */
ossl_qtx_set_msg_callback(ch->qtx, msg_callback, msg_callback_ssl);
ossl_quic_tx_packetiser_set_msg_callback(ch->txp, msg_callback,
msg_callback_ssl);
- /*
- * postpone msg callback setting for tserver until port calls
- * port_bind_channel().
- */
- if (ch->is_tserver_ch == 0)
- ossl_qrx_set_msg_callback(ch->qrx, msg_callback, msg_callback_ssl);
+ ossl_qrx_set_msg_callback(ch->qrx, msg_callback, msg_callback_ssl);
}
void ossl_quic_channel_set_msg_callback_arg(QUIC_CHANNEL *ch,
ch->msg_callback_arg = msg_callback_arg;
ossl_qtx_set_msg_callback_arg(ch->qtx, msg_callback_arg);
ossl_quic_tx_packetiser_set_msg_callback_arg(ch->txp, msg_callback_arg);
-
- /*
- * postpone msg callback setting for tserver until port calls
- * port_bind_channel().
- */
- if (ch->is_tserver_ch == 0)
- ossl_qrx_set_msg_callback_arg(ch->qrx, msg_callback_arg);
+ ossl_qrx_set_msg_callback_arg(ch->qrx, msg_callback_arg);
}
void ossl_quic_channel_set_txku_threshold_override(QUIC_CHANNEL *ch,
/* Has qlog been requested? */
unsigned int use_qlog : 1;
- /* Has qlog been requested? */
- unsigned int is_tserver_ch : 1;
-
/* Saved error stack in case permanent error was encountered */
ERR_STATE *err_state;
return tls;
}
-static QUIC_CHANNEL *port_make_channel(QUIC_PORT *port, SSL *tls, OSSL_QRX *qrx,
- int is_server, int is_tserver)
+static QUIC_CHANNEL *port_make_channel(QUIC_PORT *port, SSL *tls, int is_server)
{
QUIC_CHANNEL_ARGS args = {0};
QUIC_CHANNEL *ch;
- args.port = port;
- args.is_server = is_server;
- args.lcidm = port->lcidm;
- args.srtm = port->srtm;
- args.qrx = qrx;
- args.is_tserver_ch = is_tserver;
+ args.port = port;
+ args.is_server = is_server;
+ args.lcidm = port->lcidm;
+ args.srtm = port->srtm;
/*
* Creating a a new channel is made a bit tricky here as there is a
QUIC_CHANNEL *ossl_quic_port_create_outgoing(QUIC_PORT *port, SSL *tls)
{
- return port_make_channel(port, tls, NULL, /* is_server= */ 0,
- /* is_tserver= */ 0);
+ return port_make_channel(port, tls, /*is_server=*/0);
}
QUIC_CHANNEL *ossl_quic_port_create_incoming(QUIC_PORT *port, SSL *tls)
assert(port->tserver_ch == NULL);
- /*
- * pass -1 for qrx to indicate port will create qrx
- * later in port_default_packet_handler() when calling port_bind_channel().
- */
- ch = port_make_channel(port, tls, NULL, /* is_server= */ 1,
- /* is_tserver_ch */ 1);
+ ch = port_make_channel(port, tls, /*is_server=*/1);
port->tserver_ch = ch;
port->allow_incoming = 1;
return ch;
*/
static void port_bind_channel(QUIC_PORT *port, const BIO_ADDR *peer,
const QUIC_CONN_ID *scid, const QUIC_CONN_ID *dcid,
- const QUIC_CONN_ID *odcid, OSSL_QRX *qrx,
- QUIC_CHANNEL **new_ch)
+ const QUIC_CONN_ID *odcid, QUIC_CHANNEL **new_ch)
{
QUIC_CHANNEL *ch;
if (port->tserver_ch != NULL) {
ch = port->tserver_ch;
port->tserver_ch = NULL;
- ossl_quic_channel_bind_qrx(ch, qrx);
- ossl_qrx_set_msg_callback(ch->qrx, ch->msg_callback,
- ch->msg_callback_ssl);
- ossl_qrx_set_msg_callback_arg(ch->qrx, ch->msg_callback_arg);
} else {
- ch = port_make_channel(port, NULL, qrx, /* is_server= */ 1,
- /* is_tserver */ 0);
+ ch = port_make_channel(port, NULL, /* is_server= */1);
}
if (ch == NULL)
QUIC_CHANNEL *ch = NULL, *new_ch = NULL;
QUIC_CONN_ID odcid, scid;
uint8_t gen_new_token = 0;
- OSSL_QRX *qrx = NULL;
- OSSL_QRX_ARGS qrx_args = {0};
uint64_t cause_flags = 0;
/* Don't handle anything if we are no longer running. */
odcid.id_len = 0;
- /*
- * Create qrx now so we can check integrity of packet
- * which does not belong to any channel.
- */
- qrx_args.libctx = port->engine->libctx;
- qrx_args.demux = port->demux;
- qrx_args.short_conn_id_len = dcid->id_len;
- qrx_args.max_deferred = 32;
- qrx = ossl_qrx_new(&qrx_args);
- if (qrx == NULL)
- goto undesirable;
-
- /*
- * Derive secrets for qrx only.
- */
- if (!ossl_quic_provide_initial_secret(port->engine->libctx,
- port->engine->propq,
- &hdr.dst_conn_id,
- /* is_server */ 1,
- qrx, NULL))
- goto undesirable;
-
- if (ossl_qrx_validate_initial_packet(qrx, e, (const QUIC_CONN_ID *)dcid) == 0)
- goto undesirable;
-
/*
* TODO(QUIC FUTURE): there should be some logic similar to accounting half-open
* states in TCP. If we reach certain threshold, then we want to
*/
if (port->validate_addr == 1 && hdr.token == NULL) {
port_send_retry(port, &e->peer, &hdr);
- /*
- * This is a kind of bummer because we forget secrets for initial
- * level encryption. The secrets costs us CPU to compute. What we can
- * do here is to store them within retry token. Then we can retrieve them
- * from initial packet which will carry our retry token to validate
- * client's address.
- */
goto undesirable;
}
* the request is valid
*/
if (port->validate_addr == 1) {
- /*
- * Again: we should consider saving initial encryption level
- * secrets to token here to save some CPU cycles.
- */
port_send_retry(port, &e->peer, &hdr);
goto undesirable;
}
}
port_bind_channel(port, &e->peer, &scid, &hdr.dst_conn_id,
- &odcid, qrx, &new_ch);
-
- /*
- * if packet validates it gets moved to channel, we've just bound
- * to port.
- */
- if (new_ch == NULL)
- goto undesirable;
+ &odcid, &new_ch);
/*
* Generate a token for sending in a later NEW_TOKEN frame
generate_new_token(new_ch, &e->peer);
/*
- * The qrx belongs to channel now, so don't free it.
- */
- qrx = NULL;
-
- /*
- * If function reaches this place, then packet got validated in
- * ossl_qrx_validate_initial_packet(). Keep in mind the function
- * ossl_qrx_validate_initial_packet() decrypts the packet to validate it.
- * If packet validation was successful (and it was because we are here),
- * then the function puts the packet to qrx->rx_pending. We must not call
- * ossl_qrx_inject_urxe() here now, because we don't want to insert
- * the packet to qrx->urx_pending which keeps packet waiting for decryption.
- *
- * We are going to call ossl_quic_demux_release_urxe() to dispose buffer
- * which still holds encrypted data.
+ * The channel will do all the LCID registration needed, but as an
+ * optimization inject this packet directly into the channel's QRX for
+ * processing without going through the DEMUX again.
*/
+ if (new_ch != NULL) {
+ ossl_qrx_inject_urxe(new_ch->qrx, e);
+ return;
+ }
undesirable:
- ossl_qrx_free(qrx);
ossl_quic_demux_release_urxe(port->demux, e);
}
SSL *msg_callback_ssl;
};
-static RXE *qrx_ensure_free_rxe(OSSL_QRX *qrx, size_t alloc_len);
-static int qrx_validate_hdr_early(OSSL_QRX *qrx, RXE *rxe,
- const QUIC_CONN_ID *first_dcid);
-static int qrx_relocate_buffer(OSSL_QRX *qrx, RXE **prxe, size_t *pi,
- const unsigned char **pptr, size_t buf_len);
-static int qrx_validate_hdr(OSSL_QRX *qrx, RXE *rxe);
-static RXE *qrx_reserve_rxe(RXE_LIST *rxl, RXE *rxe, size_t n);
-static int qrx_decrypt_pkt_body(OSSL_QRX *qrx, unsigned char *dst,
- const unsigned char *src,
- size_t src_len, size_t *dec_len,
- const unsigned char *aad, size_t aad_len,
- QUIC_PN pn, uint32_t enc_level,
- unsigned char key_phase_bit,
- uint64_t *rx_key_epoch);
-static int qrx_validate_hdr_late(OSSL_QRX *qrx, RXE *rxe);
-static uint32_t rxe_determine_pn_space(RXE *rxe);
-static void ignore_res(int x);
-
OSSL_QRX *ossl_qrx_new(const OSSL_QRX_ARGS *args)
{
OSSL_QRX *qrx;
qrx->msg_callback_arg);
}
-/*
- * qrx_validate_initial_pkt() is derived from qrx_process_pkt(). Unlike
- * qrx_process_pkt() the qrx_validate_initial_pkt() function can process
- * initial packet only. All other packets should be discarded. This allows
- * port_default_packet_handler() to validate incoming packet. If packet
- * is not valid, then port_default_packet_handler() must discard the
- * packet instead of creating a new channel for it.
- */
-static int qrx_validate_initial_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
- const QUIC_CONN_ID *first_dcid,
- size_t datagram_len)
-{
- PACKET pkt, orig_pkt;
- RXE *rxe;
- size_t i = 0, aad_len = 0, dec_len = 0;
- const unsigned char *sop;
- unsigned char *dst;
- QUIC_PKT_HDR_PTRS ptrs;
- uint32_t pn_space;
- OSSL_QRL_ENC_LEVEL *el = NULL;
- uint64_t rx_key_epoch = UINT64_MAX;
-
- if (!PACKET_buf_init(&pkt, ossl_quic_urxe_data(urxe), urxe->data_len))
- return 0;
-
- orig_pkt = pkt;
- sop = PACKET_data(&pkt);
-
- /*
- * Get a free RXE. If we need to allocate a new one, use the packet length
- * as a good ballpark figure.
- */
- rxe = qrx_ensure_free_rxe(qrx, PACKET_remaining(&pkt));
- if (rxe == NULL)
- return 0;
-
- /*
- * we expect INITIAL packet only, therefore it is OK to pass
- * short_conn_id_len as 0.
- */
- if (!ossl_quic_wire_decode_pkt_hdr(&pkt,
- 0, /* short_conn_id_len */
- 1, /* need second decode */
- 0, /* nodata -> want to read data */
- &rxe->hdr, &ptrs,
- NULL))
- goto malformed;
-
- if (rxe->hdr.type != QUIC_PKT_TYPE_INITIAL)
- goto malformed;
-
- if (!qrx_validate_hdr_early(qrx, rxe, NULL))
- goto malformed;
-
- if (ossl_qrl_enc_level_set_have_el(&qrx->el_set, QUIC_ENC_LEVEL_INITIAL) != 1)
- goto malformed;
-
- if (rxe->hdr.type == QUIC_PKT_TYPE_INITIAL) {
- const unsigned char *token = rxe->hdr.token;
-
- /*
- * This may change the value of rxe and change the value of the token
- * pointer as well. So we must make a temporary copy of the pointer to
- * the token, and then copy it back into the new location of the rxe
- */
- if (!qrx_relocate_buffer(qrx, &rxe, &i, &token, rxe->hdr.token_len))
- goto malformed;
-
- rxe->hdr.token = token;
- }
-
- pkt = orig_pkt;
-
- el = ossl_qrl_enc_level_set_get(&qrx->el_set, QUIC_ENC_LEVEL_INITIAL, 1);
- assert(el != NULL); /* Already checked above */
-
- if (!ossl_quic_hdr_protector_decrypt(&el->hpr, &ptrs))
- goto malformed;
-
- /*
- * We have removed header protection, so don't attempt to do it again if
- * the packet gets deferred and processed again.
- */
- pkt_mark(&urxe->hpr_removed, 0);
-
- /* Decode the now unprotected header. */
- if (ossl_quic_wire_decode_pkt_hdr(&pkt, 0,
- 0, 0, &rxe->hdr, NULL, NULL) != 1)
- goto malformed;
-
- /* Validate header and decode PN. */
- if (!qrx_validate_hdr(qrx, rxe))
- goto malformed;
-
- /*
- * The AAD data is the entire (unprotected) packet header including the PN.
- * The packet header has been unprotected in place, so we can just reuse the
- * PACKET buffer. The header ends where the payload begins.
- */
- aad_len = rxe->hdr.data - sop;
-
- /* Ensure the RXE buffer size is adequate for our payload. */
- if ((rxe = qrx_reserve_rxe(&qrx->rx_free, rxe, rxe->hdr.len + i)) == NULL)
- goto malformed;
-
- /*
- * We decrypt the packet body to immediately after the token at the start of
- * the RXE buffer (where present).
- *
- * Do the decryption from the PACKET (which points into URXE memory) to our
- * RXE payload (single-copy decryption), then fixup the pointers in the
- * header to point to our new buffer.
- *
- * If decryption fails this is considered a permanent error; we defer
- * packets we don't yet have decryption keys for above, so if this fails,
- * something has gone wrong with the handshake process or a packet has been
- * corrupted.
- */
- dst = (unsigned char *)rxe_data(rxe) + i;
- if (!qrx_decrypt_pkt_body(qrx, dst, rxe->hdr.data, rxe->hdr.len,
- &dec_len, sop, aad_len, rxe->pn, QUIC_ENC_LEVEL_INITIAL,
- rxe->hdr.key_phase, &rx_key_epoch))
- goto malformed;
-
- /*
- * -----------------------------------------------------
- * IMPORTANT: ANYTHING ABOVE THIS LINE IS UNVERIFIED
- * AND MUST BE TIMING-CHANNEL SAFE.
- * -----------------------------------------------------
- *
- * At this point, we have successfully authenticated the AEAD tag and no
- * longer need to worry about exposing the PN, PN length or Key Phase bit in
- * timing channels. Invoke any configured validation callback to allow for
- * rejection of duplicate PNs.
- */
- if (!qrx_validate_hdr_late(qrx, rxe))
- goto malformed;
-
- pkt_mark(&urxe->processed, 0);
-
- /*
- * Update header to point to the decrypted buffer, which may be shorter
- * due to AEAD tags, block padding, etc.
- */
- rxe->hdr.data = dst;
- rxe->hdr.len = dec_len;
- rxe->data_len = dec_len;
- rxe->datagram_len = datagram_len;
- rxe->key_epoch = rx_key_epoch;
-
- /* We processed the PN successfully, so update largest processed PN. */
- pn_space = rxe_determine_pn_space(rxe);
- if (rxe->pn > qrx->largest_pn[pn_space])
- qrx->largest_pn[pn_space] = rxe->pn;
-
- /* Copy across network addresses and RX time from URXE to RXE. */
- rxe->peer = urxe->peer;
- rxe->local = urxe->local;
- rxe->time = urxe->time;
- rxe->datagram_id = urxe->datagram_id;
-
- /*
- * The packet is decrypted, we are going to move it from
- * rx_pending queue where it waits to be further processed
- * by ch_rx().
- */
- ossl_list_rxe_remove(&qrx->rx_free, rxe);
- ossl_list_rxe_insert_tail(&qrx->rx_pending, rxe);
-
- return 1;
-
-malformed:
- /* caller (port_default_packet_handler()) should discard urxe */
- return 0;
-}
-
-int ossl_qrx_validate_initial_packet(OSSL_QRX *qrx, QUIC_URXE *urxe,
- const QUIC_CONN_ID *dcid)
-{
- urxe->processed = 0;
- urxe->hpr_removed = 0;
- urxe->deferred = 0;
-
- return qrx_validate_initial_pkt(qrx, urxe, dcid, urxe->data_len);
-}
-
static void qrx_requeue_deferred(OSSL_QRX *qrx)
{
QUIC_URXE *e;