[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Tue Jun 26 17:13:50 UTC 2018


The branch master has been updated
       via  41145c35bfee8f2b0822288fcb23a807d06d8e89 (commit)
       via  e880d4e58d1afe4d6e2d76646a8fbbe95fe05d40 (commit)
       via  6cc0b3c2171e26379e898574cb6d42b8d8dcc113 (commit)
       via  6a11d5c5ededa1543c2eeb2f9edcbe39bc58bb70 (commit)
      from  2c879241baaf9115e8ebbe228e1a624564eea64c (commit)


- Log -----------------------------------------------------------------
commit 41145c35bfee8f2b0822288fcb23a807d06d8e89
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 22 15:05:27 2018 +0100

    Document changes to SSL_OP_NO_TICKET for TLSv1.3
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6563)

commit e880d4e58d1afe4d6e2d76646a8fbbe95fe05d40
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jun 21 16:54:55 2018 +0100

    Use stateful tickets if we are doing anti-replay
    
    During anti-replay we cache the ticket anyway, so there is no point in
    using a full stateless ticket.
    
    Fixes #6391
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6563)

commit 6cc0b3c2171e26379e898574cb6d42b8d8dcc113
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jun 13 15:57:39 2018 +0100

    Respect SSL_OP_NO_TICKET in TLSv1.3
    
    Implement support for stateful TLSv1.3 tickets, and use them if
    SSL_OP_NO_TICKET is set.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6563)

commit 6a11d5c5ededa1543c2eeb2f9edcbe39bc58bb70
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jun 13 11:59:43 2018 +0100

    Restructure the ticket construction code
    
    Separate out as a new function the code to write out data which is specific
    to a stateless ticket.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6563)

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

Summary of changes:
 crypto/err/openssl.txt                 |   3 +
 doc/man3/SSL_CTX_set_options.pod       |  40 +++-
 doc/man3/SSL_read_early_data.pod       |  14 +-
 include/openssl/sslerr.h               |   3 +
 ssl/ssl_err.c                          |   6 +
 ssl/ssl_lib.c                          |   9 +-
 ssl/ssl_locl.h                         |   2 +
 ssl/ssl_sess.c                         | 134 ++++++------
 ssl/statem/extensions_srvr.c           |  42 +++-
 ssl/statem/statem_srvr.c               | 380 ++++++++++++++++++++-------------
 test/handshake_helper.c                |  26 ++-
 test/ssl-tests/10-resumption.conf      |  32 +++
 test/ssl-tests/11-dtls_resumption.conf |   8 +
 test/ssl-tests/protocol_version.pm     |   1 +
 test/sslapitest.c                      |   3 +-
 15 files changed, 464 insertions(+), 239 deletions(-)

diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index e0580a8..e65a806 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1089,7 +1089,10 @@ SSL_F_CHECK_SUITEB_CIPHER_LIST:331:check_suiteb_cipher_list
 SSL_F_CIPHERSUITE_CB:622:ciphersuite_cb
 SSL_F_CONSTRUCT_CA_NAMES:552:construct_ca_names
 SSL_F_CONSTRUCT_KEY_EXCHANGE_TBS:553:construct_key_exchange_tbs
+SSL_F_CONSTRUCT_STATEFUL_TICKET:636:construct_stateful_ticket
+SSL_F_CONSTRUCT_STATELESS_TICKET:637:construct_stateless_ticket
 SSL_F_CREATE_SYNTHETIC_MESSAGE_HASH:539:create_synthetic_message_hash
+SSL_F_CREATE_TICKET_PREQUEL:638:create_ticket_prequel
 SSL_F_CT_MOVE_SCTS:345:ct_move_scts
 SSL_F_CT_STRICT:349:ct_strict
 SSL_F_CUSTOM_EXT_ADD:554:custom_ext_add
diff --git a/doc/man3/SSL_CTX_set_options.pod b/doc/man3/SSL_CTX_set_options.pod
index 552de07..f04de32 100644
--- a/doc/man3/SSL_CTX_set_options.pod
+++ b/doc/man3/SSL_CTX_set_options.pod
@@ -145,13 +145,39 @@ DTLS connections.
 
 =item SSL_OP_NO_TICKET
 
-Normally clients and servers will, where possible, transparently make use
-of RFC4507bis tickets for stateless session resumption.
-
-If this option is set this functionality is disabled and tickets will
-not be used by clients or servers.
-
-This option only applies to TLSv1.2 and below. It is ignored for TLSv1.3.
+SSL/TLS supports two mechanisms for resuming sessions: session ids and stateless
+session tickets.
+
+When using session ids a copy of the session information is
+cached on the server and a unique id is sent to the client. When the client
+wishes to resume it provides the unique id so that the server can retrieve the
+session information from its cache.
+
+When using stateless session tickets the server uses a session ticket encryption
+key to encrypt the session information. This encrypted data is sent to the
+client as a "ticket". When the client wishes to resume it sends the encrypted
+data back to the server. The server uses its key to decrypt the data and resume
+the session. In this way the server can operate statelessly - no session
+information needs to be cached locally.
+
+The TLSv1.3 protocol only supports tickets and does not directly support session
+ids. However OpenSSL allows two modes of ticket operation in TLSv1.3: stateful
+and stateless. Stateless tickets work the same way as in TLSv1.2 and below.
+Stateful tickets mimic the session id behaviour available in TLSv1.2 and below.
+The session information is cached on the server and the session id is wrapped up
+in a ticket and sent back to the client. When the client wishes to resume, it
+presents a ticket in the same way as for stateless tickets. The server can then
+extract the session id from the ticket and retrieve the session information from
+its cache.
+
+By default OpenSSL will use stateless tickets. The SSL_OP_NO_TICKET option will
+cause stateless tickets to not be issued. In TLSv1.2 and below this means no
+ticket gets sent to the client at all. In TLSv1.3 a stateful ticket will be
+sent. This is a server-side option only.
+
+In TLSv1.3 it is possible to suppress all tickets (stateful and stateless) from
+being sent by calling L<SSL_CTX_set_num_tickets(3)> or
+L<SSL_set_num_tickets(3)>.
 
 =item SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
 
diff --git a/doc/man3/SSL_read_early_data.pod b/doc/man3/SSL_read_early_data.pod
index bc5bd0a..6a76ec2 100644
--- a/doc/man3/SSL_read_early_data.pod
+++ b/doc/man3/SSL_read_early_data.pod
@@ -226,12 +226,14 @@ was submitted will be ignored). Note that single use tickets are enforced even
 if a client does not send any early data.
 
 The replay protection mechanism relies on the internal OpenSSL server session
-cache (see L<SSL_CTX_set_session_cache_mode(3)>). By default sessions will be
-added to the cache whenever a session ticket is issued. When a client attempts
-to resume the session OpenSSL will check for its presence in the internal cache.
-If it exists then the resumption is allowed and the session is removed from the
-cache. If it does not exist then the resumption is not allowed and a full
-handshake will occur.
+cache (see L<SSL_CTX_set_session_cache_mode(3)>). When replay protection is
+being used the server will operate as if the SSL_OP_NO_TICKET option had been
+selected (see L<SSL_CTX_set_options(3)>). Sessions will be added to the cache
+whenever a session ticket is issued. When a client attempts to resume the
+session, OpenSSL will check for its presence in the internal cache. If it exists
+then the resumption is allowed and the session is removed from the cache. If it
+does not exist then the resumption is not allowed and a full handshake will
+occur.
 
 Note that some applications may maintain an external cache of sessions (see
 L<SSL_CTX_sess_set_new_cb(3)> and similar functions). It is the application's
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index 8e395cd..b2c6c1e 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -26,7 +26,10 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_CIPHERSUITE_CB                             622
 # define SSL_F_CONSTRUCT_CA_NAMES                         552
 # define SSL_F_CONSTRUCT_KEY_EXCHANGE_TBS                 553
+# define SSL_F_CONSTRUCT_STATEFUL_TICKET                  636
+# define SSL_F_CONSTRUCT_STATELESS_TICKET                 637
 # define SSL_F_CREATE_SYNTHETIC_MESSAGE_HASH              539
+# define SSL_F_CREATE_TICKET_PREQUEL                      638
 # define SSL_F_CT_MOVE_SCTS                               345
 # define SSL_F_CT_STRICT                                  349
 # define SSL_F_CUSTOM_EXT_ADD                             554
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index bce2036..03c5bf2 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -24,8 +24,14 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_CA_NAMES, 0), "construct_ca_names"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_KEY_EXCHANGE_TBS, 0),
      "construct_key_exchange_tbs"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_STATEFUL_TICKET, 0),
+     "construct_stateful_ticket"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_STATELESS_TICKET, 0),
+     "construct_stateless_ticket"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_CREATE_SYNTHETIC_MESSAGE_HASH, 0),
      "create_synthetic_message_hash"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_CREATE_TICKET_PREQUEL, 0),
+     "create_ticket_prequel"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_CT_MOVE_SCTS, 0), "ct_move_scts"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_CT_STRICT, 0), "ct_strict"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_CUSTOM_EXT_ADD, 0), "custom_ext_add"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 22f729c..6ced147 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3369,18 +3369,21 @@ void ssl_update_cache(SSL *s, int mode)
         && (!s->hit || SSL_IS_TLS13(s))) {
         /*
          * Add the session to the internal cache. In server side TLSv1.3 we
-         * normally don't do this because its a full stateless ticket with only
-         * a dummy session id so there is no reason to cache it, unless:
+         * normally don't do this because by default it's a full stateless ticket
+         * with only a dummy session id so there is no reason to cache it,
+         * unless:
          * - we are doing early_data, in which case we cache so that we can
          *   detect replays
          * - the application has set a remove_session_cb so needs to know about
          *   session timeout events
+         * - SSL_OP_NO_TICKET is set in which case it is a stateful ticket
          */
         if ((i & SSL_SESS_CACHE_NO_INTERNAL_STORE) == 0
                 && (!SSL_IS_TLS13(s)
                     || !s->server
                     || s->max_early_data > 0
-                    || s->session_ctx->remove_session_cb != NULL))
+                    || s->session_ctx->remove_session_cb != NULL
+                    || (s->options & SSL_OP_NO_TICKET) != 0))
             SSL_CTX_add_session(s->session_ctx, s->session);
 
         /*
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 86c250b..a4d1376 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -2212,6 +2212,8 @@ void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
 __owur int ssl_generate_session_id(SSL *s, SSL_SESSION *ss);
 __owur int ssl_get_new_session(SSL *s, int session);
+__owur SSL_SESSION *lookup_sess_in_cache(SSL *s, const unsigned char *sess_id,
+                                         size_t sess_id_len);
 __owur int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello);
 __owur SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket);
 __owur int ssl_cipher_id_cmp(const SSL_CIPHER *a, const SSL_CIPHER *b);
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index fde4187..628b9f0 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -12,6 +12,7 @@
 #include <openssl/rand.h>
 #include <openssl/engine.h>
 #include "internal/refcount.h"
+#include "internal/cryptlib.h"
 #include "ssl_locl.h"
 #include "statem/statem_locl.h"
 
@@ -452,6 +453,73 @@ int ssl_get_new_session(SSL *s, int session)
     return 1;
 }
 
+SSL_SESSION *lookup_sess_in_cache(SSL *s, const unsigned char *sess_id,
+                                  size_t sess_id_len)
+{
+    SSL_SESSION *ret = NULL;
+    int discard;
+
+    if ((s->session_ctx->session_cache_mode
+         & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) == 0) {
+        SSL_SESSION data;
+
+        data.ssl_version = s->version;
+        if (!ossl_assert(sess_id_len <= SSL_MAX_SSL_SESSION_ID_LENGTH))
+            return NULL;
+
+        memcpy(data.session_id, sess_id, sess_id_len);
+        data.session_id_length = sess_id_len;
+
+        CRYPTO_THREAD_read_lock(s->session_ctx->lock);
+        ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data);
+        if (ret != NULL) {
+            /* don't allow other threads to steal it: */
+            SSL_SESSION_up_ref(ret);
+        }
+        CRYPTO_THREAD_unlock(s->session_ctx->lock);
+        if (ret == NULL)
+            CRYPTO_atomic_add(&s->session_ctx->stats.sess_miss, 1, &discard,
+                              s->session_ctx->lock);
+    }
+
+    if (ret == NULL && s->session_ctx->get_session_cb != NULL) {
+        int copy = 1;
+
+        ret = s->session_ctx->get_session_cb(s, sess_id, sess_id_len, &copy);
+
+        if (ret != NULL) {
+            CRYPTO_atomic_add(&s->session_ctx->stats.sess_cb_hit, 1, &discard,
+                              s->session_ctx->lock);
+
+            /*
+             * Increment reference count now if the session callback asks us
+             * to do so (note that if the session structures returned by the
+             * callback are shared between threads, it must handle the
+             * reference count itself [i.e. copy == 0], or things won't be
+             * thread-safe).
+             */
+            if (copy)
+                SSL_SESSION_up_ref(ret);
+
+            /*
+             * Add the externally cached session to the internal cache as
+             * well if and only if we are supposed to.
+             */
+            if ((s->session_ctx->session_cache_mode &
+                 SSL_SESS_CACHE_NO_INTERNAL_STORE) == 0) {
+                /*
+                 * Either return value of SSL_CTX_add_session should not
+                 * interrupt the session resumption process. The return
+                 * value is intentionally ignored.
+                 */
+                (void)SSL_CTX_add_session(s->session_ctx, ret);
+            }
+        }
+    }
+
+    return ret;
+}
+
 /*-
  * ssl_get_prev attempts to find an SSL_SESSION to be used to resume this
  * connection. It is only called by servers.
@@ -504,8 +572,11 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
             goto err;
         case SSL_TICKET_NONE:
         case SSL_TICKET_EMPTY:
-            if (hello->session_id_len > 0)
+            if (hello->session_id_len > 0) {
                 try_session_cache = 1;
+                ret = lookup_sess_in_cache(s, hello->session_id,
+                                           hello->session_id_len);
+            }
             break;
         case SSL_TICKET_NO_DECRYPT:
         case SSL_TICKET_SUCCESS:
@@ -514,67 +585,6 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
         }
     }
 
-    if (try_session_cache &&
-        ret == NULL &&
-        !(s->session_ctx->session_cache_mode &
-          SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
-        SSL_SESSION data;
-
-        data.ssl_version = s->version;
-        memcpy(data.session_id, hello->session_id, hello->session_id_len);
-        data.session_id_length = hello->session_id_len;
-
-        CRYPTO_THREAD_read_lock(s->session_ctx->lock);
-        ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data);
-        if (ret != NULL) {
-            /* don't allow other threads to steal it: */
-            SSL_SESSION_up_ref(ret);
-        }
-        CRYPTO_THREAD_unlock(s->session_ctx->lock);
-        if (ret == NULL)
-            CRYPTO_atomic_add(&s->session_ctx->stats.sess_miss, 1, &discard,
-                              s->session_ctx->lock);
-    }
-
-    if (try_session_cache &&
-        ret == NULL && s->session_ctx->get_session_cb != NULL) {
-        int copy = 1;
-
-        ret = s->session_ctx->get_session_cb(s, hello->session_id,
-                                             hello->session_id_len,
-                                             &copy);
-
-        if (ret != NULL) {
-            CRYPTO_atomic_add(&s->session_ctx->stats.sess_cb_hit, 1, &discard,
-                              s->session_ctx->lock);
-
-            /*
-             * Increment reference count now if the session callback asks us
-             * to do so (note that if the session structures returned by the
-             * callback are shared between threads, it must handle the
-             * reference count itself [i.e. copy == 0], or things won't be
-             * thread-safe).
-             */
-            if (copy)
-                SSL_SESSION_up_ref(ret);
-
-            /*
-             * Add the externally cached session to the internal cache as
-             * well if and only if we are supposed to.
-             */
-            if (!
-                (s->session_ctx->session_cache_mode &
-                 SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
-                /*
-                 * Either return value of SSL_CTX_add_session should not
-                 * interrupt the session resumption process. The return
-                 * value is intentionally ignored.
-                 */
-                SSL_CTX_add_session(s->session_ctx, ret);
-            }
-        }
-    }
-
     if (ret == NULL)
         goto err;
 
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index 7c756c0..f58ed0b 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1009,6 +1009,33 @@ int tls_parse_ctos_early_data(SSL *s, PACKET *pkt, unsigned int context,
     return 1;
 }
 
+static SSL_TICKET_STATUS tls_get_stateful_ticket(SSL *s, PACKET *tick,
+                                                 SSL_SESSION **sess)
+{
+    SSL_SESSION *tmpsess = NULL;
+
+    switch (PACKET_remaining(tick)) {
+        case 0:
+            return SSL_TICKET_EMPTY;
+
+        case SSL_MAX_SSL_SESSION_ID_LENGTH:
+            break;
+
+        default:
+            return SSL_TICKET_NO_DECRYPT;
+    }
+
+    tmpsess = lookup_sess_in_cache(s, PACKET_data(tick),
+                                   SSL_MAX_SSL_SESSION_ID_LENGTH);
+
+    if (tmpsess == NULL)
+        return SSL_TICKET_NO_DECRYPT;
+
+    s->ext.ticket_expected = 1;
+    *sess = tmpsess;
+    return SSL_TICKET_SUCCESS;
+}
+
 int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                        size_t chainidx)
 {
@@ -1132,9 +1159,18 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
             uint32_t ticket_age = 0, now, agesec, agems;
             int ret;
 
-            ret = tls_decrypt_ticket(s, PACKET_data(&identity),
-                                     PACKET_remaining(&identity), NULL, 0,
-                                     &sess);
+            /*
+             * If we are using anti-replay protection then we behave as if
+             * SSL_OP_NO_TICKET is set - we are caching tickets anyway so there
+             * is no point in using full stateless tickets.
+             */
+            if ((s->options & SSL_OP_NO_TICKET) != 0
+                    || s->max_early_data > 0)
+                ret = tls_get_stateful_ticket(s, &identity, &sess);
+            else
+                ret = tls_decrypt_ticket(s, PACKET_data(&identity),
+                                         PACKET_remaining(&identity), NULL, 0,
+                                         &sess);
 
             if (ret == SSL_TICKET_EMPTY) {
                 SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_PSK,
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index df3f15a..9c44be0 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -3739,7 +3739,44 @@ int tls_construct_server_certificate(SSL *s, WPACKET *pkt)
     return 1;
 }
 
-int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
+static int create_ticket_prequel(SSL *s, WPACKET *pkt, uint32_t age_add,
+                                 unsigned char *tick_nonce)
+{
+    /*
+     * Ticket lifetime hint: For TLSv1.2 this is advisory only and we leave this
+     * unspecified for resumed session (for simplicity).
+     * In TLSv1.3 we reset the "time" field above, and always specify the
+     * timeout.
+     */
+    if (!WPACKET_put_bytes_u32(pkt,
+                               (s->hit && !SSL_IS_TLS13(s))
+                               ? 0 : s->session->timeout)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    if (SSL_IS_TLS13(s)) {
+        if (!WPACKET_put_bytes_u32(pkt, age_add)
+                || !WPACKET_sub_memcpy_u8(pkt, tick_nonce, TICKET_NONCE_SIZE)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
+                     ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+    }
+
+    /* Start the sub-packet for the actual ticket data */
+    if (!WPACKET_start_sub_packet_u16(pkt)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int construct_stateless_ticket(SSL *s, WPACKET *pkt, uint32_t age_add,
+                                      unsigned char *tick_nonce)
 {
     unsigned char *senc = NULL;
     EVP_CIPHER_CTX *ctx = NULL;
@@ -3752,115 +3789,8 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
     SSL_CTX *tctx = s->session_ctx;
     unsigned char iv[EVP_MAX_IV_LENGTH];
     unsigned char key_name[TLSEXT_KEYNAME_LENGTH];
-    int iv_len;
-    unsigned char tick_nonce[TICKET_NONCE_SIZE];
+    int iv_len, ok = 0;
     size_t macoffset, macendoffset;
-    union {
-        unsigned char age_add_c[sizeof(uint32_t)];
-        uint32_t age_add;
-    } age_add_u;
-
-    if (SSL_IS_TLS13(s)) {
-        size_t i, hashlen;
-        uint64_t nonce;
-        static const unsigned char nonce_label[] = "resumption";
-        const EVP_MD *md = ssl_handshake_md(s);
-        void (*cb) (const SSL *ssl, int type, int val) = NULL;
-        int hashleni = EVP_MD_size(md);
-
-        /* Ensure cast to size_t is safe */
-        if (!ossl_assert(hashleni >= 0)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                     SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-        hashlen = (size_t)hashleni;
-
-        if (s->info_callback != NULL)
-            cb = s->info_callback;
-        else if (s->ctx->info_callback != NULL)
-            cb = s->ctx->info_callback;
-
-        if (cb != NULL) {
-            /*
-             * We don't start and stop the handshake in between each ticket when
-             * sending more than one - but it should appear that way to the info
-             * callback.
-             */
-            if (s->sent_tickets != 0) {
-                ossl_statem_set_in_init(s, 0);
-                cb(s, SSL_CB_HANDSHAKE_DONE, 1);
-                ossl_statem_set_in_init(s, 1);
-            }
-            cb(s, SSL_CB_HANDSHAKE_START, 1);
-        }
-        /*
-         * If we already sent one NewSessionTicket, or we resumed then
-         * s->session may already be in a cache and so we must not modify it.
-         * Instead we need to take a copy of it and modify that.
-         */
-        if (s->sent_tickets != 0 || s->hit) {
-            SSL_SESSION *new_sess = ssl_session_dup(s->session, 0);
-
-            if (new_sess == NULL) {
-                /* SSLfatal already called */
-                goto err;
-            }
-
-            SSL_SESSION_free(s->session);
-            s->session = new_sess;
-        }
-
-        if (!ssl_generate_session_id(s, s->session)) {
-            /* SSLfatal() already called */
-            goto err;
-        }
-        if (RAND_bytes(age_add_u.age_add_c, sizeof(age_add_u)) <= 0) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                     SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
-                     ERR_R_INTERNAL_ERROR);
-            goto err;
-        }
-        s->session->ext.tick_age_add = age_add_u.age_add;
-
-        nonce = s->next_ticket_nonce;
-        for (i = TICKET_NONCE_SIZE; i > 0; i--) {
-            tick_nonce[i - 1] = (unsigned char)(nonce & 0xff);
-            nonce >>= 8;
-        }
-
-        if (!tls13_hkdf_expand(s, md, s->resumption_master_secret,
-                               nonce_label,
-                               sizeof(nonce_label) - 1,
-                               tick_nonce,
-                               TICKET_NONCE_SIZE,
-                               s->session->master_key,
-                               hashlen)) {
-            /* SSLfatal() already called */
-            goto err;
-        }
-        s->session->master_key_length = hashlen;
-
-        s->session->time = (long)time(NULL);
-        if (s->s3->alpn_selected != NULL) {
-            OPENSSL_free(s->session->ext.alpn_selected);
-            s->session->ext.alpn_selected =
-                OPENSSL_memdup(s->s3->alpn_selected, s->s3->alpn_selected_len);
-            if (s->session->ext.alpn_selected == NULL) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                         SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
-                         ERR_R_MALLOC_FAILURE);
-                goto err;
-            }
-            s->session->ext.alpn_selected_len = s->s3->alpn_selected_len;
-        }
-        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);
@@ -3869,29 +3799,29 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
      * long
      */
     if (slen_full == 0 || slen_full > 0xFF00) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+                 ERR_R_INTERNAL_ERROR);
         goto err;
     }
     senc = OPENSSL_malloc(slen_full);
     if (senc == NULL) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_MALLOC_FAILURE);
+                 SSL_F_CONSTRUCT_STATELESS_TICKET, ERR_R_MALLOC_FAILURE);
         goto err;
     }
 
     ctx = EVP_CIPHER_CTX_new();
     hctx = HMAC_CTX_new();
     if (ctx == NULL || hctx == NULL) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_MALLOC_FAILURE);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+                 ERR_R_MALLOC_FAILURE);
         goto err;
     }
 
     p = senc;
     if (!i2d_SSL_SESSION(s->session, &p)) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+                 ERR_R_INTERNAL_ERROR);
         goto err;
     }
 
@@ -3901,23 +3831,23 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
     const_p = senc;
     sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
     if (sess == NULL) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+                 ERR_R_INTERNAL_ERROR);
         goto err;
     }
 
     slen = i2d_SSL_SESSION(sess, NULL);
     if (slen == 0 || slen > slen_full) {
         /* shouldn't ever happen */
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+                 ERR_R_INTERNAL_ERROR);
         SSL_SESSION_free(sess);
         goto err;
     }
     p = senc;
     if (!i2d_SSL_SESSION(sess, &p)) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+                 ERR_R_INTERNAL_ERROR);
         SSL_SESSION_free(sess);
         goto err;
     }
@@ -3938,7 +3868,7 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
             if (!WPACKET_put_bytes_u32(pkt, 0)
                     || !WPACKET_put_bytes_u16(pkt, 0)) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                         SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+                         SSL_F_CONSTRUCT_STATELESS_TICKET,
                          ERR_R_INTERNAL_ERROR);
                 goto err;
             }
@@ -3948,8 +3878,7 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
             return 1;
         }
         if (ret < 0) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                     SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
                      SSL_R_CALLBACK_FAILED);
             goto err;
         }
@@ -3964,8 +3893,7 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
                 || !HMAC_Init_ex(hctx, tctx->ext.secure->tick_hmac_key,
                                  sizeof(tctx->ext.secure->tick_hmac_key),
                                  EVP_sha256(), NULL)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                     SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
                      ERR_R_INTERNAL_ERROR);
             goto err;
         }
@@ -3973,22 +3901,12 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
                sizeof(tctx->ext.tick_key_name));
     }
 
-    /*
-     * Ticket lifetime hint: For TLSv1.2 this is advisory only and we leave this
-     * unspecified for resumed session (for simplicity).
-     * In TLSv1.3 we reset the "time" field above, and always specify the
-     * timeout.
-     */
-    if (!WPACKET_put_bytes_u32(pkt,
-                               (s->hit && !SSL_IS_TLS13(s))
-                               ? 0 : s->session->timeout)
-            || (SSL_IS_TLS13(s)
-                && (!WPACKET_put_bytes_u32(pkt, age_add_u.age_add)
-                    || !WPACKET_sub_memcpy_u8(pkt, tick_nonce,
-                                              TICKET_NONCE_SIZE)))
-               /* Now the actual ticket data */
-            || !WPACKET_start_sub_packet_u16(pkt)
-            || !WPACKET_get_total_written(pkt, &macoffset)
+    if (!create_ticket_prequel(s, pkt, age_add, tick_nonce)) {
+        /* SSLfatal() already called */
+        goto err;
+    }
+
+    if (!WPACKET_get_total_written(pkt, &macoffset)
                /* Output key name */
             || !WPACKET_memcpy(pkt, key_name, sizeof(key_name))
                /* output IV */
@@ -4011,12 +3929,176 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
             || !HMAC_Final(hctx, macdata1, &hlen)
             || hlen > EVP_MAX_MD_SIZE
             || !WPACKET_allocate_bytes(pkt, hlen, &macdata2)
-            || macdata1 != macdata2
-            || !WPACKET_close(pkt)) {
+            || macdata1 != macdata2) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
+                 SSL_F_CONSTRUCT_STATELESS_TICKET, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    /* Close the sub-packet created by create_ticket_prequel() */
+    if (!WPACKET_close(pkt)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
+                 ERR_R_INTERNAL_ERROR);
         goto err;
     }
+
+    ok = 1;
+ err:
+    OPENSSL_free(senc);
+    EVP_CIPHER_CTX_free(ctx);
+    HMAC_CTX_free(hctx);
+    return ok;
+}
+
+static int construct_stateful_ticket(SSL *s, WPACKET *pkt, uint32_t age_add,
+                                     unsigned char *tick_nonce)
+{
+    if (!create_ticket_prequel(s, pkt, age_add, tick_nonce)) {
+        /* SSLfatal() already called */
+        return 0;
+    }
+
+    if (!WPACKET_memcpy(pkt, s->session->session_id,
+                        s->session->session_id_length)
+            || !WPACKET_close(pkt)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATEFUL_TICKET,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return 1;
+}
+
+int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
+{
+    SSL_CTX *tctx = s->session_ctx;
+    unsigned char tick_nonce[TICKET_NONCE_SIZE];
+    union {
+        unsigned char age_add_c[sizeof(uint32_t)];
+        uint32_t age_add;
+    } age_add_u;
+
+    age_add_u.age_add = 0;
+
+    if (SSL_IS_TLS13(s)) {
+        size_t i, hashlen;
+        uint64_t nonce;
+        static const unsigned char nonce_label[] = "resumption";
+        const EVP_MD *md = ssl_handshake_md(s);
+        void (*cb) (const SSL *ssl, int type, int val) = NULL;
+        int hashleni = EVP_MD_size(md);
+
+        /* Ensure cast to size_t is safe */
+        if (!ossl_assert(hashleni >= 0)) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+                     ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        hashlen = (size_t)hashleni;
+
+        if (s->info_callback != NULL)
+            cb = s->info_callback;
+        else if (s->ctx->info_callback != NULL)
+            cb = s->ctx->info_callback;
+
+        if (cb != NULL) {
+            /*
+             * We don't start and stop the handshake in between each ticket when
+             * sending more than one - but it should appear that way to the info
+             * callback.
+             */
+            if (s->sent_tickets != 0) {
+                ossl_statem_set_in_init(s, 0);
+                cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+                ossl_statem_set_in_init(s, 1);
+            }
+            cb(s, SSL_CB_HANDSHAKE_START, 1);
+        }
+        /*
+         * If we already sent one NewSessionTicket, or we resumed then
+         * s->session may already be in a cache and so we must not modify it.
+         * Instead we need to take a copy of it and modify that.
+         */
+        if (s->sent_tickets != 0 || s->hit) {
+            SSL_SESSION *new_sess = ssl_session_dup(s->session, 0);
+
+            if (new_sess == NULL) {
+                /* SSLfatal already called */
+                goto err;
+            }
+
+            SSL_SESSION_free(s->session);
+            s->session = new_sess;
+        }
+
+        if (!ssl_generate_session_id(s, s->session)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+        if (RAND_bytes(age_add_u.age_add_c, sizeof(age_add_u)) <= 0) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+                     ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        s->session->ext.tick_age_add = age_add_u.age_add;
+
+        nonce = s->next_ticket_nonce;
+        for (i = TICKET_NONCE_SIZE; i > 0; i--) {
+            tick_nonce[i - 1] = (unsigned char)(nonce & 0xff);
+            nonce >>= 8;
+        }
+
+        if (!tls13_hkdf_expand(s, md, s->resumption_master_secret,
+                               nonce_label,
+                               sizeof(nonce_label) - 1,
+                               tick_nonce,
+                               TICKET_NONCE_SIZE,
+                               s->session->master_key,
+                               hashlen)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+        s->session->master_key_length = hashlen;
+
+        s->session->time = (long)time(NULL);
+        if (s->s3->alpn_selected != NULL) {
+            OPENSSL_free(s->session->ext.alpn_selected);
+            s->session->ext.alpn_selected =
+                OPENSSL_memdup(s->s3->alpn_selected, s->s3->alpn_selected_len);
+            if (s->session->ext.alpn_selected == NULL) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                         SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
+                         ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+            s->session->ext.alpn_selected_len = s->s3->alpn_selected_len;
+        }
+        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;
+
+    /*
+     * If we are using anti-replay protection then we behave as if
+     * SSL_OP_NO_TICKET is set - we are caching tickets anyway so there
+     * is no point in using full stateless tickets.
+     */
+    if (((s->options & SSL_OP_NO_TICKET) != 0 || s->max_early_data > 0)
+            && SSL_IS_TLS13(s)) {
+        if (!construct_stateful_ticket(s, pkt, age_add_u.age_add, tick_nonce)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+    } else if (!construct_stateless_ticket(s, pkt, age_add_u.age_add,
+                                           tick_nonce)) {
+        /* SSLfatal() already called */
+        goto err;
+    }
+
     if (SSL_IS_TLS13(s)) {
         if (!tls_construct_extensions(s, pkt,
                                       SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
@@ -4033,15 +4115,9 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
         s->next_ticket_nonce++;
         ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
     }
-    EVP_CIPHER_CTX_free(ctx);
-    HMAC_CTX_free(hctx);
-    OPENSSL_free(senc);
 
     return 1;
  err:
-    OPENSSL_free(senc);
-    EVP_CIPHER_CTX_free(ctx);
-    HMAC_CTX_free(hctx);
     return 0;
 }
 
diff --git a/test/handshake_helper.c b/test/handshake_helper.c
index 3ebf64d..c40a0e7 100644
--- a/test/handshake_helper.c
+++ b/test/handshake_helper.c
@@ -1396,7 +1396,8 @@ static int create_sctp_socks(int *ssock, int *csock)
 static HANDSHAKE_RESULT *do_handshake_internal(
     SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx,
     const SSL_TEST_CTX *test_ctx, const SSL_TEST_EXTRA_CONF *extra,
-    SSL_SESSION *session_in, SSL_SESSION **session_out)
+    SSL_SESSION *session_in, SSL_SESSION *serv_sess_in,
+    SSL_SESSION **session_out, SSL_SESSION **serv_sess_out)
 {
     PEER server, client;
     BIO *client_to_server = NULL, *server_to_client = NULL;
@@ -1452,10 +1453,14 @@ static HANDSHAKE_RESULT *do_handshake_internal(
 
     configure_handshake_ssl(server.ssl, client.ssl, extra);
     if (session_in != NULL) {
+        SSL_SESSION_get_id(serv_sess_in, &sess_id_len);
         /* In case we're testing resumption without tickets. */
-        if (!TEST_true(SSL_CTX_add_session(server_ctx, session_in))
+        if ((sess_id_len > 0
+                    && !TEST_true(SSL_CTX_add_session(server_ctx,
+                                                      serv_sess_in)))
                 || !TEST_true(SSL_set_session(client.ssl, session_in)))
             goto err;
+        sess_id_len = 0;
     }
 
     ret->result = SSL_TEST_INTERNAL_ERROR;
@@ -1657,6 +1662,16 @@ static HANDSHAKE_RESULT *do_handshake_internal(
 
     if (session_out != NULL)
         *session_out = SSL_get1_session(client.ssl);
+    if (serv_sess_out != NULL) {
+        SSL_SESSION *tmp = SSL_get_session(server.ssl);
+
+        /*
+         * We create a fresh copy that is not in the server session ctx linked
+         * list.
+         */
+        if (tmp != NULL)
+            *serv_sess_out = SSL_SESSION_dup(tmp);
+    }
 
     if (SSL_get_server_tmp_key(client.ssl, &tmp_key)) {
         ret->tmp_key_type = pkey_type(tmp_key);
@@ -1699,11 +1714,11 @@ HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
                                const SSL_TEST_CTX *test_ctx)
 {
     HANDSHAKE_RESULT *result;
-    SSL_SESSION *session = NULL;
+    SSL_SESSION *session = NULL, *serv_sess = NULL;
 
     result = do_handshake_internal(server_ctx, server2_ctx, client_ctx,
                                    test_ctx, &test_ctx->extra,
-                                   NULL, &session);
+                                   NULL, NULL, &session, &serv_sess);
     if (result == NULL
             || test_ctx->handshake_mode != SSL_TEST_HANDSHAKE_RESUME
             || result->result == SSL_TEST_INTERNAL_ERROR)
@@ -1718,8 +1733,9 @@ HANDSHAKE_RESULT *do_handshake(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
     /* We don't support SNI on second handshake yet, so server2_ctx is NULL. */
     result = do_handshake_internal(resume_server_ctx, NULL, resume_client_ctx,
                                    test_ctx, &test_ctx->resume_extra,
-                                   session, NULL);
+                                   session, serv_sess, NULL, NULL);
  end:
     SSL_SESSION_free(session);
+    SSL_SESSION_free(serv_sess);
     return result;
 }
diff --git a/test/ssl-tests/10-resumption.conf b/test/ssl-tests/10-resumption.conf
index 73955de..73de974 100644
--- a/test/ssl-tests/10-resumption.conf
+++ b/test/ssl-tests/10-resumption.conf
@@ -90,6 +90,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [0-resumption-client]
@@ -126,6 +127,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [1-resumption-client]
@@ -162,6 +164,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [2-resumption-client]
@@ -198,6 +201,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [3-resumption-client]
@@ -234,6 +238,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [4-resumption-client]
@@ -270,6 +275,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [5-resumption-client]
@@ -306,6 +312,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [6-resumption-client]
@@ -342,6 +349,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [7-resumption-client]
@@ -378,6 +386,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [8-resumption-client]
@@ -414,6 +423,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [9-resumption-client]
@@ -450,6 +460,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [10-resumption-client]
@@ -486,6 +497,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [11-resumption-client]
@@ -522,6 +534,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [12-resumption-client]
@@ -558,6 +571,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [13-resumption-client]
@@ -594,6 +608,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [14-resumption-client]
@@ -630,6 +645,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [15-resumption-client]
@@ -666,6 +682,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [16-resumption-client]
@@ -702,6 +719,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [17-resumption-client]
@@ -738,6 +756,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [18-resumption-client]
@@ -774,6 +793,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [19-resumption-client]
@@ -810,6 +830,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [20-resumption-client]
@@ -846,6 +867,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [21-resumption-client]
@@ -882,6 +904,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [22-resumption-client]
@@ -918,6 +941,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [23-resumption-client]
@@ -954,6 +978,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [24-resumption-client]
@@ -990,6 +1015,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [25-resumption-client]
@@ -1026,6 +1052,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [26-resumption-client]
@@ -1062,6 +1089,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [27-resumption-client]
@@ -1098,6 +1126,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [28-resumption-client]
@@ -1134,6 +1163,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.2
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [29-resumption-client]
@@ -1170,6 +1200,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [30-resumption-client]
@@ -1206,6 +1237,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = TLSv1.3
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [31-resumption-client]
diff --git a/test/ssl-tests/11-dtls_resumption.conf b/test/ssl-tests/11-dtls_resumption.conf
index ceed959..a981fa5 100644
--- a/test/ssl-tests/11-dtls_resumption.conf
+++ b/test/ssl-tests/11-dtls_resumption.conf
@@ -41,6 +41,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [0-resumption-client]
@@ -78,6 +79,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [1-resumption-client]
@@ -115,6 +117,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1.2
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [2-resumption-client]
@@ -152,6 +155,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1.2
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [3-resumption-client]
@@ -189,6 +193,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [4-resumption-client]
@@ -226,6 +231,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [5-resumption-client]
@@ -263,6 +269,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1.2
+Options = SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [6-resumption-client]
@@ -300,6 +307,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
 CipherString = DEFAULT
 MaxProtocol = DTLSv1.2
+Options = -SessionTicket
 PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [7-resumption-client]
diff --git a/test/ssl-tests/protocol_version.pm b/test/ssl-tests/protocol_version.pm
index 215f9e8..943719e 100644
--- a/test/ssl-tests/protocol_version.pm
+++ b/test/ssl-tests/protocol_version.pm
@@ -210,6 +210,7 @@ sub generate_resumption_tests {
                         },
                         "resume_server" => {
                             "MaxProtocol" => $protocols[$resume_protocol],
+                            "Options" => $ticket,
                         },
                         "test" => {
                             "ExpectedProtocol" => $protocols[$resume_protocol],
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 9b0237e..61619a3 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -1062,7 +1062,8 @@ static int execute_test_session(int maxprot, int use_int_cache,
     sess2 = NULL;
 
     SSL_CTX_set_max_proto_version(sctx, maxprot);
-    SSL_CTX_set_options(sctx, SSL_OP_NO_TICKET);
+    if (maxprot == TLS1_2_VERSION)
+        SSL_CTX_set_options(sctx, SSL_OP_NO_TICKET);
     new_called = remove_called = get_called = 0;
     if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl1, &clientssl1,
                                       NULL, NULL))


More information about the openssl-commits mailing list