From 78b0db1fc1d9a5d8dfdabdb54eadc89256ce4323 Mon Sep 17 00:00:00 2001 From: "Benjamin W. Broersma" Date: Fri, 19 Jun 2026 19:38:03 +0200 Subject: [PATCH 1/2] Fix #2031 - TLS sigalgs / key exchange: optimize happy flow connections --- checks/tasks/tls/scans.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/checks/tasks/tls/scans.py b/checks/tasks/tls/scans.py index 09eaa0e46..e3bc4d929 100644 --- a/checks/tasks/tls/scans.py +++ b/checks/tasks/tls/scans.py @@ -869,24 +869,33 @@ def test_key_exchange_hash( Note that this is not the certificate hash, or TLS cipher hash. There are few or no hosts that do not meet this requirement. """ - bad_hash_result = _test_connection_with_limited_sigalgs(server_connectivity_info, SIGNATURE_ALGORITHMS_BAD_HASH) - if bad_hash_result: - log.info(f"SHA2 key exchange check: negotiated bad sigalg ({bad_hash_result})") - return KeyExchangeHashFunctionEvaluation( - status=KexHashFuncStatus.bad, - score=scoring.WEB_TLS_KEX_HASH_FUNC_BAD, - found_hash=bad_hash_result.name, - ) - - phase_out_hash_result = _test_connection_with_limited_sigalgs( - server_connectivity_info, SIGNATURE_ALGORITHMS_PHASE_OUT_HASH + bad_phase_out_hash_result = _test_connection_with_limited_sigalgs( + server_connectivity_info, SIGNATURE_ALGORITHMS_BAD_HASH + SIGNATURE_ALGORITHMS_PHASE_OUT_HASH ) - if phase_out_hash_result: - log.info(f"SHA2 key exchange check: negotiated phase_out hash ({phase_out_hash_result})") + if bad_phase_out_hash_result: + bad_hash_result = None + log.info(f"key exchange check: negotiated bad or phase_out sigalg ({bad_phase_out_hash_result.name})") + if bad_phase_out_hash_result in [sa[0] for sa in SIGNATURE_ALGORITHMS_PHASE_OUT_HASH]: + log.info("key exchange check: since it was phase_out, recheck with only bad") + bad_hash_result = _test_connection_with_limited_sigalgs( + server_connectivity_info, SIGNATURE_ALGORITHMS_BAD_HASH + ) + + if (bad_phase_out_hash_result in [sa[0] for sa in SIGNATURE_ALGORITHMS_BAD_HASH]) or bad_hash_result: + log.info( + f"key exchange check: negotiated bad sigalg ({(bad_hash_result or bad_phase_out_hash_result).name})" + ) + return KeyExchangeHashFunctionEvaluation( + status=KexHashFuncStatus.bad, + score=scoring.WEB_TLS_KEX_HASH_FUNC_BAD, + found_hash=bad_hash_result.name, + ) + + log.info(f"key exchange check: negotiated phase_out sigalg ({bad_phase_out_hash_result.name})") return KeyExchangeHashFunctionEvaluation( status=KexHashFuncStatus.phase_out, score=scoring.WEB_TLS_KEX_HASH_FUNC_OK, - found_hash=phase_out_hash_result.name, + found_hash=bad_phase_out_hash_result.name, ) return KeyExchangeHashFunctionEvaluation( From fac998b9de45807fa868112e6445c54ad71d7196 Mon Sep 17 00:00:00 2001 From: Sasha Romijn Date: Fri, 3 Jul 2026 15:23:21 +0200 Subject: [PATCH 2/2] refactor --- checks/tasks/tls/scans.py | 54 +++++++++++++++++-------------- checks/tasks/tls/tls_constants.py | 1 + 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/checks/tasks/tls/scans.py b/checks/tasks/tls/scans.py index e3bc4d929..6ffd82e09 100644 --- a/checks/tasks/tls/scans.py +++ b/checks/tasks/tls/scans.py @@ -89,6 +89,7 @@ CERT_RSA_MIN_GOOD_KEY_SIZE, CERT_RSA_MIN_PHASE_OUT_KEY_SIZE, SIGNATURE_ALGORITHMS_BAD_HASH, + SIGNATURE_ALGORITHMS_BAD_HASH_NIDS, SIGNATURE_ALGORITHMS_PHASE_OUT_HASH, TLS_1_3_PROBE_CIPHERS, ) @@ -867,40 +868,43 @@ def test_key_exchange_hash( """ Test key exchange hashes per NCSC 3.3.5. Note that this is not the certificate hash, or TLS cipher hash. - There are few or no hosts that do not meet this requirement. + The initial test is phase_out+bad to keep happy path connections low. """ - bad_phase_out_hash_result = _test_connection_with_limited_sigalgs( + probe_bad_phase_out = _test_connection_with_limited_sigalgs( server_connectivity_info, SIGNATURE_ALGORITHMS_BAD_HASH + SIGNATURE_ALGORITHMS_PHASE_OUT_HASH ) - if bad_phase_out_hash_result: - bad_hash_result = None - log.info(f"key exchange check: negotiated bad or phase_out sigalg ({bad_phase_out_hash_result.name})") - if bad_phase_out_hash_result in [sa[0] for sa in SIGNATURE_ALGORITHMS_PHASE_OUT_HASH]: - log.info("key exchange check: since it was phase_out, recheck with only bad") - bad_hash_result = _test_connection_with_limited_sigalgs( - server_connectivity_info, SIGNATURE_ALGORITHMS_BAD_HASH - ) + if not probe_bad_phase_out: + return KeyExchangeHashFunctionEvaluation( + status=KexHashFuncStatus.good, + score=scoring.WEB_TLS_KEX_HASH_FUNC_GOOD, + ) - if (bad_phase_out_hash_result in [sa[0] for sa in SIGNATURE_ALGORITHMS_BAD_HASH]) or bad_hash_result: - log.info( - f"key exchange check: negotiated bad sigalg ({(bad_hash_result or bad_phase_out_hash_result).name})" - ) - return KeyExchangeHashFunctionEvaluation( - status=KexHashFuncStatus.bad, - score=scoring.WEB_TLS_KEX_HASH_FUNC_BAD, - found_hash=bad_hash_result.name, - ) + if probe_bad_phase_out in SIGNATURE_ALGORITHMS_BAD_HASH_NIDS: + log.info(f"key exchange check: negotiated bad sigalg ({probe_bad_phase_out.name})") + return KeyExchangeHashFunctionEvaluation( + status=KexHashFuncStatus.bad, + score=scoring.WEB_TLS_KEX_HASH_FUNC_BAD, + found_hash=probe_bad_phase_out.name, + ) - log.info(f"key exchange check: negotiated phase_out sigalg ({bad_phase_out_hash_result.name})") + log.info( + f"key exchange check: initial probe negotiated phase_out sigalg ({probe_bad_phase_out.name}), " + "testing with only bad sigalgs" + ) + probe_bad = _test_connection_with_limited_sigalgs(server_connectivity_info, SIGNATURE_ALGORITHMS_BAD_HASH) + if probe_bad: + log.info(f"key exchange check: negotiated bad sigalg ({probe_bad.name})") return KeyExchangeHashFunctionEvaluation( - status=KexHashFuncStatus.phase_out, - score=scoring.WEB_TLS_KEX_HASH_FUNC_OK, - found_hash=bad_phase_out_hash_result.name, + status=KexHashFuncStatus.bad, + score=scoring.WEB_TLS_KEX_HASH_FUNC_BAD, + found_hash=probe_bad.name, ) + log.info(f"key exchange check: negotiated phase_out sigalg ({probe_bad_phase_out.name})") return KeyExchangeHashFunctionEvaluation( - status=KexHashFuncStatus.good, - score=scoring.WEB_TLS_KEX_HASH_FUNC_GOOD, + status=KexHashFuncStatus.phase_out, + score=scoring.WEB_TLS_KEX_HASH_FUNC_OK, + found_hash=probe_bad_phase_out.name, ) diff --git a/checks/tasks/tls/tls_constants.py b/checks/tasks/tls/tls_constants.py index 21619e394..2e15233bb 100644 --- a/checks/tasks/tls/tls_constants.py +++ b/checks/tasks/tls/tls_constants.py @@ -141,6 +141,7 @@ (OpenSslDigestNidEnum.SHA224, OpenSslEvpPkeyEnum.RSA), (OpenSslDigestNidEnum.SHA224, OpenSslEvpPkeyEnum.DSA), ] +SIGNATURE_ALGORITHMS_BAD_HASH_NIDS = {sa[0] for sa in SIGNATURE_ALGORITHMS_BAD_HASH} # Mail servers with an increased connection limit, # matched by substring matching on their hostname.