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 bool PairingAuthenticatorBase::started() const {
42 if (!v2_authenticator_
) {
45 return v2_authenticator_
->started();
48 Authenticator::RejectionReason
49 PairingAuthenticatorBase::rejection_reason() const {
50 if (!v2_authenticator_
) {
51 return PROTOCOL_ERROR
;
53 return v2_authenticator_
->rejection_reason();
56 void PairingAuthenticatorBase::ProcessMessage(
57 const buzz::XmlElement
* message
,
58 const base::Closure
& resume_callback
) {
59 DCHECK_EQ(state(), WAITING_MESSAGE
);
61 // The client authenticator creates the underlying authenticator in the ctor
62 // and the host creates it in response to the first message before deferring
63 // to this class to process it. Either way, it should exist here.
64 DCHECK(v2_authenticator_
);
66 // If pairing failed, and we haven't already done so, try again with the PIN.
67 if (using_paired_secret_
&& HasErrorMessage(message
)) {
68 using_paired_secret_
= false;
69 waiting_for_authenticator_
= true;
70 v2_authenticator_
.reset();
71 SetAuthenticatorCallback set_authenticator
= base::Bind(
72 &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage
,
73 weak_factory_
.GetWeakPtr(), base::Owned(new buzz::XmlElement(*message
)),
75 CreateV2AuthenticatorWithPIN(WAITING_MESSAGE
, set_authenticator
);
79 // Pass the message to the underlying authenticator for processing, but
80 // check for a failed SPAKE exchange if we're using the paired secret. In
81 // this case the pairing protocol can continue by communicating the error
82 // to the peer and retrying with the PIN.
83 v2_authenticator_
->ProcessMessage(
85 base::Bind(&PairingAuthenticatorBase::CheckForFailedSpakeExchange
,
86 weak_factory_
.GetWeakPtr(), resume_callback
));
89 scoped_ptr
<buzz::XmlElement
> PairingAuthenticatorBase::GetNextMessage() {
90 DCHECK_EQ(state(), MESSAGE_READY
);
91 scoped_ptr
<buzz::XmlElement
> result
= v2_authenticator_
->GetNextMessage();
92 AddPairingElements(result
.get());
93 MaybeAddErrorMessage(result
.get());
97 scoped_ptr
<ChannelAuthenticator
>
98 PairingAuthenticatorBase::CreateChannelAuthenticator() const {
99 return v2_authenticator_
->CreateChannelAuthenticator();
102 void PairingAuthenticatorBase::MaybeAddErrorMessage(buzz::XmlElement
* message
) {
103 if (!error_message_
.empty()) {
104 buzz::XmlElement
* pairing_failed_tag
=
105 new buzz::XmlElement(kPairingFailedTag
);
106 pairing_failed_tag
->AddAttr(kPairingErrorAttribute
, error_message_
);
107 message
->AddElement(pairing_failed_tag
);
108 error_message_
.clear();
112 bool PairingAuthenticatorBase::HasErrorMessage(
113 const buzz::XmlElement
* message
) const {
114 const buzz::XmlElement
* pairing_failed_tag
=
115 message
->FirstNamed(kPairingFailedTag
);
116 if (pairing_failed_tag
) {
117 std::string error
= pairing_failed_tag
->Attr(kPairingErrorAttribute
);
118 LOG(ERROR
) << "Pairing failed: " << error
;
120 return pairing_failed_tag
!= NULL
;
123 void PairingAuthenticatorBase::CheckForFailedSpakeExchange(
124 const base::Closure
& resume_callback
) {
125 // If the SPAKE exchange failed due to invalid credentials, and those
126 // credentials were the paired secret, then notify the peer that the
127 // PIN-less connection failed and retry using the PIN.
128 if (v2_authenticator_
->state() == REJECTED
&&
129 v2_authenticator_
->rejection_reason() == INVALID_CREDENTIALS
&&
130 using_paired_secret_
) {
131 using_paired_secret_
= false;
132 error_message_
= "invalid-shared-secret";
133 v2_authenticator_
.reset();
134 buzz::XmlElement
* no_message
= NULL
;
135 SetAuthenticatorCallback set_authenticator
= base::Bind(
136 &PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage
,
137 weak_factory_
.GetWeakPtr(), no_message
, resume_callback
);
138 CreateV2AuthenticatorWithPIN(MESSAGE_READY
, set_authenticator
);
142 resume_callback
.Run();
145 void PairingAuthenticatorBase::SetAuthenticatorAndProcessMessage(
146 const buzz::XmlElement
* message
,
147 const base::Closure
& resume_callback
,
148 scoped_ptr
<Authenticator
> authenticator
) {
149 DCHECK(!v2_authenticator_
);
150 DCHECK(authenticator
);
151 waiting_for_authenticator_
= false;
152 v2_authenticator_
= authenticator
.Pass();
154 ProcessMessage(message
, resume_callback
);
156 resume_callback
.Run();
160 } // namespace protocol
161 } // namespace remoting