#pragma once #include "rmcp.hpp" #include #include #include namespace cipher { namespace integrity { /** * @enum Integrity Algorithms * * The Integrity Algorithm Number specifies the algorithm used to generate the * contents for the AuthCode “signature” field that accompanies authenticated * IPMI v2.0/RMCP+ messages once the session has been established. If the * Integrity Algorithm is none the AuthCode value is not calculated and the * AuthCode field in the message is not present. Based on security * recommendations NONE will not be supported. */ enum class Algorithms : uint8_t { NONE, // Mandatory (implemented, not supported) HMAC_SHA1_96, // Mandatory (implemented, default choice in ipmitool) HMAC_MD5_128, // Optional (not implemented) MD5_128, // Optional (not implemented) HMAC_SHA256_128, // Optional (implemented, best available) }; /** * @class Interface * * Interface is the base class for the Integrity Algorithms. * Unless otherwise specified, the integrity algorithm is applied to the packet * data starting with the AuthType/Format field up to and including the field * that immediately precedes the AuthCode field itself. */ class Interface { public: /** * @brief Constructor for Interface * * @param[in] - AuthCode length */ explicit Interface(size_t authLength) : authCodeLength(authLength) {} Interface() = delete; virtual ~Interface() = default; Interface(const Interface&) = default; Interface& operator=(const Interface&) = default; Interface(Interface&&) = default; Interface& operator=(Interface&&) = default; /** * @brief Verify the integrity data of the packet * * @param[in] packet - Incoming IPMI packet * @param[in] packetLen - Packet length excluding authCode * @param[in] integrityDataBegin - Begin iterator to the authCode in the * packet * @param[in] integrityDataEnd - End to the authCode in the packet * * @return true if authcode in the packet is equal to one generated * using integrity algorithm on the packet data, false otherwise */ bool virtual verifyIntegrityData( const std::vector& packet, const size_t packetLen, std::vector::const_iterator integrityDataBegin, std::vector::const_iterator integrityDataEnd) const = 0; /** * @brief Generate integrity data for the outgoing IPMI packet * * @param[in] input - Outgoing IPMI packet * * @return authcode for the outgoing IPMI packet * */ std::vector virtual generateIntegrityData( const std::vector& input) const = 0; /** * @brief Check if the Integrity algorithm is supported * * @param[in] algo - integrity algorithm * * @return true if algorithm is supported else false * */ static bool isAlgorithmSupported(Algorithms algo) { if (algo == Algorithms::HMAC_SHA256_128) { return true; } else { return false; } } /** * @brief Generate additional keying material based on SIK * * @note * The IPMI 2.0 spec only states that the additional keying material is * generated by running HMAC(constN) using SIK as the key. It does not * state whether this is the integrity algorithm or the authentication * algorithm. Other implementations of the RMCP+ algorithm (ipmitool * and ipmiutil) are not consistent on this matter. But it does not * really matter because based on any of the defined cipher suites, the * integrity and authentication algorithms are both based on the same * digest method (integrity::Algorithms::HMAC_SHA1_96 uses SHA1 and * rakp_auth::Algorithms::RAKP_HMAC_SHA1 uses SHA1). None of the * defined cipher suites mix and match digests for integrity and * authentication. Generating Kn belongs in either the integrity or * authentication classes, so in this implementation, integrity has * been chosen. * * @param[in] sik - session integrity key * @param[in] data - 20-byte Const_n * * @return on success returns the Kn based on this integrity class * */ std::vector virtual generateKn( const std::vector& sik, const rmcp::Const_n& data) const = 0; /** @brief Authcode field * * AuthCode field length varies based on the integrity algorithm, for * HMAC-SHA1-96 the authcode field is 12 bytes. For HMAC-SHA256-128 and * HMAC-MD5-128 the authcode field is 16 bytes. */ size_t authCodeLength; protected: /** @brief K1 key used to generated the integrity data. */ std::vector k1; }; /** * @class AlgoSHA1 * * @brief Implementation of the HMAC-SHA1-96 Integrity algorithm * * HMAC-SHA1-96 take the Session Integrity Key and use it to generate K1. K1 is * then used as the key for use in HMAC to produce the AuthCode field. * For “one-key” logins, the user’s key (password) is used in the creation of * the Session Integrity Key. When the HMAC-SHA1-96 Integrity Algorithm is used * the resulting AuthCode field is 12 bytes (96 bits). */ class AlgoSHA1 final : public Interface { public: static constexpr size_t SHA1_96_AUTHCODE_LENGTH = 12; /** * @brief Constructor for AlgoSHA1 * * @param[in] - Session Integrity Key */ explicit AlgoSHA1(const std::vector& sik); AlgoSHA1() = delete; ~AlgoSHA1() = default; AlgoSHA1(const AlgoSHA1&) = default; AlgoSHA1& operator=(const AlgoSHA1&) = default; AlgoSHA1(AlgoSHA1&&) = default; AlgoSHA1& operator=(AlgoSHA1&&) = default; /** * @brief Verify the integrity data of the packet * * @param[in] packet - Incoming IPMI packet * @param[in] length - Length of the data in the packet to calculate * the integrity data * @param[in] integrityDataBegin - Begin iterator to the authCode in the * packet * @param[in] integrityDataEnd - End to the authCode in the packet * * @return true if authcode in the packet is equal to one generated * using integrity algorithm on the packet data, false otherwise */ bool verifyIntegrityData( const std::vector& packet, const size_t length, std::vector::const_iterator integrityDataBegin, std::vector::const_iterator integrityDataEnd) const override; /** * @brief Generate integrity data for the outgoing IPMI packet * * @param[in] input - Outgoing IPMI packet * * @return on success return the integrity data for the outgoing IPMI * packet */ std::vector generateIntegrityData( const std::vector& packet) const override; /** * @brief Generate additional keying material based on SIK * * @param[in] sik - session integrity key * @param[in] data - 20-byte Const_n * * @return on success returns the Kn based on HMAC-SHA1 * */ std::vector generateKn(const std::vector& sik, const rmcp::Const_n& const_n) const; private: /** * @brief Generate HMAC based on HMAC-SHA1-96 algorithm * * @param[in] input - pointer to the message * @param[in] length - length of the message * * @return on success returns the message authentication code * */ std::vector generateHMAC(const uint8_t* input, const size_t len) const; }; /** * @class AlgoSHA256 * * @brief Implementation of the HMAC-SHA256-128 Integrity algorithm * * HMAC-SHA256-128 take the Session Integrity Key and use it to generate K1. K1 * is then used as the key for use in HMAC to produce the AuthCode field. For * “one-key” logins, the user’s key (password) is used in the creation of the * Session Integrity Key. When the HMAC-SHA256-128 Integrity Algorithm is used * the resulting AuthCode field is 16 bytes (128 bits). */ class AlgoSHA256 final : public Interface { public: static constexpr size_t SHA256_128_AUTHCODE_LENGTH = 16; /** * @brief Constructor for AlgoSHA256 * * @param[in] - Session Integrity Key */ explicit AlgoSHA256(const std::vector& sik); AlgoSHA256() = delete; ~AlgoSHA256() = default; AlgoSHA256(const AlgoSHA256&) = default; AlgoSHA256& operator=(const AlgoSHA256&) = default; AlgoSHA256(AlgoSHA256&&) = default; AlgoSHA256& operator=(AlgoSHA256&&) = default; /** * @brief Verify the integrity data of the packet * * @param[in] packet - Incoming IPMI packet * @param[in] length - Length of the data in the packet to calculate * the integrity data * @param[in] integrityDataBegin - Begin iterator to the authCode in the * packet * @param[in] integrityDataEnd - End to the authCode in the packet * * @return true if authcode in the packet is equal to one generated * using integrity algorithm on the packet data, false otherwise */ bool verifyIntegrityData( const std::vector& packet, const size_t length, std::vector::const_iterator integrityDataBegin, std::vector::const_iterator integrityDataEnd) const override; /** * @brief Generate integrity data for the outgoing IPMI packet * * @param[in] packet - Outgoing IPMI packet * * @return on success return the integrity data for the outgoing IPMI * packet */ std::vector generateIntegrityData( const std::vector& packet) const override; /** * @brief Generate additional keying material based on SIK * * @param[in] sik - session integrity key * @param[in] data - 20-byte Const_n * * @return on success returns the Kn based on HMAC-SHA256 * */ std::vector generateKn(const std::vector& sik, const rmcp::Const_n& const_n) const; private: /** * @brief Generate HMAC based on HMAC-SHA256-128 algorithm * * @param[in] input - pointer to the message * @param[in] len - length of the message * * @return on success returns the message authentication code * */ std::vector generateHMAC(const uint8_t* input, const size_t len) const; }; } // namespace integrity } // namespace cipher