cryptohome: Enhanced handling of corrupted attestation database in migration.

Database migration process will now try to re-create PCR0/1 quote if
they are not found in the database.

BUG=chromium:899932
TEST=USE=asan FEATURES=test emerge-chell cryptohome

Change-Id: Ic2c77141427ad7136253ab481f405c8f242d1187
Reviewed-on: https://chromium-review.googlesource.com/1350568
Commit-Ready: John  Chen <zuan@chromium.org>
Tested-by: John  Chen <zuan@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
(cherry picked from commit 97981945a083079767c5b3f3ba4eb6c40dec15a4)
Reviewed-on: https://chromium-review.googlesource.com/c/1369446
Reviewed-by: John  Chen <zuan@chromium.org>
Reviewed-by: Louis Collard <louiscollard@chromium.org>
Commit-Queue: John  Chen <zuan@chromium.org>
diff --git a/attestation/server/attestation_service.h b/attestation/server/attestation_service.h
index fac2a6a..e7ee491 100644
--- a/attestation/server/attestation_service.h
+++ b/attestation/server/attestation_service.h
@@ -224,6 +224,7 @@
 
   // Migrates identity date in |database_| if needed. Returns true if the
   // migration was needed and successful.
+  // Note that this function is not multithread safe.
   bool MigrateIdentityData();
 
   // Shutdown to be run on the worker thread.
diff --git a/cryptohome/attestation.cc b/cryptohome/attestation.cc
index 85c1b78..f05b3e4 100644
--- a/cryptohome/attestation.cc
+++ b/cryptohome/attestation.cc
@@ -623,41 +623,6 @@
     return -1;
   }
 
-  // Quote PCR0.
-  SecureBlob external_data;
-  if (!tpm_->GetRandomDataSecureBlob(kQuoteExternalDataSize, &external_data)) {
-    LOG(ERROR) << __func__ << ": GetRandomDataSecureBlob failed.";
-    return -1;
-  }
-  Blob quoted_pcr_value0;
-  SecureBlob quoted_data0;
-  SecureBlob quote0;
-  if (!tpm_->QuotePCR(0,
-                      identity_key_blob,
-                      external_data,
-                      &quoted_pcr_value0,
-                      &quoted_data0,
-                      &quote0)) {
-    LOG(ERROR) << "Attestation: Failed to generate PCR0 quote for identity "
-               << identity << ".";
-    return -1;
-  }
-
-  // Quote PCR1.
-  Blob quoted_pcr_value1;
-  SecureBlob quoted_data1;
-  SecureBlob quote1;
-  if (!tpm_->QuotePCR(1,
-                      identity_key_blob,
-                      external_data,
-                      &quoted_pcr_value1,
-                      &quoted_data1,
-                      &quote1)) {
-    LOG(ERROR) << "Attestation: Failed to generate PCR1 quote for identity "
-               << identity << ".";
-    return -1;
-  }
-
   // This only needs to be done once when we haven't stored credentials yet.
   TPMCredentials* credentials_pb = database_pb_.mutable_credentials();
   if (!credentials_pb->has_endorsement_credential()) {
@@ -699,28 +664,27 @@
   // Store PCR quotes in the identity.
   auto* map = identity_data->mutable_pcr_quotes();
 
-  Quote quote_pb0;
-  quote_pb0.set_quote(quote0.data(), quote0.size());
-  quote_pb0.set_quoted_data(quoted_data0.data(), quoted_data0.size());
-  quote_pb0.set_quoted_pcr_value(BlobToString(quoted_pcr_value0));
-  auto in0 = map->insert(QuoteMap::value_type(0, quote_pb0));
-  if (!in0.second) {
-    LOG(ERROR) << "Attestation: Failed to store PCR0 quote for identity "
-               << identity << ".";
-    return -1;
-  }
+  // Quote PCR0 and PCR1
+  for (int i = 0; i <= 1; i++) {
+    Quote quote_pb;
+    if (!CreatePCRQuote(i, identity_key_blob, &quote_pb)) {
+      // Note that if in the future we regularly uses multiple AIK
+      // (i.e. identity key), then we'll need to print error messages here to
+      // indicate which one of the identity key failed the PCR quote process.
+      return -1;
+    }
 
-  Quote quote_pb1;
-  quote_pb1.set_quote(quote1.data(), quote1.size());
-  quote_pb1.set_quoted_data(quoted_data1.data(), quoted_data1.size());
-  quote_pb1.set_quoted_pcr_value(BlobToString(quoted_pcr_value1));
-  quote_pb1.set_pcr_source_hint(platform_->GetHardwareID());
+    // PCR1 quote needs to have the source hint
+    if (i == 1) {
+      quote_pb.set_pcr_source_hint(platform_->GetHardwareID());
+    }
 
-  auto in1 = map->insert(QuoteMap::value_type(1, quote_pb1));
-  if (!in1.second) {
-    LOG(ERROR) << "Attestation: Failed to store PCR1 quote for identity "
-               << identity << ".";
-    return -1;
+    auto in = map->insert(QuoteMap::value_type(i, quote_pb));
+    if (!in.second) {
+      LOG(ERROR) << "Attestation: Failed to store PCR" << i << " quote for "
+                 << "identity " << identity << ".";
+      return -1;
+    }
   }
 
   // Return the index of the newly created identity.
@@ -1837,79 +1801,93 @@
     return false;
   }
 
-  bool error = false;
-
   // The identity we're creating will have the next index in identities.
   LOG(INFO) << "Attestation: Migrating existing identity into identity "
             << database_pb_.identities().size() << ".";
-  AttestationDatabase::Identity* identity_data =
-      database_pb_.mutable_identities()->Add();
-  identity_data->set_features(IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID);
+  AttestationDatabase::Identity identity_data;
+  identity_data.set_features(IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID);
   if (database_pb_.has_identity_binding()) {
-    identity_data->mutable_identity_binding()->CopyFrom(
+    identity_data.mutable_identity_binding()->CopyFrom(
         database_pb_.identity_binding());
   } else {
     LOG(ERROR) << "Attestation: Identity Key Binding not found, not migrating.";
-    error = true;
+    return false;
   }
 
   if (database_pb_.has_identity_key()) {
-    identity_data->mutable_identity_key()->CopyFrom(
+    identity_data.mutable_identity_key()->CopyFrom(
         database_pb_.identity_key());
-    identity_data->mutable_identity_key()->clear_identity_credential();
-    if (database_pb_.identity_key().has_identity_credential()) {
-      // Create an identity certificate for this identity and the default PCA.
-      AttestationDatabase::IdentityCertificate identity_certificate;
-      identity_certificate.set_identity(kFirstIdentity);
-      identity_certificate.set_aca(kDefaultPCA);
-      identity_certificate.set_identity_credential(
-          database_pb_.identity_key().identity_credential());
-      auto* map = database_pb_.mutable_identity_certificates();
-      auto in = map->insert(IdentityCertificateMap::value_type(
-          kDefaultPCA, identity_certificate));
-      if (!in.second) {
-        LOG(ERROR) << "Attestation: Could not migrate existing identity.";
-        error = true;
-      }
-    }
-    if (database_pb_.identity_key().has_enrollment_id()) {
-      database_pb_.set_enrollment_id(
-          database_pb_.identity_key().enrollment_id());
-    }
+    identity_data.mutable_identity_key()->clear_identity_credential();
+    // Migration for the other part of the identity_key is moved to the end of
+    // the function, this is so that we don't have to rollback too many things
+    // when there's an error.
   } else {
     LOG(ERROR) << "Attestation: Identity Key not found, not migrating.";
-    error = true;
+    return false;
   }
 
-  if (database_pb_.has_pcr0_quote()) {
-    auto in = identity_data->mutable_pcr_quotes()->insert(
-        QuoteMap::value_type(0, database_pb_.pcr0_quote()));
+  for (int i = 0; i <= 1; i++) {
+    Quote quote_pb;
+    if (i == 0 && database_pb_.has_pcr0_quote()) {
+      quote_pb.CopyFrom(database_pb_.pcr0_quote());
+    } else if (i == 1 && database_pb_.has_pcr1_quote()) {
+      quote_pb.CopyFrom(database_pb_.pcr1_quote());
+    } else {
+      // Attempt to generate the missing PCR quote.
+      SecureBlob identity_key_blob(
+        identity_data.identity_key().identity_key_blob());
+      if (CreatePCRQuote(i, identity_key_blob, &quote_pb)) {
+        if (i == 1) {
+          quote_pb.set_pcr_source_hint(platform_->GetHardwareID());
+        }
+        LOG(WARNING) << "Attestation: Regenerated missing PCR" << i
+                     << " quote during migration.";
+      } else {
+        LOG(ERROR) << "Attestation: Failed to regenerate missing PCR"
+                   << i << " quote during migration.";
+        return false;
+      }
+    }
+
+    // If we arrive here, quote_pb is definitely filled.
+    auto in = identity_data.mutable_pcr_quotes()->insert(
+        QuoteMap::value_type(i, quote_pb));
     if (!in.second) {
       LOG(ERROR) << "Attestation: Could not migrate existing identity.";
-      error = true;
+      return false;
     }
-  } else {
-    LOG(ERROR) << "Attestation: Missing PCR0 quote in existing database.";
-    error = true;
   }
-  if (database_pb_.has_pcr1_quote()) {
-    auto in = identity_data->mutable_pcr_quotes()->insert(
-        QuoteMap::value_type(1, database_pb_.pcr1_quote()));
+
+  // Migrate the other part of identity_key, note that since we are here,
+  // identity_key is guaranteed to exist.
+  if (database_pb_.identity_key().has_identity_credential()) {
+    // Create an identity certificate for this identity and the default PCA.
+    AttestationDatabase::IdentityCertificate identity_certificate;
+    identity_certificate.set_identity(kFirstIdentity);
+    identity_certificate.set_aca(kDefaultPCA);
+    identity_certificate.set_identity_credential(
+      database_pb_.identity_key().identity_credential());
+
+    auto* map = database_pb_.mutable_identity_certificates();
+    auto in = map->insert(IdentityCertificateMap::value_type(
+      kDefaultPCA, identity_certificate));
     if (!in.second) {
       LOG(ERROR) << "Attestation: Could not migrate existing identity.";
-      error = true;
+      return false;
     }
-  } else {
-    LOG(ERROR) << "Attestation: Missing PCR1 quote in existing database.";
-    error = true;
+  }
+  if (database_pb_.identity_key().has_enrollment_id()) {
+    database_pb_.set_enrollment_id(
+      database_pb_.identity_key().enrollment_id());
   }
 
-  if (error) {
-    database_pb_.mutable_identities()->RemoveLast();
-    database_pb_.mutable_identity_certificates()->erase(kDefaultPCA);
-  }
+  // If we are here, we can be sure that identity_data actually holds a
+  // valid identity object, so we save it back into the DB.
+  AttestationDatabase::Identity* new_identity_data =
+    database_pb_.mutable_identities()->Add();
+  new_identity_data->CopyFrom(identity_data);
 
-  return !error;
+  return true;
 }
 
 void Attestation::ClearDatabase() {
@@ -2693,6 +2671,35 @@
   }
 }
 
+bool Attestation::CreatePCRQuote(
+    uint32_t pcr_index,
+    const SecureBlob& identity_key_blob,
+    Quote* output) {
+  SecureBlob external_data(kQuoteExternalDataSize);
+  CryptoLib::GetSecureRandom(external_data.data(), kQuoteExternalDataSize);
+
+  Blob quoted_pcr_value;
+  SecureBlob quoted_data;
+  SecureBlob quote;
+
+  if (!tpm_->QuotePCR(pcr_index,
+                      identity_key_blob,
+                      external_data,
+                      &quoted_pcr_value,
+                      &quoted_data,
+                      &quote)) {
+    LOG(ERROR) << "Attestation: Failed to generate PCR" << pcr_index
+               << " quote.";
+    return false;
+  }
+
+  output->set_quote(quote.data(), quote.size());
+  output->set_quoted_data(quoted_data.data(), quoted_data.size());
+  output->set_quoted_pcr_value(BlobToString(quoted_pcr_value));
+
+  return true;
+}
+
 bool Attestation::SendPCARequestAndBlock(PCAType pca_type,
                                          PCARequestType request_type,
                                          const brillo::SecureBlob& request,
diff --git a/cryptohome/attestation.h b/cryptohome/attestation.h
index b8160e3..3512587 100644
--- a/cryptohome/attestation.h
+++ b/cryptohome/attestation.h
@@ -816,6 +816,11 @@
   // a fallback if the device firmware does not already do this.
   void ExtendPCR1IfClear();
 
+  // Quote the given PCR and fill the result in the Quote object
+  bool CreatePCRQuote(uint32_t pcr_index,
+                      const brillo::SecureBlob& identity_key_blob,
+                      Quote* output);
+
   // Creates a PCA URL for the given |pca_type| and |request_type|.
   std::string GetPCAURL(PCAType pca_type, PCARequestType request_type) const;
 
diff --git a/cryptohome/attestation_unittest.cc b/cryptohome/attestation_unittest.cc
index 295265a..bb774de 100644
--- a/cryptohome/attestation_unittest.cc
+++ b/cryptohome/attestation_unittest.cc
@@ -709,16 +709,29 @@
       default_encrypted_endorsement_credential,
       db.credentials().default_encrypted_endorsement_credential()));
 
-  // The default identity could not be copied from the deprecated database.
+  // The default identity is copied from the deprecated database after
+  // re-generating the PCR0 quote.
   // The deprecated fields have not been cleared so that older code can still
   // use the database.
-  ASSERT_TRUE(db.identities().empty());
+  ASSERT_EQ(1, db.identities().size());
   ASSERT_EQ("identity_binding", db.identity_binding().identity_binding());
   ASSERT_EQ("identity_public_key", db.identity_binding().identity_public_key());
   EXPECT_EQ("pcr1_quote", db.pcr1_quote().quote());
 
+  // Check the migrated identity after re-generating the PCR0 quote is
+  // correct.
+  const AttestationDatabase::Identity& default_identity_data =
+      db.identities().Get(Attestation::kDefaultPCA);
+
+  ASSERT_EQ("identity_binding",
+            default_identity_data.identity_binding().identity_binding());
+  ASSERT_EQ("identity_public_key",
+            default_identity_data.identity_binding().identity_public_key());
+  ASSERT_EQ("pcr1_quote", default_identity_data.pcr_quotes().at(1).quote());
+  ASSERT_TRUE(default_identity_data.pcr_quotes().at(0).has_quote());
+
   // There is no identity certificate since there is no identity.
-  ASSERT_TRUE(db.identity_certificates().empty());
+  ASSERT_EQ(db.identity_certificates().size(), 1);
 }
 
 TEST_F(AttestationBaseTest,