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 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);
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
,
73 registry
->RegisterStringPref(prefs::kCryptAuthEnrollmentUserPrivateKey
,
77 void CryptAuthEnrollmentManager::Start() {
78 gcm_manager_
->AddObserver(this);
80 bool is_recovering_from_failure
=
81 pref_service_
->GetBoolean(
82 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
) ||
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
,
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
) {
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
,
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
),
170 PA_LOG(ERROR
) << "Invalid public key stored in user prefs.";
171 return std::string();
176 std::string
CryptAuthEnrollmentManager::GetUserPrivateKey() {
177 std::string private_key
;
178 if (!Base64UrlDecode(
179 pref_service_
->GetString(prefs::kCryptAuthEnrollmentUserPrivateKey
),
181 PA_LOG(ERROR
) << "Invalid private key stored in user prefs.";
182 return std::string();
187 void CryptAuthEnrollmentManager::OnGCMRegistrationResult(bool success
) {
191 PA_LOG(INFO
) << "GCM registration for CryptAuth Enrollment completed: "
194 DoCryptAuthEnrollment();
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
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
,
211 pref_service_
->SetString(prefs::kCryptAuthEnrollmentUserPrivateKey
,
213 DoCryptAuthEnrollment();
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();
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()));
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
) {
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