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