trunks: Extract RSA endorsement key from NVRAM

The certificate for endorsement key for TPM2.0 is stored in NVRAM.
The GetPublicRSAEndorsementKey method implemented here allows to
extract the public RSA endorsement key from the certificate. It is
expected to  be used to generate the unique identifier (EID) for
zero-touch enrollment.

BUG=chromium:778535
TEST=None

Change-Id: I81400a4e628c13cbca863b8d9a1cbe33f503858e
Reviewed-on: https://chromium-review.googlesource.com/763305
Commit-Ready: Igor <igorcov@chromium.org>
Tested-by: Igor <igorcov@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
diff --git a/trunks/mock_tpm_utility.h b/trunks/mock_tpm_utility.h
index adab3a1..2126163 100644
--- a/trunks/mock_tpm_utility.h
+++ b/trunks/mock_tpm_utility.h
@@ -156,6 +156,7 @@
   MOCK_METHOD3(CreateIdentityKey,
                TPM_RC(TPM_ALG_ID, AuthorizationDelegate*, std::string*));
   MOCK_METHOD0(DeclareTpmFirmwareStable, TPM_RC());
+  MOCK_METHOD1(GetPublicRSAEndorsementKey, TPM_RC(std::string*));
 };
 
 }  // namespace trunks
diff --git a/trunks/tpm_utility.h b/trunks/tpm_utility.h
index faf8a72..62e5b32 100644
--- a/trunks/tpm_utility.h
+++ b/trunks/tpm_utility.h
@@ -360,6 +360,10 @@
   // For TPMs with fixed firmware: NOP, always returns TPM_RC_SUCCESS.
   virtual TPM_RC DeclareTpmFirmwareStable() = 0;
 
+  // Reads the RSA certificate from nvram space and extracts the public key
+  // modulus into |public_key|. Returns TPM_RC_SUCCESS on success.
+  virtual TPM_RC GetPublicRSAEndorsementKey(std::string* public_key) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TpmUtility);
 };
diff --git a/trunks/tpm_utility_impl.cc b/trunks/tpm_utility_impl.cc
index 456f0e7..df2ab97 100644
--- a/trunks/tpm_utility_impl.cc
+++ b/trunks/tpm_utility_impl.cc
@@ -23,10 +23,13 @@
 #include <base/stl_util.h>
 #include <base/strings/string_number_conversions.h>
 #include <crypto/openssl_util.h>
+#include <crypto/scoped_openssl_types.h>
 #include <crypto/secure_hash.h>
 #include <crypto/sha2.h>
 #include <openssl/aes.h>
+#include <openssl/evp.h>
 #include <openssl/rand.h>
+#include <openssl/x509.h>
 
 #include "trunks/authorization_delegate.h"
 #include "trunks/blob_parser.h"
@@ -59,6 +62,9 @@
   "\x83\x71\x97\x67\x44\x84\xB3\xF8\x1A\x90\xCC\x8D\x46\xA5\xD7\x24"
   "\xFD\x52\xD7\x6E\x06\x52\x0B\x64\xF2\xA1\xDA\x1B\x33\x14\x69\xAA");
 
+// The index in NVRAM space where RSA EK certificate is stored.
+const uint32_t kRSAEndorsementCertificateIndex = 0xC00000;
+
 // Returns a serialized representation of the unmodified handle. This is useful
 // for predefined handle values, like TPM_RH_OWNER. For details on what types of
 // handles use this name formula see Table 3 in the TPM 2.0 Library Spec Part 1
@@ -1720,6 +1726,64 @@
   return rc;
 }
 
+TPM_RC TpmUtilityImpl::GetPublicRSAEndorsementKey(std::string* public_key) {
+  uint32_t index = kRSAEndorsementCertificateIndex;
+  trunks::TPMS_NV_PUBLIC nvram_public;
+  TPM_RC result = GetNVSpacePublicArea(index, &nvram_public);
+  if (result != TPM_RC_SUCCESS) {
+    LOG(ERROR) << "Error reading NV space for index " << index
+               << " with error: " << GetErrorString(result);
+    return result;
+  }
+
+  std::unique_ptr<AuthorizationDelegate> password_delegate(
+    factory_.GetPasswordAuthorization(""));
+  std::string nvram_data;
+  result = ReadNVSpace(index, 0, nvram_public.data_size, false,
+                              &nvram_data, password_delegate.get());
+  if (result != TPM_RC_SUCCESS) {
+    LOG(ERROR) << "Error reading NV space for index " << index
+               << " with error: " << GetErrorString(result);
+    return result;
+  }
+
+  // Get the X509 object.
+  const unsigned char* cert_data =
+      reinterpret_cast<const unsigned char*>(nvram_data.c_str());
+  crypto::ScopedOpenSSL<X509, X509_free> xcert(
+      d2i_X509(nullptr, &cert_data, nvram_data.size()));
+  if (!xcert) {
+    LOG(ERROR) << "Failed to get EK certificate from NVRAM";
+    return SAPI_RC_CORRUPTED_DATA;
+  }
+
+  // Get the public key.
+  crypto::ScopedEVP_PKEY pubkey(X509_get_pubkey(xcert.get()));
+  if (!pubkey || pubkey->type != EVP_PKEY_RSA) {
+    LOG(ERROR) << "Failed to get EK public key from NVRAM";
+    return SAPI_RC_CORRUPTED_DATA;
+  }
+
+  RSA* rsa = pubkey->pkey.rsa;
+  if (!rsa) {
+    LOG(ERROR) << "Failed to get RSA from NVRAM";
+    return SAPI_RC_CORRUPTED_DATA;
+  }
+
+  BIGNUM* bn = rsa->n;
+  size_t buf_len = (size_t) BN_num_bytes(bn);
+  if (buf_len == 0) {
+    LOG(ERROR) << "Invalid buffer size";
+    return SAPI_RC_CORRUPTED_DATA;
+  }
+
+  std::vector<unsigned char> key(buf_len);
+  BN_bn2bin(bn, key.data());
+  public_key->assign(reinterpret_cast<const char*>(key.data()), buf_len);
+
+  return TPM_RC_SUCCESS;
+}
+
 TPM_RC TpmUtilityImpl::SetKnownOwnerPassword(
     const std::string& known_owner_password) {
   std::unique_ptr<TpmState> tpm_state(factory_.GetTpmState());
diff --git a/trunks/tpm_utility_impl.h b/trunks/tpm_utility_impl.h
index 2fd226a..e36887c 100644
--- a/trunks/tpm_utility_impl.h
+++ b/trunks/tpm_utility_impl.h
@@ -168,6 +168,7 @@
                            AuthorizationDelegate* delegate,
                            std::string* key_blob) override;
   TPM_RC DeclareTpmFirmwareStable() override;
+  TPM_RC GetPublicRSAEndorsementKey(std::string* public_key) override;
 
  private:
   friend class TpmUtilityTest;
diff --git a/trunks/tpm_utility_test.cc b/trunks/tpm_utility_test.cc
index 43b054a..a16bb57 100644
--- a/trunks/tpm_utility_test.cc
+++ b/trunks/tpm_utility_test.cc
@@ -15,6 +15,7 @@
 //
 
 #include <base/stl_util.h>
+#include <base/strings/string_number_conversions.h>
 #include <crypto/sha2.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -2232,4 +2233,126 @@
   EXPECT_EQ(TPM_RC_FAILURE, utility_.DeclareTpmFirmwareStable());
 }
 
+TEST_F(TpmUtilityTest, GetPublicRSAEndorsementKey_NoDataInNvram) {
+  std::string public_key;
+  EXPECT_EQ(SAPI_RC_CORRUPTED_DATA,
+            utility_.GetPublicRSAEndorsementKey(&public_key));
+}
+
+TEST_F(TpmUtilityTest, GetPublicRSAEndorsementKey_EmptyNvram) {
+  uint32_t nv_index = 29360128;
+  TPM2B_MAX_NV_BUFFER nvram_data_buffer;
+  std::vector<unsigned char> cert = {};
+  nvram_data_buffer.size = cert.size();
+  memcpy(nvram_data_buffer.buffer, cert.data(), cert.size());
+
+  TPM2B_NV_PUBLIC public_area;
+  TPMS_NV_PUBLIC public_data;
+  public_area.nv_public = public_data;
+  public_data.data_size = 0;
+
+  EXPECT_CALL(mock_tpm_, NV_ReadPublicSync(nv_index, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(public_area), Return(TPM_RC_SUCCESS)));
+
+  EXPECT_CALL(mock_tpm_, NV_ReadSync(_, _, _, _, _, _, _, _))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<6>(nvram_data_buffer), Return(TPM_RC_SUCCESS)));
+
+  std::string public_key;
+  EXPECT_EQ(SAPI_RC_CORRUPTED_DATA,
+            utility_.GetPublicRSAEndorsementKey(&public_key));
+}
+
+TEST_F(TpmUtilityTest, GetPublicRSAEndorsementKey_InvalidDataInNvram) {
+  uint32_t nv_index = 29360128;
+  TPM2B_MAX_NV_BUFFER nvram_data_buffer;
+  std::vector<unsigned char> cert = {1, 2, 3, 4};
+  nvram_data_buffer.size = cert.size();
+  memcpy(nvram_data_buffer.buffer, cert.data(), cert.size());
+
+  TPM2B_NV_PUBLIC public_area;
+  TPMS_NV_PUBLIC public_data;
+  public_area.nv_public = public_data;
+  public_data.data_size = 4;
+
+  EXPECT_CALL(mock_tpm_, NV_ReadPublicSync(nv_index, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(public_area), Return(TPM_RC_SUCCESS)));
+
+  EXPECT_CALL(mock_tpm_, NV_ReadSync(_, _, _, _, _, _, _, _))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<6>(nvram_data_buffer), Return(TPM_RC_SUCCESS)));
+
+  std::string public_key;
+  EXPECT_EQ(SAPI_RC_CORRUPTED_DATA,
+            utility_.GetPublicRSAEndorsementKey(&public_key));
+}
+
+TEST_F(TpmUtilityTest, GetPublicRSAEndorsementKey_ValidCertificateInNvram) {
+  std::string hex_encoded_cert =
+      "308203EB308202D3A00302010202105A12528603AC1ABE3FE8EB925C951823300D06092A"
+      "864886F70D01010B0500308180310B30090603550406130255533113301106035504080C"
+      "0A43616C69666F726E696131143012060355040A0C0B476F6F676C6520496E632E312430"
+      "22060355040B0C1B456E67696E656572696E6720616E6420446576656C6F706D656E7431"
+      "20301E06035504030C1743524F532054504D2050524420454B20524F4F54204341301E17"
+      "0D3137303232313030303030325A170D3237303232313030303030325A30003082012230"
+      "0D06092A864886F70D01010105000382010F003082010A0282010100AC5869BD60F30463"
+      "612BB0C472AA19E5400E524A213290EBFB728D1AAC956F74B7CF6A8D57F17C94D4BE2B3D"
+      "07FD882CF708C30C476DCB1FF32695A8BAC77BDD5C04E89E2AB228D6EDFF2EFAA54BE9C3"
+      "0F9D211E2E42DE7E50CF424EEE6C310D677D8870522E8C953711BE42C9B94579D56D4815"
+      "60926606C60D74EFEEB013869C0424BB7D8585F79159BE7F476625B9BD2701D1C5ABA6D4"
+      "07A4724C2165C176C45CD2188576ADC20303C3368D11603CFEEE4CFD81EB9C9EACF0029C"
+      "4F41B2E4033AB68453884D5BB3E0DD9F680E150CB604428546CFA32B05743B073BAE9796"
+      "4A847756BB79D132EAEFF44EE1B25315C6B45CE74087A777CFD142769B5CF4E502030100"
+      "01A381DF3081DC300E0603551D0F0101FF04040302002030510603551D110101FF044730"
+      "45A443304131163014060567810502010C0B69643A3437344634463437310F300D060567"
+      "810502020C044831423231163014060567810502030C0B69643A3030313330303337300C"
+      "0603551D130101FF0402300030130603551D20040C300A3008060667810C010202301F06"
+      "03551D23041830168014153934FC5919CD2982F1F47FAD85D64469A1A17B30100603551D"
+      "25040930070605678105080130210603551D09041A3018301606056781050210310D300B"
+      "0C03322E30020100020110300D06092A864886F70D01010B05000382010100AE963A2EC0"
+      "72B8DC7C673389B62112CFDEAD6A7C2A1D5142E74D628B9FCA1599C9705A23C2FCB3A529"
+      "6B5CE3C2CB78A82B99D03D3B2E892C779EC46A2476CE70B68BE3FC87F1FC0B15A551F392"
+      "33AAB7A0E0B425C709790C05298F101AC0CF95FE5C2502D4E5D78233041EBB66CFC0AA59"
+      "983E20C915D7A35AE025FBE8ABBC898FD475288512C8BA2B70F4185E00A28A53D241188C"
+      "C9216D6AA8FA0F15DE4BD8EF11A78F55B89C1C330A6C39EC6647954C816FB74BEFA02CAB"
+      "C2B036B3E88DF7AE13F99449A2CADD70F322F64EFC437BA0A74BAE8354EAE44A5B0D5D66"
+      "A3A6F14630157CD7BABDC6B0FD45EC71D208DD7BF1EA014540E46865E34947B87A2668";
+  std::vector<uint8_t> cert;
+  base::HexStringToBytes(hex_encoded_cert, &cert);
+
+  ASSERT_TRUE(cert.size() <= MAX_NV_BUFFER_SIZE);
+
+  uint32_t nv_index = 29360128;
+  TPM2B_MAX_NV_BUFFER nvram_data_buffer;
+
+  nvram_data_buffer.size = cert.size();
+  memcpy(nvram_data_buffer.buffer, cert.data(), cert.size());
+
+  TPM2B_NV_PUBLIC public_area;
+  TPMS_NV_PUBLIC public_data;
+  public_data.data_size = cert.size();
+  public_area.nv_public = public_data;
+
+  EXPECT_CALL(mock_tpm_, NV_ReadPublicSync(nv_index, _, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(public_area), Return(TPM_RC_SUCCESS)));
+
+  EXPECT_CALL(mock_tpm_, NV_ReadSync(_, _, _, _, _, _, _, _))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<6>(nvram_data_buffer), Return(TPM_RC_SUCCESS)));
+
+  std::string public_key;
+  EXPECT_EQ(TPM_RC_SUCCESS, utility_.GetPublicRSAEndorsementKey(&public_key));
+  std::string hex_encoded_pk =
+      "AC5869BD60F30463612BB0C472AA19E5400E524A213290EBFB728D1AAC956F74B7CF6A8D"
+      "57F17C94D4BE2B3D07FD882CF708C30C476DCB1FF32695A8BAC77BDD5C04E89E2AB228D6"
+      "EDFF2EFAA54BE9C30F9D211E2E42DE7E50CF424EEE6C310D677D8870522E8C953711BE42"
+      "C9B94579D56D481560926606C60D74EFEEB013869C0424BB7D8585F79159BE7F476625B9"
+      "BD2701D1C5ABA6D407A4724C2165C176C45CD2188576ADC20303C3368D11603CFEEE4CFD"
+      "81EB9C9EACF0029C4F41B2E4033AB68453884D5BB3E0DD9F680E150CB604428546CFA32B"
+      "05743B073BAE97964A847756BB79D132EAEFF44EE1B25315C6B45CE74087A777CFD14276"
+      "9B5CF4E5";
+  EXPECT_EQ(hex_encoded_pk,
+            base::HexEncode(public_key.data(), public_key.size()));
+}
+
 }  // namespace trunks
diff --git a/trunks/trunks_client.cc b/trunks/trunks_client.cc
index 888e9ee..842c127 100644
--- a/trunks/trunks_client.cc
+++ b/trunks/trunks_client.cc
@@ -62,6 +62,7 @@
   puts("  --read_pcr --index=<N> - Reads a PCR and prints the value.");
   puts("  --extend_pcr --index=<N> --value=<value> - Extends a PCR.");
   puts("  --tpm_version - Prints TPM versions and IDs similar to tpm_version.");
+  puts("  --endorsement_public_key - Prints the public endorsement key.");
 }
 
 std::string HexEncode(const std::string& bytes) {
@@ -207,6 +208,14 @@
   return 0;
 }
 
+int EndorsementPublicKey(const TrunksFactory& factory) {
+  std::string public_key;
+  factory.GetTpmUtility()->GetPublicRSAEndorsementKey(&public_key);
+  std::string public_key_hex = HexEncode(public_key);
+  printf("  Public Endorsement Key: %s\n", public_key_hex.c_str());
+  return 0;
+}
+
 }  // namespace
 
 int main(int argc, char** argv) {
@@ -341,6 +350,10 @@
   if (cl->HasSwitch("tpm_version")) {
     return TpmVersion(factory);
   }
+  if (cl->HasSwitch("endorsement_public_key")) {
+    return EndorsementPublicKey(factory);
+  }
+
   puts("Invalid options!");
   PrintUsage();
   return -1;
diff --git a/trunks/trunks_factory_for_test.cc b/trunks/trunks_factory_for_test.cc
index 915039f..a9dc4af 100644
--- a/trunks/trunks_factory_for_test.cc
+++ b/trunks/trunks_factory_for_test.cc
@@ -402,6 +402,10 @@
     return target_->DeclareTpmFirmwareStable();
   }
 
+  TPM_RC GetPublicRSAEndorsementKey(std::string* public_key) override {
+    return target_->GetPublicRSAEndorsementKey(public_key);
+  }
+
  private:
   TpmUtility* target_;
 };