Refactor to use new version of OEM IPMI Handler

Using the new version of ipmi handler provide a higher level wrapper
over the same functionalities. It helps us parse the input and output to
have more control of the input/output we see.

Changes to note,
- All cmd are removed from the request data. That is automatically
  extracted now.

Tested:
Unit Test Passed.

All IPMI OEM command still works the same as before this change.

```
$ burn_my_bmc -command stage -image /tmp/test.txt -interface ipmipci
Set up ipmi flash updater with /flash/dummy
Received failure on delete: Received IPMI_CC: 255
Sending over the firmware image.
Find [0x1050 0x750]
bar0[0x94000000]
Upload to BMC 100% |Goooooooooooooooooooooooooooooooooooooooooooooooooooooooogle| Time: 00:00:00
Opening the verification file
Committing to /flash/verify to trigger service
Calling stat on /flash/verify session to check status
success
succeeded
```

Also tested gBMC Update workflow which worked fine.

Change-Id: Ib2bfeab0c2ec5aa72ede1ff457ef5f90e488053c
Signed-off-by: Willy Tu <wltu@google.com>
diff --git a/ipmi.cpp b/ipmi.cpp
index 07f6cd8..9fd1491 100644
--- a/ipmi.cpp
+++ b/ipmi.cpp
@@ -17,6 +17,7 @@
 #include "ipmi.hpp"
 
 #include <cstring>
+#include <span>
 #include <string>
 #include <unordered_map>
 
@@ -63,137 +64,111 @@
     return true;
 }
 
-std::string stringFromBuffer(const char* start, size_t length)
+std::string stringFromBuffer(std::span<const uint8_t> data)
 {
-    if (!start)
+    if (data.empty() || data.back() != '\0')
     {
-        return "";
+        return std::string();
     }
 
-    auto end = static_cast<const char*>(std::memchr(start, '\0', length));
-    if (end != &start[length - 1])
-    {
-        return "";
-    }
-
-    return (end == nullptr) ? std::string() : std::string(start, end);
+    // Last index is nul-terminator.
+    return std::string(data.begin(), data.end() - 1);
 }
 
-ipmi_ret_t getBlobCount(ManagerInterface* mgr, const uint8_t*,
-                        uint8_t* replyCmdBuf, size_t* dataLen)
+Resp getBlobCount(ManagerInterface* mgr, std::span<const uint8_t>)
 {
     struct BmcBlobCountRx resp;
     resp.crc = 0;
     resp.blobCount = mgr->buildBlobList();
 
     /* Copy the response into the reply buffer */
-    std::memcpy(replyCmdBuf, &resp, sizeof(resp));
-    (*dataLen) = sizeof(resp);
+    std::vector<uint8_t> output(sizeof(BmcBlobCountRx), 0);
+    std::memcpy(output.data(), &resp, sizeof(resp));
 
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(output);
 }
 
-ipmi_ret_t enumerateBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                         uint8_t* replyCmdBuf, size_t* dataLen)
+Resp enumerateBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
     /* Verify datalen is >= sizeof(request) */
     struct BmcBlobEnumerateTx request;
-    auto reply = reinterpret_cast<struct BmcBlobEnumerateRx*>(replyCmdBuf);
-    (*dataLen) = 0;
 
-    std::memcpy(&request, reqBuf, sizeof(request));
+    std::memcpy(&request, data.data(), sizeof(request));
 
     std::string blobId = mgr->getBlobId(request.blobIdx);
     if (blobId.empty())
     {
-        return IPMI_CC_INVALID_FIELD_REQUEST;
+        return ipmi::responseInvalidFieldRequest();
     }
 
-    /* TODO(venture): Need to do a hard-code check against the maximum
-     * reply buffer size. */
-    reply->crc = 0;
-    /* Explicilty copies the NUL-terminator. */
-    std::memcpy(reply + 1, blobId.c_str(), blobId.length() + 1);
-
-    (*dataLen) = sizeof(reply->crc) + blobId.length() + 1;
-
-    return IPMI_CC_OK;
+    std::vector<uint8_t> output(sizeof(BmcBlobEnumerateRx), 0);
+    output.insert(output.end(), blobId.c_str(),
+                  blobId.c_str() + blobId.length() + 1);
+    return ipmi::responseSuccess(output);
 }
 
-ipmi_ret_t openBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                    uint8_t* replyCmdBuf, size_t* dataLen)
+Resp openBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
-    size_t requestLen = (*dataLen);
-    auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(reqBuf);
+    auto request = reinterpret_cast<const struct BmcBlobOpenTx*>(data.data());
     uint16_t session;
-    (*dataLen) = 0;
 
-    std::string path =
-        stringFromBuffer(reinterpret_cast<const char*>(request + 1),
-                         requestLen - sizeof(*request));
+    std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobOpenTx)));
     if (path.empty())
     {
-        return IPMI_CC_REQ_DATA_LEN_INVALID;
+        return ipmi::responseReqDataLenInvalid();
     }
 
     /* Attempt to open. */
     if (!mgr->open(request->flags, path, &session))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
     struct BmcBlobOpenRx reply;
     reply.crc = 0;
     reply.sessionId = session;
 
-    std::memcpy(replyCmdBuf, &reply, sizeof(reply));
-    (*dataLen) = sizeof(reply);
-
-    return IPMI_CC_OK;
+    std::vector<uint8_t> output(sizeof(BmcBlobOpenRx), 0);
+    std::memcpy(output.data(), &reply, sizeof(reply));
+    return ipmi::responseSuccess(output);
 }
 
-ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
-                     size_t* dataLen)
+Resp closeBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
     struct BmcBlobCloseTx request;
-    std::memcpy(&request, reqBuf, sizeof(request));
-    (*dataLen) = 0;
+    if (data.size() < sizeof(request))
+    {
+        return ipmi::responseReqDataLenInvalid();
+    }
+    std::memcpy(&request, data.data(), sizeof(request));
 
     /* Attempt to close. */
     if (!mgr->close(request.sessionId))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(std::vector<uint8_t>{});
 }
 
-ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
-                      size_t* dataLen)
+Resp deleteBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
-    size_t requestLen = (*dataLen);
-    auto request = reinterpret_cast<const struct BmcBlobDeleteTx*>(reqBuf);
-    (*dataLen) = 0;
-
-    std::string path =
-        stringFromBuffer(reinterpret_cast<const char*>(request + 1),
-                         requestLen - sizeof(*request));
+    std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobDeleteTx)));
     if (path.empty())
     {
-        return IPMI_CC_REQ_DATA_LEN_INVALID;
+        return ipmi::responseReqDataLenInvalid();
     }
 
     /* Attempt to delete. */
     if (!mgr->deleteBlob(path))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(std::vector<uint8_t>{});
 }
 
-static ipmi_ret_t returnStatBlob(BlobMeta* meta, uint8_t* replyCmdBuf,
-                                 size_t* dataLen)
+static Resp returnStatBlob(BlobMeta* meta)
 {
     struct BmcBlobStatRx reply;
     reply.crc = 0;
@@ -201,91 +176,85 @@
     reply.size = meta->size;
     reply.metadataLen = meta->metadata.size();
 
-    std::memcpy(replyCmdBuf, &reply, sizeof(reply));
+    std::vector<uint8_t> output(sizeof(BmcBlobStatRx), 0);
+    std::memcpy(output.data(), &reply, sizeof(reply));
 
-    /* If there is metadata, copy it over. */
+    /* If there is metadata, insert it to output. */
     if (!meta->metadata.empty())
     {
-        uint8_t* metadata = &replyCmdBuf[sizeof(reply)];
-        std::memcpy(metadata, meta->metadata.data(), reply.metadataLen);
+        output.insert(output.end(), meta->metadata.begin(),
+                      meta->metadata.end());
     }
-
-    (*dataLen) = sizeof(reply) + reply.metadataLen;
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(output);
 }
 
-ipmi_ret_t statBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                    uint8_t* replyCmdBuf, size_t* dataLen)
+Resp statBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
-    size_t requestLen = (*dataLen);
-    auto request = reinterpret_cast<const struct BmcBlobStatTx*>(reqBuf);
-    (*dataLen) = 0;
-
-    std::string path =
-        stringFromBuffer(reinterpret_cast<const char*>(request + 1),
-                         requestLen - sizeof(*request));
+    std::string path = stringFromBuffer(data.subspan(sizeof(BmcBlobStatTx)));
     if (path.empty())
     {
-        return IPMI_CC_REQ_DATA_LEN_INVALID;
+        return ipmi::responseReqDataLenInvalid();
     }
 
     /* Attempt to stat. */
     BlobMeta meta;
     if (!mgr->stat(path, &meta))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
-    return returnStatBlob(&meta, replyCmdBuf, dataLen);
+    return returnStatBlob(&meta);
 }
 
-ipmi_ret_t sessionStatBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                           uint8_t* replyCmdBuf, size_t* dataLen)
+Resp sessionStatBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
     struct BmcBlobSessionStatTx request;
-    std::memcpy(&request, reqBuf, sizeof(request));
-    (*dataLen) = 0;
+    if (data.size() < sizeof(request))
+    {
+        return ipmi::responseReqDataLenInvalid();
+    }
+    std::memcpy(&request, data.data(), sizeof(request));
 
     /* Attempt to stat. */
     BlobMeta meta;
 
     if (!mgr->stat(request.sessionId, &meta))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
-    return returnStatBlob(&meta, replyCmdBuf, dataLen);
+    return returnStatBlob(&meta);
 }
 
-ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
-                      size_t* dataLen)
+Resp commitBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
-    size_t requestLen = (*dataLen);
-    auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(reqBuf);
-    (*dataLen) = 0;
+    auto request = reinterpret_cast<const struct BmcBlobCommitTx*>(data.data());
 
     /* Sanity check the commitDataLen */
-    if (request->commitDataLen > (requestLen - sizeof(struct BmcBlobCommitTx)))
+    if (request->commitDataLen > (data.size() - sizeof(struct BmcBlobCommitTx)))
     {
-        return IPMI_CC_REQ_DATA_LEN_INVALID;
+        return ipmi::responseReqDataLenInvalid();
     }
 
-    std::vector<uint8_t> data(request->commitDataLen);
-    std::memcpy(data.data(), request + 1, request->commitDataLen);
+    data = data.subspan(sizeof(struct BmcBlobCommitTx), request->commitDataLen);
 
-    if (!mgr->commit(request->sessionId, data))
+    if (!mgr->commit(request->sessionId,
+                     std::vector<uint8_t>(data.begin(), data.end())))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(std::vector<uint8_t>{});
 }
 
-ipmi_ret_t readBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                    uint8_t* replyCmdBuf, size_t* dataLen)
+Resp readBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
     struct BmcBlobReadTx request;
-    std::memcpy(&request, reqBuf, sizeof(request));
+    if (data.size() < sizeof(request))
+    {
+        return ipmi::responseReqDataLenInvalid();
+    }
+    std::memcpy(&request, data.data(), sizeof(request));
 
     /* TODO(venture): Verify requestedSize can fit in a returned IPMI packet.
      */
@@ -297,64 +266,53 @@
      * of data.
      * If there was data returned, copy into the reply buffer.
      */
-    (*dataLen) = sizeof(struct BmcBlobReadRx);
+    std::vector<uint8_t> output(sizeof(BmcBlobReadRx), 0);
 
     if (!result.empty())
     {
-        uint8_t* output = &replyCmdBuf[sizeof(struct BmcBlobReadRx)];
-        std::memcpy(output, result.data(), result.size());
-
-        (*dataLen) = sizeof(struct BmcBlobReadRx) + result.size();
+        output.insert(output.end(), result.begin(), result.end());
     }
 
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(output);
 }
 
-ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
-                     size_t* dataLen)
+Resp writeBlob(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
-    size_t requestLen = (*dataLen);
-    auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(reqBuf);
-    (*dataLen) = 0;
-
-    uint32_t size = requestLen - sizeof(struct BmcBlobWriteTx);
-    std::vector<uint8_t> data(size);
-
-    std::memcpy(data.data(), request + 1, size);
+    auto request = reinterpret_cast<const struct BmcBlobWriteTx*>(data.data());
+    data = data.subspan(sizeof(struct BmcBlobWriteTx));
 
     /* Attempt to write the bytes. */
-    if (!mgr->write(request->sessionId, request->offset, data))
+    if (!mgr->write(request->sessionId, request->offset,
+                    std::vector<uint8_t>(data.begin(), data.end())))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(std::vector<uint8_t>{});
 }
 
-ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf, uint8_t*,
-                     size_t* dataLen)
+Resp writeMeta(ManagerInterface* mgr, std::span<const uint8_t> data)
 {
-    size_t requestLen = (*dataLen);
     struct BmcBlobWriteMetaTx request;
-    (*dataLen) = 0;
+    if (data.size() < sizeof(request))
+    {
+        return ipmi::responseReqDataLenInvalid();
+    }
 
     /* Copy over the request. */
-    std::memcpy(&request, reqBuf, sizeof(request));
-
-    /* Determine number of bytes of metadata to write. */
-    uint32_t size = requestLen - sizeof(request);
+    std::memcpy(&request, data.data(), sizeof(request));
 
     /* Nothing really else to validate, we just copy those bytes. */
-    std::vector<uint8_t> data(size);
-    std::memcpy(data.data(), &reqBuf[sizeof(request)], size);
+    data = data.subspan(sizeof(struct BmcBlobWriteMetaTx));
 
     /* Attempt to write the bytes. */
-    if (!mgr->writeMeta(request.sessionId, request.offset, data))
+    if (!mgr->writeMeta(request.sessionId, request.offset,
+                        std::vector<uint8_t>(data.begin(), data.end())))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
-    return IPMI_CC_OK;
+    return ipmi::responseSuccess(std::vector<uint8_t>{});
 }
 
 } // namespace blobs
diff --git a/ipmi.hpp b/ipmi.hpp
index 318498b..51a9d79 100644
--- a/ipmi.hpp
+++ b/ipmi.hpp
@@ -5,15 +5,19 @@
 #include <ipmid/api.h>
 
 #include <blobs-ipmid/blobs.hpp>
+#include <ipmid/api-types.hpp>
+#include <span>
 #include <string>
+#include <vector>
 
 namespace blobs
 {
 
+using Resp = ipmi::RspType<std::vector<uint8_t>>;
+
 /* Used by bmcBlobGetCount */
 struct BmcBlobCountTx
 {
-    uint8_t cmd; /* bmcBlobGetCount */
 } __attribute__((packed));
 
 struct BmcBlobCountRx
@@ -25,7 +29,6 @@
 /* Used by bmcBlobEnumerate */
 struct BmcBlobEnumerateTx
 {
-    uint8_t cmd; /* bmcBlobEnumerate */
     uint16_t crc;
     uint32_t blobIdx;
 } __attribute__((packed));
@@ -38,7 +41,6 @@
 /* Used by bmcBlobOpen */
 struct BmcBlobOpenTx
 {
-    uint8_t cmd; /* bmcBlobOpen */
     uint16_t crc;
     uint16_t flags;
 } __attribute__((packed));
@@ -52,7 +54,6 @@
 /* Used by bmcBlobClose */
 struct BmcBlobCloseTx
 {
-    uint8_t cmd; /* bmcBlobClose */
     uint16_t crc;
     uint16_t sessionId; /* Returned from BmcBlobOpen. */
 } __attribute__((packed));
@@ -60,14 +61,12 @@
 /* Used by bmcBlobDelete */
 struct BmcBlobDeleteTx
 {
-    uint8_t cmd; /* bmcBlobDelete */
     uint16_t crc;
 } __attribute__((packed));
 
 /* Used by bmcBlobStat */
 struct BmcBlobStatTx
 {
-    uint8_t cmd; /* bmcBlobStat */
     uint16_t crc;
 } __attribute__((packed));
 
@@ -82,7 +81,6 @@
 /* Used by bmcBlobSessionStat */
 struct BmcBlobSessionStatTx
 {
-    uint8_t cmd; /* bmcBlobSessionStat */
     uint16_t crc;
     uint16_t sessionId;
 } __attribute__((packed));
@@ -90,7 +88,6 @@
 /* Used by bmcBlobCommit */
 struct BmcBlobCommitTx
 {
-    uint8_t cmd; /* bmcBlobCommit */
     uint16_t crc;
     uint16_t sessionId;
     uint8_t commitDataLen;
@@ -99,7 +96,6 @@
 /* Used by bmcBlobRead */
 struct BmcBlobReadTx
 {
-    uint8_t cmd; /* bmcBlobRead */
     uint16_t crc;
     uint16_t sessionId;
     uint32_t offset;        /* The byte sequence start, 0-based. */
@@ -114,7 +110,6 @@
 /* Used by bmcBlobWrite */
 struct BmcBlobWriteTx
 {
-    uint8_t cmd; /* bmcBlobWrite */
     uint16_t crc;
     uint16_t sessionId;
     uint32_t offset; /* The byte sequence start, 0-based. */
@@ -123,7 +118,6 @@
 /* Used by bmcBlobWriteMeta */
 struct BmcBlobWriteMetaTx
 {
-    uint8_t cmd; /* bmcBlobWriteMeta */
     uint16_t crc;
     uint16_t sessionId; /* Returned from BmcBlobOpen. */
     uint32_t offset;    /* The byte sequence start, 0-based. */
@@ -142,17 +136,15 @@
  * Given a pointer into an IPMI request buffer and the length of the remaining
  * buffer, builds a string.  This does no string validation w.r.t content.
  *
- * @param[in] start - the start of the expected string.
- * @param[in] length - the number of bytes remaining in the buffer.
+ * @param[in] data - Buffer containing the string.
  * @return the string if valid otherwise an empty string.
  */
-std::string stringFromBuffer(const char* start, size_t length);
+std::string stringFromBuffer(std::span<const uint8_t> data);
 
 /**
  * Writes out a BmcBlobCountRx structure and returns IPMI_OK.
  */
-ipmi_ret_t getBlobCount(ManagerInterface* mgr, const uint8_t* reqBuf,
-                        uint8_t* replyCmdBuf, size_t* dataLen);
+Resp getBlobCount(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Writes out a BmcBlobEnumerateRx in response to a BmcBlobEnumerateTx
@@ -162,61 +154,51 @@
  * It will also return failure if the response buffer is of an invalid
  * length.
  */
-ipmi_ret_t enumerateBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                         uint8_t* replyCmdBuf, size_t* dataLen);
+Resp enumerateBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempts to open the blobId specified and associate with a session id.
  */
-ipmi_ret_t openBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                    uint8_t* replyCmdBuf, size_t* dataLen);
+Resp openBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempts to close the session specified.
  */
-ipmi_ret_t closeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                     uint8_t* replyCmdBuf, size_t* dataLen);
+Resp closeBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempts to delete the blobId specified.
  */
-ipmi_ret_t deleteBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                      uint8_t* replyCmdBuf, size_t* dataLen);
+Resp deleteBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempts to retrieve the Stat for the blobId specified.
  */
-ipmi_ret_t statBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                    uint8_t* replyCmdBuf, size_t* dataLen);
+Resp statBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempts to retrieve the Stat for the session specified.
  */
-ipmi_ret_t sessionStatBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                           uint8_t* replyCmdBuf, size_t* dataLen);
+Resp sessionStatBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempts to commit the data in the blob.
  */
-ipmi_ret_t commitBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                      uint8_t* replyCmdBuf, size_t* dataLen);
+Resp commitBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempt to read data from the blob.
  */
-ipmi_ret_t readBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                    uint8_t* replyCmdBuf, size_t* dataLen);
+Resp readBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempt to write data to the blob.
  */
-ipmi_ret_t writeBlob(ManagerInterface* mgr, const uint8_t* reqBuf,
-                     uint8_t* replyCmdBuf, size_t* dataLen);
+Resp writeBlob(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 /**
  * Attempt to write metadata to the blob.
  */
-ipmi_ret_t writeMeta(ManagerInterface* mgr, const uint8_t* reqBuf,
-                     uint8_t* replyCmdBuf, size_t* dataLen);
+Resp writeMeta(ManagerInterface* mgr, std::span<const uint8_t> data);
 
 } // namespace blobs
diff --git a/main.cpp b/main.cpp
index e90969b..d3179f3 100644
--- a/main.cpp
+++ b/main.cpp
@@ -24,11 +24,14 @@
 #include <ipmid/api.h>
 
 #include <cstdio>
+#include <ipmid/api-types.hpp>
+#include <ipmid/handler.hpp>
 #include <ipmid/iana.hpp>
 #include <ipmid/oemopenbmc.hpp>
 #include <ipmid/oemrouter.hpp>
 #include <memory>
 #include <phosphor-logging/log.hpp>
+#include <span>
 
 namespace blobs
 {
@@ -39,13 +42,16 @@
 
 void setupBlobGlobalHandler()
 {
-    oem::Router* oemRouter = oem::mutableRouter();
     std::fprintf(stderr,
                  "Registering OEM:[%#08X], Cmd:[%#04X] for Blob Commands\n",
                  oem::obmcOemNumber, oem::Cmd::blobTransferCmd);
 
-    oemRouter->registerHandler(oem::obmcOemNumber, oem::Cmd::blobTransferCmd,
-                               handleBlobCommand);
+    ipmi::registerOemHandler(
+        ipmi::prioOemBase, oem::obmcOemNumber, oem::Cmd::blobTransferCmd,
+        ::ipmi::Privilege::User,
+        [](ipmi::Context::ptr, uint8_t cmd, const std::vector<uint8_t>& data) {
+            return handleBlobCommand(cmd, data);
+        });
 
     /* Install handlers. */
     try
diff --git a/process.cpp b/process.cpp
index 2481fa2..46ffea5 100644
--- a/process.cpp
+++ b/process.cpp
@@ -20,7 +20,10 @@
 
 #include <cstring>
 #include <ipmiblob/crc.hpp>
+#include <ipmid/api-types.hpp>
+#include <span>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 namespace blobs
@@ -29,7 +32,6 @@
 /* Used by all commands with data. */
 struct BmcRx
 {
-    uint8_t cmd;
     uint16_t crc;
     uint8_t data; /* one byte minimum of data. */
 } __attribute__((packed));
@@ -48,29 +50,29 @@
     {BlobOEMCommands::bmcBlobWriteMeta, writeMeta},
 };
 
-IpmiBlobHandler validateBlobCommand(const uint8_t* reqBuf,
-                                    uint8_t* /*replyCmdBuf*/, size_t* dataLen,
-                                    ipmi_ret_t* code)
+IpmiBlobHandler validateBlobCommand(uint8_t cmd, std::span<const uint8_t> data)
 {
-    size_t requestLength = (*dataLen);
+    size_t requestLength = data.size();
     /* We know dataLen is at least 1 already */
-    auto command = static_cast<BlobOEMCommands>(reqBuf[0]);
+    auto command = static_cast<BlobOEMCommands>(cmd);
 
     /* Validate it's at least well-formed. */
     if (!validateRequestLength(command, requestLength))
     {
-        *code = IPMI_CC_REQ_DATA_LEN_INVALID;
-        return nullptr;
+        return [](ManagerInterface*, std::span<const uint8_t>) {
+            return ipmi::responseReqDataLenInvalid();
+        };
     }
 
     /* If there is a payload. */
-    if (requestLength > sizeof(uint8_t))
+    if (requestLength > sizeof(cmd))
     {
         /* Verify the request includes: command, crc16, data */
         if (requestLength < sizeof(struct BmcRx))
         {
-            *code = IPMI_CC_REQ_DATA_LEN_INVALID;
-            return nullptr;
+            return [](ManagerInterface*, std::span<const uint8_t>) {
+                return ipmi::responseReqDataLenInvalid();
+            };
         }
 
         /* We don't include the command byte at offset 0 as part of the crc
@@ -81,44 +83,58 @@
         /* We start after the command byte. */
         std::vector<uint8_t> bytes(requestBodyLen);
 
-        /* It likely has a well-formed payload. */
-        struct BmcRx request;
-        std::memcpy(&request, reqBuf, sizeof(request));
-        uint16_t crcValue = request.crc;
+        /* It likely has a well-formed payload.
+         * Get the first two bytes of the request for crc.
+         */
+        uint16_t crc;
+        if (data.size() < sizeof(crc))
+        {
+            return [](ManagerInterface*, std::span<const uint8_t>) {
+                return ipmi::responseReqDataLenInvalid();
+            };
+        }
+        std::memcpy(&crc, data.data(), sizeof(crc));
 
-        /* Set the in-place CRC to zero. */
-        std::memcpy(bytes.data(), &reqBuf[3], requestBodyLen);
+        /* Set the in-place CRC to zero.
+         * Remove the first two bytes for crc and get the reset of the request.
+         */
+        data = data.subspan(sizeof(crc));
 
         /* Crc expected but didn't match. */
-        if (crcValue != ipmiblob::generateCrc(bytes))
+        if (crc != ipmiblob::generateCrc(
+                       std::vector<uint8_t>(data.begin(), data.end())))
         {
-            *code = IPMI_CC_UNSPECIFIED_ERROR;
-            return nullptr;
-        }
+            return [](ManagerInterface*, std::span<const uint8_t>) {
+                return ipmi::responseUnspecifiedError();
+            };
+        };
     }
 
     /* Grab the corresponding handler for the command. */
     auto found = handlers.find(command);
     if (found == handlers.end())
     {
-        *code = IPMI_CC_INVALID_FIELD_REQUEST;
-        return nullptr;
+        return [](ManagerInterface*, std::span<const uint8_t>) {
+            return ipmi::responseInvalidFieldRequest();
+        };
     }
 
     return found->second;
 }
 
-ipmi_ret_t processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
-                              const uint8_t* reqBuf, uint8_t* replyCmdBuf,
-                              size_t* dataLen)
+Resp processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
+                        std::span<const uint8_t> data)
 {
-    ipmi_ret_t result = cmd(mgr, reqBuf, replyCmdBuf, dataLen);
-    if (result != IPMI_CC_OK)
+    Resp result = cmd(mgr, data);
+    if (std::get<0>(result) != ipmi::ccSuccess)
     {
         return result;
     }
 
-    size_t replyLength = (*dataLen);
+    std::vector<uint8_t>& response = std::get<0>(
+        // std::variant<std::vector<uint8_t>>
+        *std::get<1>(result));
+    size_t replyLength = response.size();
 
     /* The command, whatever it was, returned success. */
     if (replyLength == 0)
@@ -131,42 +147,30 @@
      */
     if (replyLength < (sizeof(uint16_t)))
     {
-        return IPMI_CC_UNSPECIFIED_ERROR;
+        return ipmi::responseUnspecifiedError();
     }
 
     /* The command, whatever it was, replied, so let's set the CRC. */
-    std::vector<std::uint8_t> crcBuffer(replyCmdBuf + sizeof(uint16_t),
-                                        replyCmdBuf + replyLength);
+    std::span<const uint8_t> responseView = response;
+    responseView = responseView.subspan(sizeof(uint16_t));
+    std::vector<std::uint8_t> crcBuffer(responseView.begin(),
+                                        responseView.end());
     /* Copy the CRC into place. */
     uint16_t crcValue = ipmiblob::generateCrc(crcBuffer);
-    std::memcpy(replyCmdBuf, &crcValue, sizeof(crcValue));
+    if (response.size() < sizeof(crcValue))
+    {
+        return ipmi::responseReqDataLenInvalid();
+    }
+    std::memcpy(response.data(), &crcValue, sizeof(crcValue));
 
     return result;
 }
 
-ipmi_ret_t handleBlobCommand(ipmi_cmd_t, const uint8_t* reqBuf,
-                             uint8_t* replyCmdBuf, size_t* dataLen)
+Resp handleBlobCommand(uint8_t cmd, std::vector<uint8_t> data)
 {
-    /* It's holding at least a sub-command.  The OEN is trimmed from the bytes
-     * before this is called.
-     */
-    if ((*dataLen) < 1)
-    {
-        return IPMI_CC_REQ_DATA_LEN_INVALID;
-    }
-
     /* on failure rc is set to the corresponding IPMI error. */
-    ipmi_ret_t rc = IPMI_CC_OK;
-    IpmiBlobHandler command =
-        validateBlobCommand(reqBuf, replyCmdBuf, dataLen, &rc);
-    if (command == nullptr)
-    {
-        (*dataLen) = 0;
-        return rc;
-    }
-
-    return processBlobCommand(command, getBlobManager(), reqBuf, replyCmdBuf,
-                              dataLen);
+    return processBlobCommand(validateBlobCommand(cmd, data), getBlobManager(),
+                              data);
 }
 
 } // namespace blobs
diff --git a/process.hpp b/process.hpp
index da0031c..6b89413 100644
--- a/process.hpp
+++ b/process.hpp
@@ -1,30 +1,30 @@
 #pragma once
 
+#include "ipmi.hpp"
 #include "manager.hpp"
 
 #include <ipmid/api.h>
 
 #include <functional>
+#include <ipmid/api-types.hpp>
+#include <span>
+#include <utility>
+#include <vector>
 
 namespace blobs
 {
 
 using IpmiBlobHandler =
-    std::function<ipmi_ret_t(ManagerInterface* mgr, const uint8_t* reqBuf,
-                             uint8_t* replyCmdBuf, size_t* dataLen)>;
+    std::function<Resp(ManagerInterface* mgr, std::span<const uint8_t> data)>;
 
 /**
  * Validate the IPMI request and determine routing.
  *
- * @param[in] reqBuf - a pointer to the ipmi request packet buffer.
- * @param[in,out] replyCmdBuf - a pointer to the ipmi reply packet buffer.
- * @param[in,out] dataLen - initially the request length, set to reply length
- *                          on return.
- * @param[out] code - set to the IPMI error on failure, otherwise unset.
- * @return the ipmi command handler, or nullptr on failure.
+ * @param[in] cmd  Requested command
+ * @param[in] data Requested data
+ * @return the ipmi command handler, or nullopt on failure.
  */
-IpmiBlobHandler validateBlobCommand(const uint8_t* reqBuf, uint8_t* replyCmdBuf,
-                                    size_t* dataLen, ipmi_ret_t* code);
+IpmiBlobHandler validateBlobCommand(uint8_t cmd, std::span<const uint8_t> data);
 
 /**
  * Call the IPMI command and process the result, including running the CRC
@@ -32,20 +32,16 @@
  *
  * @param[in] cmd - a funtion pointer to the ipmi command to process.
  * @param[in] mgr - a pointer to the manager interface.
- * @param[in] reqBuf - a pointer to the ipmi request packet buffer.
- * @param[in,out] replyCmdBuf - a pointer to the ipmi reply packet buffer.
- * @param[in,out] dataLen - initially the request length, set to reply length
- *                          on return.
+ * @param[in] data - Requested data.
+ * @param[in,out] maxSize - Maximum ipmi reply size
  * @return the ipmi command result.
  */
-ipmi_ret_t processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
-                              const uint8_t* reqBuf, uint8_t* replyCmdBuf,
-                              size_t* dataLen);
+Resp processBlobCommand(IpmiBlobHandler cmd, ManagerInterface* mgr,
+                        std::span<const uint8_t> data);
 
 /**
  * Given an IPMI command, request buffer, and reply buffer, validate the request
  * and call processBlobCommand.
  */
-ipmi_ret_t handleBlobCommand(ipmi_cmd_t cmd, const uint8_t* reqBuf,
-                             uint8_t* replyCmdBuf, size_t* dataLen);
+Resp handleBlobCommand(uint8_t cmd, std::vector<uint8_t> data);
 } // namespace blobs
diff --git a/test/helper.cpp b/test/helper.cpp
new file mode 100644
index 0000000..73e19aa
--- /dev/null
+++ b/test/helper.cpp
@@ -0,0 +1,32 @@
+#include "helper.hpp"
+
+#include "ipmi.hpp"
+
+#include <ipmid/api-types.hpp>
+#include <optional>
+#include <span>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace blobs
+{
+std::vector<std::uint8_t>
+    validateReply(ipmi::RspType<std::vector<uint8_t>> reply, bool hasData)
+{
+    // Reply is in the form of
+    // std::tuple<ipmi::Cc, std::optional<std::tuple<RetTypes...>>>
+    EXPECT_EQ(::ipmi::ccSuccess, std::get<0>(reply));
+
+    auto actualReply = std::get<1>(reply);
+    EXPECT_TRUE(actualReply.has_value());
+
+    auto data = std::get<0>(*actualReply);
+    EXPECT_EQ(hasData, !data.empty());
+
+    return hasData ? data : std::vector<uint8_t>{};
+}
+
+} // namespace blobs
diff --git a/test/helper.hpp b/test/helper.hpp
new file mode 100644
index 0000000..8d33f5c
--- /dev/null
+++ b/test/helper.hpp
@@ -0,0 +1,15 @@
+#include <ipmid/api-types.hpp>
+#include <optional>
+#include <span>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+namespace blobs
+{
+std::vector<std::uint8_t>
+    validateReply(ipmi::RspType<std::vector<uint8_t>> reply,
+                  bool hasData = true);
+} // namespace blobs
diff --git a/test/ipmi_close_unittest.cpp b/test/ipmi_close_unittest.cpp
index af2e03e..d4bcb41 100644
--- a/test/ipmi_close_unittest.cpp
+++ b/test/ipmi_close_unittest.cpp
@@ -11,10 +11,6 @@
 
 using ::testing::Return;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 TEST(BlobCloseTest, ManagerRejectsCloseReturnsFailure)
 {
     // The session manager returned failure to close, which we need to pass on.
@@ -22,21 +18,18 @@
     ManagerMock mgr;
     uint16_t sessionId = 0x54;
     size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request;
     struct BmcBlobCloseTx req;
 
-    req.cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobClose);
     req.crc = 0;
     req.sessionId = sessionId;
 
     dataLen = sizeof(req);
-
-    std::memcpy(request, &req, sizeof(req));
+    request.resize(dataLen);
+    std::memcpy(request.data(), &req, dataLen);
 
     EXPECT_CALL(mgr, close(sessionId)).WillOnce(Return(false));
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              closeBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), closeBlob(&mgr, request));
 }
 
 TEST(BlobCloseTest, BlobClosedReturnsSuccess)
@@ -46,19 +39,18 @@
     ManagerMock mgr;
     uint16_t sessionId = 0x54;
     size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request;
     struct BmcBlobCloseTx req;
 
-    req.cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobClose);
     req.crc = 0;
     req.sessionId = sessionId;
 
     dataLen = sizeof(req);
-
-    std::memcpy(request, &req, sizeof(req));
+    request.resize(dataLen);
+    std::memcpy(request.data(), &req, dataLen);
 
     EXPECT_CALL(mgr, close(sessionId)).WillOnce(Return(true));
-    EXPECT_EQ(IPMI_CC_OK, closeBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>{}),
+              closeBlob(&mgr, request));
 }
 } // namespace blobs
diff --git a/test/ipmi_commit_unittest.cpp b/test/ipmi_commit_unittest.cpp
index d4b9c58..6cc8223 100644
--- a/test/ipmi_commit_unittest.cpp
+++ b/test/ipmi_commit_unittest.cpp
@@ -12,31 +12,22 @@
 using ::testing::ElementsAreArray;
 using ::testing::Return;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 TEST(BlobCommitTest, InvalidCommitDataLengthReturnsFailure)
 {
     // The commit command supports an optional commit blob.  This test verifies
     // we sanity check the length of that blob.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobCommitTx*>(request);
-
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobCommit);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->commitDataLen =
+    std::vector<uint8_t> request;
+    struct BmcBlobCommitTx req;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.commitDataLen =
         1; // It's one byte, but that's more than the packet size.
 
-    dataLen = sizeof(struct BmcBlobCommitTx);
-
-    EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
-              commitBlob(&mgr, request, reply, &dataLen));
+    request.resize(sizeof(struct BmcBlobCommitTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobCommitTx));
+    EXPECT_EQ(ipmi::responseReqDataLenInvalid(), commitBlob(&mgr, request));
 }
 
 TEST(BlobCommitTest, ValidCommitNoDataHandlerRejectsReturnsFailure)
@@ -44,22 +35,17 @@
     // The commit packet is valid and the manager's commit call returns failure.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobCommitTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobCommitTx req;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.commitDataLen = 0;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobCommit);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->commitDataLen = 0;
+    request.resize(sizeof(struct BmcBlobCommitTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobCommitTx));
 
-    dataLen = sizeof(struct BmcBlobCommitTx);
-
-    EXPECT_CALL(mgr, commit(req->sessionId, _)).WillOnce(Return(false));
-
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              commitBlob(&mgr, request, reply, &dataLen));
+    EXPECT_CALL(mgr, commit(req.sessionId, _)).WillOnce(Return(false));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), commitBlob(&mgr, request));
 }
 
 TEST(BlobCommitTest, ValidCommitNoDataHandlerAcceptsReturnsSuccess)
@@ -67,21 +53,18 @@
     // Commit called with no data and everything returns success.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobCommitTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobCommitTx req;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.commitDataLen = 0;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobCommit);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->commitDataLen = 0;
+    request.resize(sizeof(struct BmcBlobCommitTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobCommitTx));
+    EXPECT_CALL(mgr, commit(req.sessionId, _)).WillOnce(Return(true));
 
-    dataLen = sizeof(struct BmcBlobCommitTx);
-
-    EXPECT_CALL(mgr, commit(req->sessionId, _)).WillOnce(Return(true));
-
-    EXPECT_EQ(IPMI_CC_OK, commitBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>{}),
+              commitBlob(&mgr, request));
 }
 
 TEST(BlobCommitTest, ValidCommitWithDataHandlerAcceptsReturnsSuccess)
@@ -89,26 +72,21 @@
     // Commit called with extra data and everything returns success.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobCommitTx*>(request);
+    std::vector<uint8_t> request;
+    std::array<uint8_t, 4> expectedBlob = {0x25, 0x33, 0x45, 0x67};
+    struct BmcBlobCommitTx req;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.commitDataLen = sizeof(expectedBlob);
 
-    uint8_t expectedBlob[4] = {0x25, 0x33, 0x45, 0x67};
+    request.resize(sizeof(struct BmcBlobCommitTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobCommitTx));
+    request.insert(request.end(), expectedBlob.begin(), expectedBlob.end());
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobCommit);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->commitDataLen = sizeof(expectedBlob);
-    std::memcpy(req + 1, &expectedBlob[0], sizeof(expectedBlob));
-
-    dataLen = sizeof(struct BmcBlobCommitTx) + sizeof(expectedBlob);
-
-    EXPECT_CALL(mgr,
-                commit(req->sessionId,
-                       ElementsAreArray(expectedBlob, sizeof(expectedBlob))))
+    EXPECT_CALL(mgr, commit(req.sessionId, ElementsAreArray(expectedBlob)))
         .WillOnce(Return(true));
 
-    EXPECT_EQ(IPMI_CC_OK, commitBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>{}),
+              commitBlob(&mgr, request));
 }
 } // namespace blobs
diff --git a/test/ipmi_delete_unittest.cpp b/test/ipmi_delete_unittest.cpp
index 3336b2d..cb1401a 100644
--- a/test/ipmi_delete_unittest.cpp
+++ b/test/ipmi_delete_unittest.cpp
@@ -12,79 +12,58 @@
 using ::testing::Return;
 using ::testing::StrEq;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 TEST(BlobDeleteTest, InvalidRequestLengthReturnsFailure)
 {
     // There is a minimum blobId length of one character, this test verifies
     // we check that.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobDeleteTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobDeleteTx req;
+    req.crc = 0;
     std::string blobId = "abc";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobDelete);
-    req->crc = 0;
-    // length() doesn't include the nul-terminator.
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
+    request.resize(sizeof(struct BmcBlobDeleteTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobDeleteTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
 
-    dataLen = sizeof(struct BmcBlobDeleteTx) + blobId.length();
-
-    EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
-              deleteBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseReqDataLenInvalid(), deleteBlob(&mgr, request));
 }
 
 TEST(BlobDeleteTest, RequestRejectedReturnsFailure)
 {
     // The blobId is rejected for any reason.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobDeleteTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobDeleteTx req;
+    req.crc = 0;
     std::string blobId = "a";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobDelete);
-    req->crc = 0;
-    // length() doesn't include the nul-terminator, request buff is initialized
-    // to 0s
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
-
-    dataLen = sizeof(struct BmcBlobDeleteTx) + blobId.length() + 1;
+    request.resize(sizeof(struct BmcBlobDeleteTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobDeleteTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
+    request.emplace_back('\0');
 
     EXPECT_CALL(mgr, deleteBlob(StrEq(blobId))).WillOnce(Return(false));
-
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              deleteBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), deleteBlob(&mgr, request));
 }
 
 TEST(BlobDeleteTest, BlobDeleteReturnsOk)
 {
     // The boring case where the blobId is deleted.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobDeleteTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobDeleteTx req;
+    req.crc = 0;
     std::string blobId = "a";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobDelete);
-    req->crc = 0;
-    // length() doesn't include the nul-terminator, request buff is initialized
-    // to 0s
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
-
-    dataLen = sizeof(struct BmcBlobDeleteTx) + blobId.length() + 1;
+    request.resize(sizeof(struct BmcBlobDeleteTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobDeleteTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
+    request.emplace_back('\0');
 
     EXPECT_CALL(mgr, deleteBlob(StrEq(blobId))).WillOnce(Return(true));
 
-    EXPECT_EQ(IPMI_CC_OK, deleteBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>{}),
+              deleteBlob(&mgr, request));
 }
 } // namespace blobs
diff --git a/test/ipmi_enumerate_unittest.cpp b/test/ipmi_enumerate_unittest.cpp
index edea82b..87752ea 100644
--- a/test/ipmi_enumerate_unittest.cpp
+++ b/test/ipmi_enumerate_unittest.cpp
@@ -1,3 +1,4 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 
@@ -11,28 +12,21 @@
 
 using ::testing::Return;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 TEST(BlobEnumerateTest, VerifyIfRequestByIdInvalidReturnsFailure)
 {
     // This tests to verify that if the index is invalid, it'll return failure.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request;
     struct BmcBlobEnumerateTx req;
-    uint8_t* request = reinterpret_cast<uint8_t*>(&req);
-
-    req.cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobEnumerate);
     req.blobIdx = 0;
-    dataLen = sizeof(struct BmcBlobEnumerateTx);
+
+    request.resize(sizeof(struct BmcBlobEnumerateTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobEnumerateTx));
 
     EXPECT_CALL(mgr, getBlobId(req.blobIdx)).WillOnce(Return(""));
-
-    EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST,
-              enumerateBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseInvalidFieldRequest(),
+              enumerateBlob(&mgr, request));
 }
 
 TEST(BlobEnumerateTest, BoringRequestByIdAndReceive)
@@ -41,26 +35,23 @@
     // will return the blobId.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request;
     struct BmcBlobEnumerateTx req;
-    struct BmcBlobEnumerateRx* rep;
-    uint8_t* request = reinterpret_cast<uint8_t*>(&req);
+    req.blobIdx = 0;
     std::string blobId = "/asdf";
 
-    req.cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobEnumerate);
-    req.blobIdx = 0;
-    dataLen = sizeof(struct BmcBlobEnumerateTx);
+    request.resize(sizeof(struct BmcBlobEnumerateTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobEnumerateTx));
 
     EXPECT_CALL(mgr, getBlobId(req.blobIdx)).WillOnce(Return(blobId));
 
-    EXPECT_EQ(IPMI_CC_OK, enumerateBlob(&mgr, request, reply, &dataLen));
+    auto result = validateReply(enumerateBlob(&mgr, request));
 
     // We're expecting this as a response.
     // blobId.length + 1 + sizeof(uint16_t);
-    EXPECT_EQ(blobId.length() + 1 + sizeof(uint16_t), dataLen);
-
-    rep = reinterpret_cast<struct BmcBlobEnumerateRx*>(reply);
-    EXPECT_EQ(0, std::memcmp(rep + 1, blobId.c_str(), blobId.length() + 1));
+    EXPECT_EQ(blobId.length() + 1 + sizeof(uint16_t), result.size());
+    EXPECT_EQ(blobId,
+              // Remove crc and nul-terminator.
+              std::string(result.begin() + sizeof(uint16_t), result.end() - 1));
 }
 } // namespace blobs
diff --git a/test/ipmi_getcount_unittest.cpp b/test/ipmi_getcount_unittest.cpp
index 34fc33d..dc1202d 100644
--- a/test/ipmi_getcount_unittest.cpp
+++ b/test/ipmi_getcount_unittest.cpp
@@ -1,3 +1,4 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 
@@ -11,10 +12,6 @@
 
 using ::testing::Return;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 // the request here is only the subcommand byte and therefore there's no invalid
 // length check, etc to handle within the method.
 
@@ -24,24 +21,17 @@
     // return that there are 0 blobs.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    struct BmcBlobCountTx req;
     struct BmcBlobCountRx rep;
-    uint8_t* request = reinterpret_cast<uint8_t*>(&req);
-
-    req.cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount);
-    dataLen = sizeof(req);
 
     rep.crc = 0;
     rep.blobCount = 0;
 
     EXPECT_CALL(mgr, buildBlobList()).WillOnce(Return(0));
 
-    EXPECT_EQ(IPMI_CC_OK, getBlobCount(&mgr, request, reply, &dataLen));
+    auto result = validateReply(getBlobCount(&mgr, {}));
 
-    EXPECT_EQ(sizeof(rep), dataLen);
-    EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
+    EXPECT_EQ(sizeof(rep), result.size());
+    EXPECT_EQ(0, std::memcmp(result.data(), &rep, sizeof(rep)));
 }
 
 TEST(BlobCountTest, ReturnsTwoBlobs)
@@ -50,23 +40,16 @@
     // blobs will return that it found two blobs.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    struct BmcBlobCountTx req;
     struct BmcBlobCountRx rep;
-    uint8_t* request = reinterpret_cast<uint8_t*>(&req);
-
-    req.cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount);
-    dataLen = sizeof(req);
 
     rep.crc = 0;
     rep.blobCount = 2;
 
     EXPECT_CALL(mgr, buildBlobList()).WillOnce(Return(2));
 
-    EXPECT_EQ(IPMI_CC_OK, getBlobCount(&mgr, request, reply, &dataLen));
+    auto result = validateReply(getBlobCount(&mgr, {}));
 
-    EXPECT_EQ(sizeof(rep), dataLen);
-    EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
+    EXPECT_EQ(sizeof(rep), result.size());
+    EXPECT_EQ(0, std::memcmp(result.data(), &rep, sizeof(rep)));
 }
 } // namespace blobs
diff --git a/test/ipmi_open_unittest.cpp b/test/ipmi_open_unittest.cpp
index 5b18631..b938cbe 100644
--- a/test/ipmi_open_unittest.cpp
+++ b/test/ipmi_open_unittest.cpp
@@ -1,3 +1,4 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 
@@ -15,59 +16,44 @@
 using ::testing::Return;
 using ::testing::StrEq;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 TEST(BlobOpenTest, InvalidRequestLengthReturnsFailure)
 {
     // There is a minimum blobId length of one character, this test verifies
     // we check that.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobOpenTx*>(request);
+    std::vector<uint8_t> request;
+    BmcBlobOpenTx req;
     std::string blobId = "abc";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobOpen);
-    req->crc = 0;
-    req->flags = 0;
-    // length() doesn't include the nul-terminator.
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
+    req.crc = 0;
+    req.flags = 0;
 
-    dataLen = sizeof(struct BmcBlobOpenTx) + blobId.length();
+    // Missintg the nul-terminator.
+    request.resize(sizeof(struct BmcBlobOpenTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobOpenTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
 
-    EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
-              openBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseReqDataLenInvalid(), openBlob(&mgr, request));
 }
 
 TEST(BlobOpenTest, RequestRejectedReturnsFailure)
 {
     // The blobId is rejected for any reason.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobOpenTx*>(request);
+    std::vector<uint8_t> request;
+    BmcBlobOpenTx req;
     std::string blobId = "a";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobOpen);
-    req->crc = 0;
-    req->flags = 0;
-    // length() doesn't include the nul-terminator, request buff is initialized
-    // to 0s
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
+    req.crc = 0;
+    req.flags = 0;
+    request.resize(sizeof(struct BmcBlobOpenTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobOpenTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
+    request.emplace_back('\0');
 
-    dataLen = sizeof(struct BmcBlobOpenTx) + blobId.length() + 1;
+    EXPECT_CALL(mgr, open(req.flags, StrEq(blobId), _)).WillOnce(Return(false));
 
-    EXPECT_CALL(mgr, open(req->flags, StrEq(blobId), _))
-        .WillOnce(Return(false));
-
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              openBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), openBlob(&mgr, request));
 }
 
 TEST(BlobOpenTest, BlobOpenReturnsOk)
@@ -75,35 +61,32 @@
     // The boring case where the blobId opens.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobOpenTx*>(request);
+    std::vector<uint8_t> request;
+    BmcBlobOpenTx req;
     struct BmcBlobOpenRx rep;
     std::string blobId = "a";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobOpen);
-    req->crc = 0;
-    req->flags = 0;
-    // length() doesn't include the nul-terminator, request buff is initialized
-    // to 0s
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
+    req.crc = 0;
+    req.flags = 0;
+    request.resize(sizeof(struct BmcBlobOpenTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobOpenTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
+    request.emplace_back('\0');
 
-    dataLen = sizeof(struct BmcBlobOpenTx) + blobId.length() + 1;
     uint16_t returnedSession = 0x54;
 
-    EXPECT_CALL(mgr, open(req->flags, StrEq(blobId), NotNull()))
+    EXPECT_CALL(mgr, open(req.flags, StrEq(blobId), NotNull()))
         .WillOnce(Invoke([&](uint16_t, const std::string&, uint16_t* session) {
             (*session) = returnedSession;
             return true;
         }));
 
-    EXPECT_EQ(IPMI_CC_OK, openBlob(&mgr, request, reply, &dataLen));
+    auto result = validateReply(openBlob(&mgr, request));
 
     rep.crc = 0;
     rep.sessionId = returnedSession;
 
-    EXPECT_EQ(sizeof(rep), dataLen);
-    EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
+    EXPECT_EQ(sizeof(rep), result.size());
+    EXPECT_EQ(0, std::memcmp(result.data(), &rep, sizeof(rep)));
 }
 } // namespace blobs
diff --git a/test/ipmi_read_unittest.cpp b/test/ipmi_read_unittest.cpp
index 8d1b55f..80e1cbc 100644
--- a/test/ipmi_read_unittest.cpp
+++ b/test/ipmi_read_unittest.cpp
@@ -1,3 +1,4 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 
@@ -11,64 +12,50 @@
 
 using ::testing::Return;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 TEST(BlobReadTest, ManagerReturnsNoData)
 {
     // Verify that if no data is returned the IPMI command reply has no
     // payload.  The manager, in all failures, will just return 0 bytes.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobReadTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobReadTx req;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobRead);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
-    req->requestedSize = 0x10;
-
-    dataLen = sizeof(struct BmcBlobReadTx);
-
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
+    req.requestedSize = 0x10;
+    request.resize(sizeof(struct BmcBlobReadTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobReadTx));
     std::vector<uint8_t> data;
 
-    EXPECT_CALL(mgr, read(req->sessionId, req->offset, req->requestedSize))
+    EXPECT_CALL(mgr, read(req.sessionId, req.offset, req.requestedSize))
         .WillOnce(Return(data));
 
-    EXPECT_EQ(IPMI_CC_OK, readBlob(&mgr, request, reply, &dataLen));
-    EXPECT_EQ(sizeof(struct BmcBlobReadRx), dataLen);
+    auto result = validateReply(readBlob(&mgr, request));
+    EXPECT_EQ(sizeof(struct BmcBlobReadRx), result.size());
 }
 
 TEST(BlobReadTest, ManagerReturnsData)
 {
     // Verify that if data is returned, it's placed in the expected location.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobReadTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobReadTx req;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobRead);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
-    req->requestedSize = 0x10;
-
-    dataLen = sizeof(struct BmcBlobReadTx);
-
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
+    req.requestedSize = 0x10;
+    request.resize(sizeof(struct BmcBlobReadTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobReadTx));
     std::vector<uint8_t> data = {0x02, 0x03, 0x05, 0x06};
 
-    EXPECT_CALL(mgr, read(req->sessionId, req->offset, req->requestedSize))
+    EXPECT_CALL(mgr, read(req.sessionId, req.offset, req.requestedSize))
         .WillOnce(Return(data));
 
-    EXPECT_EQ(IPMI_CC_OK, readBlob(&mgr, request, reply, &dataLen));
-    EXPECT_EQ(sizeof(struct BmcBlobReadRx) + data.size(), dataLen);
-    EXPECT_EQ(0, std::memcmp(&reply[sizeof(struct BmcBlobReadRx)], data.data(),
+    auto result = validateReply(readBlob(&mgr, request));
+    EXPECT_EQ(sizeof(struct BmcBlobReadRx) + data.size(), result.size());
+    EXPECT_EQ(0, std::memcmp(&result[sizeof(struct BmcBlobReadRx)], data.data(),
                              data.size()));
 }
 
diff --git a/test/ipmi_sessionstat_unittest.cpp b/test/ipmi_sessionstat_unittest.cpp
index 4bf1125..3278f25 100644
--- a/test/ipmi_sessionstat_unittest.cpp
+++ b/test/ipmi_sessionstat_unittest.cpp
@@ -1,3 +1,4 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 
@@ -23,22 +24,19 @@
     // If the session ID is invalid, the request must fail.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobSessionStatTx*>(request);
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobSessionStat);
-    req->crc = 0;
-    req->sessionId = 0x54;
+    std::vector<uint8_t> request;
+    struct BmcBlobSessionStatTx req;
+    req.crc = 0;
+    req.sessionId = 0x54;
 
-    dataLen = sizeof(struct BmcBlobSessionStatTx);
+    request.resize(sizeof(struct BmcBlobSessionStatTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobSessionStatTx));
 
     EXPECT_CALL(mgr,
-                stat(Matcher<uint16_t>(req->sessionId), Matcher<BlobMeta*>(_)))
+                stat(Matcher<uint16_t>(req.sessionId), Matcher<BlobMeta*>(_)))
         .WillOnce(Return(false));
 
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              sessionStatBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), sessionStatBlob(&mgr, request));
 }
 
 TEST(BlobSessionStatTest, RequestSucceedsNoMetadata)
@@ -46,15 +44,13 @@
     // Stat request succeeeds but there were no metadata bytes.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobSessionStatTx*>(request);
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobSessionStat);
-    req->crc = 0;
-    req->sessionId = 0x54;
+    std::vector<uint8_t> request;
+    struct BmcBlobSessionStatTx req;
+    req.crc = 0;
+    req.sessionId = 0x54;
 
-    dataLen = sizeof(struct BmcBlobSessionStatTx);
+    request.resize(sizeof(struct BmcBlobSessionStatTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobSessionStatTx));
 
     struct BmcBlobStatRx rep;
     rep.crc = 0x00;
@@ -65,7 +61,7 @@
     uint16_t blobState = rep.blobState;
     uint32_t size = rep.size;
 
-    EXPECT_CALL(mgr, stat(Matcher<uint16_t>(req->sessionId),
+    EXPECT_CALL(mgr, stat(Matcher<uint16_t>(req.sessionId),
                           Matcher<BlobMeta*>(NotNull())))
         .WillOnce(Invoke([&](uint16_t, BlobMeta* meta) {
             meta->blobState = blobState;
@@ -73,10 +69,10 @@
             return true;
         }));
 
-    EXPECT_EQ(IPMI_CC_OK, sessionStatBlob(&mgr, request, reply, &dataLen));
+    auto result = validateReply(sessionStatBlob(&mgr, request));
 
-    EXPECT_EQ(sizeof(rep), dataLen);
-    EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
+    EXPECT_EQ(sizeof(rep), result.size());
+    EXPECT_EQ(0, std::memcmp(result.data(), &rep, sizeof(rep)));
 }
 
 TEST(BlobSessionStatTest, RequestSucceedsWithMetadata)
@@ -84,15 +80,13 @@
     // Stat request succeeds and there were metadata bytes.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobSessionStatTx*>(request);
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobSessionStat);
-    req->crc = 0;
-    req->sessionId = 0x54;
+    std::vector<uint8_t> request;
+    struct BmcBlobSessionStatTx req;
+    req.crc = 0;
+    req.sessionId = 0x54;
 
-    dataLen = sizeof(struct BmcBlobSessionStatTx);
+    request.resize(sizeof(struct BmcBlobSessionStatTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobSessionStatTx));
 
     BlobMeta lmeta;
     lmeta.blobState = 0x01;
@@ -108,18 +102,18 @@
     rep.size = lmeta.size;
     rep.metadataLen = lmeta.metadata.size();
 
-    EXPECT_CALL(mgr, stat(Matcher<uint16_t>(req->sessionId),
+    EXPECT_CALL(mgr, stat(Matcher<uint16_t>(req.sessionId),
                           Matcher<BlobMeta*>(NotNull())))
         .WillOnce(Invoke([&](uint16_t, BlobMeta* meta) {
             (*meta) = lmeta;
             return true;
         }));
 
-    EXPECT_EQ(IPMI_CC_OK, sessionStatBlob(&mgr, request, reply, &dataLen));
+    auto result = validateReply(sessionStatBlob(&mgr, request));
 
-    EXPECT_EQ(sizeof(rep) + lmeta.metadata.size(), dataLen);
-    EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
-    EXPECT_EQ(0, std::memcmp(reply + sizeof(rep), lmeta.metadata.data(),
+    EXPECT_EQ(sizeof(rep) + lmeta.metadata.size(), result.size());
+    EXPECT_EQ(0, std::memcmp(result.data(), &rep, sizeof(rep)));
+    EXPECT_EQ(0, std::memcmp(result.data() + sizeof(rep), lmeta.metadata.data(),
                              lmeta.metadata.size()));
 }
 } // namespace blobs
diff --git a/test/ipmi_stat_unittest.cpp b/test/ipmi_stat_unittest.cpp
index 6d4d7b2..ab091ad 100644
--- a/test/ipmi_stat_unittest.cpp
+++ b/test/ipmi_stat_unittest.cpp
@@ -1,3 +1,4 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 
@@ -24,23 +25,18 @@
 {
     // There is a minimum blobId length of one character, this test verifies
     // we check that.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobStatTx req;
     std::string blobId = "abc";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
-    req->crc = 0;
-    // length() doesn't include the nul-terminator.
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
+    req.crc = 0;
+    request.resize(sizeof(struct BmcBlobStatTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobStatTx));
+    // Do not include the nul-terminator
+    request.insert(request.end(), blobId.begin(), blobId.end());
 
-    dataLen = sizeof(struct BmcBlobStatTx) + blobId.length();
-
-    EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID,
-              statBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseReqDataLenInvalid(), statBlob(&mgr, request));
 }
 
 TEST(BlobStatTest, RequestRejectedReturnsFailure)
@@ -48,26 +44,21 @@
     // The blobId is rejected for any reason.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobStatTx req;
     std::string blobId = "a";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
-    req->crc = 0;
-    // length() doesn't include the nul-terminator, request buff is initialized
-    // to 0s
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
-
-    dataLen = sizeof(struct BmcBlobStatTx) + blobId.length() + 1;
+    req.crc = 0;
+    request.resize(sizeof(struct BmcBlobStatTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobStatTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
+    request.emplace_back('\0');
 
     EXPECT_CALL(mgr, stat(Matcher<const std::string&>(StrEq(blobId)),
                           Matcher<BlobMeta*>(_)))
         .WillOnce(Return(false));
 
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              statBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), statBlob(&mgr, request));
 }
 
 TEST(BlobStatTest, RequestSucceedsNoMetadata)
@@ -75,19 +66,15 @@
     // Stat request succeeeds but there were no metadata bytes.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobStatTx req;
     std::string blobId = "a";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
-    req->crc = 0;
-    // length() doesn't include the nul-terminator, request buff is initialized
-    // to 0s
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
-
-    dataLen = sizeof(struct BmcBlobStatTx) + blobId.length() + 1;
+    req.crc = 0;
+    request.resize(sizeof(struct BmcBlobStatTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobStatTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
+    request.emplace_back('\0');
 
     struct BmcBlobStatRx rep;
     rep.crc = 0x00;
@@ -106,10 +93,10 @@
             return true;
         }));
 
-    EXPECT_EQ(IPMI_CC_OK, statBlob(&mgr, request, reply, &dataLen));
+    auto result = validateReply(statBlob(&mgr, request));
 
-    EXPECT_EQ(sizeof(rep), dataLen);
-    EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
+    EXPECT_EQ(sizeof(rep), result.size());
+    EXPECT_EQ(0, std::memcmp(result.data(), &rep, sizeof(rep)));
 }
 
 TEST(BlobStatTest, RequestSucceedsWithMetadata)
@@ -117,19 +104,15 @@
     // Stat request succeeds and there were metadata bytes.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobStatTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobStatTx req;
     std::string blobId = "a";
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobStat);
-    req->crc = 0;
-    // length() doesn't include the nul-terminator, request buff is initialized
-    // to 0s
-    std::memcpy(req + 1, blobId.c_str(), blobId.length());
-
-    dataLen = sizeof(struct BmcBlobStatTx) + blobId.length() + 1;
+    req.crc = 0;
+    request.resize(sizeof(struct BmcBlobStatTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobStatTx));
+    request.insert(request.end(), blobId.begin(), blobId.end());
+    request.emplace_back('\0');
 
     BlobMeta lmeta;
     lmeta.blobState = 0x01;
@@ -152,11 +135,11 @@
             return true;
         }));
 
-    EXPECT_EQ(IPMI_CC_OK, statBlob(&mgr, request, reply, &dataLen));
+    auto result = validateReply(statBlob(&mgr, request));
 
-    EXPECT_EQ(sizeof(rep) + lmeta.metadata.size(), dataLen);
-    EXPECT_EQ(0, std::memcmp(reply, &rep, sizeof(rep)));
-    EXPECT_EQ(0, std::memcmp(reply + sizeof(rep), lmeta.metadata.data(),
+    EXPECT_EQ(sizeof(rep) + lmeta.metadata.size(), result.size());
+    EXPECT_EQ(0, std::memcmp(result.data(), &rep, sizeof(rep)));
+    EXPECT_EQ(0, std::memcmp(result.data() + sizeof(rep), lmeta.metadata.data(),
                              lmeta.metadata.size()));
 }
 } // namespace blobs
diff --git a/test/ipmi_unittest.cpp b/test/ipmi_unittest.cpp
index 8f27ed7..ed2f026 100644
--- a/test/ipmi_unittest.cpp
+++ b/test/ipmi_unittest.cpp
@@ -14,47 +14,37 @@
 TEST(StringInputTest, NullPointerInput)
 {
     // The method should verify it did receive a non-null input pointer.
-
-    EXPECT_STREQ("", stringFromBuffer(NULL, 5).c_str());
+    EXPECT_STREQ("", stringFromBuffer({}).c_str());
 }
 
 TEST(StringInputTest, ZeroBytesInput)
 {
     // Verify that if the input length is 0 that it'll return the empty string.
-
-    const char* request = "asdf";
-    EXPECT_STREQ("", stringFromBuffer(request, 0).c_str());
+    const std::string request = "asdf";
+    EXPECT_STREQ("", stringFromBuffer(
+                         std::vector<uint8_t>(request.begin(), request.end()))
+                         .c_str());
 }
 
 TEST(StringInputTest, NulTerminatorNotFound)
 {
     // Verify that if there isn't a nul-terminator found in an otherwise valid
     // string, it'll return the emptry string.
-
-    char request[MAX_IPMI_BUFFER];
-    std::memset(request, 'a', sizeof(request));
-    EXPECT_STREQ("", stringFromBuffer(request, sizeof(request)).c_str());
-}
-
-TEST(StringInputTest, TwoNulsFound)
-{
-    // Verify it makes you use the entire data region for the string.
-    char request[MAX_IPMI_BUFFER];
-    request[0] = 'a';
-    request[1] = 0;
-    std::memset(&request[2], 'b', sizeof(request) - 2);
-    request[MAX_IPMI_BUFFER - 1] = 0;
-
-    // This case has two strings, and the last character is a nul-terminator.
-    EXPECT_STREQ("", stringFromBuffer(request, sizeof(request)).c_str());
+    std::array<char, MAX_IPMI_BUFFER> request;
+    std::memset(request.data(), 'a', sizeof(request));
+    EXPECT_STREQ("", stringFromBuffer(
+                         std::vector<uint8_t>(request.begin(), request.end()))
+                         .c_str());
 }
 
 TEST(StringInputTest, NulTerminatorFound)
 {
     // Verify that if it's provided a valid nul-terminated string, it'll
     // return it.
-
-    const char* request = "asdf";
-    EXPECT_STREQ("asdf", stringFromBuffer(request, 5).c_str());
+    std::string request = "asdf";
+    request.push_back('\0');
+    EXPECT_STREQ("asdf", stringFromBuffer(std::vector<uint8_t>(request.begin(),
+                                                               request.end()))
+                             .c_str());
 }
 } // namespace blobs
diff --git a/test/ipmi_write_unittest.cpp b/test/ipmi_write_unittest.cpp
index 229ebd3..12cf1bc 100644
--- a/test/ipmi_write_unittest.cpp
+++ b/test/ipmi_write_unittest.cpp
@@ -1,47 +1,38 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 
 #include <cstring>
 
 #include <gtest/gtest.h>
-
 namespace blobs
 {
 
 using ::testing::ElementsAreArray;
 using ::testing::Return;
 
-// ipmid.hpp isn't installed where we can grab it and this value is per BMC
-// SoC.
-#define MAX_IPMI_BUFFER 64
-
 TEST(BlobWriteTest, ManagerReturnsFailureReturnsFailure)
 {
     // This verifies a failure from the manager is passed back.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobWriteTx req;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
 
-    uint8_t expectedBytes[2] = {0x66, 0x67};
-    std::memcpy(req + 1, &expectedBytes[0], sizeof(expectedBytes));
+    request.resize(sizeof(struct BmcBlobWriteTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
 
-    dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes);
+    std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
+    request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
 
-    EXPECT_CALL(mgr,
-                write(req->sessionId, req->offset,
-                      ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+    EXPECT_CALL(
+        mgr, write(req.sessionId, req.offset, ElementsAreArray(expectedBytes)))
         .WillOnce(Return(false));
 
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              writeBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), writeBlob(&mgr, request));
 }
 
 TEST(BlobWriteTest, ManagerReturnsTrueWriteSucceeds)
@@ -49,26 +40,24 @@
     // The case where everything works.
 
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobWriteTx req;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
 
-    uint8_t expectedBytes[2] = {0x66, 0x67};
-    std::memcpy(req + 1, &expectedBytes[0], sizeof(expectedBytes));
+    request.resize(sizeof(struct BmcBlobWriteTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
 
-    dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes);
+    std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
+    request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
 
-    EXPECT_CALL(mgr,
-                write(req->sessionId, req->offset,
-                      ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+    EXPECT_CALL(
+        mgr, write(req.sessionId, req.offset, ElementsAreArray(expectedBytes)))
         .WillOnce(Return(true));
 
-    EXPECT_EQ(IPMI_CC_OK, writeBlob(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>{}),
+              writeBlob(&mgr, request));
 }
 } // namespace blobs
diff --git a/test/ipmi_writemeta_unittest.cpp b/test/ipmi_writemeta_unittest.cpp
index dd9e742..bc8669d 100644
--- a/test/ipmi_writemeta_unittest.cpp
+++ b/test/ipmi_writemeta_unittest.cpp
@@ -17,57 +17,49 @@
 TEST(BlobWriteMetaTest, ManagerReturnsFailureReturnsFailure)
 {
     // This verifies a failure from the manager is passed back.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobWriteMetaTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobWriteMetaTx req;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
 
-    uint8_t expectedBytes[2] = {0x66, 0x67};
-    std::memcpy(req + 1, &expectedBytes[0], sizeof(expectedBytes));
+    request.resize(sizeof(struct BmcBlobWriteMetaTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteMetaTx));
 
-    dataLen = sizeof(struct BmcBlobWriteMetaTx) + sizeof(expectedBytes);
+    std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
+    request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
 
-    EXPECT_CALL(
-        mgr, writeMeta(req->sessionId, req->offset,
-                       ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+    EXPECT_CALL(mgr, writeMeta(req.sessionId, req.offset,
+                               ElementsAreArray(expectedBytes)))
         .WillOnce(Return(false));
 
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              writeMeta(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), writeMeta(&mgr, request));
 }
 
 TEST(BlobWriteMetaTest, ManagerReturnsTrueWriteSucceeds)
 {
     // The case where everything works.
-
     ManagerMock mgr;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-    auto req = reinterpret_cast<struct BmcBlobWriteMetaTx*>(request);
+    std::vector<uint8_t> request;
+    struct BmcBlobWriteMetaTx req;
 
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite);
-    req->crc = 0;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
+    req.crc = 0;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
 
-    uint8_t expectedBytes[2] = {0x66, 0x67};
-    std::memcpy(req + 1, &expectedBytes[0], sizeof(expectedBytes));
+    request.resize(sizeof(struct BmcBlobWriteMetaTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteMetaTx));
 
-    dataLen = sizeof(struct BmcBlobWriteMetaTx) + sizeof(expectedBytes);
+    std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
+    request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
 
-    EXPECT_CALL(
-        mgr, writeMeta(req->sessionId, req->offset,
-                       ElementsAreArray(expectedBytes, sizeof(expectedBytes))))
+    EXPECT_CALL(mgr, writeMeta(req.sessionId, req.offset,
+                               ElementsAreArray(expectedBytes)))
         .WillOnce(Return(true));
 
-    EXPECT_EQ(IPMI_CC_OK, writeMeta(&mgr, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>{}),
+              writeMeta(&mgr, request));
 }
 } // namespace blobs
diff --git a/test/meson.build b/test/meson.build
index db240b3..b5bdd99 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -37,6 +37,7 @@
     executable(
       t.underscorify(),
       t + '.cpp',
+      'helper.cpp',
       implicit_include_directories: false,
       dependencies: [blob_manager_dep, gtest, gmock]))
 endforeach
diff --git a/test/process_unittest.cpp b/test/process_unittest.cpp
index b72fe9a..02014d4 100644
--- a/test/process_unittest.cpp
+++ b/test/process_unittest.cpp
@@ -1,9 +1,11 @@
+#include "helper.hpp"
 #include "ipmi.hpp"
 #include "manager_mock.hpp"
 #include "process.hpp"
 
 #include <cstring>
 #include <ipmiblob/test/crc_mock.hpp>
+#include <span>
 
 #include <gtest/gtest.h>
 
@@ -12,6 +14,7 @@
 #define MAX_IPMI_BUFFER 64
 
 using ::testing::_;
+using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::Return;
 using ::testing::StrictMock;
@@ -36,15 +39,11 @@
     EXPECT_FALSE(lhs == nullptr);
     EXPECT_FALSE(rhs == nullptr);
 
-    ipmi_ret_t (*const* lPtr)(ManagerInterface*, const uint8_t*, uint8_t*,
-                              size_t*) =
-        lhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*,
-                                  size_t*)>();
+    Resp (*const* lPtr)(ManagerInterface*, std::span<const uint8_t>) =
+        lhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_t>)>();
 
-    ipmi_ret_t (*const* rPtr)(ManagerInterface*, const uint8_t*, uint8_t*,
-                              size_t*) =
-        rhs.target<ipmi_ret_t (*)(ManagerInterface*, const uint8_t*, uint8_t*,
-                                  size_t*)>();
+    Resp (*const* rPtr)(ManagerInterface*, std::span<const uint8_t>) =
+        rhs.target<Resp (*)(ManagerInterface*, std::span<const uint8_t>)>();
 
     EXPECT_TRUE(lPtr);
     EXPECT_TRUE(rPtr);
@@ -67,34 +66,20 @@
 TEST_F(ValidateBlobCommandTest, InvalidCommandReturnsFailure)
 {
     // Verify we handle an invalid command.
-
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-
-    request[0] = 0xff;         // There is no command 0xff.
-    dataLen = sizeof(uint8_t); // There is no payload for CRC.
-    ipmi_ret_t rc;
-
-    EXPECT_EQ(nullptr, validateBlobCommand(request, reply, &dataLen, &rc));
-    EXPECT_EQ(IPMI_CC_INVALID_FIELD_REQUEST, rc);
+    std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
+    // There is no command 0xff.
+    IpmiBlobHandler handler = validateBlobCommand(0xff, request);
+    EXPECT_EQ(ipmi::responseInvalidFieldRequest(), handler(nullptr, {}));
 }
 
 TEST_F(ValidateBlobCommandTest, ValidCommandWithoutPayload)
 {
     // Verify we handle a valid command that doesn't have a payload.
-
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-
-    request[0] = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount);
-    dataLen = sizeof(uint8_t); // There is no payload for CRC.
-    ipmi_ret_t rc;
-
-    IpmiBlobHandler res = validateBlobCommand(request, reply, &dataLen, &rc);
-    EXPECT_FALSE(res == nullptr);
-    EqualFunctions(getBlobCount, res);
+    std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
+    IpmiBlobHandler handler = validateBlobCommand(
+        static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request);
+    EXPECT_FALSE(handler == nullptr);
+    EqualFunctions(getBlobCount, handler);
 }
 
 TEST_F(ValidateBlobCommandTest, WithPayloadMinimumLengthIs3VerifyChecks)
@@ -102,76 +87,61 @@
     // Verify that if there's a payload, it's at least one command byte and
     // two bytes for the crc16 and then one data byte.
 
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-
-    request[0] = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount);
-    dataLen = sizeof(uint8_t) + sizeof(uint16_t);
+    std::vector<uint8_t> request(sizeof(uint16_t));
     // There is a payload, but there are insufficient bytes.
-    ipmi_ret_t rc;
 
-    EXPECT_EQ(nullptr, validateBlobCommand(request, reply, &dataLen, &rc));
-    EXPECT_EQ(IPMI_CC_REQ_DATA_LEN_INVALID, rc);
+    IpmiBlobHandler handler = validateBlobCommand(
+        static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobGetCount), request);
+    EXPECT_EQ(ipmi::responseReqDataLenInvalid(), handler(nullptr, {}));
 }
 
 TEST_F(ValidateBlobCommandTest, WithPayloadAndInvalidCrc)
 {
     // Verify that the CRC is checked, and failure is reported.
+    std::vector<uint8_t> request;
+    BmcBlobWriteTx req;
+    req.crc = 0x34;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
 
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-
-    auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request);
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite);
-    req->crc = 0x34;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
-
-    uint8_t expectedBytes[2] = {0x66, 0x67};
-    std::memcpy(req + 1, &expectedBytes[0], sizeof(expectedBytes));
-
-    dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes);
+    std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
+    request.resize(sizeof(struct BmcBlobWriteTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
+    request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
 
     // skip over cmd and crc.
-    std::vector<std::uint8_t> bytes(&request[3], request + dataLen);
+    std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc),
+                               request.end());
     EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x1234));
 
-    ipmi_ret_t rc;
-
-    EXPECT_EQ(nullptr, validateBlobCommand(request, reply, &dataLen, &rc));
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR, rc);
+    IpmiBlobHandler handler = validateBlobCommand(
+        static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request);
+    EXPECT_EQ(ipmi::responseUnspecifiedError(), handler(nullptr, {}));
 }
 
 TEST_F(ValidateBlobCommandTest, WithPayloadAndValidCrc)
 {
     // Verify the CRC is checked and if it matches, return the handler.
+    std::vector<uint8_t> request;
+    BmcBlobWriteTx req;
+    req.crc = 0x3412;
+    req.sessionId = 0x54;
+    req.offset = 0x100;
 
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
-
-    auto req = reinterpret_cast<struct BmcBlobWriteTx*>(request);
-    req->cmd = static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite);
-    req->crc = 0x3412;
-    req->sessionId = 0x54;
-    req->offset = 0x100;
-
-    uint8_t expectedBytes[2] = {0x66, 0x67};
-    std::memcpy(req + 1, &expectedBytes[0], sizeof(expectedBytes));
-
-    dataLen = sizeof(struct BmcBlobWriteTx) + sizeof(expectedBytes);
+    std::array<uint8_t, 2> expectedBytes = {0x66, 0x67};
+    request.resize(sizeof(struct BmcBlobWriteTx));
+    std::memcpy(request.data(), &req, sizeof(struct BmcBlobWriteTx));
+    request.insert(request.end(), expectedBytes.begin(), expectedBytes.end());
 
     // skip over cmd and crc.
-    std::vector<std::uint8_t> bytes(&request[3], request + dataLen);
+    std::vector<uint8_t> bytes(request.begin() + sizeof(req.crc),
+                               request.end());
     EXPECT_CALL(crcMock, generateCrc(Eq(bytes))).WillOnce(Return(0x3412));
 
-    ipmi_ret_t rc;
-
-    IpmiBlobHandler res = validateBlobCommand(request, reply, &dataLen, &rc);
-    EXPECT_FALSE(res == nullptr);
-    EqualFunctions(writeBlob, res);
+    IpmiBlobHandler handler = validateBlobCommand(
+        static_cast<std::uint8_t>(BlobOEMCommands::bmcBlobWrite), request);
+    EXPECT_FALSE(handler == nullptr);
+    EqualFunctions(writeBlob, handler);
 }
 
 class ProcessBlobCommandTest : public ::testing::Test
@@ -191,17 +161,14 @@
     // noticed and returned.
 
     StrictMock<ManagerMock> manager;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
 
-    IpmiBlobHandler h = [](ManagerInterface*, const uint8_t*, uint8_t*,
-                           size_t*) { return IPMI_CC_INVALID; };
+    IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
+        return ipmi::responseInvalidCommand();
+    };
 
-    dataLen = sizeof(request);
-
-    EXPECT_EQ(IPMI_CC_INVALID,
-              processBlobCommand(h, &manager, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseInvalidCommand(),
+              processBlobCommand(h, &manager, request));
 }
 
 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithNoPayload)
@@ -210,20 +177,14 @@
     // it doesn't try to compute a CRC.
 
     StrictMock<ManagerMock> manager;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
 
-    IpmiBlobHandler h = [](ManagerInterface*, const uint8_t*, uint8_t*,
-                           size_t* dataLen) {
-        (*dataLen) = 0;
-        return IPMI_CC_OK;
+    IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
+        return ipmi::responseSuccess(std::vector<uint8_t>());
     };
 
-    dataLen = sizeof(request);
-
-    EXPECT_EQ(IPMI_CC_OK,
-              processBlobCommand(h, &manager, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseSuccess(std::vector<uint8_t>()),
+              processBlobCommand(h, &manager, request));
 }
 
 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithInvalidPayloadLength)
@@ -232,20 +193,14 @@
     // read), this returns 1.
 
     StrictMock<ManagerMock> manager;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
 
-    IpmiBlobHandler h = [](ManagerInterface*, const uint8_t*, uint8_t*,
-                           size_t* dataLen) {
-        (*dataLen) = sizeof(uint8_t);
-        return IPMI_CC_OK;
+    IpmiBlobHandler h = [](ManagerInterface*, std::span<const uint8_t>) {
+        return ipmi::responseSuccess(std::vector<uint8_t>(1));
     };
 
-    dataLen = sizeof(request);
-
-    EXPECT_EQ(IPMI_CC_UNSPECIFIED_ERROR,
-              processBlobCommand(h, &manager, request, reply, &dataLen));
+    EXPECT_EQ(ipmi::responseUnspecifiedError(),
+              processBlobCommand(h, &manager, request));
 }
 
 TEST_F(ProcessBlobCommandTest, CommandReturnsOkWithValidPayloadLength)
@@ -254,27 +209,22 @@
     // payload of 3 bytes and the crc code is called to process the payload.
 
     StrictMock<ManagerMock> manager;
-    size_t dataLen;
-    uint8_t request[MAX_IPMI_BUFFER] = {0};
-    uint8_t reply[MAX_IPMI_BUFFER] = {0};
+    std::vector<uint8_t> request(MAX_IPMI_BUFFER - 1);
     uint32_t payloadLen = sizeof(uint16_t) + sizeof(uint8_t);
 
-    IpmiBlobHandler h = [payloadLen](ManagerInterface*, const uint8_t*,
-                                     uint8_t* replyCmdBuf, size_t* dataLen) {
-        (*dataLen) = payloadLen;
-        replyCmdBuf[2] = 0x56;
-        return IPMI_CC_OK;
+    IpmiBlobHandler h = [payloadLen](ManagerInterface*,
+                                     std::span<const uint8_t>) {
+        std::vector<uint8_t> output(payloadLen, 0);
+        output[2] = 0x56;
+        return ipmi::responseSuccess(output);
     };
 
-    dataLen = sizeof(request);
-
     EXPECT_CALL(crcMock, generateCrc(_)).WillOnce(Return(0x3412));
 
-    EXPECT_EQ(IPMI_CC_OK,
-              processBlobCommand(h, &manager, request, reply, &dataLen));
-    EXPECT_EQ(dataLen, payloadLen);
+    auto result = validateReply(processBlobCommand(h, &manager, request));
 
-    uint8_t expectedBytes[3] = {0x12, 0x34, 0x56};
-    EXPECT_EQ(0, std::memcmp(expectedBytes, reply, sizeof(expectedBytes)));
+    EXPECT_EQ(result.size(), payloadLen);
+    EXPECT_THAT(result, ElementsAre(0x12, 0x34, 0x56));
 }
+
 } // namespace blobs