OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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 "chrome/browser/chromeos/login/owner_manager.h" |
| 6 |
| 7 #include <cert.h> |
| 8 #include <keyhi.h> |
| 9 #include <keythi.h> // KeyType enum |
| 10 #include <pk11pub.h> |
| 11 #include <stdlib.h> |
| 12 |
| 13 #include <string> |
| 14 |
| 15 #include "base/file_path.h" |
| 16 #include "base/file_util.h" |
| 17 #include "base/logging.h" |
| 18 #include "base/nss_util_internal.h" |
| 19 #include "base/nss_util.h" |
| 20 #include "base/scoped_temp_dir.h" |
| 21 #include "chrome/browser/chrome_thread.h" |
| 22 #include "chrome/common/notification_registrar.h" |
| 23 #include "chrome/common/notification_service.h" |
| 24 #include "chrome/common/notification_type.h" |
| 25 #include "testing/gmock/include/gmock/gmock.h" |
| 26 #include "testing/gtest/include/gtest/gtest.h" |
| 27 |
| 28 using ::testing::DoAll; |
| 29 using ::testing::Invoke; |
| 30 using ::testing::Return; |
| 31 using ::testing::SetArgumentPointee; |
| 32 using ::testing::_; |
| 33 |
| 34 namespace chromeos { |
| 35 |
| 36 namespace { |
| 37 |
| 38 class MockKeyUtils : public OwnerKeyUtils { |
| 39 public: |
| 40 MockKeyUtils() {} |
| 41 ~MockKeyUtils() {} |
| 42 MOCK_METHOD2(GenerateKeyPair, bool(SECKEYPrivateKey** private_key_out, |
| 43 SECKEYPublicKey** public_key_out)); |
| 44 MOCK_METHOD1(ExportPublicKeyViaDbus, bool(SECKEYPublicKey* key)); |
| 45 MOCK_METHOD2(ExportPublicKeyToFile, bool(SECKEYPublicKey* key, |
| 46 const FilePath& key_file)); |
| 47 MOCK_METHOD1(ImportPublicKey, SECKEYPublicKey*(const FilePath& key_file)); |
| 48 MOCK_METHOD1(FindPrivateKey, SECKEYPrivateKey*(SECKEYPublicKey* key)); |
| 49 MOCK_METHOD2(DestroyKeys, void(SECKEYPrivateKey* private_key, |
| 50 SECKEYPublicKey* public_key)); |
| 51 MOCK_METHOD0(GetOwnerKeyFilePath, FilePath()); |
| 52 }; |
| 53 |
| 54 class MockInjector : public OwnerKeyUtils::Factory { |
| 55 public: |
| 56 // Takes ownership of |mock|. |
| 57 explicit MockInjector(MockKeyUtils* mock) : |
| 58 transient_(mock), |
| 59 delete_transient_(true) { |
| 60 } |
| 61 |
| 62 virtual ~MockInjector() { |
| 63 if (delete_transient_) |
| 64 delete transient_; |
| 65 } |
| 66 |
| 67 // If this is called, its caller takes ownership of |transient_|. |
| 68 // If it's never called, |transient_| remains our problem. |
| 69 OwnerKeyUtils* CreateOwnerKeyUtils() { |
| 70 delete_transient_ = false; |
| 71 return transient_; |
| 72 } |
| 73 |
| 74 private: |
| 75 MockKeyUtils* transient_; |
| 76 bool delete_transient_; |
| 77 }; |
| 78 |
| 79 class KeyUser : public OwnerManager::Delegate { |
| 80 public: |
| 81 explicit KeyUser(const OwnerManager::KeyOpCode expected) |
| 82 : expected_(expected) { |
| 83 } |
| 84 |
| 85 virtual ~KeyUser() {} |
| 86 |
| 87 void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code, |
| 88 const std::string& payload) { |
| 89 MessageLoop::current()->Quit(); |
| 90 EXPECT_EQ(expected_, return_code); |
| 91 } |
| 92 |
| 93 const OwnerManager::KeyOpCode expected_; |
| 94 }; |
| 95 |
| 96 static bool Win(SECKEYPublicKey* key) { |
| 97 MessageLoop::current()->Quit(); |
| 98 return true; |
| 99 } |
| 100 |
| 101 static bool Fail(SECKEYPublicKey* key) { |
| 102 MessageLoop::current()->Quit(); |
| 103 return false; |
| 104 } |
| 105 |
| 106 } // anonymous namespace |
| 107 |
| 108 class OwnerManagerTest : public ::testing::Test, |
| 109 public NotificationObserver { |
| 110 public: |
| 111 OwnerManagerTest() |
| 112 : message_loop_(MessageLoop::TYPE_UI), |
| 113 ui_thread_(ChromeThread::UI, &message_loop_), |
| 114 file_thread_(ChromeThread::FILE), |
| 115 fake_public_key_(reinterpret_cast<SECKEYPublicKey*>(7)), |
| 116 fake_private_key_(reinterpret_cast<SECKEYPrivateKey*>(7)), |
| 117 success_expected_(false), |
| 118 quit_on_observe_(true), |
| 119 mock_(new MockKeyUtils), |
| 120 injector_(mock_) /* injector_ takes ownership of mock_ */ { |
| 121 registrar_.Add( |
| 122 this, |
| 123 NotificationType::OWNER_KEY_FETCH_ATTEMPT_COMPLETE, |
| 124 NotificationService::AllSources()); |
| 125 } |
| 126 virtual ~OwnerManagerTest() {} |
| 127 |
| 128 virtual void SetUp() { |
| 129 // Mimic ownership. |
| 130 ASSERT_TRUE(tmpdir_.CreateUniqueTempDir()); |
| 131 ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir_.path(), &tmpfile_)); |
| 132 |
| 133 file_thread_.Start(); |
| 134 OwnerKeyUtils::set_factory(&injector_); |
| 135 } |
| 136 |
| 137 virtual void TearDown() { |
| 138 OwnerKeyUtils::set_factory(NULL); |
| 139 } |
| 140 |
| 141 void StartUnowned() { |
| 142 file_util::Delete(tmpfile_, false); |
| 143 } |
| 144 |
| 145 void InjectKeys(OwnerManager* manager) { |
| 146 manager->public_key_ = fake_public_key_; |
| 147 manager->private_key_ = fake_private_key_; |
| 148 } |
| 149 |
| 150 // NotificationObserver implementation. |
| 151 virtual void Observe(NotificationType type, |
| 152 const NotificationSource& source, |
| 153 const NotificationDetails& details) { |
| 154 if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_COMPLETE) { |
| 155 EXPECT_EQ(success_expected_, |
| 156 NULL != *Details<SECKEYPublicKey*>(details).ptr()); |
| 157 if (quit_on_observe_) |
| 158 MessageLoop::current()->Quit(); |
| 159 } |
| 160 } |
| 161 |
| 162 void ExpectKeyFetchSuccess(bool should_succeed) { |
| 163 success_expected_ = should_succeed; |
| 164 } |
| 165 void SetQuitOnKeyFetch(bool should_quit) { quit_on_observe_ = should_quit; } |
| 166 |
| 167 ScopedTempDir tmpdir_; |
| 168 FilePath tmpfile_; |
| 169 |
| 170 MessageLoop message_loop_; |
| 171 ChromeThread ui_thread_; |
| 172 ChromeThread file_thread_; |
| 173 |
| 174 SECKEYPublicKey* fake_public_key_; |
| 175 SECKEYPrivateKey* fake_private_key_; |
| 176 |
| 177 NotificationRegistrar registrar_; |
| 178 bool success_expected_; |
| 179 bool quit_on_observe_; |
| 180 |
| 181 MockKeyUtils* mock_; |
| 182 MockInjector injector_; |
| 183 }; |
| 184 |
| 185 TEST_F(OwnerManagerTest, LoadKeyUnowned) { |
| 186 StartUnowned(); |
| 187 |
| 188 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 189 .WillRepeatedly(Return(tmpfile_)); |
| 190 |
| 191 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 192 EXPECT_FALSE(manager->StartLoadOwnerKeyAttempt()); |
| 193 } |
| 194 |
| 195 TEST_F(OwnerManagerTest, LoadOwnerKeyFail) { |
| 196 SECKEYPublicKey* to_return = NULL; |
| 197 |
| 198 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 199 .WillRepeatedly(Return(tmpfile_)); |
| 200 EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_)) |
| 201 .WillOnce(Return(to_return)) |
| 202 .RetiresOnSaturation(); |
| 203 |
| 204 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 205 EXPECT_TRUE(manager->StartLoadOwnerKeyAttempt()); |
| 206 |
| 207 // Run remaining events, until ExportPublicKeyViaDbus(). |
| 208 message_loop_.Run(); |
| 209 } |
| 210 |
| 211 TEST_F(OwnerManagerTest, LoadOwnerKey) { |
| 212 ExpectKeyFetchSuccess(true); |
| 213 |
| 214 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 215 .WillRepeatedly(Return(tmpfile_)); |
| 216 EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_)) |
| 217 .WillOnce(Return(fake_public_key_)) |
| 218 .RetiresOnSaturation(); |
| 219 |
| 220 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 221 EXPECT_TRUE(manager->StartLoadOwnerKeyAttempt()); |
| 222 |
| 223 // Run remaining events, until ExportPublicKeyViaDbus(). |
| 224 message_loop_.Run(); |
| 225 } |
| 226 |
| 227 TEST_F(OwnerManagerTest, TakeOwnershipAlreadyOwned) { |
| 228 |
| 229 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 230 .WillRepeatedly(Return(tmpfile_)); |
| 231 |
| 232 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 233 EXPECT_FALSE(manager->StartTakeOwnershipAttempt()); |
| 234 } |
| 235 |
| 236 TEST_F(OwnerManagerTest, KeyGenerationFail) { |
| 237 StartUnowned(); |
| 238 |
| 239 EXPECT_CALL(*mock_, GenerateKeyPair(_, _)) |
| 240 .WillOnce(Return(false)) |
| 241 .RetiresOnSaturation(); |
| 242 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 243 .WillRepeatedly(Return(tmpfile_)); |
| 244 |
| 245 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 246 EXPECT_TRUE(manager->StartTakeOwnershipAttempt()); |
| 247 |
| 248 message_loop_.Run(); |
| 249 } |
| 250 |
| 251 TEST_F(OwnerManagerTest, KeyExportFail) { |
| 252 StartUnowned(); |
| 253 |
| 254 EXPECT_CALL(*mock_, GenerateKeyPair(_, _)) |
| 255 .WillOnce(Return(true)) |
| 256 .RetiresOnSaturation(); |
| 257 EXPECT_CALL(*mock_, ExportPublicKeyViaDbus(_)) |
| 258 .WillOnce(Invoke(Fail)) |
| 259 .RetiresOnSaturation(); |
| 260 EXPECT_CALL(*mock_, DestroyKeys(_, _)) |
| 261 .Times(1) |
| 262 .RetiresOnSaturation(); |
| 263 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 264 .WillRepeatedly(Return(tmpfile_)); |
| 265 |
| 266 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 267 EXPECT_TRUE(manager->StartTakeOwnershipAttempt()); |
| 268 |
| 269 message_loop_.Run(); |
| 270 } |
| 271 |
| 272 TEST_F(OwnerManagerTest, TakeOwnership) { |
| 273 StartUnowned(); |
| 274 ExpectKeyFetchSuccess(true); |
| 275 |
| 276 EXPECT_CALL(*mock_, GenerateKeyPair(_, _)) |
| 277 .WillOnce(DoAll(SetArgumentPointee<1>(fake_public_key_), |
| 278 Return(true))) |
| 279 .RetiresOnSaturation(); |
| 280 EXPECT_CALL(*mock_, ExportPublicKeyViaDbus(_)) |
| 281 .WillOnce(Invoke(Win)) |
| 282 .RetiresOnSaturation(); |
| 283 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 284 .WillRepeatedly(Return(tmpfile_)); |
| 285 |
| 286 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 287 EXPECT_TRUE(manager->StartTakeOwnershipAttempt()); |
| 288 |
| 289 message_loop_.Run(); |
| 290 } |
| 291 |
| 292 TEST_F(OwnerManagerTest, NotYetOwnedVerify) { |
| 293 StartUnowned(); |
| 294 |
| 295 // Since this shouldn't happen, don't want it to end the test if it does. |
| 296 SetQuitOnKeyFetch(false); |
| 297 |
| 298 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 299 .WillRepeatedly(Return(tmpfile_)); |
| 300 |
| 301 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 302 KeyUser delegate(OwnerManager::KEY_UNAVAILABLE); |
| 303 EXPECT_FALSE(manager->StartVerifyAttempt("", "", &delegate)); |
| 304 } |
| 305 |
| 306 TEST_F(OwnerManagerTest, AlreadyHaveKeysVerify) { |
| 307 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 308 .WillRepeatedly(Return(tmpfile_)); |
| 309 |
| 310 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 311 InjectKeys(manager.get()); |
| 312 KeyUser delegate(OwnerManager::SUCCESS); |
| 313 EXPECT_TRUE(manager->StartVerifyAttempt("", "", &delegate)); |
| 314 |
| 315 message_loop_.Run(); |
| 316 } |
| 317 |
| 318 TEST_F(OwnerManagerTest, GetKeyFailDuringVerify) { |
| 319 ExpectKeyFetchSuccess(false); |
| 320 SECKEYPublicKey* to_return = NULL; |
| 321 |
| 322 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 323 .WillRepeatedly(Return(tmpfile_)); |
| 324 EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_)) |
| 325 .WillOnce(Return(to_return)) |
| 326 .RetiresOnSaturation(); |
| 327 |
| 328 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 329 KeyUser delegate(OwnerManager::KEY_UNAVAILABLE); |
| 330 EXPECT_TRUE(manager->StartVerifyAttempt("", "", &delegate)); |
| 331 |
| 332 message_loop_.Run(); |
| 333 } |
| 334 |
| 335 TEST_F(OwnerManagerTest, GetKeyAndVerify) { |
| 336 ExpectKeyFetchSuccess(true); |
| 337 SetQuitOnKeyFetch(false); |
| 338 |
| 339 EXPECT_CALL(*mock_, GetOwnerKeyFilePath()) |
| 340 .WillRepeatedly(Return(tmpfile_)); |
| 341 EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_)) |
| 342 .WillOnce(Return(fake_public_key_)) |
| 343 .RetiresOnSaturation(); |
| 344 |
| 345 scoped_refptr<OwnerManager> manager(new OwnerManager); |
| 346 KeyUser delegate(OwnerManager::SUCCESS); |
| 347 EXPECT_TRUE(manager->StartVerifyAttempt("", "", &delegate)); |
| 348 |
| 349 message_loop_.Run(); |
| 350 } |
| 351 |
| 352 } // namespace chromeos |
OLD | NEW |