attestation: Don't read EK with owner delegate when not feasible.

Using owner incapable owner delegate to read EK causes DA increments.

For testing, the following cases are tested and making sure the DA
doesn't increase:

1. w/o TPM ownership, it can read EK.
2. w/ owner password and delegate that can't read EK, it can read EK.
3. w/o owner password but delegate that can read EK. it can read EK.
4. w/o owner password but delegate that can't read EK, it can't read EK.

BUG=b:174713900
BUG=b:176039750
TEST="attestation_client get_enrollment_id --ignore_cache" reads EK.

Change-Id: I861c9fa278c96c92ee12b1ac9fc6c68f02a7e3c2
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2595759
Tested-by: Leo Lai <cylai@google.com>
Commit-Queue: Leo Lai <cylai@google.com>
Reviewed-by: Yi Chou <yich@google.com>
diff --git a/attestation/common/tpm_utility_v1.cc b/attestation/common/tpm_utility_v1.cc
index 6ca829b..d90c8aa 100644
--- a/attestation/common/tpm_utility_v1.cc
+++ b/attestation/common/tpm_utility_v1.cc
@@ -88,6 +88,46 @@
          quoted_pcr_value;
 }
 
+// Checks if `delegate_blob`'s flag `TPM_DELEGATE_OwnerReadInternalPub` is set.
+// In case of empty input or failed parsing, returns `false`; otherwise, set
+// `can_read` and returns `true`.
+bool CanDelegateReadInternalPub(const std::string& delegate_blob,
+                                bool* can_read) {
+  if (delegate_blob.empty()) {
+    LOG(ERROR) << __func__ << ": Empty blob.";
+    return false;
+  }
+  UINT64 offset = 0;
+  // Make sure the parsing will be successful first.
+  TSS_RESULT result = Trspi_UnloadBlob_TPM_DELEGATE_OWNER_BLOB(
+      &offset,
+      const_cast<BYTE*>(reinterpret_cast<const BYTE*>(delegate_blob.data())),
+      nullptr);
+  if (offset != delegate_blob.size()) {
+    TPM_LOG(ERROR, result) << __func__ << ": Bad delegate blob.";
+    return false;
+  }
+  offset = 0;
+
+  TPM_DELEGATE_OWNER_BLOB ownerBlob = {};
+
+  // TODO(b/169392230): Fix the potential memory leak while migrating to tpm
+  // manager.
+  result = Trspi_UnloadBlob_TPM_DELEGATE_OWNER_BLOB(
+      &offset,
+      const_cast<BYTE*>(reinterpret_cast<const BYTE*>(delegate_blob.data())),
+      &ownerBlob);
+
+  if (result != TSS_SUCCESS) {
+    TPM_LOG(ERROR, result) << __func__ << ": Failed to unload delegate blob.";
+    return false;
+  }
+
+  *can_read =
+      ownerBlob.pub.permissions.per1 & TPM_DELEGATE_OwnerReadInternalPub;
+  return true;
+}
+
 }  // namespace
 
 namespace attestation {
@@ -396,19 +436,38 @@
   ScopedTssContext context_handle;
   TSS_HTPM tpm_handle;
   bool is_ready = IsTpmReady();
-  if (is_ready) {
-    if (!ConnectContextAsOwner(owner_password_, &context_handle, &tpm_handle) &&
-        !ConnectContextAsDelegate(delegate_blob_, delegate_secret_,
-                                  &context_handle, &tpm_handle)) {
-      LOG(ERROR) << __func__
-                 << ": Could not connect to the TPM as onwer or delegate.";
-      return false;
-    }
-  } else {
+  bool can_read_ek = false;
+  if (!CanDelegateReadInternalPub(delegate_blob_, &can_read_ek)) {
+    LOG(ERROR) << __func__ << ": Cannot check permission.";
+  }
+  LOG_IF(WARNING, !can_read_ek)
+      << __func__ << ": owner delegate cannot read ek.";
+
+  // Rationality check of auth values, if necessary.
+  if (is_ready && !can_read_ek && owner_password_.empty()) {
+    LOG(ERROR) << __func__ << ": No valid auth.";
+    return false;
+  }
+
+  if (!is_ready) {
     if (!ConnectContextAsUser(&context_handle, &tpm_handle)) {
       LOG(ERROR) << __func__ << ": Could not connect to the TPM as user.";
       return false;
     }
+  } else if (can_read_ek) {
+    if (!ConnectContextAsDelegate(delegate_blob_, delegate_secret_,
+                                  &context_handle, &tpm_handle)) {
+      LOG(ERROR) << __func__ << ": Could not connect to the TPM as delegate.";
+      return false;
+    }
+  } else if (!owner_password_.empty()) {
+    if (!ConnectContextAsOwner(owner_password_, &context_handle, &tpm_handle)) {
+      LOG(ERROR) << __func__ << ": Could not connect to the TPM as owner.";
+      return false;
+    }
+  } else {
+    NOTREACHED();
+    return false;
   }
 
   // Get a handle to the EK public key.