QUIC: Allow application to trigger TXKU
authorHugo Landau <hlandau@openssl.org>
Mon, 12 Jun 2023 13:13:33 +0000 (14:13 +0100)
committerPauli <pauli@openssl.org>
Thu, 15 Jun 2023 23:26:48 +0000 (09:26 +1000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21029)

doc/man3/SSL_key_update.pod
include/internal/quic_channel.h
include/internal/quic_ssl.h
ssl/quic/quic_impl.c
ssl/ssl_lib.c
test/quic_multistream_test.c

index 6d5b42e0b166f736f2ce1b5e13b7b27aefedb239..f0e535c3135b7d6d6e2a55978231c484bb7aba1a 100644 (file)
@@ -32,11 +32,11 @@ peer to additionally update its sending keys. It is an error if B<updatetype> is
 set to B<SSL_KEY_UPDATE_NONE>.
 
 SSL_key_update() must only be called after the initial handshake has been
-completed and TLSv1.3 has been negotiated, at the same time, the application
-needs to ensure that the writing of data has been completed. The key update
-will not take place until the next time an IO operation such as SSL_read_ex()
-or SSL_write_ex() takes place on the connection. Alternatively SSL_do_handshake()
-can be called to force the update to take place immediately.
+completed and TLSv1.3 or QUIC has been negotiated, at the same time, the
+application needs to ensure that the writing of data has been completed. The key
+update will not take place until the next time an IO operation such as
+SSL_read_ex() or SSL_write_ex() takes place on the connection. Alternatively
+SSL_do_handshake() can be called to force the update to take place immediately.
 
 SSL_get_key_update_type() can be used to determine whether a key update
 operation has been scheduled but not yet performed. The type of the pending key
@@ -77,6 +77,23 @@ the session in the new handshake.
 The SSL_renegotiate_pending() function returns 1 if a renegotiation or
 renegotiation request has been scheduled but not yet acted on, or 0 otherwise.
 
+=head1 USAGE WITH QUIC
+
+SSL_key_update() can also be used to perform a key update when using QUIC. The
+function must be called on a QUIC connection SSL object. This is normally done
+automatically when needed. Since a locally initiated QUIC key update always
+causes a peer to also trigger a key update, passing
+B<SSL_KEY_UPDATE_NOT_REQUESTED> as B<updatetype> has the same effect as passing
+B<SSL_KEY_UPDATE_REQUESTED>.
+
+The QUIC connection must have been fully established before a key update can be
+performed, and other QUIC protocol rules govern how frequently QUIC key update
+can be performed. SSL_key_update() will fail if these requirements are not met.
+
+Because QUIC key updates are always handled immediately,
+SSL_get_key_update_type() always returns SSL_KEY_UPDATE_NONE when called on a
+QUIC connection SSL object.
+
 =head1 RETURN VALUES
 
 SSL_key_update(), SSL_renegotiate() and SSL_renegotiate_abbreviated() return 1
index 99e3dd6c76eb423ce33d5a14c57c550dfc643202..deade706af5a0cedcf3a1f8a95e4fa6a7ed41c78 100644 (file)
@@ -338,6 +338,9 @@ void ossl_quic_channel_set_txku_threshold_override(QUIC_CHANNEL *ch,
 uint64_t ossl_quic_channel_get_tx_key_epoch(QUIC_CHANNEL *ch);
 uint64_t ossl_quic_channel_get_rx_key_epoch(QUIC_CHANNEL *ch);
 
+/* Artificially trigger a spontaneous TXKU if possible. */
+int ossl_quic_channel_trigger_txku(QUIC_CHANNEL *ch);
+
 # endif
 
 #endif
index ed69a8005797ae4c126ab84989eb32606ecabb95..28047f985c9acdf4c7f0750d8be77787ace3f18a 100644 (file)
@@ -34,6 +34,8 @@ __owur long ossl_quic_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg);
 __owur long ossl_quic_callback_ctrl(SSL *s, int cmd, void (*fp) (void));
 __owur long ossl_quic_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp) (void));
 __owur size_t ossl_quic_pending(const SSL *s);
+__owur int ossl_quic_key_update(SSL *s, int update_type);
+__owur int ossl_quic_get_key_update_type(const SSL *s);
 __owur int ossl_quic_num_ciphers(void);
 __owur const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u);
 int ossl_quic_renegotiate_check(SSL *ssl, int initok);
index 548fcbc89e502243db9ace58d577516cc872e6d1..4f379e32ed2e550fcecc7cf181a80e407b1269a0 100644 (file)
@@ -1558,6 +1558,7 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags)
  *   (BIO/)SSL_write            => ossl_quic_write
  *         SSL_pending          => ossl_quic_pending
  *         SSL_stream_conclude  => ossl_quic_conn_stream_conclude
+ *         SSL_key_update       => ossl_quic_key_update
  */
 
 /* SSL_get_error */
@@ -2688,6 +2689,56 @@ int ossl_quic_get_conn_close_info(SSL *ssl,
     return 1;
 }
 
+/*
+ * SSL_key_update
+ * --------------
+ */
+int ossl_quic_key_update(SSL *ssl, int update_type)
+{
+    QCTX ctx;
+
+    if (!expect_quic_conn_only(ssl, &ctx))
+        return 0;
+
+    switch (update_type) {
+    case SSL_KEY_UPDATE_NOT_REQUESTED:
+        /*
+         * QUIC signals peer key update implicily by triggering a local
+         * spontaneous TXKU. Silently upgrade this to SSL_KEY_UPDATE_REQUESTED.
+         */
+    case SSL_KEY_UPDATE_REQUESTED:
+        break;
+
+    default:
+        /* Unknown type - error. */
+        return 0;
+    }
+
+    quic_lock(ctx.qc);
+
+    /* Attempt to perform a TXKU. */
+    if (!ossl_quic_channel_trigger_txku(ctx.qc->ch)) {
+        quic_unlock(ctx.qc);
+        return 0;
+    }
+
+    quic_unlock(ctx.qc);
+    return 1;
+}
+
+/*
+ * SSL_get_key_update_type
+ * -----------------------
+ */
+int ossl_quic_get_key_update_type(const SSL *s)
+{
+    /*
+     * We always handle key updates immediately so a key update is never
+     * pending.
+     */
+    return SSL_KEY_UPDATE_NONE;
+}
+
 /*
  * QUIC Front-End I/O API: SSL_CTX Management
  * ==========================================
index bad54e0955589d92da5627629333e895d8f136a8..1894be7d59601c60229d403f5d752d367c7b4ac2 100644 (file)
@@ -2727,6 +2727,11 @@ int SSL_key_update(SSL *s, int updatetype)
 {
     SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
 
+#ifndef OPENSSL_NO_QUIC
+    if (IS_QUIC(s))
+        return ossl_quic_key_update(s, updatetype);
+#endif
+
     if (sc == NULL)
         return 0;
 
@@ -2760,6 +2765,11 @@ int SSL_get_key_update_type(const SSL *s)
 {
     const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);
 
+#ifndef OPENSSL_NO_QUIC
+    if (IS_QUIC(s))
+        return ossl_quic_get_key_update_type(s);
+#endif
+
     if (sc == NULL)
         return 0;
 
index 44da562da8ae44c3c8a47165904416809ca16d04..8c2f935ff4d3b6006759f20dda9e8b70c993d19b 100644 (file)
@@ -288,6 +288,14 @@ static int override_key_update(struct helper *h, const struct script_op *op)
     return 1;
 }
 
+static int trigger_key_update(struct helper *h, const struct script_op *op)
+{
+    if (!TEST_true(SSL_key_update(h->c_conn, SSL_KEY_UPDATE_REQUESTED)))
+        return 0;
+
+    return 1;
+}
+
 static int check_key_update_ge(struct helper *h, const struct script_op *op)
 {
     QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn);
@@ -683,7 +691,7 @@ static int run_script_worker(struct helper *h, const struct script_op *script,
     if (!TEST_true(helper_local_init(&hl, h, thread_idx)))
         goto out;
 
-#define SPIN_AGAIN() { no_advance = 1; continue; }
+#define SPIN_AGAIN() { OSSL_sleep(1); no_advance = 1; continue; }
 
     for (;;) {
         SSL *c_tgt              = h->c_conn;
@@ -1933,7 +1941,7 @@ static const struct script_op script_18[] = {
      * 1 packet above, which is absurd; thus this ensures we only actually
      * generate TXKUs when we are allowed to.
      */
-    OP_CHECK                (check_key_update_ge, 5)
+    OP_CHECK                (check_key_update_ge, 4)
     OP_CHECK                (check_key_update_lt, 120)
 
     /*
@@ -1949,6 +1957,27 @@ static const struct script_op script_18[] = {
     OP_END
 };
 
+/* 19. Key update test - artificially triggered */
+static const struct script_op script_19[] = {
+    OP_C_SET_ALPN           ("ossltest")
+    OP_C_CONNECT_WAIT       ()
+
+    OP_C_WRITE              (DEFAULT, "apple", 5)
+
+    OP_S_BIND_STREAM_ID     (a, C_BIDI_ID(0))
+    OP_S_READ_EXPECT        (a, "apple", 5)
+
+    OP_CHECK                (check_key_update_lt, 1)
+    OP_CHECK                (trigger_key_update, 0)
+
+    OP_C_WRITE              (DEFAULT, "orange", 6)
+    OP_S_READ_EXPECT        (a, "orange", 6)
+
+    OP_CHECK                (check_key_update_ge, 1)
+
+    OP_END
+};
+
 static const struct script_op *const scripts[] = {
     script_1,
     script_2,
@@ -1968,6 +1997,7 @@ static const struct script_op *const scripts[] = {
     script_16,
     script_17,
     script_18,
+    script_19,
 };
 
 static int test_script(int idx)