[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Mon Mar 12 10:32:03 UTC 2018


The branch master has been updated
       via  df0fed9aab239e2e9a269d06637a6442051dee3b (commit)
      from  f1c00b93e2138e5a45e8b500dec6bb3b2e035771 (commit)


- Log -----------------------------------------------------------------
commit df0fed9aab239e2e9a269d06637a6442051dee3b
Author: Todd Short <tshort at akamai.com>
Date:   Wed Mar 15 13:25:55 2017 -0400

    Session Ticket app data
    
    Adds application data into the encrypted session ticket
    
    Reviewed-by: Paul Dale <paul.dale at oracle.com>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3802)

-----------------------------------------------------------------------

Summary of changes:
 doc/man3/SSL_CTX_set_session_ticket_cb.pod | 149 +++++++++++++++++++++++++++++
 include/openssl/ssl.h                      |  32 +++++++
 ssl/ssl_asn1.c                             |  20 +++-
 ssl/ssl_lib.c                              |  11 +++
 ssl/ssl_locl.h                             |  37 +++----
 ssl/ssl_sess.c                             |  48 ++++++++--
 ssl/statem/extensions_srvr.c               |   6 +-
 ssl/statem/statem_srvr.c                   |   4 +
 ssl/t1_lib.c                               |  84 ++++++++++------
 test/handshake_helper.c                    |  44 +++++++++
 test/handshake_helper.h                    |   2 +
 test/recipes/80-test_ssl_new.t             |   2 +-
 test/ssl-tests/27-ticket-appdata.conf      | 146 ++++++++++++++++++++++++++++
 test/ssl-tests/27-ticket-appdata.conf.in   |  99 +++++++++++++++++++
 test/ssl_test.c                            |  22 +++++
 test/ssl_test_ctx.c                        |  10 ++
 test/ssl_test_ctx.h                        |   3 +
 test/ssl_test_ctx_test.c                   |   4 +
 util/libssl.num                            |   3 +
 util/private.num                           |   2 +
 20 files changed, 661 insertions(+), 67 deletions(-)
 create mode 100644 doc/man3/SSL_CTX_set_session_ticket_cb.pod
 create mode 100644 test/ssl-tests/27-ticket-appdata.conf
 create mode 100644 test/ssl-tests/27-ticket-appdata.conf.in

diff --git a/doc/man3/SSL_CTX_set_session_ticket_cb.pod b/doc/man3/SSL_CTX_set_session_ticket_cb.pod
new file mode 100644
index 0000000..e9aeb90
--- /dev/null
+++ b/doc/man3/SSL_CTX_set_session_ticket_cb.pod
@@ -0,0 +1,149 @@
+=pod
+
+=head1 NAME
+
+SSL_CTX_set_session_ticket_cb,
+SSL_SESSION_get0_ticket_appdata,
+SSL_SESSION_set1_ticket_appdata,
+SSL_CTX_generate_session_ticket_fn,
+SSL_CTX_decrypt_session_ticket_fn - manage session ticket application data
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ typedef int (*SSL_CTX_generate_session_ticket_fn)(SSL *s, void *arg);
+ typedef SSL_TICKET_RETURN (*SSL_CTX_decrypt_session_ticket_fn)(SSL *s, SSL_SESSION *ss,
+                                                                const unsigned char *keyname,
+                                                                size_t keyname_len,
+                                                                SSL_TICKET_RETURN retv,
+                                                                void *arg);
+ int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
+                                   SSL_CTX_generate_session_ticket_fn gen_cb,
+                                   SSL_CTX_decrypt_session_ticket_fn dec_cb,
+                                   void *arg);
+ int SSL_SESSION_set1_ticket_appdata(SSL_SESSION *ss, const void *data, size_t len);
+ int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len);
+
+=head1 DESCRIPTION
+
+SSL_CTX_set_set_session_ticket_cb() sets the application callbacks B<gen_cb>
+and B<dec_cb> that are used by a server to set and get application data stored
+with a session, and placed into a session ticket. Either callback function may
+be set to NULL. The value of B<arg> is passed to the callbacks.
+
+B<gen_cb> is the application defined callback invoked when a session ticket is
+about to be created. The application can call SSL_SESSION_set1_ticket_appdata()
+at this time to add application data to the session ticket. The value of B<arg>
+is the same as that given to SSL_CTX_set_session_ticket_cb(). The B<gen_cb>
+callback is defined as type B<SSL_CTX_generate_session_ticket_fn>.
+
+B<dec_cb> is the application defined callback invoked after session ticket
+decryption has been attempted and any session ticket application data is available.
+The application can call SSL_SESSION_get_ticket_appdata() at this time to retrieve
+the application data. The value of B<arg> is the same as that given to
+SSL_CTX_set_session_ticket_cb(). The B<retv> arguement is the result of the ticket
+decryption. The B<keyname> and B<keyname_len> identify the key used to decrypt the
+session ticket. The B<dec_cb> callback is defined as type
+B<SSL_CTX_decrypt_session_ticket_fn>.
+
+SSL_SESSION_set1_ticket_appdata() sets the application data specified by
+B<data> and B<len> into B<ss> which is then placed into any generated session
+tickets. It can be called at any time before a session ticket is created to
+update the data placed into the session ticket. However, given that sessions
+and tickets are created by the handshake, the B<gen_cb> is provided to notify
+the application that a session ticket is about to be generated.
+
+SSL_SESSION_get0_ticket_appdata() assigns B<data> to the session ticket
+application data and assigns B<len> to the length of the session ticket
+application data from B<ss>. The application data can be set via
+SSL_SESSION_set1_ticket_appdata() or by a session ticket. NULL will be assigned
+to B<data> and 0 will be assigned to B<len> if there is no session ticket
+application data. SSL_SESSION_get0_ticket_appdata() can be called any time
+after a session has been created. The B<dec_cb> is provided to notify the
+application that a session ticket has just been decrypted.
+
+=head1 NOTES
+
+When the B<dec_cb> callback is invoked, the SSL_SESSION B<ss> has not yet been
+assigned to the SSL B<s>. The B<retv> indicates the result of the ticket
+decryption which can be modified by the callback before being returned. The
+callback must check the B<retv> value before performing any action, as it's
+called even if ticket decryption fails.
+
+The B<keyname> and B<keyname_len> arguments to B<dec_cb> may be used to identify
+the key that was used to encrypt the session ticket.
+
+When the B<gen_cb> callback is invoked, the SSL_get_session() function can be
+used to retrieve the SSL_SESSION for SSL_SESSION_set1_ticket_appdata().
+
+=head1 RETURN VALUES
+
+The SSL_CTX_set_session_ticket_cb(), SSL_SESSION_set1_ticket_appdata() and
+SSL_SESSION_get0_ticket_appdata() functions return 1 on success and 0 on
+failure.
+
+The B<gen_cb> callback must return 1 to continue the connection. A return of 0
+will terminate the connection with an INTERNAL_ERROR alert.
+
+The B<dec_cb> callback must return one of the following B<SSL_TICKET_RETURN>
+values. Under normal circumstances the B<retv> value is returned unmodified,
+but the callback can change the behavior of the post-ticket decryption code
+by returning something different. The B<dec_cb> callback must check the B<retv>
+value before performing any action.
+
+ typedef int SSL_TICKET_RETURN;
+
+=over 4
+
+=item SSL_TICKET_FATAL_ERR_MALLOC
+
+Fatal error, malloc failure.
+
+=item SSL_TICKET_FATAL_ERR_OTHER
+
+Fatal error, either from parsing or decrypting the ticket.
+
+=item SSL_TICKET_NONE
+
+No ticket present.
+
+=item SSL_TICKET_EMPTY
+
+Empty ticket present.
+
+=item SSL_TICKET_NO_DECRYPT
+
+The ticket couldn't be decrypted.
+
+=item SSL_TICKET_SUCCESS
+
+A ticket was successfully decrypted, any session ticket application data should
+be available.
+
+=item TICKET_SUCCESS_RENEW
+
+Same as B<TICKET_SUCCESS>, but the ticket needs to be renewed.
+
+=back
+
+=head1 SEE ALSO
+
+L<ssl(7)>,
+L<SSL_get_session(3)>
+
+=head1 HISTORY
+
+SSL_CTX_set_session_ticket_cb(), SSSL_SESSION_set1_ticket_appdata() and
+SSL_SESSION_get_ticket_appdata() were added to OpenSSL 1.1.1.
+
+=head1 COPYRIGHT
+
+Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the OpenSSL license (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 3561dee..a47975d 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -2294,6 +2294,38 @@ __owur const struct openssl_ssl_test_functions *SSL_test_functions(void);
 __owur int SSL_free_buffers(SSL *ssl);
 __owur int SSL_alloc_buffers(SSL *ssl);
 
+/* Return codes for tls_get_ticket_from_client() and tls_decrypt_ticket() */
+typedef int SSL_TICKET_RETURN;
+
+/* Support for ticket appdata */
+/* fatal error, malloc failure */
+# define SSL_TICKET_FATAL_ERR_MALLOC 0
+/* fatal error, either from parsing or decrypting the ticket */
+# define SSL_TICKET_FATAL_ERR_OTHER  1
+/* No ticket present */
+# define SSL_TICKET_NONE             2
+/* Empty ticket present */
+# define SSL_TICKET_EMPTY            3
+/* the ticket couldn't be decrypted */
+# define SSL_TICKET_NO_DECRYPT       4
+/* a ticket was successfully decrypted */
+# define SSL_TICKET_SUCCESS          5
+/* same as above but the ticket needs to be renewed */
+# define SSL_TICKET_SUCCESS_RENEW    6
+
+typedef int (*SSL_CTX_generate_session_ticket_fn)(SSL *s, void *arg);
+typedef SSL_TICKET_RETURN (*SSL_CTX_decrypt_session_ticket_fn)(SSL *s, SSL_SESSION *ss,
+                                                               const unsigned char *keyname,
+                                                               size_t keyname_length,
+                                                               SSL_TICKET_RETURN retv,
+                                                               void *arg);
+int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
+                                  SSL_CTX_generate_session_ticket_fn gen_cb,
+                                  SSL_CTX_decrypt_session_ticket_fn dec_cb,
+                                  void *arg);
+int SSL_SESSION_set1_ticket_appdata(SSL_SESSION *ss, const void *data, size_t len);
+int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len);
+
 extern const char SSL_version_str[];
 
 
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 9327b33..7d39ba1 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -43,6 +43,7 @@ typedef struct {
     ASN1_OCTET_STRING *alpn_selected;
     ASN1_OCTET_STRING *tick_nonce;
     uint32_t tlsext_max_fragment_len_mode;
+    ASN1_OCTET_STRING *ticket_appdata;
 } SSL_SESSION_ASN1;
 
 ASN1_SEQUENCE(SSL_SESSION_ASN1) = {
@@ -73,7 +74,8 @@ ASN1_SEQUENCE(SSL_SESSION_ASN1) = {
     ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_early_data, ZUINT32, 15),
     ASN1_EXP_OPT(SSL_SESSION_ASN1, alpn_selected, ASN1_OCTET_STRING, 16),
     ASN1_EXP_OPT(SSL_SESSION_ASN1, tick_nonce, ASN1_OCTET_STRING, 17),
-    ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_max_fragment_len_mode, ZUINT32, 18)
+    ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_max_fragment_len_mode, ZUINT32, 18),
+    ASN1_EXP_OPT(SSL_SESSION_ASN1, ticket_appdata, ASN1_OCTET_STRING, 19)
 } static_ASN1_SEQUENCE_END(SSL_SESSION_ASN1)
 
 IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(SSL_SESSION_ASN1)
@@ -123,6 +125,7 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
 #endif
     ASN1_OCTET_STRING alpn_selected;
     ASN1_OCTET_STRING tick_nonce;
+    ASN1_OCTET_STRING ticket_appdata;
 
     long l;
 
@@ -200,6 +203,12 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
 
     as.tlsext_max_fragment_len_mode = in->ext.max_fragment_len_mode;
 
+    if (in->ticket_appdata == NULL)
+        as.ticket_appdata = NULL;
+    else
+        ssl_session_oinit(&as.ticket_appdata, &ticket_appdata,
+                          in->ticket_appdata, in->ticket_appdata_len);
+
     return i2d_SSL_SESSION_ASN1(&as, pp);
 
 }
@@ -376,6 +385,15 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp,
 
     ret->ext.max_fragment_len_mode = as->tlsext_max_fragment_len_mode;
 
+    if (as->ticket_appdata != NULL) {
+        ret->ticket_appdata = as->ticket_appdata->data;
+        ret->ticket_appdata_len = as->ticket_appdata->length;
+        as->ticket_appdata->data = NULL;
+    } else {
+        ret->ticket_appdata = NULL;
+        ret->ticket_appdata_len = 0;
+    }
+
     M_ASN1_free_of(as, SSL_SESSION_ASN1);
 
     if ((a != NULL) && (*a == NULL))
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index f5219c2..0814fb3 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -5409,3 +5409,14 @@ int SSL_verify_client_post_handshake(SSL *ssl)
     ossl_statem_set_in_init(ssl, 1);
     return 1;
 }
+
+int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
+                                  SSL_CTX_generate_session_ticket_fn gen_cb,
+                                  SSL_CTX_decrypt_session_ticket_fn dec_cb,
+                                  void *arg)
+{
+    ctx->generate_ticket_cb = gen_cb;
+    ctx->decrypt_ticket_cb = dec_cb;
+    ctx->ticket_cb_data = arg;
+    return 1;
+}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index f179efa..9eb5834 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -591,6 +591,8 @@ struct ssl_session_st {
 # ifndef OPENSSL_NO_SRP
     char *srp_username;
 # endif
+    unsigned char *ticket_appdata;
+    size_t ticket_appdata_len;
     uint32_t flags;
     CRYPTO_RWLOCK *lock;
 };
@@ -1025,6 +1027,11 @@ struct ssl_ctx_st {
     size_t (*record_padding_cb)(SSL *s, int type, size_t len, void *arg);
     void *record_padding_arg;
     size_t block_padding;
+
+    /* Session ticket appdata */
+    SSL_CTX_generate_session_ticket_fn generate_ticket_cb;
+    SSL_CTX_decrypt_session_ticket_fn decrypt_ticket_cb;
+    void *ticket_cb_data;
 };
 
 struct ssl_st {
@@ -2446,30 +2453,12 @@ void tls1_get_supported_groups(SSL *s, const uint16_t **pgroups,
 
 __owur int tls1_set_server_sigalgs(SSL *s);
 
-/* Return codes for tls_get_ticket_from_client() and tls_decrypt_ticket() */
-typedef enum ticket_en {
-    /* fatal error, malloc failure */
-    TICKET_FATAL_ERR_MALLOC,
-    /* fatal error, either from parsing or decrypting the ticket */
-    TICKET_FATAL_ERR_OTHER,
-    /* No ticket present */
-    TICKET_NONE,
-    /* Empty ticket present */
-    TICKET_EMPTY,
-    /* the ticket couldn't be decrypted */
-    TICKET_NO_DECRYPT,
-    /* a ticket was successfully decrypted */
-    TICKET_SUCCESS,
-    /* same as above but the ticket needs to be renewed */
-    TICKET_SUCCESS_RENEW
-} TICKET_RETURN;
-
-__owur TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
-                                                SSL_SESSION **ret);
-__owur TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
-                                        size_t eticklen,
-                                        const unsigned char *sess_id,
-                                        size_t sesslen, SSL_SESSION **psess);
+__owur SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
+                                                    SSL_SESSION **ret);
+__owur SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
+                                            size_t eticklen,
+                                            const unsigned char *sess_id,
+                                            size_t sesslen, SSL_SESSION **psess);
 
 __owur int tls_use_ticket(SSL *s);
 
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 5d2e171..f78c9cd 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -134,6 +134,7 @@ SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
     dest->peer_chain = NULL;
     dest->peer = NULL;
     dest->ext.tick_nonce = NULL;
+    dest->ticket_appdata = NULL;
     memset(&dest->ex_data, 0, sizeof(dest->ex_data));
 
     /* We deliberately don't copy the prev and next pointers */
@@ -244,6 +245,13 @@ SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
     }
 #endif
 
+    if (src->ticket_appdata != NULL) {
+        dest->ticket_appdata =
+            OPENSSL_memdup(src->ticket_appdata, src->ticket_appdata_len);
+        if (dest->ticket_appdata == NULL)
+            goto err;
+    }
+
     return dest;
  err:
     SSLerr(SSL_F_SSL_SESSION_DUP, ERR_R_MALLOC_FAILURE);
@@ -471,7 +479,7 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
     SSL_SESSION *ret = NULL;
     int fatal = 0, discard;
     int try_session_cache = 0;
-    TICKET_RETURN r;
+    SSL_TICKET_RETURN r;
 
     if (SSL_IS_TLS13(s)) {
         if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes,
@@ -486,20 +494,20 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
         /* sets s->ext.ticket_expected */
         r = tls_get_ticket_from_client(s, hello, &ret);
         switch (r) {
-        case TICKET_FATAL_ERR_MALLOC:
-        case TICKET_FATAL_ERR_OTHER:
+        case SSL_TICKET_FATAL_ERR_MALLOC:
+        case SSL_TICKET_FATAL_ERR_OTHER:
             fatal = 1;
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL_GET_PREV_SESSION,
                      ERR_R_INTERNAL_ERROR);
             goto err;
-        case TICKET_NONE:
-        case TICKET_EMPTY:
+        case SSL_TICKET_NONE:
+        case SSL_TICKET_EMPTY:
             if (hello->session_id_len > 0)
                 try_session_cache = 1;
             break;
-        case TICKET_NO_DECRYPT:
-        case TICKET_SUCCESS:
-        case TICKET_SUCCESS_RENEW:
+        case SSL_TICKET_NO_DECRYPT:
+        case SSL_TICKET_SUCCESS:
+        case SSL_TICKET_SUCCESS_RENEW:
             break;
         }
     }
@@ -806,6 +814,7 @@ void SSL_SESSION_free(SSL_SESSION *ss)
 #endif
     OPENSSL_free(ss->ext.alpn_selected);
     OPENSSL_free(ss->ext.tick_nonce);
+    OPENSSL_free(ss->ticket_appdata);
     CRYPTO_THREAD_lock_free(ss->lock);
     OPENSSL_clear_free(ss, sizeof(*ss));
 }
@@ -1269,4 +1278,27 @@ void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx,
     ctx->app_verify_cookie_cb = cb;
 }
 
+int SSL_SESSION_set1_ticket_appdata(SSL_SESSION *ss, const void *data, size_t len)
+{
+    OPENSSL_free(ss->ticket_appdata);
+    ss->ticket_appdata_len = 0;
+    if (data == NULL || len == 0) {
+        ss->ticket_appdata = NULL;
+        return 1;
+    }
+    ss->ticket_appdata = OPENSSL_memdup(data, len);
+    if (ss->ticket_appdata != NULL) {
+        ss->ticket_appdata_len = len;
+        return 1;
+    }
+    return 0;
+}
+
+int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len)
+{
+    *data = ss->ticket_appdata;
+    *len = ss->ticket_appdata_len;
+    return 1;
+}
+
 IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION)
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index b9692f4..74acdb2 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1124,13 +1124,13 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                                          PACKET_remaining(&identity), NULL, 0,
                                          &sess);
 
-            if (ret == TICKET_FATAL_ERR_MALLOC
-                    || ret == TICKET_FATAL_ERR_OTHER) {
+            if (ret == SSL_TICKET_FATAL_ERR_MALLOC
+                    || ret == SSL_TICKET_FATAL_ERR_OTHER) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR,
                          SSL_F_TLS_PARSE_CTOS_PSK, ERR_R_INTERNAL_ERROR);
                 return 0;
             }
-            if (ret == TICKET_NO_DECRYPT)
+            if (ret == SSL_TICKET_NO_DECRYPT)
                 continue;
 
             ticket_age = (uint32_t)ticket_agel;
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 8092684..041089c 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -3728,6 +3728,10 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
         s->session->ext.max_early_data = s->max_early_data;
     }
 
+    if (tctx->generate_ticket_cb != NULL &&
+        tctx->generate_ticket_cb(s, tctx->ticket_cb_data) == 0)
+        goto err;
+
     /* get session encoding length */
     slen_full = i2d_SSL_SESSION(s->session, NULL);
     /*
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 8b0d9aa..596fdd4 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1205,8 +1205,8 @@ int tls1_set_server_sigalgs(SSL *s)
  *   s->ctx->ext.ticket_key_cb asked to renew the client's ticket.
  *   Otherwise, s->ext.ticket_expected is set to 0.
  */
-TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
-                                         SSL_SESSION **ret)
+SSL_TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
+                                             SSL_SESSION **ret)
 {
     int retv;
     size_t size;
@@ -1221,11 +1221,11 @@ TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
      * resumption.
      */
     if (s->version <= SSL3_VERSION || !tls_use_ticket(s))
-        return TICKET_NONE;
+        return SSL_TICKET_NONE;
 
     ticketext = &hello->pre_proc_exts[TLSEXT_IDX_session_ticket];
     if (!ticketext->present)
-        return TICKET_NONE;
+        return SSL_TICKET_NONE;
 
     size = PACKET_remaining(&ticketext->data);
     if (size == 0) {
@@ -1234,7 +1234,7 @@ TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
          * one.
          */
         s->ext.ticket_expected = 1;
-        return TICKET_EMPTY;
+        return SSL_TICKET_EMPTY;
     }
     if (s->ext.session_secret_cb) {
         /*
@@ -1243,25 +1243,49 @@ TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
          * abbreviated handshake based on external mechanism to
          * calculate the master secret later.
          */
-        return TICKET_NO_DECRYPT;
+        return SSL_TICKET_NO_DECRYPT;
     }
 
     retv = tls_decrypt_ticket(s, PACKET_data(&ticketext->data), size,
                               hello->session_id, hello->session_id_len, ret);
+
+    /*
+     * If set, the decrypt_ticket_cb() is always called regardless of the
+     * return from tls_decrypt_ticket(). The callback is responsible for
+     * checking |retv| before it performs any action
+     */
+    if (s->session_ctx->decrypt_ticket_cb != NULL) {
+        size_t keyname_len = size;
+
+        if (keyname_len > TLSEXT_KEYNAME_LENGTH)
+            keyname_len = TLSEXT_KEYNAME_LENGTH;
+        retv = s->session_ctx->decrypt_ticket_cb(s, *ret,
+                                                 PACKET_data(&ticketext->data),
+                                                 keyname_len,
+                                                 retv, s->session_ctx->ticket_cb_data);
+    }
+
     switch (retv) {
-    case TICKET_NO_DECRYPT:
+    case SSL_TICKET_NO_DECRYPT:
         s->ext.ticket_expected = 1;
-        return TICKET_NO_DECRYPT;
+        return SSL_TICKET_NO_DECRYPT;
+
+    case SSL_TICKET_SUCCESS:
+        return SSL_TICKET_SUCCESS;
 
-    case TICKET_SUCCESS:
-        return TICKET_SUCCESS;
+    case SSL_TICKET_SUCCESS_RENEW:
+        s->ext.ticket_expected = 1;
+        return SSL_TICKET_SUCCESS;
 
-    case TICKET_SUCCESS_RENEW:
+    case SSL_TICKET_EMPTY:
         s->ext.ticket_expected = 1;
-        return TICKET_SUCCESS;
+        return SSL_TICKET_EMPTY;
+
+    case SSL_TICKET_NONE:
+        return SSL_TICKET_NONE;
 
     default:
-        return TICKET_FATAL_ERR_OTHER;
+        return SSL_TICKET_FATAL_ERR_OTHER;
     }
 }
 
@@ -1275,15 +1299,15 @@ TICKET_RETURN tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
  *   psess: (output) on return, if a ticket was decrypted, then this is set to
  *       point to the resulting session.
  */
-TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
-                                 size_t eticklen, const unsigned char *sess_id,
-                                 size_t sesslen, SSL_SESSION **psess)
+SSL_TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
+                                     size_t eticklen, const unsigned char *sess_id,
+                                     size_t sesslen, SSL_SESSION **psess)
 {
     SSL_SESSION *sess;
     unsigned char *sdec;
     const unsigned char *p;
     int slen, renew_ticket = 0, declen;
-    TICKET_RETURN ret = TICKET_FATAL_ERR_OTHER;
+    SSL_TICKET_RETURN ret = SSL_TICKET_FATAL_ERR_OTHER;
     size_t mlen;
     unsigned char tick_hmac[EVP_MAX_MD_SIZE];
     HMAC_CTX *hctx = NULL;
@@ -1292,17 +1316,17 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
 
     /* Need at least keyname + iv */
     if (eticklen < TLSEXT_KEYNAME_LENGTH + EVP_MAX_IV_LENGTH) {
-        ret = TICKET_NO_DECRYPT;
+        ret = SSL_TICKET_NO_DECRYPT;
         goto err;
     }
 
     /* Initialize session ticket encryption and HMAC contexts */
     hctx = HMAC_CTX_new();
     if (hctx == NULL)
-        return TICKET_FATAL_ERR_MALLOC;
+        return SSL_TICKET_FATAL_ERR_MALLOC;
     ctx = EVP_CIPHER_CTX_new();
     if (ctx == NULL) {
-        ret = TICKET_FATAL_ERR_MALLOC;
+        ret = SSL_TICKET_FATAL_ERR_MALLOC;
         goto err;
     }
     if (tctx->ext.ticket_key_cb) {
@@ -1313,7 +1337,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
         if (rv < 0)
             goto err;
         if (rv == 0) {
-            ret = TICKET_NO_DECRYPT;
+            ret = SSL_TICKET_NO_DECRYPT;
             goto err;
         }
         if (rv == 2)
@@ -1322,7 +1346,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
         /* Check key name matches */
         if (memcmp(etick, tctx->ext.tick_key_name,
                    TLSEXT_KEYNAME_LENGTH) != 0) {
-            ret = TICKET_NO_DECRYPT;
+            ret = SSL_TICKET_NO_DECRYPT;
             goto err;
         }
         if (HMAC_Init_ex(hctx, tctx->ext.tick_hmac_key,
@@ -1345,7 +1369,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
     /* Sanity check ticket length: must exceed keyname + IV + HMAC */
     if (eticklen <=
         TLSEXT_KEYNAME_LENGTH + EVP_CIPHER_CTX_iv_length(ctx) + mlen) {
-        ret = TICKET_NO_DECRYPT;
+        ret = SSL_TICKET_NO_DECRYPT;
         goto err;
     }
     eticklen -= mlen;
@@ -1357,7 +1381,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
     HMAC_CTX_free(hctx);
     if (CRYPTO_memcmp(tick_hmac, etick + eticklen, mlen)) {
         EVP_CIPHER_CTX_free(ctx);
-        return TICKET_NO_DECRYPT;
+        return SSL_TICKET_NO_DECRYPT;
     }
     /* Attempt to decrypt session data */
     /* Move p after IV to start of encrypted ticket, update length */
@@ -1368,12 +1392,12 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
                                           (int)eticklen) <= 0) {
         EVP_CIPHER_CTX_free(ctx);
         OPENSSL_free(sdec);
-        return TICKET_FATAL_ERR_OTHER;
+        return SSL_TICKET_FATAL_ERR_OTHER;
     }
     if (EVP_DecryptFinal(ctx, sdec + slen, &declen) <= 0) {
         EVP_CIPHER_CTX_free(ctx);
         OPENSSL_free(sdec);
-        return TICKET_NO_DECRYPT;
+        return SSL_TICKET_NO_DECRYPT;
     }
     slen += declen;
     EVP_CIPHER_CTX_free(ctx);
@@ -1387,7 +1411,7 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
         /* Some additional consistency checks */
         if (slen != 0 || sess->session_id_length != 0) {
             SSL_SESSION_free(sess);
-            return TICKET_NO_DECRYPT;
+            return SSL_TICKET_NO_DECRYPT;
         }
         /*
          * The session ID, if non-empty, is used by some clients to detect
@@ -1400,15 +1424,15 @@ TICKET_RETURN tls_decrypt_ticket(SSL *s, const unsigned char *etick,
         sess->session_id_length = sesslen;
         *psess = sess;
         if (renew_ticket)
-            return TICKET_SUCCESS_RENEW;
+            return SSL_TICKET_SUCCESS_RENEW;
         else
-            return TICKET_SUCCESS;
+            return SSL_TICKET_SUCCESS;
     }
     ERR_clear_error();
     /*
      * For session parse failure, indicate that we need to send a new ticket.
      */
-    return TICKET_NO_DECRYPT;
+    return SSL_TICKET_NO_DECRYPT;
  err:
     EVP_CIPHER_CTX_free(ctx);
     HMAC_CTX_free(hctx);
diff --git a/test/handshake_helper.c b/test/handshake_helper.c
index c0265ab..9b781b0 100644
--- a/test/handshake_helper.c
+++ b/test/handshake_helper.c
@@ -38,6 +38,7 @@ void HANDSHAKE_RESULT_free(HANDSHAKE_RESULT *result)
     OPENSSL_free(result->server_npn_negotiated);
     OPENSSL_free(result->client_alpn_negotiated);
     OPENSSL_free(result->server_alpn_negotiated);
+    OPENSSL_free(result->result_session_ticket_app_data);
     sk_X509_NAME_pop_free(result->server_ca_names, X509_NAME_free);
     sk_X509_NAME_pop_free(result->client_ca_names, X509_NAME_free);
     OPENSSL_free(result->cipher);
@@ -64,6 +65,7 @@ typedef struct ctx_data_st {
     size_t alpn_protocols_len;
     char *srp_user;
     char *srp_password;
+    char *session_ticket_app_data;
 } CTX_DATA;
 
 /* |ctx_data| itself is stack-allocated. */
@@ -77,6 +79,8 @@ static void ctx_data_free_data(CTX_DATA *ctx_data)
     ctx_data->srp_user = NULL;
     OPENSSL_free(ctx_data->srp_password);
     ctx_data->srp_password = NULL;
+    OPENSSL_free(ctx_data->session_ticket_app_data);
+    ctx_data->session_ticket_app_data = NULL;
 }
 
 static int ex_data_idx;
@@ -453,6 +457,26 @@ static int server_srp_cb(SSL *s, int *ad, void *arg)
 }
 #endif  /* !OPENSSL_NO_SRP */
 
+static int generate_session_ticket_cb(SSL *s, void *arg)
+{
+    CTX_DATA *server_ctx_data = arg;
+    SSL_SESSION *ss = SSL_get_session(s);
+    char *app_data = server_ctx_data->session_ticket_app_data;
+
+    if (ss == NULL || app_data == NULL)
+        return 0;
+
+    return SSL_SESSION_set1_ticket_appdata(ss, app_data, strlen(app_data));
+}
+
+static SSL_TICKET_RETURN decrypt_session_ticket_cb(SSL *s, SSL_SESSION *ss,
+                                                   const unsigned char *keyname,
+                                                   size_t keyname_len,
+                                                   SSL_TICKET_RETURN retv, void *arg)
+{
+    return retv;
+}
+
 /*
  * Configure callbacks and other properties that can't be set directly
  * in the server/client CONF.
@@ -607,6 +631,21 @@ static int configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
         OPENSSL_free(alpn_protos);
     }
 
+    if (extra->server.session_ticket_app_data != NULL) {
+        server_ctx_data->session_ticket_app_data =
+            OPENSSL_strdup(extra->server.session_ticket_app_data);
+        SSL_CTX_set_session_ticket_cb(server_ctx, generate_session_ticket_cb,
+                                      decrypt_session_ticket_cb, server_ctx_data);
+    }
+    if (extra->server2.session_ticket_app_data != NULL) {
+        if (!TEST_ptr(server2_ctx))
+            goto err;
+        server2_ctx_data->session_ticket_app_data =
+            OPENSSL_strdup(extra->server2.session_ticket_app_data);
+        SSL_CTX_set_session_ticket_cb(server2_ctx, NULL,
+                                      decrypt_session_ticket_cb, server2_ctx_data);
+    }
+
     /*
      * Use fixed session ticket keys so that we can decrypt a ticket created with
      * one CTX in another CTX. Don't address server2 for the moment.
@@ -1583,6 +1622,11 @@ static HANDSHAKE_RESULT *do_handshake_internal(
     SSL_get0_alpn_selected(server.ssl, &proto, &proto_len);
     ret->server_alpn_negotiated = dup_str(proto, proto_len);
 
+    if ((sess = SSL_get0_session(server.ssl)) != NULL) {
+        SSL_SESSION_get0_ticket_appdata(sess, (void**)&tick, &tick_len);
+        ret->result_session_ticket_app_data = OPENSSL_strndup((const char*)tick, tick_len);
+    }
+
     ret->client_resumed = SSL_session_reused(client.ssl);
     ret->server_resumed = SSL_session_reused(server.ssl);
 
diff --git a/test/handshake_helper.h b/test/handshake_helper.h
index 9dcbeb7..8b11230 100644
--- a/test/handshake_helper.h
+++ b/test/handshake_helper.h
@@ -65,6 +65,8 @@ typedef struct handshake_result {
     /* Session id status */
     ssl_session_id_t session_id;
     char *cipher;
+    /* session ticket application data */
+    char *result_session_ticket_app_data;
 } HANDSHAKE_RESULT;
 
 HANDSHAKE_RESULT *HANDSHAKE_RESULT_new(void);
diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t
index d5a130f..8d52d1b 100644
--- a/test/recipes/80-test_ssl_new.t
+++ b/test/recipes/80-test_ssl_new.t
@@ -28,7 +28,7 @@ map { s/\^// } @conf_files if $^O eq "VMS";
 
 # We hard-code the number of tests to double-check that the globbing above
 # finds all files as expected.
-plan tests => 26;  # = scalar @conf_srcs
+plan tests => 27;  # = scalar @conf_srcs
 
 # Some test results depend on the configuration of enabled protocols. We only
 # verify generated sources in the default configuration.
diff --git a/test/ssl-tests/27-ticket-appdata.conf b/test/ssl-tests/27-ticket-appdata.conf
new file mode 100644
index 0000000..863ca7a
--- /dev/null
+++ b/test/ssl-tests/27-ticket-appdata.conf
@@ -0,0 +1,146 @@
+# Generated with generate_ssl_tests.pl
+
+num_tests = 4
+
+test-0 = 0-session-ticket-app-data12
+test-1 = 1-session-ticket-app-data12
+test-2 = 2-session-ticket-app-data13
+test-3 = 3-session-ticket-app-data13
+# ===========================================================
+
+[0-session-ticket-app-data12]
+ssl_conf = 0-session-ticket-app-data12-ssl
+
+[0-session-ticket-app-data12-ssl]
+server = 0-session-ticket-app-data12-server
+client = 0-session-ticket-app-data12-client
+resume-server = 0-session-ticket-app-data12-server
+resume-client = 0-session-ticket-app-data12-client
+
+[0-session-ticket-app-data12-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = SessionTicket
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[0-session-ticket-app-data12-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.2
+Options = SessionTicket
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-0]
+ExpectedResult = Success
+ExpectedSessionTicketAppData = HelloWorld
+HandshakeMode = Resume
+ResumptionExpected = Yes
+SessionTicketExpected = Yes
+server = 0-session-ticket-app-data12-server-extra
+resume-server = 0-session-ticket-app-data12-server-extra
+
+[0-session-ticket-app-data12-server-extra]
+SessionTicketAppData = HelloWorld
+
+
+# ===========================================================
+
+[1-session-ticket-app-data12]
+ssl_conf = 1-session-ticket-app-data12-ssl
+
+[1-session-ticket-app-data12-ssl]
+server = 1-session-ticket-app-data12-server
+client = 1-session-ticket-app-data12-client
+resume-server = 1-session-ticket-app-data12-server
+resume-client = 1-session-ticket-app-data12-client
+
+[1-session-ticket-app-data12-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = SessionTicket
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[1-session-ticket-app-data12-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.2
+Options = SessionTicket
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-1]
+ExpectedResult = Success
+ExpectedSessionTicketAppData = 
+HandshakeMode = Resume
+ResumptionExpected = Yes
+SessionTicketExpected = Yes
+
+
+# ===========================================================
+
+[2-session-ticket-app-data13]
+ssl_conf = 2-session-ticket-app-data13-ssl
+
+[2-session-ticket-app-data13-ssl]
+server = 2-session-ticket-app-data13-server
+client = 2-session-ticket-app-data13-client
+resume-server = 2-session-ticket-app-data13-server
+resume-client = 2-session-ticket-app-data13-client
+
+[2-session-ticket-app-data13-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = SessionTicket
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[2-session-ticket-app-data13-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+Options = SessionTicket
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-2]
+ExpectedResult = Success
+ExpectedSessionTicketAppData = HelloWorld
+HandshakeMode = Resume
+ResumptionExpected = Yes
+SessionTicketExpected = Yes
+server = 2-session-ticket-app-data13-server-extra
+resume-server = 2-session-ticket-app-data13-server-extra
+
+[2-session-ticket-app-data13-server-extra]
+SessionTicketAppData = HelloWorld
+
+
+# ===========================================================
+
+[3-session-ticket-app-data13]
+ssl_conf = 3-session-ticket-app-data13-ssl
+
+[3-session-ticket-app-data13-ssl]
+server = 3-session-ticket-app-data13-server
+client = 3-session-ticket-app-data13-client
+resume-server = 3-session-ticket-app-data13-server
+resume-client = 3-session-ticket-app-data13-client
+
+[3-session-ticket-app-data13-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = SessionTicket
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[3-session-ticket-app-data13-client]
+CipherString = DEFAULT
+MaxProtocol = TLSv1.3
+Options = SessionTicket
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-3]
+ExpectedResult = Success
+ExpectedSessionTicketAppData = 
+HandshakeMode = Resume
+ResumptionExpected = Yes
+SessionTicketExpected = Yes
+
+
diff --git a/test/ssl-tests/27-ticket-appdata.conf.in b/test/ssl-tests/27-ticket-appdata.conf.in
new file mode 100644
index 0000000..ed919d9
--- /dev/null
+++ b/test/ssl-tests/27-ticket-appdata.conf.in
@@ -0,0 +1,99 @@
+# -*- mode: perl; -*-
+# Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+
+## Test session ticket app data
+
+use strict;
+use warnings;
+
+package ssltests;
+use OpenSSL::Test::Utils;
+
+our @tests12 = (
+    {
+	"name" => "session-ticket-app-data12",
+	"client" => {
+	    "MaxProtocol" => "TLSv1.2",
+	    "Options" => "SessionTicket",
+	},
+	"server" => {
+	    "Options" => "SessionTicket",
+	    "extra" => {
+		"SessionTicketAppData" => "HelloWorld",
+	    },
+	},
+	"test" => {
+	    "HandshakeMode" => "Resume",
+	    "ExpectedResult" => "Success",
+	    "SessionTicketExpected" => "Yes",
+	    "ResumptionExpected" => "Yes",
+	    "ExpectedSessionTicketAppData" => "HelloWorld",
+	}
+    },
+    {
+	"name" => "session-ticket-app-data12",
+	"client" => {
+	    "MaxProtocol" => "TLSv1.2",
+	    "Options" => "SessionTicket",
+	},
+	"server" => {
+	    "Options" => "SessionTicket",
+	},
+	"test" => {
+	    "HandshakeMode" => "Resume",
+	    "ExpectedResult" => "Success",
+	    "SessionTicketExpected" => "Yes",
+	    "ResumptionExpected" => "Yes",
+	    "ExpectedSessionTicketAppData" => "",
+	}
+    }
+);
+our @tests13 = (
+    {
+	"name" => "session-ticket-app-data13",
+	"client" => {
+	    "MaxProtocol" => "TLSv1.3",
+	    "Options" => "SessionTicket",
+	},
+	"server" => {
+	    "Options" => "SessionTicket",
+	    "extra" => {
+		"SessionTicketAppData" => "HelloWorld",
+	    },
+	},
+	"test" => {
+	    "HandshakeMode" => "Resume",
+	    "ExpectedResult" => "Success",
+	    "SessionTicketExpected" => "Yes",
+	    "ResumptionExpected" => "Yes",
+	    "ExpectedSessionTicketAppData" => "HelloWorld",
+	}
+    },
+    {
+	"name" => "session-ticket-app-data13",
+	"client" => {
+	    "MaxProtocol" => "TLSv1.3",
+	    "Options" => "SessionTicket",
+	},
+	"server" => {
+	    "Options" => "SessionTicket",
+	},
+	"test" => {
+	    "HandshakeMode" => "Resume",
+	    "ExpectedResult" => "Success",
+	    "SessionTicketExpected" => "Yes",
+	    "ResumptionExpected" => "Yes",
+	    "ExpectedSessionTicketAppData" => "",
+	}
+    }
+);
+
+our @tests = ();
+push @tests, @tests12 unless disabled("tls1_2");
+push @tests, @tests13 unless disabled("tls1_3");
diff --git a/test/ssl_test.c b/test/ssl_test.c
index a21a0f7..778c330 100644
--- a/test/ssl_test.c
+++ b/test/ssl_test.c
@@ -188,6 +188,27 @@ static int check_alpn(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
     return ret;
 }
 
+static int check_session_ticket_app_data(HANDSHAKE_RESULT *result,
+                                         SSL_TEST_CTX *test_ctx)
+{
+    size_t result_len = 0;
+    size_t expected_len = 0;
+
+    /* consider empty and NULL strings to be the same */
+    if (result->result_session_ticket_app_data != NULL)
+        result_len = strlen(result->result_session_ticket_app_data);
+    if (test_ctx->expected_session_ticket_app_data != NULL)
+        expected_len = strlen(test_ctx->expected_session_ticket_app_data);
+    if (result_len == 0 && expected_len == 0)
+        return 1;
+
+    if (!TEST_str_eq(result->result_session_ticket_app_data,
+                     test_ctx->expected_session_ticket_app_data))
+        return 0;
+
+    return 1;
+}
+
 static int check_resumption(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
 {
     if (!TEST_int_eq(result->client_resumed, result->server_resumed))
@@ -352,6 +373,7 @@ static int check_test(HANDSHAKE_RESULT *result, SSL_TEST_CTX *test_ctx)
 #endif
         ret &= check_cipher(result, test_ctx);
         ret &= check_alpn(result, test_ctx);
+        ret &= check_session_ticket_app_data(result, test_ctx);
         ret &= check_resumption(result, test_ctx);
         ret &= check_tmp_key(result, test_ctx);
         ret &= check_server_cert_type(result, test_ctx);
diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c
index c55b272..7676966 100644
--- a/test/ssl_test_ctx.c
+++ b/test/ssl_test_ctx.c
@@ -99,6 +99,7 @@ static const test_enum ssl_test_results[] = {
     {"ServerFail", SSL_TEST_SERVER_FAIL},
     {"ClientFail", SSL_TEST_CLIENT_FAIL},
     {"InternalError", SSL_TEST_INTERNAL_ERROR},
+    {"FirstHandshakeFailed", SSL_TEST_FIRST_HANDSHAKE_FAILED},
 };
 
 __owur static int parse_expected_result(SSL_TEST_CTX *test_ctx, const char *value)
@@ -360,6 +361,10 @@ IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_SERVER_CONF, server, srp_user)
 IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_CLIENT_CONF, client, srp_password)
 IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_SERVER_CONF, server, srp_password)
 
+/* Session Ticket App Data options */
+IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_CTX, test, expected_session_ticket_app_data)
+IMPLEMENT_SSL_TEST_STRING_OPTION(SSL_TEST_SERVER_CONF, server, session_ticket_app_data)
+
 /* Handshake mode */
 
 static const test_enum ssl_handshake_modes[] = {
@@ -664,6 +669,7 @@ static const ssl_test_ctx_option ssl_test_ctx_options[] = {
     { "ExpectedClientCANames", &parse_expected_client_ca_names },
     { "UseSCTP", &parse_test_use_sctp },
     { "ExpectedCipher", &parse_test_expected_cipher },
+    { "ExpectedSessionTicketAppData", &parse_test_expected_session_ticket_app_data },
 };
 
 /* Nested client options. */
@@ -700,6 +706,7 @@ static const ssl_test_server_option ssl_test_server_options[] = {
     { "SRPUser", &parse_server_srp_user },
     { "SRPPassword", &parse_server_srp_password },
     { "ForcePHA", &parse_server_force_pha },
+    { "SessionTicketAppData", &parse_server_session_ticket_app_data },
 };
 
 SSL_TEST_CTX *SSL_TEST_CTX_new()
@@ -729,6 +736,8 @@ static void ssl_test_extra_conf_free_data(SSL_TEST_EXTRA_CONF *conf)
     OPENSSL_free(conf->server2.srp_password);
     OPENSSL_free(conf->client.srp_user);
     OPENSSL_free(conf->client.srp_password);
+    OPENSSL_free(conf->server.session_ticket_app_data);
+    OPENSSL_free(conf->server2.session_ticket_app_data);
 }
 
 static void ssl_test_ctx_free_extra_data(SSL_TEST_CTX *ctx)
@@ -742,6 +751,7 @@ void SSL_TEST_CTX_free(SSL_TEST_CTX *ctx)
     ssl_test_ctx_free_extra_data(ctx);
     OPENSSL_free(ctx->expected_npn_protocol);
     OPENSSL_free(ctx->expected_alpn_protocol);
+    OPENSSL_free(ctx->expected_session_ticket_app_data);
     sk_X509_NAME_pop_free(ctx->expected_server_ca_names, X509_NAME_free);
     sk_X509_NAME_pop_free(ctx->expected_client_ca_names, X509_NAME_free);
     OPENSSL_free(ctx->expected_cipher);
diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h
index f9b9217..e26c207 100644
--- a/test/ssl_test_ctx.h
+++ b/test/ssl_test_ctx.h
@@ -127,6 +127,7 @@ typedef struct {
     char *srp_password;
     /* Forced PHA */
     int force_pha;
+    char *session_ticket_app_data;
 } SSL_TEST_SERVER_CONF;
 
 typedef struct {
@@ -216,6 +217,8 @@ typedef struct {
     /* Whether to expect a session id from the server */
     ssl_session_id_t session_id_expected;
     char *expected_cipher;
+    /* Expected Session Ticket Application Data */
+    char *expected_session_ticket_app_data;
 } SSL_TEST_CTX;
 
 const char *ssl_test_result_name(ssl_test_result_t result);
diff --git a/test/ssl_test_ctx_test.c b/test/ssl_test_ctx_test.c
index 70ebb2a..e0458a1 100644
--- a/test/ssl_test_ctx_test.c
+++ b/test/ssl_test_ctx_test.c
@@ -55,6 +55,8 @@ static int serverconf_eq(SSL_TEST_SERVER_CONF *serv,
             || !TEST_str_eq(serv->alpn_protocols, serv2->alpn_protocols)
             || !TEST_int_eq(serv->broken_session_ticket,
                             serv2->broken_session_ticket)
+            || !TEST_str_eq(serv->session_ticket_app_data,
+                            serv2->session_ticket_app_data)
             || !TEST_int_eq(serv->cert_status, serv2->cert_status))
         return 0;
     return 1;
@@ -95,6 +97,8 @@ static int testctx_eq(SSL_TEST_CTX *ctx, SSL_TEST_CTX *ctx2)
                             ctx2->expected_alpn_protocol)
             || !TEST_str_eq(ctx->expected_cipher,
                             ctx2->expected_cipher)
+            || !TEST_str_eq(ctx->expected_session_ticket_app_data,
+                            ctx2->expected_session_ticket_app_data)
             || !TEST_int_eq(ctx->resumption_expected,
                             ctx2->resumption_expected)
             || !TEST_int_eq(ctx->session_id_expected,
diff --git a/util/libssl.num b/util/libssl.num
index b299035..6c02a9b 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -479,3 +479,6 @@ SSL_force_post_handshake_auth           479	1_1_1	EXIST::FUNCTION:
 SSL_export_keying_material_early        480	1_1_1	EXIST::FUNCTION:
 SSL_CTX_use_cert_and_key                481	1_1_1	EXIST::FUNCTION:
 SSL_use_cert_and_key                    482	1_1_1	EXIST::FUNCTION:
+SSL_SESSION_get0_ticket_appdata         483	1_1_1	EXIST::FUNCTION:
+SSL_SESSION_set1_ticket_appdata         484	1_1_1	EXIST::FUNCTION:
+SSL_CTX_set_session_ticket_cb           485	1_1_1	EXIST::FUNCTION:
diff --git a/util/private.num b/util/private.num
index c106e60..6c1a0ef 100644
--- a/util/private.num
+++ b/util/private.num
@@ -248,7 +248,9 @@ SSL_CTX_build_cert_chain                define
 SSL_CTX_clear_chain_certs               define
 SSL_CTX_clear_extra_chain_certs         define
 SSL_CTX_clear_mode                      define
+SSL_CTX_decrypt_session_ticket_fn       define
 SSL_CTX_disable_ct                      define
+SSL_CTX_generate_session_ticket_fn      define
 SSL_CTX_get0_chain_certs                define
 SSL_CTX_get_default_read_ahead          define
 SSL_CTX_get_max_cert_list               define


More information about the openssl-commits mailing list