blob: 6ba6340d2da0136bc747f7ad3c8569ad321a6d0f [file] [log] [blame]
#pragma once
#include "rmcp.hpp"
#include <array>
#include <cstddef>
#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] 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<uint8_t>& packet, const size_t packetLen,
std::vector<uint8_t>::const_iterator integrityDataBegin,
std::vector<uint8_t>::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<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_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] 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<uint8_t>& packet, const size_t length,
std::vector<uint8_t>::const_iterator integrityDataBegin,
std::vector<uint8_t>::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<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] 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<uint8_t>& packet, const size_t length,
std::vector<uint8_t>::const_iterator integrityDataBegin,
std::vector<uint8_t>::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<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