trunks, tpm_manager: prepare for ownership earlier

Taking TPM ownership during OOBE can only be started when
Chrome sends an appropriate command to cryptohomed. However,
there are several operations that can be done on the TPM,
and which are needed for taking ownership, before that:
create SRK(s) and the salting key for sessions using the
default well-known TPM password.
Some of these operations are lengthy, so performing them
earlier allows to minimize the remaining time for taking
the TPM ownership and improve UX.

BUG=chromium:772187
TEST=go through OOBE on eve

Change-Id: Id73b4252940c767fc2fa45414ed2ff7bdbec4887
Reviewed-on: https://chromium-review.googlesource.com/731857
Commit-Ready: Andrey Pronin <apronin@chromium.org>
Tested-by: Andrey Pronin <apronin@chromium.org>
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
diff --git a/tpm_manager/server/mock_tpm_initializer.cc b/tpm_manager/server/mock_tpm_initializer.cc
index 4f9dba6..59d2305 100644
--- a/tpm_manager/server/mock_tpm_initializer.cc
+++ b/tpm_manager/server/mock_tpm_initializer.cc
@@ -21,6 +21,7 @@
 namespace tpm_manager {
 
 MockTpmInitializer::MockTpmInitializer() {
+  ON_CALL(*this, PreInitializeTpm()).WillByDefault(Return(true));
   ON_CALL(*this, InitializeTpm()).WillByDefault(Return(true));
   ON_CALL(*this, ResetDictionaryAttackLock()).WillByDefault(Return(true));
 }
diff --git a/tpm_manager/server/mock_tpm_initializer.h b/tpm_manager/server/mock_tpm_initializer.h
index 2d3936f..31f849f 100644
--- a/tpm_manager/server/mock_tpm_initializer.h
+++ b/tpm_manager/server/mock_tpm_initializer.h
@@ -28,6 +28,7 @@
   MockTpmInitializer();
   ~MockTpmInitializer() override;
 
+  MOCK_METHOD0(PreInitializeTpm, bool());
   MOCK_METHOD0(InitializeTpm, bool());
   MOCK_METHOD0(VerifiedBootHelper, void());
   MOCK_METHOD0(ResetDictionaryAttackLock, bool());
diff --git a/tpm_manager/server/tpm2_initializer_impl.cc b/tpm_manager/server/tpm2_initializer_impl.cc
index 93261d1..20868fa 100644
--- a/tpm_manager/server/tpm2_initializer_impl.cc
+++ b/tpm_manager/server/tpm2_initializer_impl.cc
@@ -58,6 +58,16 @@
       local_data_store_(local_data_store),
       tpm_status_(tpm_status) {}
 
+bool Tpm2InitializerImpl::PreInitializeTpm() {
+  TPM_RC result = trunks_factory_.GetTpmUtility()->PrepareForOwnership();
+  if (result != TPM_RC_SUCCESS) {
+    LOG(WARNING) << "Pre-initializing TPM2.0 failed.";
+    return false;
+  }
+
+  return true;
+}
+
 bool Tpm2InitializerImpl::InitializeTpm() {
   if (!SeedTpmRng()) {
     return false;
diff --git a/tpm_manager/server/tpm2_initializer_impl.h b/tpm_manager/server/tpm2_initializer_impl.h
index ca874bc..4603ce4 100644
--- a/tpm_manager/server/tpm2_initializer_impl.h
+++ b/tpm_manager/server/tpm2_initializer_impl.h
@@ -55,6 +55,7 @@
 
   // TpmInitializer methods.
   bool InitializeTpm() override;
+  bool PreInitializeTpm() override;
   void VerifiedBootHelper() override;
   bool ResetDictionaryAttackLock() override;
 
diff --git a/tpm_manager/server/tpm_initializer.h b/tpm_manager/server/tpm_initializer.h
index a1cad78..c56c8ec 100644
--- a/tpm_manager/server/tpm_initializer.h
+++ b/tpm_manager/server/tpm_initializer.h
@@ -31,6 +31,15 @@
   // the process picks up where it left off.
   virtual bool InitializeTpm() = 0;
 
+  // Performs actions that can be done on uninitialized TPM before
+  // receiving a signal that taking ownership can be attempted.
+  // This is an optional optimization: InitializeTpm() doesn't rely on
+  // it to be called first and runs pre-initialization steps, if necessary,
+  // itself.
+  // If the TPM is already initialized, does nothing.
+  // Returns an error if pre-initialization is attempted but failed.
+  virtual bool PreInitializeTpm() = 0;
+
   // This will be called when the service is initializing. It is an early
   // opportunity to perform tasks related to verified boot.
   virtual void VerifiedBootHelper() = 0;
diff --git a/tpm_manager/server/tpm_initializer_impl.cc b/tpm_manager/server/tpm_initializer_impl.cc
index 0042e65..1a79abb 100644
--- a/tpm_manager/server/tpm_initializer_impl.cc
+++ b/tpm_manager/server/tpm_initializer_impl.cc
@@ -53,6 +53,11 @@
                                        TpmStatus* tpm_status)
     : local_data_store_(local_data_store), tpm_status_(tpm_status) {}
 
+bool TpmInitializerImpl::PreInitializeTpm() {
+  // No pre-initialization steps are performed for 1.2.
+  return true;
+}
+
 bool TpmInitializerImpl::InitializeTpm() {
   if (tpm_status_->IsTpmOwned() && !TestTpmAuth(GetDefaultOwnerPassword())) {
     // Tpm is already owned, so we do not need to do anything.
diff --git a/tpm_manager/server/tpm_initializer_impl.h b/tpm_manager/server/tpm_initializer_impl.h
index 4a80b79..6f24b73 100644
--- a/tpm_manager/server/tpm_initializer_impl.h
+++ b/tpm_manager/server/tpm_initializer_impl.h
@@ -49,6 +49,7 @@
 
   // TpmInitializer methods.
   bool InitializeTpm() override;
+  bool PreInitializeTpm() override;
   void VerifiedBootHelper() override;
   bool ResetDictionaryAttackLock() override;
 
diff --git a/tpm_manager/server/tpm_manager_service.cc b/tpm_manager/server/tpm_manager_service.cc
index 39db1cc..c660e53 100644
--- a/tpm_manager/server/tpm_manager_service.cc
+++ b/tpm_manager/server/tpm_manager_service.cc
@@ -115,6 +115,9 @@
       LOG(WARNING) << __func__ << ": TPM initialization failed.";
       return;
     }
+  } else {
+    VLOG(1) << "Pre-initializing TPM.";
+    tpm_initializer_->PreInitializeTpm();
   }
 }
 
diff --git a/tpm_manager/server/tpm_manager_service_test.cc b/tpm_manager/server/tpm_manager_service_test.cc
index 52575fc..c04cb0f 100644
--- a/tpm_manager/server/tpm_manager_service_test.cc
+++ b/tpm_manager/server/tpm_manager_service_test.cc
@@ -96,6 +96,7 @@
 TEST_F(TpmManagerServiceTest_NoWaitForOwnership, AutoInitialize) {
   // Make sure InitializeTpm doesn't get multiple calls.
   EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(1);
+  EXPECT_CALL(mock_tpm_initializer_, PreInitializeTpm()).Times(0);
   SetupService();
   RunServiceWorkerAndQuit();
 }
@@ -103,6 +104,7 @@
 TEST_F(TpmManagerServiceTest_NoWaitForOwnership, AutoInitializeNoTpm) {
   EXPECT_CALL(mock_tpm_status_, IsTpmEnabled()).WillRepeatedly(Return(false));
   EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(0);
+  EXPECT_CALL(mock_tpm_initializer_, PreInitializeTpm()).Times(0);
   SetupService();
   RunServiceWorkerAndQuit();
 }
diff --git a/trunks/mock_tpm_utility.h b/trunks/mock_tpm_utility.h
index b66128b..adab3a1 100644
--- a/trunks/mock_tpm_utility.h
+++ b/trunks/mock_tpm_utility.h
@@ -36,6 +36,7 @@
   MOCK_METHOD0(InitializeTpm, TPM_RC());
   MOCK_METHOD0(CheckState, TPM_RC());
   MOCK_METHOD1(AllocatePCR, TPM_RC(const std::string&));
+  MOCK_METHOD0(PrepareForOwnership, TPM_RC());
   MOCK_METHOD3(TakeOwnership,
                TPM_RC(const std::string&,
                       const std::string&,
diff --git a/trunks/tpm_utility.h b/trunks/tpm_utility.h
index e7417b2..faf8a72 100644
--- a/trunks/tpm_utility.h
+++ b/trunks/tpm_utility.h
@@ -76,6 +76,13 @@
   // NOTE: This command needs platform authorization and PP assertion.
   virtual TPM_RC AllocatePCR(const std::string& platform_password) = 0;
 
+  // Performs steps needed for taking ownership, which can be done before
+  // a signal that an ownership can be attempted is received.
+  // This operation is an optional optimization: if PrepareForOwnership
+  // is not called, TakeOwnership will later run through those preparational
+  // steps, if needed.
+  virtual TPM_RC PrepareForOwnership() = 0;
+
   // Synchronously takes ownership of the TPM with the given passwords as
   // authorization values.
   virtual TPM_RC TakeOwnership(const std::string& owner_password,
diff --git a/trunks/tpm_utility_impl.cc b/trunks/tpm_utility_impl.cc
index 261d226..456f0e7 100644
--- a/trunks/tpm_utility_impl.cc
+++ b/trunks/tpm_utility_impl.cc
@@ -298,13 +298,27 @@
   return TPM_RC_SUCCESS;
 }
 
-TPM_RC TpmUtilityImpl::TakeOwnership(const std::string& owner_password,
-                                     const std::string& endorsement_password,
-                                     const std::string& lockout_password) {
+TPM_RC TpmUtilityImpl::PrepareForOwnership() {
+  std::unique_ptr<TpmState> tpm_state(factory_.GetTpmState());
+  TPM_RC result = tpm_state->Initialize();
+  if (result) {
+    LOG(ERROR) << __func__ << ": Error initializing state: "
+               << GetErrorString(result);
+    return result;
+  }
+  if (tpm_state->IsOwnerPasswordSet()) {
+    VLOG(1) << __func__ << ": Nothing to do. Owner password is already set.";
+    return TPM_RC_SUCCESS;
+  }
+  result = CreateStorageAndSaltingKeys();
+  LOG_IF(INFO, result == TPM_RC_SUCCESS) << __func__ << ": done.";
+  return result;
+}
+
+TPM_RC TpmUtilityImpl::CreateStorageAndSaltingKeys() {
   // First we set the storage hierarchy authorization to the well know default
   // password.
-  TPM_RC result = TPM_RC_SUCCESS;
-  result = SetKnownOwnerPassword(kWellKnownPassword);
+  TPM_RC result = result = SetKnownOwnerPassword(kWellKnownPassword);
   if (result != TPM_RC_SUCCESS) {
     LOG(ERROR) << __func__ << ": Error injecting known password: "
                << GetErrorString(result);
@@ -317,6 +331,7 @@
                << ": Error creating SRKs: " << GetErrorString(result);
     return result;
   }
+
   result = CreateSaltingKey(kWellKnownPassword);
   if (result) {
     LOG(ERROR) << __func__
@@ -324,6 +339,16 @@
     return result;
   }
 
+  return result;
+}
+
+TPM_RC TpmUtilityImpl::TakeOwnership(const std::string& owner_password,
+                                     const std::string& endorsement_password,
+                                     const std::string& lockout_password) {
+  TPM_RC result = CreateStorageAndSaltingKeys();
+  if (result != TPM_RC_SUCCESS) {
+    return result;
+  }
   std::unique_ptr<HmacSession> session = factory_.GetHmacSession();
   result = session->StartUnboundSession(true);
   if (result != TPM_RC_SUCCESS) {
diff --git a/trunks/tpm_utility_impl.h b/trunks/tpm_utility_impl.h
index 6a3624f..2fd226a 100644
--- a/trunks/tpm_utility_impl.h
+++ b/trunks/tpm_utility_impl.h
@@ -47,6 +47,7 @@
   TPM_RC CheckState() override;
   TPM_RC InitializeTpm() override;
   TPM_RC AllocatePCR(const std::string& platform_password) override;
+  TPM_RC PrepareForOwnership() override;
   TPM_RC TakeOwnership(const std::string& owner_password,
                        const std::string& endorsement_password,
                        const std::string& lockout_password) override;
@@ -175,6 +176,13 @@
   std::map<uint32_t, TPMS_NV_PUBLIC> nvram_public_area_map_;
   uint32_t vendor_id_;
 
+  // This methods sets the well-known owner authorization and creates SRK and
+  // the salting key with it. Only succeeds if owner authorization was not set
+  // yet. These are the common operations done (1) by pre-initialization when
+  // the owner authorization is not set yet, and (2) when taking ownership,
+  // which repeats them in case (pre-)initialization was interrupted earlier.
+  TPM_RC CreateStorageAndSaltingKeys();
+
   // This method sets a known owner password in the TPM_RH_OWNER hierarchy.
   TPM_RC SetKnownOwnerPassword(const std::string& known_owner_password);
 
diff --git a/trunks/tpm_utility_test.cc b/trunks/tpm_utility_test.cc
index d6abc51..43b054a 100644
--- a/trunks/tpm_utility_test.cc
+++ b/trunks/tpm_utility_test.cc
@@ -294,6 +294,33 @@
   EXPECT_EQ(TPM_RC_FAILURE, utility_.AllocatePCR(""));
 }
 
+TEST_F(TpmUtilityTest, PrepareForOwnershipSuccess) {
+  EXPECT_CALL(mock_tpm_state_, IsOwnerPasswordSet())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_tpm_, HierarchyChangeAuthSync(TPM_RH_OWNER, _, _, _))
+      .WillOnce(Return(TPM_RC_SUCCESS));
+  EXPECT_EQ(TPM_RC_SUCCESS,
+            utility_.PrepareForOwnership());
+}
+
+TEST_F(TpmUtilityTest, PrepareForOwnershipFailure) {
+  EXPECT_CALL(mock_tpm_state_, IsOwnerPasswordSet())
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_tpm_, HierarchyChangeAuthSync(TPM_RH_OWNER, _, _, _))
+      .WillOnce(Return(TPM_RC_FAILURE));
+  EXPECT_EQ(TPM_RC_FAILURE,
+            utility_.PrepareForOwnership());
+}
+
+TEST_F(TpmUtilityTest, PrepareForOwnershipAlreadyOwned) {
+  EXPECT_CALL(mock_tpm_state_, IsOwnerPasswordSet())
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_tpm_, HierarchyChangeAuthSync(_, _, _, _))
+      .Times(0);
+  EXPECT_EQ(TPM_RC_SUCCESS,
+            utility_.PrepareForOwnership());
+}
+
 TEST_F(TpmUtilityTest, TakeOwnershipSuccess) {
   EXPECT_CALL(mock_tpm_state_, IsOwnerPasswordSet())
       .WillRepeatedly(Return(false));
diff --git a/trunks/trunks_factory_for_test.cc b/trunks/trunks_factory_for_test.cc
index a72cd43..ccedab5 100644
--- a/trunks/trunks_factory_for_test.cc
+++ b/trunks/trunks_factory_for_test.cc
@@ -160,6 +160,10 @@
     return target_->AllocatePCR(platform_password);
   }
 
+  TPM_RC PrepareForOwnership() override {
+    return target_->PrepareForOwnership();
+  }
+
   TPM_RC TakeOwnership(const std::string& owner_password,
                        const std::string& endorsement_password,
                        const std::string& lockout_password) override {