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/bluetooth_util.h"
22 #include "components/proximity_auth/client_impl.h"
23 #include "components/proximity_auth/cryptauth/base64url.h"
24 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h"
25 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
26 #include "components/proximity_auth/cryptauth/secure_message_delegate.h"
27 #include "components/proximity_auth/device_to_device_authenticator.h"
28 #include "components/proximity_auth/logging/logging.h"
29 #include "components/proximity_auth/remote_status_update.h"
30 #include "components/proximity_auth/secure_context.h"
31 #include "components/proximity_auth/webui/reachable_phone_flow.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/web_ui.h"
34 #include "device/bluetooth/bluetooth_uuid.h"
36 namespace proximity_auth
{
40 // The UUID of the Smart Lock classic Bluetooth service.
41 const char kClassicBluetoothServiceUUID
[] =
42 "704EE561-3782-405A-A14B-2D47A2DDCDDF";
44 // The UUID of the Bluetooth Low Energy service.
45 const char kBLESmartLockServiceUUID
[] = "b3b7e28e-a000-3e17-bd86-6e97b9e28c11";
47 // The UUID of the characteristic used to send data to the peripheral.
48 const char kBLEToPeripheralCharUUID
[] = "977c6674-1239-4e72-993b-502369b8bb5a";
50 // The UUID of the characteristic used to receive data from the peripheral.
51 const char kBLEFromPeripheralCharUUID
[] =
52 "f4b904a2-a030-43b3-98a8-221c536c03cb";
54 // Keys in the JSON representation of a log message.
55 const char kLogMessageTextKey
[] = "text";
56 const char kLogMessageTimeKey
[] = "time";
57 const char kLogMessageFileKey
[] = "file";
58 const char kLogMessageLineKey
[] = "line";
59 const char kLogMessageSeverityKey
[] = "severity";
61 // Keys in the JSON representation of a SyncState object for enrollment or
63 const char kSyncStateLastSuccessTime
[] = "lastSuccessTime";
64 const char kSyncStateNextRefreshTime
[] = "nextRefreshTime";
65 const char kSyncStateRecoveringFromFailure
[] = "recoveringFromFailure";
66 const char kSyncStateOperationInProgress
[] = "operationInProgress";
68 // Converts |log_message| to a raw dictionary value used as a JSON argument to
69 // JavaScript functions.
70 scoped_ptr
<base::DictionaryValue
> LogMessageToDictionary(
71 const LogBuffer::LogMessage
& log_message
) {
72 scoped_ptr
<base::DictionaryValue
> dictionary(new base::DictionaryValue());
73 dictionary
->SetString(kLogMessageTextKey
, log_message
.text
);
74 dictionary
->SetString(
76 base::TimeFormatTimeOfDayWithMilliseconds(log_message
.time
));
77 dictionary
->SetString(kLogMessageFileKey
, log_message
.file
);
78 dictionary
->SetInteger(kLogMessageLineKey
, log_message
.line
);
79 dictionary
->SetInteger(kLogMessageSeverityKey
,
80 static_cast<int>(log_message
.severity
));
81 return dictionary
.Pass();
84 // Keys in the JSON representation of an ExternalDeviceInfo proto.
85 const char kExternalDevicePublicKey
[] = "publicKey";
86 const char kExternalDeviceFriendlyName
[] = "friendlyDeviceName";
87 const char kExternalDeviceBluetoothAddress
[] = "bluetoothAddress";
88 const char kExternalDeviceUnlockKey
[] = "unlockKey";
89 const char kExternalDeviceConnectionStatus
[] = "connectionStatus";
90 const char kExternalDeviceRemoteState
[] = "remoteState";
92 // The possible values of the |kExternalDeviceConnectionStatus| field.
93 const char kExternalDeviceConnected
[] = "connected";
94 const char kExternalDeviceDisconnected
[] = "disconnected";
95 const char kExternalDeviceConnecting
[] = "connecting";
97 // Keys in the JSON representation of an IneligibleDevice proto.
98 const char kIneligibleDeviceReasons
[] = "ineligibilityReasons";
100 // Creates a SyncState JSON object that can be passed to the WebUI.
101 scoped_ptr
<base::DictionaryValue
> CreateSyncStateDictionary(
102 double last_success_time
,
103 double next_refresh_time
,
104 bool is_recovering_from_failure
,
105 bool is_enrollment_in_progress
) {
106 scoped_ptr
<base::DictionaryValue
> sync_state(new base::DictionaryValue());
107 sync_state
->SetDouble(kSyncStateLastSuccessTime
, last_success_time
);
108 sync_state
->SetDouble(kSyncStateNextRefreshTime
, next_refresh_time
);
109 sync_state
->SetBoolean(kSyncStateRecoveringFromFailure
,
110 is_recovering_from_failure
);
111 sync_state
->SetBoolean(kSyncStateOperationInProgress
,
112 is_enrollment_in_progress
);
118 ProximityAuthWebUIHandler::ProximityAuthWebUIHandler(
119 ProximityAuthClient
* proximity_auth_client
)
120 : proximity_auth_client_(proximity_auth_client
),
121 web_contents_initialized_(false),
122 weak_ptr_factory_(this) {
123 cryptauth_client_factory_
=
124 proximity_auth_client_
->CreateCryptAuthClientFactory();
127 ProximityAuthWebUIHandler::~ProximityAuthWebUIHandler() {
128 LogBuffer::GetInstance()->RemoveObserver(this);
131 void ProximityAuthWebUIHandler::RegisterMessages() {
132 web_ui()->RegisterMessageCallback(
133 "onWebContentsInitialized",
134 base::Bind(&ProximityAuthWebUIHandler::OnWebContentsInitialized
,
135 base::Unretained(this)));
137 web_ui()->RegisterMessageCallback(
138 "clearLogBuffer", base::Bind(&ProximityAuthWebUIHandler::ClearLogBuffer
,
139 base::Unretained(this)));
141 web_ui()->RegisterMessageCallback(
142 "getLogMessages", base::Bind(&ProximityAuthWebUIHandler::GetLogMessages
,
143 base::Unretained(this)));
145 web_ui()->RegisterMessageCallback(
146 "toggleUnlockKey", base::Bind(&ProximityAuthWebUIHandler::ToggleUnlockKey
,
147 base::Unretained(this)));
149 web_ui()->RegisterMessageCallback(
150 "findEligibleUnlockDevices",
151 base::Bind(&ProximityAuthWebUIHandler::FindEligibleUnlockDevices
,
152 base::Unretained(this)));
154 web_ui()->RegisterMessageCallback(
155 "findReachableDevices",
156 base::Bind(&ProximityAuthWebUIHandler::FindReachableDevices
,
157 base::Unretained(this)));
159 web_ui()->RegisterMessageCallback(
160 "getLocalState", base::Bind(&ProximityAuthWebUIHandler::GetLocalState
,
161 base::Unretained(this)));
163 web_ui()->RegisterMessageCallback(
164 "forceEnrollment", base::Bind(&ProximityAuthWebUIHandler::ForceEnrollment
,
165 base::Unretained(this)));
167 web_ui()->RegisterMessageCallback(
168 "forceDeviceSync", base::Bind(&ProximityAuthWebUIHandler::ForceDeviceSync
,
169 base::Unretained(this)));
171 web_ui()->RegisterMessageCallback(
173 base::Bind(&ProximityAuthWebUIHandler::ToggleConnection
,
174 base::Unretained(this)));
177 void ProximityAuthWebUIHandler::OnLogMessageAdded(
178 const LogBuffer::LogMessage
& log_message
) {
179 scoped_ptr
<base::DictionaryValue
> dictionary
=
180 LogMessageToDictionary(log_message
);
181 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogMessageAdded",
185 void ProximityAuthWebUIHandler::OnLogBufferCleared() {
186 web_ui()->CallJavascriptFunction("LogBufferInterface.onLogBufferCleared");
189 void ProximityAuthWebUIHandler::OnEnrollmentStarted() {
190 web_ui()->CallJavascriptFunction(
191 "LocalStateInterface.onEnrollmentStateChanged",
192 *GetEnrollmentStateDictionary());
195 void ProximityAuthWebUIHandler::OnEnrollmentFinished(bool success
) {
196 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
197 GetEnrollmentStateDictionary();
198 PA_LOG(INFO
) << "Enrollment attempt completed with success=" << success
199 << ":\n" << *enrollment_state
;
200 web_ui()->CallJavascriptFunction(
201 "LocalStateInterface.onEnrollmentStateChanged", *enrollment_state
);
204 void ProximityAuthWebUIHandler::OnSyncStarted() {
205 web_ui()->CallJavascriptFunction(
206 "LocalStateInterface.onDeviceSyncStateChanged",
207 *GetDeviceSyncStateDictionary());
210 void ProximityAuthWebUIHandler::OnSyncFinished(
211 CryptAuthDeviceManager::SyncResult sync_result
,
212 CryptAuthDeviceManager::DeviceChangeResult device_change_result
) {
213 scoped_ptr
<base::DictionaryValue
> device_sync_state
=
214 GetDeviceSyncStateDictionary();
215 PA_LOG(INFO
) << "Device sync completed with result="
216 << static_cast<int>(sync_result
) << ":\n" << *device_sync_state
;
217 web_ui()->CallJavascriptFunction(
218 "LocalStateInterface.onDeviceSyncStateChanged", *device_sync_state
);
220 if (device_change_result
==
221 CryptAuthDeviceManager::DeviceChangeResult::CHANGED
) {
222 scoped_ptr
<base::ListValue
> unlock_keys
= GetUnlockKeysList();
223 PA_LOG(INFO
) << "New unlock keys obtained after device sync:\n"
225 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
230 void ProximityAuthWebUIHandler::OnWebContentsInitialized(
231 const base::ListValue
* args
) {
232 if (!web_contents_initialized_
) {
233 CryptAuthEnrollmentManager
* enrollment_manager
=
234 proximity_auth_client_
->GetCryptAuthEnrollmentManager();
235 if (enrollment_manager
)
236 enrollment_manager
->AddObserver(this);
238 CryptAuthDeviceManager
* device_manager
=
239 proximity_auth_client_
->GetCryptAuthDeviceManager();
241 device_manager
->AddObserver(this);
243 LogBuffer::GetInstance()->AddObserver(this);
245 web_contents_initialized_
= true;
249 void ProximityAuthWebUIHandler::GetLogMessages(const base::ListValue
* args
) {
250 base::ListValue json_logs
;
251 for (const auto& log
: *LogBuffer::GetInstance()->logs()) {
252 json_logs
.Append(LogMessageToDictionary(log
).release());
254 web_ui()->CallJavascriptFunction("LogBufferInterface.onGotLogMessages",
258 void ProximityAuthWebUIHandler::ClearLogBuffer(const base::ListValue
* args
) {
259 // The OnLogBufferCleared() observer function will be called after the buffer
261 LogBuffer::GetInstance()->Clear();
264 void ProximityAuthWebUIHandler::ToggleUnlockKey(const base::ListValue
* args
) {
265 std::string public_key_b64
, public_key
;
266 bool make_unlock_key
;
267 if (args
->GetSize() != 2 || !args
->GetString(0, &public_key_b64
) ||
268 !args
->GetBoolean(1, &make_unlock_key
) ||
269 !Base64UrlDecode(public_key_b64
, &public_key
)) {
270 PA_LOG(ERROR
) << "Invalid arguments to toggleUnlockKey";
274 cryptauth::ToggleEasyUnlockRequest request
;
275 request
.set_enable(make_unlock_key
);
276 request
.set_public_key(public_key
);
277 *(request
.mutable_device_classifier()) =
278 proximity_auth_client_
->GetDeviceClassifier();
280 PA_LOG(INFO
) << "Toggling unlock key:\n"
281 << " public_key: " << public_key_b64
<< "\n"
282 << " make_unlock_key: " << make_unlock_key
;
283 cryptauth_client_
= cryptauth_client_factory_
->CreateInstance();
284 cryptauth_client_
->ToggleEasyUnlock(
285 request
, base::Bind(&ProximityAuthWebUIHandler::OnEasyUnlockToggled
,
286 weak_ptr_factory_
.GetWeakPtr()),
287 base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError
,
288 weak_ptr_factory_
.GetWeakPtr()));
291 void ProximityAuthWebUIHandler::FindEligibleUnlockDevices(
292 const base::ListValue
* args
) {
293 cryptauth_client_
= cryptauth_client_factory_
->CreateInstance();
295 cryptauth::FindEligibleUnlockDevicesRequest request
;
296 *(request
.mutable_device_classifier()) =
297 proximity_auth_client_
->GetDeviceClassifier();
298 cryptauth_client_
->FindEligibleUnlockDevices(
300 base::Bind(&ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices
,
301 weak_ptr_factory_
.GetWeakPtr()),
302 base::Bind(&ProximityAuthWebUIHandler::OnCryptAuthClientError
,
303 weak_ptr_factory_
.GetWeakPtr()));
306 void ProximityAuthWebUIHandler::FindReachableDevices(
307 const base::ListValue
* args
) {
308 if (reachable_phone_flow_
) {
309 PA_LOG(INFO
) << "Waiting for existing ReachablePhoneFlow to finish.";
313 reachable_phone_flow_
.reset(
314 new ReachablePhoneFlow(cryptauth_client_factory_
.get()));
315 reachable_phone_flow_
->Run(
316 base::Bind(&ProximityAuthWebUIHandler::OnReachablePhonesFound
,
317 weak_ptr_factory_
.GetWeakPtr()));
320 void ProximityAuthWebUIHandler::ForceEnrollment(const base::ListValue
* args
) {
321 CryptAuthEnrollmentManager
* enrollment_manager
=
322 proximity_auth_client_
->GetCryptAuthEnrollmentManager();
323 if (enrollment_manager
) {
324 enrollment_manager
->ForceEnrollmentNow(cryptauth::INVOCATION_REASON_MANUAL
);
328 void ProximityAuthWebUIHandler::ForceDeviceSync(const base::ListValue
* args
) {
329 CryptAuthDeviceManager
* device_manager
=
330 proximity_auth_client_
->GetCryptAuthDeviceManager();
332 device_manager
->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL
);
335 void ProximityAuthWebUIHandler::ToggleConnection(const base::ListValue
* args
) {
336 CryptAuthEnrollmentManager
* enrollment_manager
=
337 proximity_auth_client_
->GetCryptAuthEnrollmentManager();
338 CryptAuthDeviceManager
* device_manager
=
339 proximity_auth_client_
->GetCryptAuthDeviceManager();
340 if (!enrollment_manager
|| !device_manager
)
343 std::string b64_public_key
;
344 std::string public_key
;
345 if (!enrollment_manager
|| !device_manager
|| !args
->GetSize() ||
346 !args
->GetString(0, &b64_public_key
) ||
347 !Base64UrlDecode(b64_public_key
, &public_key
)) {
351 Connection
* connection
= GetConnection();
352 for (const auto& unlock_key
: device_manager
->unlock_keys()) {
353 if (unlock_key
.public_key() == public_key
) {
354 // Check if there is an existing connection to disconnect from first.
355 if (connection
&& connection
->IsConnected() &&
356 selected_remote_device_
.public_key
== public_key
) {
357 PA_LOG(INFO
) << "Disconnecting from "
358 << unlock_key
.friendly_device_name() << "["
359 << unlock_key
.bluetooth_address() << "]";
360 connection
->Disconnect();
364 // Derive the PSK before connecting to the device.
365 PA_LOG(INFO
) << "Deriving PSK before connecting to "
366 << unlock_key
.friendly_device_name();
367 secure_message_delegate_
=
368 proximity_auth_client_
->CreateSecureMessageDelegate();
369 secure_message_delegate_
->DeriveKey(
370 enrollment_manager
->GetUserPrivateKey(), unlock_key
.public_key(),
371 base::Bind(&ProximityAuthWebUIHandler::OnPSKDerived
,
372 weak_ptr_factory_
.GetWeakPtr(), unlock_key
));
378 PA_LOG(ERROR
) << "Unlock key (" << b64_public_key
<< ") not found";
381 void ProximityAuthWebUIHandler::OnCryptAuthClientError(
382 const std::string
& error_message
) {
383 PA_LOG(WARNING
) << "CryptAuth request failed: " << error_message
;
384 base::StringValue
error_string(error_message
);
385 web_ui()->CallJavascriptFunction("CryptAuthInterface.onError", error_string
);
388 void ProximityAuthWebUIHandler::OnEasyUnlockToggled(
389 const cryptauth::ToggleEasyUnlockResponse
& response
) {
390 web_ui()->CallJavascriptFunction("CryptAuthInterface.onUnlockKeyToggled");
391 // TODO(tengs): Update the local state to reflect the toggle.
394 void ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices(
395 const cryptauth::FindEligibleUnlockDevicesResponse
& response
) {
396 base::ListValue eligible_devices
;
397 for (const auto& external_device
: response
.eligible_devices()) {
398 eligible_devices
.Append(ExternalDeviceInfoToDictionary(external_device
));
401 base::ListValue ineligible_devices
;
402 for (const auto& ineligible_device
: response
.ineligible_devices()) {
403 ineligible_devices
.Append(IneligibleDeviceToDictionary(ineligible_device
));
406 PA_LOG(INFO
) << "Found " << eligible_devices
.GetSize()
407 << " eligible devices and " << ineligible_devices
.GetSize()
408 << " ineligible devices.";
409 web_ui()->CallJavascriptFunction("CryptAuthInterface.onGotEligibleDevices",
410 eligible_devices
, ineligible_devices
);
413 void ProximityAuthWebUIHandler::OnReachablePhonesFound(
414 const std::vector
<cryptauth::ExternalDeviceInfo
>& reachable_phones
) {
415 reachable_phone_flow_
.reset();
416 base::ListValue device_list
;
417 for (const auto& external_device
: reachable_phones
) {
418 device_list
.Append(ExternalDeviceInfoToDictionary(external_device
));
420 web_ui()->CallJavascriptFunction("CryptAuthInterface.onGotReachableDevices",
424 void ProximityAuthWebUIHandler::GetLocalState(const base::ListValue
* args
) {
425 scoped_ptr
<base::DictionaryValue
> enrollment_state
=
426 GetEnrollmentStateDictionary();
427 scoped_ptr
<base::DictionaryValue
> device_sync_state
=
428 GetDeviceSyncStateDictionary();
429 scoped_ptr
<base::ListValue
> unlock_keys
= GetUnlockKeysList();
431 PA_LOG(INFO
) << "==== Got Local State ====\n"
432 << "Enrollment State: \n" << *enrollment_state
433 << "Device Sync State: \n" << *device_sync_state
434 << "Unlock Keys: \n" << *unlock_keys
;
435 web_ui()->CallJavascriptFunction("LocalStateInterface.onGotLocalState",
436 *enrollment_state
, *device_sync_state
,
440 scoped_ptr
<base::DictionaryValue
>
441 ProximityAuthWebUIHandler::GetEnrollmentStateDictionary() {
442 CryptAuthEnrollmentManager
* enrollment_manager
=
443 proximity_auth_client_
->GetCryptAuthEnrollmentManager();
444 if (!enrollment_manager
)
445 return make_scoped_ptr(new base::DictionaryValue());
447 return CreateSyncStateDictionary(
448 enrollment_manager
->GetLastEnrollmentTime().ToJsTime(),
449 enrollment_manager
->GetTimeToNextAttempt().InMillisecondsF(),
450 enrollment_manager
->IsRecoveringFromFailure(),
451 enrollment_manager
->IsEnrollmentInProgress());
454 scoped_ptr
<base::DictionaryValue
>
455 ProximityAuthWebUIHandler::GetDeviceSyncStateDictionary() {
456 CryptAuthDeviceManager
* device_manager
=
457 proximity_auth_client_
->GetCryptAuthDeviceManager();
459 return make_scoped_ptr(new base::DictionaryValue());
461 return CreateSyncStateDictionary(
462 device_manager
->GetLastSyncTime().ToJsTime(),
463 device_manager
->GetTimeToNextAttempt().InMillisecondsF(),
464 device_manager
->IsRecoveringFromFailure(),
465 device_manager
->IsSyncInProgress());
468 scoped_ptr
<base::ListValue
> ProximityAuthWebUIHandler::GetUnlockKeysList() {
469 scoped_ptr
<base::ListValue
> unlock_keys(new base::ListValue());
470 CryptAuthDeviceManager
* device_manager
=
471 proximity_auth_client_
->GetCryptAuthDeviceManager();
475 for (const auto& unlock_key
: device_manager
->unlock_keys()) {
476 unlock_keys
->Append(ExternalDeviceInfoToDictionary(unlock_key
));
482 Connection
* ProximityAuthWebUIHandler::GetConnection() {
483 Connection
* connection
= connection_
.get();
486 connection
= client_
->connection();
491 void ProximityAuthWebUIHandler::OnPSKDerived(
492 const cryptauth::ExternalDeviceInfo
& unlock_key
,
493 const std::string
& persistent_symmetric_key
) {
494 if (persistent_symmetric_key
.empty()) {
495 PA_LOG(ERROR
) << "Failed to derive PSK.";
499 selected_remote_device_
=
500 RemoteDevice(unlock_key
.friendly_device_name(), unlock_key
.public_key(),
501 unlock_key
.bluetooth_address(), persistent_symmetric_key
);
503 // TODO(tengs): We distinguish whether the unlock key uses classic Bluetooth
504 // or BLE based on the presence of the |bluetooth_address| field. However, we
505 // should ideally have a separate field specifying the protocol.
506 if (selected_remote_device_
.bluetooth_address
.empty())
507 FindBluetoothLowEnergyConnection(selected_remote_device_
);
509 FindBluetoothClassicConnection(selected_remote_device_
);
512 void ProximityAuthWebUIHandler::FindBluetoothClassicConnection(
513 const RemoteDevice
& remote_device
) {
514 PA_LOG(INFO
) << "Finding classic Bluetooth device " << remote_device
.name
515 << " [" << remote_device
.bluetooth_address
<< "].";
517 // TODO(tengs): Set a timeout to stop the connection finder eventually.
518 connection_finder_
.reset(new BluetoothConnectionFinder(
519 remote_device
, device::BluetoothUUID(kClassicBluetoothServiceUUID
),
520 base::TimeDelta::FromSeconds(3)));
521 connection_finder_
->Find(
522 base::Bind(&ProximityAuthWebUIHandler::OnConnectionFound
,
523 weak_ptr_factory_
.GetWeakPtr()));
525 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
526 *GetUnlockKeysList());
529 void ProximityAuthWebUIHandler::FindBluetoothLowEnergyConnection(
530 const RemoteDevice
& remote_device
) {
531 PrefService
* pref_service
= proximity_auth_client_
->GetPrefService();
532 if (!pref_service
->FindPreference(
533 prefs::kBluetoothLowEnergyDeviceWhitelist
)) {
534 PA_LOG(ERROR
) << "Please enable the BLE experiment in chrome://flags.";
538 PA_LOG(INFO
) << "Finding Bluetooth Low Energy device " << remote_device
.name
;
539 if (!bluetooth_throttler_
) {
540 bluetooth_throttler_
.reset(new BluetoothThrottlerImpl(
541 make_scoped_ptr(new base::DefaultTickClock())));
544 ble_device_whitelist_
.reset(new BluetoothLowEnergyDeviceWhitelist(
545 proximity_auth_client_
->GetPrefService()));
547 // TODO(tengs): Set a timeout to stop the connection finder eventually.
548 connection_finder_
.reset(new BluetoothLowEnergyConnectionFinder(
549 kBLESmartLockServiceUUID
, kBLEToPeripheralCharUUID
,
550 kBLEFromPeripheralCharUUID
, ble_device_whitelist_
.get(),
551 bluetooth_throttler_
.get(), 3));
552 connection_finder_
->Find(
553 base::Bind(&ProximityAuthWebUIHandler::OnConnectionFound
,
554 weak_ptr_factory_
.GetWeakPtr()));
556 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
557 *GetUnlockKeysList());
560 void ProximityAuthWebUIHandler::OnAuthenticationResult(
561 Authenticator::Result result
,
562 scoped_ptr
<SecureContext
> secure_context
) {
563 secure_context_
= secure_context
.Pass();
565 // Create the ClientImpl asynchronously. |client_| registers itself as an
566 // observer of |connection_|, so creating it synchronously would
567 // trigger |OnSendComplete()| as an observer call for |client_|.
568 base::ThreadTaskRunnerHandle::Get()->PostTask(
570 base::Bind(&ProximityAuthWebUIHandler::CreateStatusUpdateClient
,
571 weak_ptr_factory_
.GetWeakPtr()));
574 void ProximityAuthWebUIHandler::OnConnectionFound(
575 scoped_ptr
<Connection
> connection
) {
576 DCHECK(connection
->IsConnected());
577 connection_
= connection
.Pass();
578 connection_
->AddObserver(this);
580 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
581 *GetUnlockKeysList());
583 // TODO(tengs): Create an authenticator for BLE connections.
584 if (selected_remote_device_
.bluetooth_address
.empty())
587 authenticator_
.reset(new DeviceToDeviceAuthenticator(
588 connection_
.get(), proximity_auth_client_
->GetAccountId(),
589 proximity_auth_client_
->CreateSecureMessageDelegate()));
590 authenticator_
->Authenticate(
591 base::Bind(&ProximityAuthWebUIHandler::OnAuthenticationResult
,
592 weak_ptr_factory_
.GetWeakPtr()));
595 void ProximityAuthWebUIHandler::CreateStatusUpdateClient() {
596 client_
.reset(new ClientImpl(connection_
.Pass(), secure_context_
.Pass()));
597 client_
->AddObserver(this);
600 scoped_ptr
<base::DictionaryValue
>
601 ProximityAuthWebUIHandler::ExternalDeviceInfoToDictionary(
602 const cryptauth::ExternalDeviceInfo
& device_info
) {
603 std::string base64_public_key
;
604 Base64UrlEncode(device_info
.public_key(), &base64_public_key
);
606 // Set the fields in the ExternalDeviceInfo proto.
607 scoped_ptr
<base::DictionaryValue
> dictionary(new base::DictionaryValue());
608 dictionary
->SetString(kExternalDevicePublicKey
, base64_public_key
);
609 dictionary
->SetString(kExternalDeviceFriendlyName
,
610 device_info
.friendly_device_name());
611 dictionary
->SetString(kExternalDeviceBluetoothAddress
,
612 device_info
.bluetooth_address());
613 dictionary
->SetBoolean(kExternalDeviceUnlockKey
, device_info
.unlock_key());
614 dictionary
->SetString(kExternalDeviceConnectionStatus
,
615 kExternalDeviceDisconnected
);
617 CryptAuthDeviceManager
* device_manager
=
618 proximity_auth_client_
->GetCryptAuthDeviceManager();
622 // If |device_info| is a known unlock key, then combine the proto data with
623 // the corresponding local device data (e.g. connection status and remote
625 std::string public_key
= device_info
.public_key();
626 auto iterator
= std::find_if(
627 device_manager
->unlock_keys().begin(),
628 device_manager
->unlock_keys().end(),
629 [&public_key
](const cryptauth::ExternalDeviceInfo
& unlock_key
) {
630 return unlock_key
.public_key() == public_key
;
633 if (iterator
== device_manager
->unlock_keys().end() ||
634 selected_remote_device_
.public_key
!= device_info
.public_key())
637 // Fill in the current Bluetooth connection status.
638 std::string connection_status
= kExternalDeviceDisconnected
;
639 Connection
* connection
= GetConnection();
640 if (connection
&& connection
->IsConnected()) {
641 connection_status
= kExternalDeviceConnected
;
642 } else if (connection_finder_
) {
643 connection_status
= kExternalDeviceConnecting
;
645 dictionary
->SetString(kExternalDeviceConnectionStatus
, connection_status
);
647 // Fill the remote status dictionary.
648 if (last_remote_status_update_
) {
649 scoped_ptr
<base::DictionaryValue
> status_dictionary(
650 new base::DictionaryValue());
651 status_dictionary
->SetInteger("userPresent",
652 last_remote_status_update_
->user_presence
);
653 status_dictionary
->SetInteger(
655 last_remote_status_update_
->secure_screen_lock_state
);
656 status_dictionary
->SetInteger(
657 "trustAgent", last_remote_status_update_
->trust_agent_state
);
658 dictionary
->Set(kExternalDeviceRemoteState
, status_dictionary
.Pass());
664 scoped_ptr
<base::DictionaryValue
>
665 ProximityAuthWebUIHandler::IneligibleDeviceToDictionary(
666 const cryptauth::IneligibleDevice
& ineligible_device
) {
667 scoped_ptr
<base::ListValue
> ineligibility_reasons(new base::ListValue());
668 for (const std::string
& reason
: ineligible_device
.reasons()) {
669 ineligibility_reasons
->AppendString(reason
);
672 scoped_ptr
<base::DictionaryValue
> device_dictionary
=
673 ExternalDeviceInfoToDictionary(ineligible_device
.device());
674 device_dictionary
->Set(kIneligibleDeviceReasons
,
675 ineligibility_reasons
.Pass());
676 return device_dictionary
;
679 void ProximityAuthWebUIHandler::OnConnectionStatusChanged(
680 Connection
* connection
,
681 Connection::Status old_status
,
682 Connection::Status new_status
) {
683 PA_LOG(INFO
) << "Connection status changed from " << old_status
<< " to "
686 if (new_status
== Connection::DISCONNECTED
) {
687 last_remote_status_update_
.reset();
688 selected_remote_device_
= RemoteDevice();
689 connection_finder_
.reset();
692 scoped_ptr
<base::ListValue
> unlock_keys
= GetUnlockKeysList();
693 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
697 void ProximityAuthWebUIHandler::OnMessageReceived(const Connection
& connection
,
698 const WireMessage
& message
) {
699 std::string address
= connection
.remote_device().bluetooth_address
;
700 PA_LOG(INFO
) << "Message received from " << address
;
703 void ProximityAuthWebUIHandler::OnRemoteStatusUpdate(
704 const RemoteStatusUpdate
& status_update
) {
705 PA_LOG(INFO
) << "Remote status update:"
706 << "\n user_presence: "
707 << static_cast<int>(status_update
.user_presence
)
708 << "\n secure_screen_lock_state: "
709 << static_cast<int>(status_update
.secure_screen_lock_state
)
710 << "\n trust_agent_state: "
711 << static_cast<int>(status_update
.trust_agent_state
);
713 last_remote_status_update_
.reset(new RemoteStatusUpdate(status_update
));
714 scoped_ptr
<base::ListValue
> unlock_keys
= GetUnlockKeysList();
715 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged",
719 } // namespace proximity_auth