Add tests for split handshakes.

This change adds a couple of focused tests to ssl_test.cc, but also
programmically duplicates many runner tests in a split-handshake mode.

Change-Id: I9dafc8a394581e5daf1318722e1015de82117fd9
Reviewed-on: https://boringssl-review.googlesource.com/25388
Commit-Queue: Adam Langley <agl@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/ssl/ssl_test.cc b/ssl/ssl_test.cc
index b2042ea..0f2a33c 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1537,7 +1537,8 @@
 static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client,
                                    bssl::UniquePtr<SSL> *out_server,
                                    SSL_CTX *client_ctx, SSL_CTX *server_ctx,
-                                   const ClientConfig &config = ClientConfig()) {
+                                   const ClientConfig &config = ClientConfig(),
+                                   bool do_handshake = true) {
   bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
   if (!client || !server) {
     return false;
@@ -1561,7 +1562,7 @@
   SSL_set_bio(client.get(), bio1, bio1);
   SSL_set_bio(server.get(), bio2, bio2);
 
-  if (!CompleteHandshakes(client.get(), server.get())) {
+  if (do_handshake && !CompleteHandshakes(client.get(), server.get())) {
     return false;
   }
 
@@ -3923,6 +3924,139 @@
   EXPECT_TRUE(SSL_is_signature_algorithm_rsa_pss(SSL_SIGN_RSA_PSS_SHA384));
 }
 
+void MoveBIOs(SSL *dest, SSL *src) {
+  BIO *rbio = SSL_get_rbio(src);
+  BIO_up_ref(rbio);
+  SSL_set0_rbio(dest, rbio);
+
+  BIO *wbio = SSL_get_wbio(src);
+  BIO_up_ref(wbio);
+  SSL_set0_wbio(dest, wbio);
+
+  SSL_set0_rbio(src, nullptr);
+  SSL_set0_wbio(src, nullptr);
+}
+
+TEST(SSLTest, Handoff) {
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> handshaker_ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+  ASSERT_TRUE(server_ctx);
+  ASSERT_TRUE(handshaker_ctx);
+
+  SSL_CTX_set_handoff_mode(server_ctx.get(), 1);
+  ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_2_VERSION));
+  ASSERT_TRUE(
+      SSL_CTX_set_max_proto_version(handshaker_ctx.get(), TLS1_2_VERSION));
+
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(SSL_CTX_use_certificate(handshaker_ctx.get(), cert.get()));
+  ASSERT_TRUE(SSL_CTX_use_PrivateKey(handshaker_ctx.get(), key.get()));
+
+  bssl::UniquePtr<SSL> client, server;
+  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+                                     server_ctx.get(), ClientConfig(),
+                                     false /* don't handshake */));
+
+  int client_ret = SSL_do_handshake(client.get());
+  int client_err = SSL_get_error(client.get(), client_ret);
+  ASSERT_EQ(client_err, SSL_ERROR_WANT_READ);
+
+  int server_ret = SSL_do_handshake(server.get());
+  int server_err = SSL_get_error(server.get(), server_ret);
+  ASSERT_EQ(server_err, SSL_ERROR_HANDOFF);
+
+  ScopedCBB cbb;
+  Array<uint8_t> handoff;
+  ASSERT_TRUE(CBB_init(cbb.get(), 256));
+  ASSERT_TRUE(SSL_serialize_handoff(server.get(), cbb.get()));
+  ASSERT_TRUE(CBBFinishArray(cbb.get(), &handoff));
+
+  bssl::UniquePtr<SSL> handshaker(SSL_new(handshaker_ctx.get()));
+  ASSERT_TRUE(SSL_apply_handoff(handshaker.get(), handoff));
+
+  MoveBIOs(handshaker.get(), server.get());
+
+  int handshake_ret = SSL_do_handshake(handshaker.get());
+  int handshake_err = SSL_get_error(handshaker.get(), handshake_ret);
+  ASSERT_EQ(handshake_err, SSL_ERROR_WANT_READ);
+
+  ASSERT_TRUE(CompleteHandshakes(client.get(), handshaker.get()));
+
+  ScopedCBB cbb_handback;
+  Array<uint8_t> handback;
+  ASSERT_TRUE(CBB_init(cbb_handback.get(), 1024));
+  ASSERT_TRUE(SSL_serialize_handback(handshaker.get(), cbb_handback.get()));
+  ASSERT_TRUE(CBBFinishArray(cbb_handback.get(), &handback));
+
+  bssl::UniquePtr<SSL> server2(SSL_new(server_ctx.get()));
+  ASSERT_TRUE(SSL_apply_handback(server2.get(), handback));
+
+  MoveBIOs(server2.get(), handshaker.get());
+
+  uint8_t byte = 42;
+  EXPECT_EQ(SSL_write(client.get(), &byte, 1), 1);
+  EXPECT_EQ(SSL_read(server2.get(), &byte, 1), 1);
+  EXPECT_EQ(42, byte);
+
+  byte = 43;
+  EXPECT_EQ(SSL_write(server2.get(), &byte, 1), 1);
+  EXPECT_EQ(SSL_read(client.get(), &byte, 1), 1);
+  EXPECT_EQ(43, byte);
+}
+
+TEST(SSLTest, HandoffDeclined) {
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+  ASSERT_TRUE(server_ctx);
+
+  SSL_CTX_set_handoff_mode(server_ctx.get(), 1);
+  ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_2_VERSION));
+
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
+  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
+
+  bssl::UniquePtr<SSL> client, server;
+  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+                                     server_ctx.get(), ClientConfig(),
+                                     false /* don't handshake */));
+
+  int client_ret = SSL_do_handshake(client.get());
+  int client_err = SSL_get_error(client.get(), client_ret);
+  ASSERT_EQ(client_err, SSL_ERROR_WANT_READ);
+
+  int server_ret = SSL_do_handshake(server.get());
+  int server_err = SSL_get_error(server.get(), server_ret);
+  ASSERT_EQ(server_err, SSL_ERROR_HANDOFF);
+
+  ScopedCBB cbb;
+  ASSERT_TRUE(CBB_init(cbb.get(), 256));
+  ASSERT_TRUE(SSL_serialize_handoff(server.get(), cbb.get()));
+
+  ASSERT_TRUE(SSL_decline_handoff(server.get()));
+
+  ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));
+
+  uint8_t byte = 42;
+  EXPECT_EQ(SSL_write(client.get(), &byte, 1), 1);
+  EXPECT_EQ(SSL_read(server.get(), &byte, 1), 1);
+  EXPECT_EQ(42, byte);
+
+  byte = 43;
+  EXPECT_EQ(SSL_write(server.get(), &byte, 1), 1);
+  EXPECT_EQ(SSL_read(client.get(), &byte, 1), 1);
+  EXPECT_EQ(43, byte);
+}
+
 // TODO(davidben): Convert this file to GTest properly.
 TEST(SSLTest, AllTests) {
   if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||