Deduplicate our three ServerHello parsers.

We do this enough that it's worth extracting a common parser. And this
gives a struct we can pass around. Note this moves the server extensions
block parsing out of ssl_scan_serverhello_tlsext.

I've also consolidated a few error conditions to tighten the code up a
bit: the TLS 1.2 code distinguishes unknown from unadvertised cipher,
while the TLS 1.3 code didn't. And seeing the wrong legacy version
number in TLS 1.3 is really just a syntax error since it's not the
version field anymore. (RFC8446 specifies the value.)

Change-Id: Ia2f44ff9a3899b5a594569f1b258f2b487930496
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48908
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/extensions.cc b/ssl/extensions.cc
index 9af928c..6fec445 100644
--- a/ssl/extensions.cc
+++ b/ssl/extensions.cc
@@ -3716,18 +3716,10 @@
   return true;
 }
 
-static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,
+static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs,
                                         int *out_alert) {
-  SSL *const ssl = hs->ssl;
-  // Before TLS 1.3, ServerHello extensions blocks may be omitted if empty.
-  if (CBS_len(cbs) == 0 && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
-    return true;
-  }
-
-  // Decode the extensions block and check it is valid.
-  CBS extensions;
-  if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
-      !tls1_check_duplicate_extensions(&extensions)) {
+  CBS extensions = *cbs;
+  if (!tls1_check_duplicate_extensions(&extensions)) {
     *out_alert = SSL_AD_DECODE_ERROR;
     return false;
   }
@@ -3849,7 +3841,7 @@
   return true;
 }
 
-bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs) {
   SSL *const ssl = hs->ssl;
   int alert = SSL_AD_DECODE_ERROR;
   if (!ssl_scan_serverhello_tlsext(hs, cbs, &alert)) {
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index ba8f4b7..b5199ba 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -354,42 +354,36 @@
   return ssl->method->add_message(ssl, std::move(msg));
 }
 
-static bool parse_supported_versions(SSL_HANDSHAKE *hs, uint16_t *version,
-                                     const CBS *in) {
-  // If the outer version is not TLS 1.2, or there is no extensions block, use
-  // the outer version.
-  if (*version != TLS1_2_VERSION || CBS_len(in) == 0) {
+static bool parse_server_version(const SSL_HANDSHAKE *hs, uint16_t *out_version,
+                                 uint8_t *out_alert,
+                                 const ParsedServerHello &server_hello) {
+  // If the outer version is not TLS 1.2, use it.
+  // TODO(davidben): This function doesn't quite match the RFC8446 formulation.
+  if (server_hello.legacy_version != TLS1_2_VERSION) {
+    *out_version = server_hello.legacy_version;
     return true;
   }
 
-  SSL *const ssl = hs->ssl;
-  CBS copy = *in, extensions;
-  if (!CBS_get_u16_length_prefixed(&copy, &extensions) ||
-      CBS_len(&copy) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return false;
-  }
-
   bool have_supported_versions;
   CBS supported_versions;
   const SSL_EXTENSION_TYPE ext_types[] = {
-    {TLSEXT_TYPE_supported_versions, &have_supported_versions,
-     &supported_versions},
+      {TLSEXT_TYPE_supported_versions, &have_supported_versions,
+       &supported_versions},
   };
-
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+  CBS extensions = server_hello.extensions;
+  if (!ssl_parse_extensions(&extensions, out_alert, ext_types,
                             /*ignore_unknown=*/true)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return false;
   }
 
-  // Override the outer version with the extension, if present.
-  if (have_supported_versions &&
-      (!CBS_get_u16(&supported_versions, version) ||
-       CBS_len(&supported_versions) != 0)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+  if (!have_supported_versions) {
+    *out_version = server_hello.legacy_version;
+    return true;
+  }
+
+  if (!CBS_get_u16(&supported_versions, out_version) ||
+       CBS_len(&supported_versions) != 0) {
+    *out_alert = SSL_AD_DECODE_ERROR;
     return false;
   }
 
@@ -657,6 +651,37 @@
   return ssl_hs_flush;
 }
 
+bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
+                            const SSLMessage &msg) {
+  if (msg.type != SSL3_MT_SERVER_HELLO) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+    return false;
+  }
+  CBS body = msg.body;
+  if (!CBS_get_u16(&body, &out->legacy_version) ||
+      !CBS_get_bytes(&body, &out->random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&body, &out->session_id) ||
+      CBS_len(&out->session_id) > SSL3_SESSION_ID_SIZE ||
+      !CBS_get_u16(&body, &out->cipher_suite) ||
+      !CBS_get_u8(&body, &out->compression_method)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+  // In TLS 1.2 and below, empty extensions blocks may be omitted. In TLS 1.3,
+  // ServerHellos always have extensions, so this can be applied generically.
+  CBS_init(&out->extensions, nullptr, 0);
+  if ((CBS_len(&body) != 0 &&
+       !CBS_get_u16_length_prefixed(&body, &out->extensions)) ||
+      CBS_len(&body) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+  return true;
+}
+
 static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   SSLMessage msg;
@@ -664,26 +689,12 @@
     return ssl_hs_read_server_hello;
   }
 
-  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
-    return ssl_hs_error;
-  }
-
-  CBS server_hello = msg.body, server_random, session_id;
-  uint16_t server_version, cipher_suite;
-  uint8_t compression_method;
-  if (!CBS_get_u16(&server_hello, &server_version) ||
-      !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
-      CBS_len(&session_id) > SSL3_SESSION_ID_SIZE ||
-      !CBS_get_u16(&server_hello, &cipher_suite) ||
-      !CBS_get_u8(&server_hello, &compression_method)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
-  // Use the supported_versions extension if applicable.
-  if (!parse_supported_versions(hs, &server_version, &server_hello)) {
+  ParsedServerHello server_hello;
+  uint16_t server_version;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!ssl_parse_server_hello(&server_hello, &alert, msg) ||
+      !parse_server_version(hs, &server_version, &alert, server_hello)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -737,7 +748,7 @@
   }
 
   // Copy over the server random.
-  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
+  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
                  SSL3_RANDOM_SIZE);
 
   // Enforce the TLS 1.3 anti-downgrade feature.
@@ -760,28 +771,26 @@
     }
   }
 
-  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
-  if (cipher == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return ssl_hs_error;
-  }
-  hs->new_cipher = cipher;
-
   // The cipher must be allowed in the selected version and enabled.
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
   uint32_t mask_a, mask_k;
   ssl_get_client_disabled(hs, &mask_a, &mask_k);
-  if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
+  if (cipher == nullptr ||
+      (cipher->algorithm_mkey & mask_k) ||
+      (cipher->algorithm_auth & mask_a) ||
       SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
-      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, cipher)) {
+      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), nullptr, cipher)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
+  hs->new_cipher = cipher;
+
   if (hs->session_id_len != 0 &&
-      CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len)) {
+      CBS_mem_equal(&server_hello.session_id, hs->session_id,
+                    hs->session_id_len)) {
     // Echoing the ClientHello session ID in TLS 1.2, whether from the session
     // or a synthetic one, indicates resumption. If there was no session (or if
     // the session was only offered in ECH ClientHelloInner), this was the
@@ -799,7 +808,7 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
-    if (ssl->session->cipher != cipher) {
+    if (ssl->session->cipher != hs->new_cipher) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
@@ -822,10 +831,11 @@
       return ssl_hs_error;
     }
     // Note: session_id could be empty.
-    hs->new_session->session_id_length = CBS_len(&session_id);
-    OPENSSL_memcpy(hs->new_session->session_id, CBS_data(&session_id),
-                   CBS_len(&session_id));
-    hs->new_session->cipher = cipher;
+    hs->new_session->session_id_length = CBS_len(&server_hello.session_id);
+    OPENSSL_memcpy(hs->new_session->session_id,
+                   CBS_data(&server_hello.session_id),
+                   CBS_len(&server_hello.session_id));
+    hs->new_session->cipher = hs->new_cipher;
   }
 
   // Now that the cipher is known, initialize the handshake hash and hash the
@@ -845,26 +855,17 @@
   }
 
   // Only the NULL compression algorithm is supported.
-  if (compression_method != 0) {
+  if (server_hello.compression_method != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  // TLS extensions
-  if (!ssl_parse_serverhello_tlsext(hs, &server_hello)) {
+  if (!ssl_parse_serverhello_tlsext(hs, &server_hello.extensions)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     return ssl_hs_error;
   }
 
-  // There should be nothing left over in the record.
-  if (CBS_len(&server_hello) != 0) {
-    // wrong packet length
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
   if (ssl->session != NULL &&
       hs->extended_master_secret != ssl->session->extended_master_secret) {
     if (ssl->session->extended_master_secret) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 7505e5d..0c91724 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -2163,6 +2163,21 @@
 // flight. It returns true on success and false on error.
 bool ssl_add_client_hello(SSL_HANDSHAKE *hs);
 
+struct ParsedServerHello {
+  uint16_t legacy_version = 0;
+  CBS random;
+  CBS session_id;
+  uint16_t cipher_suite = 0;
+  uint8_t compression_method = 0;
+  CBS extensions;
+};
+
+// ssl_parse_server_hello parses |msg| as a ServerHello. On success, it writes
+// the result to |*out| and returns true. Otherwise, it returns false and sets
+// |*out_alert| to an alert to send to the peer.
+bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
+                            const SSLMessage &msg);
+
 enum ssl_cert_verify_context_t {
   ssl_cert_verify_server,
   ssl_cert_verify_client,
@@ -3304,7 +3319,7 @@
 bool ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out);
 bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
                                   const SSL_CLIENT_HELLO *client_hello);
-bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *extensions);
 
 #define tlsext_tick_md EVP_sha256
 
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 03dcbf1..1953b88 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -3833,7 +3833,7 @@
 			},
 		},
 		shouldFail:    true,
-		expectedError: ":UNKNOWN_CIPHER_RETURNED:",
+		expectedError: ":WRONG_CIPHER_RETURNED:",
 	})
 	testCases = append(testCases, testCase{
 		name: "ServerHelloBogusCipher-TLS13",
@@ -13884,7 +13884,7 @@
 			},
 		},
 		shouldFail:    true,
-		expectedError: ":WRONG_VERSION_NUMBER:",
+		expectedError: ":DECODE_ERROR:",
 	})
 
 	testCases = append(testCases, testCase{
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index bc137aa..7a19b2a 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -101,6 +101,25 @@
   return true;
 }
 
+static bool parse_server_hello_tls13(const SSL_HANDSHAKE *hs,
+                                     ParsedServerHello *out, uint8_t *out_alert,
+                                     const SSLMessage &msg) {
+  if (!ssl_parse_server_hello(out, out_alert, msg)) {
+    return false;
+  }
+  // The RFC8446 version of the structure fixes some legacy values.
+  // Additionally, the session ID must echo the original one.
+  if (out->legacy_version != TLS1_2_VERSION ||
+      out->compression_method != 0 ||
+      !CBS_mem_equal(&out->session_id, hs->session_id, hs->session_id_len) ||
+      CBS_len(&out->extensions) == 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+  return true;
+}
+
 static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   assert(ssl->s3->have_version);
@@ -117,32 +136,17 @@
     return ssl_hs_error;
   }
 
-  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
-    return ssl_hs_error;
-  }
-
-  CBS body = msg.body, extensions, server_random, session_id;
-  uint16_t server_version, cipher_suite;
-  uint8_t compression_method;
-  if (!CBS_get_u16(&body, &server_version) ||
-      !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&body, &session_id) ||
-      !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
-      !CBS_get_u16(&body, &cipher_suite) ||
-      !CBS_get_u8(&body, &compression_method) ||
-      compression_method != 0 ||
-      !CBS_get_u16_length_prefixed(&body, &extensions) ||
-      CBS_len(&extensions) == 0 ||
-      CBS_len(&body) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+  ParsedServerHello server_hello;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
   // The cipher suite must be one we offered. We currently offer all supported
   // TLS 1.3 ciphers, so check the version.
-  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
-  if (cipher == NULL ||
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
+  if (cipher == nullptr ||
       SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
@@ -152,7 +156,8 @@
 
   hs->new_cipher = cipher;
 
-  if (!CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
+  if (!CBS_mem_equal(&server_hello.random, kHelloRetryRequest,
+                     SSL3_RANDOM_SIZE)) {
     hs->tls13_state = state_read_server_hello;
     return ssl_hs_ok;
   }
@@ -166,8 +171,7 @@
        &supported_versions},
   };
 
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+  if (!ssl_parse_extensions(&server_hello.extensions, &alert, ext_types,
                             /*ignore_unknown=*/false)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
@@ -287,49 +291,29 @@
   if (!ssl->method->get_message(ssl, &msg)) {
     return ssl_hs_read_message;
   }
-  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
-    return ssl_hs_error;
-  }
-
-  CBS body = msg.body, server_random, session_id, extensions;
-  uint16_t server_version;
-  uint16_t cipher_suite;
-  uint8_t compression_method;
-  if (!CBS_get_u16(&body, &server_version) ||
-      !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&body, &session_id) ||
-      !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
-      !CBS_get_u16(&body, &cipher_suite) ||
-      !CBS_get_u8(&body, &compression_method) ||
-      compression_method != 0 ||
-      !CBS_get_u16_length_prefixed(&body, &extensions) ||
-      CBS_len(&body) != 0) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
-  if (server_version != TLS1_2_VERSION) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
+  ParsedServerHello server_hello;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
   // Forbid a second HelloRetryRequest.
-  if (CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
+  if (CBS_mem_equal(&server_hello.random, kHelloRetryRequest,
+                    SSL3_RANDOM_SIZE)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     return ssl_hs_error;
   }
 
   // Check the cipher suite, in case this is after HelloRetryRequest.
-  if (SSL_CIPHER_get_value(hs->new_cipher) != cipher_suite) {
+  if (SSL_CIPHER_get_value(hs->new_cipher) != server_hello.cipher_suite) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
+  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
                  SSL3_RANDOM_SIZE);
 
   // Parse out the extensions.
@@ -343,8 +327,7 @@
        &supported_versions},
   };
 
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+  if (!ssl_parse_extensions(&server_hello.extensions, &alert, ext_types,
                             /*ignore_unknown=*/false)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
@@ -521,17 +504,19 @@
     return ssl_hs_error;
   }
 
-  CBS body = msg.body;
-  if (!ssl_parse_serverhello_tlsext(hs, &body)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
-    return ssl_hs_error;
-  }
-  if (CBS_len(&body) != 0) {
+  CBS body = msg.body, extensions;
+  if (!CBS_get_u16_length_prefixed(&body, &extensions) ||
+      CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
+  if (!ssl_parse_serverhello_tlsext(hs, &extensions)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+    return ssl_hs_error;
+  }
+
   if (ssl->s3->early_data_accepted) {
     // The extension parser checks the server resumed the session.
     assert(ssl->s3->session_reused);