Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / proximity_auth / webui / proximity_auth_webui_handler.cc
blob5a3f8f4abd2d0eb086a95180bcc2a08850ff5115
1 // Copyright 2015 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/webui/proximity_auth_webui_handler.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/i18n/time_formatting.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/default_clock.h"
14 #include "base/time/default_tick_clock.h"
15 #include "base/values.h"
16 #include "components/proximity_auth/ble/bluetooth_low_energy_connection_finder.h"
17 #include "components/proximity_auth/ble/bluetooth_low_energy_device_whitelist.h"
18 #include "components/proximity_auth/ble/pref_names.h"
19 #include "components/proximity_auth/bluetooth_connection_finder.h"
20 #include "components/proximity_auth/bluetooth_throttler_impl.h"
21 #include "components/proximity_auth/cryptauth/base64url.h"
22 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h"
23 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
24 #include "components/proximity_auth/cryptauth/secure_message_delegate.h"
25 #include "components/proximity_auth/device_to_device_authenticator.h"
26 #include "components/proximity_auth/logging/logging.h"
27 #include "components/proximity_auth/messenger_impl.h"
28 #include "components/proximity_auth/remote_status_update.h"
29 #include "components/proximity_auth/secure_context.h"
30 #include "components/proximity_auth/webui/reachable_phone_flow.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/web_ui.h"
33 #include "device/bluetooth/bluetooth_uuid.h"
35 namespace proximity_auth {
37 namespace {
39 // The UUID of the Smart Lock classic Bluetooth service.
40 const char kClassicBluetoothServiceUUID[] =
41 "704EE561-3782-405A-A14B-2D47A2DDCDDF";
43 // The UUID of the Bluetooth Low Energy service.
44 const char kBLESmartLockServiceUUID[] = "b3b7e28e-a000-3e17-bd86-6e97b9e28c11";
46 // Keys in the JSON representation of a log message.
47 const char kLogMessageTextKey[] = "text";
48 const char kLogMessageTimeKey[] = "time";
49 const char kLogMessageFileKey[] = "file";
50 const char kLogMessageLineKey[] = "line";
51 const char kLogMessageSeverityKey[] = "severity";
53 // Keys in the JSON representation of a SyncState object for enrollment or
54 // device sync.
55 const char kSyncStateLastSuccessTime[] = "lastSuccessTime";
56 const char kSyncStateNextRefreshTime[] = "nextRefreshTime";
57 const char kSyncStateRecoveringFromFailure[] = "recoveringFromFailure";
58 const char kSyncStateOperationInProgress[] = "operationInProgress";
60 // Converts |log_message| to a raw dictionary value used as a JSON argument to
61 // JavaScript functions.
62 scoped_ptr<base::DictionaryValue> LogMessageToDictionary(
63 const LogBuffer::LogMessage& log_message) {
64 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue());
65 dictionary->SetString(kLogMessageTextKey, log_message.text);
66 dictionary->SetString(
67 kLogMessageTimeKey,
68 base::TimeFormatTimeOfDayWithMilliseconds(log_message.time));
69 dictionary->SetString(kLogMessageFileKey, log_message.file);
70 dictionary->SetInteger(kLogMessageLineKey, log_message.line);
71 dictionary->SetInteger(kLogMessageSeverityKey,
72 static_cast<int>(log_message.severity));
73 return dictionary.Pass();
76 // Keys in the JSON representation of an ExternalDeviceInfo proto.
77 const char kExternalDevicePublicKey[] = "publicKey";
78 const char kExternalDeviceFriendlyName[] = "friendlyDeviceName";
79 const char kExternalDeviceBluetoothAddress[] = "bluetoothAddress";
80 const char kExternalDeviceUnlockKey[] = "unlockKey";
81 const char kExternalDeviceConnectionStatus[] = "connectionStatus";
82 const char kExternalDeviceRemoteState[] = "remoteState";
84 // The possible values of the |kExternalDeviceConnectionStatus| field.
85 const char kExternalDeviceConnected[] = "connected";
86 const char kExternalDeviceDisconnected[] = "disconnected";
87 const char kExternalDeviceConnecting[] = "connecting";
89 // Keys in the JSON representation of an IneligibleDevice proto.
90 const char kIneligibleDeviceReasons[] = "ineligibilityReasons";
92 // Creates a SyncState JSON object that can be passed to the WebUI.
93 scoped_ptr<base::DictionaryValue> CreateSyncStateDictionary(
94 double last_success_time,
95 double next_refresh_time,
96 bool is_recovering_from_failure,
97 bool is_enrollment_in_progress) {
98 scoped_ptr<base::DictionaryValue> sync_state(new base::DictionaryValue());
99 sync_state->SetDouble(kSyncStateLastSuccessTime, last_success_time);
100 sync_state->SetDouble(kSyncStateNextRefreshTime, next_refresh_time);
101 sync_state->SetBoolean(kSyncStateRecoveringFromFailure,
102 is_recovering_from_failure);
103 sync_state->SetBoolean(kSyncStateOperationInProgress,
104 is_enrollment_in_progress);
105 return sync_state;
108 } // namespace
110 ProximityAuthWebUIHandler::ProximityAuthWebUIHandler(
111 ProximityAuthClient* proximity_auth_client)
112 : proximity_auth_client_(proximity_auth_client),
113 web_contents_initialized_(false),
114 weak_ptr_factory_(this) {
115 cryptauth_client_factory_ =
116 proximity_auth_client_->CreateCryptAuthClientFactory();
119 ProximityAuthWebUIHandler::~ProximityAuthWebUIHandler() {
120 LogBuffer::GetInstance()->RemoveObserver(this);
123 void ProximityAuthWebUIHandler::RegisterMessages() {
124 web_ui()->RegisterMessageCallback(
125 "onWebContentsInitialized",
126 base::Bind(&ProximityAuthWebUIHandler::OnWebContentsInitialized,
127 base::Unretained(this)));
129 web_ui()->RegisterMessageCallback(
130 "clearLogBuffer", base::Bind(&ProximityAuthWebUIHandler::ClearLogBuffer,
131 base::Unretained(this)));
133 web_ui()->RegisterMessageCallback(
134 "getLogMessages", base::Bind(&ProximityAuthWebUIHandler::GetLogMessages,
135 base::Unretained(this)));
137 web_ui()->RegisterMessageCallback(
138 "toggleUnlockKey", base::Bind(&ProximityAuthWebUIHandler::ToggleUnlockKey,
139 base::Unretained(this)));
141 web_ui()->RegisterMessageCallback(
142 "findEligibleUnlockDevices",
143 base::Bind(&ProximityAuthWebUIHandler::FindEligibleUnlockDevices,
144 base::Unretained(this)));
146 web_ui()->RegisterMessageCallback(
147 "findReachableDevices",
148 base::Bind(&ProximityAuthWebUIHandler::FindReachableDevices,
149 base::Unretained(this)));
151 web_ui()->RegisterMessageCallback(
152 "getLocalState", base::Bind(&ProximityAuthWebUIHandler::GetLocalState,
153 base::Unretained(this)));
155 web_ui()->RegisterMessageCallback(
156 "forceEnrollment", base::Bind(&ProximityAuthWebUIHandler::ForceEnrollment,
157 base::Unretained(this)));
159 web_ui()->RegisterMessageCallback(
160 "forceDeviceSync", base::Bind(&ProximityAuthWebUIHandler::ForceDeviceSync,
161 base::Unretained(this)));
163 web_ui()->RegisterMessageCallback(
164 "toggleConnection",
165 base::Bind(&ProximityAuthWebUIHandler::ToggleConnection,
166 base::Unretained(this)));
169 void ProximityAuthWebUIHandler::OnLogMessageAdded(
170 const LogBuffer::LogMessage& log_message) {
171 scoped_ptr<base::DictionaryValue> dictionary =
172 LogMessageToDictionary(log_message);
173 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogMessageAdded",
174 *dictionary);
177 void ProximityAuthWebUIHandler::OnLogBufferCleared() {
178 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogBufferCleared");
181 void ProximityAuthWebUIHandler::OnEnrollmentStarted() {
182 web_ui()->CallJavascriptFunction(
183 "LocalStateInterface.onEnrollmentStateChanged",
184 *GetEnrollmentStateDictionary());
187 void ProximityAuthWebUIHandler::OnEnrollmentFinished(bool success) {
188 scoped_ptr<base::DictionaryValue> enrollment_state =
189 GetEnrollmentStateDictionary();
190 PA_LOG(INFO) << "Enrollment attempt completed with success=" << success
191 << ":\n" << *enrollment_state;
192 web_ui()->CallJavascriptFunction(
193 "LocalStateInterface.onEnrollmentStateChanged", *enrollment_state);
196 void ProximityAuthWebUIHandler::OnSyncStarted() {
197 web_ui()->CallJavascriptFunction(
198 "LocalStateInterface.onDeviceSyncStateChanged",
199 *GetDeviceSyncStateDictionary());
202 void ProximityAuthWebUIHandler::OnSyncFinished(
203 CryptAuthDeviceManager::SyncResult sync_result,
204 CryptAuthDeviceManager::DeviceChangeResult device_change_result) {
205 scoped_ptr<base::DictionaryValue> device_sync_state =
206 GetDeviceSyncStateDictionary();
207 PA_LOG(INFO) << "Device sync completed with result="
208 << static_cast<int>(sync_result) << ":\n" << *device_sync_state;
209 web_ui()->CallJavascriptFunction(
210 "LocalStateInterface.onDeviceSyncStateChanged", *device_sync_state);
212 if (device_change_result ==
213 CryptAuthDeviceManager::DeviceChangeResult::CHANGED) {
214 scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList();
215 PA_LOG(INFO) << "New unlock keys obtained after device sync:\n"
216 << *unlock_keys;
217 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
218 *unlock_keys);
222 void ProximityAuthWebUIHandler::OnWebContentsInitialized(
223 const base::ListValue* args) {
224 if (!web_contents_initialized_) {
225 CryptAuthEnrollmentManager* enrollment_manager =
226 proximity_auth_client_->GetCryptAuthEnrollmentManager();
227 if (enrollment_manager)
228 enrollment_manager->AddObserver(this);
230 CryptAuthDeviceManager* device_manager =
231 proximity_auth_client_->GetCryptAuthDeviceManager();
232 if (device_manager)
233 device_manager->AddObserver(this);
235 LogBuffer::GetInstance()->AddObserver(this);
237 web_contents_initialized_ = true;
241 void ProximityAuthWebUIHandler::GetLogMessages(const base::ListValue* args) {
242 base::ListValue json_logs;
243 for (const auto& log : *LogBuffer::GetInstance()->logs()) {
244 json_logs.Append(LogMessageToDictionary(log).release());
246 web_ui()->CallJavascriptFunction("LogBufferInterface.onGotLogMessages",
247 json_logs);
250 void ProximityAuthWebUIHandler::ClearLogBuffer(const base::ListValue* args) {
251 // The OnLogBufferCleared() observer function will be called after the buffer
252 // is cleared.
253 LogBuffer::GetInstance()->Clear();
256 void ProximityAuthWebUIHandler::ToggleUnlockKey(const base::ListValue* args) {
257 std::string public_key_b64, public_key;
258 bool make_unlock_key;
259 if (args->GetSize() != 2 || !args->GetString(0, &public_key_b64) ||
260 !args->GetBoolean(1, &make_unlock_key) ||
261 !Base64UrlDecode(public_key_b64, &public_key)) {
262 PA_LOG(ERROR) << "Invalid arguments to toggleUnlockKey";
263 return;
266 cryptauth::ToggleEasyUnlockRequest request;
267 request.set_enable(make_unlock_key);
268 request.set_public_key(public_key);
269 *(request.mutable_device_classifier()) =
270 proximity_auth_client_->GetDeviceClassifier();
272 PA_LOG(INFO) << "Toggling unlock key:\n"
273 << " public_key: " << public_key_b64 << "\n"
274 << " make_unlock_key: " << make_unlock_key;
275 cryptauth_client_ = cryptauth_client_factory_->CreateInstance();
276 cryptauth_client_->ToggleEasyUnlock(
277 request, base::Bind(&ProximityAuthWebUIHandler::OnEasyUnlockToggled,
278 weak_ptr_factory_.GetWeakPtr()),
279 base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError,
280 weak_ptr_factory_.GetWeakPtr()));
283 void ProximityAuthWebUIHandler::FindEligibleUnlockDevices(
284 const base::ListValue* args) {
285 cryptauth_client_ = cryptauth_client_factory_->CreateInstance();
287 cryptauth::FindEligibleUnlockDevicesRequest request;
288 *(request.mutable_device_classifier()) =
289 proximity_auth_client_->GetDeviceClassifier();
290 cryptauth_client_->FindEligibleUnlockDevices(
291 request,
292 base::Bind(&ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices,
293 weak_ptr_factory_.GetWeakPtr()),
294 base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError,
295 weak_ptr_factory_.GetWeakPtr()));
298 void ProximityAuthWebUIHandler::FindReachableDevices(
299 const base::ListValue* args) {
300 if (reachable_phone_flow_) {
301 PA_LOG(INFO) << "Waiting for existing ReachablePhoneFlow to finish.";
302 return;
305 reachable_phone_flow_.reset(
306 new ReachablePhoneFlow(cryptauth_client_factory_.get()));
307 reachable_phone_flow_->Run(
308 base::Bind(&ProximityAuthWebUIHandler::OnReachablePhonesFound,
309 weak_ptr_factory_.GetWeakPtr()));
312 void ProximityAuthWebUIHandler::ForceEnrollment(const base::ListValue* args) {
313 CryptAuthEnrollmentManager* enrollment_manager =
314 proximity_auth_client_->GetCryptAuthEnrollmentManager();
315 if (enrollment_manager) {
316 enrollment_manager->ForceEnrollmentNow(cryptauth::INVOCATION_REASON_MANUAL);
320 void ProximityAuthWebUIHandler::ForceDeviceSync(const base::ListValue* args) {
321 CryptAuthDeviceManager* device_manager =
322 proximity_auth_client_->GetCryptAuthDeviceManager();
323 if (device_manager)
324 device_manager->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL);
327 void ProximityAuthWebUIHandler::ToggleConnection(const base::ListValue* args) {
328 CryptAuthEnrollmentManager* enrollment_manager =
329 proximity_auth_client_->GetCryptAuthEnrollmentManager();
330 CryptAuthDeviceManager* device_manager =
331 proximity_auth_client_->GetCryptAuthDeviceManager();
332 if (!enrollment_manager || !device_manager)
333 return;
335 std::string b64_public_key;
336 std::string public_key;
337 if (!enrollment_manager || !device_manager || !args->GetSize() ||
338 !args->GetString(0, &b64_public_key) ||
339 !Base64UrlDecode(b64_public_key, &public_key)) {
340 return;
343 Connection* connection = GetConnection();
344 for (const auto& unlock_key : device_manager->unlock_keys()) {
345 if (unlock_key.public_key() == public_key) {
346 // Check if there is an existing connection to disconnect from first.
347 if (connection && connection->IsConnected() &&
348 selected_remote_device_.public_key == public_key) {
349 PA_LOG(INFO) << "Disconnecting from "
350 << unlock_key.friendly_device_name() << "["
351 << unlock_key.bluetooth_address() << "]";
352 connection->Disconnect();
353 return;
356 // Derive the PSK before connecting to the device.
357 PA_LOG(INFO) << "Deriving PSK before connecting to "
358 << unlock_key.friendly_device_name();
359 secure_message_delegate_ =
360 proximity_auth_client_->CreateSecureMessageDelegate();
361 secure_message_delegate_->DeriveKey(
362 enrollment_manager->GetUserPrivateKey(), unlock_key.public_key(),
363 base::Bind(&ProximityAuthWebUIHandler::OnPSKDerived,
364 weak_ptr_factory_.GetWeakPtr(), unlock_key));
366 return;
370 PA_LOG(ERROR) << "Unlock key (" << b64_public_key << ") not found";
373 void ProximityAuthWebUIHandler::OnCryptAuthClientError(
374 const std::string& error_message) {
375 PA_LOG(WARNING) << "CryptAuth request failed: " << error_message;
376 base::StringValue error_string(error_message);
377 web_ui()->CallJavascriptFunction("CryptAuthInterface.onError", error_string);
380 void ProximityAuthWebUIHandler::OnEasyUnlockToggled(
381 const cryptauth::ToggleEasyUnlockResponse& response) {
382 web_ui()->CallJavascriptFunction("CryptAuthInterface.onUnlockKeyToggled");
383 // TODO(tengs): Update the local state to reflect the toggle.
386 void ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices(
387 const cryptauth::FindEligibleUnlockDevicesResponse& response) {
388 base::ListValue eligible_devices;
389 for (const auto& external_device : response.eligible_devices()) {
390 eligible_devices.Append(ExternalDeviceInfoToDictionary(external_device));
393 base::ListValue ineligible_devices;
394 for (const auto& ineligible_device : response.ineligible_devices()) {
395 ineligible_devices.Append(IneligibleDeviceToDictionary(ineligible_device));
398 PA_LOG(INFO) << "Found " << eligible_devices.GetSize()
399 << " eligible devices and " << ineligible_devices.GetSize()
400 << " ineligible devices.";
401 web_ui()->CallJavascriptFunction("CryptAuthInterface.onGotEligibleDevices",
402 eligible_devices, ineligible_devices);
405 void ProximityAuthWebUIHandler::OnReachablePhonesFound(
406 const std::vector<cryptauth::ExternalDeviceInfo>& reachable_phones) {
407 reachable_phone_flow_.reset();
408 base::ListValue device_list;
409 for (const auto& external_device : reachable_phones) {
410 device_list.Append(ExternalDeviceInfoToDictionary(external_device));
412 web_ui()->CallJavascriptFunction("CryptAuthInterface.onGotReachableDevices",
413 device_list);
416 void ProximityAuthWebUIHandler::GetLocalState(const base::ListValue* args) {
417 scoped_ptr<base::DictionaryValue> enrollment_state =
418 GetEnrollmentStateDictionary();
419 scoped_ptr<base::DictionaryValue> device_sync_state =
420 GetDeviceSyncStateDictionary();
421 scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList();
423 PA_LOG(INFO) << "==== Got Local State ====\n"
424 << "Enrollment State: \n" << *enrollment_state
425 << "Device Sync State: \n" << *device_sync_state
426 << "Unlock Keys: \n" << *unlock_keys;
427 web_ui()->CallJavascriptFunction("LocalStateInterface.onGotLocalState",
428 *enrollment_state, *device_sync_state,
429 *unlock_keys);
432 scoped_ptr<base::DictionaryValue>
433 ProximityAuthWebUIHandler::GetEnrollmentStateDictionary() {
434 CryptAuthEnrollmentManager* enrollment_manager =
435 proximity_auth_client_->GetCryptAuthEnrollmentManager();
436 if (!enrollment_manager)
437 return make_scoped_ptr(new base::DictionaryValue());
439 return CreateSyncStateDictionary(
440 enrollment_manager->GetLastEnrollmentTime().ToJsTime(),
441 enrollment_manager->GetTimeToNextAttempt().InMillisecondsF(),
442 enrollment_manager->IsRecoveringFromFailure(),
443 enrollment_manager->IsEnrollmentInProgress());
446 scoped_ptr<base::DictionaryValue>
447 ProximityAuthWebUIHandler::GetDeviceSyncStateDictionary() {
448 CryptAuthDeviceManager* device_manager =
449 proximity_auth_client_->GetCryptAuthDeviceManager();
450 if (!device_manager)
451 return make_scoped_ptr(new base::DictionaryValue());
453 return CreateSyncStateDictionary(
454 device_manager->GetLastSyncTime().ToJsTime(),
455 device_manager->GetTimeToNextAttempt().InMillisecondsF(),
456 device_manager->IsRecoveringFromFailure(),
457 device_manager->IsSyncInProgress());
460 scoped_ptr<base::ListValue> ProximityAuthWebUIHandler::GetUnlockKeysList() {
461 scoped_ptr<base::ListValue> unlock_keys(new base::ListValue());
462 CryptAuthDeviceManager* device_manager =
463 proximity_auth_client_->GetCryptAuthDeviceManager();
464 if (!device_manager)
465 return unlock_keys;
467 for (const auto& unlock_key : device_manager->unlock_keys()) {
468 unlock_keys->Append(ExternalDeviceInfoToDictionary(unlock_key));
471 return unlock_keys;
474 Connection* ProximityAuthWebUIHandler::GetConnection() {
475 Connection* connection = connection_.get();
476 if (messenger_) {
477 DCHECK(!connection);
478 connection = messenger_->connection();
480 return connection;
483 void ProximityAuthWebUIHandler::OnPSKDerived(
484 const cryptauth::ExternalDeviceInfo& unlock_key,
485 const std::string& persistent_symmetric_key) {
486 if (persistent_symmetric_key.empty()) {
487 PA_LOG(ERROR) << "Failed to derive PSK.";
488 return;
491 selected_remote_device_ =
492 RemoteDevice(unlock_key.friendly_device_name(), unlock_key.public_key(),
493 unlock_key.bluetooth_address(), persistent_symmetric_key);
495 // TODO(tengs): We distinguish whether the unlock key uses classic Bluetooth
496 // or BLE based on the presence of the |bluetooth_address| field. However, we
497 // should ideally have a separate field specifying the protocol.
498 if (selected_remote_device_.bluetooth_address.empty())
499 FindBluetoothLowEnergyConnection(selected_remote_device_);
500 else
501 FindBluetoothClassicConnection(selected_remote_device_);
504 void ProximityAuthWebUIHandler::FindBluetoothClassicConnection(
505 const RemoteDevice& remote_device) {
506 PA_LOG(INFO) << "Finding classic Bluetooth device " << remote_device.name
507 << " [" << remote_device.bluetooth_address << "].";
509 // TODO(tengs): Set a timeout to stop the connection finder eventually.
510 connection_finder_.reset(new BluetoothConnectionFinder(
511 remote_device, device::BluetoothUUID(kClassicBluetoothServiceUUID),
512 base::TimeDelta::FromSeconds(3)));
513 connection_finder_->Find(
514 base::Bind(&ProximityAuthWebUIHandler::OnConnectionFound,
515 weak_ptr_factory_.GetWeakPtr()));
517 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
518 *GetUnlockKeysList());
521 void ProximityAuthWebUIHandler::FindBluetoothLowEnergyConnection(
522 const RemoteDevice& remote_device) {
523 PrefService* pref_service = proximity_auth_client_->GetPrefService();
524 if (!pref_service->FindPreference(
525 prefs::kBluetoothLowEnergyDeviceWhitelist)) {
526 PA_LOG(ERROR) << "Please enable the BLE experiment in chrome://flags.";
527 return;
530 PA_LOG(INFO) << "Finding Bluetooth Low Energy device " << remote_device.name;
531 if (!bluetooth_throttler_) {
532 bluetooth_throttler_.reset(new BluetoothThrottlerImpl(
533 make_scoped_ptr(new base::DefaultTickClock())));
536 ble_device_whitelist_.reset(new BluetoothLowEnergyDeviceWhitelist(
537 proximity_auth_client_->GetPrefService()));
539 // TODO(tengs): Set a timeout to stop the connection finder eventually.
540 connection_finder_.reset(new BluetoothLowEnergyConnectionFinder(
541 remote_device, kBLESmartLockServiceUUID,
542 BluetoothLowEnergyConnectionFinder::FIND_ANY_DEVICE,
543 ble_device_whitelist_.get(), bluetooth_throttler_.get(), 3));
544 connection_finder_->Find(
545 base::Bind(&ProximityAuthWebUIHandler::OnConnectionFound,
546 weak_ptr_factory_.GetWeakPtr()));
548 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
549 *GetUnlockKeysList());
552 void ProximityAuthWebUIHandler::OnAuthenticationResult(
553 Authenticator::Result result,
554 scoped_ptr<SecureContext> secure_context) {
555 secure_context_ = secure_context.Pass();
557 // Create the MessengerImpl asynchronously. |messenger_| registers itself as
558 // an observer of |connection_|, so creating it synchronously would trigger
559 // |OnSendComplete()| as an observer call for |messenger_|.
560 base::ThreadTaskRunnerHandle::Get()->PostTask(
561 FROM_HERE,
562 base::Bind(&ProximityAuthWebUIHandler::CreateStatusUpdateMessenger,
563 weak_ptr_factory_.GetWeakPtr()));
566 void ProximityAuthWebUIHandler::OnConnectionFound(
567 scoped_ptr<Connection> connection) {
568 DCHECK(connection->IsConnected());
569 connection_ = connection.Pass();
570 connection_->AddObserver(this);
572 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
573 *GetUnlockKeysList());
575 // TODO(tengs): Create an authenticator for BLE connections.
576 if (selected_remote_device_.bluetooth_address.empty())
577 return;
579 authenticator_.reset(new DeviceToDeviceAuthenticator(
580 connection_.get(), proximity_auth_client_->GetAccountId(),
581 proximity_auth_client_->CreateSecureMessageDelegate()));
582 authenticator_->Authenticate(
583 base::Bind(&ProximityAuthWebUIHandler::OnAuthenticationResult,
584 weak_ptr_factory_.GetWeakPtr()));
587 void ProximityAuthWebUIHandler::CreateStatusUpdateMessenger() {
588 messenger_.reset(
589 new MessengerImpl(connection_.Pass(), secure_context_.Pass()));
590 messenger_->AddObserver(this);
593 scoped_ptr<base::DictionaryValue>
594 ProximityAuthWebUIHandler::ExternalDeviceInfoToDictionary(
595 const cryptauth::ExternalDeviceInfo& device_info) {
596 std::string base64_public_key;
597 Base64UrlEncode(device_info.public_key(), &base64_public_key);
599 // Set the fields in the ExternalDeviceInfo proto.
600 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue());
601 dictionary->SetString(kExternalDevicePublicKey, base64_public_key);
602 dictionary->SetString(kExternalDeviceFriendlyName,
603 device_info.friendly_device_name());
604 dictionary->SetString(kExternalDeviceBluetoothAddress,
605 device_info.bluetooth_address());
606 dictionary->SetBoolean(kExternalDeviceUnlockKey, device_info.unlock_key());
607 dictionary->SetString(kExternalDeviceConnectionStatus,
608 kExternalDeviceDisconnected);
610 CryptAuthDeviceManager* device_manager =
611 proximity_auth_client_->GetCryptAuthDeviceManager();
612 if (!device_manager)
613 return dictionary;
615 // If |device_info| is a known unlock key, then combine the proto data with
616 // the corresponding local device data (e.g. connection status and remote
617 // status updates).
618 std::string public_key = device_info.public_key();
619 auto iterator = std::find_if(
620 device_manager->unlock_keys().begin(),
621 device_manager->unlock_keys().end(),
622 [&public_key](const cryptauth::ExternalDeviceInfo& unlock_key) {
623 return unlock_key.public_key() == public_key;
626 if (iterator == device_manager->unlock_keys().end() ||
627 selected_remote_device_.public_key != device_info.public_key())
628 return dictionary;
630 // Fill in the current Bluetooth connection status.
631 std::string connection_status = kExternalDeviceDisconnected;
632 Connection* connection = GetConnection();
633 if (connection && connection->IsConnected()) {
634 connection_status = kExternalDeviceConnected;
635 } else if (connection_finder_) {
636 connection_status = kExternalDeviceConnecting;
638 dictionary->SetString(kExternalDeviceConnectionStatus, connection_status);
640 // Fill the remote status dictionary.
641 if (last_remote_status_update_) {
642 scoped_ptr<base::DictionaryValue> status_dictionary(
643 new base::DictionaryValue());
644 status_dictionary->SetInteger("userPresent",
645 last_remote_status_update_->user_presence);
646 status_dictionary->SetInteger(
647 "secureScreenLock",
648 last_remote_status_update_->secure_screen_lock_state);
649 status_dictionary->SetInteger(
650 "trustAgent", last_remote_status_update_->trust_agent_state);
651 dictionary->Set(kExternalDeviceRemoteState, status_dictionary.Pass());
654 return dictionary;
657 scoped_ptr<base::DictionaryValue>
658 ProximityAuthWebUIHandler::IneligibleDeviceToDictionary(
659 const cryptauth::IneligibleDevice& ineligible_device) {
660 scoped_ptr<base::ListValue> ineligibility_reasons(new base::ListValue());
661 for (const std::string& reason : ineligible_device.reasons()) {
662 ineligibility_reasons->AppendString(reason);
665 scoped_ptr<base::DictionaryValue> device_dictionary =
666 ExternalDeviceInfoToDictionary(ineligible_device.device());
667 device_dictionary->Set(kIneligibleDeviceReasons,
668 ineligibility_reasons.Pass());
669 return device_dictionary;
672 void ProximityAuthWebUIHandler::OnConnectionStatusChanged(
673 Connection* connection,
674 Connection::Status old_status,
675 Connection::Status new_status) {
676 PA_LOG(INFO) << "Connection status changed from " << old_status << " to "
677 << new_status;
679 if (new_status == Connection::DISCONNECTED) {
680 last_remote_status_update_.reset();
681 selected_remote_device_ = RemoteDevice();
682 connection_finder_.reset();
685 scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList();
686 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
687 *unlock_keys);
690 void ProximityAuthWebUIHandler::OnMessageReceived(const Connection& connection,
691 const WireMessage& message) {
692 std::string address = connection.remote_device().bluetooth_address;
693 PA_LOG(INFO) << "Message received from " << address;
696 void ProximityAuthWebUIHandler::OnRemoteStatusUpdate(
697 const RemoteStatusUpdate& status_update) {
698 PA_LOG(INFO) << "Remote status update:"
699 << "\n user_presence: "
700 << static_cast<int>(status_update.user_presence)
701 << "\n secure_screen_lock_state: "
702 << static_cast<int>(status_update.secure_screen_lock_state)
703 << "\n trust_agent_state: "
704 << static_cast<int>(status_update.trust_agent_state);
706 last_remote_status_update_.reset(new RemoteStatusUpdate(status_update));
707 scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList();
708 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
709 *unlock_keys);
712 } // namespace proximity_auth