Implement Get Channel Cipher Suites command

Change-Id: I8c808ce7e2bbd3ae5e225573138d7e04871f1643
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/app/channel.cpp b/app/channel.cpp
index 000c11e..3134683 100644
--- a/app/channel.cpp
+++ b/app/channel.cpp
@@ -4,6 +4,7 @@
 #include "utils.hpp"
 #include "net.hpp"
 
+#include <fstream>
 #include <string>
 #include <arpa/inet.h>
 
@@ -118,3 +119,141 @@
 
     return rc;
 }
+
+namespace cipher
+{
+
+/** @brief Get the supported Cipher records
+ *
+ * The cipher records are read from the JSON file and converted into cipher
+ * suite record format mentioned in the IPMI specification. The records can be
+ * either OEM or standard cipher. Each json entry is parsed and converted into
+ * the cipher record format and pushed into the vector.
+ *
+ * @return vector containing all the cipher suite records.
+ *
+ */
+std::vector<uint8_t> getCipherRecords()
+{
+    std::vector<uint8_t> records;
+
+    std::ifstream jsonFile(configFile);
+    if (!jsonFile.is_open())
+    {
+        log<level::ERR>("Channel Cipher suites file not found");
+        elog<InternalFailure>();
+    }
+
+    auto data = Json::parse(jsonFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        log<level::ERR>("Parsing channel cipher suites JSON failed");
+        elog<InternalFailure>();
+    }
+
+    for (const auto& record : data)
+    {
+        if (record.find(oem) != record.end())
+        {
+            // OEM cipher suite - 0xC1
+            records.push_back(oemCipherSuite);
+            // Cipher Suite ID
+            records.push_back(record.value(cipher, 0));
+            // OEM IANA - 3 bytes
+            records.push_back(record.value(oem, 0));
+            records.push_back(record.value(oem, 0) >> 8);
+            records.push_back(record.value(oem, 0) >> 16);
+
+        }
+        else
+        {
+            // OEM cipher suite - 0xC0
+            records.push_back(stdCipherSuite);
+            // Cipher Suite ID
+            records.push_back(record.value(cipher, 0));
+        }
+
+        // Authentication algorithm number
+        records.push_back(record.value(auth, 0));
+        // Integrity algorithm number
+        records.push_back(record.value(integrity, 0) | integrityTag);
+        // Confidentiality algorithm number
+        records.push_back(record.value(conf, 0) | confTag);
+    }
+
+    return records;
+}
+
+} //namespace cipher
+
+ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn,
+                                  ipmi_cmd_t cmd,
+                                  ipmi_request_t request,
+                                  ipmi_response_t response,
+                                  ipmi_data_len_t data_len,
+                                  ipmi_context_t context)
+{
+    static std::vector<uint8_t> records;
+    static auto recordInit = false;
+
+    auto requestData =
+        reinterpret_cast<const GetChannelCipherRequest*>(request);
+
+
+    if (*data_len < sizeof(GetChannelCipherRequest))
+    {
+        *data_len = 0;
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    *data_len = 0;
+
+    // Support only for list algorithms by cipher suite
+    if (cipher::listCipherSuite !=
+            (requestData->listIndex & cipher::listTypeMask))
+    {
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    if (!recordInit)
+    {
+        try
+        {
+            records = cipher::getCipherRecords();
+            recordInit = true;
+        }
+        catch (const std::exception &e)
+        {
+            return IPMI_CC_UNSPECIFIED_ERROR;
+        }
+    }
+
+    // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next
+    // set of 16 and so on.
+    auto index = static_cast<size_t>(
+            requestData->listIndex & cipher::listIndexMask);
+
+    // Calculate the number of record data bytes to be returned.
+    auto start = std::min(index * cipher::respSize, records.size());
+    auto end = std::min((index * cipher::respSize) + cipher::respSize,
+                        records.size());
+    auto size = end - start;
+
+    auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>
+            (response);
+    responseData->channelNumber = cipher::defaultChannelNumber;
+
+    if (!size)
+    {
+        *data_len = sizeof(GetChannelCipherRespHeader);
+    }
+    else
+    {
+        std::copy_n(records.data() + start,
+                    size,
+                    static_cast<uint8_t*>(response) + 1);
+        *data_len = size + sizeof(GetChannelCipherRespHeader);
+    }
+
+    return IPMI_CC_OK;
+}