attestation: Implemented RegisterKeyWithChapsToken.

BUG=brillo:737
TEST=unit, manually using 'attestation_client register' and 'p11_replay
--list_objects' as well as manually checking the chrome certificate
manager after registration.

Change-Id: I147f52fea8b0cb1c3cccaad98cffca5c08d032e3
Reviewed-on: https://chromium-review.googlesource.com/270144
Reviewed-by: Utkarsh Sanghi <usanghi@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Darren Krahn <dkrahn@chromium.org>
Tested-by: Darren Krahn <dkrahn@chromium.org>
diff --git a/client/dbus_proxy.cc b/client/dbus_proxy.cc
index 2089f05..77fea34 100644
--- a/client/dbus_proxy.cc
+++ b/client/dbus_proxy.cc
@@ -175,4 +175,22 @@
       request);
 }
 
+void DBusProxy::RegisterKeyWithChapsToken(
+    const RegisterKeyWithChapsTokenRequest& request,
+    const RegisterKeyWithChapsTokenCallback& callback) {
+  auto on_error = [callback](chromeos::Error* error) {
+    RegisterKeyWithChapsTokenReply reply;
+    reply.set_status(STATUS_NOT_AVAILABLE);
+    callback.Run(reply);
+  };
+  chromeos::dbus_utils::CallMethodWithTimeout(
+      kDBusTimeoutMS,
+      object_proxy_,
+      attestation::kAttestationInterface,
+      attestation::kRegisterKeyWithChapsToken,
+      callback,
+      base::Bind(on_error),
+      request);
+}
+
 }  // namespace attestation
diff --git a/client/dbus_proxy.h b/client/dbus_proxy.h
index 678070d..3e7ec75 100644
--- a/client/dbus_proxy.h
+++ b/client/dbus_proxy.h
@@ -45,6 +45,9 @@
   void Decrypt(const DecryptRequest& request,
                const DecryptCallback& callback) override;
   void Sign(const SignRequest& request, const SignCallback& callback) override;
+  void RegisterKeyWithChapsToken(
+      const RegisterKeyWithChapsTokenRequest& request,
+      const RegisterKeyWithChapsTokenCallback& callback) override;
 
   // Useful for testing.
   void set_object_proxy(dbus::ObjectProxy* object_proxy) {
diff --git a/client/dbus_proxy_test.cc b/client/dbus_proxy_test.cc
index f0855f0..19eb606 100644
--- a/client/dbus_proxy_test.cc
+++ b/client/dbus_proxy_test.cc
@@ -364,4 +364,39 @@
   EXPECT_EQ(1, callback_count);
 }
 
+TEST_F(DBusProxyTest, RegisterKeyWithChapsToken) {
+  auto fake_dbus_call = [](
+      dbus::MethodCall* method_call,
+      const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+    // Verify request protobuf.
+    dbus::MessageReader reader(method_call);
+    RegisterKeyWithChapsTokenRequest request_proto;
+    EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+    EXPECT_EQ("label", request_proto.key_label());
+    EXPECT_EQ("user", request_proto.username());
+    // Create reply protobuf.
+    auto response = dbus::Response::CreateEmpty();
+    dbus::MessageWriter writer(response.get());
+    RegisterKeyWithChapsTokenReply reply_proto;
+    reply_proto.set_status(STATUS_SUCCESS);
+    writer.AppendProtoAsArrayOfBytes(reply_proto);
+    response_callback.Run(response.release());
+  };
+  EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+      .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+  // Set expectations on the outputs.
+  int callback_count = 0;
+  auto callback = [&callback_count](
+      const RegisterKeyWithChapsTokenReply& reply) {
+    callback_count++;
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  proxy_.RegisterKeyWithChapsToken(request, base::Bind(callback));
+  EXPECT_EQ(1, callback_count);
+}
+
 }  // namespace attestation
diff --git a/client/main.cc b/client/main.cc
index 1d00b18..76ec20e 100644
--- a/client/main.cc
+++ b/client/main.cc
@@ -33,6 +33,7 @@
 const char kDecryptCommand[] = "decrypt";
 const char kSignCommand[] = "sign";
 const char kVerifyCommand[] = "verify";
+const char kRegisterCommand[] = "register";
 const char kUsage[] = R"(
 Usage: attestation_client <command> [<args>]
 Commands:
@@ -70,6 +71,9 @@
           --signature=<signature_file>
       Verifies the signature in |signature_file| against the contents of
       |input_file|.
+
+  register [--user=<email>] [--label=<keylabel]
+      Registers a key with a PKCS #11 token.
 )";
 
 // The Daemon class works well as a client loop as well.
@@ -239,6 +243,11 @@
                         command_line->GetSwitchValueASCII("user"),
                         input,
                         signature);
+    } else if (args.front() == kRegisterCommand) {
+      task = base::Bind(&ClientLoop::CallRegister,
+                        weak_factory_.GetWeakPtr(),
+                        command_line->GetSwitchValueASCII("label"),
+                        command_line->GetSwitchValueASCII("user"));
     } else {
       return EX_USAGE;
     }
@@ -454,6 +463,15 @@
     Quit();
   }
 
+  void CallRegister(const std::string& label, const std::string& username) {
+    RegisterKeyWithChapsTokenRequest request;
+    request.set_key_label(label);
+    request.set_username(username);
+    attestation_->RegisterKeyWithChapsToken(request, base::Bind(
+        &ClientLoop::PrintReplyAndQuit<RegisterKeyWithChapsTokenReply>,
+        weak_factory_.GetWeakPtr()));
+  }
+
   std::unique_ptr<attestation::AttestationInterface> attestation_;
 
   // Declare this last so weak pointers will be destroyed first.
diff --git a/common/attestation_interface.h b/common/attestation_interface.h
index 2ed7213..ac72bd3 100644
--- a/common/attestation_interface.h
+++ b/common/attestation_interface.h
@@ -70,17 +70,23 @@
       const CreateCertifiableKeyRequest& request,
       const CreateCertifiableKeyCallback& callback) = 0;
 
-  // Processes a DecryptRequest and responds with a
-  // DecryptReply.
+  // Processes a DecryptRequest and responds with a DecryptReply.
   using DecryptCallback = base::Callback<void(const DecryptReply&)>;
   virtual void Decrypt(const DecryptRequest& request,
                        const DecryptCallback& callback) = 0;
 
-  // Processes a SignRequest and responds with a
-  // SignReply.
+  // Processes a SignRequest and responds with a SignReply.
   using SignCallback = base::Callback<void(const SignReply&)>;
   virtual void Sign(const SignRequest& request,
                     const SignCallback& callback) = 0;
+
+  // Processes a RegisterKeyWithChapsTokenRequest and responds with a
+  // RegisterKeyWithChapsTokenReply.
+  using RegisterKeyWithChapsTokenCallback =
+      base::Callback<void(const RegisterKeyWithChapsTokenReply&)>;
+  virtual void RegisterKeyWithChapsToken(
+      const RegisterKeyWithChapsTokenRequest& request,
+      const RegisterKeyWithChapsTokenCallback& callback) = 0;
 };
 
 }  // namespace attestation
diff --git a/common/dbus_interface.h b/common/dbus_interface.h
index 97cde33..ffe90ab 100644
--- a/common/dbus_interface.h
+++ b/common/dbus_interface.h
@@ -21,6 +21,7 @@
 constexpr char kCreateCertifiableKey[] = "CreateCertifiableKey";
 constexpr char kDecrypt[] = "Decrypt";
 constexpr char kSign[] = "Sign";
+constexpr char kRegisterKeyWithChapsToken[] = "RegisterKeyWithChapsToken";
 
 }  // namespace attestation
 
diff --git a/common/interface.proto b/common/interface.proto
index 0caa67b..ffdb21d 100644
--- a/common/interface.proto
+++ b/common/interface.proto
@@ -148,3 +148,12 @@
   optional AttestationStatus status = 1;
   optional bytes signature = 2;
 }
+
+message RegisterKeyWithChapsTokenRequest {
+  optional string key_label = 1;
+  optional string username = 2;
+}
+
+message RegisterKeyWithChapsTokenReply {
+  optional AttestationStatus status = 1;
+}
diff --git a/common/mock_attestation_interface.h b/common/mock_attestation_interface.h
index d568a43..eebd05b 100644
--- a/common/mock_attestation_interface.h
+++ b/common/mock_attestation_interface.h
@@ -36,6 +36,9 @@
                                           const CreateCertifiableKeyCallback&));
   MOCK_METHOD2(Decrypt, void(const DecryptRequest&, const DecryptCallback&));
   MOCK_METHOD2(Sign, void(const SignRequest&, const SignCallback&));
+  MOCK_METHOD2(RegisterKeyWithChapsToken,
+               void(const RegisterKeyWithChapsTokenRequest&,
+                    const RegisterKeyWithChapsTokenCallback&));
 };
 
 }  // namespace attestation
diff --git a/server/attestation_service.cc b/server/attestation_service.cc
index 2d50ecf..407cfcd 100644
--- a/server/attestation_service.cc
+++ b/server/attestation_service.cc
@@ -460,6 +460,54 @@
   result->set_signature(signature);
 }
 
+void AttestationService::RegisterKeyWithChapsToken(
+    const RegisterKeyWithChapsTokenRequest& request,
+    const RegisterKeyWithChapsTokenCallback& callback) {
+  auto result = std::make_shared<RegisterKeyWithChapsTokenReply>();
+  base::Closure task = base::Bind(
+      &AttestationService::RegisterKeyWithChapsTokenTask,
+      base::Unretained(this),
+      request,
+      result);
+  base::Closure reply = base::Bind(
+      &AttestationService::TaskRelayCallback<RegisterKeyWithChapsTokenReply>,
+      GetWeakPtr(),
+      callback,
+      result);
+  worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
+}
+
+void AttestationService::RegisterKeyWithChapsTokenTask(
+    const RegisterKeyWithChapsTokenRequest& request,
+    const std::shared_ptr<RegisterKeyWithChapsTokenReply>& result) {
+  CertifiedKey key;
+  if (!FindKeyByLabel(request.username(), request.key_label(), &key)) {
+    result->set_status(STATUS_INVALID_PARAMETER);
+    return;
+  }
+  if (!key_store_->Register(request.username(), request.key_label(),
+                            key.key_type(), key.key_usage(), key.key_blob(),
+                            key.public_key(), key.certified_key_credential())) {
+    result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
+    return;
+  }
+  if (key.has_intermediate_ca_cert() &&
+      !key_store_->RegisterCertificate(request.username(),
+                                       key.intermediate_ca_cert())) {
+    result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
+    return;
+  }
+  for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) {
+    if (!key_store_->RegisterCertificate(
+            request.username(),
+            key.additional_intermediate_ca_cert(i))) {
+      result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
+      return;
+    }
+  }
+  DeleteKey(request.username(), request.key_label());
+}
+
 bool AttestationService::IsPreparedForEnrollment() {
   if (!tpm_utility_->IsTpmReady()) {
     return false;
diff --git a/server/attestation_service.h b/server/attestation_service.h
index 1876c2a..8218abe 100644
--- a/server/attestation_service.h
+++ b/server/attestation_service.h
@@ -75,6 +75,9 @@
   void Decrypt(const DecryptRequest& request,
                const DecryptCallback& callback) override;
   void Sign(const SignRequest& request, const SignCallback& callback) override;
+  void RegisterKeyWithChapsToken(
+      const RegisterKeyWithChapsTokenRequest& request,
+      const RegisterKeyWithChapsTokenCallback& callback) override;
 
   // Mutators useful for testing.
   void set_crypto_utility(CryptoUtility* crypto_utility) {
@@ -157,6 +160,11 @@
   void SignTask(const SignRequest& request,
                 const std::shared_ptr<SignReply>& result);
 
+  // A synchronous implementation of RegisterKeyWithChapsToken.
+  void RegisterKeyWithChapsTokenTask(
+      const RegisterKeyWithChapsTokenRequest& request,
+      const std::shared_ptr<RegisterKeyWithChapsTokenReply>& result);
+
   // Returns true iff all information required for enrollment with the Google
   // Attestation CA is available.
   bool IsPreparedForEnrollment();
diff --git a/server/attestation_service_test.cc b/server/attestation_service_test.cc
index ab2e2f6..2004f51 100644
--- a/server/attestation_service_test.cc
+++ b/server/attestation_service_test.cc
@@ -1003,4 +1003,176 @@
   Run();
 }
 
+TEST_F(AttestationServiceTest, RegisterSuccess) {
+  // Setup a key in the user key store.
+  CertifiedKey key;
+  key.set_key_blob("key_blob");
+  key.set_public_key("public_key");
+  key.set_certified_key_credential("fake_cert");
+  key.set_intermediate_ca_cert("fake_ca_cert");
+  *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+  key.set_key_name("label");
+  key.set_key_type(KEY_TYPE_RSA);
+  key.set_key_usage(KEY_USAGE_SIGN);
+  std::string key_bytes;
+  key.SerializeToString(&key_bytes);
+  EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+  // Cardinality is verified here to verify various steps are performed and to
+  // catch performance regressions.
+  EXPECT_CALL(mock_key_store_, Register("user",
+                                        "label",
+                                        KEY_TYPE_RSA,
+                                        KEY_USAGE_SIGN,
+                                        "key_blob",
+                                        "public_key",
+                                        "fake_cert")).Times(1);
+  EXPECT_CALL(mock_key_store_, RegisterCertificate("user", "fake_ca_cert"))
+      .Times(1);
+  EXPECT_CALL(mock_key_store_, RegisterCertificate("user", "fake_ca_cert2"))
+      .Times(1);
+  EXPECT_CALL(mock_key_store_, Delete("user", "label")).Times(1);
+  // Set expectations on the outputs.
+  auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+    Quit();
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterSuccessNoUser) {
+  // Setup a key in the device_keys field.
+  CertifiedKey& key = *mock_database_.GetMutableProtobuf()->add_device_keys();
+  key.set_key_blob("key_blob");
+  key.set_public_key("public_key");
+  key.set_certified_key_credential("fake_cert");
+  key.set_intermediate_ca_cert("fake_ca_cert");
+  *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+  key.set_key_name("label");
+  key.set_key_type(KEY_TYPE_RSA);
+  key.set_key_usage(KEY_USAGE_SIGN);
+  // Cardinality is verified here to verify various steps are performed and to
+  // catch performance regressions.
+  EXPECT_CALL(mock_key_store_, Register("",
+                                        "label",
+                                        KEY_TYPE_RSA,
+                                        KEY_USAGE_SIGN,
+                                        "key_blob",
+                                        "public_key",
+                                        "fake_cert")).Times(1);
+  EXPECT_CALL(mock_key_store_, RegisterCertificate("", "fake_ca_cert"))
+      .Times(1);
+  EXPECT_CALL(mock_key_store_, RegisterCertificate("", "fake_ca_cert2"))
+      .Times(1);
+  // Set expectations on the outputs.
+  auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+    EXPECT_EQ(0, mock_database_.GetMutableProtobuf()->device_keys_size());
+    Quit();
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterNoKey) {
+  EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+      .WillRepeatedly(Return(false));
+  // Set expectations on the outputs.
+  auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    Quit();
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterNoKeyNoUser) {
+  // Set expectations on the outputs.
+  auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    Quit();
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterFailure) {
+  // Setup a key in the user key store.
+  CertifiedKey key;
+  key.set_key_name("label");
+  std::string key_bytes;
+  key.SerializeToString(&key_bytes);
+  EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+  EXPECT_CALL(mock_key_store_, Register(_, _, _, _, _, _, _))
+      .WillRepeatedly(Return(false));
+  // Set expectations on the outputs.
+  auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    Quit();
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterIntermediateFailure) {
+  // Setup a key in the user key store.
+  CertifiedKey key;
+  key.set_key_name("label");
+  key.set_intermediate_ca_cert("fake_ca_cert");
+  std::string key_bytes;
+  key.SerializeToString(&key_bytes);
+  EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+  EXPECT_CALL(mock_key_store_, RegisterCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  // Set expectations on the outputs.
+  auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    Quit();
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterAdditionalFailure) {
+  // Setup a key in the user key store.
+  CertifiedKey key;
+  key.set_key_name("label");
+  *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+  std::string key_bytes;
+  key.SerializeToString(&key_bytes);
+  EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+      .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+  EXPECT_CALL(mock_key_store_, RegisterCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  // Set expectations on the outputs.
+  auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    Quit();
+  };
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+  Run();
+}
+
 }  // namespace attestation
diff --git a/server/dbus_service.cc b/server/dbus_service.cc
index d8bc29a..7973ad3 100644
--- a/server/dbus_service.cc
+++ b/server/dbus_service.cc
@@ -51,6 +51,10 @@
   dbus_interface->AddMethodHandler(kSign,
                                    base::Unretained(this),
                                    &DBusService::HandleSign);
+  dbus_interface->AddMethodHandler(
+      kRegisterKeyWithChapsToken,
+      base::Unretained(this),
+      &DBusService::HandleRegisterKeyWithChapsToken);
 
   dbus_object_.RegisterAsync(callback);
 }
@@ -204,4 +208,23 @@
       base::Bind(callback, SharedResponsePointer(std::move(response))));
 }
 
+void DBusService::HandleRegisterKeyWithChapsToken(
+    std::unique_ptr<DBusMethodResponse<const RegisterKeyWithChapsTokenReply&>>
+        response,
+    const RegisterKeyWithChapsTokenRequest& request) {
+  VLOG(1) << __func__;
+  // Convert |response| to a shared_ptr so |service_| can safely copy the
+  // callback.
+  using SharedResponsePointer = std::shared_ptr<
+      DBusMethodResponse<const RegisterKeyWithChapsTokenReply&>>;
+  // A callback that fills the reply protobuf and sends it.
+  auto callback = [](const SharedResponsePointer& response,
+                     const RegisterKeyWithChapsTokenReply& reply) {
+    response->Return(reply);
+  };
+  service_->RegisterKeyWithChapsToken(
+      request,
+      base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
 }  // namespace attestation
diff --git a/server/dbus_service.h b/server/dbus_service.h
index 01e18f7..7224efa 100644
--- a/server/dbus_service.h
+++ b/server/dbus_service.h
@@ -86,6 +86,12 @@
           const SignReply&>> response,
       const SignRequest& request);
 
+  // Handles a RegisterKeyWithChapsToken D-Bus call.
+  void HandleRegisterKeyWithChapsToken(
+      std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<
+          const RegisterKeyWithChapsTokenReply&>> response,
+      const RegisterKeyWithChapsTokenRequest& request);
+
   chromeos::dbus_utils::DBusObject dbus_object_;
   AttestationInterface* service_;
 
diff --git a/server/dbus_service_test.cc b/server/dbus_service_test.cc
index 8894e3b..b8c0605 100644
--- a/server/dbus_service_test.cc
+++ b/server/dbus_service_test.cc
@@ -340,4 +340,31 @@
   EXPECT_EQ("signature", reply.signature());
 }
 
+TEST_F(DBusServiceTest, RegisterKeyWithChapsToken) {
+  RegisterKeyWithChapsTokenRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  EXPECT_CALL(mock_service_, RegisterKeyWithChapsToken(_, _))
+      .WillOnce(Invoke([](
+          const RegisterKeyWithChapsTokenRequest& request,
+          const AttestationInterface::RegisterKeyWithChapsTokenCallback&
+              callback) {
+        EXPECT_EQ("label", request.key_label());
+        EXPECT_EQ("user", request.username());
+        RegisterKeyWithChapsTokenReply reply;
+        reply.set_status(STATUS_SUCCESS);
+        callback.Run(reply);
+      }));
+  std::unique_ptr<dbus::MethodCall> call =
+      CreateMethodCall(kRegisterKeyWithChapsToken);
+  dbus::MessageWriter writer(call.get());
+  writer.AppendProtoAsArrayOfBytes(request);
+  auto response = CallMethod(call.get());
+  dbus::MessageReader reader(response.get());
+  RegisterKeyWithChapsTokenReply reply;
+  EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+  EXPECT_EQ(STATUS_SUCCESS, reply.status());
+}
+
+
 }  // namespace attestation
diff --git a/server/key_store.h b/server/key_store.h
index 6fc4421..3ae6a87 100644
--- a/server/key_store.h
+++ b/server/key_store.h
@@ -9,6 +9,8 @@
 
 #include <base/macros.h>
 
+#include "attestation/common/common.pb.h"
+
 namespace attestation {
 
 // A mock-able key storage interface.
@@ -48,6 +50,8 @@
   // the key. Returns true on success.
   virtual bool Register(const std::string& username,
                         const std::string& label,
+                        KeyType key_type,
+                        KeyUsage key_usage,
                         const std::string& private_key_blob,
                         const std::string& public_key_der,
                         const std::string& certificate) = 0;
diff --git a/server/mock_key_store.cc b/server/mock_key_store.cc
index 2558f35..792ebdd 100644
--- a/server/mock_key_store.cc
+++ b/server/mock_key_store.cc
@@ -14,7 +14,7 @@
   ON_CALL(*this, Write(_, _, _)).WillByDefault(Return(true));
   ON_CALL(*this, Delete(_, _)).WillByDefault(Return(true));
   ON_CALL(*this, DeleteByPrefix(_, _)).WillByDefault(Return(true));
-  ON_CALL(*this, Register(_, _, _, _, _)).WillByDefault(Return(true));
+  ON_CALL(*this, Register(_, _, _, _, _, _, _)).WillByDefault(Return(true));
   ON_CALL(*this, RegisterCertificate(_, _)).WillByDefault(Return(true));
 }
 
diff --git a/server/mock_key_store.h b/server/mock_key_store.h
index 40d04f1..505fb76 100644
--- a/server/mock_key_store.h
+++ b/server/mock_key_store.h
@@ -29,8 +29,10 @@
                             const std::string& name));
   MOCK_METHOD2(DeleteByPrefix, bool(const std::string& username,
                                     const std::string& key_prefix));
-  MOCK_METHOD5(Register, bool(const std::string& username,
+  MOCK_METHOD7(Register, bool(const std::string& username,
                               const std::string& label,
+                              KeyType key_type,
+                              KeyUsage key_usage,
                               const std::string& private_key_blob,
                               const std::string& public_key_der,
                               const std::string& certificate));
diff --git a/server/pkcs11_key_store.cc b/server/pkcs11_key_store.cc
index 4f59928..bb9ffa2 100644
--- a/server/pkcs11_key_store.cc
+++ b/server/pkcs11_key_store.cc
@@ -88,7 +88,7 @@
                           std::string* key_data) {
   CK_SLOT_ID slot;
   if (!GetUserSlot(username, &slot)) {
-    LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+    LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
     return false;
   }
   ScopedSession session(slot);
@@ -130,7 +130,7 @@
   }
   CK_SLOT_ID slot;
   if (!GetUserSlot(username, &slot)) {
-    LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+    LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
     return false;
   }
   ScopedSession session(slot);
@@ -181,7 +181,7 @@
                             const std::string& key_name) {
   CK_SLOT_ID slot;
   if (!GetUserSlot(username, &slot)) {
-    LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+    LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
     return false;
   }
   ScopedSession session(slot);
@@ -203,7 +203,7 @@
                                     const std::string& key_prefix) {
   CK_SLOT_ID slot;
   if (!GetUserSlot(username, &slot)) {
-    LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+    LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
     return false;
   }
   ScopedSession session(slot);
@@ -225,14 +225,20 @@
 
 bool Pkcs11KeyStore::Register(const std::string& username,
                               const std::string& label,
+                              KeyType key_type,
+                              KeyUsage key_usage,
                               const std::string& private_key_blob,
                               const std::string& public_key_der,
                               const std::string& certificate) {
   const CK_ATTRIBUTE_TYPE kKeyBlobAttribute = CKA_VENDOR_DEFINED + 1;
 
+  if (key_type != KEY_TYPE_RSA) {
+    LOG(ERROR) << "Pkcs11KeyStore: Only RSA supported.";
+    return false;
+  }
   CK_SLOT_ID slot;
   if (!GetUserSlot(username, &slot)) {
-    LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+    LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
     return false;
   }
   ScopedSession session(slot);
@@ -263,21 +269,23 @@
   // Construct a PKCS #11 template for the public key object.
   CK_BBOOL true_value = CK_TRUE;
   CK_BBOOL false_value = CK_FALSE;
-  CK_KEY_TYPE key_type = CKK_RSA;
+  CK_KEY_TYPE p11_key_type = CKK_RSA;
   CK_OBJECT_CLASS public_key_class = CKO_PUBLIC_KEY;
   std::string id = Sha1(modulus);
   std::string mutable_label(label);
   CK_ULONG modulus_bits = modulus.size() * 8;
+  CK_BBOOL sign_usage = (key_usage == KEY_USAGE_SIGN);
+  CK_BBOOL decrypt_usage = (key_usage == KEY_USAGE_DECRYPT);
   unsigned char public_exponent[] = {1, 0, 1};
   CK_ATTRIBUTE public_key_attributes[] = {
     {CKA_CLASS, &public_key_class, sizeof(public_key_class)},
     {CKA_TOKEN, &true_value, sizeof(true_value)},
     {CKA_DERIVE, &false_value, sizeof(false_value)},
     {CKA_WRAP, &false_value, sizeof(false_value)},
-    {CKA_VERIFY, &true_value, sizeof(true_value)},
+    {CKA_VERIFY, &sign_usage, sizeof(sign_usage)},
     {CKA_VERIFY_RECOVER, &false_value, sizeof(false_value)},
-    {CKA_ENCRYPT, &false_value, sizeof(false_value)},
-    {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
+    {CKA_ENCRYPT, &decrypt_usage, sizeof(decrypt_usage)},
+    {CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
     {CKA_ID, string_as_array(&id), id.size()},
     {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
     {CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits)},
@@ -305,10 +313,10 @@
     {CKA_EXTRACTABLE, &false_value, sizeof(false_value)},
     {CKA_DERIVE, &false_value, sizeof(false_value)},
     {CKA_UNWRAP, &false_value, sizeof(false_value)},
-    {CKA_SIGN, &true_value, sizeof(true_value)},
+    {CKA_SIGN, &sign_usage, sizeof(sign_usage)},
     {CKA_SIGN_RECOVER, &false_value, sizeof(false_value)},
-    {CKA_DECRYPT, &false_value, sizeof(false_value)},
-    {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
+    {CKA_DECRYPT, &decrypt_usage, sizeof(decrypt_usage)},
+    {CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
     {CKA_ID, string_as_array(&id), id.size()},
     {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
     {CKA_PUBLIC_EXPONENT, public_exponent, arraysize(public_exponent)},
@@ -330,8 +338,10 @@
 
   if (!certificate.empty()) {
     std::string subject;
-    if (!GetCertificateSubject(certificate, &subject)) {
-      LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate subject.";
+    std::string issuer;
+    std::string serial_number;
+    if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
+      LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
     }
     // Construct a PKCS #11 template for a certificate object.
     std::string mutable_certificate = certificate;
@@ -345,6 +355,12 @@
       {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
       {CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
       {CKA_SUBJECT, string_as_array(&subject), subject.size()},
+      {CKA_ISSUER, string_as_array(&issuer), issuer.size()},
+      {
+        CKA_SERIAL_NUMBER,
+        string_as_array(&serial_number),
+        serial_number.size()
+      },
       {
         CKA_VALUE,
         string_as_array(&mutable_certificate),
@@ -372,7 +388,7 @@
                                          const std::string& certificate) {
   CK_SLOT_ID slot;
   if (!GetUserSlot(username, &slot)) {
-    LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+    LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
     return false;
   }
   ScopedSession session(slot);
@@ -386,8 +402,10 @@
     return true;
   }
   std::string subject;
-  if (!GetCertificateSubject(certificate, &subject)) {
-    LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate subject.";
+  std::string issuer;
+  std::string serial_number;
+  if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
+    LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
   }
   // Construct a PKCS #11 template for a certificate object.
   std::string mutable_certificate = certificate;
@@ -401,6 +419,8 @@
     {CKA_PRIVATE, &false_value, sizeof(false_value)},
     {CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
     {CKA_SUBJECT, string_as_array(&subject), subject.size()},
+    {CKA_ISSUER, string_as_array(&issuer), issuer.size()},
+    {CKA_SERIAL_NUMBER, string_as_array(&serial_number), serial_number.size()},
     {
       CKA_VALUE,
       string_as_array(&mutable_certificate),
@@ -579,8 +599,10 @@
   return true;
 }
 
-bool Pkcs11KeyStore::GetCertificateSubject(const std::string& certificate,
-                                           std::string* subject) {
+bool Pkcs11KeyStore::GetCertificateFields(const std::string& certificate,
+                                          std::string* subject,
+                                          std::string* issuer,
+                                          std::string* serial_number) {
   const unsigned char* asn1_ptr = reinterpret_cast<const unsigned char*>(
       certificate.data());
   ScopedX509 x509(d2i_X509(nullptr, &asn1_ptr, certificate.size()));
@@ -588,14 +610,34 @@
     LOG(WARNING) << "Pkcs11KeyStore: Failed to decode certificate.";
     return false;
   }
-  unsigned char* buffer = nullptr;
-  int length = i2d_X509_NAME(x509->cert_info->subject, &buffer);
-  crypto::ScopedOpenSSLBytes scoped_buffer(buffer);
+  unsigned char* subject_buffer = nullptr;
+  int length = i2d_X509_NAME(x509->cert_info->subject, &subject_buffer);
+  crypto::ScopedOpenSSLBytes scoped_subject_buffer(subject_buffer);
   if (length <= 0) {
     LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate subject.";
     return false;
   }
-  subject->assign(reinterpret_cast<char*>(buffer), length);
+  subject->assign(reinterpret_cast<char*>(subject_buffer), length);
+
+  unsigned char* issuer_buffer = nullptr;
+  length = i2d_X509_NAME(x509->cert_info->issuer, &issuer_buffer);
+  crypto::ScopedOpenSSLBytes scoped_issuer_buffer(issuer_buffer);
+  if (length <= 0) {
+    LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate issuer.";
+    return false;
+  }
+  issuer->assign(reinterpret_cast<char*>(issuer_buffer), length);
+
+  unsigned char* serial_number_buffer = nullptr;
+  length = i2d_ASN1_INTEGER(x509->cert_info->serialNumber,
+                            &serial_number_buffer);
+  crypto::ScopedOpenSSLBytes scoped_serial_number_buffer(serial_number_buffer);
+  if (length <= 0) {
+    LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate serial "
+                    "number.";
+    return false;
+  }
+  serial_number->assign(reinterpret_cast<char*>(serial_number_buffer), length);
   return true;
 }
 
diff --git a/server/pkcs11_key_store.h b/server/pkcs11_key_store.h
index 5dbda4b..5fb02cd 100644
--- a/server/pkcs11_key_store.h
+++ b/server/pkcs11_key_store.h
@@ -48,6 +48,8 @@
                       const std::string& key_prefix) override;
   bool Register(const std::string& username,
                 const std::string& label,
+                KeyType key_type,
+                KeyUsage key_usage,
                 const std::string& private_key_blob,
                 const std::string& public_key_der,
                 const std::string& certificate) override;
@@ -88,10 +90,12 @@
                              const std::string& key_name,
                              CK_OBJECT_HANDLE object_handle);
 
-  // Extracts the subject information from an X.509 certificate. Returns false
-  // if the subject cannot be determined.
-  bool GetCertificateSubject(const std::string& certificate,
-                             std::string* subject);
+  // Extracts the |subject|, |issuer|, and |serial_number| information from an
+  // X.509 |certificate|. Returns false if the value cannot be determined.
+  bool GetCertificateFields(const std::string& certificate,
+                            std::string* subject,
+                            std::string* issuer,
+                            std::string* serial_number);
 
   // Returns true iff the given certificate already exists in the token.
   bool DoesCertificateExist(CK_SESSION_HANDLE session_handle,
diff --git a/server/pkcs11_key_store_test.cc b/server/pkcs11_key_store_test.cc
index 7e9dbf7..f8c9058 100644
--- a/server/pkcs11_key_store_test.cc
+++ b/server/pkcs11_key_store_test.cc
@@ -451,15 +451,17 @@
 TEST_F(KeyStoreTest, RegisterKeyWithoutCertificate) {
   Pkcs11KeyStore key_store(&token_manager_);
   // Try with a malformed public key.
-  EXPECT_FALSE(key_store.Register(kDefaultUser, "test_label",
-                                  "private_key_blob", "bad_pubkey", ""));
+  EXPECT_FALSE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+                                  KEY_USAGE_SIGN, "private_key_blob",
+                                  "bad_pubkey", ""));
   // Try with a well-formed public key.
   std::string public_key_der = HexDecode(kValidPublicKeyHex);
   EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
       .Times(2)  // Public, private (no certificate).
       .WillRepeatedly(Return(CKR_OK));
-  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label",
-                                 "private_key_blob", public_key_der, ""));
+  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+                                 KEY_USAGE_SIGN, "private_key_blob",
+                                 public_key_der, ""));
 }
 
 TEST_F(KeyStoreTest, RegisterKeyWithCertificate) {
@@ -469,13 +471,15 @@
   Pkcs11KeyStore key_store(&token_manager_);
   std::string public_key_der = HexDecode(kValidPublicKeyHex);
   std::string certificate_der = HexDecode(kValidCertificateHex);
-  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", "private_key_blob",
+  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+                                 KEY_USAGE_SIGN, "private_key_blob",
                                  public_key_der, certificate_der));
   // Also try with the system token.
   EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
       .Times(3)  // Public, private, and certificate.
       .WillRepeatedly(Return(CKR_OK));
-  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", "private_key_blob",
+  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+                                 KEY_USAGE_SIGN, "private_key_blob",
                                  public_key_der, certificate_der));
 }
 
@@ -485,10 +489,29 @@
       .WillRepeatedly(Return(CKR_OK));
   Pkcs11KeyStore key_store(&token_manager_);
   std::string public_key_der = HexDecode(kValidPublicKeyHex);
-  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", "private_key_blob",
+  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+                                 KEY_USAGE_SIGN, "private_key_blob",
                                  public_key_der, "bad_certificate"));
 }
 
+TEST_F(KeyStoreTest, RegisterWithUnsupportedKeyType) {
+  Pkcs11KeyStore key_store(&token_manager_);
+  std::string public_key_der = HexDecode(kValidPublicKeyHex);
+  EXPECT_FALSE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_ECC,
+                                  KEY_USAGE_SIGN, "private_key_blob",
+                                  public_key_der, ""));
+}
+
+TEST_F(KeyStoreTest, RegisterDecryptionKey) {
+  EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+      .WillRepeatedly(Return(CKR_OK));
+  Pkcs11KeyStore key_store(&token_manager_);
+  std::string public_key_der = HexDecode(kValidPublicKeyHex);
+  EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+                                 KEY_USAGE_DECRYPT, "private_key_blob",
+                                 public_key_der, ""));
+}
+
 TEST_F(KeyStoreTest, RegisterCertificate) {
   Pkcs11KeyStore key_store(&token_manager_);
   std::string certificate_der = HexDecode(kValidCertificateHex);