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