Adding support for sending early data on the client.

BUG=76

Change-Id: If58a73da38e46549fd55f84a9104e2dfebfda43f
Reviewed-on: https://boringssl-review.googlesource.com/14164
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Steven Valdez <svaldez@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index 6de51e5..6b39371 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -33,6 +33,7 @@
   state_send_second_client_hello,
   state_process_server_hello,
   state_process_encrypted_extensions,
+  state_continue_second_server_flight,
   state_process_certificate_request,
   state_process_server_certificate,
   state_process_server_certificate_verify,
@@ -141,13 +142,15 @@
 
   hs->received_hello_retry_request = 1;
   hs->tls13_state = state_send_second_client_hello;
+  /* 0-RTT is rejected if we receive a HelloRetryRequest. */
+  if (hs->in_early_data) {
+    return ssl_hs_early_data_rejected;
+  }
   return ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_send_second_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  /* TODO(svaldez): Ensure that we set can_early_write to false since 0-RTT is
-   * rejected if we receive a HelloRetryRequest. */
   if (!ssl->method->set_write_state(ssl, NULL) ||
       !ssl_write_client_hello(hs)) {
     return ssl_hs_error;
@@ -259,6 +262,7 @@
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
+    ssl_set_session(ssl, NULL);
 
     /* Resumption incorporates fresh key material, so refresh the timeout. */
     ssl_session_renew_timeout(ssl, hs->new_session,
@@ -358,9 +362,9 @@
   }
 
   if (ssl->early_data_accepted) {
-    if (ssl->session->cipher != hs->new_session->cipher ||
-        ssl->session->early_alpn_len != ssl->s3->alpn_selected_len ||
-        OPENSSL_memcmp(ssl->session->early_alpn, ssl->s3->alpn_selected,
+    if (hs->early_session->cipher != hs->new_session->cipher ||
+        hs->early_session->early_alpn_len != ssl->s3->alpn_selected_len ||
+        OPENSSL_memcmp(hs->early_session->early_alpn, ssl->s3->alpn_selected,
                        ssl->s3->alpn_selected_len) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ALPN_MISMATCH_ON_EARLY_DATA);
       return ssl_hs_error;
@@ -371,15 +375,18 @@
     }
   }
 
-  /* Release offered session now that it is no longer needed. */
-  if (ssl->s3->session_reused) {
-    ssl_set_session(ssl, NULL);
-  }
-
   if (!ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
+  hs->tls13_state = state_continue_second_server_flight;
+  if (hs->in_early_data && !ssl->early_data_accepted) {
+    return ssl_hs_early_data_rejected;
+  }
+  return ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_continue_second_server_flight(SSL_HANDSHAKE *hs) {
   hs->tls13_state = state_process_certificate_request;
   return ssl_hs_read_message;
 }
@@ -485,11 +492,13 @@
 
 static enum ssl_hs_wait_t do_send_end_of_early_data(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  /* TODO(svaldez): Stop sending early data. */
-  if (ssl->early_data_accepted &&
-      !ssl->method->add_alert(ssl, SSL3_AL_WARNING,
-                              TLS1_AD_END_OF_EARLY_DATA)) {
-    return ssl_hs_error;
+
+  if (ssl->early_data_accepted) {
+    hs->can_early_write = 0;
+    if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING,
+                                TLS1_AD_END_OF_EARLY_DATA)) {
+      return ssl_hs_error;
+    }
   }
 
   if (hs->early_data_offered &&
@@ -618,6 +627,9 @@
       case state_process_encrypted_extensions:
         ret = do_process_encrypted_extensions(hs);
         break;
+      case state_continue_second_server_flight:
+        ret = do_continue_second_server_flight(hs);
+        break;
       case state_process_certificate_request:
         ret = do_process_certificate_request(hs);
         break;