summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorcmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-21 04:46:51 +0000
committercmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-21 04:46:51 +0000
commit501a21ec8f0afa4da8ee7f6b139c39deb6a23c74 (patch)
tree76a692f20fac8fc8cef6e3dd72eada1ec85ba072 /chrome
parent594cd7d54eac55129bffd0c273675e643c6838b6 (diff)
downloadchromium_src-501a21ec8f0afa4da8ee7f6b139c39deb6a23c74.zip
chromium_src-501a21ec8f0afa4da8ee7f6b139c39deb6a23c74.tar.gz
chromium_src-501a21ec8f0afa4da8ee7f6b139c39deb6a23c74.tar.bz2
The owner of a chrome os device will have an RSA keypair that we'll use for various purposes. This CL implements some helper functions that we'll use to generate this key and store the halves in the places we need them.
BUG=chromium-os:4485 TEST=unit tests Review URL: http://codereview.chromium.org/2891001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53153 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.h83
-rw-r--r--chrome/browser/chromeos/login/owner_manager_unittest.cc112
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
5 files changed, 411 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..79b34c31
--- /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 <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo()
+#include <pk11pub.h>
+#include <prerror.h> // PR_GetError()
+#include <secder.h> // DER_Encode()
+#include <secmod.h>
+
+#include <limits>
+
+#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_ptr.h"
+#include "base/string_util.h"
+
+// static
+const char OwnerManager::kOwnerKeyFile[] = "/var/lib/whitelist/owner.key";
+
+// We're generating and using 2048-bit RSA keys.
+// static
+const uint32 OwnerManager::kKeyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
+// static
+const unsigned long OwnerManager::kExponent = 65537UL;
+// static
+const int OwnerManager::kKeySizeInBits = 2048;
+
+// static
+bool OwnerManager::GenerateKeyPair(SECKEYPrivateKey** private_key_out,
+ SECKEYPublicKey** public_key_out) {
+ DCHECK(private_key_out);
+ DCHECK(public_key_out);
+
+ *private_key_out = NULL;
+ *public_key_out = NULL;
+
+ // Temporary structures used for generating the result
+ // in the right format.
+ PK11SlotInfo* slot = NULL;
+ PK11RSAGenParams rsa_key_gen_params;
+ void* key_gen_params;
+
+ bool is_success = true; // Set to false as soon as a step fails.
+
+ rsa_key_gen_params.keySizeInBits = kKeySizeInBits;
+ rsa_key_gen_params.pe = kExponent;
+ key_gen_params = &rsa_key_gen_params;
+
+ // Ensure NSS is initialized.
+ base::EnsureNSSInit();
+
+ slot = base::GetDefaultNSSKeySlot();
+ if (!slot) {
+ LOG(ERROR) << "Couldn't get Internal key slot!";
+ is_success = false;
+ goto failure;
+ }
+
+ // Need to make sure that the token was initialized.
+ // Assume a null password.
+ if (SECSuccess != PK11_Authenticate(slot, PR_TRUE, NULL)) {
+ LOG(ERROR) << "Couldn't initialze PK11 token!";
+ is_success = false;
+ goto failure;
+ }
+
+ LOG(INFO) << "Creating key pair...";
+ {
+ base::AutoNSSWriteLock lock;
+ *private_key_out = PK11_GenerateKeyPair(slot,
+ kKeyGenMechanism,
+ key_gen_params,
+ public_key_out,
+ PR_TRUE, // isPermanent?
+ PR_TRUE, // isSensitive?
+ NULL);
+ }
+ LOG(INFO) << "done.";
+
+ if (!*private_key_out) {
+ LOG(INFO) << "Generation of Keypair failed!";
+ is_success = false;
+ goto failure;
+ }
+
+ failure:
+ if (!is_success) {
+ LOG(ERROR) << "Owner key generation failed! (NSS error code "
+ << PR_GetError() << ")";
+ // Do cleanups
+ base::AutoNSSWriteLock lock;
+ if (*private_key_out) {
+ PK11_DestroyTokenObject((*private_key_out)->pkcs11Slot,
+ (*private_key_out)->pkcs11ID);
+ SECKEY_DestroyPrivateKey(*private_key_out);
+ }
+ if (*public_key_out) {
+ PK11_DestroyTokenObject((*public_key_out)->pkcs11Slot,
+ (*public_key_out)->pkcs11ID);
+ SECKEY_DestroyPublicKey(*public_key_out);
+ }
+ } else {
+ LOG(INFO) << "Owner key generation succeeded!";
+ }
+ if (slot != NULL) {
+ PK11_FreeSlot(slot);
+ }
+
+ return is_success;
+}
+
+// static
+bool OwnerManager::ExportPublicKey(SECKEYPublicKey* key,
+ const FilePath& key_file) {
+ DCHECK(key);
+ SECItem* der;
+ bool ok = false;
+ int safe_file_size = 0;
+
+ // Instead of exporting/importing the key directly, I'm actually
+ // going to use a SubjectPublicKeyInfo. The reason is because NSS
+ // exports functions that encode/decode these kinds of structures, while
+ // it does not export the ones that deal directly with public keys.
+ der = SECKEY_EncodeDERSubjectPublicKeyInfo(key);
+ if (!der) {
+ LOG(ERROR) << "Could not encode public key for export!";
+ return false;
+ }
+
+ if (der->len > static_cast<uint>(INT_MAX)) {
+ LOG(ERROR) << "key is too big! " << der->len;
+ } else {
+ safe_file_size = static_cast<int>(der->len);
+
+ ok = (safe_file_size ==
+ file_util::WriteFile(key_file,
+ reinterpret_cast<char*>(der->data),
+ der->len));
+ }
+ SECITEM_FreeItem(der, PR_TRUE);
+ return ok;
+}
+
+// static
+SECKEYPublicKey* OwnerManager::ImportPublicKey(const FilePath& key_file) {
+ SECItem key_der;
+
+ if (!ReadDERFromFile(key_file, &key_der)) {
+ PLOG(ERROR) << "Could not read in key from " << key_file.value() << ":";
+ return NULL;
+ }
+
+ // See the comment in ExportPublicKey() for why I wrote a
+ // SubjectPublicKeyInfo to disk instead of a key.
+ CERTSubjectPublicKeyInfo* spki =
+ SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der);
+ if (!spki) {
+ LOG(ERROR) << "Could not decode key info: " << PR_GetError();
+ return NULL;
+ }
+
+ SECKEYPublicKey *public_key = SECKEY_ExtractPublicKey(spki);
+ SECKEY_DestroySubjectPublicKeyInfo(spki);
+ return public_key;
+}
+
+// static
+bool OwnerManager::ReadDERFromFile(const FilePath& key_file,
+ SECItem* key_der) {
+ // I'd use NSS' SECU_ReadDERFromFile() here, but the SECU_* functions are
+ // considered internal to the NSS command line utils.
+ // This code is lifted, in spirit, from the implementation of that function.
+ DCHECK(key_der) << "Don't pass NULL for |key_der|";
+
+ // Get the file size (must fit in a 32 bit int for NSS).
+ int64 file_size;
+ if (!file_util::GetFileSize(key_file, &file_size)) {
+ LOG(ERROR) << "Could not get size of " << key_file.value();
+ return false;
+ }
+ if (file_size > static_cast<int64>(std::numeric_limits<int>::max())) {
+ LOG(ERROR) << key_file.value() << "is "
+ << file_size << "bytes!!! Too big!";
+ return false;
+ }
+ int32 safe_file_size = static_cast<int32>(file_size);
+
+ // Allocate space for the DER encoded data.
+ key_der->data = NULL;
+ if (!SECITEM_AllocItem(NULL, key_der, safe_file_size)) {
+ LOG(ERROR) << "Could not create space for DER encoded key";
+ return false;
+ }
+
+ // Get the key data off of disk
+ int data_read = file_util::ReadFile(key_file,
+ reinterpret_cast<char*>(key_der->data),
+ safe_file_size);
+
+ if (data_read != safe_file_size) {
+ LOG(ERROR) << "Read the wrong amount of data from the DER encoded key! "
+ << data_read;
+ SECITEM_FreeItem(key_der, PR_FALSE);
+ return false;
+ }
+ return true;
+}
diff --git a/chrome/browser/chromeos/login/owner_manager.h b/chrome/browser/chromeos/login/owner_manager.h
new file mode 100644
index 0000000..ed7a3d6
--- /dev/null
+++ b/chrome/browser/chromeos/login/owner_manager.h
@@ -0,0 +1,83 @@
+// 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_
+
+#include "base/basictypes.h"
+
+// Forward declarations of NSS data structures.
+struct CERTCertificateStr;
+struct CERTCertificateRequestStr;
+struct SECKEYPrivateKeyStr;
+struct SECKEYPublicKeyStr;
+struct SECItemStr;
+
+typedef struct CERTCertificateStr CERTCertificate;
+typedef struct CERTCertificateRequestStr CERTCertificateRequest;
+typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey;
+typedef struct SECKEYPublicKeyStr SECKEYPublicKey;
+typedef struct SECItemStr SECItem;
+
+class FilePath;
+
+// 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:
+ OwnerManager() {}
+ virtual ~OwnerManager() {}
+
+ bool OwnershipAlreadyTaken();
+
+ bool TakeOwnership();
+
+ // Generate a public/private RSA keypair and store them in the NSS database.
+ // The keys will be kKeySizeInBits in length (Recommend >= 2048 bits).
+ //
+ // Returns false on error.
+ //
+ // The caller takes ownership of both objects, which are allocated by libnss.
+ // To free them, call
+ // SECKEY_DestroyPrivateKey(*private_key_out);
+ // SECKEY_DestroyPublicKey(*public_key_out);
+ static bool GenerateKeyPair(SECKEYPrivateKey** private_key_out,
+ SECKEYPublicKey** public_key_out);
+
+ // DER encodes |key| and writes it out to |key_file|.
+ // The blob on disk is a DER-encoded X509 SubjectPublicKeyInfo object.
+ // Returns false on error.
+ static bool ExportPublicKey(SECKEYPublicKey* key,
+ const FilePath& key_file);
+
+ // Assumes that the file at |key_file| exists.
+ // Caller takes ownership of returned object; returns NULL on error.
+ // To free, call SECKEY_DestroyPublicKey.
+ static SECKEYPublicKey* ImportPublicKey(const FilePath& key_file);
+
+ private:
+ // Fills in fields of |key_der| with DER encoded data from a file at
+ // |key_file|. The caller must pass in a pointer to an actual SECItem
+ // struct for |key_der|. |key_der->data| should be initialized to NULL
+ // and |key_der->len| should be set to 0.
+ //
+ // Upon success, data is stored in key_der->data, and the caller takes
+ // ownership. Returns false on error.
+ //
+ // To free the data, call
+ // SECITEM_FreeItem(key_der, PR_FALSE);
+ static bool ReadDERFromFile(const FilePath& key_file, SECItem* key_der);
+
+ // The place outside the owner's encrypted home directory where her
+ // key will live.
+ static const char kOwnerKeyFile[];
+
+ // Key generation parameters.
+ static const uint32 kKeyGenMechanism; // used by PK11_GenerateKeyPair()
+ static const unsigned long kExponent;
+ static const int kKeySizeInBits;
+};
+
+#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..1f42900
--- /dev/null
+++ b/chrome/browser/chromeos/login/owner_manager_unittest.cc
@@ -0,0 +1,112 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace chromeos {
+
+class OwnerManagerTest : public ::testing::Test {
+ public:
+ OwnerManagerTest()
+ : private_key_(NULL),
+ public_key_(NULL) {
+
+ }
+ virtual ~OwnerManagerTest() {}
+
+ virtual void SetUp() {
+ base::OpenPersistentNSSDB();
+ }
+
+ virtual void TearDown() {
+ if (private_key_) {
+ PK11_DestroyTokenObject(private_key_->pkcs11Slot, private_key_->pkcs11ID);
+ SECKEY_DestroyPrivateKey(private_key_);
+ }
+ if (public_key_) {
+ PK11_DestroyTokenObject(public_key_->pkcs11Slot, public_key_->pkcs11ID);
+ SECKEY_DestroyPublicKey(public_key_);
+ }
+ }
+
+ SECKEYPrivateKey* private_key_;
+ SECKEYPublicKey* public_key_;
+};
+
+TEST_F(OwnerManagerTest, KeyGenerate) {
+ EXPECT_TRUE(OwnerManager::GenerateKeyPair(&private_key_, &public_key_));
+ EXPECT_TRUE(private_key_ != NULL);
+ ASSERT_TRUE(public_key_ != NULL);
+ EXPECT_EQ(public_key_->keyType, rsaKey);
+}
+
+TEST_F(OwnerManagerTest, ExportImportPublicKey) {
+ EXPECT_TRUE(OwnerManager::GenerateKeyPair(&private_key_, &public_key_));
+
+ ScopedTempDir tmpdir;
+ FilePath tmpfile;
+ ASSERT_TRUE(tmpdir.CreateUniqueTempDir());
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(tmpdir.path(), &tmpfile));
+
+ EXPECT_TRUE(OwnerManager::ExportPublicKey(public_key_, tmpfile));
+
+ // Now, verify that we can look up the private key, given the public key
+ // we exported. We'll create
+ // an ID from the key, and then use that ID to query the token in the
+ // default slot for a matching private key. Then we'll make sure it's
+ // the same as |private_key_|
+ PK11SlotInfo* slot = NULL;
+ SECItem* ck_id = NULL;
+ SECKEYPublicKey* from_disk = NULL;
+ SECKEYPrivateKey* found = NULL;
+
+ slot = base::GetDefaultNSSKeySlot();
+ EXPECT_TRUE(slot != NULL);
+ if (NULL == slot)
+ goto cleanup;
+
+ from_disk = OwnerManager::ImportPublicKey(tmpfile);
+ ASSERT_TRUE(from_disk != NULL);
+
+ ck_id = PK11_MakeIDFromPubKey(&(from_disk->u.rsa.modulus));
+ EXPECT_TRUE(ck_id != NULL);
+ if (NULL == ck_id)
+ goto cleanup;
+
+ found = PK11_FindKeyByKeyID(slot, ck_id, NULL);
+ EXPECT_TRUE(found != NULL);
+ if (NULL == found)
+ goto cleanup;
+
+ EXPECT_EQ(private_key_->pkcs11ID, found->pkcs11ID);
+
+ cleanup:
+ if (slot)
+ PK11_FreeSlot(slot);
+ if (from_disk)
+ SECKEY_DestroyPublicKey(from_disk);
+ if (found)
+ SECKEY_DestroyPrivateKey(found);
+ if (ck_id)
+ SECITEM_ZfreeItem(ck_id, PR_TRUE);
+}
+
+} // namespace chromeos
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index c350bd0..7d0022a 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -477,6 +477,8 @@
'browser/chromeos/login/network_screen_delegate.h',
'browser/chromeos/login/new_user_view.cc',
'browser/chromeos/login/new_user_view.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 aef4727..3507387 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -735,6 +735,7 @@
'browser/chromeos/login/cookie_fetcher_unittest.cc',
'browser/chromeos/login/google_authenticator_unittest.cc',
'browser/chromeos/login/mock_auth_response_handler.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',