Restrict when 0-RTT will be accepted in QUIC.

QUIC imposes additional restrictions on when 0-RTT data can be accepted.
With this change, a QUIC server configured to support 0-RTT will only
accept early data if the transport parameters and application protocol
specific context are a byte-for-byte match from the original connection
to the 0-RTT resumption attempt.

Bug: 295
Change-Id: Ie5d4688d1c9076b49f2131bb66b27c87e2ba041a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41145
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index 0d372cb..fa994e8 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -268,6 +268,11 @@
     if (!new_session->early_alpn.CopyFrom(session->early_alpn)) {
       return nullptr;
     }
+
+    if (!new_session->quic_early_data_hash.CopyFrom(
+            session->quic_early_data_hash)) {
+      return nullptr;
+    }
   }
 
   // Copy the ticket.
@@ -344,6 +349,25 @@
                                   session->cipher);
 }
 
+bool compute_quic_early_data_hash(const SSL_CONFIG *config,
+                                  uint8_t hash_out[SHA256_DIGEST_LENGTH]) {
+  ScopedEVP_MD_CTX hash_ctx;
+  uint32_t transport_param_len = config->quic_transport_params.size();
+  uint32_t context_len = config->quic_early_data_context.size();
+  if (!EVP_DigestInit(hash_ctx.get(), EVP_sha256()) ||
+      !EVP_DigestUpdate(hash_ctx.get(), &transport_param_len,
+                        sizeof(transport_param_len)) ||
+      !EVP_DigestUpdate(hash_ctx.get(), config->quic_transport_params.data(),
+                        config->quic_transport_params.size()) ||
+      !EVP_DigestUpdate(hash_ctx.get(), &context_len, sizeof(context_len)) ||
+      !EVP_DigestUpdate(hash_ctx.get(), config->quic_early_data_context.data(),
+                        config->quic_early_data_context.size()) ||
+      !EVP_DigestFinal(hash_ctx.get(), hash_out, nullptr)) {
+    return false;
+  }
+  return true;
+}
+
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
   SSL *const ssl = hs->ssl;
   if (ssl->mode & SSL_MODE_NO_SESSION_CREATION) {
@@ -359,6 +383,13 @@
   session->is_server = is_server;
   session->ssl_version = ssl->version;
   session->is_quic = ssl->quic_method != nullptr;
+  if (is_server && ssl->enable_early_data && session->is_quic) {
+    if (!session->quic_early_data_hash.Init(SHA256_DIGEST_LENGTH) ||
+        !compute_quic_early_data_hash(hs->config,
+                                      session->quic_early_data_hash.data())) {
+      return 0;
+    }
+  }
 
   // Fill in the time from the |SSL_CTX|'s clock.
   struct OPENSSL_timeval now;