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.h"
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/values.h"
10 #include "components/proximity_auth/base64url.h"
11 #include "components/proximity_auth/client_observer.h"
12 #include "components/proximity_auth/connection.h"
13 #include "components/proximity_auth/remote_status_update.h"
14 #include "components/proximity_auth/secure_context.h"
15 #include "components/proximity_auth/wire_message.h"
17 namespace proximity_auth
{
20 // The key names of JSON fields for messages sent between the devices.
21 const char kTypeKey
[] = "type";
22 const char kNameKey
[] = "name";
23 const char kDataKey
[] = "data";
24 const char kEncryptedDataKey
[] = "encrypted_data";
26 // The types of messages that can be sent and received.
27 const char kMessageTypeLocalEvent
[] = "event";
28 const char kMessageTypeRemoteStatusUpdate
[] = "status_update";
29 const char kMessageTypeDecryptRequest
[] = "decrypt_request";
30 const char kMessageTypeDecryptResponse
[] = "decrypt_response";
31 const char kMessageTypeUnlockRequest
[] = "unlock_request";
32 const char kMessageTypeUnlockResponse
[] = "unlock_response";
34 // The name for an unlock event originating from the local device.
35 const char kUnlockEventName
[] = "easy_unlock";
37 // Serializes the |value| to a JSON string and returns the result.
38 std::string
SerializeValueToJson(const base::Value
& value
) {
40 base::JSONWriter::Write(&value
, &json
);
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.
47 std::string
GetMessageType(const base::DictionaryValue
& message
) {
49 message
.GetString(kTypeKey
, &type
);
55 Client::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);
64 connection_
->RemoveObserver(this);
67 void Client::AddObserver(ClientObserver
* observer
) {
68 observers_
.AddObserver(observer
);
71 void Client::RemoveObserver(ClientObserver
* observer
) {
72 observers_
.RemoveObserver(observer
);
75 bool Client::SupportsSignIn() const {
76 return (secure_context_
->GetProtocolVersion() ==
77 SecureContext::PROTOCOL_VERSION_THREE_ONE
);
80 void 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();
88 void 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
,
94 OnDecryptResponse(scoped_ptr
<std::string
>()));
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
);
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();
110 void 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));
118 base::DictionaryValue message
;
119 message
.SetString(kTypeKey
, kMessageTypeUnlockRequest
);
120 queued_messages_
.push_back(PendingMessage(message
));
121 ProcessMessageQueue();
124 Client::PendingMessage::PendingMessage() {
127 Client::PendingMessage::PendingMessage(const base::DictionaryValue
& message
)
128 : json_message(SerializeValueToJson(message
)),
129 type(GetMessageType(message
)) {
132 Client::PendingMessage::~PendingMessage() {
135 void Client::ProcessMessageQueue() {
136 if (pending_message_
|| queued_messages_
.empty() ||
137 connection_
->is_sending_message())
140 pending_message_
.reset(new PendingMessage(queued_messages_
.front()));
141 queued_messages_
.pop_front();
143 connection_
->SendMessage(make_scoped_ptr(new WireMessage(
144 std::string(), secure_context_
->Encode(pending_message_
->json_message
))));
147 void 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
;
157 ClientObserver
, observers_
, OnRemoteStatusUpdate(*status_update
));
160 void 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.";
170 response
.reset(new std::string(decrypted_data
));
173 ClientObserver
, observers_
, OnDecryptResponse(response
.Pass()));
176 void Client::HandleUnlockResponseMessage(const base::DictionaryValue
& message
) {
177 FOR_EACH_OBSERVER(ClientObserver
, observers_
, OnUnlockResponse(true));
180 void Client::OnConnectionStatusChanged(const Connection
& connection
,
181 Connection::Status old_status
,
182 Connection::Status new_status
) {
183 DCHECK_EQ(&connection
, connection_
.get());
184 if (new_status
!= Connection::CONNECTED
) {
185 VLOG(1) << "[Client] Secure channel disconnected...";
186 connection_
->RemoveObserver(this);
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.
194 void 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
204 base::DictionaryValue
* message
;
205 bool success
= message_value
->GetAsDictionary(&message
);
209 if (!message
->GetString(kTypeKey
, &type
)) {
210 VLOG(1) << "[Client] Missing '" << kTypeKey
211 << "' key in message: " << json_message
<< ".";
215 // Remote status updates can be received out of the blue.
216 if (type
== kMessageTypeRemoteStatusUpdate
) {
217 HandleRemoteStatusUpdateMessage(*message
);
221 // All other messages should only be received in response to a message that
223 if (!pending_message_
) {
224 VLOG(1) << "[Client] Unexpected message received: " << json_message
;
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
;
234 NOTREACHED(); // There are no other message types that expect a response.
236 if (type
!= expected_type
) {
237 VLOG(1) << "[Client] Unexpected '" << kTypeKey
<< "' value in message. "
238 << "Expected '" << expected_type
<< "' but received '" << type
243 if (type
== kMessageTypeDecryptResponse
)
244 HandleDecryptResponseMessage(*message
);
245 else if (type
== kMessageTypeUnlockResponse
)
246 HandleUnlockResponseMessage(*message
);
248 NOTREACHED(); // There are no other message types that expect a response.
250 pending_message_
.reset();
251 ProcessMessageQueue();
254 void Client::OnSendCompleted(const Connection
& connection
,
255 const WireMessage
& wire_message
,
257 if (!pending_message_
) {
258 VLOG(1) << "[Client] Unexpected message sent.";
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
)
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
,
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
));
281 VLOG(1) << "[Client] Message of unknown type '" << pending_message_
->type
285 pending_message_
.reset();
286 ProcessMessageQueue();
289 } // namespace proximity_auth