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