attestationd: support attestation-based enrollment

This change ports the functionallity added in c/370302 to attestationd.
When the attestation daemon launches, it will read the contents of
ABE_DATA_FILE and use it to generate the enterprise_enrollment_nonce
when creating an AttestationEnrollmentRequest.

The mechanism for reading the ABE_DATA is the same as the one used in
cryptohomed:
https://chromium.googlesource.com/chromiumos/platform2/+/a5d9e02d2d7bdf68378cdacb2fc7ba2eaaaedf97/cryptohome/init/cryptohomed.conf

The ABE data is passed to attestationd, and every time
CreateEnrollRequestInternal is called, it will calculate the DEN based
on the ABE data: HMAC::SHA256("attestation_based_enrollment", ABE_DATA)

The DEN is set in the EnterpriseEnrollmentNonce field from the
AttestationEnrollmentRequest message that is going to be sent to the
PCA.

PCA then will calculate the Enrollment ID with: HMAC::SHA256(DEN,
TPMPublicKey) and add that value to the AIKCert that we receive.

BUG=chromium:641153
TEST=unit tests. Manually verified PCA Enrollment works with and without
ABE_DATA.
Change-Id: I78df5e1661f8a59df08e1baecd2879ba73a13cee
Reviewed-on: https://chromium-review.googlesource.com/562532
Commit-Ready: Marco Vanotti <mvanotti@google.com>
Tested-by: Marco Vanotti <mvanotti@google.com>
Reviewed-by: Yves Arrouye <drcrash@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
diff --git a/attestation/server/attestation_service.cc b/attestation/server/attestation_service.cc
index ff42d7e..b7b5fe4 100644
--- a/attestation/server/attestation_service.cc
+++ b/attestation/server/attestation_service.cc
@@ -20,8 +20,8 @@
 
 #include <base/callback.h>
 #include <base/sha1.h>
-#include <base/strings/stringprintf.h>
 #include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
 #include <brillo/bind_lambda.h>
 #include <brillo/cryptohome.h>
 #include <brillo/data_encoding.h>
@@ -107,6 +107,11 @@
 };
 const char kVerifiedBootMode[3] = {0, 0, 1};
 
+// Context name to derive stable secret for attestation-based enterprise
+// enrollment.
+const char kAttestationBasedEnterpriseEnrollmentContextName[] =
+    "attestation_based_enrollment";
+
 struct CertificateAuthority {
   const char* issuer;
   const char* modulus;  // In hex format.
@@ -284,7 +289,8 @@
 
 const size_t kChallengeSignatureNonceSize = 20; // For all TPMs.
 
-AttestationService::AttestationService() : weak_factory_(this) {}
+AttestationService::AttestationService(brillo::SecureBlob* abe_data)
+    : abe_data_(abe_data), weak_factory_(this) {}
 
 bool AttestationService::Initialize() {
   if (!worker_thread_) {
@@ -780,6 +786,14 @@
       database_pb.identity_binding().identity_public_key());
   *request_pb.mutable_pcr0_quote() = database_pb.pcr0_quote();
   *request_pb.mutable_pcr1_quote() = database_pb.pcr1_quote();
+
+  std::string enterprise_enrollment_nonce = ComputeEnterpriseEnrollmentNonce();
+
+  if (!enterprise_enrollment_nonce.empty()) {
+    request_pb.set_enterprise_enrollment_nonce(
+        enterprise_enrollment_nonce.data(), enterprise_enrollment_nonce.size());
+  }
+
   if (!request_pb.SerializeToString(enroll_request)) {
     LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
     return false;
@@ -2150,6 +2164,18 @@
   callback.Run(result);
 }
 
+std::string AttestationService::ComputeEnterpriseEnrollmentNonce() {
+  if (!abe_data_ || abe_data_->empty()) {
+    // If there was no device secret we cannot compute the DEN.
+    // We do not want to fail attestation for those devices.
+    return "";
+  }
+
+  std::string data(abe_data_->char_data(), abe_data_->size());
+  std::string key(kAttestationBasedEnterpriseEnrollmentContextName);
+  return crypto_utility_->HmacSha256(key, data);
+}
+
 base::WeakPtr<AttestationService> AttestationService::GetWeakPtr() {
   return weak_factory_.GetWeakPtr();
 }
diff --git a/attestation/server/attestation_service.h b/attestation/server/attestation_service.h
index e803f49..50822b4 100644
--- a/attestation/server/attestation_service.h
+++ b/attestation/server/attestation_service.h
@@ -28,6 +28,7 @@
 #include <base/threading/thread.h>
 #include <brillo/bind_lambda.h>
 #include <brillo/http/http_transport.h>
+#include <brillo/secure_blob.h>
 
 #include "attestation/common/attestation_ca.pb.h"
 #include "attestation/common/crypto_utility.h"
@@ -51,7 +52,7 @@
 // Usage:
 //   std::unique_ptr<AttestationInterface> attestation =
 //       new AttestationService();
-//   CHECK(attestation->Initialize());
+//   CHECK(attestation->Initialize(nullptr));
 //   attestation->CreateGoogleAttestedKey(...);
 //
 // THREADING NOTES:
@@ -67,7 +68,9 @@
 // back to the main thread.
 class AttestationService : public AttestationInterface {
  public:
-  AttestationService();
+  // If abe_data is not an empty blob, its contents will be
+  // used to enable attestation-based enterprise enrollment.
+  AttestationService(brillo::SecureBlob* abe_data);
   ~AttestationService() override = default;
 
   // AttestationInterface methods.
@@ -149,6 +152,8 @@
 
   void set_hwid(const std::string& hwid) { hwid_ = hwid; }
 
+  void set_abe_data(brillo::SecureBlob* abe_data) { abe_data_ = abe_data; }
+
  private:
   friend class AttestationServiceTest;
 
@@ -493,6 +498,9 @@
   bool VerifyActivateIdentity(const std::string& ek_public_key_info,
                               const std::string& aik_public_key_tpm_format);
 
+  // Compute the enterprise DEN for attestation-based enrollment.
+  std::string ComputeEnterpriseEnrollmentNonce();
+
   base::WeakPtr<AttestationService> GetWeakPtr();
 
   // Other than initialization and destruction, these are used only by the
@@ -508,6 +516,7 @@
   std::string hwid_;
   CertRequestMap pending_cert_requests_;
   std::string system_salt_;
+  brillo::SecureBlob* abe_data_;
 
   // Default implementations for the above interfaces. These will be setup
   // during Initialize() if the corresponding interface has not been set with a
diff --git a/attestation/server/attestation_service_test.cc b/attestation/server/attestation_service_test.cc
index 9fe5d48..360b5f3 100644
--- a/attestation/server/attestation_service_test.cc
+++ b/attestation/server/attestation_service_test.cc
@@ -140,7 +140,7 @@
   ~AttestationServiceTest() override = default;
 
   void SetUp() override {
-    service_.reset(new AttestationService);
+    service_.reset(new AttestationService(nullptr));
     service_->set_database(&mock_database_);
     service_->set_crypto_utility(&mock_crypto_utility_);
     fake_http_transport_ = std::make_shared<brillo::http::fake::Transport>();
@@ -1565,7 +1565,7 @@
   EXPECT_FALSE(mock_database_.GetProtobuf().has_pcr1_quote());
 }
 
-TEST_F(AttestationServiceTest, CreateEnrollRequestSuccess) {
+TEST_F(AttestationServiceTest, CreateEnrollRequestSuccessWithoutAbeData) {
   AttestationDatabase* database = mock_database_.GetMutableProtobuf();
   database->mutable_credentials()
       ->mutable_default_encrypted_endorsement_credential()
@@ -1584,6 +1584,7 @@
     EXPECT_EQ("public_key", pca_request.identity_public_key());
     EXPECT_EQ("pcr0", pca_request.pcr0_quote().quote());
     EXPECT_EQ("pcr1", pca_request.pcr1_quote().quote());
+    EXPECT_FALSE(pca_request.has_enterprise_enrollment_nonce());
     Quit();
   };
   CreateEnrollRequestRequest request;
@@ -1591,6 +1592,68 @@
   Run();
 }
 
+TEST_F(AttestationServiceTest, CreateEnrollRequestSuccessWithEmptyAbeData) {
+  AttestationDatabase* database = mock_database_.GetMutableProtobuf();
+  database->mutable_credentials()
+      ->mutable_default_encrypted_endorsement_credential()
+      ->set_wrapped_key("wrapped_key");
+  database->mutable_identity_binding()->set_identity_public_key("public_key");
+  database->mutable_pcr0_quote()->set_quote("pcr0");
+  database->mutable_pcr1_quote()->set_quote("pcr1");
+  auto callback = [this](const CreateEnrollRequestReply& reply) {
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+    EXPECT_TRUE(reply.has_pca_request());
+    AttestationEnrollmentRequest pca_request;
+    EXPECT_TRUE(pca_request.ParseFromString(reply.pca_request()));
+    EXPECT_EQ(kTpmVersionUnderTest, pca_request.tpm_version());
+    EXPECT_EQ("wrapped_key",
+              pca_request.encrypted_endorsement_credential().wrapped_key());
+    EXPECT_EQ("public_key", pca_request.identity_public_key());
+    EXPECT_EQ("pcr0", pca_request.pcr0_quote().quote());
+    EXPECT_EQ("pcr1", pca_request.pcr1_quote().quote());
+    EXPECT_FALSE(pca_request.has_enterprise_enrollment_nonce());
+    Quit();
+  };
+  brillo::SecureBlob abe_data;
+  service_->set_abe_data(&abe_data);
+  CreateEnrollRequestRequest request;
+  service_->CreateEnrollRequest(request, base::Bind(callback));
+  Run();
+}
+
+TEST_F(AttestationServiceTest, CreateEnrollRequestSuccessWithAbeData) {
+  AttestationDatabase* database = mock_database_.GetMutableProtobuf();
+  database->mutable_credentials()
+      ->mutable_default_encrypted_endorsement_credential()
+      ->set_wrapped_key("wrapped_key");
+  database->mutable_identity_binding()->set_identity_public_key("public_key");
+  database->mutable_pcr0_quote()->set_quote("pcr0");
+  database->mutable_pcr1_quote()->set_quote("pcr1");
+  auto callback = [this](const CreateEnrollRequestReply& reply) {
+    EXPECT_EQ(STATUS_SUCCESS, reply.status());
+    EXPECT_TRUE(reply.has_pca_request());
+    AttestationEnrollmentRequest pca_request;
+    EXPECT_TRUE(pca_request.ParseFromString(reply.pca_request()));
+    EXPECT_EQ(kTpmVersionUnderTest, pca_request.tpm_version());
+    EXPECT_EQ("wrapped_key",
+              pca_request.encrypted_endorsement_credential().wrapped_key());
+    EXPECT_EQ("public_key", pca_request.identity_public_key());
+    EXPECT_EQ("pcr0", pca_request.pcr0_quote().quote());
+    EXPECT_EQ("pcr1", pca_request.pcr1_quote().quote());
+    EXPECT_TRUE(pca_request.has_enterprise_enrollment_nonce());
+
+    // Mocked CryptoUtility->HmacSha256 returns always a zeroed buffer.
+    EXPECT_EQ(std::string(32, '\0'), pca_request.enterprise_enrollment_nonce());
+    Quit();
+  };
+
+  CreateEnrollRequestRequest request;
+  brillo::SecureBlob abe_data(0xCA, 32);
+  service_->set_abe_data(&abe_data);
+  service_->CreateEnrollRequest(request, base::Bind(callback));
+  Run();
+}
+
 TEST_F(AttestationServiceTest, CreateEnrollRequestNotReady) {
   // Empty database - not prepared for enrollment
   mock_database_.GetMutableProtobuf()->Clear();
diff --git a/attestation/server/attestationd.conf b/attestation/server/attestationd.conf
index 5f99032..7321017 100644
--- a/attestation/server/attestationd.conf
+++ b/attestation/server/attestationd.conf
@@ -21,11 +21,19 @@
 stop on stopping boot-services
 respawn
 
+
+# Where we store the attestation-based enterprise enrollment data. The
+# daemon will check for this environment variable and read the file at
+# startup before forking.
+env ABE_DATA_FILE=/run/attestationd.abe_data
+
 pre-start script
   # Ensure attestationd will have permissions for attestation.epb.
   chgrp preserve /mnt/stateful_partition/unencrypted/preserve
   chmod 775 /mnt/stateful_partition/unencrypted/preserve
+  (vpd -g stable_device_secret_DO_NOT_SHARE || printf '') >$ABE_DATA_FILE
 end script
 
 expect fork
 exec /usr/sbin/attestationd
+post-start exec rm -f $ABE_DATA_FILE
diff --git a/attestation/server/main.cc b/attestation/server/main.cc
index 7c43416..3de836a 100644
--- a/attestation/server/main.cc
+++ b/attestation/server/main.cc
@@ -17,13 +17,18 @@
 #include <sysexits.h>
 #include <unistd.h>
 
+#include <cstdlib>
 #include <memory>
 #include <string>
 
 #include <base/command_line.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_number_conversions.h>
 #include <brillo/daemons/dbus_daemon.h>
 #include <brillo/dbus/async_event_sequencer.h>
 #include <brillo/minijail/minijail.h>
+#include <brillo/secure_blob.h>
 #include <brillo/syslog_logging.h>
 #include <brillo/userdb_utils.h>
 
@@ -41,6 +46,39 @@
 const char kAttestationSeccompPath[] =
     "/usr/share/policy/attestationd-seccomp.policy";
 
+namespace env {
+static const char kAttestationBasedEnrollmentDataFile[] = "ABE_DATA_FILE";
+}  // namespace env
+
+// Returns the contents of the attestation-based enrollment data file.
+static std::string ReadAbeDataFileContents() {
+  std::string data;
+
+  const char* abe_data_file =
+      std::getenv(env::kAttestationBasedEnrollmentDataFile);
+  if (!abe_data_file) {
+    return data;
+  }
+
+  base::FilePath file_path(abe_data_file);
+  if (!base::ReadFileToString(file_path, &data)) {
+    LOG(FATAL) << "Could not read attestation-based enterprise enrollment data"
+                  " in: "
+               << file_path.value();
+  }
+
+  return data;
+}
+
+static bool GetAttestationEnrollmentData(const std::string& abe_data_hex,
+                                         brillo::SecureBlob* abe_data) {
+  abe_data->clear();
+  if (abe_data_hex.empty()) return true;  // no data is ok.
+  // The data must be a valid 32-byte hex string.
+  return base::HexStringToBytes(abe_data_hex, abe_data) &&
+         abe_data->size() == 32;
+}
+
 void InitMinijailSandbox() {
   uid_t attestation_uid;
   gid_t attestation_gid;
@@ -69,8 +107,10 @@
 
 class AttestationDaemon : public brillo::DBusServiceDaemon {
  public:
-  AttestationDaemon()
-      : brillo::DBusServiceDaemon(attestation::kAttestationServiceName) {}
+  AttestationDaemon(brillo::SecureBlob abe_data)
+      : brillo::DBusServiceDaemon(attestation::kAttestationServiceName),
+        abe_data_(std::move(abe_data)),
+        attestation_service_(&abe_data_) {}
 
  protected:
   int OnInit() override {
@@ -90,6 +130,7 @@
   }
 
  private:
+  brillo::SecureBlob abe_data_;
   attestation::AttestationService attestation_service_;
   std::unique_ptr<attestation::DBusService> dbus_service_;
 
@@ -104,8 +145,14 @@
     flags |= brillo::kLogToStderr;
   }
   brillo::InitLog(flags);
+  // read whole abe_data_file before we init minijail.
+  std::string abe_data_hex = ReadAbeDataFileContents();
+  brillo::SecureBlob abe_data;
+  if (!GetAttestationEnrollmentData(abe_data_hex, &abe_data)) {
+    LOG(ERROR) << "Invalid attestation-based enterprise enrollment data.";
+  }
   PLOG_IF(FATAL, daemon(0, 0) == -1) << "Failed to daemonize";
-  AttestationDaemon daemon;
+  AttestationDaemon daemon(abe_data);
   LOG(INFO) << "Attestation Daemon Started.";
   InitMinijailSandbox();
   return daemon.Run();