summaryrefslogtreecommitdiffstats
path: root/remoting/protocol
diff options
context:
space:
mode:
authorjamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-22 09:00:23 +0000
committerjamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-22 09:00:23 +0000
commit6434bfe41768fc8d86f77c6772721a60e356a01f (patch)
treee214bfb5547f84109633d879655d04c559a47033 /remoting/protocol
parent62efa785ae40cd8058db085d0d57dfe31dd3e49f (diff)
downloadchromium_src-6434bfe41768fc8d86f77c6772721a60e356a01f.zip
chromium_src-6434bfe41768fc8d86f77c6772721a60e356a01f.tar.gz
chromium_src-6434bfe41768fc8d86f77c6772721a60e356a01f.tar.bz2
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
Diffstat (limited to 'remoting/protocol')
-rw-r--r--remoting/protocol/authentication_method.cc34
-rw-r--r--remoting/protocol/authentication_method.h2
-rw-r--r--remoting/protocol/authenticator.h4
-rw-r--r--remoting/protocol/it2me_host_authenticator_factory.cc2
-rw-r--r--remoting/protocol/me2me_host_authenticator_factory.cc6
-rw-r--r--remoting/protocol/me2me_host_authenticator_factory.h10
-rw-r--r--remoting/protocol/negotiating_authenticator_base.h6
-rw-r--r--remoting/protocol/negotiating_authenticator_unittest.cc160
-rw-r--r--remoting/protocol/negotiating_client_authenticator.cc28
-rw-r--r--remoting/protocol/negotiating_client_authenticator.h20
-rw-r--r--remoting/protocol/negotiating_host_authenticator.cc29
-rw-r--r--remoting/protocol/negotiating_host_authenticator.h10
-rw-r--r--remoting/protocol/pairing_authenticator_base.cc154
-rw-r--r--remoting/protocol/pairing_authenticator_base.h106
-rw-r--r--remoting/protocol/pairing_client_authenticator.cc72
-rw-r--r--remoting/protocol/pairing_client_authenticator.h49
-rw-r--r--remoting/protocol/pairing_host_authenticator.cc104
-rw-r--r--remoting/protocol/pairing_host_authenticator.h56
18 files changed, 794 insertions, 58 deletions
diff --git a/remoting/protocol/authentication_method.cc b/remoting/protocol/authentication_method.cc
index 71ec008..e584efc 100644
--- a/remoting/protocol/authentication_method.cc
+++ b/remoting/protocol/authentication_method.cc
@@ -23,6 +23,11 @@ AuthenticationMethod AuthenticationMethod::Spake2(HashFunction 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";
diff --git a/remoting/protocol/authentication_method.h b/remoting/protocol/authentication_method.h
index df7937b..c2265d5 100644
--- a/remoting/protocol/authentication_method.h
+++ b/remoting/protocol/authentication_method.h
@@ -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
diff --git a/remoting/protocol/authenticator.h b/remoting/protocol/authenticator.h
index 53e0d26..43cf646 100644
--- a/remoting/protocol/authenticator.h
+++ b/remoting/protocol/authenticator.h
@@ -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
diff --git a/remoting/protocol/it2me_host_authenticator_factory.cc b/remoting/protocol/it2me_host_authenticator_factory.cc
index e5e446a..20a1ada 100644
--- a/remoting/protocol/it2me_host_authenticator_factory.cc
+++ b/remoting/protocol/it2me_host_authenticator_factory.cc
@@ -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
diff --git a/remoting/protocol/me2me_host_authenticator_factory.cc b/remoting/protocol/me2me_host_authenticator_factory.cc
index 5d908e7..1448102 100644
--- a/remoting/protocol/me2me_host_authenticator_factory.cc
+++ b/remoting/protocol/me2me_host_authenticator_factory.cc
@@ -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());
diff --git a/remoting/protocol/me2me_host_authenticator_factory.h b/remoting/protocol/me2me_host_authenticator_factory.h
index 7d0eebe..cecb2b6 100644
--- a/remoting/protocol/me2me_host_authenticator_factory.h
+++ b/remoting/protocol/me2me_host_authenticator_factory.h
@@ -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);
};
diff --git a/remoting/protocol/negotiating_authenticator_base.h b/remoting/protocol/negotiating_authenticator_base.h
index 97209c5..1f80967 100644
--- a/remoting/protocol/negotiating_authenticator_base.h
+++ b/remoting/protocol/negotiating_authenticator_base.h
@@ -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;
diff --git a/remoting/protocol/negotiating_authenticator_unittest.cc b/remoting/protocol/negotiating_authenticator_unittest.cc
index 92047d6..c02d0b07 100644
--- a/remoting/protocol/negotiating_authenticator_unittest.cc
+++ b/remoting/protocol/negotiating_authenticator_unittest.cc
@@ -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
diff --git a/remoting/protocol/negotiating_client_authenticator.cc b/remoting/protocol/negotiating_client_authenticator.cc
index af5d56b..c6a2a7b 100644
--- a/remoting/protocol/negotiating_client_authenticator.cc
+++ b/remoting/protocol/negotiating_client_authenticator.cc
@@ -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(
diff --git a/remoting/protocol/negotiating_client_authenticator.h b/remoting/protocol/negotiating_client_authenticator.h
index 995cf1dd..05a8b17 100644
--- a/remoting/protocol/negotiating_client_authenticator.h
+++ b/remoting/protocol/negotiating_client_authenticator.h
@@ -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.
diff --git a/remoting/protocol/negotiating_host_authenticator.cc b/remoting/protocol/negotiating_host_authenticator.cc
index f60a6d2..dfd46bd 100644
--- a/remoting/protocol/negotiating_host_authenticator.cc
+++ b/remoting/protocol/negotiating_host_authenticator.cc
@@ -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);
current_authenticator_ = V2Authenticator::CreateForHost(
local_cert_, local_key_pair_, shared_secret_hash_,
preferred_initial_state);
}
-
resume_callback.Run();
}
diff --git a/remoting/protocol/negotiating_host_authenticator.h b/remoting/protocol/negotiating_host_authenticator.h
index 588fee2..ede3e8e 100644
--- a/remoting/protocol/negotiating_host_authenticator.h
+++ b/remoting/protocol/negotiating_host_authenticator.h
@@ -11,10 +11,10 @@
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
#include "remoting/protocol/authentication_method.h"
#include "remoting/protocol/authenticator.h"
#include "remoting/protocol/negotiating_authenticator_base.h"
+#include "remoting/protocol/pairing_registry.h"
#include "remoting/protocol/third_party_host_authenticator.h"
namespace remoting {
@@ -30,11 +30,14 @@ class NegotiatingHostAuthenticator : public NegotiatingAuthenticatorBase {
virtual ~NegotiatingHostAuthenticator();
// Creates a host authenticator, using a fixed shared secret/PIN hash.
+ // If |pairing_registry| is non-NULL then the Spake2Pair method will
+ // be offered, supporting PIN-less authentication.
static scoped_ptr<Authenticator> 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);
// Creates a host authenticator, using third party authentication.
static scoped_ptr<Authenticator> CreateWithThirdPartyAuth(
@@ -68,6 +71,9 @@ class NegotiatingHostAuthenticator : public NegotiatingAuthenticatorBase {
// Used only for third party host authenticators.
scoped_ptr<ThirdPartyHostAuthenticator::TokenValidator> token_validator_;
+ // Used only for pairing authenticators.
+ scoped_refptr<PairingRegistry> pairing_registry_;
+
DISALLOW_COPY_AND_ASSIGN(NegotiatingHostAuthenticator);
};
diff --git a/remoting/protocol/pairing_authenticator_base.cc b/remoting/protocol/pairing_authenticator_base.cc
new file mode 100644
index 0000000..6f3f327
--- /dev/null
+++ b/remoting/protocol/pairing_authenticator_base.cc
@@ -0,0 +1,154 @@
+// Copyright 2013 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/protocol/pairing_authenticator_base.h"
+
+#include "base/bind.h"
+#include "remoting/base/constants.h"
+#include "remoting/protocol/channel_authenticator.h"
+
+namespace remoting {
+namespace protocol {
+
+const buzz::StaticQName PairingAuthenticatorBase::kPairingInfoTag =
+ { kChromotingXmlNamespace, "pairing-info" };
+const buzz::StaticQName PairingAuthenticatorBase::kClientIdAttribute =
+ { "", "client-id" };
+
+namespace {
+const buzz::StaticQName kPairingFailedTag =
+ { kChromotingXmlNamespace, "pairing-failed" };
+const buzz::StaticQName kPairingErrorAttribute = { "", "error" };
+} // namespace
+
+PairingAuthenticatorBase::PairingAuthenticatorBase()
+ : using_paired_secret_(false),
+ waiting_for_authenticator_(false),
+ weak_factory_(this) {
+}
+
+PairingAuthenticatorBase::~PairingAuthenticatorBase() {
+}
+
+Authenticator::State PairingAuthenticatorBase::state() const {
+ if (waiting_for_authenticator_) {
+ return PROCESSING_MESSAGE;
+ }
+ return v2_authenticator_->state();
+}
+
+Authenticator::RejectionReason
+PairingAuthenticatorBase::rejection_reason() const {
+ if (!v2_authenticator_) {
+ return PROTOCOL_ERROR;
+ }
+ return v2_authenticator_->rejection_reason();
+}
+
+void PairingAuthenticatorBase::ProcessMessage(
+ const buzz::XmlElement* message,
+ const base::Closure& resume_callback) {
+ DCHECK_EQ(state(), WAITING_MESSAGE);
+
+ // The client authenticator creates the underlying authenticator in the ctor
+ // and the host creates it in response to the first message before deferring
+ // to this class to process it. Either way, it should exist here.
+ DCHECK(v2_authenticator_);
+
+ // If pairing failed, and we haven't already done so, try again with the PIN.
+ if (using_paired_secret_ && HasErrorMessage(message)) {
+ using_paired_secret_ = false;
+ waiting_for_authenticator_ = true;
+ v2_authenticator_.reset();
+ SetAuthenticatorCallback set_authenticator = base::Bind(
+ &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage,
+ weak_factory_.GetWeakPtr(), base::Owned(new buzz::XmlElement(*message)),
+ resume_callback);
+ CreateV2AuthenticatorWithPIN(WAITING_MESSAGE, set_authenticator);
+ return;
+ }
+
+ // Pass the message to the underlying authenticator for processing, but
+ // check for a failed SPAKE exchange if we're using the paired secret. In
+ // this case the pairing protocol can continue by communicating the error
+ // to the peer and retrying with the PIN.
+ v2_authenticator_->ProcessMessage(
+ message,
+ base::Bind(&PairingAuthenticatorBase::CheckForFailedSpakeExchange,
+ weak_factory_.GetWeakPtr(), resume_callback));
+}
+
+scoped_ptr<buzz::XmlElement> PairingAuthenticatorBase::GetNextMessage() {
+ DCHECK_EQ(state(), MESSAGE_READY);
+ scoped_ptr<buzz::XmlElement> result = v2_authenticator_->GetNextMessage();
+ AddPairingElements(result.get());
+ MaybeAddErrorMessage(result.get());
+ return result.Pass();
+}
+
+scoped_ptr<ChannelAuthenticator>
+PairingAuthenticatorBase::CreateChannelAuthenticator() const {
+ return v2_authenticator_->CreateChannelAuthenticator();
+}
+
+void PairingAuthenticatorBase::MaybeAddErrorMessage(buzz::XmlElement* message) {
+ if (!error_message_.empty()) {
+ buzz::XmlElement* pairing_failed_tag =
+ new buzz::XmlElement(kPairingFailedTag);
+ pairing_failed_tag->AddAttr(kPairingErrorAttribute, error_message_);
+ message->AddElement(pairing_failed_tag);
+ error_message_.clear();
+ }
+}
+
+bool PairingAuthenticatorBase::HasErrorMessage(
+ const buzz::XmlElement* message) const {
+ const buzz::XmlElement* pairing_failed_tag =
+ message->FirstNamed(kPairingFailedTag);
+ if (pairing_failed_tag) {
+ std::string error = pairing_failed_tag->Attr(kPairingErrorAttribute);
+ LOG(INFO) << "Pairing failed: " << error;
+ }
+ return pairing_failed_tag != NULL;
+}
+
+void PairingAuthenticatorBase::CheckForFailedSpakeExchange(
+ const base::Closure& resume_callback) {
+ // If the SPAKE exchange failed due to invalid credentials, and those
+ // credentials were the paired secret, then notify the peer that the
+ // PIN-less connection failed and retry using the PIN.
+ if (v2_authenticator_->state() == REJECTED &&
+ v2_authenticator_->rejection_reason() == INVALID_CREDENTIALS &&
+ using_paired_secret_) {
+ using_paired_secret_ = false;
+ error_message_ = "invalid-shared-secret";
+ v2_authenticator_.reset();
+ buzz::XmlElement* no_message = NULL;
+ SetAuthenticatorCallback set_authenticator = base::Bind(
+ &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage,
+ weak_factory_.GetWeakPtr(), no_message, resume_callback);
+ CreateV2AuthenticatorWithPIN(MESSAGE_READY, set_authenticator);
+ return;
+ }
+
+ resume_callback.Run();
+}
+
+void PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage(
+ const buzz::XmlElement* message,
+ const base::Closure& resume_callback,
+ scoped_ptr<Authenticator> authenticator) {
+ DCHECK(!v2_authenticator_);
+ DCHECK(authenticator);
+ waiting_for_authenticator_ = false;
+ v2_authenticator_ = authenticator.Pass();
+ if (message) {
+ ProcessMessage(message, resume_callback);
+ } else {
+ resume_callback.Run();
+ }
+}
+
+} // namespace protocol
+} // namespace remoting
diff --git a/remoting/protocol/pairing_authenticator_base.h b/remoting/protocol/pairing_authenticator_base.h
new file mode 100644
index 0000000..2a4bc4e
--- /dev/null
+++ b/remoting/protocol/pairing_authenticator_base.h
@@ -0,0 +1,106 @@
+// Copyright 2013 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.
+
+#ifndef REMOTING_PROTOCOL_PAIRING_AUTHENTICATOR_BASE_H_
+#define REMOTING_PROTOCOL_PAIRING_AUTHENTICATOR_BASE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "remoting/protocol/authenticator.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+namespace remoting {
+namespace protocol {
+
+// The pairing authenticator builds on top of V2Authenticator to add
+// support for PIN-less authentication via device pairing:
+//
+// * If a client device is already paired, it includes in the initial
+// authentication message a Client Id and the first SPAKE message
+// using the Paired Secret and HMAC_SHA256.
+// * If the host recognizes the Client Id, it looks up the corresponding
+// Paired Secret and continue the SPAKE exchange.
+// * If it does not recognize the Client Id, it initiates a SPAKE exchange
+// with HMAC_SHA256 using the PIN as the shared secret. The initial
+// message of this exchange includes an an error message, which
+// informs the client that the PIN-less connection failed and causes
+// it to prompt the user for a PIN to use for authentication
+// instead.
+// * If, at any point, the SPAKE exchange fails with the Paired Secret,
+// the endpoint that detects the failure initiates a new SPAKE exchange
+// using the PIN, and includes an error message to instruct the peer
+// to do likewise.
+//
+// If a client device is not already paired, but supports pairing, then
+// the V2Authenticator is used instead of this class. Only the method name
+// differs, which the client uses to determine that pairing should be offered
+// to the user (see NegotiatingHostAuthenticator::CreateAuthenticator and
+// NegotiatingClientAuthenticator::CreateAuthenticatorForCurrentMethod).
+class PairingAuthenticatorBase : public Authenticator {
+ public:
+ PairingAuthenticatorBase();
+ virtual ~PairingAuthenticatorBase();
+
+ // Authenticator interface.
+ virtual State state() const OVERRIDE;
+ virtual RejectionReason rejection_reason() const OVERRIDE;
+ virtual void ProcessMessage(const buzz::XmlElement* message,
+ const base::Closure& resume_callback) OVERRIDE;
+ virtual scoped_ptr<buzz::XmlElement> GetNextMessage() OVERRIDE;
+ virtual scoped_ptr<ChannelAuthenticator>
+ CreateChannelAuthenticator() const OVERRIDE;
+
+ protected:
+ typedef base::Callback<void(scoped_ptr<Authenticator> authenticator)>
+ SetAuthenticatorCallback;
+
+ static const buzz::StaticQName kPairingInfoTag;
+ static const buzz::StaticQName kClientIdAttribute;
+
+ // Create a V2 authenticator in the specified state, prompting the user for
+ // the PIN first if necessary.
+ virtual void CreateV2AuthenticatorWithPIN(
+ State initial_state,
+ const SetAuthenticatorCallback& callback) = 0;
+
+ // Amend an authenticator message, for example to add client- or host-specific
+ // elements to it.
+ virtual void AddPairingElements(buzz::XmlElement* message) = 0;
+
+ // A non-fatal error message that derived classes should set in order to
+ // cause the peer to be notified that pairing has failed and that it should
+ // fall back on PIN authentication. This string need not be human-readable,
+ // nor is it currently used other than being logged.
+ std::string error_message_;
+
+ // The underlying V2 authenticator, created with either the PIN or the
+ // Paired Secret by the derived class.
+ scoped_ptr<Authenticator> v2_authenticator_;
+
+ // Derived classes must set this to True if the underlying authenticator is
+ // using the Paired Secret.
+ bool using_paired_secret_;
+
+ private:
+ // Helper methods for ProcessMessage and GetNextMessage
+ void MaybeAddErrorMessage(buzz::XmlElement* message);
+ bool HasErrorMessage(const buzz::XmlElement* message) const;
+ void CheckForFailedSpakeExchange(const base::Closure& resume_callback);
+ void SetAuthenticatorAndProcessMessage(
+ const buzz::XmlElement* message,
+ const base::Closure& resume_callback,
+ scoped_ptr<Authenticator> authenticator);
+
+ // Set to true if a PIN-based authenticator has been requested but has not
+ // yet been set.
+ bool waiting_for_authenticator_;
+
+ base::WeakPtrFactory<PairingAuthenticatorBase> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PairingAuthenticatorBase);
+};
+
+} // namespace protocol
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_PAIRING_AUTHENTICATOR_H_
diff --git a/remoting/protocol/pairing_client_authenticator.cc b/remoting/protocol/pairing_client_authenticator.cc
new file mode 100644
index 0000000..a80f328
--- /dev/null
+++ b/remoting/protocol/pairing_client_authenticator.cc
@@ -0,0 +1,72 @@
+// Copyright 2013 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/protocol/pairing_client_authenticator.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "remoting/base/constants.h"
+#include "remoting/base/rsa_key_pair.h"
+#include "remoting/protocol/authentication_method.h"
+#include "remoting/protocol/channel_authenticator.h"
+#include "remoting/protocol/v2_authenticator.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+namespace remoting {
+namespace protocol {
+
+PairingClientAuthenticator::PairingClientAuthenticator(
+ const std::string& client_id,
+ const std::string& paired_secret,
+ const FetchSecretCallback& fetch_pin_callback,
+ const std::string& authentication_tag)
+ : sent_client_id_(false),
+ client_id_(client_id),
+ paired_secret_(paired_secret),
+ fetch_pin_callback_(fetch_pin_callback),
+ authentication_tag_(authentication_tag),
+ weak_factory_(this) {
+ v2_authenticator_ = V2Authenticator::CreateForClient(
+ paired_secret_, MESSAGE_READY);
+ using_paired_secret_ = true;
+}
+
+PairingClientAuthenticator::~PairingClientAuthenticator() {
+}
+
+void PairingClientAuthenticator::CreateV2AuthenticatorWithPIN(
+ State initial_state,
+ const SetAuthenticatorCallback& set_authenticator_callback) {
+ SecretFetchedCallback callback = base::Bind(
+ &PairingClientAuthenticator::OnPinFetched,
+ weak_factory_.GetWeakPtr(), initial_state, set_authenticator_callback);
+ fetch_pin_callback_.Run(callback);
+}
+
+void PairingClientAuthenticator::AddPairingElements(buzz::XmlElement* message) {
+ // If the client id and secret have not yet been sent, do so now. Note that
+ // in this case the V2Authenticator is being used optimistically to send the
+ // first message of the SPAKE exchange since we don't yet know whether or not
+ // the host will accept the client id or request that we fall back to the PIN.
+ if (!sent_client_id_) {
+ buzz::XmlElement* pairing_tag = new buzz::XmlElement(kPairingInfoTag);
+ pairing_tag->AddAttr(kClientIdAttribute, client_id_);
+ message->AddElement(pairing_tag);
+ sent_client_id_ = true;
+ }
+}
+
+void PairingClientAuthenticator::OnPinFetched(
+ State initial_state,
+ const SetAuthenticatorCallback& callback,
+ const std::string& pin) {
+ callback.Run(V2Authenticator::CreateForClient(
+ AuthenticationMethod::ApplyHashFunction(
+ AuthenticationMethod::HMAC_SHA256,
+ authentication_tag_, pin),
+ initial_state));
+}
+
+} // namespace protocol
+} // namespace remoting
diff --git a/remoting/protocol/pairing_client_authenticator.h b/remoting/protocol/pairing_client_authenticator.h
new file mode 100644
index 0000000..21f96e2
--- /dev/null
+++ b/remoting/protocol/pairing_client_authenticator.h
@@ -0,0 +1,49 @@
+// Copyright 2013 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.
+
+#ifndef REMOTING_PROTOCOL_PAIRING_CLIENT_AUTHENTICATOR_H_
+#define REMOTING_PROTOCOL_PAIRING_CLIENT_AUTHENTICATOR_H_
+
+#include "base/memory/weak_ptr.h"
+#include "remoting/protocol/pairing_authenticator_base.h"
+
+namespace remoting {
+namespace protocol {
+
+class PairingClientAuthenticator : public PairingAuthenticatorBase {
+ public:
+ PairingClientAuthenticator(
+ const std::string& client_id,
+ const std::string& paired_secret,
+ const FetchSecretCallback& fetch_pin_callback,
+ const std::string& authentication_tag);
+ virtual ~PairingClientAuthenticator();
+
+ private:
+ // PairingAuthenticatorBase interface.
+ virtual void CreateV2AuthenticatorWithPIN(
+ State initial_state,
+ const SetAuthenticatorCallback& callback) OVERRIDE;
+ virtual void AddPairingElements(buzz::XmlElement* message) OVERRIDE;
+
+ void OnPinFetched(State initial_state,
+ const SetAuthenticatorCallback& callback,
+ const std::string& pin);
+
+ // Protocol state.
+ bool sent_client_id_;
+ std::string client_id_;
+ const std::string& paired_secret_;
+ FetchSecretCallback fetch_pin_callback_;
+ std::string authentication_tag_;
+
+ base::WeakPtrFactory<PairingClientAuthenticator> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PairingClientAuthenticator);
+};
+
+} // namespace protocol
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_PAIRING_AUTHENTICATOR_H_
diff --git a/remoting/protocol/pairing_host_authenticator.cc b/remoting/protocol/pairing_host_authenticator.cc
new file mode 100644
index 0000000..9e57265
--- /dev/null
+++ b/remoting/protocol/pairing_host_authenticator.cc
@@ -0,0 +1,104 @@
+// Copyright 2013 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/protocol/pairing_host_authenticator.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "remoting/base/constants.h"
+#include "remoting/base/rsa_key_pair.h"
+#include "remoting/protocol/channel_authenticator.h"
+#include "remoting/protocol/pairing_registry.h"
+#include "remoting/protocol/v2_authenticator.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+namespace remoting {
+namespace protocol {
+
+PairingHostAuthenticator::PairingHostAuthenticator(
+ scoped_refptr<PairingRegistry> pairing_registry,
+ const std::string& local_cert,
+ scoped_refptr<RsaKeyPair> key_pair,
+ const std::string& pin)
+ : pairing_registry_(pairing_registry),
+ local_cert_(local_cert),
+ key_pair_(key_pair),
+ pin_(pin),
+ protocol_error_(false),
+ weak_factory_(this) {
+}
+
+PairingHostAuthenticator::~PairingHostAuthenticator() {
+}
+
+Authenticator::State PairingHostAuthenticator::state() const {
+ if (protocol_error_) {
+ return REJECTED;
+ } else if (!v2_authenticator_) {
+ return WAITING_MESSAGE;
+ }
+ return PairingAuthenticatorBase::state();
+}
+
+Authenticator::RejectionReason
+PairingHostAuthenticator::rejection_reason() const {
+ if (protocol_error_) {
+ return PROTOCOL_ERROR;
+ }
+ return PairingAuthenticatorBase::rejection_reason();
+}
+
+void PairingHostAuthenticator::CreateV2AuthenticatorWithPIN(
+ State initial_state,
+ const SetAuthenticatorCallback& callback) {
+ callback.Run(V2Authenticator::CreateForHost(
+ local_cert_, key_pair_, pin_, initial_state));
+}
+
+void PairingHostAuthenticator::ProcessMessage(
+ const buzz::XmlElement* message,
+ const base::Closure& resume_callback) {
+ if (!v2_authenticator_) {
+ std::string client_id;
+ std::string paired_secret;
+
+ const buzz::XmlElement* pairing_tag = message->FirstNamed(kPairingInfoTag);
+ if (pairing_tag) {
+ client_id = pairing_tag->Attr(kClientIdAttribute);
+ }
+
+ if (client_id.empty()) {
+ LOG(ERROR) << "No client id specified.";
+ protocol_error_ = true;
+ } else {
+ paired_secret = pairing_registry_->GetSecret(client_id);
+ if (paired_secret.empty()) {
+ LOG(INFO) << "Unknown client id";
+ error_message_ = "unknown-client-id";
+ }
+ }
+
+ using_paired_secret_ = !paired_secret.empty();
+ if (using_paired_secret_) {
+ v2_authenticator_ = V2Authenticator::CreateForHost(
+ local_cert_, key_pair_, paired_secret, WAITING_MESSAGE);
+ } else {
+ v2_authenticator_ = V2Authenticator::CreateForHost(
+ local_cert_, key_pair_, pin_, MESSAGE_READY);
+ // The client's optimistic SPAKE message is using a Paired Secret to
+ // which the host doesn't have access, so don't bother processing it.
+ resume_callback.Run();
+ return;
+ }
+ }
+
+ PairingAuthenticatorBase::ProcessMessage(message, resume_callback);
+}
+
+void PairingHostAuthenticator::AddPairingElements(buzz::XmlElement* message) {
+ // Nothing to do here
+}
+
+} // namespace protocol
+} // namespace remoting
diff --git a/remoting/protocol/pairing_host_authenticator.h b/remoting/protocol/pairing_host_authenticator.h
new file mode 100644
index 0000000..a4884c4
--- /dev/null
+++ b/remoting/protocol/pairing_host_authenticator.h
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+
+#ifndef REMOTING_PROTOCOL_PAIRING_HOST_AUTHENTICATOR_H_
+#define REMOTING_PROTOCOL_PAIRING_HOST_AUTHENTICATOR_H_
+
+#include "base/memory/weak_ptr.h"
+#include "remoting/protocol/pairing_authenticator_base.h"
+
+namespace remoting {
+
+class RsaKeyPair;
+
+namespace protocol {
+
+class PairingRegistry;
+
+class PairingHostAuthenticator : public PairingAuthenticatorBase {
+ public:
+ PairingHostAuthenticator(
+ scoped_refptr<PairingRegistry> pairing_registry,
+ const std::string& local_cert,
+ scoped_refptr<RsaKeyPair> key_pair,
+ const std::string& pin);
+ virtual ~PairingHostAuthenticator();
+
+ // Authenticator interface.
+ virtual State state() const OVERRIDE;
+ virtual RejectionReason rejection_reason() const OVERRIDE;
+ virtual void ProcessMessage(const buzz::XmlElement* message,
+ const base::Closure& resume_callback) OVERRIDE;
+
+ private:
+ // PairingAuthenticatorBase interface.
+ virtual void CreateV2AuthenticatorWithPIN(
+ State initial_state,
+ const SetAuthenticatorCallback& callback) OVERRIDE;
+ virtual void AddPairingElements(buzz::XmlElement* message) OVERRIDE;
+
+ // Protocol state.
+ scoped_refptr<PairingRegistry> pairing_registry_;
+ std::string local_cert_;
+ scoped_refptr<RsaKeyPair> key_pair_;
+ const std::string& pin_;
+ bool protocol_error_;
+
+ base::WeakPtrFactory<PairingHostAuthenticator> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PairingHostAuthenticator);
+};
+
+} // namespace protocol
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_PAIRING_AUTHENTICATOR_H_