summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorcmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-29 21:28:11 +0000
committercmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-29 21:28:11 +0000
commit4ada238a76785ee78396cfb8e5992044c302de1e (patch)
tree5c6ad77fbf8b38164b18c987febe844e6a516177 /chrome
parent5066a78ce88613ce6f52e246ea22fb3cc772be05 (diff)
downloadchromium_src-4ada238a76785ee78396cfb8e5992044c302de1e.zip
chromium_src-4ada238a76785ee78396cfb8e5992044c302de1e.tar.gz
chromium_src-4ada238a76785ee78396cfb8e5992044c302de1e.tar.bz2
OwnerManager, allows use of OwnerKeyUtils to take ownership of a device
BUG=chromium-os:4485 TEST=unit tests Review URL: http://codereview.chromium.org/3058021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54199 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/chromeos/login/owner_manager.cc213
-rw-r--r--chrome/browser/chromeos/login/owner_manager.h155
-rw-r--r--chrome/browser/chromeos/login/owner_manager_unittest.cc352
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/notification_type.h4
6 files changed, 727 insertions, 0 deletions
diff --git a/chrome/browser/chromeos/login/owner_manager.cc b/chrome/browser/chromeos/login/owner_manager.cc
new file mode 100644
index 0000000..546952a3
--- /dev/null
+++ b/chrome/browser/chromeos/login/owner_manager.cc
@@ -0,0 +1,213 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/owner_manager.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
+
+namespace chromeos {
+
+OwnerManager::OwnerManager()
+ : private_key_(NULL),
+ public_key_(NULL),
+ utils_(OwnerKeyUtils::Create()) {
+}
+
+OwnerManager::~OwnerManager() {}
+
+bool OwnerManager::IsAlreadyOwned() {
+ return file_util::PathExists(utils_->GetOwnerKeyFilePath());
+}
+
+bool OwnerManager::StartLoadOwnerKeyAttempt() {
+ if (!IsAlreadyOwned()) {
+ LOG(ERROR) << "Device not yet owned";
+ return false;
+ }
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &OwnerManager::LoadOwnerKey));
+ return true;
+}
+
+bool OwnerManager::StartTakeOwnershipAttempt() {
+ if (IsAlreadyOwned()) {
+ LOG(ERROR) << "Device is already owned";
+ return false;
+ }
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &OwnerManager::GenerateKeysAndExportPublic));
+ return true;
+}
+
+bool OwnerManager::StartSigningAttempt(const std::string& data, Delegate* d) {
+ if (!IsAlreadyOwned()) {
+ LOG(ERROR) << "Device not yet owned";
+ return false;
+ }
+ ChromeThread::ID thread_id;
+ if (!ChromeThread::GetCurrentThreadIdentifier(&thread_id))
+ thread_id = ChromeThread::UI;
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &OwnerManager::Sign, thread_id, data, d));
+ return true;
+}
+
+bool OwnerManager::StartVerifyAttempt(const std::string& data,
+ const std::string& signature,
+ Delegate* d) {
+ if (!IsAlreadyOwned()) {
+ LOG(ERROR) << "Device not yet owned";
+ return false;
+ }
+ ChromeThread::ID thread_id;
+ if (!ChromeThread::GetCurrentThreadIdentifier(&thread_id))
+ thread_id = ChromeThread::UI;
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::Verify,
+ thread_id,
+ data,
+ signature,
+ d));
+ return true;
+}
+
+void OwnerManager::LoadOwnerKey() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+ public_key_ = utils_->ImportPublicKey(utils_->GetOwnerKeyFilePath());
+
+ // Whether we loaded the public key or not, send a notification indicating
+ // that we're done with this attempt. We send along the key if we
+ // got it, NULL if not.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::SendNotification,
+ NotificationType::OWNER_KEY_FETCH_ATTEMPT_COMPLETE,
+ Details<SECKEYPublicKey*>(&public_key_)));
+}
+
+void OwnerManager::GenerateKeysAndExportPublic() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+ public_key_ = NULL;
+ private_key_ = NULL;
+
+ if (utils_->GenerateKeyPair(&private_key_, &public_key_)) {
+ // If we generated the keys successfully, export them.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &OwnerManager::ExportKey));
+ } else {
+ // If we didn't generate the key, send along NULL with the notification
+ // that we're done with this attempt.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::SendNotification,
+ NotificationType::OWNER_KEY_FETCH_ATTEMPT_COMPLETE,
+ Details<SECKEYPublicKey*>(&public_key_)));
+ }
+}
+
+void OwnerManager::ExportKey() {
+ if (!utils_->ExportPublicKeyViaDbus(public_key_)) {
+ utils_->DestroyKeys(private_key_, public_key_);
+ private_key_ = NULL;
+ public_key_ = NULL;
+ }
+
+ // Whether we generated the keys or not, send a notification indicating
+ // that we're done with this attempt. We send along the public key if we
+ // got it, NULL if not.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::SendNotification,
+ NotificationType::OWNER_KEY_FETCH_ATTEMPT_COMPLETE,
+ Details<SECKEYPublicKey*>(&public_key_)));
+}
+
+bool OwnerManager::EnsurePublicKey() {
+ if (!public_key_)
+ LoadOwnerKey();
+
+ return public_key_ != NULL;
+}
+
+bool OwnerManager::EnsurePrivateKey() {
+ if (!EnsurePublicKey())
+ return false;
+
+ if (!private_key_)
+ private_key_ = utils_->FindPrivateKey(public_key_);
+
+ return private_key_ != NULL;
+}
+
+void OwnerManager::Sign(const ChromeThread::ID thread_id,
+ const std::string& data,
+ Delegate* d) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+
+ // If it's not the case that we can get both keys...
+ if (!(EnsurePublicKey() && EnsurePrivateKey())) {
+ ChromeThread::PostTask(
+ thread_id, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::CallDelegate,
+ d, KEY_UNAVAILABLE, std::string()));
+ return;
+ }
+
+ // TODO(cmasone): Sign |data| with |private_key_|, return
+ // appropriate errors via CallDelegate.
+ ChromeThread::PostTask(
+ thread_id, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::CallDelegate,
+ d, SUCCESS, data));
+}
+
+void OwnerManager::Verify(const ChromeThread::ID thread_id,
+ const std::string& data,
+ const std::string& signature,
+ Delegate* d) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+
+ if (!EnsurePublicKey()) {
+ ChromeThread::PostTask(
+ thread_id, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::CallDelegate,
+ d, KEY_UNAVAILABLE, std::string()));
+ return;
+ }
+
+ LOG(INFO) << "Starting verify attempt";
+ // TODO(cmasone): Verify |signature| over |data| with |public_key_|, return
+ // appropriate errors via CallDelegate.
+ ChromeThread::PostTask(
+ thread_id, FROM_HERE,
+ NewRunnableMethod(this,
+ &OwnerManager::CallDelegate,
+ d, SUCCESS, std::string()));
+}
+
+void OwnerManager::SendNotification(NotificationType type,
+ const NotificationDetails& details) {
+ NotificationService::current()->Notify(
+ type,
+ NotificationService::AllSources(),
+ details);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/owner_manager.h b/chrome/browser/chromeos/login/owner_manager.h
new file mode 100644
index 0000000..6ce7fc9
--- /dev/null
+++ b/chrome/browser/chromeos/login/owner_manager.h
@@ -0,0 +1,155 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_OWNER_MANAGER_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_OWNER_MANAGER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/chromeos/login/owner_key_utils.h"
+#include "chrome/browser/chrome_thread.h"
+
+class FilePath;
+class NotificationDetails;
+class NotificationType;
+
+namespace chromeos {
+
+// This class allows the registration of an Owner of a Chromium OS device.
+// It handles generating the appropriate keys and storing them in the
+// appropriate locations.
+class OwnerManager : public base::RefCountedThreadSafe<OwnerManager> {
+ public:
+ // Return codes for public/private key operations.
+ enum KeyOpCode {
+ SUCCESS,
+ KEY_UNAVAILABLE, // The necessary key isn't available yet.
+ OPERATION_FAILED // The crypto operation failed.
+ };
+
+ class Delegate {
+ public:
+ // Upon completion of a key operation, this method will be called.
+ // |return_code| indicates what happened, |payload| will be used to pass
+ // back any artifacts of the operation. For example, if the operation
+ // was a signature attempt, the signature blob would come back in |payload|.
+ virtual void OnKeyOpComplete(const KeyOpCode return_code,
+ const std::string& payload) = 0;
+ };
+
+ OwnerManager();
+ virtual ~OwnerManager();
+
+ bool IsAlreadyOwned();
+
+ // If the device has been owned already, posts a task to the FILE thread to
+ // fetch the public key off disk.
+ // Returns true if the attempt was initiated, false otherwise.
+ //
+ // Sends out a OWNER_KEY_FETCH_ATTEMPT_COMPLETE notification on completion.
+ // Notification comes with a Details<SECKEYPublicKey*> that contains a pointer
+ // to the public key, or NULL if the fetch attempt failed.
+ bool StartLoadOwnerKeyAttempt();
+
+ // If the device has not yet been owned, posts a task to the FILE
+ // thread to generate the owner's keys and put them in the right
+ // places. Keeps them in memory as well, for later use.
+ // Returns true if the attempt was initiated, false otherwise.
+ //
+ // Sends out a OWNER_KEY_FETCH_ATTEMPT_COMPLETE notification on completion.
+ // Notification comes with a Details<SECKEYPublicKey*> that contains a pointer
+ // to the public key, or NULL if the fetch attempt failed.
+ bool StartTakeOwnershipAttempt();
+
+ // Initiate an attempt to sign |data| with |private_key_|. Will call
+ // d->OnKeyOpComplete() when done. Upon success, the signature will be passed
+ // as the |payload| argument to d->OnKeyOpComplete().
+ // Returns true if the attempt was initiated, false otherwise.
+ //
+ // If you call this on a well-known thread, you'll be called back on that
+ // thread. Otherwise, you'll get called back on the UI thread.
+ bool StartSigningAttempt(const std::string& data, Delegate* d);
+
+ // Initiate an attempt to verify that |signature| is valid over |data| with
+ // |public_key_|. When the attempt is completed, an appropriate KeyOpCode
+ // will be passed to d->OnKeyOpComplete().
+ // Returns true if the attempt was initiated, false otherwise.
+ //
+ // If you call this on a well-known thread, you'll be called back on that
+ // thread. Otherwise, you'll get called back on the UI thread.
+ bool StartVerifyAttempt(const std::string& data,
+ const std::string& signature,
+ Delegate* d);
+
+ private:
+ // Pulls the owner's public key off disk and into memory.
+ //
+ // Call this on the FILE thread.
+ void LoadOwnerKey();
+
+ // Generates the owner's keys in the default NSS token. Also stores
+ // them in |public_key_| and |private_key_|. When done, causes the
+ // public key to get exported via DBus.
+ //
+ // Call this on the FILE thread.
+ void GenerateKeysAndExportPublic();
+
+ // Exports |public_key_| via DBus.
+ //
+ // Call this on the UI thread (because of DBus usage).
+ void ExportKey();
+
+ bool EnsurePublicKey();
+ bool EnsurePrivateKey();
+
+ // Do the actual work of signing |data| with |private_key_|. First,
+ // ensures that we have the keys we need. Then, computes the signature.
+ //
+ // On success, calls d->OnKeyOpComplete() on |thread_id| with a
+ // successful return code, passing the signaure blob in |payload|.
+ // On failure, calls d->OnKeyOpComplete() on |thread_id| with an appropriate
+ // error and passes an empty string for |payload|.
+ void Sign(const ChromeThread::ID thread_id,
+ const std::string& data,
+ Delegate* d);
+
+ // Do the actual work of verifying that |signature| is valid over
+ // |data| with |public_key_|. First, ensures we have the key we
+ // need, then does the verify.
+ //
+ // On success, calls d->OnKeyOpComplete() on |thread_id| with a
+ // successful return code, passing an empty string for |payload|.
+ // On failure, calls d->OnKeyOpComplete() on |thread_id| with an appropriate
+ // error code, passing an empty string for |payload|.
+ void Verify(const ChromeThread::ID thread_id,
+ const std::string& data,
+ const std::string& signature,
+ Delegate* d);
+
+ // A helper method to send a notification on another thread.
+ void SendNotification(NotificationType type,
+ const NotificationDetails& details);
+
+ // A helper method to call back a delegte on another thread.
+ void CallDelegate(Delegate* d,
+ const KeyOpCode return_code,
+ const std::string& payload) {
+ d->OnKeyOpComplete(return_code, payload);
+ }
+
+ SECKEYPrivateKey* private_key_;
+ SECKEYPublicKey* public_key_;
+
+ scoped_ptr<OwnerKeyUtils> utils_;
+
+ friend class OwnerManagerTest;
+
+ DISALLOW_COPY_AND_ASSIGN(OwnerManager);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_LOGIN_OWNER_MANAGER_H_
diff --git a/chrome/browser/chromeos/login/owner_manager_unittest.cc b/chrome/browser/chromeos/login/owner_manager_unittest.cc
new file mode 100644
index 0000000..1db81e9
--- /dev/null
+++ b/chrome/browser/chromeos/login/owner_manager_unittest.cc
@@ -0,0 +1,352 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/login/owner_manager.h"
+
+#include <cert.h>
+#include <keyhi.h>
+#include <keythi.h> // KeyType enum
+#include <pk11pub.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/nss_util_internal.h"
+#include "base/nss_util.h"
+#include "base/scoped_temp_dir.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::_;
+
+namespace chromeos {
+
+namespace {
+
+class MockKeyUtils : public OwnerKeyUtils {
+ public:
+ MockKeyUtils() {}
+ ~MockKeyUtils() {}
+ MOCK_METHOD2(GenerateKeyPair, bool(SECKEYPrivateKey** private_key_out,
+ SECKEYPublicKey** public_key_out));
+ MOCK_METHOD1(ExportPublicKeyViaDbus, bool(SECKEYPublicKey* key));
+ MOCK_METHOD2(ExportPublicKeyToFile, bool(SECKEYPublicKey* key,
+ const FilePath& key_file));
+ MOCK_METHOD1(ImportPublicKey, SECKEYPublicKey*(const FilePath& key_file));
+ MOCK_METHOD1(FindPrivateKey, SECKEYPrivateKey*(SECKEYPublicKey* key));
+ MOCK_METHOD2(DestroyKeys, void(SECKEYPrivateKey* private_key,
+ SECKEYPublicKey* public_key));
+ MOCK_METHOD0(GetOwnerKeyFilePath, FilePath());
+};
+
+class MockInjector : public OwnerKeyUtils::Factory {
+ public:
+ // Takes ownership of |mock|.
+ explicit MockInjector(MockKeyUtils* mock) :
+ transient_(mock),
+ delete_transient_(true) {
+ }
+
+ virtual ~MockInjector() {
+ if (delete_transient_)
+ delete transient_;
+ }
+
+ // If this is called, its caller takes ownership of |transient_|.
+ // If it's never called, |transient_| remains our problem.
+ OwnerKeyUtils* CreateOwnerKeyUtils() {
+ delete_transient_ = false;
+ return transient_;
+ }
+
+ private:
+ MockKeyUtils* transient_;
+ bool delete_transient_;
+};
+
+class KeyUser : public OwnerManager::Delegate {
+ public:
+ explicit KeyUser(const OwnerManager::KeyOpCode expected)
+ : expected_(expected) {
+ }
+
+ virtual ~KeyUser() {}
+
+ void OnKeyOpComplete(const OwnerManager::KeyOpCode return_code,
+ const std::string& payload) {
+ MessageLoop::current()->Quit();
+ EXPECT_EQ(expected_, return_code);
+ }
+
+ const OwnerManager::KeyOpCode expected_;
+};
+
+static bool Win(SECKEYPublicKey* key) {
+ MessageLoop::current()->Quit();
+ return true;
+}
+
+static bool Fail(SECKEYPublicKey* key) {
+ MessageLoop::current()->Quit();
+ return false;
+}
+
+} // anonymous namespace
+
+class OwnerManagerTest : public ::testing::Test,
+ public NotificationObserver {
+ public:
+ OwnerManagerTest()
+ : message_loop_(MessageLoop::TYPE_UI),
+ ui_thread_(ChromeThread::UI, &message_loop_),
+ file_thread_(ChromeThread::FILE),
+ fake_public_key_(reinterpret_cast<SECKEYPublicKey*>(7)),
+ fake_private_key_(reinterpret_cast<SECKEYPrivateKey*>(7)),
+ success_expected_(false),
+ quit_on_observe_(true),
+ mock_(new MockKeyUtils),
+ injector_(mock_) /* injector_ takes ownership of mock_ */ {
+ registrar_.Add(
+ this,
+ NotificationType::OWNER_KEY_FETCH_ATTEMPT_COMPLETE,
+ NotificationService::AllSources());
+ }
+ virtual ~OwnerManagerTest() {}
+
+ virtual void SetUp() {
+ // Mimic ownership.
+ ASSERT_TRUE(tmpdir_.CreateUniqueTempDir());
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir_.path(), &tmpfile_));
+
+ file_thread_.Start();
+ OwnerKeyUtils::set_factory(&injector_);
+ }
+
+ virtual void TearDown() {
+ OwnerKeyUtils::set_factory(NULL);
+ }
+
+ void StartUnowned() {
+ file_util::Delete(tmpfile_, false);
+ }
+
+ void InjectKeys(OwnerManager* manager) {
+ manager->public_key_ = fake_public_key_;
+ manager->private_key_ = fake_private_key_;
+ }
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_COMPLETE) {
+ EXPECT_EQ(success_expected_,
+ NULL != *Details<SECKEYPublicKey*>(details).ptr());
+ if (quit_on_observe_)
+ MessageLoop::current()->Quit();
+ }
+ }
+
+ void ExpectKeyFetchSuccess(bool should_succeed) {
+ success_expected_ = should_succeed;
+ }
+ void SetQuitOnKeyFetch(bool should_quit) { quit_on_observe_ = should_quit; }
+
+ ScopedTempDir tmpdir_;
+ FilePath tmpfile_;
+
+ MessageLoop message_loop_;
+ ChromeThread ui_thread_;
+ ChromeThread file_thread_;
+
+ SECKEYPublicKey* fake_public_key_;
+ SECKEYPrivateKey* fake_private_key_;
+
+ NotificationRegistrar registrar_;
+ bool success_expected_;
+ bool quit_on_observe_;
+
+ MockKeyUtils* mock_;
+ MockInjector injector_;
+};
+
+TEST_F(OwnerManagerTest, LoadKeyUnowned) {
+ StartUnowned();
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ EXPECT_FALSE(manager->StartLoadOwnerKeyAttempt());
+}
+
+TEST_F(OwnerManagerTest, LoadOwnerKeyFail) {
+ SECKEYPublicKey* to_return = NULL;
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_))
+ .WillOnce(Return(to_return))
+ .RetiresOnSaturation();
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ EXPECT_TRUE(manager->StartLoadOwnerKeyAttempt());
+
+ // Run remaining events, until ExportPublicKeyViaDbus().
+ message_loop_.Run();
+}
+
+TEST_F(OwnerManagerTest, LoadOwnerKey) {
+ ExpectKeyFetchSuccess(true);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_))
+ .WillOnce(Return(fake_public_key_))
+ .RetiresOnSaturation();
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ EXPECT_TRUE(manager->StartLoadOwnerKeyAttempt());
+
+ // Run remaining events, until ExportPublicKeyViaDbus().
+ message_loop_.Run();
+}
+
+TEST_F(OwnerManagerTest, TakeOwnershipAlreadyOwned) {
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ EXPECT_FALSE(manager->StartTakeOwnershipAttempt());
+}
+
+TEST_F(OwnerManagerTest, KeyGenerationFail) {
+ StartUnowned();
+
+ EXPECT_CALL(*mock_, GenerateKeyPair(_, _))
+ .WillOnce(Return(false))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ EXPECT_TRUE(manager->StartTakeOwnershipAttempt());
+
+ message_loop_.Run();
+}
+
+TEST_F(OwnerManagerTest, KeyExportFail) {
+ StartUnowned();
+
+ EXPECT_CALL(*mock_, GenerateKeyPair(_, _))
+ .WillOnce(Return(true))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, ExportPublicKeyViaDbus(_))
+ .WillOnce(Invoke(Fail))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, DestroyKeys(_, _))
+ .Times(1)
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ EXPECT_TRUE(manager->StartTakeOwnershipAttempt());
+
+ message_loop_.Run();
+}
+
+TEST_F(OwnerManagerTest, TakeOwnership) {
+ StartUnowned();
+ ExpectKeyFetchSuccess(true);
+
+ EXPECT_CALL(*mock_, GenerateKeyPair(_, _))
+ .WillOnce(DoAll(SetArgumentPointee<1>(fake_public_key_),
+ Return(true)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, ExportPublicKeyViaDbus(_))
+ .WillOnce(Invoke(Win))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ EXPECT_TRUE(manager->StartTakeOwnershipAttempt());
+
+ message_loop_.Run();
+}
+
+TEST_F(OwnerManagerTest, NotYetOwnedVerify) {
+ StartUnowned();
+
+ // Since this shouldn't happen, don't want it to end the test if it does.
+ SetQuitOnKeyFetch(false);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ KeyUser delegate(OwnerManager::KEY_UNAVAILABLE);
+ EXPECT_FALSE(manager->StartVerifyAttempt("", "", &delegate));
+}
+
+TEST_F(OwnerManagerTest, AlreadyHaveKeysVerify) {
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ InjectKeys(manager.get());
+ KeyUser delegate(OwnerManager::SUCCESS);
+ EXPECT_TRUE(manager->StartVerifyAttempt("", "", &delegate));
+
+ message_loop_.Run();
+}
+
+TEST_F(OwnerManagerTest, GetKeyFailDuringVerify) {
+ ExpectKeyFetchSuccess(false);
+ SECKEYPublicKey* to_return = NULL;
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_))
+ .WillOnce(Return(to_return))
+ .RetiresOnSaturation();
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ KeyUser delegate(OwnerManager::KEY_UNAVAILABLE);
+ EXPECT_TRUE(manager->StartVerifyAttempt("", "", &delegate));
+
+ message_loop_.Run();
+}
+
+TEST_F(OwnerManagerTest, GetKeyAndVerify) {
+ ExpectKeyFetchSuccess(true);
+ SetQuitOnKeyFetch(false);
+
+ EXPECT_CALL(*mock_, GetOwnerKeyFilePath())
+ .WillRepeatedly(Return(tmpfile_));
+ EXPECT_CALL(*mock_, ImportPublicKey(tmpfile_))
+ .WillOnce(Return(fake_public_key_))
+ .RetiresOnSaturation();
+
+ scoped_refptr<OwnerManager> manager(new OwnerManager);
+ KeyUser delegate(OwnerManager::SUCCESS);
+ EXPECT_TRUE(manager->StartVerifyAttempt("", "", &delegate));
+
+ message_loop_.Run();
+}
+
+} // namespace chromeos
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index b2bd6c5..021028e 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -492,6 +492,8 @@
'browser/chromeos/login/oobe_progress_bar.h',
'browser/chromeos/login/owner_key_utils.cc',
'browser/chromeos/login/owner_key_utils.h',
+ 'browser/chromeos/login/owner_manager.cc',
+ 'browser/chromeos/login/owner_manager.h',
'browser/chromeos/login/password_changed_view.cc',
'browser/chromeos/login/password_changed_view.h',
'browser/chromeos/login/registration_screen.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index f35e03a..b61efcf 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -739,6 +739,7 @@
'browser/chromeos/login/google_authenticator_unittest.cc',
'browser/chromeos/login/mock_auth_response_handler.cc',
'browser/chromeos/login/owner_key_utils_unittest.cc',
+ 'browser/chromeos/login/owner_manager_unittest.cc',
'browser/chromeos/notifications/desktop_notifications_unittest.cc',
'browser/chromeos/offline/offline_load_page_unittest.cc',
'browser/chromeos/options/language_config_model_unittest.cc',
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 36aca8b..188f3b9 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -1068,6 +1068,10 @@ class NotificationType {
// in chrome/browser/chromeos/network_state_notifier.h.
// TODO(oshima): Port this to all platforms.
NETWORK_STATE_CHANGED,
+
+ // Sent when an attempt to acquire the public key of the owner of a chromium
+ // os device has completed. Details are a boolean value indicating success.
+ OWNER_KEY_FETCH_ATTEMPT_COMPLETE,
#endif
// Sent before the repost form warning is brought up.