1 #include "rakp34.hpp"
2 
3 #include <algorithm>
4 #include <cstring>
5 #include <iostream>
6 
7 #include "comm_module.hpp"
8 #include "endian.hpp"
9 #include "guid.hpp"
10 #include "main.hpp"
11 
12 namespace command
13 {
14 
15 void applyIntegrityAlgo(const uint32_t bmcSessionID)
16 {
17     auto session = (std::get<session::Manager&>(singletonPool).getSession(
18             bmcSessionID)).lock();
19 
20     auto authAlgo = session->getAuthAlgo();
21 
22     switch (authAlgo->intAlgo)
23     {
24         case cipher::integrity::Algorithms::HMAC_SHA1_96:
25         {
26             session->setIntegrityAlgo(
27                     std::make_unique<cipher::integrity::AlgoSHA1>(
28                         authAlgo->sessionIntegrityKey));
29             break;
30         }
31         default:
32             break;
33     }
34 }
35 
36 void applyCryptAlgo(const uint32_t bmcSessionID)
37 {
38     auto session = (std::get<session::Manager&>(singletonPool).getSession(
39             bmcSessionID)).lock();
40 
41     auto authAlgo = session->getAuthAlgo();
42 
43     switch (authAlgo->cryptAlgo)
44     {
45         case cipher::crypt::Algorithms::AES_CBC_128:
46         {
47             session->setCryptAlgo(std::make_unique<cipher::crypt::AlgoAES128>(
48                                  authAlgo->sessionIntegrityKey));
49             break;
50         }
51         default:
52             break;
53     }
54 }
55 
56 std::vector<uint8_t> RAKP34(const std::vector<uint8_t>& inPayload,
57                             const message::Handler& handler)
58 {
59     std::cout << ">> RAKP34\n";
60 
61     std::vector<uint8_t> outPayload(sizeof(RAKP4response));
62     auto request = reinterpret_cast<const RAKP3request*>(inPayload.data());
63     auto response = reinterpret_cast<RAKP4response*>(outPayload.data());
64 
65     // Check if the RAKP3 Payload Length is as expected
66     if(inPayload.size() != sizeof(RAKP3request))
67     {
68         std::cerr << "RAKP34: Invalid RAKP3 request\n";
69         response->rmcpStatusCode =
70             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
71         return outPayload;
72     }
73 
74     // Session ID zero is reserved for Session Setup
75     if(endian::from_ipmi(request->managedSystemSessionID) ==
76                          session::SESSION_ZERO)
77     {
78         std::cerr << "RAKP34: BMC invalid Session ID\n";
79         response->rmcpStatusCode =
80             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
81         return outPayload;
82     }
83 
84     std::shared_ptr<session::Session> session;
85     try
86     {
87         session = (std::get<session::Manager&>(singletonPool).getSession(
88             endian::from_ipmi(request->managedSystemSessionID))).lock();
89     }
90     catch (std::exception& e)
91     {
92         std::cerr << e.what() << "\n";
93         response->rmcpStatusCode =
94             static_cast<uint8_t>(RAKP_ReturnCode::INVALID_SESSION_ID);
95         return outPayload;
96     }
97 
98     session->updateLastTransactionTime();
99 
100     auto authAlgo = session->getAuthAlgo();
101     /*
102      * Key Authentication Code - RAKP 3
103      *
104      * 1) Managed System Random Number - 16 bytes
105      * 2) Remote Console Session ID - 4 bytes
106      * 3) Session Privilege Level - 1 byte
107      * 4) User Name Length Byte - 1 byte (0 for 'null' username)
108      * 5) User Name - variable (absent for 'null' username)
109      */
110 
111     // Remote Console Session ID
112     auto rcSessionID = endian::to_ipmi(session->getRCSessionID());
113 
114     // Session Privilege Level
115     auto sessPrivLevel = static_cast<uint8_t>(session->curPrivLevel);
116 
117     // User Name Length Byte
118     uint8_t userLength = 0;
119 
120     std::vector<uint8_t> input;
121     input.resize(cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
122                  sizeof(rcSessionID) + sizeof(sessPrivLevel) +
123                  sizeof(userLength));
124 
125     auto iter = input.begin();
126 
127     // Managed System Random Number
128     std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
129               iter);
130     std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
131 
132     // Remote Console Session ID
133     std::copy_n(reinterpret_cast<uint8_t*>(&rcSessionID), sizeof(rcSessionID),
134                 iter);
135     std::advance(iter, sizeof(rcSessionID));
136 
137     // Session Privilege Level
138     std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
139                 sizeof(sessPrivLevel), iter);
140     std::advance(iter, sizeof(sessPrivLevel));
141 
142     // User Name Length Byte
143     std::copy_n(&userLength, sizeof(userLength), iter);
144 
145     // Generate Key Exchange Authentication Code - RAKP2
146     auto output = authAlgo->generateHMAC(input);
147 
148     if (std::memcmp(output.data(), request->keyExchangeAuthCode,
149                     output.size()))
150     {
151         std::cerr << "Mismatch in HMAC sent by remote console\n";
152 
153         response->messageTag = request->messageTag;
154         response->rmcpStatusCode = static_cast<uint8_t>
155                                    (RAKP_ReturnCode::INVALID_INTEGRITY_VALUE);
156         response->reserved = 0;
157         response->remoteConsoleSessionID = rcSessionID;
158 
159         //close the session
160         std::get<session::Manager&>(singletonPool).stopSession(
161             session->getBMCSessionID());
162 
163         return outPayload;
164     }
165 
166     /*
167      * Session Integrity Key
168      *
169      * 1) Remote Console Random Number - 16 bytes
170      * 2) Managed System Random Number - 16 bytes
171      * 3) Session Privilege Level - 1 byte
172      * 4) User Name Length Byte - 1 byte (0 for 'null' username)
173      * 5) User Name - variable (absent for 'null' username)
174      */
175 
176     input.clear();
177 
178     input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
179                  cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN +
180                  sizeof(sessPrivLevel) + sizeof(userLength));
181     iter = input.begin();
182 
183     // Remote Console Random Number
184     std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(),
185               iter);
186     std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
187 
188     // Managed Console Random Number
189     std::copy(authAlgo->bmcRandomNum.begin(), authAlgo->bmcRandomNum.end(),
190               iter);
191     std::advance(iter, cipher::rakp_auth::BMC_RANDOM_NUMBER_LEN);
192 
193     // Session Privilege Level
194     std::copy_n(reinterpret_cast<uint8_t*>(&sessPrivLevel),
195                 sizeof(sessPrivLevel), iter);
196     std::advance(iter, sizeof(sessPrivLevel));
197 
198     // User Name Length Byte
199     std::copy_n(&userLength, sizeof(userLength), iter);
200 
201     // Generate Session Integrity Key
202     auto sikOutput = authAlgo->generateHMAC(input);
203 
204     // Update the SIK in the Authentication Algo Interface
205     authAlgo->sessionIntegrityKey.insert(authAlgo->sessionIntegrityKey.begin(),
206                                          sikOutput.begin(), sikOutput.end());
207 
208     /*
209      * Integrity Check Value
210      *
211      * 1) Remote Console Random Number - 16 bytes
212      * 2) Managed System Session ID - 4 bytes
213      * 3) Managed System GUID - 16 bytes
214      */
215 
216     // Get Managed System Session ID
217     auto bmcSessionID = endian::to_ipmi(session->getBMCSessionID());
218 
219     input.clear();
220 
221     input.resize(cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN +
222                  sizeof(bmcSessionID) + BMC_GUID_LEN);
223     iter = input.begin();
224 
225     // Remote Console Random Number
226     std::copy(authAlgo->rcRandomNum.begin(), authAlgo->rcRandomNum.end(),
227               iter);
228     std::advance(iter, cipher::rakp_auth::REMOTE_CONSOLE_RANDOM_NUMBER_LEN);
229 
230     // Managed System Session ID
231     std::copy_n(reinterpret_cast<uint8_t*>(&bmcSessionID), sizeof(bmcSessionID),
232                 iter);
233     std::advance(iter, sizeof(bmcSessionID));
234 
235     // Managed System GUID
236     std::copy_n(cache::guid.data(), cache::guid.size(), iter);
237 
238     // Integrity Check Value
239     auto icv = authAlgo->generateICV(input);
240 
241     outPayload.resize(sizeof(RAKP4response));
242 
243     response->messageTag = request->messageTag;
244     response->rmcpStatusCode = static_cast<uint8_t>(RAKP_ReturnCode::NO_ERROR);
245     response->reserved = 0;
246     response->remoteConsoleSessionID = rcSessionID;
247 
248     // Insert the HMAC output into the payload
249     outPayload.insert(outPayload.end(), icv.begin(), icv.end());
250 
251     // Set the Integrity Algorithm
252     applyIntegrityAlgo(session->getBMCSessionID());
253 
254     // Set the Confidentiality Algorithm
255     applyCryptAlgo(session->getBMCSessionID());
256 
257     session->state = session::State::ACTIVE;
258 
259     std::cout << "<< RAKP34\n";
260     return outPayload;
261 }
262 
263 } // namespace command
264