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) == 8850fb50a9STom Joseph session::SESSION_ZERO) 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 129*4021b1f7STom 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 27550fb50a9STom Joseph session->state = session::State::ACTIVE; 27650fb50a9STom Joseph return outPayload; 27750fb50a9STom Joseph } 27850fb50a9STom Joseph 27950fb50a9STom Joseph } // namespace command 280