| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "components/proximity_auth/cryptauth/cryptauth_device_manager.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "base/base64url.h" | |
| 12 #include "base/memory/ptr_util.h" | |
| 13 #include "components/prefs/pref_registry_simple.h" | |
| 14 #include "components/prefs/pref_service.h" | |
| 15 #include "components/prefs/scoped_user_pref_update.h" | |
| 16 #include "components/proximity_auth/cryptauth/cryptauth_client.h" | |
| 17 #include "components/proximity_auth/cryptauth/pref_names.h" | |
| 18 #include "components/proximity_auth/cryptauth/sync_scheduler_impl.h" | |
| 19 #include "components/proximity_auth/logging/logging.h" | |
| 20 | |
| 21 namespace proximity_auth { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // The normal period between successful syncs, in hours. | |
| 26 const int kRefreshPeriodHours = 24; | |
| 27 | |
| 28 // A more aggressive period between sync attempts to recover when the last | |
| 29 // sync attempt fails, in minutes. This is a base time that increases for each | |
| 30 // subsequent failure. | |
| 31 const int kDeviceSyncBaseRecoveryPeriodMinutes = 10; | |
| 32 | |
| 33 // The bound on the amount to jitter the period between syncs. | |
| 34 const double kDeviceSyncMaxJitterRatio = 0.2; | |
| 35 | |
| 36 // Keys for ExternalDeviceInfo dictionaries that are stored in the user's prefs. | |
| 37 const char kExternalDeviceKeyPublicKey[] = "public_key"; | |
| 38 const char kExternalDeviceKeyDeviceName[] = "device_name"; | |
| 39 const char kExternalDeviceKeyBluetoothAddress[] = "bluetooth_address"; | |
| 40 | |
| 41 // Converts an unlock key proto to a dictionary that can be stored in user | |
| 42 // prefs. | |
| 43 std::unique_ptr<base::DictionaryValue> UnlockKeyToDictionary( | |
| 44 const cryptauth::ExternalDeviceInfo& device) { | |
| 45 std::unique_ptr<base::DictionaryValue> dictionary( | |
| 46 new base::DictionaryValue()); | |
| 47 | |
| 48 // We store the device information in Base64Url form because dictionary values | |
| 49 // must be valid UTF8 strings. | |
| 50 std::string public_key_b64, device_name_b64, bluetooth_address_b64; | |
| 51 base::Base64UrlEncode(device.public_key(), | |
| 52 base::Base64UrlEncodePolicy::INCLUDE_PADDING, | |
| 53 &public_key_b64); | |
| 54 base::Base64UrlEncode(device.friendly_device_name(), | |
| 55 base::Base64UrlEncodePolicy::INCLUDE_PADDING, | |
| 56 &device_name_b64); | |
| 57 base::Base64UrlEncode(device.bluetooth_address(), | |
| 58 base::Base64UrlEncodePolicy::INCLUDE_PADDING, | |
| 59 &bluetooth_address_b64); | |
| 60 | |
| 61 dictionary->SetString(kExternalDeviceKeyPublicKey, public_key_b64); | |
| 62 dictionary->SetString(kExternalDeviceKeyDeviceName, device_name_b64); | |
| 63 dictionary->SetString(kExternalDeviceKeyBluetoothAddress, | |
| 64 bluetooth_address_b64); | |
| 65 return dictionary; | |
| 66 } | |
| 67 | |
| 68 // Converts an unlock key dictionary stored in user prefs to an | |
| 69 // ExternalDeviceInfo proto. Returns true if the dictionary is valid, and the | |
| 70 // parsed proto is written to |external_device|. | |
| 71 bool DictionaryToUnlockKey(const base::DictionaryValue& dictionary, | |
| 72 cryptauth::ExternalDeviceInfo* external_device) { | |
| 73 std::string public_key_b64, device_name_b64, bluetooth_address_b64; | |
| 74 if (!dictionary.GetString(kExternalDeviceKeyPublicKey, &public_key_b64) || | |
| 75 !dictionary.GetString(kExternalDeviceKeyDeviceName, &device_name_b64) || | |
| 76 !dictionary.GetString(kExternalDeviceKeyBluetoothAddress, | |
| 77 &bluetooth_address_b64)) { | |
| 78 return false; | |
| 79 } | |
| 80 | |
| 81 // We store the device information in Base64Url form because dictionary values | |
| 82 // must be valid UTF8 strings. | |
| 83 std::string public_key, device_name, bluetooth_address; | |
| 84 if (!base::Base64UrlDecode(public_key_b64, | |
| 85 base::Base64UrlDecodePolicy::REQUIRE_PADDING, | |
| 86 &public_key) || | |
| 87 !base::Base64UrlDecode(device_name_b64, | |
| 88 base::Base64UrlDecodePolicy::REQUIRE_PADDING, | |
| 89 &device_name) || | |
| 90 !base::Base64UrlDecode(bluetooth_address_b64, | |
| 91 base::Base64UrlDecodePolicy::REQUIRE_PADDING, | |
| 92 &bluetooth_address)) { | |
| 93 return false; | |
| 94 } | |
| 95 | |
| 96 external_device->set_public_key(public_key); | |
| 97 external_device->set_friendly_device_name(device_name); | |
| 98 external_device->set_bluetooth_address(bluetooth_address); | |
| 99 external_device->set_unlock_key(true); | |
| 100 external_device->set_unlockable(false); | |
| 101 return true; | |
| 102 } | |
| 103 | |
| 104 } // namespace | |
| 105 | |
| 106 CryptAuthDeviceManager::CryptAuthDeviceManager( | |
| 107 std::unique_ptr<base::Clock> clock, | |
| 108 std::unique_ptr<CryptAuthClientFactory> client_factory, | |
| 109 CryptAuthGCMManager* gcm_manager, | |
| 110 PrefService* pref_service) | |
| 111 : clock_(std::move(clock)), | |
| 112 client_factory_(std::move(client_factory)), | |
| 113 gcm_manager_(gcm_manager), | |
| 114 pref_service_(pref_service), | |
| 115 weak_ptr_factory_(this) { | |
| 116 UpdateUnlockKeysFromPrefs(); | |
| 117 } | |
| 118 | |
| 119 CryptAuthDeviceManager::~CryptAuthDeviceManager() { | |
| 120 gcm_manager_->RemoveObserver(this); | |
| 121 } | |
| 122 | |
| 123 // static | |
| 124 void CryptAuthDeviceManager::RegisterPrefs(PrefRegistrySimple* registry) { | |
| 125 registry->RegisterDoublePref(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds, | |
| 126 0.0); | |
| 127 registry->RegisterBooleanPref( | |
| 128 prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure, false); | |
| 129 registry->RegisterIntegerPref(prefs::kCryptAuthDeviceSyncReason, | |
| 130 cryptauth::INVOCATION_REASON_UNKNOWN); | |
| 131 registry->RegisterListPref(prefs::kCryptAuthDeviceSyncUnlockKeys); | |
| 132 } | |
| 133 | |
| 134 void CryptAuthDeviceManager::Start() { | |
| 135 gcm_manager_->AddObserver(this); | |
| 136 | |
| 137 base::Time last_successful_sync = GetLastSyncTime(); | |
| 138 base::TimeDelta elapsed_time_since_last_sync = | |
| 139 clock_->Now() - last_successful_sync; | |
| 140 | |
| 141 bool is_recovering_from_failure = | |
| 142 pref_service_->GetBoolean( | |
| 143 prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure) || | |
| 144 last_successful_sync.is_null(); | |
| 145 | |
| 146 scheduler_ = CreateSyncScheduler(); | |
| 147 scheduler_->Start(elapsed_time_since_last_sync, | |
| 148 is_recovering_from_failure | |
| 149 ? SyncScheduler::Strategy::AGGRESSIVE_RECOVERY | |
| 150 : SyncScheduler::Strategy::PERIODIC_REFRESH); | |
| 151 } | |
| 152 | |
| 153 void CryptAuthDeviceManager::AddObserver(Observer* observer) { | |
| 154 observers_.AddObserver(observer); | |
| 155 } | |
| 156 | |
| 157 void CryptAuthDeviceManager::RemoveObserver(Observer* observer) { | |
| 158 observers_.RemoveObserver(observer); | |
| 159 } | |
| 160 | |
| 161 void CryptAuthDeviceManager::ForceSyncNow( | |
| 162 cryptauth::InvocationReason invocation_reason) { | |
| 163 pref_service_->SetInteger(prefs::kCryptAuthDeviceSyncReason, | |
| 164 invocation_reason); | |
| 165 scheduler_->ForceSync(); | |
| 166 } | |
| 167 | |
| 168 base::Time CryptAuthDeviceManager::GetLastSyncTime() const { | |
| 169 return base::Time::FromDoubleT( | |
| 170 pref_service_->GetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds)); | |
| 171 } | |
| 172 | |
| 173 base::TimeDelta CryptAuthDeviceManager::GetTimeToNextAttempt() const { | |
| 174 return scheduler_->GetTimeToNextSync(); | |
| 175 } | |
| 176 | |
| 177 bool CryptAuthDeviceManager::IsSyncInProgress() const { | |
| 178 return scheduler_->GetSyncState() == | |
| 179 SyncScheduler::SyncState::SYNC_IN_PROGRESS; | |
| 180 } | |
| 181 | |
| 182 bool CryptAuthDeviceManager::IsRecoveringFromFailure() const { | |
| 183 return scheduler_->GetStrategy() == | |
| 184 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY; | |
| 185 } | |
| 186 | |
| 187 void CryptAuthDeviceManager::OnGetMyDevicesSuccess( | |
| 188 const cryptauth::GetMyDevicesResponse& response) { | |
| 189 // Update the unlock keys stored in the user's prefs. | |
| 190 std::unique_ptr<base::ListValue> unlock_keys_pref(new base::ListValue()); | |
| 191 std::unique_ptr<base::ListValue> devices_as_list(new base::ListValue()); | |
| 192 for (const auto& device : response.devices()) { | |
| 193 devices_as_list->Append(UnlockKeyToDictionary(device)); | |
| 194 if (device.unlock_key()) | |
| 195 unlock_keys_pref->Append(UnlockKeyToDictionary(device)); | |
| 196 } | |
| 197 PA_LOG(INFO) << "Devices Synced:\n" << *devices_as_list; | |
| 198 | |
| 199 bool unlock_keys_changed = !unlock_keys_pref->Equals( | |
| 200 pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys)); | |
| 201 { | |
| 202 ListPrefUpdate update(pref_service_, prefs::kCryptAuthDeviceSyncUnlockKeys); | |
| 203 update.Get()->Swap(unlock_keys_pref.get()); | |
| 204 } | |
| 205 UpdateUnlockKeysFromPrefs(); | |
| 206 | |
| 207 // Reset metadata used for scheduling syncing. | |
| 208 pref_service_->SetBoolean(prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure, | |
| 209 false); | |
| 210 pref_service_->SetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds, | |
| 211 clock_->Now().ToDoubleT()); | |
| 212 pref_service_->SetInteger(prefs::kCryptAuthDeviceSyncReason, | |
| 213 cryptauth::INVOCATION_REASON_UNKNOWN); | |
| 214 | |
| 215 sync_request_->OnDidComplete(true); | |
| 216 cryptauth_client_.reset(); | |
| 217 sync_request_.reset(); | |
| 218 for (auto& observer : observers_) { | |
| 219 observer.OnSyncFinished(SyncResult::SUCCESS, | |
| 220 unlock_keys_changed | |
| 221 ? DeviceChangeResult::CHANGED | |
| 222 : DeviceChangeResult::UNCHANGED); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 void CryptAuthDeviceManager::OnGetMyDevicesFailure(const std::string& error) { | |
| 227 PA_LOG(ERROR) << "GetMyDevices API failed: " << error; | |
| 228 pref_service_->SetBoolean(prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure, | |
| 229 true); | |
| 230 sync_request_->OnDidComplete(false); | |
| 231 cryptauth_client_.reset(); | |
| 232 sync_request_.reset(); | |
| 233 for (auto& observer : observers_) | |
| 234 observer.OnSyncFinished(SyncResult::FAILURE, DeviceChangeResult::UNCHANGED); | |
| 235 } | |
| 236 | |
| 237 std::unique_ptr<SyncScheduler> CryptAuthDeviceManager::CreateSyncScheduler() { | |
| 238 return base::MakeUnique<SyncSchedulerImpl>( | |
| 239 this, base::TimeDelta::FromHours(kRefreshPeriodHours), | |
| 240 base::TimeDelta::FromMinutes(kDeviceSyncBaseRecoveryPeriodMinutes), | |
| 241 kDeviceSyncMaxJitterRatio, "CryptAuth DeviceSync"); | |
| 242 } | |
| 243 | |
| 244 void CryptAuthDeviceManager::OnResyncMessage() { | |
| 245 ForceSyncNow(cryptauth::INVOCATION_REASON_SERVER_INITIATED); | |
| 246 } | |
| 247 | |
| 248 void CryptAuthDeviceManager::UpdateUnlockKeysFromPrefs() { | |
| 249 const base::ListValue* unlock_key_list = | |
| 250 pref_service_->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys); | |
| 251 unlock_keys_.clear(); | |
| 252 for (size_t i = 0; i < unlock_key_list->GetSize(); ++i) { | |
| 253 const base::DictionaryValue* unlock_key_dictionary; | |
| 254 if (unlock_key_list->GetDictionary(i, &unlock_key_dictionary)) { | |
| 255 cryptauth::ExternalDeviceInfo unlock_key; | |
| 256 if (DictionaryToUnlockKey(*unlock_key_dictionary, &unlock_key)) { | |
| 257 unlock_keys_.push_back(unlock_key); | |
| 258 } else { | |
| 259 PA_LOG(ERROR) << "Unable to deserialize unlock key dictionary " | |
| 260 << "(index=" << i << "):\n" << *unlock_key_dictionary; | |
| 261 } | |
| 262 } else { | |
| 263 PA_LOG(ERROR) << "Can not get dictionary in list of unlock keys " | |
| 264 << "(index=" << i << "):\n" << *unlock_key_list; | |
| 265 } | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 void CryptAuthDeviceManager::OnSyncRequested( | |
| 270 std::unique_ptr<SyncScheduler::SyncRequest> sync_request) { | |
| 271 for (auto& observer : observers_) | |
| 272 observer.OnSyncStarted(); | |
| 273 | |
| 274 sync_request_ = std::move(sync_request); | |
| 275 cryptauth_client_ = client_factory_->CreateInstance(); | |
| 276 | |
| 277 cryptauth::InvocationReason invocation_reason = | |
| 278 cryptauth::INVOCATION_REASON_UNKNOWN; | |
| 279 | |
| 280 int reason_stored_in_prefs = | |
| 281 pref_service_->GetInteger(prefs::kCryptAuthDeviceSyncReason); | |
| 282 | |
| 283 // If the sync attempt is not forced, it is acceptable for CryptAuth to return | |
| 284 // a cached copy of the user's devices, rather taking a database hit for the | |
| 285 // freshest data. | |
| 286 bool is_sync_speculative = | |
| 287 reason_stored_in_prefs != cryptauth::INVOCATION_REASON_UNKNOWN; | |
| 288 | |
| 289 if (cryptauth::InvocationReason_IsValid(reason_stored_in_prefs) && | |
| 290 reason_stored_in_prefs != cryptauth::INVOCATION_REASON_UNKNOWN) { | |
| 291 invocation_reason = | |
| 292 static_cast<cryptauth::InvocationReason>(reason_stored_in_prefs); | |
| 293 } else if (GetLastSyncTime().is_null()) { | |
| 294 invocation_reason = cryptauth::INVOCATION_REASON_INITIALIZATION; | |
| 295 } else if (IsRecoveringFromFailure()) { | |
| 296 invocation_reason = cryptauth::INVOCATION_REASON_FAILURE_RECOVERY; | |
| 297 } else { | |
| 298 invocation_reason = cryptauth::INVOCATION_REASON_PERIODIC; | |
| 299 } | |
| 300 | |
| 301 cryptauth::GetMyDevicesRequest request; | |
| 302 request.set_invocation_reason(invocation_reason); | |
| 303 request.set_allow_stale_read(is_sync_speculative); | |
| 304 cryptauth_client_->GetMyDevices( | |
| 305 request, base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesSuccess, | |
| 306 weak_ptr_factory_.GetWeakPtr()), | |
| 307 base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesFailure, | |
| 308 weak_ptr_factory_.GetWeakPtr())); | |
| 309 } | |
| 310 | |
| 311 } // namespace proximity_auth | |
| OLD | NEW |