diff options
-rw-r--r-- | crypto/bio/bio_local.h | 12 | ||||
-rw-r--r-- | include/internal/bio_addr.h | 29 | ||||
-rw-r--r-- | include/internal/quic_demux.h | 251 | ||||
-rw-r--r-- | include/internal/quic_record.h | 311 | ||||
-rw-r--r-- | include/internal/quic_record_util.h | 62 | ||||
-rw-r--r-- | include/internal/quic_types.h | 47 | ||||
-rw-r--r-- | include/internal/quic_wire.h | 8 | ||||
-rw-r--r-- | include/internal/quic_wire_pkt.h | 435 | ||||
-rw-r--r-- | ssl/quic/build.info | 2 | ||||
-rw-r--r-- | ssl/quic/quic_demux.c | 523 | ||||
-rw-r--r-- | ssl/quic/quic_record.c | 1315 | ||||
-rw-r--r-- | ssl/quic/quic_record_util.c | 141 | ||||
-rw-r--r-- | ssl/quic/quic_wire.c | 8 | ||||
-rw-r--r-- | ssl/quic/quic_wire_pkt.c | 678 | ||||
-rw-r--r-- | ssl/ssl_local.h | 10 | ||||
-rw-r--r-- | ssl/tls13_enc.c | 48 | ||||
-rw-r--r-- | test/build.info | 6 | ||||
-rw-r--r-- | test/quic_record_test.c | 2378 | ||||
-rw-r--r-- | test/quic_wire_test.c | 67 | ||||
-rw-r--r-- | test/recipes/70-test_quic_record.t | 19 |
20 files changed, 6305 insertions, 45 deletions
diff --git a/crypto/bio/bio_local.h b/crypto/bio/bio_local.h index bcb383f0ea..56ad6d7d87 100644 --- a/crypto/bio/bio_local.h +++ b/crypto/bio/bio_local.h @@ -9,6 +9,7 @@ #include "internal/e_os.h" #include "internal/sockets.h" +#include "internal/bio_addr.h" /* BEGIN BIO_ADDRINFO/BIO_ADDR stuff. */ @@ -63,17 +64,6 @@ struct bio_addrinfo_st { struct bio_addrinfo_st *bai_next; }; # endif - -union bio_addr_st { - struct sockaddr sa; -# if OPENSSL_USE_IPV6 - struct sockaddr_in6 s_in6; -# endif - struct sockaddr_in s_in; -# ifndef OPENSSL_NO_UNIX_SOCK - struct sockaddr_un s_un; -# endif -}; #endif /* END BIO_ADDRINFO/BIO_ADDR stuff. */ diff --git a/include/internal/bio_addr.h b/include/internal/bio_addr.h new file mode 100644 index 0000000000..a6449b7eb0 --- /dev/null +++ b/include/internal/bio_addr.h @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_BIO_ADDR_H +# define OSSL_BIO_ADDR_H + +# include "internal/e_os.h" +# include "internal/sockets.h" + +# ifndef OPENSSL_NO_SOCK +union bio_addr_st { + struct sockaddr sa; +# if OPENSSL_USE_IPV6 + struct sockaddr_in6 s_in6; +# endif + struct sockaddr_in s_in; +# ifndef OPENSSL_NO_UNIX_SOCK + struct sockaddr_un s_un; +# endif +}; +# endif + +#endif diff --git a/include/internal/quic_demux.h b/include/internal/quic_demux.h new file mode 100644 index 0000000000..7d4b0df67e --- /dev/null +++ b/include/internal/quic_demux.h @@ -0,0 +1,251 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_QUIC_DEMUX_H +# define OSSL_QUIC_DEMUX_H + +# include <openssl/ssl.h> +# include "internal/quic_types.h" +# include "internal/bio_addr.h" + +/* + * QUIC Demuxer + * ============ + * + * The QUIC connection demuxer is the entity responsible for receiving datagrams + * from the network via a datagram BIO. It parses packet headers to determine + * each packet's destination connection ID (DCID) and hands off processing of + * the packet to the correct QUIC Record Layer (QRL)'s RX side. + * + * A QRL is instantiated per QUIC connection and contains the cryptographic + * resources needed to decrypt QUIC packets for that connection. Received + * datagrams are passed from the demuxer to the QRL via a callback registered + * for a specific DCID by the QRL; thus the demuxer has no specific knowledge of + * the QRL and is not coupled to it. + * + * A connection may have multiple connection IDs associated with it; a QRL + * handles this simply by registering multiple connection IDs with the demuxer + * via multiple register calls. + * + * URX Queue + * --------- + * + * Since the demuxer must handle the initial reception of datagrams from the OS, + * RX queue management for new, unprocessed datagrams is also handled by the + * demuxer. + * + * The demuxer maintains a queue of Unprocessed RX Entries (URXEs), which store + * unprocessed (i.e., encrypted, unvalidated) data received from the network. + * The URXE queue is designed to allow multiple datagrams to be received in a + * single call to BIO_recvmmsg, where supported. + * + * One URXE is used per received datagram. Each datagram may contain multiple + * packets, however, this is not the demuxer's concern. QUIC prohibits different + * packets in the same datagram from containing different DCIDs; the demuxer + * only considers the DCID of the first packet in a datagram when deciding how + * to route a received datagram, and it is the responsibility of the QRL to + * enforce this rule. Packets other than the first packet in a datagram are not + * examined by the demuxer, and the demuxer does not perform validation of + * packet headers other than to the minimum extent necessary to extract the + * DCID; further parsing and validation of packet headers is the responsibility + * of the QRL. + * + * Rather than defining an opaque interface, the URXE structure internals + * are exposed. Since the demuxer is only exposed to other parts of the QUIC + * implementation internals, this poses no problem, and has a number of + * advantages: + * + * - Fields in the URXE can be allocated to support requirements in other + * components, like the QRL, which would otherwise have to allocate extra + * memory corresponding to each URXE. + * + * - Other components, like the QRL, can keep the URXE in queues of its own + * when it is not being managed by the demuxer. + * + * URX Queue Structure + * ------------------- + * + * The URXE queue is maintained as a simple doubly-linked list. URXE entries are + * moved between different lists in their lifecycle (for example, from a free + * list to a pending list and vice versa). The buffer into which datagrams are + * received immediately follows this URXE header structure and is part of the + * same allocation. + */ + +typedef struct quic_urxe_st QUIC_URXE; + +/* Maximum number of packets we allow to exist in one datagram. */ +#define QUIC_MAX_PKT_PER_URXE (sizeof(uint64_t) * 8) + +struct quic_urxe_st { + QUIC_URXE *prev, *next; + + /* + * The URXE data starts after this structure so we don't need a pointer. + * data_len stores the current length (i.e., the length of the received + * datagram) and alloc_len stores the allocation length. The URXE will be + * reallocated if we need a larger allocation than is available, though this + * should not be common as we will have a good idea of worst-case MTUs up + * front. + */ + size_t data_len, alloc_len; + + /* + * Bitfields per packet. processed indicates the packet has been processed + * and must not be processed again, hpr_removed indicates header protection + * has already been removed. Used by QRL only; not used by the demuxer. + */ + uint64_t processed, hpr_removed; + + /* + * Address of peer we received the datagram from, and the local interface + * address we received it on. If local address support is not enabled, local + * is zeroed. + */ + BIO_ADDR peer, local; +}; + +/* Accessors for URXE buffer. */ +static ossl_unused ossl_inline unsigned char * +ossl_quic_urxe_data(const QUIC_URXE *e) +{ + return (unsigned char *)&e[1]; +} + +static ossl_unused ossl_inline unsigned char * +ossl_quic_urxe_data_end(const QUIC_URXE *e) +{ + return ossl_quic_urxe_data(e) + e->data_len; +} + +/* List structure tracking a queue of URXEs. */ +typedef struct quic_urxe_list_st { + QUIC_URXE *head, *tail; +} QUIC_URXE_LIST; + +/* + * List management helpers. These are used by the demuxer but can also be used + * by users of the demuxer to manage URXEs. + */ +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); +void ossl_quic_urxe_insert_tail(QUIC_URXE_LIST *l, QUIC_URXE *e); + +/* Opaque type representing a demuxer. */ +typedef struct quic_demux_st QUIC_DEMUX; + +/* + * Called when a datagram is received for a given connection ID. + * + * e is a URXE containing the datagram payload. It is permissible for the callee + * to mutate this buffer; once the demuxer calls this callback, it will never + * read the buffer again. + * + * The callee must arrange for ossl_quic_demux_release_urxe to be called on the URXE + * at some point in the future (this need not be before the callback returns). + * + * At the time the callback is made, the URXE will not be in any queue, + * therefore the callee can use the prev and next fields as it wishes. + */ +typedef void (ossl_quic_demux_cb_fn)(QUIC_URXE *e, void *arg); + +/* + * Creates a new demuxer. The given BIO is used to receive datagrams from the + * network using BIO_recvmmsg. short_conn_id_len is the length of destination + * connection IDs used in RX'd packets; it must have the same value for all + * 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. + */ +QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio, + size_t short_conn_id_len, + size_t default_urxe_alloc_len); + +/* + * Destroy a demuxer. All URXEs must have been released back to the demuxer + * before calling this. No-op if demux is NULL. + */ +void ossl_quic_demux_free(QUIC_DEMUX *demux); + +/* + * Register a datagram handler callback for a connection ID. + * + * ossl_quic_demux_pump will call the specified function if it receives a datagram + * the first packet of which has the specified destination connection ID. + * + * It is assumed all packets in a datagram have the same destination connection + * ID (as QUIC mandates this), but it is the user's responsibility to check for + * this and reject subsequent packets in a datagram that violate this rule. + * + * dst_conn_id is a destination connection ID; it is copied and need not remain + * valid after this function returns. + * + * cb_arg is passed to cb when it is called. For information on the callback, + * see its typedef above. + * + * Only one handler can be set for a given connection ID. If a handler is + * already set for the given connection ID, returns 0. + * + * Returns 1 on success or 0 on failure. + */ +int ossl_quic_demux_register(QUIC_DEMUX *demux, + const QUIC_CONN_ID *dst_conn_id, + ossl_quic_demux_cb_fn *cb, + void *cb_arg); + +/* + * Unregisters any datagram handler callback set for the given connection ID. + * Fails if no handler is registered for the given connection ID. + * + * Returns 1 on success or 0 on failure. + */ +int ossl_quic_demux_unregister(QUIC_DEMUX *demux, + const QUIC_CONN_ID *dst_conn_id); + +/* + * Unregisters any datagram handler callback from all connection IDs it is used + * for. cb and cb_arg must both match the values passed to + * ossl_quic_demux_register. + */ +void ossl_quic_demux_unregister_by_cb(QUIC_DEMUX *demux, + ossl_quic_demux_cb_fn *cb, + void *cb_arg); + +/* + * Releases a URXE back to the demuxer. No reference must be made to the URXE or + * its buffer after calling this function. The URXE must not be in any queue; + * that is, its prev and next pointers must be NULL. + */ +void ossl_quic_demux_release_urxe(QUIC_DEMUX *demux, + QUIC_URXE *e); + +/* + * Process any unprocessed RX'd datagrams, by calling registered callbacks by + * connection ID, reading more datagrams from the BIO if necessary. + * + * Returns 1 on success or 0 on failure. + */ +int ossl_quic_demux_pump(QUIC_DEMUX *demux); + +/* + * Artificially inject a packet into the demuxer for testing purposes. The + * buffer must not exceed the URXE size being used by the demuxer. + * + * If peer or local are NULL, their respective fields are zeroed in the injected + * URXE. + * + * Returns 1 on success or 0 on failure. + */ +int ossl_quic_demux_inject(QUIC_DEMUX *demux, + const unsigned char *buf, + size_t buf_len, + const BIO_ADDR *peer, + const BIO_ADDR *local); + +#endif diff --git a/include/internal/quic_record.h b/include/internal/quic_record.h new file mode 100644 index 0000000000..06284c251b --- /dev/null +++ b/include/internal/quic_record.h @@ -0,0 +1,311 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_QUIC_RECORD_H +# define OSSL_QUIC_RECORD_H + +# include <openssl/ssl.h> +# include "internal/quic_wire_pkt.h" +# include "internal/quic_types.h" +# include "internal/quic_record_util.h" +# include "internal/quic_demux.h" + +/* + * QUIC Record Layer + * ================= + */ +typedef struct ossl_qrl_st OSSL_QRL; + +typedef struct ossl_qrl_args_st { + OSSL_LIB_CTX *libctx; + const char *propq; + + /* Demux to receive datagrams from. */ + QUIC_DEMUX *rx_demux; + + /* Length of connection IDs used in short-header packets in bytes. */ + size_t short_conn_id_len; + + /* Initial reference PN used for RX. */ + QUIC_PN rx_init_largest_pn[QUIC_PN_SPACE_NUM]; +} OSSL_QRL_ARGS; + +/* Instantiates a new QRL. */ +OSSL_QRL *ossl_qrl_new(const OSSL_QRL_ARGS *args); + +/* + * Frees the QRL. All packets obtained using ossl_qrl_read_pkt must already + * have been released by calling ossl_qrl_release_pkt. + * + * You do not need to call ossl_qrl_remove_dst_conn_id first; this function will + * unregister the QRL from the demuxer for all registered destination connection + * IDs (DCIDs) automatically. + */ +void ossl_qrl_free(OSSL_QRL *qrl); + +/* + * DCID Management + * =============== + */ + +/* + * Adds a given DCID to the QRL. The QRL will register the DCID with the demuxer + * so that incoming packets with that DCID are passed to the given QRL. Multiple + * DCIDs may be associated with a QRL at any one time. You will need to add at + * least one DCID after instantiating the QRL. A zero-length DCID is a valid + * input to this function. This function fails if the DCID is already + * registered. + * + * Returns 1 on success or 0 on error. + */ +int ossl_qrl_add_dst_conn_id(OSSL_QRL *qrl, + const QUIC_CONN_ID *dst_conn_id); + +/* + * Remove a DCID previously registered with ossl_qrl_add_dst_conn_id. The DCID + * is unregistered from the demuxer. Fails if the DCID is not registered with + * the demuxer. + * + * Returns 1 on success or 0 on error. + */ +int ossl_qrl_remove_dst_conn_id(OSSL_QRL *qrl, + const QUIC_CONN_ID *dst_conn_id); + +/* + * Secret Management + * ================= + * + * A QRL has several encryption levels (Initial, Handshake, 0-RTT, 1-RTT) and + * two directions (RX, TX). At any given time, key material is managed for each + * (EL, RX/TX) combination. + * + * Broadly, for a given (EL, RX/TX), the following state machine is applicable: + * + * WAITING_FOR_KEYS --[Provide]--> HAVE_KEYS --[Discard]--> | DISCARDED | + * \-------------------------------------[Discard]--> | | + * + * To transition the RX side of an EL from WAITING_FOR_KEYS to HAVE_KEYS, call + * ossl_qrl_provide_rx_secret (or for the INITIAL EL, + * ossl_qrl_provide_rx_secret_initial). + * + * Once keys have been provisioned for an EL, you call + * ossl_qrl_discard_enc_level to transition the EL to the DISCARDED state. You + * can also call this function to transition directly to the DISCARDED state + * even before any keys have been provisioned for that EL. + * + * The DISCARDED state is terminal for a given EL; you cannot provide a secret + * again for that EL after reaching it. + * + * Incoming packets cannot be processed and decrypted if they target an EL + * not in the HAVE_KEYS state. However, there is a distinction between + * the WAITING_FOR_KEYS and DISCARDED states: + * + * - In the WAITING_FOR_KEYS state, the QRL assumes keys for the given + * EL will eventually arrive. Therefore, if it receives any packet + * for an EL in this state, it buffers it and tries to process it + * again once the EL reaches HAVE_KEYS. + * + * - In the DISCARDED state, the QRL assumes no keys for the given + * EL will ever arrive again. If it receives any packet for an EL + * in this state, it is simply discarded. + * + * If the user wishes to instantiate a new QRL to replace an old one for + * whatever reason, for example to take over for an already established QUIC + * connection, it is important that all ELs no longer being used (i.e., INITIAL, + * 0-RTT, 1-RTT) are transitioned to the DISCARDED state. Otherwise, the QRL + * will assume that keys for these ELs will arrive in future, and will buffer + * any received packets for those ELs perpetually. This can be done by calling + * ossl_qrl_discard_enc_level for all non-1-RTT ELs immediately after + * instantiating the QRL. + * + * The INITIAL EL is not setup automatically when the QRL is instantiated. This + * allows the caller to instead discard it immediately after instantiation of + * the QRL if it is not needed, for example if the QRL 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_qrl_provide_rx_secret_initial + * should be called immediately after instantiation. + */ + +/* + * A QUIC client sends its first INITIAL packet with a random DCID, which is + * used to compute the secret used for INITIAL packet encryption. This function + * must be called to provide the DCID used for INITIAL packet secret computation + * before the QRL can process any INITIAL response packets. + * + * It is possible to use the QRL without ever calling this, for example if there + * is no desire to handle INITIAL packets (e.g. if the QRL is instantiated to + * succeed a previous QRL and handle a connection which is already established.) + * However, in this case you should make sure you call + * ossl_qrl_discard_enc_level (see above). + * + * Returns 1 on success or 0 on error. + */ +int ossl_qrl_provide_rx_secret_initial(OSSL_QRL *qrl, + const QUIC_CONN_ID *dst_conn_id); + +/* + * Provides a secret to the QRL, which arises due to an encryption level change. + * enc_level is a QUIC_ENC_LEVEL_* value. This function cannot be used to + * initialise the INITIAL encryption level; see + * ossl_qrl_provide_rx_secret_initial instead. + * + * You should seek to call this function for a given EL before packets of that + * EL arrive and are processed by the QRL. However, if packets have already + * arrived for a given EL, the QRL will defer processing of them and perform + * processing of them when this function is eventually called for the EL in + * question. + * + * suite_id is a QRL_SUITE_* value which determines the AEAD function used for + * the QRL. + * + * The secret passed is used directly to derive the "quic key", "quic iv" and + * "quic hp" values. + * + * secret_len is the length of the secret buffer in bytes. The buffer must be + * sized correctly to the chosen suite, else the function fails. + * + * This function can only be called once for a given EL. Subsequent calls fail, + * as do calls made after a corresponding call to ossl_qrl_discard_enc_level for + * that EL. The secret for a EL cannot be changed after it is set because QUIC + * has no facility for introducing additional key material after an EL is setup. + * QUIC key updates are managed automatically by the QRL and do not require user + * intervention. + * + * Returns 1 on success or 0 on failure. + */ +int ossl_qrl_provide_rx_secret(OSSL_QRL *qrl, + uint32_t enc_level, + uint32_t suite_id, + const unsigned char *secret, + size_t secret_len); + +/* + * Informs the QRL that it can now discard key material for a given EL. The QRL + * will no longer be able to process incoming packets received at that + * encryption level. This function is idempotent and succeeds if the EL has + * already been discarded. + * + * Returns 1 on success and 0 on failure. + */ +int ossl_qrl_discard_enc_level(OSSL_QRL *qrl, uint32_t enc_level); + +/* + * Packet Reception + * ================ + */ + +/* Information about a received packet. */ +typedef struct ossl_qrl_rx_pkt_st { + /* Opaque handle to be passed to ossl_qrl_release_pkt. */ + void *handle; + + /* + * Points to a logical representation of the decoded QUIC packet header. The + * data and len fields point to the decrypted QUIC payload (i.e., to a + * sequence of zero or more (potentially malformed) frames to be decoded). + */ + QUIC_PKT_HDR *hdr; + + /* + * Address the packet was received from. If this is not available for this + * packet, this field is NULL (but this can only occur for manually injected + * packets). + */ + const BIO_ADDR *peer; + + /* + * Local address the packet was sent to. If this is not available for this + * packet, this field is NULL. + */ + const BIO_ADDR *local; + + /* + * This is the length of the datagram which contained this packet. Note that + * the datagram may have contained other packets than this. The intended use + * for this is so that the user can enforce minimum datagram sizes (e.g. for + * datagrams containing INITIAL packets), as required by RFC 9000. + */ + size_t datagram_len; +} OSSL_QRL_RX_PKT; + +/* + * Tries to read a new decrypted packet from the QRL. + * + * 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_qrl_release_pkt, + * which must be called once you are done with the packet. + */ +int ossl_qrl_read_pkt(OSSL_QRL *qrl, OSSL_QRL_RX_PKT *pkt); + +/* + * Release the resources pointed to by an OSSL_QRL_RX_PKT returned by + * ossl_qrl_read_pkt. Pass the opaque value pkt->handle returned in the + * structure. + */ +void ossl_qrl_release_pkt(OSSL_QRL *qrl, void *handle); + +/* + * Returns 1 if there are any already processed (i.e. decrypted) packets waiting + * to be read from the QRL. + */ +int ossl_qrl_processed_read_pending(OSSL_QRL *qrl); + +/* + * Returns 1 if there arre any unprocessed (i.e. not yet decrypted) packets + * waiting to be processed by the QRL. These may or may not result in + * successfully decrypted packets once processed. This indicates whether + * unprocessed data is buffered by the QRL, not whether any data is available in + * a kernel socket buffer. + */ +int ossl_qrl_unprocessed_read_pending(OSSL_QRL *qrl); + +/* + * Returns the number of UDP payload bytes received from the network so far + * since the last time this counter was cleared. If clear is 1, clears the + * counter and returns the old value. + * + * The intended use of this is to allow callers to determine how much credit to + * add to their anti-amplification budgets. This is reported separately instead + * of in the OSSL_QRL_RX_PKT structure so that a caller can apply + * anti-amplification credit as soon as a datagram is received, before it has + * necessarily read all processed packets contained within that datagram from + * the QRL. + */ +uint64_t ossl_qrl_get_bytes_received(OSSL_QRL *qrl, int clear); + +/* + * Sets a callback which is called when a packet is received and being + * validated before being queued in the read queue. This is called before packet + * body decryption. pn_space is a QUIC_PN_SPACE_* value denoting which PN space + * the PN belongs to. + * + * If this callback returns 1, processing continues normally. + * If this callback returns 0, the packet is discarded. + * + * Other packets in the same datagram will still be processed where possible. + * + * The intended use for this function is to allow early validation of whether + * a PN is a potential duplicate before spending CPU time decrypting the + * packet payload. + * + * The callback is optional and can be unset by passing NULL for cb. + * cb_arg is an opaque value passed to cb. + */ +typedef int (ossl_qrl_early_rx_validation_cb)(QUIC_PN pn, int pn_space, + void *arg); + +int ossl_qrl_set_early_rx_validation_cb(OSSL_QRL *qrl, + ossl_qrl_early_rx_validation_cb *cb, + void *cb_arg); + +#endif diff --git a/include/internal/quic_record_util.h b/include/internal/quic_record_util.h new file mode 100644 index 0000000000..cc103505c4 --- /dev/null +++ b/include/internal/quic_record_util.h @@ -0,0 +1,62 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_QUIC_RECORD_UTIL_H +# define OSSL_QUIC_RECORD_UTIL_H + +# include <openssl/ssl.h> + +/* + * QUIC Key Derivation Utilities + * ============================= + */ + +/* HKDF-Extract(salt, IKM) (RFC 5869) */ +int ossl_quic_hkdf_extract(OSSL_LIB_CTX *libctx, + const char *propq, + const EVP_MD *md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *out, size_t out_len); + +/* + * QUIC Record Layer Ciphersuite Info + * ================================== + */ + +/* Available QUIC Record Layer (QRL) ciphersuites. */ +# define QRL_SUITE_AES128GCM 1 /* SHA256 */ +# define QRL_SUITE_AES256GCM 2 /* SHA384 */ +# define QRL_SUITE_CHACHA20POLY1305 3 /* SHA256 */ + +/* Returns cipher name in bytes or NULL if suite ID is invalid. */ +const char *ossl_qrl_get_suite_cipher_name(uint32_t suite_id); + +/* Returns hash function name in bytes or NULL if suite ID is invalid. */ +const char *ossl_qrl_get_suite_md_name(uint32_t suite_id); + +/* Returns secret length in bytes or 0 if suite ID is invalid. */ +uint32_t ossl_qrl_get_suite_secret_len(uint32_t suite_id); + +/* Returns key length in bytes or 0 if suite ID is invalid. */ +uint32_t ossl_qrl_get_suite_cipher_key_len(uint32_t suite_id); + +/* Returns IV length in bytes or 0 if suite ID is invalid. */ +uint32_t ossl_qrl_get_suite_cipher_iv_len(uint32_t suite_id); + +/* Returns AEAD auth tag length in bytes or 0 if suite ID is invalid. */ +uint32_t ossl_qrl_get_suite_cipher_tag_len(uint32_t suite_id); + +/* Returns a QUIC_HDR_PROT_CIPHER_* value or 0 if suite ID is invalid. */ +uint32_t ossl_qrl_get_suite_hdr_prot_cipher_id(uint32_t suite_id); + +/* Returns header protection key length in bytes or 0 if suite ID is invalid. */ +uint32_t ossl_qrl_get_suite_hdr_prot_key_len(uint32_t suite_id); + +#endif diff --git a/include/internal/quic_types.h b/include/internal/quic_types.h index bd37019d21..b8b60c5caf 100644 --- a/include/internal/quic_types.h +++ b/include/internal/quic_types.h @@ -11,6 +11,38 @@ # define OSSL_QUIC_TYPES_H # include <openssl/ssl.h> +# include <assert.h> +# include <string.h> + +/* QUIC encryption levels. */ +#define QUIC_ENC_LEVEL_INITIAL 0 +#define QUIC_ENC_LEVEL_HANDSHAKE 1 +#define QUIC_ENC_LEVEL_0RTT 2 +#define QUIC_ENC_LEVEL_1RTT 3 +#define QUIC_ENC_LEVEL_NUM 4 + +/* QUIC packet number spaces. */ +#define QUIC_PN_SPACE_INITIAL 0 +#define QUIC_PN_SPACE_HANDSHAKE 1 +#define QUIC_PN_SPACE_APP 2 +#define QUIC_PN_SPACE_NUM 3 + +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; + } +} /* QUIC packet number spaces. */ #define QUIC_PN_SPACE_INITIAL 0 @@ -32,4 +64,19 @@ static ossl_unused ossl_inline QUIC_PN ossl_quic_pn_min(QUIC_PN a, QUIC_PN b) return a < b ? a : b; } +/* QUIC connection ID representation. */ +#define QUIC_MAX_CONN_ID_LEN 20 + +typedef struct quic_conn_id_st { + unsigned char id_len, id[QUIC_MAX_CONN_ID_LEN]; +} QUIC_CONN_ID; + +static ossl_unused ossl_inline int ossl_quic_conn_id_eq(const QUIC_CONN_ID *a, + const QUIC_CONN_ID *b) +{ + if (a->id_len != b->id_len || a->id_len > QUIC_MAX_CONN_ID_LEN) + return 0; + return memcmp(a->id, b->id, a->id_len) == 0; +} + #endif diff --git a/include/internal/quic_wire.h b/include/internal/quic_wire.h index 1ead6fcf99..704684b0b4 100644 --- a/include/internal/quic_wire.h +++ b/include/internal/quic_wire.h @@ -166,16 +166,10 @@ typedef struct ossl_quic_frame_stop_sending_st { } OSSL_QUIC_FRAME_STOP_SENDING; /* QUIC Frame: NEW_CONNECTION_ID */ -#define OSSL_QUIC_MAX_CONN_ID_LEN 20 -typedef struct ossl_quic_conn_id_st { - unsigned char id_len; /* length of id in bytes */ - unsigned char id[OSSL_QUIC_MAX_CONN_ID_LEN]; -} OSSL_QUIC_CONN_ID; - typedef struct ossl_quic_frame_new_conn_id_st { uint64_t seq_num; uint64_t retire_prior_to; - OSSL_QUIC_CONN_ID conn_id; + QUIC_CONN_ID conn_id; unsigned char stateless_reset_token[16]; } OSSL_QUIC_FRAME_NEW_CONN_ID; diff --git a/include/internal/quic_wire_pkt.h b/include/internal/quic_wire_pkt.h new file mode 100644 index 0000000000..614593b920 --- /dev/null +++ b/include/internal/quic_wire_pkt.h @@ -0,0 +1,435 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_QUIC_WIRE_PKT_H +# define OSSL_QUIC_WIRE_PKT_H + +# include <openssl/ssl.h> +# include "internal/packet.h" +# include "internal/quic_types.h" + +# define QUIC_VERSION_NONE ((uint32_t)0) /* Used for version negotiation */ +# define QUIC_VERSION_1 ((uint32_t)1) /* QUIC v1 */ + +/* QUIC logical packet type. These do not match wire values. */ +# define QUIC_PKT_TYPE_INITIAL 1 +# define QUIC_PKT_TYPE_0RTT 2 +# define QUIC_PKT_TYPE_HANDSHAKE 3 +# define QUIC_PKT_TYPE_RETRY 4 +# define QUIC_PKT_TYPE_1RTT 5 +# define QUIC_PKT_TYPE_VERSION_NEG 6 + +/* + * Smallest possible QUIC packet size as per RFC (aside from version negotiation + * packets). + */ +#define QUIC_MIN_VALID_PKT_LEN_CRYPTO 21 +#define QUIC_MIN_VALID_PKT_LEN_VERSION_NEG 7 +#define QUIC_MIN_VALID_PKT_LEN QUIC_MIN_VALID_PKT_LEN_VERSION_NEG + +typedef struct quic_pkt_hdr_ptrs_st QUIC_PKT_HDR_PTRS; + +/* + * QUIC Packet Header Protection + * ============================= + * + * 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. + */ +typedef struct quic_hdr_protector_st { + OSSL_LIB_CTX *libctx; + const char *propq; + EVP_CIPHER_CTX *cipher_ctx; + EVP_CIPHER *cipher; + uint32_t cipher_id; +} QUIC_HDR_PROTECTOR; + +# define QUIC_HDR_PROT_CIPHER_AES_128 1 +# define QUIC_HDR_PROT_CIPHER_AES_256 2 +# define QUIC_HDR_PROT_CIPHER_CHACHA 3 + +/* + * Initialises a header protector. + * + * cipher_id: + * The header protection cipher method to use. One of + * QUIC_HDR_PROT_CIPHER_*. Must be chosen based on negotiated TLS cipher + * suite. + * + * quic_hp_key: + * This must be the "quic hp" key derived from a traffic secret. + * + * The length of the quic_hp_key must correspond to that expected for the + * given cipher ID. + * + * The header protector performs amortisable initialisation in this function, + * therefore a header protector should be used for as long as possible. + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_hdr_protector_init(QUIC_HDR_PROTECTOR *hpr, + OSSL_LIB_CTX *libctx, + const char *propq, + uint32_t cipher_id, + const unsigned char *quic_hp_key, + size_t quic_hp_key_len); + +/* + * Destroys a header protector. This is also safe to call on a zero-initialized + * 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); + +/* + * Removes header protection from a packet. The packet payload must currently be + * encrypted (i.e., you must remove header protection before decrypting packets + * received). The function examines the header buffer to determine which bytes + * of the header need to be decrypted. + * + * If this function fails, no data is modified. + * + * This is implemented as a call to ossl_quic_hdr_protector_decrypt_fields(). + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_hdr_protector_decrypt(QUIC_HDR_PROTECTOR *hpr, + QUIC_PKT_HDR_PTRS *ptrs); + +/* + * Applies header protection to a packet. The packet payload must already have + * been encrypted (i.e., you must apply header protection after encrypting + * a packet). The function examines the header buffer to determine which bytes + * of the header need to be encrypted. + * + * This is implemented as a call to ossl_quic_hdr_protector_encrypt_fields(). + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_hdr_protector_encrypt(QUIC_HDR_PROTECTOR *hpr, + QUIC_PKT_HDR_PTRS *ptrs); + +/* + * Removes header protection from a packet. The packet payload must currently + * be encrypted. This is a low-level function which assumes you have already + * determined which parts of the packet header need to be decrypted. + * + * sample: + * The range of bytes in the packet to be used to generate the header + * protection mask. It is permissible to set sample_len to the size of the + * remainder of the packet; this function will only use as many bytes as + * needed. If not enough sample bytes are provided, this function fails. + * + * first_byte: + * The first byte of the QUIC packet header to be decrypted. + * + * pn: + * Pointer to the start of the PN field. The caller is responsible + * for ensuring at least four bytes follow this pointer. + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_hdr_protector_decrypt_fields(QUIC_HDR_PROTECTOR *hpr, + const unsigned char *sample, + size_t sample_len, + unsigned char *first_byte, + unsigned char *pn_bytes); + +/* + * Works analogously to ossl_hdr_protector_decrypt_fields, but applies header + * protection instead of removing it. + */ +int ossl_quic_hdr_protector_encrypt_fields(QUIC_HDR_PROTECTOR *hpr, + const unsigned char *sample, + size_t sample_len, + unsigned char *first_byte, + unsigned char *pn_bytes); + +/* + * QUIC Packet Header + * ================== + * + * This structure provides a logical representation of a QUIC packet header. + * + * QUIC packet formats fall into the following categories: + * + * Long Packets, which is subdivided into five possible packet types: + * Version Negotiation (a special case); + * Initial; + * 0-RTT; + * Handshake; and + * Retry + * + * Short Packets, which comprises only a single packet type (1-RTT). + * + * The packet formats vary and common fields are found in some packets but + * not others. The below table indicates which fields are present in which + * kinds of packet. * indicates header protection is applied. + * + * SLLLLL Legend: 1=1-RTT, i=Initial, 0=0-RTT, h=Handshake + * 1i0hrv r=Retry, v=Version Negotiation + * ------ + * 1i0hrv Header Form (0=Short, 1=Long) + * 1i0hr Fixed Bit (always 1) + * 1 Spin Bit + * 1 * Reserved Bits + * 1 * Key Phase + * 1i0h * Packet Number Length + * i0hr? Long Packet Type + * i0h Type-Specific Bits + * i0hr Version (note: always 0 for Version Negotiation packets) + * 1i0hrv Destination Connection ID + * i0hrv Source Connection ID + * 1i0h * Packet Number + * i Token + * i0h Length + * r Retry Token + * r Retry Integrity Tag + * + * For each field below, the conditions under which the field is valid are + * specified. If a field is not currently valid, it is initialized to a zero or + * NULL value. + */ +typedef struct quic_pkt_hdr_st { + /* [ALL] A QUIC_PKT_TYPE_* value. Always valid. */ + unsigned int type :8; + + /* [S] Value of the spin bit. Valid if (type == 1RTT). */ + unsigned int spin_bit :1; + + /* + * [S] Value of the Key Phase bit in the short packet. + * Valid if (type == 1RTT && !partial). + */ + unsigned int key_phase :1; + + /* + * [1i0h] Length of packet number in bytes. This is the decoded value. + * Valid if ((type == 1RTT || (version && type != RETRY)) && !partial). + */ + unsigned int pn_len :4; + + /* + * [ALL] Set to 1 if this is a partial decode because the packet header + * has not yet been deprotected. pn_len, pn and key_phase are not valid if + * this is set. + */ + unsigned int partial :1; + + /* + * [ALL] Whether the fixed bit was set. Note that only Version Negotiation + * packets are allowed to have this unset, so this will always be 1 for all + * other packet types (decode will fail if it is not set). Ignored when + * encoding unless encoding a Version Negotiation packet. + */ + unsigned int fixed :1; + + /* [L] Version field. Valid if (type != 1RTT). */ + uint32_t version; + + /* [ALL] Number of bytes in the connection ID (max 20). Always valid. */ + QUIC_CONN_ID dst_conn_id; + + /* + * [L] Number of bytes in the connection ID (max 20). + * Valid if (type != 1RTT). + */ + QUIC_CONN_ID src_conn_id; + + /* + * [1i0h] Relatively-encoded packet number in raw, encoded form. The correct + * decoding of this value is context-dependent. The number of bytes valid in + * this buffer is determined by pn_len above. If the decode was partial, + * this field is not valid. + * + * Valid if ((type == 1RTT || (version && type != RETRY)) && !partial). + */ + unsigned char pn[4]; + + /* + * [i] Token field in Initial packet. Points to memory inside the decoded + * PACKET, and therefore is valid for as long as the PACKET's buffer is + * valid. token_len is the length of the token in bytes. + * + * Valid if (type == INITIAL). + */ + const unsigned char *token; + size_t token_len; + + /* + * [i0h] Payload length in bytes. + * + * Valid if (type != 1RTT && type != RETRY && version). + */ + size_t len; + + /* + * Pointer to start of payload data in the packet. Points to memory inside + * the decoded PACKET, and therefore is valid for as long as the PACKET'S + * buffer is valid. The length of the buffer in bytes is in len above. + * + * For Version Negotiation packets, points to the array of supported + * versions. + * + * For Retry packets, points to the Retry packet payload, which comprises + * the Retry Token followed by a 16-byte Retry Integrity Tag. + * + * Regardless of whether a packet is a Version Negotiation packet (where the + * payload contains a list of supported versions), a Retry packet (where the + * payload contains a Retry Token and Retry Integrity Tag), or any other + * packet type (where the payload contains frames), the payload is not + * validated and the user must parse the payload bearing this in mind. + * + * If the decode was partial (partial is set), this points to the start of + * the packet number field, rather than the protected payload, as the length + * of the packet number field is unknown. The len field reflects this in + * this case (i.e., the len field is the number of payload bytes plus the + * number of bytes comprising the PN). + */ + const unsigned char *data; +} QUIC_PKT_HDR; + +/* + * Extra information which can be output by the packet header decode functions + * for the assistance of the header protector. This avoids the header protector + * needing to partially re-decode the packet header. + */ +struct quic_pkt_hdr_ptrs_st { + unsigned char *raw_start; /* start of packet */ + unsigned char *raw_sample; /* start of sampling range */ + size_t raw_sample_len; /* maximum length of sampling range */ + + /* + * Start of PN field. Guaranteed to be NULL unless at least four bytes are + * available via this pointer. + */ + unsigned char *raw_pn; +}; + +/* + * If partial is 1, reads the unprotected parts of a protected packet header + * from a PACKET, performing a partial decode. + * + * If partial is 0, the input is assumed to have already had header protection + * removed, and all header fields are decoded. + * + * On success, the logical decode of the packet header is written to *hdr. + * hdr->partial is set or cleared according to whether a partial decode was + * performed. *ptrs is filled with pointers to various parts of the packet + * buffer. + * + * In order to decode short packets, the connection ID length being used must be + * known contextually, and should be passed as short_conn_id_len. If + * short_conn_id_len is set to an invalid value (a value greater than + * QUIC_MAX_CONN_ID_LEN), this function fails when trying to decode a short + * packet, but succeeds for long packets. + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_wire_decode_pkt_hdr(PACKET *pkt, + size_t short_conn_id_len, + int partial, + QUIC_PKT_HDR *hdr, + QUIC_PKT_HDR_PTRS *ptrs); + +/* + * Encodes a packet header. The packet is written to pkt. + * + * The length of the (encrypted) packet payload should be written to hdr->len + * and will be placed in the serialized packet header. The payload data itself + * is not copied; the caller should write hdr->len bytes of encrypted payload to + * the WPACKET immediately after the call to this function. However, + * WPACKET_reserve_bytes is called for the payload size. + * + * This function does not apply header protection. You must apply header + * protection yourself after calling this function. *ptrs is filled with + * pointers which can be passed to a header protector, but this must be + * performed after the encrypted payload is written. + * + * The pointers in *ptrs are direct pointers into the WPACKET buffer. If more + * data is written to the WPACKET buffer, WPACKET buffer reallocations may + * occur, causing these pointers to become invalid. Therefore, you must not call + * any write WPACKET function between this call and the call to + * ossl_quic_hdr_protector_encrypt. This function calls WPACKET_reserve_bytes + * for the payload length, so you may assume hdr->len bytes are already free to + * write at the WPACKET cursor location once this function returns successfully. + * It is recommended that you call this function, write the encrypted payload, + * call ossl_quic_hdr_protector_encrypt, and then call + * WPACKET_allocate_bytes(hdr->len). + * + * Version Negotiation and Retry packets do not use header protection; for these + * header types, the fields in *ptrs are all written as zero. Version + * Negotiation, Retry and 1-RTT packets do not contain a Length field, but + * hdr->len bytes of data are still reserved in the WPACKET. + * + * If serializing a short packet and short_conn_id_len does not match the DCID + * specified in hdr, the function fails. + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_wire_encode_pkt_hdr(WPACKET *pkt, + size_t short_conn_id_len, + const QUIC_PKT_HDR *hdr, + QUIC_PKT_HDR_PTRS *ptrs); + +/* + * Retrieves only the DCID from a packet header. This is intended for demuxer + * use. It avoids the need to parse the rest of the packet header twice. + * + * Information on packet length is not decoded, as this only needs to be used on + * the first packet in a datagram, therefore this takes a buffer and not a + * PACKET. + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_wire_get_pkt_hdr_dst_conn_id(const unsigned char *buf, + size_t buf_len, + size_t short_conn_id_len, + QUIC_CONN_ID *dst_conn_id); + +/* + * Packet Number Encoding + * ====================== + */ + +/* + * Decode an encoded packet header QUIC PN. + * + * enc_pn is the raw encoded PN to decode. enc_pn_len is its length in bytes as + * indicated by packet headers. largest_pn is the largest PN successfully + * processed in the relevant PN space. + * + * The resulting PN is written to *res_pn. + * + * Returns 1 on success or 0 on failure. + */ +int ossl_quic_wire_decode_pkt_hdr_pn(const unsigned char *enc_pn, + size_t enc_pn_len, + QUIC_PN largest_pn, + QUIC_PN *res_pn); + +/* + * Determine how many bytes should be used to encode a PN. Returns the number of + * bytes (which will be in range [1, 4]). + */ +int ossl_quic_wire_determine_pn_len(QUIC_PN pn, QUIC_PN largest_acked); + +/* + * Encode a PN for a packet header using the specified number of bytes, which + * should have been determined by calling ossl_quic_wire_determine_pn_len. The + * PN encoding process is done in two parts to allow the caller to override PN + * encoding length if it wishes. + * + * Returns 1 on success and 0 on failure. + */ +int ossl_quic_wire_encode_pkt_hdr_pn(QUIC_PN pn, + unsigned char *enc_pn, + size_t enc_pn_len); +#endif diff --git a/ssl/quic/build.info b/ssl/quic/build.info index 9e411011e7..482338be95 100644 --- a/ssl/quic/build.info +++ b/ssl/quic/build.info @@ -1,3 +1,3 @@ $LIBSSL=../../libssl -SOURCE[$LIBSSL]=quic_method.c quic_impl.c quic_wire.c quic_ackm.c quic_statm.c cc_dummy.c +SOURCE[$LIBSSL]=quic_method.c quic_impl.c quic_wire.c quic_ackm.c quic_statm.c cc_dummy.c quic_demux.c quic_record.c quic_record_util.c quic_wire_pkt.c diff --git a/ssl/quic/quic_demux.c b/ssl/quic/quic_demux.c new file mode 100644 index 0000000000..3eb4f6dfb4 --- /dev/null +++ b/ssl/quic/quic_demux.c @@ -0,0 +1,523 @@ +#include "internal/quic_demux.h" +#include "internal/quic_wire_pkt.h" +#include "internal/common.h" +#include <openssl/lhash.h> + +#define OSSL_QUIC_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)); + + if (e->prev != NULL) + e->prev->next = e->next; + if (e->next != NULL) + e->next->prev = e->prev; + + if (e == l->head) + l->head = e->next; + if (e == l->tail) + l->tail = e->prev; + + e->next = e->prev = NULL; +} + +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); + + if (l->head == NULL) { + l->head = l->tail = e; + e->next = e->prev = NULL; + return; + } + + l->head->prev = e; + e->next = l->head; + e->prev = NULL; + l->head = 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); + + if (l->tail == NULL) { + l->head = l->tail = e; + e->next = e->prev = NULL; + return; + } + + l->tail->next = e; + e->prev = l->tail; + e->next = NULL; + l->tail = e; +} + +/* Structure used to track a given connection ID. */ +typedef struct quic_demux_conn_st QUIC_DEMUX_CONN; + +struct quic_demux_conn_st { + QUIC_DEMUX_CONN *next; /* used when unregistering only */ + QUIC_CONN_ID dst_conn_id; + ossl_quic_demux_cb_fn *cb; + void *cb_arg; +}; + +DEFINE_LHASH_OF_EX(QUIC_DEMUX_CONN); + +static unsigned long demux_conn_hash(const QUIC_DEMUX_CONN *conn) +{ + size_t i; + unsigned long v = 0; + + assert(conn->dst_conn_id.id_len <= QUIC_MAX_CONN_ID_LEN); + + for (i = 0; i < conn->dst_conn_id.id_len; ++i) + v ^= ((unsigned long)conn->dst_conn_id.id[i]) + << ((i * 8) % (sizeof(unsigned long) * 8)); + + return v; +} + +static int demux_conn_cmp(const QUIC_DEMUX_CONN *a, const QUIC_DEMUX_CONN *b) +{ + return !ossl_quic_conn_id_eq(&a->dst_conn_id, &b->dst_conn_id); +} + +struct quic_demux_st { + /* The underlying transport BIO with datagram semantics. */ + BIO *net_bio; + + /* + * QUIC short packets do not contain the length of the connection ID field, + * therefore it must be known contextually. The demuxer requires connection + * IDs of the same length to be used for all incoming packets. + */ + size_t short_conn_id_len; + + /* Default URXE buffer size in bytes. */ + size_t default_urxe_alloc_len; + + /* Hashtable mapping connection IDs to QUIC_DEMUX_CONN structures. */ + LHASH_OF(QUIC_DEMUX_CONN) *conns_by_id; + + /* + * List of URXEs which are not currently in use (i.e., not filled with + * unconsumed data). These are moved to the pending list as they are filled. + */ + QUIC_URXE_LIST urx_free; + size_t num_urx_free; + + /* + * List of URXEs which are filled with received encrypted data. These are + * removed from this list as we invoke the callbacks for each of them. They + * are then not on any list managed by us; we forget about them until our + * user calls ossl_quic_demux_release_urxe to return the URXE to us, at + * which point we add it to the free list. + */ + QUIC_URXE_LIST urx_pending; + + /* Whether to use local address support. */ + char use_local_addr; +}; + +QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio, + size_t short_conn_id_len, + size_t default_urxe_alloc_len) +{ + QUIC_DEMUX *demux; + + demux = OPENSSL_zalloc(sizeof(QUIC_DEMUX)); + if (demux == NULL) + return NULL; + + demux->net_bio = net_bio; + demux->short_conn_id_len = short_conn_id_len; + demux->default_urxe_alloc_len = default_urxe_alloc_len; + + demux->conns_by_id + = lh_QUIC_DEMUX_CONN_new(demux_conn_hash, demux_conn_cmp); + if (demux->conns_by_id == NULL) { + OPENSSL_free(demux); + return NULL; + } + + if (net_bio != NULL + && BIO_dgram_get_local_addr_cap(net_bio) + && BIO_dgram_set_local_addr_enable(net_bio, 1)) + demux->use_local_addr = 1; + + return demux; +} + +static void demux_free_conn_it(QUIC_DEMUX_CONN *conn, void *arg) +{ + OPENSSL_free(conn); +} + +static void demux_free_urxl(QUIC_URXE_LIST *l) +{ + QUIC_URXE *e, *enext; + + for (e = l->head; e != NULL; e = enext) { + enext = e->next; + OPENSSL_free(e); + } + + l->head = l->tail = NULL; +} + +void ossl_quic_demux_free(QUIC_DEMUX *demux) +{ + if (demux == NULL) + return; + + /* Free all connection structures. */ + lh_QUIC_DEMUX_CONN_doall_arg(demux->conns_by_id, demux_free_conn_it, NULL); + lh_QUIC_DEMUX_CONN_free(demux->conns_by_id); + + /* Free all URXEs we are holding. */ + demux_free_urxl(&demux->urx_free); + demux_free_urxl(&demux->urx_pending); + + OPENSSL_free(demux); +} + +static QUIC_DEMUX_CONN *demux_get_by_conn_id(QUIC_DEMUX *demux, + const QUIC_CONN_ID *dst_conn_id) +{ + QUIC_DEMUX_CONN key; + + if (dst_conn_id->id_len > QUIC_MAX_CONN_ID_LEN) + return 0; + + key.dst_conn_id = *dst_conn_id; + return lh_QUIC_DEMUX_CONN_retrieve(demux->conns_by_id, &key); +} + +int ossl_quic_demux_register(QUIC_DEMUX *demux, + const QUIC_CONN_ID *dst_conn_id, + ossl_quic_demux_cb_fn *cb, void *cb_arg) +{ + QUIC_DEMUX_CONN *conn; + + if (dst_conn_id == NULL + || dst_conn_id->id_len > QUIC_MAX_CONN_ID_LEN + || cb == NULL) + return 0; + + /* Ensure not already registered. */ + if (demux_get_by_conn_id(demux, dst_conn_id) != NULL) + /* Handler already registered with this connection ID. */ + return 0; + + conn = OPENSSL_zalloc(sizeof(QUIC_DEMUX_CONN)); + if (conn == NULL) + return 0; + + conn->dst_conn_id = *dst_conn_id; + conn->cb = cb; + conn->cb_arg = cb_arg; + + lh_QUIC_DEMUX_CONN_insert(demux->conns_by_id, conn); + return 1; +} + +static void demux_unregister(QUIC_DEMUX *demux, + QUIC_DEMUX_CONN *conn) +{ + lh_QUIC_DEMUX_CONN_delete(demux->conns_by_id, conn); + OPENSSL_free(conn); +} + +int ossl_quic_demux_unregister(QUIC_DEMUX *demux, + const QUIC_CONN_ID *dst_conn_id) +{ + QUIC_DEMUX_CONN *conn; + + if (dst_conn_id == NULL + || dst_conn_id->id_len > QUIC_MAX_CONN_ID_LEN) + return 0; + + conn = demux_get_by_conn_id(demux, dst_conn_id); + if (conn == NULL) + return 0; + + demux_unregister(demux, conn); + return 1; +} + +struct unreg_arg { + ossl_quic_demux_cb_fn *cb; + void *cb_arg; + QUIC_DEMUX_CONN *head; +}; + +static void demux_unregister_by_cb(QUIC_DEMUX_CONN *conn, void *arg_) +{ + struct unreg_arg *arg = arg_; + + if (conn->cb == arg->cb && conn->cb_arg == arg->cb_arg) { + conn->next = arg->head; + arg->head = conn; + } +} + +void ossl_quic_demux_unregister_by_cb(QUIC_DEMUX *demux, + ossl_quic_demux_cb_fn *cb, + void *cb_arg) +{ + QUIC_DEMUX_CONN *conn, *cnext; + struct unreg_arg arg = {0}; + arg.cb = cb; + arg.cb_arg = cb_arg; + + lh_QUIC_DEMUX_CONN_doall_arg(demux->conns_by_id, + demux_unregister_by_cb, &arg); + + for (conn = arg.head; conn != NULL; conn = cnext) { + cnext = conn->next; + demux_unregister(demux, conn); + } +} + +static QUIC_URXE *demux_alloc_urxe(size_t alloc_len) +{ + QUIC_URXE *e; + + if (alloc_len >= SIZE_MAX - sizeof(QUIC_URXE)) + return NULL; + + e = OPENSSL_malloc(sizeof(QUIC_URXE) + alloc_len); + if (e == NULL) + return NULL; + + e->prev = e->next = NULL; + e->alloc_len = alloc_len; + e->data_len = 0; + return e; +} + +static int demux_ensure_free_urxe(QUIC_DEMUX *demux, size_t min_num_free) +{ + QUIC_URXE *e; + + while (demux->num_urx_free < min_num_free) { + e = demux_alloc_urxe(demux->default_urxe_alloc_len); + if (e == NULL) + return 0; + + ossl_quic_urxe_insert_tail(&demux->urx_free, e); + ++demux->num_urx_free; + } + + return 1; +} + +/* + * Receive datagrams from network, placing them into URXEs. + * + * Returns 1 on success or 0 on failure. + * + * Precondition: at least one URXE is free + * Precondition: there are no pending URXEs + */ +static int demux_recv(QUIC_DEMUX *demux) +{ + BIO_MSG msg[OSSL_QUIC_DEMUX_MAX_MSGS_PER_CALL]; + ossl_ssize_t rd, i; + QUIC_URXE *urxe = demux->urx_free.head, *unext; + + /* This should never be called when we have any pending URXE. */ + assert(demux->urx_pending.head == NULL); + + if (demux->net_bio == NULL) + return 0; + + /* + * Opportunistically receive as many messages as possible in a single + * syscall, determined by how many free URXEs are available. + */ + for (i = 0; i < (ossl_ssize_t)OSSL_NELEM(msg); ++i, urxe = urxe->next) { + if (urxe == NULL) { + /* We need at least one URXE to receive into. */ + if (!ossl_assert(i > 0)) + return 0; + + break; + } + + /* Ensure we zero any fields added to BIO_MSG at a later date. */ + memset(&msg[i], 0, sizeof(BIO_MSG)); + msg[i].data = ossl_quic_urxe_data(urxe); + msg[i].data_len = urxe->alloc_len; + msg[i].peer = &urxe->peer; + if (demux->use_local_addr) + msg[i].local = &urxe->local; + else + BIO_ADDR_clear(&urxe->local); + } + + rd = BIO_recvmmsg(demux->net_bio, msg, sizeof(BIO_MSG), i, 0); + if (rd <= 0) + return 0; + + 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; + /* Move from free list to pending list. */ + ossl_quic_urxe_remove(&demux->urx_free, urxe); + --demux->num_urx_free; + ossl_quic_urxe_insert_tail(&demux->urx_pending, urxe); + } + + return 1; +} + +/* Extract destination connection ID from the first packet in a datagram. */ +static int demux_identify_conn_id(QUIC_DEMUX *demux, + QUIC_URXE *e, + QUIC_CONN_ID *dst_conn_id) +{ + return ossl_quic_wire_get_pkt_hdr_dst_conn_id(ossl_quic_urxe_data(e), + e->data_len, + demux->short_conn_id_len, + dst_conn_id); +} + +/* Identify the connection structure corresponding to a given URXE. */ +static QUIC_DEMUX_CONN *demux_identify_conn(QUIC_DEMUX *demux, QUIC_URXE *e) +{ + QUIC_CONN_ID dst_conn_id; + + if (!demux_identify_conn_id(demux, e, &dst_conn_id)) + /* + * Datagram is so badly malformed we can't get the DCID from the first + * packet in it, so just give up. + */ + return NULL; + + return demux_get_by_conn_id(demux, &dst_conn_id); +} + +/* Process a single pending URXE. */ +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); + + conn = demux_identify_conn(demux, e); + if (conn == NULL) { + /* + * We could not identify a connection. We will never be able to process + * this datagram, so get rid of it. + */ + ossl_quic_urxe_remove(&demux->urx_pending, e); + ossl_quic_urxe_insert_tail(&demux->urx_free, e); + ++demux->num_urx_free; + return 1; /* keep processing pending URXEs */ + } + + /* + * Remove from list and invoke callback. The URXE now belongs to the + * callback. (QUIC_DEMUX_CONN never has non-NULL cb.) + */ + ossl_quic_urxe_remove(&demux->urx_pending, e); + conn->cb(e, conn->cb_arg); + return 1; +} + +/* Process pending URXEs to generate callbacks. */ +static int demux_process_pending_urxl(QUIC_DEMUX *demux) +{ + QUIC_URXE *e; + + while ((e = demux->urx_pending.head) != NULL) + if (!demux_process_pending_urxe(demux, e)) + return 0; + + return 1; +} + +/* + * Drain the pending URXE list, processing any pending URXEs by making their + * callbacks. If no URXEs are pending, a network read is attempted first. + */ +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); + if (ret != 1) + return 0; + + ret = demux_recv(demux); + if (ret != 1) + return 0; + + /* + * If demux_recv returned successfully, we should always have something. + */ + assert(demux->urx_pending.head != NULL); + } + + return demux_process_pending_urxl(demux); +} + +/* Artificially inject a packet into the demuxer for testing purposes. */ +int ossl_quic_demux_inject(QUIC_DEMUX *demux, + const unsigned char *buf, + size_t buf_len, + const BIO_ADDR *peer, + const BIO_ADDR *local) +{ + int ret; + QUIC_URXE *urxe; + + ret = demux_ensure_free_urxe(demux, 1); + if (ret != 1) + return 0; + + urxe = demux->urx_free.head; + if (buf_len > urxe->alloc_len) + return 0; + + memcpy(ossl_quic_urxe_data(urxe), buf, buf_len); + urxe->data_len = buf_len; + + if (peer != NULL) + urxe->peer = *peer; + else + BIO_ADDR_clear(&urxe->local); + + if (local != NULL) + urxe->local = *local; + else + BIO_ADDR_clear(&urxe->local); + + /* Move from free list to pending list. */ + ossl_quic_urxe_remove(&demux->urx_free, urxe); + --demux->num_urx_free; + ossl_quic_urxe_insert_tail(&demux->urx_pending, urxe); + + return demux_process_pending_urxl(demux); +} + +/* Called by our user to return a URXE to the free list. */ +void ossl_quic_demux_release_urxe(QUIC_DEMUX *demux, + QUIC_URXE *e) +{ + OPENSSL_assert(e->prev == NULL && e->next == NULL); + ossl_quic_urxe_insert_tail(&demux->urx_free, e); + ++demux->num_urx_free; +} diff --git a/ssl/quic/quic_record.c b/ssl/quic/quic_record.c new file mode 100644 index 0000000000..95044d2836 --- /dev/null +++ b/ssl/quic/quic_record.c @@ -0,0 +1,1315 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/quic_record.h" +#include "internal/common.h" +#include "../ssl_local.h" + +/* + * Mark a packet in a bitfield. + * + * pkt_idx: index of packet within datagram. + */ +static ossl_inline void pkt_mark(uint64_t *bitf, size_t pkt_idx) +{ + assert(pkt_idx < QUIC_MAX_PKT_PER_URXE); + *bitf |= ((uint64_t)1) << pkt_idx; +} + +/* Returns 1 if a packet is in the bitfield. */ +static ossl_inline int pkt_is_marked(const uint64_t *bitf, size_t pkt_idx) +{ + assert(pkt_idx < QUIC_MAX_PKT_PER_URXE); + return (*bitf & (((uint64_t)1) << pkt_idx)) != 0; +} + +/* + * RXE + * === + * + * RX Entries (RXEs) store processed (i.e., decrypted) data received from the + * network. One RXE is used per received QUIC packet. + */ +typedef struct rxe_st RXE; + +struct rxe_st { + RXE *prev, *next; + size_t data_len, alloc_len; + + /* Extra fields for per-packet information. */ + QUIC_PKT_HDR hdr; /* data/len are decrypted payload */ + + /* Decoded packet number. */ + QUIC_PN pn; + + /* Addresses copied from URXE. */ + BIO_ADDR peer, local; + + /* Total length of the datagram which contained this packet. */ + size_t datagram_len; +}; + +typedef struct ossl_qrl_rxe_list_st { + RXE *head, *tail; +} RXE_LIST; + +static ossl_inline unsigned char *rxe_data(const RXE *e) +{ + return (unsigned char *)(e + 1); +} + +static void rxe_remove(RXE_LIST *l, RXE *e) +{ + if (e->prev != NULL) + e->prev->next = e->next; + if (e->next != NULL) + e->next->prev = e->prev; + + if (e == l->head) + l->head = e->next; + if (e == l->tail) + l->tail = e->prev; + + e->next = e->prev = NULL; +} + +static void rxe_insert_tail(RXE_LIST *l, RXE *e) +{ + if (l->tail == NULL) { + l->head = l->tail = e; + e->next = e->prev = NULL; + return; + } + + l->tail->next = e; + e->prev = l->tail; + e->next = NULL; + l->tail = e; +} + +/* + * QRL + * === + */ + +/* (Encryption level, direction)-specific 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; +} OSSL_QRL_ENC_LEVEL; + +struct ossl_qrl_st { + OSSL_LIB_CTX *libctx; + const char *propq; + + /* Demux to receive datagrams from. */ + QUIC_DEMUX *rx_demux; + + /* Length of connection IDs used in short-header packets in bytes. */ + size_t short_conn_id_len; + + /* + * List of URXEs which are filled with received encrypted data. + * These are returned to the DEMUX's free list as they are processed. + */ + QUIC_URXE_LIST urx_pending; + + /* + * List of URXEs which we could not decrypt immediately and which are being + * kept in case they can be decrypted later. + */ + QUIC_URXE_LIST urx_deferred; + + /* + * List of RXEs which are not currently in use. These are moved + * to the pending list as they are filled. + */ + RXE_LIST rx_free; + + /* + * List of RXEs which are filled with decrypted packets ready to be passed + * to the user. A RXE is removed from all lists inside the QRL when passed + * to the user, then returned to the free list when the user returns it. + */ + RXE_LIST rx_pending; + + /* Largest PN we have received and processed in a given PN space. */ + QUIC_PN rx_largest_pn[QUIC_PN_SPACE_NUM]; + + /* Per encryption-level state. */ + OSSL_QRL_ENC_LEVEL rx_el[QUIC_ENC_LEVEL_NUM]; + OSSL_QRL_ENC_LEVEL tx_el[QUIC_ENC_LEVEL_NUM]; + + /* Bytes we have received since this counter was last cleared. */ + uint64_t bytes_received; + + /* Validation callback. */ + ossl_qrl_early_rx_validation_cb *rx_validation_cb; + void *rx_validation_cb_arg; +}; + +static void qrl_on_rx(QUIC_URXE *urxe, void *arg); + +/* Constants used for key derivation in QUIC v1. */ +static const unsigned char quic_client_in_label[] = { + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e /* "client in" */ +}; +static const unsigned char quic_server_in_label[] = { + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x69, 0x6e /* "server in" */ +}; +static const unsigned char quic_v1_iv_label[] = { + 0x71, 0x75, 0x69, 0x63, 0x20, 0x69, 0x76 /* "quic iv" */ +}; +static const unsigned char quic_v1_key_label[] = { + 0x71, 0x75, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79 /* "quic key" */ +}; +static const unsigned char quic_v1_hp_label[] = { + 0x71, 0x75, 0x69, 0x63, 0x20, 0x68, 0x70 /* "quic hp" */ +}; +/* Salt used to derive Initial packet protection keys (RFC 9001 Section 5.2). */ +static const unsigned char quic_v1_initial_salt[] = { + 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, + 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a +}; + +static ossl_inline OSSL_QRL_ENC_LEVEL *qrl_get_el(OSSL_QRL *qrl, + uint32_t enc_level, + int is_tx) +{ + if (!ossl_assert(enc_level < QUIC_ENC_LEVEL_NUM)) + return NULL; + return is_tx ? &qrl->tx_el[enc_level] : &qrl->rx_el[enc_level]; +} + +/* + * 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. + */ +static int qrl_have_el(OSSL_QRL *qrl, uint32_t enc_level, int is_tx) +{ + OSSL_QRL_ENC_LEVEL *el = qrl_get_el(qrl, enc_level, is_tx); + + if (el->cctx != NULL) + return 1; + if (el->discarded) + return -1; + return 0; +} + +/* Drops keying material for a given encryption level. */ +static void qrl_el_discard(OSSL_QRL *qrl, uint32_t enc_level, + int is_tx, int final) +{ + OSSL_QRL_ENC_LEVEL *el = qrl_get_el(qrl, enc_level, is_tx); + + if (el->discarded) + return; + + if (el->cctx != NULL) { + ossl_quic_hdr_protector_destroy(&el->hpr); + + EVP_CIPHER_CTX_free(el->cctx); + el->cctx = NULL; + + EVP_MD_free(el->md); + el->md = NULL; + } + + /* Zeroise IV. */ + OPENSSL_cleanse(el->iv, sizeof(el->iv)); + + if (final) + el->discarded = 1; +} + +/* + * 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. + */ +static int qrl_el_set_secret(OSSL_QRL *qrl, uint32_t enc_level, + uint32_t suite_id, EVP_MD *md, + int is_tx, + const unsigned char *secret, + size_t secret_len) +{ + OSSL_QRL_ENC_LEVEL *el = qrl_get_el(qrl, enc_level, is_tx); + 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; + + if (el->discarded) + /* Should not be trying to reinitialise an EL which was discarded. */ + 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); + 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)) + return 0; + + if (md == NULL) { + md_name = ossl_qrl_get_suite_md_name(suite_id); + + if ((md = EVP_MD_fetch(qrl->libctx, + md_name, qrl->propq)) == NULL) + return 0; + + own_md = 1; + } + + /* Derive "quic iv" key. */ + if (!tls13_hkdf_expand_ex(qrl->libctx, qrl->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(qrl->libctx, qrl->propq, + md, + secret, + quic_v1_key_label, + sizeof(quic_v1_key_label), + NULL, 0, + key, key_len, 0)) + goto err; + + /* Derive "quic hp" key. */ + if (!tls13_hkdf_expand_ex(qrl->libctx, qrl->propq, + md, + secret, + quic_v1_hp_label, + sizeof(quic_v1_hp_label), + NULL, 0, + 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 header protection context. */ + if (!ossl_quic_hdr_protector_init(&el->hpr, + qrl->libctx, + qrl->propq, + ossl_qrl_get_suite_hdr_prot_cipher_id(suite_id), + hpr_key, + hpr_key_len)) + goto err; + + have_hpr = 1; + + /* Create and initialise cipher context. */ + if ((cipher = EVP_CIPHER_fetch(qrl->libctx, cipher_name, + qrl->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 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); + + /* Zeroize intermediate keys. */ + OPENSSL_cleanse(key, sizeof(key)); + OPENSSL_cleanse(hpr_key, sizeof(hpr_key)); + EVP_CIPHER_free(cipher); + return 1; + +err: + if (have_hpr) + ossl_quic_hdr_protector_destroy(&el->hpr); + EVP_CIPHER_CTX_free(cctx); + EVP_CIPHER_free(cipher); + if (own_md) + EVP_MD_free(md); + return 0; +} + +OSSL_QRL *ossl_qrl_new(const OSSL_QRL_ARGS *args) +{ + OSSL_QRL *qrl; + size_t i; + + if (args->rx_demux == NULL) + return 0; + + qrl = OPENSSL_zalloc(sizeof(OSSL_QRL)); + if (qrl == NULL) + return 0; + + for (i = 0; i < OSSL_NELEM(qrl->rx_largest_pn); ++i) + qrl->rx_largest_pn[i] = args->rx_init_largest_pn[i]; + + qrl->libctx = args->libctx; + qrl->propq = args->propq; + qrl->rx_demux = args->rx_demux; + qrl->short_conn_id_len = args->short_conn_id_len; + return qrl; +} + +static void qrl_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 qrl_cleanup_urxl(OSSL_QRL *qrl, QUIC_URXE_LIST *l) +{ + QUIC_URXE *e, *enext; + for (e = l->head; e != NULL; e = enext) { + enext = e->next; + ossl_quic_demux_release_urxe(qrl->rx_demux, e); + } + l->head = l->tail = NULL; +} + +void ossl_qrl_free(OSSL_QRL *qrl) +{ + uint32_t i; + + /* Unregister from the RX DEMUX. */ + ossl_quic_demux_unregister_by_cb(qrl->rx_demux, qrl_on_rx, qrl); + + /* Free RXE queue data. */ + qrl_cleanup_rxl(&qrl->rx_free); + qrl_cleanup_rxl(&qrl->rx_pending); + qrl_cleanup_urxl(qrl, &qrl->urx_pending); + qrl_cleanup_urxl(qrl, &qrl->urx_deferred); + + /* Drop keying material and crypto resources. */ + for (i = 0; i < QUIC_ENC_LEVEL_NUM; ++i) { + qrl_el_discard(qrl, i, 0, 1); + qrl_el_discard(qrl, i, 1, 1); + } + + OPENSSL_free(qrl); +} + +static void qrl_on_rx(QUIC_URXE *urxe, void *arg) +{ + OSSL_QRL *qrl = arg; + + /* Initialize our own fields inside the URXE and add to the pending list. */ + urxe->processed = 0; + urxe->hpr_removed = 0; + ossl_quic_urxe_insert_tail(&qrl->urx_pending, urxe); +} + +int ossl_qrl_add_dst_conn_id(OSSL_QRL *qrl, + const QUIC_CONN_ID *dst_conn_id) +{ + return ossl_quic_demux_register(qrl->rx_demux, + dst_conn_id, + qrl_on_rx, + qrl); +} + +int ossl_qrl_remove_dst_conn_id(OSSL_QRL *qrl, + const QUIC_CONN_ID *dst_conn_id) +{ + return ossl_quic_demux_unregister(qrl->rx_demux, dst_conn_id); +} + +static void qrl_requeue_deferred(OSSL_QRL *qrl) +{ + QUIC_URXE *e; + + while ((e = qrl->urx_deferred.head) != NULL) { + ossl_quic_urxe_remove(&qrl->urx_deferred, e); + ossl_quic_urxe_insert_head(&qrl->urx_pending, e); + } +} + +int ossl_qrl_provide_rx_secret(OSSL_QRL *qrl, uint32_t enc_level, + uint32_t suite_id, + const unsigned char *secret, size_t secret_len) +{ + if (enc_level == QUIC_ENC_LEVEL_INITIAL || enc_level >= QUIC_ENC_LEVEL_NUM) + return 0; + + if (!qrl_el_set_secret(qrl, enc_level, suite_id, NULL, + /*is_tx=*/0, secret, secret_len)) + return 0; + + /* + * Any packets we previously could not decrypt, we may now be able to + * decrypt, so move any datagrams containing deferred packets from the + * deferred to the pending queue. + */ + qrl_requeue_deferred(qrl); + return 1; +} + +/* Initialise key material for the INITIAL encryption level. */ +int ossl_qrl_provide_rx_secret_initial(OSSL_QRL *qrl, + const QUIC_CONN_ID *dst_conn_id) +{ + unsigned char initial_secret[32]; + unsigned char client_initial_secret[32], server_initial_secret[32]; + EVP_MD *sha256; + int have_rx = 0; + + /* Initial encryption always uses SHA-256. */ + if ((sha256 = EVP_MD_fetch(qrl->libctx, + "SHA256", qrl->propq)) == NULL) + return 0; + + /* Derive initial secret from destination connection ID. */ + if (!ossl_quic_hkdf_extract(qrl->libctx, qrl->propq, + sha256, + quic_v1_initial_salt, + sizeof(quic_v1_initial_salt), + dst_conn_id->id, + dst_conn_id->id_len, + initial_secret, + sizeof(initial_secret))) + goto err; + + /* Derive "client in" secret. */ + if (!tls13_hkdf_expand_ex(qrl->libctx, qrl->propq, + sha256, + initial_secret, + quic_client_in_label, + sizeof(quic_client_in_label), + NULL, 0, + client_initial_secret, + sizeof(client_initial_secret), 0)) + goto err; + + /* Derive "server in" secret. */ + if (!tls13_hkdf_expand_ex(qrl->libctx, qrl->propq, + sha256, + initial_secret, + quic_server_in_label, + sizeof(quic_server_in_label), + NULL, 0, + server_initial_secret, + sizeof(server_initial_secret), 0)) + goto err; + + /* Setup RX cipher. Initial encryption always uses AES-128-GCM. */ + if (!qrl_el_set_secret(qrl, QUIC_ENC_LEVEL_INITIAL, + QRL_SUITE_AES128GCM, + sha256, + /*is_tx=*/0, + server_initial_secret, + sizeof(server_initial_secret))) + goto err; + + have_rx = 1; + + /* + * qrl_el_set_secret takes ownership of our ref to SHA256, so get a new ref + * for the following call for the TX side. + */ + if (!EVP_MD_up_ref(sha256)) { + sha256 = NULL; + goto err; + } + + /* Setup TX cipher. */ + if (!qrl_el_set_secret(qrl, QUIC_ENC_LEVEL_INITIAL, + QRL_SUITE_AES128GCM, + sha256, + /*is_tx=*/1, + client_initial_secret, + sizeof(client_initial_secret))) + goto err; + + /* + * Any packets we previously could not decrypt, we may now be able to + * decrypt, so move any datagrams containing deferred packets from the + * deferred to the pending queue. + */ + qrl_requeue_deferred(qrl); + return 1; + +err: + if (have_rx) + qrl_el_discard(qrl, QUIC_ENC_LEVEL_INITIAL, /*is_tx=*/0, 0); + + EVP_MD_free(sha256); + return 0; +} + +int ossl_qrl_discard_enc_level(OSSL_QRL *qrl, uint32_t enc_level) +{ + if (enc_level >= QUIC_ENC_LEVEL_NUM) + return 0; + + qrl_el_discard(qrl, enc_level, 0, 1); + return 1; +} + +/* Returns 1 if there are one or more pending RXEs. */ +int ossl_qrl_processed_read_pending(OSSL_QRL *qrl) +{ + return qrl->rx_pending.head != NULL; +} + +/* Returns 1 if there are yet-unprocessed packets. */ +int ossl_qrl_unprocessed_read_pending(OSSL_QRL *qrl) +{ + return qrl->urx_pending.head != NULL || qrl->urx_deferred.head != NULL; +} + +/* Pop the next pending RXE. Returns NULL if no RXE is pending. */ +static RXE *qrl_pop_pending_rxe(OSSL_QRL *qrl) +{ + RXE *rxe = qrl->rx_pending.head; + + if (rxe == NULL) + return NULL; + + rxe_remove(&qrl->rx_pending, rxe); + return rxe; +} + +/* Allocate a new RXE. */ +static RXE *qrl_alloc_rxe(size_t alloc_len) +{ + RXE *rxe; + + if (alloc_len >= SIZE_MAX - sizeof(RXE)) + return NULL; + + rxe = OPENSSL_malloc(sizeof(RXE) + alloc_len); + if (rxe == NULL) + return NULL; + + rxe->prev = rxe->next = NULL; + rxe->alloc_len = alloc_len; + rxe->data_len = 0; + return rxe; +} + +/* + * Ensures there is at least one RXE in the RX free list, allocating a new entry + * if necessary. The returned RXE is in the RX free list; it is not popped. + * + * alloc_len is a hint which may be used to determine the RXE size if allocation + * is necessary. Returns NULL on allocation failure. + */ +static RXE *qrl_ensure_free_rxe(OSSL_QRL *qrl, size_t alloc_len) +{ + RXE *rxe; + + if (qrl->rx_free.head != NULL) + return qrl->rx_free.head; + + rxe = qrl_alloc_rxe(alloc_len); + if (rxe == NULL) + return NULL; + + rxe_insert_tail(&qrl->rx_free, rxe); + return rxe; +} + +/* + * Resize the data buffer attached to an RXE to be n bytes in size. The address + * of the RXE might change; the new address is returned, or NULL on failure, in + * which case the original RXE remains valid. + */ +static RXE *qrl_resize_rxe(RXE_LIST *rxl, RXE *rxe, size_t n) +{ + RXE *rxe2; + + /* Should never happen. */ + if (rxe == NULL) + return NULL; + + if (n >= SIZE_MAX - sizeof(RXE)) + return NULL; + + /* + * NOTE: We do not clear old memory, although it does contain decrypted + * data. + */ + rxe2 = OPENSSL_realloc(rxe, sizeof(RXE) + n); + if (rxe2 == NULL) + /* original RXE is still in tact unchanged */ + return NULL; + + if (rxe != rxe2) { + if (rxl->head == rxe) + rxl->head = rxe2; + if (rxl->tail == rxe) + rxl->tail = rxe2; + if (rxe->prev != NULL) + rxe->prev->next = rxe2; + if (rxe->next != NULL) + rxe->next->prev = rxe2; + } + + rxe2->alloc_len = n; + return rxe2; +} + +/* + * Ensure the data buffer attached to an RXE is at least n bytes in size. + * Returns NULL on failure. + */ +static RXE *qrl_reserve_rxe(RXE_LIST *rxl, + RXE *rxe, size_t n) +{ + if (rxe->alloc_len >= n) + return rxe; + + return qrl_resize_rxe(rxl, rxe, n); +} + +/* Return a RXE handed out to the user back to our freelist. */ +static void qrl_recycle_rxe(OSSL_QRL *qrl, RXE *rxe) +{ + /* RXE should not be in any list */ + assert(rxe->prev == NULL && rxe->next == NULL); + rxe_insert_tail(&qrl->rx_free, rxe); +} + +/* + * Given a pointer to a pointer pointing to a buffer and the size of that + * buffer, copy the buffer into *prxe, expanding the RXE if necessary (its + * pointer may change due to realloc). *pi is the offset in bytes to copy the + * buffer to, and on success is updated to be the offset pointing after the + * copied buffer. *pptr is updated to point to the new location of the buffer. + */ +static int qrl_relocate_buffer(OSSL_QRL *qrl, RXE **prxe, size_t *pi, + const unsigned char **pptr, size_t buf_len) +{ + RXE *rxe; + unsigned char *dst; + + if (!buf_len) + return 1; + + if ((rxe = qrl_reserve_rxe(&qrl->rx_free, *prxe, *pi + buf_len)) == NULL) + return 0; + + *prxe = rxe; + dst = (unsigned char *)rxe_data(rxe) + *pi; + + memcpy(dst, *pptr, buf_len); + *pi += buf_len; + *pptr = dst; + return 1; +} + +static uint32_t qrl_determine_enc_level(const QUIC_PKT_HDR *hdr) +{ + switch (hdr->type) { + case QUIC_PKT_TYPE_INITIAL: + return QUIC_ENC_LEVEL_INITIAL; + case QUIC_PKT_TYPE_HANDSHAKE: + return QUIC_ENC_LEVEL_HANDSHAKE; + case QUIC_PKT_TYPE_0RTT: + return QUIC_ENC_LEVEL_0RTT; + case QUIC_PKT_TYPE_1RTT: + return QUIC_ENC_LEVEL_1RTT; + + default: + assert(0); + case QUIC_PKT_TYPE_RETRY: + case QUIC_PKT_TYPE_VERSION_NEG: + return QUIC_ENC_LEVEL_INITIAL; /* not used */ + } +} + +static uint32_t rxe_determine_pn_space(RXE *rxe) +{ + uint32_t enc_level; + + enc_level = qrl_determine_enc_level(&rxe->hdr); + return ossl_quic_enc_level_to_pn_space(enc_level); +} + +static int qrl_validate_hdr_early(OSSL_QRL *qrl, RXE *rxe, + RXE *first_rxe) +{ + /* Ensure version is what we want. */ + if (rxe->hdr.version != QUIC_VERSION_1 + && rxe->hdr.version != QUIC_VERSION_NONE) + return 0; + + /* Clients should never receive 0-RTT packets. */ + if (rxe->hdr.type == QUIC_PKT_TYPE_0RTT) + 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)) + return 0; + + /* + * If this is not the first packet in a datagram, the destination connection + * ID must match the one in that packet. + */ + if (first_rxe != NULL && + !ossl_quic_conn_id_eq(&first_rxe->hdr.dst_conn_id, + &rxe->hdr.dst_conn_id)) + return 0; + + return 1; +} + +/* Validate header and decode PN. */ +static int qrl_validate_hdr(OSSL_QRL *qrl, RXE *rxe) +{ + int pn_space = rxe_determine_pn_space(rxe); + + if (!ossl_quic_wire_decode_pkt_hdr_pn(rxe->hdr.pn, rxe->hdr.pn_len, + qrl->rx_largest_pn[pn_space], + &rxe->pn)) + return 0; + + /* + * Allow our user to decide whether to discard the packet before we try and + * decrypt it. + */ + if (qrl->rx_validation_cb != NULL + && !qrl->rx_validation_cb(rxe->pn, pn_space, qrl->rx_validation_cb_arg)) + return 0; + + return 1; +} + +/* + * Tries to decrypt a packet payload. + * + * Returns 1 on success or 0 on failure (which is permanent). The payload is + * decrypted from src and written to dst. The buffer dst must be of at least + * src_len bytes in length. The actual length of the output in bytes is written + * to *dec_len on success, which will always be equal to or less than (usually + * less than) src_len. + */ +static int qrl_decrypt_pkt_body(OSSL_QRL *qrl, 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) +{ + int l = 0, l2 = 0; + unsigned char nonce[EVP_MAX_IV_LENGTH]; + size_t nonce_len, i; + OSSL_QRL_ENC_LEVEL *el = &qrl->rx_el[enc_level]; + + if (src_len > INT_MAX || aad_len > INT_MAX || el->tag_len >= src_len) + return 0; + + /* We should not have been called if we do not have key material. */ + if (!ossl_assert(qrl_have_el(qrl, enc_level, /*is_tx=*/0) == 1)) + return 0; + + /* Construct nonce (nonce=IV ^ PN). */ + nonce_len = EVP_CIPHER_CTX_get_iv_length(el->cctx); + if (!ossl_assert(nonce_len >= sizeof(QUIC_PN))) + return 0; + + memcpy(nonce, el->iv, 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=*/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, + 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) + return 0; + + /* Feed encrypted packet body. */ + if (EVP_CipherUpdate(el->cctx, dst, &l, src, src_len - el->tag_len) != 1) + return 0; + + /* Ensure authentication succeeded. */ + if (EVP_CipherFinal_ex(el->cctx, NULL, &l2) != 1) + return 0; + + *dec_len = l; + return 1; +} + +static ossl_inline void ignore_res(int x) +{ + /* No-op. */ +} + +/* Process a single packet in a datagram. */ +static int qrl_process_pkt(OSSL_QRL *qrl, QUIC_URXE *urxe, + PACKET *pkt, size_t pkt_idx, + RXE **first_rxe, + size_t datagram_len) +{ + RXE *rxe; + const unsigned char *eop = NULL; + size_t i, aad_len = 0, dec_len = 0; + PACKET orig_pkt = *pkt; + const unsigned char *sop = PACKET_data(pkt); + unsigned char *dst; + char need_second_decode = 0, already_processed = 0; + QUIC_PKT_HDR_PTRS ptrs; + uint32_t pn_space, enc_level; + + /* + * Get a free RXE. If we need to allocate a new one, use the packet length + * as a good ballpark figure. + */ + rxe = qrl_ensure_free_rxe(qrl, PACKET_remaining(pkt)); + if (rxe == NULL) + return 0; + + /* Have we already processed this packet? */ + if (pkt_is_marked(&urxe->processed, pkt_idx)) + already_processed = 1; + + /* + * Decode the header into the RXE structure. We first decrypt and read the + * unprotected part of the packet header (unless we already removed header + * protection, in which case we decode all of it). + */ + need_second_decode = !pkt_is_marked(&urxe->hpr_removed, pkt_idx); + if (!ossl_quic_wire_decode_pkt_hdr(pkt, + qrl->short_conn_id_len, + need_second_decode, &rxe->hdr, &ptrs)) + goto malformed; + + /* + * Our successful decode above included an intelligible length and the + * PACKET is now pointing to the end of the QUIC packet. + */ + eop = PACKET_data(pkt); + + /* + * Make a note of the first RXE so we can later ensure the destination + * connection IDs of all packets in a datagram mater. + */ + if (pkt_idx == 0) + *first_rxe = rxe; + + /* + * Early header validation. Since we now know the packet length, we can also + * now skip over it if we already processed it. + */ + if (already_processed + || !qrl_validate_hdr_early(qrl, rxe, pkt_idx == 0 ? NULL : *first_rxe)) + goto malformed; + + if (rxe->hdr.type == QUIC_PKT_TYPE_VERSION_NEG + || rxe->hdr.type == QUIC_PKT_TYPE_RETRY) { + /* + * Version negotiation and retry packets are a special case. They do not + * contain a payload which needs decrypting and have no header + * protection. + */ + + /* Just copy the payload from the URXE to the RXE. */ + if ((rxe = qrl_reserve_rxe(&qrl->rx_free, rxe, rxe->hdr.len)) == NULL) + /* + * Allocation failure. EOP will be pointing to the end of the + * datagram so processing of this datagram will end here. + */ + goto malformed; + + /* We are now committed to returning the packet. */ + memcpy(rxe_data(rxe), rxe->hdr.data, rxe->hdr.len); + pkt_mark(&urxe->processed, pkt_idx); + + rxe->hdr.data = rxe_data(rxe); + + /* Move RXE to pending. */ + rxe_remove(&qrl->rx_free, rxe); + rxe_insert_tail(&qrl->rx_pending, rxe); + return 0; /* success, did not defer */ + } + + /* Determine encryption level of packet. */ + enc_level = qrl_determine_enc_level(&rxe->hdr); + + /* If we do not have keying material for this encryption level yet, defer. */ + switch (qrl_have_el(qrl, enc_level, /*is_tx=*/0)) { + case 1: + /* We have keys. */ + break; + case 0: + /* No keys yet. */ + goto cannot_decrypt; + default: + /* We already discarded keys for this EL, we will never process this.*/ + goto malformed; + } + + /* + * We will copy any token included in the packet to the start of our RXE + * data buffer (so that we don't reference the URXE buffer any more and can + * recycle it). Track our position in the RXE buffer by index instead of + * pointer as the pointer may change as reallocs occur. + */ + i = 0; + + /* + * rxe->hdr.data is now pointing at the (encrypted) packet payload. rxe->hdr + * also has fields pointing into the PACKET buffer which will be going away + * soon (the URXE will be reused for another incoming packet). + * + * Firstly, relocate some of these fields into the RXE as needed. + * + * Relocate token buffer and fix pointer. + */ + if (rxe->hdr.type == QUIC_PKT_TYPE_INITIAL + && !qrl_relocate_buffer(qrl, &rxe, &i, &rxe->hdr.token, + rxe->hdr.token_len)) + goto malformed; + + /* Now remove header protection. */ + *pkt = orig_pkt; + + if (need_second_decode) { + if (!ossl_quic_hdr_protector_decrypt(&qrl->rx_el[enc_level].hpr, &ptrs)) + goto malformed; + + /* + * We have removed header protection, so don't attempt to do it again if + * the packet gets deferred and processed again. + */ + pkt_mark(&urxe->hpr_removed, pkt_idx); + + /* Decode the now unprotected header. */ + if (ossl_quic_wire_decode_pkt_hdr(pkt, qrl->short_conn_id_len, + 0, &rxe->hdr, NULL) != 1) + goto malformed; + } + + /* Validate header and decode PN. */ + if (!qrl_validate_hdr(qrl, rxe)) + goto malformed; + + /* + * We automatically discard INITIAL keys when successfully decrypting a + * HANDSHAKE packet. + */ + if (enc_level == QUIC_ENC_LEVEL_HANDSHAKE) + qrl_el_discard(qrl, QUIC_ENC_LEVEL_INITIAL, 0, 1); + + /* + * The AAD data is the entire (unprotected) packet header including the PN. + * The packet header has been unprotected in place, so we can just reuse the + * PACKET buffer. The header ends where the payload begins. + */ + aad_len = rxe->hdr.data - sop; + + /* Ensure the RXE buffer size is adequate for our payload. */ + if ((rxe = qrl_reserve_rxe(&qrl->rx_free, rxe, rxe->hdr.len + i)) == NULL) { + /* + * Allocation failure, treat as malformed and do not bother processing + * any further packets in the datagram as they are likely to also + * encounter allocation failures. + */ + eop = NULL; + goto malformed; + } + + /* + * We decrypt the packet body to immediately after the token at the start of + * the RXE buffer (where present). + * + * Do the decryption from the PACKET (which points into URXE memory) to our + * RXE payload (single-copy decryption), then fixup the pointers in the + * header to point to our new buffer. + * + * If decryption fails this is considered a permanent error; we defer + * packets we don't yet have decryption keys for above, so if this fails, + * something has gone wrong with the handshake process or a packet has been + * corrupted. + */ + dst = (unsigned char *)rxe_data(rxe) + i; + if (!qrl_decrypt_pkt_body(qrl, dst, rxe->hdr.data, rxe->hdr.len, + &dec_len, sop, aad_len, rxe->pn, enc_level)) + goto malformed; + + /* + * We have now successfully decrypted the packet payload. If there are + * additional packets in the datagram, it is possible we will fail to + * decrypt them and need to defer them until we have some key material we + * don't currently possess. If this happens, the URXE will be moved to the + * deferred queue. Since a URXE corresponds to one datagram, which may + * contain multiple packets, we must ensure any packets we have already + * processed in the URXE are not processed again (this is an RFC + * requirement). We do this by marking the nth packet in the datagram as + * processed. + * + * We are now committed to returning this decrypted packet to the user, + * meaning we now consider the packet processed and must mark it + * accordingly. + */ + pkt_mark(&urxe->processed, pkt_idx); + + /* + * Update header to point to the decrypted buffer, which may be shorter + * due to AEAD tags, block padding, etc. + */ + rxe->hdr.data = dst; + rxe->hdr.len = dec_len; + rxe->data_len = dec_len; + rxe->datagram_len = datagram_len; + + /* We processed the PN successfully, so update largest processed PN. */ + pn_space = rxe_determine_pn_space(rxe); + if (rxe->pn > qrl->rx_largest_pn[pn_space]) + qrl->rx_largest_pn[pn_space] = rxe->pn; + + /* Copy across network addresses from URXE to RXE. */ + rxe->peer = urxe->peer; + rxe->local = urxe->local; + + /* Move RXE to pending. */ + rxe_remove(&qrl->rx_free, rxe); + rxe_insert_tail(&qrl->rx_pending, rxe); + return 0; /* success, did not defer; not distinguished from failure */ + +cannot_decrypt: + /* + * We cannot process this packet right now (but might be able to later). We + * MUST attempt to process any other packets in the datagram, so defer it + * and skip over it. + */ + assert(eop != NULL && eop >= PACKET_data(pkt)); + /* + * We don't care if this fails as it will just result in the packet being at + * the end of the datagram buffer. + */ + ignore_res(PACKET_forward(pkt, eop - PACKET_data(pkt))); + return 1; /* deferred */ + +malformed: + if (eop != NULL) { + /* + * This packet cannot be processed and will never be processable. We + * were at least able to decode its header and determine its length, so + * we can skip over it and try to process any subsequent packets in the + * datagram. + * + * Mark as processed as an optimization. + */ + assert(eop >= PACKET_data(pkt)); + pkt_mark(&urxe->processed, pkt_idx); + /* We don't care if this fails (see above) */ + ignore_res(PACKET_forward(pkt, eop - PACKET_data(pkt))); + } else { + /* + * This packet cannot be processed and will never be processable. + * Because even its header is not intelligible, we cannot examine any + * further packets in the datagram because its length cannot be + * discerned. + * + * Advance over the entire remainder of the datagram, and mark it as + * processed gap as an optimization. + */ + pkt_mark(&urxe->processed, pkt_idx); + /* We don't care if this fails (see above) */ + ignore_res(PACKET_forward(pkt, PACKET_remaining(pkt))); + } + return 0; /* failure, did not defer; not distinguished from success */ +} + +/* Process a datagram which was received. */ +static int qrl_process_datagram(OSSL_QRL *qrl, QUIC_URXE *e, + const unsigned char *data, + size_t data_len) +{ + int have_deferred = 0; + PACKET pkt; + size_t pkt_idx = 0; + RXE *first_rxe = NULL; + + qrl->bytes_received += data_len; + + if (!PACKET_buf_init(&pkt, data, data_len)) + return 0; + + for (; PACKET_remaining(&pkt) > 0; ++pkt_idx) { + /* + * A packet smallest than the minimum possible QUIC packet size is not + * considered valid. We also ignore more than a certain number of + * packets within the same datagram. + */ + if (PACKET_remaining(&pkt) < QUIC_MIN_VALID_PKT_LEN + || pkt_idx >= QUIC_MAX_PKT_PER_URXE) + break; + + /* + * We note whether packet processing resulted in a deferral since + * this means we need to move the URXE to the deferred list rather + * than the free list after we're finished dealing with it for now. + * + * However, we don't otherwise care here whether processing succeeded or + * failed, as the RFC says even if a packet in a datagram is malformed, + * 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, qrl_process_pkt will take care of advancing to the end of + * the packet, so we will exit the loop automatically in this case. + */ + if (qrl_process_pkt(qrl, e, &pkt, pkt_idx, &first_rxe, data_len)) + have_deferred = 1; + } + + /* Only report whether there were any deferrals. */ + return have_deferred; +} + +/* Process a single pending URXE. */ +static int qrl_process_one_urxl(OSSL_QRL *qrl, QUIC_URXE *e) +{ + int was_deferred; + + /* The next URXE we process should be at the head of the pending list. */ + if (!ossl_assert(e == qrl->urx_pending.head)) + return 0; + + /* + * Attempt to process the datagram. The return value indicates only if + * processing of the datagram was deferred. If we failed to process the + * datagram, we do not attempt to process it again and silently eat the + * error. + */ + was_deferred = qrl_process_datagram(qrl, e, ossl_quic_urxe_data(e), + e->data_len); + + /* + * Remove the URXE from the pending list and return it to + * either the free or deferred list. + */ + ossl_quic_urxe_remove(&qrl->urx_pending, e); + if (was_deferred > 0) + ossl_quic_urxe_insert_tail(&qrl->urx_deferred, e); + else + ossl_quic_demux_release_urxe(qrl->rx_demux, e); + + return 1; +} + +/* Process any pending URXEs to generate pending RXEs. */ +static int qrl_process_urxl(OSSL_QRL *qrl) +{ + QUIC_URXE *e; + + while ((e = qrl->urx_pending.head) != NULL) + if (!qrl_process_one_urxl(qrl, e)) + return 0; + + return 1; +} + +int ossl_qrl_read_pkt(OSSL_QRL *qrl, OSSL_QRL_RX_PKT *pkt) +{ + RXE *rxe; + + if (!ossl_qrl_processed_read_pending(qrl)) { + if (!qrl_process_urxl(qrl)) + return 0; + + if (!ossl_qrl_processed_read_pending(qrl)) + return 0; + } + + rxe = qrl_pop_pending_rxe(qrl); + if (!ossl_assert(rxe != NULL)) + return 0; + + pkt->handle = rxe; + pkt->hdr = &rxe->hdr; + pkt->peer + = BIO_ADDR_family(&rxe->peer) != AF_UNSPEC ? &rxe->peer : NULL; + pkt->local + = BIO_ADDR_family(&rxe->local) != AF_UNSPEC ? &rxe->local : NULL; + return 1; +} + +void ossl_qrl_release_pkt(OSSL_QRL *qrl, void *handle) +{ + RXE *rxe = handle; + + qrl_recycle_rxe(qrl, rxe); +} + +uint64_t ossl_qrl_get_bytes_received(OSSL_QRL *qrl, int clear) +{ + uint64_t v = qrl->bytes_received; + + if (clear) + qrl->bytes_received = 0; + + return v; +} + +int ossl_qrl_set_early_rx_validation_cb(OSSL_QRL *qrl, + ossl_qrl_early_rx_validation_cb *cb, + void *cb_arg) +{ + qrl->rx_validation_cb = cb; + qrl->rx_validation_cb_arg = cb_arg; + return 1; +} diff --git a/ssl/quic/quic_record_util.c b/ssl/quic/quic_record_util.c new file mode 100644 index 0000000000..6d0eeb5759 --- /dev/null +++ b/ssl/quic/quic_record_util.c @@ -0,0 +1,141 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/quic_record_util.h" +#include "internal/quic_wire_pkt.h" +#include <openssl/kdf.h> +#include <openssl/core_names.h> + +/* + * QUIC Key Derivation Utilities + * ============================= + */ +int ossl_quic_hkdf_extract(OSSL_LIB_CTX *libctx, + const char *propq, + const EVP_MD *md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *out, size_t out_len) +{ + int ret = 0; + EVP_KDF *kdf = NULL; + EVP_KDF_CTX *kctx = NULL; + OSSL_PARAM params[7], *p = params; + int mode = EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY; + const char *md_name; + + if ((md_name = EVP_MD_get0_name(md)) == NULL + || (kdf = EVP_KDF_fetch(libctx, OSSL_KDF_NAME_HKDF, propq)) == NULL + || (kctx = EVP_KDF_CTX_new(kdf)) == NULL) + goto err; + + *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)md_name, 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, + (unsigned char *)salt, salt_len); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, + (unsigned char *)ikm, ikm_len); + *p++ = OSSL_PARAM_construct_end(); + + ret = EVP_KDF_derive(kctx, out, out_len, params); + +err: + EVP_KDF_CTX_free(kctx); + EVP_KDF_free(kdf); + return ret; +} + +/* + * QUIC Record Layer Ciphersuite Info + * ================================== + */ + +struct suite_info { + const char *cipher_name, *md_name; + uint32_t secret_len, cipher_key_len, cipher_iv_len, cipher_tag_len; + uint32_t hdr_prot_key_len, hdr_prot_cipher_id; +}; + +static const struct suite_info suite_aes128gcm = { + "AES-128-GCM", "SHA256", 32, 16, 12, 16, 16, + QUIC_HDR_PROT_CIPHER_AES_128 +}; + +static const struct suite_info suite_aes256gcm = { + "AES-256-GCM", "SHA384", 48, 32, 12, 16, 32, + QUIC_HDR_PROT_CIPHER_AES_256 +}; + +static const struct suite_info suite_chacha20poly1305 = { + "ChaCha20-Poly1305", "SHA256", 32, 32, 12, 16, 32, + QUIC_HDR_PROT_CIPHER_CHACHA +}; + +static const struct suite_info *get_suite(uint32_t suite_id) +{ + switch (suite_id) { + case QRL_SUITE_AES128GCM: + return &suite_aes128gcm; + case QRL_SUITE_AES256GCM: + return &suite_aes256gcm; + case QRL_SUITE_CHACHA20POLY1305: + return &suite_chacha20poly1305; + default: + return NULL; + } +} + +const char *ossl_qrl_get_suite_cipher_name(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->cipher_name : NULL; +} + +const char *ossl_qrl_get_suite_md_name(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->md_name : NULL; +} + +uint32_t ossl_qrl_get_suite_secret_len(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->secret_len : 0; +} + +uint32_t ossl_qrl_get_suite_cipher_key_len(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->cipher_key_len : 0; +} + +uint32_t ossl_qrl_get_suite_cipher_iv_len(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->cipher_iv_len : 0; +} + +uint32_t ossl_qrl_get_suite_cipher_tag_len(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->cipher_tag_len : 0; +} + +uint32_t ossl_qrl_get_suite_hdr_prot_cipher_id(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->hdr_prot_cipher_id : 0; +} + +uint32_t ossl_qrl_get_suite_hdr_prot_key_len(uint32_t suite_id) +{ + const struct suite_info *c = get_suite(suite_id); + return c != NULL ? c->hdr_prot_key_len : 0; +} diff --git a/ssl/quic/quic_wire.c b/ssl/quic/quic_wire.c index a3a9b252fa..4d19ad6013 100644 --- a/ssl/quic/quic_wire.c +++ b/ssl/quic/quic_wire.c @@ -263,7 +263,7 @@ int ossl_quic_wire_encode_frame_streams_blocked(WPACKET *pkt, int ossl_quic_wire_encode_frame_new_conn_id(WPACKET *pkt, const OSSL_QUIC_FRAME_NEW_CONN_ID *f) { - if (f->conn_id.id_len > OSSL_QUIC_MAX_CONN_ID_LEN) + if (f->conn_id.id_len > QUIC_MAX_CONN_ID_LEN) return 0; if (!encode_frame_hdr(pkt, OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID) @@ -680,7 +680,7 @@ int ossl_quic_wire_decode_frame_new_conn_id(PACKET *pkt, || !PACKET_get_quic_vlint(pkt, &f->seq_num) || !PACKET_get_quic_vlint(pkt, &f->retire_prior_to) || !PACKET_get_1(pkt, &len) - || len > OSSL_QUIC_MAX_CONN_ID_LEN) + || len > QUIC_MAX_CONN_ID_LEN) return 0; f->conn_id.id_len = (unsigned char)len; @@ -688,8 +688,8 @@ int ossl_quic_wire_decode_frame_new_conn_id(PACKET *pkt, return 0; /* Clear unused bytes to allow consistent memcmp. */ - if (len < OSSL_QUIC_MAX_CONN_ID_LEN) - memset(f->conn_id.id + len, 0, OSSL_QUIC_MAX_CONN_ID_LEN - len); + if (len < QUIC_MAX_CONN_ID_LEN) + memset(f->conn_id.id + len, 0, QUIC_MAX_CONN_ID_LEN - len); if (!PACKET_copy_bytes(pkt, f->stateless_reset_token, sizeof(f->stateless_reset_token))) diff --git a/ssl/quic/quic_wire_pkt.c b/ssl/quic/quic_wire_pkt.c new file mode 100644 index 0000000000..5d90d70c15 --- /dev/null +++ b/ssl/quic/quic_wire_pkt.c @@ -0,0 +1,678 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/quic_wire_pkt.h" + +int ossl_quic_hdr_protector_init(QUIC_HDR_PROTECTOR *hpr, + OSSL_LIB_CTX *libctx, + const char *propq, + uint32_t cipher_id, + const unsigned char *quic_hp_key, + size_t quic_hp_key_len) +{ + const char *cipher_name = NULL; + + switch (cipher_id) { + case QUIC_HDR_PROT_CIPHER_AES_128: + cipher_name = "AES-128-ECB"; + break; + case QUIC_HDR_PROT_CIPHER_AES_256: + cipher_name = "AES-256-ECB"; + break; + case QUIC_HDR_PROT_CIPHER_CHACHA: + cipher_name = "ChaCha20"; + break; + default: + return 0; + } + + hpr->cipher_ctx = EVP_CIPHER_CTX_new(); + if (hpr->cipher_ctx == NULL) + return 0; + + hpr->cipher = EVP_CIPHER_fetch(libctx, cipher_name, propq); + if (hpr->cipher == NULL + || quic_hp_key_len != (size_t)EVP_CIPHER_get_key_length(hpr->cipher)) + goto err; + + if (!EVP_CipherInit_ex(hpr->cipher_ctx, hpr->cipher, NULL, + quic_hp_key, NULL, 1)) + goto err; + + hpr->libctx = libctx; + hpr->propq = propq; + hpr->cipher_id = cipher_id; + return 1; + +err: + ossl_quic_hdr_protector_destroy(hpr); + return 0; +} + +void ossl_quic_hdr_protector_destroy(QUIC_HDR_PROTECTOR *hpr) +{ + EVP_CIPHER_CTX_free(hpr->cipher_ctx); + hpr->cipher_ctx = NULL; + + EVP_CIPHER_free(hpr->cipher); + hpr->cipher = NULL; +} + +static int hdr_generate_mask(QUIC_HDR_PROTECTOR *hpr, + const unsigned char *sample, size_t sample_len, + unsigned char *mask) +{ + int l = 0; + unsigned char dst[16]; + static const unsigned char zeroes[5] = {0}; + size_t i; + + if (hpr->cipher_id == QUIC_HDR_PROT_CIPHER_AES_128 + || hpr->cipher_id == QUIC_HDR_PROT_CIPHER_AES_256) { + if (sample_len < 16) + return 0; + + if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, NULL, 1) + || !EVP_CipherUpdate(hpr->cipher_ctx, dst, &l, sample, 16)) + return 0; + + for (i = 0; i < 5; ++i) + mask[i] = dst[i]; + } else if (hpr->cipher_id == QUIC_HDR_PROT_CIPHER_CHACHA) { + if (sample_len < 16) + return 0; + + if (!EVP_CipherInit_ex(hpr->cipher_ctx, NULL, NULL, NULL, sample, 1) + || !EVP_CipherUpdate(hpr->cipher_ctx, mask, &l, + zeroes, sizeof(zeroes))) + return 0; + } else { + assert(0); + return 0; + } + + return 1; +} + +int ossl_quic_hdr_protector_decrypt(QUIC_HDR_PROTECTOR *hpr, + QUIC_PKT_HDR_PTRS *ptrs) +{ + return ossl_quic_hdr_protector_decrypt_fields(hpr, + ptrs->raw_sample, + ptrs->raw_sample_len, + ptrs->raw_start, + ptrs->raw_pn); +} + +int ossl_quic_hdr_protector_decrypt_fields(QUIC_HDR_PROTECTOR *hpr, + const unsigned char *sample, + size_t sample_len, + unsigned char *first_byte, + unsigned char *pn_bytes) +{ + unsigned char mask[5], pn_len, i; + + if (!hdr_generate_mask(hpr, sample, sample_len, mask)) + return 0; + + *first_byte ^= mask[0] & ((*first_byte & 0x80) != 0 ? 0xf : 0x1f); + pn_len = (*first_byte & 0x3) + 1; + + for (i = 0; i < pn_len; ++i) + pn_bytes[i] ^= mask[i + 1]; + + return 1; +} + +int ossl_quic_hdr_protector_encrypt(QUIC_HDR_PROTECTOR *hpr, + QUIC_PKT_HDR_PTRS *ptrs) +{ + return ossl_quic_hdr_protector_encrypt_fields(hpr, + ptrs->raw_sample, + ptrs->raw_sample_len, + ptrs->raw_start, + ptrs->raw_pn); +} + +int ossl_quic_hdr_protector_encrypt_fields(QUIC_HDR_PROTECTOR *hpr, + const unsigned char *sample, + size_t sample_len, + unsigned char *first_byte, + unsigned char *pn_bytes) +{ + unsigned char mask[5], pn_len, i; + + if (!hdr_generate_mask(hpr, sample, sample_len, mask)) + return 0; + + pn_len = (*first_byte & 0x3) + 1; + for (i = 0; i < pn_len; ++i) + pn_bytes[i] ^= mask[i + 1]; + + *first_byte ^= mask[0] & ((*first_byte & 0x80) != 0 ? 0xf : 0x1f); + return 1; +} + +int ossl_quic_wire_decode_pkt_hdr(PACKET *pkt, + size_t short_conn_id_len, + int partial, + QUIC_PKT_HDR *hdr, + QUIC_PKT_HDR_PTRS *ptrs) +{ + unsigned int b0; + unsigned char *pn = NULL; + size_t l = PACKET_remaining(pkt); + + if (ptrs != NULL) { + ptrs->raw_start = (unsigned char *)PACKET_data(pkt); + ptrs->raw_sample = NULL; + ptrs->raw_sample_len = 0; + ptrs->raw_pn = NULL; + } + + if (l < QUIC_MIN_VALID_PKT_LEN + || !PACKET_get_1(pkt, &b0)) + return 0; + + hdr->partial = partial; + + if ((b0 & 0x80) == 0) { + /* Short header. */ + if (short_conn_id_len > QUIC_MAX_CONN_ID_LEN) + return 0; + + if ((b0 & 0x40) == 0 /* fixed bit not set? */ + || l < QUIC_MIN_VALID_PKT_LEN_CRYPTO) + return 0; + + hdr->type = QUIC_PKT_TYPE_1RTT; + hdr->fixed = 1; + hdr->spin_bit = (b0 & 0x20) != 0; + if (partial) { + hdr->key_phase = 0; /* protected, zero for now */ + hdr->pn_len = 0; /* protected, zero for now */ + } else { + hdr->key_phase = (b0 & 0x4) != 0; + hdr->pn_len = (b0 & 0x3) + 1; + } + + /* Copy destination connection ID field to header structure. */ + if (!PACKET_copy_bytes(pkt, hdr->dst_conn_id.id, short_conn_id_len)) + return 0; + + hdr->dst_conn_id.id_len = short_conn_id_len; + + /* + * Skip over the PN. If this is a partial decode, the PN length field + * currently has header protection applied. Thus we do not know the + * length of the PN but we are allowed to assume it is 4 bytes long at + * this stage. + */ + memset(hdr->pn, 0, sizeof(hdr->pn)); + pn = (unsigned char *)PACKET_data(pkt); + if (partial) { + if (!PACKET_forward(pkt, sizeof(hdr->pn))) + return 0; + } else { + if (!PACKET_copy_bytes(pkt, hdr->pn, hdr->pn_len)) + return 0; + } + + /* Fields not used in short-header packets. */ + hdr->version = 0; + hdr->src_conn_id.id_len = 0; + hdr->token = NULL; + hdr->token_len = 0; + + /* + * Short-header packets always come last in a datagram, the length + * is the remainder of the buffer. + */ + hdr->len = PACKET_remaining(pkt); + hdr->data = PACKET_data(pkt); + + /* + * Skip over payload so we are pointing at the start of the next packet, + * if any. + */ + if (!PACKET_forward(pkt, hdr->len)) + return 0; + } else { + /* Long header. */ + unsigned long version; + unsigned int dst_conn_id_len, src_conn_id_len, raw_type; + + if (!PACKET_get_net_4(pkt, &version)) + return 0; + + /* + * All QUIC packets must have the fixed bit set, except exceptionally + * for Version Negotiation packets. + */ + if (version != 0 && (b0 & 0x40) == 0) + return 0; + + if (!PACKET_get_1(pkt, &dst_conn_id_len) + || dst_conn_id_len > QUIC_MAX_CONN_ID_LEN + || !PACKET_copy_bytes(pkt, hdr->dst_conn_id.id, dst_conn_id_len) + || !PACKET_get_1(pkt, &src_conn_id_len) + || src_conn_id_len > QUIC_MAX_CONN_ID_LEN + || !PACKET_copy_bytes(pkt, hdr->src_conn_id.id, src_conn_id_len)) + return 0; + + hdr->version = (uint32_t)version; + hdr->dst_conn_id.id_len = (unsigned char)dst_conn_id_len; + hdr->src_conn_id.id_len = (unsigned char)src_conn_id_len; + + if (version == 0) { + /* + * Version negotiation packet. Version negotiation packets are + * identified by a version field of 0 and the type bits in the first + * byte are ignored (they may take any value, and we ignore them). + */ + hdr->type = QUIC_PKT_TYPE_VERSION_NEG; + hdr->fixed = (b0 & 0x40) != 0; + + hdr->data = PACKET_data(pkt); + hdr->len = PACKET_remaining(pkt); + + /* Version negotiation packets are always fully decoded. */ + hdr->partial = 0; + + /* Fields not used in version negotiation packets. */ + hdr->pn_len = 0; + hdr->spin_bit = 0; + hdr->key_phase = 0; + hdr->token = NULL; + hdr->token_len = 0; + memset(hdr->pn, 0, sizeof(hdr->pn)); + + if (!PACKET_forward(pkt, hdr->len)) + return 0; + } else if (version != QUIC_VERSION_1) { + /* Unknown version, do not decode. */ + return 0; + } else { + if (l < QUIC_MIN_VALID_PKT_LEN_CRYPTO) + return 0; + + /* Get long packet type and decode to QUIC_PKT_TYPE_*. */ + 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; + } + + hdr->pn_len = 0; + hdr->fixed = 1; + + /* Fields not used in long-header packets. */ + hdr->spin_bit = 0; + hdr->key_phase = 0; + + if (hdr->type == QUIC_PKT_TYPE_INITIAL) { + /* Initial packet. */ + uint64_t token_len; + + if (!PACKET_get_quic_vlint(pkt, &token_len) + || token_len > SIZE_MAX + || !PACKET_get_bytes(pkt, &hdr->token, token_len)) + return 0; + + hdr->token_len = (size_t)token_len; + if (token_len == 0) + hdr->token = NULL; + } else { + hdr->token = NULL; + hdr->token_len = 0; + } + + if (hdr->type == QUIC_PKT_TYPE_RETRY) { + /* Retry packet. */ + hdr->data = PACKET_data(pkt); + hdr->len = PACKET_remaining(pkt); + + /* Retry packets are always fully decoded. */ + hdr->partial = 0; + + /* Fields not used in Retry packets. */ + memset(hdr->pn, 0, sizeof(hdr->pn)); + + if (!PACKET_forward(pkt, hdr->len)) + return 0; + } else { + /* Initial, 0-RTT or Handshake packet. */ + uint64_t len; + + hdr->pn_len = partial ? 0 : (b0 & 3) + 1; + + if (!PACKET_get_quic_vlint(pkt, &len) + || len < sizeof(hdr->pn) + || len > PACKET_remaining(pkt)) + return 0; + + /* + * Skip over the PN. If this is a partial decode, the PN length + * field currently has header protection applied. Thus we do not + * know the length of the PN but we are allowed to assume it is + * 4 bytes long at this stage. + */ + pn = (unsigned char *)PACKET_data(pkt); + memset(hdr->pn, 0, sizeof(hdr->pn)); + if (partial) { + if (!PACKET_forward(pkt, sizeof(hdr->pn))) + return 0; + + hdr->len = (size_t)(len - sizeof(hdr->pn)); + } else { + if (!PACKET_copy_bytes(pkt, hdr->pn, hdr->pn_len)) + return 0; + + hdr->len = (size_t)(len - hdr->pn_len); + } + + hdr->data = PACKET_data(pkt); + + /* Skip over packet body. */ + if (!PACKET_forward(pkt, hdr->len)) + return 0; + } + } + } + + if (ptrs != NULL) { + ptrs->raw_pn = pn; + if (pn != NULL) { + ptrs->raw_sample = pn + 4; + ptrs->raw_sample_len = PACKET_end(pkt) - ptrs->raw_sample; + } + } + + return 1; +} + +int ossl_quic_wire_encode_pkt_hdr(WPACKET *pkt, + size_t short_conn_id_len, + const QUIC_PKT_HDR *hdr, + QUIC_PKT_HDR_PTRS *ptrs) +{ + unsigned char b0; + size_t off_start, off_sample, off_sample_end, off_pn; + + if (!WPACKET_get_total_written(pkt, &off_start)) + return 0; + + if (ptrs != NULL) { + ptrs->raw_start = NULL; + ptrs->raw_sample = NULL; + ptrs->raw_sample_len = 0; + ptrs->raw_pn = 0; + } + + /* Cannot serialize a partial header, or one whose DCID length is wrong. */ + if (hdr->partial + || (hdr->type == QUIC_PKT_TYPE_1RTT + && hdr->dst_conn_id.id_len != short_conn_id_len)) + return 0; + + if (hdr->type == QUIC_PKT_TYPE_1RTT) { + /* Short header. */ + + /* + * Cannot serialize a header whose DCID length is wrong, or with an + * invalid PN length. + */ + if (hdr->dst_conn_id.id_len != short_conn_id_len + || short_conn_id_len > QUIC_MAX_CONN_ID_LEN + || hdr->pn_len < 1 || hdr->pn_len > 4) + return 0; + + b0 = (hdr->spin_bit << 5) + | (hdr->key_phase << 2) + | (hdr->pn_len - 1) + | 0x40; /* fixed bit */ + + if (!WPACKET_put_bytes_u8(pkt, b0) + || !WPACKET_memcpy(pkt, hdr->dst_conn_id.id, short_conn_id_len) + || !WPACKET_get_total_written(pkt, &off_pn) + || !WPACKET_memcpy(pkt, hdr->pn, hdr->pn_len)) + return 0; + } else { + /* Long header. */ + unsigned int raw_type; + + if (hdr->dst_conn_id.id_len > QUIC_MAX_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; + + switch (hdr->type) { + case QUIC_PKT_TYPE_VERSION_NEG: + if (hdr->version != 0) + return 0; + + /* Version negotiation packets use zero for the type bits */ + raw_type = 0; + break; + + case QUIC_PKT_TYPE_INITIAL: raw_type = 0; break; + case QUIC_PKT_TYPE_0RTT: raw_type = 1; break; + case QUIC_PKT_TYPE_HANDSHAKE: raw_type = 2; break; + case QUIC_PKT_TYPE_RETRY: raw_type = 3; break; + default: + return 0; + } + + 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) + b0 |= hdr->pn_len - 1; + + if (!WPACKET_put_bytes_u8(pkt, b0) + || !WPACKET_put_bytes_u32(pkt, hdr->version) + || !WPACKET_put_bytes_u8(pkt, hdr->dst_conn_id.id_len) + || !WPACKET_memcpy(pkt, hdr->dst_conn_id.id, + hdr->dst_conn_id.id_len) + || !WPACKET_put_bytes_u8(pkt, hdr->src_conn_id.id_len) + || !WPACKET_memcpy(pkt, hdr->src_conn_id.id, + hdr->src_conn_id.id_len)) + return 0; + + if (hdr->type == QUIC_PKT_TYPE_VERSION_NEG + || hdr->type == QUIC_PKT_TYPE_RETRY) { + if (!WPACKET_reserve_bytes(pkt, hdr->len, NULL)) + return 0; + + return 1; + } + + if (hdr->type == QUIC_PKT_TYPE_INITIAL) { + if (!WPACKET_quic_write_vlint(pkt, hdr->token_len) + || !WPACKET_memcpy(pkt, hdr->token, hdr->token_len)) + return 0; + } + + if (!WPACKET_quic_write_vlint(pkt, hdr->len + hdr->pn_len) + || !WPACKET_get_total_written(pkt, &off_pn) + || !WPACKET_memcpy(pkt, hdr->pn, hdr->pn_len)) + return 0; + } + + if (!WPACKET_reserve_bytes(pkt, hdr->len, NULL)) + return 0; + + off_sample = off_pn + 4; + if (!WPACKET_get_total_written(pkt, &off_sample_end)) + return 0; + + if (ptrs != NULL) { + ptrs->raw_start = (unsigned char *)pkt->buf->data + off_start; + ptrs->raw_sample = (unsigned char *)pkt->buf->data + off_sample; + ptrs->raw_sample_len = off_sample_end - off_sample; + ptrs->raw_pn = (unsigned char *)pkt->buf->data + off_pn; + } + + return 1; +} + +int ossl_quic_wire_get_pkt_hdr_dst_conn_id(const unsigned char *buf, + size_t buf_len, + size_t short_conn_id_len, + QUIC_CONN_ID *dst_conn_id) +{ + unsigned char b0; + size_t blen; + + if (buf_len < QUIC_MIN_VALID_PKT_LEN + || short_conn_id_len > QUIC_MAX_CONN_ID_LEN) + return 0; + + b0 = buf[0]; + if ((b0 & 0x80) != 0) { + /* + * Long header. We need 6 bytes (initial byte, 4 version bytes, DCID + * length byte to begin with). This is covered by the buf_len test + * above. + */ + + /* + * If the version field is non-zero (meaning that this is not a Version + * Negotiation packet), the fixed bit must be set. + */ + if ((buf[1] || buf[2] || buf[3] || buf[4]) && (b0 & 0x40) == 0) + return 0; + + blen = (size_t)buf[5]; /* DCID Length */ + if (blen > QUIC_MAX_CONN_ID_LEN + || buf_len < QUIC_MIN_VALID_PKT_LEN + blen) + return 0; + + dst_conn_id->id_len = (unsigned char)blen; + memcpy(dst_conn_id->id, buf + 6, blen); + return 1; + } else { + /* Short header. */ + if ((b0 & 0x40) == 0) + /* Fixed bit not set, not a valid QUIC packet header. */ + return 0; + + if (buf_len < QUIC_MIN_VALID_PKT_LEN_CRYPTO + short_conn_id_len) + return 0; + + dst_conn_id->id_len = short_conn_id_len; + memcpy(dst_conn_id->id, buf + 1, short_conn_id_len); + return 1; + } +} + +int ossl_quic_wire_decode_pkt_hdr_pn(const unsigned char *enc_pn, + size_t enc_pn_len, + QUIC_PN largest_pn, + QUIC_PN *res_pn) +{ + int64_t expected_pn, truncated_pn, candidate_pn, pn_win, pn_hwin, pn_mask; + + switch (enc_pn_len) { + case 1: + truncated_pn = enc_pn[0]; + break; + case 2: + truncated_pn = ((QUIC_PN)enc_pn[0] << 8) + | (QUIC_PN)enc_pn[1]; + break; + case 3: + truncated_pn = ((QUIC_PN)enc_pn[0] << 16) + | ((QUIC_PN)enc_pn[1] << 8) + | (QUIC_PN)enc_pn[2]; + break; + case 4: + truncated_pn = ((QUIC_PN)enc_pn[0] << 24) + | ((QUIC_PN)enc_pn[1] << 16) + | ((QUIC_PN)enc_pn[2] << 8) + | (QUIC_PN)enc_pn[3]; + break; + default: + return 0; + } + + /* Implemented as per RFC 9000 Section A.3. */ + expected_pn = largest_pn + 1; + pn_win = ((int64_t)1) << (enc_pn_len * 8); + pn_hwin = pn_win / 2; + pn_mask = pn_win - 1; + candidate_pn = (expected_pn & ~pn_mask) | truncated_pn; + if (candidate_pn <= expected_pn - pn_hwin + && candidate_pn < (((int64_t)1) << 62) - pn_win) + *res_pn = candidate_pn + pn_win; + else if (candidate_pn > expected_pn + pn_hwin + && candidate_pn >= pn_win) + *res_pn = candidate_pn - pn_win; + else + *res_pn = candidate_pn; + return 1; +} + +/* From RFC 9000 Section A.2. Simplified implementation. */ +int ossl_quic_wire_determine_pn_len(QUIC_PN pn, + QUIC_PN largest_acked) +{ + uint64_t num_unacked + = (largest_acked == QUIC_PN_INVALID) ? pn + 1 : pn - largest_acked; + + /* + * num_unacked \in [ 0, 2** 7] -> 1 byte + * num_unacked \in (2** 7, 2**15] -> 2 bytes + * num_unacked \in (2**15, 2**23] -> 3 bytes + * num_unacked \in (2**23, ] -> 4 bytes + */ + + if (num_unacked <= (1U<<7)) return 1; + if (num_unacked <= (1U<<15)) return 2; + if (num_unacked <= (1U<<23)) return 3; + return 4; +} + +int ossl_quic_wire_encode_pkt_hdr_pn(QUIC_PN pn, + unsigned char *enc_pn, + size_t enc_pn_len) +{ + switch (enc_pn_len) { + case 1: + enc_pn[0] = (unsigned char)pn; + break; + case 2: + enc_pn[1] = (unsigned char)pn; + enc_pn[0] = (unsigned char)(pn >> 8); + break; + case 3: + enc_pn[2] = (unsigned char)pn; + enc_pn[1] = (unsigned char)(pn >> 8); + enc_pn[0] = (unsigned char)(pn >> 16); + break; + case 4: + enc_pn[3] = (unsigned char)pn; + enc_pn[2] = (unsigned char)(pn >> 8); + enc_pn[1] = (unsigned char)(pn >> 16); + enc_pn[0] = (unsigned char)(pn >> 24); + break; + default: + return 0; + } + + return 1; +} diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 38b2c7e970..fec587ee8a 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -2688,11 +2688,19 @@ __owur size_t tls13_final_finish_mac(SSL_CONNECTION *s, const char *str, size_t unsigned char *p); __owur int tls13_change_cipher_state(SSL_CONNECTION *s, int which); __owur int tls13_update_key(SSL_CONNECTION *s, int send); -__owur int tls13_hkdf_expand(SSL_CONNECTION *s, const EVP_MD *md, +__owur int tls13_hkdf_expand(SSL_CONNECTION *s, + const EVP_MD *md, const unsigned char *secret, const unsigned char *label, size_t labellen, const unsigned char *data, size_t datalen, unsigned char *out, size_t outlen, int fatal); +__owur int tls13_hkdf_expand_ex(OSSL_LIB_CTX *libctx, const char *propq, + const EVP_MD *md, + const unsigned char *secret, + const unsigned char *label, size_t labellen, + const unsigned char *data, size_t datalen, + unsigned char *out, size_t outlen, + int raise_error); __owur int tls13_derive_key(SSL_CONNECTION *s, const EVP_MD *md, const unsigned char *secret, unsigned char *key, size_t keylen); diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index 0af8ad2918..0d0c0a14e5 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -30,16 +30,16 @@ static const unsigned char label_prefix[] = "tls13 "; * secret |outlen| bytes long and store it in the location pointed to be |out|. * The |data| value may be zero length. Any errors will be treated as fatal if * |fatal| is set. Returns 1 on success 0 on failure. + * If |raise_error| is set, ERR_raise is called on failure. */ -int tls13_hkdf_expand(SSL_CONNECTION *s, const EVP_MD *md, - const unsigned char *secret, - const unsigned char *label, size_t labellen, - const unsigned char *data, size_t datalen, - unsigned char *out, size_t outlen, int fatal) +int tls13_hkdf_expand_ex(OSSL_LIB_CTX *libctx, const char *propq, + const EVP_MD *md, + const unsigned char *secret, + const unsigned char *label, size_t labellen, + const unsigned char *data, size_t datalen, + unsigned char *out, size_t outlen, int raise_error) { - SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); - EVP_KDF *kdf = EVP_KDF_fetch(sctx->libctx, OSSL_KDF_NAME_TLS1_3_KDF, - sctx->propq); + EVP_KDF *kdf = EVP_KDF_fetch(libctx, OSSL_KDF_NAME_TLS1_3_KDF, propq); EVP_KDF_CTX *kctx; OSSL_PARAM params[7], *p = params; int mode = EVP_PKEY_HKDEF_MODE_EXPAND_ONLY; @@ -53,24 +53,20 @@ int tls13_hkdf_expand(SSL_CONNECTION *s, const EVP_MD *md, return 0; if (labellen > TLS13_MAX_LABEL_LEN) { - if (fatal) { - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - } else { + if (raise_error) /* * Probably we have been called from SSL_export_keying_material(), * or SSL_export_keying_material_early(). */ ERR_raise(ERR_LIB_SSL, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL); - } + EVP_KDF_CTX_free(kctx); return 0; } if ((ret = EVP_MD_get_size(md)) <= 0) { EVP_KDF_CTX_free(kctx); - if (fatal) - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - else + if (raise_error) ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); return 0; } @@ -96,15 +92,31 @@ int tls13_hkdf_expand(SSL_CONNECTION *s, const EVP_MD *md, EVP_KDF_CTX_free(kctx); if (ret != 0) { - if (fatal) - SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - else + if (raise_error) ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); } return ret == 0; } +int tls13_hkdf_expand(SSL_CONNECTION *s, const EVP_MD *md, + const unsigned char *secret, + const unsigned char *label, size_t labellen, + const unsigned char *data, size_t datalen, + unsigned char *out, size_t outlen, int fatal) +{ + int ret; + SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s); + + ret = tls13_hkdf_expand_ex(sctx->libctx, sctx->propq, md, + secret, label, labellen, data, datalen, + out, outlen, !fatal); + if (ret == 0 && fatal) + SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + + return ret; +} + /* * Given a |secret| generate a |key| of length |keylen| bytes. Returns 1 on * success 0 on failure. diff --git a/test/build.info b/test/build.info index ccd969ab01..44dff9d475 100644 --- a/test/build.info +++ b/test/build.info @@ -282,6 +282,10 @@ IF[{- !$disabled{tests} -}] INCLUDE[quic_wire_test]=../include ../apps/include DEPEND[quic_wire_test]=../libcrypto.a ../libssl.a libtestutil.a + SOURCE[quic_record_test]=quic_record_test.c + INCLUDE[quic_record_test]=../include ../apps/include + DEPEND[quic_record_test]=../libcrypto.a ../libssl.a libtestutil.a + SOURCE[asynctest]=asynctest.c INCLUDE[asynctest]=../include ../apps/include DEPEND[asynctest]=../libcrypto @@ -968,7 +972,7 @@ ENDIF ENDIF IF[{- !$disabled{'quic'} -}] - PROGRAMS{noinst}=quicapitest quic_wire_test quic_ackm_test + PROGRAMS{noinst}=quicapitest quic_wire_test quic_ackm_test quic_record_test ENDIF SOURCE[quicapitest]=quicapitest.c helpers/ssltestlib.c diff --git a/test/quic_record_test.c b/test/quic_record_test.c new file mode 100644 index 0000000000..29c0dcf57b --- /dev/null +++ b/test/quic_record_test.c @@ -0,0 +1,2378 @@ +/* + * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/quic_record.h" +#include "testutil.h" + +static const QUIC_CONN_ID empty_conn_id = {0, {0}}; + +struct test_case { + const unsigned char *dgram; + size_t dgram_len; +}; + +#define TEST_OP_END 0 /* end of script */ +#define TEST_OP_SET_SCID_LEN 1 /* change SCID length */ +#define TEST_OP_SET_INIT_LARGEST_PN 2 /* set initial largest PN */ +#define TEST_OP_ADD_RX_DCID 3 /* register an RX DCID */ +#define TEST_OP_INJECT 4 /* inject a datagram into demux */ +#define TEST_OP_PROVIDE_SECRET 5 /* provide RX secret */ +#define TEST_OP_PROVIDE_SECRET_INITIAL 6 /* provide RX secret for initial */ +#define TEST_OP_DISCARD_EL 7 /* discard an encryption level */ +#define TEST_OP_CHECK_PKT 8 /* read packet, compare to expected */ +#define TEST_OP_CHECK_NO_PKT 9 /* check no packet is available to read */ + +struct test_op { + unsigned char op; + const unsigned char *buf; + size_t buf_len; + const QUIC_PKT_HDR *hdr; + uint32_t enc_level, suite_id; + QUIC_PN largest_pn; + const QUIC_CONN_ID *dcid; + int (*new_qrl)(QUIC_DEMUX **demux, OSSL_QRL **qrl); +}; + +#define OP_END \ + { TEST_OP_END } +#define OP_SET_SCID_LEN(scid_len) \ + { TEST_OP_SET_SCID_LEN, NULL, 0, NULL, (scid_len), 0, 0, NULL, NULL }, +#define OP_SET_INIT_LARGEST_PN(largest_pn) \ + { TEST_OP_SET_INIT_LARGEST_PN, NULL, 0, NULL, 0, 0, (largest_pn), NULL, NULL }, +#define OP_ADD_RX_DCID(dcid) \ + { TEST_OP_ADD_RX_DCID, NULL, 0, NULL, 0, 0, 0, &(dcid), NULL }, +#define OP_INJECT(dgram) \ + { TEST_OP_INJECT, (dgram), sizeof(dgram), NULL, 0, 0, 0, NULL }, +#define OP_PROVIDE_SECRET(el, suite, key) \ + { \ + TEST_OP_PROVIDE_SECRET, (key), sizeof(key), \ + NULL, (el), (suite), 0, NULL, NULL \ + }, +#define OP_PROVIDE_SECRET_INITIAL(dcid) \ + { TEST_OP_PROVIDE_SECRET_INITIAL, NULL, 0, NULL, 0, 0, 0, &(dcid), NULL }, +#define OP_DISCARD_EL(el) \ + { TEST_OP_DISCARD_EL, NULL, 0, NULL, (el), 0, 0, NULL, NULL }, +#define OP_CHECK_PKT(expect_hdr, expect_body) \ + { \ + TEST_OP_CHECK_PKT, (expect_body), sizeof(expect_body), \ + &(expect_hdr), 0, 0, 0, NULL, NULL \ + }, +#define OP_CHECK_NO_PKT() \ + { TEST_OP_CHECK_NO_PKT, NULL, 0, NULL, 0, 0, 0, NULL, NULL }, + +#define OP_INJECT_N(n) \ + OP_INJECT(script_##n##_in) +#define OP_CHECK_PKT_N(n) \ + OP_CHECK_PKT(script_##n##_expect_hdr, script_##n##_body) + +#define OP_INJECT_CHECK(n) \ + OP_INJECT_N(n) \ + OP_CHECK_PKT_N(n) + +/* 1. RFC 9001 - A.3 Server Initial */ +static const unsigned char script_1_in[] = { + 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, + 0x42, 0x62, 0xb5, 0x00, 0x40, 0x75, 0xc0, 0xd9, 0x5a, 0x48, 0x2c, 0xd0, + 0x99, 0x1c, 0xd2, 0x5b, 0x0a, 0xac, 0x40, 0x6a, 0x58, 0x16, 0xb6, 0x39, + 0x41, 0x00, 0xf3, 0x7a, 0x1c, 0x69, 0x79, 0x75, 0x54, 0x78, 0x0b, 0xb3, + 0x8c, 0xc5, 0xa9, 0x9f, 0x5e, 0xde, 0x4c, 0xf7, 0x3c, 0x3e, 0xc2, 0x49, + 0x3a, 0x18, 0x39, 0xb3, 0xdb, 0xcb, 0xa3, 0xf6, 0xea, 0x46, 0xc5, 0xb7, + 0x68, 0x4d, 0xf3, 0x54, 0x8e, 0x7d, 0xde, 0xb9, 0xc3, 0xbf, 0x9c, 0x73, + 0xcc, 0x3f, 0x3b, 0xde, 0xd7, 0x4b, 0x56, 0x2b, 0xfb, 0x19, 0xfb, 0x84, + 0x02, 0x2f, 0x8e, 0xf4, 0xcd, 0xd9, 0x37, 0x95, 0xd7, 0x7d, 0x06, 0xed, + 0xbb, 0x7a, 0xaf, 0x2f, 0x58, 0x89, 0x18, 0x50, 0xab, 0xbd, 0xca, 0x3d, + 0x20, 0x39, 0x8c, 0x27, 0x64, 0x56, 0xcb, 0xc4, 0x21, 0x58, 0x40, 0x7d, + 0xd0, 0x74, 0xee +}; + +static const unsigned char script_1_body[] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, 0x00, + 0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1, 0x63, + 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf, 0xc7, 0x98, + 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, 0x04, 0x5a, 0x12, 0x00, + 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, + 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69, 0x0b, 0x84, 0xd0, 0x8a, 0x60, + 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, + 0x4d, 0x53, 0x11, 0xbc, 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, + 0x02, 0x03, 0x04 +}; + +static const QUIC_CONN_ID script_1_dcid = { + 8, { 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08 } +}; + +static const QUIC_PKT_HDR script_1_expect_hdr = { + QUIC_PKT_TYPE_INITIAL, + 0, 0, 2, 0, 1, 1, { 0, {0} }, + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, + { 0, 1, 0, 0 }, + NULL, 0, + 99, NULL +}; + +static const struct test_op script_1[] = { + OP_SET_SCID_LEN(2) + OP_SET_INIT_LARGEST_PN(0) + OP_ADD_RX_DCID(empty_conn_id) + OP_PROVIDE_SECRET_INITIAL(script_1_dcid) + OP_INJECT_CHECK(1) + OP_CHECK_NO_PKT() + OP_END +}; + +/* 2. RFC 9001 - A.5 ChaCha20-Poly1305 Short Header Packet */ +static const unsigned char script_2_in[] = { + 0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, + 0x80, 0x57, 0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb +}; + +static const unsigned char script_2_secret[] = { + 0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, 0x27, + 0x48, 0xad, 0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0, 0x7d, 0x60, + 0x60, 0xf6, 0x88, 0xf3, 0x0f, 0x21, 0x63, 0x2b +}; + +static const unsigned char script_2_body[] = { + 0x01 +}; + +static const QUIC_PKT_HDR script_2_expect_hdr = { + QUIC_PKT_TYPE_1RTT, + 0, 0, 3, 0, 1, 0, {0, {0}}, {0, {0}}, + {0x00, 0xbf, 0xf4, 0x00}, + NULL, 0, + 1, NULL +}; + +static const struct test_op script_2[] = { + OP_SET_INIT_LARGEST_PN(654360560) + OP_ADD_RX_DCID(empty_conn_id) + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, QRL_SUITE_CHACHA20POLY1305, + script_2_secret) + OP_INJECT_CHECK(2) + OP_CHECK_NO_PKT() + OP_END +}; + +/* 3. Real World - Version Negotiation Response */ +static const unsigned char script_3_in[] = { + 0xc7, /* Long; Random Bits */ + 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 const QUIC_PKT_HDR script_3_expect_hdr = { + QUIC_PKT_TYPE_VERSION_NEG, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 0, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 0, /* Version */ + {0, {0}}, /* DCID */ + {12, {0x35, 0x3c, 0x1b, 0x97, 0xca, 0xf8, /* SCID */ + 0x99, 0x11, 0x39, 0xad, 0x79, 0x1f}}, + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 8, NULL +}; + +static const unsigned char script_3_body[] = { + 0x00, 0x00, 0x00, 0x01, + 0xaa, 0x9a, 0x3a, 0x9a +}; + +static const struct test_op script_3[] = { + OP_ADD_RX_DCID(empty_conn_id) + OP_INJECT_CHECK(3) + OP_CHECK_NO_PKT() + OP_END +}; + +/* 4. Real World - Retry (S2C) */ +static const unsigned char script_4_in[] = { + 0xf0, /* Long; Retry */ + 0x00, 0x00, 0x00, 0x01, /* Version 1 */ + 0x00, /* DCID */ + 0x04, 0xad, 0x15, 0x3f, 0xae, /* SCID */ + /* Retry Token, including 16-byte Retry Integrity Tag */ + 0xf6, 0x8b, 0x6e, 0xa3, 0xdc, 0x40, 0x38, 0xc6, 0xa5, 0x99, 0x1c, 0xa9, + 0x77, 0xe6, 0x1d, 0x4f, 0x09, 0x36, 0x12, 0x26, 0x00, 0x56, 0x0b, 0x29, + 0x7d, 0x5e, 0xda, 0x39, 0xc6, 0x61, 0x57, 0x69, 0x15, 0xff, 0x93, 0x39, + 0x95, 0xf0, 0x57, 0xf1, 0xe5, 0x36, 0x08, 0xad, 0xd2, 0x75, 0xa9, 0x68, + 0x29, 0xed, 0xaa, 0x03, 0x0e, 0x5f, 0xac, 0xbd, 0x26, 0x07, 0x95, 0x4e, + 0x48, 0x61, 0x26, 0xc5, 0xe2, 0x6c, 0x60, 0xbf, 0xa8, 0x6f, 0x51, 0xbb, + 0x1d, 0xf7, 0x98, 0x95, 0x3b, 0x2c, 0x50, 0x79, 0xcc, 0xde, 0x27, 0x84, + 0x44, 0x9b, 0xb2, 0x4a, 0x94, 0x4d, 0x4d, 0x3d, 0xbc, 0x00, 0x9d, 0x69, + 0xad, 0x45, 0x89, 0x04, 0x48, 0xca, 0x04, 0xf6, 0x3a, 0x62, 0xc1, 0x38, + 0x9d, 0x82, 0xb3, 0x45, 0x62, 0x4c, +}; + +static const QUIC_PKT_HDR script_4_expect_hdr = { + QUIC_PKT_TYPE_RETRY, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 0, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 1, /* Version */ + {0, {0}}, /* DCID */ + {4, {0xad, 0x15, 0x3f, 0xae}}, /* SCID */ + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 114, NULL +}; + +static const unsigned char script_4_body[] = { + 0xf6, 0x8b, 0x6e, 0xa3, 0xdc, 0x40, 0x38, 0xc6, 0xa5, 0x99, 0x1c, 0xa9, + 0x77, 0xe6, 0x1d, 0x4f, 0x09, 0x36, 0x12, 0x26, 0x00, 0x56, 0x0b, 0x29, + 0x7d, 0x5e, 0xda, 0x39, 0xc6, 0x61, 0x57, 0x69, 0x15, 0xff, 0x93, 0x39, + 0x95, 0xf0, 0x57, 0xf1, 0xe5, 0x36, 0x08, 0xad, 0xd2, 0x75, 0xa9, 0x68, + 0x29, 0xed, 0xaa, 0x03, 0x0e, 0x5f, 0xac, 0xbd, 0x26, 0x07, 0x95, 0x4e, + 0x48, 0x61, 0x26, 0xc5, 0xe2, 0x6c, 0x60, 0xbf, 0xa8, 0x6f, 0x51, 0xbb, + 0x1d, 0xf7, 0x98, 0x95, 0x3b, 0x2c, 0x50, 0x79, 0xcc, 0xde, 0x27, 0x84, + 0x44, 0x9b, 0xb2, 0x4a, 0x94, 0x4d, 0x4d, 0x3d, 0xbc, 0x00, 0x9d, 0x69, + 0xad, 0x45, 0x89, 0x04, 0x48, 0xca, 0x04, 0xf6, 0x3a, 0x62, 0xc1, 0x38, + 0x9d, 0x82, 0xb3, 0x45, 0x62, 0x4c +}; + +static const struct test_op script_4[] = { + OP_ADD_RX_DCID(empty_conn_id) + OP_INJECT_CHECK(4) + OP_CHECK_NO_PKT() + OP_END +}; + +/* + * 5. Real World - S2C Multiple Packets + * - Initial, Handshake, 1-RTT (AES-128-GCM/SHA256) + */ +static const QUIC_CONN_ID script_5_c2s_init_dcid = { + 4, {0xad, 0x15, 0x3f, 0xae} +}; + +static const unsigned char script_5_handshake_secret[32] = { + 0x5e, 0xc6, 0x4a, 0x4d, 0x0d, 0x40, 0x43, 0x3b, 0xd5, 0xbd, 0xe0, 0x19, + 0x71, 0x47, 0x56, 0xf3, 0x59, 0x3a, 0xa6, 0xc9, 0x3e, 0xdc, 0x81, 0x1e, + 0xc7, 0x72, 0x9d, 0x83, 0xd8, 0x8f, 0x88, 0x77 +}; + +static const unsigned char script_5_1rtt_secret[32] = { + 0x53, 0xf2, 0x1b, 0x94, 0xa7, 0x65, 0xf7, 0x76, 0xfb, 0x06, 0x27, 0xaa, + 0xd2, 0x3f, 0xe0, 0x9a, 0xbb, 0xcf, 0x99, 0x6f, 0x13, 0x2c, 0x6a, 0x37, + 0x95, 0xf3, 0xda, 0x21, 0xcb, 0xcb, 0xa5, 0x26, +}; + +static const unsigned char script_5_in[] = { + /* First Packet: Initial */ + 0xc4, /* Long, Initial, PN Length=2 bytes */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID */ + 0x04, 0x83, 0xd0, 0x0a, 0x27, /* SCID */ + 0x00, /* Token Length */ + 0x41, 0xd2, /* Length (466) */ + 0xe3, 0xab, /* PN (0) */ + 0x22, 0x35, 0x34, 0x12, 0xcf, 0x20, 0x2b, 0x16, 0xaf, 0x08, 0xd4, 0xe0, + 0x94, 0x8b, 0x1e, 0x62, 0xdf, 0x31, 0x61, 0xcc, 0xf9, 0xfa, 0x66, 0x4f, + 0x18, 0x61, 0x07, 0xcb, 0x13, 0xd3, 0xf9, 0xbf, 0xe2, 0x8e, 0x25, 0x8d, + 0xd1, 0xdf, 0x58, 0x9c, 0x05, 0x20, 0xf9, 0xf2, 0x01, 0x20, 0xe9, 0x39, + 0xc3, 0x80, 0x77, 0xec, 0xa4, 0x57, 0xcf, 0x57, 0x8c, 0xdd, 0x68, 0x82, + 0x91, 0xfe, 0x71, 0xa0, 0xfa, 0x56, 0x4c, 0xf2, 0xe7, 0x2b, 0xd0, 0xc0, + 0xda, 0x81, 0xe2, 0x39, 0xb5, 0xf0, 0x0f, 0xd9, 0x07, 0xd5, 0x67, 0x09, + 0x02, 0xf0, 0xff, 0x74, 0xb0, 0xa0, 0xd9, 0x3a, 0x7e, 0xb6, 0x57, 0x82, + 0x47, 0x18, 0x66, 0xed, 0xe2, 0x18, 0x4d, 0xc2, 0x5c, 0x9f, 0x05, 0x09, + 0x18, 0x24, 0x0e, 0x3f, 0x3d, 0xf9, 0x15, 0x8b, 0x08, 0xfd, 0x25, 0xe9, + 0xc9, 0xb7, 0x8c, 0x18, 0x7b, 0xf3, 0x37, 0x58, 0xf0, 0xf0, 0xac, 0x33, + 0x55, 0x3f, 0x39, 0xbc, 0x62, 0x03, 0x8a, 0xc0, 0xd6, 0xcc, 0x49, 0x47, + 0xeb, 0x85, 0xb6, 0x72, 0xd7, 0xf8, 0xdc, 0x01, 0x32, 0xec, 0x1b, 0x4e, + 0x38, 0x6e, 0x2c, 0xc5, 0x80, 0xf2, 0x43, 0x4a, 0xf5, 0xe5, 0xa2, 0xf8, + 0x76, 0xa7, 0xa8, 0x57, 0x32, 0x67, 0x72, 0xeb, 0x82, 0xac, 0x3e, 0xc0, + 0x15, 0x67, 0xac, 0x32, 0x19, 0x18, 0x0a, 0xef, 0x20, 0xa1, 0xe8, 0xaf, + 0xac, 0x33, 0x87, 0x4c, 0x55, 0x05, 0x9b, 0x78, 0xf0, 0x3a, 0xce, 0x02, + 0x28, 0x06, 0x84, 0x61, 0x97, 0xac, 0x87, 0x8f, 0x25, 0xe7, 0x1b, 0xa3, + 0x02, 0x08, 0x4c, 0x2e, 0xef, 0xbd, 0x4f, 0x82, 0xe7, 0x37, 0x6c, 0x27, + 0x6f, 0x85, 0xb4, 0xbc, 0x79, 0x38, 0x45, 0x80, 0x8a, 0xda, 0x2f, 0x11, + 0x11, 0xac, 0x9c, 0xf3, 0x93, 0xc1, 0x49, 0x1b, 0x94, 0x12, 0x77, 0x07, + 0xdc, 0xbf, 0xc2, 0xfd, 0x8b, 0xf6, 0xf1, 0x66, 0x1c, 0x7f, 0x07, 0xbf, + 0x1f, 0xae, 0x27, 0x6c, 0x66, 0xe9, 0xa3, 0x64, 0x7a, 0x96, 0x78, 0x45, + 0xfe, 0x4b, 0x8c, 0x6f, 0x7f, 0x03, 0x47, 0x3c, 0xd7, 0xf7, 0x63, 0x92, + 0x58, 0x5b, 0x63, 0x83, 0x03, 0x05, 0xc3, 0x5d, 0x36, 0x62, 0x63, 0x5e, + 0xcf, 0xfe, 0x0a, 0x29, 0xfa, 0xeb, 0xc8, 0xaf, 0xce, 0x31, 0x07, 0x6a, + 0x09, 0x41, 0xc0, 0x2d, 0x98, 0x70, 0x05, 0x3b, 0x41, 0xfc, 0x7d, 0x61, + 0xe0, 0x41, 0x7d, 0x13, 0x41, 0x51, 0x52, 0xb4, 0x78, 0xd5, 0x46, 0x51, + 0x3b, 0xf1, 0xcd, 0xcc, 0x2e, 0x49, 0x30, 0x8b, 0x2a, 0xd2, 0xe6, 0x69, + 0xb5, 0x6b, 0x7a, 0xf4, 0xbb, 0xd1, 0xf8, 0x4a, 0xe8, 0x53, 0x10, 0x46, + 0x85, 0x8d, 0x66, 0x8e, 0x2b, 0xe8, 0x5d, 0xab, 0x7e, 0xfe, 0x5a, 0x79, + 0xcf, 0xc5, 0x0c, 0x30, 0x9e, 0x98, 0x02, 0xb3, 0xa6, 0xd5, 0xfa, 0x25, + 0xa8, 0xc8, 0xc1, 0xd9, 0x51, 0x60, 0x57, 0x5d, 0xfe, 0x75, 0x97, 0x05, + 0xda, 0xbb, 0xc6, 0x6a, 0xbe, 0x5c, 0xa5, 0x65, 0x0a, 0x12, 0x33, 0x1c, + 0xdf, 0xee, 0x08, 0xa9, 0x13, 0x13, 0x28, 0xce, 0x61, 0x59, 0xd1, 0x4e, + 0xc7, 0x74, 0xfd, 0x64, 0xde, 0x08, 0xce, 0xda, 0x3f, 0xec, 0xad, 0xc9, + 0xe1, 0xf9, 0x1f, 0x74, 0xf6, 0x86, 0x37, 0x6a, 0xa0, 0xc8, 0x0b, 0x1b, + 0x94, 0x98, 0x86, 0x81, 0x3b, 0xfc, 0x47, 0x6c, 0xc9, 0x3e, 0x3c, 0x30, + 0xc5, 0x9e, 0xb2, 0x32, 0x47, 0xf5, 0x0c, 0x6f, + + /* Second Packet: Handshake */ + 0xe6, /* Long, Handshake, PN Length=2 bytes */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID */ + 0x04, 0x83, 0xd0, 0x0a, 0x27, /* SCID */ + 0x42, 0x9c, /* Length (668) */ + 0x9c, 0x55, /* PN (0) */ + 0x55, 0xd4, 0x50, 0x02, 0x1a, 0x57, 0x84, 0x22, 0xcd, 0x01, 0xe5, 0x42, + 0x1b, 0x1e, 0x06, 0xf1, 0x86, 0xe2, 0x90, 0xf8, 0x9c, 0x3d, 0xa2, 0x7c, + 0xde, 0x2b, 0xc9, 0x2e, 0xcd, 0xa8, 0x4f, 0x5a, 0x20, 0xca, 0x96, 0xb6, + 0x11, 0x4b, 0xc8, 0x71, 0x32, 0xb5, 0xc7, 0x1a, 0x69, 0x7f, 0x1e, 0x37, + 0x49, 0xfb, 0x08, 0xce, 0x83, 0x5f, 0x02, 0x6d, 0x8a, 0x8f, 0xe7, 0x5d, + 0xe1, 0x34, 0x31, 0x22, 0x53, 0x53, 0x32, 0xcb, 0x04, 0x21, 0xce, 0xbc, + 0xa5, 0x1b, 0xdd, 0x4d, 0xd5, 0x1c, 0xd6, 0x5d, 0x88, 0x29, 0x5a, 0x19, + 0x71, 0x6a, 0xc2, 0xfa, 0xb7, 0xb4, 0x7d, 0xd1, 0x72, 0x93, 0x8f, 0x7c, + 0xb5, 0x36, 0x1b, 0xea, 0xf3, 0xf1, 0xd7, 0x6e, 0xd3, 0x91, 0x96, 0x62, + 0x4d, 0xc6, 0xec, 0xb7, 0xb0, 0xb7, 0x9b, 0x95, 0x8b, 0x14, 0x8d, 0x1a, + 0x0d, 0xb6, 0x3e, 0xec, 0xfe, 0x3b, 0x51, 0xea, 0x1a, 0x05, 0x14, 0x12, + 0x93, 0x0e, 0x7e, 0xe6, 0xa2, 0xc5, 0x22, 0x87, 0x65, 0xf8, 0x5d, 0x3c, + 0x55, 0x18, 0xcb, 0xe9, 0xef, 0x23, 0x43, 0xfe, 0xe8, 0x0d, 0xb2, 0x0f, + 0xc5, 0xf4, 0xb3, 0xde, 0x0c, 0xea, 0xa4, 0x48, 0x8e, 0xbf, 0x1f, 0xc7, + 0x99, 0x53, 0x8c, 0xc1, 0x3d, 0xba, 0xf4, 0x8e, 0x8e, 0x02, 0x52, 0xf6, + 0x1f, 0xcf, 0x1d, 0xaa, 0xb3, 0xcb, 0x08, 0xc2, 0xe1, 0x70, 0x68, 0x74, + 0x78, 0xa9, 0x30, 0x67, 0xba, 0x2b, 0xea, 0x35, 0x63, 0x47, 0xff, 0x29, + 0x73, 0x29, 0xc6, 0xe8, 0x08, 0xa9, 0x1e, 0x8f, 0x28, 0x41, 0xa4, 0x24, + 0x54, 0x26, 0x5f, 0x42, 0x77, 0xb1, 0x2b, 0x3d, 0x65, 0x67, 0x60, 0xa7, + 0x23, 0x0d, 0xa7, 0xf4, 0xd6, 0xe9, 0x4e, 0x58, 0x43, 0x9f, 0x3c, 0x9e, + 0x77, 0x61, 0xe5, 0x04, 0x4f, 0x73, 0xc9, 0x10, 0x79, 0xd0, 0xda, 0x3b, + 0xc6, 0x19, 0x93, 0x9f, 0x48, 0x3b, 0x76, 0x38, 0xa1, 0x72, 0x49, 0x7d, + 0x86, 0x7f, 0xe8, 0x1b, 0xa9, 0x5b, 0xc0, 0x47, 0xa0, 0x9c, 0x3f, 0x65, + 0x60, 0x76, 0x59, 0xaf, 0x20, 0x2d, 0x40, 0xa6, 0x80, 0x49, 0x5a, 0x8f, + 0x09, 0xf8, 0xf6, 0x97, 0xc1, 0xbd, 0xe1, 0x9f, 0x9b, 0xa2, 0x4c, 0x7b, + 0x88, 0xac, 0xbe, 0x4b, 0x11, 0x28, 0xd7, 0x67, 0xe6, 0xad, 0xaf, 0xd0, + 0xad, 0x01, 0x29, 0xa4, 0x4a, 0xc4, 0xb8, 0x2e, 0x42, 0x79, 0x24, 0x9e, + 0xd5, 0x34, 0xae, 0x45, 0xf1, 0x0b, 0x38, 0x4a, 0x76, 0xfb, 0x50, 0xa2, + 0x99, 0xc9, 0x5b, 0x6d, 0xc0, 0xb7, 0x55, 0xd8, 0x8d, 0x49, 0xdd, 0x1b, + 0xb8, 0xec, 0x10, 0x57, 0x9e, 0x33, 0xb4, 0x10, 0x16, 0x19, 0xac, 0x69, + 0xa2, 0x19, 0x1b, 0xd0, 0x77, 0x45, 0xeb, 0x49, 0x5c, 0xc5, 0x7c, 0xbe, + 0x4b, 0x4a, 0x22, 0x5c, 0x3d, 0x0e, 0x6e, 0xe5, 0x4b, 0x36, 0x06, 0x63, + 0x03, 0x97, 0xab, 0xed, 0xdc, 0xea, 0x64, 0xc2, 0x70, 0xb6, 0x7e, 0x35, + 0xfb, 0x13, 0x66, 0x37, 0xa3, 0x3f, 0x28, 0x16, 0x6c, 0xe7, 0xd4, 0xe6, + 0xca, 0x26, 0x0f, 0x19, 0xdd, 0x02, 0xae, 0xc1, 0xcf, 0x18, 0x7d, 0x56, + 0xe6, 0x52, 0xf3, 0x37, 0xb5, 0x86, 0x9d, 0x1d, 0x55, 0xb3, 0x95, 0x19, + 0x19, 0xa5, 0x44, 0x95, 0x81, 0xed, 0x02, 0x18, 0xf1, 0x85, 0x57, 0x78, + 0x28, 0xc4, 0x9a, 0xba, 0xe8, 0x5e, 0x22, 0x8d, 0xc1, 0x7b, 0x2a, 0x8a, + 0xc8, 0xb9, 0xdd, 0x82, 0xb2, 0x7b, 0x9f, 0x3d, 0xf5, 0x27, 0x2a, 0x48, + 0x53, 0xc7, 0xa0, 0x70, 0x0e, 0x9d, 0x61, 0xaa, 0xe2, 0xad, 0x28, 0xf2, + 0xb4, 0xfc, 0x56, 0x6b, 0x89, 0xe7, 0xf9, 0x51, 0xc9, 0xe9, 0xd3, 0x8a, + 0x8c, 0x7e, 0x86, 0xdd, 0xba, 0x2f, 0x39, 0xbf, 0x26, 0x62, 0x23, 0xd6, + 0x98, 0x6d, 0x3e, 0x72, 0xd7, 0x1b, 0xe1, 0x62, 0x94, 0x35, 0xe2, 0x18, + 0x19, 0x46, 0xb8, 0x2c, 0xb5, 0x8f, 0x8f, 0xb0, 0x5b, 0x76, 0x7b, 0x7e, + 0xb8, 0xc6, 0xb7, 0xe9, 0x4e, 0x9d, 0x30, 0x68, 0x03, 0x1e, 0x19, 0x73, + 0xc5, 0x3e, 0x24, 0xe2, 0x95, 0x60, 0x1b, 0x27, 0x93, 0x7c, 0x17, 0xc2, + 0xc6, 0xa3, 0xbd, 0xbd, 0x70, 0xc6, 0x60, 0x59, 0xc8, 0x5c, 0xd7, 0x9a, + 0xc4, 0x29, 0xac, 0x0f, 0xaa, 0x0d, 0xa9, 0x92, 0xa3, 0x95, 0xd7, 0x0f, + 0x6f, 0x74, 0x99, 0x9b, 0xc1, 0xd3, 0x68, 0x6d, 0xac, 0x82, 0x2d, 0x32, + 0x41, 0x9e, 0x0c, 0xf7, 0x31, 0x59, 0x4c, 0x93, 0x1c, 0x3b, 0x71, 0x69, + 0xcf, 0xc5, 0xca, 0x2b, 0xdf, 0xe7, 0xaa, 0xfd, 0x1d, 0x71, 0x01, 0x7e, + 0x1c, 0x70, 0x62, 0x20, 0x61, 0xf8, 0x35, 0xc1, 0x71, 0xe7, 0x02, 0x0d, + 0x88, 0x44, 0xd9, 0x00, 0xc5, 0xcc, 0x63, 0xe4, 0xf0, 0x86, 0xa7, 0xd0, + 0xfe, 0xcc, 0xb7, 0x1d, 0xfc, 0x21, 0x61, 0x54, 0x15, 0xea, 0x81, 0x5e, + 0xc0, 0x31, 0xfa, 0xbf, 0x7d, 0xb9, 0x3b, 0xa2, 0x1e, 0x42, 0x73, 0x05, + 0x3c, 0xdb, 0x21, 0x59, 0x4f, 0x63, + + /* Third Packet: 1-RTT */ + 0x5f, /* Short, 1-RTT, Spin=0, KP=0, PN Length=2 bytes */ + 0x68, 0x47, /* PN (0) */ + 0xa3, 0x3c, 0xa5, 0x27, 0x5e, 0xf9, 0x8d, 0xec, 0xea, 0x6c, 0x09, 0x18, + 0x40, 0x80, 0xee, 0x9f, 0x6f, 0x73, 0x5c, 0x49, 0xe3, 0xec, 0xb7, 0x58, + 0x05, 0x66, 0x8f, 0xa3, 0x52, 0x37, 0xa1, 0x22, 0x1f, 0xc6, 0x92, 0xd6, + 0x59, 0x04, 0x99, 0xcb, 0x44, 0xef, 0x66, 0x05, 0x2d, 0xd0, 0x85, 0x24, + 0xbb, 0xe3, 0xa1, 0xd1, 0xbe, 0xf7, 0x54, 0xad, 0x65, 0xf4, 0xd4, 0x59, + 0x54, 0x87, 0x4e, 0x22, 0x4f, 0x06, 0x07, 0xa7, 0x8a, 0x14, 0x89, 0xd1, + 0x3f, 0xd3, 0xe4, 0x6f, 0x71, 0x8f, 0x9a, 0xd2, 0x3b, 0x61, 0x0a, 0xba, + 0x9a, 0x31, 0x56, 0xc7, +}; + +static const QUIC_PKT_HDR script_5a_expect_hdr = { + QUIC_PKT_TYPE_INITIAL, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 2, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 1, /* Version */ + {0, {0}}, /* DCID */ + {4, {0x83, 0xd0, 0x0a, 0x27}}, /* SCID */ + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 448, NULL +}; + +static const unsigned char script_5a_body[] = { + 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, + 0x00, 0x56, 0x03, 0x03, 0xe2, 0xd2, 0x0a, 0x3b, 0xa2, 0xc4, 0xd2, 0x29, + 0xc8, 0xe8, 0xba, 0x23, 0x31, 0x88, 0x2c, 0x71, 0xeb, 0xba, 0x42, 0x5f, + 0x94, 0xe9, 0x0a, 0x90, 0x35, 0x31, 0x1e, 0xca, 0xed, 0xf8, 0x8a, 0x8d, + 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, + 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x96, 0x0b, 0x4b, 0x30, + 0x66, 0x3a, 0x75, 0x01, 0x4a, 0xdc, 0x2a, 0x75, 0x1f, 0xce, 0x7a, 0x30, + 0x9d, 0x00, 0xca, 0x20, 0xb4, 0xe0, 0x6b, 0x81, 0x23, 0x18, 0x0b, 0x20, + 0x1f, 0x54, 0x86, 0x1d, +}; + +static const QUIC_PKT_HDR script_5b_expect_hdr = { + QUIC_PKT_TYPE_HANDSHAKE, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 2, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 1, /* Version */ + {0, {0}}, /* DCID */ + {4, {0x83, 0xd0, 0x0a, 0x27}}, /* SCID */ + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 650, NULL +}; + +static const unsigned char script_5b_body[] = { + 0x06, 0x00, 0x42, 0x86, 0x08, 0x00, 0x00, 0x7d, 0x00, 0x7b, 0x00, 0x10, + 0x00, 0x08, 0x00, 0x06, 0x05, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x00, 0x39, + 0x00, 0x6b, 0x4b, 0x20, 0x0b, 0x1b, 0xe1, 0x1f, 0xd0, 0x78, 0xc0, 0x69, + 0x72, 0x9c, 0xe2, 0xf7, 0x05, 0x04, 0x80, 0x08, 0x00, 0x00, 0x06, 0x04, + 0x80, 0x08, 0x00, 0x00, 0x07, 0x04, 0x80, 0x08, 0x00, 0x00, 0x04, 0x04, + 0x80, 0x0c, 0x00, 0x00, 0x08, 0x02, 0x40, 0x64, 0x09, 0x02, 0x40, 0x64, + 0x01, 0x04, 0x80, 0x00, 0x75, 0x30, 0x03, 0x02, 0x45, 0xac, 0x0b, 0x01, + 0x1a, 0x0c, 0x00, 0x02, 0x10, 0x41, 0x94, 0x41, 0x8d, 0x0d, 0xfb, 0x60, + 0x7b, 0xdc, 0xcc, 0xa2, 0x9c, 0x3e, 0xa5, 0xdf, 0x8d, 0x00, 0x08, 0x2d, + 0x71, 0x8a, 0x38, 0xdf, 0xdd, 0xe0, 0x03, 0x0e, 0x01, 0x04, 0x0f, 0x04, + 0x83, 0xd0, 0x0a, 0x27, 0x10, 0x04, 0xad, 0x15, 0x3f, 0xae, 0x20, 0x01, + 0x00, 0x0b, 0x00, 0x01, 0x8f, 0x00, 0x00, 0x01, 0x8b, 0x00, 0x01, 0x86, + 0x30, 0x82, 0x01, 0x82, 0x30, 0x82, 0x01, 0x29, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x0a, 0x73, 0x0f, 0x86, 0x18, 0xf2, 0xc3, 0x30, 0x01, + 0xd2, 0xc0, 0xc1, 0x62, 0x52, 0x13, 0xf1, 0x9c, 0x13, 0x39, 0xb5, 0x30, + 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, + 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0c, + 0x6d, 0x61, 0x70, 0x61, 0x6b, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x38, 0x30, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x31, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x39, 0x30, + 0x31, 0x31, 0x32, 0x30, 0x30, 0x31, 0x38, 0x5a, 0x30, 0x17, 0x31, 0x15, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0c, 0x6d, 0x61, 0x70, + 0x61, 0x6b, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x30, 0x59, 0x30, + 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, + 0x67, 0xf4, 0xd3, 0x8f, 0x15, 0x6d, 0xee, 0x85, 0xcc, 0x2a, 0x77, 0xfc, + 0x0b, 0x8f, 0x9f, 0xcf, 0xa9, 0x95, 0x5d, 0x5b, 0xcd, 0xb7, 0x8b, 0xba, + 0x31, 0x0a, 0x73, 0x62, 0xc5, 0xd0, 0x0e, 0x07, 0x90, 0xae, 0x38, 0x43, + 0x79, 0xce, 0x5e, 0x33, 0xad, 0x31, 0xbf, 0x9f, 0x2a, 0x56, 0x83, 0xa5, + 0x24, 0x16, 0xab, 0x0c, 0xf1, 0x64, 0xbe, 0xe4, 0x93, 0xb5, 0x89, 0xd6, + 0x05, 0xe4, 0xf7, 0x7b, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x02, 0x64, 0x0f, 0x55, 0x69, + 0x14, 0x91, 0x19, 0xed, 0xf9, 0x1a, 0xe9, 0x1d, 0xa5, 0x5a, 0xd0, 0x48, + 0x96, 0x9f, 0x60, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0x02, 0x64, 0x0f, 0x55, 0x69, 0x14, 0x91, 0x19, + 0xed, 0xf9, 0x1a, 0xe9, 0x1d, 0xa5, 0x5a, 0xd0, 0x48, 0x96, 0x9f, 0x60, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, + 0x0a, 0x82, 0x92, 0x6e, 0xd3, 0xc6, 0x66, 0xd9, 0xd3, 0x75, 0xff, 0x71, + 0x3b, 0x61, 0x46, 0x21, 0x00, 0xe6, 0x21, 0x5d, 0x9c, 0x86, 0xe9, 0x65, + 0x40, 0x4f, 0xeb, 0x70, 0x4f, 0x2c, 0xad, 0x00, 0x02, 0x20, 0x08, 0xc2, + 0x07, 0x5d, 0x16, 0xfc, 0x54, 0x34, 0x2b, 0xb4, 0x18, 0x67, 0x44, 0x81, + 0xc9, 0xa9, 0x67, 0x2e, 0xce, 0xa1, 0x02, 0x9f, 0x3b, 0xe5, 0x61, 0x16, + 0x0b, 0x50, 0xf6, 0xa1, 0x50, 0x94, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x4a, + 0x04, 0x03, 0x00, 0x46, 0x30, 0x44, 0x02, 0x20, 0x7d, 0x57, 0x17, 0x14, + 0x46, 0x09, 0x95, 0x70, 0x09, 0x45, 0xe8, 0x9e, 0x5c, 0x87, 0x55, 0xd9, + 0x08, 0xc6, 0x5e, 0x47, 0x73, 0x5e, 0xb1, 0xc9, 0xef, 0xcb, 0xe5, 0x7f, + 0xcc, 0xb0, 0x28, 0xbc, 0x02, 0x20, 0x5d, 0xe4, 0x2b, 0x83, 0xd9, 0x78, + 0x75, 0x45, 0xf3, 0x22, 0x2b, 0x38, 0xeb, 0x68, 0xe5, 0x71, 0x5d, 0xcb, + 0xc3, 0x68, 0xb3, 0x0e, 0x7d, 0x5e, 0x1d, 0xc2, 0x1b, 0x8a, 0x62, 0x80, + 0x48, 0x3e, 0x14, 0x00, 0x00, 0x20, 0x37, 0xcd, 0x55, 0xca, 0x3f, 0x4b, + 0xf0, 0x95, 0xf8, 0xe4, 0xfe, 0x59, 0xab, 0xbc, 0xc1, 0x8f, 0x0c, 0x3f, + 0x41, 0x59, 0xf6, 0x96, 0xdb, 0x75, 0xae, 0xe7, 0x86, 0x1a, 0x92, 0xa7, + 0x53, 0x0a, +}; + +static const QUIC_PKT_HDR script_5c_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}, /* PN */ + NULL, 0, /* Token/Token Len */ + 72, NULL +}; + +static const unsigned char script_5c_body[] = { + 0x18, 0x03, 0x00, 0x04, 0x92, 0xec, 0xaa, 0xd6, 0x47, 0xd8, 0x8b, 0x56, + 0x3b, 0x5f, 0x67, 0xe6, 0xb9, 0xb9, 0xca, 0x72, 0xca, 0xf2, 0x49, 0x7d, + 0x18, 0x02, 0x00, 0x04, 0xa9, 0x6e, 0x9b, 0x84, 0x26, 0x43, 0x00, 0xc7, + 0x55, 0x71, 0x67, 0x2e, 0x52, 0xdd, 0x47, 0xfd, 0x06, 0x51, 0x33, 0x08, + 0x18, 0x01, 0x00, 0x04, 0x36, 0xd5, 0x1f, 0x06, 0x4e, 0xbf, 0xb4, 0xc9, + 0xef, 0x97, 0x1e, 0x9a, 0x3c, 0xab, 0x1e, 0xfc, 0xb7, 0x90, 0xc3, 0x1a, +}; + +static const struct test_op script_5[] = { + OP_ADD_RX_DCID(empty_conn_id) + OP_PROVIDE_SECRET_INITIAL(script_5_c2s_init_dcid) + OP_INJECT_N(5) + OP_CHECK_PKT_N(5a) + OP_CHECK_NO_PKT() /* not got secret for next packet yet */ + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE, + QRL_SUITE_AES128GCM, script_5_handshake_secret) + OP_CHECK_PKT_N(5b) + OP_CHECK_NO_PKT() /* not got secret for next packet yet */ + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, + QRL_SUITE_AES128GCM, script_5_1rtt_secret) + OP_CHECK_PKT_N(5c) + OP_CHECK_NO_PKT() + + /* Try injecting the packet again */ + OP_INJECT_N(5) + /* + * Initial packet is not output due to receiving a Handshake packet causing + * auto-discard of Initial keys + */ + OP_CHECK_PKT_N(5b) + OP_CHECK_PKT_N(5c) + OP_CHECK_NO_PKT() + /* Try again with discarded keys */ + OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE) + OP_INJECT_N(5) + OP_CHECK_PKT_N(5c) + OP_CHECK_NO_PKT() + /* Try again */ + OP_INJECT_N(5) + OP_CHECK_PKT_N(5c) + OP_CHECK_NO_PKT() + /* Try again with discarded 1-RTT keys */ + OP_DISCARD_EL(QUIC_ENC_LEVEL_1RTT) + OP_INJECT_N(5) + OP_CHECK_NO_PKT() + + /* Recreate QRL, test reading packets received before key */ + OP_SET_SCID_LEN(0) + OP_ADD_RX_DCID(empty_conn_id) + OP_INJECT_N(5) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET_INITIAL(script_5_c2s_init_dcid) + OP_CHECK_PKT_N(5a) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE, + QRL_SUITE_AES128GCM, script_5_handshake_secret) + OP_CHECK_PKT_N(5b) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, + QRL_SUITE_AES128GCM, script_5_1rtt_secret) + OP_CHECK_PKT_N(5c) + OP_CHECK_NO_PKT() + + OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE) + OP_DISCARD_EL(QUIC_ENC_LEVEL_1RTT) + OP_INJECT_N(5) + OP_CHECK_NO_PKT() + + OP_END +}; + +/* + * 6. Real World - S2C Multiple Packets + * - Initial, Handshake, 1-RTT (AES-256-GCM/SHA384) + */ +static const QUIC_CONN_ID script_6_c2s_init_dcid = { + 4, {0xac, 0x88, 0x95, 0xbd} +}; + +static const unsigned char script_6_handshake_secret[48] = { + 0xd1, 0x41, 0xb0, 0xf6, 0x0d, 0x8b, 0xbd, 0xe8, 0x5b, 0xa8, 0xff, 0xd7, + 0x18, 0x9a, 0x23, 0x7b, 0x13, 0x5c, 0x1e, 0x90, 0x1d, 0x08, 0x95, 0xcc, + 0xc5, 0x8e, 0x73, 0x4e, 0x02, 0x6f, 0x3c, 0xb6, 0x26, 0x77, 0x8d, 0x53, + 0xc5, 0x62, 0x9f, 0xb5, 0xf0, 0x88, 0xfb, 0xe5, 0x14, 0x71, 0xab, 0xe6, +}; + +static const unsigned char script_6_1rtt_secret[48] = { + 0x2d, 0x6b, 0x9d, 0xd4, 0x39, 0xa0, 0xe7, 0xff, 0x17, 0xe2, 0xcb, 0x5c, + 0x0d, 0x4a, 0xf6, 0x3f, 0xf4, 0xfe, 0xfc, 0xe5, 0x22, 0xfa, 0xf5, 0x5b, + 0xc0, 0xb2, 0x18, 0xbb, 0x92, 0x4d, 0x35, 0xea, 0x67, 0xa6, 0xe7, 0xc1, + 0x90, 0x10, 0xc9, 0x14, 0x46, 0xf5, 0x95, 0x57, 0x8b, 0x90, 0x88, 0x5d, +}; + +static const unsigned char script_6_in[] = { + /* First Packet: Initial */ + 0xc5, /* Long, Initial, PN Length=2 bytes */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID */ + 0x04, 0x36, 0xf4, 0x75, 0x2d, /* SCID */ + 0x00, /* Token Length */ + 0x41, 0xbe, /* Length (446) */ + 0xa9, 0xe2, /* PN (0) */ + 0x83, 0x39, 0x95, 0x8f, 0x8f, 0x8c, 0xa9, 0xaf, 0x10, 0x29, 0x3d, 0xfc, + 0x56, 0x4a, 0x1c, 0x4b, 0xc9, 0x48, 0xb1, 0xaf, 0x36, 0xd5, 0xac, 0x95, + 0xbf, 0xfd, 0x2c, 0x4d, 0x70, 0x2e, 0x5b, 0x7c, 0x22, 0x5f, 0x5f, 0xee, + 0x10, 0x8f, 0xfb, 0x0b, 0x5f, 0x9d, 0x7e, 0x68, 0x2f, 0x94, 0x0b, 0xdb, + 0xed, 0xef, 0xfa, 0x4e, 0xc6, 0xd5, 0xe7, 0xef, 0xe0, 0x78, 0x3c, 0xdc, + 0xe9, 0xd8, 0xe8, 0x56, 0x71, 0xd7, 0xe7, 0x6c, 0x7f, 0x5d, 0xaa, 0x7a, + 0x52, 0x1d, 0x95, 0x7a, 0x80, 0x70, 0x38, 0xc0, 0x8b, 0xa1, 0x2f, 0x09, + 0x16, 0xd2, 0xec, 0xa3, 0x23, 0x72, 0x45, 0x3c, 0xbd, 0x8c, 0xda, 0xbb, + 0x37, 0x5a, 0x8d, 0xb2, 0x00, 0x7e, 0x67, 0x0c, 0xa0, 0x32, 0xdd, 0x80, + 0x07, 0x71, 0xb0, 0x95, 0x21, 0xbc, 0x1e, 0xbd, 0x63, 0x0a, 0x10, 0xe7, + 0x4b, 0x6e, 0x2e, 0x85, 0x3a, 0x65, 0xf7, 0x06, 0x6e, 0x7e, 0x8f, 0x65, + 0x8c, 0xb1, 0x93, 0xe9, 0x0d, 0xe8, 0x46, 0xe7, 0xcf, 0xa7, 0xd2, 0x8b, + 0x15, 0x23, 0xec, 0xc3, 0xec, 0x44, 0xda, 0x62, 0x15, 0x35, 0x34, 0x2f, + 0x62, 0x77, 0xc8, 0x1f, 0x83, 0x22, 0x00, 0xe5, 0xc0, 0x89, 0xb8, 0x97, + 0xd2, 0x37, 0x02, 0xea, 0xa2, 0x35, 0xbf, 0x19, 0xf0, 0xba, 0x1d, 0xb7, + 0xaa, 0x36, 0xbb, 0x11, 0x60, 0xc3, 0x45, 0x1f, 0xe5, 0x18, 0xde, 0x4c, + 0x01, 0x23, 0x2d, 0x17, 0x78, 0xdd, 0x4c, 0x8a, 0x1e, 0x1b, 0xd4, 0xda, + 0x56, 0x43, 0x13, 0xa4, 0x4f, 0xfd, 0xd5, 0x92, 0x6a, 0x05, 0x5f, 0x14, + 0x63, 0x85, 0x7d, 0xf1, 0x31, 0xb8, 0x27, 0x0b, 0xa6, 0xb5, 0x50, 0xca, + 0x8b, 0x0e, 0xa1, 0x0d, 0xf9, 0xc4, 0xea, 0x6a, 0x6e, 0x4b, 0x6d, 0xdf, + 0x49, 0xe8, 0x32, 0xf6, 0x85, 0xc4, 0x29, 0x26, 0x32, 0xfb, 0x5e, 0xa8, + 0x55, 0x6b, 0x67, 0xe9, 0xaa, 0x35, 0x33, 0x90, 0xd8, 0x2a, 0x71, 0x0b, + 0x6a, 0x48, 0xc4, 0xa3, 0x8b, 0xe0, 0xe7, 0x00, 0x3d, 0xee, 0x30, 0x70, + 0x84, 0xbd, 0xa3, 0x3c, 0x9e, 0xa3, 0x5c, 0x69, 0xab, 0x55, 0x7b, 0xe2, + 0xe5, 0x86, 0x13, 0xcb, 0x93, 0x3f, 0xcb, 0x3e, 0x6d, 0xc9, 0xc2, 0x10, + 0x2b, 0x00, 0x9b, 0x3f, 0x14, 0x4e, 0x04, 0x27, 0xc0, 0xae, 0x1d, 0x48, + 0x89, 0x3a, 0xf4, 0xac, 0xe0, 0x05, 0x07, 0xc9, 0x74, 0x6e, 0x21, 0x01, + 0xe9, 0x26, 0xfd, 0xb4, 0xb2, 0x2a, 0xda, 0x72, 0xda, 0xbf, 0x63, 0x9d, + 0x37, 0xaf, 0x90, 0x05, 0xd6, 0x89, 0xc7, 0xa6, 0x81, 0x4e, 0x2a, 0x30, + 0xe3, 0x05, 0x88, 0x9f, 0xd0, 0xba, 0x8d, 0xc4, 0x21, 0x52, 0x5a, 0x7a, + 0xe1, 0xad, 0xd3, 0x88, 0xc2, 0x18, 0xad, 0x4c, 0xb1, 0x66, 0x73, 0x1b, + 0xf2, 0xd1, 0xb9, 0x43, 0xaa, 0xc4, 0x66, 0xcd, 0x42, 0xfa, 0x80, 0xec, + 0xa1, 0x7c, 0x45, 0x02, 0x53, 0x45, 0xd5, 0x07, 0xd4, 0x70, 0x12, 0x1b, + 0x08, 0x05, 0x6e, 0x99, 0x0a, 0xd3, 0x5b, 0x99, 0x6b, 0x65, 0xc4, 0xc0, + 0x04, 0x1b, 0x75, 0xf2, 0x86, 0x99, 0x09, 0x4a, 0x50, 0x70, 0x00, 0x7a, + 0x93, 0xaa, 0xe6, 0xf4, 0x03, 0x29, 0x06, 0xa4, 0x30, 0x6d, 0x52, 0xbd, + 0x60, 0xd1, 0x7e, 0xd6, 0x07, 0xc0, 0x41, 0x01, 0x12, 0x3e, 0x16, 0x94, + + /* Second Packet: Handshake */ + 0xea, /* Long, Handshake, PN Length=2 bytes */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID */ + 0x04, 0x36, 0xf4, 0x75, 0x2d, /* SCID */ + 0x42, 0xb0, /* Length (688) */ + 0x3a, 0xc5, /* PN (0) */ + 0x3b, 0x8e, 0x4c, 0x01, 0x72, 0x6b, 0xfa, 0xbb, 0xad, 0xf9, 0x9e, 0x21, + 0xb1, 0xd0, 0x01, 0xf1, 0xd4, 0x67, 0x8d, 0x2c, 0xee, 0x04, 0x60, 0x4a, + 0xe2, 0xe4, 0xc6, 0x89, 0x01, 0xae, 0x3c, 0x1f, 0xf7, 0xe6, 0xf7, 0xac, + 0x26, 0xcf, 0x3c, 0x6d, 0x1d, 0xfd, 0x11, 0x02, 0x51, 0x73, 0xb5, 0xe1, + 0xb2, 0x44, 0x42, 0x32, 0x0f, 0xf5, 0x3d, 0x55, 0x2d, 0x1f, 0x02, 0x29, + 0x51, 0x35, 0xdb, 0xc7, 0x7a, 0x34, 0x4b, 0xec, 0x60, 0x49, 0xa2, 0x90, + 0x11, 0xef, 0x5a, 0xa9, 0x1c, 0xf7, 0xd9, 0x21, 0x68, 0x1c, 0x2b, 0xc6, + 0x57, 0xde, 0xb1, 0x0b, 0x31, 0xed, 0xef, 0x16, 0xba, 0x08, 0xb9, 0xe2, + 0xd9, 0xd0, 0xd8, 0x1f, 0xc4, 0x32, 0xe8, 0x45, 0x2a, 0x86, 0xe4, 0xd3, + 0xaf, 0x72, 0x4f, 0x30, 0x01, 0x71, 0x15, 0x9b, 0xa9, 0x55, 0x35, 0xf7, + 0x39, 0x7e, 0x6a, 0x59, 0x18, 0x4f, 0xe6, 0xdf, 0xb5, 0x0d, 0xc2, 0xe7, + 0xb2, 0xa1, 0xa6, 0xa3, 0x9c, 0xf0, 0x0d, 0x59, 0x05, 0x49, 0x95, 0xfa, + 0xcc, 0x72, 0xd7, 0xc0, 0x84, 0x2e, 0xc4, 0x1c, 0xd4, 0xa0, 0xe3, 0x6c, + 0x5a, 0x8c, 0x94, 0x4d, 0x37, 0x1a, 0x1c, 0x68, 0x93, 0x5f, 0xe5, 0x99, + 0x27, 0xc6, 0x06, 0xaa, 0x1f, 0x29, 0x17, 0xc5, 0x8c, 0x3d, 0x53, 0xa7, + 0x05, 0x3a, 0x44, 0x53, 0x86, 0xed, 0x56, 0x99, 0x4c, 0xe2, 0x7b, 0x3a, + 0x1e, 0x5d, 0x6d, 0xac, 0x78, 0x1e, 0xfa, 0x55, 0x58, 0x6e, 0x72, 0xee, + 0xf9, 0x33, 0x64, 0x7f, 0x93, 0x3c, 0xfe, 0x18, 0x97, 0x6b, 0x02, 0x74, + 0x90, 0x0d, 0xba, 0x89, 0xc0, 0x22, 0x0a, 0x0a, 0x37, 0x4c, 0x28, 0x74, + 0xa7, 0x3a, 0x44, 0x74, 0x42, 0xff, 0xf1, 0xd2, 0x8d, 0x0c, 0xc1, 0xed, + 0x98, 0x98, 0x8e, 0xa8, 0x6b, 0x95, 0x6a, 0x86, 0x0b, 0xb4, 0x95, 0x58, + 0x34, 0x12, 0xb0, 0xc0, 0xf8, 0x2d, 0x5b, 0x40, 0x51, 0x80, 0x07, 0x91, + 0x31, 0x77, 0xd3, 0x06, 0xa5, 0xe5, 0x1f, 0xe2, 0xf8, 0x92, 0xe4, 0x23, + 0x2b, 0xf0, 0x4c, 0xa9, 0xa5, 0x6c, 0x6f, 0xaf, 0xaf, 0xbf, 0x97, 0xcf, + 0x46, 0xf2, 0x8d, 0x61, 0x0e, 0x73, 0xcd, 0xc5, 0xde, 0xda, 0x50, 0x82, + 0x61, 0x6d, 0xb1, 0xa2, 0xbe, 0x6b, 0x99, 0xcd, 0x5b, 0x99, 0x8f, 0x66, + 0xab, 0x11, 0x78, 0xcc, 0xdb, 0x66, 0x98, 0xca, 0x19, 0x92, 0xf4, 0x05, + 0xae, 0xe6, 0xf3, 0xe7, 0xf0, 0x30, 0x28, 0x31, 0x74, 0xff, 0xe2, 0xb3, + 0x3a, 0x4f, 0x79, 0xe7, 0x2a, 0x9f, 0xe3, 0x41, 0xb2, 0x88, 0xc8, 0x8f, + 0x77, 0x57, 0x42, 0x65, 0xdb, 0x07, 0xf6, 0x5f, 0xb8, 0x34, 0x17, 0xe3, + 0x8d, 0x22, 0x5b, 0x88, 0x94, 0x60, 0x97, 0x32, 0x3d, 0x8a, 0x51, 0x9d, + 0xb5, 0xac, 0xd7, 0x99, 0x96, 0x23, 0x6d, 0xc9, 0xab, 0x61, 0x41, 0x8f, + 0x72, 0x1b, 0xf8, 0x84, 0xd9, 0x57, 0x88, 0x68, 0x3d, 0x73, 0x5f, 0xb1, + 0x18, 0x5c, 0x3a, 0x35, 0xd2, 0xc5, 0xb7, 0x29, 0xc7, 0x95, 0xdd, 0x21, + 0xc0, 0x78, 0x49, 0xf3, 0x24, 0xe0, 0x4c, 0x5c, 0x32, 0x08, 0xb7, 0x00, + 0x43, 0x70, 0x5a, 0x95, 0x23, 0x91, 0xf5, 0xb7, 0x61, 0x85, 0x6f, 0xb3, + 0xa4, 0x6b, 0x05, 0x9d, 0x39, 0xa3, 0xb1, 0x1c, 0x61, 0xc5, 0xa5, 0xe7, + 0x9a, 0xe9, 0x5d, 0xaa, 0xca, 0x11, 0xd8, 0x4b, 0xa4, 0x9c, 0x18, 0x4e, + 0x2b, 0x2d, 0x75, 0xc1, 0x12, 0x20, 0xe4, 0x66, 0xa5, 0x59, 0x67, 0x4b, + 0xcc, 0x52, 0x2d, 0xfa, 0xaa, 0xa4, 0xe9, 0xfc, 0x79, 0xd7, 0xff, 0x03, + 0x3e, 0xec, 0xba, 0x97, 0x37, 0x52, 0xc1, 0x57, 0x31, 0x8e, 0x57, 0x0c, + 0x54, 0x92, 0x9c, 0x25, 0x5c, 0xfa, 0x9f, 0xa5, 0x36, 0x18, 0xd0, 0xaa, + 0xf3, 0x3b, 0x5b, 0x59, 0xbd, 0x33, 0x5e, 0x7d, 0x74, 0x7c, 0xaf, 0xe9, + 0x54, 0x80, 0xc4, 0xb4, 0xa1, 0x24, 0x9e, 0x23, 0x0d, 0xbf, 0x4e, 0x0f, + 0xaf, 0xa5, 0x16, 0xcb, 0x3b, 0xfa, 0x33, 0xa5, 0x68, 0xa6, 0x64, 0x48, + 0x2f, 0x5e, 0xfa, 0x64, 0x4e, 0xe3, 0x27, 0x4f, 0x13, 0xe6, 0x37, 0xf6, + 0xb9, 0x63, 0x4b, 0xdc, 0x49, 0x3c, 0x5e, 0x9e, 0x06, 0xea, 0xac, 0xa3, + 0xdf, 0x6c, 0x49, 0xfb, 0xa1, 0x01, 0x4f, 0x6f, 0x74, 0x1f, 0xd3, 0x26, + 0xa1, 0x92, 0x3e, 0xe0, 0x73, 0xd6, 0x3b, 0x67, 0x13, 0x53, 0x2e, 0xcb, + 0xbc, 0x83, 0xd0, 0x6e, 0x28, 0xb1, 0xcb, 0xd9, 0x66, 0xe0, 0x33, 0x59, + 0x45, 0xd3, 0x13, 0xc2, 0x48, 0xd5, 0x9e, 0x88, 0xba, 0x75, 0x7b, 0xb1, + 0xfe, 0x6f, 0xec, 0xde, 0xff, 0x14, 0x59, 0x75, 0xbf, 0x1a, 0x74, 0x47, + 0xc5, 0xd8, 0xe8, 0x1b, 0x3c, 0x86, 0xd7, 0x1f, 0x99, 0x11, 0xd3, 0x29, + 0xfd, 0x5d, 0x22, 0x7e, 0x03, 0x78, 0xed, 0x62, 0x0e, 0xbe, 0x6d, 0x75, + 0xf4, 0xa8, 0x6e, 0xc7, 0x21, 0x76, 0xc5, 0xa0, 0x0c, 0xaa, 0x58, 0x78, + 0x7e, 0x6e, 0xfc, 0x1e, 0x2a, 0x1c, 0xdd, 0xe5, 0x78, 0x08, 0xbd, 0xdb, + 0xea, 0x8f, 0x8a, 0xa5, 0xbf, 0x93, 0xfe, 0x0f, 0x03, 0xa1, 0xc8, 0x64, + 0x9f, 0x4a, + + /* Third Packet: 1-RTT */ + 0x48, /* Short, 1-RTT, Spin=0, KP=0, PN Length=2 bytes */ + 0x3e, 0x28, /* PN (0) */ + 0xb9, 0xdb, 0x61, 0xf8, 0x8b, 0x3a, 0xef, 0x26, 0x69, 0xf2, 0x57, 0xc6, + 0x84, 0x25, 0x6b, 0x77, 0xbe, 0x8c, 0x43, 0x32, 0xf3, 0x9a, 0xd1, 0x85, + 0x14, 0xbc, 0x89, 0x3b, 0x9c, 0xf3, 0xfc, 0x00, 0xa1, 0x3a, 0xc3, 0xc4, + 0x1e, 0xdf, 0xd0, 0x11, 0x70, 0xd9, 0x02, 0x7a, 0xd4, 0xef, 0x86, 0x67, + 0xb1, 0x1e, 0x5d, 0xe3, 0x7f, 0x82, 0x14, 0x52, 0xa5, 0x8a, 0x89, 0xa7, + 0x98, 0x75, 0x2f, 0x8a, 0x00, 0xf3, 0xbd, 0x49, 0x26, 0x4d, 0x0c, 0xc7, + 0x38, 0xe7, 0x91, 0x85, 0xc9, 0x21, 0x6a, 0x1c, 0xc4, 0xa3, 0x0e, 0xd8, + 0xfe, 0xb1, 0x25, 0x1a, +}; + +static const QUIC_PKT_HDR script_6a_expect_hdr = { + QUIC_PKT_TYPE_INITIAL, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 2, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 1, /* Version */ + {0, {0}}, /* DCID */ + {4, {0x36, 0xf4, 0x75, 0x2d}}, /* SCID */ + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 428, NULL +}; + +static const unsigned char script_6a_body[] = { + 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x40, 0x5a, 0x02, 0x00, 0x00, 0x56, 0x03, 0x03, 0xc3, 0x45, 0xe8, 0xb8, + 0xf9, 0x7c, 0x9f, 0x5d, 0xcf, 0x66, 0x25, 0xe4, 0x91, 0x0e, 0xb0, 0x5a, + 0x14, 0xce, 0xaf, 0xea, 0x83, 0x12, 0xde, 0x68, 0xd9, 0x31, 0xf2, 0x23, + 0x11, 0x3a, 0x15, 0xcb, 0x00, 0x13, 0x02, 0x00, 0x00, 0x2e, 0x00, 0x2b, + 0x00, 0x02, 0x03, 0x04, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, + 0xab, 0xd3, 0xc6, 0x9f, 0x36, 0xd3, 0x52, 0x93, 0x87, 0xee, 0x92, 0x01, + 0xa2, 0xd6, 0x9a, 0x5e, 0x61, 0x43, 0xcc, 0x4a, 0xcc, 0x7a, 0xcd, 0x83, + 0xb2, 0xd9, 0xad, 0xd1, 0x14, 0xdc, 0x84, 0x61, +}; + +static const QUIC_PKT_HDR script_6b_expect_hdr = { + QUIC_PKT_TYPE_HANDSHAKE, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 2, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 1, /* Version */ + {0, {0}}, /* DCID */ + {4, {0x36, 0xf4, 0x75, 0x2d}}, /* SCID */ + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 670, NULL +}; + +static const unsigned char script_6b_body[] = { + 0x06, 0x00, 0x42, 0x9a, 0x08, 0x00, 0x00, 0x80, 0x00, 0x7e, 0x00, 0x10, + 0x00, 0x08, 0x00, 0x06, 0x05, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x00, 0x39, + 0x00, 0x6e, 0x47, 0xfa, 0x05, 0x5a, 0xe0, 0xec, 0x4a, 0xf3, 0x05, 0x04, + 0x80, 0x08, 0x00, 0x00, 0x06, 0x04, 0x80, 0x08, 0x00, 0x00, 0x07, 0x04, + 0x80, 0x08, 0x00, 0x00, 0x04, 0x04, 0x80, 0x0c, 0x00, 0x00, 0x08, 0x02, + 0x40, 0x64, 0x09, 0x02, 0x40, 0x64, 0x01, 0x04, 0x80, 0x00, 0x75, 0x30, + 0x03, 0x02, 0x45, 0xac, 0x0b, 0x01, 0x1a, 0x0c, 0x00, 0x02, 0x10, 0x35, + 0xd7, 0x7d, 0x8b, 0xc5, 0xb1, 0x89, 0xb1, 0x5c, 0x23, 0x74, 0x50, 0xfd, + 0x47, 0xfe, 0xd2, 0x00, 0x11, 0x96, 0x38, 0x27, 0xde, 0x7d, 0xfb, 0x2b, + 0x38, 0x56, 0xe5, 0x2a, 0xb8, 0x6b, 0xfa, 0xaa, 0xde, 0x81, 0x0e, 0x01, + 0x04, 0x0f, 0x04, 0x36, 0xf4, 0x75, 0x2d, 0x10, 0x04, 0xac, 0x88, 0x95, + 0xbd, 0x20, 0x01, 0x00, 0x0b, 0x00, 0x01, 0x8f, 0x00, 0x00, 0x01, 0x8b, + 0x00, 0x01, 0x86, 0x30, 0x82, 0x01, 0x82, 0x30, 0x82, 0x01, 0x29, 0xa0, + 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x0a, 0x73, 0x0f, 0x86, 0x18, 0xf2, + 0xc3, 0x30, 0x01, 0xd2, 0xc0, 0xc1, 0x62, 0x52, 0x13, 0xf1, 0x9c, 0x13, + 0x39, 0xb5, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, + 0x03, 0x02, 0x30, 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x0c, 0x6d, 0x61, 0x70, 0x61, 0x6b, 0x74, 0x2e, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x38, 0x30, + 0x32, 0x31, 0x32, 0x30, 0x30, 0x31, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x32, + 0x30, 0x39, 0x30, 0x31, 0x31, 0x32, 0x30, 0x30, 0x31, 0x38, 0x5a, 0x30, + 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0c, + 0x6d, 0x61, 0x70, 0x61, 0x6b, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x67, 0xf4, 0xd3, 0x8f, 0x15, 0x6d, 0xee, 0x85, 0xcc, + 0x2a, 0x77, 0xfc, 0x0b, 0x8f, 0x9f, 0xcf, 0xa9, 0x95, 0x5d, 0x5b, 0xcd, + 0xb7, 0x8b, 0xba, 0x31, 0x0a, 0x73, 0x62, 0xc5, 0xd0, 0x0e, 0x07, 0x90, + 0xae, 0x38, 0x43, 0x79, 0xce, 0x5e, 0x33, 0xad, 0x31, 0xbf, 0x9f, 0x2a, + 0x56, 0x83, 0xa5, 0x24, 0x16, 0xab, 0x0c, 0xf1, 0x64, 0xbe, 0xe4, 0x93, + 0xb5, 0x89, 0xd6, 0x05, 0xe4, 0xf7, 0x7b, 0xa3, 0x53, 0x30, 0x51, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x02, 0x64, + 0x0f, 0x55, 0x69, 0x14, 0x91, 0x19, 0xed, 0xf9, 0x1a, 0xe9, 0x1d, 0xa5, + 0x5a, 0xd0, 0x48, 0x96, 0x9f, 0x60, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x02, 0x64, 0x0f, 0x55, 0x69, + 0x14, 0x91, 0x19, 0xed, 0xf9, 0x1a, 0xe9, 0x1d, 0xa5, 0x5a, 0xd0, 0x48, + 0x96, 0x9f, 0x60, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, + 0x44, 0x02, 0x20, 0x0a, 0x82, 0x92, 0x6e, 0xd3, 0xc6, 0x66, 0xd9, 0xd3, + 0x75, 0xff, 0x71, 0x3b, 0x61, 0x46, 0x21, 0x00, 0xe6, 0x21, 0x5d, 0x9c, + 0x86, 0xe9, 0x65, 0x40, 0x4f, 0xeb, 0x70, 0x4f, 0x2c, 0xad, 0x00, 0x02, + 0x20, 0x08, 0xc2, 0x07, 0x5d, 0x16, 0xfc, 0x54, 0x34, 0x2b, 0xb4, 0x18, + 0x67, 0x44, 0x81, 0xc9, 0xa9, 0x67, 0x2e, 0xce, 0xa1, 0x02, 0x9f, 0x3b, + 0xe5, 0x61, 0x16, 0x0b, 0x50, 0xf6, 0xa1, 0x50, 0x94, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x4b, 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x78, + 0x9e, 0xe0, 0x6a, 0x7a, 0xbd, 0xc3, 0x84, 0x3d, 0x25, 0x6a, 0x59, 0x23, + 0x97, 0x52, 0x64, 0x4e, 0xb6, 0x9f, 0xcc, 0xd3, 0xd7, 0xa9, 0x29, 0x44, + 0x75, 0x6d, 0x50, 0xfc, 0x22, 0xde, 0xd3, 0x02, 0x21, 0x00, 0xe5, 0x28, + 0xd6, 0x5a, 0xd1, 0xec, 0x4a, 0xcc, 0x20, 0xb4, 0xea, 0x15, 0xfb, 0x8e, + 0x73, 0xa8, 0x6b, 0xbb, 0x42, 0x70, 0x90, 0x08, 0x6e, 0x74, 0x6f, 0x5a, + 0x05, 0xb5, 0x39, 0xee, 0x01, 0x04, 0x14, 0x00, 0x00, 0x30, 0xff, 0x9f, + 0xb2, 0x1d, 0xcb, 0x4f, 0xfc, 0x7a, 0xac, 0xf4, 0x75, 0x24, 0x83, 0x5f, + 0x8d, 0xa3, 0x3e, 0x9d, 0xef, 0x43, 0x67, 0x89, 0x5d, 0x55, 0xc7, 0xce, + 0x80, 0xab, 0xc3, 0xc7, 0x74, 0xc7, 0xb2, 0x91, 0x27, 0xce, 0xd8, 0x5e, + 0xc4, 0x4e, 0x96, 0x19, 0x68, 0x2d, 0xbe, 0x6f, 0x49, 0xfa, +}; + +static const QUIC_PKT_HDR script_6c_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}, /* PN */ + NULL, 0, /* Token/Token Len */ + 72, NULL +}; + +static const unsigned char script_6c_body[] = { + 0x18, 0x03, 0x00, 0x04, 0xf2, 0x94, 0x49, 0xc3, 0x34, 0xa1, 0xf4, 0x0f, + 0xcb, 0xb8, 0x03, 0x04, 0x1f, 0xc8, 0x69, 0xb9, 0x3b, 0xd5, 0xc6, 0x93, + 0x18, 0x02, 0x00, 0x04, 0x9a, 0x4f, 0xec, 0x52, 0xde, 0xd2, 0xc8, 0xb7, + 0x1c, 0x0c, 0xf3, 0x4e, 0x46, 0xf0, 0x6c, 0x54, 0x34, 0x1b, 0x0d, 0x98, + 0x18, 0x01, 0x00, 0x04, 0xe3, 0x33, 0x9e, 0x59, 0x00, 0x69, 0xc3, 0xac, + 0xfc, 0x58, 0x0e, 0xa4, 0xf4, 0xf3, 0x23, 0x1b, 0xd6, 0x8e, 0x5b, 0x08, +}; + +static const struct test_op script_6[] = { + OP_ADD_RX_DCID(empty_conn_id) + OP_PROVIDE_SECRET_INITIAL(script_6_c2s_init_dcid) + OP_INJECT_N(6) + OP_CHECK_PKT_N(6a) + OP_CHECK_NO_PKT() /* not got secret for next packet yet */ + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE, + QRL_SUITE_AES256GCM, script_6_handshake_secret) + OP_CHECK_PKT_N(6b) + OP_CHECK_NO_PKT() /* not got secret for next packet yet */ + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, + QRL_SUITE_AES256GCM, script_6_1rtt_secret) + OP_CHECK_PKT_N(6c) + OP_CHECK_NO_PKT() + + /* Try injecting the packet again */ + OP_INJECT_N(6) + /* + * Initial packet is not output due to receiving a Handshake packet causing + * auto-discard of Initial keys + */ + OP_CHECK_PKT_N(6b) + OP_CHECK_PKT_N(6c) + OP_CHECK_NO_PKT() + /* Try again with discarded keys */ + OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE) + OP_INJECT_N(6) + OP_CHECK_PKT_N(6c) + OP_CHECK_NO_PKT() + /* Try again */ + OP_INJECT_N(6) + OP_CHECK_PKT_N(6c) + OP_CHECK_NO_PKT() + /* Try again with discarded 1-RTT keys */ + OP_DISCARD_EL(QUIC_ENC_LEVEL_1RTT) + OP_INJECT_N(6) + OP_CHECK_NO_PKT() + + /* Recreate QRL, test reading packets received before key */ + OP_SET_SCID_LEN(0) + OP_ADD_RX_DCID(empty_conn_id) + OP_INJECT_N(6) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET_INITIAL(script_6_c2s_init_dcid) + OP_CHECK_PKT_N(6a) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE, + QRL_SUITE_AES256GCM, script_6_handshake_secret) + OP_CHECK_PKT_N(6b) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, + QRL_SUITE_AES256GCM, script_6_1rtt_secret) + OP_CHECK_PKT_N(6c) + OP_CHECK_NO_PKT() + + OP_END +}; + +/* + * 7. Real World - S2C Multiple Packets + * - Initial, Handshake, 1-RTT (ChaCha20-Poly1305) + */ +static const QUIC_CONN_ID script_7_c2s_init_dcid = { + 4, {0xfa, 0x5d, 0xd6, 0x80} +}; + +static const unsigned char script_7_handshake_secret[32] = { + 0x85, 0x44, 0xa4, 0x02, 0x46, 0x5b, 0x2a, 0x92, 0x80, 0x71, 0xfd, 0x11, + 0x89, 0x73, 0x84, 0xeb, 0x3e, 0x0d, 0x89, 0x4f, 0x71, 0xdc, 0x9c, 0xdd, + 0x55, 0x77, 0x9e, 0x79, 0x7b, 0xeb, 0xfa, 0x86, +}; + +static const unsigned char script_7_1rtt_secret[32] = { + 0x4a, 0x77, 0xb6, 0x0e, 0xfd, 0x90, 0xca, 0xbf, 0xc0, 0x1a, 0x64, 0x9f, + 0xc0, 0x03, 0xd3, 0x8d, 0xc5, 0x41, 0x04, 0x50, 0xb1, 0x5b, 0x74, 0xe7, + 0xe3, 0x99, 0x0c, 0xdf, 0x74, 0x61, 0x35, 0xe6, +}; + +static const unsigned char script_7_in[] = { + /* First Packet: Initial */ + 0xc2, /* Long, Initial, PN Length=2 bytes */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID */ + 0x04, 0x03, 0x45, 0x0c, 0x7a, /* SCID */ + 0x00, /* Token Length */ + 0x41, 0xcb, /* Length (459) */ + 0x3c, 0xe0, /* PN (0) */ + 0x85, 0x05, 0xc2, 0x4d, 0x0f, 0xf3, 0x62, 0x51, 0x04, 0x33, 0xfa, 0xb5, + 0xa3, 0x02, 0xbd, 0x5c, 0x22, 0x0c, 0x1d, 0xda, 0x06, 0xf1, 0xd7, 0xe0, + 0xc8, 0x56, 0xb0, 0x3d, 0xc1, 0x49, 0x8c, 0xc2, 0x88, 0x5a, 0x0e, 0xd5, + 0x67, 0x72, 0xec, 0xcc, 0x7a, 0x2b, 0x46, 0x17, 0x49, 0x4b, 0x28, 0x6a, + 0x89, 0x71, 0xfd, 0x31, 0x9a, 0xa1, 0x97, 0x64, 0xe2, 0xbf, 0xa0, 0x6d, + 0xf6, 0x76, 0x83, 0x28, 0xc4, 0xd5, 0x39, 0x87, 0x22, 0x7c, 0x11, 0x9a, + 0x53, 0x66, 0xb4, 0x27, 0xf1, 0xab, 0x6f, 0x49, 0x43, 0x3f, 0x9a, 0x23, + 0xd3, 0x53, 0x06, 0xe8, 0x14, 0xfd, 0xc0, 0x67, 0x1f, 0x88, 0x2a, 0xa8, + 0xae, 0x5f, 0x05, 0x0a, 0xeb, 0x66, 0x72, 0x8c, 0x46, 0xcc, 0x54, 0x21, + 0x5e, 0x14, 0xfe, 0x68, 0xc7, 0xf7, 0x60, 0x67, 0xb5, 0xa7, 0x0d, 0xf4, + 0xe1, 0xff, 0x60, 0xe3, 0x11, 0x38, 0x92, 0x90, 0xc2, 0x48, 0x28, 0xbf, + 0xf3, 0x85, 0x27, 0xfe, 0xbf, 0x42, 0x26, 0x1a, 0x4e, 0x78, 0xf1, 0xf0, + 0x88, 0x16, 0x1b, 0x64, 0x5f, 0x66, 0x02, 0x0b, 0x45, 0x3d, 0x38, 0xd9, + 0x09, 0xd5, 0xff, 0xc2, 0x68, 0x02, 0x2c, 0xc4, 0x3f, 0x60, 0x6e, 0x2f, + 0x7f, 0x43, 0xf7, 0x1a, 0x37, 0xcc, 0xe0, 0xe0, 0x4b, 0x96, 0xc1, 0xb1, + 0x8b, 0x1c, 0x7c, 0x6e, 0x80, 0xe3, 0x92, 0x9b, 0x86, 0x87, 0x1f, 0x9a, + 0x6a, 0x62, 0x18, 0xf4, 0x86, 0xc2, 0x3e, 0x33, 0xa3, 0xbf, 0x43, 0x96, + 0x6e, 0xff, 0x94, 0xaf, 0x6d, 0x23, 0x5c, 0x42, 0xed, 0xe7, 0xb9, 0x2c, + 0x33, 0xb0, 0xc6, 0x3d, 0x44, 0x00, 0x0b, 0xa3, 0x39, 0xa8, 0xeb, 0x8c, + 0x81, 0x1a, 0x99, 0x20, 0xbd, 0xfa, 0xf3, 0xf4, 0xf0, 0x11, 0xd8, 0x41, + 0x31, 0x8d, 0xdc, 0x0d, 0x00, 0xa6, 0x31, 0x40, 0xc6, 0xc6, 0xad, 0x74, + 0x93, 0x62, 0x1c, 0x55, 0xce, 0x5f, 0x8c, 0x5b, 0x3c, 0xcb, 0x25, 0x5e, + 0xbf, 0xed, 0xbb, 0x3c, 0x97, 0x4b, 0x62, 0xe0, 0xba, 0xf1, 0xb0, 0x30, + 0xbf, 0x35, 0x89, 0x7e, 0x25, 0x61, 0x54, 0x86, 0x52, 0x11, 0x86, 0x90, + 0xc3, 0xf5, 0xad, 0xa0, 0x96, 0x30, 0xb2, 0xf0, 0xa6, 0x79, 0x39, 0x1c, + 0x51, 0x42, 0xa1, 0x00, 0x6f, 0x55, 0x7d, 0xdc, 0xd0, 0x7c, 0xcf, 0x01, + 0x88, 0x03, 0xd7, 0x2d, 0x65, 0x2b, 0x40, 0xee, 0xba, 0x10, 0xd8, 0x0c, + 0x85, 0x14, 0xb7, 0x4d, 0x9e, 0x7d, 0x7c, 0xde, 0x7f, 0x0d, 0x0e, 0x3b, + 0x3d, 0xe3, 0xd3, 0x63, 0xc2, 0xed, 0xc7, 0x41, 0xaf, 0x05, 0x85, 0x87, + 0x46, 0x55, 0x7e, 0xbe, 0x14, 0x5b, 0x98, 0xae, 0x6e, 0x67, 0x1a, 0x65, + 0xc6, 0xcf, 0xe1, 0x28, 0x50, 0x6b, 0xb4, 0xf6, 0xba, 0x63, 0xbc, 0xf1, + 0xd7, 0xa4, 0x97, 0x2d, 0x4d, 0x04, 0x26, 0x96, 0xec, 0x0c, 0xd4, 0xae, + 0x6a, 0xca, 0x7e, 0x65, 0xc5, 0x43, 0x7e, 0xf8, 0x77, 0x61, 0xd0, 0x2c, + 0xe5, 0x37, 0x0a, 0xb3, 0x7a, 0x8c, 0x2a, 0xa1, 0xdc, 0x29, 0xdb, 0xec, + 0xca, 0xdc, 0xfe, 0xdd, 0x38, 0xd2, 0x13, 0x9f, 0x94, 0x6d, 0x5b, 0x87, + 0xf3, 0x15, 0xa8, 0xe5, 0xe9, 0x65, 0x1d, 0x4f, 0x92, 0x1b, 0xf4, 0xa6, + 0xa4, 0xd6, 0x22, 0xfc, 0x26, 0x1b, 0x35, 0xa4, 0x1c, 0x88, 0x9f, 0x7d, + 0xe0, 0x9a, 0x89, 0x0f, 0x6c, 0xc1, 0xda, 0x6e, 0x45, 0xce, 0x74, 0xb1, + 0xff, + + /* Second Packet: Handshake */ + 0xeb, /* Long, Handshake, PN Length=2 bytes */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID */ + 0x04, 0x03, 0x45, 0x0c, 0x7a, /* SCID */ + 0x42, 0xa3, /* Length (675) */ + 0x43, 0x29, /* PN (0) */ + 0xff, 0xdb, 0xcf, 0x3c, 0x17, 0xcf, 0xdc, 0x42, 0x3a, 0x59, 0x88, 0xdb, + 0x13, 0xef, 0x09, 0x3d, 0xf2, 0x24, 0xf3, 0xeb, 0xca, 0xb0, 0xe1, 0xa4, + 0x67, 0x64, 0x65, 0x80, 0x5f, 0x73, 0x29, 0x69, 0x29, 0xba, 0x03, 0x77, + 0x22, 0xc8, 0xa8, 0xd5, 0x21, 0xf2, 0xa2, 0x30, 0x7f, 0x86, 0x3a, 0x8a, + 0xdd, 0x92, 0x33, 0xa6, 0x57, 0x21, 0x39, 0xdd, 0x34, 0xb4, 0x39, 0xa7, + 0x6f, 0x0a, 0x14, 0xba, 0x9e, 0x3b, 0x3a, 0x6a, 0x4b, 0xc5, 0xda, 0x44, + 0x82, 0xca, 0x52, 0x86, 0x68, 0x8a, 0x0c, 0x5e, 0xeb, 0x1e, 0x81, 0x43, + 0x3a, 0x59, 0x2c, 0x26, 0x63, 0xa3, 0x89, 0x92, 0x80, 0xe9, 0x75, 0xc2, + 0xdb, 0xb9, 0x58, 0x6d, 0xab, 0xfd, 0x21, 0xe0, 0x35, 0x79, 0x2e, 0x56, + 0x7b, 0xfb, 0xb3, 0x7a, 0x05, 0x33, 0x0f, 0x13, 0xe5, 0xef, 0x04, 0x41, + 0x69, 0x85, 0x91, 0x24, 0xce, 0xb5, 0x21, 0x8d, 0x0a, 0x13, 0xda, 0xae, + 0x86, 0x2f, 0x25, 0x1f, 0x9c, 0x70, 0x8a, 0xaa, 0x05, 0xeb, 0x30, 0x93, + 0x50, 0xc1, 0x39, 0xab, 0x99, 0x8a, 0x31, 0xc1, 0xc1, 0x5e, 0x39, 0xcf, + 0x64, 0x3f, 0x9f, 0x5c, 0xa5, 0xa1, 0x88, 0xb2, 0x5f, 0x23, 0xcb, 0x76, + 0xe5, 0xf3, 0x2d, 0xa0, 0xed, 0xad, 0xcf, 0x30, 0x05, 0x44, 0xdc, 0xa5, + 0x81, 0xb1, 0x7f, 0x78, 0x0d, 0x4d, 0x96, 0xa3, 0xcb, 0xcb, 0x45, 0xcf, + 0x5f, 0x22, 0xb8, 0x93, 0x2b, 0x16, 0xe0, 0x1c, 0x53, 0x34, 0x76, 0x3b, + 0x7b, 0x78, 0xa1, 0x46, 0x40, 0x43, 0x4b, 0x0e, 0x1c, 0xfd, 0xcf, 0x01, + 0xf1, 0x2c, 0xee, 0xd0, 0xbd, 0x9f, 0x44, 0xd2, 0xd7, 0x13, 0xf9, 0x65, + 0x82, 0xf5, 0x42, 0xec, 0x9f, 0x5d, 0x51, 0x5a, 0x7b, 0xf2, 0x39, 0xbb, + 0xa6, 0x19, 0x5c, 0x73, 0x95, 0x65, 0x5b, 0x64, 0x2f, 0xda, 0x50, 0xd0, + 0x02, 0x34, 0x3f, 0x35, 0xc1, 0xd6, 0x31, 0x3b, 0xcf, 0x3f, 0x81, 0x8d, + 0xe0, 0x40, 0xfd, 0x6d, 0x32, 0x68, 0xa4, 0xf2, 0x4e, 0x3a, 0x4a, 0x42, + 0x2c, 0x07, 0x2d, 0x27, 0xa3, 0x34, 0xe7, 0x27, 0x87, 0x80, 0x76, 0xc0, + 0xa0, 0x72, 0x05, 0xf2, 0x88, 0x81, 0xe3, 0x32, 0x00, 0x76, 0x8d, 0x24, + 0x5c, 0x97, 0x2d, 0xd6, 0xb8, 0x34, 0xf8, 0x1c, 0x1a, 0x6d, 0xc7, 0x3f, + 0xcf, 0x56, 0xae, 0xec, 0x26, 0x74, 0x53, 0x69, 0xcd, 0x7a, 0x97, 0x29, + 0xab, 0x12, 0x7d, 0x75, 0xf8, 0x8d, 0x5b, 0xc0, 0x77, 0x20, 0xb6, 0x6a, + 0x0b, 0xce, 0x98, 0x50, 0xca, 0x47, 0x42, 0x1e, 0x5d, 0xc3, 0x24, 0x5a, + 0x47, 0x48, 0x3b, 0xa0, 0x9e, 0x43, 0xe9, 0x8d, 0x18, 0x23, 0xda, 0x6f, + 0x8c, 0xda, 0xd0, 0x3e, 0xdb, 0x37, 0xff, 0xfc, 0x7e, 0x17, 0xbe, 0x42, + 0xfd, 0xdb, 0x51, 0xb1, 0xa4, 0xfd, 0x9a, 0x20, 0x27, 0x24, 0x17, 0x04, + 0x70, 0xb6, 0x21, 0x87, 0x88, 0xe9, 0xda, 0x63, 0xcb, 0xcb, 0x1d, 0xaf, + 0x4a, 0x46, 0x76, 0x88, 0xa1, 0xf8, 0x48, 0x6c, 0x06, 0xb4, 0x62, 0x1a, + 0x67, 0x18, 0xb0, 0x1d, 0x58, 0x6a, 0xfe, 0x1f, 0xf1, 0x48, 0xff, 0xcb, + 0xa4, 0xd1, 0xa8, 0x12, 0x1f, 0x45, 0x94, 0x2f, 0x55, 0x80, 0x6a, 0x06, + 0xcc, 0x7b, 0xb0, 0xcc, 0xb8, 0x06, 0x52, 0x16, 0xe3, 0x6e, 0x7e, 0xb0, + 0x42, 0xfd, 0x3b, 0x7e, 0x0a, 0x42, 0x7b, 0x73, 0xaf, 0x2c, 0xf3, 0xbd, + 0xe5, 0x72, 0x8c, 0x16, 0xb2, 0xd7, 0x7a, 0x11, 0xb6, 0x9f, 0xd1, 0x69, + 0xc1, 0x1a, 0xe0, 0x26, 0x26, 0x13, 0xe2, 0x75, 0xf5, 0x74, 0xae, 0x3f, + 0xee, 0x1e, 0x09, 0x63, 0x5a, 0x30, 0x19, 0xa5, 0x59, 0x48, 0x90, 0x9b, + 0x46, 0x56, 0xd8, 0x6f, 0x6b, 0x76, 0x82, 0x32, 0xc7, 0x29, 0x76, 0x2e, + 0x32, 0xb6, 0x23, 0x99, 0xeb, 0x92, 0x5d, 0xc4, 0x4c, 0xa1, 0xe9, 0x26, + 0x37, 0x9a, 0x7d, 0x4c, 0x16, 0x9c, 0x18, 0xe9, 0xc0, 0xff, 0x48, 0x79, + 0xb1, 0x7b, 0x0b, 0x1e, 0x6f, 0xb1, 0x77, 0xa5, 0xd2, 0xc6, 0x9a, 0xa9, + 0xfc, 0xd1, 0x0f, 0x69, 0xf3, 0xe0, 0x49, 0x70, 0x57, 0x80, 0x86, 0xa7, + 0x3f, 0x54, 0xa8, 0x60, 0xfb, 0xe4, 0x06, 0xa3, 0x13, 0xb9, 0x2f, 0xa7, + 0x37, 0x80, 0x0c, 0x43, 0xac, 0x2f, 0xae, 0x6e, 0x62, 0x2b, 0x53, 0xe4, + 0xfe, 0x58, 0xd7, 0x8b, 0x96, 0xdc, 0xe6, 0xd3, 0x86, 0xb8, 0xd6, 0x42, + 0x5b, 0x68, 0x03, 0x48, 0x3f, 0xcd, 0xee, 0x39, 0x8b, 0xc4, 0x53, 0x30, + 0x87, 0x48, 0x2a, 0x01, 0x9d, 0x6f, 0x8e, 0x36, 0x75, 0x73, 0xef, 0x77, + 0x3a, 0x82, 0xd8, 0x4c, 0x0e, 0x7f, 0xb3, 0x8f, 0x16, 0xd1, 0x10, 0xcf, + 0x2f, 0xa3, 0xdf, 0x65, 0xba, 0x91, 0x79, 0xf6, 0x93, 0x60, 0x08, 0xe5, + 0xdb, 0x73, 0x02, 0x7a, 0x0b, 0x0e, 0xcc, 0x3b, 0x1f, 0x08, 0x2d, 0x51, + 0x3e, 0x87, 0x48, 0xd3, 0xd3, 0x75, 0xc2, 0x28, 0xa3, 0xf3, 0x02, 0xde, + 0x8f, 0xa6, 0xbd, 0xb3, 0x19, 0xa0, 0xdb, 0x48, 0x51, 0x03, 0x5f, 0x98, + 0xbe, + + /* Third Packet: 1-RTT */ + 0x5c, /* Short, 1-RTT, Spin=0, KP=0, PN Length=2 bytes */ + 0x4f, 0x33, /* PN (0) */ + 0x16, 0x75, 0x98, 0x67, 0x04, 0x16, 0x61, 0xe3, 0x00, 0xb7, 0x9d, 0x5c, + 0x53, 0x4c, 0x26, 0x90, 0x92, 0x8e, 0x0e, 0xc0, 0x9c, 0x6d, 0x8b, 0xac, + 0x15, 0x6d, 0x89, 0x74, 0x2f, 0xe7, 0x84, 0xe3, 0x46, 0x46, 0x8c, 0xc1, + 0x21, 0x7c, 0x44, 0xa5, 0x00, 0x29, 0xca, 0xf2, 0x11, 0x18, 0xe0, 0x04, + 0x40, 0x55, 0xd2, 0xa7, 0xe5, 0x9d, 0x22, 0xa2, 0x2a, 0x6c, 0x03, 0x87, + 0xa3, 0xa3, 0xfa, 0xf5, 0x6c, 0xd7, 0x7d, 0xae, 0x3f, 0x28, 0x01, 0xae, + 0x06, 0x11, 0x69, 0x67, 0x90, 0x57, 0x5a, 0xd0, 0xeb, 0xdd, 0xac, 0xbd, + 0x7f, 0x33, 0x86, 0xbb, +}; + +static const QUIC_PKT_HDR script_7a_expect_hdr = { + QUIC_PKT_TYPE_INITIAL, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 2, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 1, /* Version */ + {0, {0}}, /* DCID */ + {4, {0x03, 0x45, 0x0c, 0x7a}}, /* SCID */ + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 441, NULL +}; + +static const unsigned char script_7a_body[] = { + 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x40, 0x5a, 0x02, 0x00, 0x00, 0x56, 0x03, 0x03, 0xd5, 0xfb, 0x6a, + 0x81, 0x1c, 0xdb, 0xa2, 0x5c, 0x11, 0x31, 0xda, 0x15, 0x28, 0x97, 0x94, + 0x83, 0xfd, 0x9d, 0x91, 0x0e, 0x87, 0x71, 0x46, 0x64, 0xb4, 0xd9, 0x9e, + 0xbd, 0xa8, 0x48, 0x32, 0xbf, 0x00, 0x13, 0x03, 0x00, 0x00, 0x2e, 0x00, + 0x2b, 0x00, 0x02, 0x03, 0x04, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, + 0x20, 0xef, 0xbb, 0x46, 0xe9, 0xb4, 0xf6, 0x54, 0xc4, 0x07, 0x71, 0xdc, + 0x50, 0xd5, 0x69, 0x40, 0xbc, 0x85, 0x7f, 0xf9, 0x48, 0x14, 0xe3, 0xd6, + 0x08, 0xa9, 0x0b, 0xfd, 0xbe, 0xf1, 0x57, 0x21, 0x34, +}; + +static const QUIC_PKT_HDR script_7b_expect_hdr = { + QUIC_PKT_TYPE_HANDSHAKE, + 0, /* Spin Bit */ + 0, /* Key Phase */ + 2, /* PN Length */ + 0, /* Partial */ + 1, /* Fixed */ + 1, /* Version */ + {0, {0}}, /* DCID */ + {4, {0x03, 0x45, 0x0c, 0x7a}}, /* SCID */ + {0}, /* PN */ + NULL, 0, /* Token/Token Len */ + 657, NULL +}; + +static const unsigned char script_7b_body[] = { + 0x06, 0x00, 0x42, 0x8d, 0x08, 0x00, 0x00, 0x82, 0x00, 0x80, 0x00, 0x10, + 0x00, 0x08, 0x00, 0x06, 0x05, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x00, 0x39, + 0x00, 0x70, 0x46, 0x0a, 0x0d, 0xdc, 0x59, 0xf0, 0x4e, 0xb2, 0x2c, 0xac, + 0x69, 0x6a, 0xc9, 0x77, 0xa9, 0x99, 0x05, 0x04, 0x80, 0x08, 0x00, 0x00, + 0x06, 0x04, 0x80, 0x08, 0x00, 0x00, 0x07, 0x04, 0x80, 0x08, 0x00, 0x00, + 0x04, 0x04, 0x80, 0x0c, 0x00, 0x00, 0x08, 0x02, 0x40, 0x64, 0x09, 0x02, + 0x40, 0x64, 0x01, 0x04, 0x80, 0x00, 0x75, 0x30, 0x03, 0x02, 0x45, 0xac, + 0x0b, 0x01, 0x1a, 0x0c, 0x00, 0x02, 0x10, 0x42, 0xf0, 0xed, 0x09, 0x07, + 0x5b, 0xd9, 0x5a, 0xb2, 0x39, 0x5d, 0x73, 0x2c, 0x57, 0x1f, 0x50, 0x00, + 0x0b, 0xe0, 0x3e, 0xf3, 0xd6, 0x91, 0x6f, 0x9c, 0xcc, 0x31, 0xf7, 0xa5, + 0x0e, 0x01, 0x04, 0x0f, 0x04, 0x03, 0x45, 0x0c, 0x7a, 0x10, 0x04, 0xfa, + 0x5d, 0xd6, 0x80, 0x20, 0x01, 0x00, 0x0b, 0x00, 0x01, 0x8f, 0x00, 0x00, + 0x01, 0x8b, 0x00, 0x01, 0x86, 0x30, 0x82, 0x01, 0x82, 0x30, 0x82, 0x01, + 0x29, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x0a, 0x73, 0x0f, 0x86, + 0x18, 0xf2, 0xc3, 0x30, 0x01, 0xd2, 0xc0, 0xc1, 0x62, 0x52, 0x13, 0xf1, + 0x9c, 0x13, 0x39, 0xb5, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x04, 0x03, 0x02, 0x30, 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0c, 0x6d, 0x61, 0x70, 0x61, 0x6b, 0x74, 0x2e, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x30, + 0x38, 0x30, 0x32, 0x31, 0x32, 0x30, 0x30, 0x31, 0x38, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x30, 0x39, 0x30, 0x31, 0x31, 0x32, 0x30, 0x30, 0x31, 0x38, + 0x5a, 0x30, 0x17, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x0c, 0x6d, 0x61, 0x70, 0x61, 0x6b, 0x74, 0x2e, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, + 0x07, 0x03, 0x42, 0x00, 0x04, 0x67, 0xf4, 0xd3, 0x8f, 0x15, 0x6d, 0xee, + 0x85, 0xcc, 0x2a, 0x77, 0xfc, 0x0b, 0x8f, 0x9f, 0xcf, 0xa9, 0x95, 0x5d, + 0x5b, 0xcd, 0xb7, 0x8b, 0xba, 0x31, 0x0a, 0x73, 0x62, 0xc5, 0xd0, 0x0e, + 0x07, 0x90, 0xae, 0x38, 0x43, 0x79, 0xce, 0x5e, 0x33, 0xad, 0x31, 0xbf, + 0x9f, 0x2a, 0x56, 0x83, 0xa5, 0x24, 0x16, 0xab, 0x0c, 0xf1, 0x64, 0xbe, + 0xe4, 0x93, 0xb5, 0x89, 0xd6, 0x05, 0xe4, 0xf7, 0x7b, 0xa3, 0x53, 0x30, + 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0x02, 0x64, 0x0f, 0x55, 0x69, 0x14, 0x91, 0x19, 0xed, 0xf9, 0x1a, 0xe9, + 0x1d, 0xa5, 0x5a, 0xd0, 0x48, 0x96, 0x9f, 0x60, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x02, 0x64, 0x0f, + 0x55, 0x69, 0x14, 0x91, 0x19, 0xed, 0xf9, 0x1a, 0xe9, 0x1d, 0xa5, 0x5a, + 0xd0, 0x48, 0x96, 0x9f, 0x60, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0a, + 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, + 0x00, 0x30, 0x44, 0x02, 0x20, 0x0a, 0x82, 0x92, 0x6e, 0xd3, 0xc6, 0x66, + 0xd9, 0xd3, 0x75, 0xff, 0x71, 0x3b, 0x61, 0x46, 0x21, 0x00, 0xe6, 0x21, + 0x5d, 0x9c, 0x86, 0xe9, 0x65, 0x40, 0x4f, 0xeb, 0x70, 0x4f, 0x2c, 0xad, + 0x00, 0x02, 0x20, 0x08, 0xc2, 0x07, 0x5d, 0x16, 0xfc, 0x54, 0x34, 0x2b, + 0xb4, 0x18, 0x67, 0x44, 0x81, 0xc9, 0xa9, 0x67, 0x2e, 0xce, 0xa1, 0x02, + 0x9f, 0x3b, 0xe5, 0x61, 0x16, 0x0b, 0x50, 0xf6, 0xa1, 0x50, 0x94, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x4c, 0x04, 0x03, 0x00, 0x48, 0x30, 0x46, 0x02, + 0x21, 0x00, 0xaa, 0x18, 0x61, 0x93, 0xdf, 0xbb, 0x79, 0xe7, 0x34, 0x7e, + 0x2e, 0x61, 0x13, 0x8c, 0xa0, 0x33, 0xfb, 0x33, 0xca, 0xfc, 0xd2, 0x45, + 0xb0, 0xc7, 0x89, 0x3d, 0xf1, 0xd6, 0x54, 0x94, 0x05, 0xb6, 0x02, 0x21, + 0x00, 0xef, 0x6c, 0xb6, 0xf2, 0x00, 0xb2, 0x32, 0xb1, 0xf3, 0x3f, 0x59, + 0xf5, 0xc8, 0x18, 0xbe, 0x39, 0xbb, 0x27, 0xf8, 0x67, 0xac, 0xcb, 0x63, + 0xa4, 0x29, 0xfb, 0x8e, 0x88, 0x0f, 0xe5, 0xe9, 0x7e, 0x14, 0x00, 0x00, + 0x20, 0xfc, 0x2c, 0x4c, 0xa7, 0x77, 0x24, 0x79, 0x29, 0xa8, 0x82, 0x1a, + 0x4d, 0x58, 0x9d, 0x82, 0xe2, 0x09, 0x36, 0x63, 0x0e, 0x0b, 0x55, 0x51, + 0x80, 0x93, 0x40, 0xda, 0x41, 0x33, 0x08, 0x10, 0x2c, +}; + +static const QUIC_PKT_HDR script_7c_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}, /* PN */ + NULL, 0, /* Token/Token Len */ + 72, NULL +}; + +static const unsigned char script_7c_body[] = { + 0x18, 0x03, 0x00, 0x04, 0xf7, 0x75, 0x72, 0xa2, 0xfd, 0x17, 0xd4, 0x82, + 0x8e, 0xe9, 0x5b, 0xce, 0xed, 0xec, 0x88, 0xb9, 0x73, 0xbf, 0x36, 0x9f, + 0x18, 0x02, 0x00, 0x04, 0x5f, 0x43, 0x96, 0xe4, 0x15, 0xdc, 0x56, 0x6b, + 0x67, 0x4c, 0x36, 0xb2, 0xe2, 0x77, 0xdc, 0x6e, 0xb9, 0x2c, 0x0d, 0x79, + 0x18, 0x01, 0x00, 0x04, 0xcb, 0x83, 0x4a, 0xf4, 0x8d, 0x7b, 0x69, 0x90, + 0xaf, 0x0d, 0xd2, 0x38, 0xa4, 0xf1, 0x94, 0xff, 0x63, 0x24, 0xd3, 0x7a, +}; + +static const struct test_op script_7[] = { + OP_ADD_RX_DCID(empty_conn_id) + OP_PROVIDE_SECRET_INITIAL(script_7_c2s_init_dcid) + OP_INJECT_N(7) + OP_CHECK_PKT_N(7a) + OP_CHECK_NO_PKT() /* not got secret for next packet yet */ + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE, + QRL_SUITE_CHACHA20POLY1305, script_7_handshake_secret) + OP_CHECK_PKT_N(7b) + OP_CHECK_NO_PKT() /* not got secret for next packet yet */ + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, + QRL_SUITE_CHACHA20POLY1305, script_7_1rtt_secret) + OP_CHECK_PKT_N(7c) + OP_CHECK_NO_PKT() + + /* Try injecting the packet again */ + OP_INJECT_N(7) + /* + * Initial packet is not output due to receiving a Handshake packet causing + * auto-discard of Initial keys + */ + OP_CHECK_PKT_N(7b) + OP_CHECK_PKT_N(7c) + OP_CHECK_NO_PKT() + /* Try again with discarded keys */ + OP_DISCARD_EL(QUIC_ENC_LEVEL_HANDSHAKE) + OP_INJECT_N(7) + OP_CHECK_PKT_N(7c) + OP_CHECK_NO_PKT() + /* Try again */ + OP_INJECT_N(7) + OP_CHECK_PKT_N(7c) + OP_CHECK_NO_PKT() + /* Try again with discarded 1-RTT keys */ + OP_DISCARD_EL(QUIC_ENC_LEVEL_1RTT) + OP_INJECT_N(7) + OP_CHECK_NO_PKT() + + /* Recreate QRL, test reading packets received before key */ + OP_SET_SCID_LEN(0) + OP_ADD_RX_DCID(empty_conn_id) + OP_INJECT_N(7) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET_INITIAL(script_7_c2s_init_dcid) + OP_CHECK_PKT_N(7a) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_HANDSHAKE, + QRL_SUITE_CHACHA20POLY1305, script_7_handshake_secret) + OP_CHECK_PKT_N(7b) + OP_CHECK_NO_PKT() + OP_PROVIDE_SECRET(QUIC_ENC_LEVEL_1RTT, + QRL_SUITE_CHACHA20POLY1305, script_7_1rtt_secret) + OP_CHECK_PKT_N(7c) + OP_CHECK_NO_PKT() + + OP_END +}; + +static const struct test_op *scripts[] = { + script_1, + script_2, + script_3, + script_4, + script_5, + script_6, + script_7 +}; + +static int cmp_pkt_hdr(const QUIC_PKT_HDR *a, const QUIC_PKT_HDR *b, + const unsigned char *b_data, size_t b_len, + int cmp_data) +{ + int ok = 1; + + if (b_data == NULL) { + b_data = b->data; + b_len = b->len; + } + + if (!TEST_int_eq(a->type, b->type) + || !TEST_int_eq(a->spin_bit, b->spin_bit) + || !TEST_int_eq(a->key_phase, b->key_phase) + || !TEST_int_eq(a->pn_len, b->pn_len) + || !TEST_int_eq(a->partial, b->partial) + || !TEST_int_eq(a->fixed, b->fixed) + || !TEST_uint_eq(a->version, b->version) + || !TEST_true(ossl_quic_conn_id_eq(&a->dst_conn_id, &b->dst_conn_id)) + || !TEST_true(ossl_quic_conn_id_eq(&a->src_conn_id, &b->src_conn_id)) + || !TEST_mem_eq(a->pn, sizeof(a->pn), b->pn, sizeof(b->pn)) + || !TEST_size_t_eq(a->token_len, b->token_len) + || !TEST_uint64_t_eq(a->len, b->len)) + ok = 0; + + if (a->token_len > 0 && b->token_len > 0 + && !TEST_mem_eq(a->token, a->token_len, b->token, b->token_len)) + ok = 0; + + if ((a->token_len == 0 && !TEST_ptr_null(a->token)) + || (b->token_len == 0 && !TEST_ptr_null(b->token))) + ok = 0; + + if (cmp_data && !TEST_mem_eq(a->data, a->len, b_data, b_len)) + ok = 0; + + return ok; +} + +struct state { + QUIC_DEMUX *demux; + OSSL_QRL *qrl; + OSSL_QRL_ARGS args; +}; + +static void state_teardown(struct state *s) +{ + if (s->qrl != NULL) { + ossl_qrl_free(s->qrl); + s->qrl = NULL; + } + + if (s->demux != NULL) { + ossl_quic_demux_free(s->demux); + s->demux = NULL; + } +} + +static int state_ensure(struct state *s) +{ + if (s->demux == NULL + && !TEST_ptr(s->demux = ossl_quic_demux_new(NULL, + s->args.short_conn_id_len, + 1500))) + return 0; + + s->args.rx_demux = s->demux; + + if (s->qrl == NULL + && !TEST_ptr(s->qrl = ossl_qrl_new(&s->args))) + return 0; + + return 1; +} + +static int run_script(const struct test_op *script) +{ + int testresult = 0, pkt_outstanding = 0; + struct state s = {0}; + size_t i; + OSSL_QRL_RX_PKT pkt = {0}; + const struct test_op *op = script; + + for (; op->op != TEST_OP_END; ++op) + switch (op->op) { + case TEST_OP_SET_SCID_LEN: + state_teardown(&s); + s.args.short_conn_id_len = op->enc_level; + break; + case TEST_OP_SET_INIT_LARGEST_PN: + state_teardown(&s); + for (i = 0; i < QUIC_PN_SPACE_NUM; ++i) + s.args.rx_init_largest_pn[i] = op->largest_pn; + break; + case TEST_OP_ADD_RX_DCID: + if (!TEST_true(state_ensure(&s))) + goto err; + if (!TEST_true(ossl_qrl_add_dst_conn_id(s.qrl, op->dcid))) + goto err; + break; + case TEST_OP_PROVIDE_SECRET: + if (!TEST_true(state_ensure(&s))) + goto err; + if (!TEST_true(ossl_qrl_provide_rx_secret(s.qrl, op->enc_level, + op->suite_id, + op->buf, + op->buf_len))) + goto err; + break; + case TEST_OP_PROVIDE_SECRET_INITIAL: + if (!TEST_true(state_ensure(&s))) + goto err; + if (!TEST_true(ossl_qrl_provide_rx_secret_initial(s.qrl, + op->dcid))) + goto err; + break; + case TEST_OP_DISCARD_EL: + if (!TEST_true(state_ensure(&s))) + goto err; + if (!TEST_true(ossl_qrl_discard_enc_level(s.qrl, op->enc_level))) + goto err; + break; + case TEST_OP_INJECT: + if (!TEST_true(state_ensure(&s))) + goto err; + if (!TEST_true(ossl_quic_demux_inject(s.demux, + op->buf, op->buf_len, + NULL, NULL))) + goto err; + break; + case TEST_OP_CHECK_PKT: + if (!TEST_true(state_ensure(&s))) + goto err; + + if (!TEST_true(ossl_qrl_read_pkt(s.qrl, &pkt))) + goto err; + + pkt_outstanding = 1; + if (!TEST_ptr(pkt.hdr)) + goto err; + + if (!TEST_mem_eq(pkt.hdr->data, pkt.hdr->len, + op->buf, op->buf_len)) + goto err; + + if (!TEST_true(cmp_pkt_hdr(pkt.hdr, op->hdr, + op->buf, op->buf_len, 1))) + goto err; + + ossl_qrl_release_pkt(s.qrl, pkt.handle); + pkt_outstanding = 0; + break; + case TEST_OP_CHECK_NO_PKT: + if (!TEST_true(state_ensure(&s))) + goto err; + + if (!TEST_false(ossl_qrl_read_pkt(s.qrl, &pkt))) + goto err; + + break; + default: + OPENSSL_assert(0); + goto err; + } + + testresult = 1; +err: + if (pkt_outstanding) + ossl_qrl_release_pkt(s.qrl, pkt.handle); + state_teardown(&s); + return testresult; +} + +static int test_script(int idx) +{ + return run_script(scripts[idx]); +} + +/* Packet Header Tests */ +struct pkt_hdr_test { + QUIC_PKT_HDR hdr; + const unsigned char *expected; + size_t expected_len; + const unsigned char *payload; + size_t payload_len; + size_t short_conn_id_len; + /* + * Minimum number of bytes which should be required for a successful decode. + * SIZE_MAX if should never decode successfully. + */ + size_t min_success_len; + size_t pn_offset, sample_offset; +}; + +/* Packet Header Test 1: INITIAL With SCID */ +static const unsigned char pkt_hdr_test_1_expected[] = { + 0xc1, /* Long|Fixed, Type=Initial, PN Len=2 */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID Length */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x00, /* Token Length */ + 0x15, /* Length=21 */ + 0x33, 0x44, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_1_payload[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const struct pkt_hdr_test pkt_hdr_test_1 = { + { + QUIC_PKT_TYPE_INITIAL, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 2, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 1, /* version */ + { 0, {0} }, /* DCID */ + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, /* SCID */ + { 0x33, 0x44 }, /* PN */ + NULL, 0, /* Token/Token Len */ + 19, NULL /* Len/Data */ + }, + pkt_hdr_test_1_expected, OSSL_NELEM(pkt_hdr_test_1_expected), + pkt_hdr_test_1_payload, OSSL_NELEM(pkt_hdr_test_1_payload), + 0, sizeof(pkt_hdr_test_1_expected), + 17, 21 +}; + +/* Packet Header Test 2: INITIAL With SCID and Token */ +static const unsigned char pkt_hdr_test_2_expected[] = { + 0xc1, /* Long|Fixed, Type=Initial, PN Len=2 */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x00, /* DCID Length */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x07, /* Token Length */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x15, /* Length=21 */ + 0x33, 0x44, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_2_payload[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_2_token[] = { + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96 +}; + +static const struct pkt_hdr_test pkt_hdr_test_2 = { + { + QUIC_PKT_TYPE_INITIAL, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 2, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 1, /* version */ + { 0, {0} }, /* DCID */ + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, /* SCID */ + { 0x33, 0x44 }, /* PN */ + pkt_hdr_test_2_token, sizeof(pkt_hdr_test_2_token), /* Token */ + 19, NULL /* Len/Data */ + }, + pkt_hdr_test_2_expected, OSSL_NELEM(pkt_hdr_test_2_expected), + pkt_hdr_test_2_payload, OSSL_NELEM(pkt_hdr_test_2_payload), + 0, sizeof(pkt_hdr_test_2_expected), + 24, 28 +}; + +/* Packet Header Test 3: INITIAL With DCID and SCID and Token */ +static const unsigned char pkt_hdr_test_3_expected[] = { + 0xc1, /* Long|Fixed, Type=Initial, PN Len=2 */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x03, /* DCID Length */ + 0x70, 0x71, 0x72, /* DCID */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x06, /* Token Length */ + 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x15, /* Length=21 */ + 0x33, 0x44, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_3_payload[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_3_token[] = { + 0x91, 0x92, 0x93, 0x94, 0x95, 0x96 +}; + +static const struct pkt_hdr_test pkt_hdr_test_3 = { + { + QUIC_PKT_TYPE_INITIAL, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 2, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 1, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, /* SCID */ + { 0x33, 0x44 }, /* PN */ + pkt_hdr_test_3_token, sizeof(pkt_hdr_test_3_token), /* Token */ + 19, NULL /* Len/Data */ + }, + pkt_hdr_test_3_expected, OSSL_NELEM(pkt_hdr_test_3_expected), + pkt_hdr_test_3_payload, OSSL_NELEM(pkt_hdr_test_3_payload), + 0, sizeof(pkt_hdr_test_3_expected), + 26, 30 +}; + +/* Packet Header Test 4: 0-RTT */ +static const unsigned char pkt_hdr_test_4_expected[] = { + 0xd0, /* Long|Fixed, Type=0-RTT, PN Len=1 */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x03, /* DCID Length */ + 0x70, 0x71, 0x72, /* DCID */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x14, /* Length=20 */ + 0x33, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_4_payload[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const struct pkt_hdr_test pkt_hdr_test_4 = { + { + QUIC_PKT_TYPE_0RTT, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 1, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 1, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, /* SCID */ + { 0x33 }, /* PN */ + NULL, 0, /* Token */ + 19, NULL /* Len/Data */ + }, + pkt_hdr_test_4_expected, OSSL_NELEM(pkt_hdr_test_4_expected), + pkt_hdr_test_4_payload, OSSL_NELEM(pkt_hdr_test_4_payload), + 0, sizeof(pkt_hdr_test_4_expected), + 19, 23 +}; + +/* Packet Header Test 5: Handshake */ +static const unsigned char pkt_hdr_test_5_expected[] = { + 0xe0, /* Long|Fixed, Type=Handshake, PN Len=1 */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x03, /* DCID Length */ + 0x70, 0x71, 0x72, /* DCID */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x14, /* Length=20 */ + 0x33, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_5_payload[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const struct pkt_hdr_test pkt_hdr_test_5 = { + { + QUIC_PKT_TYPE_HANDSHAKE, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 1, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 1, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, /* SCID */ + { 0x33 }, /* PN */ + NULL, 0, /* Token */ + 19, NULL /* Len/Data */ + }, + pkt_hdr_test_5_expected, OSSL_NELEM(pkt_hdr_test_5_expected), + pkt_hdr_test_5_payload, OSSL_NELEM(pkt_hdr_test_5_payload), + 0, sizeof(pkt_hdr_test_5_expected), + 19, 23 +}; + +/* Packet Header Test 6: Retry */ +static const unsigned char pkt_hdr_test_6_expected[] = { + 0xf0, /* Long|Fixed, Type=Retry */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x03, /* DCID Length */ + 0x70, 0x71, 0x72, /* DCID */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* Retry Token */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f /* Retry Integrity Tag */ +}; + +static const unsigned char pkt_hdr_test_6_payload[] = { + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* Retry Token */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f /* Retry Integrity Tag */ +}; + +static const struct pkt_hdr_test pkt_hdr_test_6 = { + { + QUIC_PKT_TYPE_RETRY, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 0, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 1, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, /* SCID */ + { 0 }, /* PN */ + NULL, 0, /* Token */ + 24, NULL /* Len/Data */ + }, + pkt_hdr_test_6_expected, OSSL_NELEM(pkt_hdr_test_6_expected), + pkt_hdr_test_6_payload, OSSL_NELEM(pkt_hdr_test_6_payload), + 0, 21, + SIZE_MAX, SIZE_MAX +}; + +/* Packet Header Test 7: 1-RTT */ +static const unsigned char pkt_hdr_test_7_expected[] = { + 0x42, /* Short|Fixed, Type=1-RTT, PN Len=3 */ + 0x70, 0x71, 0x72, /* DCID */ + 0x50, 0x51, 0x52, /* PN */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const unsigned char pkt_hdr_test_7_payload[] = { + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const struct pkt_hdr_test pkt_hdr_test_7 = { + { + QUIC_PKT_TYPE_1RTT, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 3, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 0, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 0, {0} }, /* SCID */ + { 0x50, 0x51, 0x52 }, /* PN */ + NULL, 0, /* Token */ + 18, NULL /* Len/Data */ + }, + pkt_hdr_test_7_expected, OSSL_NELEM(pkt_hdr_test_7_expected), + pkt_hdr_test_7_payload, OSSL_NELEM(pkt_hdr_test_7_payload), + 3, 21, + 4, 8 +}; + +/* Packet Header Test 8: 1-RTT with Spin Bit */ +static const unsigned char pkt_hdr_test_8_expected[] = { + 0x62, /* Short|Fixed, Type=1-RTT, PN Len=3, Spin=1 */ + 0x70, 0x71, 0x72, /* DCID */ + 0x50, 0x51, 0x52, /* PN */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const unsigned char pkt_hdr_test_8_payload[] = { + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const struct pkt_hdr_test pkt_hdr_test_8 = { + { + QUIC_PKT_TYPE_1RTT, /* type */ + 1, /* spin bit */ + 0, /* key phase */ + 3, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 0, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 0, {0} }, /* SCID */ + { 0x50, 0x51, 0x52 }, /* PN */ + NULL, 0, /* Token */ + 18, NULL /* Len/Data */ + }, + pkt_hdr_test_8_expected, OSSL_NELEM(pkt_hdr_test_8_expected), + pkt_hdr_test_8_payload, OSSL_NELEM(pkt_hdr_test_8_payload), + 3, 21, + 4, 8 +}; + +/* Packet Header Test 9: 1-RTT with Key Phase Bit */ +static const unsigned char pkt_hdr_test_9_expected[] = { + 0x46, /* Short|Fixed, Type=1-RTT, PN Len=3, Key Phase=1 */ + 0x70, 0x71, 0x72, /* DCID */ + 0x50, 0x51, 0x52, /* PN */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const unsigned char pkt_hdr_test_9_payload[] = { + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const struct pkt_hdr_test pkt_hdr_test_9 = { + { + QUIC_PKT_TYPE_1RTT, /* type */ + 0, /* spin bit */ + 1, /* key phase */ + 3, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 0, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 0, {0} }, /* SCID */ + { 0x50, 0x51, 0x52 }, /* PN */ + NULL, 0, /* Token */ + 18, NULL /* Len/Data */ + }, + pkt_hdr_test_9_expected, OSSL_NELEM(pkt_hdr_test_9_expected), + pkt_hdr_test_9_payload, OSSL_NELEM(pkt_hdr_test_9_payload), + 3, 21, + 4, 8 +}; + +/* Packet Header Test 10: Handshake with 4-Byte PN */ +static const unsigned char pkt_hdr_test_10_expected[] = { + 0xe3, /* Long|Fixed, Type=Handshake, PN Len=4 */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x03, /* DCID Length */ + 0x70, 0x71, 0x72, /* DCID */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x17, /* Length=20 */ + 0x33, 0x44, 0x55, 0x66, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const unsigned char pkt_hdr_test_10_payload[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const struct pkt_hdr_test pkt_hdr_test_10 = { + { + QUIC_PKT_TYPE_HANDSHAKE, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 4, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 1, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 8, {0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5 } }, /* SCID */ + { 0x33, 0x44, 0x55, 0x66 }, /* PN */ + NULL, 0, /* Token */ + 19, NULL /* Len/Data */ + }, + pkt_hdr_test_10_expected, OSSL_NELEM(pkt_hdr_test_10_expected), + pkt_hdr_test_10_payload, OSSL_NELEM(pkt_hdr_test_10_payload), + 0, sizeof(pkt_hdr_test_10_expected), + 19, 23 +}; + +/* Packet Header Test 11: 1-RTT with 4-Byte PN */ +static const unsigned char pkt_hdr_test_11_expected[] = { + 0x43, /* Short|Fixed, Type=1-RTT, PN Len=4 */ + 0x70, 0x71, 0x72, /* DCID */ + 0x50, 0x51, 0x52, 0x53, /* PN */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const unsigned char pkt_hdr_test_11_payload[] = { + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const struct pkt_hdr_test pkt_hdr_test_11 = { + { + QUIC_PKT_TYPE_1RTT, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 4, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 0, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 0, {0} }, /* SCID */ + { 0x50, 0x51, 0x52, 0x53 }, /* PN */ + NULL, 0, /* Token */ + 18, NULL /* Len/Data */ + }, + pkt_hdr_test_11_expected, OSSL_NELEM(pkt_hdr_test_11_expected), + pkt_hdr_test_11_payload, OSSL_NELEM(pkt_hdr_test_11_payload), + 3, 21, + 4, 8 +}; + +/* Packet Header Test 12: Version Negotiation */ +static const unsigned char pkt_hdr_test_12_expected[] = { + 0xc0, /* Long|Fixed, Type=Version Neg */ + 0x00, 0x00, 0x00, 0x00, /* Version (0) */ + 0x03, 0x70, 0x71, 0x72, /* DCID */ + 0x02, 0x81, 0x82, /* SCID */ + 0x11, 0x22, 0x33, 0x44 /* One Version */ +}; + +static const unsigned char pkt_hdr_test_12_payload[] = { + 0x11, 0x22, 0x33, 0x44 +}; + +static const struct pkt_hdr_test pkt_hdr_test_12 = { + { + QUIC_PKT_TYPE_VERSION_NEG, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 0, /* PN length */ + 0, /* partial */ + 1, /* fixed */ + 0, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 2, {0x81, 0x82} }, /* SCID */ + { 0 }, /* PN */ + NULL, 0, /* Token */ + 4, NULL /* Len/Data */ + }, + pkt_hdr_test_12_expected, OSSL_NELEM(pkt_hdr_test_12_expected), + pkt_hdr_test_12_payload, OSSL_NELEM(pkt_hdr_test_12_payload), + 0, 12, + SIZE_MAX, SIZE_MAX +}; + +/* Packet Header Test 13: Version Negotiation without Fixed Bit */ +static const unsigned char pkt_hdr_test_13_expected[] = { + 0x80, /* Long|Fixed, Type=Version Neg */ + 0x00, 0x00, 0x00, 0x00, /* Version (0) */ + 0x03, 0x70, 0x71, 0x72, /* DCID */ + 0x02, 0x81, 0x82, /* SCID */ + 0x11, 0x22, 0x33, 0x44 /* One Version */ +}; + +static const unsigned char pkt_hdr_test_13_payload[] = { + 0x11, 0x22, 0x33, 0x44 +}; + +static const struct pkt_hdr_test pkt_hdr_test_13 = { + { + QUIC_PKT_TYPE_VERSION_NEG, /* type */ + 0, /* spin bit */ + 0, /* key phase */ + 0, /* PN length */ + 0, /* partial */ + 0, /* fixed */ + 0, /* version */ + { 3, {0x70, 0x71, 0x72} }, /* DCID */ + { 2, {0x81, 0x82} }, /* SCID */ + { 0 }, /* PN */ + NULL, 0, /* Token */ + 4, NULL /* Len/Data */ + }, + pkt_hdr_test_13_expected, OSSL_NELEM(pkt_hdr_test_13_expected), + pkt_hdr_test_13_payload, OSSL_NELEM(pkt_hdr_test_13_payload), + 0, 12, + SIZE_MAX, SIZE_MAX +}; + +/* Packet Header Test 14: 1-RTT - Malformed - No Fixed Bit */ +static const unsigned char pkt_hdr_test_14_expected[] = { + 0x02, /* Fixed, Type=1-RTT, PN Len=3 */ + 0x70, 0x71, 0x72, /* DCID */ + 0x50, 0x51, 0x52, /* PN */ + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1 +}; + +static const struct pkt_hdr_test pkt_hdr_test_14 = { + { 0 }, + pkt_hdr_test_14_expected, OSSL_NELEM(pkt_hdr_test_14_expected), + NULL, 0, + 3, SIZE_MAX, + 4, 8 +}; + +/* Packet Header Test 15: Handshake - Malformed - No Fixed Bit */ +static const unsigned char pkt_hdr_test_15_expected[] = { + 0xa0, /* Long, Type=Handshake, PN Len=1 */ + 0x00, 0x00, 0x00, 0x01, /* Version */ + 0x03, /* DCID Length */ + 0x70, 0x71, 0x72, /* DCID */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x14, /* Length=20 */ + 0x33, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const struct pkt_hdr_test pkt_hdr_test_15 = { + { 0 }, + pkt_hdr_test_15_expected, OSSL_NELEM(pkt_hdr_test_15_expected), + NULL, 0, + 0, SIZE_MAX, + 19, 23 +}; + +/* Packet Header Test 16: Handshake - Malformed - Wrong Version */ +static const unsigned char pkt_hdr_test_16_expected[] = { + 0xe0, /* Long|Fixed, Type=Handshake, PN Len=1 */ + 0x00, 0x00, 0x00, 0x02, /* Version */ + 0x03, /* DCID Length */ + 0x70, 0x71, 0x72, /* DCID */ + 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, /* SCID Length, SCID */ + 0x14, /* Length=20 */ + 0x33, /* Encoded PN */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* Payload */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22 +}; + +static const struct pkt_hdr_test pkt_hdr_test_16 = { + { 0 }, + pkt_hdr_test_16_expected, OSSL_NELEM(pkt_hdr_test_16_expected), + NULL, 0, + 0, SIZE_MAX, + 19, 23 +}; + +static const struct pkt_hdr_test *const pkt_hdr_tests[] = { + &pkt_hdr_test_1, + &pkt_hdr_test_2, + &pkt_hdr_test_3, + &pkt_hdr_test_4, + &pkt_hdr_test_5, + &pkt_hdr_test_6, + &pkt_hdr_test_7, + &pkt_hdr_test_8, + &pkt_hdr_test_9, + &pkt_hdr_test_10, + &pkt_hdr_test_11, + &pkt_hdr_test_12, + &pkt_hdr_test_13, + &pkt_hdr_test_14, + &pkt_hdr_test_15, + &pkt_hdr_test_16 +}; + +#define HPR_REPEAT_COUNT 4 +#define HPR_CIPHER_COUNT 3 + +/* + * Count of number of times we observed an unchanged (u) or changed (c) bit in + * each header-protectable bit over all test suites. + */ +static unsigned int counts_u[HPR_CIPHER_COUNT][37] = {0}; +static unsigned int counts_c[HPR_CIPHER_COUNT][37] = {0}; + +static int test_wire_pkt_hdr_actual(int tidx, int repeat, int cipher, + size_t trunc_len) +{ + int testresult = 0; + const struct pkt_hdr_test *t = pkt_hdr_tests[tidx]; + QUIC_PKT_HDR hdr = {0}; + QUIC_PKT_HDR_PTRS ptrs = {0}, wptrs = {0}; + PACKET pkt = {0}; + WPACKET wpkt = {0}; + BUF_MEM *buf = NULL; + size_t l = 0, i, j; + QUIC_HDR_PROTECTOR hpr = {0}; + unsigned char hpr_key[32] = {0,1,2,3,4,5,6,7}; + int have_hpr = 0, hpr_cipher_id, hpr_key_len; + unsigned char *hbuf = NULL; + int is_trunc = trunc_len < t->expected_len; + int expect_fail = trunc_len < t->min_success_len; + hpr_key[8] = (unsigned char)tidx; + hpr_key[9] = (unsigned char)repeat; + + switch (cipher) { + case 0: + hpr_cipher_id = QUIC_HDR_PROT_CIPHER_AES_128; + hpr_key_len = 16; + break; + case 1: + hpr_cipher_id = QUIC_HDR_PROT_CIPHER_AES_256; + hpr_key_len = 32; + break; + case 2: + hpr_cipher_id = QUIC_HDR_PROT_CIPHER_CHACHA; + hpr_key_len = 32; + break; + default: + goto err; + } + + if (!TEST_ptr(buf = BUF_MEM_new())) + goto err; + + if (!TEST_true(WPACKET_init(&wpkt, buf))) + goto err; + + if (!TEST_true(PACKET_buf_init(&pkt, t->expected, trunc_len))) + goto err; + + if (!TEST_int_eq(ossl_quic_wire_decode_pkt_hdr(&pkt, t->short_conn_id_len, + 0, &hdr, &ptrs), + !expect_fail)) + goto err; + + if (!expect_fail && !is_trunc) { + if (!TEST_true(cmp_pkt_hdr(&hdr, &t->hdr, t->payload, t->payload_len, 1))) + goto err; + + if (!TEST_ptr_eq(ptrs.raw_start, t->expected)) + goto err; + + if (t->pn_offset == SIZE_MAX) { + if (!TEST_ptr_null(ptrs.raw_pn)) + goto err; + } else { + if (!TEST_ptr_eq(ptrs.raw_pn, t->expected + t->pn_offset)) + goto err; + } + + if (t->sample_offset != SIZE_MAX) { + if (!TEST_ptr_eq(ptrs.raw_sample, t->expected + t->sample_offset)) + goto err; + if (!TEST_size_t_eq(ptrs.raw_sample_len, + t->expected_len - t->sample_offset)) + goto err; + } + + if (!TEST_true(ossl_quic_wire_encode_pkt_hdr(&wpkt, t->short_conn_id_len, &hdr, &wptrs))) + goto err; + + if (!TEST_true(WPACKET_memcpy(&wpkt, t->payload, t->payload_len))) + goto err; + + if (!TEST_true(WPACKET_get_total_written(&wpkt, &l))) + goto err; + + if (!TEST_mem_eq(buf->data, l, t->expected, t->expected_len)) + goto err; + + /* Test header protection. */ + if (t->sample_offset != SIZE_MAX) { /* if packet type has protection */ + if (!TEST_true(ossl_quic_hdr_protector_init(&hpr, NULL, NULL, + hpr_cipher_id, + hpr_key, + hpr_key_len))) + goto err; + + have_hpr = 1; + + /* + * Copy into a duplicate buffer to test header protection by + * comparing it against the original. + */ + hbuf = OPENSSL_malloc(t->expected_len); + if (!TEST_ptr(hbuf)) + goto err; + + memcpy(hbuf, t->expected, t->expected_len); + + /* Fixup pointers to new buffer and encrypt. */ + ptrs.raw_pn = hbuf + (ptrs.raw_pn - ptrs.raw_start); + ptrs.raw_sample = hbuf + (ptrs.raw_sample - ptrs.raw_start); + ptrs.raw_start = hbuf; + if (!TEST_true(ossl_quic_hdr_protector_encrypt(&hpr, &ptrs))) + goto err; + + /* Ensure that bytes which should not have changed did not change */ + for (i = 0; i < t->expected_len; ++i) { + unsigned char d = t->expected[i] ^ hbuf[i], rej_mask = 0xff; + size_t jrel = 0; + if (i == 0) { + /* Bits in first byte which must not change */ + rej_mask = (t->hdr.type == QUIC_PKT_TYPE_1RTT) ? ~0x1f : ~0xf; + } else if (i >= t->pn_offset && i < t->pn_offset + t->hdr.pn_len) { + /* PN bytes change */ + rej_mask = 0; + jrel = 5 + (i - t->pn_offset) * 8; + } + + if (rej_mask != 0xff) + for (j = 0; j < 8; ++j) { + if (((1U << j) & rej_mask) != 0) + /* + * Bit unrelated to header protection, do not record + * stats about it. + */ + continue; + + OPENSSL_assert(jrel + j < OSSL_NELEM(counts_u[cipher])); + if ((d & (1U << j)) != 0) + ++counts_c[cipher][jrel + j]; /* bit did change */ + else + ++counts_u[cipher][jrel + j]; /* bit did not change */ + } + + /* Bits in rej_mask must not change */ + if (!TEST_int_eq(d & rej_mask, 0)) + goto err; + } + + /* Decrypt and check matches original. */ + if (!TEST_true(ossl_quic_hdr_protector_decrypt(&hpr, &ptrs))) + goto err; + + if (!TEST_mem_eq(hbuf, t->expected_len, t->expected, t->expected_len)) + goto err; + } + } + + testresult = 1; +err: + if (have_hpr) + ossl_quic_hdr_protector_destroy(&hpr); + WPACKET_finish(&wpkt); + BUF_MEM_free(buf); + OPENSSL_free(hbuf); + return testresult; +} + +static int test_wire_pkt_hdr_inner(int tidx, int repeat, int cipher) +{ + int testresult = 0; + const struct pkt_hdr_test *t = pkt_hdr_tests[tidx]; + size_t i; + + /* Test with entire packet */ + if (!TEST_true(test_wire_pkt_hdr_actual(tidx, repeat, cipher, + t->expected_len))) + goto err; + + /* Now repeat for every possible truncation of the packet */ + for (i = 0; i < t->expected_len; ++i) + if (!TEST_true(test_wire_pkt_hdr_actual(tidx, repeat, cipher, i))) + goto err; + + testresult = 1; +err: + return testresult; +} + +static int test_hdr_prot_stats(void) +{ + int testresult = 0; + size_t i, cipher; + + /* + * Test that, across all previously executed tests for each header + * protection cipher, every bit which can have header protection applied a) + * was changed in at least one test of applying header protection, and b) + * was unchanged in at least one test of applying header protection. + */ + for (cipher = 0; cipher < HPR_CIPHER_COUNT; ++cipher) + for (i = 0; i < OSSL_NELEM(counts_u[0]); ++i) { + if (!TEST_true(counts_u[cipher][i])) + goto err; + if (!TEST_true(counts_c[cipher][i])) + goto err; + } + + testresult = 1; +err: + return testresult; +} + +#define NUM_WIRE_PKT_HDR_TESTS \ + (OSSL_NELEM(pkt_hdr_tests) * HPR_REPEAT_COUNT * HPR_CIPHER_COUNT) + +static int test_wire_pkt_hdr(int idx) +{ + int tidx, repeat, cipher; + + if (idx == NUM_WIRE_PKT_HDR_TESTS) + return test_hdr_prot_stats(); + + cipher = idx % HPR_CIPHER_COUNT; + idx /= HPR_CIPHER_COUNT; + + repeat = idx % HPR_REPEAT_COUNT; + idx /= HPR_REPEAT_COUNT; + + tidx = idx; + + return test_wire_pkt_hdr_inner(tidx, repeat, cipher); +} + +int setup_tests(void) +{ + ADD_ALL_TESTS(test_script, OSSL_NELEM(scripts)); + /* + * Each instance of this test is executed multiple times to get enough + * statistical coverage for our statistical test, as well as for each + * supported key type. + * + * We call the statistical test as the last index in the wire_pkt_hdr + * test rather than as a separate case, as it needs to execute last + * and otherwise random test ordering will cause itt to randomly fail. + */ + ADD_ALL_TESTS(test_wire_pkt_hdr, NUM_WIRE_PKT_HDR_TESTS + 1); + return 1; +} diff --git a/test/quic_wire_test.c b/test/quic_wire_test.c index 10c7835e0c..b10b14014f 100644 --- a/test/quic_wire_test.c +++ b/test/quic_wire_test.c @@ -9,6 +9,7 @@ #include "internal/packet.h" #include "internal/quic_wire.h" +#include "internal/quic_wire_pkt.h" #include "testutil.h" struct encode_test_case { @@ -1357,9 +1358,71 @@ err: return testresult; } +/* Packet Header PN Encoding Tests */ +struct pn_test { + QUIC_PN pn, tx_largest_acked, rx_largest_pn; + char expected_len; + unsigned char expected_bytes[4]; +}; + +static const struct pn_test pn_tests[] = { + /* RFC 9000 Section A.2 */ + { 0xac5c02, 0xabe8b3, 0xabe8b3, 2, {0x5c,0x02} }, + { 0xace8fe, 0xabe8b3, 0xabe8b3, 3, {0xac,0xe8,0xfe} }, + /* RFC 9000 Section A.3 */ + { 0xa82f9b32, 0xa82f30ea, 0xa82f30ea, 2, {0x9b,0x32} }, + /* Boundary Cases */ + { 1, 0, 0, 1, {0x01} }, + { 256, 255, 255, 1, {0x00} }, + { 257, 255, 255, 1, {0x01} }, + { 256, 128, 128, 1, {0x00} }, + { 256, 127, 127, 2, {0x01,0x00} }, + { 65536, 32768, 32768, 2, {0x00,0x00} }, + { 65537, 32769, 32769, 2, {0x00,0x01} }, + { 65536, 32767, 32767, 3, {0x01,0x00,0x00} }, + { 65537, 32768, 32768, 3, {0x01,0x00,0x01} }, + { 16777216, 8388608, 8388608, 3, {0x00,0x00,0x00} }, + { 16777217, 8388609, 8388609, 3, {0x00,0x00,0x01} }, + { 16777216, 8388607, 8388607, 4, {0x01,0x00,0x00,0x00} }, + { 16777217, 8388608, 8388608, 4, {0x01,0x00,0x00,0x01} }, + { 4294967296, 2147483648, 2147483648, 4, {0x00,0x00,0x00,0x00} }, + { 4294967297, 2147483648, 2147483648, 4, {0x00,0x00,0x00,0x01} }, +}; + +static int test_wire_pkt_hdr_pn(int tidx) +{ + int testresult = 0; + const struct pn_test *t = &pn_tests[tidx]; + unsigned char buf[4]; + int pn_len; + QUIC_PN res_pn; + + pn_len = ossl_quic_wire_determine_pn_len(t->pn, t->tx_largest_acked); + if (!TEST_int_eq(pn_len, (int)t->expected_len)) + goto err; + + if (!TEST_true(ossl_quic_wire_encode_pkt_hdr_pn(t->pn, buf, pn_len))) + goto err; + + if (!TEST_mem_eq(t->expected_bytes, t->expected_len, buf, pn_len)) + goto err; + + if (!TEST_true(ossl_quic_wire_decode_pkt_hdr_pn(buf, pn_len, + t->rx_largest_pn, &res_pn))) + goto err; + + if (!TEST_uint64_t_eq(res_pn, t->pn)) + goto err; + + testresult = 1; +err: + return testresult; +} + int setup_tests(void) { - ADD_ALL_TESTS(test_wire_encode, OSSL_NELEM(encode_cases)); - ADD_ALL_TESTS(test_wire_ack, OSSL_NELEM(ack_cases)); + ADD_ALL_TESTS(test_wire_encode, OSSL_NELEM(encode_cases)); + ADD_ALL_TESTS(test_wire_ack, OSSL_NELEM(ack_cases)); + ADD_ALL_TESTS(test_wire_pkt_hdr_pn, OSSL_NELEM(pn_tests)); return 1; } diff --git a/test/recipes/70-test_quic_record.t b/test/recipes/70-test_quic_record.t new file mode 100644 index 0000000000..3fd782000c --- /dev/null +++ b/test/recipes/70-test_quic_record.t @@ -0,0 +1,19 @@ +#! /usr/bin/env perl +# Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use OpenSSL::Test; +use OpenSSL::Test::Utils; + +setup("test_quic_record"); + +plan skip_all => "QUIC protocol is not supported by this OpenSSL build" + if disabled('quic'); + +plan tests => 1; + +ok(run(test(["quic_record_test"]))); |