]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
tls13/key_update: Expose a manual KeyUpdate function
authorAlistair Francis <alistair.francis@wdc.com>
Mon, 26 May 2025 04:41:46 +0000 (14:41 +1000)
committerDaiki Ueno <ueno@gnu.org>
Thu, 25 Sep 2025 05:22:26 +0000 (14:22 +0900)
As part of supporting KeyUpdate in ktls-utils and NVMe-OF we need to
trigger an update of the local keys after the kernel has received a
KeyUpdate message.

This patch creates a new gnutls_handshake_update_receiving_key() function
that allows updating the local keys without sending any KeyUpdate
requests.

Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Modified-by: Daiki Ueno <ueno@gnu.org>
NEWS
devel/symbols.last
doc/Makefile.am
doc/manpages/Makefile.am
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/tls13/key_update.c
lib/tls13/key_update.h
tests/handshake-write.c

diff --git a/NEWS b/NEWS
index f2457db51f31b1f82bfe4df67498037e81ab01db..648206d805bbc9203a160324635ffdc410c4cc68 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,11 @@ See the end for copying conditions.
    A new function gnutls_record_get_max_send_size() has been added to
    determine the maximum size of a TLS record to be sent to the peer.
 
+** libgnutls: Expose a new function to update keys without sending a KeyUpdate
+   to the peer. A new function gnutls_handshake_update_receiving_key()
+   has been added to allow updating the local receiving key without
+   sending any KeyUpdate messages.
+
 ** libgnutls: PKCS#11 cryptographic provider configuration takes a token URI
    instead of a module path. To allow using a PKCS#11 module exposing
    multiple tokens, the "path" configuration keyword was replaced with
@@ -26,6 +31,7 @@ See the end for copying conditions.
 gnutls_psk_allocate_client_credentials2: New function
 gnutls_psk_allocate_server_credentials2: New function
 gnutls_record_get_max_send_size: New function
+gnutls_handshake_update_receiving_key: New function
 
 * Version 3.8.10 (released 2025-07-08)
 
index 7cdfc8c3e7149efcb9ff45b06c000c761d6338c9..25084c13c6ffd7ee7f98293a61c5cb48c4f7e4b4 100644 (file)
@@ -22,6 +22,7 @@ GNUTLS_3_8_1@GNUTLS_3_8_1
 GNUTLS_3_8_2@GNUTLS_3_8_2
 GNUTLS_3_8_4@GNUTLS_3_8_4
 GNUTLS_3_8_6@GNUTLS_3_8_6
+GNUTLS_3_8_10@GNUTLS_3_8_10
 _gnutls_global_init_skip@GNUTLS_3_4
 gnutls_aead_cipher_decrypt@GNUTLS_3_4
 gnutls_aead_cipher_decryptv2@GNUTLS_3_6_10
@@ -832,6 +833,7 @@ gnutls_session_ticket_enable_client@GNUTLS_3_4
 gnutls_session_ticket_enable_server@GNUTLS_3_4
 gnutls_session_ticket_key_generate@GNUTLS_3_4
 gnutls_session_ticket_send@GNUTLS_3_6_3
+gnutls_handshake_update_receiving_key@GNUTLS_3_8_6
 gnutls_set_default_priority@GNUTLS_3_4
 gnutls_set_default_priority_append@GNUTLS_3_6_3
 gnutls_sign_algorithm_get@GNUTLS_3_4
index d697e66bdcfe53478a1dea6c490b2289f5b6220b..6b95ae255c52d2563b846eab351701af64d5490c 100644 (file)
@@ -1131,6 +1131,8 @@ FUNCS += functions/gnutls_handshake_set_secret_function
 FUNCS += functions/gnutls_handshake_set_secret_function.short
 FUNCS += functions/gnutls_handshake_set_timeout
 FUNCS += functions/gnutls_handshake_set_timeout.short
+FUNCS += functions/gnutls_handshake_update_receiving_key
+FUNCS += functions/gnutls_handshake_update_receiving_key.short
 FUNCS += functions/gnutls_handshake_write
 FUNCS += functions/gnutls_handshake_write.short
 FUNCS += functions/gnutls_hash
index eeca130abd6e716c643f827246a4f1c6b0f4e873..65a329ae14204bd0d34163a57089ff81ee9eb8c0 100644 (file)
@@ -411,6 +411,7 @@ APIMANS += gnutls_handshake_set_random.3
 APIMANS += gnutls_handshake_set_read_function.3
 APIMANS += gnutls_handshake_set_secret_function.3
 APIMANS += gnutls_handshake_set_timeout.3
+APIMANS += gnutls_handshake_update_receiving_key.3
 APIMANS += gnutls_handshake_write.3
 APIMANS += gnutls_hash.3
 APIMANS += gnutls_hash_copy.3
index d14cd4bf43dda443a186727fccfaa039c3fe9245..781c9be4c2f49532904cdcbde9657c009befe71b 100644 (file)
@@ -1330,6 +1330,8 @@ int gnutls_rehandshake(gnutls_session_t session);
 #define GNUTLS_KU_PEER 1
 int gnutls_session_key_update(gnutls_session_t session, unsigned flags);
 
+int gnutls_handshake_update_receiving_key(gnutls_session_t session);
+
 gnutls_alert_description_t gnutls_alert_get(gnutls_session_t session);
 int gnutls_alert_send(gnutls_session_t session, gnutls_alert_level_t level,
                      gnutls_alert_description_t desc);
index 2d86a1e14ac6b5837404281bbe566f2dbbfe2c71..2ae72e5477959ff0144e8ece6b2e4b8dfca92058 100644 (file)
@@ -1456,6 +1456,7 @@ GNUTLS_3_8_11
        gnutls_psk_allocate_client_credentials2;
        gnutls_psk_allocate_server_credentials2;
        gnutls_record_get_max_send_size;
+       gnutls_handshake_update_receiving_key;
  local:
        *;
 } GNUTLS_3_8_6;
index beee1dc41a4822ab672477088451b696e719af4a..f7fe13a945513e4dc8b8ed4f75c3c5d165abe197 100644 (file)
@@ -274,3 +274,42 @@ int gnutls_session_key_update(gnutls_session_t session, unsigned flags)
 
        return 0;
 }
+
+/**
+ * gnutls_handshake_update_receiving_key:
+ * @session: is a #gnutls_session_t type.
+ *
+ * This function will update/refresh the session receiving keys when
+ * the TLS protocol is 1.3 or better. Unlike gnutls_session_key_update()
+ * this function does not notify the peer, it will only update
+ * the local keys.
+ *
+ * If the negotiated version is not TLS 1.3 or better this
+ * function will return %GNUTLS_E_INVALID_REQUEST.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.8.11
+ **/
+int gnutls_handshake_update_receiving_key(gnutls_session_t session)
+{
+       int ret;
+       const version_entry_st *vers = get_version(session);
+
+       if (!vers->tls13_sem)
+               return GNUTLS_E_INVALID_REQUEST;
+
+       _gnutls_epoch_gc(session);
+
+       ret = _tls13_update_secret(session,
+                                  session->key.proto.tls13.temp_secret,
+                                  session->key.proto.tls13.temp_secret_size);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       ret = update_receiving_key(session, STAGE_UPD_PEERS);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       return 0;
+}
index 326af381bca301c3386224a134713d5b577c9de4..9e7040bf979a660caa49cd5886d2c7b734214a38 100644 (file)
@@ -23,6 +23,8 @@
 #ifndef GNUTLS_LIB_TLS13_KEY_UPDATE_H
 #define GNUTLS_LIB_TLS13_KEY_UPDATE_H
 
+int gnutls_handshake_update_receiving_key(gnutls_session_t session);
+
 int _gnutls13_recv_key_update(gnutls_session_t session, gnutls_buffer_st *buf);
 int _gnutls13_send_key_update(gnutls_session_t session, unsigned again,
                              unsigned flags);
index 1cecb069935281a4a865acd60fe1b27be78fcfec..600cebf8d0c17deb909c13bdd31788482cb48c91 100644 (file)
@@ -73,6 +73,9 @@ static int handshake_read_func(gnutls_session_t session,
        if (htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
                return 0;
 
+       if (htype == GNUTLS_HANDSHAKE_KEY_UPDATE)
+               return 0;
+
        return gnutls_handshake_write(peer, level, data, data_size);
 }
 
@@ -144,6 +147,17 @@ static void run(const char *name, const char *prio)
        TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
        TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
 
+       /* Trigger a KeyUpdate that won't actually be sent to the client,
+        * as handshake_read_func() will drop the message.
+        */
+       gnutls_session_key_update(server, GNUTLS_KU_PEER);
+
+       /* Manually update the client keys */
+       gnutls_handshake_update_receiving_key(client);
+
+       TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+       TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+
        gnutls_bye(client, GNUTLS_SHUT_WR);
        gnutls_bye(server, GNUTLS_SHUT_WR);