Construct the ticket_early_data_info extension

Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2737)
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 70764b3..788417e 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -357,6 +357,8 @@
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_STOC_CRYPTOPRO_BUG),
      "tls_construct_stoc_cryptopro_bug"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_STOC_DONE), "TLS_CONSTRUCT_STOC_DONE"},
+    {ERR_FUNC(SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA_INFO),
+     "tls_construct_stoc_early_data_info"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_STOC_EC_PT_FORMATS),
      "tls_construct_stoc_ec_pt_formats"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_STOC_EMS), "tls_construct_stoc_ems"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 39254f1..d3dddaf 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -551,6 +551,7 @@
     s->mode = ctx->mode;
     s->max_cert_list = ctx->max_cert_list;
     s->references = 1;
+    s->max_early_data = ctx->max_early_data;
 
     /*
      * Earlier library versions used to copy the pointer to the CERT, not
@@ -4657,3 +4658,27 @@
     sk_SSL_CIPHER_free(scsvs);
     return 0;
 }
+
+int SSL_CTX_set_max_early_data(SSL_CTX *ctx, uint32_t max_early_data)
+{
+    ctx->max_early_data = max_early_data;
+
+    return 1;
+}
+
+uint32_t SSL_CTX_get_max_early_data(SSL_CTX *ctx)
+{
+    return ctx->max_early_data;
+}
+
+int SSL_set_max_early_data(SSL *s, uint32_t max_early_data)
+{
+    s->max_early_data = max_early_data;
+
+    return 1;
+}
+
+uint32_t SSL_get_max_early_data(SSL_CTX *s)
+{
+    return s->max_early_data;
+}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index c253c99..f8350af 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -922,6 +922,9 @@
      * Wireshark. The callback should log `line` followed by a newline.
      */
     SSL_CTX_keylog_cb_func keylog_callback;
+
+    /* The maximum number of bytes that can be sent as early data */
+    uint32_t max_early_data;
 };
 
 struct ssl_st {
@@ -1218,6 +1221,9 @@
     ASYNC_WAIT_CTX *waitctx;
     size_t asyncrw;
 
+    /* The maximum number of bytes that can be sent as early data */
+    uint32_t max_early_data;
+
     CRYPTO_RWLOCK *lock;
 };
 
@@ -1724,6 +1730,7 @@
     TLSEXT_IDX_renegotiate,
     TLSEXT_IDX_server_name,
     TLSEXT_IDX_srp,
+    TLSEXT_IDX_early_data_info,
     TLSEXT_IDX_ec_point_formats,
     TLSEXT_IDX_supported_groups,
     TLSEXT_IDX_session_ticket,
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index c011445..19795b1 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -129,6 +129,11 @@
 #else
     INVALID_EXTENSION,
 #endif
+    {
+        TLSEXT_TYPE_early_data_info,
+        EXT_TLS1_3_NEW_SESSION_TICKET,
+        NULL, NULL, NULL, tls_construct_stoc_early_data_info, NULL, NULL
+    },
 #ifndef OPENSSL_NO_EC
     {
         TLSEXT_TYPE_ec_point_formats,
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index ecfd00b..690cc30 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -801,6 +801,24 @@
     return 1;
 }
 
+int tls_construct_stoc_early_data_info(SSL *s, WPACKET *pkt,
+                                       unsigned int context, X509 *x,
+                                       size_t chainidx, int *al)
+{
+    if (s->max_early_data == 0)
+        return 1;
+
+    if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data_info)
+            || !WPACKET_start_sub_packet_u16(pkt)
+            || !WPACKET_put_bytes_u32(pkt, s->max_early_data)
+            || !WPACKET_close(pkt)) {
+        SSLerr(SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA_INFO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return 1;
+}
+
 #ifndef OPENSSL_NO_EC
 int tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, unsigned int context,
                                      X509 *x, size_t chainidx, int *al)
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 9230332..41ebbbc 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -230,6 +230,9 @@
                                    X509 *x, size_t chainidx, int *al);
 int tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, unsigned int context,
                                    X509 *x, size_t chainidx, int *al);
+int tls_construct_stoc_early_data_info(SSL *s, WPACKET *pkt,
+                                       unsigned int context, X509 *x,
+                                       size_t chainidx, int *al);
 #ifndef OPENSSL_NO_EC
 int tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, unsigned int context,
                                      X509 *x, size_t chainidx, int *al);