ClientHello Padding for Fast Radio Opening in 3G.

The ClientHello record is padded to 1024 bytes when
fastradio_padding is enabled. As a result, the 3G cellular radio
is fast forwarded to DCH (high data rate) state. This mechanism
leads to a substantial redunction in terms of TLS handshake
latency, and benefits mobile apps that are running on top of TLS.

Change-Id: I3d55197b6d601761c94c0f22871774b5a3dad614
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 9d33c02..0658947 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1110,6 +1110,12 @@
 				void *arg);
 OPENSSL_EXPORT void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
 			    unsigned *len); 
+
+/* SSL_enable_fastradio_padding controls whether fastradio padding is enabled
+ * on |ssl|. If it is, ClientHello messages are padded to 1024 bytes. This
+ * causes 3G radios to switch to DCH mode (high data rate). */
+OPENSSL_EXPORT void SSL_enable_fastradio_padding(SSL *ssl, char on_off);
+
 /* the maximum length of the buffer given to callbacks containing the
  * resulting identity/psk */
 #define PSK_MAX_IDENTITY_LEN 128
@@ -1358,6 +1364,11 @@
 	/* fallback_scsv is non-zero iff we are sending the TLS_FALLBACK_SCSV
 	 * cipher suite value. Only applies to a client. */
 	char fallback_scsv;
+
+	/* fastradio_padding, if true, causes ClientHellos to be padded to 1024
+	 * bytes. This ensures that the cellular radio is fast forwarded to DCH
+	 * (high data rate) state in 3G networks. */
+	 char fastradio_padding;
 	};
 
 #endif
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 535baeb..219cbf5 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3139,3 +3139,8 @@
 	{
 	return s->server;
 	}
+
+void SSL_enable_fastradio_padding(SSL *s, char on_off)
+	{
+	s->fastradio_padding = on_off;
+	}
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index fa506bd..4f93bda 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1144,17 +1144,33 @@
 		}
 
 #ifdef TLSEXT_TYPE_padding
-	/* Add padding to workaround bugs in F5 terminators.
-	 * See https://tools.ietf.org/html/draft-agl-tls-padding-03
-	 *
-	 * NB: because this code works out the length of all existing
-	 * extensions it MUST always appear last. */
 	if (header_len > 0)
 		{
+		size_t clienthello_minsize = 0;
 		header_len += ret - orig;
 		if (header_len > 0xff && header_len < 0x200)
 			{
-			size_t padding_len = 0x200 - header_len;
+			/* Add padding to workaround bugs in F5 terminators.
+			 * See https://tools.ietf.org/html/draft-agl-tls-padding-03
+			 *
+			 * NB: because this code works out the length of all existing
+			 * extensions it MUST always appear last. */
+			clienthello_minsize = 0x200;
+			}
+		if (s->fastradio_padding)
+			{
+			/* Pad the ClientHello record to 1024 bytes to fast forward
+			* the radio into DCH (high data rate) state in 3G networks.
+			* Note that when fastradio_padding is enabled, even if the
+			* header_len is less than 255 bytes, the padding will be
+			* applied regardless. This is slightly different from the TLS
+			* padding extension suggested in
+			* https://tools.ietf.org/html/draft-agl-tls-padding-03 */
+			clienthello_minsize = 0x400;
+			}
+		if (header_len < clienthello_minsize)
+			{
+			size_t padding_len = clienthello_minsize - header_len;
 			/* Extensions take at least four bytes to encode. Always
 			 * include least one byte of data if including the
 			 * extension. WebSphere Application Server 7.0 is
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 30cdab7..e04e44b 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -429,6 +429,7 @@
     BIO_print_errors_fp(stdout);
     return 1;
   }
+  SSL_enable_fastradio_padding(ssl, config->fastradio_padding);
 
   BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
   if (bio == NULL) {
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 628c208..90c5294 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -582,6 +582,10 @@
 	// to require that all ClientHellos match in offered version
 	// across a renego.
 	RequireSameRenegoClientVersion bool
+
+	// RequireFastradioPadding, if true, requires that ClientHello messages
+	// be at least 1000 bytes long.
+	RequireFastradioPadding bool
 }
 
 func (c *Config) serverInit() {
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 4bdede1..284f314 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -120,6 +120,9 @@
 		c.sendAlert(alertUnexpectedMessage)
 		return false, unexpectedMessageError(hs.clientHello, msg)
 	}
+	if config.Bugs.RequireFastradioPadding && len(hs.clientHello.raw) < 1000 {
+		return false, errors.New("tls: ClientHello record size should be larger than 1000 bytes when padding enabled.")
+	}
 
 	if c.isDTLS && !config.Bugs.SkipHelloVerifyRequest {
 		// Per RFC 6347, the version field in HelloVerifyRequest SHOULD
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 421940d..ecf80db 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2156,6 +2156,29 @@
 	})
 }
 
+func addFastRadioPaddingTests() {
+	testCases = append(testCases, testCase {
+		protocol:	tls,
+		name:		"FastRadio-Padding",
+		config: Config{
+			Bugs: ProtocolBugs{
+				RequireFastradioPadding: true,
+			},
+		},
+		flags:		[]string{"-fastradio-padding"},
+	})
+	testCases = append(testCases, testCase {
+		protocol:	dtls,
+		name:		"FastRadio-Padding",
+		config: Config{
+			Bugs: ProtocolBugs{
+				RequireFastradioPadding: true,
+			},
+		},
+		flags:		[]string{"-fastradio-padding"},
+	})
+}
+
 var testHashes = []struct {
 	name string
 	id   uint8
@@ -2337,6 +2360,7 @@
 	addRenegotiationTests()
 	addDTLSReplayTests()
 	addSigningHashTests()
+	addFastRadioPaddingTests()
 	for _, async := range []bool{false, true} {
 		for _, splitHandshake := range []bool{false, true} {
 			for _, protocol := range []protocol{tls, dtls} {
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index e8034ce..59874ef 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -65,6 +65,7 @@
   { "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling },
   { "-enable-signed-cert-timestamps",
     &TestConfig::enable_signed_cert_timestamps },
+  { "-fastradio-padding", &TestConfig::fastradio_padding },
 };
 
 const size_t kNumBoolFlags = sizeof(kBoolFlags) / sizeof(kBoolFlags[0]);
@@ -124,7 +125,8 @@
       renegotiate(false),
       allow_unsafe_legacy_renegotiation(false),
       enable_ocsp_stapling(false),
-      enable_signed_cert_timestamps(false) {
+      enable_signed_cert_timestamps(false),
+      fastradio_padding(false) {
 }
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config) {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 5f050a8..f778c28 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -63,6 +63,7 @@
   std::string expected_ocsp_response;
   bool enable_signed_cert_timestamps;
   std::string expected_signed_cert_timestamps;
+  bool fastradio_padding;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);