| Index: remoting/protocol/pairing_authenticator_base.cc
 | 
| diff --git a/remoting/protocol/pairing_authenticator_base.cc b/remoting/protocol/pairing_authenticator_base.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..6f3f3277a4cd881e861ce40fe37c414ce45446e1
 | 
| --- /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
 | 
| 
 |