From d708030ecb20cebc548fced882141cc7f03a8ac1 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sun, 7 Jan 2018 16:57:55 -0500 Subject: For TLS client auth add callback giving list of trusted CA names Fixes #1261 --- src/lib/tls/credentials_manager.cpp | 11 ++++++++++- src/lib/tls/credentials_manager.h | 25 +++++++++++++++++++++++++ src/lib/tls/tls_client.cpp | 7 ++++--- src/lib/tls/tls_messages.h | 2 +- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/lib/tls/credentials_manager.cpp b/src/lib/tls/credentials_manager.cpp index 9a7e25ddf..158d6f77c 100644 --- a/src/lib/tls/credentials_manager.cpp +++ b/src/lib/tls/credentials_manager.cpp @@ -59,6 +59,15 @@ bool Credentials_Manager::srp_verifier(const std::string&, return false; } +std::vector Credentials_Manager::find_cert_chain( + const std::vector& key_types, + const std::vector&, + const std::string& type, + const std::string& context) + { + return cert_chain(key_types, type, context); + } + std::vector Credentials_Manager::cert_chain( const std::vector&, const std::string&, @@ -74,7 +83,7 @@ std::vector Credentials_Manager::cert_chain_single_type( { std::vector cert_types; cert_types.push_back(cert_key_type); - return cert_chain(cert_types, type, context); + return find_cert_chain(cert_types, std::vector(), type, context); } Private_Key* Credentials_Manager::private_key_for(const X509_Certificate&, diff --git a/src/lib/tls/credentials_manager.h b/src/lib/tls/credentials_manager.h index e544fd51d..627894a87 100644 --- a/src/lib/tls/credentials_manager.h +++ b/src/lib/tls/credentials_manager.h @@ -16,6 +16,7 @@ namespace Botan { +class X509_DN; class BigInt; /** @@ -55,6 +56,30 @@ class BOTAN_PUBLIC_API(2,0) Credentials_Manager * "DSA", "ECDSA", etc), or empty if there * is no preference by the caller. * + * @param acceptable_CAs the CAs the requestor will accept (possibly empty) + * @param type specifies the type of operation occurring + * @param context specifies a context relative to type. + */ + virtual std::vector find_cert_chain( + const std::vector& cert_key_types, + const std::vector& acceptable_CAs, + const std::string& type, + const std::string& context); + + /** + * Return a cert chain we can use, ordered from leaf to root, + * or else an empty vector. + * + * This virtual function is deprecated, and will be removed in a + * future release. Use (and override) find_cert_chain instead. + * + * It is assumed that the caller can get the private key of the + * leaf with private_key_for + * + * @param cert_key_types specifies the key types desired ("RSA", + * "DSA", "ECDSA", etc), or empty if there + * is no preference by the caller. + * * @param type specifies the type of operation occurring * * @param context specifies a context relative to type. diff --git a/src/lib/tls/tls_client.cpp b/src/lib/tls/tls_client.cpp index 5f84481ac..4647e11cb 100644 --- a/src/lib/tls/tls_client.cpp +++ b/src/lib/tls/tls_client.cpp @@ -522,9 +522,10 @@ void Client::process_handshake_msg(const Handshake_State* active_state, const auto& types = state.cert_req()->acceptable_cert_types(); std::vector client_certs = - m_creds.cert_chain(types, - "tls-client", - m_info.hostname()); + m_creds.find_cert_chain(types, + state.cert_req()->acceptable_CAs(), + "tls-client", + m_info.hostname()); state.client_certs(new Certificate(state.handshake_io(), state.hash(), diff --git a/src/lib/tls/tls_messages.h b/src/lib/tls/tls_messages.h index 75e65fa7f..cd06517d7 100644 --- a/src/lib/tls/tls_messages.h +++ b/src/lib/tls/tls_messages.h @@ -412,7 +412,7 @@ class BOTAN_UNSTABLE_API Certificate_Req final : public Handshake_Message const std::vector& acceptable_cert_types() const { return m_cert_key_types; } - std::vector acceptable_CAs() const { return m_names; } + const std::vector& acceptable_CAs() const { return m_names; } std::vector > supported_algos() const { return m_supported_algos; } -- cgit v1.2.3 From ca32f9fd4128bf8ab4e8b78a16c44777bf7b041f Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sat, 27 Jan 2018 14:23:42 -0500 Subject: Add tests for server passing CA names for client auth --- src/tests/unit_tls.cpp | 885 ++++++++++++++++--------------------------------- 1 file changed, 289 insertions(+), 596 deletions(-) diff --git a/src/tests/unit_tls.cpp b/src/tests/unit_tls.cpp index ae2146304..8368445e0 100644 --- a/src/tests/unit_tls.cpp +++ b/src/tests/unit_tls.cpp @@ -105,13 +105,17 @@ class Credentials_Manager_Test final : public Botan::Credentials_Manager return v; } - std::vector cert_chain( + std::vector find_cert_chain( const std::vector& cert_key_types, + const std::vector& acceptable_CAs, const std::string& type, const std::string& context) override { std::vector chain; + if(m_acceptable_cas.empty()) + m_acceptable_cas = acceptable_CAs; + if(type == "tls-server" || (type == "tls-client" && m_provides_client_certs)) { for(auto const& key_type : cert_key_types) @@ -189,6 +193,8 @@ class Credentials_Manager_Test final : public Botan::Credentials_Manager throw Test_Error("No PSK set for " + type + "/" + context); } + const std::vector& get_acceptable_cas() const { return m_acceptable_cas; } + private: Botan::X509_Certificate m_rsa_cert, m_rsa_ca; std::unique_ptr m_rsa_key; @@ -201,6 +207,7 @@ class Credentials_Manager_Test final : public Botan::Credentials_Manager std::unique_ptr m_dsa_crl; std::vector> m_stores; bool m_provides_client_certs; + std::vector m_acceptable_cas; }; Botan::Credentials_Manager* @@ -289,41 +296,196 @@ create_creds(Botan::RandomNumberGenerator& rng, return cmt; } -std::function queue_inserter(std::vector& q) +class Test_TLS_Alert_Strings : public Test { - return [&](const uint8_t buf[], size_t sz) { q.insert(q.end(), buf, buf + sz); }; - } + public: + std::vector run() override + { + Test::Result result("TLS::Alert::type_string"); + + const std::vector alert_types = + { + Botan::TLS::Alert::CLOSE_NOTIFY, + Botan::TLS::Alert::UNEXPECTED_MESSAGE, + Botan::TLS::Alert::BAD_RECORD_MAC, + Botan::TLS::Alert::DECRYPTION_FAILED, + Botan::TLS::Alert::RECORD_OVERFLOW, + Botan::TLS::Alert::DECOMPRESSION_FAILURE, + Botan::TLS::Alert::HANDSHAKE_FAILURE, + Botan::TLS::Alert::NO_CERTIFICATE, + Botan::TLS::Alert::BAD_CERTIFICATE, + Botan::TLS::Alert::UNSUPPORTED_CERTIFICATE, + Botan::TLS::Alert::CERTIFICATE_REVOKED, + Botan::TLS::Alert::CERTIFICATE_EXPIRED, + Botan::TLS::Alert::CERTIFICATE_UNKNOWN, + Botan::TLS::Alert::ILLEGAL_PARAMETER, + Botan::TLS::Alert::UNKNOWN_CA, + Botan::TLS::Alert::ACCESS_DENIED, + Botan::TLS::Alert::DECODE_ERROR, + Botan::TLS::Alert::DECRYPT_ERROR, + Botan::TLS::Alert::EXPORT_RESTRICTION, + Botan::TLS::Alert::PROTOCOL_VERSION, + Botan::TLS::Alert::INSUFFICIENT_SECURITY, + Botan::TLS::Alert::INTERNAL_ERROR, + Botan::TLS::Alert::INAPPROPRIATE_FALLBACK, + Botan::TLS::Alert::USER_CANCELED, + Botan::TLS::Alert::NO_RENEGOTIATION, + Botan::TLS::Alert::UNSUPPORTED_EXTENSION, + Botan::TLS::Alert::CERTIFICATE_UNOBTAINABLE, + Botan::TLS::Alert::UNRECOGNIZED_NAME, + Botan::TLS::Alert::BAD_CERTIFICATE_STATUS_RESPONSE, + Botan::TLS::Alert::BAD_CERTIFICATE_HASH_VALUE, + Botan::TLS::Alert::UNKNOWN_PSK_IDENTITY, + Botan::TLS::Alert:: NO_APPLICATION_PROTOCOL, + }; + + std::set seen; + + for(auto alert : alert_types) + { + const std::string str = Botan::TLS::Alert(alert).type_string(); + result.test_eq("No duplicate strings", seen.count(str), 0); + seen.insert(str); + } + + Botan::TLS::Alert unknown_alert = Botan::TLS::Alert({01, 66}); + + result.test_eq("Unknown alert str", unknown_alert.type_string(), "unrecognized_alert_66"); + + return {result}; + } + }; -void print_alert(Botan::TLS::Alert) +BOTAN_REGISTER_TEST("tls_alert_strings", Test_TLS_Alert_Strings); + +class Test_TLS_Ciphersuites : public Test { - } + public: + std::vector run() override + { + Test::Result result("TLS::Ciphersuite"); + + for(size_t csuite_id = 0; csuite_id <= 0xFFFF; ++csuite_id) + { + Botan::TLS::Ciphersuite ciphersuite = Botan::TLS::Ciphersuite::by_id(csuite_id); + + if(ciphersuite.valid()) + { + result.test_eq("Valid Ciphersuite is not SCSV", Botan::TLS::Ciphersuite::is_scsv(csuite_id), false); -void alert_cb_with_data(Botan::TLS::Alert, const uint8_t[], size_t) + if(ciphersuite.cbc_ciphersuite() == false) + { + result.test_eq("Expected MAC name for AEAD ciphersuites", ciphersuite.mac_algo(), "AEAD"); + } + else + { + result.test_eq("MAC algo and PRF algo same for CBC suites", ciphersuite.prf_algo(), ciphersuite.mac_algo()); + } + + // TODO more tests here + } + } + + return {result}; + } + }; + +BOTAN_REGISTER_TEST("tls_ciphersuites", Test_TLS_Ciphersuites); + +class Test_TLS_Policy_Test : public Test { - } + public: + std::vector run() override + { + Test::Result result("TLS Policy"); + + const std::vector policies = { "default", "suiteb", "strict", "datagram", "bsi" }; + + for(std::string policy : policies) + { + result.test_eq("Values for TLS " + policy + " policy", + tls_policy_string(policy), + read_tls_policy(policy)); + } + + return {result}; + } + + private: + std::string read_tls_policy(const std::string& policy_str) + { + const std::string fspath = Test::data_file("tls-policy/" + policy_str + ".txt"); + + std::ifstream is(fspath.c_str()); + if(!is.good()) + { + throw Test_Error("Missing policy file " + fspath); + } + + Botan::TLS::Text_Policy policy(is); + return policy.to_string(); + } + + std::string tls_policy_string(const std::string& policy_str) + { + std::unique_ptr policy; + if(policy_str == "default") + { + policy.reset(new Botan::TLS::Policy); + } + else if(policy_str == "suiteb") + { + policy.reset(new Botan::TLS::NSA_Suite_B_128); + } + else if(policy_str == "bsi") + { + policy.reset(new Botan::TLS::BSI_TR_02102_2); + } + else if(policy_str == "strict") + { + policy.reset(new Botan::TLS::Strict_Policy); + } + else if(policy_str == "datagram") + { + policy.reset(new Botan::TLS::Datagram_Policy); + } + else + { + throw Test_Error("Unknown TLS policy type '" + policy_str + "'"); + } + + return policy->to_string(); + } + }; + +BOTAN_REGISTER_TEST("tls_policy_test", Test_TLS_Policy_Test); class TLS_Handshake_Test final { public: - TLS_Handshake_Test(Botan::TLS::Protocol_Version offer_version, + TLS_Handshake_Test(const std::string& test_descr, + Botan::TLS::Protocol_Version offer_version, Botan::Credentials_Manager& creds, const Botan::TLS::Policy& client_policy, const Botan::TLS::Policy& server_policy, Botan::RandomNumberGenerator& rng, Botan::TLS::Session_Manager& client_sessions, - Botan::TLS::Session_Manager& server_sessions) : + Botan::TLS::Session_Manager& server_sessions, + bool expect_client_auth) : m_offer_version(offer_version), - m_results(offer_version.to_string()), // TODO descriptive constructor arg + m_results(test_descr), m_creds(creds), m_client_policy(client_policy), m_client_sessions(client_sessions), - m_rng(rng) + m_rng(rng), + m_client_auth(expect_client_auth) { m_server_cb.reset(new Test_Callbacks(m_results, offer_version, m_s2c, m_server_recv)); m_client_cb.reset(new Test_Callbacks(m_results, offer_version, m_c2s, m_client_recv)); m_server.reset( - new Botan::TLS::Server(*m_server_cb, server_sessions, m_creds, server_policy, m_rng) + new Botan::TLS::Server(*m_server_cb, server_sessions, m_creds, server_policy, m_rng, + offer_version.is_datagram_protocol()) ); } @@ -470,6 +632,8 @@ class TLS_Handshake_Test final std::unique_ptr m_server_cb; std::unique_ptr m_server; + const bool m_client_auth; + std::vector m_c2s, m_s2c, m_client_recv, m_server_recv; }; @@ -482,14 +646,12 @@ void TLS_Handshake_Test::go() const std::vector protocols_offered = { "test/1", "test/2" }; // Choose random application data to send - //const size_t c_len = 1 + ((static_cast(rng.next_byte()) << 4) ^ rng.next_byte()); - const size_t c_len = 180; + const size_t c_len = 1 + ((static_cast(rng.next_byte()) << 4) ^ rng.next_byte()); std::vector client_msg(c_len); Test::rng().randomize(client_msg.data(), client_msg.size()); bool client_has_written = false; - //const size_t s_len = 1 + ((static_cast(rng.next_byte()) << 4) ^ rng.next_byte()); - const size_t s_len = 400; + const size_t s_len = 1 + ((static_cast(rng.next_byte()) << 4) ^ rng.next_byte()); std::vector server_msg(s_len); Test::rng().randomize(server_msg.data(), server_msg.size()); bool server_has_written = false; @@ -607,364 +769,43 @@ void TLS_Handshake_Test::go() break; } - if(m_server_recv.size() && m_client_recv.size()) + if(m_server->is_active()) { - Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32); - Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32); - - m_results.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of()); - - client->close(); - } - } - - m_results.end_timer(); - } - -Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, - Botan::Credentials_Manager& creds, - const Botan::TLS::Policy& client_policy, - const Botan::TLS::Policy& server_policy, - Botan::RandomNumberGenerator& rng, - Botan::TLS::Session_Manager& client_sessions, - Botan::TLS::Session_Manager& server_sessions) - { - TLS_Handshake_Test test(offer_version, creds, - client_policy, server_policy, rng, - client_sessions, server_sessions); - - test.go(); - return test.results(); - } - -Test::Result test_tls_handshake(Botan::TLS::Protocol_Version offer_version, - Botan::Credentials_Manager& creds, - const Botan::TLS::Policy& policy, - Botan::RandomNumberGenerator& rng, - Botan::TLS::Session_Manager& client_sessions, - Botan::TLS::Session_Manager& server_sessions) - { - return test_tls_handshake(offer_version, creds, policy, policy, rng, - client_sessions, server_sessions); - } - -Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, - Botan::Credentials_Manager& creds, - const Botan::TLS::Policy& client_policy, - const Botan::TLS::Policy& server_policy, - Botan::RandomNumberGenerator& rng, - Botan::TLS::Session_Manager& client_sessions, - Botan::TLS::Session_Manager& server_sessions) - { - BOTAN_ASSERT(offer_version.is_datagram_protocol(), "Test is for datagram version"); - - Test::Result result(offer_version.to_string()); - - result.start_timer(); - - for(size_t r = 1; r <= 2; ++r) - { - bool handshake_done = false; - - auto handshake_complete = [&](const Botan::TLS::Session & session) -> bool - { - handshake_done = true; - - if(session.version() != offer_version) + std::vector certs = m_server->peer_cert_chain(); + if(m_client_auth) { - result.test_failure("Offered " + offer_version.to_string() + " got " + session.version().to_string()); - } + m_results.test_eq("got client certs", certs.size(), 2); - return true; - }; + Credentials_Manager_Test& test_creds = dynamic_cast(m_creds); - auto next_protocol_chooser = [&](std::vector protos) -> std::string - { - if(r <= 2) - { - result.test_eq("protocol count", protos.size(), 2); - result.test_eq("protocol[0]", protos[0], "test/1"); - result.test_eq("protocol[1]", protos[1], "test/2"); - } - return "test/3"; - }; + std::vector acceptable_CAs = test_creds.get_acceptable_cas(); - const std::vector protocols_offered = { "test/1", "test/2" }; + m_results.test_gte("client got CA list", acceptable_CAs.size(), 2); // DSA is optional - try - { - std::vector c2s_traffic, s2c_traffic, client_recv, server_recv, client_sent, server_sent; - - std::unique_ptr server_cb(new Botan::TLS::Compat_Callbacks( - queue_inserter(s2c_traffic), - queue_inserter(server_recv), - std::function(print_alert), - handshake_complete, - nullptr, - next_protocol_chooser)); - - std::unique_ptr client_cb(new Botan::TLS::Compat_Callbacks( - queue_inserter(c2s_traffic), - queue_inserter(client_recv), - std::function(print_alert), - handshake_complete)); - - // TLS::Server object constructed by new constructor using virtual callback interface. - std::unique_ptr server( - new Botan::TLS::Server(*server_cb, - server_sessions, - creds, - server_policy, - rng, - true)); - - // TLS::Client object constructed by new constructor using virtual callback interface. - std::unique_ptr client( - new Botan::TLS::Client(*client_cb, - client_sessions, - creds, - client_policy, - rng, - Botan::TLS::Server_Information("server.example.com"), - offer_version, - protocols_offered)); - - size_t rounds = 0; - - // Test DTLS using both new and legacy constructors. - for(size_t ctor_sel = 0; ctor_sel < 2; ++ctor_sel) - { - if(ctor_sel == 1) + for(const Botan::X509_DN& dn : acceptable_CAs) { - c2s_traffic.clear(); - s2c_traffic.clear(); - server_recv.clear(); - client_recv.clear(); - client_sent.clear(); - server_sent.clear(); - // TLS::Server object constructed by legacy constructor. - server.reset( - new Botan::TLS::Server(queue_inserter(s2c_traffic), - queue_inserter(server_recv), - alert_cb_with_data, - handshake_complete, - server_sessions, - creds, - server_policy, - rng, - next_protocol_chooser, - true)); - - // TLS::Client object constructed by legacy constructor. - client.reset( - new Botan::TLS::Client(queue_inserter(c2s_traffic), - queue_inserter(client_recv), - alert_cb_with_data, - handshake_complete, - client_sessions, - creds, - client_policy, - rng, - Botan::TLS::Server_Information("server.example.com"), - offer_version, - protocols_offered)); - } - - while(true) - { -#if defined(BOTAN_TARGET_OS_HAS_THREADS) - // TODO: client and server should be in different threads - std::this_thread::sleep_for(std::chrono::microseconds(rng.next_byte() % 128)); -#endif - ++rounds; - - if(rounds > 100) - { - result.test_failure("Still here after many rounds"); - break; - } - - if(handshake_done && (client->is_closed() || server->is_closed())) - { - break; - } - - if(client->is_active() && client_sent.empty()) - { - // Choose a len between 1 and 511, todo use random chunks - const size_t c_len = 1 + rng.next_byte() + rng.next_byte(); - client_sent = unlock(rng.random_vec(c_len)); - client->send(client_sent); - } - - if(server->is_active() && server_sent.empty()) - { - result.test_eq("server ALPN", server->next_protocol(), "test/3"); - - const size_t s_len = 1 + rng.next_byte() + rng.next_byte(); - server_sent = unlock(rng.random_vec(s_len)); - server->send(server_sent); - } - - const bool corrupt_client_data = (r == 3 && rng.next_byte() % 3 <= 1 && rounds < 10); - const bool corrupt_server_data = (r == 4 && rng.next_byte() % 3 <= 1 && rounds < 10); - - if(c2s_traffic.size() > 0) - { - /* - * Use this as a temp value to hold the queues as otherwise they - * might end up appending more in response to messages during the - * handshake. - */ - std::vector input; - std::swap(c2s_traffic, input); - - if(corrupt_server_data) - { - try - { - input = Test::mutate_vec(input, true, 5); - size_t needed = server->received_data(input.data(), input.size()); - - if(needed > 0 && - result.test_lt("Never requesting more than max protocol len", needed, Botan::TLS::MAX_CIPHERTEXT_SIZE + 1)) - { - input.resize(needed); - rng.randomize(input.data(), input.size()); - client->received_data(input.data(), input.size()); - } - } - catch(std::exception&) - { - result.test_note("corruption caused server exception"); - } - } - else - { - try - { - size_t needed = server->received_data(input.data(), input.size()); - result.test_eq("full packet received", needed, 0); - } - catch(std::exception& e) - { - result.test_failure("server error", e.what()); - } - } - - continue; - } - - if(s2c_traffic.size() > 0) - { - std::vector input; - std::swap(s2c_traffic, input); - - if(corrupt_client_data) - { - try - { - input = Test::mutate_vec(input, true, 5); - size_t needed = client->received_data(input.data(), input.size()); - - if(needed > 0 && - result.test_lt("Never requesting more than max protocol len", needed, Botan::TLS::MAX_CIPHERTEXT_SIZE + 1)) - { - input.resize(needed); - rng.randomize(input.data(), input.size()); - client->received_data(input.data(), input.size()); - } - } - catch(std::exception&) - { - result.test_note("corruption caused client exception"); - } - } - else - { - try - { - size_t needed = client->received_data(input.data(), input.size()); - result.test_eq("full packet received", needed, 0); - } - catch(std::exception& e) - { - result.test_failure("client error", e.what()); - } - } - - continue; - } - - // If we corrupted a DTLS application message, resend it: - if(client->is_active() && corrupt_client_data && server_recv.empty()) - { - client->send(client_sent); - } - if(server->is_active() && corrupt_server_data && client_recv.empty()) - { - server->send(server_sent); - } - - if(client_recv.size()) - { - result.test_eq("client recv", client_recv, server_sent); - } - - if(server_recv.size()) - { - result.test_eq("server recv", server_recv, client_sent); - } - - if(client->is_closed() && server->is_closed()) - { - break; - } - - if(server_recv.size() && client_recv.size()) - { - Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32); - Botan::SymmetricKey server_key = server->key_material_export("label", "context", 32); - - result.test_eq("key material export", client_key.bits_of(), server_key.bits_of()); - - if(r % 2 == 0) - { - client->close(); - } - else - { - server->close(); - } - } + m_results.test_eq("Expected CA country field", + dn.get_first_attribute("C"), "VT"); } } - } - catch(std::exception& e) - { - if(r > 2) - { - result.test_note("Corruption caused failure"); - } else { - result.test_failure("DTLS handshake", e.what()); + m_results.test_eq("no client certs", certs.size(), 0); } } - } - result.end_timer(); - return result; - } + if(m_server_recv.size() && m_client_recv.size()) + { + Botan::SymmetricKey client_key = client->key_material_export("label", "context", 32); + Botan::SymmetricKey server_key = m_server->key_material_export("label", "context", 32); -Test::Result test_dtls_handshake(Botan::TLS::Protocol_Version offer_version, - Botan::Credentials_Manager& creds, - const Botan::TLS::Policy& policy, - Botan::RandomNumberGenerator& rng, - Botan::TLS::Session_Manager& client_ses, - Botan::TLS::Session_Manager& server_ses) - { - return test_dtls_handshake(offer_version, creds, policy, policy, rng, client_ses, server_ses); + m_results.test_eq("TLS key material export", client_key.bits_of(), server_key.bits_of()); + + client->close(); + } + } + + m_results.end_timer(); } class Test_Policy final : public Botan::TLS::Text_Policy @@ -1000,157 +841,40 @@ class Test_Policy final : public Botan::TLS::Text_Policy } }; -Test::Result test_tls_alert_strings() - { - Test::Result result("TLS::Alert::type_string"); - - const std::vector alert_types = - { - Botan::TLS::Alert::CLOSE_NOTIFY, - Botan::TLS::Alert::UNEXPECTED_MESSAGE, - Botan::TLS::Alert::BAD_RECORD_MAC, - Botan::TLS::Alert::DECRYPTION_FAILED, - Botan::TLS::Alert::RECORD_OVERFLOW, - Botan::TLS::Alert::DECOMPRESSION_FAILURE, - Botan::TLS::Alert::HANDSHAKE_FAILURE, - Botan::TLS::Alert::NO_CERTIFICATE, - Botan::TLS::Alert::BAD_CERTIFICATE, - Botan::TLS::Alert::UNSUPPORTED_CERTIFICATE, - Botan::TLS::Alert::CERTIFICATE_REVOKED, - Botan::TLS::Alert::CERTIFICATE_EXPIRED, - Botan::TLS::Alert::CERTIFICATE_UNKNOWN, - Botan::TLS::Alert::ILLEGAL_PARAMETER, - Botan::TLS::Alert::UNKNOWN_CA, - Botan::TLS::Alert::ACCESS_DENIED, - Botan::TLS::Alert::DECODE_ERROR, - Botan::TLS::Alert::DECRYPT_ERROR, - Botan::TLS::Alert::EXPORT_RESTRICTION, - Botan::TLS::Alert::PROTOCOL_VERSION, - Botan::TLS::Alert::INSUFFICIENT_SECURITY, - Botan::TLS::Alert::INTERNAL_ERROR, - Botan::TLS::Alert::INAPPROPRIATE_FALLBACK, - Botan::TLS::Alert::USER_CANCELED, - Botan::TLS::Alert::NO_RENEGOTIATION, - Botan::TLS::Alert::UNSUPPORTED_EXTENSION, - Botan::TLS::Alert::CERTIFICATE_UNOBTAINABLE, - Botan::TLS::Alert::UNRECOGNIZED_NAME, - Botan::TLS::Alert::BAD_CERTIFICATE_STATUS_RESPONSE, - Botan::TLS::Alert::BAD_CERTIFICATE_HASH_VALUE, - Botan::TLS::Alert::UNKNOWN_PSK_IDENTITY, - Botan::TLS::Alert:: NO_APPLICATION_PROTOCOL, - }; - - std::set seen; - - for(auto alert : alert_types) - { - const std::string str = Botan::TLS::Alert(alert).type_string(); - result.test_eq("No duplicate strings", seen.count(str), 0); - seen.insert(str); - } - - Botan::TLS::Alert unknown_alert = Botan::TLS::Alert({01, 66}); - - result.test_eq("Unknown alert str", unknown_alert.type_string(), "unrecognized_alert_66"); - - return result; - } - - -std::string read_tls_policy(const std::string& policy_str) - { - const std::string fspath = Test::data_file("tls-policy/" + policy_str + ".txt"); - - std::ifstream is(fspath.c_str()); - if(!is.good()) - { - throw Test_Error("Missing policy file " + fspath); - } - - Botan::TLS::Text_Policy policy(is); - return policy.to_string(); - } - -std::string tls_policy_string(const std::string& policy_str) - { - std::unique_ptr policy; - if(policy_str == "default") - { - policy.reset(new Botan::TLS::Policy); - } - else if(policy_str == "suiteb") - { - policy.reset(new Botan::TLS::NSA_Suite_B_128); - } - else if(policy_str == "bsi") - { - policy.reset(new Botan::TLS::BSI_TR_02102_2); - } - else if(policy_str == "strict") - { - policy.reset(new Botan::TLS::Strict_Policy); - } - else if(policy_str == "datagram") - { - policy.reset(new Botan::TLS::Datagram_Policy); - } - else - { - throw Test_Error("Unknown TLS policy type '" + policy_str + "'"); - } - - return policy->to_string(); - } - -Test::Result test_tls_policy() - { - Test::Result result("TLS Policy"); - - const std::vector policies = { "default", "suiteb", "strict", "datagram", "bsi" }; - - for(std::string policy : policies) - { - result.test_eq("Values for TLS " + policy + " policy", - tls_policy_string(policy), - read_tls_policy(policy)); - } - - return result; - } - class TLS_Unit_Tests final : public Test { private: - void test_with_policy(std::vector& results, + void test_with_policy(const std::string& test_descr, + std::vector& results, Botan::TLS::Session_Manager& client_ses, Botan::TLS::Session_Manager& server_ses, Botan::Credentials_Manager& creds, const std::vector& versions, - const Botan::TLS::Policy& policy) + const Botan::TLS::Policy& policy, + bool client_auth = false) { Botan::RandomNumberGenerator& rng = Test::rng(); for(auto const& version : versions) { - if(version.is_datagram_protocol()) - { - results.push_back(test_dtls_handshake(version, creds, policy, rng, client_ses, server_ses)); - } - else - { - results.push_back(test_tls_handshake(version, creds, policy, rng, client_ses, server_ses)); - } + TLS_Handshake_Test test( + version.to_string() + " " + test_descr, + version, creds, policy, policy, rng, client_ses, server_ses, client_auth); + test.go(); + results.push_back(test.results()); } } - void test_all_versions(std::vector& results, + void test_all_versions(const std::string& test_descr, + std::vector& results, Botan::TLS::Session_Manager& client_ses, Botan::TLS::Session_Manager& server_ses, Botan::Credentials_Manager& creds, const std::string& kex_policy, const std::string& cipher_policy, const std::string& mac_policy, - const std::string& etm_policy) + const std::string& etm_policy, + bool client_auth = false) { Test_Policy policy; policy.set("ciphers", cipher_policy); @@ -1172,30 +896,34 @@ class TLS_Unit_Tests final : public Test Botan::TLS::Protocol_Version::DTLS_V12 }; - return test_with_policy(results, client_ses, server_ses, creds, versions, policy); + return test_with_policy(test_descr, results, client_ses, server_ses, creds, versions, policy, client_auth); } - void test_modern_versions(std::vector& results, + void test_modern_versions(const std::string& test_descr, + std::vector& results, Botan::TLS::Session_Manager& client_ses, Botan::TLS::Session_Manager& server_ses, Botan::Credentials_Manager& creds, const std::string& kex_policy, const std::string& cipher_policy, - const std::string& mac_policy = "AEAD") + const std::string& mac_policy = "AEAD", + bool client_auth = false) { std::map no_extra_policies; - return test_modern_versions(results, client_ses, server_ses, creds, - kex_policy, cipher_policy, mac_policy, no_extra_policies); + return test_modern_versions(test_descr, results, client_ses, server_ses, creds, + kex_policy, cipher_policy, mac_policy, no_extra_policies, client_auth); } - void test_modern_versions(std::vector& results, + void test_modern_versions(const std::string& test_descr, + std::vector& results, Botan::TLS::Session_Manager& client_ses, Botan::TLS::Session_Manager& server_ses, Botan::Credentials_Manager& creds, const std::string& kex_policy, const std::string& cipher_policy, const std::string& mac_policy, - const std::map& extra_policies) + const std::map& extra_policies, + bool client_auth = false) { Test_Policy policy; policy.set("ciphers", cipher_policy); @@ -1213,44 +941,13 @@ class TLS_Unit_Tests final : public Test Botan::TLS::Protocol_Version::DTLS_V12 }; - return test_with_policy(results, client_ses, server_ses, creds, versions, policy); - } - - Test::Result test_tls_ciphersuites() - { - Test::Result result("TLS::Ciphersuite"); - - for(size_t csuite_id = 0; csuite_id <= 0xFFFF; ++csuite_id) - { - Botan::TLS::Ciphersuite ciphersuite = Botan::TLS::Ciphersuite::by_id(csuite_id); - - if(ciphersuite.valid()) - { - result.test_eq("Valid Ciphersuite is not SCSV", Botan::TLS::Ciphersuite::is_scsv(csuite_id), false); - - if(ciphersuite.cbc_ciphersuite() == false) - { - result.test_eq("Expected MAC name for AEAD ciphersuites", ciphersuite.mac_algo(), "AEAD"); - } - else - { - result.test_eq("MAC algo and PRF algo same for CBC suites", ciphersuite.prf_algo(), ciphersuite.mac_algo()); - } - - // TODO more tests here - } - } - - return result; + return test_with_policy(test_descr, results, client_ses, server_ses, creds, versions, policy, client_auth); } public: std::vector run() override { std::vector results; - results.push_back(test_tls_alert_strings()); - results.push_back(test_tls_policy()); - results.push_back(test_tls_ciphersuites()); Botan::RandomNumberGenerator& rng = Test::rng(); @@ -1259,11 +956,10 @@ class TLS_Unit_Tests final : public Test #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) client_ses.reset( - new Botan::TLS::Session_Manager_SQLite("pass", rng, ":memory:", 5, - std::chrono::seconds(2))); + new Botan::TLS::Session_Manager_SQLite("pass", rng, ":memory:", 5, std::chrono::seconds(2))); server_ses.reset( - new Botan::TLS::Session_Manager_SQLite("pass", rng, ":memory:", 10, - std::chrono::seconds(4))); + new Botan::TLS::Session_Manager_SQLite("pass", rng, ":memory:", 10, std::chrono::seconds(4))); + #else client_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng)); server_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng)); @@ -1274,119 +970,115 @@ class TLS_Unit_Tests final : public Test #if defined(BOTAN_HAS_TLS_CBC) for(std::string etm_setting : { "false", "true" }) { - test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting); - test_all_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting); - - test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "AES-256", "SHA-1", etm_setting); - test_all_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-256", "SHA-1", etm_setting); + test_all_versions("AES-128 RSA", results, *client_ses, *server_ses, *creds, "RSA", "AES-128", "SHA-256 SHA-1", etm_setting); + test_all_versions("AES-128 ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128", "SHA-256 SHA-1", etm_setting); #if defined(BOTAN_HAS_CAMELLIA) - test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "Camellia-128", "SHA-256 SHA-1", etm_setting); - test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "Camellia-256", "SHA-256 SHA-384 SHA-1", - etm_setting); + test_all_versions("Camellia-128 RSA", results, *client_ses, *server_ses, + *creds, "RSA", "Camellia-128", "SHA-256 SHA-1", etm_setting); + test_all_versions("Camellia-128 RSA SHA-2", results, *client_ses, *server_ses, + *creds, "RSA", "Camellia-256", "SHA-256 SHA-384 SHA-1", etm_setting); #endif #if defined(BOTAN_HAS_DES) - test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "3DES", "SHA-1", etm_setting); - test_all_versions(results, *client_ses, *server_ses, *creds, "ECDH", "3DES", "SHA-1", etm_setting); + test_all_versions("3DES RSA", results, *client_ses, *server_ses, *creds, "RSA", "3DES", "SHA-1", etm_setting); + test_all_versions("3DES ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "3DES", "SHA-1", etm_setting); #endif #if defined(BOTAN_HAS_SEED) - test_all_versions(results, *client_ses, *server_ses, *creds, "RSA", "SEED", "SHA-1", etm_setting); + test_all_versions("SEED RSA", results, *client_ses, *server_ses, *creds, "RSA", "SEED", "SHA-1", etm_setting); #endif server_ses->remove_all(); } client_ses->remove_all(); - test_modern_versions(results, *client_ses, *server_ses, *creds, "DH", "AES-128", "SHA-256"); + test_modern_versions("AES-128 DH", results, *client_ses, *server_ses, *creds, "DH", "AES-128", "SHA-256"); #if defined(BOTAN_HAS_DSA) - test_modern_versions(results, *client_ses, *server_ses, *creds, "DH", "AES-128", "SHA-256", - { { "signature_methods", "DSA" } }); - test_modern_versions(results, *client_ses, *server_ses, *creds, "DH", "AES-256", "SHA-256", - { { "signature_methods", "DSA" } }); + test_modern_versions("AES-128 DSA", results, *client_ses, *server_ses, *creds, "DH", "AES-128", "SHA-256", + { { "signature_methods", "DSA" } }); + + test_modern_versions("AES-128/GCM DSA", results, *client_ses, *server_ses, *creds, "DH", "AES-128/GCM", "AEAD", + { { "signature_methods", "DSA" } }); #endif #endif Botan::TLS::Strict_Policy strict_policy; - test_with_policy(results, *client_ses, *server_ses, *creds, + test_with_policy("Strict policy", results, *client_ses, *server_ses, *creds, {Botan::TLS::Protocol_Version::TLS_V12}, strict_policy); Botan::TLS::NSA_Suite_B_128 suiteb_128; - test_with_policy(results, *client_ses, *server_ses, *creds, + test_with_policy("Suite B", results, *client_ses, *server_ses, *creds, {Botan::TLS::Protocol_Version::TLS_V12}, suiteb_128); // Remove server sessions before client, so clients retry with session server doesn't know server_ses->remove_all(); - test_modern_versions(results, *client_ses, *server_ses, *creds, "RSA", "AES-128/GCM"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM"); - - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", - { { "signature_methods", "RSA" } }); + test_modern_versions("AES-128/GCM RSA", results, *client_ses, *server_ses, *creds, "RSA", "AES-128/GCM"); + test_modern_versions("AES-128/GCM ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", - { { "support_cert_status_message", "false" } }); + test_modern_versions("AES-128/GCM ECDH RSA", + results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", + { { "signature_methods", "RSA" } }); -#if defined(BOTAN_HAS_DSA) - test_modern_versions(results, *client_ses, *server_ses, *creds, "DH", "AES-128/GCM", "AEAD", - { { "signature_methods", "DSA" } }); - test_modern_versions(results, *client_ses, *server_ses, *creds, "DH", "AES-256/GCM", "AEAD", - { { "signature_methods", "DSA" } }); -#endif + test_modern_versions("AES-128/GCM ECDH no OCSP", + results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", + { { "support_cert_status_message", "false" } }); client_ses->remove_all(); #if defined(BOTAN_HAS_CAMELLIA) - test_modern_versions(results, *client_ses, *server_ses, *creds, "RSA", "Camellia-128", "SHA-256"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "RSA", "Camellia-256", "SHA-384 SHA-256"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "Camellia-128/GCM", "AEAD"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "Camellia-256/GCM", "AEAD"); + test_modern_versions("Camellia-256 SHA-2", results, *client_ses, *server_ses, *creds, "RSA", "Camellia-256", "SHA-384 SHA-256"); + test_modern_versions("Camellia-128/GCM ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "Camellia-128/GCM", "AEAD"); #endif #if defined(BOTAN_HAS_ARIA) - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "ARIA-128/GCM", "AEAD"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "ARIA-256/GCM", "AEAD"); + test_modern_versions("ARIA ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "ARIA-128/GCM", "AEAD"); #endif #if defined(BOTAN_HAS_CECPQ1) #if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_GCM) - test_modern_versions(results, *client_ses, *server_ses, *creds, "CECPQ1", "AES-256/GCM", "AEAD"); + test_modern_versions("AES-256/GCM CECPQ1", results, *client_ses, *server_ses, *creds, "CECPQ1", "AES-256/GCM", "AEAD"); #endif #if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_AEAD_OCB) - test_modern_versions(results, *client_ses, *server_ses, *creds, "CECPQ1", "AES-256/OCB(12)", "AEAD"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "CECPQ1", "AES-256/OCB(12)", "AEAD", - {{ "signature_methods", "RSA" }}); + test_modern_versions("AES-256/OCB CECPQ1", results, *client_ses, *server_ses, *creds, + "CECPQ1", "AES-256/OCB(12)", "AEAD"); + test_modern_versions("AES-256/OCB CECPQ1 RSA", results, *client_ses, *server_ses, *creds, + "CECPQ1", "AES-256/OCB(12)", "AEAD", + {{ "signature_methods", "RSA" }}); #endif #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) - test_modern_versions(results, *client_ses, *server_ses, *creds, "CECPQ1", "ChaCha20Poly1305", "AEAD", - { { "signature_methods", "RSA" }}); + test_modern_versions("ChaCha20Poly1305 CECPQ1", results, *client_ses, *server_ses, *creds, + "CECPQ1", "ChaCha20Poly1305", "AEAD", + { { "signature_methods", "RSA" }}); #endif #endif - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", - { { "use_ecc_point_compression", "true" } }); - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-256/GCM", "AEAD", - { { "groups", "secp521r1" } }); - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", - { { "groups", "brainpool256r1" } }); + test_modern_versions("AES-128/GCM point compression", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", + { { "use_ecc_point_compression", "true" } }); + test_modern_versions("AES-256/GCM p521", results, *client_ses, *server_ses, *creds, "ECDH", "AES-256/GCM", "AEAD", + { { "groups", "secp521r1" } }); + test_modern_versions("AES-128/GCM bp256r1", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", + { { "groups", "brainpool256r1" } }); #if defined(BOTAN_HAS_CURVE_25519) - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", - { { "groups", "x25519" } }); + test_modern_versions("AES-128/GCM x25519", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", + { { "groups", "x25519" } }); #endif - test_modern_versions(results, *client_ses, *server_ses, *creds, "DH", "AES-128/GCM", "AEAD", - { { "groups", "ffdhe/ietf/2048" } }); + test_modern_versions("AES-128/GCM FFDHE-2048", + results, *client_ses, *server_ses, *creds, "DH", "AES-128/GCM", "AEAD", + { { "groups", "ffdhe/ietf/2048" } }); std::unique_ptr creds_with_client_cert(create_creds(rng, true)); - test_modern_versions(results, *client_ses, *server_ses, *creds_with_client_cert, "ECDH", "AES-256/GCM"); + test_modern_versions("AES-256/GCM client certs", + results, *client_ses, *server_ses, *creds_with_client_cert, "ECDH", "AES-256/GCM", "AEAD", true); #if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) client_ses.reset(new Botan::TLS::Session_Manager_In_Memory(rng)); @@ -1394,31 +1086,32 @@ class TLS_Unit_Tests final : public Test #endif #if defined(BOTAN_HAS_AEAD_OCB) - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/OCB(12)"); + test_modern_versions("AES-128/OCB ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/OCB(12)"); #endif server_ses->remove_all(); #if defined(BOTAN_HAS_AEAD_CHACHA20_POLY1305) - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "ChaCha20Poly1305"); + test_modern_versions("ChaCha20Poly1305 ECDH", results, *client_ses, *server_ses, *creds, "ECDH", "ChaCha20Poly1305"); #endif - test_modern_versions(results, *client_ses, *server_ses, *creds, "PSK", "AES-128/GCM"); + test_modern_versions("AES-128/GCM PSK", results, *client_ses, *server_ses, *creds, "PSK", "AES-128/GCM"); #if defined(BOTAN_HAS_CCM) - test_modern_versions(results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM(8)"); + test_modern_versions("AES-128/CCM PSK", results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM"); + test_modern_versions("AES-128/CCM-8 PSK", results, *client_ses, *server_ses, *creds, "PSK", "AES-128/CCM(8)"); #endif #if defined(BOTAN_HAS_TLS_CBC) // For whatever reason no (EC)DHE_PSK GCM ciphersuites are defined - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDHE_PSK", "AES-128", "SHA-256"); - test_modern_versions(results, *client_ses, *server_ses, *creds, "DHE_PSK", "AES-128", "SHA-1"); + test_modern_versions("AES-128 ECDHE_PSK", results, *client_ses, *server_ses, *creds, "ECDHE_PSK", "AES-128", "SHA-256"); + test_modern_versions("AES-128 DHE_PSK", results, *client_ses, *server_ses, *creds, "DHE_PSK", "AES-128", "SHA-1"); #endif #if defined(BOTAN_HOUSE_ECC_CURVE_NAME) - test_modern_versions(results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", - { { "groups", BOTAN_HOUSE_ECC_CURVE_NAME } }); + test_modern_versions("AES-128/GCM house curve", + results, *client_ses, *server_ses, *creds, "ECDH", "AES-128/GCM", "AEAD", + { { "groups", BOTAN_HOUSE_ECC_CURVE_NAME } }); #endif return results; -- cgit v1.2.3