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
{
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
40 const char kDeviceSoftwarePackage
[] = "com.google.chrome.cryptauth";
44 CryptAuthEnrollmentManager::CryptAuthEnrollmentManager(
45 scoped_ptr
<base::Clock
> clock
,
46 scoped_ptr
<CryptAuthEnrollerFactory
> enroller_factory
,
47 const std::string
& user_public_key
,
48 const std::string
& user_private_key
,
49 const cryptauth::GcmDeviceInfo
& device_info
,
50 CryptAuthGCMManager
* gcm_manager
,
51 PrefService
* pref_service
)
52 : clock_(clock
.Pass()),
53 enroller_factory_(enroller_factory
.Pass()),
54 user_public_key_(user_public_key
),
55 user_private_key_(user_private_key
),
56 device_info_(device_info
),
57 gcm_manager_(gcm_manager
),
58 pref_service_(pref_service
),
59 weak_ptr_factory_(this) {}
61 CryptAuthEnrollmentManager::~CryptAuthEnrollmentManager() {
62 gcm_manager_
->RemoveObserver(this);
66 void CryptAuthEnrollmentManager::RegisterPrefs(PrefRegistrySimple
* registry
) {
67 registry
->RegisterBooleanPref(
68 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
, false);
69 registry
->RegisterDoublePref(
70 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds
, 0.0);
71 registry
->RegisterIntegerPref(prefs::kCryptAuthEnrollmentReason
,
72 cryptauth::INVOCATION_REASON_UNKNOWN
);
75 void CryptAuthEnrollmentManager::Start() {
76 gcm_manager_
->AddObserver(this);
78 bool is_recovering_from_failure
=
79 pref_service_
->GetBoolean(
80 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
) ||
83 base::Time last_successful_enrollment
= GetLastEnrollmentTime();
84 base::TimeDelta elapsed_time_since_last_sync
=
85 clock_
->Now() - last_successful_enrollment
;
87 scheduler_
= CreateSyncScheduler();
88 scheduler_
->Start(elapsed_time_since_last_sync
,
89 is_recovering_from_failure
90 ? SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
91 : SyncScheduler::Strategy::PERIODIC_REFRESH
);
94 void CryptAuthEnrollmentManager::AddObserver(Observer
* observer
) {
95 observers_
.AddObserver(observer
);
98 void CryptAuthEnrollmentManager::RemoveObserver(Observer
* observer
) {
99 observers_
.RemoveObserver(observer
);
102 void CryptAuthEnrollmentManager::ForceEnrollmentNow(
103 cryptauth::InvocationReason invocation_reason
) {
104 // We store the invocation reason in a preference so that it can persist
105 // across browser restarts. If the sync fails, the next retry should still use
106 // this original reason instead of INVOCATION_REASON_FAILURE_RECOVERY.
107 pref_service_
->SetInteger(prefs::kCryptAuthEnrollmentReason
,
109 scheduler_
->ForceSync();
112 bool CryptAuthEnrollmentManager::IsEnrollmentValid() const {
113 base::Time last_enrollment_time
= GetLastEnrollmentTime();
114 return !last_enrollment_time
.is_null() &&
115 (clock_
->Now() - last_enrollment_time
) <
116 base::TimeDelta::FromDays(kValidEnrollmentPeriodDays
);
119 base::Time
CryptAuthEnrollmentManager::GetLastEnrollmentTime() const {
120 return base::Time::FromDoubleT(pref_service_
->GetDouble(
121 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds
));
124 base::TimeDelta
CryptAuthEnrollmentManager::GetTimeToNextAttempt() const {
125 return scheduler_
->GetTimeToNextSync();
128 bool CryptAuthEnrollmentManager::IsEnrollmentInProgress() const {
129 return scheduler_
->GetSyncState() ==
130 SyncScheduler::SyncState::SYNC_IN_PROGRESS
;
133 bool CryptAuthEnrollmentManager::IsRecoveringFromFailure() const {
134 return scheduler_
->GetStrategy() ==
135 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
;
138 void CryptAuthEnrollmentManager::OnEnrollmentFinished(bool success
) {
140 pref_service_
->SetDouble(
141 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds
,
142 clock_
->Now().ToDoubleT());
143 pref_service_
->SetInteger(prefs::kCryptAuthEnrollmentReason
,
144 cryptauth::INVOCATION_REASON_UNKNOWN
);
147 pref_service_
->SetBoolean(prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
,
150 sync_request_
->OnDidComplete(success
);
151 cryptauth_enroller_
.reset();
152 sync_request_
.reset();
153 FOR_EACH_OBSERVER(Observer
, observers_
, OnEnrollmentFinished(success
));
156 scoped_ptr
<SyncScheduler
> CryptAuthEnrollmentManager::CreateSyncScheduler() {
157 return make_scoped_ptr(new SyncSchedulerImpl(
158 this, base::TimeDelta::FromDays(kEnrollmentRefreshPeriodDays
),
159 base::TimeDelta::FromMinutes(kEnrollmentBaseRecoveryPeriodMinutes
),
160 kEnrollmentMaxJitterRatio
, "CryptAuth Enrollment"));
163 void CryptAuthEnrollmentManager::OnGCMRegistrationResult(bool success
) {
167 PA_LOG(INFO
) << "GCM registration for CryptAuth Enrollment completed: "
170 DoCryptAuthEnrollment();
172 OnEnrollmentFinished(false);
175 void CryptAuthEnrollmentManager::OnReenrollMessage() {
176 ForceEnrollmentNow(cryptauth::INVOCATION_REASON_SERVER_INITIATED
);
179 void CryptAuthEnrollmentManager::OnSyncRequested(
180 scoped_ptr
<SyncScheduler::SyncRequest
> sync_request
) {
181 FOR_EACH_OBSERVER(Observer
, observers_
, OnEnrollmentStarted());
183 sync_request_
= sync_request
.Pass();
185 if (gcm_manager_
->GetRegistrationId().empty() ||
186 pref_service_
->GetInteger(prefs::kCryptAuthEnrollmentReason
) ==
187 cryptauth::INVOCATION_REASON_MANUAL
) {
188 gcm_manager_
->RegisterWithGCM();
190 DoCryptAuthEnrollment();
194 void CryptAuthEnrollmentManager::DoCryptAuthEnrollment() {
195 DCHECK(sync_request_
);
196 cryptauth::InvocationReason invocation_reason
=
197 cryptauth::INVOCATION_REASON_UNKNOWN
;
199 int reason_stored_in_prefs
=
200 pref_service_
->GetInteger(prefs::kCryptAuthEnrollmentReason
);
202 if (cryptauth::InvocationReason_IsValid(reason_stored_in_prefs
) &&
203 reason_stored_in_prefs
!= cryptauth::INVOCATION_REASON_UNKNOWN
) {
205 static_cast<cryptauth::InvocationReason
>(reason_stored_in_prefs
);
206 } else if (GetLastEnrollmentTime().is_null()) {
207 invocation_reason
= cryptauth::INVOCATION_REASON_INITIALIZATION
;
208 } else if (!IsEnrollmentValid()) {
209 invocation_reason
= cryptauth::INVOCATION_REASON_EXPIRATION
;
210 } else if (scheduler_
->GetStrategy() ==
211 SyncScheduler::Strategy::PERIODIC_REFRESH
) {
212 invocation_reason
= cryptauth::INVOCATION_REASON_PERIODIC
;
213 } else if (scheduler_
->GetStrategy() ==
214 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
) {
215 invocation_reason
= cryptauth::INVOCATION_REASON_FAILURE_RECOVERY
;
218 // Fill in the current GCM registration id before enrolling, and explicitly
219 // make sure that the software package is the same as the GCM app id.
220 cryptauth::GcmDeviceInfo
device_info(device_info_
);
221 device_info
.set_gcm_registration_id(gcm_manager_
->GetRegistrationId());
222 device_info
.set_device_software_package(kDeviceSoftwarePackage
);
224 std::string public_key_b64
;
225 Base64UrlEncode(user_public_key_
, &public_key_b64
);
226 PA_LOG(INFO
) << "Making enrollment:\n"
227 << " public_key: " << public_key_b64
<< "\n"
228 << " invocation_reason: " << invocation_reason
<< "\n"
229 << " gcm_registration_id: "
230 << device_info
.gcm_registration_id();
232 cryptauth_enroller_
= enroller_factory_
->CreateInstance();
233 cryptauth_enroller_
->Enroll(
234 user_public_key_
, user_private_key_
, device_info
, invocation_reason
,
235 base::Bind(&CryptAuthEnrollmentManager::OnEnrollmentFinished
,
236 weak_ptr_factory_
.GetWeakPtr()));
239 } // namespace proximity_auth