1 #pragma once 2 3 #include "rmcp.hpp" 4 5 #include <array> 6 #include <vector> 7 8 namespace cipher 9 { 10 11 namespace integrity 12 { 13 14 /** 15 * @enum Integrity Algorithms 16 * 17 * The Integrity Algorithm Number specifies the algorithm used to generate the 18 * contents for the AuthCode “signature” field that accompanies authenticated 19 * IPMI v2.0/RMCP+ messages once the session has been established. If the 20 * Integrity Algorithm is none the AuthCode value is not calculated and the 21 * AuthCode field in the message is not present. Based on security 22 * recommendations NONE will not be supported. 23 */ 24 enum class Algorithms : uint8_t 25 { 26 NONE, // Mandatory (implemented, not supported) 27 HMAC_SHA1_96, // Mandatory (implemented, default choice in ipmitool) 28 HMAC_MD5_128, // Optional (not implemented) 29 MD5_128, // Optional (not implemented) 30 HMAC_SHA256_128, // Optional (implemented, best available) 31 }; 32 33 /** 34 * @class Interface 35 * 36 * Interface is the base class for the Integrity Algorithms. 37 * Unless otherwise specified, the integrity algorithm is applied to the packet 38 * data starting with the AuthType/Format field up to and including the field 39 * that immediately precedes the AuthCode field itself. 40 */ 41 class Interface 42 { 43 public: 44 /** 45 * @brief Constructor for Interface 46 * 47 * @param[in] - AuthCode length 48 */ 49 explicit Interface(size_t authLength) : authCodeLength(authLength) 50 { 51 } 52 53 Interface() = delete; 54 virtual ~Interface() = default; 55 Interface(const Interface&) = default; 56 Interface& operator=(const Interface&) = default; 57 Interface(Interface&&) = default; 58 Interface& operator=(Interface&&) = default; 59 60 /** 61 * @brief Verify the integrity data of the packet 62 * 63 * @param[in] packet - Incoming IPMI packet 64 * @param[in] packetLen - Packet length excluding authCode 65 * @param[in] integrityData - Iterator to the authCode in the packet 66 * 67 * @return true if authcode in the packet is equal to one generated 68 * using integrity algorithm on the packet data, false otherwise 69 */ 70 bool virtual verifyIntegrityData( 71 const std::vector<uint8_t>& packet, const size_t packetLen, 72 std::vector<uint8_t>::const_iterator integrityData) const = 0; 73 74 /** 75 * @brief Generate integrity data for the outgoing IPMI packet 76 * 77 * @param[in] input - Outgoing IPMI packet 78 * 79 * @return authcode for the outgoing IPMI packet 80 * 81 */ 82 std::vector<uint8_t> virtual generateIntegrityData( 83 const std::vector<uint8_t>& input) const = 0; 84 85 /** 86 * @brief Check if the Integrity algorithm is supported 87 * 88 * @param[in] algo - integrity algorithm 89 * 90 * @return true if algorithm is supported else false 91 * 92 */ 93 static bool isAlgorithmSupported(Algorithms algo) 94 { 95 if (algo == Algorithms::HMAC_SHA256_128) 96 { 97 return true; 98 } 99 else 100 { 101 return false; 102 } 103 } 104 105 /** 106 * @brief Generate additional keying material based on SIK 107 * 108 * @note 109 * The IPMI 2.0 spec only states that the additional keying material is 110 * generated by running HMAC(constN) using SIK as the key. It does not 111 * state whether this is the integrity algorithm or the authentication 112 * algorithm. Other implementations of the RMCP+ algorithm (ipmitool 113 * and ipmiutil) are not consistent on this matter. But it does not 114 * really matter because based on any of the defined cipher suites, the 115 * integrity and authentication algorithms are both based on the same 116 * digest method (integrity::Algorithms::HMAC_SHA1_96 uses SHA1 and 117 * rakp_auth::Algorithms::RAKP_HMAC_SHA1 uses SHA1). None of the 118 * defined cipher suites mix and match digests for integrity and 119 * authentication. Generating Kn belongs in either the integrity or 120 * authentication classes, so in this implementation, integrity has 121 * been chosen. 122 * 123 * @param[in] sik - session integrity key 124 * @param[in] data - 20-byte Const_n 125 * 126 * @return on success returns the Kn based on this integrity class 127 * 128 */ 129 std::vector<uint8_t> virtual generateKn( 130 const std::vector<uint8_t>& sik, const rmcp::Const_n& data) const = 0; 131 132 /** @brief Authcode field 133 * 134 * AuthCode field length varies based on the integrity algorithm, for 135 * HMAC-SHA1-96 the authcode field is 12 bytes. For HMAC-SHA256-128 and 136 * HMAC-MD5-128 the authcode field is 16 bytes. 137 */ 138 size_t authCodeLength; 139 140 protected: 141 /** @brief K1 key used to generated the integrity data. */ 142 std::vector<uint8_t> k1; 143 }; 144 145 /** 146 * @class AlgoSHA1 147 * 148 * @brief Implementation of the HMAC-SHA1-96 Integrity algorithm 149 * 150 * HMAC-SHA1-96 take the Session Integrity Key and use it to generate K1. K1 is 151 * then used as the key for use in HMAC to produce the AuthCode field. 152 * For “one-key” logins, the user’s key (password) is used in the creation of 153 * the Session Integrity Key. When the HMAC-SHA1-96 Integrity Algorithm is used 154 * the resulting AuthCode field is 12 bytes (96 bits). 155 */ 156 class AlgoSHA1 final : public Interface 157 { 158 public: 159 static constexpr size_t SHA1_96_AUTHCODE_LENGTH = 12; 160 161 /** 162 * @brief Constructor for AlgoSHA1 163 * 164 * @param[in] - Session Integrity Key 165 */ 166 explicit AlgoSHA1(const std::vector<uint8_t>& sik); 167 168 AlgoSHA1() = delete; 169 ~AlgoSHA1() = default; 170 AlgoSHA1(const AlgoSHA1&) = default; 171 AlgoSHA1& operator=(const AlgoSHA1&) = default; 172 AlgoSHA1(AlgoSHA1&&) = default; 173 AlgoSHA1& operator=(AlgoSHA1&&) = default; 174 175 /** 176 * @brief Verify the integrity data of the packet 177 * 178 * @param[in] packet - Incoming IPMI packet 179 * @param[in] length - Length of the data in the packet to calculate 180 * the integrity data 181 * @param[in] integrityData - Iterator to the authCode in the packet 182 * 183 * @return true if authcode in the packet is equal to one generated 184 * using integrity algorithm on the packet data, false otherwise 185 */ 186 bool verifyIntegrityData( 187 const std::vector<uint8_t>& packet, const size_t length, 188 std::vector<uint8_t>::const_iterator integrityData) const override; 189 190 /** 191 * @brief Generate integrity data for the outgoing IPMI packet 192 * 193 * @param[in] input - Outgoing IPMI packet 194 * 195 * @return on success return the integrity data for the outgoing IPMI 196 * packet 197 */ 198 std::vector<uint8_t> generateIntegrityData( 199 const std::vector<uint8_t>& packet) const override; 200 201 /** 202 * @brief Generate additional keying material based on SIK 203 * 204 * @param[in] sik - session integrity key 205 * @param[in] data - 20-byte Const_n 206 * 207 * @return on success returns the Kn based on HMAC-SHA1 208 * 209 */ 210 std::vector<uint8_t> generateKn(const std::vector<uint8_t>& sik, 211 const rmcp::Const_n& const_n) const; 212 213 private: 214 /** 215 * @brief Generate HMAC based on HMAC-SHA1-96 algorithm 216 * 217 * @param[in] input - pointer to the message 218 * @param[in] length - length of the message 219 * 220 * @return on success returns the message authentication code 221 * 222 */ 223 std::vector<uint8_t> generateHMAC(const uint8_t* input, 224 const size_t len) const; 225 }; 226 227 /** 228 * @class AlgoSHA256 229 * 230 * @brief Implementation of the HMAC-SHA256-128 Integrity algorithm 231 * 232 * HMAC-SHA256-128 take the Session Integrity Key and use it to generate K1. K1 233 * is then used as the key for use in HMAC to produce the AuthCode field. For 234 * “one-key” logins, the user’s key (password) is used in the creation of the 235 * Session Integrity Key. When the HMAC-SHA256-128 Integrity Algorithm is used 236 * the resulting AuthCode field is 16 bytes (128 bits). 237 */ 238 class AlgoSHA256 final : public Interface 239 { 240 public: 241 static constexpr size_t SHA256_128_AUTHCODE_LENGTH = 16; 242 243 /** 244 * @brief Constructor for AlgoSHA256 245 * 246 * @param[in] - Session Integrity Key 247 */ 248 explicit AlgoSHA256(const std::vector<uint8_t>& sik); 249 250 AlgoSHA256() = delete; 251 ~AlgoSHA256() = default; 252 AlgoSHA256(const AlgoSHA256&) = default; 253 AlgoSHA256& operator=(const AlgoSHA256&) = default; 254 AlgoSHA256(AlgoSHA256&&) = default; 255 AlgoSHA256& operator=(AlgoSHA256&&) = default; 256 257 /** 258 * @brief Verify the integrity data of the packet 259 * 260 * @param[in] packet - Incoming IPMI packet 261 * @param[in] length - Length of the data in the packet to calculate 262 * the integrity data 263 * @param[in] integrityData - Iterator to the authCode in the packet 264 * 265 * @return true if authcode in the packet is equal to one generated 266 * using integrity algorithm on the packet data, false otherwise 267 */ 268 bool verifyIntegrityData( 269 const std::vector<uint8_t>& packet, const size_t length, 270 std::vector<uint8_t>::const_iterator integrityData) const override; 271 272 /** 273 * @brief Generate integrity data for the outgoing IPMI packet 274 * 275 * @param[in] packet - Outgoing IPMI packet 276 * 277 * @return on success return the integrity data for the outgoing IPMI 278 * packet 279 */ 280 std::vector<uint8_t> generateIntegrityData( 281 const std::vector<uint8_t>& packet) const override; 282 283 /** 284 * @brief Generate additional keying material based on SIK 285 * 286 * @param[in] sik - session integrity key 287 * @param[in] data - 20-byte Const_n 288 * 289 * @return on success returns the Kn based on HMAC-SHA256 290 * 291 */ 292 std::vector<uint8_t> generateKn(const std::vector<uint8_t>& sik, 293 const rmcp::Const_n& const_n) const; 294 295 private: 296 /** 297 * @brief Generate HMAC based on HMAC-SHA256-128 algorithm 298 * 299 * @param[in] input - pointer to the message 300 * @param[in] len - length of the message 301 * 302 * @return on success returns the message authentication code 303 * 304 */ 305 std::vector<uint8_t> generateHMAC(const uint8_t* input, 306 const size_t len) const; 307 }; 308 309 } // namespace integrity 310 311 } // namespace cipher 312