1 package org.metricshub.ipmi.core.coding.commands.session;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import org.metricshub.ipmi.core.coding.commands.IpmiCommandCoder;
26 import org.metricshub.ipmi.core.coding.commands.IpmiVersion;
27 import org.metricshub.ipmi.core.coding.commands.PrivilegeLevel;
28 import org.metricshub.ipmi.core.coding.payload.IpmiPayload;
29 import org.metricshub.ipmi.core.coding.payload.PlainMessage;
30 import org.metricshub.ipmi.core.coding.payload.lan.IPMIException;
31 import org.metricshub.ipmi.core.coding.payload.lan.NetworkFunction;
32 import org.metricshub.ipmi.core.coding.protocol.AuthenticationType;
33 import org.metricshub.ipmi.core.coding.protocol.IpmiMessage;
34 import org.metricshub.ipmi.core.coding.protocol.Ipmiv20Message;
35 import org.metricshub.ipmi.core.coding.protocol.PayloadType;
36 import org.metricshub.ipmi.core.coding.security.CipherSuite;
37 import org.metricshub.ipmi.core.coding.security.ConfidentialityNone;
38 import org.metricshub.ipmi.core.common.Randomizer;
39 import org.metricshub.ipmi.core.common.TypeConverter;
40
41 import java.nio.charset.StandardCharsets;
42 import java.security.InvalidKeyException;
43 import java.security.NoSuchAlgorithmException;
44 import java.util.Arrays;
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class Rakp1 extends IpmiCommandCoder<Rakp1ResponseData> {
59
60
61
62
63
64 private static final int MAX_KEY_LENGTH = 20;
65
66
67
68
69 private static final int MAX_USERNAME_LENGTH = 16;
70
71
72
73
74
75 private int managedSystemSessionId;
76
77
78
79
80 private byte[] consoleRandomNumber;
81
82 private PrivilegeLevel requestedMaximumPrivilegeLevel;
83
84
85
86
87
88 private String username;
89
90
91
92
93 private byte[] password;
94
95
96
97
98
99
100
101
102 private byte[] bmcKey;
103
104 public void setManagedSystemSessionId(int managedSystemSessionId) {
105 this.managedSystemSessionId = managedSystemSessionId;
106 }
107
108 public int getManagedSystemSessionId() {
109 return managedSystemSessionId;
110 }
111
112 public void setRequestedMaximumPrivilegeLevel(PrivilegeLevel requestedMaximumPrivilegeLevel) {
113 this.requestedMaximumPrivilegeLevel = requestedMaximumPrivilegeLevel;
114 }
115
116 public PrivilegeLevel getRequestedMaximumPrivilegeLevel() {
117 return requestedMaximumPrivilegeLevel;
118 }
119
120 public void setUsername(String username) {
121 checkLength("Username", username.getBytes(StandardCharsets.UTF_8), MAX_USERNAME_LENGTH);
122 this.username = username;
123 }
124
125
126
127
128
129
130
131
132
133
134 public static void checkCredentials(String username, byte[] password, byte[] bmcKey) {
135 checkLength("Username", username.getBytes(StandardCharsets.UTF_8), MAX_USERNAME_LENGTH);
136 checkLength("Password", password, MAX_KEY_LENGTH);
137 checkLength("BMC key", bmcKey, MAX_KEY_LENGTH);
138 }
139
140 private static void checkLength(String name, byte[] value, int maxLength) {
141 if (value != null && value.length > maxLength) {
142 throw new IllegalArgumentException(name + " is too long. Its length cannot exceed " + maxLength + " bytes");
143 }
144 }
145
146 public String getUsername() {
147 return username;
148 }
149
150
151
152
153 byte[] getUsernameBytes() {
154 return username.getBytes(StandardCharsets.UTF_8);
155 }
156
157 private void setPassword(byte[] password) {
158 checkLength("Password", password, MAX_KEY_LENGTH);
159 this.password = password;
160 }
161
162
163
164
165
166 byte[] getPasswordBytes() {
167 return password == null ? new byte[0] : password;
168 }
169
170 private void setConsoleRandomNumber(byte[] randomNumber) {
171 this.consoleRandomNumber = randomNumber;
172 }
173
174 public byte[] getConsoleRandomNumber() {
175 return consoleRandomNumber;
176 }
177
178 private void setBmcKey(byte[] bmcKey) {
179 checkLength("BMC key", bmcKey, MAX_KEY_LENGTH);
180 this.bmcKey = bmcKey;
181 }
182
183 public byte[] getBmcKey() {
184 return bmcKey;
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 public Rakp1(int managedSystemSessionId, PrivilegeLevel privilegeLevel, String username, byte[] password,
215 byte[] bmcKey, CipherSuite cipherSuite) {
216 setSessionParameters(IpmiVersion.V20, cipherSuite, AuthenticationType.RMCPPlus);
217 setManagedSystemSessionId(managedSystemSessionId);
218 setRequestedMaximumPrivilegeLevel(privilegeLevel);
219 setUsername(username);
220 setPassword(password);
221 this.setBmcKey(bmcKey);
222
223 setConsoleRandomNumber(Randomizer.getBytes(16));
224 }
225
226 @Override
227 public IpmiMessage encodePayload(int messageSequenceNumber, int sessionSequenceNumber, int sessionId) {
228 if (sessionId != 0) {
229 throw new IllegalArgumentException("Session ID must be 0");
230 }
231 Ipmiv20Message message = new Ipmiv20Message(new ConfidentialityNone());
232
233 message.setPayloadType(PayloadType.Rakp1);
234 message.setSessionID(0);
235 message.setSessionSequenceNumber(0);
236 message.setAuthenticationType(getAuthenticationType());
237 message.setPayloadAuthenticated(false);
238 message.setPayloadEncrypted(false);
239
240 message.setPayload(preparePayload(messageSequenceNumber));
241
242 return message;
243 }
244
245 @Override
246 protected IpmiPayload preparePayload(int sequenceNumber) {
247 byte[] usernameBytes = getUsernameBytes();
248 byte[] payload = new byte[28 + usernameBytes.length];
249
250
251 payload[0] = TypeConverter.intToByte(sequenceNumber);
252
253 payload[1] = 0;
254 payload[2] = 0;
255 payload[3] = 0;
256
257 byte[] sId = TypeConverter.intToLittleEndianByteArray(getManagedSystemSessionId());
258
259 System.arraycopy(sId, 0, payload, 4, 4);
260
261 System.arraycopy(consoleRandomNumber, 0, payload, 8, 16);
262
263
264
265
266 payload[24] = TypeConverter.intToByte(encodePrivilegeLevel(requestedMaximumPrivilegeLevel) | 0x10);
267
268 payload[25] = 0;
269 payload[26] = 0;
270
271 payload[27] = TypeConverter.intToByte(usernameBytes.length);
272 System.arraycopy(usernameBytes, 0, payload, 28, usernameBytes.length);
273
274 return new PlainMessage(payload);
275 }
276
277 @Override
278 public byte getCommandCode() {
279 return 0;
280 }
281
282 @Override
283 public NetworkFunction getNetworkFunction() {
284 return null;
285 }
286
287
288
289
290
291
292
293
294
295
296
297 @Override
298 public Rakp1ResponseData getResponseData(IpmiMessage message)
299 throws IPMIException,
300 NoSuchAlgorithmException,
301 InvalidKeyException {
302 if (!isCommandResponse(message)) {
303 throw new IllegalArgumentException("This is not RAKP 2 message!");
304 }
305
306 byte[] payload = validateSessionSetupResponse(message);
307
308 Rakp1ResponseData data = new Rakp1ResponseData();
309
310 data.setMessageTag(payload[0]);
311
312 data.setStatusCode(payload[1]);
313
314 int length = getCipherSuite().getAuthenticationAlgorithm().getKeyLength();
315
316 if (payload.length < 40 + length) {
317 throw new IllegalArgumentException("Invalid payload length");
318 }
319
320 byte[] buffer = new byte[4];
321
322 System.arraycopy(payload, 4, buffer, 0, 4);
323
324 data.setRemoteConsoleSessionId(TypeConverter.littleEndianByteArrayToInt(buffer));
325
326 byte[] managedSystemGuid = new byte[16];
327
328 System.arraycopy(payload, 24, managedSystemGuid, 0, 16);
329
330 data.setManagedSystemGuid(managedSystemGuid);
331
332 byte[] managedSystemRandomNumber = new byte[16];
333
334 System.arraycopy(payload, 8, managedSystemRandomNumber, 0, 16);
335
336 data.setManagedSystemRandomNumber(managedSystemRandomNumber);
337
338 byte[] key = Arrays.copyOfRange(payload, 40, 40 + length);
339
340 if (!getCipherSuite().getAuthenticationAlgorithm()
341 .checkKeyExchangeAuthenticationCode(prepareKeyExchangeAuthenticationCodeBase(data), key, getPasswordBytes())) {
342 throw new AuthenticationException("Authentication check failed");
343 }
344
345 return data;
346 }
347
348
349
350
351
352 private byte[] prepareKeyExchangeAuthenticationCodeBase(Rakp1ResponseData responseData) {
353 byte[] usernameBytes = getUsernameBytes();
354 byte[] keac = new byte[58 + usernameBytes.length];
355
356 byte[] rSID = TypeConverter.intToLittleEndianByteArray(responseData.getRemoteConsoleSessionId());
357
358 System.arraycopy(rSID, 0, keac, 0, 4);
359
360 byte[] mSID = TypeConverter.intToLittleEndianByteArray(getManagedSystemSessionId());
361
362 System.arraycopy(mSID, 0, keac, 4, 4);
363
364 System.arraycopy(getConsoleRandomNumber(), 0, keac, 8, 16);
365
366 System.arraycopy(responseData.getManagedSystemRandomNumber(), 0, keac, 24, 16);
367
368 System.arraycopy(responseData.getManagedSystemGuid(), 0, keac, 40, 16);
369
370 keac[56] = TypeConverter.intToByte(encodePrivilegeLevel(requestedMaximumPrivilegeLevel) | 0x10);
371
372 keac[57] = TypeConverter.intToByte(usernameBytes.length);
373 System.arraycopy(usernameBytes, 0, keac, 58, usernameBytes.length);
374
375 return keac;
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389
390 public byte[] calculateSik(Rakp1ResponseData responseData) throws InvalidKeyException, NoSuchAlgorithmException {
391 byte[] key = null;
392
393 if (getBmcKey() == null || Arrays.equals(getBmcKey(), new byte[getBmcKey().length])) {
394 key = getPasswordBytes();
395 } else {
396 key = getBmcKey();
397 }
398
399 return getCipherSuite().getAuthenticationAlgorithm()
400 .getKeyExchangeAuthenticationCode(prepareSikBase(responseData), key);
401 }
402
403
404
405
406
407 private byte[] prepareSikBase(Rakp1ResponseData responseData) {
408 byte[] usernameBytes = getUsernameBytes();
409 byte[] sikBase = new byte[34 + usernameBytes.length];
410
411 System.arraycopy(getConsoleRandomNumber(), 0, sikBase, 0, 16);
412
413 System.arraycopy(responseData.getManagedSystemRandomNumber(), 0, sikBase, 16, 16);
414
415 sikBase[32] = TypeConverter.intToByte(encodePrivilegeLevel(requestedMaximumPrivilegeLevel) | 0x10);
416
417 sikBase[33] = TypeConverter.intToByte(usernameBytes.length);
418 System.arraycopy(usernameBytes, 0, sikBase, 34, usernameBytes.length);
419
420 return sikBase;
421 }
422
423 @Override
424 public boolean isCommandResponse(IpmiMessage message) {
425 return message instanceof Ipmiv20Message && ((Ipmiv20Message) message).getPayloadType() == PayloadType.Rakp2;
426 }
427 }