Moving TLS 1.3 version negotiation into extension.

Change-Id: I73f9fd64b46f26978b897409d817b34ec9d93afd
Reviewed-on: https://boringssl-review.googlesource.com/11080
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index f797585..fd0223f 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -564,38 +564,75 @@
     return 0;
   }
 
-  /* For TLS versions which use ClientHello.version, convert it to a version we
-   * are aware of. */
   uint16_t version = 0;
-  if (SSL_is_dtls(ssl)) {
-    if (client_hello->version <= DTLS1_2_VERSION) {
-      version = TLS1_2_VERSION;
-    } else if (client_hello->version <= DTLS1_VERSION) {
-      version = TLS1_1_VERSION;
+  /* Check supported_versions extension if it is present. */
+  CBS supported_versions;
+  if (ssl_early_callback_get_extension(client_hello, &supported_versions,
+                                       TLSEXT_TYPE_supported_versions)) {
+    CBS versions;
+    if (!CBS_get_u8_length_prefixed(&supported_versions, &versions) ||
+        CBS_len(&supported_versions) != 0 ||
+        CBS_len(&versions) == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      *out_alert = SSL_AD_DECODE_ERROR;
+      return 0;
+    }
+
+    int found_version = 0;
+    while (CBS_len(&versions) != 0) {
+      uint16_t ext_version;
+      if (!CBS_get_u16(&versions, &ext_version)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        *out_alert = SSL_AD_DECODE_ERROR;
+        return 0;
+      }
+      if (!ssl->method->version_from_wire(&ext_version, ext_version)) {
+        continue;
+      }
+      if (min_version <= ext_version &&
+          ext_version <= max_version) {
+        version = ext_version;
+        found_version = 1;
+        break;
+      }
+    }
+
+    if (!found_version) {
+      goto unsupported_protocol;
     }
   } else {
-    if (client_hello->version >= TLS1_3_VERSION) {
-      version = TLS1_3_VERSION;
-    } else if (client_hello->version >= TLS1_2_VERSION) {
-      version = TLS1_2_VERSION;
-    } else if (client_hello->version >= TLS1_1_VERSION) {
-      version = TLS1_1_VERSION;
-    } else if (client_hello->version >= TLS1_VERSION) {
-      version = TLS1_VERSION;
-    } else if (client_hello->version >= SSL3_VERSION) {
-      version = SSL3_VERSION;
+    /* Process ClientHello.version instead. Note that versions beyond (D)TLS 1.2
+     * do not use this mechanism. */
+    if (SSL_is_dtls(ssl)) {
+      if (client_hello->version <= DTLS1_2_VERSION) {
+        version = TLS1_2_VERSION;
+      } else if (client_hello->version <= DTLS1_VERSION) {
+        version = TLS1_1_VERSION;
+      } else {
+        goto unsupported_protocol;
+      }
+    } else {
+      if (client_hello->version >= TLS1_2_VERSION) {
+        version = TLS1_2_VERSION;
+      } else if (client_hello->version >= TLS1_1_VERSION) {
+        version = TLS1_1_VERSION;
+      } else if (client_hello->version >= TLS1_VERSION) {
+        version = TLS1_VERSION;
+      } else if (client_hello->version >= SSL3_VERSION) {
+        version = SSL3_VERSION;
+      } else {
+        goto unsupported_protocol;
+      }
     }
-  }
 
-  /* Apply our minimum and maximum version. */
-  if (version > max_version) {
-    version = max_version;
-  }
+    /* Apply our minimum and maximum version. */
+    if (version > max_version) {
+      version = max_version;
+    }
 
-  if (version < min_version) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
-    *out_alert = SSL_AD_PROTOCOL_VERSION;
-    return 0;
+    if (version < min_version) {
+      goto unsupported_protocol;
+    }
   }
 
   /* Handle FALLBACK_SCSV. */
@@ -617,6 +654,11 @@
   ssl->s3->have_version = 1;
 
   return 1;
+
+unsupported_protocol:
+  OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
+  *out_alert = SSL_AD_PROTOCOL_VERSION;
+  return 0;
 }
 
 static int ssl3_get_client_hello(SSL *ssl) {