1 // Copyright 2013 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.
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "chrome/browser/chromeos/attestation/attestation_signed_data.pb.h"
12 #include "chrome/browser/chromeos/attestation/fake_certificate.h"
13 #include "chrome/browser/chromeos/attestation/platform_verification_flow.h"
14 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
15 #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
16 #include "chrome/browser/profiles/profile_impl.h"
17 #include "chrome/common/pref_names.h"
18 #include "chromeos/attestation/mock_attestation_flow.h"
19 #include "chromeos/cryptohome/mock_async_method_caller.h"
20 #include "chromeos/dbus/fake_cryptohome_client.h"
21 #include "chromeos/settings/cros_settings_names.h"
22 #include "content/public/test/test_browser_thread.h"
23 #include "testing/gtest/include/gtest/gtest.h"
27 using testing::Invoke
;
28 using testing::Return
;
29 using testing::SetArgumentPointee
;
30 using testing::StrictMock
;
31 using testing::WithArgs
;
34 namespace attestation
{
38 const char kTestID
[] = "test_id";
39 const char kTestChallenge
[] = "test_challenge";
40 const char kTestSignedData
[] = "test_challenge_with_salt";
41 const char kTestSignature
[] = "test_signature";
42 const char kTestCertificate
[] = "test_certificate";
43 const char kTestEmail
[] = "test_email@chromium.org";
44 const char kTestURL
[] = "http://mytestdomain/test";
46 class FakeDelegate
: public PlatformVerificationFlow::Delegate
{
50 is_permitted_by_user_(true),
51 is_in_supported_mode_(true) {
52 // Configure a user for the mock user manager.
53 mock_user_manager_
.SetActiveUser(kTestEmail
);
55 ~FakeDelegate() override
{}
57 const GURL
& GetURL(content::WebContents
* web_contents
) override
{
61 user_manager::User
* GetUser(content::WebContents
* web_contents
) override
{
62 return mock_user_manager_
.GetActiveUser();
65 bool IsPermittedByUser(content::WebContents
* web_contents
) override
{
66 return is_permitted_by_user_
;
69 bool IsInSupportedMode(content::WebContents
* web_contents
) override
{
70 return is_in_supported_mode_
;
73 void set_url(const GURL
& url
) {
77 void set_is_permitted_by_user(bool is_permitted_by_user
) {
78 is_permitted_by_user_
= is_permitted_by_user
;
81 void set_is_in_supported_mode(bool is_in_supported_mode
) {
82 is_in_supported_mode_
= is_in_supported_mode
;
86 MockUserManager mock_user_manager_
;
88 bool is_permitted_by_user_
;
89 bool is_in_supported_mode_
;
91 DISALLOW_COPY_AND_ASSIGN(FakeDelegate
);
94 class CustomFakeCryptohomeClient
: public FakeCryptohomeClient
{
96 CustomFakeCryptohomeClient() : call_status_(DBUS_METHOD_CALL_SUCCESS
),
97 attestation_enrolled_(true),
98 attestation_prepared_(true) {}
99 void TpmAttestationIsEnrolled(
100 const BoolDBusMethodCallback
& callback
) override
{
101 base::MessageLoop::current()->PostTask(FROM_HERE
,
104 attestation_enrolled_
));
107 void TpmAttestationIsPrepared(
108 const BoolDBusMethodCallback
& callback
) override
{
109 base::MessageLoop::current()->PostTask(FROM_HERE
,
112 attestation_prepared_
));
115 void set_call_status(DBusMethodCallStatus call_status
) {
116 call_status_
= call_status
;
119 void set_attestation_enrolled(bool attestation_enrolled
) {
120 attestation_enrolled_
= attestation_enrolled
;
123 void set_attestation_prepared(bool attestation_prepared
) {
124 attestation_prepared_
= attestation_prepared
;
128 DBusMethodCallStatus call_status_
;
129 bool attestation_enrolled_
;
130 bool attestation_prepared_
;
135 class PlatformVerificationFlowTest
: public ::testing::Test
{
137 PlatformVerificationFlowTest()
138 : ui_thread_(content::BrowserThread::UI
, &message_loop_
),
139 certificate_success_(true),
140 fake_certificate_index_(0),
141 sign_challenge_success_(true),
142 result_(PlatformVerificationFlow::INTERNAL_ERROR
) {}
145 // Create a verifier for tests to call.
146 verifier_
= new PlatformVerificationFlow(&mock_attestation_flow_
,
148 &fake_cryptohome_client_
,
151 // Create callbacks for tests to use with verifier_.
152 callback_
= base::Bind(&PlatformVerificationFlowTest::FakeChallengeCallback
,
153 base::Unretained(this));
155 settings_helper_
.ReplaceProvider(kAttestationForContentProtectionEnabled
);
156 settings_helper_
.SetBoolean(kAttestationForContentProtectionEnabled
, true);
159 void ExpectAttestationFlow() {
160 // When consent is not given or the feature is disabled, it is important
161 // that there are no calls to the attestation service. Thus, a test must
162 // explicitly expect these calls or the mocks will fail the test.
164 // Configure the mock AttestationFlow to call FakeGetCertificate.
165 EXPECT_CALL(mock_attestation_flow_
,
166 GetCertificate(PROFILE_CONTENT_PROTECTION_CERTIFICATE
,
167 kTestEmail
, kTestID
, _
, _
))
168 .WillRepeatedly(WithArgs
<4>(Invoke(
169 this, &PlatformVerificationFlowTest::FakeGetCertificate
)));
171 // Configure the mock AsyncMethodCaller to call FakeSignChallenge.
172 std::string expected_key_name
= std::string(kContentProtectionKeyPrefix
) +
173 std::string(kTestID
);
174 EXPECT_CALL(mock_async_caller_
,
175 TpmAttestationSignSimpleChallenge(KEY_USER
, kTestEmail
,
178 .WillRepeatedly(WithArgs
<4>(Invoke(
179 this, &PlatformVerificationFlowTest::FakeSignChallenge
)));
182 void FakeGetCertificate(
183 const AttestationFlow::CertificateCallback
& callback
) {
184 std::string certificate
=
185 (fake_certificate_index_
< fake_certificate_list_
.size()) ?
186 fake_certificate_list_
[fake_certificate_index_
] : kTestCertificate
;
187 base::MessageLoop::current()->PostTask(FROM_HERE
,
189 certificate_success_
,
191 ++fake_certificate_index_
;
194 void FakeSignChallenge(
195 const cryptohome::AsyncMethodCaller::DataCallback
& callback
) {
196 base::MessageLoop::current()->PostTask(
199 sign_challenge_success_
,
200 CreateFakeResponseProto()));
203 void FakeChallengeCallback(PlatformVerificationFlow::Result result
,
204 const std::string
& salt
,
205 const std::string
& signature
,
206 const std::string
& certificate
) {
208 challenge_salt_
= salt
;
209 challenge_signature_
= signature
;
210 certificate_
= certificate
;
213 std::string
CreateFakeResponseProto() {
215 pb
.set_data(kTestSignedData
);
216 pb
.set_signature(kTestSignature
);
218 CHECK(pb
.SerializeToString(&serial
));
223 base::MessageLoopForUI message_loop_
;
224 content::TestBrowserThread ui_thread_
;
225 StrictMock
<MockAttestationFlow
> mock_attestation_flow_
;
226 cryptohome::MockAsyncMethodCaller mock_async_caller_
;
227 CustomFakeCryptohomeClient fake_cryptohome_client_
;
228 FakeDelegate fake_delegate_
;
229 ScopedCrosSettingsTestHelper settings_helper_
;
230 scoped_refptr
<PlatformVerificationFlow
> verifier_
;
232 // Controls result of FakeGetCertificate.
233 bool certificate_success_
;
234 std::vector
<std::string
> fake_certificate_list_
;
235 size_t fake_certificate_index_
;
237 // Controls result of FakeSignChallenge.
238 bool sign_challenge_success_
;
240 // Callback functions and data.
241 PlatformVerificationFlow::ChallengeCallback callback_
;
242 PlatformVerificationFlow::Result result_
;
243 std::string challenge_salt_
;
244 std::string challenge_signature_
;
245 std::string certificate_
;
248 TEST_F(PlatformVerificationFlowTest
, Success
) {
249 ExpectAttestationFlow();
250 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
251 base::RunLoop().RunUntilIdle();
252 EXPECT_EQ(PlatformVerificationFlow::SUCCESS
, result_
);
253 EXPECT_EQ(kTestSignedData
, challenge_salt_
);
254 EXPECT_EQ(kTestSignature
, challenge_signature_
);
255 EXPECT_EQ(kTestCertificate
, certificate_
);
258 TEST_F(PlatformVerificationFlowTest
, NotPermittedByUser
) {
259 fake_delegate_
.set_is_permitted_by_user(false);
260 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
261 base::RunLoop().RunUntilIdle();
262 EXPECT_EQ(PlatformVerificationFlow::USER_REJECTED
, result_
);
265 TEST_F(PlatformVerificationFlowTest
, FeatureDisabledByPolicy
) {
266 settings_helper_
.SetBoolean(kAttestationForContentProtectionEnabled
, false);
267 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
268 base::RunLoop().RunUntilIdle();
269 EXPECT_EQ(PlatformVerificationFlow::POLICY_REJECTED
, result_
);
272 TEST_F(PlatformVerificationFlowTest
, NotVerified
) {
273 certificate_success_
= false;
274 ExpectAttestationFlow();
275 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
276 base::RunLoop().RunUntilIdle();
277 EXPECT_EQ(PlatformVerificationFlow::PLATFORM_NOT_VERIFIED
, result_
);
280 TEST_F(PlatformVerificationFlowTest
, ChallengeSigningError
) {
281 sign_challenge_success_
= false;
282 ExpectAttestationFlow();
283 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
284 base::RunLoop().RunUntilIdle();
285 EXPECT_EQ(PlatformVerificationFlow::INTERNAL_ERROR
, result_
);
288 TEST_F(PlatformVerificationFlowTest
, DBusFailure
) {
289 fake_cryptohome_client_
.set_call_status(DBUS_METHOD_CALL_FAILURE
);
290 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
291 base::RunLoop().RunUntilIdle();
292 EXPECT_EQ(PlatformVerificationFlow::INTERNAL_ERROR
, result_
);
295 TEST_F(PlatformVerificationFlowTest
, Timeout
) {
296 verifier_
->set_timeout_delay(base::TimeDelta::FromSeconds(0));
297 ExpectAttestationFlow();
298 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
299 base::RunLoop().RunUntilIdle();
300 EXPECT_EQ(PlatformVerificationFlow::TIMEOUT
, result_
);
303 TEST_F(PlatformVerificationFlowTest
, ExpiredCert
) {
304 ExpectAttestationFlow();
305 fake_certificate_list_
.resize(3);
306 ASSERT_TRUE(GetFakeCertificatePEM(base::TimeDelta::FromDays(-1),
307 &fake_certificate_list_
[0]));
308 ASSERT_TRUE(GetFakeCertificatePEM(base::TimeDelta::FromDays(1),
309 &fake_certificate_list_
[1]));
310 // This is the opportunistic renewal certificate. Send it back expired to test
311 // that it does not pass through the certificate expiry check again.
312 ASSERT_TRUE(GetFakeCertificatePEM(base::TimeDelta::FromDays(-1),
313 &fake_certificate_list_
[2]));
314 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
315 base::RunLoop().RunUntilIdle();
316 EXPECT_EQ(PlatformVerificationFlow::SUCCESS
, result_
);
317 EXPECT_EQ(fake_certificate_list_
[1], certificate_
);
318 // Once for the expired certificate, once for the almost expired certificate,
319 // and once for the opportunistic renewal attempt.
320 EXPECT_EQ(3ul, fake_certificate_index_
);
323 TEST_F(PlatformVerificationFlowTest
, ExpiredIntermediateCert
) {
324 ExpectAttestationFlow();
325 fake_certificate_list_
.resize(2);
326 std::string leaf_cert
;
327 ASSERT_TRUE(GetFakeCertificatePEM(base::TimeDelta::FromDays(60), &leaf_cert
));
328 std::string intermediate_cert
;
330 GetFakeCertificatePEM(base::TimeDelta::FromDays(-1), &intermediate_cert
));
331 fake_certificate_list_
[0] = leaf_cert
+ intermediate_cert
;
332 ASSERT_TRUE(GetFakeCertificatePEM(base::TimeDelta::FromDays(90),
333 &fake_certificate_list_
[1]));
334 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
335 base::RunLoop().RunUntilIdle();
336 EXPECT_EQ(PlatformVerificationFlow::SUCCESS
, result_
);
337 EXPECT_EQ(fake_certificate_list_
[1], certificate_
);
338 // Once for the expired intermediate, once for the renewal.
339 EXPECT_EQ(2ul, fake_certificate_index_
);
342 TEST_F(PlatformVerificationFlowTest
, AsyncRenewalMultipleHits
) {
343 ExpectAttestationFlow();
344 fake_certificate_list_
.resize(4);
345 ASSERT_TRUE(GetFakeCertificatePEM(base::TimeDelta::FromDays(1),
346 &fake_certificate_list_
[0]));
347 std::fill(fake_certificate_list_
.begin() + 1, fake_certificate_list_
.end(),
348 fake_certificate_list_
[0]);
349 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
350 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
351 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
352 base::RunLoop().RunUntilIdle();
353 EXPECT_EQ(PlatformVerificationFlow::SUCCESS
, result_
);
354 EXPECT_EQ(fake_certificate_list_
[0], certificate_
);
355 // Once per challenge and only one renewal.
356 EXPECT_EQ(4ul, fake_certificate_index_
);
359 TEST_F(PlatformVerificationFlowTest
, CertificateNotPEM
) {
360 ExpectAttestationFlow();
361 fake_certificate_list_
.push_back("invalid_pem");
362 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
363 base::RunLoop().RunUntilIdle();
364 EXPECT_EQ(PlatformVerificationFlow::SUCCESS
, result_
);
365 EXPECT_EQ(fake_certificate_list_
[0], certificate_
);
368 TEST_F(PlatformVerificationFlowTest
, CertificateNotX509
) {
369 ExpectAttestationFlow();
370 std::string not_x509
=
371 "-----BEGIN CERTIFICATE-----\n"
372 "Vm0wd2QyUXlWa1pOVldoVFYwZDRWVll3WkRSV1JteFZVMjA1VjFadGVEQmFWVll3WVd4YWMx"
373 "TnNiRlZXYkhCUVdWZHplRll5VGtWUwpiSEJPVWpKb1RWZFhkR0ZUTWs1eVRsWmtZUXBTYlZK"
374 "d1ZXcEtiMDFzWkZkV2JVWlVZbFpHTTFSc1dsZFZaM0JwVTBWS2RsWkdZM2hpCk1rbDRWMnhX"
375 "VkdGc1NsaFpiRnBIVGtaYVNFNVZkRmRhTTBKd1ZteGFkMVpXWkZobFIzUnBDazFXY0VoV01X"
376 "aHpZV3hLV1ZWc1ZscGkKUm5Cb1dsZDRXbVZWTlZkYVIyaFdWMFZLVlZacVFsZFRNVnBYV2ta"
377 "b2JGSXpVbGREYlVwWFYydG9WMDF1VW5aWmExcExZMnMxVjFScwpjRmdLVTBWS1dWWnRjRWRq"
378 "TWs1elYyNVNVRll5YUZkV01GWkxWbXhhVlZGc1pGUk5Wa3BJVmpKNGIyRnNTbGxWYkVKRVlr"
379 "VndWbFZ0CmVHOVdNVWw2WVVkb1dGWnNjRXhXTUZwWFpGWk9jd3BhUjJkTFdWUkNkMDVzV2to"
380 "TlZGSmFWbTFTUjFSV1ZsZFdNa3BKVVd4a1YwMUcKV2t4V01uaGhWMGRXU0dSRk9WTk5WWEJa"
381 "Vm1wR2IySXhXblJTV0hCV1lrWktSVmxZY0VkbGJGbDVDbU5GVGxkTlZtdzJWbGMxWVZkdApS"
382 "WGhqUlhSaFZucEdTRlZ0TVZOU2QzQmhVbTFPVEZkWGVGWmtNbEY0VjJ0V1UySkhVbFpVVjNS"
383 "M1pXeFdXR1ZHWkZWaVJYQmFWa2QwCk5GSkdjRFlLVFVSc1JGcDZNRGxEWnowOUNnPT0K\n"
384 "-----END CERTIFICATE-----\n";
385 fake_certificate_list_
.push_back(not_x509
);
386 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
387 base::RunLoop().RunUntilIdle();
388 EXPECT_EQ(PlatformVerificationFlow::SUCCESS
, result_
);
389 EXPECT_EQ(fake_certificate_list_
[0], certificate_
);
392 TEST_F(PlatformVerificationFlowTest
, UnsupportedMode
) {
393 fake_delegate_
.set_is_in_supported_mode(false);
394 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
395 base::RunLoop().RunUntilIdle();
396 EXPECT_EQ(PlatformVerificationFlow::PLATFORM_NOT_VERIFIED
, result_
);
399 TEST_F(PlatformVerificationFlowTest
, AttestationNotPrepared
) {
400 fake_cryptohome_client_
.set_attestation_enrolled(false);
401 fake_cryptohome_client_
.set_attestation_prepared(false);
402 verifier_
->ChallengePlatformKey(NULL
, kTestID
, kTestChallenge
, callback_
);
403 base::RunLoop().RunUntilIdle();
404 EXPECT_EQ(PlatformVerificationFlow::PLATFORM_NOT_VERIFIED
, result_
);
407 } // namespace attestation
408 } // namespace chromeos