RAKP Message 3 & RAKP Message 4 Implementation

Change-Id: I0206a04fec2531e5c5dfee8677d4a2b6942022f7
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/command/rakp34.cpp b/command/rakp34.cpp
new file mode 100644
index 0000000..aa7ad3b
--- /dev/null
+++ b/command/rakp34.cpp
@@ -0,0 +1,217 @@
+#include "rakp34.hpp"
+
+#include <algorithm>
+#include <cstring>
+#include <iostream>
+
+#include "comm_module.hpp"
+#include "endian.hpp"
+#include "guid.hpp"
+#include "main.hpp"
+
+namespace command
+{
+
+std::vector<uint8_t> RAKP34(std::vector<uint8_t>& inPayload,
+                            const message::Handler& handler)
+{
+    std::cout << ">> RAKP34\n";
+
+    std::vector<uint8_t> outPayload(sizeof(RAKP4response));
+    auto request = reinterpret_cast<RAKP3request*>(inPayload.data());
+    auto response = reinterpret_cast<RAKP4response*>(outPayload.data());
+
+    // Check if the RAKP3 Payload Length is as expected
+    if(inPayload.size() != sizeof(RAKP3request))
+    {
+        std::cerr << "RAKP34: Invalid RAKP3 request\n";
+        response->rmcpStatusCode =
+            static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
+        return outPayload;
+    }
+
+    // Session ID zero is reserved for Session Setup
+    if(endian::from_ipmi(request->managedSystemSessionID) ==
+                         session::SESSION_ZERO)
+    {
+        std::cerr << "RAKP34: 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;
+    }
+
+    session->updateLastTransactionTime();
+
+    auto authAlgo = session->getAuthAlgo();
+    /*
+     * Key Authentication Code - RAKP 3
+     *
+     * 1) Managed System Random Number - 16 bytes
+     * 2) Remote Console Session ID - 4 bytes
+     * 3) Session Privilege Level - 1 byte
+     * 4) User Name Length Byte - 1 byte (0 for 'null' username)
+     * 5) User Name - variable (absent for 'null' username)
+     */
+
+    // Remote Console Session ID
+    auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
+
+    // Session Privilege Level
+    auto sessPrivLevel = static_cast<uint8_t>(session->curPrivLevel);
+
+    // User Name Length Byte
+    uint8_t userLength = 0;
+
+    std::vector<uint8_t> input;
+    input.resize(cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
+                 sizeof(rcSessionID) + sizeof(sessPrivLevel) +
+                 sizeof(userLength));
+
+    auto iter = input.begin();
+
+    // Managed System Random Number
+    std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
+              iter);
+    std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
+
+    // Remote Console Session ID
+    std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
+                iter);
+    std::advance(iter, sizeof(rcSessionID));
+
+    // Session Privilege Level
+    std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
+                sizeof(sessPrivLevel), iter);
+    std::advance(iter, sizeof(sessPrivLevel));
+
+    // User Name Length Byte
+    std::copy_n(&userLength, sizeof(userLength), iter);
+
+    // Generate Key Exchange Authentication Code - RAKP2
+    auto output = authAlgo->generateHMAC(input);
+
+    if (std::memcmp(output.data(), request->keyExchangeAuthCode,
+                    output.size()))
+    {
+        std::cerr << "Mismatch in HMAC sent by remote console\n";
+
+        response->messageTag = request->messageTag;
+        response->rmcpStatusCode = static_cast<uint8_t>
+                                   (RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
+        response->reserved = 0;
+        response->remoteConsoleSessionID = rcSessionID;
+
+        //close the session
+        std::get<session::Manager&>(singletonPool).stopSession(
+            session->getBMCSessionID());
+
+        return outPayload;
+    }
+
+    /*
+     * Session Integrity Key
+     *
+     * 1) Remote Console Random Number - 16 bytes
+     * 2) Managed System Random Number - 16 bytes
+     * 3) Session Privilege Level - 1 byte
+     * 4) User Name Length Byte - 1 byte (0 for 'null' username)
+     * 5) User Name - variable (absent for 'null' username)
+     */
+
+    input.clear();
+
+    input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
+                 cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
+                 sizeof(sessPrivLevel) + sizeof(userLength));
+    iter = input.begin();
+
+    // Remote Console Random Number
+    std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(),
+              iter);
+    std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
+
+    // Managed Console Random Number
+    std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
+              iter);
+    std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
+
+    // Session Privilege Level
+    std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
+                sizeof(sessPrivLevel), iter);
+    std::advance(iter, sizeof(sessPrivLevel));
+
+    // User Name Length Byte
+    std::copy_n(&userLength, sizeof(userLength), iter);
+
+    // Generate Session Integrity Key
+    auto sikOutput = authAlgo->generateHMAC(input);
+
+    // Update the SIK in the Authentication Algo Interface
+    authAlgo->sessionIntegrityKey.insert(authAlgo->sessionIntegrityKey.begin(),
+                                         sikOutput.begin(), sikOutput.end());
+
+    /*
+     * Integrity Check Value
+     *
+     * 1) Remote Console Random Number - 16 bytes
+     * 2) Managed System Session ID - 4 bytes
+     * 3) Managed System GUID - 16 bytes
+     */
+
+    // Get Managed System Session ID
+    auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
+
+    input.clear();
+
+    input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
+                 sizeof(bmcSessionID) + BMC_GUID_LEN);
+    iter = input.begin();
+
+    // Remote Console Random Number
+    std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(),
+              iter);
+    std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
+
+    // Managed System Session ID
+    std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
+                iter);
+    std::advance(iter, sizeof(bmcSessionID));
+
+    // Managed System GUID
+    auto guid = getSystemGUID();
+    std::copy_n(guid.data(), guid.size(), iter);
+
+    // Integrity Check Value
+    auto icv = authAlgo->generateICV(input);
+
+    outPayload.resize(sizeof(RAKP4response));
+
+    response->messageTag = request->messageTag;
+    response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
+    response->reserved = 0;
+    response->remoteConsoleSessionID = rcSessionID;
+
+    // Insert the HMAC output into the payload
+    outPayload.insert(outPayload.end(), icv.begin(), icv.end());
+
+    session->state = session::State::ACTIVE;
+
+    std::cout << "<< RAKP34\n";
+    return outPayload;
+}
+
+} // namespace command
diff --git a/command/rakp34.hpp b/command/rakp34.hpp
new file mode 100644
index 0000000..e3d82ff
--- /dev/null
+++ b/command/rakp34.hpp
@@ -0,0 +1,56 @@
+#pragma once
+
+#include <vector>
+
+#include "message_handler.hpp"
+#include "comm_module.hpp"
+
+namespace command
+{
+
+/*
+ * @struct RAKP3request
+ *
+ * IPMI Payload for RAKP Message 3
+ */
+struct RAKP3request
+{
+    uint8_t messageTag;
+    uint8_t rmcpStatusCode;
+    uint16_t reserved;
+    uint32_t managedSystemSessionID;
+    uint8_t keyExchangeAuthCode[20];
+} __attribute__((packed));
+
+/*
+ * @struct RAKP4response
+ *
+ * IPMI Payload for RAKP Message 4
+ */
+struct RAKP4response
+{
+    uint8_t messageTag;
+    uint8_t rmcpStatusCode;
+    uint16_t reserved;
+    uint32_t remoteConsoleSessionID;
+} __attribute__((packed));
+
+/*
+ * @brief RAKP Message 3, RAKP Message 4
+ *
+ * The session activation process is completed by the remote console and BMC
+ * exchanging messages that are signed according to the Authentication Algorithm
+ * that was negotiated, and the parameters that were passed in the earlier
+ * messages. RAKP Message 3 is the signed message from the remote console to the
+ * BMC. After receiving RAKP Message 3, the BMC returns RAKP Message 4 - a
+ * signed message from BMC to the remote console.
+ *
+ * @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> RAKP34(std::vector<uint8_t>& inPayload,
+                            const message::Handler& handler);
+
+} // namespace command