// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/ssl/server_bound_cert_service.h" #include #include #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/threading/sequenced_worker_pool.h" #include "crypto/ec_private_key.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/cert/asn1_util.h" #include "net/cert/x509_certificate.h" #include "net/ssl/default_server_bound_cert_store.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace { void FailTest(int /* result */) { FAIL(); } class ServerBoundCertServiceTest : public testing::Test { public: ServerBoundCertServiceTest() : sequenced_worker_pool_(new base::SequencedWorkerPool( 3, "ServerBoundCertServiceTest")), service_(new ServerBoundCertService( new DefaultServerBoundCertStore(NULL), sequenced_worker_pool_)) { } virtual ~ServerBoundCertServiceTest() { if (sequenced_worker_pool_.get()) sequenced_worker_pool_->Shutdown(); } protected: scoped_refptr sequenced_worker_pool_; scoped_ptr service_; }; TEST_F(ServerBoundCertServiceTest, GetDomainForHost) { EXPECT_EQ("google.com", ServerBoundCertService::GetDomainForHost("google.com")); EXPECT_EQ("google.com", ServerBoundCertService::GetDomainForHost("www.google.com")); EXPECT_EQ("foo.appspot.com", ServerBoundCertService::GetDomainForHost("foo.appspot.com")); EXPECT_EQ("bar.appspot.com", ServerBoundCertService::GetDomainForHost("foo.bar.appspot.com")); EXPECT_EQ("appspot.com", ServerBoundCertService::GetDomainForHost("appspot.com")); EXPECT_EQ("google.com", ServerBoundCertService::GetDomainForHost("www.mail.google.com")); EXPECT_EQ("goto", ServerBoundCertService::GetDomainForHost("goto")); EXPECT_EQ("127.0.0.1", ServerBoundCertService::GetDomainForHost("127.0.0.1")); } // See http://crbug.com/91512 - implement OpenSSL version of CreateSelfSigned. #if !defined(USE_OPENSSL) TEST_F(ServerBoundCertServiceTest, CacheHit) { std::string host("encrypted.google.com"); int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); TestCompletionCallback callback; ServerBoundCertService::RequestHandle request_handle; // Asynchronous completion. SSLClientCertType type1; std::string private_key_info1, der_cert1; EXPECT_EQ(0, service_->cert_count()); error = service_->GetDomainBoundCert( host, types, &type1, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); error = callback.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(1, service_->cert_count()); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); EXPECT_FALSE(private_key_info1.empty()); EXPECT_FALSE(der_cert1.empty()); EXPECT_FALSE(request_handle.is_active()); // Synchronous completion. SSLClientCertType type2; std::string private_key_info2, der_cert2; error = service_->GetDomainBoundCert( host, types, &type2, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_FALSE(request_handle.is_active()); EXPECT_EQ(OK, error); EXPECT_EQ(1, service_->cert_count()); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); EXPECT_EQ(private_key_info1, private_key_info2); EXPECT_EQ(der_cert1, der_cert2); EXPECT_EQ(2u, service_->requests()); EXPECT_EQ(1u, service_->cert_store_hits()); EXPECT_EQ(0u, service_->inflight_joins()); } TEST_F(ServerBoundCertServiceTest, UnsupportedTypes) { std::string host("encrypted.google.com"); int error; std::vector types; TestCompletionCallback callback; ServerBoundCertService::RequestHandle request_handle; // Empty requested_types. SSLClientCertType type1; std::string private_key_info1, der_cert1; error = service_->GetDomainBoundCert( host, types, &type1, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(ERR_INVALID_ARGUMENT, error); EXPECT_FALSE(request_handle.is_active()); // No supported types in requested_types. types.push_back(CLIENT_CERT_RSA_SIGN); types.push_back(2); types.push_back(3); error = service_->GetDomainBoundCert( host, types, &type1, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, error); EXPECT_FALSE(request_handle.is_active()); // Supported types after unsupported ones in requested_types. types.push_back(CLIENT_CERT_ECDSA_SIGN); // Asynchronous completion. EXPECT_EQ(0, service_->cert_count()); error = service_->GetDomainBoundCert( host, types, &type1, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); error = callback.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(1, service_->cert_count()); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); EXPECT_FALSE(private_key_info1.empty()); EXPECT_FALSE(der_cert1.empty()); // Now that the cert is created, doing requests for unsupported types // shouldn't affect the created cert. // Empty requested_types. types.clear(); SSLClientCertType type2; std::string private_key_info2, der_cert2; error = service_->GetDomainBoundCert( host, types, &type2, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_EQ(ERR_INVALID_ARGUMENT, error); EXPECT_FALSE(request_handle.is_active()); // No supported types in requested_types. types.push_back(CLIENT_CERT_RSA_SIGN); types.push_back(2); types.push_back(3); error = service_->GetDomainBoundCert( host, types, &type2, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_EQ(ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED, error); EXPECT_FALSE(request_handle.is_active()); // If we request EC, the cert we created before should still be there. types.push_back(CLIENT_CERT_ECDSA_SIGN); error = service_->GetDomainBoundCert( host, types, &type2, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_FALSE(request_handle.is_active()); EXPECT_EQ(OK, error); EXPECT_EQ(1, service_->cert_count()); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); EXPECT_EQ(private_key_info1, private_key_info2); EXPECT_EQ(der_cert1, der_cert2); } TEST_F(ServerBoundCertServiceTest, StoreCerts) { int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); TestCompletionCallback callback; ServerBoundCertService::RequestHandle request_handle; std::string host1("encrypted.google.com"); SSLClientCertType type1; std::string private_key_info1, der_cert1; EXPECT_EQ(0, service_->cert_count()); error = service_->GetDomainBoundCert( host1, types, &type1, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); error = callback.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(1, service_->cert_count()); std::string host2("www.verisign.com"); SSLClientCertType type2; std::string private_key_info2, der_cert2; error = service_->GetDomainBoundCert( host2, types, &type2, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); error = callback.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(2, service_->cert_count()); std::string host3("www.twitter.com"); SSLClientCertType type3; std::string private_key_info3, der_cert3; error = service_->GetDomainBoundCert( host3, types, &type3, &private_key_info3, &der_cert3, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); error = callback.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(3, service_->cert_count()); EXPECT_NE(private_key_info1, private_key_info2); EXPECT_NE(der_cert1, der_cert2); EXPECT_NE(private_key_info1, private_key_info3); EXPECT_NE(der_cert1, der_cert3); EXPECT_NE(private_key_info2, private_key_info3); EXPECT_NE(der_cert2, der_cert3); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type3); } // Tests an inflight join. TEST_F(ServerBoundCertServiceTest, InflightJoin) { std::string host("encrypted.google.com"); int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); SSLClientCertType type1; std::string private_key_info1, der_cert1; TestCompletionCallback callback1; ServerBoundCertService::RequestHandle request_handle1; SSLClientCertType type2; std::string private_key_info2, der_cert2; TestCompletionCallback callback2; ServerBoundCertService::RequestHandle request_handle2; error = service_->GetDomainBoundCert( host, types, &type1, &private_key_info1, &der_cert1, callback1.callback(), &request_handle1); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle1.is_active()); // If we request RSA and EC in the 2nd request, should still join with the // original request. types.insert(types.begin(), CLIENT_CERT_RSA_SIGN); error = service_->GetDomainBoundCert( host, types, &type2, &private_key_info2, &der_cert2, callback2.callback(), &request_handle2); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle2.is_active()); error = callback1.WaitForResult(); EXPECT_EQ(OK, error); error = callback2.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); EXPECT_EQ(2u, service_->requests()); EXPECT_EQ(0u, service_->cert_store_hits()); EXPECT_EQ(1u, service_->inflight_joins()); } TEST_F(ServerBoundCertServiceTest, ExtractValuesFromBytesEC) { std::string host("encrypted.google.com"); SSLClientCertType type; std::string private_key_info, der_cert; int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); TestCompletionCallback callback; ServerBoundCertService::RequestHandle request_handle; error = service_->GetDomainBoundCert( host, types, &type, &private_key_info, &der_cert, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); error = callback.WaitForResult(); EXPECT_EQ(OK, error); base::StringPiece spki_piece; ASSERT_TRUE(asn1::ExtractSPKIFromDERCert(der_cert, &spki_piece)); std::vector spki( spki_piece.data(), spki_piece.data() + spki_piece.size()); // Check that we can retrieve the key from the bytes. std::vector key_vec(private_key_info.begin(), private_key_info.end()); scoped_ptr private_key( crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( ServerBoundCertService::kEPKIPassword, key_vec, spki)); EXPECT_TRUE(private_key != NULL); // Check that we can retrieve the cert from the bytes. scoped_refptr x509cert( X509Certificate::CreateFromBytes(der_cert.data(), der_cert.size())); EXPECT_TRUE(x509cert.get() != NULL); } // Tests that the callback of a canceled request is never made. TEST_F(ServerBoundCertServiceTest, CancelRequest) { std::string host("encrypted.google.com"); SSLClientCertType type; std::string private_key_info, der_cert; int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); ServerBoundCertService::RequestHandle request_handle; error = service_->GetDomainBoundCert(host, types, &type, &private_key_info, &der_cert, base::Bind(&FailTest), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); request_handle.Cancel(); EXPECT_FALSE(request_handle.is_active()); // Wait for generation to finish. sequenced_worker_pool_->FlushForTesting(); // Wait for reply from ServerBoundCertServiceWorker to be posted back to the // ServerBoundCertService. base::MessageLoop::current()->RunUntilIdle(); // Even though the original request was cancelled, the service will still // store the result, it just doesn't call the callback. EXPECT_EQ(1, service_->cert_count()); } // Tests that destructing the RequestHandle cancels the request. TEST_F(ServerBoundCertServiceTest, CancelRequestByHandleDestruction) { std::string host("encrypted.google.com"); SSLClientCertType type; std::string private_key_info, der_cert; int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); { ServerBoundCertService::RequestHandle request_handle; error = service_->GetDomainBoundCert(host, types, &type, &private_key_info, &der_cert, base::Bind(&FailTest), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); } // Wait for generation to finish. sequenced_worker_pool_->FlushForTesting(); // Wait for reply from ServerBoundCertServiceWorker to be posted back to the // ServerBoundCertService. base::MessageLoop::current()->RunUntilIdle(); // Even though the original request was cancelled, the service will still // store the result, it just doesn't call the callback. EXPECT_EQ(1, service_->cert_count()); } TEST_F(ServerBoundCertServiceTest, DestructionWithPendingRequest) { std::string host("encrypted.google.com"); SSLClientCertType type; std::string private_key_info, der_cert; int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); ServerBoundCertService::RequestHandle request_handle; error = service_->GetDomainBoundCert(host, types, &type, &private_key_info, &der_cert, base::Bind(&FailTest), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); // Cancel request and destroy the ServerBoundCertService. request_handle.Cancel(); service_.reset(); // Wait for generation to finish. sequenced_worker_pool_->FlushForTesting(); // ServerBoundCertServiceWorker should not post anything back to the // non-existant ServerBoundCertService, but run the loop just to be sure it // doesn't. base::MessageLoop::current()->RunUntilIdle(); // If we got here without crashing or a valgrind error, it worked. } // Tests that shutting down the sequenced worker pool and then making new // requests gracefully fails. // This is a regression test for http://crbug.com/236387 TEST_F(ServerBoundCertServiceTest, RequestAfterPoolShutdown) { // Shutdown the pool immediately. sequenced_worker_pool_->Shutdown(); sequenced_worker_pool_ = NULL; // Ensure any shutdown code is processed. base::MessageLoop::current()->RunUntilIdle(); // Make a request that will force synchronous completion. std::string host("encrypted.google.com"); SSLClientCertType type; std::string private_key_info, der_cert; int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); ServerBoundCertService::RequestHandle request_handle; error = service_->GetDomainBoundCert(host, types, &type, &private_key_info, &der_cert, base::Bind(&FailTest), &request_handle); // If we got here without crashing or a valgrind error, it worked. ASSERT_EQ(ERR_INSUFFICIENT_RESOURCES, error); EXPECT_FALSE(request_handle.is_active()); } // Tests that simultaneous creation of different certs works. TEST_F(ServerBoundCertServiceTest, SimultaneousCreation) { int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); std::string host1("encrypted.google.com"); SSLClientCertType type1; std::string private_key_info1, der_cert1; TestCompletionCallback callback1; ServerBoundCertService::RequestHandle request_handle1; std::string host2("foo.com"); SSLClientCertType type2; std::string private_key_info2, der_cert2; TestCompletionCallback callback2; ServerBoundCertService::RequestHandle request_handle2; std::string host3("bar.com"); SSLClientCertType type3; std::string private_key_info3, der_cert3; TestCompletionCallback callback3; ServerBoundCertService::RequestHandle request_handle3; error = service_->GetDomainBoundCert(host1, types, &type1, &private_key_info1, &der_cert1, callback1.callback(), &request_handle1); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle1.is_active()); error = service_->GetDomainBoundCert(host2, types, &type2, &private_key_info2, &der_cert2, callback2.callback(), &request_handle2); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle2.is_active()); error = service_->GetDomainBoundCert(host3, types, &type3, &private_key_info3, &der_cert3, callback3.callback(), &request_handle3); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle3.is_active()); error = callback1.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); EXPECT_FALSE(private_key_info1.empty()); EXPECT_FALSE(der_cert1.empty()); error = callback2.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); EXPECT_FALSE(private_key_info2.empty()); EXPECT_FALSE(der_cert2.empty()); error = callback3.WaitForResult(); EXPECT_EQ(OK, error); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type3); EXPECT_FALSE(private_key_info3.empty()); EXPECT_FALSE(der_cert3.empty()); EXPECT_NE(private_key_info1, private_key_info2); EXPECT_NE(der_cert1, der_cert2); EXPECT_NE(private_key_info1, private_key_info3); EXPECT_NE(der_cert1, der_cert3); EXPECT_NE(private_key_info2, private_key_info3); EXPECT_NE(der_cert2, der_cert3); EXPECT_EQ(3, service_->cert_count()); } TEST_F(ServerBoundCertServiceTest, Expiration) { ServerBoundCertStore* store = service_->GetCertStore(); base::Time now = base::Time::Now(); store->SetServerBoundCert("good", CLIENT_CERT_ECDSA_SIGN, now, now + base::TimeDelta::FromDays(1), "a", "b"); store->SetServerBoundCert("expired", CLIENT_CERT_ECDSA_SIGN, now - base::TimeDelta::FromDays(2), now - base::TimeDelta::FromDays(1), "c", "d"); EXPECT_EQ(2, service_->cert_count()); int error; std::vector types; types.push_back(CLIENT_CERT_ECDSA_SIGN); TestCompletionCallback callback; ServerBoundCertService::RequestHandle request_handle; // Cert is valid - synchronous completion. SSLClientCertType type1; std::string private_key_info1, der_cert1; error = service_->GetDomainBoundCert( "good", types, &type1, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(OK, error); EXPECT_FALSE(request_handle.is_active()); EXPECT_EQ(2, service_->cert_count()); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type1); EXPECT_STREQ("a", private_key_info1.c_str()); EXPECT_STREQ("b", der_cert1.c_str()); // Expired cert is valid as well - synchronous completion. SSLClientCertType type2; std::string private_key_info2, der_cert2; error = service_->GetDomainBoundCert( "expired", types, &type2, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_EQ(OK, error); EXPECT_FALSE(request_handle.is_active()); EXPECT_EQ(2, service_->cert_count()); EXPECT_EQ(CLIENT_CERT_ECDSA_SIGN, type2); EXPECT_STREQ("c", private_key_info2.c_str()); EXPECT_STREQ("d", der_cert2.c_str()); } #endif // !defined(USE_OPENSSL) } // namespace } // namespace net