Roll src/third_party/WebKit e0ce2a6:675d715 (svn 202297:202300)
[chromium-blink-merge.git] / components / proximity_auth / client_impl.cc
blob7a96f50a05cf0831066cd668caf6b3e9369146e1
1 // Copyright 2014 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 "components/proximity_auth/client_impl.h"
7 #include "base/bind.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/values.h"
11 #include "components/proximity_auth/client_observer.h"
12 #include "components/proximity_auth/connection.h"
13 #include "components/proximity_auth/cryptauth/base64url.h"
14 #include "components/proximity_auth/logging/logging.h"
15 #include "components/proximity_auth/remote_status_update.h"
16 #include "components/proximity_auth/secure_context.h"
17 #include "components/proximity_auth/wire_message.h"
19 namespace proximity_auth {
20 namespace {
22 // The key names of JSON fields for messages sent between the devices.
23 const char kTypeKey[] = "type";
24 const char kNameKey[] = "name";
25 const char kDataKey[] = "data";
26 const char kEncryptedDataKey[] = "encrypted_data";
28 // The types of messages that can be sent and received.
29 const char kMessageTypeLocalEvent[] = "event";
30 const char kMessageTypeRemoteStatusUpdate[] = "status_update";
31 const char kMessageTypeDecryptRequest[] = "decrypt_request";
32 const char kMessageTypeDecryptResponse[] = "decrypt_response";
33 const char kMessageTypeUnlockRequest[] = "unlock_request";
34 const char kMessageTypeUnlockResponse[] = "unlock_response";
36 // The name for an unlock event originating from the local device.
37 const char kUnlockEventName[] = "easy_unlock";
39 // Serializes the |value| to a JSON string and returns the result.
40 std::string SerializeValueToJson(const base::Value& value) {
41 std::string json;
42 base::JSONWriter::Write(value, &json);
43 return json;
46 // Returns the message type represented by the |message|. This is a convenience
47 // wrapper that should only be called when the |message| is known to specify its
48 // message type, i.e. this should not be called for untrusted input.
49 std::string GetMessageType(const base::DictionaryValue& message) {
50 std::string type;
51 message.GetString(kTypeKey, &type);
52 return type;
55 } // namespace
57 ClientImpl::ClientImpl(scoped_ptr<Connection> connection,
58 scoped_ptr<SecureContext> secure_context)
59 : connection_(connection.Pass()),
60 secure_context_(secure_context.Pass()),
61 weak_ptr_factory_(this) {
62 DCHECK(connection_->IsConnected());
63 connection_->AddObserver(this);
66 ClientImpl::~ClientImpl() {
67 if (connection_)
68 connection_->RemoveObserver(this);
71 void ClientImpl::AddObserver(ClientObserver* observer) {
72 observers_.AddObserver(observer);
75 void ClientImpl::RemoveObserver(ClientObserver* observer) {
76 observers_.RemoveObserver(observer);
79 bool ClientImpl::SupportsSignIn() const {
80 return (secure_context_->GetProtocolVersion() ==
81 SecureContext::PROTOCOL_VERSION_THREE_ONE);
84 void ClientImpl::DispatchUnlockEvent() {
85 base::DictionaryValue message;
86 message.SetString(kTypeKey, kMessageTypeLocalEvent);
87 message.SetString(kNameKey, kUnlockEventName);
88 queued_messages_.push_back(PendingMessage(message));
89 ProcessMessageQueue();
92 void ClientImpl::RequestDecryption(const std::string& challenge) {
93 if (!SupportsSignIn()) {
94 PA_LOG(WARNING) << "Dropping decryption request, as remote device "
95 << "does not support protocol v3.1.";
96 FOR_EACH_OBSERVER(ClientObserver, observers_,
97 OnDecryptResponse(scoped_ptr<std::string>()));
98 return;
101 // TODO(isherman): Compute the encrypted message data for realz.
102 const std::string encrypted_message_data = challenge;
103 std::string encrypted_message_data_base64;
104 Base64UrlEncode(encrypted_message_data, &encrypted_message_data_base64);
106 base::DictionaryValue message;
107 message.SetString(kTypeKey, kMessageTypeDecryptRequest);
108 message.SetString(kEncryptedDataKey, encrypted_message_data_base64);
109 queued_messages_.push_back(PendingMessage(message));
110 ProcessMessageQueue();
113 void ClientImpl::RequestUnlock() {
114 if (!SupportsSignIn()) {
115 PA_LOG(WARNING) << "Dropping unlock request, as remote device does not "
116 << "support protocol v3.1.";
117 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockResponse(false));
118 return;
121 base::DictionaryValue message;
122 message.SetString(kTypeKey, kMessageTypeUnlockRequest);
123 queued_messages_.push_back(PendingMessage(message));
124 ProcessMessageQueue();
127 ClientImpl::PendingMessage::PendingMessage() {
130 ClientImpl::PendingMessage::PendingMessage(const base::DictionaryValue& message)
131 : json_message(SerializeValueToJson(message)),
132 type(GetMessageType(message)) {
135 ClientImpl::PendingMessage::~PendingMessage() {
138 void ClientImpl::ProcessMessageQueue() {
139 if (pending_message_ || queued_messages_.empty() ||
140 connection_->is_sending_message())
141 return;
143 pending_message_.reset(new PendingMessage(queued_messages_.front()));
144 queued_messages_.pop_front();
146 secure_context_->Encode(pending_message_->json_message,
147 base::Bind(&ClientImpl::OnMessageEncoded,
148 weak_ptr_factory_.GetWeakPtr()));
151 void ClientImpl::OnMessageEncoded(const std::string& encoded_message) {
152 connection_->SendMessage(make_scoped_ptr(new WireMessage(encoded_message)));
155 void ClientImpl::OnMessageDecoded(const std::string& decoded_message) {
156 // The decoded message should be a JSON string.
157 scoped_ptr<base::Value> message_value =
158 base::JSONReader::Read(decoded_message);
159 if (!message_value || !message_value->IsType(base::Value::TYPE_DICTIONARY)) {
160 PA_LOG(ERROR) << "Unable to parse message as JSON:\n" << decoded_message;
161 return;
164 base::DictionaryValue* message;
165 bool success = message_value->GetAsDictionary(&message);
166 DCHECK(success);
168 std::string type;
169 if (!message->GetString(kTypeKey, &type)) {
170 PA_LOG(ERROR) << "Missing '" << kTypeKey << "' key in message:\n "
171 << decoded_message;
172 return;
175 // Remote status updates can be received out of the blue.
176 if (type == kMessageTypeRemoteStatusUpdate) {
177 HandleRemoteStatusUpdateMessage(*message);
178 return;
181 // All other messages should only be received in response to a message that
182 // the client sent.
183 if (!pending_message_) {
184 PA_LOG(WARNING) << "Unexpected message received:\n" << decoded_message;
185 return;
188 std::string expected_type;
189 if (pending_message_->type == kMessageTypeDecryptRequest)
190 expected_type = kMessageTypeDecryptResponse;
191 else if (pending_message_->type == kMessageTypeUnlockRequest)
192 expected_type = kMessageTypeUnlockResponse;
193 else
194 NOTREACHED(); // There are no other message types that expect a response.
196 if (type != expected_type) {
197 PA_LOG(ERROR) << "Unexpected '" << kTypeKey << "' value in message. "
198 << "Expected '" << expected_type << "' but received '" << type
199 << "'.";
200 return;
203 if (type == kMessageTypeDecryptResponse)
204 HandleDecryptResponseMessage(*message);
205 else if (type == kMessageTypeUnlockResponse)
206 HandleUnlockResponseMessage(*message);
207 else
208 NOTREACHED(); // There are no other message types that expect a response.
210 pending_message_.reset();
211 ProcessMessageQueue();
214 void ClientImpl::HandleRemoteStatusUpdateMessage(
215 const base::DictionaryValue& message) {
216 scoped_ptr<RemoteStatusUpdate> status_update =
217 RemoteStatusUpdate::Deserialize(message);
218 if (!status_update) {
219 PA_LOG(ERROR) << "Unexpected remote status update: " << message;
220 return;
223 FOR_EACH_OBSERVER(ClientObserver, observers_,
224 OnRemoteStatusUpdate(*status_update));
227 void ClientImpl::HandleDecryptResponseMessage(
228 const base::DictionaryValue& message) {
229 std::string base64_data;
230 std::string decrypted_data;
231 scoped_ptr<std::string> response;
232 if (!message.GetString(kDataKey, &base64_data) || base64_data.empty()) {
233 PA_LOG(ERROR) << "Decrypt response missing '" << kDataKey << "' value.";
234 } else if (!Base64UrlDecode(base64_data, &decrypted_data)) {
235 PA_LOG(ERROR) << "Unable to base64-decode decrypt response.";
236 } else {
237 response.reset(new std::string(decrypted_data));
239 FOR_EACH_OBSERVER(ClientObserver, observers_,
240 OnDecryptResponse(response.Pass()));
243 void ClientImpl::HandleUnlockResponseMessage(
244 const base::DictionaryValue& message) {
245 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockResponse(true));
248 void ClientImpl::OnConnectionStatusChanged(Connection* connection,
249 Connection::Status old_status,
250 Connection::Status new_status) {
251 DCHECK_EQ(connection, connection_.get());
252 if (new_status == Connection::DISCONNECTED) {
253 PA_LOG(INFO) << "Secure channel disconnected...";
254 connection_->RemoveObserver(this);
255 connection_.reset();
256 FOR_EACH_OBSERVER(ClientObserver, observers_, OnDisconnected());
257 // TODO(isherman): Determine whether it's also necessary/appropriate to fire
258 // this notification from the destructor.
262 void ClientImpl::OnMessageReceived(const Connection& connection,
263 const WireMessage& wire_message) {
264 secure_context_->Decode(wire_message.payload(),
265 base::Bind(&ClientImpl::OnMessageDecoded,
266 weak_ptr_factory_.GetWeakPtr()));
269 void ClientImpl::OnSendCompleted(const Connection& connection,
270 const WireMessage& wire_message,
271 bool success) {
272 if (!pending_message_) {
273 PA_LOG(ERROR) << "Unexpected message sent.";
274 return;
277 // In the common case, wait for a response from the remote device.
278 // Don't wait if the message could not be sent, as there won't ever be a
279 // response in that case. Likewise, don't wait for a response to local
280 // event messages, as there is no response for such messages.
281 if (success && pending_message_->type != kMessageTypeLocalEvent)
282 return;
284 // Notify observer of failure if sending the message fails.
285 // For local events, we don't expect a response, so on success, we
286 // notify observers right away.
287 if (pending_message_->type == kMessageTypeDecryptRequest) {
288 FOR_EACH_OBSERVER(ClientObserver, observers_,
289 OnDecryptResponse(scoped_ptr<std::string>()));
290 } else if (pending_message_->type == kMessageTypeUnlockRequest) {
291 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockResponse(false));
292 } else if (pending_message_->type == kMessageTypeLocalEvent) {
293 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockEventSent(success));
294 } else {
295 PA_LOG(ERROR) << "Message of unknown type '" << pending_message_->type
296 << "' sent.";
299 pending_message_.reset();
300 ProcessMessageQueue();
303 } // namespace proximity_auth