| #pragma once |
| |
| #include "rmcp.hpp" |
| |
| #include <array> |
| #include <vector> |
| |
| 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] integrityData - Iterator 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<uint8_t>& packet, const size_t packetLen, |
| std::vector<uint8_t>::const_iterator integrityData) 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<uint8_t> virtual generateIntegrityData( |
| const std::vector<uint8_t>& 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_SHA1_96 || |
| 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<uint8_t> virtual generateKn( |
| const std::vector<uint8_t>& 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<uint8_t> 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<uint8_t>& 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] integrityData - Iterator 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<uint8_t>& packet, const size_t length, |
| std::vector<uint8_t>::const_iterator integrityData) 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<uint8_t> generateIntegrityData( |
| const std::vector<uint8_t>& 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<uint8_t> generateKn(const std::vector<uint8_t>& 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<uint8_t> 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<uint8_t>& 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] integrityData - Iterator 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<uint8_t>& packet, const size_t length, |
| std::vector<uint8_t>::const_iterator integrityData) 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<uint8_t> generateIntegrityData( |
| const std::vector<uint8_t>& 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<uint8_t> generateKn(const std::vector<uint8_t>& 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<uint8_t> generateHMAC(const uint8_t* input, |
| const size_t len) const; |
| }; |
| |
| } // namespace integrity |
| |
| } // namespace cipher |