| // Copyright 2016 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 "remoting/host/token_validator_base.h" | 
 |  | 
 | #include <vector> | 
 |  | 
 | #include "base/atomic_sequence_num.h" | 
 | #include "crypto/rsa_private_key.h" | 
 | #include "net/cert/x509_util.h" | 
 | #include "net/ssl/client_cert_identity_test_util.h" | 
 | #include "net/ssl/test_ssl_private_key.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace { | 
 |  | 
 | const char kTokenUrl[] = "https://example.com/token"; | 
 | const char kTokenValidationUrl[] = "https://example.com/validate"; | 
 | const char kTokenValidationCertIssuer[] = "*"; | 
 |  | 
 | base::AtomicSequenceNumber g_serial_number; | 
 |  | 
 | std::unique_ptr<net::FakeClientCertIdentity> CreateFakeCert( | 
 |     base::Time valid_start, | 
 |     base::Time valid_expiry) { | 
 |   std::unique_ptr<crypto::RSAPrivateKey> rsa_private_key; | 
 |   std::string cert_der; | 
 |   net::x509_util::CreateKeyAndSelfSignedCert( | 
 |       "CN=subject", g_serial_number.GetNext(), valid_start, valid_expiry, | 
 |       &rsa_private_key, &cert_der); | 
 |  | 
 |   scoped_refptr<net::X509Certificate> cert = | 
 |       net::X509Certificate::CreateFromBytes(cert_der.data(), cert_der.size()); | 
 |   if (!cert) | 
 |     return nullptr; | 
 |  | 
 |   scoped_refptr<net::SSLPrivateKey> ssl_private_key = | 
 |       net::WrapRSAPrivateKey(rsa_private_key.get()); | 
 |   if (!ssl_private_key) | 
 |     return nullptr; | 
 |  | 
 |   return std::make_unique<net::FakeClientCertIdentity>(cert, ssl_private_key); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | namespace remoting { | 
 |  | 
 | class TestTokenValidator : TokenValidatorBase { | 
 |  public: | 
 |   explicit TestTokenValidator(const ThirdPartyAuthConfig& config); | 
 |   ~TestTokenValidator() override; | 
 |  | 
 |   void SelectCertificates(net::ClientCertIdentityList selected_certs); | 
 |  | 
 |   void ExpectContinueWithCertificate( | 
 |       const net::FakeClientCertIdentity* identity); | 
 |  | 
 |  protected: | 
 |   void ContinueWithCertificate( | 
 |       scoped_refptr<net::X509Certificate> client_cert, | 
 |       scoped_refptr<net::SSLPrivateKey> client_private_key) override; | 
 |  | 
 |  private: | 
 |   void StartValidateRequest(const std::string& token) override {} | 
 |  | 
 |   net::X509Certificate* expected_client_cert_ = nullptr; | 
 |   net::SSLPrivateKey* expected_private_key_ = nullptr; | 
 | }; | 
 |  | 
 | TestTokenValidator::TestTokenValidator(const ThirdPartyAuthConfig& config) : | 
 |     TokenValidatorBase(config, "", nullptr) { | 
 | } | 
 |  | 
 | TestTokenValidator::~TestTokenValidator() = default; | 
 |  | 
 | void TestTokenValidator::SelectCertificates( | 
 |     net::ClientCertIdentityList selected_certs) { | 
 |   OnCertificatesSelected(nullptr, std::move(selected_certs)); | 
 | } | 
 |  | 
 | void TestTokenValidator::ExpectContinueWithCertificate( | 
 |     const net::FakeClientCertIdentity* identity) { | 
 |   if (identity) { | 
 |     expected_client_cert_ = identity->certificate(); | 
 |     expected_private_key_ = identity->ssl_private_key(); | 
 |   } else { | 
 |     expected_client_cert_ = nullptr; | 
 |     expected_private_key_ = nullptr; | 
 |   } | 
 | } | 
 |  | 
 | void TestTokenValidator::ContinueWithCertificate( | 
 |     scoped_refptr<net::X509Certificate> client_cert, | 
 |     scoped_refptr<net::SSLPrivateKey> client_private_key) { | 
 |   EXPECT_EQ(expected_client_cert_, client_cert.get()); | 
 |   EXPECT_EQ(expected_private_key_, client_private_key.get()); | 
 | } | 
 |  | 
 | class TokenValidatorBaseTest : public testing::Test { | 
 |  public: | 
 |   void SetUp() override; | 
 |  protected: | 
 |   std::unique_ptr<TestTokenValidator> token_validator_; | 
 | }; | 
 |  | 
 | void TokenValidatorBaseTest::SetUp() { | 
 |   ThirdPartyAuthConfig config; | 
 |   config.token_url = GURL(kTokenUrl); | 
 |   config.token_validation_url = GURL(kTokenValidationUrl); | 
 |   config.token_validation_cert_issuer = kTokenValidationCertIssuer; | 
 |   token_validator_.reset(new TestTokenValidator(config)); | 
 | } | 
 |  | 
 | TEST_F(TokenValidatorBaseTest, TestSelectCertificate) { | 
 |   base::Time now = base::Time::Now(); | 
 |  | 
 |   std::unique_ptr<net::FakeClientCertIdentity> cert_expired_5_minutes_ago = | 
 |       CreateFakeCert(now - base::TimeDelta::FromMinutes(10), | 
 |                      now - base::TimeDelta::FromMinutes(5)); | 
 |   ASSERT_TRUE(cert_expired_5_minutes_ago); | 
 |  | 
 |   std::unique_ptr<net::FakeClientCertIdentity> cert_start_5min_expire_5min = | 
 |       CreateFakeCert(now - base::TimeDelta::FromMinutes(5), | 
 |                      now + base::TimeDelta::FromMinutes(5)); | 
 |   ASSERT_TRUE(cert_start_5min_expire_5min); | 
 |  | 
 |   std::unique_ptr<net::FakeClientCertIdentity> cert_start_10min_expire_5min = | 
 |       CreateFakeCert(now - base::TimeDelta::FromMinutes(10), | 
 |                      now + base::TimeDelta::FromMinutes(5)); | 
 |   ASSERT_TRUE(cert_start_10min_expire_5min); | 
 |  | 
 |   std::unique_ptr<net::FakeClientCertIdentity> cert_start_5min_expire_10min = | 
 |       CreateFakeCert(now - base::TimeDelta::FromMinutes(5), | 
 |                      now + base::TimeDelta::FromMinutes(10)); | 
 |   ASSERT_TRUE(cert_start_5min_expire_10min); | 
 |  | 
 |   // No certificate. | 
 |   token_validator_->ExpectContinueWithCertificate(nullptr); | 
 |   token_validator_->SelectCertificates(net::ClientCertIdentityList()); | 
 |   { | 
 |     // One invalid certificate. | 
 |     net::ClientCertIdentityList client_certs; | 
 |     client_certs.push_back(cert_expired_5_minutes_ago->Copy()); | 
 |     token_validator_->ExpectContinueWithCertificate(nullptr); | 
 |     token_validator_->SelectCertificates(std::move(client_certs)); | 
 |   } | 
 |   { | 
 |     // One valid certificate. | 
 |     net::ClientCertIdentityList client_certs; | 
 |     client_certs.push_back(cert_start_5min_expire_5min->Copy()); | 
 |     token_validator_->ExpectContinueWithCertificate( | 
 |         cert_start_5min_expire_5min.get()); | 
 |     token_validator_->SelectCertificates(std::move(client_certs)); | 
 |   } | 
 |   { | 
 |     // One valid one invalid. | 
 |     net::ClientCertIdentityList client_certs; | 
 |     client_certs.push_back(cert_expired_5_minutes_ago->Copy()); | 
 |     client_certs.push_back(cert_start_5min_expire_5min->Copy()); | 
 |     token_validator_->ExpectContinueWithCertificate( | 
 |         cert_start_5min_expire_5min.get()); | 
 |     token_validator_->SelectCertificates(std::move(client_certs)); | 
 |   } | 
 |   { | 
 |     // Two valid certs. Choose latest created. | 
 |     net::ClientCertIdentityList client_certs; | 
 |     client_certs.push_back(cert_start_10min_expire_5min->Copy()); | 
 |     client_certs.push_back(cert_start_5min_expire_5min->Copy()); | 
 |     token_validator_->ExpectContinueWithCertificate( | 
 |         cert_start_5min_expire_5min.get()); | 
 |     token_validator_->SelectCertificates(std::move(client_certs)); | 
 |   } | 
 |   { | 
 |     // Two valid certs. Choose latest expires. | 
 |     net::ClientCertIdentityList client_certs; | 
 |     client_certs.push_back(cert_start_5min_expire_5min->Copy()); | 
 |     client_certs.push_back(cert_start_5min_expire_10min->Copy()); | 
 |     token_validator_->ExpectContinueWithCertificate( | 
 |         cert_start_5min_expire_10min.get()); | 
 |     token_validator_->SelectCertificates(std::move(client_certs)); | 
 |   } | 
 |   { | 
 |     // Pick the best given all certificates. | 
 |     net::ClientCertIdentityList client_certs; | 
 |     client_certs.push_back(cert_expired_5_minutes_ago->Copy()); | 
 |     client_certs.push_back(cert_start_5min_expire_5min->Copy()); | 
 |     client_certs.push_back(cert_start_5min_expire_10min->Copy()); | 
 |     client_certs.push_back(cert_start_10min_expire_5min->Copy()); | 
 |     token_validator_->ExpectContinueWithCertificate( | 
 |         cert_start_5min_expire_10min.get()); | 
 |     token_validator_->SelectCertificates(std::move(client_certs)); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace remoting |