1// Copyright (c) 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/attestation_key_payload.pb.h" 11#include "chrome/browser/chromeos/attestation/attestation_policy_observer.h" 12#include "chrome/browser/chromeos/attestation/fake_certificate.h" 13#include "chrome/browser/chromeos/settings/cros_settings.h" 14#include "chrome/browser/chromeos/settings/device_settings_service.h" 15#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" 16#include "chromeos/attestation/mock_attestation_flow.h" 17#include "chromeos/dbus/mock_cryptohome_client.h" 18#include "chromeos/settings/cros_settings_names.h" 19#include "components/policy/core/common/cloud/mock_cloud_policy_client.h" 20#include "content/public/test/test_browser_thread.h" 21#include "testing/gtest/include/gtest/gtest.h" 22 23using testing::_; 24using testing::Invoke; 25using testing::StrictMock; 26using testing::WithArgs; 27 28namespace chromeos { 29namespace attestation { 30 31namespace { 32 33const int64 kCertValid = 90; 34const int64 kCertExpiringSoon = 20; 35const int64 kCertExpired = -20; 36 37void DBusCallbackFalse(const BoolDBusMethodCallback& callback) { 38 base::MessageLoop::current()->PostTask( 39 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false)); 40} 41 42void DBusCallbackTrue(const BoolDBusMethodCallback& callback) { 43 base::MessageLoop::current()->PostTask( 44 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true)); 45} 46 47void DBusCallbackError(const BoolDBusMethodCallback& callback) { 48 base::MessageLoop::current()->PostTask( 49 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_FAILURE, false)); 50} 51 52void CertCallbackSuccess(const AttestationFlow::CertificateCallback& callback) { 53 base::MessageLoop::current()->PostTask( 54 FROM_HERE, base::Bind(callback, true, "fake_cert")); 55} 56 57void StatusCallbackSuccess( 58 const policy::CloudPolicyClient::StatusCallback& callback) { 59 base::MessageLoop::current()->PostTask( 60 FROM_HERE, base::Bind(callback, true)); 61} 62 63class FakeDBusData { 64 public: 65 explicit FakeDBusData(const std::string& data) : data_(data) {} 66 67 void operator() (const CryptohomeClient::DataMethodCallback& callback) { 68 base::MessageLoop::current()->PostTask( 69 FROM_HERE, 70 base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true, data_)); 71 } 72 73 private: 74 std::string data_; 75}; 76 77} // namespace 78 79class AttestationPolicyObserverTest : public ::testing::Test { 80 public: 81 AttestationPolicyObserverTest() 82 : ui_thread_(content::BrowserThread::UI, &message_loop_) { 83 // Remove the real DeviceSettingsProvider and replace it with a stub. 84 CrosSettings* cros_settings = CrosSettings::Get(); 85 device_settings_provider_ = 86 cros_settings->GetProvider(kDeviceAttestationEnabled); 87 cros_settings->RemoveSettingsProvider(device_settings_provider_); 88 cros_settings->AddSettingsProvider(&stub_settings_provider_); 89 cros_settings->SetBoolean(kDeviceAttestationEnabled, true); 90 policy_client_.SetDMToken("fake_dm_token"); 91 } 92 93 virtual ~AttestationPolicyObserverTest() { 94 // Restore the real DeviceSettingsProvider. 95 CrosSettings* cros_settings = CrosSettings::Get(); 96 cros_settings->RemoveSettingsProvider(&stub_settings_provider_); 97 cros_settings->AddSettingsProvider(device_settings_provider_); 98 } 99 100 protected: 101 enum MockOptions { 102 MOCK_KEY_EXISTS = 1, // Configure so a certified key exists. 103 MOCK_KEY_UPLOADED = (1 << 1), // Configure so an upload has occurred. 104 MOCK_NEW_KEY = (1 << 2) // Configure expecting new key generation. 105 }; 106 107 // Configures mock expectations according to |mock_options|. If options 108 // require that a certificate exists, |certificate| will be used. 109 void SetupMocks(int mock_options, const std::string& certificate) { 110 bool key_exists = (mock_options & MOCK_KEY_EXISTS); 111 // Setup expected key / cert queries. 112 if (key_exists) { 113 EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) 114 .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackTrue))); 115 EXPECT_CALL(cryptohome_client_, TpmAttestationGetCertificate(_, _, _, _)) 116 .WillRepeatedly(WithArgs<3>(Invoke(FakeDBusData(certificate)))); 117 } else { 118 EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) 119 .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackFalse))); 120 } 121 122 // Setup expected key payload queries. 123 bool key_uploaded = (mock_options & MOCK_KEY_UPLOADED); 124 std::string payload = CreatePayload(); 125 EXPECT_CALL(cryptohome_client_, TpmAttestationGetKeyPayload(_, _, _, _)) 126 .WillRepeatedly(WithArgs<3>(Invoke( 127 FakeDBusData(key_uploaded ? payload : "")))); 128 129 // Setup expected key uploads. Use WillOnce() so StrictMock will trigger an 130 // error if our expectations are not met exactly. We want to verify that 131 // during a single run through the observer only one upload operation occurs 132 // (because it is costly) and similarly, that the writing of the uploaded 133 // status in the key payload matches the upload operation. 134 bool new_key = (mock_options & MOCK_NEW_KEY); 135 if (new_key || !key_uploaded) { 136 EXPECT_CALL(policy_client_, 137 UploadCertificate(new_key ? "fake_cert" : certificate, _)) 138 .WillOnce(WithArgs<1>(Invoke(StatusCallbackSuccess))); 139 EXPECT_CALL(cryptohome_client_, 140 TpmAttestationSetKeyPayload(_, _, _, payload, _)) 141 .WillOnce(WithArgs<4>(Invoke(DBusCallbackTrue))); 142 } 143 144 // Setup expected key generations. Again use WillOnce(). Key generation is 145 // another costly operation and if it gets triggered more than once during 146 // a single pass this indicates a logical problem in the observer. 147 if (new_key) { 148 EXPECT_CALL(attestation_flow_, GetCertificate(_, _, _, _, _)) 149 .WillOnce(WithArgs<4>(Invoke(CertCallbackSuccess))); 150 } 151 } 152 153 void Run() { 154 AttestationPolicyObserver observer(&policy_client_, 155 &cryptohome_client_, 156 &attestation_flow_); 157 observer.set_retry_delay(0); 158 base::RunLoop().RunUntilIdle(); 159 } 160 161 std::string CreatePayload() { 162 AttestationKeyPayload proto; 163 proto.set_is_certificate_uploaded(true); 164 std::string serialized; 165 proto.SerializeToString(&serialized); 166 return serialized; 167 } 168 169 base::MessageLoopForUI message_loop_; 170 content::TestBrowserThread ui_thread_; 171 ScopedTestDeviceSettingsService test_device_settings_service_; 172 ScopedTestCrosSettings test_cros_settings_; 173 CrosSettingsProvider* device_settings_provider_; 174 StubCrosSettingsProvider stub_settings_provider_; 175 StrictMock<MockCryptohomeClient> cryptohome_client_; 176 StrictMock<MockAttestationFlow> attestation_flow_; 177 StrictMock<policy::MockCloudPolicyClient> policy_client_; 178}; 179 180TEST_F(AttestationPolicyObserverTest, FeatureDisabled) { 181 CrosSettings* cros_settings = CrosSettings::Get(); 182 cros_settings->SetBoolean(kDeviceAttestationEnabled, false); 183 Run(); 184} 185 186TEST_F(AttestationPolicyObserverTest, UnregisteredPolicyClient) { 187 policy_client_.SetDMToken(""); 188 Run(); 189} 190 191TEST_F(AttestationPolicyObserverTest, NewCertificate) { 192 SetupMocks(MOCK_NEW_KEY, ""); 193 Run(); 194} 195 196TEST_F(AttestationPolicyObserverTest, KeyExistsNotUploaded) { 197 std::string certificate; 198 ASSERT_TRUE(GetFakeCertificate(base::TimeDelta::FromDays(kCertValid), 199 &certificate)); 200 SetupMocks(MOCK_KEY_EXISTS, certificate); 201 Run(); 202} 203 204TEST_F(AttestationPolicyObserverTest, KeyExistsAlreadyUploaded) { 205 std::string certificate; 206 ASSERT_TRUE(GetFakeCertificate(base::TimeDelta::FromDays(kCertValid), 207 &certificate)); 208 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED, certificate); 209 Run(); 210} 211 212TEST_F(AttestationPolicyObserverTest, KeyExistsCertExpiringSoon) { 213 std::string certificate; 214 ASSERT_TRUE(GetFakeCertificate(base::TimeDelta::FromDays(kCertExpiringSoon), 215 &certificate)); 216 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED | MOCK_NEW_KEY, certificate); 217 Run(); 218} 219 220TEST_F(AttestationPolicyObserverTest, KeyExistsCertExpired) { 221 std::string certificate; 222 ASSERT_TRUE(GetFakeCertificate(base::TimeDelta::FromDays(kCertExpired), 223 &certificate)); 224 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED | MOCK_NEW_KEY, certificate); 225 Run(); 226} 227 228TEST_F(AttestationPolicyObserverTest, IgnoreUnknownCertFormat) { 229 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED, "unsupported"); 230 Run(); 231} 232 233TEST_F(AttestationPolicyObserverTest, DBusFailureRetry) { 234 SetupMocks(MOCK_NEW_KEY, ""); 235 // Simulate a DBus failure. 236 EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) 237 .WillOnce(WithArgs<3>(Invoke(DBusCallbackError))) 238 .WillRepeatedly(WithArgs<3>(Invoke(DBusCallbackFalse))); 239 Run(); 240} 241 242} // namespace attestation 243} // namespace chromeos 244