From 6966659a31ef839d6d741426f3e3eac11728cad4 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 14 Jan 2026 15:49:33 +0000 Subject: [PATCH 01/11] Add new X509 verification result for 'not started' Add a new verification result bitflag MBEDTLS_X509_VERIFY_NOT_STARTED to use as a safe initial value for verify_result. This is better than the current initial value which is 0 (indicating success). Signed-off-by: David Horstmann --- include/mbedtls/x509.h | 1 + include/mbedtls/x509_crt.h | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h index 6b104613d7..ac324fddf6 100644 --- a/include/mbedtls/x509.h +++ b/include/mbedtls/x509.h @@ -108,6 +108,7 @@ #define MBEDTLS_X509_BADCRL_BAD_MD 0x020000 /**< The CRL is signed with an unacceptable hash. */ #define MBEDTLS_X509_BADCRL_BAD_PK 0x040000 /**< The CRL is signed with an unacceptable PK alg (eg RSA vs ECDSA). */ #define MBEDTLS_X509_BADCRL_BAD_KEY 0x080000 /**< The CRL is signed with an unacceptable key (eg bad curve, RSA too short). */ +#define MBEDTLS_X509_VERIFY_NOT_STARTED 0x100000 /**< No verification has yet been performed (used as a safe initial value). */ /** \} name X509 Verify codes */ /** \} addtogroup x509_module */ diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index 6b96039597..6ac17af67d 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -209,7 +209,10 @@ mbedtls_x509_crt_profile; "The CRL is signed with an unacceptable PK alg (eg RSA vs ECDSA).") \ X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_BAD_KEY, \ "MBEDTLS_X509_BADCRL_BAD_KEY", \ - "The CRL is signed with an unacceptable key (eg bad curve, RSA too short).") + "The CRL is signed with an unacceptable key (eg bad curve, RSA too short).") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_VERIFY_NOT_STARTED, \ + "MBEDTLS_X509_VERIFY_NOT_STARTED", \ + "No verification has yet been performed.") /** * Container for writing a certificate (CRT) From 710aaa7ae762b69c9b31592e5f80d885a8d7b32d Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 3 Sep 2025 11:21:00 +0100 Subject: [PATCH 02/11] Set verify_result to failure by default At initialization, set the verify_result field of the ssl session to MBEDTLS_X509_VERIFY_NOT_STARTED, rather than 0 as it is by default currently. This prevents mbedtls_ssl_get_verify_result() from indicating that certificate verification has passed if it is called prior to the handshake happening. Signed-off-by: David Horstmann --- library/ssl_tls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 30cde27923..0585a53ef0 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -1048,6 +1048,8 @@ void mbedtls_ssl_transform_init(mbedtls_ssl_transform *transform) void mbedtls_ssl_session_init(mbedtls_ssl_session *session) { memset(session, 0, sizeof(mbedtls_ssl_session)); + /* Set verify_result to indicate failure by default. */ + session->verify_result = MBEDTLS_X509_VERIFY_NOT_STARTED; } MBEDTLS_CHECK_RETURN_CRITICAL From b413935518337f12d33d29b7b8d5d68f7c1c741a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 7 Oct 2025 16:07:57 +0100 Subject: [PATCH 03/11] Add non-regression test for verify_result init Write a testcase to get verify_result before we have performed a handshake and make sure that it is initialised to a failure value. Signed-off-by: David Horstmann --- tests/suites/test_suite_ssl.data | 3 +++ tests/suites/test_suite_ssl.function | 33 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index 92bda3efd1..53cf1a0796 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3524,3 +3524,6 @@ ssl_tls_exporter_rejects_bad_parameters:MBEDTLS_SSL_VERSION_TLS1_3:24:250:10 TLS 1.3 Keying Material Exporter: Handshake not done depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:PSA_WANT_ALG_RSA_PKCS1V15_SIGN:MBEDTLS_X509_RSASSA_PSS_SUPPORT ssl_tls_exporter_too_early:MBEDTLS_SSL_VERSION_TLS1_3:1:MBEDTLS_SSL_SERVER_CERTIFICATE + +Default verify_result before doing a handshake +verify_result_without_handshake diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 6797a4dd7e..86fac0078b 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -5999,3 +5999,36 @@ exit: MD_OR_USE_PSA_DONE(); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ +void verify_result_without_handshake(void) +{ + /* Test the result of verification before we perform a handshake. */ + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + + PSA_INIT(); + + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + + TEST_EQUAL(mbedtls_ssl_config_defaults(&conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT), 0); + + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_ca_chain(&conf, NULL, NULL); + + TEST_EQUAL(mbedtls_ssl_setup(&ssl, &conf), 0); + + uint32_t verify_result = mbedtls_ssl_get_verify_result(&ssl); + + TEST_EQUAL(verify_result, MBEDTLS_X509_VERIFY_NOT_STARTED); + +exit: + mbedtls_ssl_config_free(&conf); + mbedtls_ssl_free(&ssl); + PSA_DONE(); +} +/* END_CASE */ From 6ca2d7da8b124e728b62e99d769c1b6fd14d442c Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Oct 2025 10:49:24 +0100 Subject: [PATCH 04/11] Add ChangeLog entry for verify_result hardening Signed-off-by: David Horstmann --- ChangeLog.d/verify-result-default-value.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ChangeLog.d/verify-result-default-value.txt diff --git a/ChangeLog.d/verify-result-default-value.txt b/ChangeLog.d/verify-result-default-value.txt new file mode 100644 index 0000000000..d85dfe2670 --- /dev/null +++ b/ChangeLog.d/verify-result-default-value.txt @@ -0,0 +1,5 @@ +Changes + * Harden mbedtls_ssl_get_verify_result() against misuse. + Return failure if the handshake has not yet been attempted. Previously + the result of verification was zero-initialized so the function would + return 0 (indicating success). From c42f73fe34a6f833fbfd76b165ed23541ae28d71 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 28 Jan 2026 17:49:19 +0000 Subject: [PATCH 05/11] Switch to a default value of -1u Since we explicitly document the value 0xFFFFFFFF or -1u as representing 'result not available', we can use it as a sensible default value without creating an API change. Use this value instead of introducing a new verification result value. Signed-off-by: David Horstmann --- include/mbedtls/x509.h | 1 - include/mbedtls/x509_crt.h | 5 +---- library/ssl_tls.c | 4 ++-- tests/suites/test_suite_ssl.function | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h index ac324fddf6..6b104613d7 100644 --- a/include/mbedtls/x509.h +++ b/include/mbedtls/x509.h @@ -108,7 +108,6 @@ #define MBEDTLS_X509_BADCRL_BAD_MD 0x020000 /**< The CRL is signed with an unacceptable hash. */ #define MBEDTLS_X509_BADCRL_BAD_PK 0x040000 /**< The CRL is signed with an unacceptable PK alg (eg RSA vs ECDSA). */ #define MBEDTLS_X509_BADCRL_BAD_KEY 0x080000 /**< The CRL is signed with an unacceptable key (eg bad curve, RSA too short). */ -#define MBEDTLS_X509_VERIFY_NOT_STARTED 0x100000 /**< No verification has yet been performed (used as a safe initial value). */ /** \} name X509 Verify codes */ /** \} addtogroup x509_module */ diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index 6ac17af67d..6b96039597 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -209,10 +209,7 @@ mbedtls_x509_crt_profile; "The CRL is signed with an unacceptable PK alg (eg RSA vs ECDSA).") \ X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_BAD_KEY, \ "MBEDTLS_X509_BADCRL_BAD_KEY", \ - "The CRL is signed with an unacceptable key (eg bad curve, RSA too short).") \ - X509_CRT_ERROR_INFO(MBEDTLS_X509_VERIFY_NOT_STARTED, \ - "MBEDTLS_X509_VERIFY_NOT_STARTED", \ - "No verification has yet been performed.") + "The CRL is signed with an unacceptable key (eg bad curve, RSA too short).") /** * Container for writing a certificate (CRT) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 0585a53ef0..eb015d20da 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -1048,8 +1048,8 @@ void mbedtls_ssl_transform_init(mbedtls_ssl_transform *transform) void mbedtls_ssl_session_init(mbedtls_ssl_session *session) { memset(session, 0, sizeof(mbedtls_ssl_session)); - /* Set verify_result to indicate failure by default. */ - session->verify_result = MBEDTLS_X509_VERIFY_NOT_STARTED; + /* Set verify_result to -1u to indicate 'result not available'. */ + session->verify_result = 0xFFFFFFFF; } MBEDTLS_CHECK_RETURN_CRITICAL diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 86fac0078b..276f08f42c 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -6024,7 +6024,7 @@ void verify_result_without_handshake(void) uint32_t verify_result = mbedtls_ssl_get_verify_result(&ssl); - TEST_EQUAL(verify_result, MBEDTLS_X509_VERIFY_NOT_STARTED); + TEST_EQUAL(verify_result, 0xFFFFFFFF); exit: mbedtls_ssl_config_free(&conf); From 01ef42d5fec4639f9d720e182cf0a1fd746d918a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 5 Feb 2026 14:17:47 +0000 Subject: [PATCH 06/11] Initialize verify_result in session free Initialize the verify_result field in mbedtls_ssl_session_free(). Previously we were just zeroising the entire session object, which would yield a default 'success' value if the same object were reused. Test that this initialisation is actually happening by setting verify_result manually to zero and calling mbedtls_ssl_session_free() on the session before checking its value. Signed-off-by: David Horstmann --- library/ssl_tls.c | 3 +++ tests/suites/test_suite_ssl.function | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index eb015d20da..a65740463c 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -5005,6 +5005,9 @@ void mbedtls_ssl_session_free(mbedtls_ssl_session *session) #endif mbedtls_platform_zeroize(session, sizeof(mbedtls_ssl_session)); + + /* Set verify_result to -1u to indicate 'result not available'. */ + session->verify_result = 0xFFFFFFFF; } #if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 276f08f42c..552c06a7b9 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -6026,6 +6026,16 @@ void verify_result_without_handshake(void) TEST_EQUAL(verify_result, 0xFFFFFFFF); + /* Set the verify result manually and check that session_free resets it. */ + + /* Set the verify result to 0. */ + ssl.session_negotiate->verify_result = 0; + + mbedtls_ssl_session_free(ssl.session_negotiate); + + verify_result = mbedtls_ssl_get_verify_result(&ssl); + TEST_EQUAL(verify_result, 0xFFFFFFFF); + exit: mbedtls_ssl_config_free(&conf); mbedtls_ssl_free(&ssl); From 79b698088758c9a036475acacfc28c3bfead2836 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 16 Feb 2026 10:57:09 +0000 Subject: [PATCH 07/11] Set verify_result in non-verification cases When we are using PSK or when authmode == MBEDTLS_SSL_VERIFY_NONE, we intentionally do not verify the certificate. In these cases, do not keep verify_result at -1u but set it to MBEDTLS_X509_BADCERT_SKIP_VERIFY to indicate that no certificate verification took place. Signed-off-by: David Horstmann --- library/ssl_tls.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index a65740463c..88c5b8a43c 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2288,6 +2288,9 @@ int mbedtls_ssl_set_hs_psk(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; } + /* Since we're not using a certificate, set verify_result to skipped */ + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY; + /* Allow calling psa_destroy_key() on psk remove */ ssl->handshake->psk_opaque_is_internal = 1; return mbedtls_ssl_set_hs_psk_opaque(ssl, key); @@ -7934,6 +7937,7 @@ static int ssl_parse_certificate_coordinate(mbedtls_ssl_context *ssl, ssl->handshake->ciphersuite_info; if (!mbedtls_ssl_ciphersuite_uses_srv_cert(ciphersuite_info)) { + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY; return SSL_CERTIFICATE_SKIP; } @@ -9878,6 +9882,7 @@ int mbedtls_ssl_verify_certificate(mbedtls_ssl_context *ssl, void *rs_ctx) { if (authmode == MBEDTLS_SSL_VERIFY_NONE) { + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY; return 0; } From c0faf73be6c4a23f1af5ddf8bce0e07916587160 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 16 Feb 2026 16:18:01 +0000 Subject: [PATCH 08/11] Reword ChangeLog entry We do not return failure, but return -1u which is documented as a value that indicates that the result is not available. Signed-off-by: David Horstmann --- ChangeLog.d/verify-result-default-value.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog.d/verify-result-default-value.txt b/ChangeLog.d/verify-result-default-value.txt index d85dfe2670..2cf3f0c21b 100644 --- a/ChangeLog.d/verify-result-default-value.txt +++ b/ChangeLog.d/verify-result-default-value.txt @@ -1,5 +1,5 @@ Changes * Harden mbedtls_ssl_get_verify_result() against misuse. - Return failure if the handshake has not yet been attempted. Previously - the result of verification was zero-initialized so the function would - return 0 (indicating success). + If the handshake has not yet been attempted, return -1u to indicate + that the result is not available. Previously the result of verification + was zero-initialized so the function would return 0 (indicating success). From d179019ec22c4a29db9d9f15ad086d37b71e0bf7 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 17 Feb 2026 14:41:59 +0000 Subject: [PATCH 09/11] Set verify_result to 0 when not checking certs When we are using PSK or authmode is MBEDTLS_SSL_VERIFY_NONE, set verify_result to 0 rather than MBEDTLS_X509_BADCERT_SKIP_VERIFY. This is specific to the 3.6 LTS, to preserve the previous behaviour of the library in these cases, which was determined by the default value of verify_result being 0. Signed-off-by: David Horstmann --- library/ssl_tls.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 88c5b8a43c..ca3be8b50b 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2288,8 +2288,8 @@ int mbedtls_ssl_set_hs_psk(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; } - /* Since we're not using a certificate, set verify_result to skipped */ - ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY; + /* Since we're not using a certificate, set verify_result to success */ + ssl->session_negotiate->verify_result = 0; /* Allow calling psa_destroy_key() on psk remove */ ssl->handshake->psk_opaque_is_internal = 1; @@ -7937,7 +7937,7 @@ static int ssl_parse_certificate_coordinate(mbedtls_ssl_context *ssl, ssl->handshake->ciphersuite_info; if (!mbedtls_ssl_ciphersuite_uses_srv_cert(ciphersuite_info)) { - ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY; + ssl->session_negotiate->verify_result = 0; return SSL_CERTIFICATE_SKIP; } @@ -9882,7 +9882,7 @@ int mbedtls_ssl_verify_certificate(mbedtls_ssl_context *ssl, void *rs_ctx) { if (authmode == MBEDTLS_SSL_VERIFY_NONE) { - ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY; + ssl->session_negotiate->verify_result = 0; return 0; } From 611f3fb072904082e154f681a697f4f51e0a7387 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 16 Mar 2026 11:15:01 +0000 Subject: [PATCH 10/11] Configure the RNG in 3.6 testcase In Mbed TLS 3.6 we still need to manually configure the RNG for TLS. Add this to the testcase for default verify_result. Signed-off-by: David Horstmann --- tests/suites/test_suite_ssl.function | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 552c06a7b9..38837f2ead 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -6019,6 +6019,7 @@ void verify_result_without_handshake(void) mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL); mbedtls_ssl_conf_ca_chain(&conf, NULL, NULL); + mbedtls_ssl_conf_rng(&conf, mbedtls_test_random, NULL); TEST_EQUAL(mbedtls_ssl_setup(&ssl, &conf), 0); From 05012095075c63532bd4c87b1860b4fec9ebd8ae Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 16 Mar 2026 17:19:30 +0000 Subject: [PATCH 11/11] Move TLS 1.3 verify-result setting for PSK When we are doing PSK, we'd like to set verify_result to 0 to indicate success. Previously this was done in mbedtls_ssl_set_hs_psk() but this is inadequate since this function may be called for early data (where certificate verification happens later in the handshake). Instead, set this value after writing / processing the encrypted extensions on the server / client respectively, so that we know whether we are doing certificate verification or not for sure. This change is effective only for TLS 1.3 as TLS 1.2 sets verify_result for PSK in ssl_parse_certificate_coordinate(). Signed-off-by: David Horstmann --- library/ssl_tls.c | 3 --- library/ssl_tls13_client.c | 3 +++ library/ssl_tls13_server.c | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index ca3be8b50b..7fc3b10072 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -2288,9 +2288,6 @@ int mbedtls_ssl_set_hs_psk(mbedtls_ssl_context *ssl, return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; } - /* Since we're not using a certificate, set verify_result to success */ - ssl->session_negotiate->verify_result = 0; - /* Allow calling psa_destroy_key() on psk remove */ ssl->handshake->psk_opaque_is_internal = 1; return mbedtls_ssl_set_hs_psk_opaque(ssl, key); diff --git a/library/ssl_tls13_client.c b/library/ssl_tls13_client.c index 78a069f792..752bc033fe 100644 --- a/library/ssl_tls13_client.c +++ b/library/ssl_tls13_client.c @@ -2270,6 +2270,9 @@ static int ssl_tls13_process_encrypted_extensions(mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) if (mbedtls_ssl_tls13_key_exchange_mode_with_psk(ssl)) { mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); + + /* Since we're not using a certificate, set verify_result to success */ + ssl->session_negotiate->verify_result = 0; } else { mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CERTIFICATE_REQUEST); } diff --git a/library/ssl_tls13_server.c b/library/ssl_tls13_server.c index 5757d20180..e370a6b3f3 100644 --- a/library/ssl_tls13_server.c +++ b/library/ssl_tls13_server.c @@ -2637,6 +2637,9 @@ static int ssl_tls13_write_encrypted_extensions(mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) if (mbedtls_ssl_tls13_key_exchange_mode_with_psk(ssl)) { mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); + + /* Since we're not using a certificate, set verify_result to success */ + ssl->session_negotiate->verify_result = 0; } else { mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CERTIFICATE_REQUEST); }