1 #pragma once
2 
3 #include <openssl/sha.h>
4 #include <array>
5 #include <vector>
6 
7 namespace cipher
8 {
9 
10 namespace integrity
11 {
12 
13 using Buffer = std::vector<uint8_t>;
14 using Key = std::array<uint8_t, SHA_DIGEST_LENGTH>;
15 
16 /*
17  * RSP needs more keying material than can be provided by session integrity key
18  * alone. As a result all keying material for the RSP integrity algorithms
19  * will be generated by processing a pre-defined set of constants using HMAC per
20  * [RFC2104], keyed by SIK. These constants are constructed using a hexadecimal
21  * octet value repeated up to the HMAC block size in length starting with the
22  * constant 01h. This mechanism can be used to derive up to 255
23  * HMAC-block-length pieces of keying material from a single SIK. For the
24  * mandatory integrity algorithm HMAC-SHA1-96, processing the following
25  * constant will generate the required amount of keying material.
26  */
27 constexpr Key const1 = { 0x01, 0x01, 0x01, 0x01, 0x01,
28                          0x01, 0x01, 0x01, 0x01, 0x01,
29                          0x01, 0x01, 0x01, 0x01, 0x01,
30                          0x01, 0x01, 0x01, 0x01, 0x01
31                        };
32 
33 /*
34  * @enum Integrity Algorithms
35  *
36  * The Integrity Algorithm Number specifies the algorithm used to generate the
37  * contents for the AuthCode “signature” field that accompanies authenticated
38  * IPMI v2.0/RMCP+ messages once the session has been established. If the
39  * Integrity Algorithm is none the AuthCode value is not calculated and the
40  * AuthCode field in the message is not present.
41  */
42 enum class Algorithms : uint8_t
43 {
44     NONE,                  // Mandatory
45     HMAC_SHA1_96,          // Mandatory
46     HMAC_MD5_128,          // Optional
47     MD5_128,               // Optional
48     HMAC_SHA256_128,       // Optional
49 };
50 
51 /*
52  * @class Interface
53  *
54  * Interface is the base class for the Integrity Algorithms.
55  * Unless otherwise specified, the integrity algorithm is applied to the packet
56  * data starting with the AuthType/Format field up to and including the field
57  * that immediately precedes the AuthCode field itself.
58  */
59 class Interface
60 {
61     public:
62         /*
63          * @brief Constructor for Interface
64          *
65          * @param[in] - Session Integrity Key to generate K1
66          * @param[in] - Additional keying material to generate K1
67          * @param[in] - AuthCode length
68          */
69         explicit Interface(const Buffer& sik,
70                            const Key& addKey,
71                            size_t authLength);
72 
73         Interface() = delete;
74         virtual ~Interface() = default;
75         Interface(const Interface&) = default;
76         Interface& operator=(const Interface&) = default;
77         Interface(Interface&&) = default;
78         Interface& operator=(Interface&&) = default;
79 
80         /*
81          * @brief Verify the integrity data of the packet
82          *
83          * @param[in] packet - Incoming IPMI packet
84          * @param[in] packetLen - Packet length excluding authCode
85          * @param[in] integrityData - Iterator to the authCode in the packet
86          *
87          * @return true if authcode in the packet is equal to one generated
88          *         using integrity algorithm on the packet data, false otherwise
89          */
90         bool virtual verifyIntegrityData(
91                 const Buffer& packet,
92                 const size_t packetLen,
93                 Buffer::const_iterator integrityData) const = 0;
94 
95         /*
96          * @brief Generate integrity data for the outgoing IPMI packet
97          *
98          * @param[in] input - Outgoing IPMI packet
99          *
100          * @return authcode for the outgoing IPMI packet
101          *
102          */
103         Buffer virtual generateIntegrityData(const Buffer& input) const = 0;
104 
105         /**
106          * @brief Check if the Integrity algorithm is supported
107          *
108          * @param[in] algo - integrity algorithm
109          *
110          * @return true if algorithm is supported else false
111          *
112          */
113         static bool isAlgorithmSupported(Algorithms algo)
114         {
115             if (algo == Algorithms::NONE || algo == Algorithms::HMAC_SHA1_96)
116             {
117                return true;
118             }
119             else
120             {
121                 return false;
122             }
123         }
124 
125         /*
126          * AuthCode field length varies based on the integrity algorithm, for
127          * HMAC-SHA1-96 the authcode field is 12 bytes. For HMAC-SHA256-128 and
128          * HMAC-MD5-128 the authcode field is 16 bytes.
129          */
130         size_t authCodeLength;
131 
132     protected:
133 
134         // K1 key used to generated the integrity data
135         Key K1;
136 };
137 
138 /*
139  * @class AlgoSHA1
140  *
141  * @brief Implementation of the HMAC-SHA1-96 Integrity algorithm
142  *
143  * HMAC-SHA1-96 take the Session Integrity Key and use it to generate K1. K1 is
144  * then used as the key for use in HMAC to produce the AuthCode field.
145  * For “one-key” logins, the user’s key (password) is used in the creation of
146  * the Session Integrity Key. When the HMAC-SHA1-96 Integrity Algorithm is used
147  * the resulting AuthCode field is 12 bytes (96 bits).
148  */
149 class AlgoSHA1 final : public Interface
150 {
151     public:
152         static constexpr size_t SHA1_96_AUTHCODE_LENGTH = 12;
153 
154         /*
155          * @brief Constructor for AlgoSHA1
156          *
157          * @param[in] - Session Integrity Key
158          */
159         explicit AlgoSHA1(const Buffer& sik) :
160             Interface(sik, const1, SHA1_96_AUTHCODE_LENGTH) {}
161 
162         AlgoSHA1() = delete;
163         ~AlgoSHA1() = default;
164         AlgoSHA1(const AlgoSHA1&) = default;
165         AlgoSHA1& operator=(const AlgoSHA1&) = default;
166         AlgoSHA1(AlgoSHA1&&) = default;
167         AlgoSHA1& operator=(AlgoSHA1&&) = default;
168 
169         /*
170          * @brief Verify the integrity data of the packet
171          *
172          * @param[in] packet - Incoming IPMI packet
173          * @param[in] length - Length of the data in the packet to calculate
174          *                     the integrity data
175          * @param[in] integrityData - Iterator to the authCode in the packet
176          *
177          * @return true if authcode in the packet is equal to one generated
178          *         using integrity algorithm on the packet data, false otherwise
179          */
180         bool verifyIntegrityData(
181                 const Buffer& packet,
182                 const size_t length,
183                 Buffer::const_iterator integrityData) const override;
184 
185         /*
186          * @brief Generate integrity data for the outgoing IPMI packet
187          *
188          * @param[in] input - Outgoing IPMI packet
189          *
190          * @return on success return the integrity data for the outgoing IPMI
191          *         packet
192          */
193         Buffer generateIntegrityData(const Buffer& packet) const override;
194 
195     private:
196         /*
197          * @brief Generate HMAC based on HMAC-SHA1-96 algorithm
198          *
199          * @param[in] input - pointer to the message
200          * @param[in] length - length of the message
201          *
202          * @return on success returns the message authentication code
203          *
204          */
205         Buffer generateHMAC(const uint8_t* input, const size_t len) const;
206 };
207 
208 }// namespace integrity
209 
210 }// namespace cipher
211 
212