1 #pragma once
2 
3 #include "crypt_algo.hpp"
4 #include "integrity_algo.hpp"
5 
6 #include <array>
7 #include <cstddef>
8 #include <string>
9 #include <vector>
10 
11 namespace cipher
12 {
13 namespace rakp_auth
14 {
15 constexpr size_t USER_KEY_MAX_LENGTH = 20;
16 constexpr size_t BMC_RANDOM_NUMBER_LEN = 16;
17 constexpr size_t REMOTE_CONSOLE_RANDOM_NUMBER_LEN = 16;
18 
19 /**
20  * @enum RAKP Authentication Algorithms
21  *
22  * RMCP+ Authenticated Key-Exchange Protocol (RAKP)
23  *
24  * RAKP-None is not supported as per the following recommendation
25  * (https://www.us-cert.gov/ncas/alerts/TA13-207A)
26  * ("cipher 0" is an option enabled by default on many IPMI enabled devices that
27  * allows authentication to be bypassed.  Disable "cipher 0" to prevent
28  * attackers from bypassing authentication and sending arbitrary IPMI commands.)
29  */
30 enum class Algorithms : uint8_t
31 {
32     RAKP_NONE = 0,    // Mandatory (implemented, not supported)
33     RAKP_HMAC_SHA1,   // Mandatory (implemented, default choice in ipmitool)
34     RAKP_HMAC_MD5,    // Optional (not implemented)
35     RAKP_HMAC_SHA256, // Optional (implemented, best available)
36     // Reserved used to indicate an invalid authentication algorithm
37     RAKP_HMAC_INVALID = 0xB0
38 };
39 
40 /**
41  * @class Interface
42  *
43  * Interface is the base class for the Authentication Algorithms.
44  * The Authentication Algorithm specifies the type of authentication “handshake”
45  * process that is used and identifies any particular variations of hashing or
46  * signature algorithm that is used as part of the process.
47  *
48  */
49 class Interface
50 {
51   public:
Interface(integrity::Algorithms intAlgo,crypt::Algorithms cryptAlgo)52     explicit Interface(integrity::Algorithms intAlgo,
53                        crypt::Algorithms cryptAlgo) :
54         intAlgo(intAlgo), cryptAlgo(cryptAlgo)
55     {}
56 
57     Interface() = delete;
58     virtual ~Interface() = default;
59     Interface(const Interface&) = default;
60     Interface& operator=(const Interface&) = default;
61     Interface(Interface&&) = default;
62     Interface& operator=(Interface&&) = default;
63 
64     /**
65      * @brief Generate the Hash Message Authentication Code
66      *
67      * This API is invoked to generate the Key Exchange Authentication Code
68      * in the RAKP2 and RAKP4 sequence and for generating the Session
69      * Integrity Key.
70      *
71      * @param input message
72      *
73      * @return hash output
74      *
75      * @note The user key which is the secret key for the hash operation
76      *        needs to be set before this operation.
77      */
78     std::vector<uint8_t> virtual generateHMAC(
79         const std::vector<uint8_t>& input) const = 0;
80 
81     /**
82      * @brief Generate the Integrity Check Value
83      *
84      * This API is invoked in the RAKP4 sequence for generating the
85      * Integrity Check Value.
86      *
87      * @param input message
88      *
89      * @return hash output
90      *
91      * @note The session integrity key which is the secret key for the
92      *        hash operation needs to be set before this operation.
93      */
94     std::vector<uint8_t> virtual generateICV(
95         const std::vector<uint8_t>& input) const = 0;
96 
97     /**
98      * @brief Check if the Authentication algorithm is supported
99      *
100      * @param[in] algo - authentication algorithm
101      *
102      * @return true if algorithm is supported else false
103      *
104      */
isAlgorithmSupported(Algorithms algo)105     static bool isAlgorithmSupported(Algorithms algo)
106     {
107         if (algo == Algorithms::RAKP_HMAC_SHA256)
108         {
109             return true;
110         }
111         else
112         {
113             return false;
114         }
115     }
116 
117     // User Key is hardcoded to PASSW0RD till the IPMI User account
118     // management is in place.
119     std::array<uint8_t, USER_KEY_MAX_LENGTH> userKey = {"0penBmc"};
120 
121     // Managed System Random Number
122     std::array<uint8_t, BMC_RANDOM_NUMBER_LEN> bmcRandomNum;
123 
124     // Remote Console Random Number
125     std::array<uint8_t, REMOTE_CONSOLE_RANDOM_NUMBER_LEN> rcRandomNum;
126 
127     // Session Integrity Key
128     std::vector<uint8_t> sessionIntegrityKey;
129 
130     /**
131      * Integrity Algorithm is activated and set in the session data only
132      * once the session setup is succeeded in the RAKP34 command. But the
133      * integrity algorithm is negotiated in the Open Session Request command
134      * . So the integrity algorithm successfully negotiated is stored
135      * in the authentication algorithm's instance.
136      */
137     integrity::Algorithms intAlgo;
138 
139     /**
140      * Confidentiality Algorithm is activated and set in the session data
141      * only once the session setup is succeeded in the RAKP34 command. But
142      * the confidentiality algorithm is negotiated in the Open Session
143      * Request command. So the confidentiality algorithm successfully
144      * negotiated is stored in the authentication algorithm's instance.
145      */
146     crypt::Algorithms cryptAlgo;
147 };
148 
149 /**
150  * @class AlgoSHA1
151  *
152  * RAKP-HMAC-SHA1 specifies the use of RAKP messages for the key exchange
153  * portion of establishing the session, and that HMAC-SHA1 (per [RFC2104]) is
154  * used to create 20-byte Key Exchange Authentication Code fields in RAKP
155  * Message 2 and RAKP Message 3. HMAC-SHA1-96(per [RFC2404]) is used for
156  * generating a 12-byte Integrity Check Value field for RAKP Message 4.
157  */
158 
159 class AlgoSHA1 : public Interface
160 {
161   public:
162     static constexpr size_t integrityCheckValueLength = 12;
163 
AlgoSHA1(integrity::Algorithms intAlgo,crypt::Algorithms cryptAlgo)164     explicit AlgoSHA1(integrity::Algorithms intAlgo,
165                       crypt::Algorithms cryptAlgo) :
166         Interface(intAlgo, cryptAlgo)
167     {}
168 
169     AlgoSHA1() = delete;
170     ~AlgoSHA1() = default;
171     AlgoSHA1(const AlgoSHA1&) = default;
172     AlgoSHA1& operator=(const AlgoSHA1&) = default;
173     AlgoSHA1(AlgoSHA1&&) = default;
174     AlgoSHA1& operator=(AlgoSHA1&&) = default;
175 
176     std::vector<uint8_t>
177         generateHMAC(const std::vector<uint8_t>& input) const override;
178 
179     std::vector<uint8_t>
180         generateICV(const std::vector<uint8_t>& input) const override;
181 };
182 
183 /**
184  * @class AlgoSHA256
185  *
186  * RAKP-HMAC-SHA256 specifies the use of RAKP messages for the key exchange
187  * portion of establishing the session, and that HMAC-SHA256 (per [FIPS 180-2]
188  * and [RFC4634] and is used to create a 32-byte Key Exchange Authentication
189  * Code fields in RAKP Message 2 and RAKP Message 3. HMAC-SHA256-128 (per
190  * [RFC4868]) is used for generating a 16-byte Integrity Check Value field for
191  * RAKP Message 4.
192  */
193 
194 class AlgoSHA256 : public Interface
195 {
196   public:
197     static constexpr size_t integrityCheckValueLength = 16;
198 
AlgoSHA256(integrity::Algorithms intAlgo,crypt::Algorithms cryptAlgo)199     explicit AlgoSHA256(integrity::Algorithms intAlgo,
200                         crypt::Algorithms cryptAlgo) :
201         Interface(intAlgo, cryptAlgo)
202     {}
203 
204     ~AlgoSHA256() = default;
205     AlgoSHA256(const AlgoSHA256&) = default;
206     AlgoSHA256& operator=(const AlgoSHA256&) = default;
207     AlgoSHA256(AlgoSHA256&&) = default;
208     AlgoSHA256& operator=(AlgoSHA256&&) = default;
209 
210     std::vector<uint8_t>
211         generateHMAC(const std::vector<uint8_t>& input) const override;
212 
213     std::vector<uint8_t>
214         generateICV(const std::vector<uint8_t>& input) const override;
215 };
216 
217 } // namespace rakp_auth
218 
219 } // namespace cipher
220