Enforce the ticket_age parameter for 0-RTT.

For now just hard-code a tolerance of 1 minute.
SSL_get_early_data_reason and SSL_get_ticket_age_skew will allow us to
tune this.

Bug: 113
Change-Id: I85a530494d5405a3e11198d49bfa9cfd355f4f35
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/35886
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index 82c9689..8f31704 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -53,6 +53,12 @@
 
 static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
 
+// Allow a minute of ticket age skew in either direction. This covers
+// transmission delays in ClientHello and NewSessionTicket, as well as
+// drift between client and server clock rate since the ticket was issued.
+// See RFC 8446, section 8.3.
+static const int32_t kMaxTicketAgeSkewSeconds = 60;
+
 static int resolve_ecdhe_secret(SSL_HANDSHAKE *hs, bool *out_need_retry,
                                 SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
@@ -433,6 +439,10 @@
       // a fresh session.
       hs->new_session =
           SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
+      if (hs->new_session == nullptr) {
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        return ssl_hs_error;
+      }
 
       if (!ssl->enable_early_data) {
         ssl->s3->early_data_reason = ssl_early_data_disabled;
@@ -449,16 +459,14 @@
       } else if (MakeConstSpan(ssl->s3->alpn_selected) != session->early_alpn) {
         // The negotiated ALPN must match the one in the ticket.
         ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
+      } else if (ssl->s3->ticket_age_skew < -kMaxTicketAgeSkewSeconds ||
+                 kMaxTicketAgeSkewSeconds < ssl->s3->ticket_age_skew) {
+        ssl->s3->early_data_reason = ssl_early_data_ticket_age_skew;
       } else {
         ssl->s3->early_data_reason = ssl_early_data_accepted;
         ssl->s3->early_data_accepted = true;
       }
 
-      if (hs->new_session == NULL) {
-        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-        return ssl_hs_error;
-      }
-
       ssl->s3->session_reused = true;
 
       // Resumption incorporates fresh key material, so refresh the timeout.