/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ssl.h" #include "sslerr.h" #include "sslproto.h" #include #include "tls_connect.h" #include "tls_filter.h" namespace nss_test { class TlsCipherOrderTest : public TlsConnectTestBase { protected: virtual void ConfigureTLS() { EnsureTlsSetup(); ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3); } virtual SECStatus BuildTestLists(std::vector &cs_initial_list, std::vector &cs_new_list) { // This is the current CipherSuites order of enabled CipherSuites as defined // in ssl3con.c const PRUint16 *kCipherSuites = SSL_GetImplementedCiphers(); for (unsigned int i = 0; i < kNumImplementedCiphers; i++) { PRBool pref = PR_FALSE, policy = PR_FALSE; SECStatus rv; rv = SSL_CipherPolicyGet(kCipherSuites[i], &policy); if (rv != SECSuccess) { return SECFailure; } rv = SSL_CipherPrefGetDefault(kCipherSuites[i], &pref); if (rv != SECSuccess) { return SECFailure; } if (pref && policy) { cs_initial_list.push_back(kCipherSuites[i]); } } // We will test set function with the first 15 enabled ciphers. const PRUint16 kNumCiphersToSet = 15; for (unsigned int i = 0; i < kNumCiphersToSet; i++) { cs_new_list.push_back(cs_initial_list[i]); } cs_new_list[0] = cs_initial_list[1]; cs_new_list[1] = cs_initial_list[0]; return SECSuccess; } public: TlsCipherOrderTest() : TlsConnectTestBase(ssl_variant_stream, 0) {} const unsigned int kNumImplementedCiphers = SSL_GetNumImplementedCiphers(); }; const PRUint16 kCSUnsupported[] = {20196, 10101}; const PRUint16 kNumCSUnsupported = PR_ARRAY_SIZE(kCSUnsupported); const PRUint16 kCSEmpty[] = {0}; // Get the active CipherSuites odered as they were compiled TEST_F(TlsCipherOrderTest, CipherOrderGet) { std::vector initial_cs_order; std::vector new_cs_order; SECStatus result = BuildTestLists(initial_cs_order, new_cs_order); ASSERT_EQ(result, SECSuccess); ConfigureTLS(); std::vector current_cs_order(SSL_GetNumImplementedCiphers() + 1); unsigned int current_num_active_cs = 0; result = SSL_CipherSuiteOrderGet(client_->ssl_fd(), current_cs_order.data(), ¤t_num_active_cs); ASSERT_EQ(result, SECSuccess); ASSERT_EQ(current_num_active_cs, initial_cs_order.size()); for (unsigned int i = 0; i < initial_cs_order.size(); i++) { EXPECT_EQ(initial_cs_order[i], current_cs_order[i]); } // Get the chosen CipherSuite during the Handshake without any modification. Connect(); SSLChannelInfo channel; result = SSL_GetChannelInfo(client_->ssl_fd(), &channel, sizeof channel); ASSERT_EQ(result, SECSuccess); EXPECT_EQ(channel.cipherSuite, initial_cs_order[0]); } // The "server" used for gtests honor only its ciphersuites order. // So, we apply the new set for the server instead of client. // This is enough to test the effect of SSL_CipherSuiteOrderSet function. TEST_F(TlsCipherOrderTest, CipherOrderSet) { std::vector initial_cs_order; std::vector new_cs_order; SECStatus result = BuildTestLists(initial_cs_order, new_cs_order); ASSERT_EQ(result, SECSuccess); ConfigureTLS(); // change the server_ ciphersuites order. result = SSL_CipherSuiteOrderSet(server_->ssl_fd(), new_cs_order.data(), new_cs_order.size()); ASSERT_EQ(result, SECSuccess); // The function expect an array. We are using vector for VStudio // compatibility. std::vector current_cs_order(SSL_GetNumImplementedCiphers() + 1); unsigned int current_num_active_cs = 0; result = SSL_CipherSuiteOrderGet(server_->ssl_fd(), current_cs_order.data(), ¤t_num_active_cs); ASSERT_EQ(result, SECSuccess); ASSERT_EQ(current_num_active_cs, new_cs_order.size()); for (unsigned int i = 0; i < new_cs_order.size(); i++) { ASSERT_EQ(new_cs_order[i], current_cs_order[i]); } Connect(); SSLChannelInfo channel; // changes in server_ order reflect in client chosen ciphersuite. result = SSL_GetChannelInfo(client_->ssl_fd(), &channel, sizeof channel); ASSERT_EQ(result, SECSuccess); EXPECT_EQ(channel.cipherSuite, new_cs_order[0]); } // Duplicate socket configuration from a model. TEST_F(TlsCipherOrderTest, CipherOrderCopySocket) { std::vector initial_cs_order; std::vector new_cs_order; SECStatus result = BuildTestLists(initial_cs_order, new_cs_order); ASSERT_EQ(result, SECSuccess); ConfigureTLS(); // Use the existing sockets for this test. result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(), new_cs_order.size()); ASSERT_EQ(result, SECSuccess); std::vector current_cs_order(SSL_GetNumImplementedCiphers() + 1); unsigned int current_num_active_cs = 0; result = SSL_CipherSuiteOrderGet(server_->ssl_fd(), current_cs_order.data(), ¤t_num_active_cs); ASSERT_EQ(result, SECSuccess); ASSERT_EQ(current_num_active_cs, initial_cs_order.size()); for (unsigned int i = 0; i < current_num_active_cs; i++) { ASSERT_EQ(initial_cs_order[i], current_cs_order[i]); } // Import/Duplicate configurations from client_ to server_ PRFileDesc *rv = SSL_ImportFD(client_->ssl_fd(), server_->ssl_fd()); EXPECT_NE(nullptr, rv); result = SSL_CipherSuiteOrderGet(server_->ssl_fd(), current_cs_order.data(), ¤t_num_active_cs); ASSERT_EQ(result, SECSuccess); ASSERT_EQ(current_num_active_cs, new_cs_order.size()); for (unsigned int i = 0; i < new_cs_order.size(); i++) { EXPECT_EQ(new_cs_order.data()[i], current_cs_order[i]); } } // If the infomed num of elements is lower than the actual list size, only the // first "informed num" elements will be considered. The rest is ignored. TEST_F(TlsCipherOrderTest, CipherOrderSetLower) { std::vector initial_cs_order; std::vector new_cs_order; SECStatus result = BuildTestLists(initial_cs_order, new_cs_order); ASSERT_EQ(result, SECSuccess); ConfigureTLS(); result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(), new_cs_order.size() - 1); ASSERT_EQ(result, SECSuccess); std::vector current_cs_order(SSL_GetNumImplementedCiphers() + 1); unsigned int current_num_active_cs = 0; result = SSL_CipherSuiteOrderGet(client_->ssl_fd(), current_cs_order.data(), ¤t_num_active_cs); ASSERT_EQ(result, SECSuccess); ASSERT_EQ(current_num_active_cs, new_cs_order.size() - 1); for (unsigned int i = 0; i < new_cs_order.size() - 1; i++) { ASSERT_EQ(new_cs_order.data()[i], current_cs_order[i]); } } // Testing Errors Controls TEST_F(TlsCipherOrderTest, CipherOrderSetControls) { std::vector initial_cs_order; std::vector new_cs_order; SECStatus result = BuildTestLists(initial_cs_order, new_cs_order); ASSERT_EQ(result, SECSuccess); ConfigureTLS(); // Create a new vector with diplicated entries std::vector repeated_cs_order(SSL_GetNumImplementedCiphers() + 1); std::copy(initial_cs_order.begin(), initial_cs_order.end(), repeated_cs_order.begin()); repeated_cs_order[0] = repeated_cs_order[1]; // Repeated ciphersuites in the list result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), repeated_cs_order.data(), initial_cs_order.size()); EXPECT_EQ(result, SECFailure); // Zero size for the sent list result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(), 0); EXPECT_EQ(result, SECFailure); // Wrong size, greater than actual result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(), SSL_GetNumImplementedCiphers() + 1); EXPECT_EQ(result, SECFailure); // Wrong ciphersuites, not implemented result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), kCSUnsupported, kNumCSUnsupported); EXPECT_EQ(result, SECFailure); // Null list result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), nullptr, new_cs_order.size()); EXPECT_EQ(result, SECFailure); // Empty list result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), kCSEmpty, new_cs_order.size()); EXPECT_EQ(result, SECFailure); // Confirm that the controls are working, as the current ciphersuites // remained untouched std::vector current_cs_order(SSL_GetNumImplementedCiphers() + 1); unsigned int current_num_active_cs = 0; result = SSL_CipherSuiteOrderGet(client_->ssl_fd(), current_cs_order.data(), ¤t_num_active_cs); ASSERT_EQ(result, SECSuccess); ASSERT_EQ(current_num_active_cs, initial_cs_order.size()); for (unsigned int i = 0; i < initial_cs_order.size(); i++) { ASSERT_EQ(initial_cs_order[i], current_cs_order[i]); } } } // namespace nss_test