Implement draft-davidben-tls-grease-01.

This GREASEs cipher suites, groups, and extensions. For now, we'll
always place them in a hard-coded position. We can experiment with more
interesting strategies later.

If we add new ciphers and curves, presumably we prefer them over current
ones, so place GREASE values at the front. This prevents implementations
from parsing only the first value and ignoring the rest.

Add two new extensions, one empty and one non-empty. Place the empty one
in front (IBM WebSphere can't handle trailing empty extensions) and the
non-empty one at the end.

Change-Id: If2e009936bc298cedf2a7a593ce7d5d5ddbb841a
Reviewed-on: https://boringssl-review.googlesource.com/11241
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 8454c30..3cf4e03 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -3101,6 +3101,10 @@
 OPENSSL_EXPORT void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx,
                                                                    int enable);
 
+/* SSL_CTX_set_grease_enabled configures whether client sockets on |ctx| should
+ * enable GREASE. See draft-davidben-tls-grease-01. */
+OPENSSL_EXPORT void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled);
+
 
 /* Deprecated functions. */
 
@@ -3992,11 +3996,15 @@
   /* If true, a client will request certificate timestamps. */
   unsigned signed_cert_timestamps_enabled:1;
 
-  /* tlsext_channel_id_enabled is copied from the |SSL_CTX|. For a server,
-   * means that we'll accept Channel IDs from clients. For a client, means that
-   * we'll advertise support. */
+  /* tlsext_channel_id_enabled is one if Channel ID is enabled and zero
+   * otherwise. For a server, means that we'll accept Channel IDs from clients.
+   * For a client, means that we'll advertise support. */
   unsigned tlsext_channel_id_enabled:1;
 
+  /* grease_enabled is one if draft-davidben-tls-grease-01 is enabled and zero
+   * otherwise. */
+  unsigned grease_enabled:1;
+
   /* extra_certs is a dummy value included for compatibility.
    * TODO(agl): remove once node.js no longer references this. */
   STACK_OF(X509)* extra_certs;
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index d78d0a4..584e9ea 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -579,6 +579,18 @@
   return ret;
 }
 
+uint16_t ssl_get_grease_value(const SSL *ssl, enum ssl_grease_index_t index) {
+  /* Use the client_random for entropy. This both avoids calling |RAND_bytes| on
+   * a single byte repeatedly and ensures the values are deterministic. This
+   * allows the same ClientHello be sent twice for a HelloRetryRequest or the
+   * same group be advertised in both supported_groups and key_shares. */
+  uint16_t ret = ssl->s3->client_random[index];
+  /* This generates a random value of the form 0xωaωa, for all 0 ≤ ω < 16. */
+  ret = (ret & 0xf0) | 0x0a;
+  ret |= ret << 8;
+  return ret;
+}
+
 static int ssl_write_client_cipher_list(SSL *ssl, CBB *out,
                                         uint16_t min_version,
                                         uint16_t max_version) {
@@ -590,6 +602,12 @@
     return 0;
   }
 
+  /* Add a fake cipher suite. See draft-davidben-tls-grease-01. */
+  if (ssl->ctx->grease_enabled &&
+      !CBB_add_u16(&child, ssl_get_grease_value(ssl, ssl_grease_cipher))) {
+    return 0;
+  }
+
   STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
 
   int any_enabled = 0;
diff --git a/ssl/internal.h b/ssl/internal.h
index 232364e..fca2dda 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1014,6 +1014,22 @@
     const struct ssl_early_callback_ctx *client_hello, uint16_t id);
 
 
+/* GREASE. */
+
+enum ssl_grease_index_t {
+  ssl_grease_cipher = 0,
+  ssl_grease_group,
+  ssl_grease_extension1,
+  ssl_grease_extension2,
+};
+
+/* ssl_get_grease_value returns a GREASE value for |ssl|. For a given
+ * connection, the values for each index will be deterministic. This allows the
+ * same ClientHello be sent twice for a HelloRetryRequest or the same group be
+ * advertised in both supported_groups and key_shares. */
+uint16_t ssl_get_grease_value(const SSL *ssl, enum ssl_grease_index_t index);
+
+
 /* Underdocumented functions.
  *
  * Functions below here haven't been touched up and may be underdocumented. */
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a51688d..51c16f0 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2884,6 +2884,10 @@
   ctx->retain_only_sha256_of_client_certs = !!enabled;
 }
 
+void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled) {
+  ctx->grease_enabled = !!enabled;
+}
+
 int SSL_clear(SSL *ssl) {
   if (ssl->method == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_METHOD_SPECIFIED);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 8db132f..baa2d45 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -2106,6 +2106,15 @@
 
     group_id = ssl->s3->hs->retry_group;
   } else {
+    /* Add a fake group. See draft-davidben-tls-grease-01. */
+    if (ssl->ctx->grease_enabled &&
+        (!CBB_add_u16(&kse_bytes,
+                      ssl_get_grease_value(ssl, ssl_grease_group)) ||
+         !CBB_add_u16(&kse_bytes, 1 /* length */) ||
+         !CBB_add_u8(&kse_bytes, 0 /* one byte key share */))) {
+      return 0;
+    }
+
     /* Predict the most preferred group. */
     const uint16_t *groups;
     size_t groups_len;
@@ -2293,6 +2302,13 @@
     return 0;
   }
 
+  /* Add a fake group. See draft-davidben-tls-grease-01. */
+  if (ssl->ctx->grease_enabled &&
+      !CBB_add_u16(&groups_bytes,
+                   ssl_get_grease_value(ssl, ssl_grease_group))) {
+    return 0;
+  }
+
   const uint16_t *groups;
   size_t groups_len;
   tls1_get_grouplist(ssl, 0, &groups, &groups_len);
@@ -2546,6 +2562,16 @@
     }
   }
 
+  uint16_t grease_ext1 = 0;
+  if (ssl->ctx->grease_enabled) {
+    /* Add a fake empty extension. See draft-davidben-tls-grease-01. */
+    grease_ext1 = ssl_get_grease_value(ssl, ssl_grease_extension1);
+    if (!CBB_add_u16(&extensions, grease_ext1) ||
+        !CBB_add_u16(&extensions, 0 /* zero length */)) {
+      goto err;
+    }
+  }
+
   for (size_t i = 0; i < kNumExtensions; i++) {
     const size_t len_before = CBB_len(&extensions);
     if (!kExtensions[i].add_clienthello(ssl, &extensions)) {
@@ -2563,6 +2589,24 @@
     goto err;
   }
 
+  if (ssl->ctx->grease_enabled) {
+    /* Add a fake non-empty extension. See draft-davidben-tls-grease-01. */
+    uint16_t grease_ext2 = ssl_get_grease_value(ssl, ssl_grease_extension2);
+
+    /* The two fake extensions must not have the same value. GREASE values are
+     * of the form 0x1a1a, 0x2a2a, 0x3a3a, etc., so XOR to generate a different
+     * one. */
+    if (grease_ext1 == grease_ext2) {
+      grease_ext2 ^= 0x1010;
+    }
+
+    if (!CBB_add_u16(&extensions, grease_ext2) ||
+        !CBB_add_u16(&extensions, 1 /* one byte length */) ||
+        !CBB_add_u8(&extensions, 0 /* single zero byte as contents */)) {
+      goto err;
+    }
+  }
+
   if (!SSL_is_dtls(ssl)) {
     header_len += 2 + CBB_len(&extensions);
     if (header_len > 0xff && header_len < 0x200) {
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 533e7b2..0064e40 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -942,6 +942,10 @@
     SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr);
   }
 
+  if (config->enable_grease) {
+    SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
+  }
+
   return ssl_ctx;
 }
 
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 3300d33..02ab23b 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -1084,6 +1084,10 @@
 	// InvalidChannelIDSignature, if true, causes the client to generate an
 	// invalid Channel ID signature.
 	InvalidChannelIDSignature bool
+
+	// ExpectGREASE, if true, causes the server to reject a ClientHello
+	// unless it contains GREASE values. See draft-davidben-tls-grease-01.
+	ExpectGREASE bool
 }
 
 func (c *Config) serverInit() {
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 63290fb..8cec98b 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -159,6 +159,7 @@
 	srtpMasterKeyIdentifier string
 	sctListSupported        bool
 	customExtension         string
+	hasGREASEExtension      bool
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -199,7 +200,8 @@
 		eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) &&
 		m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier &&
 		m.sctListSupported == m1.sctListSupported &&
-		m.customExtension == m1.customExtension
+		m.customExtension == m1.customExtension &&
+		m.hasGREASEExtension == m1.hasGREASEExtension
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -705,6 +707,10 @@
 			m.customExtension = string(data[:length])
 		}
 		data = data[length:]
+
+		if isGREASEValue(extension) {
+			m.hasGREASEExtension = true
+		}
 	}
 
 	return true
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index e04075c..604fa14 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -246,11 +246,13 @@
 	}
 	c.haveVers = true
 
-	var scsvFound bool
+	var scsvFound, greaseFound bool
 	for _, cipherSuite := range hs.clientHello.cipherSuites {
 		if cipherSuite == fallbackSCSV {
 			scsvFound = true
-			break
+		}
+		if isGREASEValue(cipherSuite) {
+			greaseFound = true
 		}
 	}
 
@@ -260,6 +262,36 @@
 		return errors.New("tls: fallback SCSV found when not expected")
 	}
 
+	if !greaseFound && config.Bugs.ExpectGREASE {
+		return errors.New("tls: no GREASE cipher suite value found")
+	}
+
+	greaseFound = false
+	for _, curve := range hs.clientHello.supportedCurves {
+		if isGREASEValue(uint16(curve)) {
+			greaseFound = true
+			break
+		}
+	}
+
+	if !greaseFound && config.Bugs.ExpectGREASE {
+		return errors.New("tls: no GREASE curve value found")
+	}
+
+	if len(hs.clientHello.keyShares) > 0 {
+		greaseFound = false
+		for _, keyShare := range hs.clientHello.keyShares {
+			if isGREASEValue(uint16(keyShare.group)) {
+				greaseFound = true
+				break
+			}
+		}
+
+		if !greaseFound && config.Bugs.ExpectGREASE {
+			return errors.New("tls: no GREASE curve value found")
+		}
+	}
+
 	if config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
 		hs.clientHello.signatureAlgorithms = config.signSignatureAlgorithms()
 	}
@@ -1002,6 +1034,10 @@
 		serverExtensions.ticketSupported = true
 	}
 
+	if !hs.clientHello.hasGREASEExtension && config.Bugs.ExpectGREASE {
+		return errors.New("tls: no GREASE extension found")
+	}
+
 	return nil
 }
 
@@ -1673,3 +1709,7 @@
 	// Unknown cipher.
 	return false
 }
+
+func isGREASEValue(val uint16) bool {
+	return val&0x0f0f == 0x0a0a && val&0xff == val >> 8
+}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 0d2e6ef..ea660ab 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2309,6 +2309,26 @@
 			expectedError:      ":INVALID_COMPRESSION_LIST:",
 			expectedLocalError: "remote error: illegal parameter",
 		},
+		{
+			name: "GREASE-TLS12",
+			config: Config{
+				MaxVersion: VersionTLS12,
+				Bugs: ProtocolBugs{
+					ExpectGREASE: true,
+				},
+			},
+			flags: []string{"-enable-grease"},
+		},
+		{
+			name: "GREASE-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					ExpectGREASE: true,
+				},
+			},
+			flags: []string{"-enable-grease"},
+		},
 	}
 	testCases = append(testCases, basicTests...)
 }
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index b7dc511..bca194e 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -105,6 +105,7 @@
   { "-use-null-client-ca-list", &TestConfig::use_null_client_ca_list },
   { "-send-alert", &TestConfig::send_alert },
   { "-peek-then-read", &TestConfig::peek_then_read },
+  { "-enable-grease", &TestConfig::enable_grease },
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index e0029a5..d20d1c6 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -113,6 +113,7 @@
   bool use_null_client_ca_list = false;
   bool send_alert = false;
   bool peek_then_read = false;
+  bool enable_grease = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/tool/client.cc b/tool/client.cc
index 04a217a..b9f1c13 100644
--- a/tool/client.cc
+++ b/tool/client.cc
@@ -89,6 +89,10 @@
       " values: 'smtp'",
     },
     {
+     "-grease", kBooleanArgument,
+     "Enable GREASE",
+    },
+    {
      "", kOptionalArgument, "",
     },
 };
@@ -269,6 +273,10 @@
     SSL_CTX_sess_set_new_cb(ctx.get(), NewSessionCallback);
   }
 
+  if (args_map.count("-grease") != 0) {
+    SSL_CTX_set_grease_enabled(ctx.get(), 1);
+  }
+
   int sock = -1;
   if (!Connect(&sock, args_map["-connect"])) {
     return false;