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/memory/weak_ptr.h"
8 #include "base/prefs/testing_pref_service.h"
9 #include "base/test/simple_test_clock.h"
10 #include "base/time/clock.h"
11 #include "base/time/time.h"
12 #include "components/proximity_auth/cryptauth/cryptauth_enroller.h"
13 #include "components/proximity_auth/cryptauth/fake_cryptauth_gcm_manager.h"
14 #include "components/proximity_auth/cryptauth/mock_sync_scheduler.h"
15 #include "components/proximity_auth/cryptauth/pref_names.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
20 using ::testing::NiceMock
;
21 using ::testing::Return
;
22 using ::testing::SaveArg
;
24 namespace proximity_auth
{
28 // The GCM registration id from a successful registration.
29 const char kGCMRegistrationId
[] = "new gcm registration id";
31 // The user's persistent key pair identifying the local device.
32 const char kUserPublicKey
[] = "user public key";
33 const char kUserPrivateKey
[] = "user private key";
35 // The initial "Now" time for testing.
36 const double kInitialTimeNowSeconds
= 20000000;
38 // A later "Now" time for testing.
39 const double kLaterTimeNow
= kInitialTimeNowSeconds
+ 30;
41 // The timestamp of a last successful enrollment that is still valid.
42 const double kLastEnrollmentTimeSeconds
=
43 kInitialTimeNowSeconds
- (60 * 60 * 24 * 15);
45 // The timestamp of a last successful enrollment that is expired.
46 const double kLastExpiredEnrollmentTimeSeconds
=
47 kInitialTimeNowSeconds
- (60 * 60 * 24 * 100);
49 // Mocks out the actual enrollment flow.
50 class MockCryptAuthEnroller
: public CryptAuthEnroller
{
52 MockCryptAuthEnroller() {}
53 ~MockCryptAuthEnroller() override
{}
56 void(const std::string
& user_public_key
,
57 const std::string
& user_private_key
,
58 const cryptauth::GcmDeviceInfo
& device_info
,
59 cryptauth::InvocationReason invocation_reason
,
60 const EnrollmentFinishedCallback
& callback
));
63 DISALLOW_COPY_AND_ASSIGN(MockCryptAuthEnroller
);
66 // Creates MockCryptAuthEnroller instances, and allows expecations to be set
67 // before they are returned.
68 class MockCryptAuthEnrollerFactory
: public CryptAuthEnrollerFactory
{
70 MockCryptAuthEnrollerFactory()
71 : next_cryptauth_enroller_(new NiceMock
<MockCryptAuthEnroller
>()) {}
72 ~MockCryptAuthEnrollerFactory() override
{}
74 // CryptAuthEnrollerFactory:
75 scoped_ptr
<CryptAuthEnroller
> CreateInstance() override
{
76 auto passed_cryptauth_enroller
= next_cryptauth_enroller_
.Pass();
77 next_cryptauth_enroller_
.reset(new NiceMock
<MockCryptAuthEnroller
>());
78 return passed_cryptauth_enroller
.Pass();
81 MockCryptAuthEnroller
* next_cryptauth_enroller() {
82 return next_cryptauth_enroller_
.get();
86 // Stores the next CryptAuthEnroller to be created.
87 // Ownership is passed to the caller of |CreateInstance()|.
88 scoped_ptr
<MockCryptAuthEnroller
> next_cryptauth_enroller_
;
90 DISALLOW_COPY_AND_ASSIGN(MockCryptAuthEnrollerFactory
);
93 // Harness for testing CryptAuthEnrollmentManager.
94 class TestCryptAuthEnrollmentManager
: public CryptAuthEnrollmentManager
{
96 TestCryptAuthEnrollmentManager(
97 scoped_ptr
<base::Clock
> clock
,
98 scoped_ptr
<CryptAuthEnrollerFactory
> enroller_factory
,
99 const cryptauth::GcmDeviceInfo
& device_info
,
100 CryptAuthGCMManager
* gcm_manager
,
101 PrefService
* pref_service
)
102 : CryptAuthEnrollmentManager(clock
.Pass(),
103 enroller_factory
.Pass(),
109 scoped_sync_scheduler_(new NiceMock
<MockSyncScheduler
>()),
110 weak_sync_scheduler_factory_(scoped_sync_scheduler_
.get()) {}
112 ~TestCryptAuthEnrollmentManager() override
{}
114 scoped_ptr
<SyncScheduler
> CreateSyncScheduler() override
{
115 EXPECT_TRUE(scoped_sync_scheduler_
);
116 return scoped_sync_scheduler_
.Pass();
119 base::WeakPtr
<MockSyncScheduler
> GetSyncScheduler() {
120 return weak_sync_scheduler_factory_
.GetWeakPtr();
124 // Ownership is passed to |CryptAuthEnrollmentManager| super class when
125 // |CreateSyncScheduler()| is called.
126 scoped_ptr
<MockSyncScheduler
> scoped_sync_scheduler_
;
128 // Stores the pointer of |scoped_sync_scheduler_| after ownership is passed to
130 // This should be safe because the life-time this SyncScheduler will always be
131 // within the life of the TestCryptAuthEnrollmentManager object.
132 base::WeakPtrFactory
<MockSyncScheduler
> weak_sync_scheduler_factory_
;
134 DISALLOW_COPY_AND_ASSIGN(TestCryptAuthEnrollmentManager
);
139 class ProximityAuthCryptAuthEnrollmentManagerTest
140 : public testing::Test
,
141 public CryptAuthEnrollmentManager::Observer
{
143 ProximityAuthCryptAuthEnrollmentManagerTest()
144 : clock_(new base::SimpleTestClock()),
145 enroller_factory_(new MockCryptAuthEnrollerFactory()),
146 gcm_manager_(kGCMRegistrationId
),
147 enrollment_manager_(make_scoped_ptr(clock_
),
148 make_scoped_ptr(enroller_factory_
),
154 void SetUp() override
{
155 clock_
->SetNow(base::Time::FromDoubleT(kInitialTimeNowSeconds
));
156 enrollment_manager_
.AddObserver(this);
158 CryptAuthEnrollmentManager::RegisterPrefs(pref_service_
.registry());
159 pref_service_
.SetUserPref(
160 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
,
161 new base::FundamentalValue(false));
162 pref_service_
.SetUserPref(
163 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds
,
164 new base::FundamentalValue(kLastEnrollmentTimeSeconds
));
165 pref_service_
.SetUserPref(
166 prefs::kCryptAuthEnrollmentReason
,
167 new base::FundamentalValue(cryptauth::INVOCATION_REASON_UNKNOWN
));
169 ON_CALL(*sync_scheduler(), GetStrategy())
170 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
173 void TearDown() override
{ enrollment_manager_
.RemoveObserver(this); }
175 // CryptAuthEnrollmentManager::Observer:
176 void OnEnrollmentStarted() override
{ OnEnrollmentStartedProxy(); }
178 void OnEnrollmentFinished(bool success
) override
{
179 // Simulate the scheduler changing strategies based on success or failure.
180 SyncScheduler::Strategy new_strategy
=
181 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
;
182 ON_CALL(*sync_scheduler(), GetStrategy())
183 .WillByDefault(Return(new_strategy
));
185 OnEnrollmentFinishedProxy(success
);
188 MOCK_METHOD0(OnEnrollmentStartedProxy
, void());
189 MOCK_METHOD1(OnEnrollmentFinishedProxy
, void(bool success
));
191 // Simulates firing the SyncScheduler to trigger an enrollment attempt.
192 CryptAuthEnroller::EnrollmentFinishedCallback
FireSchedulerForEnrollment(
193 cryptauth::InvocationReason expected_invocation_reason
) {
194 CryptAuthEnroller::EnrollmentFinishedCallback completion_callback
;
195 EXPECT_CALL(*next_cryptauth_enroller(),
196 Enroll(kUserPublicKey
, kUserPrivateKey
, _
,
197 expected_invocation_reason
, _
))
198 .WillOnce(SaveArg
<4>(&completion_callback
));
200 auto sync_request
= make_scoped_ptr(
201 new SyncScheduler::SyncRequest(enrollment_manager_
.GetSyncScheduler()));
202 EXPECT_CALL(*this, OnEnrollmentStartedProxy());
204 SyncScheduler::Delegate
* delegate
=
205 static_cast<SyncScheduler::Delegate
*>(&enrollment_manager_
);
206 delegate
->OnSyncRequested(sync_request
.Pass());
208 return completion_callback
;
211 MockSyncScheduler
* sync_scheduler() {
212 return enrollment_manager_
.GetSyncScheduler().get();
215 MockCryptAuthEnroller
* next_cryptauth_enroller() {
216 return enroller_factory_
->next_cryptauth_enroller();
219 // Owned by |enrollment_manager_|.
220 base::SimpleTestClock
* clock_
;
222 // Owned by |enrollment_manager_|.
223 MockCryptAuthEnrollerFactory
* enroller_factory_
;
225 cryptauth::GcmDeviceInfo device_info_
;
227 TestingPrefServiceSimple pref_service_
;
229 FakeCryptAuthGCMManager gcm_manager_
;
231 TestCryptAuthEnrollmentManager enrollment_manager_
;
233 DISALLOW_COPY_AND_ASSIGN(ProximityAuthCryptAuthEnrollmentManagerTest
);
236 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, RegisterPrefs
) {
237 TestingPrefServiceSimple pref_service
;
238 CryptAuthEnrollmentManager::RegisterPrefs(pref_service
.registry());
239 EXPECT_TRUE(pref_service
.FindPreference(
240 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds
));
241 EXPECT_TRUE(pref_service
.FindPreference(
242 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
));
243 EXPECT_TRUE(pref_service
.FindPreference(prefs::kCryptAuthEnrollmentReason
));
246 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, GetEnrollmentState
) {
247 enrollment_manager_
.Start();
249 ON_CALL(*sync_scheduler(), GetStrategy())
250 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
251 EXPECT_FALSE(enrollment_manager_
.IsRecoveringFromFailure());
253 ON_CALL(*sync_scheduler(), GetStrategy())
254 .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
));
255 EXPECT_TRUE(enrollment_manager_
.IsRecoveringFromFailure());
257 base::TimeDelta time_to_next_sync
= base::TimeDelta::FromMinutes(60);
258 ON_CALL(*sync_scheduler(), GetTimeToNextSync())
259 .WillByDefault(Return(time_to_next_sync
));
260 EXPECT_EQ(time_to_next_sync
, enrollment_manager_
.GetTimeToNextAttempt());
262 ON_CALL(*sync_scheduler(), GetSyncState())
263 .WillByDefault(Return(SyncScheduler::SyncState::SYNC_IN_PROGRESS
));
264 EXPECT_TRUE(enrollment_manager_
.IsEnrollmentInProgress());
266 ON_CALL(*sync_scheduler(), GetSyncState())
267 .WillByDefault(Return(SyncScheduler::SyncState::WAITING_FOR_REFRESH
));
268 EXPECT_FALSE(enrollment_manager_
.IsEnrollmentInProgress());
271 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, InitWithDefaultPrefs
) {
272 scoped_ptr
<base::SimpleTestClock
> clock(new base::SimpleTestClock());
273 clock
->SetNow(base::Time::FromDoubleT(kInitialTimeNowSeconds
));
274 base::TimeDelta elapsed_time
= clock
->Now() - base::Time::FromDoubleT(0);
276 TestingPrefServiceSimple pref_service
;
277 CryptAuthEnrollmentManager::RegisterPrefs(pref_service
.registry());
279 TestCryptAuthEnrollmentManager
enrollment_manager(
280 clock
.Pass(), make_scoped_ptr(new MockCryptAuthEnrollerFactory()),
281 device_info_
, &gcm_manager_
, &pref_service
);
284 *enrollment_manager
.GetSyncScheduler(),
285 Start(elapsed_time
, SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
));
286 enrollment_manager
.Start();
288 EXPECT_FALSE(enrollment_manager
.IsEnrollmentValid());
289 EXPECT_TRUE(enrollment_manager
.GetLastEnrollmentTime().is_null());
292 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, InitWithExistingPrefs
) {
295 Start(clock_
->Now() - base::Time::FromDoubleT(kLastEnrollmentTimeSeconds
),
296 SyncScheduler::Strategy::PERIODIC_REFRESH
));
298 enrollment_manager_
.Start();
299 EXPECT_TRUE(enrollment_manager_
.IsEnrollmentValid());
300 EXPECT_EQ(base::Time::FromDoubleT(kLastEnrollmentTimeSeconds
),
301 enrollment_manager_
.GetLastEnrollmentTime());
304 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, InitWithExpiredEnrollment
) {
305 pref_service_
.SetUserPref(
306 prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds
,
307 new base::FundamentalValue(kLastExpiredEnrollmentTimeSeconds
));
309 EXPECT_CALL(*sync_scheduler(),
310 Start(clock_
->Now() - base::Time::FromDoubleT(
311 kLastExpiredEnrollmentTimeSeconds
),
312 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
));
314 enrollment_manager_
.Start();
315 EXPECT_FALSE(enrollment_manager_
.IsEnrollmentValid());
316 EXPECT_EQ(base::Time::FromDoubleT(kLastExpiredEnrollmentTimeSeconds
),
317 enrollment_manager_
.GetLastEnrollmentTime());
320 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
,
321 EnrollmentSucceedsForFirstTime
) {
322 pref_service_
.ClearPref(prefs::kCryptAuthEnrollmentLastEnrollmentTimeSeconds
);
323 enrollment_manager_
.Start();
324 EXPECT_FALSE(enrollment_manager_
.IsEnrollmentValid());
326 auto completion_callback
=
327 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_INITIALIZATION
);
328 ASSERT_FALSE(completion_callback
.is_null());
330 clock_
->SetNow(base::Time::FromDoubleT(kLaterTimeNow
));
331 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
332 completion_callback
.Run(true);
334 EXPECT_EQ(clock_
->Now(), enrollment_manager_
.GetLastEnrollmentTime());
335 EXPECT_TRUE(enrollment_manager_
.IsEnrollmentValid());
338 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, ForceEnrollment
) {
339 enrollment_manager_
.Start();
341 EXPECT_CALL(*sync_scheduler(), ForceSync());
342 enrollment_manager_
.ForceEnrollmentNow(cryptauth::INVOCATION_REASON_MANUAL
);
344 auto completion_callback
=
345 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_MANUAL
);
347 clock_
->SetNow(base::Time::FromDoubleT(kLaterTimeNow
));
348 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
349 completion_callback
.Run(true);
350 EXPECT_EQ(clock_
->Now(), enrollment_manager_
.GetLastEnrollmentTime());
353 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
,
354 EnrollmentFailsThenSucceeds
) {
355 enrollment_manager_
.Start();
356 base::Time old_enrollment_time
= enrollment_manager_
.GetLastEnrollmentTime();
358 // The first periodic enrollment fails.
359 ON_CALL(*sync_scheduler(), GetStrategy())
360 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
361 auto completion_callback
=
362 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_PERIODIC
);
363 clock_
->SetNow(base::Time::FromDoubleT(kLaterTimeNow
));
364 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(false));
365 completion_callback
.Run(false);
366 EXPECT_EQ(old_enrollment_time
, enrollment_manager_
.GetLastEnrollmentTime());
367 EXPECT_TRUE(pref_service_
.GetBoolean(
368 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
));
370 // The second recovery enrollment succeeds.
371 ON_CALL(*sync_scheduler(), GetStrategy())
372 .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
));
373 completion_callback
=
374 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_FAILURE_RECOVERY
);
375 clock_
->SetNow(base::Time::FromDoubleT(kLaterTimeNow
+ 30));
376 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
377 completion_callback
.Run(true);
378 EXPECT_EQ(clock_
->Now(), enrollment_manager_
.GetLastEnrollmentTime());
379 EXPECT_FALSE(pref_service_
.GetBoolean(
380 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
));
383 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
,
384 EnrollWithoutGCMRegistrationId
) {
385 // Initialize |enrollment_manager_|.
386 ON_CALL(*sync_scheduler(), GetStrategy())
387 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
388 gcm_manager_
.set_registration_id(std::string());
389 enrollment_manager_
.Start();
391 // Trigger a sync request.
392 EXPECT_CALL(*this, OnEnrollmentStartedProxy());
393 auto sync_request
= make_scoped_ptr(
394 new SyncScheduler::SyncRequest(enrollment_manager_
.GetSyncScheduler()));
395 static_cast<SyncScheduler::Delegate
*>(&enrollment_manager_
)
396 ->OnSyncRequested(sync_request
.Pass());
398 // Complete GCM registration successfully.
399 CryptAuthEnroller::EnrollmentFinishedCallback enrollment_callback
;
400 EXPECT_CALL(*next_cryptauth_enroller(),
401 Enroll(kUserPublicKey
, kUserPrivateKey
, _
,
402 cryptauth::INVOCATION_REASON_PERIODIC
, _
))
403 .WillOnce(SaveArg
<4>(&enrollment_callback
));
404 ASSERT_TRUE(gcm_manager_
.registration_in_progress());
405 gcm_manager_
.CompleteRegistration(kGCMRegistrationId
);
407 // Complete CryptAuth enrollment.
408 ASSERT_FALSE(enrollment_callback
.is_null());
409 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
410 enrollment_callback
.Run(true);
413 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, GCMRegistrationFails
) {
414 // Initialize |enrollment_manager_|.
415 ON_CALL(*sync_scheduler(), GetStrategy())
416 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
417 gcm_manager_
.set_registration_id(std::string());
418 enrollment_manager_
.Start();
420 // Trigger a sync request.
421 EXPECT_CALL(*this, OnEnrollmentStartedProxy());
422 auto sync_request
= make_scoped_ptr(
423 new SyncScheduler::SyncRequest(enrollment_manager_
.GetSyncScheduler()));
424 static_cast<SyncScheduler::Delegate
*>(&enrollment_manager_
)
425 ->OnSyncRequested(sync_request
.Pass());
427 // Complete GCM registration with failure.
428 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(false));
429 gcm_manager_
.CompleteRegistration(std::string());
432 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, ReenrollOnGCMPushMessage
) {
433 enrollment_manager_
.Start();
435 // Simulate receiving a GCM push message, forcing the device to re-enroll.
436 gcm_manager_
.PushReenrollMessage();
437 auto completion_callback
=
438 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_SERVER_INITIATED
);
440 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
441 completion_callback
.Run(true);
444 } // namespace proximity_auth