| 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_enrollment_manager.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/base64url.h" | |
| 10 #include "base/memory/ptr_util.h" | |
| 11 #include "base/time/clock.h" | |
| 12 #include "base/time/time.h" | |
| 13 #include "components/prefs/pref_registry_simple.h" | |
| 14 #include "components/prefs/pref_service.h" | |
| 15 #include "components/proximity_auth/cryptauth/cryptauth_enroller.h" | |
| 16 #include "components/proximity_auth/cryptauth/pref_names.h" | |
| 17 #include "components/proximity_auth/cryptauth/secure_message_delegate.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 number of days that an enrollment is valid. Note that we try to refresh | |
| 26 // the enrollment well before this time elapses. | |
| 27 const int kValidEnrollmentPeriodDays = 45; | |
| 28 | |
| 29 // The normal period between successful enrollments in days. | |
| 30 const int kEnrollmentRefreshPeriodDays = 30; | |
| 31 | |
| 32 // A more aggressive period between enrollments to recover when the last | |
| 33 // enrollment fails, in minutes. This is a base time that increases for each | |
| 34 // subsequent failure. | |
| 35 const int kEnrollmentBaseRecoveryPeriodMinutes = 10; | |
| 36 | |
| 37 // The bound on the amount to jitter the period between enrollments. | |
| 38 const double kEnrollmentMaxJitterRatio = 0.2; | |
| 39 | |
| 40 // The value of the device_software_package field in the device info uploaded | |
| 41 // during enrollment. This value must be the same as the app id used for GCM | |
| 42 // registration. | |
| 43 const char kDeviceSoftwarePackage[] = "com.google.chrome.cryptauth"; | |
| 44 | |
| 45 } // namespace | |
| 46 | |
| 47 CryptAuthEnrollmentManager::CryptAuthEnrollmentManager( | |
| 48 std::unique_ptr<base::Clock> clock, | |
| 49 std::unique_ptr<CryptAuthEnrollerFactory> enroller_factory, | |
| 50 std::unique_ptr<SecureMessageDelegate> secure_message_delegate, | |
| 51 const cryptauth::GcmDeviceInfo& device_info, | |
| 52 CryptAuthGCMManager* gcm_manager, | |
| 53 PrefService* pref_service) | |
| 54 : clock_(std::move(clock)), | |
| 55 enroller_factory_(std::move(enroller_factory)), | |
| 56 secure_message_delegate_(std::move(secure_message_delegate)), | |
| 57 device_info_(device_info), | |
| 58 gcm_manager_(gcm_manager), | |
| 59 pref_service_(pref_service), | |
| 60 weak_ptr_factory_(this) {} | |
| 61 | |
| 62 CryptAuthEnrollmentManager::~CryptAuthEnrollmentManager() { | |
| 63 gcm_manager_->RemoveObserver(this); | |
| 64 } | |
| 65 | |
| 66 // static | |
| 67 void CryptAuthEnrollmentManager::RegisterPrefs(PrefRegistrySimple* registry) { | |
| 68 registry->RegisterBooleanPref( | |
| 69 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure, false); | |
| 70 registry->RegisterDoublePref( | |
| 71 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds, 0.0); | |
| 72 registry->RegisterIntegerPref(prefs::kCryptAuthEnrollmentReason, | |
| 73 cryptauth::INVOCATION_REASON_UNKNOWN); | |
| 74 registry->RegisterStringPref(prefs::kCryptAuthEnrollmentUserPublicKey, | |
| 75 std::string()); | |
| 76 registry->RegisterStringPref(prefs::kCryptAuthEnrollmentUserPrivateKey, | |
| 77 std::string()); | |
| 78 } | |
| 79 | |
| 80 void CryptAuthEnrollmentManager::Start() { | |
| 81 gcm_manager_->AddObserver(this); | |
| 82 | |
| 83 bool is_recovering_from_failure = | |
| 84 pref_service_->GetBoolean( | |
| 85 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure) || | |
| 86 !IsEnrollmentValid(); | |
| 87 | |
| 88 base::Time last_successful_enrollment = GetLastEnrollmentTime(); | |
| 89 base::TimeDelta elapsed_time_since_last_sync = | |
| 90 clock_->Now() - last_successful_enrollment; | |
| 91 | |
| 92 scheduler_ = CreateSyncScheduler(); | |
| 93 scheduler_->Start(elapsed_time_since_last_sync, | |
| 94 is_recovering_from_failure | |
| 95 ? SyncScheduler::Strategy::AGGRESSIVE_RECOVERY | |
| 96 : SyncScheduler::Strategy::PERIODIC_REFRESH); | |
| 97 } | |
| 98 | |
| 99 void CryptAuthEnrollmentManager::AddObserver(Observer* observer) { | |
| 100 observers_.AddObserver(observer); | |
| 101 } | |
| 102 | |
| 103 void CryptAuthEnrollmentManager::RemoveObserver(Observer* observer) { | |
| 104 observers_.RemoveObserver(observer); | |
| 105 } | |
| 106 | |
| 107 void CryptAuthEnrollmentManager::ForceEnrollmentNow( | |
| 108 cryptauth::InvocationReason invocation_reason) { | |
| 109 // We store the invocation reason in a preference so that it can persist | |
| 110 // across browser restarts. If the sync fails, the next retry should still use | |
| 111 // this original reason instead of INVOCATION_REASON_FAILURE_RECOVERY. | |
| 112 pref_service_->SetInteger(prefs::kCryptAuthEnrollmentReason, | |
| 113 invocation_reason); | |
| 114 scheduler_->ForceSync(); | |
| 115 } | |
| 116 | |
| 117 bool CryptAuthEnrollmentManager::IsEnrollmentValid() const { | |
| 118 base::Time last_enrollment_time = GetLastEnrollmentTime(); | |
| 119 return !last_enrollment_time.is_null() && | |
| 120 (clock_->Now() - last_enrollment_time) < | |
| 121 base::TimeDelta::FromDays(kValidEnrollmentPeriodDays); | |
| 122 } | |
| 123 | |
| 124 base::Time CryptAuthEnrollmentManager::GetLastEnrollmentTime() const { | |
| 125 return base::Time::FromDoubleT(pref_service_->GetDouble( | |
| 126 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds)); | |
| 127 } | |
| 128 | |
| 129 base::TimeDelta CryptAuthEnrollmentManager::GetTimeToNextAttempt() const { | |
| 130 return scheduler_->GetTimeToNextSync(); | |
| 131 } | |
| 132 | |
| 133 bool CryptAuthEnrollmentManager::IsEnrollmentInProgress() const { | |
| 134 return scheduler_->GetSyncState() == | |
| 135 SyncScheduler::SyncState::SYNC_IN_PROGRESS; | |
| 136 } | |
| 137 | |
| 138 bool CryptAuthEnrollmentManager::IsRecoveringFromFailure() const { | |
| 139 return scheduler_->GetStrategy() == | |
| 140 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY; | |
| 141 } | |
| 142 | |
| 143 void CryptAuthEnrollmentManager::OnEnrollmentFinished(bool success) { | |
| 144 if (success) { | |
| 145 pref_service_->SetDouble( | |
| 146 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds, | |
| 147 clock_->Now().ToDoubleT()); | |
| 148 pref_service_->SetInteger(prefs::kCryptAuthEnrollmentReason, | |
| 149 cryptauth::INVOCATION_REASON_UNKNOWN); | |
| 150 } | |
| 151 | |
| 152 pref_service_->SetBoolean(prefs::kCryptAuthEnrollmentIsRecoveringFromFailure, | |
| 153 !success); | |
| 154 | |
| 155 sync_request_->OnDidComplete(success); | |
| 156 cryptauth_enroller_.reset(); | |
| 157 sync_request_.reset(); | |
| 158 for (auto& observer : observers_) | |
| 159 observer.OnEnrollmentFinished(success); | |
| 160 } | |
| 161 | |
| 162 std::unique_ptr<SyncScheduler> | |
| 163 CryptAuthEnrollmentManager::CreateSyncScheduler() { | |
| 164 return base::MakeUnique<SyncSchedulerImpl>( | |
| 165 this, base::TimeDelta::FromDays(kEnrollmentRefreshPeriodDays), | |
| 166 base::TimeDelta::FromMinutes(kEnrollmentBaseRecoveryPeriodMinutes), | |
| 167 kEnrollmentMaxJitterRatio, "CryptAuth Enrollment"); | |
| 168 } | |
| 169 | |
| 170 std::string CryptAuthEnrollmentManager::GetUserPublicKey() { | |
| 171 std::string public_key; | |
| 172 if (!base::Base64UrlDecode( | |
| 173 pref_service_->GetString(prefs::kCryptAuthEnrollmentUserPublicKey), | |
| 174 base::Base64UrlDecodePolicy::REQUIRE_PADDING, &public_key)) { | |
| 175 PA_LOG(ERROR) << "Invalid public key stored in user prefs."; | |
| 176 return std::string(); | |
| 177 } | |
| 178 return public_key; | |
| 179 } | |
| 180 | |
| 181 std::string CryptAuthEnrollmentManager::GetUserPrivateKey() { | |
| 182 std::string private_key; | |
| 183 if (!base::Base64UrlDecode( | |
| 184 pref_service_->GetString(prefs::kCryptAuthEnrollmentUserPrivateKey), | |
| 185 base::Base64UrlDecodePolicy::REQUIRE_PADDING, &private_key)) { | |
| 186 PA_LOG(ERROR) << "Invalid private key stored in user prefs."; | |
| 187 return std::string(); | |
| 188 } | |
| 189 return private_key; | |
| 190 } | |
| 191 | |
| 192 void CryptAuthEnrollmentManager::OnGCMRegistrationResult(bool success) { | |
| 193 if (!sync_request_) | |
| 194 return; | |
| 195 | |
| 196 PA_LOG(INFO) << "GCM registration for CryptAuth Enrollment completed: " | |
| 197 << success; | |
| 198 if (success) | |
| 199 DoCryptAuthEnrollment(); | |
| 200 else | |
| 201 OnEnrollmentFinished(false); | |
| 202 } | |
| 203 | |
| 204 void CryptAuthEnrollmentManager::OnKeyPairGenerated( | |
| 205 const std::string& public_key, | |
| 206 const std::string& private_key) { | |
| 207 if (!public_key.empty() && !private_key.empty()) { | |
| 208 PA_LOG(INFO) << "Key pair generated for CryptAuth enrollment"; | |
| 209 // Store the keypair in Base64 format because pref values require readable | |
| 210 // string values. | |
| 211 std::string public_key_b64, private_key_b64; | |
| 212 base::Base64UrlEncode(public_key, | |
| 213 base::Base64UrlEncodePolicy::INCLUDE_PADDING, | |
| 214 &public_key_b64); | |
| 215 base::Base64UrlEncode(private_key, | |
| 216 base::Base64UrlEncodePolicy::INCLUDE_PADDING, | |
| 217 &private_key_b64); | |
| 218 pref_service_->SetString(prefs::kCryptAuthEnrollmentUserPublicKey, | |
| 219 public_key_b64); | |
| 220 pref_service_->SetString(prefs::kCryptAuthEnrollmentUserPrivateKey, | |
| 221 private_key_b64); | |
| 222 DoCryptAuthEnrollment(); | |
| 223 } else { | |
| 224 OnEnrollmentFinished(false); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 void CryptAuthEnrollmentManager::OnReenrollMessage() { | |
| 229 ForceEnrollmentNow(cryptauth::INVOCATION_REASON_SERVER_INITIATED); | |
| 230 } | |
| 231 | |
| 232 void CryptAuthEnrollmentManager::OnSyncRequested( | |
| 233 std::unique_ptr<SyncScheduler::SyncRequest> sync_request) { | |
| 234 for (auto& observer : observers_) | |
| 235 observer.OnEnrollmentStarted(); | |
| 236 | |
| 237 sync_request_ = std::move(sync_request); | |
| 238 if (gcm_manager_->GetRegistrationId().empty() || | |
| 239 pref_service_->GetInteger(prefs::kCryptAuthEnrollmentReason) == | |
| 240 cryptauth::INVOCATION_REASON_MANUAL) { | |
| 241 gcm_manager_->RegisterWithGCM(); | |
| 242 } else { | |
| 243 DoCryptAuthEnrollment(); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 void CryptAuthEnrollmentManager::DoCryptAuthEnrollment() { | |
| 248 if (GetUserPublicKey().empty() || GetUserPrivateKey().empty()) { | |
| 249 secure_message_delegate_->GenerateKeyPair( | |
| 250 base::Bind(&CryptAuthEnrollmentManager::OnKeyPairGenerated, | |
| 251 weak_ptr_factory_.GetWeakPtr())); | |
| 252 } else { | |
| 253 DoCryptAuthEnrollmentWithKeys(); | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 void CryptAuthEnrollmentManager::DoCryptAuthEnrollmentWithKeys() { | |
| 258 DCHECK(sync_request_); | |
| 259 cryptauth::InvocationReason invocation_reason = | |
| 260 cryptauth::INVOCATION_REASON_UNKNOWN; | |
| 261 | |
| 262 int reason_stored_in_prefs = | |
| 263 pref_service_->GetInteger(prefs::kCryptAuthEnrollmentReason); | |
| 264 | |
| 265 if (cryptauth::InvocationReason_IsValid(reason_stored_in_prefs) && | |
| 266 reason_stored_in_prefs != cryptauth::INVOCATION_REASON_UNKNOWN) { | |
| 267 invocation_reason = | |
| 268 static_cast<cryptauth::InvocationReason>(reason_stored_in_prefs); | |
| 269 } else if (GetLastEnrollmentTime().is_null()) { | |
| 270 invocation_reason = cryptauth::INVOCATION_REASON_INITIALIZATION; | |
| 271 } else if (!IsEnrollmentValid()) { | |
| 272 invocation_reason = cryptauth::INVOCATION_REASON_EXPIRATION; | |
| 273 } else if (scheduler_->GetStrategy() == | |
| 274 SyncScheduler::Strategy::PERIODIC_REFRESH) { | |
| 275 invocation_reason = cryptauth::INVOCATION_REASON_PERIODIC; | |
| 276 } else if (scheduler_->GetStrategy() == | |
| 277 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY) { | |
| 278 invocation_reason = cryptauth::INVOCATION_REASON_FAILURE_RECOVERY; | |
| 279 } | |
| 280 | |
| 281 // Fill in the current GCM registration id before enrolling, and explicitly | |
| 282 // make sure that the software package is the same as the GCM app id. | |
| 283 cryptauth::GcmDeviceInfo device_info(device_info_); | |
| 284 device_info.set_gcm_registration_id(gcm_manager_->GetRegistrationId()); | |
| 285 device_info.set_device_software_package(kDeviceSoftwarePackage); | |
| 286 | |
| 287 std::string public_key_b64; | |
| 288 base::Base64UrlEncode(GetUserPublicKey(), | |
| 289 base::Base64UrlEncodePolicy::INCLUDE_PADDING, | |
| 290 &public_key_b64); | |
| 291 PA_LOG(INFO) << "Making enrollment:\n" | |
| 292 << " public_key: " << public_key_b64 << "\n" | |
| 293 << " invocation_reason: " << invocation_reason << "\n" | |
| 294 << " gcm_registration_id: " | |
| 295 << device_info.gcm_registration_id(); | |
| 296 | |
| 297 cryptauth_enroller_ = enroller_factory_->CreateInstance(); | |
| 298 cryptauth_enroller_->Enroll( | |
| 299 GetUserPublicKey(), GetUserPrivateKey(), device_info, invocation_reason, | |
| 300 base::Bind(&CryptAuthEnrollmentManager::OnEnrollmentFinished, | |
| 301 weak_ptr_factory_.GetWeakPtr())); | |
| 302 } | |
| 303 | |
| 304 } // namespace proximity_auth | |
| OLD | NEW |