Enable a system PKCS #11 token.

Also added support for importing PKCS #8 formatted private keys as well
as a --slot option in p11_replay, which helps to test this feature.

BUG=chromium:210525
TEST=unit, manual, platform_Pkcs11* autotests

Change-Id: I301029286432a568436bb1ce9d592a5a74ae9c9f
Reviewed-on: https://chromium-review.googlesource.com/183525
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
Commit-Queue: Darren Krahn <dkrahn@chromium.org>
Tested-by: Darren Krahn <dkrahn@chromium.org>
diff --git a/chapsd.conf b/chapsd.conf
index 5b7934f..d2e8143 100644
--- a/chapsd.conf
+++ b/chapsd.conf
@@ -20,5 +20,7 @@
     VERBOSE="--v=1"
     rm /home/chronos/.chaps_debug_1
   fi
+  mkdir -p /var/lib/chaps
+  chown chaps:chronos-access /var/lib/chaps
   exec chapsd ${VERBOSE}
 end script
diff --git a/p11_replay.cc b/p11_replay.cc
index 97afaeb..339ff85 100644
--- a/p11_replay.cc
+++ b/p11_replay.cc
@@ -60,7 +60,6 @@
     LOG(INFO) << "No slots.";
     exit(-1);
   }
-  LOG(INFO) << "Choosing slot " << slot_list[0];
   return slot_list[0];
 }
 
@@ -386,10 +385,28 @@
     CreateCertificate(session, object_data, object_id, certificate);
     X509_free(certificate);
   } else if (type == kPrivateKey) {
+    // Try decoding a PKCS#1 RSAPrivateKey structure.
     RSA* rsa = d2i_RSAPrivateKey(NULL, &data_start, object_data.length());
     if (!rsa) {
-      LOG(ERROR) << "OpenSSL error in RSA private key parsing.";
-      exit(-1);
+      // Rewind the ptr just in case it was modified.
+      data_start = reinterpret_cast<const unsigned char*>(object_data.c_str());
+      // Try decoding a PKCS#8 structure.
+      PKCS8_PRIV_KEY_INFO *p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL,
+                                                        &data_start,
+                                                        object_data.length());
+      if (!p8 || ASN1_TYPE_get(p8->pkey) != V_ASN1_OCTET_STRING) {
+        LOG(ERROR) << "OpenSSL error in PKCS #8 private key parsing.";
+        exit(-1);
+      }
+      // See if we have a RSAPrivateKey in the PKCS#8 structure.
+      data_start = p8->pkey->value.octet_string->data;
+      rsa = d2i_RSAPrivateKey(NULL, &data_start,
+                              p8->pkey->value.octet_string->length);
+      PKCS8_PRIV_KEY_INFO_free(p8);
+      if (!rsa) {
+        LOG(ERROR) << "OpenSSL error in RSA private key parsing.";
+        exit(-1);
+      }
     }
     CreatePrivateKey(session, object_id, "testing_key", rsa);
     RSA_free(rsa);
@@ -465,7 +482,7 @@
 }
 
 void PrintHelp() {
-  printf("Usage: p11_replay [COMMAND]\n");
+  printf("Usage: p11_replay [--slot=<slot>] [COMMAND]\n");
   printf("Commands:\n");
   printf("  --cleanup : Deletes all test keys.\n");
   printf("  --generate [--label=<key_label> --key_size=<size_in_bits>]"
@@ -474,8 +491,8 @@
          "useful for comparing key generation on different TPM models.\n");
   printf("  --import --path=<path to file> --type=<cert, privkey, pubkey>"
          " --id=<token id str>"
-         " : Reads an object into the token.  Accepts DER formatted"
-         " certificates and DER formatted private keys.\n");
+         " : Reads an object into the token.  Accepts DER formatted X.509"
+         " certificates and DER formatted PKCS#1 or PKCS#8 private keys.\n");
   printf("  --inject [--label=<key_label> --key_size=<size_in_bits>]"
          " : Locally generates a key pair suitable for replay tests and injects"
          " it into the token.\n");
@@ -592,6 +609,11 @@
   chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogToStderr);
   base::TimeTicks start_ticks = base::TimeTicks::Now();
   CK_SLOT_ID slot = Initialize();
+  int tmp_slot = 0;
+  if (cl->HasSwitch("slot") &&
+      base::StringToInt(cl->GetSwitchValueASCII("slot"), &tmp_slot))
+    slot = tmp_slot;
+  LOG(INFO) << "Using slot " << slot;
   CK_SESSION_HANDLE session = OpenSession(slot);
   PrintTicks(&start_ticks);
   string label = "_default";
diff --git a/slot_manager_impl.cc b/slot_manager_impl.cc
index faa7d07..d6df63b 100644
--- a/slot_manager_impl.cc
+++ b/slot_manager_impl.cc
@@ -14,6 +14,7 @@
 
 #include <base/basictypes.h>
 #include <base/file_path.h>
+#include <base/file_util.h>
 #include <base/logging.h>
 #include <base/memory/scoped_ptr.h>
 #include <chromeos/secure_blob.h>
@@ -46,6 +47,10 @@
 const CK_ULONG kMaxPinLen = 127;
 const CK_ULONG kMinPinLen = 6;
 const char kSlotDescription[] = "TPM Slot";
+const FilePath::CharType kSystemTokenPath[] =
+    FILE_PATH_LITERAL("/var/lib/chaps");
+const char kSystemTokenAuthData[] = "000000";
+const char kSystemTokenLabel[] = "System TPM Token";
 const char kTokenLabel[] = "User-Specific TPM Token";
 const char kTokenModel[] = "";
 const char kTokenSerialNumber[] = "Not Available";
@@ -289,15 +294,29 @@
     LOG(WARNING) << "TPM failed to generate random data.";
   }
 
-  // Add default isolate
+  // Add default isolate.
   AddIsolate(IsolateCredentialManager::GetDefaultIsolateCredential());
 
-  // Default semantics are to always start with two slots.  One 'system' slot
+  // By default we'll start with two slots.  This allows for one 'system' slot
   // which always has a token available, and one 'user' slot which will have no
   // token until a login event is received.
-  // TODO(dkrahn): Make this 2 once we're ready to enable the system token.
-  // crosbug.com/27759.
-  AddSlots(1);
+  AddSlots(2);
+
+  if (file_util::DirectoryExists(FilePath(kSystemTokenPath))) {
+    // Setup the system token.
+    int system_slot_id = 0;
+    if (!LoadToken(IsolateCredentialManager::GetDefaultIsolateCredential(),
+                   FilePath(kSystemTokenPath),
+                   SecureBlob(kSystemTokenAuthData),
+                   kSystemTokenLabel,
+                   &system_slot_id)) {
+      LOG(ERROR) << "Failed to load the system token.";
+      return false;
+    }
+  } else {
+    LOG(WARNING) << "System token not loaded because " << kSystemTokenPath
+                 << " does not exist.";
+  }
   return true;
 }
 
diff --git a/slot_manager_test.cc b/slot_manager_test.cc
index 2da601b..0d066da 100644
--- a/slot_manager_test.cc
+++ b/slot_manager_test.cc
@@ -209,7 +209,7 @@
 }
 
 TEST_F(TestSlotManager, DefaultSlotSetup) {
-  EXPECT_EQ(1, slot_manager_->GetSlotCount());
+  EXPECT_EQ(2, slot_manager_->GetSlotCount());
   EXPECT_FALSE(slot_manager_->IsTokenAccessible(ic_, 0));
 }
 
@@ -304,7 +304,6 @@
                                        kTokenLabel,
                                        &slot_id2));
   EXPECT_EQ(slot_id, slot_id2);
-  EXPECT_EQ(2, slot_manager_->GetSlotCount());
   EXPECT_TRUE(slot_manager_->LoadToken(ic_,
                                        FilePath("another_path"),
                                        MakeBlob(kAuthData),
@@ -317,7 +316,6 @@
   slot_manager_->ChangeTokenAuthData(FilePath("yet_another_path"),
                                        MakeBlob(kAuthData),
                                        MakeBlob(kNewAuthData));
-  EXPECT_LT(slot_manager_->GetSlotCount(), 5);
   // Logout with an unknown path.
   slot_manager_->UnloadToken(ic_, FilePath("still_yet_another_path"));
   slot_manager_->UnloadToken(ic_, FilePath("some_path"));
@@ -446,7 +444,6 @@
                                        MakeBlob(kAuthData),
                                        kTokenLabel,
                                        &slot_id));
-  EXPECT_EQ(1, slot_manager_->GetSlotCount());
 
   EXPECT_TRUE(slot_manager_->OpenIsolate(&new_isolate_1, &new_isolate_created));
   EXPECT_TRUE(new_isolate_created);
@@ -455,7 +452,6 @@
                                        MakeBlob(kAuthData),
                                        kTokenLabel,
                                        &slot_id));
-  EXPECT_EQ(2, slot_manager_->GetSlotCount());
 
   // Ensure tokens are only accessible with the valid isolate cred.
   EXPECT_TRUE(slot_manager_->IsTokenAccessible(new_isolate_0, 0));