Refactor logic around unsupported command

Check if supported before querying for auth record and keyrotation
Do not log when unsupported

Tested: gpaste/4541942956032000

Google-Bug-Id: 435699868
Google-Bug-Id: 435214999

Change-Id: Iab5703ef4c8b1b9fb85aa8d7683411d14eecb75b
Signed-off-by: Christian Kungler <ckungler@google.com>
diff --git a/ec_util.cpp b/ec_util.cpp
index ccd9596..416dace 100644
--- a/ec_util.cpp
+++ b/ec_util.cpp
@@ -14,6 +14,7 @@
 
 #include "ec_util.hpp"
 
+#include <cstdint>
 #include <span>
 #include <stdplus/print.hpp>
 #include <stdplus/raw.hpp>
@@ -47,46 +48,43 @@
 
 }  // namespace
 
-class EcException {
- public:
-  explicit EcException(uint16_t result) : result(result) {}
-  uint16_t get_result() const { return result; }
-
- private:
-  uint16_t result;
-};
-
-[[nodiscard]] std::span<const uint8_t> EcUtilImpl::getResponseBody(
-    std::vector<uint8_t>& response) {
-  std::span<const uint8_t> output = response;
-  auto& rsp = stdplus::raw::extractRef<RspHeader>(output);
+[[nodiscard]] std::vector<uint8_t> EcUtilImpl::sendCommand(
+    uint16_t command, uint8_t* request_ptr, size_t request_size) const {
+  std::vector<uint8_t> response_bytes =
+      hostCmd->sendCommand(command, kVersionZero, request_ptr, request_size);
+  std::span<uint8_t> response = response_bytes;
+  auto& rsp = stdplus::raw::extractRef<RspHeader>(response);
   if (rsp.result != EC_RES_SUCCESS) {
-    throw EcException(rsp.result);
+    throw CommandRunException(command, rsp.result);
   }
+  return {response.begin(), response.end()};
+}
 
-  return output;
+bool EcUtilImpl::isHostCommandSupported(uint16_t cmd) const {
+  ec_request_is_host_command_supported req = {.command = cmd};
+
+  std::vector<uint8_t> response_body = sendCommand(
+      EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_IS_HOST_COMMAND_SUPPORTED,
+      reinterpret_cast<uint8_t*>(&req), sizeof(req));
+
+  ec_response_is_host_command_supported response_struct =
+      stdplus::raw::copyFrom<ec_response_is_host_command_supported>(
+          response_body);
+
+  return response_struct.is_allowed != 0;
 }
 
 ec_response_statistics EcUtilImpl::getHothStatistics() const {
-  std::vector<uint8_t> response = hostCmd->sendCommand(
-      EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_STATISTICS, kVersionZero,
-      /*request=*/nullptr, /*requestSize=*/0);
-  std::span<const uint8_t> response_body;
-  try {
-    response_body = getResponseBody(response);
-  } catch (const EcException& ec_exception) {
-    stdplus::print(stderr, "{} received a bad response from Hoth {:#x}\n",
-                   __func__, static_cast<uint8_t>(ec_exception.get_result()));
-    throw ResponseFailure();
-  }
+  std::vector<uint8_t> response_body =
+      sendCommand(EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_STATISTICS,
+                  /*request_ptr=*/nullptr, /*request_size=*/0);
   return stdplus::raw::copyFrom<ec_response_statistics>(response_body);
 }
 
 ec_response_chip_info EcUtilImpl::getHothChipInfo() const {
-  std::vector<uint8_t> response = hostCmd->sendCommand(
-      EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_CHIP_INFO, kVersionZero,
-      /*request=*/nullptr, /*requestSize=*/0);
-  std::span<const uint8_t> response_body = getResponseBody(response);
+  std::vector<uint8_t> response_body =
+      sendCommand(EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_CHIP_INFO,
+                  /*request_ptr=*/nullptr, /*request_size=*/0);
   return stdplus::raw::copyFrom<ec_response_chip_info>(response_body);
 }
 
@@ -111,21 +109,11 @@
         .index = i,
     };
 
-    std::vector<uint8_t> response = hostCmd->sendCommand(
+    std::vector<uint8_t> response_body = sendCommand(
         EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
-        kVersionZero, &req, sizeof(req));
-    std::span<const uint8_t> response_body;
-    try {
-      response_body = getResponseBody(response);
-    } catch (const EcException& ec_exception) {
-      stdplus::print(stderr,
-                     "{} received a bad response "
-                     "from Hoth {:#x}\n",
-                     __func__, static_cast<uint8_t>(ec_exception.get_result()));
-      throw ResponseFailure();
-    }
+        reinterpret_cast<uint8_t*>(&req), sizeof(req));
     if (response_body.size() != chunk_size) {
-      stdplus::print(stderr, "Bad response length %d (expected %d)\n",
+      stdplus::print(stderr, "Bad response length {} (expected {})\n",
                      response_body.size(), chunk_size);
       throw ResponseFailure();
     }
@@ -150,22 +138,12 @@
       .index = 0,
   };
 
-  std::vector<uint8_t> response = hostCmd->sendCommand(
+  std::vector<uint8_t> response_body = sendCommand(
       EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
-      kVersionZero, &req, sizeof(req));
-  std::span<const uint8_t> response_body;
-  try {
-    response_body = getResponseBody(response);
-  } catch (const EcException& ec_exception) {
-    stdplus::print(stderr,
-                   "{} received a bad response "
-                   "from Hoth {:#x}\n",
-                   __func__, static_cast<uint8_t>(ec_exception.get_result()));
-    throw ResponseFailure();
-  }
+      reinterpret_cast<uint8_t*>(&req), sizeof(req));
   const size_t chunk_size = HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE;
   if (response_body.size() != chunk_size) {
-    stdplus::print(stderr, "Bad response length %d (expected %d)\n",
+    stdplus::print(stderr, "Bad response length {} (expected {})\n",
                    response_body.size(), chunk_size);
     throw ResponseFailure();
   }
@@ -174,30 +152,30 @@
 }
 
 ec_authz_record_get_response EcUtilImpl::getHothAuthRecord() const {
+  const uint16_t kGetAuthRecordCmd =
+      EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_AUTHZ_RECORD;
+
+  if (!isHostCommandSupported(kGetAuthRecordCmd)) {
+    throw CommandNotSupportedException(kGetAuthRecordCmd);
+  }
+
   ec_authz_record_get_request request = {
       .index = 0,
       .reserved = {},
   };
 
-  std::vector<uint8_t> response = hostCmd->sendCommand(
-      EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_AUTHZ_RECORD,
-      kVersionZero, &request, sizeof(request));
-  std::span<const uint8_t> response_body;
-  try {
-    response_body = getResponseBody(response);
-  } catch (const EcException& ec_exception) {
-    if (ec_exception.get_result() == EC_RES_UNAVAILABLE) {
-      // Unsupported, probably a Haven, do not log.
-    } else {
-      stdplus::print(stderr, "{} received a bad response from Hoth {:#x}\n",
-                     __func__, static_cast<uint8_t>(ec_exception.get_result()));
-    }
-    throw ResponseFailure();
-  }
+  std::vector<uint8_t> response_body = sendCommand(
+      kGetAuthRecordCmd, reinterpret_cast<uint8_t*>(&request), sizeof(request));
   return stdplus::raw::copyFrom<ec_authz_record_get_response>(response_body);
 }
 
 ec_response_key_rotation_status EcUtilImpl::getHothKeyRotationStatus() const {
+  const uint16_t keyRotationCommand =
+      EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_KEY_ROTATION_OP;
+  if (!isHostCommandSupported(keyRotationCommand)) {
+    throw CommandNotSupportedException(keyRotationCommand);
+  }
+
   ec_request_key_rotation_record request = {
       .operation = KEY_ROTATION_RECORD_GET_STATUS,
       .packet_offset = 0,
@@ -205,25 +183,9 @@
       .reserved = 0,
   };
 
-  std::vector<uint8_t> response = hostCmd->sendCommand(
-      EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_KEY_ROTATION_OP,
-      kVersionZero, &request, sizeof(request));
-
-  std::span<const uint8_t> response_body;
-  try {
-    response_body = getResponseBody(response);
-  } catch (const EcException& ec_exception) {
-    if (ec_exception.get_result() == EC_RES_INVALID_COMMAND) {
-      // TODO: b/434689086 - Do not log error to prevent spam whilst KeyRotation
-      // support is being deployed.
-    } else {
-      stdplus::print(stderr,
-                     "{} received a bad response from "
-                     "Hoth {:#x}\n",
-                     __func__, static_cast<uint8_t>(ec_exception.get_result()));
-    }
-    throw ResponseFailure();
-  }
+  std::vector<uint8_t> response_body =
+      sendCommand(keyRotationCommand, reinterpret_cast<uint8_t*>(&request),
+                  sizeof(request));
   return stdplus::raw::copyFrom<ec_response_key_rotation_status>(response_body);
 }
 
diff --git a/ec_util.hpp b/ec_util.hpp
index 1a07fed..5b8f25d 100644
--- a/ec_util.hpp
+++ b/ec_util.hpp
@@ -14,6 +14,9 @@
 
 #pragma once
 
+#include <cstddef>
+#include <cstdint>
+
 #include "ec_util_interface.hpp"
 #include "google3/host_commands.h"
 #include "host_command.hpp"
@@ -22,10 +25,49 @@
 namespace hoth {
 namespace internal {
 
+class CommandNotSupportedException : public std::exception {
+ public:
+  explicit CommandNotSupportedException(uint16_t command) : command(command) {
+    std::stringstream ss;
+    ss << "EC Command 0x" << std::hex << command << " not supported";
+    this->message = ss.str();
+  }
+  const char* what() const noexcept override { return message.c_str(); }
+
+  uint16_t get_command() const { return command; }
+
+ private:
+  uint16_t command;
+  std::string message;
+};
+
+class CommandRunException : public std::exception {
+ public:
+  explicit CommandRunException(uint16_t command, uint16_t result)
+      : command(command), result(result) {
+    std::stringstream ss;
+    ss << "EC Command 0x" << std::hex << command << " returned error 0x"
+       << std::hex << result;
+    this->message = ss.str();
+  }
+  const char* what() const noexcept override { return message.c_str(); }
+
+  uint16_t get_command() const { return command; }
+  uint16_t get_result() const { return result; }
+
+ private:
+  uint16_t command;
+  uint16_t result;
+  std::string message;
+};
+
 class EcUtilImpl : public EcUtil {
  public:
   explicit EcUtilImpl(HostCommand* hostCmd) : hostCmd(hostCmd) {}
 
+  // Check wether a command is supported and authorized.
+  bool isHostCommandSupported(uint16_t cmd) const override;
+
   // Get the Hoth statistics by issuing an EC_PRV_CMD_HOTH_GET_STATISTICS
   // Command.
   ec_response_statistics getHothStatistics() const override;
@@ -50,8 +92,9 @@
   ec_response_key_rotation_status getHothKeyRotationStatus() const override;
 
  private:
-  [[nodiscard]] static std::span<const uint8_t> getResponseBody(
-      std::vector<uint8_t>& response);
+  [[nodiscard]] std::vector<uint8_t> sendCommand(uint16_t command,
+                                                 uint8_t* request_ptr,
+                                                 size_t request_size) const;
 
   /** @brief Connection to Hoth for sending and receiving host commands */
   HostCommand* hostCmd;
diff --git a/ec_util_interface.hpp b/ec_util_interface.hpp
index a26af17..cb12818 100644
--- a/ec_util_interface.hpp
+++ b/ec_util_interface.hpp
@@ -32,6 +32,9 @@
 
     virtual ~EcUtil() = default;
 
+    // Check wether a command is supported (and authorized).
+    virtual bool isHostCommandSupported(uint16_t cmd) const = 0;
+
     // Get the Hoth statistics by issuing an EC_PRV_CMD_HOTH_GET_STATISTICS
     // Command.
     virtual ec_response_statistics getHothStatistics() const = 0;
diff --git a/google3/host_commands.h b/google3/host_commands.h
index 5b5af87..4c0cbc2 100644
--- a/google3/host_commands.h
+++ b/google3/host_commands.h
@@ -506,6 +506,18 @@
   little_uint32_t padding[3];
 };
 
+/* Reports on whether a command is allowed to run given the current host command
+ * filtering that the firmware is using. In order for this command to return
+ * true, the command must exist and must have is_command_allowed() return true.
+ */
+#define EC_PRV_CMD_HOTH_IS_HOST_COMMAND_SUPPORTED 0x0011
+struct ec_request_is_host_command_supported {
+  little_uint16_t command;
+};
+struct ec_response_is_host_command_supported {
+  uint8_t is_allowed;
+};
+
 /* Reset the target device. */
 #define EC_PRV_CMD_HOTH_RESET_TARGET 0x0012
 
diff --git a/hoth_state.cpp b/hoth_state.cpp
index 2e81b4e..ff6b3ef 100644
--- a/hoth_state.cpp
+++ b/hoth_state.cpp
@@ -24,7 +24,7 @@
 #include <stdplus/raw.hpp>
 #include <xyz/openbmc_project/Control/Hoth/State/error.hpp>
 
-#include "ec_util_interface.hpp"
+#include "ec_util.hpp"
 #include "google3/host_commands.h"
 
 namespace google {
@@ -150,9 +150,13 @@
       HothStateObject::authRecordCapabilities(
           auth_record_resp.record.capabilities);
     }
+  } catch (const internal::CommandNotSupportedException &e) {
+    // Do not log if unsupported
+    HothStateObject::hasValidAuthRecord(false);
   } catch (const std::exception &e) {
     stdplus::print(stderr, "Fetching Hoth auth record had an exception: {}\n",
                    e.what());
+    HothStateObject::hasValidAuthRecord(false);
   }
 
   try {
@@ -169,9 +173,15 @@
         key_rotation_status.validation_key_data);
     HothStateObject::keyRotationValidationHashData(
         key_rotation_status.validation_hash_data);
+  } catch (const internal::CommandNotSupportedException &e) {
+    // Do not log if unsupported
+    HothStateObject::keyRotationValidationMethod(
+        KEY_ROTATION_VALIDATION_METHOD_GET_ERROR);
   } catch (const std::exception &e) {
     HothStateObject::keyRotationValidationMethod(
         KEY_ROTATION_VALIDATION_METHOD_GET_ERROR);
+    stdplus::print(stderr, "Fetching Hoth key rotation had an exception: {}\n",
+                   e.what());
   }
 }
 
diff --git a/test/ec_util_mock.hpp b/test/ec_util_mock.hpp
index 9b0daa6..a756057 100644
--- a/test/ec_util_mock.hpp
+++ b/test/ec_util_mock.hpp
@@ -14,31 +14,30 @@
 
 #pragma once
 
-#include "google3/host_commands.h"
-
-#include "ec_util.hpp"
-
 #include <gmock/gmock.h>
 
-namespace google
-{
-namespace hoth
-{
-namespace internal
-{
+#include <cstdint>
 
-class EcUtilMock : public EcUtil
-{
-  public:
-    MOCK_CONST_METHOD0(getHothStatistics, ec_response_statistics());
-    MOCK_CONST_METHOD0(getHothChipInfo, ec_response_chip_info());
-    MOCK_CONST_METHOD0(checkHothPersistentPanicInfo, bool());
-    MOCK_CONST_METHOD0(getHothAuthRecord, ec_authz_record_get_response());
-    MOCK_CONST_METHOD0(getHothKeyRotationStatus, ec_response_key_rotation_status());
+#include "ec_util.hpp"
+#include "google3/host_commands.h"
+
+namespace google {
+namespace hoth {
+namespace internal {
+
+class EcUtilMock : public EcUtil {
+ public:
+  MOCK_CONST_METHOD0(getHothStatistics, ec_response_statistics());
+  MOCK_CONST_METHOD0(getHothChipInfo, ec_response_chip_info());
+  MOCK_CONST_METHOD0(checkHothPersistentPanicInfo, bool());
+  MOCK_CONST_METHOD0(getHothAuthRecord, ec_authz_record_get_response());
+  MOCK_CONST_METHOD0(getHothKeyRotationStatus,
+                     ec_response_key_rotation_status());
+  MOCK_CONST_METHOD1(isHostCommandSupported, bool(uint16_t));
 };
 
-} // namespace internal
+}  // namespace internal
 
-} // namespace hoth
+}  // namespace hoth
 
-} // namespace google
+}  // namespace google
diff --git a/test/ec_util_unittest.cpp b/test/ec_util_unittest.cpp
index d9ad71f..575b785 100644
--- a/test/ec_util_unittest.cpp
+++ b/test/ec_util_unittest.cpp
@@ -12,17 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "google3/host_commands.h"
-
 #include "ec_util.hpp"
-#include "host_command_mock.hpp"
-#include "payload_update.hpp"
-
-#include <xyz/openbmc_project/Control/Hoth/error.hpp>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <xyz/openbmc_project/Control/Hoth/error.hpp>
+
+#include "google3/host_commands.h"
+#include "host_command_mock.hpp"
+#include "payload_update.hpp"
+
 // NOLINTNEXTLINE(google-build-using-namespace)
 using namespace std::literals;
 using sdbusplus::error::xyz::openbmc_project::control::hoth::ResponseFailure;
@@ -30,76 +30,63 @@
 using ::testing::_;
 using ::testing::Return;
 
-namespace google
-{
-namespace hoth
-{
-namespace internal
-{
-namespace
-{
+namespace google {
+namespace hoth {
+namespace internal {
+namespace {
 
 auto const goodResponseStr = "\x03\xfd\x00\x00\x00\x00\x00\x00"s;
 
-class EcUtilTest : public ::testing::Test
-{
-  protected:
-    EcUtilTest() : ecUtil(&hostCmd)
-    {}
+class EcUtilTest : public ::testing::Test {
+ protected:
+  EcUtilTest() : ecUtil(&hostCmd) {}
 
-    // Host Command interface handle
-    internal::HostCommandMock hostCmd;
+  // Host Command interface handle
+  internal::HostCommandMock hostCmd;
 
-    // EcUtil interface handle
-    internal::EcUtilImpl ecUtil;
+  // EcUtil interface handle
+  internal::EcUtilImpl ecUtil;
 };
 
-MATCHER_P(dummyMatches, req, "")
-{
-  const auto *const arg_req = static_cast<const uint8_t *>(arg);
+MATCHER_P(dummyMatches, req, "") {
+  const auto* const arg_req = static_cast<const uint8_t*>(arg);
   return *arg_req == req;
 }
 
-class EcUtilStatisticTest : public EcUtilTest
-{};
+class EcUtilStatisticTest : public EcUtilTest {};
 
-TEST_F(EcUtilStatisticTest, sendCommandReturnsBadResultFails)
-{
-    std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
-    // Change the RspHeader.result to something other than EC_RES_SUCCESS
-    rsp[2] = internal::EC_RES_ERROR;
+TEST_F(EcUtilStatisticTest, sendCommandReturnsBadResultFails) {
+  std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
+  // Change the RspHeader.result to something other than EC_RES_SUCCESS
+  rsp[2] = internal::EC_RES_ERROR;
 
-    EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
-                                         EC_PRV_CMD_HOTH_GET_STATISTICS,
-                                     ecUtil.kVersionZero, nullptr, 0))
-        .WillOnce(Return(rsp));
+  EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                                       EC_PRV_CMD_HOTH_GET_STATISTICS,
+                                   ecUtil.kVersionZero, nullptr, 0))
+      .WillOnce(Return(rsp));
 
-    EXPECT_THROW(ecUtil.getHothStatistics(), ResponseFailure);
+  EXPECT_THROW(ecUtil.getHothStatistics(), CommandRunException);
 }
 
-TEST_F(EcUtilStatisticTest, sendCommandReturnsGoodResponseSuccess)
-{
-    std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
-    for (uint16_t i = 0; i < 256; i++)
-    {
-      rsp.push_back(0);
-    }
+TEST_F(EcUtilStatisticTest, sendCommandReturnsGoodResponseSuccess) {
+  std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
+  for (uint16_t i = 0; i < 256; i++) {
+    rsp.push_back(0);
+  }
 
-    EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
-                                         EC_PRV_CMD_HOTH_GET_STATISTICS,
-                                     ecUtil.kVersionZero, nullptr, 0))
-        .WillOnce(Return(rsp));
+  EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                                       EC_PRV_CMD_HOTH_GET_STATISTICS,
+                                   ecUtil.kVersionZero, nullptr, 0))
+      .WillOnce(Return(rsp));
 
-    EXPECT_NO_THROW(ecUtil.getHothStatistics());
+  EXPECT_NO_THROW(ecUtil.getHothStatistics());
 }
 
-class EcUtilPersistentPanicTest : public EcUtilTest
-{};
+class EcUtilPersistentPanicTest : public EcUtilTest {};
 
-struct panic_host_command_response
-{
-    RspHeader hdr;
-    uint8_t body[HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE];
+struct panic_host_command_response {
+  RspHeader hdr;
+  uint8_t body[HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE];
 };
 
 // Template of a persistent panic response, with the correct data_len, checksum
@@ -126,91 +113,112 @@
         },
 };
 
-TEST_F(EcUtilPersistentPanicTest, incorrectResponseSizeThrows)
-{
-    std::vector<uint8_t> rsp_buf(sizeof(kPanicResponseTemplate), 0);
-    auto* rsp = reinterpret_cast<panic_host_command_response*>(rsp_buf.data());
+TEST_F(EcUtilPersistentPanicTest, incorrectResponseSizeThrows) {
+  std::vector<uint8_t> rsp_buf(sizeof(kPanicResponseTemplate), 0);
+  auto* rsp = reinterpret_cast<panic_host_command_response*>(rsp_buf.data());
+  *rsp = kPanicResponseTemplate;
+  rsp->hdr.data_len -= 1;
+  rsp->hdr.checksum += 1;
+  rsp_buf.resize(sizeof(kPanicResponseTemplate) - 1);
+  EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                                       EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
+                                   ecUtil.kVersionZero, _,
+                                   sizeof(ec_request_persistent_panic_info)))
+      .Times(2)
+      .WillRepeatedly(Return(rsp_buf));
+  EXPECT_THROW(ecUtil.checkHothPersistentPanicInfo(), ResponseFailure);
+  EXPECT_THROW(ecUtil.getHothPersistentPanicInfo(), ResponseFailure);
+}
+
+TEST_F(EcUtilPersistentPanicTest, incorrectPanicMagicReturnsNullopt) {
+  std::vector<uint8_t> rsp_buf(sizeof(kPanicResponseTemplate), 0);
+  auto* rsp = reinterpret_cast<panic_host_command_response*>(rsp_buf.data());
+  *rsp = kPanicResponseTemplate;
+  rsp->body[143] -= 1;
+  rsp->hdr.checksum += 1;
+  EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                                       EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
+                                   ecUtil.kVersionZero, _,
+                                   sizeof(ec_request_persistent_panic_info)))
+      .Times(2)
+      .WillRepeatedly(Return(rsp_buf));
+  EXPECT_FALSE(ecUtil.checkHothPersistentPanicInfo());
+  EXPECT_FALSE(ecUtil.getHothPersistentPanicInfo());
+}
+
+TEST_F(EcUtilPersistentPanicTest, correctHostCommandReturnsFullPanicRecord) {
+  std::vector<uint8_t> rsp_bufs[12];
+  for (int i = 0; i < 12; ++i) {
+    rsp_bufs[i].resize(sizeof(panic_host_command_response));
+    auto* rsp =
+        reinterpret_cast<panic_host_command_response*>(rsp_bufs[i].data());
     *rsp = kPanicResponseTemplate;
-    rsp->hdr.data_len -= 1;
-    rsp->hdr.checksum += 1;
-    rsp_buf.resize(sizeof(kPanicResponseTemplate) - 1);
-    EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
-                                         EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
-                                     ecUtil.kVersionZero, _,
-                                     sizeof(ec_request_persistent_panic_info)))
-        .Times(2)
-        .WillRepeatedly(Return(rsp_buf));
-    EXPECT_THROW(ecUtil.checkHothPersistentPanicInfo(), ResponseFailure);
-    EXPECT_THROW(ecUtil.getHothPersistentPanicInfo(), ResponseFailure);
+    // Tweak the response a little bit to make each chunk slightly
+    // different.
+    rsp->body[0] += i;
+    rsp->body[HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE - 1] -= i;
+  }
+  EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                                       EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
+                                   ecUtil.kVersionZero, _,
+                                   sizeof(ec_request_persistent_panic_info)))
+      .Times(13)
+      .WillOnce(Return(rsp_bufs[0]))
+      .WillOnce(Return(rsp_bufs[0]))
+      .WillOnce(Return(rsp_bufs[1]))
+      .WillOnce(Return(rsp_bufs[2]))
+      .WillOnce(Return(rsp_bufs[3]))
+      .WillOnce(Return(rsp_bufs[4]))
+      .WillOnce(Return(rsp_bufs[5]))
+      .WillOnce(Return(rsp_bufs[6]))
+      .WillOnce(Return(rsp_bufs[7]))
+      .WillOnce(Return(rsp_bufs[8]))
+      .WillOnce(Return(rsp_bufs[9]))
+      .WillOnce(Return(rsp_bufs[10]))
+      .WillOnce(Return(rsp_bufs[11]));
+
+  EXPECT_TRUE(ecUtil.checkHothPersistentPanicInfo());
+
+  auto panic = ecUtil.getHothPersistentPanicInfo();
+  std::span<uint8_t> panic_buf(reinterpret_cast<uint8_t*>(&panic.value()),
+                               sizeof(panic.value()));
+  for (uint8_t i = 0; i < 12; ++i) {
+    size_t chunk_start =
+        static_cast<uint32_t>(i) * HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE;
+    EXPECT_EQ(panic_buf[chunk_start], i);
+    EXPECT_EQ(
+        panic_buf[chunk_start + HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE - 1],
+        static_cast<uint8_t>(-i));
+  }
 }
 
-TEST_F(EcUtilPersistentPanicTest, incorrectPanicMagicReturnsNullopt)
-{
-    std::vector<uint8_t> rsp_buf(sizeof(kPanicResponseTemplate), 0);
-    auto* rsp = reinterpret_cast<panic_host_command_response*>(rsp_buf.data());
-    *rsp = kPanicResponseTemplate;
-    rsp->body[143] -= 1;
-    rsp->hdr.checksum += 1;
-    EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
-                                         EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
-                                     ecUtil.kVersionZero, _,
-                                     sizeof(ec_request_persistent_panic_info)))
-        .Times(2)
-        .WillRepeatedly(Return(rsp_buf));
-    EXPECT_FALSE(ecUtil.checkHothPersistentPanicInfo());
-    EXPECT_FALSE(ecUtil.getHothPersistentPanicInfo());
+class EcUtilAuthRecordTest : public EcUtilTest {};
+
+TEST_F(EcUtilAuthRecordTest, authRecordNotSupported) {
+  std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
+  rsp.push_back(0);
+  EXPECT_CALL(hostCmd,
+              sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                              EC_PRV_CMD_HOTH_IS_HOST_COMMAND_SUPPORTED,
+                          _, _, _))
+      .WillOnce(Return(rsp));
+  EXPECT_THROW(ecUtil.getHothAuthRecord(), CommandNotSupportedException);
 }
 
-TEST_F(EcUtilPersistentPanicTest, correctHostCommandReturnsFullPanicRecord)
-{
-    std::vector<uint8_t> rsp_bufs[12];
-    for (int i = 0; i < 12; ++i)
-    {
-        rsp_bufs[i].resize(sizeof(panic_host_command_response));
-        auto* rsp =
-            reinterpret_cast<panic_host_command_response*>(rsp_bufs[i].data());
-        *rsp = kPanicResponseTemplate;
-        // Tweak the response a little bit to make each chunk slightly
-        // different.
-        rsp->body[0] += i;
-        rsp->body[HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE - 1] -= i;
-    }
-    EXPECT_CALL(hostCmd, sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
-                                         EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
-                                     ecUtil.kVersionZero, _,
-                                     sizeof(ec_request_persistent_panic_info)))
-        .Times(13)
-        .WillOnce(Return(rsp_bufs[0]))
-        .WillOnce(Return(rsp_bufs[0]))
-        .WillOnce(Return(rsp_bufs[1]))
-        .WillOnce(Return(rsp_bufs[2]))
-        .WillOnce(Return(rsp_bufs[3]))
-        .WillOnce(Return(rsp_bufs[4]))
-        .WillOnce(Return(rsp_bufs[5]))
-        .WillOnce(Return(rsp_bufs[6]))
-        .WillOnce(Return(rsp_bufs[7]))
-        .WillOnce(Return(rsp_bufs[8]))
-        .WillOnce(Return(rsp_bufs[9]))
-        .WillOnce(Return(rsp_bufs[10]))
-        .WillOnce(Return(rsp_bufs[11]));
+class EcUtilKeyRotationTest : public EcUtilTest {};
 
-    EXPECT_TRUE(ecUtil.checkHothPersistentPanicInfo());
-
-    auto panic = ecUtil.getHothPersistentPanicInfo();
-    std::span<uint8_t> panic_buf(reinterpret_cast<uint8_t*>(&panic.value()),
-                                 sizeof(panic.value()));
-    for (uint8_t i = 0; i < 12; ++i)
-    {
-        size_t chunk_start =
-            static_cast<uint32_t>(i) * HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE;
-        EXPECT_EQ(panic_buf[chunk_start], i);
-        EXPECT_EQ(
-            panic_buf[chunk_start + HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE - 1],
-            static_cast<uint8_t>(-i));
-    }
+TEST_F(EcUtilKeyRotationTest, authRecordNotSupported) {
+  std::vector<uint8_t> rsp(goodResponseStr.begin(), goodResponseStr.end());
+  rsp.push_back(0);
+  EXPECT_CALL(hostCmd,
+              sendCommand(EC_CMD_BOARD_SPECIFIC_BASE +
+                              EC_PRV_CMD_HOTH_IS_HOST_COMMAND_SUPPORTED,
+                          _, _, _))
+      .WillOnce(Return(rsp));
+  EXPECT_THROW(ecUtil.getHothKeyRotationStatus(), CommandNotSupportedException);
 }
 
-} // namespace
-} // namespace internal
-} // namespace hoth
-} // namespace google
+}  // namespace
+}  // namespace internal
+}  // namespace hoth
+}  // namespace google