Your message dated Sat, 16 May 2026 10:23:17 +0000
with message-id <[email protected]>
and subject line Released with 13.5
has caused the Debian Bug report #1130735,
regarding trixie-pu: package gnutls28/3.8.9-3+deb13u3
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1130735: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1130735
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], Simon McVittie <[email protected]>
Control: affects -1 + src:gnutls28
User: [email protected]
Usertags: pu


Hello,

I would like to fix #1130152 for trixie. Simon has done all the heavy
and not so heavy lifting on this, let's quote #1130152:

8X-------------------
A regression in GnuTLS 3.8.5, which started shuffling the extensions
order, causes an interoperability issue leading to handshake failures
with some SSL/TLS servers. I'm reporting this at important severity since
it's an interop regression affecting an unknown number of remote services.

From the linked regression report https://github.com/luakit/luakit/issues/1101,
it seems that at the time of writing, search.dismail.de is a good test-case,
for example:
[...]
    # gnutls-cli search.dismail.de
    Processed 150 CA certificate(s).
    Resolving 'search.dismail.de:443'...
    Connecting to '128.140.68.142:443'...
    *** Fatal error: A TLS fatal alert has been received.
    *** Received alert [47]: Illegal parameter
[...]
I've confirmed that 3.8.12-2 in forky and 3.7.9-2+deb12u6 in bookworm
are both unaffected by this: they successfully connect to that server,
with gnutls-cli output that includes "Handshake was completed". (Press
Ctrl+D to exit after seeing this.)

This appears to have been fixed by
https://gitlab.com/gnutls/gnutls/-/merge_requests/1930
after the 3.8.9 release, commit
[...]
8X-------------------


I have verified the proposed change and that it fixes the issue.

TIA, cu Andreas

-- 
"You people are noisy," Nia said.
I made the gesture of agreement.
diff --git a/debian/changelog b/debian/changelog
index ee6981c..867d2f8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+gnutls28 (3.8.9-3+deb13u3) trixie; urgency=medium
+
+  [ Simon McVittie ]
+  * d/p/51_handshake-only-shuffle-extensions-in-the-first-Client-Hel.patch:
+    Preserve extension order across client Hello retry.
+    This resolves an interop regression in 3.8.5 with servers that enforce
+    the RFC requirement that the Client Hello after a Hello Retry Request
+    has the same extensions as the original Client Hello, in the same order
+    (Closes: #1130152)
+
+ -- Andreas Metzler <[email protected]>  Sat, 14 Mar 2026 07:19:14 +0100
+
 gnutls28 (3.8.9-3+deb13u2) trixie-security; urgency=high
 
   * libgnutls: Fix name constraint processing performance issue
diff --git a/debian/patches/51_handshake-only-shuffle-extensions-in-the-first-Client-Hel.patch b/debian/patches/51_handshake-only-shuffle-extensions-in-the-first-Client-Hel.patch
new file mode 100644
index 0000000..dd48dfa
--- /dev/null
+++ b/debian/patches/51_handshake-only-shuffle-extensions-in-the-first-Client-Hel.patch
@@ -0,0 +1,213 @@
+From: Daiki Ueno <[email protected]>
+Date: Sun, 9 Feb 2025 10:31:20 +0900
+Subject: handshake: only shuffle extensions in the first Client Hello
+
+RFC 8446 section 4.1.2 states that the second Client Hello after HRR
+should preserve the same content as the first Client Hello with
+limited exceptions.  Since GnuTLS 3.8.5, however, the library started
+shuffling the order of extensions for privacy reasons and that didn't
+comply with the RFC, leading to a connectivity issue against the
+server configuration with a stricter check on that.
+
+Signed-off-by: Daiki Ueno <[email protected]>
+Origin: upstream, 3.8.10, commit:dc5ee80c3a28577e9de0f82fb08164e4c02b96af
+Bug: https://gitlab.com/gnutls/gnutls/-/work_items/1660
+Bug-Debian: https://bugs.debian.org/1130152
+Bug-SteamRT: steamrt/tasks#938
+---
+ lib/gnutls_int.h                  |  4 +++
+ lib/hello_ext.c                   | 41 +++++++++++++++++++------------
+ lib/state.c                       |  2 ++
+ tests/tls13/hello_retry_request.c | 51 ++++++++++++++++++++++++++++++++++++---
+ 4 files changed, 79 insertions(+), 19 deletions(-)
+
+diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
+index d10a028..572de5a 100644
+--- a/lib/gnutls_int.h
++++ b/lib/gnutls_int.h
+@@ -1666,6 +1666,10 @@ typedef struct {
+ 	/* Compression method for certificate compression */
+ 	gnutls_compression_method_t compress_certificate_method;
+ 
++	/* To shuffle extension sending order */
++	extensions_t client_hello_exts[MAX_EXT_TYPES];
++	bool client_hello_exts_set;
++
+ 	/* If you add anything here, check _gnutls_handshake_internal_state_clear().
+ 	 */
+ } internals_st;
+diff --git a/lib/hello_ext.c b/lib/hello_ext.c
+index 45237b8..94e6d86 100644
+--- a/lib/hello_ext.c
++++ b/lib/hello_ext.c
+@@ -438,8 +438,6 @@ int _gnutls_gen_hello_extensions(gnutls_session_t session,
+ 	int pos, ret;
+ 	size_t i;
+ 	hello_ext_ctx_st ctx;
+-	/* To shuffle extension sending order */
+-	extensions_t indices[MAX_EXT_TYPES];
+ 
+ 	msg &= GNUTLS_EXT_FLAG_SET_ONLY_FLAGS_MASK;
+ 
+@@ -469,26 +467,39 @@ int _gnutls_gen_hello_extensions(gnutls_session_t session,
+ 				ret - 4);
+ 	}
+ 
+-	/* Initializing extensions array */
+-	for (i = 0; i < MAX_EXT_TYPES; i++) {
+-		indices[i] = i;
+-	}
++	if (msg & GNUTLS_EXT_FLAG_CLIENT_HELLO &&
++	    !session->internals.client_hello_exts_set) {
++		/* Initializing extensions array */
++		for (i = 0; i < MAX_EXT_TYPES; i++) {
++			session->internals.client_hello_exts[i] = i;
++		}
+ 
+-	if (!session->internals.priorities->no_shuffle_extensions) {
+-		/* Ordering padding and pre_shared_key as last extensions */
+-		swap_exts(indices, MAX_EXT_TYPES - 2, GNUTLS_EXTENSION_DUMBFW);
+-		swap_exts(indices, MAX_EXT_TYPES - 1,
+-			  GNUTLS_EXTENSION_PRE_SHARED_KEY);
++		if (!session->internals.priorities->no_shuffle_extensions) {
++			/* Ordering padding and pre_shared_key as last extensions */
++			swap_exts(session->internals.client_hello_exts,
++				  MAX_EXT_TYPES - 2, GNUTLS_EXTENSION_DUMBFW);
++			swap_exts(session->internals.client_hello_exts,
++				  MAX_EXT_TYPES - 1,
++				  GNUTLS_EXTENSION_PRE_SHARED_KEY);
+ 
+-		ret = shuffle_exts(indices, MAX_EXT_TYPES - 2);
+-		if (ret < 0)
+-			return gnutls_assert_val(ret);
++			ret = shuffle_exts(session->internals.client_hello_exts,
++					   MAX_EXT_TYPES - 2);
++			if (ret < 0)
++				return gnutls_assert_val(ret);
++		}
++		session->internals.client_hello_exts_set = true;
+ 	}
+ 
+ 	/* hello_ext_send() ensures we don't send duplicates, in case
+ 	 * of overridden extensions */
+ 	for (i = 0; i < MAX_EXT_TYPES; i++) {
+-		size_t ii = indices[i];
++		size_t ii;
++
++		if (msg & GNUTLS_EXT_FLAG_CLIENT_HELLO)
++			ii = session->internals.client_hello_exts[i];
++		else
++			ii = i;
++
+ 		if (!extfunc[ii])
+ 			continue;
+ 
+diff --git a/lib/state.c b/lib/state.c
+index 020f212..8090686 100644
+--- a/lib/state.c
++++ b/lib/state.c
+@@ -518,6 +518,8 @@ static void handshake_internal_state_clear1(gnutls_session_t session)
+ 
+ 	session->internals.hrr_cs[0] = CS_INVALID_MAJOR;
+ 	session->internals.hrr_cs[1] = CS_INVALID_MINOR;
++
++	session->internals.client_hello_exts_set = false;
+ }
+ 
+ /* This function will clear all the variables in internals
+diff --git a/tests/tls13/hello_retry_request.c b/tests/tls13/hello_retry_request.c
+index f407b64..6c5f698 100644
+--- a/tests/tls13/hello_retry_request.c
++++ b/tests/tls13/hello_retry_request.c
+@@ -51,14 +51,37 @@ static void tls_log_func(int level, const char *str)
+ }
+ 
+ #define HANDSHAKE_SESSION_ID_POS 34
++#define MAX_EXT_TYPES 64
+ 
+ struct ctx_st {
+ 	unsigned hrr_seen;
+ 	unsigned hello_counter;
+ 	uint8_t session_id[32];
+ 	size_t session_id_len;
++	unsigned extensions[MAX_EXT_TYPES];
++	size_t extensions_size1;
++	size_t extensions_size2;
+ };
+ 
++static int ext_callback(void *_ctx, unsigned tls_id, const unsigned char *data,
++			unsigned size)
++{
++	struct ctx_st *ctx = _ctx;
++	if (ctx->hello_counter == 0) {
++		assert(ctx->extensions_size1 < MAX_EXT_TYPES);
++		ctx->extensions[ctx->extensions_size1++] = tls_id;
++	} else {
++		assert(ctx->extensions_size2 < MAX_EXT_TYPES);
++		if (tls_id != ctx->extensions[ctx->extensions_size2]) {
++			fail("extension doesn't match at position %zu, %u != %u\n",
++			     ctx->extensions_size2, tls_id,
++			     ctx->extensions[ctx->extensions_size2]);
++		}
++		ctx->extensions_size2++;
++	}
++	return 0;
++}
++
+ static int hello_callback(gnutls_session_t session, unsigned int htype,
+ 			  unsigned post, unsigned int incoming,
+ 			  const gnutls_datum_t *msg)
+@@ -73,15 +96,25 @@ static int hello_callback(gnutls_session_t session, unsigned int htype,
+ 	    post == GNUTLS_HOOK_POST) {
+ 		size_t session_id_len;
+ 		uint8_t *session_id;
++		unsigned pos = HANDSHAKE_SESSION_ID_POS;
++		gnutls_datum_t mmsg;
++		int ret;
+ 
+-		assert(msg->size > HANDSHAKE_SESSION_ID_POS + 1);
+-		session_id_len = msg->data[HANDSHAKE_SESSION_ID_POS];
+-		session_id = &msg->data[HANDSHAKE_SESSION_ID_POS + 1];
++		assert(msg->size > pos + 1);
++		session_id_len = msg->data[pos];
++		session_id = &msg->data[pos + 1];
++
++		SKIP8(pos, msg->size);
++		SKIP16(pos, msg->size);
++		SKIP8(pos, msg->size);
++
++		mmsg.data = &msg->data[pos];
++		mmsg.size = msg->size - pos;
+ 
+ 		if (ctx->hello_counter > 0) {
+ 			assert(msg->size > 4);
+ 			if (msg->data[0] != 0x03 || msg->data[1] != 0x03) {
+-				fail("version is %d.%d expected 3,3\n",
++				fail("version is %d.%d expected 3.3\n",
+ 				     (int)msg->data[0], (int)msg->data[1]);
+ 			}
+ 
+@@ -95,6 +128,12 @@ static int hello_callback(gnutls_session_t session, unsigned int htype,
+ 		ctx->session_id_len = session_id_len;
+ 		memcpy(ctx->session_id, session_id, session_id_len);
+ 
++		ret = gnutls_ext_raw_parse(ctx, ext_callback, &mmsg, 0);
++		if (ret < 0) {
++			fail("unable to parse extensions: %s\n",
++			     gnutls_strerror(ret));
++		}
++
+ 		ctx->hello_counter++;
+ 	}
+ 
+@@ -164,6 +203,10 @@ void doit(void)
+ 		myfail("group doesn't match the expected: %s\n",
+ 		       gnutls_group_get_name(gnutls_group_get(server)));
+ 
++	if (ctx.extensions_size1 != ctx.extensions_size2)
++		myfail("the number of extensions don't match in second Client Hello: %zu != %zu\n",
++		       ctx.extensions_size1, ctx.extensions_size2);
++
+ 	gnutls_bye(client, GNUTLS_SHUT_WR);
+ 	gnutls_bye(server, GNUTLS_SHUT_WR);
+ 
diff --git a/debian/patches/series b/debian/patches/series
index 8270734..6d55279 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -21,3 +21,4 @@
 50_0007-x509-name_constraints-implement-name_constraints_nod.patch
 50_0008-x509-name_constraints-make-types_with_empty_intersec.patch
 50_0009-x509-name_constraints-name_constraints_node_list_int.patch
+51_handshake-only-shuffle-extensions-in-the-first-Client-Hel.patch

Attachment: signature.asc
Description: PGP signature


--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 13.5

This update has been released as part of Debian 13.5.

--- End Message ---

Reply via email to