u2fd: Set vendor mode before every U2F command

This is to work around the fact that cr50 loses
U2F state during deep sleep, and u2fd doesn't know
when that has happened.

BUG=b:140361043, chromium:998008
TEST=build u2fd and flash to eve
     register on webauthn.io
     lock screen, wait for suspend, cr50 to enter deep sleep
     authenticate on webauthn.io

Change-Id: I5db871b23d80f35c1a8a84ae586807a037716e84
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/1781140
Tested-by: Louis Collard <louiscollard@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
Commit-Queue: Louis Collard <louiscollard@chromium.org>
diff --git a/u2fd/tpm_vendor_cmd.cc b/u2fd/tpm_vendor_cmd.cc
index 2818a93..a61fdbf 100644
--- a/u2fd/tpm_vendor_cmd.cc
+++ b/u2fd/tpm_vendor_cmd.cc
@@ -43,7 +43,7 @@
 
 namespace u2f {
 
-TpmVendorCommandProxy::TpmVendorCommandProxy() {}
+TpmVendorCommandProxy::TpmVendorCommandProxy() : last_u2f_vendor_mode_(0) {}
 TpmVendorCommandProxy::~TpmVendorCommandProxy() {}
 
 uint32_t TpmVendorCommandProxy::VendorCommand(uint16_t cc,
@@ -140,10 +140,11 @@
   int rc = SendU2fApdu(vendor_mode, &rmode);
 
   if (!rc) {
+    last_u2f_vendor_mode_ = rmode[0];
     // remove the 16-bit status code at the end
-    VLOG(1) << "current mode " << static_cast<int>(rmode[0]);
+    VLOG(1) << "current mode " << static_cast<int>(last_u2f_vendor_mode_);
     // record the individual attestation certificate if the extension is on.
-    if (rmode[0] == kU2fExtended && VLOG_IS_ON(1))
+    if (last_u2f_vendor_mode_ == kU2fExtended && VLOG_IS_ON(1))
       LogIndividualCertificate();
   }
 
@@ -217,11 +218,19 @@
 
 uint32_t TpmVendorCommandProxy::SendU2fGenerate(const U2F_GENERATE_REQ& req,
                                                 U2F_GENERATE_RESP* resp_out) {
+  if (!ReloadCr50State()) {
+    return kVendorRcInvalidResponse;
+  }
+
   return VendorCommandStruct(kVendorCcU2fGenerate, req, resp_out);
 }
 
 uint32_t TpmVendorCommandProxy::SendU2fSign(const U2F_SIGN_REQ& req,
                                             U2F_SIGN_RESP* resp_out) {
+  if (!ReloadCr50State()) {
+    return kVendorRcInvalidResponse;
+  }
+
   std::string output_str;
   uint32_t resp_code =
       VendorCommand(kVendorCcU2fSign, RequestToString(req), &output_str);
@@ -250,6 +259,10 @@
 
 uint32_t TpmVendorCommandProxy::SendU2fAttest(const U2F_ATTEST_REQ& req,
                                               U2F_ATTEST_RESP* resp_out) {
+  if (!ReloadCr50State()) {
+    return kVendorRcInvalidResponse;
+  }
+
   return VendorCommandStruct(kVendorCcU2fAttest, req, resp_out);
 }
 
@@ -320,4 +333,14 @@
   }
 }
 
+bool TpmVendorCommandProxy::ReloadCr50State() {
+  if (last_u2f_vendor_mode_) {
+    return SetU2fVendorMode(last_u2f_vendor_mode_) == 0;
+  } else {
+    // Vendor mode is part of cr50 state, and must be set before we can attempt
+    // to re-load state.
+    return false;
+  }
+}
+
 }  // namespace u2f
diff --git a/u2fd/tpm_vendor_cmd.h b/u2fd/tpm_vendor_cmd.h
index 3640ca9..e286be9 100644
--- a/u2fd/tpm_vendor_cmd.h
+++ b/u2fd/tpm_vendor_cmd.h
@@ -107,6 +107,13 @@
   // Retrieve and record in the log the individual attestation certificate.
   void LogIndividualCertificate();
 
+  // Cr50 loses U2F state during deep sleep; this function sends a command
+  // that will re-load it. Returns true iff state was successfully reloaded.
+  bool ReloadCr50State();
+
+  // Mode set on the most recent call to SetU2fVendorMode();
+  uint8_t last_u2f_vendor_mode_;
+
   DISALLOW_COPY_AND_ASSIGN(TpmVendorCommandProxy);
 };