platform_verification_flow_unittest.cc revision 3551c9c881056c480085172ff9840cab31610854
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.
4
5#include <string>
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "chrome/browser/chromeos/attestation/platform_verification_flow.h"
11#include "chrome/browser/chromeos/login/mock_user_manager.h"
12#include "chromeos/attestation/mock_attestation_flow.h"
13#include "chromeos/cryptohome/mock_async_method_caller.h"
14#include "chromeos/dbus/fake_cryptohome_client.h"
15#include "content/public/test/test_browser_thread.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18using testing::_;
19using testing::Invoke;
20using testing::StrictMock;
21using testing::WithArgs;
22
23namespace chromeos {
24namespace attestation {
25
26namespace {
27
28const char kTestID[] = "test_id";
29const char kTestChallenge[] = "test_challenge";
30const char kTestResponse[] = "test_challenge_response";
31const char kTestCertificate[] = "test_certificate";
32const char kTestEmail[] = "test_email@chromium.org";
33
34class FakeDelegate : public PlatformVerificationFlow::Delegate {
35 public:
36  FakeDelegate() : response_(PlatformVerificationFlow::CONSENT_RESPONSE_ALLOW),
37                   num_consent_calls_(0),
38                   attestation_disabled_(false),
39                   origin_consent_required_(false),
40                   always_ask_required_(false),
41                   update_settings_result_(true) {}
42  virtual ~FakeDelegate() {}
43
44  virtual void ShowConsentPrompt(
45      PlatformVerificationFlow::ConsentType type,
46      content::WebContents* web_contents,
47      const PlatformVerificationFlow::Delegate::ConsentCallback& callback)
48      OVERRIDE {
49    num_consent_calls_++;
50    callback.Run(response_);
51  }
52
53  virtual bool IsAttestationDisabled() OVERRIDE {
54    return attestation_disabled_;
55  }
56
57  virtual bool IsOriginConsentRequired(
58      content::WebContents* web_contents) OVERRIDE {
59    return origin_consent_required_;
60  }
61
62  virtual bool IsAlwaysAskRequired(
63      content::WebContents* web_contents) OVERRIDE {
64    return always_ask_required_;
65  }
66
67  virtual bool UpdateSettings(
68      content::WebContents* web_contents,
69      PlatformVerificationFlow::ConsentType consent_type,
70      PlatformVerificationFlow::ConsentResponse consent_response) OVERRIDE {
71    return update_settings_result_;
72  }
73
74  void set_response(PlatformVerificationFlow::ConsentResponse response) {
75    response_ = response;
76  }
77
78  int num_consent_calls() {
79    return num_consent_calls_;
80  }
81
82  void set_attestation_disabled(bool attestation_disabled) {
83    attestation_disabled_ = attestation_disabled;
84  }
85
86  void set_origin_consent_required(bool origin_consent_required) {
87    origin_consent_required_ = origin_consent_required;
88  }
89
90  void set_always_ask_required(bool always_ask_required) {
91    always_ask_required_ = always_ask_required;
92  }
93
94  void set_update_settings_result(bool update_settings_result) {
95    update_settings_result_ = update_settings_result;
96  }
97
98 private:
99  PlatformVerificationFlow::ConsentResponse response_;
100  int num_consent_calls_;
101  bool attestation_disabled_;
102  bool origin_consent_required_;
103  bool always_ask_required_;
104  bool update_settings_result_;
105
106
107  DISALLOW_COPY_AND_ASSIGN(FakeDelegate);
108};
109
110class CustomFakeCryptohomeClient : public FakeCryptohomeClient {
111 public:
112  CustomFakeCryptohomeClient() : call_status_(DBUS_METHOD_CALL_SUCCESS),
113                                 attestation_enrolled_(true) {}
114  virtual void TpmAttestationIsEnrolled(
115      const BoolDBusMethodCallback& callback) OVERRIDE {
116    base::MessageLoop::current()->PostTask(FROM_HERE,
117                                           base::Bind(callback,
118                                                      call_status_,
119                                                      attestation_enrolled_));
120  }
121
122  void set_call_status(DBusMethodCallStatus call_status) {
123    call_status_ = call_status;
124  }
125
126  void set_attestation_enrolled(bool attestation_enrolled) {
127    attestation_enrolled_ = attestation_enrolled;
128  }
129
130 private:
131  DBusMethodCallStatus call_status_;
132  bool attestation_enrolled_;
133};
134
135}  // namespace
136
137class PlatformVerificationFlowTest : public ::testing::Test {
138 public:
139  PlatformVerificationFlowTest()
140      : message_loop_(base::MessageLoop::TYPE_UI),
141        ui_thread_(content::BrowserThread::UI, &message_loop_),
142        certificate_success_(true),
143        sign_challenge_success_(true),
144        result_(PlatformVerificationFlow::INTERNAL_ERROR) {}
145
146  void SetUp() {
147    // Configure a user for the mock user manager.
148    mock_user_manager_.SetActiveUser(kTestEmail);
149
150    // Create a verifier for tests to call.
151    verifier_.reset(new PlatformVerificationFlow(&mock_attestation_flow_,
152                                                 &mock_async_caller_,
153                                                 &fake_cryptohome_client_,
154                                                 &mock_user_manager_,
155                                                 &fake_delegate_));
156
157    // Create a callback for tests to use with verifier_.
158    callback_ = base::Bind(&PlatformVerificationFlowTest::FakeChallengeCallback,
159                           base::Unretained(this));
160  }
161
162  void TearDown() {
163    verifier_.reset();
164  }
165
166  void ExpectAttestationFlow() {
167    // When consent is not given or the feature is disabled, it is important
168    // that there are no calls to the attestation service.  Thus, a test must
169    // explicitly expect these calls or the mocks will fail the test.
170
171    // Configure the mock AttestationFlow to call FakeGetCertificate.
172    EXPECT_CALL(mock_attestation_flow_,
173                GetCertificate(PROFILE_CONTENT_PROTECTION_CERTIFICATE,
174                               kTestEmail, kTestID, _, _))
175        .WillRepeatedly(WithArgs<4>(Invoke(
176            this, &PlatformVerificationFlowTest::FakeGetCertificate)));
177
178    // Configure the mock AsyncMethodCaller to call FakeSignChallenge.
179    std::string expected_key_name = std::string(kContentProtectionKeyPrefix) +
180                                    std::string(kTestID);
181    EXPECT_CALL(mock_async_caller_,
182                TpmAttestationSignSimpleChallenge(KEY_USER, expected_key_name,
183                                                  kTestChallenge, _))
184        .WillRepeatedly(WithArgs<3>(Invoke(
185            this, &PlatformVerificationFlowTest::FakeSignChallenge)));
186  }
187
188  void FakeGetCertificate(
189      const AttestationFlow::CertificateCallback& callback) {
190    base::MessageLoop::current()->PostTask(FROM_HERE,
191                                           base::Bind(callback,
192                                                      certificate_success_,
193                                                      kTestCertificate));
194  }
195
196  void FakeSignChallenge(
197      const cryptohome::AsyncMethodCaller::DataCallback& callback) {
198    base::MessageLoop::current()->PostTask(FROM_HERE,
199                                           base::Bind(callback,
200                                                      sign_challenge_success_,
201                                                      kTestResponse));
202  }
203
204  void FakeChallengeCallback(PlatformVerificationFlow::Result result,
205                             const std::string& response,
206                             const std::string& certificate) {
207    result_ = result;
208    challenge_response_ = response;
209    certificate_ = certificate;
210  }
211
212 protected:
213  base::MessageLoop message_loop_;
214  content::TestBrowserThread ui_thread_;
215  StrictMock<MockAttestationFlow> mock_attestation_flow_;
216  cryptohome::MockAsyncMethodCaller mock_async_caller_;
217  CustomFakeCryptohomeClient fake_cryptohome_client_;
218  MockUserManager mock_user_manager_;
219  FakeDelegate fake_delegate_;
220  scoped_ptr<PlatformVerificationFlow> verifier_;
221
222  // Controls result of FakeGetCertificate.
223  bool certificate_success_;
224
225  // Controls result of FakeSignChallenge.
226  bool sign_challenge_success_;
227
228  // Callback function and data.
229  PlatformVerificationFlow::ChallengeCallback callback_;
230  PlatformVerificationFlow::Result result_;
231  std::string challenge_response_;
232  std::string certificate_;
233};
234
235TEST_F(PlatformVerificationFlowTest, SuccessNoConsent) {
236  // Make sure the call will fail if consent is requested.
237  fake_delegate_.set_response(PlatformVerificationFlow::CONSENT_RESPONSE_DENY);
238  ExpectAttestationFlow();
239  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
240  base::RunLoop().RunUntilIdle();
241  EXPECT_EQ(PlatformVerificationFlow::SUCCESS, result_);
242  EXPECT_EQ(kTestResponse, challenge_response_);
243  EXPECT_EQ(kTestCertificate, certificate_);
244  EXPECT_EQ(0, fake_delegate_.num_consent_calls());
245}
246
247TEST_F(PlatformVerificationFlowTest, SuccessWithOriginConsent) {
248  // Enable two conditions and make sure consent is not requested twice.
249  fake_delegate_.set_origin_consent_required(true);
250  fake_delegate_.set_always_ask_required(true);
251  ExpectAttestationFlow();
252  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
253  base::RunLoop().RunUntilIdle();
254  EXPECT_EQ(PlatformVerificationFlow::SUCCESS, result_);
255  EXPECT_EQ(kTestResponse, challenge_response_);
256  EXPECT_EQ(kTestCertificate, certificate_);
257  EXPECT_EQ(1, fake_delegate_.num_consent_calls());
258}
259
260TEST_F(PlatformVerificationFlowTest, SuccessWithAlwaysAskConsent) {
261  fake_delegate_.set_always_ask_required(true);
262  ExpectAttestationFlow();
263  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
264  base::RunLoop().RunUntilIdle();
265  EXPECT_EQ(PlatformVerificationFlow::SUCCESS, result_);
266  EXPECT_EQ(kTestResponse, challenge_response_);
267  EXPECT_EQ(kTestCertificate, certificate_);
268  EXPECT_EQ(1, fake_delegate_.num_consent_calls());
269}
270
271TEST_F(PlatformVerificationFlowTest, SuccessWithAttestationConsent) {
272  fake_cryptohome_client_.set_attestation_enrolled(false);
273  ExpectAttestationFlow();
274  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
275  base::RunLoop().RunUntilIdle();
276  EXPECT_EQ(PlatformVerificationFlow::SUCCESS, result_);
277  EXPECT_EQ(kTestResponse, challenge_response_);
278  EXPECT_EQ(kTestCertificate, certificate_);
279  EXPECT_EQ(1, fake_delegate_.num_consent_calls());
280}
281
282TEST_F(PlatformVerificationFlowTest, ConsentRejected) {
283  fake_delegate_.set_always_ask_required(true);
284  fake_delegate_.set_response(PlatformVerificationFlow::CONSENT_RESPONSE_DENY);
285  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
286  base::RunLoop().RunUntilIdle();
287  EXPECT_EQ(PlatformVerificationFlow::USER_REJECTED, result_);
288  EXPECT_EQ(1, fake_delegate_.num_consent_calls());
289}
290
291TEST_F(PlatformVerificationFlowTest, FeatureDisabled) {
292  fake_delegate_.set_attestation_disabled(true);
293  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
294  base::RunLoop().RunUntilIdle();
295  EXPECT_EQ(PlatformVerificationFlow::POLICY_REJECTED, result_);
296  EXPECT_EQ(0, fake_delegate_.num_consent_calls());
297}
298
299TEST_F(PlatformVerificationFlowTest, NotVerified) {
300  certificate_success_ = false;
301  ExpectAttestationFlow();
302  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
303  base::RunLoop().RunUntilIdle();
304  EXPECT_EQ(PlatformVerificationFlow::PLATFORM_NOT_VERIFIED, result_);
305}
306
307TEST_F(PlatformVerificationFlowTest, ChallengeSigningError) {
308  sign_challenge_success_ = false;
309  ExpectAttestationFlow();
310  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
311  base::RunLoop().RunUntilIdle();
312  EXPECT_EQ(PlatformVerificationFlow::INTERNAL_ERROR, result_);
313}
314
315TEST_F(PlatformVerificationFlowTest, DBusFailure) {
316  fake_cryptohome_client_.set_call_status(DBUS_METHOD_CALL_FAILURE);
317  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
318  base::RunLoop().RunUntilIdle();
319  EXPECT_EQ(PlatformVerificationFlow::INTERNAL_ERROR, result_);
320}
321
322TEST_F(PlatformVerificationFlowTest, UpdateSettingsFailure) {
323  fake_delegate_.set_origin_consent_required(true);
324  fake_delegate_.set_update_settings_result(false);
325  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
326  base::RunLoop().RunUntilIdle();
327  EXPECT_EQ(PlatformVerificationFlow::INTERNAL_ERROR, result_);
328}
329
330TEST_F(PlatformVerificationFlowTest, ConsentNoResponse) {
331  fake_delegate_.set_response(PlatformVerificationFlow::CONSENT_RESPONSE_NONE);
332  fake_delegate_.set_origin_consent_required(true);
333  verifier_->ChallengePlatformKey(NULL, kTestID, kTestChallenge, callback_);
334  base::RunLoop().RunUntilIdle();
335  EXPECT_EQ(PlatformVerificationFlow::USER_REJECTED, result_);
336}
337
338}  // namespace attestation
339}  // namespace chromeos
340