OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/proximity_auth/webui/proximity_auth_webui_handler.h" | 5 #include "components/proximity_auth/webui/proximity_auth_webui_handler.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/i18n/time_formatting.h" | 10 #include "base/i18n/time_formatting.h" |
11 #include "base/prefs/pref_service.h" | 11 #include "base/prefs/pref_service.h" |
12 #include "base/thread_task_runner_handle.h" | 12 #include "base/thread_task_runner_handle.h" |
13 #include "base/time/default_clock.h" | 13 #include "base/time/default_clock.h" |
14 #include "base/time/default_tick_clock.h" | 14 #include "base/time/default_tick_clock.h" |
15 #include "base/values.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" | 16 #include "components/proximity_auth/ble/pref_names.h" |
19 #include "components/proximity_auth/bluetooth_connection_finder.h" | 17 #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" | 18 #include "components/proximity_auth/cryptauth/base64url.h" |
22 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h" | 19 #include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h" |
23 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h" | 20 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h" |
24 #include "components/proximity_auth/cryptauth/secure_message_delegate.h" | 21 #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" | 22 #include "components/proximity_auth/logging/logging.h" |
27 #include "components/proximity_auth/messenger_impl.h" | 23 #include "components/proximity_auth/messenger.h" |
| 24 #include "components/proximity_auth/remote_device_life_cycle_impl.h" |
28 #include "components/proximity_auth/remote_status_update.h" | 25 #include "components/proximity_auth/remote_status_update.h" |
29 #include "components/proximity_auth/secure_context.h" | 26 #include "components/proximity_auth/secure_context.h" |
30 #include "components/proximity_auth/webui/reachable_phone_flow.h" | 27 #include "components/proximity_auth/webui/reachable_phone_flow.h" |
31 #include "content/public/browser/browser_thread.h" | 28 #include "content/public/browser/browser_thread.h" |
32 #include "content/public/browser/web_ui.h" | 29 #include "content/public/browser/web_ui.h" |
33 #include "device/bluetooth/bluetooth_uuid.h" | 30 #include "device/bluetooth/bluetooth_uuid.h" |
34 | 31 |
35 namespace proximity_auth { | 32 namespace proximity_auth { |
36 | 33 |
37 namespace { | 34 namespace { |
38 | 35 |
39 // The UUID of the Smart Lock classic Bluetooth service. | |
40 const char kClassicBluetoothServiceUUID[] = | |
41 "704EE561-3782-405A-A14B-2D47A2DDCDDF"; | |
42 | |
43 // The UUID of the Bluetooth Low Energy service. | |
44 const char kBLESmartLockServiceUUID[] = "b3b7e28e-a000-3e17-bd86-6e97b9e28c11"; | |
45 | |
46 // Keys in the JSON representation of a log message. | 36 // Keys in the JSON representation of a log message. |
47 const char kLogMessageTextKey[] = "text"; | 37 const char kLogMessageTextKey[] = "text"; |
48 const char kLogMessageTimeKey[] = "time"; | 38 const char kLogMessageTimeKey[] = "time"; |
49 const char kLogMessageFileKey[] = "file"; | 39 const char kLogMessageFileKey[] = "file"; |
50 const char kLogMessageLineKey[] = "line"; | 40 const char kLogMessageLineKey[] = "line"; |
51 const char kLogMessageSeverityKey[] = "severity"; | 41 const char kLogMessageSeverityKey[] = "severity"; |
52 | 42 |
53 // Keys in the JSON representation of a SyncState object for enrollment or | 43 // Keys in the JSON representation of a SyncState object for enrollment or |
54 // device sync. | 44 // device sync. |
55 const char kSyncStateLastSuccessTime[] = "lastSuccessTime"; | 45 const char kSyncStateLastSuccessTime[] = "lastSuccessTime"; |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 return; | 323 return; |
334 | 324 |
335 std::string b64_public_key; | 325 std::string b64_public_key; |
336 std::string public_key; | 326 std::string public_key; |
337 if (!enrollment_manager || !device_manager || !args->GetSize() || | 327 if (!enrollment_manager || !device_manager || !args->GetSize() || |
338 !args->GetString(0, &b64_public_key) || | 328 !args->GetString(0, &b64_public_key) || |
339 !Base64UrlDecode(b64_public_key, &public_key)) { | 329 !Base64UrlDecode(b64_public_key, &public_key)) { |
340 return; | 330 return; |
341 } | 331 } |
342 | 332 |
343 Connection* connection = GetConnection(); | |
344 for (const auto& unlock_key : device_manager->unlock_keys()) { | 333 for (const auto& unlock_key : device_manager->unlock_keys()) { |
345 if (unlock_key.public_key() == public_key) { | 334 if (unlock_key.public_key() == public_key) { |
346 // Check if there is an existing connection to disconnect from first. | 335 if (life_cycle_ && selected_remote_device_.public_key == public_key) { |
347 if (connection && connection->IsConnected() && | 336 CleanUpRemoteDeviceLifeCycle(); |
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; | 337 return; |
354 } | 338 } |
355 | 339 |
356 // Derive the PSK before connecting to the device. | 340 // Derive the PSK before connecting to the device. |
357 PA_LOG(INFO) << "Deriving PSK before connecting to " | 341 PA_LOG(INFO) << "Deriving PSK before connecting to " |
358 << unlock_key.friendly_device_name(); | 342 << unlock_key.friendly_device_name(); |
359 secure_message_delegate_ = | 343 secure_message_delegate_ = |
360 proximity_auth_client_->CreateSecureMessageDelegate(); | 344 proximity_auth_client_->CreateSecureMessageDelegate(); |
361 secure_message_delegate_->DeriveKey( | 345 secure_message_delegate_->DeriveKey( |
362 enrollment_manager->GetUserPrivateKey(), unlock_key.public_key(), | 346 enrollment_manager->GetUserPrivateKey(), unlock_key.public_key(), |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 if (!device_manager) | 448 if (!device_manager) |
465 return unlock_keys; | 449 return unlock_keys; |
466 | 450 |
467 for (const auto& unlock_key : device_manager->unlock_keys()) { | 451 for (const auto& unlock_key : device_manager->unlock_keys()) { |
468 unlock_keys->Append(ExternalDeviceInfoToDictionary(unlock_key)); | 452 unlock_keys->Append(ExternalDeviceInfoToDictionary(unlock_key)); |
469 } | 453 } |
470 | 454 |
471 return unlock_keys; | 455 return unlock_keys; |
472 } | 456 } |
473 | 457 |
474 Connection* ProximityAuthWebUIHandler::GetConnection() { | |
475 Connection* connection = connection_.get(); | |
476 if (messenger_) { | |
477 DCHECK(!connection); | |
478 connection = messenger_->connection(); | |
479 } | |
480 return connection; | |
481 } | |
482 | |
483 void ProximityAuthWebUIHandler::OnPSKDerived( | 458 void ProximityAuthWebUIHandler::OnPSKDerived( |
484 const cryptauth::ExternalDeviceInfo& unlock_key, | 459 const cryptauth::ExternalDeviceInfo& unlock_key, |
485 const std::string& persistent_symmetric_key) { | 460 const std::string& persistent_symmetric_key) { |
486 if (persistent_symmetric_key.empty()) { | 461 if (persistent_symmetric_key.empty()) { |
487 PA_LOG(ERROR) << "Failed to derive PSK."; | 462 PA_LOG(ERROR) << "Failed to derive PSK."; |
488 return; | 463 return; |
489 } | 464 } |
490 | 465 |
491 selected_remote_device_ = | 466 selected_remote_device_ = |
492 RemoteDevice(unlock_key.friendly_device_name(), unlock_key.public_key(), | 467 RemoteDevice(unlock_key.friendly_device_name(), unlock_key.public_key(), |
493 unlock_key.bluetooth_address(), persistent_symmetric_key); | 468 unlock_key.bluetooth_address(), persistent_symmetric_key); |
494 | 469 |
495 // TODO(tengs): We distinguish whether the unlock key uses classic Bluetooth | 470 life_cycle_.reset(new RemoteDeviceLifeCycleImpl(selected_remote_device_, |
496 // or BLE based on the presence of the |bluetooth_address| field. However, we | 471 proximity_auth_client_)); |
497 // should ideally have a separate field specifying the protocol. | 472 life_cycle_->AddObserver(this); |
498 if (selected_remote_device_.bluetooth_address.empty()) | 473 life_cycle_->Start(); |
499 FindBluetoothLowEnergyConnection(selected_remote_device_); | |
500 else | |
501 FindBluetoothClassicConnection(selected_remote_device_); | |
502 } | |
503 | |
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 << "]."; | |
508 | |
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())); | |
516 | |
517 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", | |
518 *GetUnlockKeysList()); | |
519 } | |
520 | |
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; | |
528 } | |
529 | |
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()))); | |
534 } | |
535 | |
536 ble_device_whitelist_.reset(new BluetoothLowEnergyDeviceWhitelist( | |
537 proximity_auth_client_->GetPrefService())); | |
538 | |
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())); | |
547 | |
548 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", | |
549 *GetUnlockKeysList()); | |
550 } | |
551 | |
552 void ProximityAuthWebUIHandler::OnAuthenticationResult( | |
553 Authenticator::Result result, | |
554 scoped_ptr<SecureContext> secure_context) { | |
555 secure_context_ = secure_context.Pass(); | |
556 | |
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())); | |
564 } | |
565 | |
566 void ProximityAuthWebUIHandler::OnConnectionFound( | |
567 scoped_ptr<Connection> connection) { | |
568 DCHECK(connection->IsConnected()); | |
569 connection_ = connection.Pass(); | |
570 connection_->AddObserver(this); | |
571 | |
572 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", | |
573 *GetUnlockKeysList()); | |
574 | |
575 // TODO(tengs): Create an authenticator for BLE connections. | |
576 if (selected_remote_device_.bluetooth_address.empty()) | |
577 return; | |
578 | |
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())); | |
585 } | |
586 | |
587 void ProximityAuthWebUIHandler::CreateStatusUpdateMessenger() { | |
588 messenger_.reset( | |
589 new MessengerImpl(connection_.Pass(), secure_context_.Pass())); | |
590 messenger_->AddObserver(this); | |
591 } | 474 } |
592 | 475 |
593 scoped_ptr<base::DictionaryValue> | 476 scoped_ptr<base::DictionaryValue> |
594 ProximityAuthWebUIHandler::ExternalDeviceInfoToDictionary( | 477 ProximityAuthWebUIHandler::ExternalDeviceInfoToDictionary( |
595 const cryptauth::ExternalDeviceInfo& device_info) { | 478 const cryptauth::ExternalDeviceInfo& device_info) { |
596 std::string base64_public_key; | 479 std::string base64_public_key; |
597 Base64UrlEncode(device_info.public_key(), &base64_public_key); | 480 Base64UrlEncode(device_info.public_key(), &base64_public_key); |
598 | 481 |
599 // Set the fields in the ExternalDeviceInfo proto. | 482 // Set the fields in the ExternalDeviceInfo proto. |
600 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue()); | 483 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue()); |
(...skipping 21 matching lines...) Expand all Loading... |
622 [&public_key](const cryptauth::ExternalDeviceInfo& unlock_key) { | 505 [&public_key](const cryptauth::ExternalDeviceInfo& unlock_key) { |
623 return unlock_key.public_key() == public_key; | 506 return unlock_key.public_key() == public_key; |
624 }); | 507 }); |
625 | 508 |
626 if (iterator == device_manager->unlock_keys().end() || | 509 if (iterator == device_manager->unlock_keys().end() || |
627 selected_remote_device_.public_key != device_info.public_key()) | 510 selected_remote_device_.public_key != device_info.public_key()) |
628 return dictionary; | 511 return dictionary; |
629 | 512 |
630 // Fill in the current Bluetooth connection status. | 513 // Fill in the current Bluetooth connection status. |
631 std::string connection_status = kExternalDeviceDisconnected; | 514 std::string connection_status = kExternalDeviceDisconnected; |
632 Connection* connection = GetConnection(); | 515 if (life_cycle_ && |
633 if (connection && connection->IsConnected()) { | 516 life_cycle_->GetState() == |
| 517 RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) { |
634 connection_status = kExternalDeviceConnected; | 518 connection_status = kExternalDeviceConnected; |
635 } else if (connection_finder_) { | 519 } else if (life_cycle_) { |
636 connection_status = kExternalDeviceConnecting; | 520 connection_status = kExternalDeviceConnecting; |
637 } | 521 } |
638 dictionary->SetString(kExternalDeviceConnectionStatus, connection_status); | 522 dictionary->SetString(kExternalDeviceConnectionStatus, connection_status); |
639 | 523 |
640 // Fill the remote status dictionary. | 524 // Fill the remote status dictionary. |
641 if (last_remote_status_update_) { | 525 if (last_remote_status_update_) { |
642 scoped_ptr<base::DictionaryValue> status_dictionary( | 526 scoped_ptr<base::DictionaryValue> status_dictionary( |
643 new base::DictionaryValue()); | 527 new base::DictionaryValue()); |
644 status_dictionary->SetInteger("userPresent", | 528 status_dictionary->SetInteger("userPresent", |
645 last_remote_status_update_->user_presence); | 529 last_remote_status_update_->user_presence); |
(...skipping 16 matching lines...) Expand all Loading... |
662 ineligibility_reasons->AppendString(reason); | 546 ineligibility_reasons->AppendString(reason); |
663 } | 547 } |
664 | 548 |
665 scoped_ptr<base::DictionaryValue> device_dictionary = | 549 scoped_ptr<base::DictionaryValue> device_dictionary = |
666 ExternalDeviceInfoToDictionary(ineligible_device.device()); | 550 ExternalDeviceInfoToDictionary(ineligible_device.device()); |
667 device_dictionary->Set(kIneligibleDeviceReasons, | 551 device_dictionary->Set(kIneligibleDeviceReasons, |
668 ineligibility_reasons.Pass()); | 552 ineligibility_reasons.Pass()); |
669 return device_dictionary; | 553 return device_dictionary; |
670 } | 554 } |
671 | 555 |
672 void ProximityAuthWebUIHandler::OnConnectionStatusChanged( | 556 void ProximityAuthWebUIHandler::CleanUpRemoteDeviceLifeCycle() { |
673 Connection* connection, | 557 PA_LOG(INFO) << "Cleaning up connection to " << selected_remote_device_.name |
674 Connection::Status old_status, | 558 << " [" << selected_remote_device_.bluetooth_address << "]"; |
675 Connection::Status new_status) { | 559 life_cycle_.reset(); |
676 PA_LOG(INFO) << "Connection status changed from " << old_status << " to " | 560 selected_remote_device_ = RemoteDevice(); |
677 << new_status; | 561 last_remote_status_update_.reset(); |
| 562 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", |
| 563 *GetUnlockKeysList()); |
| 564 } |
678 | 565 |
679 if (new_status == Connection::DISCONNECTED) { | 566 void ProximityAuthWebUIHandler::OnLifeCycleStateChanged( |
680 last_remote_status_update_.reset(); | 567 RemoteDeviceLifeCycle::State old_state, |
681 selected_remote_device_ = RemoteDevice(); | 568 RemoteDeviceLifeCycle::State new_state) { |
682 connection_finder_.reset(); | 569 // Do not re-attempt to find a connection after the first one fails--just |
| 570 // abort. |
| 571 if ((old_state != RemoteDeviceLifeCycle::State::STOPPED && |
| 572 new_state == RemoteDeviceLifeCycle::State::FINDING_CONNECTION) || |
| 573 new_state == RemoteDeviceLifeCycle::State::AUTHENTICATION_FAILED) { |
| 574 // Clean up the life cycle asynchronously, because we are currently in the |
| 575 // call stack of |life_cycle_|. |
| 576 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 577 FROM_HERE, |
| 578 base::Bind(&ProximityAuthWebUIHandler::CleanUpRemoteDeviceLifeCycle, |
| 579 weak_ptr_factory_.GetWeakPtr())); |
| 580 } else if (new_state == |
| 581 RemoteDeviceLifeCycle::State::SECURE_CHANNEL_ESTABLISHED) { |
| 582 life_cycle_->GetMessenger()->AddObserver(this); |
683 } | 583 } |
684 | 584 |
685 scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList(); | |
686 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", | 585 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", |
687 *unlock_keys); | 586 *GetUnlockKeysList()); |
688 } | |
689 | |
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; | |
694 } | 587 } |
695 | 588 |
696 void ProximityAuthWebUIHandler::OnRemoteStatusUpdate( | 589 void ProximityAuthWebUIHandler::OnRemoteStatusUpdate( |
697 const RemoteStatusUpdate& status_update) { | 590 const RemoteStatusUpdate& status_update) { |
698 PA_LOG(INFO) << "Remote status update:" | 591 PA_LOG(INFO) << "Remote status update:" |
699 << "\n user_presence: " | 592 << "\n user_presence: " |
700 << static_cast<int>(status_update.user_presence) | 593 << static_cast<int>(status_update.user_presence) |
701 << "\n secure_screen_lock_state: " | 594 << "\n secure_screen_lock_state: " |
702 << static_cast<int>(status_update.secure_screen_lock_state) | 595 << static_cast<int>(status_update.secure_screen_lock_state) |
703 << "\n trust_agent_state: " | 596 << "\n trust_agent_state: " |
704 << static_cast<int>(status_update.trust_agent_state); | 597 << static_cast<int>(status_update.trust_agent_state); |
705 | 598 |
706 last_remote_status_update_.reset(new RemoteStatusUpdate(status_update)); | 599 last_remote_status_update_.reset(new RemoteStatusUpdate(status_update)); |
707 scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList(); | 600 scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList(); |
708 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", | 601 web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", |
709 *unlock_keys); | 602 *unlock_keys); |
710 } | 603 } |
711 | 604 |
712 } // namespace proximity_auth | 605 } // namespace proximity_auth |
OLD | NEW |