|  | // 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 "base/bind.h" | 
|  | #include "base/macros.h" | 
|  | #include "net/base/net_errors.h" | 
|  | #include "remoting/base/rsa_key_pair.h" | 
|  | #include "remoting/protocol/authenticator_test_base.h" | 
|  | #include "remoting/protocol/channel_authenticator.h" | 
|  | #include "remoting/protocol/connection_tester.h" | 
|  | #include "remoting/protocol/negotiating_authenticator_base.h" | 
|  | #include "remoting/protocol/negotiating_client_authenticator.h" | 
|  | #include "remoting/protocol/negotiating_host_authenticator.h" | 
|  | #include "remoting/protocol/pairing_registry.h" | 
|  | #include "remoting/protocol/protocol_mock_objects.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "third_party/webrtc/libjingle/xmllite/xmlelement.h" | 
|  |  | 
|  | using testing::_; | 
|  | using testing::DeleteArg; | 
|  | using testing::SaveArg; | 
|  |  | 
|  | namespace remoting { | 
|  | namespace protocol { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const int kMessageSize = 100; | 
|  | const int kMessages = 1; | 
|  |  | 
|  | const char kNoClientId[] = ""; | 
|  | const char kNoPairedSecret[] = ""; | 
|  | const char kTestClientName[] = "client-name"; | 
|  | const char kTestClientId[] = "client-id"; | 
|  | const char kTestHostId[] = "12345678910123456"; | 
|  |  | 
|  | const char kTestPairedSecret[] = "1111-2222-3333"; | 
|  | const char kTestPairedSecretBad[] = "4444-5555-6666"; | 
|  | const char kTestPin[] = "123456"; | 
|  | const char kTestPinBad[] = "654321"; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class NegotiatingAuthenticatorTest : public AuthenticatorTestBase { | 
|  | public: | 
|  | NegotiatingAuthenticatorTest() { | 
|  | } | 
|  | ~NegotiatingAuthenticatorTest() override {} | 
|  |  | 
|  | protected: | 
|  | void InitAuthenticators( | 
|  | const std::string& client_id, | 
|  | const std::string& client_paired_secret, | 
|  | const std::string& client_interactive_pin, | 
|  | const std::string& host_secret, | 
|  | AuthenticationMethod::HashFunction hash_function, | 
|  | bool client_hmac_only) { | 
|  | std::string host_secret_hash = AuthenticationMethod::ApplyHashFunction( | 
|  | hash_function, kTestHostId, host_secret); | 
|  | host_ = NegotiatingHostAuthenticator::CreateWithSharedSecret( | 
|  | host_cert_, key_pair_, host_secret_hash, hash_function, | 
|  | pairing_registry_); | 
|  |  | 
|  | std::vector<AuthenticationMethod> methods; | 
|  | methods.push_back(AuthenticationMethod::Spake2Pair()); | 
|  | methods.push_back(AuthenticationMethod::Spake2( | 
|  | AuthenticationMethod::HMAC_SHA256)); | 
|  | if (!client_hmac_only) { | 
|  | methods.push_back(AuthenticationMethod::Spake2( | 
|  | AuthenticationMethod::NONE)); | 
|  | } | 
|  | bool pairing_expected = pairing_registry_.get() != nullptr; | 
|  | FetchSecretCallback fetch_secret_callback = | 
|  | base::Bind(&NegotiatingAuthenticatorTest::FetchSecret, | 
|  | client_interactive_pin, | 
|  | pairing_expected); | 
|  | client_as_negotiating_authenticator_ = new NegotiatingClientAuthenticator( | 
|  | client_id, client_paired_secret, | 
|  | kTestHostId, fetch_secret_callback, | 
|  | nullptr, methods); | 
|  | client_.reset(client_as_negotiating_authenticator_); | 
|  | } | 
|  |  | 
|  | void CreatePairingRegistry(bool with_paired_client) { | 
|  | pairing_registry_ = new SynchronousPairingRegistry( | 
|  | make_scoped_ptr(new MockPairingRegistryDelegate())); | 
|  | if (with_paired_client) { | 
|  | PairingRegistry::Pairing pairing( | 
|  | base::Time(), kTestClientName, kTestClientId, kTestPairedSecret); | 
|  | pairing_registry_->AddPairing(pairing); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void FetchSecret( | 
|  | const std::string& client_secret, | 
|  | bool pairing_supported, | 
|  | bool pairing_expected, | 
|  | const protocol::SecretFetchedCallback& secret_fetched_callback) { | 
|  | secret_fetched_callback.Run(client_secret); | 
|  | ASSERT_EQ(pairing_supported, pairing_expected); | 
|  | } | 
|  |  | 
|  | void VerifyRejected(Authenticator::RejectionReason reason) { | 
|  | ASSERT_TRUE(client_->state() == Authenticator::REJECTED || | 
|  | host_->state() == Authenticator::REJECTED); | 
|  | if (client_->state() == Authenticator::REJECTED) { | 
|  | ASSERT_EQ(client_->rejection_reason(), reason); | 
|  | } | 
|  | if (host_->state() == Authenticator::REJECTED) { | 
|  | ASSERT_EQ(host_->rejection_reason(), reason); | 
|  | } | 
|  | } | 
|  |  | 
|  | void VerifyAccepted(const AuthenticationMethod& expected_method) { | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  |  | 
|  | ASSERT_EQ(Authenticator::ACCEPTED, host_->state()); | 
|  | ASSERT_EQ(Authenticator::ACCEPTED, client_->state()); | 
|  |  | 
|  | client_auth_ = client_->CreateChannelAuthenticator(); | 
|  | host_auth_ = host_->CreateChannelAuthenticator(); | 
|  | RunChannelAuth(false); | 
|  |  | 
|  | EXPECT_TRUE(client_socket_.get() != nullptr); | 
|  | EXPECT_TRUE(host_socket_.get() != nullptr); | 
|  |  | 
|  | StreamConnectionTester tester(host_socket_.get(), client_socket_.get(), | 
|  | kMessageSize, kMessages); | 
|  |  | 
|  | tester.Start(); | 
|  | message_loop_.Run(); | 
|  | tester.CheckResults(); | 
|  | EXPECT_EQ( | 
|  | expected_method, | 
|  | client_as_negotiating_authenticator_->current_method_for_testing()); | 
|  | } | 
|  |  | 
|  | // Use a bare pointer because the storage is managed by the base class. | 
|  | NegotiatingClientAuthenticator* client_as_negotiating_authenticator_; | 
|  |  | 
|  | private: | 
|  | scoped_refptr<PairingRegistry> pairing_registry_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(NegotiatingAuthenticatorTest); | 
|  | }; | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, SuccessfulAuthHmac) { | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kNoClientId, kNoPairedSecret, kTestPin, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | VerifyAccepted( | 
|  | AuthenticationMethod::Spake2(AuthenticationMethod::HMAC_SHA256)); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, SuccessfulAuthPlain) { | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kNoClientId, kNoPairedSecret, kTestPin, kTestPin, | 
|  | AuthenticationMethod::NONE, false)); | 
|  | VerifyAccepted(AuthenticationMethod::Spake2(AuthenticationMethod::NONE)); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, InvalidSecretHmac) { | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kNoClientId, kNoPairedSecret, kTestPinBad, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  |  | 
|  | VerifyRejected(Authenticator::INVALID_CREDENTIALS); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, InvalidSecretPlain) { | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kNoClientId, kNoPairedSecret, kTestPin, kTestPinBad, | 
|  | AuthenticationMethod::NONE, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  |  | 
|  | VerifyRejected(Authenticator::INVALID_CREDENTIALS); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, IncompatibleMethods) { | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kNoClientId, kNoPairedSecret, kTestPin, kTestPinBad, | 
|  | AuthenticationMethod::NONE, true)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  |  | 
|  | VerifyRejected(Authenticator::PROTOCOL_ERROR); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, PairingNotSupported) { | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kTestClientId, kTestPairedSecret, kTestPin, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  | VerifyAccepted( | 
|  | AuthenticationMethod::Spake2(AuthenticationMethod::HMAC_SHA256)); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, PairingSupportedButNotPaired) { | 
|  | CreatePairingRegistry(false); | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kNoClientId, kNoPairedSecret, kTestPin, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  | VerifyAccepted(AuthenticationMethod::Spake2Pair()); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, PairingRevokedPinOkay) { | 
|  | CreatePairingRegistry(false); | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kTestClientId, kTestPairedSecret, kTestPin, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  | VerifyAccepted(AuthenticationMethod::Spake2Pair()); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, PairingRevokedPinBad) { | 
|  | CreatePairingRegistry(false); | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kTestClientId, kTestPairedSecret, kTestPinBad, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  | VerifyRejected(Authenticator::INVALID_CREDENTIALS); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, PairingSucceeded) { | 
|  | CreatePairingRegistry(true); | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kTestClientId, kTestPairedSecret, kTestPinBad, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  | VerifyAccepted(AuthenticationMethod::Spake2Pair()); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, | 
|  | PairingSucceededInvalidSecretButPinOkay) { | 
|  | CreatePairingRegistry(true); | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kTestClientId, kTestPairedSecretBad, kTestPin, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  | VerifyAccepted(AuthenticationMethod::Spake2Pair()); | 
|  | } | 
|  |  | 
|  | TEST_F(NegotiatingAuthenticatorTest, PairingFailedInvalidSecretAndPin) { | 
|  | CreatePairingRegistry(true); | 
|  | ASSERT_NO_FATAL_FAILURE(InitAuthenticators( | 
|  | kTestClientId, kTestPairedSecretBad, kTestPinBad, kTestPin, | 
|  | AuthenticationMethod::HMAC_SHA256, false)); | 
|  | ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); | 
|  | VerifyRejected(Authenticator::INVALID_CREDENTIALS); | 
|  | } | 
|  |  | 
|  | }  // namespace protocol | 
|  | }  // namespace remoting |