shim: extract a |DoSplitHandshake| helper function.

This makes the shim code read more naturally, in that the split-
handshake special case now lives in its own file.

This helps with creating a separate binary to perform split
handshakes.

Change-Id: I7970a8f368417791d18d4d44eeb379ef4b46c960
Reviewed-on: https://boringssl-review.googlesource.com/29347
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 5044915..161d64b 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -90,20 +90,6 @@
   return 1;
 }
 
-// MoveBIOs moves the |BIO|s of |src| to |dst|.  It is used for handoff.
-static 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);
-}
-
 template<typename T>
 struct Free {
   void operator()(T *buf) {
@@ -759,14 +745,6 @@
   return true;
 }
 
-static bool HandoffReady(SSL *ssl, int ret) {
-  return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDOFF;
-}
-
-static bool HandbackReady(SSL *ssl, int ret) {
-  return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK;
-}
-
 static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
                        bssl::UniquePtr<SSL> *ssl_uniqueptr,
                        const TestConfig *config, bool is_resume, bool is_retry,
@@ -777,57 +755,10 @@
 
   if (!config->implicit_handshake) {
     if (config->handoff) {
-      bssl::UniquePtr<SSL_CTX> ctx_handoff =
-          config->SetupCtx(SSL_get_SSL_CTX(ssl));
-      if (!ctx_handoff) {
+      if (!DoSplitHandshake(ssl_uniqueptr, writer, is_resume)) {
         return false;
       }
-      bssl::SSL_CTX_set_handoff_mode(ctx_handoff.get(), 1);
-
-      bssl::UniquePtr<SSL> ssl_handoff =
-          config->NewSSL(ctx_handoff.get(), nullptr, false, nullptr);
-      if (!ssl_handoff) {
-        return false;
-      }
-      SSL_set_accept_state(ssl_handoff.get());
-      if (!MoveTestState(ssl_handoff.get(), ssl)) {
-        return false;
-      }
-      MoveBIOs(ssl_handoff.get(), ssl);
-
-      do {
-        ret = CheckIdempotentError("SSL_do_handshake", ssl_handoff.get(),
-                                   [&]() -> int {
-          return SSL_do_handshake(ssl_handoff.get());
-        });
-      } while (!HandoffReady(ssl_handoff.get(), ret) &&
-               config->async &&
-               RetryAsync(ssl_handoff.get(), ret));
-
-      if (!HandoffReady(ssl_handoff.get(), ret)) {
-        fprintf(stderr, "Handshake failed while waiting for handoff.\n");
-        return false;
-      }
-
-      bssl::ScopedCBB cbb;
-      bssl::Array<uint8_t> handoff;
-      if (!CBB_init(cbb.get(), 512) ||
-          !bssl::SSL_serialize_handoff(ssl_handoff.get(), cbb.get()) ||
-          !CBBFinishArray(cbb.get(), &handoff) ||
-          !writer->WriteHandoff(handoff)) {
-        fprintf(stderr, "Handoff serialisation failed.\n");
-        return false;
-      }
-
-      MoveBIOs(ssl, ssl_handoff.get());
-      if (!MoveTestState(ssl, ssl_handoff.get())) {
-        return false;
-      }
-
-      if (!SSL_apply_handoff(ssl, handoff)) {
-        fprintf(stderr, "Handoff application failed.\n");
-        return false;
-      }
+      ssl = ssl_uniqueptr->get();
     }
 
     do {
@@ -836,52 +767,6 @@
       });
     } while (config->async && RetryAsync(ssl, ret));
 
-    if (config->handoff) {
-      if (!HandbackReady(ssl, ret)) {
-        fprintf(stderr, "Connection failed to handback.\n");
-        return false;
-      }
-
-      bssl::ScopedCBB cbb;
-      bssl::Array<uint8_t> handback;
-      if (!CBB_init(cbb.get(), 512) ||
-          !bssl::SSL_serialize_handback(ssl, cbb.get()) ||
-          !CBBFinishArray(cbb.get(), &handback) ||
-          !writer->WriteHandback(handback)) {
-        fprintf(stderr, "Handback serialisation failed.\n");
-        return false;
-      }
-
-      bssl::UniquePtr<SSL_CTX> ctx_handback =
-          config->SetupCtx(SSL_get_SSL_CTX(ssl));
-      if (!ctx_handback) {
-        return false;
-      }
-      bssl::UniquePtr<SSL> ssl_handback =
-          config->NewSSL(ctx_handback.get(), nullptr, false, nullptr);
-      if (!ssl_handback) {
-        return false;
-      }
-      MoveBIOs(ssl_handback.get(), ssl);
-      if (!MoveTestState(ssl_handback.get(), ssl)) {
-        return false;
-      }
-
-      if (!SSL_apply_handback(ssl_handback.get(), handback)) {
-        fprintf(stderr, "Applying handback failed.\n");
-        return false;
-      }
-
-      *ssl_uniqueptr = std::move(ssl_handback);
-      ssl = ssl_uniqueptr->get();
-
-      do {
-        ret = CheckIdempotentError("SSL_do_handshake", ssl, [&]() -> int {
-          return SSL_do_handshake(ssl);
-        });
-      } while (config->async && RetryAsync(ssl, ret));
-    }
-
     if (config->forbid_renegotiation_after_handshake) {
       SSL_set_renegotiate_mode(ssl, ssl_renegotiate_never);
     }
diff --git a/ssl/test/handshake_util.cc b/ssl/test/handshake_util.cc
index b3d39f4..b1440fc 100644
--- a/ssl/test/handshake_util.cc
+++ b/ssl/test/handshake_util.cc
@@ -25,6 +25,8 @@
 
 #include <openssl/ssl.h>
 
+using namespace bssl;
+
 bool RetryAsync(SSL *ssl, int ret) {
   // No error; don't retry.
   if (ret >= 0) {
@@ -59,7 +61,7 @@
       AsyncBioAllowWrite(test_state->async_bio, 1);
       return true;
     case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
-      bssl::UniquePtr<EVP_PKEY> pkey =
+      UniquePtr<EVP_PKEY> pkey =
           LoadPrivateKey(GetTestConfig(ssl)->send_channel_id);
       if (!pkey) {
         return false;
@@ -110,3 +112,100 @@
   }
   return ret;
 }
+
+// MoveBIOs moves the |BIO|s of |src| to |dst|.  It is used for handoff.
+static 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);
+}
+
+static bool HandoffReady(SSL *ssl, int ret) {
+  return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDOFF;
+}
+
+static bool HandbackReady(SSL *ssl, int ret) {
+  return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK;
+}
+
+bool DoSplitHandshake(UniquePtr<SSL> *ssl_uniqueptr, SettingsWriter *writer,
+                      bool is_resume) {
+  SSL *ssl = ssl_uniqueptr->get();
+  SSL_set_handoff_mode(ssl, 1);
+
+  const TestConfig *config = GetTestConfig(ssl);
+  int ret = -1;
+  do {
+    ret = CheckIdempotentError("SSL_do_handshake", ssl,
+                               [&]() -> int { return SSL_do_handshake(ssl); });
+  } while (!HandoffReady(ssl, ret) &&
+           config->async &&
+           RetryAsync(ssl, ret));
+
+  ScopedCBB cbb;
+  Array<uint8_t> handoff;
+  if (!HandoffReady(ssl, ret) ||
+      !CBB_init(cbb.get(), 512) ||
+      !SSL_serialize_handoff(ssl, cbb.get()) ||
+      !CBBFinishArray(cbb.get(), &handoff) ||
+      !writer->WriteHandoff(handoff)) {
+    fprintf(stderr, "Handoff failed.\n");
+    return false;
+  }
+
+  UniquePtr<SSL_CTX> ctx = config->SetupCtx(ssl->ctx.get());
+  if (!ctx) {
+    return false;
+  }
+  UniquePtr<SSL> ssl_handshaker =
+      config->NewSSL(ctx.get(), nullptr, false, nullptr);
+  if (!ssl_handshaker) {
+    return false;
+  }
+  MoveBIOs(ssl_handshaker.get(), ssl);
+
+  if (!MoveTestState(ssl_handshaker.get(), ssl) ||
+      !SSL_apply_handoff(ssl_handshaker.get(), handoff)) {
+    fprintf(stderr, "Handoff application failed.\n");
+    return false;
+  }
+
+  do {
+    ret = CheckIdempotentError(
+        "SSL_do_handshake", ssl_handshaker.get(),
+        [&]() -> int { return SSL_do_handshake(ssl_handshaker.get()); });
+  } while (config->async && RetryAsync(ssl_handshaker.get(), ret));
+
+  Array<uint8_t> handback;
+  if (!HandbackReady(ssl_handshaker.get(), ret) ||
+      !CBB_init(cbb.get(), 512) ||
+      !SSL_serialize_handback(ssl_handshaker.get(), cbb.get()) ||
+      !CBBFinishArray(cbb.get(), &handback) ||
+      !writer->WriteHandback(handback)) {
+    fprintf(stderr, "Handback failed.\n");
+    return false;
+  }
+
+  UniquePtr<SSL> ssl_handback =
+      config->NewSSL(ctx.get(), nullptr, false, nullptr);
+  if (!ssl_handback) {
+    return false;
+  }
+  MoveBIOs(ssl_handback.get(), ssl_handshaker.get());
+
+  if (!MoveTestState(ssl_handback.get(), ssl_handshaker.get()) ||
+      !SSL_apply_handback(ssl_handback.get(), handback)) {
+    fprintf(stderr, "Handback application failed.\n");
+    return false;
+  }
+
+  *ssl_uniqueptr = std::move(ssl_handback);
+  return true;
+}
diff --git a/ssl/test/handshake_util.h b/ssl/test/handshake_util.h
index 2798695..ec86b8e 100644
--- a/ssl/test/handshake_util.h
+++ b/ssl/test/handshake_util.h
@@ -19,6 +19,8 @@
 
 #include <openssl/base.h>
 
+#include "settings_writer.h"
+
 // RetryAsync is called after a failed operation on |ssl| with return code
 // |ret|. If the operation should be retried, it simulates one asynchronous
 // event and returns true. Otherwise it returns false.
@@ -28,4 +30,9 @@
 // errors are idempotent.
 int CheckIdempotentError(const char *name, SSL *ssl, std::function<int()> func);
 
+// DoSplitHandshake performs a handoff and handback of an in-progress handshake,
+// updating |ssl_uniqueptr| in place.
+bool DoSplitHandshake(bssl::UniquePtr<SSL> *ssl_uniqueptr,
+                      SettingsWriter *writer, bool is_resume);
+
 #endif  // HEADER_TEST_HANDSHAKE