Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(304)

Unified Diff: net/socket/ssl_server_socket_unittest.cc

Issue 1518613002: Support for server session cache. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@client_certs
Patch Set: Rebase only Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/socket/ssl_server_socket_unittest.cc
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc
index e4eb5d617eb1a762a5cd32d7c53b822dfac467ef..2649827f422ab1b2ed56d8c395903883f8f7191e 100644
--- a/net/socket/ssl_server_socket_unittest.cc
+++ b/net/socket/ssl_server_socket_unittest.cc
@@ -73,7 +73,7 @@ const char kWrongClientCertFileName[] = "client_2.pem";
const char kWrongClientPrivateKeyFileName[] = "client_2.pk8";
const char kClientCertCAFileName[] = "client_1_ca.pem";
-class FakeDataChannel {
+class FakeDataChannel : public base::RefCountedThreadSafe<FakeDataChannel> {
davidben 2016/01/22 23:57:48 Reference-counted mutable classes are usually an i
ryanchung 2016/01/29 23:28:16 Done. Changed it back. These were used by both the
public:
FakeDataChannel()
: read_buf_len_(0),
@@ -134,6 +134,9 @@ class FakeDataChannel {
}
private:
+ ~FakeDataChannel() {}
+ friend class base::RefCountedThreadSafe<FakeDataChannel>;
+
void DoReadCallback() {
if (read_callback_.is_null() || data_.empty())
return;
@@ -188,10 +191,10 @@ class FakeDataChannel {
class FakeSocket : public StreamSocket {
public:
- FakeSocket(FakeDataChannel* incoming_channel,
- FakeDataChannel* outgoing_channel)
- : incoming_(incoming_channel),
- outgoing_(outgoing_channel) {
+ FakeSocket(const scoped_refptr<FakeDataChannel>& incoming_channel,
+ const scoped_refptr<FakeDataChannel>& outgoing_channel) {
+ incoming_ = incoming_channel;
+ outgoing_ = outgoing_channel;
}
~FakeSocket() override {}
@@ -269,8 +272,8 @@ class FakeSocket : public StreamSocket {
private:
BoundNetLog net_log_;
- FakeDataChannel* incoming_;
- FakeDataChannel* outgoing_;
+ scoped_refptr<FakeDataChannel> incoming_;
+ scoped_refptr<FakeDataChannel> outgoing_;
DISALLOW_COPY_AND_ASSIGN(FakeSocket);
};
@@ -338,10 +341,10 @@ class TestSSLPrivateKey : public SSLPrivateKey {
// Verify the correctness of the test helper classes first.
TEST(FakeSocketTest, DataTransfer) {
// Establish channels between two sockets.
- FakeDataChannel channel_1;
- FakeDataChannel channel_2;
- FakeSocket client(&channel_1, &channel_2);
- FakeSocket server(&channel_2, &channel_1);
+ scoped_refptr<FakeDataChannel> channel_1(new FakeDataChannel());
+ scoped_refptr<FakeDataChannel> channel_2(new FakeDataChannel());
+ FakeSocket client(channel_1, channel_2);
+ FakeSocket server(channel_2, channel_1);
const char kTestData[] = "testing123";
const int kTestDataSize = strlen(kTestData);
@@ -381,45 +384,56 @@ class SSLServerSocketTest : public PlatformTest {
: socket_factory_(ClientSocketFactory::GetDefaultFactory()),
cert_verifier_(new MockCertVerifier()),
client_cert_verifier_(new MockClientCertVerifier()),
- transport_security_state_(new TransportSecurityState) {
+ transport_security_state_(new TransportSecurityState),
+ initialized_(false) {
cert_verifier_->set_default_result(ERR_CERT_AUTHORITY_INVALID);
client_cert_verifier_->set_default_result(ERR_CERT_AUTHORITY_INVALID);
}
protected:
- void Initialize() {
+ void Initialize(bool reinitialize_server_context = false) {
davidben 2016/01/22 23:57:48 Style: no default arguments.
ryanchung 2016/01/29 23:28:16 Done.
+ scoped_refptr<FakeDataChannel> channel_1_ = new FakeDataChannel();
+ scoped_refptr<FakeDataChannel> channel_2_ = new FakeDataChannel();
scoped_ptr<ClientSocketHandle> client_connection(new ClientSocketHandle);
client_connection->SetSocket(
- scoped_ptr<StreamSocket>(new FakeSocket(&channel_1_, &channel_2_)));
+ scoped_ptr<StreamSocket>(new FakeSocket(channel_1_, channel_2_)));
scoped_ptr<StreamSocket> server_socket(
- new FakeSocket(&channel_2_, &channel_1_));
+ new FakeSocket(channel_2_, channel_1_));
std::string server_cert_der;
- scoped_refptr<X509Certificate> server_cert(
- ReadTestCert("unittest.selfsigned.der", &server_cert_der));
- scoped_ptr<crypto::RSAPrivateKey> server_private_key(
- ReadTestKey("unittest.key.bin"));
+ if (!initialized_ || reinitialize_server_context) {
+ scoped_refptr<X509Certificate> server_cert(
+ ReadTestCert("unittest.selfsigned.der", &server_cert_der));
+ scoped_ptr<crypto::RSAPrivateKey> server_private_key(
+ ReadTestKey("unittest.key.bin"));
+ server_context_ = CreateSSLServerSocketContext(
+ server_cert.get(), *server_private_key, server_ssl_config_);
+ }
+
+ if (!initialized_) {
+ client_ssl_config_.false_start_enabled = false;
+ client_ssl_config_.channel_id_enabled = false;
- client_ssl_config_.false_start_enabled = false;
- client_ssl_config_.channel_id_enabled = false;
+ // Certificate provided by the host doesn't need authority.
+ SSLConfig::CertAndStatus cert_and_status;
+ cert_and_status.cert_status = CERT_STATUS_AUTHORITY_INVALID;
+ cert_and_status.der_cert = server_cert_der;
+ client_ssl_config_.allowed_bad_certs.push_back(cert_and_status);
- // Certificate provided by the host doesn't need authority.
- SSLConfig::CertAndStatus cert_and_status;
- cert_and_status.cert_status = CERT_STATUS_AUTHORITY_INVALID;
- cert_and_status.der_cert = server_cert_der;
- client_ssl_config_.allowed_bad_certs.push_back(cert_and_status);
+ socket_factory_->ClearSSLSessionCache();
+ }
HostPortPair host_and_pair("unittest", 0);
SSLClientSocketContext context;
context.cert_verifier = cert_verifier_.get();
context.transport_security_state = transport_security_state_.get();
- socket_factory_->ClearSSLSessionCache();
client_socket_ = socket_factory_->CreateSSLClientSocket(
std::move(client_connection), host_and_pair, client_ssl_config_,
context);
+
server_socket_ =
- CreateSSLServerSocket(std::move(server_socket), server_cert.get(),
- *server_private_key, server_ssl_config_);
+ server_context_->CreateSSLServerSocket(std::move(server_socket));
+ initialized_ = true;
}
void ConfigureClientCertsForClient(const char* cert_file_name,
@@ -488,8 +502,6 @@ class SSLServerSocketTest : public PlatformTest {
return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector);
}
- FakeDataChannel channel_1_;
- FakeDataChannel channel_2_;
SSLConfig client_ssl_config_;
SSLServerConfig server_ssl_config_;
scoped_ptr<SSLClientSocket> client_socket_;
@@ -498,7 +510,9 @@ class SSLServerSocketTest : public PlatformTest {
scoped_ptr<MockCertVerifier> cert_verifier_;
scoped_ptr<MockClientCertVerifier> client_cert_verifier_;
scoped_ptr<TransportSecurityState> transport_security_state_;
+ scoped_ptr<SSLServerSocketContext> server_context_;
CertificateList trusted_certs_;
+ bool initialized_;
};
// This test only executes creation of client and server sockets. This is to
@@ -546,6 +560,102 @@ TEST_F(SSLServerSocketTest, Handshake) {
EXPECT_TRUE(is_aead);
}
+// This tests makes sure the session cache is working
+TEST_F(SSLServerSocketTest, HandshakeCached) {
+ Initialize();
+
+ TestCompletionCallback handshake_callback;
+ int server_ret = server_socket_->Handshake(handshake_callback.callback());
+ ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING);
davidben 2016/01/22 23:57:48 I wouldn't bother with assertions like this one. T
ryanchung 2016/01/29 23:28:16 Done.
+
+ TestCompletionCallback connect_callback;
+ int client_ret = client_socket_->Connect(connect_callback.callback());
+ ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING);
+
+ client_ret = connect_callback.GetResult(client_ret);
+ server_ret = handshake_callback.GetResult(server_ret);
+
+ ASSERT_EQ(OK, client_ret);
+ ASSERT_EQ(OK, server_ret);
+
+ // Make sure the cert status is expected.
+ SSLInfo ssl_info;
+ ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info.cert_status);
+ EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL);
+
+ // Make sure the second connection is cached
+ Initialize();
+
+ TestCompletionCallback handshake_callback2;
+ int server_ret2 = server_socket_->Handshake(handshake_callback2.callback());
+ ASSERT_TRUE(server_ret2 == OK || server_ret2 == ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback2;
+ int client_ret2 = client_socket_->Connect(connect_callback2.callback());
+ ASSERT_TRUE(client_ret2 == OK || client_ret2 == ERR_IO_PENDING);
+
+ client_ret2 = connect_callback2.GetResult(client_ret2);
+ server_ret2 = handshake_callback2.GetResult(server_ret2);
+
+ ASSERT_EQ(OK, client_ret2);
+ ASSERT_EQ(OK, server_ret2);
+
+ // Make sure the cert status is expected.
+ SSLInfo ssl_info2;
+ ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info2));
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info2.cert_status);
+ EXPECT_EQ(ssl_info2.handshake_type, SSLInfo::HANDSHAKE_RESUME);
+}
+
+// This tests makes sure the session cache separates out by server context
+TEST_F(SSLServerSocketTest, HandshakeCachedContextSwitch) {
+ Initialize();
+
+ TestCompletionCallback handshake_callback;
+ int server_ret = server_socket_->Handshake(handshake_callback.callback());
+ ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback;
+ int client_ret = client_socket_->Connect(connect_callback.callback());
+ ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING);
+
+ client_ret = connect_callback.GetResult(client_ret);
+ server_ret = handshake_callback.GetResult(server_ret);
+
+ ASSERT_EQ(OK, client_ret);
+ ASSERT_EQ(OK, server_ret);
+
+ // Make sure the cert status is expected.
+ SSLInfo ssl_info;
+ ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info));
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info.cert_status);
+ EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL);
+
+ // Make sure the second connection is NOT cached when using a new context.
+ Initialize(true);
+
+ TestCompletionCallback handshake_callback2;
+ int server_ret2 = server_socket_->Handshake(handshake_callback2.callback());
+ ASSERT_TRUE(server_ret2 == OK || server_ret2 == ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback2;
+ int client_ret2 = client_socket_->Connect(connect_callback2.callback());
+ ASSERT_TRUE(client_ret2 == OK || client_ret2 == ERR_IO_PENDING);
+
+ client_ret2 = connect_callback2.GetResult(client_ret2);
+ server_ret2 = handshake_callback2.GetResult(server_ret2);
+
+ ASSERT_EQ(OK, client_ret2);
+ ASSERT_EQ(OK, server_ret2);
+
+ // Make sure the cert status is expected.
+ SSLInfo ssl_info2;
+ ASSERT_TRUE(client_socket_->GetSSLInfo(&ssl_info2));
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info2.cert_status);
+ EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL);
+}
+
// NSS ports don't support client certificates
#if defined(USE_OPENSSL)
@@ -582,6 +692,69 @@ TEST_F(SSLServerSocketTest, HandshakeWithClientCert) {
EXPECT_TRUE(client_cert->Equals(ssl_info.cert.get()));
}
+// This test executes Connect() on SSLClientSocket and Handshake() twice on
+// SSLServerSocket to make sure handshaking between the two sockets is
+// completed successfully, using client certificate. The second connection is
+// expected to succeed through the session cache.
+TEST_F(SSLServerSocketTest, HandshakeWithClientCertCached) {
+ scoped_refptr<X509Certificate> client_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName);
+ ConfigureClientCertsForClient(kClientCertFileName, kClientPrivateKeyFileName);
+ ConfigureClientCertsForServer(true);
+ Initialize();
+
+ TestCompletionCallback handshake_callback;
+ int server_ret = server_socket_->Handshake(handshake_callback.callback());
+ ASSERT_TRUE(server_ret == OK || server_ret == ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback;
+ int client_ret = client_socket_->Connect(connect_callback.callback());
+ ASSERT_TRUE(client_ret == OK || client_ret == ERR_IO_PENDING);
+
+ client_ret = connect_callback.GetResult(client_ret);
+ server_ret = handshake_callback.GetResult(server_ret);
+
+ ASSERT_EQ(OK, client_ret);
+ ASSERT_EQ(OK, server_ret);
+
+ // Make sure the cert status is expected.
+ SSLInfo ssl_info;
+ client_socket_->GetSSLInfo(&ssl_info);
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info.cert_status);
+ server_socket_->GetSSLInfo(&ssl_info);
+ EXPECT_TRUE(ssl_info.cert.get());
+ EXPECT_TRUE(client_cert->Equals(ssl_info.cert.get()));
+ EXPECT_EQ(ssl_info.handshake_type, SSLInfo::HANDSHAKE_FULL);
+ server_socket_->Disconnect();
+ client_socket_->Disconnect();
+
+ // Create the connection again
+ Initialize();
+
+ TestCompletionCallback handshake_callback2;
+ int server_ret2 = server_socket_->Handshake(handshake_callback2.callback());
+ ASSERT_TRUE(server_ret2 == OK || server_ret2 == ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback2;
+ int client_ret2 = client_socket_->Connect(connect_callback2.callback());
+ ASSERT_TRUE(client_ret2 == OK || client_ret2 == ERR_IO_PENDING);
+
+ client_ret2 = connect_callback2.GetResult(client_ret2);
+ server_ret2 = handshake_callback2.GetResult(server_ret2);
+
+ ASSERT_EQ(OK, client_ret2);
+ ASSERT_EQ(OK, server_ret2);
+
+ // Make sure the cert status is expected.
+ SSLInfo ssl_info2;
+ client_socket_->GetSSLInfo(&ssl_info2);
+ EXPECT_EQ(CERT_STATUS_AUTHORITY_INVALID, ssl_info2.cert_status);
+ server_socket_->GetSSLInfo(&ssl_info2);
+ EXPECT_TRUE(ssl_info2.cert.get());
+ EXPECT_TRUE(client_cert->Equals(ssl_info2.cert.get()));
+ EXPECT_EQ(ssl_info2.handshake_type, SSLInfo::HANDSHAKE_RESUME);
+}
+
TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSupplied) {
ConfigureClientCertsForServer(true);
Initialize();
@@ -614,6 +787,62 @@ TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSupplied) {
EXPECT_EQ(ERR_FAILED, handshake_callback.GetResult(server_ret));
}
+TEST_F(SSLServerSocketTest, HandshakeWithClientCertRequiredNotSuppliedCached) {
+ ConfigureClientCertsForServer(true);
+ Initialize();
+ // Use the default setting for the client socket, which is to not send
+ // a client certificate. This will cause the client to receive an
+ // ERR_SSL_CLIENT_AUTH_CERT_NEEDED error, and allow for inspecting the
+ // requested cert_authorities from the CertificateRequest sent by the
+ // server.
+
+ TestCompletionCallback handshake_callback;
+ int server_ret = server_socket_->Handshake(handshake_callback.callback());
+ ASSERT_EQ(server_ret, ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback;
+ EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
+ connect_callback.GetResult(
+ client_socket_->Connect(connect_callback.callback())));
+
+ scoped_refptr<SSLCertRequestInfo> request_info = new SSLCertRequestInfo();
+ client_socket_->GetSSLCertRequestInfo(request_info.get());
+
+ // Check that the authority name that arrived in the CertificateRequest
+ // handshake message is as expected.
+ scoped_refptr<X509Certificate> client_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName);
+ EXPECT_TRUE(client_cert->IsIssuedByEncoded(request_info->cert_authorities));
+
+ client_socket_->Disconnect();
+
+ EXPECT_EQ(ERR_FAILED, handshake_callback.GetResult(server_ret));
+ server_socket_->Disconnect();
+
+ // Below, check that the cache didn't store the result of a failed handshake
+ Initialize();
+
+ TestCompletionCallback handshake_callback2;
+ int server_ret2 = server_socket_->Handshake(handshake_callback2.callback());
+ ASSERT_EQ(server_ret2, ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback2;
+ EXPECT_EQ(ERR_SSL_CLIENT_AUTH_CERT_NEEDED,
+ connect_callback2.GetResult(
+ client_socket_->Connect(connect_callback2.callback())));
+
+ scoped_refptr<SSLCertRequestInfo> request_info2 = new SSLCertRequestInfo();
+ client_socket_->GetSSLCertRequestInfo(request_info2.get());
+
+ // Check that the authority name that arrived in the CertificateRequest
+ // handshake message is as expected.
+ EXPECT_TRUE(client_cert->IsIssuedByEncoded(request_info2->cert_authorities));
+
+ client_socket_->Disconnect();
+
+ EXPECT_EQ(ERR_FAILED, handshake_callback2.GetResult(server_ret2));
+}
+
TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSupplied) {
scoped_refptr<X509Certificate> client_cert =
ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName);
@@ -634,6 +863,45 @@ TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSupplied) {
EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT,
handshake_callback.GetResult(server_ret));
}
+
+TEST_F(SSLServerSocketTest, HandshakeWithWrongClientCertSuppliedCached) {
+ scoped_refptr<X509Certificate> client_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), kClientCertFileName);
+ ConfigureClientCertsForClient(kWrongClientCertFileName,
+ kWrongClientPrivateKeyFileName);
+ ConfigureClientCertsForServer(true);
+ Initialize();
+
+ TestCompletionCallback handshake_callback;
+ int server_ret = server_socket_->Handshake(handshake_callback.callback());
+ EXPECT_EQ(server_ret, ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback;
+ int client_ret = client_socket_->Connect(connect_callback.callback());
+
+ EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT,
+ connect_callback.GetResult(client_ret));
+ EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT,
+ handshake_callback.GetResult(server_ret));
+
+ client_socket_->Disconnect();
+ server_socket_->Disconnect();
+
+ // Below, check that the cache didn't store the result of a failed handshake
+ Initialize();
+
+ TestCompletionCallback handshake_callback2;
+ int server_ret2 = server_socket_->Handshake(handshake_callback2.callback());
+ EXPECT_EQ(server_ret, ERR_IO_PENDING);
+
+ TestCompletionCallback connect_callback2;
+ int client_ret2 = client_socket_->Connect(connect_callback2.callback());
+
+ EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT,
+ connect_callback2.GetResult(client_ret2));
+ EXPECT_EQ(ERR_BAD_SSL_CLIENT_AUTH_CERT,
+ handshake_callback2.GetResult(server_ret2));
+}
#endif // defined(USE_OPENSSL)
TEST_F(SSLServerSocketTest, DataTransfer) {

Powered by Google App Engine
This is Rietveld 408576698