From 1773a4ab4173b5aad5ee8952f8cc5f1cf906a117 Mon Sep 17 00:00:00 2001 From: Kareem Date: Tue, 30 Dec 2025 13:18:48 -0700 Subject: [PATCH 1/2] Send no_renegotiation alert when rejecting renegotation attempt as defined in RFC 5246 section 7.2.2. --- src/internal.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/internal.c b/src/internal.c index 841e49fc8f0..179c3d2830c 100644 --- a/src/internal.c +++ b/src/internal.c @@ -18073,6 +18073,16 @@ int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, } #endif +#if !defined(HAVE_SECURE_RENEGOTIATION) + if (ssl->options.handShakeState == HANDSHAKE_DONE && type == client_hello && + ssl->options.side == WOLFSSL_SERVER_END){ + WOLFSSL_MSG("Renegotiation request rejected"); + SendAlert(ssl, alert_fatal, no_renegotiation); + WOLFSSL_ERROR_VERBOSE(SECURE_RENEGOTIATION_E); + return SECURE_RENEGOTIATION_E; + } +#endif + if (ssl->options.handShakeState == HANDSHAKE_DONE && type != hello_request){ WOLFSSL_MSG("HandShake message after handshake complete"); SendAlert(ssl, alert_fatal, unexpected_message); From ddb2fb628e1c4aab02df0b8f317e0460b66368ec Mon Sep 17 00:00:00 2001 From: Kareem Date: Tue, 30 Dec 2025 13:19:04 -0700 Subject: [PATCH 2/2] Add a runtime option to enable or disable the secure renegotation check. --- doc/dox_comments/header_files/ssl.h | 67 +++++++++++++++++ src/internal.c | 8 +- src/ssl.c | 24 ++++++ tests/api.c | 110 ++++++++++++++++++++++++++++ wolfssl/internal.h | 4 + wolfssl/ssl.h | 6 ++ 6 files changed, 217 insertions(+), 2 deletions(-) diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 46b9df9fdb0..955b04093ce 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -15984,3 +15984,70 @@ void wolfSSL_CTX_set_default_passwd_cb(WOLFSSL_CTX* ctx, */ void wolfSSL_CTX_set_default_passwd_cb_userdata(WOLFSSL_CTX* ctx, void* userdata); + +/*! + \ingroup Setup + + \brief Gets the state of the secure renegotiation (SCR) check requirement. + + This function returns whether the client requires the server to acknowledge + the secure renegotiation extension and enable secure renegotiation when + sending it from the client. When enabled, the client will generate a fatal + handshake_failure alert if the server does not acknowledge the extension + in the ServerHello message, as required by RFC 9325. + + \return 1 if the SCR check is enabled. + \return 0 if the SCR check is disabled. + \return BAD_FUNC_ARG if ssl is NULL. + + \param ssl Pointer to the WOLFSSL structure, created with wolfSSL_new(). + + _Example_ + \code + WOLFSSL* ssl; + int enabled; + + ssl = wolfSSL_new(ctx); + enabled = wolfSSL_get_scr_check_enabled(ssl); + if (enabled) { + // SCR check is enabled + } + \endcode + + \sa wolfSSL_set_scr_check_enabled +*/ +byte wolfSSL_get_scr_check_enabled(WOLFSSL* ssl); + +/*! + \ingroup Setup + + \brief Sets the state of the secure renegotiation (SCR) check requirement. + + This function enables or disables the requirement for the server to + acknowledge the secure renegotiation extension and enable secure + renegotiation when sending it from the client. When enabled, the client + will generate a fatal handshake_failure alert if the server does not + acknowledge the extension in the ServerHello message, as required by + RFC 9325. + + \return WOLFSSL_SUCCESS on success. + \return BAD_FUNC_ARG if ssl is NULL. + + \param ssl Pointer to the WOLFSSL structure, created with wolfSSL_new(). + \param enabled Non-zero to enable the SCR check, zero to disable it. + + _Example_ + \code + WOLFSSL* ssl; + int ret; + + ssl = wolfSSL_new(ctx); + ret = wolfSSL_set_scr_check_enabled(ssl, 1); + if (ret != WOLFSSL_SUCCESS) { + // Error setting SCR check + } + \endcode + + \sa wolfSSL_get_scr_check_enabled +*/ +int wolfSSL_set_scr_check_enabled(WOLFSSL* ssl, byte enabled); diff --git a/src/internal.c b/src/internal.c index 179c3d2830c..467274b511f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7850,6 +7850,10 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) ssl->disabledCurves = ctx->disabledCurves; #endif +#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) + ssl->scr_check_enabled = 1; +#endif InitCiphers(ssl); InitCipherSpecs(&ssl->specs); @@ -31666,8 +31670,8 @@ static int DhSetKey(WOLFSSL* ssl) #endif /* HAVE_TLS_EXTENSIONS */ #if defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) - if (ssl->secure_renegotiation == NULL || - !ssl->secure_renegotiation->enabled) { + if (ssl->scr_check_enabled && (ssl->secure_renegotiation == NULL || + !ssl->secure_renegotiation->enabled)) { /* If the server does not acknowledge the extension, the client * MUST generate a fatal handshake_failure alert prior to * terminating the connection. diff --git a/src/ssl.c b/src/ssl.c index 06654001c8a..a128d3b627c 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -4215,6 +4215,30 @@ long wolfSSL_SSL_get_secure_renegotiation_support(WOLFSSL* ssl) #endif /* HAVE_SECURE_RENEGOTIATION_INFO */ +#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) +WOLFSSL_API int wolfSSL_get_scr_check_enabled(WOLFSSL* ssl) +{ + WOLFSSL_ENTER("wolfSSL_get_scr_check_enabled"); + + if (ssl == NULL) + return BAD_FUNC_ARG; + + return ssl->scr_check_enabled; +} + +WOLFSSL_API int wolfSSL_set_scr_check_enabled(WOLFSSL* ssl, byte enabled) +{ + WOLFSSL_ENTER("wolfSSL_set_scr_check_enabled"); + + if (ssl == NULL) + return BAD_FUNC_ARG; + + ssl->scr_check_enabled = !!enabled; + return WOLFSSL_SUCCESS; +} +#endif + #if defined(HAVE_SESSION_TICKET) /* Session Ticket */ diff --git a/tests/api.c b/tests/api.c index 10631d5e9bc..e8c59d97afc 100644 --- a/tests/api.c +++ b/tests/api.c @@ -12844,6 +12844,115 @@ static int test_wolfSSL_SCR_Reconnect(void) return EXPECT_RESULT(); } +/* Test SCR check when server doesn't reply to secure_renegotiation. */ +#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) && \ + defined(HAVE_SECURE_RENEGOTIATION) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) +/* IO callback to remove secure renegotiation extension from ServerHello */ +static int test_SCR_check_remove_ext_io_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + static int sentServerHello = FALSE; + + if (!sentServerHello) { + /* Look for secure renegotiation extension: 0xFF 0x01 (extension type) */ + byte renegExt[] = { 0xFF, 0x01 }; + size_t i; + + if (sz < (int)sizeof(renegExt)) + return test_memio_write_cb(ssl, buf, sz, ctx); + + /* Search for the extension in the buffer */ + for (i = 0; i < (size_t)sz - sizeof(renegExt); i++) { + if (XMEMCMP(buf + i, renegExt, sizeof(renegExt)) == 0) { + /* Found the extension. Remove it by changing the type to something + * unrecognized so it won't be parsed as secure renegotiation. */ + buf[i+1] = 0x11; + break; + } + } + sentServerHello = TRUE; + } + + /* Call the original test_memio_write_cb */ + return test_memio_write_cb(ssl, buf, sz, ctx); +} +#endif + +static int test_wolfSSL_SCR_check_enabled(void) +{ + EXPECT_DECLS; +#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) && \ + defined(HAVE_SECURE_RENEGOTIATION) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + int ret; + int enabled; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + /* Set up client and server */ + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* Enable secure renegotiation on client (so it sends the extension) */ + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSecureRenegotiation(ctx_c)); + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_UseSecureRenegotiation(ssl_c)); + + /* Set up IO callback on server to remove the extension from ServerHello */ + wolfSSL_SSLSetIOSend(ssl_s, test_SCR_check_remove_ext_io_cb); + + /* Try to connect - should fail with SECURE_RENEGOTIATION_E */ + ret = test_memio_do_handshake(ssl_c, ssl_s, 10, NULL); + ExpectIntNE(0, ret); /* Handshake should fail */ + ret = wolfSSL_get_error(ssl_c, 0); + ExpectIntEQ(WC_NO_ERR_TRACE(SECURE_RENEGOTIATION_E), ret); + + /* Clean up for next attempt */ + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 1); + test_memio_clear_buffer(&test_ctx, 0); + + /* Set up new client and server */ + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + /* Enable secure renegotiation on client */ + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_CTX_UseSecureRenegotiation(ctx_c)); + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_UseSecureRenegotiation(ssl_c)); + + /* Set up IO callback on server to remove the extension from ServerHello */ + wolfSSL_SSLSetIOSend(ssl_s, test_SCR_check_remove_ext_io_cb); + + /* Disable the SCR check */ + ExpectIntEQ(WOLFSSL_SUCCESS, wolfSSL_set_scr_check_enabled(ssl_c, 0)); + + /* Verify the state is 0 */ + enabled = wolfSSL_get_scr_check_enabled(ssl_c); + ExpectIntEQ(0, enabled); + + /* Now connection should succeed */ + ExpectIntEQ(0, test_memio_do_handshake(ssl_c, ssl_s, 10, NULL)); + + /* Cleanup */ + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); + ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); + ctx_s = NULL; +#endif + return EXPECT_RESULT(); +} + #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \ !defined(NO_FILESYSTEM) && (!defined(NO_RSA) || defined(HAVE_ECC)) /* Called when writing. */ @@ -42164,6 +42273,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_certificate_authorities_client_hello), TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), + TEST_DECL(test_wolfSSL_SCR_check_enabled), TEST_DECL(test_tls_ext_duplicate), TEST_DECL(test_tls_bad_legacy_version), #if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 3187bea290d..4603db3bc9f 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -6333,6 +6333,10 @@ struct WOLFSSL { #if defined(WOLFSSL_SYS_CRYPTO_POLICY) int secLevel; /* The security level of system-wide crypto policy. */ #endif /* WOLFSSL_SYS_CRYPTO_POLICY */ +#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) + WC_BITFIELD scr_check_enabled:1; /* enable/disable SCR check */ +#endif }; #if defined(WOLFSSL_SYS_CRYPTO_POLICY) diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 2b58808a1d9..888139af97a 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -4726,6 +4726,12 @@ WOLFSSL_API int wolfSSL_SecureResume(WOLFSSL* ssl); #endif WOLFSSL_API long wolfSSL_SSL_get_secure_renegotiation_support(WOLFSSL* ssl); +#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK) +WOLFSSL_API int wolfSSL_get_scr_check_enabled(WOLFSSL* ssl); +WOLFSSL_API int wolfSSL_set_scr_check_enabled(WOLFSSL* ssl, byte enabled); +#endif + #endif #if defined(HAVE_SELFTEST) && \