diff options
author | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-22 09:00:23 +0000 |
---|---|---|
committer | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-22 09:00:23 +0000 |
commit | 6434bfe41768fc8d86f77c6772721a60e356a01f (patch) | |
tree | e214bfb5547f84109633d879655d04c559a47033 /remoting/protocol | |
parent | 62efa785ae40cd8058db085d0d57dfe31dd3e49f (diff) | |
download | chromium_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')
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_ |