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