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