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