mirror of https://github.com/openssl/openssl
TX key update support, RX time and PN reporting, general refactoring
- Adds an RX time field to the OSSL_QRX_PKT structure. - Adds a timekeeping argument to ossl_demux_new which is used to determine packet reception time. - Adds a decoded PN field to the OSSL_QRX_PKT structure. This has to be decoded by the QRX anyway, and its omission was an oversight. - Key update support for the TX side. - Minor refactoring. Reviewed-by: Paul Dale <pauli@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/18949)
This commit is contained in:
parent
1957148384
commit
948c656c66
|
@ -13,6 +13,7 @@
|
|||
# include <openssl/ssl.h>
|
||||
# include "internal/quic_types.h"
|
||||
# include "internal/bio_addr.h"
|
||||
# include "internal/time.h"
|
||||
|
||||
/*
|
||||
* QUIC Demuxer
|
||||
|
@ -110,6 +111,12 @@ struct quic_urxe_st {
|
|||
* is zeroed.
|
||||
*/
|
||||
BIO_ADDR peer, local;
|
||||
|
||||
/*
|
||||
* Time at which datagram was received (or ossl_time_zero()) if a now
|
||||
* function was not provided).
|
||||
*/
|
||||
OSSL_TIME time;
|
||||
};
|
||||
|
||||
/* Accessors for URXE buffer. */
|
||||
|
@ -163,10 +170,16 @@ typedef void (ossl_quic_demux_cb_fn)(QUIC_URXE *e, void *arg);
|
|||
* connections used on a socket. default_urxe_alloc_len is the buffer size to
|
||||
* receive datagrams into; it should be a value large enough to contain any
|
||||
* received datagram according to local MTUs, etc.
|
||||
*
|
||||
* now is an optional function used to determine the time a datagram was
|
||||
* received. now_arg is an opaque argument passed to the function. If now is
|
||||
* NULL, ossl_time_zero() is used as the datagram reception time.
|
||||
*/
|
||||
QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
|
||||
size_t short_conn_id_len,
|
||||
size_t default_urxe_alloc_len);
|
||||
size_t default_urxe_alloc_len,
|
||||
OSSL_TIME (*now)(void *arg),
|
||||
void *now_arg);
|
||||
|
||||
/*
|
||||
* Destroy a demuxer. All URXEs must have been released back to the demuxer
|
||||
|
|
|
@ -34,6 +34,9 @@ typedef struct ossl_qrx_args_st {
|
|||
|
||||
/* Initial reference PN used for RX. */
|
||||
QUIC_PN init_largest_pn[QUIC_PN_SPACE_NUM];
|
||||
|
||||
/* Initial key phase. For debugging use only; always 0 in real use. */
|
||||
unsigned char init_key_phase_bit;
|
||||
} OSSL_QRX_ARGS;
|
||||
|
||||
/* Instantiates a new QRX. */
|
||||
|
@ -92,7 +95,7 @@ int ossl_qrx_remove_dst_conn_id(OSSL_QRX *qrx,
|
|||
*
|
||||
* To transition the RX side of an EL from WAITING_FOR_KEYS to HAVE_KEYS, call
|
||||
* ossl_qrx_provide_secret (for the INITIAL EL, use of
|
||||
* ossl_qrl_provide_initial_secret is recommended).
|
||||
* ossl_quic_provide_initial_secret is recommended).
|
||||
*
|
||||
* Once keys have been provisioned for an EL, you call
|
||||
* ossl_qrx_discard_enc_level to transition the EL to the DISCARDED state. You
|
||||
|
@ -129,14 +132,14 @@ int ossl_qrx_remove_dst_conn_id(OSSL_QRX *qrx,
|
|||
* the QRX if it is not needed, for example if the QRX is being instantiated to
|
||||
* take over handling of an existing connection which has already passed the
|
||||
* INITIAL phase. This avoids the unnecessary derivation of INITIAL keys where
|
||||
* they are not needed. In the ordinary case, ossl_qrx_provide_secret_initial
|
||||
* they are not needed. In the ordinary case, ossl_quic_provide_initial_secret
|
||||
* should be called immediately after instantiation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Provides a secret to the QRX, which arises due to an encryption level change.
|
||||
* enc_level is a QUIC_ENC_LEVEL_* value. To initialise the INITIAL encryption
|
||||
* level, it is recommended to use ossl_qrl_provide_initial_secret instead.
|
||||
* level, it is recommended to use ossl_quic_provide_initial_secret instead.
|
||||
*
|
||||
* You should seek to call this function for a given EL before packets of that
|
||||
* EL arrive and are processed by the QRX. However, if packets have already
|
||||
|
@ -144,7 +147,7 @@ int ossl_qrx_remove_dst_conn_id(OSSL_QRX *qrx,
|
|||
* processing of them when this function is eventually called for the EL in
|
||||
* question.
|
||||
*
|
||||
* suite_id is a QRX_SUITE_* value which determines the AEAD function used for
|
||||
* suite_id is a QRL_SUITE_* value which determines the AEAD function used for
|
||||
* the QRX.
|
||||
*
|
||||
* The secret passed is used directly to derive the "quic key", "quic iv" and
|
||||
|
@ -218,6 +221,15 @@ typedef struct ossl_qrx_pkt_st {
|
|||
* datagrams containing INITIAL packets), as required by RFC 9000.
|
||||
*/
|
||||
size_t datagram_len;
|
||||
|
||||
/* The PN which was decoded for the packet, if the packet has a PN field. */
|
||||
QUIC_PN pn;
|
||||
|
||||
/*
|
||||
* Time the packet was received, or ossl_time_zero() if the demuxer is not
|
||||
* using a now() function.
|
||||
*/
|
||||
OSSL_TIME time;
|
||||
} OSSL_QRX_PKT;
|
||||
|
||||
/*
|
||||
|
@ -226,9 +238,9 @@ typedef struct ossl_qrx_pkt_st {
|
|||
* On success, all fields of *pkt are filled and 1 is returned.
|
||||
* Else, returns 0.
|
||||
*
|
||||
* The resources referenced by pkt->hdr, pkt->data and pkt->peer will remain
|
||||
* allocated at least until the user frees them by calling ossl_qrx_release_pkt,
|
||||
* which must be called once you are done with the packet.
|
||||
* The resources referenced by pkt->hdr, pkt->hdr->data and pkt->peer will
|
||||
* remain allocated at least until the user frees them by calling
|
||||
* ossl_qrx_release_pkt, which must be called once you are done with the packet.
|
||||
*/
|
||||
int ossl_qrx_read_pkt(OSSL_QRX *qrx, OSSL_QRX_PKT *pkt);
|
||||
|
||||
|
@ -326,7 +338,7 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
* Two keys and a timer
|
||||
*
|
||||
* "Alternatively, endpoints can retain only two sets of packet protection
|
||||
* neys, swapping previous keys for next after enough time has passed to
|
||||
* keys, swapping previous keys for next after enough time has passed to
|
||||
* allow for reordering in the network. In this case, the KP bit alone can
|
||||
* be used to select keys."
|
||||
*
|
||||
|
@ -342,11 +354,11 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
* PROVISIONED
|
||||
* _______________________________
|
||||
* | |
|
||||
* UNPROVISIONED --|----> NORMAL <----------\ |------> DROPPED
|
||||
* UNPROVISIONED --|----> NORMAL <----------\ |------> DISCARDED
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | v | |
|
||||
* | UPDATE_CONFIRMED | |
|
||||
* | UPDATING | |
|
||||
* | | | |
|
||||
* | | | |
|
||||
* | v | |
|
||||
|
@ -362,16 +374,16 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
* recorded. When a flipped Key Phase bit is detected, the RX attempts to
|
||||
* decrypt and authenticate the received packet with the 'next' keys rather than
|
||||
* the 'current' keys. If (and only if) this authentication is successful, we
|
||||
* move to the UPDATE_CONFIRMED state. (An attacker in the network could flip
|
||||
* move to the UPDATING state. (An attacker in the network could flip
|
||||
* the Key Phase bit randomly, so it is essential we do nothing until AEAD
|
||||
* authentication is complete.)
|
||||
*
|
||||
* In the UPDATE_CONFIRMED state, we know a key update is occurring and record
|
||||
* In the UPDATING state, we know a key update is occurring and record
|
||||
* the new Key Phase bit value as the newly current value, but we still keep the
|
||||
* old keys around so that we can still process any packets which were still in
|
||||
* flight when the key update was initiated. In the UPDATE_CONFIRMED state, a
|
||||
* flight when the key update was initiated. In the UPDATING state, a
|
||||
* Key Phase bit value different to the current expected value is treated not as
|
||||
* the initiation of another key update, but a reference to our old keys.
|
||||
* the initiation of another key update, but a reference to our old keys.
|
||||
*
|
||||
* Eventually we will be reasonably sure we are not going to receive any more
|
||||
* packets with the old keys. At this point, we can transition to the COOLDOWN
|
||||
|
@ -386,21 +398,25 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
* as a request for a Key Update, but this request is ignored and the packet is
|
||||
* treated as malformed. We do this to allow mitigation against malicious peers
|
||||
* trying to initiate an excessive number of Key Updates. The timeout for the
|
||||
* transition from UPDATE_CONFIRMED to COOLDOWN is recommended as adequate for
|
||||
* transition from UPDATING to COOLDOWN is recommended as adequate for
|
||||
* this purpose in itself by the RFC, so the normal additional timeout value for
|
||||
* the transition from COOLDOWN to normal is zero (immediate transition).
|
||||
*
|
||||
* A summary of each state:
|
||||
*
|
||||
* Exp KP Uses Keys KS0 KS1 If Non-Expected KP Bit
|
||||
* ------ --------- ------ ----- ----------------------
|
||||
* NORMAL 0 Keyset 0 Gen 0 Gen 1 → UPDATE_CONFIRMED
|
||||
* UPDATE_CONFIRMED 1 Keyset 1 Gen 0 Gen 1 Use Keyset 0
|
||||
* COOLDOWN 1 Keyset 1 Erased Gen 1 Ignore Packet
|
||||
* Epoch Exp KP Uses Keys KS0 KS1 If Non-Expected KP Bit
|
||||
* ----- ------ --------- ------ ----- ----------------------
|
||||
* NORMAL 0 0 Keyset 0 Gen 0 Gen 1 → UPDATING
|
||||
* UPDATING 1 1 Keyset 1 Gen 0 Gen 1 Use Keyset 0
|
||||
* COOLDOWN 1 1 Keyset 1 Erased Gen 1 Ignore Packet (*)
|
||||
*
|
||||
* NORMAL 1 Keyset 1 Gen 2 Gen 1 → UPDATE_CONFIRMED
|
||||
* UPDATE_CONFIRMED 0 Keyset 0 Gen 2 Gen 1 Use Keyset 1
|
||||
* COOLDOWN 0 Keyset 0 Gen 2 Erased Ignore Packet
|
||||
* NORMAL 1 1 Keyset 1 Gen 2 Gen 1 → UPDATING
|
||||
* UPDATING 2 0 Keyset 0 Gen 2 Gen 1 Use Keyset 1
|
||||
* COOLDOWN 2 0 Keyset 0 Gen 2 Erased Ignore Packet (*)
|
||||
*
|
||||
* (*) Actually implemented by attempting to decrypt the packet with the
|
||||
* wrong keys (which ultimately has the same outcome), as recommended
|
||||
* by RFC 9001 to avoid creating timing channels.
|
||||
*
|
||||
* Note that the key material for the next key generation ("key epoch") is
|
||||
* always kept in the NORMAL state (necessary to avoid side-channel attacks).
|
||||
|
@ -411,11 +427,11 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
* and making the necessary calls to the TX side by detecting changes to the
|
||||
* return value of ossl_qrx_get_key_epoch().
|
||||
*
|
||||
* The above states (NORMAL, UPDATE_CONFIRMED, COOLDOWN) can themselves be
|
||||
* The above states (NORMAL, UPDATING, COOLDOWN) can themselves be
|
||||
* considered substates of the PROVISIONED state. Providing a secret to the QRX
|
||||
* for an EL transitions from UNPROVISIONED, the initial state, to PROVISIONED
|
||||
* (NORMAL). Dropping key material for an EL transitions from whatever the
|
||||
* current substate of the PROVISIONED state is to the DROPPED state, which is
|
||||
* current substate of the PROVISIONED state is to the DISCARDED state, which is
|
||||
* the terminal state.
|
||||
*
|
||||
* Note that non-1RTT ELs cannot undergo key update, therefore a non-1RT EL is
|
||||
|
@ -423,8 +439,10 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
*/
|
||||
|
||||
/*
|
||||
* Return the current RX key epoch. This is initially zero and is incremented by
|
||||
* one for every Key Update successfully signalled by the peer.
|
||||
* Return the current RX key epoch for the 1-RTT encryption level. This is
|
||||
* initially zero and is incremented by one for every Key Update successfully
|
||||
* signalled by the peer. If the 1-RTT EL has not yet been provisioned or has
|
||||
* been discarded, returns UINT64_MAX.
|
||||
*
|
||||
* A necessary implication of this API is that the least significant bit of the
|
||||
* returned value corresponds to the currently expected Key Phase bit, though
|
||||
|
@ -438,21 +456,33 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
* and use it to initiate a key update on the TX side.
|
||||
*
|
||||
* The value returned by this function increments specifically at the transition
|
||||
* from the NORMAL to the UPDATE_CONFIRMED state discussed above.
|
||||
* from the NORMAL to the UPDATING state discussed above.
|
||||
*/
|
||||
uint64_t ossl_qrx_get_key_epoch(OSSL_QRX *qrx);
|
||||
|
||||
/*
|
||||
* The caller should call this after the UPDATE_CONFIRMED state is reached,
|
||||
* after a timeout to be determined by the caller.
|
||||
* Sets an optional callback which will be called when the key epoch changes.
|
||||
*
|
||||
* This transitions from the UPDATE_CONFIRMED state to the COOLDOWN state (if
|
||||
* still in the UPDATE_CONFIRMED state). If normal is 1, then transitions from
|
||||
* The callback is optional and can be unset by passing NULL for cb.
|
||||
* cb_arg is an opaque value passed to cb.
|
||||
*/
|
||||
typedef void (ossl_qrx_key_update_cb)(void *arg);
|
||||
|
||||
int ossl_qrx_set_key_update_cb(OSSL_QRX *qrx,
|
||||
ossl_qrx_key_update_cb *cb, void *cb_arg);
|
||||
|
||||
/*
|
||||
* Relates to the 1-RTT encryption level. The caller should call this after the
|
||||
* UPDATING state is reached, after a timeout to be determined by the caller.
|
||||
*
|
||||
* This transitions from the UPDATING state to the COOLDOWN state (if
|
||||
* still in the UPDATING state). If normal is 1, then transitions from
|
||||
* the COOLDOWN state to the NORMAL state. Both transitions can be performed at
|
||||
* once if desired.
|
||||
*
|
||||
* If in the normal state, or if in the COOLDOWN state and normal is 0, this is
|
||||
* a no-op and returns 1.
|
||||
* a no-op and returns 1. Returns 0 if the 1-RTT EL has not been provisioned or
|
||||
* has been dropped.
|
||||
*
|
||||
* It is essential that the caller call this within a few PTO intervals of a key
|
||||
* update occurring (as detected by the caller in a call to
|
||||
|
@ -470,19 +500,22 @@ int ossl_qrx_key_update_timeout(OSSL_QRX *qrx, int normal);
|
|||
/*
|
||||
* Returns the number of seemingly forged packets which have been received by
|
||||
* the QRX. If this value reaches the value returned by
|
||||
* ossl_qrx_get_max_epoch_forged_pkt_count(), all further received encrypted
|
||||
* packets will be discarded without processing; thus, callers should trigger a
|
||||
* key update on the TX side (which will cause the peer to trigger a key update
|
||||
* on our RX side) well before this occurs.
|
||||
* ossl_qrx_get_max_epoch_forged_pkt_count() for a given EL, all further
|
||||
* received encrypted packets for that EL will be discarded without processing.
|
||||
*
|
||||
* Note that the forged packet limit is for the connection lifetime, thus it is
|
||||
* not reset by a key update. It is suggested that the caller terminate the
|
||||
* connection a reasonable margin before the limit is reached. However, the
|
||||
* exact limit imposed does vary by EL due to the possibility that different ELs
|
||||
* use different AEADs.
|
||||
*/
|
||||
uint64_t ossl_qrx_get_cur_epoch_forged_pkt_count(OSSL_QRX *qrx,
|
||||
uint32_t enc_level);
|
||||
uint64_t ossl_qrx_get_cur_forged_pkt_count(OSSL_QRX *qrx);
|
||||
|
||||
/*
|
||||
* Returns the maximum number of forged packets which the record layer
|
||||
* will permit to be verified using the current set of RX keys.
|
||||
* Returns the maximum number of forged packets which the record layer will
|
||||
* permit to be verified using this QRX instance.
|
||||
*/
|
||||
uint64_t ossl_qrx_get_max_epoch_forged_pkt_count(OSSL_QRX *qrx,
|
||||
uint32_t enc_level);
|
||||
uint64_t ossl_qrx_get_max_forged_pkt_count(OSSL_QRX *qrx,
|
||||
uint32_t enc_level);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -163,7 +163,18 @@ typedef struct ossl_qtx_pkt_st {
|
|||
* 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);
|
||||
|
|
|
@ -31,16 +31,16 @@ static ossl_unused ossl_inline uint32_t
|
|||
ossl_quic_enc_level_to_pn_space(uint32_t enc_level)
|
||||
{
|
||||
switch (enc_level) {
|
||||
case QUIC_ENC_LEVEL_INITIAL:
|
||||
return QUIC_PN_SPACE_INITIAL;
|
||||
case QUIC_ENC_LEVEL_HANDSHAKE:
|
||||
return QUIC_PN_SPACE_HANDSHAKE;
|
||||
case QUIC_ENC_LEVEL_0RTT:
|
||||
case QUIC_ENC_LEVEL_1RTT:
|
||||
return QUIC_PN_SPACE_APP;
|
||||
default:
|
||||
assert(0);
|
||||
return QUIC_PN_SPACE_APP;
|
||||
case QUIC_ENC_LEVEL_INITIAL:
|
||||
return QUIC_PN_SPACE_INITIAL;
|
||||
case QUIC_ENC_LEVEL_HANDSHAKE:
|
||||
return QUIC_PN_SPACE_HANDSHAKE;
|
||||
case QUIC_ENC_LEVEL_0RTT:
|
||||
case QUIC_ENC_LEVEL_1RTT:
|
||||
return QUIC_PN_SPACE_APP;
|
||||
default:
|
||||
assert(0);
|
||||
return QUIC_PN_SPACE_APP;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,59 @@ ossl_quic_pkt_type_to_enc_level(uint32_t pkt_type)
|
|||
}
|
||||
}
|
||||
|
||||
/* Determine if a packet type contains an encrypted payload. */
|
||||
static ossl_inline ossl_unused int
|
||||
ossl_quic_pkt_type_is_encrypted(uint32_t pkt_type)
|
||||
{
|
||||
switch (pkt_type) {
|
||||
case QUIC_PKT_TYPE_RETRY:
|
||||
case QUIC_PKT_TYPE_VERSION_NEG:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine if a packet type contains a PN field. */
|
||||
static ossl_inline ossl_unused int
|
||||
ossl_quic_pkt_type_has_pn(uint32_t pkt_type)
|
||||
{
|
||||
/*
|
||||
* Currently a packet has a PN iff it is encrypted. This could change
|
||||
* someday.
|
||||
*/
|
||||
return ossl_quic_pkt_type_is_encrypted(pkt_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if a packet type can appear with other packets in a datagram. Some
|
||||
* packet types must be the sole packet in a datagram.
|
||||
*/
|
||||
static ossl_inline ossl_unused int
|
||||
ossl_quic_pkt_type_can_share_dgram(uint32_t pkt_type)
|
||||
{
|
||||
/*
|
||||
* Currently only the encrypted packet types can share a datagram. This
|
||||
* could change someday.
|
||||
*/
|
||||
return ossl_quic_pkt_type_is_encrypted(pkt_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if the packet type must come at the end of the datagram (due to the
|
||||
* lack of a length field).
|
||||
*/
|
||||
static ossl_inline ossl_unused int
|
||||
ossl_quic_pkt_type_must_be_last(uint32_t pkt_type)
|
||||
{
|
||||
/*
|
||||
* Any packet type which cannot share a datagram obviously must come last.
|
||||
* 1-RTT also must come last as it lacks a length field.
|
||||
*/
|
||||
return !ossl_quic_pkt_type_can_share_dgram(pkt_type)
|
||||
|| pkt_type == QUIC_PKT_TYPE_1RTT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Smallest possible QUIC packet size as per RFC (aside from version negotiation
|
||||
* packets).
|
||||
|
@ -62,7 +115,7 @@ typedef struct quic_pkt_hdr_ptrs_st QUIC_PKT_HDR_PTRS;
|
|||
*
|
||||
* Functions to apply and remove QUIC packet header protection. A header
|
||||
* protector is initialised using ossl_quic_hdr_protector_init and must be
|
||||
* destroyed using ossl_quic_hdr_protector_destroy when no longer needed.
|
||||
* destroyed using ossl_quic_hdr_protector_cleanup when no longer needed.
|
||||
*/
|
||||
typedef struct quic_hdr_protector_st {
|
||||
OSSL_LIB_CTX *libctx;
|
||||
|
@ -107,7 +160,7 @@ int ossl_quic_hdr_protector_init(QUIC_HDR_PROTECTOR *hpr,
|
|||
* OSSL_QUIC_HDR_PROTECTOR structure which has not been initialized, or which
|
||||
* has already been destroyed.
|
||||
*/
|
||||
void ossl_quic_hdr_protector_destroy(QUIC_HDR_PROTECTOR *hpr);
|
||||
void ossl_quic_hdr_protector_cleanup(QUIC_HDR_PROTECTOR *hpr);
|
||||
|
||||
/*
|
||||
* Removes header protection from a packet. The packet payload must currently be
|
||||
|
@ -255,11 +308,11 @@ typedef struct quic_pkt_hdr_st {
|
|||
/* [L] Version field. Valid if (type != 1RTT). */
|
||||
uint32_t version;
|
||||
|
||||
/* [ALL] Number of bytes in the connection ID (max 20). Always valid. */
|
||||
/* [ALL] The destination connection ID. Always valid. */
|
||||
QUIC_CONN_ID dst_conn_id;
|
||||
|
||||
/*
|
||||
* [L] Number of bytes in the connection ID (max 20).
|
||||
* [L] The source connection ID.
|
||||
* Valid if (type != 1RTT).
|
||||
*/
|
||||
QUIC_CONN_ID src_conn_id;
|
||||
|
@ -285,9 +338,13 @@ typedef struct quic_pkt_hdr_st {
|
|||
size_t token_len;
|
||||
|
||||
/*
|
||||
* [i0h] Payload length in bytes.
|
||||
* [ALL] Payload length in bytes.
|
||||
*
|
||||
* Valid if (type != 1RTT && type != RETRY && version).
|
||||
* Though 1-RTT, Retry and Version Negotiation packets do not contain an
|
||||
* explicit length field, this field is always valid and is used by the
|
||||
* packet header encoding and decoding routines to describe the payload
|
||||
* length, regardless of whether the packet type encoded or decoded uses an
|
||||
* explicit length indication.
|
||||
*/
|
||||
size_t len;
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
#include "internal/common.h"
|
||||
#include <openssl/lhash.h>
|
||||
|
||||
#define OSSL_QUIC_DEMUX_MAX_MSGS_PER_CALL 32
|
||||
#define DEMUX_MAX_MSGS_PER_CALL 32
|
||||
|
||||
void ossl_quic_urxe_remove(QUIC_URXE_LIST *l, QUIC_URXE *e)
|
||||
{
|
||||
/* Must be in list currently. */
|
||||
OPENSSL_assert((e->prev != NULL || l->head == e)
|
||||
&& (e->next != NULL || l->tail == e));
|
||||
assert((e->prev != NULL || l->head == e)
|
||||
&& (e->next != NULL || l->tail == e));
|
||||
|
||||
if (e->prev != NULL)
|
||||
e->prev->next = e->next;
|
||||
|
@ -27,7 +27,7 @@ void ossl_quic_urxe_remove(QUIC_URXE_LIST *l, QUIC_URXE *e)
|
|||
void ossl_quic_urxe_insert_head(QUIC_URXE_LIST *l, QUIC_URXE *e)
|
||||
{
|
||||
/* Must not be in list. */
|
||||
OPENSSL_assert(e->prev == NULL && e->next == NULL);
|
||||
assert(e->prev == NULL && e->next == NULL);
|
||||
|
||||
if (l->head == NULL) {
|
||||
l->head = l->tail = e;
|
||||
|
@ -44,7 +44,7 @@ void ossl_quic_urxe_insert_head(QUIC_URXE_LIST *l, QUIC_URXE *e)
|
|||
void ossl_quic_urxe_insert_tail(QUIC_URXE_LIST *l, QUIC_URXE *e)
|
||||
{
|
||||
/* Must not be in list. */
|
||||
OPENSSL_assert(e->prev == NULL && e->next == NULL);
|
||||
assert(e->prev == NULL && e->next == NULL);
|
||||
|
||||
if (l->tail == NULL) {
|
||||
l->head = l->tail = e;
|
||||
|
@ -103,6 +103,10 @@ struct quic_demux_st {
|
|||
/* Default URXE buffer size in bytes. */
|
||||
size_t default_urxe_alloc_len;
|
||||
|
||||
/* Time retrieval callback. */
|
||||
OSSL_TIME (*now)(void *arg);
|
||||
void *now_arg;
|
||||
|
||||
/* Hashtable mapping connection IDs to QUIC_DEMUX_CONN structures. */
|
||||
LHASH_OF(QUIC_DEMUX_CONN) *conns_by_id;
|
||||
|
||||
|
@ -128,7 +132,9 @@ struct quic_demux_st {
|
|||
|
||||
QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
|
||||
size_t short_conn_id_len,
|
||||
size_t default_urxe_alloc_len)
|
||||
size_t default_urxe_alloc_len,
|
||||
OSSL_TIME (*now)(void *arg),
|
||||
void *now_arg)
|
||||
{
|
||||
QUIC_DEMUX *demux;
|
||||
|
||||
|
@ -139,6 +145,8 @@ QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
|
|||
demux->net_bio = net_bio;
|
||||
demux->short_conn_id_len = short_conn_id_len;
|
||||
demux->default_urxe_alloc_len = default_urxe_alloc_len;
|
||||
demux->now = now;
|
||||
demux->now_arg = now_arg;
|
||||
|
||||
demux->conns_by_id
|
||||
= lh_QUIC_DEMUX_CONN_new(demux_conn_hash, demux_conn_cmp);
|
||||
|
@ -194,7 +202,7 @@ static QUIC_DEMUX_CONN *demux_get_by_conn_id(QUIC_DEMUX *demux,
|
|||
QUIC_DEMUX_CONN key;
|
||||
|
||||
if (dst_conn_id->id_len > QUIC_MAX_CONN_ID_LEN)
|
||||
return 0;
|
||||
return NULL;
|
||||
|
||||
key.dst_conn_id = *dst_conn_id;
|
||||
return lh_QUIC_DEMUX_CONN_retrieve(demux->conns_by_id, &key);
|
||||
|
@ -329,9 +337,10 @@ static int demux_ensure_free_urxe(QUIC_DEMUX *demux, size_t min_num_free)
|
|||
*/
|
||||
static int demux_recv(QUIC_DEMUX *demux)
|
||||
{
|
||||
BIO_MSG msg[OSSL_QUIC_DEMUX_MAX_MSGS_PER_CALL];
|
||||
ossl_ssize_t rd, i;
|
||||
BIO_MSG msg[DEMUX_MAX_MSGS_PER_CALL];
|
||||
size_t rd, i;
|
||||
QUIC_URXE *urxe = demux->urx_free.head, *unext;
|
||||
OSSL_TIME now;
|
||||
|
||||
/* This should never be called when we have any pending URXE. */
|
||||
assert(demux->urx_pending.head == NULL);
|
||||
|
@ -363,15 +372,18 @@ static int demux_recv(QUIC_DEMUX *demux)
|
|||
BIO_ADDR_clear(&urxe->local);
|
||||
}
|
||||
|
||||
rd = BIO_recvmmsg(demux->net_bio, msg, sizeof(BIO_MSG), i, 0);
|
||||
if (rd <= 0)
|
||||
if (!BIO_recvmmsg(demux->net_bio, msg, sizeof(BIO_MSG), i, 0, &rd))
|
||||
return 0;
|
||||
|
||||
now = demux->now != NULL ? demux->now(demux->now_arg) : ossl_time_zero();
|
||||
|
||||
urxe = demux->urx_free.head;
|
||||
for (i = 0; i < rd; ++i, urxe = unext) {
|
||||
unext = urxe->next;
|
||||
/* Set URXE with actual length of received datagram. */
|
||||
urxe->data_len = msg[i].data_len;
|
||||
/* Time we received datagram. */
|
||||
urxe->time = now;
|
||||
/* Move from free list to pending list. */
|
||||
ossl_quic_urxe_remove(&demux->urx_free, urxe);
|
||||
--demux->num_urx_free;
|
||||
|
@ -413,7 +425,8 @@ static int demux_process_pending_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
|
|||
QUIC_DEMUX_CONN *conn;
|
||||
|
||||
/* The next URXE we process should be at the head of the pending list. */
|
||||
OPENSSL_assert(e == demux->urx_pending.head);
|
||||
if (!ossl_assert(e == demux->urx_pending.head))
|
||||
return 0;
|
||||
|
||||
conn = demux_identify_conn(demux, e);
|
||||
if (conn == NULL) {
|
||||
|
@ -457,7 +470,7 @@ int ossl_quic_demux_pump(QUIC_DEMUX *demux)
|
|||
int ret;
|
||||
|
||||
if (demux->urx_pending.head == NULL) {
|
||||
ret = demux_ensure_free_urxe(demux, OSSL_QUIC_DEMUX_MAX_MSGS_PER_CALL);
|
||||
ret = demux_ensure_free_urxe(demux, DEMUX_MAX_MSGS_PER_CALL);
|
||||
if (ret != 1)
|
||||
return 0;
|
||||
|
||||
|
@ -517,7 +530,7 @@ int ossl_quic_demux_inject(QUIC_DEMUX *demux,
|
|||
void ossl_quic_demux_release_urxe(QUIC_DEMUX *demux,
|
||||
QUIC_URXE *e)
|
||||
{
|
||||
OPENSSL_assert(e->prev == NULL && e->next == NULL);
|
||||
assert(e->prev == NULL && e->next == NULL);
|
||||
ossl_quic_urxe_insert_tail(&demux->urx_free, e);
|
||||
++demux->num_urx_free;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,9 @@ struct rxe_st {
|
|||
/* Addresses copied from URXE. */
|
||||
BIO_ADDR peer, local;
|
||||
|
||||
/* Time we received the packet (not when we processed it). */
|
||||
OSSL_TIME time;
|
||||
|
||||
/* Total length of the datagram which contained this packet. */
|
||||
size_t datagram_len;
|
||||
|
||||
|
@ -147,9 +150,23 @@ struct ossl_qrx_st {
|
|||
/* Bytes we have received since this counter was last cleared. */
|
||||
uint64_t bytes_received;
|
||||
|
||||
/*
|
||||
* Number of forged packets we have received since the QRX was instantiated.
|
||||
* Note that as per RFC 9001, this is connection-level state; it is not per
|
||||
* EL and is not reset by a key update.
|
||||
*/
|
||||
uint64_t forged_pkt_count;
|
||||
|
||||
/* Validation callback. */
|
||||
ossl_qrx_early_validation_cb *validation_cb;
|
||||
void *validation_cb_arg;
|
||||
ossl_qrx_early_validation_cb *validation_cb;
|
||||
void *validation_cb_arg;
|
||||
|
||||
/* Key update callback. */
|
||||
ossl_qrx_key_update_cb *key_update_cb;
|
||||
void *key_update_cb_arg;
|
||||
|
||||
/* Initial key phase. For debugging use only; always 0 in real use. */
|
||||
unsigned char init_key_phase_bit;
|
||||
};
|
||||
|
||||
static void qrx_on_rx(QUIC_URXE *urxe, void *arg);
|
||||
|
@ -173,26 +190,31 @@ OSSL_QRX *ossl_qrx_new(const OSSL_QRX_ARGS *args)
|
|||
qrx->propq = args->propq;
|
||||
qrx->demux = args->demux;
|
||||
qrx->short_conn_id_len = args->short_conn_id_len;
|
||||
qrx->init_key_phase_bit = args->init_key_phase_bit;
|
||||
return qrx;
|
||||
}
|
||||
|
||||
static void qrx_cleanup_rxl(RXE_LIST *l)
|
||||
{
|
||||
RXE *e, *enext;
|
||||
|
||||
for (e = l->head; e != NULL; e = enext) {
|
||||
enext = e->next;
|
||||
OPENSSL_free(e);
|
||||
}
|
||||
|
||||
l->head = l->tail = NULL;
|
||||
}
|
||||
|
||||
static void qrx_cleanup_urxl(OSSL_QRX *qrx, QUIC_URXE_LIST *l)
|
||||
{
|
||||
QUIC_URXE *e, *enext;
|
||||
|
||||
for (e = l->head; e != NULL; e = enext) {
|
||||
enext = e->next;
|
||||
ossl_quic_demux_release_urxe(qrx->demux, e);
|
||||
}
|
||||
|
||||
l->head = l->tail = NULL;
|
||||
}
|
||||
|
||||
|
@ -211,7 +233,7 @@ void ossl_qrx_free(OSSL_QRX *qrx)
|
|||
|
||||
/* Drop keying material and crypto resources. */
|
||||
for (i = 0; i < QUIC_ENC_LEVEL_NUM; ++i)
|
||||
ossl_qrl_enc_level_set_discard(&qrx->el_set, i, 1);
|
||||
ossl_qrl_enc_level_set_discard(&qrx->el_set, i);
|
||||
|
||||
OPENSSL_free(qrx);
|
||||
}
|
||||
|
@ -265,7 +287,9 @@ int ossl_qrx_provide_secret(OSSL_QRX *qrx, uint32_t enc_level,
|
|||
suite_id,
|
||||
md,
|
||||
secret,
|
||||
secret_len))
|
||||
secret_len,
|
||||
qrx->init_key_phase_bit,
|
||||
/*is_tx=*/0))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -282,7 +306,7 @@ int ossl_qrx_discard_enc_level(OSSL_QRX *qrx, uint32_t enc_level)
|
|||
if (enc_level >= QUIC_ENC_LEVEL_NUM)
|
||||
return 0;
|
||||
|
||||
ossl_qrl_enc_level_set_discard(&qrx->el_set, enc_level, 1);
|
||||
ossl_qrl_enc_level_set_discard(&qrx->el_set, enc_level);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -372,7 +396,7 @@ static RXE *qrx_resize_rxe(RXE_LIST *rxl, RXE *rxe, size_t n)
|
|||
*/
|
||||
rxe2 = OPENSSL_realloc(rxe, sizeof(RXE) + n);
|
||||
if (rxe2 == NULL)
|
||||
/* original RXE is still in tact unchanged */
|
||||
/* original RXE is still intact unchanged */
|
||||
return NULL;
|
||||
|
||||
if (rxe != rxe2) {
|
||||
|
@ -480,8 +504,7 @@ static int qrx_validate_hdr_early(OSSL_QRX *qrx, RXE *rxe,
|
|||
return 0;
|
||||
|
||||
/* Version negotiation and retry packets must be the first packet. */
|
||||
if (first_rxe != NULL && (rxe->hdr.type == QUIC_PKT_TYPE_VERSION_NEG
|
||||
|| rxe->hdr.type == QUIC_PKT_TYPE_RETRY))
|
||||
if (first_rxe != NULL && !ossl_quic_pkt_type_can_share_dgram(rxe->hdr.type))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -517,6 +540,44 @@ static int qrx_validate_hdr(OSSL_QRX *qrx, RXE *rxe)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Retrieves the correct cipher context for an EL and key phase. */
|
||||
static size_t qrx_get_cipher_ctx_idx(OSSL_QRX *qrx, OSSL_QRL_ENC_LEVEL *el,
|
||||
uint32_t enc_level,
|
||||
unsigned char key_phase_bit)
|
||||
{
|
||||
if (enc_level != QUIC_ENC_LEVEL_1RTT)
|
||||
return 0;
|
||||
|
||||
if (!ossl_assert(key_phase_bit <= 1))
|
||||
return SIZE_MAX;
|
||||
|
||||
/*
|
||||
* RFC 9001 requires that we not create timing channels which could reveal
|
||||
* the decrypted value of the Key Phase bit. We usually handle this by
|
||||
* keeping the cipher contexts for both the current and next key epochs
|
||||
* around, so that we just select a cipher context blindly using the key
|
||||
* phase bit, which is time-invariant.
|
||||
*
|
||||
* In the COOLDOWN state, we only have one keyslot/cipher context. RFC 9001
|
||||
* suggests an implementation strategy to avoid creating a timing channel in
|
||||
* this case:
|
||||
*
|
||||
* Endpoints can use randomized packet protection keys in place of
|
||||
* discarded keys when key updates are not yet permitted.
|
||||
*
|
||||
* Rather than use a randomised key, we simply use our existing key as it
|
||||
* will fail AEAD verification anyway. This avoids the need to keep around a
|
||||
* dedicated garbage key.
|
||||
*
|
||||
* Note: Accessing different cipher contexts is technically not
|
||||
* timing-channel safe due to microarchitectural side channels, but this is
|
||||
* the best we can reasonably do and appears to be directly suggested by the
|
||||
* RFC.
|
||||
*/
|
||||
return el->state == QRL_EL_STATE_PROV_COOLDOWN ? el->key_epoch & 1
|
||||
: key_phase_bit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to decrypt a packet payload.
|
||||
*
|
||||
|
@ -530,13 +591,15 @@ 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)
|
||||
QUIC_PN pn, uint32_t enc_level,
|
||||
unsigned char key_phase_bit)
|
||||
{
|
||||
int l = 0, l2 = 0;
|
||||
unsigned char nonce[EVP_MAX_IV_LENGTH];
|
||||
size_t nonce_len, i;
|
||||
size_t nonce_len, i, cctx_idx;
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(&qrx->el_set,
|
||||
enc_level, 1);
|
||||
EVP_CIPHER_CTX *cctx;
|
||||
|
||||
if (src_len > INT_MAX || aad_len > INT_MAX)
|
||||
return 0;
|
||||
|
@ -548,45 +611,51 @@ static int qrx_decrypt_pkt_body(OSSL_QRX *qrx, unsigned char *dst,
|
|||
if (el->tag_len >= src_len)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
/*
|
||||
* If we have failed to authenticate a certain number of ciphertexts, refuse
|
||||
* to decrypt any more ciphertexts.
|
||||
*/
|
||||
if (el->op_count >= ossl_qrl_get_suite_max_forged_pkt(el->suite_id))
|
||||
if (qrx->forged_pkt_count >= ossl_qrl_get_suite_max_forged_pkt(el->suite_id))
|
||||
return 0;
|
||||
|
||||
cctx_idx = qrx_get_cipher_ctx_idx(qrx, el, enc_level, key_phase_bit);
|
||||
if (!ossl_assert(cctx_idx < OSSL_NELEM(el->cctx)))
|
||||
return 0;
|
||||
|
||||
cctx = el->cctx[cctx_idx];
|
||||
|
||||
/* Construct nonce (nonce=IV ^ PN). */
|
||||
nonce_len = EVP_CIPHER_CTX_get_iv_length(el->cctx);
|
||||
nonce_len = EVP_CIPHER_CTX_get_iv_length(cctx);
|
||||
if (!ossl_assert(nonce_len >= sizeof(QUIC_PN)))
|
||||
return 0;
|
||||
|
||||
memcpy(nonce, el->iv, nonce_len);
|
||||
memcpy(nonce, el->iv[cctx_idx], nonce_len);
|
||||
for (i = 0; i < sizeof(QUIC_PN); ++i)
|
||||
nonce[nonce_len - i - 1] ^= (unsigned char)(pn >> (i * 8));
|
||||
|
||||
/* type and key will already have been setup; feed the IV. */
|
||||
if (EVP_CipherInit_ex(el->cctx, NULL,
|
||||
if (EVP_CipherInit_ex(cctx, NULL,
|
||||
NULL, NULL, nonce, /*enc=*/0) != 1)
|
||||
return 0;
|
||||
|
||||
/* Feed the AEAD tag we got so the cipher can validate it. */
|
||||
if (EVP_CIPHER_CTX_ctrl(el->cctx, EVP_CTRL_AEAD_SET_TAG,
|
||||
if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_SET_TAG,
|
||||
el->tag_len,
|
||||
(unsigned char *)src + src_len - el->tag_len) != 1)
|
||||
return 0;
|
||||
|
||||
/* Feed AAD data. */
|
||||
if (EVP_CipherUpdate(el->cctx, NULL, &l, aad, aad_len) != 1)
|
||||
if (EVP_CipherUpdate(cctx, NULL, &l, aad, aad_len) != 1)
|
||||
return 0;
|
||||
|
||||
/* Feed encrypted packet body. */
|
||||
if (EVP_CipherUpdate(el->cctx, dst, &l, src, src_len - el->tag_len) != 1)
|
||||
if (EVP_CipherUpdate(cctx, dst, &l, src, src_len - el->tag_len) != 1)
|
||||
return 0;
|
||||
|
||||
/* Ensure authentication succeeded. */
|
||||
if (EVP_CipherFinal_ex(el->cctx, NULL, &l2) != 1) {
|
||||
if (EVP_CipherFinal_ex(cctx, NULL, &l2) != 1) {
|
||||
/* Authentication failed, increment failed auth counter. */
|
||||
++el->op_count;
|
||||
++qrx->forged_pkt_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -599,6 +668,15 @@ static ossl_inline void ignore_res(int x)
|
|||
/* No-op. */
|
||||
}
|
||||
|
||||
static void qrx_key_update_initiated(OSSL_QRX *qrx)
|
||||
{
|
||||
if (!ossl_qrl_enc_level_set_key_update(&qrx->el_set, QUIC_ENC_LEVEL_1RTT))
|
||||
return;
|
||||
|
||||
if (qrx->key_update_cb != NULL)
|
||||
qrx->key_update_cb(qrx->key_update_cb_arg);
|
||||
}
|
||||
|
||||
/* Process a single packet in a datagram. */
|
||||
static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
||||
PACKET *pkt, size_t pkt_idx,
|
||||
|
@ -614,6 +692,7 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
char need_second_decode = 0, already_processed = 0;
|
||||
QUIC_PKT_HDR_PTRS ptrs;
|
||||
uint32_t pn_space, enc_level;
|
||||
OSSL_QRL_ENC_LEVEL *el = NULL;
|
||||
|
||||
/*
|
||||
* Get a free RXE. If we need to allocate a new one, use the packet length
|
||||
|
@ -634,8 +713,8 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
*/
|
||||
need_second_decode = !pkt_is_marked(&urxe->hpr_removed, pkt_idx);
|
||||
if (!ossl_quic_wire_decode_pkt_hdr(pkt,
|
||||
qrx->short_conn_id_len,
|
||||
need_second_decode, &rxe->hdr, &ptrs))
|
||||
qrx->short_conn_id_len,
|
||||
need_second_decode, &rxe->hdr, &ptrs))
|
||||
goto malformed;
|
||||
|
||||
/*
|
||||
|
@ -646,7 +725,7 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
|
||||
/*
|
||||
* Make a note of the first RXE so we can later ensure the destination
|
||||
* connection IDs of all packets in a datagram mater.
|
||||
* connection IDs of all packets in a datagram match.
|
||||
*/
|
||||
if (pkt_idx == 0)
|
||||
*first_rxe = rxe;
|
||||
|
@ -657,10 +736,13 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
*/
|
||||
if (already_processed
|
||||
|| !qrx_validate_hdr_early(qrx, rxe, pkt_idx == 0 ? NULL : *first_rxe))
|
||||
/*
|
||||
* Already processed packets are handled identically to malformed
|
||||
* packets; i.e., they are ignored.
|
||||
*/
|
||||
goto malformed;
|
||||
|
||||
if (rxe->hdr.type == QUIC_PKT_TYPE_VERSION_NEG
|
||||
|| rxe->hdr.type == QUIC_PKT_TYPE_RETRY) {
|
||||
if (!ossl_quic_pkt_type_is_encrypted(rxe->hdr.type)) {
|
||||
/*
|
||||
* Version negotiation and retry packets are a special case. They do not
|
||||
* contain a payload which needs decrypting and have no header
|
||||
|
@ -679,7 +761,8 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
memcpy(rxe_data(rxe), rxe->hdr.data, rxe->hdr.len);
|
||||
pkt_mark(&urxe->processed, pkt_idx);
|
||||
|
||||
rxe->hdr.data = rxe_data(rxe);
|
||||
rxe->hdr.data = rxe_data(rxe);
|
||||
rxe->pn = QUIC_PN_INVALID;
|
||||
|
||||
/* Move RXE to pending. */
|
||||
rxe_remove(&qrx->rx_free, rxe);
|
||||
|
@ -728,11 +811,10 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
/* Now remove header protection. */
|
||||
*pkt = orig_pkt;
|
||||
|
||||
if (need_second_decode) {
|
||||
OSSL_QRL_ENC_LEVEL *el
|
||||
= ossl_qrl_enc_level_set_get(&qrx->el_set, enc_level, 1);
|
||||
el = ossl_qrl_enc_level_set_get(&qrx->el_set, enc_level, 1);
|
||||
assert(el != NULL); /* Already checked above */
|
||||
|
||||
assert(el != NULL); /* Already checked above */
|
||||
if (need_second_decode) {
|
||||
if (!ossl_quic_hdr_protector_decrypt(&el->hpr, &ptrs))
|
||||
goto malformed;
|
||||
|
||||
|
@ -757,7 +839,7 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
* HANDSHAKE packet.
|
||||
*/
|
||||
if (enc_level == QUIC_ENC_LEVEL_HANDSHAKE)
|
||||
ossl_qrl_enc_level_set_discard(&qrx->el_set, QUIC_ENC_LEVEL_INITIAL, 1);
|
||||
ossl_qrl_enc_level_set_discard(&qrx->el_set, QUIC_ENC_LEVEL_INITIAL);
|
||||
|
||||
/*
|
||||
* The AAD data is the entire (unprotected) packet header including the PN.
|
||||
|
@ -792,9 +874,19 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
*/
|
||||
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, enc_level))
|
||||
&dec_len, sop, aad_len, rxe->pn, enc_level,
|
||||
rxe->hdr.key_phase))
|
||||
goto malformed;
|
||||
|
||||
/*
|
||||
* At this point, we have successfully authenticated the AEAD tag and no
|
||||
* longer need to worry about exposing the Key Phase bit in timing channels.
|
||||
* Check for a Key Phase bit differing from our expectation.
|
||||
*/
|
||||
if (rxe->hdr.type == QUIC_PKT_TYPE_1RTT
|
||||
&& rxe->hdr.key_phase != (el->key_epoch & 1))
|
||||
qrx_key_update_initiated(qrx);
|
||||
|
||||
/*
|
||||
* We have now successfully decrypted the packet payload. If there are
|
||||
* additional packets in the datagram, it is possible we will fail to
|
||||
|
@ -826,9 +918,10 @@ static int qrx_process_pkt(OSSL_QRX *qrx, QUIC_URXE *urxe,
|
|||
if (rxe->pn > qrx->largest_pn[pn_space])
|
||||
qrx->largest_pn[pn_space] = rxe->pn;
|
||||
|
||||
/* Copy across network addresses from URXE to RXE. */
|
||||
/* Copy across network addresses and RX time from URXE to RXE. */
|
||||
rxe->peer = urxe->peer;
|
||||
rxe->local = urxe->local;
|
||||
rxe->time = urxe->time;
|
||||
|
||||
/* Move RXE to pending. */
|
||||
rxe_remove(&qrx->rx_free, rxe);
|
||||
|
@ -915,7 +1008,7 @@ static int qrx_process_datagram(OSSL_QRX *qrx, QUIC_URXE *e,
|
|||
* we should still try to process any packets following it.
|
||||
*
|
||||
* In the case where the packet is so malformed we can't determine its
|
||||
* lenngth, qrx_process_pkt will take care of advancing to the end of
|
||||
* length, qrx_process_pkt will take care of advancing to the end of
|
||||
* the packet, so we will exit the loop automatically in this case.
|
||||
*/
|
||||
if (qrx_process_pkt(qrx, e, &pkt, pkt_idx, &first_rxe, data_len))
|
||||
|
@ -927,7 +1020,7 @@ static int qrx_process_datagram(OSSL_QRX *qrx, QUIC_URXE *e,
|
|||
}
|
||||
|
||||
/* Process a single pending URXE. */
|
||||
static int qrx_process_one_urxl(OSSL_QRX *qrx, QUIC_URXE *e)
|
||||
static int qrx_process_one_urxe(OSSL_QRX *qrx, QUIC_URXE *e)
|
||||
{
|
||||
int was_deferred;
|
||||
|
||||
|
@ -958,12 +1051,12 @@ static int qrx_process_one_urxl(OSSL_QRX *qrx, QUIC_URXE *e)
|
|||
}
|
||||
|
||||
/* Process any pending URXEs to generate pending RXEs. */
|
||||
static int qrx_process_urxl(OSSL_QRX *qrx)
|
||||
static int qrx_process_pending_urxl(OSSL_QRX *qrx)
|
||||
{
|
||||
QUIC_URXE *e;
|
||||
|
||||
while ((e = qrx->urx_pending.head) != NULL)
|
||||
if (!qrx_process_one_urxl(qrx, e))
|
||||
if (!qrx_process_one_urxe(qrx, e))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
|
@ -974,7 +1067,7 @@ int ossl_qrx_read_pkt(OSSL_QRX *qrx, OSSL_QRX_PKT *pkt)
|
|||
RXE *rxe;
|
||||
|
||||
if (!ossl_qrx_processed_read_pending(qrx)) {
|
||||
if (!qrx_process_urxl(qrx))
|
||||
if (!qrx_process_pending_urxl(qrx))
|
||||
return 0;
|
||||
|
||||
if (!ossl_qrx_processed_read_pending(qrx))
|
||||
|
@ -987,6 +1080,8 @@ int ossl_qrx_read_pkt(OSSL_QRX *qrx, OSSL_QRX_PKT *pkt)
|
|||
|
||||
pkt->handle = rxe;
|
||||
pkt->hdr = &rxe->hdr;
|
||||
pkt->pn = rxe->pn;
|
||||
pkt->time = rxe->time;
|
||||
pkt->peer
|
||||
= BIO_ADDR_family(&rxe->peer) != AF_UNSPEC ? &rxe->peer : NULL;
|
||||
pkt->local
|
||||
|
@ -1020,17 +1115,51 @@ int ossl_qrx_set_early_validation_cb(OSSL_QRX *qrx,
|
|||
return 1;
|
||||
}
|
||||
|
||||
uint64_t ossl_qrx_get_cur_epoch_forged_pkt_count(OSSL_QRX *qrx,
|
||||
uint32_t enc_level)
|
||||
int ossl_qrx_set_key_update_cb(OSSL_QRX *qrx,
|
||||
ossl_qrx_key_update_cb *cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(&qrx->el_set,
|
||||
enc_level, 1);
|
||||
|
||||
return el == NULL ? UINT64_MAX : el->op_count;
|
||||
qrx->key_update_cb = cb;
|
||||
qrx->key_update_cb_arg = cb_arg;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t ossl_qrx_get_max_epoch_forged_pkt_count(OSSL_QRX *qrx,
|
||||
uint32_t enc_level)
|
||||
uint64_t ossl_qrx_get_key_epoch(OSSL_QRX *qrx)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(&qrx->el_set,
|
||||
QUIC_ENC_LEVEL_1RTT, 1);
|
||||
|
||||
return el == NULL ? UINT64_MAX : el->key_epoch;
|
||||
}
|
||||
|
||||
int ossl_qrx_key_update_timeout(OSSL_QRX *qrx, int normal)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(&qrx->el_set,
|
||||
QUIC_ENC_LEVEL_1RTT, 1);
|
||||
|
||||
if (el == NULL)
|
||||
return 0;
|
||||
|
||||
if (el->state == QRL_EL_STATE_PROV_UPDATING
|
||||
&& !ossl_qrl_enc_level_set_key_update_done(&qrx->el_set,
|
||||
QUIC_ENC_LEVEL_1RTT))
|
||||
return 0;
|
||||
|
||||
if (normal && el->state == QRL_EL_STATE_PROV_COOLDOWN
|
||||
&& !ossl_qrl_enc_level_set_key_cooldown_done(&qrx->el_set,
|
||||
QUIC_ENC_LEVEL_1RTT))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t ossl_qrx_get_cur_forged_pkt_count(OSSL_QRX *qrx)
|
||||
{
|
||||
return qrx->forged_pkt_count;
|
||||
}
|
||||
|
||||
uint64_t ossl_qrx_get_max_forged_pkt_count(OSSL_QRX *qrx,
|
||||
uint32_t enc_level)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(&qrx->el_set,
|
||||
enc_level, 1);
|
||||
|
|
|
@ -13,10 +13,13 @@ static const unsigned char quic_v1_key_label[] = {
|
|||
static const unsigned char quic_v1_hp_label[] = {
|
||||
0x71, 0x75, 0x69, 0x63, 0x20, 0x68, 0x70 /* "quic hp" */
|
||||
};
|
||||
static const unsigned char quic_v1_ku_label[] = {
|
||||
0x71, 0x75, 0x69, 0x63, 0x20, 0x6b, 0x75 /* "quic ku" */
|
||||
};
|
||||
|
||||
OSSL_QRL_ENC_LEVEL *ossl_qrl_enc_level_set_get(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level,
|
||||
int require_valid)
|
||||
int require_prov)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el;
|
||||
|
||||
|
@ -25,8 +28,15 @@ OSSL_QRL_ENC_LEVEL *ossl_qrl_enc_level_set_get(OSSL_QRL_ENC_LEVEL_SET *els,
|
|||
|
||||
el = &els->el[enc_level];
|
||||
|
||||
if (require_valid && (el->cctx == NULL || el->discarded))
|
||||
return NULL;
|
||||
if (require_prov)
|
||||
switch (el->state) {
|
||||
case QRL_EL_STATE_PROV_NORMAL:
|
||||
case QRL_EL_STATE_PROV_UPDATING:
|
||||
case QRL_EL_STATE_PROV_COOLDOWN:
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
@ -36,27 +46,139 @@ int ossl_qrl_enc_level_set_have_el(OSSL_QRL_ENC_LEVEL_SET *els,
|
|||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
|
||||
if (el == NULL)
|
||||
switch (el->state) {
|
||||
case QRL_EL_STATE_UNPROV:
|
||||
return 0;
|
||||
case QRL_EL_STATE_PROV_NORMAL:
|
||||
case QRL_EL_STATE_PROV_UPDATING:
|
||||
case QRL_EL_STATE_PROV_COOLDOWN:
|
||||
return 1;
|
||||
default:
|
||||
case QRL_EL_STATE_DISCARDED:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ossl_qrl_enc_level_set_has_keyslot(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level,
|
||||
unsigned char tgt_state,
|
||||
size_t keyslot)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
|
||||
if (!ossl_assert(el != NULL && keyslot < 2))
|
||||
return 0;
|
||||
if (el->cctx != NULL)
|
||||
return 1;
|
||||
if (el->discarded)
|
||||
return -1;
|
||||
|
||||
switch (tgt_state) {
|
||||
case QRL_EL_STATE_PROV_NORMAL:
|
||||
case QRL_EL_STATE_PROV_UPDATING:
|
||||
return enc_level == QUIC_ENC_LEVEL_1RTT || keyslot == 0;
|
||||
case QRL_EL_STATE_PROV_COOLDOWN:
|
||||
assert(enc_level == QUIC_ENC_LEVEL_1RTT);
|
||||
return keyslot == (el->key_epoch & 1);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void el_teardown_keyslot(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level,
|
||||
size_t keyslot)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
|
||||
if (!ossl_qrl_enc_level_set_has_keyslot(els, enc_level, el->state, keyslot))
|
||||
return;
|
||||
|
||||
if (el->cctx[keyslot] != NULL) {
|
||||
EVP_CIPHER_CTX_free(el->cctx[keyslot]);
|
||||
el->cctx[keyslot] = NULL;
|
||||
}
|
||||
|
||||
OPENSSL_cleanse(el->iv[keyslot], sizeof(el->iv[keyslot]));
|
||||
}
|
||||
|
||||
static int el_setup_keyslot(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level,
|
||||
unsigned char tgt_state,
|
||||
size_t keyslot,
|
||||
const unsigned char *secret,
|
||||
size_t secret_len)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
unsigned char key[EVP_MAX_KEY_LENGTH];
|
||||
size_t key_len = 0, iv_len = 0;
|
||||
const char *cipher_name = NULL;
|
||||
EVP_CIPHER *cipher = NULL;
|
||||
EVP_CIPHER_CTX *cctx = NULL;
|
||||
|
||||
if (!ossl_assert(el != NULL
|
||||
&& ossl_qrl_enc_level_set_has_keyslot(els, enc_level,
|
||||
tgt_state, keyslot)))
|
||||
return 0;
|
||||
|
||||
cipher_name = ossl_qrl_get_suite_cipher_name(el->suite_id);
|
||||
iv_len = ossl_qrl_get_suite_cipher_iv_len(el->suite_id);
|
||||
key_len = ossl_qrl_get_suite_cipher_key_len(el->suite_id);
|
||||
if (cipher_name == NULL)
|
||||
return 0;
|
||||
|
||||
if (secret_len != ossl_qrl_get_suite_secret_len(el->suite_id)
|
||||
|| secret_len > EVP_MAX_KEY_LENGTH)
|
||||
return 0;
|
||||
|
||||
assert(el->cctx[keyslot] == NULL);
|
||||
|
||||
/* Derive "quic iv" key. */
|
||||
if (!tls13_hkdf_expand_ex(el->libctx, el->propq,
|
||||
el->md,
|
||||
secret,
|
||||
quic_v1_iv_label,
|
||||
sizeof(quic_v1_iv_label),
|
||||
NULL, 0,
|
||||
el->iv[keyslot], iv_len, 0))
|
||||
goto err;
|
||||
|
||||
/* Derive "quic key" key. */
|
||||
if (!tls13_hkdf_expand_ex(el->libctx, el->propq,
|
||||
el->md,
|
||||
secret,
|
||||
quic_v1_key_label,
|
||||
sizeof(quic_v1_key_label),
|
||||
NULL, 0,
|
||||
key, key_len, 0))
|
||||
goto err;
|
||||
|
||||
/* Create and initialise cipher context. */
|
||||
if ((cipher = EVP_CIPHER_fetch(el->libctx, cipher_name, el->propq)) == NULL)
|
||||
goto err;
|
||||
|
||||
if ((cctx = EVP_CIPHER_CTX_new()) == NULL)
|
||||
goto err;
|
||||
|
||||
if (!ossl_assert(iv_len == (size_t)EVP_CIPHER_get_iv_length(cipher))
|
||||
|| !ossl_assert(key_len == (size_t)EVP_CIPHER_get_key_length(cipher)))
|
||||
goto err;
|
||||
|
||||
/* IV will be changed on RX/TX so we don't need to use a real value here. */
|
||||
if (!EVP_CipherInit_ex(cctx, cipher, NULL, key, el->iv[keyslot], 0))
|
||||
goto err;
|
||||
|
||||
el->cctx[keyslot] = cctx;
|
||||
|
||||
/* Zeroize intermediate keys. */
|
||||
OPENSSL_cleanse(key, sizeof(key));
|
||||
EVP_CIPHER_free(cipher);
|
||||
return 1;
|
||||
|
||||
err:
|
||||
EVP_CIPHER_CTX_free(cctx);
|
||||
EVP_CIPHER_free(cipher);
|
||||
OPENSSL_cleanse(el->iv[keyslot], sizeof(el->iv[keyslot]));
|
||||
OPENSSL_cleanse(key, sizeof(key));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets up cryptographic state for a given encryption level and direction by
|
||||
* deriving "quic iv", "quic key" and "quic hp" values from a given secret.
|
||||
*
|
||||
* md is a hash function used for key derivation. If it is NULL, this function
|
||||
* fetches the necessary hash function itself. If it is non-NULL, this function
|
||||
* can reuse the caller's reference to a suitable EVP_MD; the EVP_MD provided
|
||||
* must match the suite.
|
||||
*
|
||||
* On success where md is non-NULL, takes ownership of the caller's reference to
|
||||
* md.
|
||||
*/
|
||||
int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
OSSL_LIB_CTX *libctx,
|
||||
const char *propq,
|
||||
|
@ -64,58 +186,41 @@ int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els,
|
|||
uint32_t suite_id,
|
||||
EVP_MD *md,
|
||||
const unsigned char *secret,
|
||||
size_t secret_len)
|
||||
size_t secret_len,
|
||||
unsigned char init_key_phase_bit,
|
||||
int is_tx)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
unsigned char key[EVP_MAX_KEY_LENGTH], hpr_key[EVP_MAX_KEY_LENGTH];
|
||||
size_t key_len = 0, hpr_key_len = 0, iv_len = 0;
|
||||
const char *cipher_name = NULL, *md_name = NULL;
|
||||
EVP_CIPHER *cipher = NULL;
|
||||
EVP_CIPHER_CTX *cctx = NULL;
|
||||
int own_md = 0, have_hpr = 0;
|
||||
unsigned char ku_key[EVP_MAX_KEY_LENGTH], hpr_key[EVP_MAX_KEY_LENGTH];
|
||||
int have_ks0 = 0, have_ks1 = 0, own_md = 0;
|
||||
const char *md_name = ossl_qrl_get_suite_md_name(suite_id);
|
||||
size_t hpr_key_len, init_keyslot;
|
||||
|
||||
if (el == NULL || el->discarded)
|
||||
/* Should not be trying to reinitialise an EL which was discarded. */
|
||||
if (el == NULL || el->state != QRL_EL_STATE_UNPROV || md_name == NULL
|
||||
|| init_key_phase_bit > 1 || is_tx < 0 || is_tx > 1)
|
||||
return 0;
|
||||
|
||||
cipher_name = ossl_qrl_get_suite_cipher_name(suite_id);
|
||||
iv_len = ossl_qrl_get_suite_cipher_iv_len(suite_id);
|
||||
key_len = ossl_qrl_get_suite_cipher_key_len(suite_id);
|
||||
init_keyslot = is_tx ? 0 : init_key_phase_bit;
|
||||
hpr_key_len = ossl_qrl_get_suite_hdr_prot_key_len(suite_id);
|
||||
if (cipher_name == NULL)
|
||||
return 0;
|
||||
|
||||
if (secret_len != ossl_qrl_get_suite_secret_len(suite_id))
|
||||
if (hpr_key_len == 0)
|
||||
return 0;
|
||||
|
||||
if (md == NULL) {
|
||||
md_name = ossl_qrl_get_suite_md_name(suite_id);
|
||||
|
||||
if ((md = EVP_MD_fetch(libctx, md_name, propq)) == NULL)
|
||||
md = EVP_MD_fetch(libctx, md_name, propq);
|
||||
if (md == NULL)
|
||||
return 0;
|
||||
|
||||
own_md = 1;
|
||||
}
|
||||
|
||||
/* Derive "quic iv" key. */
|
||||
if (!tls13_hkdf_expand_ex(libctx, propq,
|
||||
md,
|
||||
secret,
|
||||
quic_v1_iv_label,
|
||||
sizeof(quic_v1_iv_label),
|
||||
NULL, 0,
|
||||
el->iv, iv_len, 0))
|
||||
goto err;
|
||||
|
||||
/* Derive "quic key" key. */
|
||||
if (!tls13_hkdf_expand_ex(libctx, propq,
|
||||
md,
|
||||
secret,
|
||||
quic_v1_key_label,
|
||||
sizeof(quic_v1_key_label),
|
||||
NULL, 0,
|
||||
key, key_len, 0))
|
||||
goto err;
|
||||
el->libctx = libctx;
|
||||
el->propq = propq;
|
||||
el->md = md;
|
||||
el->suite_id = suite_id;
|
||||
el->tag_len = ossl_qrl_get_suite_cipher_tag_len(suite_id);
|
||||
el->op_count = 0;
|
||||
el->key_epoch = (uint64_t)init_key_phase_bit;
|
||||
el->is_tx = (unsigned char)is_tx;
|
||||
|
||||
/* Derive "quic hp" key. */
|
||||
if (!tls13_hkdf_expand_ex(libctx, propq,
|
||||
|
@ -127,83 +232,206 @@ int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els,
|
|||
hpr_key, hpr_key_len, 0))
|
||||
goto err;
|
||||
|
||||
/* Free any old context which is using old keying material. */
|
||||
if (el->cctx != NULL) {
|
||||
ossl_quic_hdr_protector_destroy(&el->hpr);
|
||||
EVP_CIPHER_CTX_free(el->cctx);
|
||||
el->cctx = NULL;
|
||||
/* Setup KS0 (or KS1 if init_key_phase_bit), our initial keyslot. */
|
||||
if (!el_setup_keyslot(els, enc_level, QRL_EL_STATE_PROV_NORMAL,
|
||||
init_keyslot, secret, secret_len))
|
||||
goto err;
|
||||
|
||||
have_ks0 = 1;
|
||||
|
||||
if (enc_level == QUIC_ENC_LEVEL_1RTT) {
|
||||
/* Derive "quic ku" key (the epoch 1 secret). */
|
||||
if (!tls13_hkdf_expand_ex(libctx, propq,
|
||||
md,
|
||||
secret,
|
||||
quic_v1_ku_label,
|
||||
sizeof(quic_v1_ku_label),
|
||||
NULL, 0,
|
||||
is_tx ? el->ku : ku_key, secret_len, 0))
|
||||
goto err;
|
||||
|
||||
if (!is_tx) {
|
||||
/* Setup KS1 (or KS0 if init_key_phase_bit), our next keyslot. */
|
||||
if (!el_setup_keyslot(els, enc_level, QRL_EL_STATE_PROV_NORMAL,
|
||||
!init_keyslot, ku_key, secret_len))
|
||||
goto err;
|
||||
|
||||
have_ks1 = 1;
|
||||
|
||||
/* Derive NEXT "quic ku" key (the epoch 2 secret). */
|
||||
if (!tls13_hkdf_expand_ex(libctx, propq,
|
||||
md,
|
||||
ku_key,
|
||||
quic_v1_ku_label,
|
||||
sizeof(quic_v1_ku_label),
|
||||
NULL, 0,
|
||||
el->ku, secret_len, 0))
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup header protection context. */
|
||||
if (!ossl_quic_hdr_protector_init(&el->hpr,
|
||||
libctx,
|
||||
propq,
|
||||
libctx, propq,
|
||||
ossl_qrl_get_suite_hdr_prot_cipher_id(suite_id),
|
||||
hpr_key,
|
||||
hpr_key_len))
|
||||
hpr_key, hpr_key_len))
|
||||
goto err;
|
||||
|
||||
have_hpr = 1;
|
||||
|
||||
/* Create and initialise cipher context. */
|
||||
if ((cipher = EVP_CIPHER_fetch(libctx, cipher_name, propq)) == NULL)
|
||||
goto err;
|
||||
|
||||
if (!ossl_assert(iv_len == (size_t)EVP_CIPHER_get_iv_length(cipher))
|
||||
|| !ossl_assert(key_len == (size_t)EVP_CIPHER_get_key_length(cipher)))
|
||||
goto err;
|
||||
|
||||
if ((cctx = EVP_CIPHER_CTX_new()) == NULL)
|
||||
goto err;
|
||||
|
||||
/* IV will be changed on RX/TX so we don't need to use a real value here. */
|
||||
if (!EVP_CipherInit_ex(cctx, cipher, NULL, key, el->iv, 0))
|
||||
goto err;
|
||||
|
||||
el->suite_id = suite_id;
|
||||
el->cctx = cctx;
|
||||
el->md = md;
|
||||
el->tag_len = ossl_qrl_get_suite_cipher_tag_len(suite_id);
|
||||
el->op_count = 0;
|
||||
|
||||
/* Zeroize intermediate keys. */
|
||||
OPENSSL_cleanse(key, sizeof(key));
|
||||
/*
|
||||
* We are now provisioned: KS0 has our current key (for key epoch 0), KS1
|
||||
* has our next key (for key epoch 1, in the case of the 1-RTT EL only), and
|
||||
* el->ku has the secret which will be used to generate keys for key epoch
|
||||
* 2.
|
||||
*/
|
||||
OPENSSL_cleanse(hpr_key, sizeof(hpr_key));
|
||||
EVP_CIPHER_free(cipher);
|
||||
OPENSSL_cleanse(ku_key, sizeof(ku_key));
|
||||
el->state = QRL_EL_STATE_PROV_NORMAL;
|
||||
return 1;
|
||||
|
||||
err:
|
||||
if (have_hpr)
|
||||
ossl_quic_hdr_protector_destroy(&el->hpr);
|
||||
EVP_CIPHER_CTX_free(cctx);
|
||||
EVP_CIPHER_free(cipher);
|
||||
el->suite_id = 0;
|
||||
OPENSSL_cleanse(hpr_key, sizeof(hpr_key));
|
||||
OPENSSL_cleanse(ku_key, sizeof(ku_key));
|
||||
OPENSSL_cleanse(el->ku, sizeof(el->ku));
|
||||
if (have_ks0)
|
||||
el_teardown_keyslot(els, enc_level, 0);
|
||||
if (have_ks1)
|
||||
el_teardown_keyslot(els, enc_level, 1);
|
||||
if (own_md)
|
||||
EVP_MD_free(md);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Drops keying material for a given encryption level. */
|
||||
void ossl_qrl_enc_level_set_discard(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level, int is_final)
|
||||
int ossl_qrl_enc_level_set_key_update(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
size_t secret_len;
|
||||
unsigned char new_ku[EVP_MAX_KEY_LENGTH];
|
||||
|
||||
if (el == NULL || !ossl_assert(enc_level == QUIC_ENC_LEVEL_1RTT))
|
||||
return 0;
|
||||
|
||||
if (el->state != QRL_EL_STATE_PROV_NORMAL)
|
||||
return 0;
|
||||
|
||||
if (!el->is_tx) {
|
||||
/*
|
||||
* We already have the key for the next epoch, so just move to using it.
|
||||
*/
|
||||
++el->key_epoch;
|
||||
el->state = QRL_EL_STATE_PROV_UPDATING;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TX case. For the TX side we use only keyslot 0; it replaces the old key
|
||||
* immediately.
|
||||
*/
|
||||
secret_len = ossl_qrl_get_suite_secret_len(el->suite_id);
|
||||
|
||||
/* Derive NEXT "quic ku" key (the epoch n+1 secret). */
|
||||
if (!tls13_hkdf_expand_ex(el->libctx, el->propq,
|
||||
el->md, el->ku,
|
||||
quic_v1_ku_label,
|
||||
sizeof(quic_v1_ku_label),
|
||||
NULL, 0,
|
||||
new_ku, secret_len, 0))
|
||||
return 0;
|
||||
|
||||
el_teardown_keyslot(els, enc_level, 0);
|
||||
|
||||
/* Setup keyslot for CURRENT "quic ku" key. */
|
||||
if (!el_setup_keyslot(els, enc_level, QRL_EL_STATE_PROV_NORMAL,
|
||||
0, el->ku, secret_len))
|
||||
return 0;
|
||||
|
||||
++el->key_epoch;
|
||||
el->op_count = 0;
|
||||
memcpy(el->ku, new_ku, secret_len);
|
||||
/* Remain in PROV_NORMAL state */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Transitions from PROV_UPDATING to PROV_COOLDOWN. */
|
||||
int ossl_qrl_enc_level_set_key_update_done(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
|
||||
if (el == NULL || el->discarded)
|
||||
return;
|
||||
if (el == NULL || !ossl_assert(enc_level == QUIC_ENC_LEVEL_1RTT))
|
||||
return 0;
|
||||
|
||||
if (el->cctx != NULL) {
|
||||
ossl_quic_hdr_protector_destroy(&el->hpr);
|
||||
/* No new key yet, but erase key material to aid PFS. */
|
||||
el_teardown_keyslot(els, enc_level, ~el->key_epoch & 1);
|
||||
el->state = QRL_EL_STATE_PROV_COOLDOWN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_free(el->cctx);
|
||||
el->cctx = NULL;
|
||||
/*
|
||||
* Transitions from PROV_COOLDOWN to PROV_NORMAL. (If in PROV_UPDATING,
|
||||
* auto-transitions to PROV_COOLDOWN first.)
|
||||
*/
|
||||
int ossl_qrl_enc_level_set_key_cooldown_done(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
size_t secret_len;
|
||||
unsigned char new_ku[EVP_MAX_KEY_LENGTH];
|
||||
|
||||
EVP_MD_free(el->md);
|
||||
el->md = NULL;
|
||||
if (el == NULL || !ossl_assert(enc_level == QUIC_ENC_LEVEL_1RTT))
|
||||
return 0;
|
||||
|
||||
if (el->state == QRL_EL_STATE_PROV_UPDATING
|
||||
&& !ossl_qrl_enc_level_set_key_update_done(els, enc_level))
|
||||
return 0;
|
||||
|
||||
if (el->state != QRL_EL_STATE_PROV_COOLDOWN)
|
||||
return 0;
|
||||
|
||||
secret_len = ossl_qrl_get_suite_secret_len(el->suite_id);
|
||||
|
||||
if (!el_setup_keyslot(els, enc_level, QRL_EL_STATE_PROV_NORMAL,
|
||||
~el->key_epoch & 1, el->ku, secret_len))
|
||||
return 0;
|
||||
|
||||
/* Derive NEXT "quic ku" key (the epoch n+1 secret). */
|
||||
if (!tls13_hkdf_expand_ex(el->libctx, el->propq,
|
||||
el->md,
|
||||
el->ku,
|
||||
quic_v1_ku_label,
|
||||
sizeof(quic_v1_ku_label),
|
||||
NULL, 0,
|
||||
new_ku, secret_len, 0)) {
|
||||
el_teardown_keyslot(els, enc_level, ~el->key_epoch & 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Zeroise IV. */
|
||||
OPENSSL_cleanse(el->iv, sizeof(el->iv));
|
||||
|
||||
if (is_final)
|
||||
el->discarded = 1;
|
||||
memcpy(el->ku, new_ku, secret_len);
|
||||
el->state = QRL_EL_STATE_PROV_NORMAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Discards keying material for a given encryption level. Transitions from any
|
||||
* state to DISCARDED.
|
||||
*/
|
||||
void ossl_qrl_enc_level_set_discard(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el = ossl_qrl_enc_level_set_get(els, enc_level, 0);
|
||||
|
||||
if (el == NULL || el->state == QRL_EL_STATE_DISCARDED)
|
||||
return;
|
||||
|
||||
if (ossl_qrl_enc_level_set_have_el(els, enc_level) == 1) {
|
||||
ossl_quic_hdr_protector_cleanup(&el->hpr);
|
||||
|
||||
el_teardown_keyslot(els, enc_level, 0);
|
||||
el_teardown_keyslot(els, enc_level, 1);
|
||||
}
|
||||
|
||||
EVP_MD_free(el->md);
|
||||
el->md = NULL;
|
||||
el->state = QRL_EL_STATE_DISCARDED;
|
||||
}
|
||||
|
|
|
@ -22,26 +22,65 @@
|
|||
* encryption level, as this functionality is shared between QRX and QTX. For
|
||||
* QRL use only.
|
||||
*/
|
||||
|
||||
/*
|
||||
* States an EL can be in. The Updating and Cooldown states are used by RX only;
|
||||
* a TX EL in the Provisioned state is always in the Normal substate.
|
||||
*
|
||||
* Key material is available if in the Provisioned state.
|
||||
*/
|
||||
#define QRL_EL_STATE_UNPROV 0 /* Unprovisioned (initial state) */
|
||||
#define QRL_EL_STATE_PROV_NORMAL 1 /* Provisioned - Normal */
|
||||
#define QRL_EL_STATE_PROV_UPDATING 2 /* Provisioned - Updating */
|
||||
#define QRL_EL_STATE_PROV_COOLDOWN 3 /* Provisioned - Cooldown */
|
||||
#define QRL_EL_STATE_DISCARDED 4 /* Discarded (terminal state) */
|
||||
|
||||
typedef struct ossl_qrl_enc_level_st {
|
||||
/* Hash function used for key derivation. */
|
||||
EVP_MD *md;
|
||||
/* Context used for packet body ciphering. */
|
||||
EVP_CIPHER_CTX *cctx;
|
||||
/* IV used to construct nonces used for AEAD packet body ciphering. */
|
||||
unsigned char iv[EVP_MAX_IV_LENGTH];
|
||||
/* Have we permanently discarded this encryption level? */
|
||||
unsigned char discarded;
|
||||
/* QRL_SUITE_* value. */
|
||||
uint32_t suite_id;
|
||||
/* Length of authentication tag. */
|
||||
uint32_t tag_len;
|
||||
/*
|
||||
* Cryptographic context used to apply and remove header protection from
|
||||
* packet headers.
|
||||
*/
|
||||
QUIC_HDR_PROTECTOR hpr;
|
||||
/* Usage counter. The caller maintains this. */
|
||||
|
||||
/* Hash function used for key derivation. */
|
||||
EVP_MD *md;
|
||||
|
||||
/* Context used for packet body ciphering. One for each keyslot. */
|
||||
EVP_CIPHER_CTX *cctx[2];
|
||||
|
||||
OSSL_LIB_CTX *libctx;
|
||||
const char *propq;
|
||||
|
||||
/*
|
||||
* Key epoch, essentially the number of times we have done a key update.
|
||||
*
|
||||
* The least significant bit of this is therefore by definition the current
|
||||
* Key Phase bit value.
|
||||
*/
|
||||
uint64_t key_epoch;
|
||||
|
||||
/* Usage counter. The caller maintains this. Used by TX side only. */
|
||||
uint64_t op_count;
|
||||
|
||||
/* QRL_SUITE_* value. */
|
||||
uint32_t suite_id;
|
||||
|
||||
/* Length of authentication tag. */
|
||||
uint32_t tag_len;
|
||||
|
||||
/* Current EL state. */
|
||||
unsigned char state; /* QRL_EL_STATE_* */
|
||||
|
||||
/* 1 if for TX, else RX. Initialised when secret provided. */
|
||||
unsigned char is_tx;
|
||||
|
||||
/* IV used to construct nonces used for AEAD packet body ciphering. */
|
||||
unsigned char iv[2][EVP_MAX_IV_LENGTH];
|
||||
|
||||
/*
|
||||
* Secret for next key epoch.
|
||||
*/
|
||||
unsigned char ku[EVP_MAX_KEY_LENGTH];
|
||||
} OSSL_QRL_ENC_LEVEL;
|
||||
|
||||
typedef struct ossl_qrl_enc_level_set_st {
|
||||
|
@ -49,21 +88,22 @@ typedef struct ossl_qrl_enc_level_set_st {
|
|||
} OSSL_QRL_ENC_LEVEL_SET;
|
||||
|
||||
/*
|
||||
* Returns 1 if we have key material for a given encryption level, 0 if we do
|
||||
* not yet have material and -1 if the EL is discarded.
|
||||
* Returns 1 if we have key material for a given encryption level (that is, if
|
||||
* we are in the PROVISIONED state), 0 if we do not yet have material (we are in
|
||||
* the UNPROVISIONED state) and -1 if the EL is discarded (we are in the
|
||||
* DISCARDED state).
|
||||
*/
|
||||
int ossl_qrl_enc_level_set_have_el(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level);
|
||||
|
||||
/*
|
||||
* Returns EL in a set. If enc_level is not a valid QUIC_ENC_LEVEL_* value,
|
||||
* returns NULL. If require_valid is 1, returns NULL if the EL is not
|
||||
* provisioned or has been discarded; otherwise, the returned EL may be
|
||||
* unprovisioned or discarded.
|
||||
* returns NULL. If require_prov is 1, returns NULL if the EL is not in
|
||||
* the PROVISIONED state; otherwise, the returned EL may be in any state.
|
||||
*/
|
||||
OSSL_QRL_ENC_LEVEL *ossl_qrl_enc_level_set_get(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level,
|
||||
int require_valid);
|
||||
int require_prov);
|
||||
|
||||
/* Provide secret to an EL. md may be NULL. */
|
||||
int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
|
@ -73,14 +113,38 @@ int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els,
|
|||
uint32_t suite_id,
|
||||
EVP_MD *md,
|
||||
const unsigned char *secret,
|
||||
size_t secret_len);
|
||||
size_t secret_len,
|
||||
unsigned char init_key_phase_bit,
|
||||
int is_tx);
|
||||
|
||||
/*
|
||||
* Discard an EL. If is_final is non-zero, no secret can be provided for the EL
|
||||
* ever again.
|
||||
* Returns 1 if the given keyslot index is currently valid for a given EL and EL
|
||||
* state.
|
||||
*/
|
||||
int ossl_qrl_enc_level_set_has_keyslot(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level,
|
||||
unsigned char tgt_state,
|
||||
size_t keyslot);
|
||||
|
||||
/* Perform a key update. Transitions from PROV_NORMAL to PROV_UPDATING. */
|
||||
int ossl_qrl_enc_level_set_key_update(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level);
|
||||
|
||||
/* Transitions from PROV_UPDATING to PROV_COOLDOWN. */
|
||||
int ossl_qrl_enc_level_set_key_update_done(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level);
|
||||
|
||||
/*
|
||||
* Transitions from PROV_COOLDOWN to PROV_NORMAL. (If in PROV_UPDATING,
|
||||
* auto-transitions to PROV_COOLDOWN first.)
|
||||
*/
|
||||
int ossl_qrl_enc_level_set_key_cooldown_done(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level);
|
||||
|
||||
/*
|
||||
* Discard an EL. No secret can be provided for the EL ever again.
|
||||
*/
|
||||
void ossl_qrl_enc_level_set_discard(OSSL_QRL_ENC_LEVEL_SET *els,
|
||||
uint32_t enc_level,
|
||||
int is_final);
|
||||
uint32_t enc_level);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -79,28 +79,6 @@ static void txe_insert_tail(TXE_LIST *l, TXE *e)
|
|||
* QTX
|
||||
* ===
|
||||
*/
|
||||
|
||||
/* (Encryption level, direction)-specific state. */
|
||||
typedef struct ossl_qtx_enc_level_st {
|
||||
/* Hash function used for key derivation. */
|
||||
EVP_MD *md;
|
||||
/* Context used for packet body ciphering. */
|
||||
EVP_CIPHER_CTX *cctx;
|
||||
/* IV used to construct nonces used for AEAD packet body ciphering. */
|
||||
unsigned char iv[EVP_MAX_IV_LENGTH];
|
||||
/* Have we permanently discarded this encryption level? */
|
||||
unsigned char discarded;
|
||||
/* QTX_SUITE_* value. */
|
||||
uint32_t suite_id;
|
||||
/* Length of authentication tag. */
|
||||
uint32_t tag_len;
|
||||
/*
|
||||
* Cryptographic context used to apply and remove header protection from
|
||||
* packet headers.
|
||||
*/
|
||||
QUIC_HDR_PROTECTOR hpr;
|
||||
} OSSL_QTX_ENC_LEVEL;
|
||||
|
||||
struct ossl_qtx_st {
|
||||
OSSL_LIB_CTX *libctx;
|
||||
const char *propq;
|
||||
|
@ -135,6 +113,12 @@ struct ossl_qtx_st {
|
|||
*/
|
||||
TXE *cons;
|
||||
size_t cons_count; /* num packets */
|
||||
|
||||
/*
|
||||
* Number of packets transmitted in this key epoch. Used to enforce AEAD
|
||||
* confidentiality limit.
|
||||
*/
|
||||
uint64_t epoch_pkt_count;
|
||||
};
|
||||
|
||||
/* Instantiates a new QTX. */
|
||||
|
@ -176,10 +160,11 @@ void ossl_qtx_free(OSSL_QTX *qtx)
|
|||
/* Free TXE queue data. */
|
||||
qtx_cleanup_txl(&qtx->pending);
|
||||
qtx_cleanup_txl(&qtx->free);
|
||||
OPENSSL_free(qtx->cons);
|
||||
|
||||
/* Drop keying material and crypto resources. */
|
||||
for (i = 0; i < QUIC_ENC_LEVEL_NUM; ++i)
|
||||
ossl_qrl_enc_level_set_discard(&qtx->el_set, i, 1);
|
||||
ossl_qrl_enc_level_set_discard(&qtx->el_set, i);
|
||||
|
||||
OPENSSL_free(qtx);
|
||||
}
|
||||
|
@ -201,7 +186,9 @@ int ossl_qtx_provide_secret(OSSL_QTX *qtx,
|
|||
suite_id,
|
||||
md,
|
||||
secret,
|
||||
secret_len);
|
||||
secret_len,
|
||||
0,
|
||||
/*is_tx=*/1);
|
||||
}
|
||||
|
||||
int ossl_qtx_discard_enc_level(OSSL_QTX *qtx, uint32_t enc_level)
|
||||
|
@ -209,7 +196,7 @@ int ossl_qtx_discard_enc_level(OSSL_QTX *qtx, uint32_t enc_level)
|
|||
if (enc_level >= QUIC_ENC_LEVEL_NUM)
|
||||
return 0;
|
||||
|
||||
ossl_qrl_enc_level_set_discard(&qtx->el_set, enc_level, 1);
|
||||
ossl_qrl_enc_level_set_discard(&qtx->el_set, enc_level);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -432,7 +419,7 @@ static int qtx_write_hdr(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
|
|||
txe->alloc_len - txe->data_len, 0))
|
||||
return 0;
|
||||
|
||||
if (!ossl_quic_wire_encode_pkt_hdr(&wpkt, pkt->hdr->src_conn_id.id_len,
|
||||
if (!ossl_quic_wire_encode_pkt_hdr(&wpkt, pkt->hdr->dst_conn_id.id_len,
|
||||
pkt->hdr, ptrs)
|
||||
|| !WPACKET_get_total_written(&wpkt, &l)) {
|
||||
WPACKET_finish(&wpkt);
|
||||
|
@ -454,6 +441,7 @@ static int qtx_encrypt_into_txe(OSSL_QTX *qtx, struct iovec_cur *cur, TXE *txe,
|
|||
= ossl_qrl_enc_level_set_get(&qtx->el_set, enc_level, 1);
|
||||
unsigned char nonce[EVP_MAX_IV_LENGTH];
|
||||
size_t nonce_len, i;
|
||||
EVP_CIPHER_CTX *cctx = NULL;
|
||||
|
||||
/* We should not have been called if we do not have key material. */
|
||||
if (!ossl_assert(el != NULL))
|
||||
|
@ -466,21 +454,30 @@ static int qtx_encrypt_into_txe(OSSL_QTX *qtx, struct iovec_cur *cur, TXE *txe,
|
|||
if (el->op_count >= ossl_qrl_get_suite_max_pkt(el->suite_id))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* TX key update is simpler than for RX; once we initiate a key update, we
|
||||
* never need the old keys, as we never deliberately send a packet with old
|
||||
* keys. Thus the EL always uses keyslot 0 for the TX side.
|
||||
*/
|
||||
cctx = el->cctx[0];
|
||||
if (!ossl_assert(cctx != NULL))
|
||||
return 0;
|
||||
|
||||
/* Construct nonce (nonce=IV ^ PN). */
|
||||
nonce_len = EVP_CIPHER_CTX_get_iv_length(el->cctx);
|
||||
nonce_len = EVP_CIPHER_CTX_get_iv_length(cctx);
|
||||
if (!ossl_assert(nonce_len >= sizeof(QUIC_PN)))
|
||||
return 0;
|
||||
|
||||
memcpy(nonce, el->iv, nonce_len);
|
||||
memcpy(nonce, el->iv[0], nonce_len);
|
||||
for (i = 0; i < sizeof(QUIC_PN); ++i)
|
||||
nonce[nonce_len - i - 1] ^= (unsigned char)(pn >> (i * 8));
|
||||
|
||||
/* type and key will already have been setup; feed the IV. */
|
||||
if (EVP_CipherInit_ex(el->cctx, NULL, NULL, NULL, nonce, /*enc=*/1) != 1)
|
||||
if (EVP_CipherInit_ex(cctx, NULL, NULL, NULL, nonce, /*enc=*/1) != 1)
|
||||
return 0;
|
||||
|
||||
/* Feed AAD data. */
|
||||
if (EVP_CipherUpdate(el->cctx, NULL, &l, hdr, hdr_len) != 1)
|
||||
if (EVP_CipherUpdate(cctx, NULL, &l, hdr, hdr_len) != 1)
|
||||
return 0;
|
||||
|
||||
/* Encrypt plaintext directly into TXE. */
|
||||
|
@ -492,7 +489,7 @@ static int qtx_encrypt_into_txe(OSSL_QTX *qtx, struct iovec_cur *cur, TXE *txe,
|
|||
if (src_len == 0)
|
||||
break;
|
||||
|
||||
if (EVP_CipherUpdate(el->cctx, txe_data(txe) + txe->data_len,
|
||||
if (EVP_CipherUpdate(cctx, txe_data(txe) + txe->data_len,
|
||||
&l, src, src_len) != 1)
|
||||
return 0;
|
||||
|
||||
|
@ -501,10 +498,10 @@ static int qtx_encrypt_into_txe(OSSL_QTX *qtx, struct iovec_cur *cur, TXE *txe,
|
|||
}
|
||||
|
||||
/* Finalise and get tag. */
|
||||
if (EVP_CipherFinal_ex(el->cctx, NULL, &l2) != 1)
|
||||
if (EVP_CipherFinal_ex(cctx, NULL, &l2) != 1)
|
||||
return 0;
|
||||
|
||||
if (EVP_CIPHER_CTX_ctrl(el->cctx, EVP_CTRL_AEAD_GET_TAG,
|
||||
if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_GET_TAG,
|
||||
el->tag_len, txe_data(txe) + txe->data_len) != 1)
|
||||
return 0;
|
||||
|
||||
|
@ -531,18 +528,21 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
|
|||
struct iovec_cur cur;
|
||||
QUIC_PKT_HDR_PTRS ptrs;
|
||||
unsigned char *hdr_start;
|
||||
OSSL_QRL_ENC_LEVEL *el = NULL;
|
||||
|
||||
/*
|
||||
* Determine if the packet needs encryption and the minimum conceivable
|
||||
* serialization length.
|
||||
*/
|
||||
if (pkt->hdr->type == QUIC_PKT_TYPE_RETRY
|
||||
|| pkt->hdr->type == QUIC_PKT_TYPE_VERSION_NEG) {
|
||||
if (!ossl_quic_pkt_type_is_encrypted(pkt->hdr->type)) {
|
||||
needs_encrypt = 0;
|
||||
min_len = QUIC_MIN_VALID_PKT_LEN;
|
||||
} else {
|
||||
needs_encrypt = 1;
|
||||
min_len = QUIC_MIN_VALID_PKT_LEN_CRYPTO;
|
||||
el = ossl_qrl_enc_level_set_get(&qtx->el_set, enc_level, 1);
|
||||
if (!ossl_assert(el != NULL)) /* should already have been checked */
|
||||
return 0;
|
||||
}
|
||||
|
||||
orig_data_len = txe->data_len;
|
||||
|
@ -564,7 +564,7 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
|
|||
/* Determine header length. */
|
||||
pkt->hdr->data = NULL;
|
||||
pkt->hdr->len = payload_len;
|
||||
pred_hdr_len = ossl_quic_wire_get_encoded_pkt_hdr_len(pkt->hdr->src_conn_id.id_len,
|
||||
pred_hdr_len = ossl_quic_wire_get_encoded_pkt_hdr_len(pkt->hdr->dst_conn_id.id_len,
|
||||
pkt->hdr);
|
||||
if (pred_hdr_len == 0) {
|
||||
ret = QTX_FAIL_GENERIC;
|
||||
|
@ -580,12 +580,16 @@ static int qtx_write(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt, TXE *txe,
|
|||
}
|
||||
|
||||
/* Set some fields in the header we are responsible for. */
|
||||
pkt->hdr->key_phase = 0; /* TODO */
|
||||
if (!ossl_quic_wire_encode_pkt_hdr_pn(pkt->pn,
|
||||
pkt->hdr->pn,
|
||||
pkt->hdr->pn_len)) {
|
||||
ret = QTX_FAIL_GENERIC;
|
||||
goto err;
|
||||
if (pkt->hdr->type == QUIC_PKT_TYPE_1RTT)
|
||||
pkt->hdr->key_phase = (unsigned char)(el->key_epoch & 1);
|
||||
|
||||
if (ossl_quic_pkt_type_has_pn(pkt->hdr->type)) {
|
||||
if (!ossl_quic_wire_encode_pkt_hdr_pn(pkt->pn,
|
||||
pkt->hdr->pn,
|
||||
pkt->hdr->pn_len)) {
|
||||
ret = QTX_FAIL_GENERIC;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append the header to the TXE. */
|
||||
|
@ -674,13 +678,13 @@ int ossl_qtx_write_pkt(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt)
|
|||
enc_level = ossl_quic_pkt_type_to_enc_level(pkt->hdr->type);
|
||||
|
||||
/* Some packet types must be in a packet all by themselves. */
|
||||
if (pkt->hdr->type == QUIC_PKT_TYPE_RETRY
|
||||
|| pkt->hdr->type == QUIC_PKT_TYPE_VERSION_NEG)
|
||||
if (!ossl_quic_pkt_type_can_share_dgram(pkt->hdr->type))
|
||||
ossl_qtx_finish_dgram(qtx);
|
||||
else if (enc_level >= QUIC_ENC_LEVEL_NUM
|
||||
|| ossl_qrl_enc_level_set_have_el(&qtx->el_set, enc_level) != 1)
|
||||
|| ossl_qrl_enc_level_set_have_el(&qtx->el_set, enc_level) != 1) {
|
||||
/* All other packet types are encrypted. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
was_coalescing = (qtx->cons != NULL && qtx->cons->data_len > 0);
|
||||
if (was_coalescing)
|
||||
|
@ -751,9 +755,7 @@ int ossl_qtx_write_pkt(OSSL_QTX *qtx, const OSSL_QTX_PKT *pkt)
|
|||
/*
|
||||
* Some packet types cannot have another packet come after them.
|
||||
*/
|
||||
if (pkt->hdr->type == QUIC_PKT_TYPE_RETRY
|
||||
|| pkt->hdr->type == QUIC_PKT_TYPE_VERSION_NEG
|
||||
|| pkt->hdr->type == QUIC_PKT_TYPE_1RTT)
|
||||
if (ossl_quic_pkt_type_must_be_last(pkt->hdr->type))
|
||||
coalescing = 0;
|
||||
|
||||
if (!coalescing)
|
||||
|
@ -802,9 +804,8 @@ static void txe_to_msg(TXE *txe, BIO_MSG *msg)
|
|||
void ossl_qtx_flush_net(OSSL_QTX *qtx)
|
||||
{
|
||||
BIO_MSG msg[MAX_MSGS_PER_SEND];
|
||||
size_t i;
|
||||
size_t wr, i;
|
||||
TXE *txe;
|
||||
ossl_ssize_t wr;
|
||||
|
||||
if (qtx->bio == NULL)
|
||||
return;
|
||||
|
@ -819,8 +820,7 @@ void ossl_qtx_flush_net(OSSL_QTX *qtx)
|
|||
/* Nothing to send. */
|
||||
return;
|
||||
|
||||
wr = BIO_sendmmsg(qtx->bio, msg, sizeof(BIO_MSG), i, 0);
|
||||
if (wr <= 0)
|
||||
if (!BIO_sendmmsg(qtx->bio, msg, sizeof(BIO_MSG), i, 0, &wr) || wr == 0)
|
||||
/*
|
||||
* We did not get anything, so further calls will probably not
|
||||
* succeed either.
|
||||
|
@ -830,7 +830,7 @@ void ossl_qtx_flush_net(OSSL_QTX *qtx)
|
|||
/*
|
||||
* Remove everything which was successfully sent from the pending queue.
|
||||
*/
|
||||
for (i = 0; i < (size_t)wr; ++i)
|
||||
for (i = 0; i < wr; ++i)
|
||||
qtx_pending_to_free(qtx);
|
||||
}
|
||||
}
|
||||
|
@ -883,6 +883,12 @@ size_t ossl_qtx_get_unflushed_pkt_count(OSSL_QTX *qtx)
|
|||
return qtx->cons_count;
|
||||
}
|
||||
|
||||
int ossl_qtx_trigger_key_update(OSSL_QTX *qtx)
|
||||
{
|
||||
return ossl_qrl_enc_level_set_key_update(&qtx->el_set,
|
||||
QUIC_ENC_LEVEL_1RTT);
|
||||
}
|
||||
|
||||
uint64_t ossl_qtx_get_cur_epoch_pkt_count(OSSL_QTX *qtx, uint32_t enc_level)
|
||||
{
|
||||
OSSL_QRL_ENC_LEVEL *el;
|
||||
|
|
|
@ -51,11 +51,11 @@ int ossl_quic_hdr_protector_init(QUIC_HDR_PROTECTOR *hpr,
|
|||
return 1;
|
||||
|
||||
err:
|
||||
ossl_quic_hdr_protector_destroy(hpr);
|
||||
ossl_quic_hdr_protector_cleanup(hpr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ossl_quic_hdr_protector_destroy(QUIC_HDR_PROTECTOR *hpr)
|
||||
void ossl_quic_hdr_protector_cleanup(QUIC_HDR_PROTECTOR *hpr)
|
||||
{
|
||||
EVP_CIPHER_CTX_free(hpr->cipher_ctx);
|
||||
hpr->cipher_ctx = NULL;
|
||||
|
@ -238,8 +238,9 @@ int ossl_quic_wire_decode_pkt_hdr(PACKET *pkt,
|
|||
hdr->data = PACKET_data(pkt);
|
||||
|
||||
/*
|
||||
* Skip over payload so we are pointing at the start of the next packet,
|
||||
* if any.
|
||||
* Skip over payload. Since this is a short header packet, which cannot
|
||||
* be followed by any other kind of packet, this advances us to the end
|
||||
* of the datagram.
|
||||
*/
|
||||
if (!PACKET_forward(pkt, hdr->len))
|
||||
return 0;
|
||||
|
@ -306,10 +307,18 @@ int ossl_quic_wire_decode_pkt_hdr(PACKET *pkt,
|
|||
raw_type = ((b0 >> 4) & 0x3);
|
||||
|
||||
switch (raw_type) {
|
||||
case 0: hdr->type = QUIC_PKT_TYPE_INITIAL; break;
|
||||
case 1: hdr->type = QUIC_PKT_TYPE_0RTT; break;
|
||||
case 2: hdr->type = QUIC_PKT_TYPE_HANDSHAKE; break;
|
||||
case 3: hdr->type = QUIC_PKT_TYPE_RETRY; break;
|
||||
case 0:
|
||||
hdr->type = QUIC_PKT_TYPE_INITIAL;
|
||||
break;
|
||||
case 1:
|
||||
hdr->type = QUIC_PKT_TYPE_0RTT;
|
||||
break;
|
||||
case 2:
|
||||
hdr->type = QUIC_PKT_TYPE_HANDSHAKE;
|
||||
break;
|
||||
case 3:
|
||||
hdr->type = QUIC_PKT_TYPE_RETRY;
|
||||
break;
|
||||
}
|
||||
|
||||
hdr->pn_len = 0;
|
||||
|
@ -455,8 +464,7 @@ int ossl_quic_wire_encode_pkt_hdr(WPACKET *pkt,
|
|||
|| hdr->src_conn_id.id_len > QUIC_MAX_CONN_ID_LEN)
|
||||
return 0;
|
||||
|
||||
if (hdr->type != QUIC_PKT_TYPE_VERSION_NEG
|
||||
&& hdr->type != QUIC_PKT_TYPE_RETRY
|
||||
if (ossl_quic_pkt_type_has_pn(hdr->type)
|
||||
&& (hdr->pn_len < 1 || hdr->pn_len > 4))
|
||||
return 0;
|
||||
|
||||
|
@ -480,8 +488,7 @@ int ossl_quic_wire_encode_pkt_hdr(WPACKET *pkt,
|
|||
b0 = (raw_type << 4) | 0x80; /* long */
|
||||
if (hdr->type != QUIC_PKT_TYPE_VERSION_NEG || hdr->fixed)
|
||||
b0 |= 0x40; /* fixed */
|
||||
if (hdr->type != QUIC_PKT_TYPE_RETRY
|
||||
&& hdr->type != QUIC_PKT_TYPE_VERSION_NEG)
|
||||
if (ossl_quic_pkt_type_has_pn(hdr->type))
|
||||
b0 |= hdr->pn_len - 1;
|
||||
|
||||
if (!WPACKET_put_bytes_u8(pkt, b0)
|
||||
|
@ -560,15 +567,17 @@ int ossl_quic_wire_get_encoded_pkt_hdr_len(size_t short_conn_id_len,
|
|||
|| hdr->src_conn_id.id_len > QUIC_MAX_CONN_ID_LEN)
|
||||
return 0;
|
||||
|
||||
if (hdr->type != QUIC_PKT_TYPE_VERSION_NEG
|
||||
&& hdr->type != QUIC_PKT_TYPE_RETRY
|
||||
&& (hdr->pn_len < 1 || hdr->pn_len > 4))
|
||||
return 0;
|
||||
|
||||
len += 1 /* Initial byte */ + 4 /* Version */
|
||||
+ 1 + hdr->dst_conn_id.id_len /* DCID Len, DCID */
|
||||
+ 1 + hdr->src_conn_id.id_len /* SCID Len, SCID */
|
||||
+ hdr->pn_len; /* PN */
|
||||
;
|
||||
|
||||
if (ossl_quic_pkt_type_has_pn(hdr->type)) {
|
||||
if (hdr->pn_len < 1 || hdr->pn_len > 4)
|
||||
return 0;
|
||||
|
||||
len += hdr->pn_len;
|
||||
}
|
||||
|
||||
if (hdr->type == QUIC_PKT_TYPE_INITIAL) {
|
||||
enclen = ossl_quic_vlint_encode_len(hdr->token_len);
|
||||
|
@ -577,11 +586,14 @@ int ossl_quic_wire_get_encoded_pkt_hdr_len(size_t short_conn_id_len,
|
|||
len += enclen;
|
||||
}
|
||||
|
||||
enclen = ossl_quic_vlint_encode_len(hdr->len);
|
||||
if (!enclen)
|
||||
return 0;
|
||||
if (!ossl_quic_pkt_type_must_be_last(hdr->type)) {
|
||||
enclen = ossl_quic_vlint_encode_len(hdr->len);
|
||||
if (!enclen)
|
||||
return 0;
|
||||
|
||||
len += enclen;
|
||||
}
|
||||
|
||||
len += enclen;
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ static const QUIC_CONN_ID empty_conn_id = {0, {0}};
|
|||
#define RX_TEST_OP_DISCARD_EL 7 /* discard an encryption level */
|
||||
#define RX_TEST_OP_CHECK_PKT 8 /* read packet, compare to expected */
|
||||
#define RX_TEST_OP_CHECK_NO_PKT 9 /* check no packet is available to read */
|
||||
#define RX_TEST_OP_CHECK_KEY_EPOCH 10 /* check key epoch value matches */
|
||||
#define RX_TEST_OP_KEY_UPDATE_TIMEOUT 11 /* complete key update process */
|
||||
#define RX_TEST_OP_SET_INIT_KEY_PHASE 12 /* initial Key Phase bit value */
|
||||
|
||||
struct rx_test_op {
|
||||
unsigned char op;
|
||||
|
@ -61,6 +64,12 @@ struct rx_test_op {
|
|||
},
|
||||
#define RX_OP_CHECK_NO_PKT() \
|
||||
{ RX_TEST_OP_CHECK_NO_PKT, NULL, 0, NULL, 0, 0, 0, NULL, NULL },
|
||||
#define RX_OP_CHECK_KEY_EPOCH(expected) \
|
||||
{ RX_TEST_OP_CHECK_KEY_EPOCH, NULL, 0, NULL, 0, 0, (expected), NULL },
|
||||
#define RX_OP_KEY_UPDATE_TIMEOUT(normal) \
|
||||
{ RX_TEST_OP_KEY_UPDATE_TIMEOUT, NULL, 0, NULL, (normal), 0, 0, NULL },
|
||||
#define RX_OP_SET_INIT_KEY_PHASE(kp_bit) \
|
||||
{ RX_TEST_OP_SET_INIT_KEY_PHASE, NULL, 0, NULL, (kp_bit), 0, 0, NULL },
|
||||
|
||||
#define RX_OP_INJECT_N(n) \
|
||||
RX_OP_INJECT(rx_script_##n##_in)
|
||||
|
@ -1327,6 +1336,279 @@ static const struct rx_test_op rx_script_7[] = {
|
|||
RX_OP_END
|
||||
};
|
||||
|
||||
/*
|
||||
* 8. Real World - S2C Multiple Packets with Peer Initiated Key Phase Update
|
||||
*/
|
||||
static const unsigned char rx_script_8_1rtt_secret[32] = {
|
||||
0x5f, 0x1f, 0x47, 0xea, 0xc3, 0xb2, 0xce, 0x73, 0xfb, 0xa2, 0x9f, 0xac,
|
||||
0xc3, 0xa0, 0xfe, 0x9b, 0xf3, 0xc0, 0xde, 0x5d, 0x33, 0x11, 0x1c, 0x70,
|
||||
0xdd, 0xb4, 0x06, 0xcc, 0xdf, 0x7d, 0xe9, 0x9a
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8a_in[] = {
|
||||
0x51, /* Short, 1-RTT, PN Length=2 bytes, KP=0 */
|
||||
0xcb, 0xf4, /* PN (4) */
|
||||
0x3f, 0x68, 0x7b, 0xa8, 0x2b, 0xb9, 0xfa, 0x7d, 0xe4, 0x6b, 0x20, 0x48,
|
||||
0xd1, 0x3c, 0xcb, 0x4b, 0xef, 0xb1, 0xfd, 0x5e, 0x1b, 0x19, 0x83, 0xa9,
|
||||
0x47, 0x62, 0xc1, 0x6e, 0xef, 0x27, 0xc3, 0x9b, 0x8f, 0x3f, 0xce, 0x11,
|
||||
0x68, 0xf5, 0x73, 0x0d, 0xf2, 0xdc, 0xe0, 0x28, 0x28, 0x79, 0xa6, 0x39,
|
||||
0xc3, 0xb9, 0xd3,
|
||||
};
|
||||
|
||||
static const QUIC_PKT_HDR rx_script_8a_expect_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT,
|
||||
0, /* Spin Bit */
|
||||
0, /* Key Phase */
|
||||
2, /* PN Length */
|
||||
0, /* Partial */
|
||||
1, /* Fixed */
|
||||
0, /* Version */
|
||||
{0, {0}}, /* DCID */
|
||||
{0, {0}}, /* SCID */
|
||||
{0, 4}, /* PN */
|
||||
NULL, 0, /* Token/Token Len */
|
||||
35, NULL
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8a_body[] = {
|
||||
0x02, 0x03, 0x06, 0x00, 0x03, 0x0c, 0x00, 0x1b, 0x49, 0x27, 0x6d, 0x20,
|
||||
0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x6f, 0x6e,
|
||||
0x64, 0x65, 0x72, 0x66, 0x75, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8b_in[] = {
|
||||
0x52, /* Short, 1-RTT, PN Length=2 bytes, KP=1 */
|
||||
0x21, 0x8e, /* PN (5) */
|
||||
0xa2, 0x6a, 0x9c, 0x83, 0x24, 0x48, 0xae, 0x60, 0x1e, 0xc2, 0xa5, 0x91,
|
||||
0xfa, 0xe5, 0xf2, 0x05, 0x14, 0x37, 0x04, 0x6a, 0xa8, 0xae, 0x06, 0x58,
|
||||
0xd7, 0x85, 0x48, 0xd7, 0x3b, 0x85, 0x9e, 0x5a, 0xb3, 0x46, 0x89, 0x1b,
|
||||
0x4b, 0x6e, 0x1d, 0xd1, 0xfc, 0xb7, 0x47, 0xda, 0x6a, 0x64, 0x4b, 0x8e,
|
||||
0xf2, 0x69, 0x16,
|
||||
};
|
||||
|
||||
static const QUIC_PKT_HDR rx_script_8b_expect_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT,
|
||||
0, /* Spin Bit */
|
||||
1, /* Key Phase */
|
||||
2, /* PN Length */
|
||||
0, /* Partial */
|
||||
1, /* Fixed */
|
||||
0, /* Version */
|
||||
{0, {0}}, /* DCID */
|
||||
{0, {0}}, /* SCID */
|
||||
{0, 5}, /* PN */
|
||||
NULL, 0, /* Token/Token Len */
|
||||
35, NULL
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8b_body[] = {
|
||||
0x02, 0x04, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x36, 0x49, 0x27, 0x6d, 0x20,
|
||||
0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x6f, 0x6e,
|
||||
0x64, 0x65, 0x72, 0x66, 0x75, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8c_in[] = {
|
||||
0x5b, /* Short, 1-RTT, PN Length=2 bytes, KP=0 */
|
||||
0x98, 0xd6, /* PN (3) */
|
||||
0x3c, 0x6f, 0x94, 0x20, 0x5e, 0xfc, 0x5b, 0x3a, 0x4a, 0x65, 0x1a, 0x9a,
|
||||
0x6c, 0x00, 0x52, 0xb6, 0x0c, 0x9b, 0x07, 0xf9, 0x6f, 0xbc, 0x3d, 0xb4,
|
||||
0x57, 0xe0, 0x15, 0x74, 0xfe, 0x76, 0xea, 0x1f, 0x23, 0xae, 0x22, 0x62,
|
||||
0xb7, 0x90, 0x94, 0x89, 0x38, 0x9b, 0x5b, 0x47, 0xed,
|
||||
};
|
||||
|
||||
static const QUIC_PKT_HDR rx_script_8c_expect_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT,
|
||||
0, /* Spin Bit */
|
||||
0, /* Key Phase */
|
||||
2, /* PN Length */
|
||||
0, /* Partial */
|
||||
1, /* Fixed */
|
||||
0, /* Version */
|
||||
{0, {0}}, /* DCID */
|
||||
{0, {0}}, /* SCID */
|
||||
{0, 3}, /* PN */
|
||||
NULL, 0, /* Token/Token Len */
|
||||
29, NULL
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8c_body[] = {
|
||||
0x08, 0x00, 0x49, 0x27, 0x6d, 0x20, 0x68, 0x61, 0x76, 0x69, 0x6e, 0x67,
|
||||
0x20, 0x61, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72, 0x66, 0x75, 0x6c,
|
||||
0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8d_in[] = {
|
||||
0x55, /* Short, 1-RTT, PN Length=2 bytes, KP=1 */
|
||||
0x98, 0x20, /* PN (6) */
|
||||
0x45, 0x53, 0x05, 0x29, 0x30, 0x42, 0x29, 0x02, 0xf2, 0xa7, 0x27, 0xd6,
|
||||
0xb0, 0xb7, 0x30, 0xad, 0x45, 0xd8, 0x73, 0xd7, 0xe3, 0x65, 0xee, 0xd9,
|
||||
0x35, 0x33, 0x03, 0x3a, 0x35, 0x0b, 0x59, 0xa7, 0xbc, 0x23, 0x37, 0xc2,
|
||||
0x5e, 0x13, 0x88, 0x18, 0x79, 0x94, 0x6c, 0x15, 0xe3, 0x1f, 0x0d, 0xd1,
|
||||
0xc3, 0xfa, 0x40, 0xff,
|
||||
};
|
||||
|
||||
static const QUIC_PKT_HDR rx_script_8d_expect_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT,
|
||||
0, /* Spin Bit */
|
||||
1, /* Key Phase */
|
||||
2, /* PN Length */
|
||||
0, /* Partial */
|
||||
1, /* Fixed */
|
||||
0, /* Version */
|
||||
{0, {0}}, /* DCID */
|
||||
{0, {0}}, /* SCID */
|
||||
{0, 6}, /* PN */
|
||||
NULL, 0, /* Token/Token Len */
|
||||
36, NULL
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8d_body[] = {
|
||||
0x02, 0x05, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x40, 0x51, 0x49, 0x27, 0x6d,
|
||||
0x20, 0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x6f,
|
||||
0x6e, 0x64, 0x65, 0x72, 0x66, 0x75, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8e_in[] = {
|
||||
0x55, /* Short, 1-RTTT, PN Length=2 bytes, KP=0 */
|
||||
0x76, 0x25, /* PN (10) */
|
||||
0x1c, 0x0d, 0x70, 0x4c, 0x2b, 0xc5, 0x7d, 0x7b, 0x77, 0x64, 0x03, 0x27,
|
||||
0xb3, 0x5d, 0x83, 0x9e, 0x35, 0x05, 0x10, 0xd2, 0xa4, 0x5c, 0x83, 0xd6,
|
||||
0x94, 0x12, 0x18, 0xc5, 0xb3, 0x0f, 0x0a, 0xb1, 0x8a, 0x82, 0x9f, 0xd6,
|
||||
0xa9, 0xab, 0x40, 0xc1, 0x05, 0xe8, 0x1b, 0x74, 0xaa, 0x8e, 0xd6, 0x8b,
|
||||
0xa5, 0xa3, 0x77, 0x79,
|
||||
};
|
||||
|
||||
static const QUIC_PKT_HDR rx_script_8e_expect_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT,
|
||||
0, /* Spin Bit */
|
||||
0, /* Key Phase */
|
||||
2, /* PN Length */
|
||||
0, /* Partial */
|
||||
1, /* Fixed */
|
||||
0, /* Version */
|
||||
{0, {0}}, /* DCID */
|
||||
{0, {0}}, /* SCID */
|
||||
{0, 10}, /* PN */
|
||||
NULL, 0, /* Token/Token Len */
|
||||
36, NULL
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8e_body[] = {
|
||||
0x02, 0x09, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x40, 0xbd, 0x49, 0x27, 0x6d,
|
||||
0x20, 0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x6f,
|
||||
0x6e, 0x64, 0x65, 0x72, 0x66, 0x75, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8f_in[] = {
|
||||
0x48, /* Short, 1-RTT, PN Length=2 Bytes, KP=1 */
|
||||
0x4d, 0xf6, /* PN (15) */
|
||||
0x42, 0x86, 0xa1, 0xfa, 0x69, 0x6b, 0x1a, 0x45, 0xf2, 0xcd, 0xf6, 0x92,
|
||||
0xe1, 0xe6, 0x1a, 0x49, 0x37, 0xd7, 0x10, 0xae, 0x09, 0xbd
|
||||
};
|
||||
|
||||
static const QUIC_PKT_HDR rx_script_8f_expect_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT,
|
||||
0, /* Spin Bit */
|
||||
1, /* Key Phase */
|
||||
2, /* PN Length */
|
||||
0, /* Partial */
|
||||
1, /* Fixed */
|
||||
0, /* Version */
|
||||
{0, {0}}, /* DCID */
|
||||
{0, {0}}, /* SCID */
|
||||
{0, 15}, /* PN */
|
||||
NULL, 0, /* Token/Token Len */
|
||||
6, NULL
|
||||
};
|
||||
|
||||
static const unsigned char rx_script_8f_body[] = {
|
||||
0x02, 0x0e, 0x4c, 0x54, 0x00, 0x02
|
||||
};
|
||||
|
||||
static const struct rx_test_op rx_script_8[] = {
|
||||
RX_OP_ADD_RX_DCID(empty_conn_id)
|
||||
/* Inject before we get the keys */
|
||||
RX_OP_INJECT_N(8a)
|
||||
/* Nothing yet */
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
/* Provide keys */
|
||||
RX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT,
|
||||
QRL_SUITE_AES128GCM, rx_script_8_1rtt_secret)
|
||||
/* Now the injected packet is successfully returned */
|
||||
RX_OP_CHECK_PKT_N(8a)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(0)
|
||||
|
||||
/* Packet with new key phase */
|
||||
RX_OP_INJECT_N(8b)
|
||||
/* Packet is successfully decrypted and returned */
|
||||
RX_OP_CHECK_PKT_N(8b)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
/* Key epoch has increased */
|
||||
RX_OP_CHECK_KEY_EPOCH(1)
|
||||
|
||||
/*
|
||||
* Now inject an old packet with the old keys (perhaps reordered in
|
||||
* network).
|
||||
*/
|
||||
RX_OP_INJECT_N(8c)
|
||||
/* Should still be decrypted OK */
|
||||
RX_OP_CHECK_PKT_N(8c)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
/* Epoch has not changed */
|
||||
RX_OP_CHECK_KEY_EPOCH(1)
|
||||
|
||||
/* Another packet with the new keys. */
|
||||
RX_OP_INJECT_N(8d)
|
||||
RX_OP_CHECK_PKT_N(8d)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(1)
|
||||
|
||||
/* We can inject the old packet multiple times and it still works */
|
||||
RX_OP_INJECT_N(8c)
|
||||
RX_OP_CHECK_PKT_N(8c)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(1)
|
||||
|
||||
/* Until we move from UPDATING to COOLDOWN */
|
||||
RX_OP_KEY_UPDATE_TIMEOUT(0)
|
||||
RX_OP_INJECT_N(8c)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(1)
|
||||
|
||||
/*
|
||||
* Injecting a packet from the next epoch (epoch 2) while in COOLDOWN
|
||||
* doesn't work
|
||||
*/
|
||||
RX_OP_INJECT_N(8e)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(1)
|
||||
|
||||
/* Move from COOLDOWN to NORMAL and try again */
|
||||
RX_OP_KEY_UPDATE_TIMEOUT(1)
|
||||
RX_OP_INJECT_N(8e)
|
||||
RX_OP_CHECK_PKT_N(8e)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(2)
|
||||
|
||||
/* Can still receive old packet */
|
||||
RX_OP_INJECT_N(8d)
|
||||
RX_OP_CHECK_PKT_N(8d)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(2)
|
||||
|
||||
/* Move straight from UPDATING to NORMAL */
|
||||
RX_OP_KEY_UPDATE_TIMEOUT(1)
|
||||
|
||||
/* Try a packet from epoch 3 */
|
||||
RX_OP_INJECT_N(8f)
|
||||
RX_OP_CHECK_PKT_N(8f)
|
||||
RX_OP_CHECK_NO_PKT()
|
||||
RX_OP_CHECK_KEY_EPOCH(3)
|
||||
|
||||
RX_OP_END
|
||||
};
|
||||
|
||||
static const struct rx_test_op *rx_scripts[] = {
|
||||
rx_script_1,
|
||||
rx_script_2,
|
||||
|
@ -1334,7 +1616,8 @@ static const struct rx_test_op *rx_scripts[] = {
|
|||
rx_script_4,
|
||||
rx_script_5,
|
||||
rx_script_6,
|
||||
rx_script_7
|
||||
rx_script_7,
|
||||
rx_script_8
|
||||
};
|
||||
|
||||
static int cmp_pkt_hdr(const QUIC_PKT_HDR *a, const QUIC_PKT_HDR *b,
|
||||
|
@ -1395,12 +1678,26 @@ static void rx_state_teardown(struct rx_state *s)
|
|||
}
|
||||
}
|
||||
|
||||
static uint64_t time_counter = 0;
|
||||
|
||||
static OSSL_TIME expected_time(uint64_t counter)
|
||||
{
|
||||
return ossl_time_multiply(ossl_ticks2time(OSSL_TIME_MS), counter);
|
||||
}
|
||||
|
||||
static OSSL_TIME fake_time(void *arg)
|
||||
{
|
||||
return expected_time(++time_counter);
|
||||
}
|
||||
|
||||
static int rx_state_ensure(struct rx_state *s)
|
||||
{
|
||||
if (s->demux == NULL
|
||||
&& !TEST_ptr(s->demux = ossl_quic_demux_new(NULL,
|
||||
s->args.short_conn_id_len,
|
||||
1500)))
|
||||
1500,
|
||||
fake_time,
|
||||
NULL)))
|
||||
return 0;
|
||||
|
||||
s->args.demux = s->demux;
|
||||
|
@ -1497,6 +1794,28 @@ static int rx_run_script(const struct rx_test_op *script)
|
|||
if (!TEST_false(ossl_qrx_read_pkt(s.qrx, &pkt)))
|
||||
goto err;
|
||||
|
||||
break;
|
||||
case RX_TEST_OP_CHECK_KEY_EPOCH:
|
||||
if (!TEST_true(rx_state_ensure(&s)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_uint64_t_eq(ossl_qrx_get_key_epoch(s.qrx),
|
||||
op->largest_pn))
|
||||
goto err;
|
||||
|
||||
break;
|
||||
case RX_TEST_OP_KEY_UPDATE_TIMEOUT:
|
||||
if (!TEST_true(rx_state_ensure(&s)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_true(ossl_qrx_key_update_timeout(s.qrx,
|
||||
op->enc_level)))
|
||||
goto err;
|
||||
|
||||
break;
|
||||
case RX_TEST_OP_SET_INIT_KEY_PHASE:
|
||||
rx_state_teardown(&s);
|
||||
s.args.init_key_phase_bit = (unsigned char)op->enc_level;
|
||||
break;
|
||||
default:
|
||||
OPENSSL_assert(0);
|
||||
|
@ -2285,7 +2604,7 @@ static int test_wire_pkt_hdr_actual(int tidx, int repeat, int cipher,
|
|||
testresult = 1;
|
||||
err:
|
||||
if (have_hpr)
|
||||
ossl_quic_hdr_protector_destroy(&hpr);
|
||||
ossl_quic_hdr_protector_cleanup(&hpr);
|
||||
WPACKET_finish(&wpkt);
|
||||
BUF_MEM_free(buf);
|
||||
OPENSSL_free(hbuf);
|
||||
|
@ -2366,6 +2685,7 @@ static int test_wire_pkt_hdr(int idx)
|
|||
#define TX_TEST_OP_DISCARD_EL 4 /* discard an encryption level */
|
||||
#define TX_TEST_OP_CHECK_DGRAM 5 /* read datagram, compare to expected */
|
||||
#define TX_TEST_OP_CHECK_NO_DGRAM 6 /* check no datagram is in queue */
|
||||
#define TX_TEST_OP_KEY_UPDATE 7 /* perform key update for 1-RTT */
|
||||
|
||||
struct tx_test_op {
|
||||
unsigned char op;
|
||||
|
@ -2407,6 +2727,9 @@ struct tx_test_op {
|
|||
TX_OP_WRITE_N(n) \
|
||||
TX_OP_CHECK_DGRAM_N(n)
|
||||
|
||||
#define TX_OP_KEY_UPDATE() \
|
||||
{ TX_TEST_OP_KEY_UPDATE, NULL, 0, NULL, 0, 0, NULL },
|
||||
|
||||
/* 1. RFC 9001 - A.2 Client Initial */
|
||||
static const unsigned char tx_script_1_body[1162] = {
|
||||
0x06, 0x00, 0x40, 0xf1, 0x01, 0x00, 0x00, 0xed, 0x03, 0x03, 0xeb, 0xf8,
|
||||
|
@ -2681,10 +3004,271 @@ static const struct tx_test_op tx_script_3[] = {
|
|||
TX_OP_END
|
||||
};
|
||||
|
||||
/* 4. Real World - AES-128-GCM Key Update */
|
||||
static const unsigned char tx_script_4_secret[] = {
|
||||
0x70, 0x82, 0xc0, 0x45, 0x61, 0x4d, 0xfe, 0x04, 0x76, 0xa6, 0x4e, 0xf0,
|
||||
0x38, 0xe6, 0x63, 0xd9, 0xdd, 0x4a, 0x75, 0x16, 0xa8, 0xa0, 0x06, 0x5a,
|
||||
0xf2, 0x56, 0xfd, 0x84, 0x78, 0xfd, 0xf6, 0x5e
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_4a_body[] = {
|
||||
0x02, 0x03, 0x09, 0x00, 0x03, 0x0c, 0x00, 0x36, 0x49, 0x27, 0x6d, 0x20,
|
||||
0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x6f, 0x6e,
|
||||
0x64, 0x65, 0x72, 0x66, 0x75, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_4a_dgram[] = {
|
||||
0x47, 0x6e, 0x4e, 0xbd, 0x49, 0x7e, 0xbd, 0x15, 0x1c, 0xd1, 0x3e, 0xc8,
|
||||
0xcd, 0x43, 0x87, 0x6b, 0x84, 0xdb, 0xeb, 0x06, 0x8b, 0x8a, 0xae, 0x37,
|
||||
0xed, 0x9c, 0xeb, 0xbc, 0xcf, 0x0d, 0x3c, 0xf0, 0xa1, 0x6f, 0xee, 0xd2,
|
||||
0x7c, 0x07, 0x6e, 0xd1, 0xbe, 0x40, 0x6a, 0xd4, 0x53, 0x38, 0x9e, 0x63,
|
||||
0xb5, 0xde, 0x35, 0x09, 0xb2, 0x78, 0x94, 0xe4, 0x2b, 0x37
|
||||
};
|
||||
|
||||
static QUIC_PKT_HDR tx_script_4a_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT, /* type */
|
||||
0, /* spin bit */
|
||||
0, /* key phase */
|
||||
2, /* PN length */
|
||||
0, /* partial */
|
||||
0, /* fixed */
|
||||
0, /* version */
|
||||
{ 4, {0x6e, 0x4e, 0xbd, 0x49} }, /* DCID */
|
||||
{ 0, {0} }, /* SCID */
|
||||
{ 0 }, /* PN */
|
||||
NULL, 0, /* Token */
|
||||
5555, NULL /* Len/Data */
|
||||
};
|
||||
|
||||
static const OSSL_QTX_IOVEC tx_script_4a_iovec[] = {
|
||||
{ tx_script_4a_body, sizeof(tx_script_4a_body) }
|
||||
};
|
||||
|
||||
static const OSSL_QTX_PKT tx_script_4a_pkt = {
|
||||
&tx_script_4a_hdr,
|
||||
tx_script_4a_iovec,
|
||||
OSSL_NELEM(tx_script_4a_iovec),
|
||||
NULL, NULL,
|
||||
4,
|
||||
0
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_4b_body[] = {
|
||||
0x02, 0x04, 0x07, 0x00, 0x00, 0x0c, 0x00, 0x40, 0x51, 0x49, 0x27, 0x6d,
|
||||
0x20, 0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x6f,
|
||||
0x6e, 0x64, 0x65, 0x72, 0x66, 0x75, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_4b_dgram[] = {
|
||||
0x58, 0x6e, 0x4e, 0xbd, 0x49, 0xa4, 0x43, 0x33, 0xea, 0x11, 0x3a, 0x6c,
|
||||
0xf5, 0x20, 0xef, 0x55, 0x8d, 0x25, 0xe2, 0x3b, 0x0e, 0x8c, 0xea, 0x17,
|
||||
0xfc, 0x2b, 0x7a, 0xab, 0xfa, 0x3d, 0x07, 0xda, 0xa7, 0x7c, 0xc7, 0x47,
|
||||
0x82, 0x02, 0x46, 0x40, 0x4f, 0x01, 0xad, 0xb2, 0x9d, 0x97, 0xdb, 0xfc,
|
||||
0x9c, 0x4b, 0x46, 0xb1, 0x5a, 0x7f, 0x0b, 0x12, 0xaf, 0x49, 0xdf,
|
||||
};
|
||||
|
||||
static QUIC_PKT_HDR tx_script_4b_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT, /* type */
|
||||
0, /* spin bit */
|
||||
1, /* key phase */
|
||||
2, /* PN length */
|
||||
0, /* partial */
|
||||
0, /* fixed */
|
||||
0, /* version */
|
||||
{ 4, {0x6e, 0x4e, 0xbd, 0x49} }, /* DCID */
|
||||
{ 0, {0} }, /* SCID */
|
||||
{ 0 }, /* PN */
|
||||
NULL, 0, /* Token */
|
||||
5555, NULL /* Len/Data */
|
||||
};
|
||||
|
||||
static const OSSL_QTX_IOVEC tx_script_4b_iovec[] = {
|
||||
{ tx_script_4b_body, sizeof(tx_script_4b_body) }
|
||||
};
|
||||
|
||||
static const OSSL_QTX_PKT tx_script_4b_pkt = {
|
||||
&tx_script_4b_hdr,
|
||||
tx_script_4b_iovec,
|
||||
OSSL_NELEM(tx_script_4b_iovec),
|
||||
NULL, NULL,
|
||||
5,
|
||||
0
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_4c_body[] = {
|
||||
0x02, 0x09, 0x0e, 0x00, 0x00, 0x0c, 0x00, 0x40, 0xd8, 0x49, 0x27, 0x6d,
|
||||
0x20, 0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x77, 0x6f,
|
||||
0x6e, 0x64, 0x65, 0x72, 0x66, 0x75, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65,
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_4c_dgram[] = {
|
||||
0x49, 0x6e, 0x4e, 0xbd, 0x49, 0x4d, 0xd9, 0x85, 0xba, 0x26, 0xfb, 0x68,
|
||||
0x83, 0x9b, 0x94, 0x34, 0x7d, 0xc1, 0x7a, 0x05, 0xb7, 0x38, 0x43, 0x21,
|
||||
0xe2, 0xec, 0x2b, 0xc1, 0x81, 0x74, 0x2d, 0xda, 0x24, 0xba, 0xbd, 0x99,
|
||||
0x69, 0xd2, 0x56, 0xfa, 0xae, 0x29, 0x24, 0xb2, 0xaa, 0xda, 0xbd, 0x82,
|
||||
0x80, 0xf1, 0xbb, 0x6a, 0xfd, 0xae, 0xda, 0x0e, 0x09, 0xcf, 0x09,
|
||||
};
|
||||
|
||||
static QUIC_PKT_HDR tx_script_4c_hdr = {
|
||||
QUIC_PKT_TYPE_1RTT, /* type */
|
||||
0, /* spin bit */
|
||||
0, /* key phase */
|
||||
2, /* PN length */
|
||||
0, /* partial */
|
||||
0, /* fixed */
|
||||
0, /* version */
|
||||
{ 4, {0x6e, 0x4e, 0xbd, 0x49} }, /* DCID */
|
||||
{ 0, {0} }, /* SCID */
|
||||
{ 0 }, /* PN */
|
||||
NULL, 0, /* Token */
|
||||
5555, NULL /* Len/Data */
|
||||
};
|
||||
|
||||
static const OSSL_QTX_IOVEC tx_script_4c_iovec[] = {
|
||||
{ tx_script_4c_body, sizeof(tx_script_4c_body) }
|
||||
};
|
||||
|
||||
static const OSSL_QTX_PKT tx_script_4c_pkt = {
|
||||
&tx_script_4c_hdr,
|
||||
tx_script_4c_iovec,
|
||||
OSSL_NELEM(tx_script_4c_iovec),
|
||||
NULL, NULL,
|
||||
10,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct tx_test_op tx_script_4[] = {
|
||||
TX_OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, QRL_SUITE_AES128GCM, tx_script_4_secret)
|
||||
TX_OP_WRITE_CHECK(4a)
|
||||
TX_OP_KEY_UPDATE()
|
||||
TX_OP_WRITE_CHECK(4b)
|
||||
TX_OP_KEY_UPDATE()
|
||||
TX_OP_WRITE_CHECK(4c)
|
||||
TX_OP_END
|
||||
};
|
||||
|
||||
/* 5. Real World - Retry Packet */
|
||||
static const unsigned char tx_script_5_body[] = {
|
||||
/* Retry Token */
|
||||
0x92, 0xe7, 0xc6, 0xd8, 0x09, 0x65, 0x72, 0x55, 0xe5, 0xe2, 0x73, 0x04,
|
||||
0xf3, 0x07, 0x5b, 0x21, 0x9f, 0x50, 0xcb, 0xbc, 0x79, 0xc5, 0x77, 0x5a,
|
||||
0x29, 0x43, 0x65, 0x49, 0xf0, 0x6e, 0xc1, 0xc0, 0x3a, 0xe8, 0xca, 0xd2,
|
||||
0x44, 0x69, 0xdd, 0x23, 0x31, 0x93, 0x52, 0x02, 0xf7, 0x42, 0x07, 0x78,
|
||||
0xa1, 0x81, 0x61, 0x9c, 0x39, 0x07, 0x18, 0x69, 0x6e, 0x4f, 0xdc, 0xa0,
|
||||
0xbe, 0x4b, 0xe5, 0xf2, 0xe9, 0xd2, 0xa4, 0xa7, 0x34, 0x55, 0x5e, 0xf3,
|
||||
0xf8, 0x9c, 0x49, 0x8f, 0x0c, 0xc8, 0xb2, 0x75, 0x4b, 0x4d, 0x2f, 0xfe,
|
||||
0x05, 0x5a, 0xdd, 0x4b, 0xe6, 0x14, 0xb4, 0xd2, 0xc0, 0x93, 0x6e, 0x0e,
|
||||
0x84, 0x41, 0x4d, 0x31,
|
||||
/* Retry Integrity Tag */
|
||||
0x43, 0x8e, 0xab, 0xcd, 0xce, 0x24, 0x44, 0xc2, 0x20, 0xe1, 0xe2, 0xc8,
|
||||
0xae, 0xa3, 0x8d, 0x4e,
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_5_dgram[] = {
|
||||
0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0xa9, 0x20, 0xcc, 0xc2, 0x92,
|
||||
0xe7, 0xc6, 0xd8, 0x09, 0x65, 0x72, 0x55, 0xe5, 0xe2, 0x73, 0x04, 0xf3,
|
||||
0x07, 0x5b, 0x21, 0x9f, 0x50, 0xcb, 0xbc, 0x79, 0xc5, 0x77, 0x5a, 0x29,
|
||||
0x43, 0x65, 0x49, 0xf0, 0x6e, 0xc1, 0xc0, 0x3a, 0xe8, 0xca, 0xd2, 0x44,
|
||||
0x69, 0xdd, 0x23, 0x31, 0x93, 0x52, 0x02, 0xf7, 0x42, 0x07, 0x78, 0xa1,
|
||||
0x81, 0x61, 0x9c, 0x39, 0x07, 0x18, 0x69, 0x6e, 0x4f, 0xdc, 0xa0, 0xbe,
|
||||
0x4b, 0xe5, 0xf2, 0xe9, 0xd2, 0xa4, 0xa7, 0x34, 0x55, 0x5e, 0xf3, 0xf8,
|
||||
0x9c, 0x49, 0x8f, 0x0c, 0xc8, 0xb2, 0x75, 0x4b, 0x4d, 0x2f, 0xfe, 0x05,
|
||||
0x5a, 0xdd, 0x4b, 0xe6, 0x14, 0xb4, 0xd2, 0xc0, 0x93, 0x6e, 0x0e, 0x84,
|
||||
0x41, 0x4d, 0x31, 0x43, 0x8e, 0xab, 0xcd, 0xce, 0x24, 0x44, 0xc2, 0x20,
|
||||
0xe1, 0xe2, 0xc8, 0xae, 0xa3, 0x8d, 0x4e,
|
||||
};
|
||||
|
||||
static QUIC_PKT_HDR tx_script_5_hdr = {
|
||||
QUIC_PKT_TYPE_RETRY, /* type */
|
||||
0, /* spin bit */
|
||||
0, /* key phase */
|
||||
0, /* PN length */
|
||||
0, /* partial */
|
||||
0, /* fixed */
|
||||
1, /* version */
|
||||
{ 0, {0} }, /* DCID */
|
||||
{ 4, {0xa9, 0x20, 0xcc, 0xc2} }, /* SCID */
|
||||
{ 0 }, /* PN */
|
||||
NULL, 0, /* Token */
|
||||
5555, NULL /* Len/Data */
|
||||
};
|
||||
|
||||
static const OSSL_QTX_IOVEC tx_script_5_iovec[] = {
|
||||
{ tx_script_5_body, sizeof(tx_script_5_body) }
|
||||
};
|
||||
|
||||
static const OSSL_QTX_PKT tx_script_5_pkt = {
|
||||
&tx_script_5_hdr,
|
||||
tx_script_5_iovec,
|
||||
OSSL_NELEM(tx_script_5_iovec),
|
||||
NULL, NULL,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct tx_test_op tx_script_5[] = {
|
||||
TX_OP_WRITE_CHECK(5)
|
||||
TX_OP_END
|
||||
};
|
||||
|
||||
/* 6. Real World - Version Negotiation Packet */
|
||||
static const unsigned char tx_script_6_body[] = {
|
||||
0x00, 0x00, 0x00, 0x01, /* Supported Version: 1 */
|
||||
0xaa, 0x9a, 0x3a, 0x9a /* Supported Version: Random (GREASE) */
|
||||
};
|
||||
|
||||
static const unsigned char tx_script_6_dgram[] = {
|
||||
0x80, /* Long */
|
||||
0x00, 0x00, 0x00, 0x00, /* Version 0 (Version Negotiation) */
|
||||
0x00, /* DCID */
|
||||
0x0c, 0x35, 0x3c, 0x1b, 0x97, 0xca, /* SCID */
|
||||
0xf8, 0x99, 0x11, 0x39, 0xad, 0x79,
|
||||
0x1f,
|
||||
0x00, 0x00, 0x00, 0x01, /* Supported Version: 1 */
|
||||
0xaa, 0x9a, 0x3a, 0x9a /* Supported Version: Random (GREASE) */
|
||||
};
|
||||
|
||||
static QUIC_PKT_HDR tx_script_6_hdr = {
|
||||
QUIC_PKT_TYPE_VERSION_NEG, /* type */
|
||||
0, /* spin bit */
|
||||
0, /* key phase */
|
||||
0, /* PN length */
|
||||
0, /* partial */
|
||||
0, /* fixed */
|
||||
0, /* version */
|
||||
{ 0, {0} }, /* DCID */
|
||||
{ 12, {0x35, 0x3c, 0x1b, 0x97, 0xca, 0xf8, 0x99,
|
||||
0x11, 0x39, 0xad, 0x79, 0x1f} }, /* SCID */
|
||||
{ 0 }, /* PN */
|
||||
NULL, 0, /* Token */
|
||||
5555, NULL /* Len/Data */
|
||||
};
|
||||
|
||||
static const OSSL_QTX_IOVEC tx_script_6_iovec[] = {
|
||||
{ tx_script_6_body, sizeof(tx_script_6_body) }
|
||||
};
|
||||
|
||||
static const OSSL_QTX_PKT tx_script_6_pkt = {
|
||||
&tx_script_6_hdr,
|
||||
tx_script_6_iovec,
|
||||
OSSL_NELEM(tx_script_6_iovec),
|
||||
NULL, NULL,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct tx_test_op tx_script_6[] = {
|
||||
TX_OP_WRITE_CHECK(6)
|
||||
TX_OP_END
|
||||
};
|
||||
|
||||
static const struct tx_test_op *const tx_scripts[] = {
|
||||
tx_script_1,
|
||||
tx_script_2,
|
||||
tx_script_3
|
||||
tx_script_3,
|
||||
tx_script_4,
|
||||
tx_script_5,
|
||||
tx_script_6
|
||||
};
|
||||
|
||||
static int tx_run_script(const struct tx_test_op *script)
|
||||
|
@ -2758,6 +3342,10 @@ static int tx_run_script(const struct tx_test_op *script)
|
|||
if (!TEST_false(ossl_qtx_pop_net(qtx, &msg)))
|
||||
goto err;
|
||||
break;
|
||||
case TX_TEST_OP_KEY_UPDATE:
|
||||
if (!TEST_true(ossl_qtx_trigger_key_update(qtx)))
|
||||
goto err;
|
||||
break;
|
||||
default:
|
||||
OPENSSL_assert(0);
|
||||
goto err;
|
||||
|
|
Loading…
Reference in New Issue