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