RAKP Message 1 & RAKP Message 2 Implementation

Change-Id: Ibe7309651d022de6bd6c7b561493fd085d0e27c0
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/command/rakp12.cpp b/command/rakp12.cpp
new file mode 100644
index 0000000..1249d80
--- /dev/null
+++ b/command/rakp12.cpp
@@ -0,0 +1,156 @@
+#include "rakp12.hpp"
+
+#include <openssl/rand.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+
+#include "comm_module.hpp"
+#include "endian.hpp"
+#include "guid.hpp"
+#include "main.hpp"
+
+namespace command
+{
+
+std::vector<uint8_t> RAKP12(std::vector<uint8_t>& inPayload,
+                            const message::Handler& handler)
+{
+    std::cout << ">> RAKP12\n";
+
+    std::vector<uint8_t> outPayload(sizeof(RAKP2response));
+    auto request = reinterpret_cast<RAKP1request*>(inPayload.data());
+    auto response = reinterpret_cast<RAKP2response*>(outPayload.data());
+
+    // Session ID zero is reserved for Session Setup
+    if(endian::from_ipmi(request->managedSystemSessionID) ==
+                         session::SESSION_ZERO)
+    {
+        std::cerr << "RAKP12: BMC invalid Session ID\n";
+        response->rmcpStatusCode =
+            static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
+        return outPayload;
+    }
+
+    std::shared_ptr<session::Session> session;
+    try
+    {
+        session = (std::get<session::Manager&>(singletonPool).getSession(
+            endian::from_ipmi(request->managedSystemSessionID))).lock();
+    }
+    catch (std::exception& e)
+    {
+        std::cerr << e.what() << "\n";
+        response->rmcpStatusCode =
+            static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
+        return outPayload;
+    }
+
+    // Update transaction time
+    session->updateLastTransactionTime();
+
+    auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
+    auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
+    auto authAlgo = session->getAuthAlgo();
+
+    /*
+     * Generate Key Authentication Code - RAKP 2
+     *
+     * 1) Remote Console Session ID - 4 bytes
+     * 2) Managed System Session ID - 4 bytes
+     * 3) Remote Console Random Number - 16 bytes
+     * 4) Managed System Random Number - 16 bytes
+     * 5) Managed System GUID - 16 bytes
+     * 6) Requested Privilege Level - 1 byte
+     * 7) User Name Length Byte - 1 byte (0 for 'null' username)
+     * 8) User Name - variable (absent for 'null' username)
+     */
+
+    std::vector<uint8_t> input;
+    input.resize(sizeof(rcSessionID) + sizeof(bmcSessionID) +
+                 cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
+                 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
+                 BMC_GUID_LEN + sizeof(request->req_max_privilege_level) +
+                 sizeof(request->user_name_len));
+
+    auto iter = input.begin();
+
+    // Remote Console Session ID
+    std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID),
+                sizeof(rcSessionID), iter);
+    std::advance(iter, sizeof(rcSessionID));
+
+    // Managed System Session ID
+    std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
+                iter);
+    std::advance(iter, sizeof(bmcSessionID));
+
+    // Copy the Remote Console Random Number from the RAKP1 request to the
+    // Authentication Algorithm
+    std::copy_n(reinterpret_cast<uint8_t*>
+                (request->remote_console_random_number),
+                cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
+                authAlgo->rcRandomNum.begin());
+
+    std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(),
+              iter);
+    std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
+
+    // Generate the Managed System Random Number
+    if (!RAND_bytes(input.data() + sizeof(rcSessionID) + sizeof(bmcSessionID) +
+                    cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN,
+                    cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN))
+    {
+        response->rmcpStatusCode =
+            static_cast<uint8_t>(RAKP_ReturnCode::INSUFFICIENT_RESOURCE);
+        return outPayload;
+    }
+
+    // Copy the Managed System Random Number to the Authentication Algorithm
+    std::copy_n(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN,
+                authAlgo->bmcRandomNum.begin());
+    std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
+
+    // Managed System GUID
+    auto guid = getSystemGUID();
+    std::copy_n(guid.data(), guid.size(), iter);
+    std::advance(iter, BMC_GUID_LEN);
+
+    // Requested Privilege Level
+    session->curPrivLevel = static_cast<session::Privilege>
+                            (request->req_max_privilege_level);
+    std::copy_n(&(request->req_max_privilege_level),
+                sizeof(request->req_max_privilege_level), iter);
+    std::advance(iter, sizeof(request->req_max_privilege_level));
+
+    // Set Max Privilege to ADMIN
+    session->maxPrivLevel = session::Privilege::ADMIN;
+
+    // User Name Length Byte
+    std::copy_n(&(request->user_name_len), sizeof(request->user_name_len),
+                iter);
+
+    // Generate Key Exchange Authentication Code - RAKP2
+    auto output = authAlgo->generateHMAC(input);
+
+    response->messageTag = request->messageTag;
+    response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
+    response->reserved = 0;
+    response->remoteConsoleSessionID = rcSessionID ;
+
+    // Copy Managed System Random Number to the Response
+    std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
+              response->managed_system_random_number);
+
+    // Copy System GUID to the Response
+    std::copy_n(guid.data(), guid.size(), response->managed_system_guid);
+
+    // Insert the HMAC output into the payload
+    outPayload.insert(outPayload.end(), output.begin(), output.end());
+
+    std::cout << "<< RAKP12\n";
+    return outPayload;
+}
+
+} // namespace command
diff --git a/command/rakp12.hpp b/command/rakp12.hpp
new file mode 100644
index 0000000..f68ec47
--- /dev/null
+++ b/command/rakp12.hpp
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <vector>
+
+#include "message_handler.hpp"
+#include "comm_module.hpp"
+
+namespace command
+{
+
+/*
+ * @struct RAKP1request
+ *
+ * IPMI Payload for RAKP Message 1
+ */
+struct RAKP1request
+{
+    uint8_t messageTag;
+    uint8_t reserved1;
+    uint16_t reserved2;
+    uint32_t managedSystemSessionID;
+    uint8_t remote_console_random_number[16];
+    uint8_t req_max_privilege_level;
+    uint16_t reserved3;
+    uint8_t user_name_len;
+    char user_name[16];
+} __attribute__((packed));
+
+/*
+ * @struct RAKP2response
+ *
+ * IPMI Payload for RAKP Message 2
+ */
+struct RAKP2response
+{
+    uint8_t messageTag;
+    uint8_t rmcpStatusCode;
+    uint16_t reserved;
+    uint32_t remoteConsoleSessionID;
+    uint8_t managed_system_random_number[16];
+    uint8_t managed_system_guid[16];
+} __attribute__((packed));
+
+/*
+ * @brief RAKP Message 1, RAKP Message 2
+ *
+ * These messages are used to exchange random number and identification
+ * information between the BMC and the remote console that are, in effect,
+ * mutual challenges for a challenge/response. (Unlike IPMI v1.5, the v2.0/RMCP+
+ * challenge/response is symmetric. I.e. the remote console and BMC both issues
+ * challenges,and both need to provide valid responses for the session to be
+ * activated.)
+ *
+ * The remote console request (RAKP Message 1) passes a random number and
+ * username/privilege information that the BMC will later use to ‘sign’ a
+ * response message based on key information associated with the user and the
+ * Authentication Algorithm negotiated in the Open Session Request/Response
+ * exchange. The BMC responds with RAKP Message 2 and passes a random number and
+ * GUID (globally unique ID) for the managed system that the remote console
+ * uses according the Authentication Algorithm to sign a response back to the
+ * BMC.
+ *
+ * @param[in] inPayload - Request Data for the command
+ * @param[in] handler - Reference to the Message Handler
+ *
+ * @return Response data for the command
+ */
+std::vector<uint8_t> RAKP12(std::vector<uint8_t>& inPayload,
+                            const message::Handler& handler);
+
+} // namespace command