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