xref: /openbmc/phosphor-net-ipmid/command/rakp34.cpp (revision f8a34fc47183c553b9e78301e6f55170dbb7976d)
150fb50a9STom Joseph #include "rakp34.hpp"
250fb50a9STom Joseph 
350fb50a9STom Joseph #include "comm_module.hpp"
450fb50a9STom Joseph #include "endian.hpp"
550fb50a9STom Joseph #include "guid.hpp"
650fb50a9STom Joseph #include "main.hpp"
79b307be6SVernon Mauery #include "rmcp.hpp"
850fb50a9STom Joseph 
99e801a2bSVernon Mauery #include <algorithm>
109e801a2bSVernon Mauery #include <cstring>
11fc37e59eSVernon Mauery #include <phosphor-logging/log.hpp>
12fc37e59eSVernon Mauery 
13fc37e59eSVernon Mauery using namespace phosphor::logging;
149e801a2bSVernon Mauery 
1550fb50a9STom Joseph namespace command
1650fb50a9STom Joseph {
1750fb50a9STom Joseph 
18ef02fb3dSTom Joseph void applyIntegrityAlgo(const uint32_t bmcSessionID)
19ef02fb3dSTom Joseph {
209e801a2bSVernon Mauery     auto session =
21ae1fda44SVernon Mauery         std::get<session::Manager&>(singletonPool).getSession(bmcSessionID);
22ef02fb3dSTom Joseph 
23ef02fb3dSTom Joseph     auto authAlgo = session->getAuthAlgo();
24ef02fb3dSTom Joseph 
25ef02fb3dSTom Joseph     switch (authAlgo->intAlgo)
26ef02fb3dSTom Joseph     {
27ef02fb3dSTom Joseph         case cipher::integrity::Algorithms::HMAC_SHA1_96:
28ef02fb3dSTom Joseph         {
29ef02fb3dSTom Joseph             session->setIntegrityAlgo(
30ef02fb3dSTom Joseph                 std::make_unique<cipher::integrity::AlgoSHA1>(
31ef02fb3dSTom Joseph                     authAlgo->sessionIntegrityKey));
32ef02fb3dSTom Joseph             break;
33ef02fb3dSTom Joseph         }
347e9e2ef6SVernon Mauery         case cipher::integrity::Algorithms::HMAC_SHA256_128:
357e9e2ef6SVernon Mauery         {
367e9e2ef6SVernon Mauery             session->setIntegrityAlgo(
377e9e2ef6SVernon Mauery                 std::make_unique<cipher::integrity::AlgoSHA256>(
387e9e2ef6SVernon Mauery                     authAlgo->sessionIntegrityKey));
397e9e2ef6SVernon Mauery             break;
407e9e2ef6SVernon Mauery         }
41ef02fb3dSTom Joseph         default:
42ef02fb3dSTom Joseph             break;
43ef02fb3dSTom Joseph     }
44ef02fb3dSTom Joseph }
45ef02fb3dSTom Joseph 
464c766eb1STom Joseph void applyCryptAlgo(const uint32_t bmcSessionID)
474c766eb1STom Joseph {
489e801a2bSVernon Mauery     auto session =
49ae1fda44SVernon Mauery         std::get<session::Manager&>(singletonPool).getSession(bmcSessionID);
504c766eb1STom Joseph 
514c766eb1STom Joseph     auto authAlgo = session->getAuthAlgo();
524c766eb1STom Joseph 
534c766eb1STom Joseph     switch (authAlgo->cryptAlgo)
544c766eb1STom Joseph     {
554c766eb1STom Joseph         case cipher::crypt::Algorithms::AES_CBC_128:
564c766eb1STom Joseph         {
579b307be6SVernon Mauery             auto intAlgo = session->getIntegrityAlgo();
589e801a2bSVernon Mauery             auto k2 = intAlgo->generateKn(authAlgo->sessionIntegrityKey,
599e801a2bSVernon Mauery                                           rmcp::const_2);
609b307be6SVernon Mauery             session->setCryptAlgo(
619b307be6SVernon Mauery                 std::make_unique<cipher::crypt::AlgoAES128>(k2));
624c766eb1STom Joseph             break;
634c766eb1STom Joseph         }
644c766eb1STom Joseph         default:
654c766eb1STom Joseph             break;
664c766eb1STom Joseph     }
674c766eb1STom Joseph }
684c766eb1STom Joseph 
6918a45e9dSTom Joseph std::vector<uint8_t> RAKP34(const std::vector<uint8_t>& inPayload,
7050fb50a9STom Joseph                             const message::Handler& handler)
7150fb50a9STom Joseph {
7250fb50a9STom Joseph 
7350fb50a9STom Joseph     std::vector<uint8_t> outPayload(sizeof(RAKP4response));
7418a45e9dSTom Joseph     auto request = reinterpret_cast<const RAKP3request*>(inPayload.data());
7550fb50a9STom Joseph     auto response = reinterpret_cast<RAKP4response*>(outPayload.data());
7650fb50a9STom Joseph 
7750fb50a9STom Joseph     // Check if the RAKP3 Payload Length is as expected
789b307be6SVernon Mauery     if (inPayload.size() < sizeof(RAKP3request))
7950fb50a9STom Joseph     {
80fc37e59eSVernon Mauery         log<level::INFO>("RAKP34: Invalid RAKP3 request");
8150fb50a9STom Joseph         response->rmcpStatusCode =
8250fb50a9STom Joseph             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
8350fb50a9STom Joseph         return outPayload;
8450fb50a9STom Joseph     }
8550fb50a9STom Joseph 
8650fb50a9STom Joseph     // Session ID zero is reserved for Session Setup
8750fb50a9STom Joseph     if (endian::from_ipmi(request->managedSystemSessionID) ==
88*f8a34fc4SSuryakanth Sekar         session::sessionZero)
8950fb50a9STom Joseph     {
90fc37e59eSVernon Mauery         log<level::INFO>("RAKP34: BMC invalid Session ID");
9150fb50a9STom Joseph         response->rmcpStatusCode =
9250fb50a9STom Joseph             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
9350fb50a9STom Joseph         return outPayload;
9450fb50a9STom Joseph     }
9550fb50a9STom Joseph 
9650fb50a9STom Joseph     std::shared_ptr<session::Session> session;
9750fb50a9STom Joseph     try
9850fb50a9STom Joseph     {
99ae1fda44SVernon Mauery         session =
100ae1fda44SVernon Mauery             std::get<session::Manager&>(singletonPool)
101ae1fda44SVernon Mauery                 .getSession(endian::from_ipmi(request->managedSystemSessionID));
10250fb50a9STom Joseph     }
10350fb50a9STom Joseph     catch (std::exception& e)
10450fb50a9STom Joseph     {
105fc37e59eSVernon Mauery         log<level::ERR>("RAKP12 : session not found",
106fc37e59eSVernon Mauery                         entry("EXCEPTION=%s", e.what()));
10750fb50a9STom Joseph         response->rmcpStatusCode =
10850fb50a9STom Joseph             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
10950fb50a9STom Joseph         return outPayload;
11050fb50a9STom Joseph     }
11150fb50a9STom Joseph 
11250fb50a9STom Joseph     session->updateLastTransactionTime();
11350fb50a9STom Joseph 
11450fb50a9STom Joseph     auto authAlgo = session->getAuthAlgo();
11550fb50a9STom Joseph     /*
11650fb50a9STom Joseph      * Key Authentication Code - RAKP 3
11750fb50a9STom Joseph      *
11850fb50a9STom Joseph      * 1) Managed System Random Number - 16 bytes
11950fb50a9STom Joseph      * 2) Remote Console Session ID - 4 bytes
12050fb50a9STom Joseph      * 3) Session Privilege Level - 1 byte
12150fb50a9STom Joseph      * 4) User Name Length Byte - 1 byte (0 for 'null' username)
12250fb50a9STom Joseph      * 5) User Name - variable (absent for 'null' username)
12350fb50a9STom Joseph      */
12450fb50a9STom Joseph 
12550fb50a9STom Joseph     // Remote Console Session ID
12650fb50a9STom Joseph     auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
12750fb50a9STom Joseph 
12850fb50a9STom Joseph     // Session Privilege Level
1294021b1f7STom Joseph     auto sessPrivLevel = static_cast<uint8_t>(session->reqMaxPrivLevel);
13050fb50a9STom Joseph 
13150fb50a9STom Joseph     // User Name Length Byte
13256527b93STom Joseph     auto userLength = static_cast<uint8_t>(session->userName.size());
13350fb50a9STom Joseph 
13450fb50a9STom Joseph     std::vector<uint8_t> input;
13550fb50a9STom Joseph     input.resize(cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
13650fb50a9STom Joseph                  sizeof(rcSessionID) + sizeof(sessPrivLevel) +
13756527b93STom Joseph                  sizeof(userLength) + userLength);
13850fb50a9STom Joseph 
13950fb50a9STom Joseph     auto iter = input.begin();
14050fb50a9STom Joseph 
14150fb50a9STom Joseph     // Managed System Random Number
14250fb50a9STom Joseph     std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
14350fb50a9STom Joseph               iter);
14450fb50a9STom Joseph     std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
14550fb50a9STom Joseph 
14650fb50a9STom Joseph     // Remote Console Session ID
14750fb50a9STom Joseph     std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
14850fb50a9STom Joseph                 iter);
14950fb50a9STom Joseph     std::advance(iter, sizeof(rcSessionID));
15050fb50a9STom Joseph 
15150fb50a9STom Joseph     // Session Privilege Level
15250fb50a9STom Joseph     std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
15350fb50a9STom Joseph                 sizeof(sessPrivLevel), iter);
15450fb50a9STom Joseph     std::advance(iter, sizeof(sessPrivLevel));
15550fb50a9STom Joseph 
15650fb50a9STom Joseph     // User Name Length Byte
15750fb50a9STom Joseph     std::copy_n(&userLength, sizeof(userLength), iter);
15856527b93STom Joseph     std::advance(iter, sizeof(userLength));
15956527b93STom Joseph 
16056527b93STom Joseph     std::copy_n(session->userName.data(), userLength, iter);
16150fb50a9STom Joseph 
16250fb50a9STom Joseph     // Generate Key Exchange Authentication Code - RAKP2
16350fb50a9STom Joseph     auto output = authAlgo->generateHMAC(input);
16450fb50a9STom Joseph 
1659b307be6SVernon Mauery     if (inPayload.size() != (sizeof(RAKP3request) + output.size()) ||
1669b307be6SVernon Mauery         std::memcmp(output.data(), request + 1, output.size()))
16750fb50a9STom Joseph     {
168fc37e59eSVernon Mauery         log<level::INFO>("Mismatch in HMAC sent by remote console");
16950fb50a9STom Joseph 
17050fb50a9STom Joseph         response->messageTag = request->messageTag;
1719e801a2bSVernon Mauery         response->rmcpStatusCode =
1729e801a2bSVernon Mauery             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
17350fb50a9STom Joseph         response->reserved = 0;
17450fb50a9STom Joseph         response->remoteConsoleSessionID = rcSessionID;
17550fb50a9STom Joseph 
17650fb50a9STom Joseph         // close the session
1779e801a2bSVernon Mauery         std::get<session::Manager&>(singletonPool)
1789e801a2bSVernon Mauery             .stopSession(session->getBMCSessionID());
17950fb50a9STom Joseph 
18050fb50a9STom Joseph         return outPayload;
18150fb50a9STom Joseph     }
18250fb50a9STom Joseph 
18350fb50a9STom Joseph     /*
18450fb50a9STom Joseph      * Session Integrity Key
18550fb50a9STom Joseph      *
18650fb50a9STom Joseph      * 1) Remote Console Random Number - 16 bytes
18750fb50a9STom Joseph      * 2) Managed System Random Number - 16 bytes
18850fb50a9STom Joseph      * 3) Session Privilege Level - 1 byte
18950fb50a9STom Joseph      * 4) User Name Length Byte - 1 byte (0 for 'null' username)
19050fb50a9STom Joseph      * 5) User Name - variable (absent for 'null' username)
19150fb50a9STom Joseph      */
19250fb50a9STom Joseph 
19350fb50a9STom Joseph     input.clear();
19450fb50a9STom Joseph 
19550fb50a9STom Joseph     input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
19650fb50a9STom Joseph                  cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
19756527b93STom Joseph                  sizeof(sessPrivLevel) + sizeof(userLength) + userLength);
19850fb50a9STom Joseph     iter = input.begin();
19950fb50a9STom Joseph 
20050fb50a9STom Joseph     // Remote Console Random Number
2019e801a2bSVernon Mauery     std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
20250fb50a9STom Joseph     std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
20350fb50a9STom Joseph 
20450fb50a9STom Joseph     // Managed Console Random Number
20550fb50a9STom Joseph     std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
20650fb50a9STom Joseph               iter);
20750fb50a9STom Joseph     std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
20850fb50a9STom Joseph 
20950fb50a9STom Joseph     // Session Privilege Level
21050fb50a9STom Joseph     std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
21150fb50a9STom Joseph                 sizeof(sessPrivLevel), iter);
21250fb50a9STom Joseph     std::advance(iter, sizeof(sessPrivLevel));
21350fb50a9STom Joseph 
21450fb50a9STom Joseph     // User Name Length Byte
21550fb50a9STom Joseph     std::copy_n(&userLength, sizeof(userLength), iter);
21656527b93STom Joseph     std::advance(iter, sizeof(userLength));
21756527b93STom Joseph 
21856527b93STom Joseph     std::copy_n(session->userName.data(), userLength, iter);
21950fb50a9STom Joseph 
22050fb50a9STom Joseph     // Generate Session Integrity Key
22150fb50a9STom Joseph     auto sikOutput = authAlgo->generateHMAC(input);
22250fb50a9STom Joseph 
22350fb50a9STom Joseph     // Update the SIK in the Authentication Algo Interface
22450fb50a9STom Joseph     authAlgo->sessionIntegrityKey.insert(authAlgo->sessionIntegrityKey.begin(),
22550fb50a9STom Joseph                                          sikOutput.begin(), sikOutput.end());
22650fb50a9STom Joseph 
22750fb50a9STom Joseph     /*
22850fb50a9STom Joseph      * Integrity Check Value
22950fb50a9STom Joseph      *
23050fb50a9STom Joseph      * 1) Remote Console Random Number - 16 bytes
23150fb50a9STom Joseph      * 2) Managed System Session ID - 4 bytes
23250fb50a9STom Joseph      * 3) Managed System GUID - 16 bytes
23350fb50a9STom Joseph      */
23450fb50a9STom Joseph 
23550fb50a9STom Joseph     // Get Managed System Session ID
23650fb50a9STom Joseph     auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
23750fb50a9STom Joseph 
23850fb50a9STom Joseph     input.clear();
23950fb50a9STom Joseph 
24050fb50a9STom Joseph     input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
24150fb50a9STom Joseph                  sizeof(bmcSessionID) + BMC_GUID_LEN);
24250fb50a9STom Joseph     iter = input.begin();
24350fb50a9STom Joseph 
24450fb50a9STom Joseph     // Remote Console Random Number
2459e801a2bSVernon Mauery     std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(), iter);
24650fb50a9STom Joseph     std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
24750fb50a9STom Joseph 
24850fb50a9STom Joseph     // Managed System Session ID
24950fb50a9STom Joseph     std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
25050fb50a9STom Joseph                 iter);
25150fb50a9STom Joseph     std::advance(iter, sizeof(bmcSessionID));
25250fb50a9STom Joseph 
25350fb50a9STom Joseph     // Managed System GUID
25483029cb8STom Joseph     std::copy_n(cache::guid.data(), cache::guid.size(), iter);
25550fb50a9STom Joseph 
25650fb50a9STom Joseph     // Integrity Check Value
25750fb50a9STom Joseph     auto icv = authAlgo->generateICV(input);
25850fb50a9STom Joseph 
25950fb50a9STom Joseph     outPayload.resize(sizeof(RAKP4response));
26050fb50a9STom Joseph 
26150fb50a9STom Joseph     response->messageTag = request->messageTag;
26250fb50a9STom Joseph     response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
26350fb50a9STom Joseph     response->reserved = 0;
26450fb50a9STom Joseph     response->remoteConsoleSessionID = rcSessionID;
26550fb50a9STom Joseph 
26650fb50a9STom Joseph     // Insert the HMAC output into the payload
26750fb50a9STom Joseph     outPayload.insert(outPayload.end(), icv.begin(), icv.end());
26850fb50a9STom Joseph 
269ef02fb3dSTom Joseph     // Set the Integrity Algorithm
270ef02fb3dSTom Joseph     applyIntegrityAlgo(session->getBMCSessionID());
271818d0701STom Joseph 
2724c766eb1STom Joseph     // Set the Confidentiality Algorithm
2734c766eb1STom Joseph     applyCryptAlgo(session->getBMCSessionID());
2744c766eb1STom Joseph 
275*f8a34fc4SSuryakanth Sekar     session->state(static_cast<uint8_t>(session::State::active));
27650fb50a9STom Joseph     return outPayload;
27750fb50a9STom Joseph }
27850fb50a9STom Joseph 
27950fb50a9STom Joseph } // namespace command
280