]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC Record Layer: Allow INITIAL EL to be rekeyed
authorHugo Landau <hlandau@openssl.org>
Mon, 31 Oct 2022 15:58:48 +0000 (15:58 +0000)
committerHugo Landau <hlandau@openssl.org>
Fri, 13 Jan 2023 13:20:12 +0000 (13:20 +0000)
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 <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19703)

include/internal/quic_record_rx.h
include/internal/quic_record_tx.h
ssl/quic/quic_record_shared.c

index 6df4013533932cad8bb7fce186c0230d03c2e5a2..5e0429f77321ad53233c506f4e06f373c0c8c53a 100644 (file)
@@ -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.
  *
index b0e5e148c1759586d5043963ff34d86601cced84..1e89b49baecca8eb273c49cc33e90c869c6f7c33 100644 (file)
@@ -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.
  */
index 9832b0a3e7523f9ad9db85824d64a9cb0688f592..d5c8ede58cf9dfb3d4561462c2262cae1cb6cf32 100644 (file)
@@ -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)