#pragma once #include "crypt_algo.hpp" #include "integrity_algo.hpp" #include #include #include #include namespace cipher { namespace rakp_auth { constexpr size_t USER_KEY_MAX_LENGTH = 20; constexpr size_t BMC_RANDOM_NUMBER_LEN = 16; constexpr size_t REMOTE_CONSOLE_RANDOM_NUMBER_LEN = 16; /** * @enum RAKP Authentication Algorithms * * RMCP+ Authenticated Key-Exchange Protocol (RAKP) * * RAKP-None is not supported as per the following recommendation * (https://www.us-cert.gov/ncas/alerts/TA13-207A) * ("cipher 0" is an option enabled by default on many IPMI enabled devices that * allows authentication to be bypassed. Disable "cipher 0" to prevent * attackers from bypassing authentication and sending arbitrary IPMI commands.) */ enum class Algorithms : uint8_t { RAKP_NONE = 0, // Mandatory (implemented, not supported) RAKP_HMAC_SHA1, // Mandatory (implemented, default choice in ipmitool) RAKP_HMAC_MD5, // Optional (not implemented) RAKP_HMAC_SHA256, // Optional (implemented, best available) // Reserved used to indicate an invalid authentication algorithm RAKP_HMAC_INVALID = 0xB0 }; /** * @class Interface * * Interface is the base class for the Authentication Algorithms. * The Authentication Algorithm specifies the type of authentication “handshake” * process that is used and identifies any particular variations of hashing or * signature algorithm that is used as part of the process. * */ class Interface { public: explicit Interface(integrity::Algorithms intAlgo, crypt::Algorithms cryptAlgo) : intAlgo(intAlgo), cryptAlgo(cryptAlgo) {} Interface() = delete; virtual ~Interface() = default; Interface(const Interface&) = default; Interface& operator=(const Interface&) = default; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; /** * @brief Generate the Hash Message Authentication Code * * This API is invoked to generate the Key Exchange Authentication Code * in the RAKP2 and RAKP4 sequence and for generating the Session * Integrity Key. * * @param input message * * @return hash output * * @note The user key which is the secret key for the hash operation * needs to be set before this operation. */ std::vector virtual generateHMAC( const std::vector& input) const = 0; /** * @brief Generate the Integrity Check Value * * This API is invoked in the RAKP4 sequence for generating the * Integrity Check Value. * * @param input message * * @return hash output * * @note The session integrity key which is the secret key for the * hash operation needs to be set before this operation. */ std::vector virtual generateICV( const std::vector& input) const = 0; /** * @brief Check if the Authentication algorithm is supported * * @param[in] algo - authentication algorithm * * @return true if algorithm is supported else false * */ static bool isAlgorithmSupported(Algorithms algo) { if (algo == Algorithms::RAKP_HMAC_SHA256) { return true; } else { return false; } } // User Key is hardcoded to PASSW0RD till the IPMI User account // management is in place. std::array userKey = {"0penBmc"}; // Managed System Random Number std::array bmcRandomNum; // Remote Console Random Number std::array rcRandomNum; // Session Integrity Key std::vector sessionIntegrityKey; /** * Integrity Algorithm is activated and set in the session data only * once the session setup is succeeded in the RAKP34 command. But the * integrity algorithm is negotiated in the Open Session Request command * . So the integrity algorithm successfully negotiated is stored * in the authentication algorithm's instance. */ integrity::Algorithms intAlgo; /** * Confidentiality Algorithm is activated and set in the session data * only once the session setup is succeeded in the RAKP34 command. But * the confidentiality algorithm is negotiated in the Open Session * Request command. So the confidentiality algorithm successfully * negotiated is stored in the authentication algorithm's instance. */ crypt::Algorithms cryptAlgo; }; /** * @class AlgoSHA1 * * RAKP-HMAC-SHA1 specifies the use of RAKP messages for the key exchange * portion of establishing the session, and that HMAC-SHA1 (per [RFC2104]) is * used to create 20-byte Key Exchange Authentication Code fields in RAKP * Message 2 and RAKP Message 3. HMAC-SHA1-96(per [RFC2404]) is used for * generating a 12-byte Integrity Check Value field for RAKP Message 4. */ class AlgoSHA1 : public Interface { public: static constexpr size_t integrityCheckValueLength = 12; explicit AlgoSHA1(integrity::Algorithms intAlgo, crypt::Algorithms cryptAlgo) : Interface(intAlgo, cryptAlgo) {} AlgoSHA1() = delete; ~AlgoSHA1() = default; AlgoSHA1(const AlgoSHA1&) = default; AlgoSHA1& operator=(const AlgoSHA1&) = default; AlgoSHA1(AlgoSHA1&&) = default; AlgoSHA1& operator=(AlgoSHA1&&) = default; std::vector generateHMAC(const std::vector& input) const override; std::vector generateICV(const std::vector& input) const override; }; /** * @class AlgoSHA256 * * RAKP-HMAC-SHA256 specifies the use of RAKP messages for the key exchange * portion of establishing the session, and that HMAC-SHA256 (per [FIPS 180-2] * and [RFC4634] and is used to create a 32-byte Key Exchange Authentication * Code fields in RAKP Message 2 and RAKP Message 3. HMAC-SHA256-128 (per * [RFC4868]) is used for generating a 16-byte Integrity Check Value field for * RAKP Message 4. */ class AlgoSHA256 : public Interface { public: static constexpr size_t integrityCheckValueLength = 16; explicit AlgoSHA256(integrity::Algorithms intAlgo, crypt::Algorithms cryptAlgo) : Interface(intAlgo, cryptAlgo) {} ~AlgoSHA256() = default; AlgoSHA256(const AlgoSHA256&) = default; AlgoSHA256& operator=(const AlgoSHA256&) = default; AlgoSHA256(AlgoSHA256&&) = default; AlgoSHA256& operator=(AlgoSHA256&&) = default; std::vector generateHMAC(const std::vector& input) const override; std::vector generateICV(const std::vector& input) const override; }; } // namespace rakp_auth } // namespace cipher