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