1 #pragma once 2 3 #include <array> 4 #include <vector> 5 #include "rmcp.hpp" 6 7 namespace cipher 8 { 9 10 namespace integrity 11 { 12 13 /** 14 * @enum Integrity Algorithms 15 * 16 * The Integrity Algorithm Number specifies the algorithm used to generate the 17 * contents for the AuthCode “signature” field that accompanies authenticated 18 * IPMI v2.0/RMCP+ messages once the session has been established. If the 19 * Integrity Algorithm is none the AuthCode value is not calculated and the 20 * AuthCode field in the message is not present. Based on security 21 * recommendations NONE will not be supported. 22 */ 23 enum class Algorithms : uint8_t 24 { 25 NONE, // Mandatory (implemented, not supported) 26 HMAC_SHA1_96, // Mandatory (implemented, default choice in ipmitool) 27 HMAC_MD5_128, // Optional (not implemented) 28 MD5_128, // Optional (not implemented) 29 HMAC_SHA256_128, // Optional (implemented, best available) 30 }; 31 32 /** 33 * @class Interface 34 * 35 * Interface is the base class for the Integrity Algorithms. 36 * Unless otherwise specified, the integrity algorithm is applied to the packet 37 * data starting with the AuthType/Format field up to and including the field 38 * that immediately precedes the AuthCode field itself. 39 */ 40 class Interface 41 { 42 public: 43 /** 44 * @brief Constructor for Interface 45 * 46 * @param[in] - AuthCode length 47 */ 48 explicit Interface(size_t authLength) 49 : authCodeLength(authLength) {} 50 51 Interface() = delete; 52 virtual ~Interface() = default; 53 Interface(const Interface&) = default; 54 Interface& operator=(const Interface&) = default; 55 Interface(Interface&&) = default; 56 Interface& operator=(Interface&&) = default; 57 58 /** 59 * @brief Verify the integrity data of the packet 60 * 61 * @param[in] packet - Incoming IPMI packet 62 * @param[in] packetLen - Packet length excluding authCode 63 * @param[in] integrityData - Iterator to the authCode in the packet 64 * 65 * @return true if authcode in the packet is equal to one generated 66 * using integrity algorithm on the packet data, false otherwise 67 */ 68 bool virtual verifyIntegrityData( 69 const std::vector<uint8_t>& packet, 70 const size_t packetLen, 71 std::vector<uint8_t>::const_iterator integrityData) const = 0; 72 73 /** 74 * @brief Generate integrity data for the outgoing IPMI packet 75 * 76 * @param[in] input - Outgoing IPMI packet 77 * 78 * @return authcode for the outgoing IPMI packet 79 * 80 */ 81 std::vector<uint8_t> virtual generateIntegrityData( 82 const std::vector<uint8_t>& input) const = 0; 83 84 /** 85 * @brief Check if the Integrity algorithm is supported 86 * 87 * @param[in] algo - integrity algorithm 88 * 89 * @return true if algorithm is supported else false 90 * 91 */ 92 static bool isAlgorithmSupported(Algorithms algo) 93 { 94 if (algo == Algorithms::HMAC_SHA1_96 || 95 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, 131 const rmcp::Const_n& data) const = 0; 132 133 /** @brief Authcode field 134 * 135 * AuthCode field length varies based on the integrity algorithm, for 136 * HMAC-SHA1-96 the authcode field is 12 bytes. For HMAC-SHA256-128 and 137 * HMAC-MD5-128 the authcode field is 16 bytes. 138 */ 139 size_t authCodeLength; 140 141 protected: 142 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] integrityData - Iterator to the authCode in the packet 184 * 185 * @return true if authcode in the packet is equal to one generated 186 * using integrity algorithm on the packet data, false otherwise 187 */ 188 bool verifyIntegrityData( 189 const std::vector<uint8_t>& packet, 190 const size_t length, 191 std::vector<uint8_t>::const_iterator integrityData) 192 const override; 193 194 /** 195 * @brief Generate integrity data for the outgoing IPMI packet 196 * 197 * @param[in] input - Outgoing IPMI packet 198 * 199 * @return on success return the integrity data for the outgoing IPMI 200 * packet 201 */ 202 std::vector<uint8_t> generateIntegrityData( 203 const std::vector<uint8_t>& packet) const override; 204 205 /** 206 * @brief Generate additional keying material based on SIK 207 * 208 * @param[in] sik - session integrity key 209 * @param[in] data - 20-byte Const_n 210 * 211 * @return on success returns the Kn based on HMAC-SHA1 212 * 213 */ 214 std::vector<uint8_t> generateKn( 215 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] integrityData - Iterator to the authCode in the packet 269 * 270 * @return true if authcode in the packet is equal to one generated 271 * using integrity algorithm on the packet data, false otherwise 272 */ 273 bool verifyIntegrityData( 274 const std::vector<uint8_t>& packet, 275 const size_t length, 276 std::vector<uint8_t>::const_iterator integrityData) 277 const override; 278 279 /** 280 * @brief Generate integrity data for the outgoing IPMI packet 281 * 282 * @param[in] packet - Outgoing IPMI packet 283 * 284 * @return on success return the integrity data for the outgoing IPMI 285 * packet 286 */ 287 std::vector<uint8_t> generateIntegrityData( 288 const std::vector<uint8_t>& packet) const override; 289 290 /** 291 * @brief Generate additional keying material based on SIK 292 * 293 * @param[in] sik - session integrity key 294 * @param[in] data - 20-byte Const_n 295 * 296 * @return on success returns the Kn based on HMAC-SHA256 297 * 298 */ 299 std::vector<uint8_t> generateKn( 300 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 321