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"
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
{
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
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(
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
);
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(
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",
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"
217 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
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();
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",
250 void ProximityAuthWebUIHandler::ClearLogBuffer(const base::ListValue
* args
) {
251 // The OnLogBufferCleared() observer function will be called after the buffer
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";
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(
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.";
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();
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
)
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
)) {
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();
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
));
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",
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
,
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();
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();
467 for (const auto& unlock_key
: device_manager
->unlock_keys()) {
468 unlock_keys
->Append(ExternalDeviceInfoToDictionary(unlock_key
));
474 Connection
* ProximityAuthWebUIHandler::GetConnection() {
475 Connection
* connection
= connection_
.get();
478 connection
= messenger_
->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.";
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_
);
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.";
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(
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())
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() {
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();
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
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())
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(
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());
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 "
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",
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",
712 } // namespace proximity_auth