// 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/auth_util.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 kClientJid[] = "alice@gmail.com/abc"; const char kHostJid[] = "alice@gmail.com/123"; 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: virtual void InitAuthenticators(const std::string& client_id, const std::string& client_paired_secret, const std::string& client_interactive_pin, const std::string& host_secret) { std::string host_secret_hash = GetSharedSecretHash(kTestHostId, host_secret); scoped_ptr host = NegotiatingHostAuthenticator::CreateWithSharedSecret( kHostJid, kClientJid, host_cert_, key_pair_, host_secret_hash, pairing_registry_); host_as_negotiating_authenticator_ = host.get(); host_ = std::move(host); protocol::ClientAuthenticationConfig client_auth_config; client_auth_config.host_id = kTestHostId; client_auth_config.pairing_client_id = client_id; client_auth_config.pairing_secret = client_paired_secret; bool pairing_expected = pairing_registry_.get() != nullptr; client_auth_config.fetch_secret_callback = base::Bind(&NegotiatingAuthenticatorTest::FetchSecret, client_interactive_pin, pairing_expected); client_as_negotiating_authenticator_ = new NegotiatingClientAuthenticator( kClientJid, kHostJid, client_auth_config); client_.reset(client_as_negotiating_authenticator_); } void DisableMethodOnClient(NegotiatingAuthenticatorBase::Method method) { auto* methods = &(client_as_negotiating_authenticator_->methods_); auto iter = std::find(methods->begin(), methods->end(), method); ASSERT_TRUE(iter != methods->end()); methods->erase(iter); } void DisableMethodOnHost(NegotiatingAuthenticatorBase::Method method) { auto* methods = &(host_as_negotiating_authenticator_->methods_); auto iter = std::find(methods->begin(), methods->end(), method); ASSERT_TRUE(iter != methods->end()); methods->erase(iter); } 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); } } virtual void VerifyAccepted() { 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(); } NegotiatingAuthenticatorBase::Method current_method() { return client_as_negotiating_authenticator_->current_method_; } // Use a bare pointer because the storage is managed by the base class. NegotiatingHostAuthenticator* host_as_negotiating_authenticator_; NegotiatingClientAuthenticator* client_as_negotiating_authenticator_; private: scoped_refptr pairing_registry_; DISALLOW_COPY_AND_ASSIGN(NegotiatingAuthenticatorTest); }; struct PairingTestParameters { bool p224_on_client; bool curve25519_on_client; bool p224_on_host; bool curve25519_on_host; bool expect_curve25519_used; }; class NegotiatingPairingAuthenticatorTest : public NegotiatingAuthenticatorTest, public testing::WithParamInterface { public: void InitAuthenticators(const std::string& client_id, const std::string& client_paired_secret, const std::string& client_interactive_pin, const std::string& host_secret) override { NegotiatingAuthenticatorTest::InitAuthenticators( client_id, client_paired_secret, client_interactive_pin, host_secret); if (!GetParam().p224_on_client) { DisableMethodOnClient( NegotiatingAuthenticatorBase::Method::PAIRED_SPAKE2_P224); } if (!GetParam().curve25519_on_client) { DisableMethodOnClient( NegotiatingAuthenticatorBase::Method::PAIRED_SPAKE2_CURVE25519); } if (!GetParam().p224_on_host) { DisableMethodOnHost( NegotiatingAuthenticatorBase::Method::PAIRED_SPAKE2_P224); } if (!GetParam().curve25519_on_host) { DisableMethodOnHost( NegotiatingAuthenticatorBase::Method::PAIRED_SPAKE2_CURVE25519); } } void VerifyAccepted() override { NegotiatingAuthenticatorTest::VerifyAccepted(); EXPECT_TRUE( current_method() == NegotiatingAuthenticatorBase::Method::PAIRED_SPAKE2_P224 || current_method() == NegotiatingAuthenticatorBase::Method::PAIRED_SPAKE2_CURVE25519); } }; INSTANTIATE_TEST_CASE_P( PairingParams, NegotiatingPairingAuthenticatorTest, testing::Values( // Only P224. PairingTestParameters{true, false, true, false}, // Only curve25519. PairingTestParameters{false, true, false, true}, // Both P224 and curve25519. PairingTestParameters{true, true, true, true}, // One end supports both, the other supports only P224 or curve25519. PairingTestParameters{false, true, true, true}, PairingTestParameters{true, false, true, true}, PairingTestParameters{true, true, false, true}, PairingTestParameters{true, true, true, false})); TEST_F(NegotiatingAuthenticatorTest, SuccessfulAuthSharedSecret) { ASSERT_NO_FATAL_FAILURE(InitAuthenticators(kNoClientId, kNoPairedSecret, kTestPin, kTestPin)); VerifyAccepted(); EXPECT_EQ( NegotiatingAuthenticatorBase::Method::SHARED_SECRET_SPAKE2_CURVE25519, current_method()); } TEST_F(NegotiatingAuthenticatorTest, InvalidSharedSecret) { ASSERT_NO_FATAL_FAILURE(InitAuthenticators(kNoClientId, kNoPairedSecret, kTestPinBad, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyRejected(Authenticator::INVALID_CREDENTIALS); } TEST_F(NegotiatingAuthenticatorTest, IncompatibleMethods) { ASSERT_NO_FATAL_FAILURE( InitAuthenticators(kNoClientId, kNoPairedSecret, kTestPin, kTestPinBad)); DisableMethodOnClient( NegotiatingAuthenticatorBase::Method::SHARED_SECRET_SPAKE2_P224); DisableMethodOnHost( NegotiatingAuthenticatorBase::Method::SHARED_SECRET_SPAKE2_CURVE25519); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyRejected(Authenticator::PROTOCOL_ERROR); } TEST_F(NegotiatingAuthenticatorTest, PairingNotSupported) { ASSERT_NO_FATAL_FAILURE( InitAuthenticators(kTestClientId, kTestPairedSecret, kTestPin, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyAccepted(); EXPECT_EQ( NegotiatingAuthenticatorBase::Method::SHARED_SECRET_SPAKE2_CURVE25519, current_method()); } TEST_P(NegotiatingPairingAuthenticatorTest, PairingSupportedButNotPaired) { CreatePairingRegistry(false); ASSERT_NO_FATAL_FAILURE( InitAuthenticators(kNoClientId, kNoPairedSecret, kTestPin, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyAccepted(); } TEST_P(NegotiatingPairingAuthenticatorTest, PairingRevokedPinOkay) { CreatePairingRegistry(false); ASSERT_NO_FATAL_FAILURE( InitAuthenticators(kTestClientId, kTestPairedSecret, kTestPin, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyAccepted(); } TEST_P(NegotiatingPairingAuthenticatorTest, PairingRevokedPinBad) { CreatePairingRegistry(false); ASSERT_NO_FATAL_FAILURE(InitAuthenticators(kTestClientId, kTestPairedSecret, kTestPinBad, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyRejected(Authenticator::INVALID_CREDENTIALS); } TEST_P(NegotiatingPairingAuthenticatorTest, PairingSucceeded) { CreatePairingRegistry(true); ASSERT_NO_FATAL_FAILURE(InitAuthenticators(kTestClientId, kTestPairedSecret, kTestPinBad, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyAccepted(); } TEST_P(NegotiatingPairingAuthenticatorTest, PairingSucceededInvalidSecretButPinOkay) { CreatePairingRegistry(true); ASSERT_NO_FATAL_FAILURE(InitAuthenticators( kTestClientId, kTestPairedSecretBad, kTestPin, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyAccepted(); } TEST_P(NegotiatingPairingAuthenticatorTest, PairingFailedInvalidSecretAndPin) { CreatePairingRegistry(true); ASSERT_NO_FATAL_FAILURE(InitAuthenticators( kTestClientId, kTestPairedSecretBad, kTestPinBad, kTestPin)); ASSERT_NO_FATAL_FAILURE(RunAuthExchange()); VerifyRejected(Authenticator::INVALID_CREDENTIALS); } } // namespace protocol } // namespace remoting