blob: b33a8dd21e468df632b2bb26322b2f7acb6ad5f9 [file] [log] [blame]
// 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 <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/run_loop.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/libjingle_xmpp/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() = default;
~NegotiatingAuthenticatorTest() override = default;
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);
std::unique_ptr<NegotiatingHostAuthenticator> 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(
std::make_unique<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);
base::RunLoop run_loop;
tester.Start(run_loop.QuitClosure());
run_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<PairingRegistry> 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<PairingTestParameters> {
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_SUITE_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