diff options
author | tengs <tengs@chromium.org> | 2015-08-03 15:49:08 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-03 22:49:36 +0000 |
commit | da506c83c44c94f391a282e94fa4ef417861611d (patch) | |
tree | bd38e1bf990f8f9095d2b1b0bf44317cf0a31c8e /components/proximity_auth | |
parent | eb2801e9cd3e6406282bc1b3f825f0957e76bd87 (diff) | |
download | chromium_src-da506c83c44c94f391a282e94fa4ef417861611d.zip chromium_src-da506c83c44c94f391a282e94fa4ef417861611d.tar.gz chromium_src-da506c83c44c94f391a282e94fa4ef417861611d.tar.bz2 |
Implement debugging local unlock keys in chrome://proximity-auth.
This CL also updates some older log messages to use the new PA_LOG macro, and removes a metadata check that is not being set on the GMS Core side.
BUG=409158
TEST=manual
Review URL: https://codereview.chromium.org/1264703003
Cr-Commit-Position: refs/heads/master@{#341634}
Diffstat (limited to 'components/proximity_auth')
16 files changed, 510 insertions, 107 deletions
diff --git a/components/proximity_auth/bluetooth_connection.cc b/components/proximity_auth/bluetooth_connection.cc index f038564..f06d52f 100644 --- a/components/proximity_auth/bluetooth_connection.cc +++ b/components/proximity_auth/bluetooth_connection.cc @@ -9,6 +9,7 @@ #include "base/numerics/safe_conversions.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" +#include "components/proximity_auth/logging/logging.h" #include "components/proximity_auth/remote_device.h" #include "components/proximity_auth/wire_message.h" #include "device/bluetooth/bluetooth_adapter_factory.h" @@ -26,19 +27,20 @@ BluetoothConnection::BluetoothConnection(const RemoteDevice& remote_device, } BluetoothConnection::~BluetoothConnection() { - Disconnect(); + if (status() != DISCONNECTED) + Disconnect(); } void BluetoothConnection::Connect() { if (status() != DISCONNECTED) { - VLOG(1) - << "[BC] Ignoring attempt to connect a non-disconnected connection."; + PA_LOG(WARNING) + << "Ignoring attempt to connect a non-disconnected connection."; return; } if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) { - VLOG(1) - << "[BC] Connection failed: Bluetooth is unsupported on this platform."; + PA_LOG(WARNING) + << "Connection failed: Bluetooth is unsupported on this platform."; return; } @@ -50,8 +52,8 @@ void BluetoothConnection::Connect() { void BluetoothConnection::Disconnect() { if (status() == DISCONNECTED) { - VLOG(1) - << "[BC] Ignoring attempt to disconnect a non-connected connection."; + PA_LOG(WARNING) + << "Ignoring attempt to disconnect a non-connected connection."; return; } @@ -93,7 +95,7 @@ void BluetoothConnection::DeviceRemoved(device::BluetoothAdapter* adapter, return; DCHECK_NE(status(), DISCONNECTED); - VLOG(1) << "[BC] Device disconnected..."; + PA_LOG(INFO) << "Device disconnected..."; Disconnect(); } @@ -109,8 +111,8 @@ void BluetoothConnection::OnAdapterInitialized( const std::string address = remote_device().bluetooth_address; device::BluetoothDevice* bluetooth_device = adapter->GetDevice(address); if (!bluetooth_device) { - VLOG(1) << "[BC] Device with address " << address - << " is not known to the system Bluetooth daemon."; + PA_LOG(WARNING) << "Device with address " << address + << " is not known to the system Bluetooth daemon."; // TOOD(isherman): Optimistically attempt to seek the device and connect // anyway, as was previously implemented in BluetoothConnectionFinder. Disconnect(); @@ -133,31 +135,31 @@ void BluetoothConnection::OnConnected( // This case is reachable if the client of |this| connection called // |Disconnect()| while the backing Bluetooth connection was pending. DCHECK_EQ(status(), DISCONNECTED); - VLOG(1) << "[BC] Ignoring successful backend Bluetooth connection to an " - << "already disconnected logical connection."; + PA_LOG(WARNING) << "Ignoring successful backend Bluetooth connection to an " + << "already disconnected logical connection."; return; } - VLOG(1) << "[BC] Connection established with " - << remote_device().bluetooth_address; + PA_LOG(INFO) << "Connection established with " + << remote_device().bluetooth_address; socket_ = socket; SetStatus(CONNECTED); StartReceive(); } void BluetoothConnection::OnConnectionError(const std::string& error_message) { - VLOG(1) << "[BC] Connection failed: " << error_message; + PA_LOG(WARNING) << "Connection failed: " << error_message; Disconnect(); } void BluetoothConnection::OnSend(int bytes_sent) { - VLOG(1) << "[BC] Successfully sent " << bytes_sent << " bytes."; + PA_LOG(INFO) << "Successfully sent " << bytes_sent << " bytes."; OnDidSendMessage(*pending_message_, true); pending_message_.reset(); } void BluetoothConnection::OnSendError(const std::string& error_message) { - VLOG(1) << "[BC] Error when sending bytes: " << error_message; + PA_LOG(WARNING) << "Error when sending bytes: " << error_message; OnDidSendMessage(*pending_message_, false); pending_message_.reset(); @@ -166,7 +168,7 @@ void BluetoothConnection::OnSendError(const std::string& error_message) { void BluetoothConnection::OnReceive(int bytes_received, scoped_refptr<net::IOBuffer> buffer) { - VLOG(1) << "[BC] Received " << bytes_received << " bytes."; + PA_LOG(INFO) << "Received " << bytes_received << " bytes."; OnBytesReceived(std::string(buffer->data(), bytes_received)); // Post a task to delay the read until the socket is available, as @@ -179,7 +181,7 @@ void BluetoothConnection::OnReceive(int bytes_received, void BluetoothConnection::OnReceiveError( device::BluetoothSocket::ErrorReason error_reason, const std::string& error_message) { - VLOG(1) << "[BC] Error receiving bytes: " << error_message; + PA_LOG(WARNING) << "Error receiving bytes: " << error_message; // Post a task to delay the read until the socket is available, as // calling StartReceive at this point would error with ERR_IO_PENDING. diff --git a/components/proximity_auth/bluetooth_connection.h b/components/proximity_auth/bluetooth_connection.h index affa5b2..8b39116 100644 --- a/components/proximity_auth/bluetooth_connection.h +++ b/components/proximity_auth/bluetooth_connection.h @@ -35,10 +35,12 @@ class BluetoothConnection : public Connection, const device::BluetoothUUID& uuid); ~BluetoothConnection() override; - protected: // Connection: void Connect() override; void Disconnect() override; + + protected: + // Connection: void SendMessageImpl(scoped_ptr<WireMessage> message) override; // BluetoothAdapter::Observer: diff --git a/components/proximity_auth/bluetooth_connection_finder.cc b/components/proximity_auth/bluetooth_connection_finder.cc index 3ab43a4..7596a11 100644 --- a/components/proximity_auth/bluetooth_connection_finder.cc +++ b/components/proximity_auth/bluetooth_connection_finder.cc @@ -10,6 +10,7 @@ #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "components/proximity_auth/bluetooth_connection.h" +#include "components/proximity_auth/logging/logging.h" #include "device/bluetooth/bluetooth_adapter_factory.h" using device::BluetoothAdapter; @@ -34,12 +35,12 @@ BluetoothConnectionFinder::~BluetoothConnectionFinder() { void BluetoothConnectionFinder::Find( const ConnectionCallback& connection_callback) { if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) { - VLOG(1) << "[BCF] Bluetooth is unsupported on this platform. Aborting."; + PA_LOG(WARNING) << "Bluetooth is unsupported on this platform. Aborting."; return; } DCHECK(start_time_.is_null()); - VLOG(1) << "[BCF] Finding Bluetooth connection..."; + PA_LOG(WARNING) << "Finding Bluetooth connection..."; start_time_ = base::TimeTicks::Now(); connection_callback_ = connection_callback; @@ -56,8 +57,8 @@ scoped_ptr<Connection> BluetoothConnectionFinder::CreateConnection() { bool BluetoothConnectionFinder::IsReadyToPoll() { bool is_adapter_available = adapter_.get() && adapter_->IsPresent() && adapter_->IsPowered(); - VLOG(1) << "[BCF] Readiness: adapter=" - << (is_adapter_available ? "available" : "unavailable"); + PA_LOG(INFO) << "Readiness: adapter=" + << (is_adapter_available ? "available" : "unavailable"); return is_adapter_available; } @@ -76,7 +77,7 @@ void BluetoothConnectionFinder::PollIfReady() { if (connection_) return; - VLOG(1) << "[BCF] Polling for connection..."; + PA_LOG(INFO) << "Polling for connection..."; connection_ = CreateConnection(); connection_->AddObserver(this); connection_->Connect(); @@ -127,12 +128,13 @@ void BluetoothConnectionFinder::OnConnectionStatusChanged( if (connection_->IsConnected()) { base::TimeDelta elapsed = base::TimeTicks::Now() - start_time_; - VLOG(1) << "[BCF] Connection found! Elapsed Time: " - << elapsed.InMilliseconds() << "ms."; + PA_LOG(WARNING) << "Connection found! Elapsed Time: " + << elapsed.InMilliseconds() << "ms."; UnregisterAsObserver(); connection_callback_.Run(connection_.Pass()); } else if (old_status == Connection::IN_PROGRESS) { - VLOG(1) << "[BCF] Connection failed! Scheduling another polling iteration."; + PA_LOG(WARNING) + << "Connection failed! Scheduling another polling iteration."; connection_.reset(); has_delayed_poll_scheduled_ = true; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( diff --git a/components/proximity_auth/client_impl.h b/components/proximity_auth/client_impl.h index d046092..11582c9 100644 --- a/components/proximity_auth/client_impl.h +++ b/components/proximity_auth/client_impl.h @@ -42,9 +42,10 @@ class ClientImpl : public Client, public ConnectionObserver { void RequestDecryption(const std::string& challenge) override; void RequestUnlock() override; - protected: // Exposed for testing. Connection* connection() { return connection_.get(); } + + protected: SecureContext* secure_context() { return secure_context_.get(); } private: diff --git a/components/proximity_auth/client_observer.h b/components/proximity_auth/client_observer.h index 1c59ab0..b883a6bd 100644 --- a/components/proximity_auth/client_observer.h +++ b/components/proximity_auth/client_observer.h @@ -18,23 +18,22 @@ class ClientObserver { public: // Called when sending an "Easy Unlock used" local event message completes. // |success| is true iff the event was sent successfully. - virtual void OnUnlockEventSent(bool success) = 0; + virtual void OnUnlockEventSent(bool success){}; // Called when a RemoteStatusUpdate is received. - virtual void OnRemoteStatusUpdate( - const RemoteStatusUpdate& status_update) = 0; + virtual void OnRemoteStatusUpdate(const RemoteStatusUpdate& status_update){}; // Called when a response to a 'decrypt_request' is received, with the // |decrypted_bytes| that were returned by the remote device. A null pointer // indicates failure. - virtual void OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) = 0; + virtual void OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes){}; // Called when a response to a 'unlock_request' is received. // |success| is true iff the request was made successfully. - virtual void OnUnlockResponse(bool success) = 0; + virtual void OnUnlockResponse(bool success){}; // Called when the underlying secure channel disconnects. - virtual void OnDisconnected() = 0; + virtual void OnDisconnected(){}; }; } // namespace proximity_auth diff --git a/components/proximity_auth/connection.h b/components/proximity_auth/connection.h index 48d3b16..cbaf3a5 100644 --- a/components/proximity_auth/connection.h +++ b/components/proximity_auth/connection.h @@ -54,14 +54,14 @@ class Connection { // Disconnects from the remote device. virtual void Disconnect() = 0; + Status status() const { return status_; } + protected: // Sets the connection's status to |status|. If this is different from the // previous status, notifies observers of the change in status. // Virtual for testing. virtual void SetStatus(Status status); - Status status() const { return status_; } - // Called after attempting to send bytes over the connection, whether the // message was successfully sent or not. // Virtual for testing. diff --git a/components/proximity_auth/cryptauth/cryptauth_device_manager.h b/components/proximity_auth/cryptauth/cryptauth_device_manager.h index ea16cd0..0c61571 100644 --- a/components/proximity_auth/cryptauth/cryptauth_device_manager.h +++ b/components/proximity_auth/cryptauth/cryptauth_device_manager.h @@ -104,7 +104,7 @@ class CryptAuthDeviceManager : public SyncScheduler::Delegate, bool IsRecoveringFromFailure() const; // Returns a list of remote devices that can unlock the user's other devices. - const std::vector<cryptauth::ExternalDeviceInfo>& unlock_keys() { + const std::vector<cryptauth::ExternalDeviceInfo>& unlock_keys() const { return unlock_keys_; } diff --git a/components/proximity_auth/device_to_device_initiator_operations.cc b/components/proximity_auth/device_to_device_initiator_operations.cc index d0560ca..0124140 100644 --- a/components/proximity_auth/device_to_device_initiator_operations.cc +++ b/components/proximity_auth/device_to_device_initiator_operations.cc @@ -215,10 +215,11 @@ void OnInnerMessageUnwrappedForResponderAuth( if (!verified) PA_LOG(INFO) << "Failed to unwrap inner [Responder Auth] message."; + // Note: The GMS Core implementation does not properly set the metadata + // version, so we only check that the type is UNLOCK_KEY_SIGNED_CHALLENGE. cryptauth::GcmMetadata gcm_metadata; if (!gcm_metadata.ParseFromString(header.public_metadata()) || - gcm_metadata.type() != cryptauth::UNLOCK_KEY_SIGNED_CHALLENGE || - gcm_metadata.version() != kGcmMetadataVersion) { + gcm_metadata.type() != cryptauth::UNLOCK_KEY_SIGNED_CHALLENGE) { PA_LOG(WARNING) << "Failed to validate GcmMetadata in inner-most " << "[Responder Auth] message."; context.callback.Run(false, std::string()); diff --git a/components/proximity_auth/webui/BUILD.gn b/components/proximity_auth/webui/BUILD.gn index 61a0805..69d5ec7 100644 --- a/components/proximity_auth/webui/BUILD.gn +++ b/components/proximity_auth/webui/BUILD.gn @@ -19,6 +19,7 @@ static_library("webui") { deps = [ "//base", "//components/proximity_auth/cryptauth", + "//components/proximity_auth/cryptauth/proto", "//components/resources", "//content/public/browser", "//ui/resources", diff --git a/components/proximity_auth/webui/proximity_auth_ui_delegate.h b/components/proximity_auth/webui/proximity_auth_ui_delegate.h index bbb3787..c4c866d 100644 --- a/components/proximity_auth/webui/proximity_auth_ui_delegate.h +++ b/components/proximity_auth/webui/proximity_auth_ui_delegate.h @@ -38,6 +38,9 @@ class ProximityAuthUIDelegate { // API requests. virtual cryptauth::DeviceClassifier GetDeviceClassifier() = 0; + // Returns the account id of the user. + virtual std::string GetAccountId() = 0; + // Returns the GCMDriver instance used by Chrome. virtual gcm::GCMDriver* GetGCMDriver() = 0; }; diff --git a/components/proximity_auth/webui/proximity_auth_webui_handler.cc b/components/proximity_auth/webui/proximity_auth_webui_handler.cc index c71f04c..9b55d6c 100644 --- a/components/proximity_auth/webui/proximity_auth_webui_handler.cc +++ b/components/proximity_auth/webui/proximity_auth_webui_handler.cc @@ -4,24 +4,39 @@ #include "components/proximity_auth/webui/proximity_auth_webui_handler.h" +#include <algorithm> + #include "base/bind.h" #include "base/i18n/time_formatting.h" #include "base/prefs/pref_service.h" +#include "base/thread_task_runner_handle.h" #include "base/time/default_clock.h" #include "base/values.h" +#include "components/proximity_auth/bluetooth_connection.h" +#include "components/proximity_auth/bluetooth_util.h" +#include "components/proximity_auth/client_impl.h" #include "components/proximity_auth/cryptauth/base64url.h" #include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h" #include "components/proximity_auth/cryptauth/cryptauth_gcm_manager_impl.h" #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h" +#include "components/proximity_auth/cryptauth/secure_message_delegate.h" +#include "components/proximity_auth/device_to_device_authenticator.h" #include "components/proximity_auth/logging/logging.h" +#include "components/proximity_auth/remote_status_update.h" +#include "components/proximity_auth/secure_context.h" #include "components/proximity_auth/webui/cryptauth_enroller_factory_impl.h" #include "components/proximity_auth/webui/proximity_auth_ui_delegate.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/web_ui.h" +#include "device/bluetooth/bluetooth_uuid.h" namespace proximity_auth { namespace { +// The UUID of the Smart Lock Bluetooth service. +const char kBluetoothServiceUUID[] = "704EE561-3782-405A-A14B-2D47A2DDCDDF"; + // Keys in the JSON representation of a log message. const char kLogMessageTextKey[] = "text"; const char kLogMessageTimeKey[] = "time"; @@ -55,46 +70,19 @@ scoped_ptr<base::DictionaryValue> LogMessageToDictionary( // Keys in the JSON representation of an ExternalDeviceInfo proto. const char kExternalDevicePublicKey[] = "publicKey"; const char kExternalDeviceFriendlyName[] = "friendlyDeviceName"; +const char kExternalDeviceBluetoothAddress[] = "bluetoothAddress"; const char kExternalDeviceUnlockKey[] = "unlockKey"; const char kExternalDeviceConnectionStatus[] = "connectionStatus"; +const char kExternalDeviceRemoteState[] = "remoteState"; // The possible values of the |kExternalDeviceConnectionStatus| field. +const char kExternalDeviceConnected[] = "connected"; const char kExternalDeviceDisconnected[] = "disconnected"; - -// Converts an ExternalDeviceInfo proto to a JSON dictionary used in JavaScript. -scoped_ptr<base::DictionaryValue> ExternalDeviceInfoToDictionary( - const cryptauth::ExternalDeviceInfo& device_info) { - std::string base64_public_key; - Base64UrlEncode(device_info.public_key(), &base64_public_key); - - scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue()); - dictionary->SetString(kExternalDevicePublicKey, base64_public_key); - dictionary->SetString(kExternalDeviceFriendlyName, - device_info.friendly_device_name()); - dictionary->SetBoolean(kExternalDeviceUnlockKey, device_info.unlock_key()); - dictionary->SetString(kExternalDeviceConnectionStatus, - kExternalDeviceDisconnected); - return dictionary.Pass(); -} +const char kExternalDeviceConnecting[] = "connecting"; // Keys in the JSON representation of an IneligibleDevice proto. const char kIneligibleDeviceReasons[] = "ineligibilityReasons"; -// Converts an IneligibleDevice proto to a JSON dictionary used in JavaScript. -scoped_ptr<base::DictionaryValue> IneligibleDeviceToDictionary( - const cryptauth::IneligibleDevice& ineligible_device) { - scoped_ptr<base::ListValue> ineligibility_reasons(new base::ListValue()); - for (const std::string& reason : ineligible_device.reasons()) { - ineligibility_reasons->AppendString(reason); - } - - scoped_ptr<base::DictionaryValue> device_dictionary = - ExternalDeviceInfoToDictionary(ineligible_device.device()); - device_dictionary->Set(kIneligibleDeviceReasons, - ineligibility_reasons.Pass()); - return device_dictionary; -} - // Creates a SyncState JSON object that can be passed to the WebUI. scoped_ptr<base::DictionaryValue> CreateSyncStateDictionary( double last_success_time, @@ -115,14 +103,14 @@ scoped_ptr<base::DictionaryValue> CreateSyncStateDictionary( ProximityAuthWebUIHandler::ProximityAuthWebUIHandler( ProximityAuthUIDelegate* delegate) - : delegate_(delegate), weak_ptr_factory_(this) { + : delegate_(delegate), + web_contents_initialized_(false), + weak_ptr_factory_(this) { cryptauth_client_factory_ = delegate_->CreateCryptAuthClientFactory(); } ProximityAuthWebUIHandler::~ProximityAuthWebUIHandler() { LogBuffer::GetInstance()->RemoveObserver(this); - if (enrollment_manager_) - enrollment_manager_->RemoveObserver(this); } void ProximityAuthWebUIHandler::RegisterMessages() { @@ -140,7 +128,7 @@ void ProximityAuthWebUIHandler::RegisterMessages() { base::Unretained(this))); web_ui()->RegisterMessageCallback( - "getSyncStates", base::Bind(&ProximityAuthWebUIHandler::GetSyncStates, + "getLocalState", base::Bind(&ProximityAuthWebUIHandler::GetLocalState, base::Unretained(this))); web_ui()->RegisterMessageCallback( @@ -151,11 +139,15 @@ void ProximityAuthWebUIHandler::RegisterMessages() { "forceDeviceSync", base::Bind(&ProximityAuthWebUIHandler::ForceDeviceSync, base::Unretained(this))); - LogBuffer::GetInstance()->AddObserver(this); + web_ui()->RegisterMessageCallback( + "toggleConnection", + base::Bind(&ProximityAuthWebUIHandler::ToggleConnection, + base::Unretained(this))); InitGCMManager(); InitEnrollmentManager(); InitDeviceManager(); + LogBuffer::GetInstance()->AddObserver(this); } void ProximityAuthWebUIHandler::OnLogMessageAdded( @@ -172,7 +164,7 @@ void ProximityAuthWebUIHandler::OnLogBufferCleared() { void ProximityAuthWebUIHandler::OnEnrollmentStarted() { web_ui()->CallJavascriptFunction( - "SyncStateInterface.onEnrollmentStateChanged", + "LocalStateInterface.onEnrollmentStateChanged", *GetEnrollmentStateDictionary()); } @@ -182,12 +174,12 @@ void ProximityAuthWebUIHandler::OnEnrollmentFinished(bool success) { PA_LOG(INFO) << "Enrollment attempt completed with success=" << success << ":\n" << *enrollment_state; web_ui()->CallJavascriptFunction( - "SyncStateInterface.onEnrollmentStateChanged", *enrollment_state); + "LocalStateInterface.onEnrollmentStateChanged", *enrollment_state); } void ProximityAuthWebUIHandler::OnSyncStarted() { web_ui()->CallJavascriptFunction( - "SyncStateInterface.onDeviceSyncStateChanged", + "LocalStateInterface.onDeviceSyncStateChanged", *GetDeviceSyncStateDictionary()); } @@ -199,7 +191,16 @@ void ProximityAuthWebUIHandler::OnSyncFinished( PA_LOG(INFO) << "Device sync completed with result=" << static_cast<int>(sync_result) << ":\n" << *device_sync_state; web_ui()->CallJavascriptFunction( - "SyncStateInterface.onDeviceSyncStateChanged", *device_sync_state); + "LocalStateInterface.onDeviceSyncStateChanged", *device_sync_state); + + if (device_change_result == + CryptAuthDeviceManager::DeviceChangeResult::CHANGED) { + scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList(); + PA_LOG(INFO) << "New unlock keys obtained after device sync:\n" + << *unlock_keys; + web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", + *unlock_keys); + } } void ProximityAuthWebUIHandler::GetLogMessages(const base::ListValue* args) { @@ -243,6 +244,44 @@ void ProximityAuthWebUIHandler::ForceDeviceSync(const base::ListValue* args) { device_manager_->ForceSyncNow(cryptauth::INVOCATION_REASON_MANUAL); } +void ProximityAuthWebUIHandler::ToggleConnection(const base::ListValue* args) { + std::string b64_public_key; + std::string public_key; + if (!device_manager_ || !args->GetSize() || + !args->GetString(0, &b64_public_key) || + !Base64UrlDecode(b64_public_key, &public_key)) { + return; + } + + Connection* connection = GetConnection(); + for (const auto& unlock_key : device_manager_->unlock_keys()) { + if (unlock_key.public_key() == public_key) { + // Check if there is an existing connection to disconnect from first. + if (connection && connection->IsConnected() && + connection->remote_device().public_key == public_key) { + PA_LOG(INFO) << "Disconnecting from " + << unlock_key.friendly_device_name() << "[" + << unlock_key.bluetooth_address() << "]"; + connection->Disconnect(); + return; + } + + // Derive the PSK before connecting to the device. + PA_LOG(INFO) << "Connecting to " << unlock_key.friendly_device_name() + << "[" << unlock_key.bluetooth_address() << "]"; + secure_message_delegate_ = delegate_->CreateSecureMessageDelegate(); + secure_message_delegate_->DeriveKey( + user_private_key_, unlock_key.public_key(), + base::Bind(&ProximityAuthWebUIHandler::OnPSKDerived, + weak_ptr_factory_.GetWeakPtr(), unlock_key)); + + return; + } + } + + PA_LOG(ERROR) << "Unlock key (" << b64_public_key << ") not found"; +} + void ProximityAuthWebUIHandler::InitGCMManager() { gcm_manager_.reset(new CryptAuthGCMManagerImpl(delegate_->GetGCMDriver(), delegate_->GetPrefService())); @@ -254,13 +293,11 @@ void ProximityAuthWebUIHandler::InitEnrollmentManager() { // development and testing purposes until it is ready to be moved into Chrome. // The public/private key pair has been generated and serialized in a previous // session. - std::string user_public_key; Base64UrlDecode( "CAESRgohAD1lP_wgQ8XqVVwz4aK_89SqdvAQG5L_NZH5zXxwg5UbEiEAZFMlgCZ9h8OlyE4" "QYKY5oiOBu0FmLSKeTAXEq2jnVJI=", - &user_public_key); + &user_public_key_); - std::string user_private_key; Base64UrlDecode( "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP____8AAAABAAAAAAA" "AAAAAAAAA________________MFsEIP____8AAAABAAAAAAAAAAAAAAAA______________" @@ -270,7 +307,7 @@ void ProximityAuthWebUIHandler::InitEnrollmentManager() { "RAgEBBG0wawIBAQQgKZ4Dsm5xe4p5U2XPGxjrG376ZWWIa9E6r0y1BdjIntyhRANCAAQ9ZT" "_8IEPF6lVcM-Giv_PUqnbwEBuS_zWR-c18cIOVG2RTJYAmfYfDpchOEGCmOaIjgbtBZi0in" "kwFxKto51SS", - &user_private_key); + &user_private_key_); // This serialized DeviceInfo proto was previously captured from a real // CryptAuth enrollment, and is replayed here for testing purposes. @@ -302,7 +339,7 @@ void ProximityAuthWebUIHandler::InitEnrollmentManager() { enrollment_manager_.reset(new CryptAuthEnrollmentManager( make_scoped_ptr(new base::DefaultClock()), make_scoped_ptr(new CryptAuthEnrollerFactoryImpl(delegate_)), - user_public_key, user_private_key, device_info, gcm_manager_.get(), + user_public_key_, user_private_key_, device_info, gcm_manager_.get(), delegate_->GetPrefService())); enrollment_manager_->AddObserver(this); enrollment_manager_->Start(); @@ -346,15 +383,20 @@ void ProximityAuthWebUIHandler::OnFoundEligibleUnlockDevices( eligible_devices, ineligible_devices); } -void ProximityAuthWebUIHandler::GetSyncStates(const base::ListValue* args) { +void ProximityAuthWebUIHandler::GetLocalState(const base::ListValue* args) { scoped_ptr<base::DictionaryValue> enrollment_state = GetEnrollmentStateDictionary(); scoped_ptr<base::DictionaryValue> device_sync_state = GetDeviceSyncStateDictionary(); - PA_LOG(INFO) << "Enrollment State: \n" << *enrollment_state - << "Device Sync State: \n" << *device_sync_state; - web_ui()->CallJavascriptFunction("SyncStateInterface.onGotSyncStates", - *enrollment_state, *device_sync_state); + scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList(); + + PA_LOG(INFO) << "==== Got Local State ====\n" + << "Enrollment State: \n" << *enrollment_state + << "Device Sync State: \n" << *device_sync_state + << "Unlock Keys: \n" << *unlock_keys; + web_ui()->CallJavascriptFunction("LocalStateInterface.onGotLocalState", + *enrollment_state, *device_sync_state, + *unlock_keys); } scoped_ptr<base::DictionaryValue> @@ -381,4 +423,211 @@ ProximityAuthWebUIHandler::GetDeviceSyncStateDictionary() { device_manager_->IsSyncInProgress()); } +scoped_ptr<base::ListValue> ProximityAuthWebUIHandler::GetUnlockKeysList() { + scoped_ptr<base::ListValue> unlock_keys(new base::ListValue()); + if (!device_manager_) + return unlock_keys; + + for (const auto& unlock_key : device_manager_->unlock_keys()) { + unlock_keys->Append(ExternalDeviceInfoToDictionary(unlock_key)); + } + + return unlock_keys; +} + +Connection* ProximityAuthWebUIHandler::GetConnection() { + Connection* connection = bluetooth_connection_.get(); + if (client_) { + DCHECK(!connection); + connection = client_->connection(); + } + return connection; +} + +void ProximityAuthWebUIHandler::OnPSKDerived( + const cryptauth::ExternalDeviceInfo& unlock_key, + const std::string& persistent_symmetric_key) { + if (persistent_symmetric_key.empty()) { + PA_LOG(ERROR) << "Failed to derive PSK."; + return; + } + + RemoteDevice remote_device( + unlock_key.friendly_device_name(), unlock_key.public_key(), + unlock_key.bluetooth_address(), persistent_symmetric_key); + + bluetooth_connection_.reset(new BluetoothConnection( + remote_device, device::BluetoothUUID(kBluetoothServiceUUID))); + bluetooth_connection_->AddObserver(this); + + // This SeekDeviceByAddress operation is needed to connect to a device if + // it is not already known to the local device. + bluetooth_util::SeekDeviceByAddress( + remote_device.bluetooth_address, + base::Bind(&ProximityAuthWebUIHandler::OnSeekedDeviceByAddress, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&ProximityAuthWebUIHandler::OnSeekedDeviceByAddressError, + weak_ptr_factory_.GetWeakPtr()), + content::BrowserThread::GetBlockingPool() + ->GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN) + .get()); +} + +void ProximityAuthWebUIHandler::OnSeekedDeviceByAddress() { + PA_LOG(INFO) << "Found Bluetooth device: " + << bluetooth_connection_->remote_device().bluetooth_address; + bluetooth_connection_->Connect(); +} + +void ProximityAuthWebUIHandler::OnSeekedDeviceByAddressError( + const std::string& error_message) { + PA_LOG(WARNING) << "Failed to seek device by address: " + << bluetooth_connection_->remote_device().bluetooth_address; +} + +void ProximityAuthWebUIHandler::OnAuthenticationResult( + Authenticator::Result result, + scoped_ptr<SecureContext> secure_context) { + secure_context_ = secure_context.Pass(); + + // Create the ClientImpl asynchronously. |client_| registers itself as an + // observer of |bluetooth_connection_|, so creating it synchronously would + // trigger |OnSendComplete()| as an observer call for |client_|. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&ProximityAuthWebUIHandler::CreateStatusUpdateClient, + weak_ptr_factory_.GetWeakPtr())); +} + +void ProximityAuthWebUIHandler::CreateStatusUpdateClient() { + client_.reset( + new ClientImpl(bluetooth_connection_.Pass(), secure_context_.Pass())); + client_->AddObserver(this); +} + +scoped_ptr<base::DictionaryValue> +ProximityAuthWebUIHandler::ExternalDeviceInfoToDictionary( + const cryptauth::ExternalDeviceInfo& device_info) { + std::string base64_public_key; + Base64UrlEncode(device_info.public_key(), &base64_public_key); + + // Set the fields in the ExternalDeviceInfo proto. + scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue()); + dictionary->SetString(kExternalDevicePublicKey, base64_public_key); + dictionary->SetString(kExternalDeviceFriendlyName, + device_info.friendly_device_name()); + dictionary->SetString(kExternalDeviceBluetoothAddress, + device_info.bluetooth_address()); + dictionary->SetBoolean(kExternalDeviceUnlockKey, device_info.unlock_key()); + dictionary->SetString(kExternalDeviceConnectionStatus, + kExternalDeviceDisconnected); + + if (!device_manager_) + return dictionary; + + // If |device_info| is a known unlock key, then combine the proto data with + // the corresponding local device data (e.g. connection status and remote + // status updates). + std::string public_key = device_info.public_key(); + auto iterator = std::find_if( + device_manager_->unlock_keys().begin(), + device_manager_->unlock_keys().end(), + [&public_key](const cryptauth::ExternalDeviceInfo& unlock_key) { + return unlock_key.public_key() == public_key; + }); + + if (iterator == device_manager_->unlock_keys().end()) + return dictionary; + + // Fill in the current Bluetooth connection status. + Connection* connection = GetConnection(); + if (!connection || + connection->remote_device().public_key != device_info.public_key()) + return dictionary; + + std::string connection_status = kExternalDeviceDisconnected; + if (connection->IsConnected()) { + connection_status = kExternalDeviceConnected; + } else if (connection->status() == Connection::IN_PROGRESS) { + connection_status = kExternalDeviceConnecting; + } + dictionary->SetString(kExternalDeviceConnectionStatus, connection_status); + + // Fill the remote status dictionary. + if (last_remote_status_update_) { + scoped_ptr<base::DictionaryValue> status_dictionary( + new base::DictionaryValue()); + status_dictionary->SetInteger("userPresent", + last_remote_status_update_->user_presence); + status_dictionary->SetInteger( + "secureScreenLock", + last_remote_status_update_->secure_screen_lock_state); + status_dictionary->SetInteger( + "trustAgent", last_remote_status_update_->trust_agent_state); + dictionary->Set(kExternalDeviceRemoteState, status_dictionary.Pass()); + } + + return dictionary; +} + +scoped_ptr<base::DictionaryValue> +ProximityAuthWebUIHandler::IneligibleDeviceToDictionary( + const cryptauth::IneligibleDevice& ineligible_device) { + scoped_ptr<base::ListValue> ineligibility_reasons(new base::ListValue()); + for (const std::string& reason : ineligible_device.reasons()) { + ineligibility_reasons->AppendString(reason); + } + + scoped_ptr<base::DictionaryValue> device_dictionary = + ExternalDeviceInfoToDictionary(ineligible_device.device()); + device_dictionary->Set(kIneligibleDeviceReasons, + ineligibility_reasons.Pass()); + return device_dictionary; +} + +void ProximityAuthWebUIHandler::OnConnectionStatusChanged( + Connection* connection, + Connection::Status old_status, + Connection::Status new_status) { + PA_LOG(INFO) << "Connection status changed from " << old_status << " to " + << new_status; + if (new_status == Connection::CONNECTED) { + authenticator_.reset(new DeviceToDeviceAuthenticator( + connection, delegate_->GetAccountId(), + delegate_->CreateSecureMessageDelegate())); + authenticator_->Authenticate( + base::Bind(&ProximityAuthWebUIHandler::OnAuthenticationResult, + weak_ptr_factory_.GetWeakPtr())); + } else if (new_status == Connection::DISCONNECTED) { + last_remote_status_update_.reset(); + } + + scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList(); + web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", + *unlock_keys); +} + +void ProximityAuthWebUIHandler::OnMessageReceived(const Connection& connection, + const WireMessage& message) { + std::string address = connection.remote_device().bluetooth_address; + PA_LOG(INFO) << "Message received from " << address; +} + +void ProximityAuthWebUIHandler::OnRemoteStatusUpdate( + const RemoteStatusUpdate& status_update) { + PA_LOG(INFO) << "Remote status update:" + << "\n user_presence: " + << static_cast<int>(status_update.user_presence) + << "\n secure_screen_lock_state: " + << static_cast<int>(status_update.secure_screen_lock_state) + << "\n trust_agent_state: " + << static_cast<int>(status_update.trust_agent_state); + + last_remote_status_update_.reset(new RemoteStatusUpdate(status_update)); + scoped_ptr<base::ListValue> unlock_keys = GetUnlockKeysList(); + web_ui()->CallJavascriptFunction("LocalStateInterface.onUnlockKeysChanged", + *unlock_keys); +} + } // namespace proximity_auth diff --git a/components/proximity_auth/webui/proximity_auth_webui_handler.h b/components/proximity_auth/webui/proximity_auth_webui_handler.h index 70e9894..ce6f3b69 100644 --- a/components/proximity_auth/webui/proximity_auth_webui_handler.h +++ b/components/proximity_auth/webui/proximity_auth_webui_handler.h @@ -7,6 +7,9 @@ #include "base/memory/weak_ptr.h" #include "base/values.h" +#include "components/proximity_auth/authenticator.h" +#include "components/proximity_auth/client_observer.h" +#include "components/proximity_auth/connection_observer.h" #include "components/proximity_auth/cryptauth/cryptauth_client.h" #include "components/proximity_auth/cryptauth/cryptauth_device_manager.h" #include "components/proximity_auth/cryptauth/cryptauth_enrollment_manager.h" @@ -21,11 +24,20 @@ class ListValue; namespace proximity_auth { +class Authenticator; +class BluetoothConnection; +class Connection; +class ClientImpl; +struct RemoteStatusUpdate; +class SecureContext; + // Handles messages from the chrome://proximity-auth page. class ProximityAuthWebUIHandler : public content::WebUIMessageHandler, public LogBuffer::Observer, public CryptAuthEnrollmentManager::Observer, - public CryptAuthDeviceManager::Observer { + public CryptAuthDeviceManager::Observer, + public ConnectionObserver, + public ClientObserver { public: // |delegate| is not owned and must outlive this instance. explicit ProximityAuthWebUIHandler(ProximityAuthUIDelegate* delegate); @@ -53,9 +65,10 @@ class ProximityAuthWebUIHandler : public content::WebUIMessageHandler, void GetLogMessages(const base::ListValue* args); void ClearLogBuffer(const base::ListValue* args); void FindEligibleUnlockDevices(const base::ListValue* args); - void GetSyncStates(const base::ListValue* args); + void GetLocalState(const base::ListValue* args); void ForceEnrollment(const base::ListValue* args); void ForceDeviceSync(const base::ListValue* args); + void ToggleConnection(const base::ListValue* args); // Initializes CryptAuth managers, used for development purposes. void InitGCMManager(); @@ -69,12 +82,53 @@ class ProximityAuthWebUIHandler : public content::WebUIMessageHandler, void OnFoundEligibleUnlockDevices( const cryptauth::FindEligibleUnlockDevicesResponse& response); + // Called when the key agreement of PSK of the remote device completes. + void OnPSKDerived(const cryptauth::ExternalDeviceInfo& unlock_key, + const std::string& persistent_symmetric_key); + + // Callbacks for bluetooth_util::SeekDeviceByAddress(). + void OnSeekedDeviceByAddress(); + void OnSeekedDeviceByAddressError(const std::string& error_message); + + // Callback when |authenticator_| completes authentication. + void OnAuthenticationResult(Authenticator::Result result, + scoped_ptr<SecureContext> secure_context); + + // Creates the client which parses status updates. + void CreateStatusUpdateClient(); + + // Returns the active connection, whether it's owned the |this| instance or + // |client_|. + Connection* GetConnection(); + + // Converts an ExternalDeviceInfo proto to a JSON dictionary used in + // JavaScript. + scoped_ptr<base::DictionaryValue> ExternalDeviceInfoToDictionary( + const cryptauth::ExternalDeviceInfo& device_info); + + // Converts an IneligibleDevice proto to a JSON dictionary used in JavaScript. + scoped_ptr<base::DictionaryValue> IneligibleDeviceToDictionary( + const cryptauth::IneligibleDevice& ineligible_device); + + // ConnectionObserver: + void OnConnectionStatusChanged(Connection* connection, + Connection::Status old_status, + Connection::Status new_status) override; + void OnMessageReceived(const Connection& connection, + const WireMessage& message) override; + + // ClientObserver: + void OnRemoteStatusUpdate(const RemoteStatusUpdate& status_update) override; + // Returns the current enrollment state that can be used as a JSON object. scoped_ptr<base::DictionaryValue> GetEnrollmentStateDictionary(); // Returns the current device sync state that can be used as a JSON object. scoped_ptr<base::DictionaryValue> GetDeviceSyncStateDictionary(); + // Returns the current unlock keys that can be used as a JSON object. + scoped_ptr<base::ListValue> GetUnlockKeysList(); + // The delegate used to fetch dependencies. Must outlive this instance. ProximityAuthUIDelegate* delegate_; @@ -84,11 +138,26 @@ class ProximityAuthWebUIHandler : public content::WebUIMessageHandler, // We only support one concurrent API call. scoped_ptr<CryptAuthClient> cryptauth_client_; + // True if the WebContents backing the WebUI has been initialized. + bool web_contents_initialized_; + + // Member variables related to CryptAuth debugging. // TODO(tengs): These members are temporarily used for development. scoped_ptr<PrefService> pref_service; scoped_ptr<CryptAuthGCMManager> gcm_manager_; scoped_ptr<CryptAuthEnrollmentManager> enrollment_manager_; scoped_ptr<CryptAuthDeviceManager> device_manager_; + std::string user_public_key_; + std::string user_private_key_; + + // Member variables for connecting to and authenticating the remote device. + // TODO(tengs): Support multiple simultaenous connections. + scoped_ptr<SecureMessageDelegate> secure_message_delegate_; + scoped_ptr<BluetoothConnection> bluetooth_connection_; + scoped_ptr<Authenticator> authenticator_; + scoped_ptr<SecureContext> secure_context_; + scoped_ptr<ClientImpl> client_; + scoped_ptr<RemoteStatusUpdate> last_remote_status_update_; base::WeakPtrFactory<ProximityAuthWebUIHandler> weak_ptr_factory_; diff --git a/components/proximity_auth/webui/resources/device-list.html b/components/proximity_auth/webui/resources/device-list.html index 7141559..15a5ecd 100644 --- a/components/proximity_auth/webui/resources/device-list.html +++ b/components/proximity_auth/webui/resources/device-list.html @@ -95,15 +95,21 @@ <div hidden> <div> User Present: - <span>[[item.remoteState.userPresent]]</span> + <span> + [[getUserPresenceText_(item.remoteState.userPresent)]] + </span> </div> <div> Secure Screen Lock: - <span>[[item.remoteState.secureScreenLock]]</span> + <span> + [[getScreenLockText_(item.remoteState.secureScreenLock)]] + </span> </div> <div> Trust Agent: - <span>[[item.remoteState.trustAgent]]</span> + <span> + [[getTrustAgentText_(item.remoteState.trustAgent)]] + </span> </div> </div> </core-tooltip> @@ -123,6 +129,8 @@ </template> </div> <paper-icon-button class="end-icon" + bluetooth-address="[[item.bluetoothAddress]]" + on-click="toggleConnection_" icon="[[getIconForConnection_(item.connectionStatus)]]" hidden$="{{!item.unlockKey}}"> </paper-icon-button> diff --git a/components/proximity_auth/webui/resources/device-list.js b/components/proximity_auth/webui/resources/device-list.js index 60fbdec..74ce87f 100644 --- a/components/proximity_auth/webui/resources/device-list.js +++ b/components/proximity_auth/webui/resources/device-list.js @@ -23,6 +23,15 @@ Polymer({ }, /** + * Handles when the toggle connection button is clicked for a list item. + * @param {Event} event + */ + toggleConnection_: function(event) { + var deviceInfo = event.model.item; + chrome.send('toggleConnection', [deviceInfo.publicKey]); + }, + + /** * @param {string} reason The device ineligibility reason. * @return {string} The prettified ineligibility reason. * @private @@ -92,5 +101,44 @@ Polymer({ default: return 'error'; }; - } + }, + + /** + * @param {number} userPresence + * @return {string} + */ + getUserPresenceText_: function(userPresence) { + var userPresenceMap = { + 0: 'User Present', + 1: 'User Absent', + 2: 'User Presence Unknown', + }; + return userPresenceMap[userPresence]; + }, + + /** + * @param {number} screenLock + * @return {string} + */ + getScreenLockText_: function(screenLock) { + var screenLockMap = { + 0: 'Secure Screen Lock Enabled', + 1: 'Secure Screen Lock Disabled', + 2: 'Secure Screen Lock State Unknown', + }; + return screenLockMap[screenLock]; + }, + + /** + * @param {number} trustAgent + * @return {string} + */ + getTrustAgentText_: function(trustAgent) { + var trustAgentMap = { + 0: 'Trust Agent Enabled', + 1: 'Trust Agent Disabled', + 2: 'Trust Agent Unsupported', + }; + return trustAgentMap[trustAgent]; + }, }); diff --git a/components/proximity_auth/webui/resources/local-state.js b/components/proximity_auth/webui/resources/local-state.js index d606820..865d203 100644 --- a/components/proximity_auth/webui/resources/local-state.js +++ b/components/proximity_auth/webui/resources/local-state.js @@ -67,8 +67,8 @@ Polymer({ * Called when the page is about to be shown. */ activate: function() { - SyncStateInterface = this; - chrome.send('getSyncStates'); + LocalStateInterface = this; + chrome.send('getLocalState'); }, /** @@ -102,13 +102,23 @@ Polymer({ }, /** + * Called when the locally stored unlock keys change. + * @param {Array<DeviceInfo>} unlockKeys + */ + onUnlockKeysChanged: function(unlockKeys) { + this.unlockKeys_ = unlockKeys; + }, + + /** * Called for the chrome.send('getSyncStates') response. * @param {SyncState} enrollmentState * @param {SyncState} deviceSyncState + * @param {Array<DeviceInfo>} unlockKeys */ - onGotSyncStates: function(enrollmentState, deviceSyncState) { + onGotLocalState: function(enrollmentState, deviceSyncState, unlockKeys) { this.enrollmentState_ = enrollmentState; this.deviceSyncState_ = deviceSyncState; + this.unlockKeys_ = unlockKeys; }, /** @@ -170,9 +180,12 @@ Polymer({ }, }); -// Interface with the native WebUI component for the CryptAuthSync state (i.e. -// enrollment and device sync). -SyncStateInterface = { +// Interface with the native WebUI component for getting the local state and +// being notified when the local state changes. +// The local state refers to state stored on the device rather than online in +// CryptAuth. This state includes the enrollment and device sync states, as well +// as the list of unlock keys. +LocalStateInterface = { /** * Called when the enrollment state changes. For example, when a new * enrollment is initiated. @@ -188,9 +201,14 @@ SyncStateInterface = { onDeviceSyncStateChanged: function(deviceSyncState) {}, /** - * Called in response to chrome.send('getSyncStates') with the current - * enrollment and device sync states of the user and device. - * @type {function(SyncState, SyncState)} + * Called when the locally stored unlock keys changes. + * @type {function(Array<DeviceInfo>)} + */ + onUnlockKeysChanged: function(unlockKeys) {}, + + /** + * Called in response to chrome.send('getLocalState') with the local state. + * @type {function(SyncState, SyncState, Array<DeviceInfo>)} */ - onGotSyncStates: function(enrollmentState, deviceSyncState) {}, + onGotLocalState: function(enrollmentState, deviceSyncState, unlockKeys) {}, }; diff --git a/components/proximity_auth/webui/resources/log-panel.html b/components/proximity_auth/webui/resources/log-panel.html index ac98111..b59d909 100644 --- a/components/proximity_auth/webui/resources/log-panel.html +++ b/components/proximity_auth/webui/resources/log-panel.html @@ -70,7 +70,7 @@ <log-buffer id='logBuffer' logs="{{logs}}"></log-buffer> <div id="list" class="flex"> <template is="dom-repeat" items="[[logs]]"> - <div class="list-item" severity="[[item.severity]]"> + <div class="list-item" severity$="[[item.severity]]"> <div class="item-metadata layout horizontal"> <div>[[item.time]]</div> <div class="flex"></div> |