mirror of
https://github.com/openssl/openssl.git
synced 2024-12-03 05:41:46 +08:00
b58abf9822
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Neil Horman <nhorman@openssl.org> (Merged from https://github.com/openssl/openssl/pull/22037)
392 lines
15 KiB
C
392 lines
15 KiB
C
/*
|
|
* Copyright 2022-2023 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
|
|
*/
|
|
|
|
#ifndef OSSL_QUIC_RECORD_TX_H
|
|
# define OSSL_QUIC_RECORD_TX_H
|
|
|
|
# include <openssl/ssl.h>
|
|
# include "internal/quic_wire_pkt.h"
|
|
# include "internal/quic_types.h"
|
|
# include "internal/quic_predef.h"
|
|
# include "internal/quic_record_util.h"
|
|
# include "internal/qlog.h"
|
|
|
|
# ifndef OPENSSL_NO_QUIC
|
|
|
|
/*
|
|
* QUIC Record Layer - TX
|
|
* ======================
|
|
*/
|
|
typedef struct ossl_qtx_iovec_st {
|
|
const unsigned char *buf;
|
|
size_t buf_len;
|
|
} OSSL_QTX_IOVEC;
|
|
|
|
typedef struct ossl_qtx_st OSSL_QTX;
|
|
|
|
typedef int (*ossl_mutate_packet_cb)(const QUIC_PKT_HDR *hdrin,
|
|
const OSSL_QTX_IOVEC *iovecin, size_t numin,
|
|
QUIC_PKT_HDR **hdrout,
|
|
const OSSL_QTX_IOVEC **iovecout,
|
|
size_t *numout,
|
|
void *arg);
|
|
|
|
typedef void (*ossl_finish_mutate_cb)(void *arg);
|
|
|
|
typedef struct ossl_qtx_args_st {
|
|
OSSL_LIB_CTX *libctx;
|
|
const char *propq;
|
|
|
|
/* BIO to transmit to. */
|
|
BIO *bio;
|
|
|
|
/* Maximum datagram payload length (MDPL) for TX purposes. */
|
|
size_t mdpl;
|
|
|
|
/* QLOG instance to use, or NULL. */
|
|
QLOG *qlog;
|
|
} OSSL_QTX_ARGS;
|
|
|
|
/* Instantiates a new QTX. */
|
|
OSSL_QTX *ossl_qtx_new(const OSSL_QTX_ARGS *args);
|
|
|
|
/* Frees the QTX. */
|
|
void ossl_qtx_free(OSSL_QTX *qtx);
|
|
|
|
/* Set mutator callbacks for test framework support */
|
|
void ossl_qtx_set_mutator(OSSL_QTX *qtx, ossl_mutate_packet_cb mutatecb,
|
|
ossl_finish_mutate_cb finishmutatecb, void *mutatearg);
|
|
|
|
/* Setters for the msg_callback and the msg_callback_arg */
|
|
void ossl_qtx_set_msg_callback(OSSL_QTX *qtx, ossl_msg_cb msg_callback,
|
|
SSL *msg_callback_ssl);
|
|
void ossl_qtx_set_msg_callback_arg(OSSL_QTX *qtx, void *msg_callback_arg);
|
|
|
|
/* Change QLOG instance in use after instantiation. */
|
|
void ossl_qtx_set0_qlog(OSSL_QTX *qtx, QLOG *qlog);
|
|
|
|
/*
|
|
* Secret Management
|
|
* -----------------
|
|
*/
|
|
|
|
/*
|
|
* Provides a secret to the QTX, which arises due to an encryption level change.
|
|
* enc_level is a QUIC_ENC_LEVEL_* value.
|
|
*
|
|
* This function can be used to initialise the INITIAL encryption level, but you
|
|
* should not do so directly; see the utility function
|
|
* ossl_qrl_provide_initial_secret() instead, which can initialise the INITIAL
|
|
* encryption level of a QRX and QTX simultaneously without duplicating certain
|
|
* key derivation steps.
|
|
*
|
|
* You must call this function for a given EL before transmitting packets at
|
|
* that EL using this QTX, otherwise ossl_qtx_write_pkt will fail.
|
|
*
|
|
* suite_id is a QRL_SUITE_* value which determines the AEAD function used for
|
|
* the QTX.
|
|
*
|
|
* The secret passed is used directly to derive the "quic key", "quic iv" and
|
|
* "quic hp" values.
|
|
*
|
|
* secret_len is the length of the secret buffer in bytes. The buffer must be
|
|
* sized correctly to the chosen suite, else the function fails.
|
|
*
|
|
* This function can only be called once for a given EL, except for the INITIAL
|
|
* EL, as the INITIAL EL can need to be rekeyed if connection retry occurs.
|
|
* Subsequent calls for non-INITIAL ELs fail. Calls made after a corresponding
|
|
* call to ossl_qtx_discard_enc_level for a given EL also fail, including for
|
|
* the INITIAL EL. The secret for a non-INITIAL EL cannot be changed after it is
|
|
* set because QUIC has no facility for introducing additional key material
|
|
* after an EL is setup. (QUIC key updates generate new keys from existing key
|
|
* material and do not introduce new entropy into a connection's key material.)
|
|
*
|
|
* Returns 1 on success or 0 on failure.
|
|
*/
|
|
int ossl_qtx_provide_secret(OSSL_QTX *qtx,
|
|
uint32_t enc_level,
|
|
uint32_t suite_id,
|
|
EVP_MD *md,
|
|
const unsigned char *secret,
|
|
size_t secret_len);
|
|
|
|
/*
|
|
* Informs the QTX that it can now discard key material for a given EL. The QTX
|
|
* will no longer be able to generate packets at that EL. This function is
|
|
* idempotent and succeeds if the EL has already been discarded.
|
|
*
|
|
* Returns 1 on success and 0 on failure.
|
|
*/
|
|
int ossl_qtx_discard_enc_level(OSSL_QTX *qtx, uint32_t enc_level);
|
|
|
|
/* Returns 1 if the given encryption level is provisioned. */
|
|
int ossl_qtx_is_enc_level_provisioned(OSSL_QTX *qtx, uint32_t enc_level);
|
|
|
|
/*
|
|
* Given the value ciphertext_len representing an encrypted packet payload
|
|
* length in bytes, determines how many plaintext bytes it will decrypt to.
|
|
* Returns 0 if the specified EL is not provisioned or ciphertext_len is too
|
|
* small. The result is written to *plaintext_len.
|
|
*/
|
|
int ossl_qtx_calculate_plaintext_payload_len(OSSL_QTX *qtx, uint32_t enc_level,
|
|
size_t ciphertext_len,
|
|
size_t *plaintext_len);
|
|
|
|
/*
|
|
* Given the value plaintext_len represented a plaintext packet payload length
|
|
* in bytes, determines how many ciphertext bytes it will encrypt to. The value
|
|
* output does not include packet headers. Returns 0 if the specified EL is not
|
|
* provisioned. The result is written to *ciphertext_len.
|
|
*/
|
|
int ossl_qtx_calculate_ciphertext_payload_len(OSSL_QTX *qtx, uint32_t enc_level,
|
|
size_t plaintext_len,
|
|
size_t *ciphertext_len);
|
|
|
|
uint32_t ossl_qrl_get_suite_cipher_tag_len(uint32_t suite_id);
|
|
|
|
|
|
/*
|
|
* Packet Transmission
|
|
* -------------------
|
|
*/
|
|
|
|
struct ossl_qtx_pkt_st {
|
|
/* Logical packet header to be serialized. */
|
|
QUIC_PKT_HDR *hdr;
|
|
|
|
/*
|
|
* iovecs expressing the logical packet payload buffer. Zero-length entries
|
|
* are permitted.
|
|
*/
|
|
const OSSL_QTX_IOVEC *iovec;
|
|
size_t num_iovec;
|
|
|
|
/* Destination address. Will be passed through to the BIO if non-NULL. */
|
|
const BIO_ADDR *peer;
|
|
|
|
/*
|
|
* Local address (optional). Specify as non-NULL only if TX BIO
|
|
* has local address support enabled.
|
|
*/
|
|
const BIO_ADDR *local;
|
|
|
|
/*
|
|
* Logical PN. Used for encryption. This will automatically be encoded to
|
|
* hdr->pn, which need not be initialized.
|
|
*/
|
|
QUIC_PN pn;
|
|
|
|
/* Packet flags. Zero or more OSSL_QTX_PKT_FLAG_* values. */
|
|
uint32_t flags;
|
|
};
|
|
|
|
/*
|
|
* More packets will be written which should be coalesced into a single
|
|
* datagram; do not send this packet yet. To use this, set this flag for all
|
|
* packets but the final packet in a datagram, then send the final packet
|
|
* without this flag set.
|
|
*
|
|
* This flag is not a guarantee and the QTX may transmit immediately anyway if
|
|
* it is not possible to fit any more packets in the current datagram.
|
|
*
|
|
* If the caller change its mind and needs to cause a packet queued with
|
|
* COALESCE after having passed it to this function but without writing another
|
|
* packet, it should call ossl_qtx_flush_pkt().
|
|
*/
|
|
#define OSSL_QTX_PKT_FLAG_COALESCE (1U << 0)
|
|
|
|
/*
|
|
* Writes a packet.
|
|
*
|
|
* *pkt need be valid only for the duration of the call to this function.
|
|
*
|
|
* pkt->hdr->data and pkt->hdr->len are unused. The payload buffer is specified
|
|
* via an array of OSSL_QTX_IOVEC structures. The API is designed to support
|
|
* single-copy transmission; data is copied from the iovecs as it is encrypted
|
|
* into an internal staging buffer for transmission.
|
|
*
|
|
* The function may modify and clobber pkt->hdr->data, pkt->hdr->len,
|
|
* pkt->hdr->key_phase and pkt->hdr->pn for its own internal use. No other
|
|
* fields of pkt or pkt->hdr will be modified.
|
|
*
|
|
* It is the callers responsibility to determine how long the PN field in the
|
|
* encoded packet should be by setting pkt->hdr->pn_len. This function takes
|
|
* care of the PN encoding. Set pkt->pn to the desired PN.
|
|
*
|
|
* Note that 1-RTT packets do not have a DCID Length field, therefore the DCID
|
|
* length must be understood contextually. This function assumes the caller
|
|
* knows what it is doing and will serialize a DCID of whatever length is given.
|
|
* It is the caller's responsibility to ensure it uses a consistent DCID length
|
|
* for communication with any given set of remote peers.
|
|
*
|
|
* The packet is queued regardless of whether it is able to be sent immediately.
|
|
* This enables packets to be batched and sent at once on systems which support
|
|
* system calls to send multiple datagrams in a single system call (see
|
|
* BIO_sendmmsg). To flush queued datagrams to the network, see
|
|
* ossl_qtx_flush_net().
|
|
*
|
|
* Returns 1 on success or 0 on failure.
|
|
*/
|
|
int ossl_qtx_write_pkt(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt);
|
|
|
|
/*
|
|
* Finish any incomplete datagrams for transmission which were flagged for
|
|
* coalescing. If there is no current coalescing datagram, this is a no-op.
|
|
*/
|
|
void ossl_qtx_finish_dgram(OSSL_QTX *qtx);
|
|
|
|
/*
|
|
* (Attempt to) flush any datagrams which are queued for transmission. Note that
|
|
* this does not cancel coalescing; call ossl_qtx_finish_dgram() first if that
|
|
* is desired. The queue is drained into the OS's sockets as much as possible.
|
|
* To determine if there is still data to be sent after calling this function,
|
|
* use ossl_qtx_get_queue_len_bytes().
|
|
*
|
|
* Returns one of the following values:
|
|
*
|
|
* QTX_FLUSH_NET_RES_OK
|
|
* Either no packets are currently queued for transmission,
|
|
* or at least one packet was successfully submitted.
|
|
*
|
|
* QTX_FLUSH_NET_RES_TRANSIENT_FAIL
|
|
* The underlying network write BIO indicated a transient error
|
|
* (e.g. buffers full).
|
|
*
|
|
* QTX_FLUSH_NET_RES_PERMANENT_FAIL
|
|
* Internal error (e.g. assertion or allocation error)
|
|
* or the underlying network write BIO indicated a non-transient
|
|
* error.
|
|
*/
|
|
#define QTX_FLUSH_NET_RES_OK 1
|
|
#define QTX_FLUSH_NET_RES_TRANSIENT_FAIL (-1)
|
|
#define QTX_FLUSH_NET_RES_PERMANENT_FAIL (-2)
|
|
|
|
int ossl_qtx_flush_net(OSSL_QTX *qtx);
|
|
|
|
/*
|
|
* Diagnostic function. If there is any datagram pending transmission, pops it
|
|
* and writes the details of the datagram as they would have been passed to
|
|
* *msg. Returns 1, or 0 if there are no datagrams pending. For test use only.
|
|
*/
|
|
int ossl_qtx_pop_net(OSSL_QTX *qtx, BIO_MSG *msg);
|
|
|
|
/* Returns number of datagrams which are fully-formed but not yet sent. */
|
|
size_t ossl_qtx_get_queue_len_datagrams(OSSL_QTX *qtx);
|
|
|
|
/*
|
|
* Returns number of payload bytes across all datagrams which are fully-formed
|
|
* but not yet sent. Does not count any incomplete coalescing datagram.
|
|
*/
|
|
size_t ossl_qtx_get_queue_len_bytes(OSSL_QTX *qtx);
|
|
|
|
/*
|
|
* Returns number of bytes in the current coalescing datagram, or 0 if there is
|
|
* no current coalescing datagram. Returns 0 after a call to
|
|
* ossl_qtx_finish_dgram().
|
|
*/
|
|
size_t ossl_qtx_get_cur_dgram_len_bytes(OSSL_QTX *qtx);
|
|
|
|
/*
|
|
* Returns number of queued coalesced packets which have not been put into a
|
|
* datagram yet. If this is non-zero, ossl_qtx_flush_pkt() needs to be called.
|
|
*/
|
|
size_t ossl_qtx_get_unflushed_pkt_count(OSSL_QTX *qtx);
|
|
|
|
/*
|
|
* Change the BIO being used by the QTX. May be NULL if actual transmission is
|
|
* not currently required. Does not up-ref the BIO; the caller is responsible
|
|
* for ensuring the lifetime of the BIO exceeds the lifetime of the QTX.
|
|
*/
|
|
void ossl_qtx_set_bio(OSSL_QTX *qtx, BIO *bio);
|
|
|
|
/* Changes the MDPL. */
|
|
int ossl_qtx_set_mdpl(OSSL_QTX *qtx, size_t mdpl);
|
|
|
|
/* Retrieves the current MDPL. */
|
|
size_t ossl_qtx_get_mdpl(OSSL_QTX *qtx);
|
|
|
|
|
|
/*
|
|
* Key Update
|
|
* ----------
|
|
*
|
|
* For additional discussion of key update considerations, see QRX header file.
|
|
*/
|
|
|
|
/*
|
|
* Triggers a key update. The key update will be started by inverting the Key
|
|
* Phase bit of the next packet transmitted; no key update occurs until the next
|
|
* packet is transmitted. Thus, this function should generally be called
|
|
* immediately before queueing the next packet.
|
|
*
|
|
* There are substantial requirements imposed by RFC 9001 on under what
|
|
* circumstances a key update can be initiated. The caller is responsible for
|
|
* meeting most of these requirements. For example, this function cannot be
|
|
* called too soon after a previous key update has occurred. Key updates also
|
|
* cannot be initiated until the 1-RTT encryption level is reached.
|
|
*
|
|
* As a sanity check, this function will fail and return 0 if the non-1RTT
|
|
* encryption levels have not yet been dropped.
|
|
*
|
|
* The caller may decide itself to initiate a key update, but it also MUST
|
|
* initiate a key update where it detects that the peer has initiated a key
|
|
* update. The caller is responsible for initiating a TX key update by calling
|
|
* this function in this circumstance; thus, the caller is responsible for
|
|
* coupling the RX and TX QUIC record layers in this way.
|
|
*/
|
|
int ossl_qtx_trigger_key_update(OSSL_QTX *qtx);
|
|
|
|
|
|
/*
|
|
* Key Expiration
|
|
* --------------
|
|
*/
|
|
|
|
/*
|
|
* Returns the number of packets which have been encrypted for transmission with
|
|
* the current set of TX keys (the current "TX key epoch"). Reset to zero after
|
|
* a key update and incremented for each packet queued. If enc_level is not
|
|
* valid or relates to an EL which is not currently available, returns
|
|
* UINT64_MAX.
|
|
*/
|
|
uint64_t ossl_qtx_get_cur_epoch_pkt_count(OSSL_QTX *qtx, uint32_t enc_level);
|
|
|
|
/*
|
|
* Returns the maximum number of packets which the record layer will permit to
|
|
* be encrypted using the current set of TX keys. If this limit is reached (that
|
|
* is, if the counter returned by ossl_qrx_tx_get_cur_epoch_pkt_count() reaches
|
|
* this value), as a safety measure, the QTX will not permit any further packets
|
|
* to be queued. All calls to ossl_qrx_write_pkt that try to send packets of a
|
|
* kind which need to be encrypted will fail. It is not possible to recover from
|
|
* this condition and the QTX must then be destroyed; therefore, callers should
|
|
* ensure they always trigger a key update well in advance of reaching this
|
|
* limit.
|
|
*
|
|
* The value returned by this function is based on the ciphersuite configured
|
|
* for the given encryption level. If keys have not been provisioned for the
|
|
* specified enc_level or the enc_level argument is invalid, this function
|
|
* returns UINT64_MAX, which is not a valid value. Note that it is not possible
|
|
* to perform a key update at any encryption level other than 1-RTT, therefore
|
|
* if this limit is reached at earlier encryption levels (which should not be
|
|
* possible) the connection must be terminated. Since this condition precludes
|
|
* the transmission of further packets, the only possible signalling of such an
|
|
* error condition to a peer is a Stateless Reset packet.
|
|
*/
|
|
uint64_t ossl_qtx_get_max_epoch_pkt_count(OSSL_QTX *qtx, uint32_t enc_level);
|
|
|
|
/*
|
|
* Get the 1-RTT EL key epoch number for the QTX. This is intended for
|
|
* diagnostic purposes. Returns 0 if 1-RTT EL is not provisioned yet.
|
|
*/
|
|
uint64_t ossl_qtx_get_key_epoch(OSSL_QTX *qtx);
|
|
|
|
# endif
|
|
|
|
#endif
|