PairingAuthenticator implementation and plumbing.

This CL introduces the pairing authenticator classes, adds support for them
to the negotiating authenticator classes and some minimal plumbing.

BUG=156182

Review URL: https://chromiumcodereview.appspot.com/14793021

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201472 0039d316-1c4b-4281-b951-d872f2087c98
parent 62efa785
......@@ -55,8 +55,12 @@ void ChromotingClient::Start(
scoped_ptr<protocol::TransportFactory> transport_factory) {
DCHECK(task_runner_->BelongsToCurrentThread());
// TODO(jamiewalch): Add the plumbing required to get the client id and
// shared secret from the web-app.
std::string client_id, shared_secret;
scoped_ptr<protocol::Authenticator> authenticator(
new protocol::NegotiatingClientAuthenticator(
client_id, shared_secret,
config_.authentication_tag, config_.fetch_secret_callback,
user_interface_->GetTokenFetcher(config_.host_public_key),
config_.authentication_methods));
......
......@@ -67,6 +67,7 @@
#include "remoting/host/usage_stats_consent.h"
#include "remoting/jingle_glue/xmpp_signal_strategy.h"
#include "remoting/protocol/me2me_host_authenticator_factory.h"
#include "remoting/protocol/pairing_registry.h"
#if defined(OS_POSIX)
#include <signal.h>
......@@ -466,8 +467,12 @@ void HostProcess::CreateAuthenticatorFactory() {
scoped_ptr<protocol::AuthenticatorFactory> factory;
if (token_url_.is_empty() && token_validation_url_.is_empty()) {
// TODO(jamiewalch): Add a pairing registry here once all the code
// is committed.
scoped_refptr<remoting::protocol::PairingRegistry> pairing_registry;
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
local_certificate, key_pair_, host_secret_hash_);
local_certificate, key_pair_, host_secret_hash_, pairing_registry);
} else if (token_url_.is_valid() && token_validation_url_.is_valid()) {
scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidatorFactory>
token_validator_factory(new TokenValidatorFactoryImpl(
......@@ -475,6 +480,7 @@ void HostProcess::CreateAuthenticatorFactory() {
context_->url_request_context_getter()));
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
local_certificate, key_pair_, token_validator_factory.Pass());
} else {
// TODO(rmsousa): If the policy is bad the host should not go online. It
// should keep running, but not connected, until the policies are fixed.
......
......@@ -22,6 +22,11 @@ AuthenticationMethod AuthenticationMethod::Spake2(HashFunction hash_function) {
return AuthenticationMethod(SPAKE2, hash_function);
}
// static
AuthenticationMethod AuthenticationMethod::Spake2Pair() {
return AuthenticationMethod(SPAKE2_PAIR, HMAC_SHA256);
}
// static
AuthenticationMethod AuthenticationMethod::ThirdParty() {
return AuthenticationMethod(THIRD_PARTY, NONE);
......@@ -30,7 +35,9 @@ AuthenticationMethod AuthenticationMethod::ThirdParty() {
// static
AuthenticationMethod AuthenticationMethod::FromString(
const std::string& value) {
if (value == "spake2_plain") {
if (value == "spake2_pair") {
return Spake2Pair();
} else if (value == "spake2_plain") {
return Spake2(NONE);
} else if (value == "spake2_hmac") {
return Spake2(HMAC_SHA256);
......@@ -90,16 +97,25 @@ AuthenticationMethod::HashFunction AuthenticationMethod::hash_function() const {
const std::string AuthenticationMethod::ToString() const {
DCHECK(is_valid());
if (type_ == THIRD_PARTY)
return "third_party";
switch (type_) {
case INVALID:
NOTREACHED();
break;
case SPAKE2_PAIR:
return "spake2_pair";
DCHECK_EQ(type_, SPAKE2);
case SPAKE2:
switch (hash_function_) {
case NONE:
return "spake2_plain";
case HMAC_SHA256:
return "spake2_hmac";
}
break;
switch (hash_function_) {
case NONE:
return "spake2_plain";
case HMAC_SHA256:
return "spake2_hmac";
case THIRD_PARTY:
return "third_party";
}
return "invalid";
......
......@@ -24,6 +24,7 @@ class AuthenticationMethod {
enum MethodType {
INVALID,
SPAKE2,
SPAKE2_PAIR,
THIRD_PARTY
};
......@@ -35,6 +36,7 @@ class AuthenticationMethod {
// Constructors for various authentication methods.
static AuthenticationMethod Invalid();
static AuthenticationMethod Spake2(HashFunction hash_function);
static AuthenticationMethod Spake2Pair();
static AuthenticationMethod ThirdParty();
// Parses a string that defines an authentication method. Returns an
......
......@@ -19,6 +19,10 @@ namespace protocol {
class ChannelAuthenticator;
typedef base::Callback<void(const std::string& secret)> SecretFetchedCallback;
typedef base::Callback<void(
const SecretFetchedCallback& secret_fetched_callback)> FetchSecretCallback;
// Authenticator is an abstract interface for authentication protocol
// implementations. Different implementations of this interface may be
// used on each side of the connection depending of type of the auth
......
......@@ -28,7 +28,7 @@ scoped_ptr<Authenticator> It2MeHostAuthenticatorFactory::CreateAuthenticator(
const std::string& remote_jid,
const buzz::XmlElement* first_message) {
return NegotiatingHostAuthenticator::CreateWithSharedSecret(
local_cert_, key_pair_, shared_secret_, AuthenticationMethod::NONE);
local_cert_, key_pair_, shared_secret_, AuthenticationMethod::NONE, NULL);
}
} // namespace protocol
......
......@@ -63,12 +63,14 @@ scoped_ptr<AuthenticatorFactory>
Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const SharedSecretHash& shared_secret_hash) {
const SharedSecretHash& shared_secret_hash,
scoped_refptr<PairingRegistry> pairing_registry) {
scoped_ptr<Me2MeHostAuthenticatorFactory> result(
new Me2MeHostAuthenticatorFactory());
result->local_cert_ = local_cert;
result->key_pair_ = key_pair;
result->shared_secret_hash_ = shared_secret_hash;
result->pairing_registry_ = pairing_registry;
return scoped_ptr<AuthenticatorFactory>(result.Pass());
}
......@@ -131,7 +133,7 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator(
return NegotiatingHostAuthenticator::CreateWithSharedSecret(
local_cert_, key_pair_, shared_secret_hash_.value,
shared_secret_hash_.hash_function);
shared_secret_hash_.hash_function, pairing_registry_);
}
return scoped_ptr<Authenticator>(new RejectingAuthenticator());
......
......@@ -21,19 +21,24 @@ class RsaKeyPair;
namespace protocol {
class PairingRegistry;
class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
public:
// Create a factory that dispenses shared secret authenticators.
static scoped_ptr<AuthenticatorFactory> CreateWithSharedSecret(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const SharedSecretHash& shared_secret_hash);
const SharedSecretHash& shared_secret_hash,
scoped_refptr<PairingRegistry> pairing_registry);
// Create a factory that dispenses third party authenticators.
static scoped_ptr<AuthenticatorFactory> CreateWithThirdPartyAuth(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
scoped_ptr<ThirdPartyHostAuthenticator::TokenValidatorFactory>
token_validator_factory);
// Create a factory that dispenses rejecting authenticators (used when the
// host config/policy is inconsistent)
static scoped_ptr<AuthenticatorFactory> CreateRejecting();
......@@ -59,6 +64,9 @@ class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
scoped_ptr<ThirdPartyHostAuthenticator::TokenValidatorFactory>
token_validator_factory_;
// Used only for pairing host authenticators.
scoped_refptr<PairingRegistry> pairing_registry_;
DISALLOW_COPY_AND_ASSIGN(Me2MeHostAuthenticatorFactory);
};
......
......@@ -71,7 +71,11 @@ class NegotiatingAuthenticatorBase : public Authenticator {
// Calls |current_authenticator_| to process |message|, passing the supplied
// |resume_callback|.
void ProcessMessageInternal(const buzz::XmlElement* message,
const base::Closure& resume_callback);
const base::Closure& resume_callback);
const AuthenticationMethod& current_method_for_testing() const {
return current_method_;
}
protected:
static const buzz::StaticQName kMethodAttributeQName;
......
......@@ -27,10 +27,15 @@ namespace {
const int kMessageSize = 100;
const int kMessages = 1;
const char kNoClientId[] = "";
const char kNoPairedSecret[] = "";
const char kTestClientId[] = "client-id";
const char kTestHostId[] = "12345678910123456";
const char kTestSharedSecret[] = "1234-1234-5678";
const char kTestSharedSecretBad[] = "0000-0000-0001";
const char kTestPairedSecret[] = "1111-2222-3333";
const char kTestPairedSecretBad[] = "4444-5555-6666";
const char kTestPin[] = "123456";
const char kTestPinBad[] = "654321";
} // namespace
......@@ -43,26 +48,47 @@ class NegotiatingAuthenticatorTest : public AuthenticatorTestBase {
protected:
void InitAuthenticators(
const std::string& client_secret,
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) {
bool client_hmac_only,
scoped_refptr<PairingRegistry> pairing_registry) {
std::string host_secret_hash = AuthenticationMethod::ApplyHashFunction(
hash_function, kTestHostId, host_secret);
host_ = NegotiatingHostAuthenticator::CreateWithSharedSecret(
host_cert_, key_pair_, host_secret_hash, hash_function);
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));
}
client_.reset(new NegotiatingClientAuthenticator(
client_as_negotiating_authenticator_ = new NegotiatingClientAuthenticator(
client_id, client_paired_secret,
kTestHostId, base::Bind(&NegotiatingAuthenticatorTest::FetchSecret,
client_secret),
scoped_ptr<ThirdPartyClientAuthenticator::TokenFetcher>(), methods));
client_interactive_pin),
scoped_ptr<ThirdPartyClientAuthenticator::TokenFetcher>(), methods);
client_.reset(client_as_negotiating_authenticator_);
}
scoped_refptr<PairingRegistry> CreatePairingRegistry(
PairingRegistry::Pairing* pairings, size_t num_pairings) {
PairingRegistry::PairedClients clients;
for (size_t i = 0; i < num_pairings; ++i) {
clients[pairings[i].client_id] = pairings[i];
}
scoped_refptr<PairingRegistry> result(
new PairingRegistry(
scoped_ptr<PairingRegistry::Delegate>(
new NotImplementedPairingRegistryDelegate),
clients));
return result;
}
static void FetchSecret(
......@@ -70,14 +96,19 @@ class NegotiatingAuthenticatorTest : public AuthenticatorTestBase {
const protocol::SecretFetchedCallback& secret_fetched_callback) {
secret_fetched_callback.Run(client_secret);
}
void VerifyRejected(Authenticator::RejectionReason reason) {
ASSERT_TRUE((client_->state() == Authenticator::REJECTED &&
(client_->rejection_reason() == reason)) ||
(host_->state() == Authenticator::REJECTED &&
(host_->rejection_reason() == 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() {
void VerifyAccepted(const AuthenticationMethod& expected_method) {
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
ASSERT_EQ(Authenticator::ACCEPTED, host_->state());
......@@ -96,30 +127,37 @@ class NegotiatingAuthenticatorTest : public AuthenticatorTestBase {
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:
DISALLOW_COPY_AND_ASSIGN(NegotiatingAuthenticatorTest);
};
TEST_F(NegotiatingAuthenticatorTest, SuccessfulAuthHmac) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestSharedSecret, kTestSharedSecret,
AuthenticationMethod::HMAC_SHA256, false));
VerifyAccepted();
kNoClientId, kNoPairedSecret, kTestPin, kTestPin,
AuthenticationMethod::HMAC_SHA256, false, NULL));
VerifyAccepted(
AuthenticationMethod::Spake2(AuthenticationMethod::HMAC_SHA256));
}
TEST_F(NegotiatingAuthenticatorTest, SuccessfulAuthPlain) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestSharedSecret, kTestSharedSecret,
AuthenticationMethod::NONE, false));
VerifyAccepted();
kNoClientId, kNoPairedSecret, kTestPin, kTestPin,
AuthenticationMethod::NONE, false, NULL));
VerifyAccepted(AuthenticationMethod::Spake2(AuthenticationMethod::NONE));
}
TEST_F(NegotiatingAuthenticatorTest, InvalidSecretHmac) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestSharedSecret, kTestSharedSecretBad,
AuthenticationMethod::HMAC_SHA256, false));
kNoClientId, kNoPairedSecret, kTestPinBad, kTestPin,
AuthenticationMethod::HMAC_SHA256, false, NULL));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyRejected(Authenticator::INVALID_CREDENTIALS);
......@@ -127,8 +165,8 @@ TEST_F(NegotiatingAuthenticatorTest, InvalidSecretHmac) {
TEST_F(NegotiatingAuthenticatorTest, InvalidSecretPlain) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestSharedSecret, kTestSharedSecretBad,
AuthenticationMethod::NONE, false));
kNoClientId, kNoPairedSecret, kTestPin, kTestPinBad,
AuthenticationMethod::NONE, false, NULL));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyRejected(Authenticator::INVALID_CREDENTIALS);
......@@ -136,12 +174,84 @@ TEST_F(NegotiatingAuthenticatorTest, InvalidSecretPlain) {
TEST_F(NegotiatingAuthenticatorTest, IncompatibleMethods) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestSharedSecret, kTestSharedSecretBad,
AuthenticationMethod::NONE, true));
kNoClientId, kNoPairedSecret, kTestPin, kTestPinBad,
AuthenticationMethod::NONE, true, NULL));
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, NULL));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyAccepted(
AuthenticationMethod::Spake2(AuthenticationMethod::HMAC_SHA256));
}
TEST_F(NegotiatingAuthenticatorTest, PairingSupportedButNotPaired) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kNoClientId, kNoPairedSecret, kTestPin, kTestPin,
AuthenticationMethod::HMAC_SHA256, false,
CreatePairingRegistry(NULL, 0)));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyAccepted(AuthenticationMethod::Spake2Pair());
}
TEST_F(NegotiatingAuthenticatorTest, PairingRevokedPinOkay) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestClientId, kTestPairedSecret, kTestPin, kTestPin,
AuthenticationMethod::HMAC_SHA256, false,
CreatePairingRegistry(NULL, 0)));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyAccepted(AuthenticationMethod::Spake2Pair());
}
TEST_F(NegotiatingAuthenticatorTest, PairingRevokedPinBad) {
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestClientId, kTestPairedSecret, kTestPinBad, kTestPin,
AuthenticationMethod::HMAC_SHA256, false,
CreatePairingRegistry(NULL, 0)));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyRejected(Authenticator::INVALID_CREDENTIALS);
}
TEST_F(NegotiatingAuthenticatorTest, PairingSucceeded) {
PairingRegistry::Pairing pairing;
pairing.client_id = kTestClientId;
pairing.shared_secret = kTestPairedSecret;
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestClientId, kTestPairedSecret, kTestPinBad, kTestPin,
AuthenticationMethod::HMAC_SHA256, false,
CreatePairingRegistry(&pairing, 1)));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyAccepted(AuthenticationMethod::Spake2Pair());
}
TEST_F(NegotiatingAuthenticatorTest, PairingSucceededInvalidSecretButPinOkay) {
PairingRegistry::Pairing pairing;
pairing.client_id = kTestClientId;
pairing.shared_secret = kTestPairedSecret;
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestClientId, kTestPairedSecretBad, kTestPin, kTestPin,
AuthenticationMethod::HMAC_SHA256, false,
CreatePairingRegistry(&pairing, 1)));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyAccepted(AuthenticationMethod::Spake2Pair());
}
TEST_F(NegotiatingAuthenticatorTest, PairingFailedInvalidSecretAndPin) {
PairingRegistry::Pairing pairing;
pairing.client_id = kTestClientId;
pairing.shared_secret = kTestPairedSecret;
ASSERT_NO_FATAL_FAILURE(InitAuthenticators(
kTestClientId, kTestPairedSecretBad, kTestPinBad, kTestPin,
AuthenticationMethod::HMAC_SHA256, false,
CreatePairingRegistry(&pairing, 1)));
ASSERT_NO_FATAL_FAILURE(RunAuthExchange());
VerifyRejected(Authenticator::INVALID_CREDENTIALS);
}
} // namespace protocol
} // namespace remoting
......@@ -12,6 +12,7 @@
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "remoting/protocol/channel_authenticator.h"
#include "remoting/protocol/pairing_client_authenticator.h"
#include "remoting/protocol/v2_authenticator.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
......@@ -19,11 +20,15 @@ namespace remoting {
namespace protocol {
NegotiatingClientAuthenticator::NegotiatingClientAuthenticator(
const std::string& client_pairing_id,
const std::string& shared_secret,
const std::string& authentication_tag,
const FetchSecretCallback& fetch_secret_callback,
scoped_ptr<ThirdPartyClientAuthenticator::TokenFetcher> token_fetcher,
const std::vector<AuthenticationMethod>& methods)
: NegotiatingAuthenticatorBase(MESSAGE_READY),
client_pairing_id_(client_pairing_id),
shared_secret_(shared_secret),
authentication_tag_(authentication_tag),
fetch_secret_callback_(fetch_secret_callback),
token_fetcher_(token_fetcher.Pass()),
......@@ -76,12 +81,13 @@ void NegotiatingClientAuthenticator::ProcessMessage(
scoped_ptr<buzz::XmlElement> NegotiatingClientAuthenticator::GetNextMessage() {
DCHECK_EQ(state(), MESSAGE_READY);
// This is the first message to the host, send a list of supported methods.
if (!current_method_.is_valid()) {
// If no authentication method has been chosen, see if we can optimistically
// choose one.
scoped_ptr<buzz::XmlElement> result;
current_authenticator_ = CreatePreferredAuthenticator();
CreatePreferredAuthenticator();
if (current_authenticator_) {
DCHECK(current_authenticator_->state() == MESSAGE_READY);
result = GetNextMessageInternal();
......@@ -117,16 +123,28 @@ void NegotiatingClientAuthenticator::CreateAuthenticatorForCurrentMethod(
token_fetcher_.Pass()));
resume_callback.Run();
} else {
DCHECK(current_method_.type() == AuthenticationMethod::SPAKE2 ||
current_method_.type() == AuthenticationMethod::SPAKE2_PAIR);
// TODO(jamiewalch): Add a bool parameter to the fetch secret callback to
// indicate whether or not to show the "remember me" checkbox. Set it to
// (current_method_.type() == AuthenticationMethod::SPAKE2_PAIR).
fetch_secret_callback_.Run(base::Bind(
&NegotiatingClientAuthenticator::CreateV2AuthenticatorWithSecret,
weak_factory_.GetWeakPtr(), preferred_initial_state, resume_callback));
}
}
scoped_ptr<Authenticator>
NegotiatingClientAuthenticator::CreatePreferredAuthenticator() {
NOTIMPLEMENTED();
return scoped_ptr<Authenticator>();
void NegotiatingClientAuthenticator::CreatePreferredAuthenticator() {
if (!client_pairing_id_.empty() && !shared_secret_.empty() &&
std::find(methods_.begin(), methods_.end(),
AuthenticationMethod::Spake2Pair()) != methods_.end()) {
// If the client specified a pairing id and shared secret, then create a
// PairingAuthenticator.
current_authenticator_.reset(new PairingClientAuthenticator(
client_pairing_id_, shared_secret_, fetch_secret_callback_,
authentication_tag_));
current_method_ = AuthenticationMethod::Spake2Pair();
}
}
void NegotiatingClientAuthenticator::CreateV2AuthenticatorWithSecret(
......
......@@ -19,15 +19,14 @@
namespace remoting {
namespace protocol {
typedef base::Callback<void(const std::string& secret)> SecretFetchedCallback;
typedef base::Callback<void(
const SecretFetchedCallback& secret_fetched_callback)> FetchSecretCallback;
// Client-side implementation of NegotiatingAuthenticatorBase.
// See comments in negotiating_authenticator_base.h for a general explanation.
class NegotiatingClientAuthenticator : public NegotiatingAuthenticatorBase {
public:
// TODO(jamiewalch): Pass ClientConfig instead of separate parameters.
NegotiatingClientAuthenticator(
const std::string& client_pairing_id,
const std::string& shared_secret,
const std::string& authentication_tag,
const FetchSecretCallback& fetch_secret_callback,
scoped_ptr<ThirdPartyClientAuthenticator::TokenFetcher> token_fetcher_,
......@@ -55,9 +54,10 @@ class NegotiatingClientAuthenticator : public NegotiatingAuthenticatorBase {
// and to instead reply with an alternative method. See the comments
// in negotiating_authenticator_base.h for more details.
//
// Returns the preferred authenticator if possible, or NULL otherwise.
scoped_ptr<Authenticator> CreatePreferredAuthenticator();
// Sets |current_authenticator_| and |current_method_| iff the client
// has a preferred authenticator that can optimistically send an initial
// message.
void CreatePreferredAuthenticator();
// Creates a V2Authenticator in state |initial_state| with the given
// |shared_secret|, then runs |resume_callback|.
......@@ -66,7 +66,11 @@ class NegotiatingClientAuthenticator : public NegotiatingAuthenticatorBase {
const base::Closure& resume_callback,
const std::string& shared_secret);
// Used for both authenticators.
// Used for pairing authenticators
std::string client_pairing_id_;
std::string shared_secret_;
// Used for all authenticators.
std::string authentication_tag_;
// Used for shared secret authenticators.
......
......@@ -13,6 +13,8 @@
#include "base/strings/string_split.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/protocol/channel_authenticator.h"
#include "remoting/protocol/pairing_host_authenticator.h"
#include "remoting/protocol/pairing_registry.h"
#include "remoting/protocol/v2_authenticator.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
......@@ -32,11 +34,16 @@ scoped_ptr<Authenticator> NegotiatingHostAuthenticator::CreateWithSharedSecret(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& shared_secret_hash,
AuthenticationMethod::HashFunction hash_function) {
AuthenticationMethod::HashFunction hash_function,
scoped_refptr<PairingRegistry> pairing_registry) {
scoped_ptr<NegotiatingHostAuthenticator> result(
new NegotiatingHostAuthenticator(local_cert, key_pair));
result->shared_secret_hash_ = shared_secret_hash;
result->pairing_registry_ = pairing_registry;
result->AddMethod(AuthenticationMethod::Spake2(hash_function));
if (pairing_registry) {
result->AddMethod(AuthenticationMethod::Spake2Pair());
}
return scoped_ptr<Authenticator>(result.Pass());
}
......@@ -72,8 +79,9 @@ void NegotiatingHostAuthenticator::ProcessMessage(
return;
}
// If the client did not specify auth method or specified unknown method,
// then select the first known method from the supported-methods attribute.
// If the client did not specify a preferred auth method, or specified an
// unknown or unsupported method, then select the first known method from
// the supported-methods attribute.
if (!method.is_valid() ||
std::find(methods_.begin(), methods_.end(), method) == methods_.end()) {
method = AuthenticationMethod::Invalid();
......@@ -155,12 +163,25 @@ void NegotiatingHostAuthenticator::CreateAuthenticator(
DCHECK(token_validator_);
current_authenticator_.reset(new ThirdPartyHostAuthenticator(
local_cert_, local_key_pair_, token_validator_.Pass()));
} else if (current_method_ == AuthenticationMethod::Spake2Pair() &&
preferred_initial_state == WAITING_MESSAGE) {
// If the client requested Spake2Pair and sent an initial message, attempt
// the paired connection protocol.
current_authenticator_.reset(new PairingHostAuthenticator(
pairing_registry_, local_cert_, local_key_pair_, shared_secret_hash_));
} else {
// In all other cases, use the V2 protocol. Note that this includes the
// case where the protocol is Spake2Pair but the client is not yet paired.
// In this case, the on-the-wire protocol is plain Spake2, advertised as
// Spake2Pair so that the client knows that the host supports pairing and
// that it can therefore present the option to the user when they enter
// the PIN.
DCHECK(current_method_.type() == AuthenticationMethod::SPAKE2 ||
current_method_.type() == AuthenticationMethod::SPAKE2_PAIR);