Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / remoting / protocol / negotiating_host_authenticator.cc
blob37c7a57e52002601460d83025f940473430b62fd
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/negotiating_host_authenticator.h"
7 #include <algorithm>
8 #include <sstream>
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/logging.h"
13 #include "base/strings/string_split.h"
14 #include "remoting/base/rsa_key_pair.h"
15 #include "remoting/protocol/channel_authenticator.h"
16 #include "remoting/protocol/pairing_host_authenticator.h"
17 #include "remoting/protocol/pairing_registry.h"
18 #include "remoting/protocol/token_validator.h"
19 #include "remoting/protocol/v2_authenticator.h"
20 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
22 namespace remoting {
23 namespace protocol {
25 NegotiatingHostAuthenticator::NegotiatingHostAuthenticator(
26 const std::string& local_cert,
27 scoped_refptr<RsaKeyPair> key_pair)
28 : NegotiatingAuthenticatorBase(WAITING_MESSAGE),
29 local_cert_(local_cert),
30 local_key_pair_(key_pair) {
33 // static
34 scoped_ptr<Authenticator> NegotiatingHostAuthenticator::CreateWithSharedSecret(
35 const std::string& local_cert,
36 scoped_refptr<RsaKeyPair> key_pair,
37 const std::string& shared_secret_hash,
38 AuthenticationMethod::HashFunction hash_function,
39 scoped_refptr<PairingRegistry> pairing_registry) {
40 scoped_ptr<NegotiatingHostAuthenticator> result(
41 new NegotiatingHostAuthenticator(local_cert, key_pair));
42 result->shared_secret_hash_ = shared_secret_hash;
43 result->pairing_registry_ = pairing_registry;
44 result->AddMethod(AuthenticationMethod::Spake2(hash_function));
45 if (pairing_registry.get()) {
46 result->AddMethod(AuthenticationMethod::Spake2Pair());
48 return result.Pass();
51 // static
52 scoped_ptr<Authenticator>
53 NegotiatingHostAuthenticator::CreateWithThirdPartyAuth(
54 const std::string& local_cert,
55 scoped_refptr<RsaKeyPair> key_pair,
56 scoped_ptr<TokenValidator> token_validator) {
57 scoped_ptr<NegotiatingHostAuthenticator> result(
58 new NegotiatingHostAuthenticator(local_cert, key_pair));
59 result->token_validator_ = token_validator.Pass();
60 result->AddMethod(AuthenticationMethod::ThirdParty());
61 return result.Pass();
64 NegotiatingHostAuthenticator::~NegotiatingHostAuthenticator() {
67 void NegotiatingHostAuthenticator::ProcessMessage(
68 const buzz::XmlElement* message,
69 const base::Closure& resume_callback) {
70 DCHECK_EQ(state(), WAITING_MESSAGE);
72 std::string method_attr = message->Attr(kMethodAttributeQName);
73 AuthenticationMethod method = AuthenticationMethod::FromString(method_attr);
75 // If the host has already chosen a method, it can't be changed by the client.
76 if (current_method_.is_valid() && method != current_method_) {
77 state_ = REJECTED;
78 rejection_reason_ = PROTOCOL_ERROR;
79 resume_callback.Run();
80 return;
83 // If the client did not specify a preferred auth method, or specified an
84 // unknown or unsupported method, then select the first known method from
85 // the supported-methods attribute.
86 if (!method.is_valid() ||
87 std::find(methods_.begin(), methods_.end(), method) == methods_.end()) {
88 method = AuthenticationMethod::Invalid();
90 std::string supported_methods_attr =
91 message->Attr(kSupportedMethodsAttributeQName);
92 if (supported_methods_attr.empty()) {
93 // Message contains neither method nor supported-methods attributes.
94 state_ = REJECTED;
95 rejection_reason_ = PROTOCOL_ERROR;
96 resume_callback.Run();
97 return;
100 // Find the first mutually-supported method in the client's list of
101 // supported-methods.
102 std::vector<std::string> supported_methods_strs;
103 base::SplitString(supported_methods_attr, kSupportedMethodsSeparator,
104 &supported_methods_strs);
105 for (std::vector<std::string>::iterator it = supported_methods_strs.begin();
106 it != supported_methods_strs.end(); ++it) {
107 AuthenticationMethod list_value = AuthenticationMethod::FromString(*it);
108 if (list_value.is_valid() &&
109 std::find(methods_.begin(),
110 methods_.end(), list_value) != methods_.end()) {
111 // Found common method.
112 method = list_value;
113 break;
117 if (!method.is_valid()) {
118 // Failed to find a common auth method.
119 state_ = REJECTED;
120 rejection_reason_ = PROTOCOL_ERROR;
121 resume_callback.Run();
122 return;
125 // Drop the current message because we've chosen a different method.
126 current_method_ = method;
127 state_ = PROCESSING_MESSAGE;
128 CreateAuthenticator(MESSAGE_READY, base::Bind(
129 &NegotiatingHostAuthenticator::UpdateState,
130 base::Unretained(this), resume_callback));
131 return;
134 // If the client specified a supported method, and the host hasn't chosen a
135 // method yet, use the client's preferred method and process the message.
136 if (!current_method_.is_valid()) {
137 current_method_ = method;
138 state_ = PROCESSING_MESSAGE;
139 // Copy the message since the authenticator may process it asynchronously.
140 CreateAuthenticator(WAITING_MESSAGE, base::Bind(
141 &NegotiatingAuthenticatorBase::ProcessMessageInternal,
142 base::Unretained(this), base::Owned(new buzz::XmlElement(*message)),
143 resume_callback));
144 return;
147 // If the client is using the host's current method, just process the message.
148 ProcessMessageInternal(message, resume_callback);
151 scoped_ptr<buzz::XmlElement> NegotiatingHostAuthenticator::GetNextMessage() {
152 return GetNextMessageInternal();
155 void NegotiatingHostAuthenticator::CreateAuthenticator(
156 Authenticator::State preferred_initial_state,
157 const base::Closure& resume_callback) {
158 DCHECK(current_method_.is_valid());
160 if (current_method_.type() == AuthenticationMethod::THIRD_PARTY) {
161 // |ThirdPartyHostAuthenticator| takes ownership of |token_validator_|.
162 // The authentication method negotiation logic should guarantee that only
163 // one |ThirdPartyHostAuthenticator| will need to be created per session.
164 DCHECK(token_validator_);
165 current_authenticator_.reset(new ThirdPartyHostAuthenticator(
166 local_cert_, local_key_pair_, token_validator_.Pass()));
167 } else if (current_method_ == AuthenticationMethod::Spake2Pair() &&
168 preferred_initial_state == WAITING_MESSAGE) {
169 // If the client requested Spake2Pair and sent an initial message, attempt
170 // the paired connection protocol.
171 current_authenticator_.reset(new PairingHostAuthenticator(
172 pairing_registry_, local_cert_, local_key_pair_, shared_secret_hash_));
173 } else {
174 // In all other cases, use the V2 protocol. Note that this includes the
175 // case where the protocol is Spake2Pair but the client is not yet paired.
176 // In this case, the on-the-wire protocol is plain Spake2, advertised as
177 // Spake2Pair so that the client knows that the host supports pairing and
178 // that it can therefore present the option to the user when they enter
179 // the PIN.
180 DCHECK(current_method_.type() == AuthenticationMethod::SPAKE2 ||
181 current_method_.type() == AuthenticationMethod::SPAKE2_PAIR);
182 current_authenticator_ = V2Authenticator::CreateForHost(
183 local_cert_, local_key_pair_, shared_secret_hash_,
184 preferred_initial_state);
186 resume_callback.Run();
189 } // namespace protocol
190 } // namespace remoting