attestation: Implemented an asymmetric Decrypt operation.

Keys that are created with a KEY_USAGE_DECRYPT usage can be used to
decrypt using this operation. The encrypted data must use the
TPM_BOUND_DATA format and the TPM_ES_RSAESOAEP_SHA1_MGF1 mechanism.

BUG=brillo:737
TEST=unit, manual using 'attestation_client decrypt' and
'attestation_client encrypt'

Change-Id: Iae8fe01abf0c635d853974518c9b5e918f7b88c1
Reviewed-on: https://chromium-review.googlesource.com/269777
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Utkarsh Sanghi <usanghi@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 a8a682d..b505f04 100644
--- a/client/dbus_proxy.cc
+++ b/client/dbus_proxy.cc
@@ -142,4 +142,21 @@
       request);
 }
 
+void DBusProxy::Decrypt(const DecryptRequest& request,
+                        const DecryptCallback& callback) {
+  auto on_error = [callback](chromeos::Error* error) {
+    DecryptReply reply;
+    reply.set_status(STATUS_NOT_AVAILABLE);
+    callback.Run(reply);
+  };
+  chromeos::dbus_utils::CallMethodWithTimeout(
+      kDBusTimeoutMS,
+      object_proxy_,
+      attestation::kAttestationInterface,
+      attestation::kDecrypt,
+      callback,
+      base::Bind(on_error),
+      request);
+}
+
 }  // namespace attestation
diff --git a/client/dbus_proxy.h b/client/dbus_proxy.h
index ef53977..49ae1e7 100644
--- a/client/dbus_proxy.h
+++ b/client/dbus_proxy.h
@@ -42,6 +42,8 @@
   void CreateCertifiableKey(
       const CreateCertifiableKeyRequest& request,
       const CreateCertifiableKeyCallback& callback) override;
+  void Decrypt(const DecryptRequest& request,
+               const DecryptCallback& 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 5b66803..51f24d4 100644
--- a/client/dbus_proxy_test.cc
+++ b/client/dbus_proxy_test.cc
@@ -288,4 +288,42 @@
   EXPECT_EQ(1, callback_count);
 }
 
+TEST_F(DBusProxyTest, Decrypt) {
+  auto fake_dbus_call = [](
+      dbus::MethodCall* method_call,
+      const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+    // Verify request protobuf.
+    dbus::MessageReader reader(method_call);
+    DecryptRequest request_proto;
+    EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+    EXPECT_EQ("label", request_proto.key_label());
+    EXPECT_EQ("user", request_proto.username());
+    EXPECT_EQ("data", request_proto.encrypted_data());
+    // Create reply protobuf.
+    scoped_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
+    dbus::MessageWriter writer(response.get());
+    DecryptReply reply_proto;
+    reply_proto.set_status(STATUS_SUCCESS);
+    reply_proto.set_decrypted_data("data");
+    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 DecryptReply& reply) {
+    callback_count++;
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+    EXPECT_EQ("data", reply.decrypted_data());
+  };
+  DecryptRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  request.set_encrypted_data("data");
+  proxy_.Decrypt(request, base::Bind(callback));
+  EXPECT_EQ(1, callback_count);
+}
+
 }  // namespace attestation
diff --git a/client/main.cc b/client/main.cc
index f137efc..9c8b68e 100644
--- a/client/main.cc
+++ b/client/main.cc
@@ -29,13 +29,15 @@
 const char kAttestationKeyCommand[] = "attestation_key";
 const char kActivateCommand[] = "activate";
 const char kEncryptForActivateCommand[] = "encrypt_for_activate";
+const char kEncryptCommand[] = "encrypt";
+const char kDecryptCommand[] = "decrypt";
 const char kUsage[] = R"(
 Usage: attestation_client <command> [<args>]
 Commands:
   create_and_certify [--user=<email>] [--label=<keylabel>]
       Creates a key and requests certification by the Google Attestation CA.
       This is the default command.
-  create [--user=<email>] [--label=<keylabel]
+  create [--user=<email>] [--label=<keylabel] [--usage=sign|decrypt]
       Creates a certifiable key.
 
   info [--user=<email>] [--label=<keylabel>]
@@ -51,6 +53,13 @@
   encrypt_for_activate --input=<input_file> --output=<output_file>
       Encrypts the content of |input_file| as required by the TPM for activating
       an attestation key. The result is written to |output_file|.
+
+  encrypt [--user=<email>] [--label=<keylabel>] --input=<input_file>
+          --output=<output_file>
+      Encrypts the content of |input_file| as required by the TPM for a decrypt
+      operation. The result is written to |output_file|.
+  decrypt [--user=<email>] [--label=<keylabel>] --input=<input_file>
+      Decrypts the content of |input_file|.
 )";
 
 // The Daemon class works well as a client loop as well.
@@ -90,7 +99,7 @@
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
     const auto& args = command_line->GetArgs();
     if (command_line->HasSwitch("help") || command_line->HasSwitch("h") ||
-        args.front() == "help") {
+        (!args.empty() && args.front() == "help")) {
       return EX_USAGE;
     }
     if (args.empty() || args.front() == kCreateAndCertifyCommand) {
@@ -99,10 +108,20 @@
                         command_line->GetSwitchValueASCII("label"),
                         command_line->GetSwitchValueASCII("user"));
     } else if (args.front() == kCreateCommand) {
+      std::string usage_str = command_line->GetSwitchValueASCII("usage");
+      KeyUsage usage;
+      if (usage_str.empty() || usage_str == "sign") {
+        usage = KEY_USAGE_SIGN;
+      } else if (usage_str == "decrypt") {
+        usage = KEY_USAGE_DECRYPT;
+      } else {
+        return EX_USAGE;
+      }
       task = base::Bind(&ClientLoop::CallCreateCertifiableKey,
                         weak_factory_.GetWeakPtr(),
                         command_line->GetSwitchValueASCII("label"),
-                        command_line->GetSwitchValueASCII("user"));
+                        command_line->GetSwitchValueASCII("user"),
+                        usage);
     } else if (args.front() == kInfoCommand) {
       task = base::Bind(&ClientLoop::CallGetKeyInfo,
                         weak_factory_.GetWeakPtr(),
@@ -141,6 +160,37 @@
       task = base::Bind(&ClientLoop::EncryptForActivate,
                         weak_factory_.GetWeakPtr(),
                         input);
+    } else if (args.front() == kEncryptCommand) {
+      if (!command_line->HasSwitch("input") ||
+          !command_line->HasSwitch("output")) {
+        return EX_USAGE;
+      }
+      std::string input;
+      base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+      if (!base::ReadFileToString(filename, &input)) {
+        LOG(ERROR) << "Failed to read file: " << filename.value();
+        return EX_NOINPUT;
+      }
+      task = base::Bind(&ClientLoop::Encrypt,
+                        weak_factory_.GetWeakPtr(),
+                        command_line->GetSwitchValueASCII("label"),
+                        command_line->GetSwitchValueASCII("user"),
+                        input);
+    } else if (args.front() == kDecryptCommand) {
+      if (!command_line->HasSwitch("input")) {
+        return EX_USAGE;
+      }
+      std::string input;
+      base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+      if (!base::ReadFileToString(filename, &input)) {
+        LOG(ERROR) << "Failed to read file: " << filename.value();
+        return EX_NOINPUT;
+      }
+      task = base::Bind(&ClientLoop::CallDecrypt,
+                        weak_factory_.GetWeakPtr(),
+                        command_line->GetSwitchValueASCII("label"),
+                        command_line->GetSwitchValueASCII("user"),
+                        input);
     } else {
       return EX_USAGE;
     }
@@ -265,16 +315,54 @@
   }
 
   void CallCreateCertifiableKey(const std::string& label,
-                                const std::string& username) {
+                                const std::string& username,
+                                KeyUsage usage) {
     CreateCertifiableKeyRequest request;
     request.set_key_label(label);
     request.set_username(username);
+    request.set_key_type(KEY_TYPE_RSA);
+    request.set_key_usage(usage);
     attestation_->CreateCertifiableKey(
         request,
         base::Bind(&ClientLoop::PrintReplyAndQuit<CreateCertifiableKeyReply>,
                    weak_factory_.GetWeakPtr()));
   }
 
+  void Encrypt(const std::string& label,
+               const std::string& username,
+               const std::string& input) {
+    GetKeyInfoRequest request;
+    request.set_key_label(label);
+    request.set_username(username);
+    attestation_->GetKeyInfo(request, base::Bind(&ClientLoop::Encrypt2,
+                                                 weak_factory_.GetWeakPtr(),
+                                                 input));
+  }
+
+  void Encrypt2(const std::string& input,
+                const GetKeyInfoReply& key_info) {
+    CryptoUtilityImpl crypto(nullptr);
+    std::string output;
+    if (!crypto.EncryptForUnbind(key_info.public_key(), input, &output)) {
+      QuitWithExitCode(EX_SOFTWARE);
+    }
+    WriteOutput(output);
+    Quit();
+  }
+
+  void CallDecrypt(const std::string& label,
+                   const std::string& username,
+                   const std::string& input) {
+    DecryptRequest request;
+    request.set_key_label(label);
+    request.set_username(username);
+    request.set_encrypted_data(input);
+    attestation_->Decrypt(
+        request,
+        base::Bind(&ClientLoop::PrintReplyAndQuit<DecryptReply>,
+                   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 cc363d8..c5b7111 100644
--- a/common/attestation_interface.h
+++ b/common/attestation_interface.h
@@ -69,6 +69,12 @@
   virtual void CreateCertifiableKey(
       const CreateCertifiableKeyRequest& request,
       const CreateCertifiableKeyCallback& callback) = 0;
+
+  // 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;
 };
 
 }  // namespace attestation
diff --git a/common/crypto_utility.h b/common/crypto_utility.h
index 1f82edf..222c005 100644
--- a/common/crypto_utility.h
+++ b/common/crypto_utility.h
@@ -67,6 +67,12 @@
       const std::string& ek_public_key_info,
       const std::string& aik_public_key,
       EncryptedIdentityCredential* encrypted) = 0;
+
+  // Encrypts |data| in a format compatible with the TPM unbind operation. The
+  // |public_key| must be provided in X.509 SubjectPublicKeyInfo format.
+  virtual bool EncryptForUnbind(const std::string& public_key,
+                                const std::string& data,
+                                std::string* encrypted_data) = 0;
 };
 
 }  // namespace attestation
diff --git a/common/crypto_utility_impl.cc b/common/crypto_utility_impl.cc
index 6d63c74..dda2731 100644
--- a/common/crypto_utility_impl.cc
+++ b/common/crypto_utility_impl.cc
@@ -155,13 +155,15 @@
   crypto::ScopedRSA rsa(d2i_RSAPublicKey(nullptr, &asn1_ptr,
                                          public_key.size()));
   if (!rsa.get()) {
-    LOG(ERROR) << __func__ << ": Failed to decode public key.";
+    LOG(ERROR) << __func__ << ": Failed to decode public key: "
+               << GetOpenSSLError();
     return false;
   }
   unsigned char* buffer = nullptr;
   int length = i2d_RSA_PUBKEY(rsa.get(), &buffer);
   if (length <= 0) {
-    LOG(ERROR) << __func__ << ": Failed to encode public key.";
+    LOG(ERROR) << __func__ << ": Failed to encode public key: "
+               << GetOpenSSLError();
     return false;
   }
   crypto::ScopedOpenSSLBytes scoped_buffer(buffer);
@@ -176,13 +178,15 @@
   crypto::ScopedRSA rsa(d2i_RSA_PUBKEY(NULL, &asn1_ptr,
                                        public_key_info.size()));
   if (!rsa.get()) {
-    LOG(ERROR) << __func__ << ": Failed to decode public key.";
+    LOG(ERROR) << __func__ << ": Failed to decode public key: "
+               << GetOpenSSLError();
     return false;
   }
   unsigned char* buffer = NULL;
   int length = i2d_RSAPublicKey(rsa.get(), &buffer);
   if (length <= 0) {
-    LOG(ERROR) << __func__ << ": Failed to encode public key.";
+    LOG(ERROR) << __func__ << ": Failed to encode public key: "
+               << GetOpenSSLError();
     return false;
   }
   crypto::ScopedOpenSSLBytes scoped_buffer(buffer);
@@ -204,12 +208,12 @@
   // Generate an AES key and encrypt the credential.
   std::string aes_key;
   if (!GetRandom(kAesKeySize, &aes_key)) {
-    LOG(ERROR) << __func__ << "GetRandom failed.";
+    LOG(ERROR) << __func__ << ": GetRandom failed.";
     return false;
   }
   std::string encrypted_credential;
   if (!TssCompatibleEncrypt(credential, aes_key, &encrypted_credential)) {
-    LOG(ERROR) << __func__ << "Failed to encrypt credential.";
+    LOG(ERROR) << __func__ << ": Failed to encrypt credential.";
     return false;
   }
 
@@ -224,13 +228,14 @@
   crypto::ScopedRSA rsa(d2i_RSA_PUBKEY(NULL, &asn1_ptr,
                                        ek_public_key_info.size()));
   if (!rsa.get()) {
-    LOG(ERROR) << __func__ << "Failed to decode EK public key.";
+    LOG(ERROR) << __func__ << ": Failed to decode EK public key: "
+               << GetOpenSSLError();
     return false;
   }
   std::string encrypted_asym_content;
   if (!TpmCompatibleOAEPEncrypt(asym_content, rsa.get(),
                                 &encrypted_asym_content)) {
-    LOG(ERROR) << __func__ << "Failed to encrypt with EK public key.";
+    LOG(ERROR) << __func__ << ": Failed to encrypt with EK public key.";
     return false;
   }
 
@@ -247,6 +252,30 @@
   return true;
 }
 
+bool CryptoUtilityImpl::EncryptForUnbind(const std::string& public_key,
+                                         const std::string& data,
+                                         std::string* encrypted_data) {
+  // Construct a TPM_BOUND_DATA structure.
+  const char kBoundDataHeader[] = {1, 1, 0, 0, 2 /* TPM_PT_BIND */};
+  std::string header(std::begin(kBoundDataHeader), std::end(kBoundDataHeader));
+  std::string bound_data = header + data;
+
+  // Encrypt using the TPM_ES_RSAESOAEP_SHA1_MGF1 scheme.
+  const unsigned char* asn1_ptr = reinterpret_cast<const unsigned char*>(
+      public_key.data());
+  crypto::ScopedRSA rsa(d2i_RSA_PUBKEY(NULL, &asn1_ptr, public_key.size()));
+  if (!rsa.get()) {
+    LOG(ERROR) << __func__ << ": Failed to decode public key: "
+               << GetOpenSSLError();
+    return false;
+  }
+  if (!TpmCompatibleOAEPEncrypt(bound_data, rsa.get(), encrypted_data)) {
+    LOG(ERROR) << __func__ << ": Failed to encrypt with public key.";
+    return false;
+  }
+  return true;
+}
+
 bool CryptoUtilityImpl::AesEncrypt(const std::string& data,
                                    const std::string& key,
                                    const std::string& iv,
@@ -399,7 +428,8 @@
                                           input_buffer, input.size(),
                                           oaep_param, arraysize(oaep_param));
   if (!result) {
-    LOG(ERROR) << __func__ << ": Failed to add OAEP padding.";
+    LOG(ERROR) << __func__ << ": Failed to add OAEP padding: "
+               << GetOpenSSLError();
     return false;
   }
   output->resize(padded_input.size());
@@ -408,7 +438,8 @@
   result = RSA_public_encrypt(padded_input.size(), padded_buffer,
                               output_buffer, key, RSA_NO_PADDING);
   if (result == -1) {
-    LOG(ERROR) << __func__ << ": Failed to encrypt OAEP padded input.";
+    LOG(ERROR) << __func__ << ": Failed to encrypt OAEP padded input: "
+               << GetOpenSSLError();
     return false;
   }
   return true;
diff --git a/common/crypto_utility_impl.h b/common/crypto_utility_impl.h
index ae7c220..dbb917b 100644
--- a/common/crypto_utility_impl.h
+++ b/common/crypto_utility_impl.h
@@ -44,6 +44,9 @@
       const std::string& ek_public_key_info,
       const std::string& aik_public_key,
       EncryptedIdentityCredential* encrypted) override;
+  bool EncryptForUnbind(const std::string& public_key,
+                        const std::string& data,
+                        std::string* encrypted_data) override;
 
  private:
   // Encrypts |data| using |key| and |iv| for AES in CBC mode with PKCS #5
diff --git a/common/crypto_utility_impl_test.cc b/common/crypto_utility_impl_test.cc
index 529b38b..0892ac9 100644
--- a/common/crypto_utility_impl_test.cc
+++ b/common/crypto_utility_impl_test.cc
@@ -185,4 +185,31 @@
                                                           &output));
 }
 
+TEST_F(CryptoUtilityImplTest, EncryptForUnbind) {
+  std::string public_key = HexDecode(kValidPublicKeyHex);
+  std::string public_key_info;
+  EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
+                                                          &public_key_info));
+  std::string output;
+  EXPECT_TRUE(crypto_utility_->EncryptForUnbind(public_key_info, "input",
+                                                &output));
+  EXPECT_FALSE(output.empty());
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptForUnbindBadKey) {
+  std::string output;
+  EXPECT_FALSE(crypto_utility_->EncryptForUnbind("bad_key", "input", &output));
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptForUnbindLargeInput) {
+  std::string public_key = HexDecode(kValidPublicKeyHex);
+  std::string public_key_info;
+  EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
+                                                          &public_key_info));
+  std::string input(1000, 'A');
+  std::string output;
+  EXPECT_FALSE(crypto_utility_->EncryptForUnbind(public_key_info, input,
+                                                 &output));
+}
+
 }  // namespace attestation
diff --git a/common/dbus_interface.h b/common/dbus_interface.h
index 9201f8d..83c3de6 100644
--- a/common/dbus_interface.h
+++ b/common/dbus_interface.h
@@ -19,6 +19,7 @@
 constexpr char kGetAttestationKeyInfo[] = "GetAttestationKeyInfo";
 constexpr char kActivateAttestationKey[] = "ActivateAttestationKey";
 constexpr char kCreateCertifiableKey[] = "CreateCertifiableKey";
+constexpr char kDecrypt[] = "Decrypt";
 
 }  // namespace attestation
 
diff --git a/common/interface.proto b/common/interface.proto
index f8eb66e..946735c 100644
--- a/common/interface.proto
+++ b/common/interface.proto
@@ -126,3 +126,14 @@
   // The signature of certify_info by the Attestation Key.
   optional bytes certify_info_signature = 4;
 }
+
+message DecryptRequest {
+  optional string key_label = 1;
+  optional string username = 2;
+  optional bytes encrypted_data = 3;
+}
+
+message DecryptReply {
+  optional AttestationStatus status = 1;
+  optional bytes decrypted_data = 2;
+}
diff --git a/common/mock_attestation_interface.h b/common/mock_attestation_interface.h
index 12ce5bb..e9e89b7 100644
--- a/common/mock_attestation_interface.h
+++ b/common/mock_attestation_interface.h
@@ -32,9 +32,9 @@
   MOCK_METHOD2(ActivateAttestationKey,
                void(const ActivateAttestationKeyRequest&,
                     const ActivateAttestationKeyCallback&));
-  MOCK_METHOD2(CreateCertifiableKey,
-               void(const CreateCertifiableKeyRequest&,
-                    const CreateCertifiableKeyCallback&));
+  MOCK_METHOD2(CreateCertifiableKey, void(const CreateCertifiableKeyRequest&,
+                                          const CreateCertifiableKeyCallback&));
+  MOCK_METHOD2(Decrypt, void(const DecryptRequest&, const DecryptCallback&));
 };
 
 }  // namespace attestation
diff --git a/common/mock_crypto_utility.h b/common/mock_crypto_utility.h
index ff26ea6..0b68885 100644
--- a/common/mock_crypto_utility.h
+++ b/common/mock_crypto_utility.h
@@ -42,6 +42,9 @@
                                                const std::string&,
                                                const std::string&,
                                                EncryptedIdentityCredential*));
+  MOCK_METHOD3(EncryptForUnbind, bool(const std::string&,
+                                      const std::string&,
+                                      std::string*));
 };
 
 }  // namespace attestation
diff --git a/common/mock_tpm_utility.cc b/common/mock_tpm_utility.cc
index de7c711..2b38909 100644
--- a/common/mock_tpm_utility.cc
+++ b/common/mock_tpm_utility.cc
@@ -7,6 +7,7 @@
 using ::testing::_;
 using ::testing::Invoke;
 using ::testing::Return;
+using ::testing::WithArgs;
 
 namespace {
 
@@ -29,6 +30,8 @@
       .WillByDefault(Invoke(CopyString));
   ON_CALL(*this, Unseal(_, _))
       .WillByDefault(Invoke(CopyString));
+  ON_CALL(*this, Unbind(_, _, _))
+      .WillByDefault(WithArgs<1, 2>(Invoke(CopyString)));
 }
 
 MockTpmUtility::~MockTpmUtility() {}
diff --git a/common/mock_tpm_utility.h b/common/mock_tpm_utility.h
index e1cfed3..deb9746 100644
--- a/common/mock_tpm_utility.h
+++ b/common/mock_tpm_utility.h
@@ -37,6 +37,8 @@
   MOCK_METHOD2(SealToPCR0, bool(const std::string&, std::string*));
   MOCK_METHOD2(Unseal, bool(const std::string&, std::string*));
   MOCK_METHOD1(GetEndorsementPublicKey, bool(std::string*));
+  MOCK_METHOD3(Unbind, bool(const std::string&, const std::string&,
+                            std::string*));
 };
 
 }  // namespace attestation
diff --git a/common/tpm_utility.h b/common/tpm_utility.h
index 3e297b7..98591c3 100644
--- a/common/tpm_utility.h
+++ b/common/tpm_utility.h
@@ -63,6 +63,14 @@
 
   // Reads the endorsement public key from the TPM.
   virtual bool GetEndorsementPublicKey(std::string* public_key) = 0;
+
+  // Unbinds |bound_data| with the key loaded from |key_blob| by decrypting
+  // using the TPM_ES_RSAESOAEP_SHA1_MGF1 scheme. The input must be in the
+  // format of a TPM_BOUND_DATA structure. On success returns true and provides
+  // the decrypted |data|.
+  virtual bool Unbind(const std::string& key_blob,
+                      const std::string& bound_data,
+                      std::string* data) = 0;
 };
 
 }  // namespace attestation
diff --git a/common/tpm_utility_v1.cc b/common/tpm_utility_v1.cc
index 9d8d2da..b6749ae 100644
--- a/common/tpm_utility_v1.cc
+++ b/common/tpm_utility_v1.cc
@@ -26,7 +26,8 @@
 
 namespace {
 
-typedef scoped_ptr<BYTE, base::FreeDeleter> ScopedByteArray;
+using ScopedByteArray = scoped_ptr<BYTE, base::FreeDeleter>;
+using ScopedTssEncryptedData = trousers::ScopedTssObject<TSS_HENCDATA>;
 
 const char* kTpmEnabledFile = "/sys/class/misc/tpm0/device/enabled";
 const char* kTpmOwnedFile = "/sys/class/misc/tpm0/device/owned";
@@ -174,6 +175,7 @@
   UINT32 init_flags = tss_key_type |
                       TSS_KEY_NOT_MIGRATABLE |
                       TSS_KEY_VOLATILE |
+                      TSS_KEY_NO_AUTHORIZATION |
                       TSS_KEY_SIZE_2048;
   TSS_RESULT result = Tspi_Context_CreateObject(context_handle_,
                                                 TSS_OBJECT_TYPE_RSAKEY,
@@ -182,12 +184,19 @@
     TPM_LOG(ERROR, result) << __func__ << ": Failed to create object.";
     return false;
   }
-  result = Tspi_SetAttribUint32(key,
-                                TSS_TSPATTRIB_KEY_INFO,
-                                TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
-                                TSS_SS_RSASSAPKCS1V15_DER);
+  if (key_usage == KEY_USAGE_SIGN) {
+    result = Tspi_SetAttribUint32(key,
+                                  TSS_TSPATTRIB_KEY_INFO,
+                                  TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
+                                  TSS_SS_RSASSAPKCS1V15_DER);
+  } else {
+    result = Tspi_SetAttribUint32(key,
+                                  TSS_TSPATTRIB_KEY_INFO,
+                                  TSS_TSPATTRIB_KEYINFO_ENCSCHEME,
+                                  TSS_ES_RSAESOAEP_SHA1_MGF1);
+  }
   if (TPM_ERROR(result)) {
-    TPM_LOG(ERROR, result) << __func__ << ": Failed to set signature scheme.";
+    TPM_LOG(ERROR, result) << __func__ << ": Failed to set scheme.";
     return false;
   }
   result = Tspi_Key_CreateKey(key, srk_handle_, 0);
@@ -385,6 +394,49 @@
   return true;
 }
 
+bool TpmUtilityV1::Unbind(const std::string& key_blob,
+                          const std::string& bound_data,
+                          std::string* data) {
+  CHECK(data);
+  if (!SetupSrk()) {
+    LOG(ERROR) << "SRK is not ready.";
+    return false;
+  }
+  ScopedTssKey key_handle(context_handle_);
+  if (!LoadKeyFromBlob(key_blob, context_handle_, srk_handle_, &key_handle)) {
+    return false;
+  }
+  TSS_RESULT result;
+  ScopedTssEncryptedData data_handle(context_handle_);
+  if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle_,
+                                                   TSS_OBJECT_TYPE_ENCDATA,
+                                                   TSS_ENCDATA_BIND,
+                                                   data_handle.ptr()))) {
+    TPM_LOG(ERROR, result) << __func__ << ": Tspi_Context_CreateObject failed.";
+    return false;
+  }
+  std::string mutable_bound_data(bound_data);
+  if (TPM_ERROR(result = Tspi_SetAttribData(
+      data_handle,
+      TSS_TSPATTRIB_ENCDATA_BLOB,
+      TSS_TSPATTRIB_ENCDATABLOB_BLOB,
+      bound_data.size(),
+      StringAsTSSBuffer(&mutable_bound_data)))) {
+    TPM_LOG(ERROR, result) << __func__ << ": Tspi_SetAttribData failed.";
+    return false;
+  }
+
+  ScopedTssMemory decrypted_data(context_handle_);
+  UINT32 length = 0;
+  if (TPM_ERROR(result = Tspi_Data_Unbind(data_handle, key_handle,
+                                          &length, decrypted_data.ptr()))) {
+    TPM_LOG(ERROR, result) << __func__ << ": Tspi_Data_Unbind failed.";
+    return false;
+  }
+  data->assign(TSSBufferAsString(decrypted_data.value(), length));
+  return true;
+}
+
 bool TpmUtilityV1::ConnectContext(ScopedTssContext* context, TSS_HTPM* tpm) {
   *tpm = 0;
   TSS_RESULT result;
diff --git a/common/tpm_utility_v1.h b/common/tpm_utility_v1.h
index 85d9786..29de576 100644
--- a/common/tpm_utility_v1.h
+++ b/common/tpm_utility_v1.h
@@ -45,6 +45,9 @@
   bool SealToPCR0(const std::string& data, std::string* sealed_data) override;
   bool Unseal(const std::string& sealed_data, std::string* data) override;
   bool GetEndorsementPublicKey(std::string* public_key) override;
+  bool Unbind(const std::string& key_blob,
+              const std::string& bound_data,
+              std::string* data) override;
 
  private:
   // Populates |context_handle| with a valid TSS_HCONTEXT and |tpm_handle| with
diff --git a/server/attestation_service.cc b/server/attestation_service.cc
index 42e4c22..c0cf126 100644
--- a/server/attestation_service.cc
+++ b/server/attestation_service.cc
@@ -397,6 +397,38 @@
   result->set_certify_info_signature(key.certified_key_proof());
 }
 
+void AttestationService::Decrypt(const DecryptRequest& request,
+                                 const DecryptCallback& callback) {
+  auto result = std::make_shared<DecryptReply>();
+  base::Closure task = base::Bind(
+      &AttestationService::DecryptTask,
+      base::Unretained(this),
+      request,
+      result);
+  base::Closure reply = base::Bind(
+      &AttestationService::TaskRelayCallback<DecryptReply>,
+      GetWeakPtr(),
+      callback,
+      result);
+  worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
+}
+
+void AttestationService::DecryptTask(
+    const DecryptRequest& request,
+    const std::shared_ptr<DecryptReply>& result) {
+  CertifiedKey key;
+  if (!FindKeyByLabel(request.username(), request.key_label(), &key)) {
+    result->set_status(STATUS_INVALID_PARAMETER);
+    return;
+  }
+  std::string data;
+  if (!tpm_utility_->Unbind(key.key_blob(), request.encrypted_data(), &data)) {
+    result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
+    return;
+  }
+  result->set_decrypted_data(data);
+}
+
 bool AttestationService::IsPreparedForEnrollment() {
   if (!tpm_utility_->IsTpmReady()) {
     return false;
diff --git a/server/attestation_service.h b/server/attestation_service.h
index 32fe094..ae6dead 100644
--- a/server/attestation_service.h
+++ b/server/attestation_service.h
@@ -72,6 +72,8 @@
   void CreateCertifiableKey(
       const CreateCertifiableKeyRequest& request,
       const CreateCertifiableKeyCallback& callback) override;
+  void Decrypt(const DecryptRequest& request,
+               const DecryptCallback& callback) override;
 
   // Mutators useful for testing.
   void set_crypto_utility(CryptoUtility* crypto_utility) {
@@ -146,6 +148,10 @@
       const CreateCertifiableKeyRequest& request,
       const std::shared_ptr<CreateCertifiableKeyReply>& result);
 
+  // A synchronous implementation of Decrypt.
+  void DecryptTask(const DecryptRequest& request,
+                   const std::shared_ptr<DecryptReply>& 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 0c72c2c..ecc2eec 100644
--- a/server/attestation_service_test.cc
+++ b/server/attestation_service_test.cc
@@ -847,4 +847,81 @@
   Run();
 }
 
+TEST_F(AttestationServiceTest, DecryptSuccess) {
+  // Set expectations on the outputs.
+  auto callback = [this](const DecryptReply& reply) {
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+    EXPECT_EQ("data", reply.decrypted_data());
+    Quit();
+  };
+  DecryptRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  request.set_encrypted_data("data");
+  service_->Decrypt(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptSuccessNoUser) {
+  mock_database_.GetMutableProtobuf()->add_device_keys()->set_key_name("label");
+  // Set expectations on the outputs.
+  auto callback = [this](const DecryptReply& reply) {
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+    EXPECT_EQ("data", reply.decrypted_data());
+    Quit();
+  };
+  DecryptRequest request;
+  request.set_key_label("label");
+  request.set_encrypted_data("data");
+  service_->Decrypt(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptKeyNotFound) {
+  EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+      .WillRepeatedly(Return(false));
+  // Set expectations on the outputs.
+  auto callback = [this](const DecryptReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    EXPECT_FALSE(reply.has_decrypted_data());
+    Quit();
+  };
+  DecryptRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  request.set_encrypted_data("data");
+  service_->Decrypt(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptKeyNotFoundNoUser) {
+  // Set expectations on the outputs.
+  auto callback = [this](const DecryptReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    EXPECT_FALSE(reply.has_decrypted_data());
+    Quit();
+  };
+  DecryptRequest request;
+  request.set_key_label("label");
+  request.set_encrypted_data("data");
+  service_->Decrypt(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptUnbindFailure) {
+  EXPECT_CALL(mock_tpm_utility_, Unbind(_, _, _)).WillRepeatedly(Return(false));
+  // Set expectations on the outputs.
+  auto callback = [this](const DecryptReply& reply) {
+    EXPECT_NE(STATUS_SUCCESS, reply.status());
+    EXPECT_FALSE(reply.has_decrypted_data());
+    Quit();
+  };
+  DecryptRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  request.set_encrypted_data("data");
+  service_->Decrypt(request, base::Bind(callback));
+  Run();
+}
+
 }  // namespace attestation
diff --git a/server/dbus_service.cc b/server/dbus_service.cc
index 55df17f..815d0f5 100644
--- a/server/dbus_service.cc
+++ b/server/dbus_service.cc
@@ -27,30 +27,27 @@
   chromeos::dbus_utils::DBusInterface* dbus_interface =
       dbus_object_.AddOrGetInterface(kAttestationInterface);
 
-  dbus_interface->AddMethodHandler(
-      kCreateGoogleAttestedKey,
-      base::Unretained(this),
-      &DBusService::HandleCreateGoogleAttestedKey);
-  dbus_interface->AddMethodHandler(
-      kGetKeyInfo,
-      base::Unretained(this),
-      &DBusService::HandleGetKeyInfo);
-  dbus_interface->AddMethodHandler(
-      kGetEndorsementInfo,
-      base::Unretained(this),
-      &DBusService::HandleGetEndorsementInfo);
-  dbus_interface->AddMethodHandler(
-      kGetAttestationKeyInfo,
-      base::Unretained(this),
-      &DBusService::HandleGetAttestationKeyInfo);
-  dbus_interface->AddMethodHandler(
-      kActivateAttestationKey,
-      base::Unretained(this),
-      &DBusService::HandleActivateAttestationKey);
-  dbus_interface->AddMethodHandler(
-      kCreateCertifiableKey,
-      base::Unretained(this),
-      &DBusService::HandleCreateCertifiableKey);
+  dbus_interface->AddMethodHandler(kCreateGoogleAttestedKey,
+                                   base::Unretained(this),
+                                   &DBusService::HandleCreateGoogleAttestedKey);
+  dbus_interface->AddMethodHandler(kGetKeyInfo,
+                                   base::Unretained(this),
+                                   &DBusService::HandleGetKeyInfo);
+  dbus_interface->AddMethodHandler(kGetEndorsementInfo,
+                                   base::Unretained(this),
+                                   &DBusService::HandleGetEndorsementInfo);
+  dbus_interface->AddMethodHandler(kGetAttestationKeyInfo,
+                                   base::Unretained(this),
+                                   &DBusService::HandleGetAttestationKeyInfo);
+  dbus_interface->AddMethodHandler(kActivateAttestationKey,
+                                   base::Unretained(this),
+                                   &DBusService::HandleActivateAttestationKey);
+  dbus_interface->AddMethodHandler(kCreateCertifiableKey,
+                                   base::Unretained(this),
+                                   &DBusService::HandleCreateCertifiableKey);
+  dbus_interface->AddMethodHandler(kDecrypt,
+                                   base::Unretained(this),
+                                   &DBusService::HandleDecrypt);
 
   dbus_object_.RegisterAsync(callback);
 }
@@ -168,4 +165,22 @@
       base::Bind(callback, SharedResponsePointer(std::move(response))));
 }
 
+void DBusService::HandleDecrypt(
+    std::unique_ptr<DBusMethodResponse<const DecryptReply&>> response,
+    const DecryptRequest& request) {
+  VLOG(1) << __func__;
+  // Convert |response| to a shared_ptr so |service_| can safely copy the
+  // callback.
+  using SharedResponsePointer = std::shared_ptr<
+      DBusMethodResponse<const DecryptReply&>>;
+  // A callback that fills the reply protobuf and sends it.
+  auto callback = [](const SharedResponsePointer& response,
+                     const DecryptReply& reply) {
+    response->Return(reply);
+  };
+  service_->Decrypt(
+      request,
+      base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
 }  // namespace attestation
diff --git a/server/dbus_service.h b/server/dbus_service.h
index 7ba461c..35fe7be 100644
--- a/server/dbus_service.h
+++ b/server/dbus_service.h
@@ -74,6 +74,12 @@
           const CreateCertifiableKeyReply&>> response,
       const CreateCertifiableKeyRequest& request);
 
+  // Handles a Decrypt D-Bus call.
+  void HandleDecrypt(
+      std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<
+          const DecryptReply&>> response,
+      const DecryptRequest& request);
+
   chromeos::dbus_utils::DBusObject dbus_object_;
   AttestationInterface* service_;
 
diff --git a/server/dbus_service_test.cc b/server/dbus_service_test.cc
index 5ccbb1f..11f1987 100644
--- a/server/dbus_service_test.cc
+++ b/server/dbus_service_test.cc
@@ -258,8 +258,7 @@
   EXPECT_CALL(mock_service_, CreateCertifiableKey(_, _))
       .WillOnce(Invoke([](
           const CreateCertifiableKeyRequest& request,
-          const AttestationInterface::CreateCertifiableKeyCallback&
-              callback) {
+          const AttestationInterface::CreateCertifiableKeyCallback& callback) {
         EXPECT_EQ("label", request.key_label());
         EXPECT_EQ(KEY_TYPE_ECC, request.key_type());
         EXPECT_EQ(KEY_USAGE_SIGN, request.key_usage());
@@ -285,4 +284,33 @@
   EXPECT_EQ("signature", reply.certify_info_signature());
 }
 
+TEST_F(DBusServiceTest, Decrypt) {
+  DecryptRequest request;
+  request.set_key_label("label");
+  request.set_username("user");
+  request.set_encrypted_data("data");
+  EXPECT_CALL(mock_service_, Decrypt(_, _))
+      .WillOnce(Invoke([](
+          const DecryptRequest& request,
+          const AttestationInterface::DecryptCallback& callback) {
+        EXPECT_EQ("label", request.key_label());
+        EXPECT_EQ("user", request.username());
+        EXPECT_EQ("data", request.encrypted_data());
+        DecryptReply reply;
+        reply.set_status(STATUS_SUCCESS);
+        reply.set_decrypted_data("data");
+        callback.Run(reply);
+      }));
+  std::unique_ptr<dbus::MethodCall> call =
+      CreateMethodCall(kDecrypt);
+  dbus::MessageWriter writer(call.get());
+  writer.AppendProtoAsArrayOfBytes(request);
+  auto response = CallMethod(call.get());
+  dbus::MessageReader reader(response.get());
+  DecryptReply reply;
+  EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+  EXPECT_EQ(STATUS_SUCCESS, reply.status());
+  EXPECT_EQ("data", reply.decrypted_data());
+}
+
 }  // namespace attestation