Add a draft TLS 1.3 anti-downgrade signal.

TLS 1.3 includes a server-random-based anti-downgrade signal, as a
workaround for TLS 1.2's ServerKeyExchange signature failing to cover
the entire handshake. However, because TLS 1.3 draft versions are each
doomed to die, we cannot deploy it until the final RFC. (Suppose a
draft-TLS-1.3 client checked the signal and spoke to a final-TLS-1.3
server. The server would correctly negotiate TLS 1.2 and send the
signal. But the client would then break. An anologous situation exists
with reversed roles.)

However, it appears that Cisco devices have non-compliant TLS 1.2
implementations[1] and copy over another server's server-random when
acting as a TLS terminator (client and server back-to-back).

Hopefully they are the only ones doing this. Implement a
measurement-only version with a different value. This sentinel must not
be enforced, but it will tell us whether enforcing it will cause
problems.

[1] https://www.ietf.org/mail-archive/web/tls/current/msg25168.html

Bug: 226
Change-Id: I976880bdb2ef26f51592b2f6b3b97664342679c8
Reviewed-on: https://boringssl-review.googlesource.com/24284
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 14aab12..1a65e2b 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -3482,6 +3482,10 @@
 OPENSSL_EXPORT void SSL_CTX_set_false_start_allowed_without_alpn(SSL_CTX *ctx,
                                                                  int allowed);
 
+// SSL_is_draft_downgrade returns one if the TLS 1.3 anti-downgrade mechanism
+// would have aborted |ssl|'s handshake and zero otherwise.
+OPENSSL_EXPORT int SSL_is_draft_downgrade(const SSL *ssl);
+
 
 // Deprecated functions.
 
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index ce9d278..4c7012b 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -632,8 +632,20 @@
   OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
                  SSL3_RANDOM_SIZE);
 
-  // TODO(davidben): Implement the TLS 1.1 and 1.2 downgrade sentinels once TLS
-  // 1.3 is finalized and we are not implementing a draft version.
+  // Measure, but do not enforce, the TLS 1.3 anti-downgrade feature, with a
+  // different value.
+  //
+  // For draft TLS 1.3 versions, it is not safe to deploy this feature. However,
+  // some TLS terminators are non-compliant and copy the origin server's value,
+  // so we wish to measure eventual compatibility impact.
+  if (!ssl->s3->initial_handshake_complete &&
+      hs->max_version >= TLS1_3_VERSION &&
+      OPENSSL_memcmp(ssl->s3->server_random + SSL3_RANDOM_SIZE -
+                         sizeof(kDraftDowngradeRandom),
+                     kDraftDowngradeRandom,
+                     sizeof(kDraftDowngradeRandom)) == 0) {
+    ssl->s3->draft_downgrade = true;
+  }
 
   if (!ssl->s3->initial_handshake_complete && ssl->session != NULL &&
       ssl->session->session_id_length != 0 &&
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index bb565e9..bcbd7e2 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -707,8 +707,16 @@
     return ssl_hs_error;
   }
 
-  // TODO(davidben): Implement the TLS 1.1 and 1.2 downgrade sentinels once TLS
-  // 1.3 is finalized and we are not implementing a draft version.
+  // Implement the TLS 1.3 anti-downgrade feature, but with a different value.
+  //
+  // For draft TLS 1.3 versions, it is not safe to deploy this feature. However,
+  // some TLS terminators are non-compliant and copy the origin server's value,
+  // so we wish to measure eventual compatibility impact.
+  if (hs->max_version >= TLS1_3_VERSION) {
+    OPENSSL_memcpy(ssl->s3->server_random + SSL3_RANDOM_SIZE -
+                       sizeof(kDraftDowngradeRandom),
+                   kDraftDowngradeRandom, sizeof(kDraftDowngradeRandom));
+  }
 
   const SSL_SESSION *session = hs->new_session.get();
   if (ssl->session != NULL) {
diff --git a/ssl/internal.h b/ssl/internal.h
index ae0e593..78d7aa6 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1010,6 +1010,7 @@
 #define SSL_MAX_HANDSHAKE_FLIGHT 7
 
 extern const uint8_t kHelloRetryRequest[SSL3_RANDOM_SIZE];
+extern const uint8_t kDraftDowngradeRandom[8];
 
 // ssl_max_handshake_message_len returns the maximum number of bytes permitted
 // in a handshake message for |ssl|.
@@ -2300,6 +2301,10 @@
   // early_data_accepted is true if early data was accepted by the server.
   bool early_data_accepted:1;
 
+  // draft_downgrade is whether the TLS 1.3 anti-downgrade logic would have
+  // fired, were it not a draft.
+  bool draft_downgrade:1;
+
   uint8_t send_alert[2] = {0};
 
   // hs_buf is the buffer of handshake data to process.
diff --git a/ssl/s3_lib.cc b/ssl/s3_lib.cc
index 9a17573..a3fc8d7 100644
--- a/ssl/s3_lib.cc
+++ b/ssl/s3_lib.cc
@@ -176,7 +176,8 @@
       tlsext_channel_id_valid(false),
       key_update_pending(false),
       wpend_pending(false),
-      early_data_accepted(false) {}
+      early_data_accepted(false),
+      draft_downgrade(false) {}
 
 SSL3_STATE::~SSL3_STATE() {}
 
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index af4ffc4..5e420e8 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -2571,6 +2571,8 @@
   ctx->false_start_allowed_without_alpn = !!allowed;
 }
 
+int SSL_is_draft_downgrade(const SSL *ssl) { return ssl->s3->draft_downgrade; }
+
 int SSL_clear(SSL *ssl) {
   // In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
   // established session to be offered the next time around. wpa_supplicant
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 0b4c760..38e4077 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1808,6 +1808,11 @@
     return false;
   }
 
+  if (config->expect_draft_downgrade != !!SSL_is_draft_downgrade(ssl)) {
+    fprintf(stderr, "Got %sdraft downgrade signal, but wanted the opposite.\n",
+            SSL_is_draft_downgrade(ssl) ? "" : "no ");
+  }
+
   return true;
 }
 
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index de1e313..51effed 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -1513,6 +1513,14 @@
 	// PadClientHello, if non-zero, pads the ClientHello to a multiple of
 	// that many bytes.
 	PadClientHello int
+
+	// SendDraftTLS13DowngradeRandom, if true, causes the server to send the
+	// draft TLS 1.3 anti-downgrade signal.
+	SendDraftTLS13DowngradeRandom bool
+
+	// ExpectDraftTLS13DowngradeRandom, if true, causes the client to
+	// require the server send the draft TLS 1.3 anti-downgrade signal.
+	ExpectDraftTLS13DowngradeRandom bool
 }
 
 func (c *Config) serverInit() {
@@ -1939,6 +1947,8 @@
 	// See draft-ietf-tls-tls13-16, section 6.3.1.2.
 	downgradeTLS13 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01}
 	downgradeTLS12 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00}
+
+	downgradeTLS13Draft = []uint8{0x95, 0xb9, 0x9f, 0x87, 0x22, 0xfe, 0x9b, 0x64}
 )
 
 func containsGREASE(values []uint16) bool {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index cb24211..0f4a714 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -600,6 +600,9 @@
 			return errors.New("tls: downgrade from TLS 1.2 detected")
 		}
 	}
+	if c.config.Bugs.ExpectDraftTLS13DowngradeRandom && !bytes.Equal(serverHello.random[len(serverHello.random)-8:], downgradeTLS13Draft) {
+		return errors.New("tls: server did not send draft TLS 1.3 anti-downgrade signal")
+	}
 
 	suite := mutualCipherSuite(hello.cipherSuites, serverHello.cipherSuite)
 	if suite == nil {
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 0357889..6b7daee 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -1181,6 +1181,9 @@
 	if c.vers <= VersionTLS11 && config.maxVersion(c.isDTLS) == VersionTLS12 {
 		copy(hs.hello.random[len(hs.hello.random)-8:], downgradeTLS12)
 	}
+	if config.Bugs.SendDraftTLS13DowngradeRandom {
+		copy(hs.hello.random[len(hs.hello.random)-8:], downgradeTLS13Draft)
+	}
 
 	if len(hs.clientHello.sessionId) == 0 && c.config.Bugs.ExpectClientHelloSessionID {
 		return false, errors.New("tls: expected non-empty session ID from client")
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 545faf7..7506280 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -5647,6 +5647,27 @@
 		// TODO(davidben): This test should fail once TLS 1.3 is final
 		// and the fallback signal restored.
 	})
+
+	testCases = append(testCases, testCase{
+		name: "Draft-Downgrade-Client",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				SendDraftTLS13DowngradeRandom: true,
+			},
+		},
+		flags: []string{"-expect-draft-downgrade"},
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "Draft-Downgrade-Server",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				ExpectDraftTLS13DowngradeRandom: true,
+			},
+		},
+	})
 }
 
 func addMinimumVersionTests() {
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 579cf89..6744a00 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -130,6 +130,7 @@
   { "-use-custom-verify-callback", &TestConfig::use_custom_verify_callback },
   { "-allow-false-start-without-alpn",
     &TestConfig::allow_false_start_without_alpn },
+  { "-expect-draft-downgrade", &TestConfig::expect_draft_downgrade },
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 49b86ed..a459ae5 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -146,6 +146,7 @@
   bool use_custom_verify_callback = false;
   std::string expect_msg_callback;
   bool allow_false_start_without_alpn = false;
+  bool expect_draft_downgrade = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_initial,
diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc
index 57acbcb..2a5a935 100644
--- a/ssl/tls13_both.cc
+++ b/ssl/tls13_both.cc
@@ -43,6 +43,14 @@
     0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c,
 };
 
+// This value was selected by truncating the SHA-256 hash of "Draft TLS 1.3
+// Downgrade" to 8 bytes:
+//
+//   echo -n 'Draft TLS 1.3 Downgrade' | sha256sum | head -c 16
+const uint8_t kDraftDowngradeRandom[8] = {0x95, 0xb9, 0x9f, 0x87,
+                                          0x22, 0xfe, 0x9b, 0x64};
+
+
 bool tls13_get_cert_verify_signature_input(
     SSL_HANDSHAKE *hs, Array<uint8_t> *out,
     enum ssl_cert_verify_context_t cert_verify_context) {