From 24c9a24640c137b47bb1e8cc5fee2315f57219ad Mon Sep 17 00:00:00 2001
From: Daiki Ueno <ueno@gnu.org>
Date: Thu, 22 Apr 2021 16:42:01 +0200
Subject: [PATCH] handshake: don't regenerate legacy_session_id in second CH
 after HRR

According to RFC 8446 4.1.2, the client must send the same Client
Hello after Hello Retry Request, except for the certain extensions,
and thus legacy_session_id must be preserved.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
---
 lib/handshake.c                   | 20 +++++++++++---------
 tests/tls13/hello_retry_request.c | 20 ++++++++++++++++++++
 2 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/lib/handshake.c b/lib/handshake.c
index 33bc7f7be6..c30703ccbd 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -2216,15 +2216,17 @@ static int send_client_hello(gnutls_session_t session, int again)
 		if (max_ver->tls13_sem &&
 		    session->internals.priorities->tls13_compat_mode &&
 		    !resuming) {
-			/* Under TLS1.3 we generate a random session ID to make
-			 * the TLS1.3 session look like a resumed TLS1.2 session */
-			ret = _gnutls_generate_session_id(session->security_parameters.
-							  session_id,
-							  &session->security_parameters.
-							  session_id_size);
-			if (ret < 0) {
-				gnutls_assert();
-				goto cleanup;
+			if (!(session->internals.hsk_flags & HSK_HRR_RECEIVED)) {
+				/* Under TLS1.3 we generate a random session ID to make
+				 * the TLS1.3 session look like a resumed TLS1.2 session */
+				ret = _gnutls_generate_session_id(session->security_parameters.
+								  session_id,
+								  &session->security_parameters.
+								  session_id_size);
+				if (ret < 0) {
+					gnutls_assert();
+					goto cleanup;
+				}
 			}
 			session_id = session->security_parameters.session_id;
 			session_id_len = session->security_parameters.session_id_size;
diff --git a/tests/tls13/hello_retry_request.c b/tests/tls13/hello_retry_request.c
index f90d4ad676..dd4506b6f9 100644
--- a/tests/tls13/hello_retry_request.c
+++ b/tests/tls13/hello_retry_request.c
@@ -69,9 +69,13 @@ static void client_log_func(int level, const char *str)
 	fprintf(stderr, "client|<%d>| %s", level, str);
 }
 
+#define HANDSHAKE_SESSION_ID_POS 34
+
 struct ctx_st {
 	unsigned hrr_seen;
 	unsigned hello_counter;
+	uint8_t session_id[32];
+	size_t session_id_len;
 };
 
 static int hello_callback(gnutls_session_t session, unsigned int htype,
@@ -84,12 +88,28 @@ static int hello_callback(gnutls_session_t session, unsigned int htype,
 		ctx->hrr_seen = 1;
 
 	if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && post == GNUTLS_HOOK_POST) {
+		size_t session_id_len;
+		uint8_t *session_id;
+
+		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];
+
 		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", (int)msg->data[0], (int)msg->data[1]);
 			}
+
+			if (session_id_len != ctx->session_id_len ||
+			    memcmp(session_id, ctx->session_id, session_id_len) != 0) {
+				fail("different legacy_session_id is sent after HRR\n");
+			}
 		}
+
+		ctx->session_id_len = session_id_len;
+		memcpy(ctx->session_id, session_id, session_id_len);
+
 		ctx->hello_counter++;
 	}
 
-- 
2.30.2

