Move ssl->version to ssl->s3->version

The version is not user configuration but a property of the connection
that we learn partway during the handshake. That state needs to be
reset on SSL_clear, so it's cleaner to implement this by just making it
naturally reset as part of getting a new ssl->s3. (That really should
just be called SSLConnection or something and be a common interface
between TLS and DTLS, but we'll get to that one later.)

Change-Id: I6eaa4ab1df5cbc6569732ed2aa01d84900410080
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/71530
Reviewed-by: Nick Harper <nharper@chromium.org>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/d1_lib.cc b/ssl/d1_lib.cc
index 49aa50a..4caadad 100644
--- a/ssl/d1_lib.cc
+++ b/ssl/d1_lib.cc
@@ -108,13 +108,6 @@
   }
 
   ssl->d1 = d1.release();
-
-  // Set the version to the highest supported version.
-  //
-  // TODO(davidben): Move this field into |s3|, have it store the normalized
-  // protocol version, and implement this pre-negotiation quirk in |SSL_version|
-  // at the API boundary rather than in internal state.
-  ssl->version = DTLS1_2_VERSION;
   return true;
 }
 
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index 9417eb9..2032042 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -329,7 +329,7 @@
   const uint8_t *write_iv = nullptr;
   if ((type == handback_after_session_resumption ||
        type == handback_after_handshake) &&
-      ssl->version == TLS1_VERSION &&
+      ssl->s3->version == TLS1_VERSION &&
       SSL_CIPHER_is_block_cipher(s3->aead_write_ctx->cipher()) &&
       !s3->aead_write_ctx->GetIV(&write_iv, &write_iv_len)) {
     return false;
@@ -337,7 +337,7 @@
   size_t read_iv_len = 0;
   const uint8_t *read_iv = nullptr;
   if (type == handback_after_handshake &&
-      ssl->version == TLS1_VERSION &&
+      ssl->s3->version == TLS1_VERSION &&
       SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) &&
       !s3->aead_read_ctx->GetIV(&read_iv, &read_iv_len)) {
       return false;
@@ -637,9 +637,9 @@
     s3->early_data_reason = ssl_early_data_protocol_version;
   }
 
-  ssl->version = session->ssl_version;
+  ssl->s3->version = session->ssl_version;
   s3->have_version = true;
-  if (!ssl_method_supports_version(ssl->method, ssl->version) ||
+  if (!ssl_method_supports_version(ssl->method, ssl->s3->version) ||
       session->cipher != hs->new_cipher ||
       ssl_protocol_version(ssl) < SSL_CIPHER_get_min_version(session->cipher) ||
       SSL_CIPHER_get_max_version(session->cipher) < ssl_protocol_version(ssl)) {
@@ -690,7 +690,7 @@
   hs->wait = ssl_hs_flush;
   hs->extended_master_secret = extended_master_secret;
   hs->ticket_expected = ticket_expected;
-  s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
+  s3->aead_write_ctx->SetVersionIfNullCipher(ssl->s3->version);
   hs->cert_request = cert_request;
 
   if (type != handback_after_handshake &&
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 976e969..be24ea0 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -725,12 +725,12 @@
 
   assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete);
   if (!ssl->s3->have_version) {
-    ssl->version = server_version;
-    // At this point, the connection's version is known and ssl->version is
+    ssl->s3->version = server_version;
+    // At this point, the connection's version is known and ssl->s3->version is
     // fixed. Begin enforcing the record-layer version.
     ssl->s3->have_version = true;
-    ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
-  } else if (server_version != ssl->version) {
+    ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->s3->version);
+  } else if (server_version != ssl->s3->version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return ssl_hs_error;
@@ -829,7 +829,7 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
-    if (ssl->session->ssl_version != ssl->version) {
+    if (ssl->session->ssl_version != ssl->s3->version) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 148a29c..1c433ce 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -241,14 +241,14 @@
     }
   }
 
-  if (!ssl_negotiate_version(hs, out_alert, &ssl->version, &versions)) {
+  if (!ssl_negotiate_version(hs, out_alert, &ssl->s3->version, &versions)) {
     return false;
   }
 
-  // At this point, the connection's version is known and |ssl->version| is
+  // At this point, the connection's version is known and |ssl->s3->version| is
   // fixed. Begin enforcing the record-layer version.
   ssl->s3->have_version = true;
-  ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
+  ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->s3->version);
 
   // Handle FALLBACK_SCSV.
   if (ssl_client_cipher_list_contains_cipher(client_hello,
@@ -1086,7 +1086,7 @@
   ScopedCBB cbb;
   CBB body, session_id_bytes;
   if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_SERVER_HELLO) ||
-      !CBB_add_u16(&body, ssl->version) ||
+      !CBB_add_u16(&body, ssl->s3->version) ||
       !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
       !CBB_add_u8_length_prefixed(&body, &session_id_bytes) ||
       !CBB_add_bytes(&session_id_bytes, session_id.data(), session_id.size()) ||
diff --git a/ssl/internal.h b/ssl/internal.h
index 8038e51..3d6b419 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -2854,6 +2854,11 @@
   enum ssl_encryption_level_t read_level = ssl_encryption_initial;
   enum ssl_encryption_level_t write_level = ssl_encryption_initial;
 
+  // version is the protocol version, or zero if the version has not yet been
+  // set.
+  // TODO(davidben): This is redundant with |have_version|.
+  uint16_t version = 0;
+
   // early_data_skipped is the amount of early data that has been skipped by the
   // record layer.
   uint16_t early_data_skipped = 0;
@@ -3950,9 +3955,6 @@
   // that instead, and skip the null check.)
   bssl::UniquePtr<bssl::SSL_CONFIG> config;
 
-  // version is the protocol version.
-  uint16_t version = 0;
-
   uint16_t max_send_fragment = 0;
 
   // There are 2 BIO's even though they are normally both the same. This is so
diff --git a/ssl/s3_lib.cc b/ssl/s3_lib.cc
index b8b37b4..8b822e2 100644
--- a/ssl/s3_lib.cc
+++ b/ssl/s3_lib.cc
@@ -196,13 +196,6 @@
   }
 
   ssl->s3 = s3.release();
-
-  // Set the version to the highest supported version.
-  //
-  // TODO(davidben): Move this field into |s3|, have it store the normalized
-  // protocol version, and implement this pre-negotiation quirk in |SSL_version|
-  // at the API boundary rather than in internal state.
-  ssl->version = TLS1_2_VERSION;
   return true;
 }
 
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index e64243e..92bd822 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -362,7 +362,7 @@
   }
 
   session->is_server = ssl->server;
-  session->ssl_version = ssl->version;
+  session->ssl_version = ssl->s3->version;
   session->is_quic = ssl->quic_method != nullptr;
 
   // Fill in the time from the |SSL_CTX|'s clock.
@@ -616,7 +616,7 @@
          ssl_session_is_time_valid(ssl, session) &&
          // Only resume if the session's version matches the negotiated
          // version.
-         ssl->version == session->ssl_version &&
+         ssl->s3->version == session->ssl_version &&
          // Only resume if the session's cipher matches the negotiated one. This
          // is stricter than necessary for TLS 1.3, which allows cross-cipher
          // resumption if the PRF hashes match. We require an exact match for
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc
index c521a61..13ad4d1 100644
--- a/ssl/ssl_versions.cc
+++ b/ssl/ssl_versions.cc
@@ -254,14 +254,19 @@
   if (SSL_in_early_data(ssl) && !ssl->server) {
     return ssl->s3->hs->early_session->ssl_version;
   }
-  return ssl->version;
+  if (ssl->s3->have_version) {
+    return ssl->s3->version;
+  }
+  // The TLS versions has not yet been negotiated. Historically, we would return
+  // (D)TLS 1.2, so preserve that behavior.
+  return SSL_is_dtls(ssl) ? DTLS1_2_VERSION : TLS1_2_VERSION;
 }
 
 uint16_t ssl_protocol_version(const SSL *ssl) {
   assert(ssl->s3->have_version);
   uint16_t version;
-  if (!ssl_protocol_version_from_wire(&version, ssl->version)) {
-    // |ssl->version| will always be set to a valid version.
+  if (!ssl_protocol_version_from_wire(&version, ssl->s3->version)) {
+    // |ssl->s3->version| will always be set to a valid version.
     assert(0);
     return 0;
   }
diff --git a/ssl/t1_enc.cc b/ssl/t1_enc.cc
index a985d38..82d1600 100644
--- a/ssl/t1_enc.cc
+++ b/ssl/t1_enc.cc
@@ -244,7 +244,7 @@
   }
 
   UniquePtr<SSLAEADContext> aead_ctx =
-      SSLAEADContext::Create(direction, ssl->version, SSL_is_dtls(ssl),
+      SSLAEADContext::Create(direction, ssl->s3->version, SSL_is_dtls(ssl),
                              session->cipher, key, mac_secret, iv);
   if (!aead_ctx) {
     return false;
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index 78aa246..644be6a 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -86,7 +86,7 @@
                                         /*secret_for_quic=*/{})) {
         return false;
       }
-      ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
+      ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->s3->version);
     } else {
       assert(level == ssl_encryption_handshake);
       if (!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_seal,
@@ -437,7 +437,7 @@
   if (!supported_versions.present ||
       !CBS_get_u16(&supported_versions.data, &version) ||
       CBS_len(&supported_versions.data) != 0 ||
-      version != ssl->version) {
+      version != ssl->s3->version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SECOND_SERVERHELLO_VERSION_MISMATCH);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
@@ -451,7 +451,7 @@
       return ssl_hs_error;
     }
 
-    if (ssl->session->ssl_version != ssl->version) {
+    if (ssl->session->ssl_version != ssl->s3->version) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
diff --git a/ssl/tls13_enc.cc b/ssl/tls13_enc.cc
index 7c193a3..d4193fd 100644
--- a/ssl/tls13_enc.cc
+++ b/ssl/tls13_enc.cc
@@ -404,7 +404,7 @@
   size_t context_hash_len;
   if (!hs->transcript.GetHash(context_hash, &context_hash_len) ||
       !tls13_verify_data(out, out_len, hs->transcript.Digest(),
-                         hs->ssl->version, traffic_secret,
+                         hs->ssl->s3->version, traffic_secret,
                          MakeConstSpan(context_hash, context_hash_len),
                          SSL_is_dtls(hs->ssl))) {
     return false;
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index c82fdea..e163d70 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -101,7 +101,7 @@
   CBB contents;
   if (!CBB_add_u16(out, TLSEXT_TYPE_supported_versions) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
-      !CBB_add_u16(&contents, hs->ssl->version) ||
+      !CBB_add_u16(&contents, hs->ssl->s3->version) ||
       !CBB_flush(out)) {
     return 0;
   }
@@ -615,7 +615,7 @@
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
       !CBB_add_u16(&extensions, TLSEXT_TYPE_supported_versions) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
-      !CBB_add_u16(&extensions, ssl->version) ||
+      !CBB_add_u16(&extensions, ssl->s3->version) ||
       !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
       !CBB_add_u16(&extensions, hs->new_session->group_id)) {