1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/protocol/pairing_authenticator_base.h"
8 #include "remoting/base/constants.h"
9 #include "remoting/protocol/channel_authenticator.h"
14 const buzz::StaticQName PairingAuthenticatorBase::kPairingInfoTag =
15 { kChromotingXmlNamespace, "pairing-info" };
16 const buzz::StaticQName PairingAuthenticatorBase::kClientIdAttribute =
20 const buzz::StaticQName kPairingFailedTag =
21 { kChromotingXmlNamespace, "pairing-failed" };
22 const buzz::StaticQName kPairingErrorAttribute = { "", "error" };
25 PairingAuthenticatorBase::PairingAuthenticatorBase()
26 : using_paired_secret_(false),
27 waiting_for_authenticator_(false),
31 PairingAuthenticatorBase::~PairingAuthenticatorBase() {
34 Authenticator::State PairingAuthenticatorBase::state() const {
35 if (waiting_for_authenticator_) {
36 return PROCESSING_MESSAGE;
38 return v2_authenticator_->state();
41 Authenticator::RejectionReason
42 PairingAuthenticatorBase::rejection_reason() const {
43 if (!v2_authenticator_) {
44 return PROTOCOL_ERROR;
46 return v2_authenticator_->rejection_reason();
49 void PairingAuthenticatorBase::ProcessMessage(
50 const buzz::XmlElement* message,
51 const base::Closure& resume_callback) {
52 DCHECK_EQ(state(), WAITING_MESSAGE);
54 // The client authenticator creates the underlying authenticator in the ctor
55 // and the host creates it in response to the first message before deferring
56 // to this class to process it. Either way, it should exist here.
57 DCHECK(v2_authenticator_);
59 // If pairing failed, and we haven't already done so, try again with the PIN.
60 if (using_paired_secret_ && HasErrorMessage(message)) {
61 using_paired_secret_ = false;
62 waiting_for_authenticator_ = true;
63 v2_authenticator_.reset();
64 SetAuthenticatorCallback set_authenticator = base::Bind(
65 &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage,
66 weak_factory_.GetWeakPtr(), base::Owned(new buzz::XmlElement(*message)),
68 CreateV2AuthenticatorWithPIN(WAITING_MESSAGE, set_authenticator);
72 // Pass the message to the underlying authenticator for processing, but
73 // check for a failed SPAKE exchange if we're using the paired secret. In
74 // this case the pairing protocol can continue by communicating the error
75 // to the peer and retrying with the PIN.
76 v2_authenticator_->ProcessMessage(
78 base::Bind(&PairingAuthenticatorBase::CheckForFailedSpakeExchange,
79 weak_factory_.GetWeakPtr(), resume_callback));
82 scoped_ptr<buzz::XmlElement> PairingAuthenticatorBase::GetNextMessage() {
83 DCHECK_EQ(state(), MESSAGE_READY);
84 scoped_ptr<buzz::XmlElement> result = v2_authenticator_->GetNextMessage();
85 AddPairingElements(result.get());
86 MaybeAddErrorMessage(result.get());
90 scoped_ptr<ChannelAuthenticator>
91 PairingAuthenticatorBase::CreateChannelAuthenticator() const {
92 return v2_authenticator_->CreateChannelAuthenticator();
95 void PairingAuthenticatorBase::MaybeAddErrorMessage(buzz::XmlElement* message) {
96 if (!error_message_.empty()) {
97 buzz::XmlElement* pairing_failed_tag =
98 new buzz::XmlElement(kPairingFailedTag);
99 pairing_failed_tag->AddAttr(kPairingErrorAttribute, error_message_);
100 message->AddElement(pairing_failed_tag);
101 error_message_.clear();
105 bool PairingAuthenticatorBase::HasErrorMessage(
106 const buzz::XmlElement* message) const {
107 const buzz::XmlElement* pairing_failed_tag =
108 message->FirstNamed(kPairingFailedTag);
109 if (pairing_failed_tag) {
110 std::string error = pairing_failed_tag->Attr(kPairingErrorAttribute);
111 LOG(ERROR) << "Pairing failed: " << error;
113 return pairing_failed_tag != NULL;
116 void PairingAuthenticatorBase::CheckForFailedSpakeExchange(
117 const base::Closure& resume_callback) {
118 // If the SPAKE exchange failed due to invalid credentials, and those
119 // credentials were the paired secret, then notify the peer that the
120 // PIN-less connection failed and retry using the PIN.
121 if (v2_authenticator_->state() == REJECTED &&
122 v2_authenticator_->rejection_reason() == INVALID_CREDENTIALS &&
123 using_paired_secret_) {
124 using_paired_secret_ = false;
125 error_message_ = "invalid-shared-secret";
126 v2_authenticator_.reset();
127 buzz::XmlElement* no_message = NULL;
128 SetAuthenticatorCallback set_authenticator = base::Bind(
129 &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage,
130 weak_factory_.GetWeakPtr(), no_message, resume_callback);
131 CreateV2AuthenticatorWithPIN(MESSAGE_READY, set_authenticator);
135 resume_callback.Run();
138 void PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage(
139 const buzz::XmlElement* message,
140 const base::Closure& resume_callback,
141 scoped_ptr<Authenticator> authenticator) {
142 DCHECK(!v2_authenticator_);
143 DCHECK(authenticator);
144 waiting_for_authenticator_ = false;
145 v2_authenticator_ = authenticator.Pass();
147 ProcessMessage(message, resume_callback);
149 resume_callback.Run();
153 } // namespace protocol
154 } // namespace remoting