blob: a0b6a06545f081dfa652da82d11d06376e1d8100 [file] [log] [blame]
isherman40e54e02014-10-15 02:20:071// 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.
4
5#include "components/proximity_auth/client.h"
6
7#include "base/json/json_reader.h"
8#include "base/json/json_writer.h"
9#include "base/values.h"
isherman40e54e02014-10-15 02:20:0710#include "components/proximity_auth/client_observer.h"
11#include "components/proximity_auth/connection.h"
tengsf32e94c2015-04-09 07:49:1012#include "components/proximity_auth/cryptauth/base64url.h"
isherman40e54e02014-10-15 02:20:0713#include "components/proximity_auth/remote_status_update.h"
14#include "components/proximity_auth/secure_context.h"
15#include "components/proximity_auth/wire_message.h"
16
17namespace proximity_auth {
18namespace {
19
20// The key names of JSON fields for messages sent between the devices.
21const char kTypeKey[] = "type";
22const char kNameKey[] = "name";
23const char kDataKey[] = "data";
24const char kEncryptedDataKey[] = "encrypted_data";
25
26// The types of messages that can be sent and received.
27const char kMessageTypeLocalEvent[] = "event";
28const char kMessageTypeRemoteStatusUpdate[] = "status_update";
29const char kMessageTypeDecryptRequest[] = "decrypt_request";
30const char kMessageTypeDecryptResponse[] = "decrypt_response";
31const char kMessageTypeUnlockRequest[] = "unlock_request";
32const char kMessageTypeUnlockResponse[] = "unlock_response";
33
34// The name for an unlock event originating from the local device.
35const char kUnlockEventName[] = "easy_unlock";
36
37// Serializes the |value| to a JSON string and returns the result.
38std::string SerializeValueToJson(const base::Value& value) {
39 std::string json;
40 base::JSONWriter::Write(&value, &json);
41 return json;
42}
43
44// Returns the message type represented by the |message|. This is a convenience
45// wrapper that should only be called when the |message| is known to specify its
46// message type, i.e. this should not be called for untrusted input.
47std::string GetMessageType(const base::DictionaryValue& message) {
48 std::string type;
49 message.GetString(kTypeKey, &type);
50 return type;
51}
52
53} // namespace
54
55Client::Client(scoped_ptr<Connection> connection,
56 scoped_ptr<SecureContext> secure_context)
57 : connection_(connection.Pass()), secure_context_(secure_context.Pass()) {
58 DCHECK(connection_->IsConnected());
59 connection_->AddObserver(this);
60}
61
62Client::~Client() {
63 if (connection_)
64 connection_->RemoveObserver(this);
65}
66
67void Client::AddObserver(ClientObserver* observer) {
68 observers_.AddObserver(observer);
69}
70
71void Client::RemoveObserver(ClientObserver* observer) {
72 observers_.RemoveObserver(observer);
73}
74
75bool Client::SupportsSignIn() const {
76 return (secure_context_->GetProtocolVersion() ==
77 SecureContext::PROTOCOL_VERSION_THREE_ONE);
78}
79
80void Client::DispatchUnlockEvent() {
81 base::DictionaryValue message;
82 message.SetString(kTypeKey, kMessageTypeLocalEvent);
83 message.SetString(kNameKey, kUnlockEventName);
84 queued_messages_.push_back(PendingMessage(message));
85 ProcessMessageQueue();
86}
87
88void Client::RequestDecryption(const std::string& challenge) {
89 if (!SupportsSignIn()) {
90 VLOG(1) << "[Client] Dropping decryption request, as remote device "
91 << "does not support protocol v3.1.";
92 FOR_EACH_OBSERVER(ClientObserver,
93 observers_,
94 OnDecryptResponse(scoped_ptr<std::string>()));
95 return;
96 }
97
98 // TODO(isherman): Compute the encrypted message data for realz.
99 const std::string encrypted_message_data = challenge;
100 std::string encrypted_message_data_base64;
101 Base64UrlEncode(encrypted_message_data, &encrypted_message_data_base64);
102
103 base::DictionaryValue message;
104 message.SetString(kTypeKey, kMessageTypeDecryptRequest);
105 message.SetString(kEncryptedDataKey, encrypted_message_data_base64);
106 queued_messages_.push_back(PendingMessage(message));
107 ProcessMessageQueue();
108}
109
110void Client::RequestUnlock() {
111 if (!SupportsSignIn()) {
112 VLOG(1) << "[Client] Dropping unlock request, as remote device does not "
113 << "support protocol v3.1.";
114 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockResponse(false));
115 return;
116 }
117
118 base::DictionaryValue message;
119 message.SetString(kTypeKey, kMessageTypeUnlockRequest);
120 queued_messages_.push_back(PendingMessage(message));
121 ProcessMessageQueue();
122}
123
124Client::PendingMessage::PendingMessage() {
125}
126
127Client::PendingMessage::PendingMessage(const base::DictionaryValue& message)
128 : json_message(SerializeValueToJson(message)),
129 type(GetMessageType(message)) {
130}
131
132Client::PendingMessage::~PendingMessage() {
133}
134
135void Client::ProcessMessageQueue() {
136 if (pending_message_ || queued_messages_.empty() ||
137 connection_->is_sending_message())
138 return;
139
140 pending_message_.reset(new PendingMessage(queued_messages_.front()));
141 queued_messages_.pop_front();
142
143 connection_->SendMessage(make_scoped_ptr(new WireMessage(
144 std::string(), secure_context_->Encode(pending_message_->json_message))));
145}
146
147void Client::HandleRemoteStatusUpdateMessage(
148 const base::DictionaryValue& message) {
149 scoped_ptr<RemoteStatusUpdate> status_update =
150 RemoteStatusUpdate::Deserialize(message);
151 if (!status_update) {
152 VLOG(1) << "[Client] Unexpected remote status update: " << message;
153 return;
154 }
155
156 FOR_EACH_OBSERVER(
157 ClientObserver, observers_, OnRemoteStatusUpdate(*status_update));
158}
159
160void Client::HandleDecryptResponseMessage(
161 const base::DictionaryValue& message) {
162 std::string base64_data;
163 std::string decrypted_data;
164 scoped_ptr<std::string> response;
165 if (!message.GetString(kDataKey, &base64_data) || base64_data.empty()) {
166 VLOG(1) << "[Client] Decrypt response missing '" << kDataKey << "' value.";
167 } else if (!Base64UrlDecode(base64_data, &decrypted_data)) {
168 VLOG(1) << "[Client] Unable to base64-decode decrypt response.";
169 } else {
170 response.reset(new std::string(decrypted_data));
171 }
172 FOR_EACH_OBSERVER(
173 ClientObserver, observers_, OnDecryptResponse(response.Pass()));
174}
175
176void Client::HandleUnlockResponseMessage(const base::DictionaryValue& message) {
177 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockResponse(true));
178}
179
ishermanfd7c5342015-04-24 00:43:53180void Client::OnConnectionStatusChanged(Connection* connection,
isherman40e54e02014-10-15 02:20:07181 Connection::Status old_status,
182 Connection::Status new_status) {
ishermanfd7c5342015-04-24 00:43:53183 DCHECK_EQ(connection, connection_.get());
isherman40e54e02014-10-15 02:20:07184 if (new_status != Connection::CONNECTED) {
185 VLOG(1) << "[Client] Secure channel disconnected...";
186 connection_->RemoveObserver(this);
187 connection_.reset();
188 FOR_EACH_OBSERVER(ClientObserver, observers_, OnDisconnected());
189 // TODO(isherman): Determine whether it's also necessary/appropriate to fire
190 // this notification from the destructor.
191 }
192}
193
194void Client::OnMessageReceived(const Connection& connection,
195 const WireMessage& wire_message) {
196 std::string json_message = secure_context_->Decode(wire_message.payload());
197 scoped_ptr<base::Value> message_value(base::JSONReader::Read(json_message));
198 if (!message_value || !message_value->IsType(base::Value::TYPE_DICTIONARY)) {
199 VLOG(1) << "[Client] Unable to parse message as JSON: " << json_message
200 << ".";
201 return;
202 }
203
204 base::DictionaryValue* message;
205 bool success = message_value->GetAsDictionary(&message);
206 DCHECK(success);
207
208 std::string type;
209 if (!message->GetString(kTypeKey, &type)) {
210 VLOG(1) << "[Client] Missing '" << kTypeKey
211 << "' key in message: " << json_message << ".";
212 return;
213 }
214
215 // Remote status updates can be received out of the blue.
216 if (type == kMessageTypeRemoteStatusUpdate) {
217 HandleRemoteStatusUpdateMessage(*message);
218 return;
219 }
220
221 // All other messages should only be received in response to a message that
222 // the client sent.
223 if (!pending_message_) {
224 VLOG(1) << "[Client] Unexpected message received: " << json_message;
225 return;
226 }
227
228 std::string expected_type;
229 if (pending_message_->type == kMessageTypeDecryptRequest)
230 expected_type = kMessageTypeDecryptResponse;
231 else if (pending_message_->type == kMessageTypeUnlockRequest)
232 expected_type = kMessageTypeUnlockResponse;
233 else
234 NOTREACHED(); // There are no other message types that expect a response.
235
236 if (type != expected_type) {
237 VLOG(1) << "[Client] Unexpected '" << kTypeKey << "' value in message. "
238 << "Expected '" << expected_type << "' but received '" << type
239 << "'.";
240 return;
241 }
242
243 if (type == kMessageTypeDecryptResponse)
244 HandleDecryptResponseMessage(*message);
245 else if (type == kMessageTypeUnlockResponse)
246 HandleUnlockResponseMessage(*message);
247 else
248 NOTREACHED(); // There are no other message types that expect a response.
249
250 pending_message_.reset();
251 ProcessMessageQueue();
252}
253
254void Client::OnSendCompleted(const Connection& connection,
255 const WireMessage& wire_message,
256 bool success) {
257 if (!pending_message_) {
258 VLOG(1) << "[Client] Unexpected message sent.";
259 return;
260 }
261
262 // In the common case, wait for a response from the remote device.
263 // Don't wait if the message could not be sent, as there won't ever be a
264 // response in that case. Likewise, don't wait for a response to local
265 // event messages, as there is no response for such messages.
266 if (success && pending_message_->type != kMessageTypeLocalEvent)
267 return;
268
269 // Notify observer of failure if sending the message fails.
270 // For local events, we don't expect a response, so on success, we
271 // notify observers right away.
272 if (pending_message_->type == kMessageTypeDecryptRequest) {
273 FOR_EACH_OBSERVER(ClientObserver,
274 observers_,
275 OnDecryptResponse(scoped_ptr<std::string>()));
276 } else if (pending_message_->type == kMessageTypeUnlockRequest) {
277 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockResponse(false));
278 } else if (pending_message_->type == kMessageTypeLocalEvent) {
279 FOR_EACH_OBSERVER(ClientObserver, observers_, OnUnlockEventSent(success));
280 } else {
281 VLOG(1) << "[Client] Message of unknown type '" << pending_message_->type
282 << "sent.";
283 }
284
285 pending_message_.reset();
286 ProcessMessageQueue();
287}
288
289} // namespace proximity_auth