Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .wolfssl_known_macro_extras
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,7 @@ WOLFSSL_TI_CURRTIME
WOLFSSL_TLS13_DRAFT
WOLFSSL_TLS13_IGNORE_AEAD_LIMITS
WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT
WOLFSSL_TLS13_SHA512
WOLFSSL_TLS13_TICKET_BEFORE_FINISHED
WOLFSSL_TLSX_PQC_MLKEM_STORE_PRIV_KEY
Expand Down
50 changes: 43 additions & 7 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3731,6 +3731,15 @@ static word16 InitSuites_Tls13(Suites* suites, word16 idx, int tls1_3,
#endif

#ifdef HAVE_NULL_CIPHER
/* RFC 9150 integrity-only (zero-confidentiality) TLS 1.3 suites.
* These provide authentication and integrity but no confidentiality, so
* they are NOT advertised in the default cipher preference list. A caller
* that genuinely needs them (e.g. constrained/IoT deployments) must opt in
* explicitly with a cipher list, for example:
* wolfSSL_set_cipher_list(ssl, "TLS13-SHA256-SHA256");
* Define WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT to restore the legacy
* behaviour of including them in the default list. */
#ifdef WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT
#ifdef BUILD_TLS_SHA256_SHA256
if (tls1_3 && haveNull) {
suites->suites[idx++] = ECC_BYTE;
Expand All @@ -3744,6 +3753,7 @@ static word16 InitSuites_Tls13(Suites* suites, word16 idx, int tls1_3,
suites->suites[idx++] = TLS_SHA384_SHA384;
}
#endif
#endif /* WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT */
#endif

return idx;
Expand Down Expand Up @@ -32682,16 +32692,41 @@ static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
if (ssl->options.resuming && ssl->session->ticketLen > 0) {
SessionTicket* ticket;

ticket = TLSX_SessionTicket_Create(0, ssl->session->ticket,
#if !defined(WOLFSSL_NO_TICKET_EXPIRE) && !defined(NO_ASN_TIME)
/* RFC 5077 Section 3.3 / RFC 8446 Section 4.6.1: a client SHOULD
* NOT use a ticket whose lifetime has expired. Drop the expired
* ticket and fall back to a full handshake. Skip the check when
* bornOn is 0 or a secret callback is set (session is managed
* externally, e.g. hostap). */
if (ssl->session->bornOn != 0 &&
Comment thread
kareem-wolfssl marked this conversation as resolved.
#ifdef HAVE_SECRET_CALLBACK
ssl->sessionSecretCb == NULL &&
#endif
LowResTimer() >=
(ssl->session->bornOn + ssl->session->timeout)) {
WOLFSSL_MSG("Stored session ticket expired; full handshake");
ssl->options.resuming = 0;
/* Send an empty SessionTicket extension (NULL ticket) so the
* client still requests a new ticket from the server without
* sending the stale one. */
ret = TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap);
if (ret != WOLFSSL_SUCCESS)
return ret;
}
else
#endif
{
ticket = TLSX_SessionTicket_Create(0, ssl->session->ticket,
ssl->session->ticketLen, ssl->heap);
if (ticket == NULL) return MEMORY_E;
if (ticket == NULL) return MEMORY_E;

ret = TLSX_UseSessionTicket(&ssl->extensions, ticket, ssl->heap);
if (ret != WOLFSSL_SUCCESS) {
TLSX_SessionTicket_Free(ticket, ssl->heap);
return ret;
ret = TLSX_UseSessionTicket(&ssl->extensions, ticket,
ssl->heap);
if (ret != WOLFSSL_SUCCESS) {
TLSX_SessionTicket_Free(ticket, ssl->heap);
return ret;
}
}

idSz = 0;
}
#endif /* HAVE_SESSION_TICKET */
Expand Down Expand Up @@ -36928,6 +36963,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
{
switch (err) {
case WC_NO_ERR_TRACE(BUFFER_ERROR):
case WC_NO_ERR_TRACE(BUFFER_E):
return decode_error;
case WC_NO_ERR_TRACE(EXT_NOT_ALLOWED):
case WC_NO_ERR_TRACE(PEER_KEY_ERROR):
Expand Down
10 changes: 10 additions & 0 deletions src/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3326,6 +3326,16 @@ int wolfSSL_export_keying_material(WOLFSSL *ssl,
return WOLFSSL_FAILURE;
}

/* RFC 8446 Section 7.5 / RFC 5705: keying-material exporters derive from
* exporter_master_secret, which exists only after the handshake is
* complete. Refuse the export until the handshake has completed so that
* a premature call cannot derive material from an uninitialised
* exporterSecret buffer. */
if (ssl->options.handShakeDone == 0) {
WOLFSSL_MSG("Handshake not complete; refusing keying-material export");
return WOLFSSL_FAILURE;
}

/* Sanity check contextLen to prevent integer overflow when cast to word32
* and to ensure it fits in the 2-byte length encoding (max 65535). */
if (use_context && contextLen > WOLFSSL_MAX_16BIT) {
Expand Down
35 changes: 29 additions & 6 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -2512,21 +2512,44 @@ static int test_wolfSSL_set_cipher_list_exclusions(void)
wolfSSL_free(ssl);
ssl = NULL;

/* OpenSSL compat: "ALL" is "all but eNULL" - it does not generate NULL
* suites, but it is not a delete directive, so a following "eNULL"
* re-enables them. (The earlier sticky excludeNull-for-ALL wrongly kept
* them disabled; only "!eNULL"/"DEFAULT" should.) */
/* By default the RFC 9150 integrity-only TLS 1.3 suites are
* zero-confidentiality and are treated as SSL_NOT_DEFAULT:
* they are never produced by the generic cipher-string directives ("ALL",
* "eNULL", ...) and are kept out of the default preference list, so they
* must be requested by explicit suite name.
*
* Defining WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT restores the legacy
* behaviour where the "eNULL" directive re-enables them via the generated
* list. "ALL" on its own never generates NULL ciphers in either mode. */
ExpectNotNull(ssl = wolfSSL_new(ctx));
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, "ALL"), WOLFSSL_SUCCESS);
ExpectIntEQ(test_suites_contains(ssl, ECC_BYTE,
TLS_SHA256_SHA256), 0); /* ALL alone: no NULL generated */
wolfSSL_free(ssl);
ssl = NULL;

{
#ifdef WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT
const int eNullExpectsSuite = 1;
#else
const int eNullExpectsSuite = 0;
#endif
ExpectNotNull(ssl = wolfSSL_new(ctx));
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, "ALL:eNULL"),
WOLFSSL_SUCCESS);
ExpectIntEQ(test_suites_contains(ssl, ECC_BYTE, TLS_SHA256_SHA256),
eNullExpectsSuite);
wolfSSL_free(ssl);
ssl = NULL;
}

/* Explicit suite name remains the supported opt-in and works in both
* modes. */
ExpectNotNull(ssl = wolfSSL_new(ctx));
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, "ALL:eNULL"), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_cipher_list(ssl, "TLS13-SHA256-SHA256"),
WOLFSSL_SUCCESS);
ExpectIntEQ(test_suites_contains(ssl, ECC_BYTE,
TLS_SHA256_SHA256), 1); /* "eNULL" after "ALL" re-enables NULL */
TLS_SHA256_SHA256), 1);
wolfSSL_free(ssl);
ssl = NULL;
#endif /* BUILD_TLS_SHA256_SHA256 */
Expand Down
20 changes: 0 additions & 20 deletions tests/test-dtls13.conf
Original file line number Diff line number Diff line change
Expand Up @@ -270,23 +270,3 @@
-u
-v 4
-l TLS_AES_128_GCM_SHA256

# server DTLSv1.3 Integrity-only SHA256
-u
-v 4
-l TLS13-SHA256-SHA256

# client DTLSv1.3 Integrity-only SHA256
-u
-v 4
-l TLS13-SHA256-SHA256

# server DTSv1.3 Integrity-only SHA384
-u
-v 4
-l TLS13-SHA384-SHA384

# client DTLSv1.3 Integrity-only SHA384
-u
-v 4
-l TLS13-SHA384-SHA384
16 changes: 0 additions & 16 deletions tests/test-tls13.conf
Original file line number Diff line number Diff line change
Expand Up @@ -213,19 +213,3 @@
# client TLSv1.3 Send Ticket explicitly
-v 4
-l TLS13-AES128-GCM-SHA256

# server TLSv1.3 Integrity-only SHA256
-v 4
-l TLS13-SHA256-SHA256

# client TLSv1.3 Integrity-only SHA256
-v 4
-l TLS13-SHA256-SHA256

# server TLSv1.3 Integrity-only SHA384
-v 4
-l TLS13-SHA384-SHA384

# client TLSv1.3 Integrity-only SHA384
-v 4
-l TLS13-SHA384-SHA384
Loading