1// Copyright (c) 2012 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 "base/bind.h"
6#include "base/memory/scoped_ptr.h"
7#include "base/run_loop.h"
8#include "chromeos/attestation/mock_attestation_flow.h"
9#include "chromeos/cryptohome/mock_async_method_caller.h"
10#include "chromeos/dbus/mock_cryptohome_client.h"
11#include "testing/gmock/include/gmock/gmock.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14using testing::_;
15using testing::AtLeast;
16using testing::DoDefault;
17using testing::Invoke;
18using testing::NiceMock;
19using testing::Return;
20using testing::Sequence;
21using testing::StrictMock;
22using testing::WithArgs;
23
24namespace chromeos {
25namespace attestation {
26
27namespace {
28
29void DBusCallbackFalse(const BoolDBusMethodCallback& callback) {
30  base::MessageLoop::current()->PostTask(
31      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false));
32}
33
34void DBusCallbackTrue(const BoolDBusMethodCallback& callback) {
35  base::MessageLoop::current()->PostTask(
36      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
37}
38
39void DBusCallbackFail(const BoolDBusMethodCallback& callback) {
40  base::MessageLoop::current()->PostTask(
41      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_FAILURE, false));
42}
43
44void AsyncCallbackFalse(cryptohome::AsyncMethodCaller::Callback callback) {
45  callback.Run(false, cryptohome::MOUNT_ERROR_NONE);
46}
47
48class FakeDBusData {
49 public:
50  explicit FakeDBusData(const std::string& data) : data_(data) {}
51
52  void operator() (const CryptohomeClient::DataMethodCallback& callback) {
53    base::MessageLoop::current()->PostTask(
54        FROM_HERE,
55        base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true, data_));
56  }
57
58 private:
59  std::string data_;
60};
61
62}  // namespace
63
64class AttestationFlowTest : public testing::Test {
65 protected:
66  void Run() {
67    base::RunLoop run_loop;
68    run_loop.RunUntilIdle();
69  }
70  base::MessageLoop message_loop_;
71};
72
73TEST_F(AttestationFlowTest, GetCertificate) {
74  // Verify the order of calls in a sequence.
75  Sequence flow_order;
76
77  // Use DBusCallbackFalse so the full enrollment flow is triggered.
78  chromeos::MockCryptohomeClient client;
79  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
80      .InSequence(flow_order)
81      .WillRepeatedly(Invoke(DBusCallbackFalse));
82
83  // Use StrictMock when we want to verify invocation frequency.
84  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
85  async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE);
86  EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_, _))
87      .Times(1)
88      .InSequence(flow_order);
89
90  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
91  proxy->DeferToFake(true);
92  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
93  EXPECT_CALL(*proxy, SendEnrollRequest(
94      cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest,
95      _)).Times(1)
96         .InSequence(flow_order);
97
98  std::string fake_enroll_response =
99      cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest;
100  fake_enroll_response += "_response";
101  EXPECT_CALL(async_caller,
102              AsyncTpmAttestationEnroll(_, fake_enroll_response, _))
103      .Times(1)
104      .InSequence(flow_order);
105
106  EXPECT_CALL(
107      async_caller,
108      AsyncTpmAttestationCreateCertRequest(_,
109                                           PROFILE_ENTERPRISE_USER_CERTIFICATE,
110                                           "fake@test.com", "fake_origin", _))
111          .Times(1)
112          .InSequence(flow_order);
113
114  EXPECT_CALL(*proxy, SendCertificateRequest(
115      cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest,
116      _)).Times(1)
117         .InSequence(flow_order);
118
119  std::string fake_cert_response =
120      cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest;
121  fake_cert_response += "_response";
122  EXPECT_CALL(async_caller,
123              AsyncTpmAttestationFinishCertRequest(fake_cert_response,
124                                                   KEY_USER,
125                                                   "fake@test.com",
126                                                   kEnterpriseUserKey,
127                                                   _))
128      .Times(1)
129      .InSequence(flow_order);
130
131  StrictMock<MockObserver> observer;
132  EXPECT_CALL(observer, MockCertificateCallback(
133      true,
134      cryptohome::MockAsyncMethodCaller::kFakeAttestationCert))
135      .Times(1)
136      .InSequence(flow_order);
137  AttestationFlow::CertificateCallback mock_callback = base::Bind(
138      &MockObserver::MockCertificateCallback,
139      base::Unretained(&observer));
140
141  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
142  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
143  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "fake@test.com",
144                      "fake_origin", true, mock_callback);
145  Run();
146}
147
148TEST_F(AttestationFlowTest, GetCertificate_NoEK) {
149  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
150  async_caller.SetUp(false, cryptohome::MOUNT_ERROR_NONE);
151  EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_, _))
152      .Times(1);
153
154  chromeos::MockCryptohomeClient client;
155  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
156      .WillRepeatedly(Invoke(DBusCallbackFalse));
157
158  // We're not expecting any server calls in this case; StrictMock will verify.
159  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
160  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
161
162  StrictMock<MockObserver> observer;
163  EXPECT_CALL(observer, MockCertificateCallback(false, ""))
164      .Times(1);
165  AttestationFlow::CertificateCallback mock_callback = base::Bind(
166      &MockObserver::MockCertificateCallback,
167      base::Unretained(&observer));
168
169  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
170  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
171  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", true,
172                      mock_callback);
173  Run();
174}
175
176TEST_F(AttestationFlowTest, GetCertificate_EKRejected) {
177  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
178  async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE);
179  EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_, _))
180      .Times(1);
181
182  chromeos::MockCryptohomeClient client;
183  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
184      .WillRepeatedly(Invoke(DBusCallbackFalse));
185
186  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
187  proxy->DeferToFake(false);
188  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
189  EXPECT_CALL(*proxy, SendEnrollRequest(
190      cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest,
191      _)).Times(1);
192
193  StrictMock<MockObserver> observer;
194  EXPECT_CALL(observer, MockCertificateCallback(false, ""))
195      .Times(1);
196  AttestationFlow::CertificateCallback mock_callback = base::Bind(
197      &MockObserver::MockCertificateCallback,
198      base::Unretained(&observer));
199
200  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
201  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
202  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", true,
203                      mock_callback);
204  Run();
205}
206
207TEST_F(AttestationFlowTest, GetCertificate_FailEnroll) {
208  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
209  async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE);
210  EXPECT_CALL(async_caller, AsyncTpmAttestationCreateEnrollRequest(_, _))
211      .Times(1);
212  std::string fake_enroll_response =
213      cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest;
214  fake_enroll_response += "_response";
215  EXPECT_CALL(async_caller,
216              AsyncTpmAttestationEnroll(_, fake_enroll_response, _))
217      .WillOnce(WithArgs<2>(Invoke(AsyncCallbackFalse)));
218
219  chromeos::MockCryptohomeClient client;
220  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
221      .WillRepeatedly(Invoke(DBusCallbackFalse));
222
223  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
224  proxy->DeferToFake(true);
225  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
226  EXPECT_CALL(*proxy, SendEnrollRequest(
227      cryptohome::MockAsyncMethodCaller::kFakeAttestationEnrollRequest,
228      _)).Times(1);
229
230  StrictMock<MockObserver> observer;
231  EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1);
232  AttestationFlow::CertificateCallback mock_callback = base::Bind(
233      &MockObserver::MockCertificateCallback,
234      base::Unretained(&observer));
235
236  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
237  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
238  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", true,
239                      mock_callback);
240  Run();
241}
242
243TEST_F(AttestationFlowTest, GetMachineCertificateAlreadyEnrolled) {
244  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
245  async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE);
246  EXPECT_CALL(async_caller,
247              AsyncTpmAttestationCreateCertRequest(
248                  _, PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, "", "", _))
249      .Times(1);
250  std::string fake_cert_response =
251      cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest;
252  fake_cert_response += "_response";
253  EXPECT_CALL(async_caller,
254              AsyncTpmAttestationFinishCertRequest(fake_cert_response,
255                                                   KEY_DEVICE,
256                                                   "",
257                                                   kEnterpriseMachineKey,
258                                                   _))
259      .Times(1);
260
261  chromeos::MockCryptohomeClient client;
262  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
263      .WillRepeatedly(Invoke(DBusCallbackTrue));
264
265  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
266  proxy->DeferToFake(true);
267  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
268  EXPECT_CALL(*proxy, SendCertificateRequest(
269      cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest,
270      _)).Times(1);
271
272  StrictMock<MockObserver> observer;
273  EXPECT_CALL(observer, MockCertificateCallback(
274      true,
275      cryptohome::MockAsyncMethodCaller::kFakeAttestationCert)).Times(1);
276  AttestationFlow::CertificateCallback mock_callback = base::Bind(
277      &MockObserver::MockCertificateCallback,
278      base::Unretained(&observer));
279
280  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
281  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
282  flow.GetCertificate(PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, "", "", true,
283                      mock_callback);
284  Run();
285}
286
287TEST_F(AttestationFlowTest, GetCertificate_FailCreateCertRequest) {
288  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
289  async_caller.SetUp(false, cryptohome::MOUNT_ERROR_NONE);
290  EXPECT_CALL(async_caller,
291              AsyncTpmAttestationCreateCertRequest(
292                  _, PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", _))
293      .Times(1);
294
295  chromeos::MockCryptohomeClient client;
296  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
297      .WillRepeatedly(Invoke(DBusCallbackTrue));
298
299  // We're not expecting any server calls in this case; StrictMock will verify.
300  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
301  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
302
303  StrictMock<MockObserver> observer;
304  EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1);
305  AttestationFlow::CertificateCallback mock_callback = base::Bind(
306      &MockObserver::MockCertificateCallback,
307      base::Unretained(&observer));
308
309  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
310  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
311  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", true,
312                      mock_callback);
313  Run();
314}
315
316TEST_F(AttestationFlowTest, GetCertificate_CertRequestRejected) {
317  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
318  async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE);
319  EXPECT_CALL(async_caller,
320              AsyncTpmAttestationCreateCertRequest(
321                  _, PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", _))
322      .Times(1);
323
324  chromeos::MockCryptohomeClient client;
325  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
326      .WillRepeatedly(Invoke(DBusCallbackTrue));
327
328  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
329  proxy->DeferToFake(false);
330  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
331  EXPECT_CALL(*proxy, SendCertificateRequest(
332      cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest,
333      _)).Times(1);
334
335  StrictMock<MockObserver> observer;
336  EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1);
337  AttestationFlow::CertificateCallback mock_callback = base::Bind(
338      &MockObserver::MockCertificateCallback,
339      base::Unretained(&observer));
340
341  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
342  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
343  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", true,
344                      mock_callback);
345  Run();
346}
347
348TEST_F(AttestationFlowTest, GetCertificate_FailIsEnrolled) {
349  // We're not expecting any async calls in this case; StrictMock will verify.
350  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
351
352  chromeos::MockCryptohomeClient client;
353  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
354      .WillRepeatedly(Invoke(DBusCallbackFail));
355
356  // We're not expecting any server calls in this case; StrictMock will verify.
357  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
358  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
359
360  StrictMock<MockObserver> observer;
361  EXPECT_CALL(observer, MockCertificateCallback(false, "")).Times(1);
362  AttestationFlow::CertificateCallback mock_callback = base::Bind(
363      &MockObserver::MockCertificateCallback,
364      base::Unretained(&observer));
365
366  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
367  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
368  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", true,
369                      mock_callback);
370  Run();
371}
372
373TEST_F(AttestationFlowTest, GetCertificate_CheckExisting) {
374  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
375  async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE);
376  EXPECT_CALL(async_caller,
377              AsyncTpmAttestationCreateCertRequest(
378                  _, PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", _))
379      .Times(1);
380  std::string fake_cert_response =
381      cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest;
382  fake_cert_response += "_response";
383  EXPECT_CALL(async_caller,
384              AsyncTpmAttestationFinishCertRequest(fake_cert_response,
385                                                   KEY_USER,
386                                                   "",
387                                                   kEnterpriseUserKey,
388                                                   _))
389      .Times(1);
390
391  chromeos::MockCryptohomeClient client;
392  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
393      .WillRepeatedly(Invoke(DBusCallbackTrue));
394  EXPECT_CALL(client,
395              TpmAttestationDoesKeyExist(KEY_USER, "", kEnterpriseUserKey, _))
396      .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackFalse)));
397
398  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
399  proxy->DeferToFake(true);
400  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
401  EXPECT_CALL(*proxy, SendCertificateRequest(
402      cryptohome::MockAsyncMethodCaller::kFakeAttestationCertRequest,
403      _)).Times(1);
404
405  StrictMock<MockObserver> observer;
406  EXPECT_CALL(observer, MockCertificateCallback(
407      true,
408      cryptohome::MockAsyncMethodCaller::kFakeAttestationCert)).Times(1);
409  AttestationFlow::CertificateCallback mock_callback = base::Bind(
410      &MockObserver::MockCertificateCallback,
411      base::Unretained(&observer));
412
413  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
414  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
415  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", false,
416                      mock_callback);
417  Run();
418}
419
420TEST_F(AttestationFlowTest, GetCertificate_AlreadyExists) {
421  // We're not expecting any async calls in this case; StrictMock will verify.
422  StrictMock<cryptohome::MockAsyncMethodCaller> async_caller;
423
424  chromeos::MockCryptohomeClient client;
425  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
426      .WillRepeatedly(Invoke(DBusCallbackTrue));
427  EXPECT_CALL(client,
428              TpmAttestationDoesKeyExist(KEY_USER, "", kEnterpriseUserKey, _))
429      .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackTrue)));
430  EXPECT_CALL(client,
431              TpmAttestationGetCertificate(KEY_USER, "", kEnterpriseUserKey, _))
432      .WillRepeatedly(WithArgs<3>(Invoke(FakeDBusData("fake_cert"))));
433
434  // We're not expecting any server calls in this case; StrictMock will verify.
435  scoped_ptr<MockServerProxy> proxy(new StrictMock<MockServerProxy>());
436  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(DoDefault());
437
438  StrictMock<MockObserver> observer;
439  EXPECT_CALL(observer, MockCertificateCallback(true, "fake_cert")).Times(1);
440  AttestationFlow::CertificateCallback mock_callback = base::Bind(
441      &MockObserver::MockCertificateCallback,
442      base::Unretained(&observer));
443
444  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
445  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
446  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", false,
447                      mock_callback);
448  Run();
449}
450
451TEST_F(AttestationFlowTest, AlternatePCA) {
452  // Strategy: Create a ServerProxy mock which reports ALTERNATE_PCA and check
453  // that all calls to the AsyncMethodCaller reflect this PCA type.
454  scoped_ptr<MockServerProxy> proxy(new NiceMock<MockServerProxy>());
455  proxy->DeferToFake(true);
456  EXPECT_CALL(*proxy, GetType()).WillRepeatedly(Return(ALTERNATE_PCA));
457
458  chromeos::MockCryptohomeClient client;
459  EXPECT_CALL(client, TpmAttestationIsEnrolled(_))
460      .WillRepeatedly(Invoke(DBusCallbackFalse));
461
462  NiceMock<cryptohome::MockAsyncMethodCaller> async_caller;
463  async_caller.SetUp(true, cryptohome::MOUNT_ERROR_NONE);
464  EXPECT_CALL(async_caller,
465              AsyncTpmAttestationCreateEnrollRequest(ALTERNATE_PCA, _))
466      .Times(AtLeast(1));
467  EXPECT_CALL(async_caller,
468              AsyncTpmAttestationEnroll(ALTERNATE_PCA, _, _))
469      .Times(AtLeast(1));
470  EXPECT_CALL(async_caller,
471              AsyncTpmAttestationCreateCertRequest(ALTERNATE_PCA, _, _, _, _))
472      .Times(AtLeast(1));
473
474  NiceMock<MockObserver> observer;
475  AttestationFlow::CertificateCallback mock_callback = base::Bind(
476      &MockObserver::MockCertificateCallback,
477      base::Unretained(&observer));
478
479  scoped_ptr<ServerProxy> proxy_interface(proxy.release());
480  AttestationFlow flow(&async_caller, &client, proxy_interface.Pass());
481  flow.GetCertificate(PROFILE_ENTERPRISE_USER_CERTIFICATE, "", "", true,
482                      mock_callback);
483  Run();
484}
485
486}  // namespace attestation
487}  // namespace chromeos
488