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 |