EMS code review feedback:

Check as early in the connection as possible
Abort resumption rather than fully aborting connection when EMS is disabled
Make version check around haveEMS consistent
Clear premaster secret when EMS check fails
Update documentation
pull/10978/head
Kareem 2026-08-10 16:18:36 -07:00
parent 15bd0bb841
commit c26842949d
7 changed files with 162 additions and 49 deletions

View File

@ -17481,8 +17481,10 @@ int wolfSSL_set_scr_check_enabled(WOLFSSL* ssl, byte enabled);
\ingroup Setup
\brief Disables the TLS Extended Master Secret extension (RFC 7627) on
the context: a client stops advertising it and a server ignores the
peer's request, so a standard master secret is negotiated. TLS 1.2 and
earlier only. Requires HAVE_EXTENDED_MASTER.
peer's request, so a standard master secret is negotiated. A server also
declines resumption of sessions or tickets that used EMS and does a full
handshake instead. TLS 1.2 and earlier only. Requires
HAVE_EXTENDED_MASTER.
\return WOLFSSL_SUCCESS on success.
\return BAD_FUNC_ARG if ctx is NULL.
@ -17505,8 +17507,10 @@ int wolfSSL_CTX_DisableExtendedMasterSecret(WOLFSSL_CTX* ctx);
\ingroup Setup
\brief Disables the TLS Extended Master Secret extension (RFC 7627) on
the SSL object: a client stops advertising it and a server ignores the
peer's request, so a standard master secret is negotiated. TLS 1.2 and
earlier only. Requires HAVE_EXTENDED_MASTER.
peer's request, so a standard master secret is negotiated. A server also
declines resumption of sessions or tickets that used EMS and does a full
handshake instead. TLS 1.2 and earlier only. Requires
HAVE_EXTENDED_MASTER.
\return WOLFSSL_SUCCESS on success.
\return BAD_FUNC_ARG if ssl is NULL.
@ -17576,7 +17580,8 @@ int wolfSSL_EnableExtendedMasterSecret(WOLFSSL* ssl);
\brief Makes the TLS Extended Master Secret extension (RFC 7627)
mandatory on the context: if it is not negotiated, the connection
is aborted with EXT_MASTER_SECRET_NEEDED_E. A client advertises
the extension even after a previous disable. TLS 1.2 and earlier
the extension even after a previous disable. Sessions using a
session-secret callback (EAP-FAST) are exempt. TLS 1.2 and earlier
only. Requires HAVE_EXTENDED_MASTER.
\return WOLFSSL_SUCCESS on success.
@ -17601,8 +17606,9 @@ int wolfSSL_CTX_RequireExtendedMasterSecret(WOLFSSL_CTX* ctx);
\brief Makes the TLS Extended Master Secret extension (RFC 7627)
mandatory on the SSL object: if it is not negotiated, including on
resumption, the connection is aborted with EXT_MASTER_SECRET_NEEDED_E. A
client advertises the extension even after a previous disable. TLS 1.2
and earlier only. Requires HAVE_EXTENDED_MASTER.
client advertises the extension even after a previous disable. Sessions
using a session-secret callback (EAP-FAST) are exempt. TLS 1.2 and
earlier only. Requires HAVE_EXTENDED_MASTER.
\return WOLFSSL_SUCCESS on success.
\return BAD_FUNC_ARG if ssl is NULL.

View File

@ -25036,15 +25036,6 @@ static int CheckResumptionConsistency(WOLFSSL* ssl)
WOLFSSL_ERROR_VERBOSE(EXT_MASTER_SECRET_NEEDED_E);
return EXT_MASTER_SECRET_NEEDED_E;
}
#ifdef HAVE_EXTENDED_MASTER
/* Resumption skips MakeMasterSecret, so enforce required EMS here. */
if (!skipEmsCheck && ssl->options.requireEMS && !ssl->options.haveEMS) {
WOLFSSL_MSG("EMS required but not negotiated with peer");
SendAlert(ssl, alert_fatal, handshake_failure);
WOLFSSL_ERROR_VERBOSE(EXT_MASTER_SECRET_NEEDED_E);
return EXT_MASTER_SECRET_NEEDED_E;
}
#endif /* HAVE_EXTENDED_MASTER */
#ifndef NO_RESUME_SUITE_CHECK
/* Suite must match (RFC 5246 7.4.1.3), tickets included. Skip when no suite
* was retained (both zero = TLS_NULL_WITH_NULL_NULL, e.g. EAP-FAST PAC). */
@ -35197,6 +35188,31 @@ static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
}
#endif /* HAVE_TLS_EXTENSIONS */
#ifdef HAVE_EXTENDED_MASTER
/* The negotiated EMS state is final once the ServerHello extensions
* are parsed: abort a requiring client here, before any key material
* is computed or sent. */
if (ssl->options.requireEMS && !ssl->options.haveEMS) {
byte skipEmsCheck = 0;
#ifdef HAVE_SECRET_CALLBACK
/* Skip for EAP-FAST (session-secret callback): the master secret
* comes from the callback. */
skipEmsCheck = (ssl->sessionSecretCb != NULL
#ifdef HAVE_SESSION_TICKET
&& ssl->session != NULL
&& ssl->session->ticketLen > 0
#endif
) ? 1 : 0;
#endif
if (!skipEmsCheck) {
WOLFSSL_MSG("EMS required but not negotiated with peer");
SendAlert(ssl, alert_fatal, handshake_failure);
WOLFSSL_ERROR_VERBOSE(EXT_MASTER_SECRET_NEEDED_E);
return EXT_MASTER_SECRET_NEEDED_E;
}
}
#endif /* HAVE_EXTENDED_MASTER */
#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \
defined(HAVE_SERVER_RENEGOTIATION_INFO) && \
!defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK)
@ -41378,7 +41394,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
#endif /* !WOLFSSL_NO_TICKET_EXPIRE && !NO_ASN_TIME */
if (!ssl->options.resuming) {
/* Expired above: DoClientHello falls back to a full handshake. */
/* Resumption abandoned: DoClientHello runs a full handshake. */
return ret;
}
@ -41393,13 +41409,25 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
}
/* if old sess used EMS, but new doesn't, MUST abort */
else if (session->haveEMS && !ssl->options.haveEMS) {
WOLFSSL_MSG("Trying to resume a session with EMS without "
"using EMS");
#ifdef WOLFSSL_EXTRA_ALERTS
SendAlert(ssl, alert_fatal, handshake_failure);
#endif
ret = EXT_MASTER_SECRET_NEEDED_E;
WOLFSSL_ERROR_VERBOSE(ret);
#ifdef HAVE_EXTENDED_MASTER
if (ssl->options.disableEMS) {
/* Local disable, not a client downgrade: decline the
* resumption and do a full handshake. */
WOLFSSL_MSG("EMS disabled locally, declining resumption "
"of an EMS session. Do full handshake.");
ssl->options.resuming = 0;
}
else
#endif
{
WOLFSSL_MSG("Trying to resume a session with EMS without "
"using EMS");
#ifdef WOLFSSL_EXTRA_ALERTS
SendAlert(ssl, alert_fatal, handshake_failure);
#endif
ret = EXT_MASTER_SECRET_NEEDED_E;
WOLFSSL_ERROR_VERBOSE(ret);
}
}
}
else {

View File

@ -3935,6 +3935,28 @@ int StoreKeys(WOLFSSL* ssl, const byte* keyData, int side)
return 0;
}
#if !defined(NO_OLD_TLS) || defined(HAVE_EXTENDED_MASTER)
static void CleanPreMaster(WOLFSSL* ssl)
{
int sz = (int)(ssl->arrays->preMasterSz);
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Add("CleanPreMaster preMasterSecret",
ssl->arrays->preMasterSecret, sz);
#endif
ForceZero(ssl->arrays->preMasterSecret, sz);
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(ssl->arrays->preMasterSecret, sz);
#endif
XFREE(ssl->arrays->preMasterSecret, ssl->heap, DYNAMIC_TYPE_SECRET);
ssl->arrays->preMasterSecret = NULL;
ssl->arrays->preMasterSz = 0;
}
#endif /* !NO_OLD_TLS || HAVE_EXTENDED_MASTER */
#ifndef NO_OLD_TLS
int DeriveKeys(WOLFSSL* ssl)
{
@ -4062,27 +4084,6 @@ int DeriveKeys(WOLFSSL* ssl)
}
static void CleanPreMaster(WOLFSSL* ssl)
{
int sz = (int)(ssl->arrays->preMasterSz);
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Add("CleanPreMaster preMasterSecret",
ssl->arrays->preMasterSecret, sz);
#endif
ForceZero(ssl->arrays->preMasterSecret, sz);
#ifdef WOLFSSL_CHECK_MEM_ZERO
wc_MemZero_Check(ssl->arrays->preMasterSecret, sz);
#endif
XFREE(ssl->arrays->preMasterSecret, ssl->heap, DYNAMIC_TYPE_SECRET);
ssl->arrays->preMasterSecret = NULL;
ssl->arrays->preMasterSz = 0;
}
/* Create and store the master secret see page 32, 6.1 */
static int MakeSslMasterSecret(WOLFSSL* ssl)
{
@ -4238,6 +4239,8 @@ int MakeMasterSecret(WOLFSSL* ssl)
WOLFSSL_MSG("EMS required but not negotiated with peer");
SendAlert(ssl, alert_fatal, handshake_failure);
WOLFSSL_ERROR_VERBOSE(EXT_MASTER_SECRET_NEEDED_E);
if (ssl->arrays->preMasterSecret != NULL)
CleanPreMaster(ssl);
return EXT_MASTER_SECRET_NEEDED_E;
}
#endif

View File

@ -1564,6 +1564,23 @@ int wolfSSL_set_SessionTicket_cb(WOLFSSL* ssl,
#ifdef HAVE_EXTENDED_MASTER
/* EMS applies to (D)TLS 1.0-1.2 only; the same version gate is applied by
* InitSSL_Ctx and InitSSL_Side when arming the default advertisement. */
static int EmsAllowedForVersion(ProtocolVersion pv)
{
int allowed = 0;
if (pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_MINOR)
allowed = 1;
#ifdef WOLFSSL_DTLS
if (pv.major == DTLS_MAJOR)
allowed = 1;
#endif
return allowed;
}
/* Disable the Extended Master Secret extension on the context.
*
* For a client this stops the extension being advertised. For a server this
@ -1642,7 +1659,8 @@ int wolfSSL_CTX_EnableExtendedMasterSecret(WOLFSSL_CTX* ctx)
/* Re-arm client advertising. A side-less (wolfSSLv23) object is
* armed by InitSSL_Side instead; arming it here would make a server
* echo an unsolicited extension. */
if (ctx->method != NULL && ctx->method->side == WOLFSSL_CLIENT_END)
if (ctx->method != NULL && ctx->method->side == WOLFSSL_CLIENT_END &&
EmsAllowedForVersion(ctx->method->version))
ctx->haveEMS = 1;
}
@ -1672,7 +1690,8 @@ int wolfSSL_EnableExtendedMasterSecret(WOLFSSL* ssl)
/* Re-arm client advertising. A side-less (wolfSSLv23) object is
* armed by InitSSL_Side instead; arming it here would make a server
* echo an unsolicited extension. */
if (ssl->options.side == WOLFSSL_CLIENT_END)
if (ssl->options.side == WOLFSSL_CLIENT_END &&
EmsAllowedForVersion(ssl->ctx->method->version))
ssl->options.haveEMS = 1;
}
@ -1706,7 +1725,8 @@ int wolfSSL_CTX_RequireExtendedMasterSecret(WOLFSSL_CTX* ctx)
/* Re-arm client advertising. A side-less (wolfSSLv23) object is
* armed by InitSSL_Side instead; arming it here would make a server
* echo an unsolicited extension. */
if (ctx->method != NULL && ctx->method->side == WOLFSSL_CLIENT_END)
if (ctx->method != NULL && ctx->method->side == WOLFSSL_CLIENT_END &&
EmsAllowedForVersion(ctx->method->version))
ctx->haveEMS = 1;
}
@ -1738,7 +1758,8 @@ int wolfSSL_RequireExtendedMasterSecret(WOLFSSL* ssl)
/* Re-arm client advertising. A side-less (wolfSSLv23) object is
* armed by InitSSL_Side instead; arming it here would make a server
* echo an unsolicited extension. */
if (ssl->options.side == WOLFSSL_CLIENT_END)
if (ssl->options.side == WOLFSSL_CLIENT_END &&
EmsAllowedForVersion(ssl->ctx->method->version))
ssl->options.haveEMS = 1;
}

View File

@ -42139,6 +42139,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_tls_ems_resumption_downgrade),
TEST_DECL(test_tls_ems_resumption_server_downgrade),
TEST_DECL(test_tls_ems_server_disable),
TEST_DECL(test_tls_ems_server_disable_resumption),
TEST_DECL(test_tls_ems_disable_v23),
TEST_DECL(test_tls_require_ems),
TEST_DECL(test_tls_require_ems_resumption),

View File

@ -352,6 +352,59 @@ int test_tls_ems_server_disable(void)
}
/* A server that disables EMS declines resumption of a session that used EMS
* from an EMS-offering client: full handshake instead of a fatal alert. */
int test_tls_ems_server_disable_resumption(void)
{
EXPECT_DECLS;
#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_EXTENDED_MASTER) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(NO_SESSION_CACHE)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
WOLFSSL *ssl_s = NULL;
WOLFSSL_SESSION *session = NULL;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
/* Establish a session that uses EMS. */
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectNotNull(session = wolfSSL_get1_session(ssl_c));
ExpectTrue(session->haveEMS);
wolfSSL_free(ssl_c);
ssl_c = NULL;
wolfSSL_free(ssl_s);
ssl_s = NULL;
test_memio_clear_buffer(&test_ctx, 0);
test_memio_clear_buffer(&test_ctx, 1);
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
ExpectIntEQ(wolfSSL_DisableExtendedMasterSecret(ssl_s), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS);
/* The handshake must complete as a full handshake without EMS. */
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_s->options.resuming, 0);
ExpectIntEQ(ssl_c->options.haveEMS, 0);
ExpectIntEQ(ssl_s->options.haveEMS, 0);
wolfSSL_SESSION_free(session);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_EXTENDED_MASTER) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)

View File

@ -26,6 +26,7 @@ int test_tls_ems_downgrade(void);
int test_tls_ems_resumption_downgrade(void);
int test_tls_ems_resumption_server_downgrade(void);
int test_tls_ems_server_disable(void);
int test_tls_ems_server_disable_resumption(void);
int test_tls_ems_disable_v23(void);
int test_tls_require_ems(void);
int test_tls_require_ems_resumption(void);