1
0
Эх сурвалжийг харах

Merge pull request #5593 from rizlik/ticket_nonce_size

tls13: support ticketNonce with size bigger than MAX_TICKET_NONCE_SZ
Sean Parkinson 1 жил өмнө
parent
commit
754d274d8c
10 өөрчлөгдсөн 854 нэмэгдсэн , 34 устгасан
  1. 9 0
      CMakeLists.txt
  2. 11 0
      configure.ac
  3. 25 4
      src/internal.c
  4. 2 2
      src/sniffer.c
  5. 310 6
      src/ssl.c
  6. 54 5
      src/tls13.c
  7. 286 1
      tests/api.c
  8. 96 0
      wolfcrypt/src/kdf.c
  9. 54 16
      wolfssl/internal.h
  10. 7 0
      wolfssl/wolfcrypt/kdf.h

+ 9 - 0
CMakeLists.txt

@@ -1340,6 +1340,15 @@ if(WOLFSSL_SESSION_TICKET)
         "-DHAVE_SESSION_TICKET")
 endif()
 
+add_option("WOLFSSL_TICKET_NONCE_MALLOC"
+    "Enable dynamic allocation of ticket nonces (default: disabled)"
+    "no" "yes;no")
+
+if(WOLFSSL_TICKET_NONCE_MALLOC)
+    list(APPEND WOLFSSL_DEFINITIONS
+        "-DWOLFSSL_TICKET_NONCE_MALLOC")
+endif()
+
 # Extended master secret extension
 add_option("WOLFSSL_EXTENDED_MASTER"
     "Enable Extended Master Secret (default: enabled)"

+ 11 - 0
configure.ac

@@ -4913,6 +4913,17 @@ then
     AM_CFLAGS="$AM_CFLAGS -DHAVE_TLS_EXTENSIONS -DHAVE_SESSION_TICKET"
 fi
 
+AC_ARG_ENABLE([ticket-nonce-malloc],
+    [AS_HELP_STRING([--enable-ticket-nonce-malloc], [Enable dynamic allocation of ticket nonces (default: disabled)])],
+    [ ENABLED_TICKET_NONCE_MALLOC=$enableval ],
+    [ ENABLED_TICKET_NONCE_MALLOC=no ]
+    )
+
+if test "$ENABLED_TICKET_NONCE_MALLOC" = "yes"
+then
+    AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TICKET_NONCE_MALLOC"
+fi
+
 # Extended Master Secret Extension
 AC_ARG_ENABLE([extended-master],
     [AS_HELP_STRING([--enable-extended-master],[Enable Extended Master Secret (default: enabled)])],

+ 25 - 4
src/internal.c

@@ -33982,8 +33982,14 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
         #endif
             /* Resumption master secret. */
             XMEMCPY(it->msecret, ssl->session->masterSecret, SECRET_LEN);
-            XMEMCPY(&it->ticketNonce, &ssl->session->ticketNonce,
-                                                           sizeof(TicketNonce));
+            if (ssl->session->ticketNonce.len > MAX_TICKET_NONCE_STATIC_SZ) {
+                WOLFSSL_MSG("Bad ticket nonce value");
+                ret = BAD_TICKET_MSG_SZ;
+                goto error;
+            }
+            XMEMCPY(it->ticketNonce, ssl->session->ticketNonce.data,
+                ssl->session->ticketNonce.len);
+            it->ticketNonceLen = ssl->session->ticketNonce.len;
 #endif
         }
 
@@ -34259,8 +34265,23 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
     #endif
                 /* Resumption master secret. */
                 XMEMCPY(ssl->session->masterSecret, it->msecret, SECRET_LEN);
-                XMEMCPY(&ssl->session->ticketNonce, &it->ticketNonce,
-                                                           sizeof(TicketNonce));
+                if (it->ticketNonceLen > MAX_TICKET_NONCE_STATIC_SZ) {
+                    WOLFSSL_MSG("Unsupported ticketNonce len in ticket");
+                    return BAD_TICKET_ENCRYPT;
+                }
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+                if (ssl->session->ticketNonce.data
+                       != ssl->session->ticketNonce.dataStatic) {
+                    XFREE(ssl->session->ticketNonce.data, ssl->heap,
+                        DYNAMIC_TYPE_SESSION_TICK);
+                    ssl->session->ticketNonce.data =
+                        ssl->session->ticketNonce.dataStatic;
+                }
+#endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */
+                XMEMCPY(ssl->session->ticketNonce.data, it->ticketNonce,
+                    it->ticketNonceLen);
+                ssl->session->ticketNonce.len = it->ticketNonceLen;
                 ato16(it->namedGroup, &ssl->session->namedGroup);
 #endif
             }

+ 2 - 2
src/sniffer.c

@@ -3292,7 +3292,7 @@ static int ProcessSessionTicket(const byte* input, int* sslBytes,
 
         /* ticket nonce */
         len = input[0];
-        if (len > MAX_TICKET_NONCE_SZ) {
+        if (len > MAX_TICKET_NONCE_STATIC_SZ) {
             SetError(BAD_INPUT_STR, error, session, FATAL_ERROR_STATE);
             return -1;
         }
@@ -3302,7 +3302,7 @@ static int ProcessSessionTicket(const byte* input, int* sslBytes,
         /* store nonce in server for DeriveResumptionPSK */
         session->sslServer->session->ticketNonce.len = len;
         if (len > 0)
-            XMEMCPY(&session->sslServer->session->ticketNonce.data, input, len);
+            XMEMCPY(session->sslServer->session->ticketNonce.data, input, len);
     #endif
         input += len;
         *sslBytes -= len;

+ 310 - 6
src/ssl.c

@@ -13555,6 +13555,40 @@ static int SslSessionCacheOff(const WOLFSSL* ssl, const WOLFSSL_SESSION* session
                 ;
 }
 
+#if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) &&                  \
+    defined(WOLFSSL_TICKET_NONCE_MALLOC) && \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+/**
+ * SessionTicketNoncePrealloc() - prealloc a buffer for ticket nonces
+ * @output: [in] pointer to WOLFSSL_SESSION object that will soon be a
+ * destination of a session duplication
+ * @buf: [out] address of the preallocated buf
+ * @len: [out] len of the preallocated buf
+ *
+ * prealloc a buffer that will likely suffice to contain a ticket nonce. It's
+ * used when copying session under lock, when syscalls need to be avoided. If
+ * output already has a dynamic buffer, it's reused.
+ */
+static int SessionTicketNoncePrealloc(byte** buf, byte* len, void *heap)
+{
+    (void)heap;
+
+    *buf = (byte*)XMALLOC(PREALLOC_SESSION_TICKET_NONCE_LEN, heap,
+        DYNAMIC_TYPE_SESSION_TICK);
+    if (*buf == NULL) {
+        WOLFSSL_MSG("Failed to preallocate ticket nonce buffer");
+        *len = 0;
+        return WOLFSSL_FAILURE;
+    }
+
+    *len = PREALLOC_SESSION_TICKET_NONCE_LEN;
+    return 0;
+}
+#endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 */
+
+static int wolfSSL_DupSessionEx(const WOLFSSL_SESSION* input,
+    WOLFSSL_SESSION* output, int avoidSysCalls, byte* ticketNonceBuf,
+    byte* ticketNonceLen, byte* preallocUsed);
 
 int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output)
 {
@@ -13571,6 +13605,11 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output)
 #else
     byte*        tmpTicket = NULL;
 #endif
+#ifdef WOLFSSL_TLS13
+    byte *preallocNonce = NULL;
+    byte preallocNonceLen = 0;
+    byte preallocNonceUsed = 0;
+#endif /* WOLFSSL_TLS13 */
     byte         tmpBufSet = 0;
 #endif
 #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
@@ -13678,6 +13717,30 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output)
     }
 #endif
 
+#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) &&                  \
+    defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    if (output->ticketNonce.data != output->ticketNonce.dataStatic) {
+        XFREE(output->ticketNonce.data, output->heap,
+            DYNAMIC_TYPE_SESSION_TICK);
+        output->ticketNonce.data = output->ticketNonce.dataStatic;
+        output->ticketNonce.len = 0;
+    }
+    error = SessionTicketNoncePrealloc(&preallocNonce, &preallocNonceLen,
+        output->heap);
+    if (error != 0) {
+        if (tmpBufSet) {
+            output->ticket = output->staticTicket;
+            output->ticketLenAlloc = 0;
+        }
+#ifdef WOLFSSL_SMALL_STACK
+        if (tmpTicket != NULL)
+            XFREE(tmpTicket, output->heap, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+        return WOLFSSL_FAILURE;
+    }
+#endif /* WOLFSSL_TLS13 && HAVE_SESSION_TICKET*/
+
     /* lock row */
     sessRow = &SessionCache[row];
     if (SESSION_ROW_LOCK(sessRow) != 0) {
@@ -13687,6 +13750,10 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output)
             output->ticket = output->staticTicket;
             output->ticketLenAlloc = 0;
         }
+#ifdef WOLFSSL_TLS13
+        if (preallocNonce != NULL)
+            XFREE(preallocNonce, output->heap, DYNAMIC_TYPE_SESSION_TICK);
+#endif /* WOLFSSL_TLS13 */
 #ifdef WOLFSSL_SMALL_STACK
         if (tmpTicket != NULL)
             XFREE(tmpTicket, output->heap, DYNAMIC_TYPE_TMP_BUFFER);
@@ -13731,7 +13798,13 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output)
             sess->peer = NULL;
         }
 #endif
+#if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13)
+        error = wolfSSL_DupSessionEx(sess, output, 1,
+            preallocNonce, &preallocNonceLen, &preallocNonceUsed);
+#else
         error = wolfSSL_DupSession(sess, output, 1);
+#endif /* WOLFSSL_TSL */
+
 #ifdef HAVE_EX_DATA
         output->ownExData = 0; /* Session cache owns external data */
 #endif
@@ -13780,6 +13853,45 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output)
     if (tmpTicket != NULL)
         XFREE(tmpTicket, output->heap, DYNAMIC_TYPE_TMP_BUFFER);
 #endif
+
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&          \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    if (error == WOLFSSL_SUCCESS && preallocNonceUsed) {
+        if (preallocNonceLen < PREALLOC_SESSION_TICKET_NONCE_LEN) {
+            /* buffer bigger than needed */
+#ifndef XREALLOC
+            output->ticketNonce.data = (byte*)XMALLOC(preallocNonceLen,
+                output->heap, DYNAMIC_TYPE_SESSION_TICK);
+            if (output->ticketNonce.data != NULL)
+                XMEMCPY(output->ticketNonce.data, preallocNonce,
+                    preallocNonceLen);
+            XFREE(preallocNonce, output->heap, DYNAMIC_TYPE_SESSION_TICK);
+            preallocNonce = NULL;
+#else
+            output->ticketNonce.data = XREALLOC(preallocNonce,
+                preallocNonceLen, output->heap, DYNAMIC_TYPE_SESSION_TICK);
+            if (output->ticketNonce.data != NULL) {
+                /* don't free the reallocated pointer */
+                preallocNonce = NULL;
+            }
+#endif /* !XREALLOC */
+            if (output->ticketNonce.data == NULL) {
+                output->ticketNonce.data = output->ticketNonce.dataStatic;
+                output->ticketNonce.len = 0;
+                error = WOLFSSL_FAILURE;
+                /* preallocNonce will be free'd after the if */
+            }
+        }
+        else {
+            output->ticketNonce.data = preallocNonce;
+            output->ticketNonce.len = preallocNonceLen;
+            preallocNonce = NULL;
+        }
+    }
+    if (preallocNonce != NULL)
+        XFREE(preallocNonce, output->heap, DYNAMIC_TYPE_SESSION_TICK);
+#endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/
+
 #endif
 
 #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
@@ -14091,7 +14203,14 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
     byte   ticBuffUsed = 0;
     byte*  ticBuff = NULL;
     int    ticLen  = 0;
-#endif
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&          \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    byte *preallocNonce = NULL;
+    byte preallocNonceLen = 0;
+    byte preallocNonceUsed = 0;
+    byte *toFree = NULL;
+#endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC */
+#endif /* HAVE_SESSION_TICKET */
     int ret = 0;
     int row;
     int i;
@@ -14113,7 +14232,6 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
         return MEMORY_E;
     }
 
-    /* Find a position for the new session in cache and use that */
 #ifdef HAVE_SESSION_TICKET
     ticLen = addSession->ticketLen;
     /* Alloc Memory here to avoid syscalls during lock */
@@ -14124,13 +14242,35 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
             return MEMORY_E;
         }
     }
-#endif
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&          \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    if (addSession->ticketNonce.data != addSession->ticketNonce.dataStatic) {
+        /* use the AddSession->heap even if the buffer maybe saved in
+         * CachedSession objects. CachedSession heap and AddSession heap should
+         * be the same */
+        preallocNonce = (byte*)XMALLOC(addSession->ticketNonce.len,
+            addSession->heap, DYNAMIC_TYPE_SESSION_TICK);
+        if (preallocNonce == NULL) {
+            if (ticBuff != NULL)
+                XFREE(ticBuff, addSession->heap, DYNAMIC_TYPE_SESSION_TICK);
+            return MEMORY_E;
+        }
+        preallocNonceLen = addSession->ticketNonce.len;
+    }
+#endif /* WOLFSSL_TLS13 && WOLFSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3) */
+#endif /* HAVE_SESSION_TICKET */
+
+    /* Find a position for the new session in cache and use that */
     /* Use the session object in the cache for external cache if required */
     row = (int)(HashObject(id, ID_LEN, &ret) % SESSION_ROWS);
     if (ret != 0) {
         WOLFSSL_MSG("Hash session failed");
     #ifdef HAVE_SESSION_TICKET
         XFREE(ticBuff, NULL, DYNAMIC_TYPE_SESSION_TICK);
+    #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKE_NONCE_MALLOC)
+        if (preallocNonce != NULL)
+            XFREE(preallocNonce, addSession->heap, DYNAMIC_TYPE_SESSION_TICK);
+    #endif
     #endif
         return ret;
     }
@@ -14139,6 +14279,10 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
     if (SESSION_ROW_LOCK(sessRow) != 0) {
     #ifdef HAVE_SESSION_TICKET
         XFREE(ticBuff, NULL, DYNAMIC_TYPE_SESSION_TICK);
+    #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKE_NONCE_MALLOC)
+        if (preallocNonce != NULL)
+            XFREE(preallocNonce, addSession->heap, DYNAMIC_TYPE_SESSION_TICK);
+    #endif
     #endif
         WOLFSSL_MSG("Session row lock failed");
         return BAD_MUTEX_E;
@@ -14193,6 +14337,19 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
         cacheSession->ticket = ticBuff;
         cacheSession->ticketLenAlloc = (word16) ticLen;
     }
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&          \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    /* cache entry never used */
+    if (cacheSession->ticketNonce.data == NULL)
+        cacheSession->ticketNonce.data = cacheSession->ticketNonce.dataStatic;
+
+    if (cacheSession->ticketNonce.data !=
+            cacheSession->ticketNonce.dataStatic) {
+        toFree = cacheSession->ticketNonce.data;
+        cacheSession->ticketNonce.data = cacheSession->ticketNonce.dataStatic;
+        cacheSession->ticketNonce.len = 0;
+    }
+#endif /* WOFLSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/
 #endif
 #ifdef SESSION_CERTS
     if (overwrite &&
@@ -14206,7 +14363,15 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
 #endif /* SESSION_CERTS */
     cacheSession->heap = NULL;
     /* Copy data into the cache object */
+#if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) &&                  \
+    defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                   \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    ret = wolfSSL_DupSessionEx(addSession, cacheSession, 1, preallocNonce,
+        &preallocNonceLen, &preallocNonceUsed) == WOLFSSL_FAILURE;
+#else
     ret = wolfSSL_DupSession(addSession, cacheSession, 1) == WOLFSSL_FAILURE;
+#endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC
+          && FIPS_VERSION_GE(5,3)*/
 
     if (ret == 0) {
         /* Increment the totalCount and the nextIdx */
@@ -14226,6 +14391,17 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
             cacheSession->rem_sess_cb = ctx->rem_sess_cb;
         }
 #endif
+#if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) &&                  \
+    defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+        if (preallocNonce != NULL && preallocNonceUsed) {
+            cacheSession->ticketNonce.data = preallocNonce;
+            cacheSession->ticketNonce.len = preallocNonceLen;
+            preallocNonce = NULL;
+            preallocNonceLen = 0;
+        }
+#endif /* HAVE_SESSION_TICKET && WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC
+        * && FIPS_VERSION_GE(5,3)*/
     }
 #ifdef HAVE_SESSION_TICKET
     else if (ticBuffUsed) {
@@ -14253,6 +14429,13 @@ int AddSessionToCache(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* addSession,
         XFREE(ticBuff, NULL, DYNAMIC_TYPE_SESSION_TICK);
     if (cacheTicBuff != NULL)
         XFREE(cacheTicBuff, NULL, DYNAMIC_TYPE_SESSION_TICK);
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&         \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    if (preallocNonce != NULL)
+        XFREE(preallocNonce, addSession->heap, DYNAMIC_TYPE_SESSION_TICK);
+    if (toFree != NULL)
+        XFREE(toFree, addSession->heap, DYNAMIC_TYPE_SESSION_TICK);
+#endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/
 #endif
 
 #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
@@ -20026,6 +20209,10 @@ WOLFSSL_SESSION* wolfSSL_NewSession(void* heap)
 #endif
     #ifdef HAVE_SESSION_TICKET
         ret->ticket = ret->staticTicket;
+        #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&  \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+        ret->ticketNonce.data = ret->ticketNonce.dataStatic;
+        #endif
     #endif
 #ifdef HAVE_STUNNEL
         /* stunnel has this funny mechanism of storing the "is_authenticated"
@@ -20088,11 +20275,16 @@ int wolfSSL_SESSION_up_ref(WOLFSSL_SESSION* session)
  *                      sessions from cache. When a cache row is locked, we
  *                      don't want to block other threads with long running
  *                      system calls.
+ * @param ticketNonceBuf If not null and @avoidSysCalls is true, the copy of the
+ *                      ticketNonce will happen in this pre allocated buffer
+ * @param ticketNonceLen @ticketNonceBuf len as input, used length on output
+ * @param ticketNonceUsed if @ticketNonceBuf was used to copy the ticket noncet
  * @return              WOLFSSL_SUCCESS on success
  *                      WOLFSSL_FAILURE on failure
  */
-int wolfSSL_DupSession(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output,
-        int avoidSysCalls)
+static int wolfSSL_DupSessionEx(const WOLFSSL_SESSION* input,
+    WOLFSSL_SESSION* output, int avoidSysCalls, byte* ticketNonceBuf,
+    byte* ticketNonceLen, byte* preallocUsed)
 {
 #ifdef HAVE_SESSION_TICKET
     int   ticLenAlloc = 0;
@@ -20102,6 +20294,9 @@ int wolfSSL_DupSession(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output,
     int ret = WOLFSSL_SUCCESS;
 
     (void)avoidSysCalls;
+    (void)ticketNonceBuf;
+    (void)ticketNonceLen;
+    (void)preallocUsed;
 
     input = ClientSessionToSession(input);
     output = ClientSessionToSession(output);
@@ -20116,7 +20311,27 @@ int wolfSSL_DupSession(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output,
         ticBuff = output->ticket;
         ticLenAlloc = output->ticketLenAlloc;
     }
-#endif
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&          \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+        /* free the data, it would be better to re-use the buffer but this
+         * maintain the code simpler. A smart allocator should re-use the free'd
+         * buffer in the next malloc without much performance penalties. */
+    if (output->ticketNonce.data != output->ticketNonce.dataStatic) {
+
+        /*  Callers that avoid syscall should never calls this with
+         * output->tickeNonce.data being a dynamic buffer.*/
+        if (avoidSysCalls) {
+            WOLFSSL_MSG("can't avoid syscalls with dynamic TicketNonce buffer");
+            return WOLFSSL_FAILURE;
+        }
+
+        XFREE(output->ticketNonce.data,
+            output->heap, DYNAMIC_TYPE_SESSION_TICK);
+        output->ticketNonce.data = output->ticketNonce.dataStatic;
+        output->ticketNonce.len = 0;
+    }
+#endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/
+#endif /* HAVE_SESSION_TICKET */
 
 #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
     if (output->peer != NULL) {
@@ -20132,6 +20347,12 @@ int wolfSSL_DupSession(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output,
     XMEMCPY((byte*)output + copyOffset, (byte*)input + copyOffset,
             sizeof(WOLFSSL_SESSION) - copyOffset);
 
+#if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) &&                  \
+    defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    /* fix pointer to static after the copy  */
+    output->ticketNonce.data = output->ticketNonce.dataStatic;
+#endif
     /* Set sane values for copy */
 #ifndef NO_SESSION_CACHE
     if (output->type != WOLFSSL_SESSION_TYPE_CACHE)
@@ -20225,10 +20446,74 @@ int wolfSSL_DupSession(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output,
         }
     }
     ticBuff = NULL;
+
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&          \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    if (preallocUsed != NULL)
+        *preallocUsed = 0;
+
+    if (input->ticketNonce.len > MAX_TICKET_NONCE_STATIC_SZ &&
+        ret == WOLFSSL_SUCCESS) {
+        /* TicketNonce does not fit in the static buffer */
+        if (!avoidSysCalls) {
+            output->ticketNonce.data = (byte*)XMALLOC(input->ticketNonce.len,
+                output->heap, DYNAMIC_TYPE_SESSION_TICK);
+
+            if (output->ticketNonce.data == NULL) {
+                WOLFSSL_MSG("Failed to allocate space for ticket nonce");
+                output->ticketNonce.data = output->ticketNonce.dataStatic;
+                output->ticketNonce.len = 0;
+                ret = WOLFSSL_FAILURE;
+            }
+            else {
+                output->ticketNonce.len = input->ticketNonce.len;
+                XMEMCPY(output->ticketNonce.data, input->ticketNonce.data,
+                    input->ticketNonce.len);
+                ret = WOLFSSL_SUCCESS;
+            }
+        }
+        /* we can't do syscalls. Use prealloc buffers if provided from the
+         * caller. */
+        else if (ticketNonceBuf != NULL &&
+                 *ticketNonceLen >= input->ticketNonce.len) {
+            XMEMCPY(ticketNonceBuf, input->ticketNonce.data,
+                input->ticketNonce.len);
+            *ticketNonceLen = input->ticketNonce.len;
+            if (preallocUsed != NULL)
+                *preallocUsed = 1;
+            ret = WOLFSSL_SUCCESS;
+        }
+        else {
+            WOLFSSL_MSG("TicketNonce bigger than static buffer, and we can't "
+                        "do syscalls");
+            ret = WOLFSSL_FAILURE;
+        }
+    }
+#endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/
+
 #endif /* HAVE_SESSION_TICKET */
     return ret;
 }
 
+/**
+ * Deep copy the contents from input to output.
+ * @param input         The source of the copy.
+ * @param output        The destination of the copy.
+ * @param avoidSysCalls If true, then system calls will be avoided or an error
+ *                      will be returned if it is not possible to proceed
+ *                      without a system call. This is useful for fetching
+ *                      sessions from cache. When a cache row is locked, we
+ *                      don't want to block other threads with long running
+ *                      system calls.
+ * @return              WOLFSSL_SUCCESS on success
+ *                      WOLFSSL_FAILURE on failure
+ */
+int wolfSSL_DupSession(const WOLFSSL_SESSION* input, WOLFSSL_SESSION* output,
+        int avoidSysCalls)
+{
+    return wolfSSL_DupSessionEx(input, output, avoidSysCalls, NULL, NULL, NULL);
+}
+
 WOLFSSL_SESSION* wolfSSL_SESSION_dup(WOLFSSL_SESSION* session)
 {
 #ifdef HAVE_EXT_CACHE
@@ -20318,6 +20603,13 @@ void wolfSSL_FreeSession(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* session)
     if (session->ticketLenAlloc > 0) {
         XFREE(session->ticket, session->heap, DYNAMIC_TYPE_SESSION_TICK);
     }
+#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&          \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    if (session->ticketNonce.data != session->ticketNonce.dataStatic) {
+        XFREE(session->ticketNonce.data, session->heap,
+            DYNAMIC_TYPE_SESSION_TICK);
+    }
+#endif /* WOLFSSL_TLS13 && WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3)*/
 #endif
 
 #ifdef HAVE_EX_DATA_CLEANUP_HOOKS
@@ -25766,7 +26058,19 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess,
         ret = BUFFER_ERROR;
         goto end;
     }
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                     \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    ret = SessionTicketNoncePopulate(s, data + idx, s->ticketNonce.len);
+    if (ret != 0)
+        goto end;
+#else
+    if (s->ticketNonce.len > MAX_TICKET_NONCE_STATIC_SZ) {
+        ret = BUFFER_ERROR;
+        goto end;
+    }
     XMEMCPY(s->ticketNonce.data, data + idx, s->ticketNonce.len);
+#endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */
+
     idx += s->ticketNonce.len;
 #endif
 #ifdef WOLFSSL_EARLY_DATA

+ 54 - 5
src/tls13.c

@@ -1036,6 +1036,7 @@ int DeriveMasterSecret(WOLFSSL* ssl)
 #define RESUMPTION_LABEL_SZ         10
 /* Resumption label for generating PSK associated with the ticket. */
 static const byte resumptionLabel[RESUMPTION_LABEL_SZ+1] = "resumption";
+
 /* Derive the PSK associated with the ticket.
  *
  * ssl       The SSL/TLS object.
@@ -1078,10 +1079,17 @@ int DeriveResumptionPSK(WOLFSSL* ssl, byte* nonce, byte nonceLen, byte* secret)
     }
 
     PRIVATE_KEY_UNLOCK();
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    ret = wc_Tls13_HKDF_Expand_Label_Alloc(secret, ssl->specs.hash_size,
+        ssl->session->masterSecret, ssl->specs.hash_size, protocol, protocolLen,
+        resumptionLabel, RESUMPTION_LABEL_SZ, nonce, nonceLen, digestAlg,
+        ssl->heap);
+#else
     ret = wc_Tls13_HKDF_Expand_Label(secret, ssl->specs.hash_size,
-                             ssl->session->masterSecret, ssl->specs.hash_size,
-                             protocol, protocolLen, resumptionLabel,
-                             RESUMPTION_LABEL_SZ, nonce, nonceLen, digestAlg);
+        ssl->session->masterSecret, ssl->specs.hash_size, protocol, protocolLen,
+        resumptionLabel, RESUMPTION_LABEL_SZ, nonce, nonceLen, digestAlg);
+#endif /* !defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3) */
     PRIVATE_KEY_LOCK();
     return ret;
 }
@@ -9181,6 +9189,31 @@ static int DoTls13EndOfEarlyData(WOLFSSL* ssl, const byte* input,
 #endif /* !NO_WOLFSSL_SERVER */
 #endif /* WOLFSSL_EARLY_DATA */
 
+#if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_NONCE_MALLOC) &&    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+int SessionTicketNoncePopulate(WOLFSSL_SESSION *session, const byte *nonce,
+    byte len)
+{
+    if (session->ticketNonce.data
+            != session->ticketNonce.dataStatic) {
+         XFREE(session->ticketNonce.data, heap,
+             DYNAMIC_TYPE_SESSION_TICK);
+         session->ticketNonce.data = session->ticketNonce.dataStatic;
+         session->ticketNonce.len = 0;
+    }
+
+    if (len > MAX_TICKET_NONCE_STATIC_SZ) {
+        WOLFSSL_MSG("Using dynamic nonce buffer");
+        session->ticketNonce.data = (byte*)XMALLOC(len,
+            session->heap, DYNAMIC_TYPE_SESSION_TICK);
+        if (session->ticketNonce.data == NULL)
+            return MEMORY_ERROR;
+    }
+    XMEMCPY(session->ticketNonce.data, nonce, len);
+    session->ticketNonce.len = len;
+    return 0;
+}
+#endif
 #ifndef NO_WOLFSSL_CLIENT
 /* Handle a New Session Ticket handshake message.
  * Message contains the information required to perform resumption.
@@ -9232,11 +9265,14 @@ static int DoTls13NewSessionTicket(WOLFSSL* ssl, const byte* input,
     if ((*inOutIdx - begin) + 1 > size)
         return BUFFER_ERROR;
     nonceLength = input[*inOutIdx];
-    if (nonceLength > MAX_TICKET_NONCE_SZ) {
+#if !defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                   \
+    (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3))
+    if (nonceLength > MAX_TICKET_NONCE_STATIC_SZ) {
         WOLFSSL_MSG("Nonce length not supported");
         WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
         return INVALID_PARAMETER;
     }
+#endif /* WOLFSSL_TICKET_NONCE_MALLOC && FIPS_VERSION_GE(5,3) */
     *inOutIdx += 1;
     if ((*inOutIdx - begin) + nonceLength > size)
         return BUFFER_ERROR;
@@ -9268,9 +9304,22 @@ static int DoTls13NewSessionTicket(WOLFSSL* ssl, const byte* input,
     #ifdef WOLFSSL_EARLY_DATA
     ssl->session->maxEarlyDataSz  = ssl->options.maxEarlyDataSz;
     #endif
+
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    ret = SessionTicketNoncePopulate(ssl->session, nonce, nonceLength);
+    if (ret != 0)
+        return ret;
+#else
     ssl->session->ticketNonce.len = nonceLength;
+    if (nonceLength > MAX_TICKET_NONCE_STATIC_SZ) {
+        ret = BUFFER_ERROR;
+        return ret;
+    }
     if (nonceLength > 0)
-        XMEMCPY(&ssl->session->ticketNonce.data, nonce, nonceLength);
+        XMEMCPY(ssl->session->ticketNonce.data, nonce, nonceLength);
+#endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */
+
     ssl->session->namedGroup      = ssl->namedGroup;
 
     if ((*inOutIdx - begin) + EXTS_SZ > size)

+ 286 - 1
tests/api.c

@@ -58570,8 +58570,289 @@ static int test_wolfSSL_DTLS_fragment_buckets(void)
 
     return 0;
 }
+
 #endif
 
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) && defined(HAVE_SESSION_TICKET)       \
+    && defined(WOLFSSL_TLS13) &&                                               \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+
+#define TEST_NONCE_BUF_SZ 64 * 1024
+struct test_nonce_ctx
+{
+    byte c_buff[TEST_NONCE_BUF_SZ];
+    int c_len;
+    byte s_buff[TEST_NONCE_BUF_SZ];
+    int s_len;
+};
+
+static int test_ticket_nonce_write_cb(WOLFSSL *ssl,
+    char *data, int sz, void *ctx)
+{
+    struct test_nonce_ctx *test_ctx;
+    byte *buf;
+    int *len;
+
+    test_ctx = (struct test_nonce_ctx*)ctx;
+
+    if (ssl->options.side == WOLFSSL_SERVER_END) {
+        buf = test_ctx->c_buff;
+        len = &test_ctx->c_len;
+    }
+    else {
+        buf = test_ctx->s_buff;
+        len = &test_ctx->s_len;
+    }
+
+    if ((unsigned)(*len + sz) > TEST_NONCE_BUF_SZ)
+            return WOLFSSL_CBIO_ERR_WANT_READ;
+
+    XMEMCPY(buf + *len, data, sz);
+    *len += sz;
+
+    return sz;
+}
+
+static int test_ticket_nonce_read_cb(WOLFSSL *ssl,
+    char *data, int sz, void *ctx)
+{
+    struct test_nonce_ctx *test_ctx;
+    int read_sz;
+    byte *buf;
+    int *len;
+
+    test_ctx = (struct test_nonce_ctx*)ctx;
+
+    if (ssl->options.side == WOLFSSL_SERVER_END) {
+        buf = test_ctx->s_buff;
+        len = &test_ctx->s_len;
+    }
+    else {
+        buf = test_ctx->c_buff;
+        len = &test_ctx->c_len;
+    }
+
+    if (*len == 0)
+        return WOLFSSL_CBIO_ERR_WANT_READ;
+
+    read_sz = sz < *len ? sz : *len;
+
+    XMEMCPY(data, buf, read_sz);
+    XMEMMOVE(buf, buf + read_sz, *len - read_sz);
+
+    *len -= read_sz;
+
+    return read_sz;
+}
+
+static int send_new_session_ticket(WOLFSSL *ssl, byte nonceLength, byte filler)
+{
+    struct test_nonce_ctx *test_ctx;
+    byte buf[2048];
+    int idx, sz;
+    word32 tmp;
+    int ret;
+
+    idx = 5; /* space for record header */
+
+    buf[idx] = session_ticket; /* type */
+    idx++;
+
+    tmp = OPAQUE32_LEN +
+        OPAQUE32_LEN +
+        OPAQUE8_LEN + nonceLength +
+        OPAQUE16_LEN + OPAQUE8_LEN + OPAQUE16_LEN;
+    c32to24(tmp, buf + idx);
+    idx += OPAQUE24_LEN;
+
+    c32toa((word32)12345, buf+idx); /* lifetime */
+    idx += OPAQUE32_LEN;
+    c32toa((word32)12345, buf+idx); /* add */
+    idx += OPAQUE32_LEN;
+    buf[idx] = nonceLength; /* nonce length */
+    idx++;
+    XMEMSET(&buf[idx], filler, nonceLength); /* nonce */
+    idx += nonceLength;
+    tmp = 1; /* ticket len */
+    c16toa((word16)tmp, buf+idx);
+    idx += 2;
+    buf[idx] = 0xFF; /* ticket */
+    idx++;
+    tmp = 0; /* ext len */
+    c16toa((word16)tmp, buf+idx);
+    idx += 2;
+
+    sz = BuildTls13Message(ssl, buf, 2048, buf+5, idx - 5,
+        handshake, 0, 0, 0);
+    test_ctx = (struct test_nonce_ctx*)wolfSSL_GetIOWriteCtx(ssl);
+    ret = test_ticket_nonce_write_cb(ssl, (char*)buf, sz, test_ctx);
+    return !(ret == sz);
+}
+
+static int test_ticket_nonce_check(WOLFSSL_SESSION *sess, byte len)
+{
+    int i;
+
+    if (sess == NULL)
+        return -1;
+
+    if (sess->ticketNonce.len != len)
+        return -1;
+
+    for (i = 0; i < len; i++)
+        if (sess->ticketNonce.data[i] != len)
+            return -1;
+
+    return 0;
+}
+
+static int test_ticket_nonce_malloc_do(WOLFSSL *ssl_s, WOLFSSL *ssl_c, byte len)
+{
+    char *buf[1024];
+    int ret;
+
+    ret = send_new_session_ticket(ssl_s, len, len);
+    if (ret != 0)
+        return -1;
+
+    ret = wolfSSL_recv(ssl_c, buf, 1024, 0);
+    if (ret != WOLFSSL_SUCCESS && ssl_c->error != WANT_READ)
+        return -1;
+
+    return test_ticket_nonce_check(ssl_c->session, len);
+}
+
+static int test_ticket_nonce_cache(WOLFSSL *ssl_s, WOLFSSL *ssl_c, byte len)
+{
+    WOLFSSL_SESSION *sess, *cached;
+    WOLFSSL_CTX *ctx;
+    int ret;
+
+    ctx = ssl_c->ctx;
+
+    ret = test_ticket_nonce_malloc_do(ssl_s, ssl_c, len);
+    if (ret != 0)
+        return -1;
+    sess = wolfSSL_get1_session(ssl_c);
+    if (sess == NULL)
+        return -1;
+
+    ret = AddSessionToCache(ctx, sess, sess->sessionID, sess->sessionIDSz,
+         NULL, ssl_c->options.side, 1,NULL);
+    if (ret != 0)
+        return -1;
+
+    cached = wolfSSL_SESSION_new();
+    if (cached == NULL)
+        return -1;
+
+    ret = wolfSSL_GetSessionFromCache(ssl_c, cached);
+    if (ret != WOLFSSL_SUCCESS)
+        return -1;
+
+    ret = test_ticket_nonce_check(cached, len);
+    if (ret != 0)
+        return -1;
+
+    wolfSSL_SESSION_free(cached);
+    wolfSSL_SESSION_free(sess);
+
+    return 0;
+}
+
+static int test_ticket_nonce_malloc(void)
+{
+    struct test_nonce_ctx test_ctx = { 0 };
+    WOLFSSL_CTX *ctx_c, *ctx_s;
+    byte small, medium, big;
+    WOLFSSL *ssl_c, *ssl_s;
+    int ret;
+
+    ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
+    ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
+    if (ctx_c == NULL || ctx_s == NULL)
+        return -1;
+
+    /* will send ticket manually */
+    wolfSSL_CTX_no_ticket_TLSv13(ctx_s);
+
+    wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_NONE, 0);
+    wolfSSL_CTX_set_verify(ctx_c, WOLFSSL_VERIFY_NONE, 0);
+
+    ret = wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile,
+        WOLFSSL_FILETYPE_PEM);
+    if (ret != WOLFSSL_SUCCESS)
+        return- -1;
+
+    ret = wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile,
+                                           WOLFSSL_FILETYPE_PEM);
+    if (ret != WOLFSSL_SUCCESS)
+        return -1;
+
+    wolfSSL_SetIORecv(ctx_c, test_ticket_nonce_read_cb);
+    wolfSSL_SetIOSend(ctx_c, test_ticket_nonce_write_cb);
+    wolfSSL_SetIORecv(ctx_s, test_ticket_nonce_read_cb);
+    wolfSSL_SetIOSend(ctx_s, test_ticket_nonce_write_cb);
+
+    ssl_c = wolfSSL_new(ctx_c);
+    ssl_s = wolfSSL_new(ctx_s);
+    if (ssl_c == NULL || ssl_s == NULL)
+        return -1;
+
+    wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
+    wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
+    wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
+    wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
+
+    while (!ssl_c->options.handShakeDone && !ssl_s->options.handShakeDone) {
+        ret = wolfSSL_connect(ssl_c);
+        if (ret != WOLFSSL_SUCCESS  && ssl_c->error != WANT_READ)
+            return -2;
+
+        ret = wolfSSL_accept(ssl_s);
+        if (ret != WOLFSSL_SUCCESS && ssl_s->error != WANT_READ)
+            return -3;
+    }
+
+    small = TLS13_TICKET_NONCE_STATIC_SZ;
+    medium = small + 20 <= 255 ? small + 20 : 255;
+    big = medium + 20 <= 255 ? small + 20 : 255;
+
+    if (test_ticket_nonce_malloc_do(ssl_s, ssl_c, small))
+        return -1;
+    if (ssl_c->session->ticketNonce.data !=
+            ssl_c->session->ticketNonce.dataStatic)
+        return -1;
+    if (test_ticket_nonce_malloc_do(ssl_s, ssl_c, medium))
+        return -1;
+    if (test_ticket_nonce_malloc_do(ssl_s, ssl_c, big))
+        return -1;
+    if (test_ticket_nonce_malloc_do(ssl_s, ssl_c, medium))
+        return -5;
+    if (test_ticket_nonce_malloc_do(ssl_s, ssl_c, small))
+        return -6;
+
+    if (test_ticket_nonce_cache(ssl_s, ssl_c, small))
+        return -1;
+    if (test_ticket_nonce_cache(ssl_s, ssl_c, medium))
+        return -1;
+    if (test_ticket_nonce_cache(ssl_s, ssl_c, big))
+        return -1;
+    if (test_ticket_nonce_cache(ssl_s, ssl_c, medium))
+        return -1;
+    if (test_ticket_nonce_cache(ssl_s, ssl_c, small))
+        return -1;
+
+    wolfSSL_free(ssl_c);
+    wolfSSL_free(ssl_s);
+    wolfSSL_CTX_free(ctx_c);
+    wolfSSL_CTX_free(ctx_s);
+
+    return 0;
+}
+
+#endif /* WOLFSSL_TICKET_NONCE_MALLOC */
+
 /*----------------------------------------------------------------------------*
  | Main
  *----------------------------------------------------------------------------*/
@@ -59479,7 +59760,11 @@ TEST_CASE testCases[] = {
     TEST_DECL(test_ForceZero),
 
     TEST_DECL(test_wolfSSL_Cleanup),
-
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) && defined(HAVE_SESSION_TICKET)       \
+    && defined(WOLFSSL_TLS13) &&                                               \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    TEST_DECL(test_ticket_nonce_malloc),
+#endif
 #if !defined(NO_RSA) && !defined(NO_SHA) && !defined(NO_FILESYSTEM) && \
     !defined(NO_CERTS) && (!defined(NO_WOLFSSL_CLIENT) || \
                            !defined(WOLFSSL_NO_CLIENT_AUTH))

+ 96 - 0
wolfcrypt/src/kdf.c

@@ -437,6 +437,13 @@ int wc_PRF_TLS(byte* digest, word32 digLen, const byte* secret, word32 secLen,
         int    idx = 0;
         byte   data[MAX_TLS13_HKDF_LABEL_SZ];
 
+        /* okmLen (2) + protocol|label len (1) + info len(1) + protocollen +
+         * labellen + infolen */
+        idx = 4 + protocolLen + labelLen + infoLen;
+        if (idx > MAX_TLS13_HKDF_LABEL_SZ)
+            return BUFFER_E;
+        idx = 0;
+
         /* Output length. */
         data[idx++] = (byte)(okmLen >> 8);
         data[idx++] = (byte)okmLen;
@@ -481,6 +488,95 @@ int wc_PRF_TLS(byte* digest, word32 digLen, const byte* secret, word32 secLen,
         return ret;
     }
 
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    /* Expand data using HMAC, salt and label and info.
+     * TLS v1.3 defines this function.
+     *
+     * okm          The generated pseudorandom key - output key material.
+     * okmLen       The length of generated pseudorandom key -
+     *              output key material.
+     * prk          The salt - pseudo-random key.
+     * prkLen       The length of the salt - pseudo-random key.
+     * protocol     The TLS protocol label.
+     * protocolLen  The length of the TLS protocol label.
+     * info         The information to expand.
+     * infoLen      The length of the information.
+     * digest       The type of digest to use.
+     *
+     * This functions is very similar to wc_Tls13_HKDF_Expand_Label() but it
+     * allocate memory if the stack space usually used isn't enough.
+     *
+     * returns 0 on success, otherwise failure.
+     */
+    int wc_Tls13_HKDF_Expand_Label_Alloc(byte* okm, word32 okmLen,
+        const byte* prk, word32 prkLen, const byte* protocol,
+        word32 protocolLen, const byte* label, word32 labelLen,
+        const byte* info, word32 infoLen, int digest, void* heap)
+    {
+        int    ret = 0;
+        int    idx = 0;
+        int    len;
+        byte   *data;
+
+        (void)heap;
+        /* okmLen (2) + protocol|label len (1) + info len(1) + protocollen +
+         * labellen + infolen */
+        len = 4 + protocolLen + labelLen + infoLen;
+
+        data = (byte*)XMALLOC(len, heap, DYNAMIC_TYPE_TMP_BUFFER);
+        if (data == NULL)
+            return BUFFER_E;
+
+        /* Output length. */
+        data[idx++] = (byte)(okmLen >> 8);
+        data[idx++] = (byte)okmLen;
+        /* Length of protocol | label. */
+        data[idx++] = (byte)(protocolLen + labelLen);
+        /* Protocol */
+        XMEMCPY(&data[idx], protocol, protocolLen);
+        idx += protocolLen;
+        /* Label */
+        XMEMCPY(&data[idx], label, labelLen);
+        idx += labelLen;
+        /* Length of hash of messages */
+        data[idx++] = (byte)infoLen;
+        /* Hash of messages */
+        XMEMCPY(&data[idx], info, infoLen);
+        idx += infoLen;
+
+    #ifdef WOLFSSL_CHECK_MEM_ZERO
+        wc_MemZero_Add("wc_Tls13_HKDF_Expand_Label data", data, idx);
+    #endif
+
+#ifdef WOLFSSL_DEBUG_TLS
+        WOLFSSL_MSG("  PRK");
+        WOLFSSL_BUFFER(prk, prkLen);
+        WOLFSSL_MSG("  Info");
+        WOLFSSL_BUFFER(data, idx);
+        WOLFSSL_MSG_EX("  Digest %d", digest);
+#endif
+
+        ret = wc_HKDF_Expand(digest, prk, prkLen, data, idx, okm, okmLen);
+
+#ifdef WOLFSSL_DEBUG_TLS
+        WOLFSSL_MSG("  OKM");
+        WOLFSSL_BUFFER(okm, okmLen);
+#endif
+
+        ForceZero(data, idx);
+
+    #ifdef WOLFSSL_CHECK_MEM_ZERO
+        wc_MemZero_Check(data, len);
+    #endif
+        XFREE(data, heap, DYNAMIC_TYPE_TMP_BUFFER);
+        return ret;
+    }
+
+#endif
+/* defined(WOLFSSL_TICKET_NONCE_MALLOC) && (!defined(HAVE_FIPS) ||
+ *  FIPS_VERSION_GE(5,3)) */
+
 #endif /* HAVE_HKDF && !NO_HMAC */
 
 

+ 54 - 16
wolfssl/internal.h

@@ -1298,6 +1298,22 @@ enum {
 #define DTLS_AEAD_AES_CCM_FAIL_LIMIT             w64From32(0x00B5, 0x04F3)
 #define DTLS_AEAD_AES_CCM_FAIL_KU_LIMIT          w64From32(0x005A, 0x8279)
 
+#define TLS13_TICKET_NONCE_MAX_SZ 255
+
+#if (defined(HAVE_FIPS) &&                                                     \
+    !(defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) &&                    \
+    defined(TLS13_TICKET_NONCE_STATIC_SZ)
+#error "TLS13_TICKET_NONCE_STATIC_SZ is not supported in this FIPS version"
+#endif
+
+#ifndef TLS13_TICKET_NONCE_STATIC_SZ
+#define TLS13_TICKET_NONCE_STATIC_SZ 8
+#endif
+
+#if TLS13_TICKET_NONCE_STATIC_SZ > TLS13_TICKET_NONCE_MAX_SZ
+#error "Max size for ticket nonce is 255 bytes"
+#endif
+
 enum Misc {
     CIPHER_BYTE    = 0x00,         /* Default ciphers */
     ECC_BYTE       = 0xC0,         /* ECC first cipher suite byte */
@@ -1388,7 +1404,8 @@ enum Misc {
     SESSION_ADD_SZ = 4,        /* session age add */
     TICKET_NONCE_LEN_SZ = 1,   /* Ticket nonce length size */
     DEF_TICKET_NONCE_SZ = 1,   /* Default ticket nonce size */
-    MAX_TICKET_NONCE_SZ = 8,   /* maximum ticket nonce size */
+    MAX_TICKET_NONCE_STATIC_SZ = TLS13_TICKET_NONCE_STATIC_SZ,
+                               /* maximum ticket nonce static size */
     MAX_LIFETIME   = 604800,   /* maximum ticket lifetime */
 
     RAN_LEN      = 32,         /* random length           */
@@ -1754,6 +1771,10 @@ enum Misc {
     #define PREALLOC_SESSION_TICKET_LEN 512
 #endif
 
+#ifndef PREALLOC_SESSION_TICKET_NONCE_LEN
+    #define PREALLOC_SESSION_TICKET_NONCE_LEN 32
+#endif
+
 #ifndef SESSION_TICKET_HINT_DEFAULT
     #define SESSION_TICKET_HINT_DEFAULT 300
 #endif
@@ -2776,18 +2797,6 @@ WOLFSSL_LOCAL int TLSX_AddEmptyRenegotiationInfo(TLSX** extensions, void* heap);
 
 #endif /* HAVE_SECURE_RENEGOTIATION */
 
-/** Session Ticket - RFC 5077 (session 3.2) */
-#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
-/* Ticket nonce - for deriving PSK.
- * Length allowed to be: 1..255. Only support 4 bytes.
- * Defined here so that it can be included in InternalTicket.
- */
-typedef struct TicketNonce {
-    byte len;
-    byte data[MAX_TICKET_NONCE_SZ];
-} TicketNonce;
-#endif
-
 #ifdef HAVE_SESSION_TICKET
 /* Our ticket format. All members need to be a byte or array of byte to
  * avoid alignment issues */
@@ -2800,7 +2809,8 @@ typedef struct InternalTicket {
 #ifdef WOLFSSL_TLS13
     byte            ageAdd[AGEADD_LEN];    /* Obfuscation of age */
     byte            namedGroup[NAMEDGROUP_LEN]; /* Named group used */
-    TicketNonce     ticketNonce;           /* Ticket nonce */
+    byte            ticketNonceLen;
+    byte            ticketNonce[MAX_TICKET_NONCE_STATIC_SZ];
 #ifdef WOLFSSL_EARLY_DATA
     byte            maxEarlyDataSz[MAXEARLYDATASZ_LEN]; /* Max size of
                                                          * early data */
@@ -3696,6 +3706,25 @@ WOLFSSL_LOCAL int wolfSSL_quic_add_transport_extensions(WOLFSSL *ssl, int msg_ty
 
 #endif /* WOLFSSL_QUIC */
 
+/** Session Ticket - RFC 5077 (session 3.2) */
+#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
+/* Ticket nonce - for deriving PSK.
+   Length allowed to be: 1..255. Only support
+ * TLS13_TICKET_NONCE_STATIC_SZ length bytes.
+ */
+typedef struct TicketNonce {
+    byte len;
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+    byte *data;
+    byte dataStatic[MAX_TICKET_NONCE_STATIC_SZ];
+#else
+    byte data[MAX_TICKET_NONCE_STATIC_SZ];
+#endif /* WOLFSSL_TICKET_NONCE_MALLOC  && FIPS_VERSION_GE(5,3) */
+} TicketNonce;
+
+#endif
+
 /* wolfSSL session type */
 struct WOLFSSL_SESSION {
     /* WARNING Do not add fields here. They will be ignored in
@@ -3789,13 +3818,21 @@ struct WOLFSSL_SESSION {
 #endif
 };
 
+#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) &&                  \
+        defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+WOLFSSL_LOCAL int SessionTicketNoncePopulate(WOLFSSL_SESSION *session,
+    const byte* nonce, byte len);
+#endif /* WOLFSSL_TLS13 &&  */
+
 WOLFSSL_LOCAL int wolfSSL_RAND_Init(void);
 
 WOLFSSL_LOCAL WOLFSSL_SESSION* wolfSSL_NewSession(void* heap);
 WOLFSSL_LOCAL WOLFSSL_SESSION* wolfSSL_GetSession(
     WOLFSSL* ssl, byte* masterSecret, byte restoreSessionCerts);
 WOLFSSL_LOCAL void AddSession(WOLFSSL* ssl);
-WOLFSSL_LOCAL int AddSessionToCache(WOLFSSL_CTX* ssl,
+/* use wolfSSL_API visibility to be able to test in tests/api.c */
+WOLFSSL_API int AddSessionToCache(WOLFSSL_CTX* ssl,
     WOLFSSL_SESSION* addSession, const byte* id, byte idSz, int* sessionIndex,
     int side, word16 useTicket, ClientSession** clientCacheEntry);
 #ifndef NO_CLIENT_CACHE
@@ -3805,7 +3842,8 @@ WOLFSSL_LOCAL ClientSession* AddSessionToClientCache(int side, int row, int idx,
 #endif
 WOLFSSL_LOCAL
 WOLFSSL_SESSION* ClientSessionToSession(const WOLFSSL_SESSION* session);
-WOLFSSL_LOCAL int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output);
+/* WOLFSSL_API to test it in tests/api.c */
+WOLFSSL_API int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output);
 WOLFSSL_LOCAL int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session);
 WOLFSSL_LOCAL void wolfSSL_FreeSession(WOLFSSL_CTX* ctx,
         WOLFSSL_SESSION* session);

+ 7 - 0
wolfssl/wolfcrypt/kdf.h

@@ -85,6 +85,13 @@ WOLFSSL_API int wc_Tls13_HKDF_Expand_Label(byte* okm, word32 okmLen,
                              const byte* label, word32 labelLen,
                              const byte* info, word32 infoLen,
                              int digest);
+#if defined(WOLFSSL_TICKET_NONCE_MALLOC) &&                                    \
+    (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
+WOLFSSL_API int wc_Tls13_HKDF_Expand_Label_Alloc(byte* okm, word32 okmLen,
+    const byte* prk, word32 prkLen, const byte* protocol, word32 protocolLen,
+    const byte* label, word32 labelLen, const byte* info, word32 infoLen,
+    int digest, void* heap);
+#endif /* !defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3) */
 
 #endif /* HAVE_HKDF */