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(
343 cryptauth::INVOCATION_REASON_SERVER_INITIATED
);
345 auto completion_callback
=
346 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_SERVER_INITIATED
);
348 clock_
->SetNow(base::Time::FromDoubleT(kLaterTimeNow
));
349 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
350 completion_callback
.Run(true);
351 EXPECT_EQ(clock_
->Now(), enrollment_manager_
.GetLastEnrollmentTime());
354 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
,
355 EnrollmentFailsThenSucceeds
) {
356 enrollment_manager_
.Start();
357 base::Time old_enrollment_time
= enrollment_manager_
.GetLastEnrollmentTime();
359 // The first periodic enrollment fails.
360 ON_CALL(*sync_scheduler(), GetStrategy())
361 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
362 auto completion_callback
=
363 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_PERIODIC
);
364 clock_
->SetNow(base::Time::FromDoubleT(kLaterTimeNow
));
365 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(false));
366 completion_callback
.Run(false);
367 EXPECT_EQ(old_enrollment_time
, enrollment_manager_
.GetLastEnrollmentTime());
368 EXPECT_TRUE(pref_service_
.GetBoolean(
369 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
));
371 // The second recovery enrollment succeeds.
372 ON_CALL(*sync_scheduler(), GetStrategy())
373 .WillByDefault(Return(SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
));
374 completion_callback
=
375 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_FAILURE_RECOVERY
);
376 clock_
->SetNow(base::Time::FromDoubleT(kLaterTimeNow
+ 30));
377 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
378 completion_callback
.Run(true);
379 EXPECT_EQ(clock_
->Now(), enrollment_manager_
.GetLastEnrollmentTime());
380 EXPECT_FALSE(pref_service_
.GetBoolean(
381 prefs::kCryptAuthEnrollmentIsRecoveringFromFailure
));
384 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
,
385 EnrollWithoutGCMRegistrationId
) {
386 // Initialize |enrollment_manager_|.
387 ON_CALL(*sync_scheduler(), GetStrategy())
388 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
389 gcm_manager_
.set_registration_id(std::string());
390 enrollment_manager_
.Start();
392 // Trigger a sync request.
393 EXPECT_CALL(*this, OnEnrollmentStartedProxy());
394 auto sync_request
= make_scoped_ptr(
395 new SyncScheduler::SyncRequest(enrollment_manager_
.GetSyncScheduler()));
396 static_cast<SyncScheduler::Delegate
*>(&enrollment_manager_
)
397 ->OnSyncRequested(sync_request
.Pass());
399 // Complete GCM registration successfully.
400 CryptAuthEnroller::EnrollmentFinishedCallback enrollment_callback
;
401 EXPECT_CALL(*next_cryptauth_enroller(),
402 Enroll(kUserPublicKey
, kUserPrivateKey
, _
,
403 cryptauth::INVOCATION_REASON_PERIODIC
, _
))
404 .WillOnce(SaveArg
<4>(&enrollment_callback
));
405 ASSERT_TRUE(gcm_manager_
.registration_in_progress());
406 gcm_manager_
.CompleteRegistration(kGCMRegistrationId
);
408 // Complete CryptAuth enrollment.
409 ASSERT_FALSE(enrollment_callback
.is_null());
410 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
411 enrollment_callback
.Run(true);
414 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, GCMRegistrationFails
) {
415 // Initialize |enrollment_manager_|.
416 ON_CALL(*sync_scheduler(), GetStrategy())
417 .WillByDefault(Return(SyncScheduler::Strategy::PERIODIC_REFRESH
));
418 gcm_manager_
.set_registration_id(std::string());
419 enrollment_manager_
.Start();
421 // Trigger a sync request.
422 EXPECT_CALL(*this, OnEnrollmentStartedProxy());
423 auto sync_request
= make_scoped_ptr(
424 new SyncScheduler::SyncRequest(enrollment_manager_
.GetSyncScheduler()));
425 static_cast<SyncScheduler::Delegate
*>(&enrollment_manager_
)
426 ->OnSyncRequested(sync_request
.Pass());
428 // Complete GCM registration with failure.
429 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(false));
430 gcm_manager_
.CompleteRegistration(std::string());
433 TEST_F(ProximityAuthCryptAuthEnrollmentManagerTest
, ReenrollOnGCMPushMessage
) {
434 enrollment_manager_
.Start();
436 // Simulate receiving a GCM push message, forcing the device to re-enroll.
437 gcm_manager_
.PushReenrollMessage();
438 auto completion_callback
=
439 FireSchedulerForEnrollment(cryptauth::INVOCATION_REASON_SERVER_INITIATED
);
441 EXPECT_CALL(*this, OnEnrollmentFinishedProxy(true));
442 completion_callback
.Run(true);
445 } // namespace proximity_auth