OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include <string> |
| 6 |
5 #include "base/bind.h" | 7 #include "base/bind.h" |
6 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
7 #include "base/run_loop.h" | 9 #include "base/run_loop.h" |
| 10 #include "chrome/browser/chromeos/attestation/attestation_key_payload.pb.h" |
8 #include "chrome/browser/chromeos/attestation/attestation_policy_observer.h" | 11 #include "chrome/browser/chromeos/attestation/attestation_policy_observer.h" |
9 #include "chrome/browser/chromeos/settings/cros_settings.h" | 12 #include "chrome/browser/chromeos/settings/cros_settings.h" |
10 #include "chrome/browser/chromeos/settings/cros_settings_names.h" | 13 #include "chrome/browser/chromeos/settings/cros_settings_names.h" |
11 #include "chrome/browser/chromeos/settings/device_settings_service.h" | 14 #include "chrome/browser/chromeos/settings/device_settings_service.h" |
12 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" | 15 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" |
13 #include "chrome/browser/policy/cloud/mock_cloud_policy_client.h" | 16 #include "chrome/browser/policy/cloud/mock_cloud_policy_client.h" |
14 #include "chromeos/attestation/mock_attestation_flow.h" | 17 #include "chromeos/attestation/mock_attestation_flow.h" |
15 #include "chromeos/dbus/mock_cryptohome_client.h" | 18 #include "chromeos/dbus/mock_cryptohome_client.h" |
16 #include "content/public/test/test_browser_thread.h" | 19 #include "content/public/test/test_browser_thread.h" |
| 20 #include "crypto/rsa_private_key.h" |
| 21 #include "net/cert/x509_certificate.h" |
| 22 #include "net/cert/x509_util_nss.h" |
17 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
18 | 24 |
19 using testing::_; | 25 using testing::_; |
20 using testing::Invoke; | 26 using testing::Invoke; |
21 using testing::StrictMock; | 27 using testing::StrictMock; |
22 using testing::WithArgs; | 28 using testing::WithArgs; |
23 | 29 |
24 namespace chromeos { | 30 namespace chromeos { |
25 namespace attestation { | 31 namespace attestation { |
26 | 32 |
27 namespace { | 33 namespace { |
28 | 34 |
| 35 // A test key encoded as ASN.1 PrivateKeyInfo from PKCS #8. |
| 36 const uint8 kTestKeyData[] = { |
| 37 0x30, 0x82, 0x01, 0x55, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, |
| 38 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, |
| 39 0x01, 0x3f, 0x30, 0x82, 0x01, 0x3b, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00, |
| 40 0xd9, 0xcd, 0xca, 0xcd, 0xc3, 0xea, 0xbe, 0x72, 0x79, 0x1c, 0x29, 0x37, |
| 41 0x39, 0x99, 0x1f, 0xd4, 0xb3, 0x0e, 0xf0, 0x7b, 0x78, 0x77, 0x0e, 0x05, |
| 42 0x3b, 0x65, 0x34, 0x12, 0x62, 0xaf, 0xa6, 0x8d, 0x33, 0xce, 0x78, 0xf8, |
| 43 0x47, 0x05, 0x1d, 0x98, 0xaa, 0x1b, 0x1f, 0x50, 0x05, 0x5b, 0x3c, 0x19, |
| 44 0x3f, 0x80, 0x83, 0x63, 0x63, 0x3a, 0xec, 0xcb, 0x2e, 0x90, 0x4f, 0xf5, |
| 45 0x26, 0x76, 0xf1, 0xd5, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x64, |
| 46 0x29, 0xc2, 0xd9, 0x6b, 0xfe, 0xf9, 0x84, 0x75, 0x73, 0xe0, 0xf4, 0x77, |
| 47 0xb5, 0x96, 0xb0, 0xdf, 0x83, 0xc0, 0x4e, 0x57, 0xf1, 0x10, 0x6e, 0x91, |
| 48 0x89, 0x12, 0x30, 0x5e, 0x57, 0xff, 0x14, 0x59, 0x5f, 0x18, 0x86, 0x4e, |
| 49 0x4b, 0x17, 0x56, 0xfc, 0x8d, 0x40, 0xdd, 0x74, 0x65, 0xd3, 0xff, 0x67, |
| 50 0x64, 0xcb, 0x9c, 0xb4, 0x14, 0x8a, 0x06, 0xb7, 0x13, 0x45, 0x94, 0x16, |
| 51 0x7d, 0x3f, 0xe1, 0x02, 0x21, 0x00, 0xf6, 0x0f, 0x31, 0x6d, 0x06, 0xcc, |
| 52 0x3b, 0xa0, 0x44, 0x1f, 0xf5, 0xc2, 0x45, 0x2b, 0x10, 0x6c, 0xf9, 0x6f, |
| 53 0x8f, 0x87, 0x3d, 0xc0, 0x3b, 0x55, 0x13, 0x37, 0x80, 0xcd, 0x9f, 0xe1, |
| 54 0xb7, 0xd9, 0x02, 0x21, 0x00, 0xe2, 0x9a, 0x5f, 0xbf, 0x95, 0x74, 0xb5, |
| 55 0x7a, 0x6a, 0xa6, 0x97, 0xbd, 0x75, 0x8c, 0x97, 0x18, 0x24, 0xd6, 0x09, |
| 56 0xcd, 0xdc, 0xb5, 0x94, 0xbf, 0xe2, 0x78, 0xaa, 0x20, 0x47, 0x9f, 0x68, |
| 57 0x5d, 0x02, 0x21, 0x00, 0xaf, 0x8f, 0x97, 0x8c, 0x5a, 0xd5, 0x4d, 0x95, |
| 58 0xc4, 0x05, 0xa9, 0xab, 0xba, 0xfe, 0x46, 0xf1, 0xf9, 0xe7, 0x07, 0x59, |
| 59 0x4f, 0x4d, 0xe1, 0x07, 0x8a, 0x76, 0x87, 0x88, 0x2f, 0x13, 0x35, 0xc1, |
| 60 0x02, 0x20, 0x24, 0xc3, 0xd9, 0x2f, 0x13, 0x47, 0x99, 0x3e, 0x20, 0x59, |
| 61 0xa1, 0x1a, 0xeb, 0x1c, 0x81, 0x53, 0x38, 0x7e, 0xc5, 0x9e, 0x71, 0xe5, |
| 62 0xc0, 0x19, 0x95, 0xdb, 0xef, 0xf6, 0x46, 0xc8, 0x95, 0x3d, 0x02, 0x21, |
| 63 0x00, 0xaa, 0xb1, 0xff, 0x8a, 0xa2, 0xb2, 0x2b, 0xef, 0x9a, 0x83, 0x3f, |
| 64 0xc5, 0xbc, 0xd4, 0x6a, 0x07, 0xe8, 0xc7, 0x0b, 0x2e, 0xd4, 0x0f, 0xf8, |
| 65 0x98, 0x68, 0xe1, 0x04, 0xa8, 0x92, 0xd0, 0x10, 0xaa, |
| 66 }; |
| 67 |
29 void DBusCallbackFalse(const BoolDBusMethodCallback& callback) { | 68 void DBusCallbackFalse(const BoolDBusMethodCallback& callback) { |
30 MessageLoop::current()->PostTask( | 69 MessageLoop::current()->PostTask( |
31 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false)); | 70 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false)); |
32 } | 71 } |
33 | 72 |
34 void DBusCallbackTrue(const BoolDBusMethodCallback& callback) { | 73 void DBusCallbackTrue(const BoolDBusMethodCallback& callback) { |
35 MessageLoop::current()->PostTask( | 74 MessageLoop::current()->PostTask( |
36 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true)); | 75 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true)); |
37 } | 76 } |
38 | 77 |
39 void DBusDataCallback(const CryptohomeClient::DataMethodCallback& callback) { | |
40 MessageLoop::current()->PostTask( | |
41 FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true, "fake")); | |
42 } | |
43 | |
44 void CertCallbackSuccess(const AttestationFlow::CertificateCallback& callback) { | 78 void CertCallbackSuccess(const AttestationFlow::CertificateCallback& callback) { |
45 MessageLoop::current()->PostTask( | 79 MessageLoop::current()->PostTask( |
46 FROM_HERE, base::Bind(callback, true, "fake_cert")); | 80 FROM_HERE, base::Bind(callback, true, "fake_cert")); |
47 } | 81 } |
48 | 82 |
| 83 void StatusCallbackSuccess( |
| 84 const policy::CloudPolicyClient::StatusCallback& callback) { |
| 85 MessageLoop::current()->PostTask( |
| 86 FROM_HERE, base::Bind(callback, true)); |
| 87 } |
| 88 |
| 89 class FakeDBusData { |
| 90 public: |
| 91 explicit FakeDBusData(const std::string& data) : data_(data) {} |
| 92 |
| 93 void operator() (const CryptohomeClient::DataMethodCallback& callback) { |
| 94 MessageLoop::current()->PostTask( |
| 95 FROM_HERE, |
| 96 base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true, data_)); |
| 97 } |
| 98 |
| 99 private: |
| 100 std::string data_; |
| 101 }; |
| 102 |
49 } // namespace | 103 } // namespace |
50 | 104 |
51 class AttestationPolicyObserverTest : public ::testing::Test { | 105 class AttestationPolicyObserverTest : public ::testing::Test { |
52 public: | 106 public: |
53 AttestationPolicyObserverTest() | 107 AttestationPolicyObserverTest() |
54 : message_loop_(MessageLoop::TYPE_UI), | 108 : message_loop_(MessageLoop::TYPE_UI), |
55 ui_thread_(content::BrowserThread::UI, &message_loop_) { | 109 ui_thread_(content::BrowserThread::UI, &message_loop_) { |
56 // Remove the real DeviceSettingsProvider and replace it with a stub. | 110 // Remove the real DeviceSettingsProvider and replace it with a stub. |
57 CrosSettings* cros_settings = CrosSettings::Get(); | 111 CrosSettings* cros_settings = CrosSettings::Get(); |
58 device_settings_provider_ = | 112 device_settings_provider_ = |
59 cros_settings->GetProvider(kDeviceAttestationEnabled); | 113 cros_settings->GetProvider(kDeviceAttestationEnabled); |
60 cros_settings->RemoveSettingsProvider(device_settings_provider_); | 114 cros_settings->RemoveSettingsProvider(device_settings_provider_); |
61 cros_settings->AddSettingsProvider(&stub_settings_provider_); | 115 cros_settings->AddSettingsProvider(&stub_settings_provider_); |
62 cros_settings->SetBoolean(kDeviceAttestationEnabled, true); | 116 cros_settings->SetBoolean(kDeviceAttestationEnabled, true); |
63 policy_client_.SetDMToken("fake_dm_token"); | 117 policy_client_.SetDMToken("fake_dm_token"); |
64 } | 118 } |
65 | 119 |
66 virtual ~AttestationPolicyObserverTest() { | 120 virtual ~AttestationPolicyObserverTest() { |
67 // Restore the real DeviceSettingsProvider. | 121 // Restore the real DeviceSettingsProvider. |
68 CrosSettings* cros_settings = CrosSettings::Get(); | 122 CrosSettings* cros_settings = CrosSettings::Get(); |
69 cros_settings->RemoveSettingsProvider(&stub_settings_provider_); | 123 cros_settings->RemoveSettingsProvider(&stub_settings_provider_); |
70 cros_settings->AddSettingsProvider(device_settings_provider_); | 124 cros_settings->AddSettingsProvider(device_settings_provider_); |
71 } | 125 } |
72 | 126 |
73 protected: | 127 protected: |
| 128 enum CertExpiryOptions { |
| 129 CERT_VALID, |
| 130 CERT_EXPIRING_SOON, |
| 131 CERT_EXPIRED |
| 132 }; |
| 133 |
| 134 enum MockOptions { |
| 135 MOCK_KEY_EXISTS = 1, // Configure so a certified key exists. |
| 136 MOCK_KEY_UPLOADED = (1 << 1), // Configure so an upload has occurred. |
| 137 MOCK_NEW_KEY = (1 << 2) // Configure expecting new key generation. |
| 138 }; |
| 139 |
| 140 // Configures mock expectations according to |mock_options|. If options |
| 141 // require that a certificate exists, |certificate| will be used. |
| 142 void SetupMocks(int mock_options, const std::string& certificate) { |
| 143 bool key_exists = (mock_options & MOCK_KEY_EXISTS); |
| 144 // Setup expected key / cert queries. |
| 145 if (key_exists) { |
| 146 EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) |
| 147 .WillRepeatedly(WithArgs<2>(Invoke(DBusCallbackTrue))); |
| 148 EXPECT_CALL(cryptohome_client_, TpmAttestationGetCertificate(_, _, _)) |
| 149 .WillRepeatedly(WithArgs<2>(Invoke(FakeDBusData(certificate)))); |
| 150 } else { |
| 151 EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) |
| 152 .WillRepeatedly(WithArgs<2>(Invoke(DBusCallbackFalse))); |
| 153 } |
| 154 |
| 155 // Setup expected key payload queries. |
| 156 bool key_uploaded = (mock_options & MOCK_KEY_UPLOADED); |
| 157 std::string payload = CreatePayload(); |
| 158 EXPECT_CALL(cryptohome_client_, TpmAttestationGetKeyPayload(_, _, _)) |
| 159 .WillRepeatedly(WithArgs<2>(Invoke( |
| 160 FakeDBusData(key_uploaded ? payload : "")))); |
| 161 |
| 162 // Setup expected key uploads. Use WillOnce() so StrictMock will trigger an |
| 163 // error if our expectations are not met exactly. We want to verify that |
| 164 // during a single run through the observer only one upload operation occurs |
| 165 // (because it is costly) and similarly, that the writing of the uploaded |
| 166 // status in the key payload matches the upload operation. |
| 167 bool new_key = (mock_options & MOCK_NEW_KEY); |
| 168 if (new_key || !key_uploaded) { |
| 169 EXPECT_CALL(policy_client_, |
| 170 UploadCertificate(new_key ? "fake_cert" : certificate, _)) |
| 171 .WillOnce(WithArgs<1>(Invoke(StatusCallbackSuccess))); |
| 172 EXPECT_CALL(cryptohome_client_, |
| 173 TpmAttestationSetKeyPayload(_, _, payload, _)) |
| 174 .WillOnce(WithArgs<3>(Invoke(DBusCallbackTrue))); |
| 175 } |
| 176 |
| 177 // Setup expected key generations. Again use WillOnce(). Key generation is |
| 178 // another costly operation and if it gets triggered more than once during |
| 179 // a single pass this indicates a logical problem in the observer. |
| 180 if (new_key) { |
| 181 EXPECT_CALL(attestation_flow_, GetCertificate(_, _)) |
| 182 .WillOnce(WithArgs<1>(Invoke(CertCallbackSuccess))); |
| 183 } |
| 184 } |
| 185 |
74 void Run() { | 186 void Run() { |
75 AttestationPolicyObserver observer(&policy_client_, | 187 AttestationPolicyObserver observer(&policy_client_, |
76 &cryptohome_client_, | 188 &cryptohome_client_, |
77 &attestation_flow_); | 189 &attestation_flow_); |
78 base::RunLoop().RunUntilIdle(); | 190 base::RunLoop().RunUntilIdle(); |
79 } | 191 } |
80 | 192 |
| 193 std::string CreatePayload() { |
| 194 AttestationKeyPayload proto; |
| 195 proto.set_is_certificate_uploaded(true); |
| 196 std::string serialized; |
| 197 proto.SerializeToString(&serialized); |
| 198 return serialized; |
| 199 } |
| 200 |
| 201 bool CreateCertificate(CertExpiryOptions options, std::string* certificate) { |
| 202 base::Time valid_start = base::Time::Now() - base::TimeDelta::FromDays(90); |
| 203 base::Time valid_expiry; |
| 204 switch (options) { |
| 205 case CERT_VALID: |
| 206 valid_expiry = base::Time::Now() + base::TimeDelta::FromDays(90); |
| 207 break; |
| 208 case CERT_EXPIRING_SOON: |
| 209 valid_expiry = base::Time::Now() + base::TimeDelta::FromDays(20); |
| 210 break; |
| 211 case CERT_EXPIRED: |
| 212 valid_expiry = base::Time::Now() - base::TimeDelta::FromDays(20); |
| 213 break; |
| 214 default: |
| 215 NOTREACHED(); |
| 216 } |
| 217 scoped_ptr<crypto::RSAPrivateKey> test_key( |
| 218 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo( |
| 219 std::vector<uint8>(&kTestKeyData[0], |
| 220 &kTestKeyData[arraysize(kTestKeyData)]))); |
| 221 if (!test_key.get()) |
| 222 return false; |
| 223 net::X509Certificate::OSCertHandle handle = |
| 224 net::x509_util::CreateSelfSignedCert(test_key->public_key(), |
| 225 test_key->key(), |
| 226 "CN=subject", |
| 227 12345, |
| 228 valid_start, |
| 229 valid_expiry); |
| 230 |
| 231 if (!handle) |
| 232 return false; |
| 233 bool result = net::X509Certificate::GetDEREncoded(handle, certificate); |
| 234 net::X509Certificate::FreeOSCertHandle(handle); |
| 235 return result; |
| 236 } |
| 237 |
81 MessageLoop message_loop_; | 238 MessageLoop message_loop_; |
82 content::TestBrowserThread ui_thread_; | 239 content::TestBrowserThread ui_thread_; |
83 ScopedTestDeviceSettingsService test_device_settings_service_; | 240 ScopedTestDeviceSettingsService test_device_settings_service_; |
84 ScopedTestCrosSettings test_cros_settings_; | 241 ScopedTestCrosSettings test_cros_settings_; |
85 CrosSettingsProvider* device_settings_provider_; | 242 CrosSettingsProvider* device_settings_provider_; |
86 StubCrosSettingsProvider stub_settings_provider_; | 243 StubCrosSettingsProvider stub_settings_provider_; |
87 StrictMock<MockCryptohomeClient> cryptohome_client_; | 244 StrictMock<MockCryptohomeClient> cryptohome_client_; |
88 StrictMock<MockAttestationFlow> attestation_flow_; | 245 StrictMock<MockAttestationFlow> attestation_flow_; |
89 StrictMock<policy::MockCloudPolicyClient> policy_client_; | 246 StrictMock<policy::MockCloudPolicyClient> policy_client_; |
90 }; | 247 }; |
91 | 248 |
92 TEST_F(AttestationPolicyObserverTest, FeatureDisabled) { | 249 TEST_F(AttestationPolicyObserverTest, FeatureDisabled) { |
93 CrosSettings* cros_settings = CrosSettings::Get(); | 250 CrosSettings* cros_settings = CrosSettings::Get(); |
94 cros_settings->SetBoolean(kDeviceAttestationEnabled, false); | 251 cros_settings->SetBoolean(kDeviceAttestationEnabled, false); |
95 Run(); | 252 Run(); |
96 } | 253 } |
97 | 254 |
98 TEST_F(AttestationPolicyObserverTest, UnregisteredPolicyClient) { | 255 TEST_F(AttestationPolicyObserverTest, UnregisteredPolicyClient) { |
99 policy_client_.SetDMToken(""); | 256 policy_client_.SetDMToken(""); |
100 Run(); | 257 Run(); |
101 } | 258 } |
102 | 259 |
103 TEST_F(AttestationPolicyObserverTest, NewCertificate) { | 260 TEST_F(AttestationPolicyObserverTest, NewCertificate) { |
104 EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) | 261 SetupMocks(MOCK_NEW_KEY, ""); |
105 .WillOnce(WithArgs<2>(Invoke(DBusCallbackFalse))); | |
106 EXPECT_CALL(attestation_flow_, GetCertificate(_, _)) | |
107 .WillOnce(WithArgs<1>(Invoke(CertCallbackSuccess))); | |
108 Run(); | 262 Run(); |
109 } | 263 } |
110 | 264 |
111 TEST_F(AttestationPolicyObserverTest, KeyExists) { | 265 TEST_F(AttestationPolicyObserverTest, KeyExistsNotUploaded) { |
112 EXPECT_CALL(cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _)) | 266 std::string certificate; |
113 .WillOnce(WithArgs<2>(Invoke(DBusCallbackTrue))); | 267 ASSERT_TRUE(CreateCertificate(CERT_VALID, &certificate)); |
114 EXPECT_CALL(cryptohome_client_, TpmAttestationGetCertificate(_, _, _)) | 268 SetupMocks(MOCK_KEY_EXISTS, certificate); |
115 .WillOnce(WithArgs<2>(Invoke(DBusDataCallback))); | |
116 Run(); | 269 Run(); |
117 } | 270 } |
118 | 271 |
| 272 TEST_F(AttestationPolicyObserverTest, KeyExistsAlreadyUploaded) { |
| 273 std::string certificate; |
| 274 ASSERT_TRUE(CreateCertificate(CERT_VALID, &certificate)); |
| 275 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED, certificate); |
| 276 Run(); |
| 277 } |
| 278 |
| 279 TEST_F(AttestationPolicyObserverTest, KeyExistsCertExpiringSoon) { |
| 280 std::string certificate; |
| 281 ASSERT_TRUE(CreateCertificate(CERT_EXPIRING_SOON, &certificate)); |
| 282 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED | MOCK_NEW_KEY, certificate); |
| 283 Run(); |
| 284 } |
| 285 |
| 286 TEST_F(AttestationPolicyObserverTest, KeyExistsCertExpired) { |
| 287 std::string certificate; |
| 288 ASSERT_TRUE(CreateCertificate(CERT_EXPIRED, &certificate)); |
| 289 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED | MOCK_NEW_KEY, certificate); |
| 290 Run(); |
| 291 } |
| 292 |
| 293 TEST_F(AttestationPolicyObserverTest, IgnoreUnknownCertFormat) { |
| 294 SetupMocks(MOCK_KEY_EXISTS | MOCK_KEY_UPLOADED, "unsupported"); |
| 295 Run(); |
| 296 } |
| 297 |
119 } // namespace attestation | 298 } // namespace attestation |
120 } // namespace chromeos | 299 } // namespace chromeos |
OLD | NEW |