diff --git a/src/bogo_shim/bogo_shim.cpp b/src/bogo_shim/bogo_shim.cpp index 24add037108..d927e573a19 100644 --- a/src/bogo_shim/bogo_shim.cpp +++ b/src/bogo_shim/bogo_shim.cpp @@ -1004,14 +1004,6 @@ class Shim_Policy final : public Botan::TLS::Policy { if(group == Botan::TLS::Group_Params::HYBRID_X25519_KYBER_768_R3_OQS) { groups.push_back(group); } - - // TODO: once `TLS::Policy::key_exchange_groups()` contains it by - // default, remove this explicit check. - // - // See: https://github.com/randombit/botan/pull/4305 - if(group == Botan::TLS::Group_Params::HYBRID_X25519_ML_KEM_768) { - groups.push_back(group); - } } return groups; diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp index 452817ac1af..4bd669481d2 100644 --- a/src/cli/tls_client.cpp +++ b/src/cli/tls_client.cpp @@ -99,14 +99,17 @@ class Callbacks : public Botan::TLS::Callbacks { void tls_session_activated() override { output() << "Handshake complete\n"; } void tls_session_established(const Botan::TLS::Session_Summary& session) override { - output() << "Handshake complete, " << session.version().to_string() << " using " - << session.ciphersuite().to_string(); + output() << "Handshake complete, " << session.version().to_string() << "\n"; if(const auto& psk = session.external_psk_identity()) { - output() << " (utilized PSK identity: " << maybe_hex_encode(psk.value()) << ")"; + output() << "Utilized PSK identity: " << maybe_hex_encode(psk.value()) << "\n"; } - output() << std::endl; + output() << "Negotiated ciphersuite " << session.ciphersuite().to_string() << "\n"; + + if(auto kex_params = session.kex_parameters()) { + output() << "Key exchange using " << *kex_params << "\n"; + } if(const auto& session_id = session.session_id(); !session_id.empty()) { output() << "Session ID " << Botan::hex_encode(session_id.get()) << "\n"; diff --git a/src/lib/tls/tls_algos.cpp b/src/lib/tls/tls_algos.cpp index 77b564c8866..991e787babd 100644 --- a/src/lib/tls/tls_algos.cpp +++ b/src/lib/tls/tls_algos.cpp @@ -134,6 +134,89 @@ Auth_Method auth_method_from_string(std::string_view str) { throw Invalid_Argument(fmt("Unknown TLS signature method '{}'", str)); } +bool Group_Params::is_available() const { +#if !defined(BOTAN_HAS_X25519) + if(is_x25519()) { + return false; + } + if(is_pqc_hybrid() && pqc_hybrid_ecc() == Group_Params_Code::X25519) { + return false; + } +#endif + +#if !defined(BOTAN_HAS_X448) + if(is_x448()) { + return false; + } + if(is_pqc_hybrid() && pqc_hybrid_ecc() == Group_Params_Code::X448) { + return false; + } +#endif + +#if !defined(BOTAN_HAS_DIFFIE_HELLMAN) + if(is_in_ffdhe_range()) { + return false; + } +#endif + +#if !defined(BOTAN_HAS_KYBER_ROUND3) + if(is_pure_kyber_r3() || is_pqc_hybrid_kyber_r3()) { + return false; + } +#endif + +#if !defined(BOTAN_HAS_ML_KEM) + if(is_pqc_hybrid_ml_kem()) { + return false; + } +#endif + +#if !defined(BOTAN_HAS_FRODOKEM) + if(is_pure_frodokem() || is_pqc_hybrid_frodokem()) { + return false; + } +#endif + + return true; +} + +std::optional Group_Params::pqc_hybrid_ecc() const { + switch(m_code) { + case Group_Params_Code::HYBRID_X25519_ML_KEM_768: + case Group_Params_Code::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE: + case Group_Params_Code::HYBRID_X25519_KYBER_512_R3_OQS: + case Group_Params_Code::HYBRID_X25519_KYBER_768_R3_OQS: + case Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS: + case Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_AES_OQS: + return Group_Params_Code::X25519; + + case Group_Params_Code::HYBRID_X448_KYBER_768_R3_OQS: + case Group_Params_Code::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS: + case Group_Params_Code::HYBRID_X448_eFRODOKEM_976_AES_OQS: + return Group_Params_Code::X448; + + case Group_Params_Code::HYBRID_SECP256R1_ML_KEM_768: + case Group_Params_Code::HYBRID_SECP256R1_KYBER_512_R3_OQS: + case Group_Params_Code::HYBRID_SECP256R1_KYBER_768_R3_OQS: + case Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS: + case Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS: + return Group_Params_Code::SECP256R1; + + case Group_Params_Code::HYBRID_SECP384R1_KYBER_768_R3_OQS: + case Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS: + case Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS: + return Group_Params_Code::SECP384R1; + + case Group_Params_Code::HYBRID_SECP521R1_KYBER_1024_R3_OQS: + case Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS: + case Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS: + return Group_Params_Code::SECP521R1; + + default: + return {}; + } +} + std::optional Group_Params::from_string(std::string_view group_name) { if(group_name == "secp256r1") { return Group_Params::SECP256R1; diff --git a/src/lib/tls/tls_algos.h b/src/lib/tls/tls_algos.h index ab2209f28ce..f7b79aedeb4 100644 --- a/src/lib/tls/tls_algos.h +++ b/src/lib/tls/tls_algos.h @@ -177,6 +177,11 @@ class BOTAN_PUBLIC_API(3, 2) Group_Params final { constexpr uint16_t wire_code() const { return static_cast(m_code); } + /** + * Returns false if this group/KEX is not available in the build configuration + */ + bool is_available() const; + constexpr bool is_x25519() const { return m_code == Group_Params_Code::X25519; } constexpr bool is_x448() const { return m_code == Group_Params_Code::X448; } @@ -198,7 +203,7 @@ class BOTAN_PUBLIC_API(3, 2) Group_Params final { m_code == Group_Params_Code::FFDHE_8192; } - constexpr bool is_pure_kyber() const { + constexpr bool is_pure_kyber_r3() const { return m_code == Group_Params_Code::KYBER_512_R3_OQS || m_code == Group_Params_Code::KYBER_768_R3_OQS || m_code == Group_Params_Code::KYBER_1024_R3_OQS; } @@ -214,37 +219,50 @@ class BOTAN_PUBLIC_API(3, 2) Group_Params final { constexpr bool is_pure_ecc_group() const { return is_x25519() || is_x448() || is_ecdh_named_curve(); } - constexpr bool is_post_quantum() const { return is_pure_kyber() || is_pure_frodokem() || is_pqc_hybrid(); } + constexpr bool is_post_quantum() const { return is_pure_kyber_r3() || is_pure_frodokem() || is_pqc_hybrid(); } - constexpr bool is_pqc_hybrid() const { + constexpr bool is_pqc_hybrid_ml_kem() const { + return m_code == Group_Params_Code::HYBRID_SECP256R1_ML_KEM_768 || + m_code == Group_Params_Code::HYBRID_X25519_ML_KEM_768; + } + + constexpr bool is_pqc_hybrid_kyber_r3() const { BOTAN_DIAGNOSTIC_PUSH BOTAN_DIAGNOSTIC_IGNORE_DEPRECATED_DECLARATIONS - return m_code == Group_Params_Code::HYBRID_SECP256R1_ML_KEM_768 || - m_code == Group_Params_Code::HYBRID_X25519_ML_KEM_768 || - m_code == Group_Params_Code::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE || + return m_code == Group_Params_Code::HYBRID_X25519_KYBER_512_R3_CLOUDFLARE || m_code == Group_Params_Code::HYBRID_X25519_KYBER_512_R3_OQS || m_code == Group_Params_Code::HYBRID_X25519_KYBER_768_R3_OQS || m_code == Group_Params_Code::HYBRID_X448_KYBER_768_R3_OQS || - m_code == Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS || + m_code == Group_Params_Code::HYBRID_SECP256R1_KYBER_512_R3_OQS || + m_code == Group_Params_Code::HYBRID_SECP256R1_KYBER_768_R3_OQS || + m_code == Group_Params_Code::HYBRID_SECP384R1_KYBER_768_R3_OQS || + m_code == Group_Params_Code::HYBRID_SECP521R1_KYBER_1024_R3_OQS; + + BOTAN_DIAGNOSTIC_POP + } + + constexpr bool is_pqc_hybrid_frodokem() const { + return m_code == Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_SHAKE_OQS || m_code == Group_Params_Code::HYBRID_X25519_eFRODOKEM_640_AES_OQS || m_code == Group_Params_Code::HYBRID_X448_eFRODOKEM_976_SHAKE_OQS || m_code == Group_Params_Code::HYBRID_X448_eFRODOKEM_976_AES_OQS || - m_code == Group_Params_Code::HYBRID_SECP256R1_KYBER_512_R3_OQS || - m_code == Group_Params_Code::HYBRID_SECP256R1_KYBER_768_R3_OQS || m_code == Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_SHAKE_OQS || m_code == Group_Params_Code::HYBRID_SECP256R1_eFRODOKEM_640_AES_OQS || - m_code == Group_Params_Code::HYBRID_SECP384R1_KYBER_768_R3_OQS || m_code == Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_SHAKE_OQS || m_code == Group_Params_Code::HYBRID_SECP384R1_eFRODOKEM_976_AES_OQS || - m_code == Group_Params_Code::HYBRID_SECP521R1_KYBER_1024_R3_OQS || m_code == Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_SHAKE_OQS || m_code == Group_Params_Code::HYBRID_SECP521R1_eFRODOKEM_1344_AES_OQS; + } - BOTAN_DIAGNOSTIC_POP + // If this is a pqc hybrid group, returns the ECC ID + std::optional pqc_hybrid_ecc() const; + + constexpr bool is_pqc_hybrid() const { + return is_pqc_hybrid_ml_kem() || is_pqc_hybrid_kyber_r3() || is_pqc_hybrid_frodokem(); } - constexpr bool is_kem() const { return is_pure_kyber() || is_pure_frodokem() || is_pqc_hybrid(); } + constexpr bool is_kem() const { return is_pure_kyber_r3() || is_pure_frodokem() || is_pqc_hybrid(); } // Returns std::nullopt if the param has no known name std::optional to_string() const; diff --git a/src/lib/tls/tls_callbacks.cpp b/src/lib/tls/tls_callbacks.cpp index 13dcc06bed5..c5955801ccc 100644 --- a/src/lib/tls/tls_callbacks.cpp +++ b/src/lib/tls/tls_callbacks.cpp @@ -229,7 +229,7 @@ std::unique_ptr TLS::Callbacks::tls_deserialize_peer_public_key( #endif #if defined(BOTAN_HAS_KYBER) - if(group_params.is_pure_kyber()) { + if(group_params.is_pure_kyber_r3()) { return std::make_unique(key_bits, KyberMode(group_params.to_string().value())); } #endif @@ -245,7 +245,7 @@ std::unique_ptr TLS::Callbacks::tls_deserialize_peer_public_key( std::unique_ptr TLS::Callbacks::tls_kem_generate_key(TLS::Group_Params group, RandomNumberGenerator& rng) { #if defined(BOTAN_HAS_KYBER) - if(group.is_pure_kyber()) { + if(group.is_pure_kyber_r3()) { return std::make_unique(rng, KyberMode(group.to_string().value())); } #endif diff --git a/src/lib/tls/tls_policy.cpp b/src/lib/tls/tls_policy.cpp index 2e2c4d05580..cb0a8ccf431 100644 --- a/src/lib/tls/tls_policy.cpp +++ b/src/lib/tls/tls_policy.cpp @@ -128,8 +128,30 @@ Group_Params Policy::choose_key_exchange_group(const std::vector& const std::vector our_groups = key_exchange_groups(); - // Prefer groups that were offered by the peer for the sake of saving - // an additional round trip. For TLS 1.2, this won't be used. + const bool client_supports_pqc = std::any_of( + supported_by_peer.begin(), supported_by_peer.end(), [](const Group_Params& g) { return g.is_post_quantum(); }); + + if(client_supports_pqc) { + // If the client supports PQ and sent us a PQ key share we can use, take it + for(auto g : offered_by_peer) { + if(g.is_post_quantum() && value_exists(our_groups, g)) { + return g; + } + } + + // If the client supports PQ but not a PQ key share, still prefer PQ + for(auto g : supported_by_peer) { + if(g.is_post_quantum() && value_exists(our_groups, g)) { + return g; + } + } + } + + // If we are here, the client did not offer any (mutually supported) + // post quantum algorithms + + // Prefer groups that were offered by the peer, for the sake of saving an + // additional round trip. For TLS 1.2, this won't be used. for(auto g : offered_by_peer) { if(value_exists(our_groups, g)) { return g; @@ -161,31 +183,63 @@ Group_Params Policy::default_dh_group() const { } std::vector Policy::key_exchange_groups() const { - // Default list is ordered by performance return { + // clang-format off +#if defined(BOTAN_HAS_TLS_13_PQC) && defined(BOTAN_HAS_ML_KEM) && defined(BOTAN_HAS_X25519) + Group_Params::HYBRID_X25519_ML_KEM_768, +#endif + #if defined(BOTAN_HAS_X25519) Group_Params::X25519, #endif + + Group_Params::SECP256R1, + #if defined(BOTAN_HAS_X448) - Group_Params::X448, + Group_Params::X448, #endif - Group_Params::SECP256R1, Group_Params::BRAINPOOL256R1, Group_Params::SECP384R1, Group_Params::BRAINPOOL384R1, - Group_Params::SECP521R1, Group_Params::BRAINPOOL512R1, + Group_Params::SECP384R1, + Group_Params::SECP521R1, - Group_Params::FFDHE_2048, Group_Params::FFDHE_3072, Group_Params::FFDHE_4096, Group_Params::FFDHE_6144, - Group_Params::FFDHE_8192, + Group_Params::BRAINPOOL256R1, + Group_Params::BRAINPOOL384R1, + Group_Params::BRAINPOOL512R1, + + Group_Params::FFDHE_2048, + Group_Params::FFDHE_3072, + + // clang-format on }; } std::vector Policy::key_exchange_groups_to_offer() const { - // by default, we offer a key share for the most-preferred group, only - std::vector groups_to_offer; - const auto supported_groups = key_exchange_groups(); - if(!supported_groups.empty()) { - groups_to_offer.push_back(supported_groups.front()); + /* + By default, we offer a key share for the most-preferred pure ECC group + by default, if any pure ECC group is enabled in the policy. + + We skip PQC (or hybrids) since the keys are much larger and they are not + yet widely supported; the most common case is we waste a lot of packet + space sending a key share that the peer will ignore. + + Likewise we skip DH since the keys are large + + However if no pure ECC is enabled then we offer the first enabled + key exchange group, no matter what kind it is. + */ + const auto kex_groups = key_exchange_groups(); + + for(auto group : kex_groups) { + if(group.is_pure_ecc_group()) { + return {group}; + } + } + + if(kex_groups.empty()) { + return {}; + } else { + return {kex_groups[0]}; } - return groups_to_offer; } size_t Policy::minimum_dh_group_size() const { @@ -651,7 +705,7 @@ void Policy::print(std::ostream& o) const { } o << "maximum_session_tickets_per_client_hello = " << maximum_session_tickets_per_client_hello() << '\n'; o << "session_ticket_lifetime = " << session_ticket_lifetime().count() << '\n'; - o << "reuse_session_tickets = " << reuse_session_tickets() << '\n'; + print_bool(o, "reuse_session_tickets", reuse_session_tickets()); o << "new_session_tickets_upon_handshake_success = " << new_session_tickets_upon_handshake_success() << '\n'; o << "minimum_dh_group_size = " << minimum_dh_group_size() << '\n'; o << "minimum_ecdh_group_size = " << minimum_ecdh_group_size() << '\n'; diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp index d4e366b77dc..a1b38dfd8e3 100644 --- a/src/lib/tls/tls_session.cpp +++ b/src/lib/tls/tls_session.cpp @@ -103,6 +103,40 @@ Session_Summary::Session_Summary(const Session_Base& base, #if defined(BOTAN_HAS_TLS_13) +namespace { + +std::string tls13_kex_to_string(bool psk, const Named_Group& group) { + if(psk) { + if(group.is_dh_named_group()) { + return kex_method_to_string(Kex_Algo::DHE_PSK); + } else if(group.is_pure_ecc_group()) { + return kex_method_to_string(Kex_Algo::ECDHE_PSK); + } else if(group.is_kem() && !group.is_pqc_hybrid()) { + return kex_method_to_string(Kex_Algo::KEM_PSK); + } else if(group.is_pqc_hybrid()) { + return kex_method_to_string(Kex_Algo::HYBRID_PSK); + } else if(auto s = group.to_string()) { + return *s; + } + } else { + if(group.is_dh_named_group()) { + return kex_method_to_string(Kex_Algo::DH); + } else if(group.is_pure_ecc_group()) { + return kex_method_to_string(Kex_Algo::ECDH); + } else if(group.is_kem() && !group.is_pqc_hybrid()) { + return kex_method_to_string(Kex_Algo::KEM); + } else if(group.is_pqc_hybrid()) { + return kex_method_to_string(Kex_Algo::HYBRID); + } else if(auto s = group.to_string()) { + return *s; + } + } + + return kex_method_to_string(Kex_Algo::UNDEFINED); +} + +} // namespace + Session_Summary::Session_Summary(const Server_Hello_13& server_hello, Connection_Side side, std::vector peer_certs, @@ -138,39 +172,22 @@ Session_Summary::Session_Summary(const Server_Hello_13& server_hello, // In TLS 1.3 the key exchange algorithm is not negotiated in the ciphersuite // anymore. This provides a compatible identifier for applications to use. - m_kex_algo = kex_method_to_string([&] { - if(psk_used() || was_resumption()) { - if(const auto keyshare = server_hello.extensions().get()) { - const auto group = keyshare->selected_group(); - if(group.is_dh_named_group()) { - return Kex_Algo::DHE_PSK; - } else if(group.is_ecdh_named_curve() || group.is_x25519() || group.is_x448()) { - return Kex_Algo::ECDHE_PSK; - } else if(group.is_pure_kyber()) { - return Kex_Algo::KEM_PSK; - } else if(group.is_pqc_hybrid()) { - return Kex_Algo::HYBRID_PSK; - } - } else { - return Kex_Algo::PSK; - } + + const auto group = [&]() -> std::optional { + if(const auto keyshare = server_hello.extensions().get()) { + return keyshare->selected_group(); } else { - const auto keyshare = server_hello.extensions().get(); - BOTAN_ASSERT_NONNULL(keyshare); - const auto group = keyshare->selected_group(); - if(group.is_dh_named_group()) { - return Kex_Algo::DH; - } else if(group.is_ecdh_named_curve() || group.is_x25519() || group.is_x448()) { - return Kex_Algo::ECDH; - } else if(group.is_pure_kyber()) { - return Kex_Algo::KEM; - } else if(group.is_pqc_hybrid()) { - return Kex_Algo::HYBRID; - } + return {}; } - - return Kex_Algo::UNDEFINED; - }()); + }(); + + if(group.has_value()) { + m_kex_parameters = group->to_string(); + m_kex_algo = tls13_kex_to_string(psk_used() || was_resumption(), group.value()); + } else { + BOTAN_ASSERT(psk_used() || was_resumption(), "Missing key share during non-PSK negotation"); + m_kex_algo = kex_method_to_string(Kex_Algo::PSK); + } } #endif diff --git a/src/lib/tls/tls_session.h b/src/lib/tls/tls_session.h index f3a4c803548..90d79662ea6 100644 --- a/src/lib/tls/tls_session.h +++ b/src/lib/tls/tls_session.h @@ -288,6 +288,8 @@ class BOTAN_PUBLIC_API(3, 0) Session_Summary : public Session_Base { std::string kex_algo() const { return m_kex_algo; } + const std::optional& kex_parameters() const { return m_kex_parameters; } + std::string cipher_algo() const { return ciphersuite().cipher_algo(); } std::string mac_algo() const { return ciphersuite().mac_algo(); } @@ -324,6 +326,7 @@ class BOTAN_PUBLIC_API(3, 0) Session_Summary : public Session_Base { bool m_was_resumption; std::string m_kex_algo; + std::optional m_kex_parameters; }; /** diff --git a/src/lib/tls/tls_text_policy.cpp b/src/lib/tls/tls_text_policy.cpp index 3f3b15060c3..4026a73ff98 100644 --- a/src/lib/tls/tls_text_policy.cpp +++ b/src/lib/tls/tls_text_policy.cpp @@ -233,14 +233,9 @@ std::vector Text_Policy::read_group_list(std::string_view group_st for(const auto& group_name : split_on(group_str, ' ')) { Group_Params group_id = Group_Params::from_string(group_name).value_or(Group_Params::NONE); -#if !defined(BOTAN_HAS_X25519) - if(group_id == Group_Params::X25519) + if(!group_id.is_available()) { continue; -#endif -#if !defined(BOTAN_HAS_X448) - if(group_id == Group_Params::X448) - continue; -#endif + } if(group_id == Group_Params::NONE) { try { diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py index 320a470b794..896cdc8888e 100755 --- a/src/scripts/test_cli.py +++ b/src/scripts/test_cli.py @@ -1150,10 +1150,10 @@ def __init__(self, name, protocol_version, policy, **kwargs): TestConfig("PSK TLS 1.2", "1.2", "allow_tls12=true\nallow_tls13=false\nkey_exchange_methods=ECDHE_PSK\n", psk=psk, psk_identity=psk_identity, - stdout_regex=f'Handshake complete, TLS v1\\.2.*utilized PSK identity: {psk_identity}.*'), + stdout_regex=f'Handshake complete, TLS v1\\.2.*\nUtilized PSK identity: {psk_identity}.*'), TestConfig("PSK TLS 1.3", "1.3", "allow_tls12=false\nallow_tls13=true\nkey_exchange_methods=ECDHE_PSK\n", psk=psk, psk_identity=psk_identity, - stdout_regex=f'Handshake complete, TLS v1\\.3.*utilized PSK identity: {psk_identity}.*'), + stdout_regex=f'Handshake complete, TLS v1\\.3.*\nUtilized PSK identity: {psk_identity}.*'), TestConfig("Kyber KEM", "1.3", "allow_tls12=false\nallow_tls13=true\nkey_exchange_groups=Kyber-512-r3"), TestConfig("Hybrid PQ/T", "1.3", "allow_tls12=false\nallow_tls13=true\nkey_exchange_groups=x25519/Kyber-512-r3"), diff --git a/src/tests/data/tls-policy/datagram.txt b/src/tests/data/tls-policy/datagram.txt index d583c51faef..2122bea5db8 100644 --- a/src/tests/data/tls-policy/datagram.txt +++ b/src/tests/data/tls-policy/datagram.txt @@ -1,23 +1,28 @@ -allow_tls10 = false -allow_tls11 = false allow_tls12 = false allow_tls13 = false -allow_dtls10 = false allow_dtls12 = true +allow_ssl_key_log_file = false ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM macs = AEAD signature_hashes = SHA-512 SHA-384 SHA-256 signature_methods = ECDSA RSA key_exchange_methods = ECDH DH -key_exchange_groups = x25519 x448 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +key_exchange_groups = x25519/ML-KEM-768 x25519 secp256r1 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false hide_unknown_users = false server_uses_own_ciphersuite_preferences = true negotiate_encrypt_then_mac = true +support_cert_status_message = true +tls_13_middlebox_compatibility_mode = true +accepted_client_certificate_types = X509 +accepted_server_certificate_types = X509 +hash_hello_random = true +maximum_session_tickets_per_client_hello = 1 session_ticket_lifetime = 86400 -dh_group = modp/ietf/2048 +reuse_session_tickets = false +new_session_tickets_upon_handshake_success = 1 minimum_dh_group_size = 2048 minimum_ecdh_group_size = 255 minimum_rsa_bits = 2048 diff --git a/src/tests/data/tls-policy/default.txt b/src/tests/data/tls-policy/default.txt index b5ec58c28f2..003a31ffd99 100644 --- a/src/tests/data/tls-policy/default.txt +++ b/src/tests/data/tls-policy/default.txt @@ -1,15 +1,12 @@ -allow_tls10 = false -allow_tls11 = false allow_tls12 = true allow_tls13 = false -allow_dtls10 = false allow_dtls12 = true ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM macs = AEAD SHA-256 SHA-384 SHA-1 signature_hashes = SHA-512 SHA-384 SHA-256 signature_methods = ECDSA RSA key_exchange_methods = ECDH DH -key_exchange_groups = x25519 x448 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +key_exchange_groups = x25519/ML-KEM-768 x25519 secp256r1 x25519/ML-KEM-768 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false diff --git a/src/tests/data/tls-policy/default_tls13.txt b/src/tests/data/tls-policy/default_tls13.txt index dfdc84dd5a0..68900f7404d 100644 --- a/src/tests/data/tls-policy/default_tls13.txt +++ b/src/tests/data/tls-policy/default_tls13.txt @@ -1,22 +1,28 @@ -allow_tls10 = false -allow_tls11 = false allow_tls12 = true allow_tls13 = true -allow_dtls10 = false allow_dtls12 = true +allow_ssl_key_log_file = false ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM macs = AEAD SHA-256 SHA-384 SHA-1 signature_hashes = SHA-512 SHA-384 SHA-256 signature_methods = ECDSA RSA key_exchange_methods = ECDH DH -key_exchange_groups = x25519 x448 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +key_exchange_groups = x25519/ML-KEM-768 x25519 secp256r1 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false hide_unknown_users = false server_uses_own_ciphersuite_preferences = true negotiate_encrypt_then_mac = true +support_cert_status_message = true +tls_13_middlebox_compatibility_mode = true +accepted_client_certificate_types = X509 +accepted_server_certificate_types = X509 +hash_hello_random = true +maximum_session_tickets_per_client_hello = 1 session_ticket_lifetime = 86400 +reuse_session_tickets = false +new_session_tickets_upon_handshake_success = 1 minimum_dh_group_size = 2048 minimum_ecdh_group_size = 255 minimum_rsa_bits = 2048 diff --git a/src/tests/data/tls-policy/strict.txt b/src/tests/data/tls-policy/strict.txt index e21c0000c77..4b48b6951f3 100644 --- a/src/tests/data/tls-policy/strict.txt +++ b/src/tests/data/tls-policy/strict.txt @@ -1,22 +1,28 @@ -allow_tls10 = false -allow_tls11 = false allow_tls12 = true allow_tls13 = false -allow_dtls10 = false allow_dtls12 = true +allow_ssl_key_log_file = false ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM macs = AEAD signature_hashes = SHA-512 SHA-384 signature_methods = ECDSA RSA key_exchange_methods = ECDH -key_exchange_groups = x25519 x448 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +key_exchange_groups = x25519/ML-KEM-768 x25519 secp256r1 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false hide_unknown_users = false server_uses_own_ciphersuite_preferences = true negotiate_encrypt_then_mac = true +support_cert_status_message = true +tls_13_middlebox_compatibility_mode = true +accepted_client_certificate_types = X509 +accepted_server_certificate_types = X509 +hash_hello_random = true +maximum_session_tickets_per_client_hello = 1 session_ticket_lifetime = 86400 +reuse_session_tickets = false +new_session_tickets_upon_handshake_success = 1 minimum_dh_group_size = 2048 minimum_ecdh_group_size = 255 minimum_rsa_bits = 2048 diff --git a/src/tests/data/tls-policy/strict_tls13.txt b/src/tests/data/tls-policy/strict_tls13.txt index 28fab2edb6a..3ac5b5335f0 100644 --- a/src/tests/data/tls-policy/strict_tls13.txt +++ b/src/tests/data/tls-policy/strict_tls13.txt @@ -1,22 +1,28 @@ -allow_tls10 = false -allow_tls11 = false allow_tls12 = true allow_tls13 = true -allow_dtls10 = false allow_dtls12 = true +allow_ssl_key_log_file = false ciphers = ChaCha20Poly1305 AES-256/GCM AES-128/GCM macs = AEAD signature_hashes = SHA-512 SHA-384 signature_methods = ECDSA RSA key_exchange_methods = ECDH -key_exchange_groups = x25519 x448 secp256r1 brainpool256r1 secp384r1 brainpool384r1 secp521r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 ffdhe/ietf/4096 ffdhe/ietf/6144 ffdhe/ietf/8192 +key_exchange_groups = x25519/ML-KEM-768 x25519 secp256r1 x448 secp384r1 secp521r1 brainpool256r1 brainpool384r1 brainpool512r1 ffdhe/ietf/2048 ffdhe/ietf/3072 allow_insecure_renegotiation = false include_time_in_hello_random = true allow_server_initiated_renegotiation = false hide_unknown_users = false server_uses_own_ciphersuite_preferences = true negotiate_encrypt_then_mac = true +support_cert_status_message = true +tls_13_middlebox_compatibility_mode = true +accepted_client_certificate_types = X509 +accepted_server_certificate_types = X509 +hash_hello_random = true +maximum_session_tickets_per_client_hello = 1 session_ticket_lifetime = 86400 +reuse_session_tickets = false +new_session_tickets_upon_handshake_success = 1 minimum_dh_group_size = 2048 minimum_ecdh_group_size = 255 minimum_rsa_bits = 2048 diff --git a/src/tests/test_tls.cpp b/src/tests/test_tls.cpp index 6f8c5a41b2d..d8d58a8b094 100644 --- a/src/tests/test_tls.cpp +++ b/src/tests/test_tls.cpp @@ -300,14 +300,16 @@ class Test_TLS_Policy_Text : public Test { for(const std::string& policy : policies) { const std::string from_policy_obj = tls_policy_string(policy); - std::string from_file = + #if defined(BOTAN_HAS_TLS_13) - read_tls_policy(policy + (policy == "default" || policy == "strict" ? "_tls13" : "")); + const std::string policy_file = policy + (policy == "default" || policy == "strict" ? "_tls13" : ""); #else - read_tls_policy(policy); + const std::string policy_file = policy; #endif - result.test_eq("Values for TLS " + policy + " policy", from_file, from_policy_obj); + const std::string from_file = read_tls_policy(policy_file); + + result.test_eq("Values for TLS policy from " + policy_file, from_policy_obj, from_file); } return {result}; diff --git a/src/tests/unit_tls_policy.cpp b/src/tests/unit_tls_policy.cpp index 2262ebcdffa..8dba5a5c5c7 100644 --- a/src/tests/unit_tls_policy.cpp +++ b/src/tests/unit_tls_policy.cpp @@ -140,15 +140,27 @@ class TLS_Policy_Unit_Tests final : public Test { Botan::TLS::Policy default_policy; result.test_eq( "default TLS Policy offers exactly one", default_policy.key_exchange_groups_to_offer().size(), 1); - result.confirm( - "default TLS Policy offers preferred group", - default_policy.key_exchange_groups().front() == default_policy.key_exchange_groups_to_offer().front()); + + auto first_pure_ecc_group = + [](std::span groups) -> std::optional { + for(auto& group : groups) { + if(group.is_pure_ecc_group()) { + return {group}; + } + } + return {}; + }; + + result.confirm("default TLS Policy offers preferred pure ECC group", + first_pure_ecc_group(default_policy.key_exchange_groups()).value() == + default_policy.key_exchange_groups_to_offer().front()); using TP = Botan::TLS::Text_Policy; result.test_eq("default behaviour from text policy (size)", TP("").key_exchange_groups_to_offer().size(), 1); - result.confirm("default behaviour from text policy (preferred)", - TP("").key_exchange_groups().front() == TP("").key_exchange_groups_to_offer().front()); + result.test_eq("default behaviour from text policy (preferred)", + first_pure_ecc_group(TP("").key_exchange_groups()).value().to_string().value(), + TP("").key_exchange_groups_to_offer().front().to_string().value()); result.confirm("no offerings", TP("key_exchange_groups_to_offer = none").key_exchange_groups_to_offer().empty());