From: Hugo Landau Date: Mon, 31 Oct 2022 15:58:48 +0000 (+0000) Subject: QUIC Record Layer: Allow INITIAL EL to be rekeyed X-Git-Tag: openssl-3.2.0-alpha1~1517 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b2c94b93994bc079ed3aa7f700adc7782bd0bb64;p=thirdparty%2Fopenssl.git QUIC Record Layer: Allow INITIAL EL to be rekeyed Ordinarily we should not allow ELs to be rekeyed as it makes no sense to do so. However the INITIAL EL can need to be rekeyed if a connection retry occurs. Modify the QRL to allow this. Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/19703) --- diff --git a/include/internal/quic_record_rx.h b/include/internal/quic_record_rx.h index 6df40135339..5e0429f7732 100644 --- a/include/internal/quic_record_rx.h +++ b/include/internal/quic_record_rx.h @@ -162,12 +162,14 @@ int ossl_qrx_remove_dst_conn_id(OSSL_QRX *qrx, * 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_qrx_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 semi-automatically by the QRX but do require - * some caller handling (see below). + * This function can only be called once for a given EL, except for the INITIAL + * EL, which can need rekeying when a connection retry occurs. Subsequent calls + * for non-INITIAL ELs fail, as do calls made after a corresponding call to + * ossl_qrx_discard_enc_level for that EL. The secret for a non-INITIAL EL + * cannot be changed after it is set because QUIC has no facility for + * introducing additional key material after an EL is setup. QUIC key updates + * are managed semi-automatically by the QRX but do require some caller handling + * (see below). * * md is for internal use and should be NULL. * diff --git a/include/internal/quic_record_tx.h b/include/internal/quic_record_tx.h index b0e5e148c17..1e89b49baec 100644 --- a/include/internal/quic_record_tx.h +++ b/include/internal/quic_record_tx.h @@ -65,12 +65,14 @@ void ossl_qtx_free(OSSL_QTX *qtx); * 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_qtx_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 generate new keys from existing key material and do not - * introduce new entropy into a connection's key material.) + * This function can only be called once for a given EL, except for the INITIAL + * EL, as the INITIAL EL can need to be rekeyed if connection retry occurs. + * Subsequent calls for non-INITIAL ELs fail. Calls made after a corresponding + * call to ossl_qtx_discard_enc_level for a given EL also fail, including for + * the INITIAL EL. The secret for a non-INITIAL EL cannot be changed after it is + * set because QUIC has no facility for introducing additional key material + * after an EL is setup. (QUIC key updates generate new keys from existing key + * material and do not introduce new entropy into a connection's key material.) * * Returns 1 on success or 0 on failure. */ diff --git a/ssl/quic/quic_record_shared.c b/ssl/quic/quic_record_shared.c index 9832b0a3e75..d5c8ede58cf 100644 --- a/ssl/quic/quic_record_shared.c +++ b/ssl/quic/quic_record_shared.c @@ -196,10 +196,29 @@ int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els, const char *md_name = ossl_qrl_get_suite_md_name(suite_id); size_t hpr_key_len, init_keyslot; - if (el == NULL || el->state != QRL_EL_STATE_UNPROV || md_name == NULL - || init_key_phase_bit > 1 || is_tx < 0 || is_tx > 1) + if (el == NULL + || md_name == NULL + || init_key_phase_bit > 1 || is_tx < 0 || is_tx > 1 + || (init_key_phase_bit > 0 && enc_level != QUIC_ENC_LEVEL_1RTT)) return 0; + if (enc_level == QUIC_ENC_LEVEL_INITIAL + && el->state == QRL_EL_STATE_PROV_NORMAL) { + /* + * Sometimes the INITIAL EL needs to be reprovisioned, namely if a + * connection retry occurs. Exceptionally, if the caller wants to + * reprovision the INITIAL EL, tear it down as usual and then override + * the state so it can be provisioned again. + */ + ossl_qrl_enc_level_set_discard(els, enc_level); + el->state = QRL_EL_STATE_UNPROV; + } + + if (el->state != QRL_EL_STATE_UNPROV) + return 0; + + + init_keyslot = is_tx ? 0 : init_key_phase_bit; hpr_key_len = ossl_qrl_get_suite_hdr_prot_key_len(suite_id); if (hpr_key_len == 0)