1 package org.metricshub.ipmi.core.coding.security;
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.session.GetChannelCipherSuites;
26 import org.metricshub.ipmi.core.coding.commands.session.GetChannelCipherSuitesResponseData;
27 import org.metricshub.ipmi.core.common.TypeConverter;
28
29 import javax.crypto.Mac;
30 import javax.crypto.NoSuchPaddingException;
31 import java.security.InvalidKeyException;
32 import java.security.NoSuchAlgorithmException;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.function.Supplier;
36
37
38
39
40
41 public class CipherSuite {
42
43
44
45
46
47
48
49 @Deprecated
50 public static final String NOT_YET_IMPLEMENTED_MESSAGE = "Not yet implemented.";
51
52
53
54
55
56 private static final byte[][] STANDARD_SUITES = {
57 { SecurityConstants.AA_RAKP_NONE, SecurityConstants.CA_NONE, SecurityConstants.IA_NONE },
58 { SecurityConstants.AA_RAKP_HMAC_SHA1, SecurityConstants.CA_NONE, SecurityConstants.IA_NONE },
59 { SecurityConstants.AA_RAKP_HMAC_SHA1, SecurityConstants.CA_NONE, SecurityConstants.IA_HMAC_SHA1_96 },
60 { SecurityConstants.AA_RAKP_HMAC_SHA1, SecurityConstants.CA_AES_CBC128, SecurityConstants.IA_HMAC_SHA1_96 },
61 { SecurityConstants.AA_RAKP_HMAC_SHA1, SecurityConstants.CA_XRC4_128, SecurityConstants.IA_HMAC_SHA1_96 },
62 { SecurityConstants.AA_RAKP_HMAC_SHA1, SecurityConstants.CA_XRC4_40, SecurityConstants.IA_HMAC_SHA1_96 },
63 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_NONE, SecurityConstants.IA_NONE },
64 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_NONE, SecurityConstants.IA_HMAC_MD5_128 },
65 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_AES_CBC128, SecurityConstants.IA_HMAC_MD5_128 },
66 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_XRC4_128, SecurityConstants.IA_HMAC_MD5_128 },
67 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_XRC4_40, SecurityConstants.IA_HMAC_MD5_128 },
68 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_NONE, SecurityConstants.IA_MD5_128 },
69 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_AES_CBC128, SecurityConstants.IA_MD5_128 },
70 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_XRC4_128, SecurityConstants.IA_MD5_128 },
71 { SecurityConstants.AA_RAKP_HMAC_MD5, SecurityConstants.CA_XRC4_40, SecurityConstants.IA_MD5_128 },
72 { SecurityConstants.AA_RAKP_HMAC_SHA256, SecurityConstants.CA_NONE, SecurityConstants.IA_NONE },
73 { SecurityConstants.AA_RAKP_HMAC_SHA256, SecurityConstants.CA_NONE, SecurityConstants.IA_HMAC_SHA256_128 },
74 { SecurityConstants.AA_RAKP_HMAC_SHA256, SecurityConstants.CA_AES_CBC128, SecurityConstants.IA_HMAC_SHA256_128 },
75 { SecurityConstants.AA_RAKP_HMAC_SHA256, SecurityConstants.CA_XRC4_128, SecurityConstants.IA_HMAC_SHA256_128 },
76 { SecurityConstants.AA_RAKP_HMAC_SHA256, SecurityConstants.CA_XRC4_40, SecurityConstants.IA_HMAC_SHA256_128 } };
77
78 private byte id;
79
80 private byte authenticationAlgorithm;
81 private byte integrityAlgorithm;
82 private byte confidentialityAlgorithm;
83
84 private AuthenticationAlgorithm aa;
85 private ConfidentialityAlgorithm ca;
86 private IntegrityAlgorithm ia;
87
88
89
90
91 public byte getId() {
92 return id;
93 }
94
95
96
97
98
99 public byte getAuthenticationAlgorithmCode() {
100 return authenticationAlgorithm;
101 }
102
103
104
105
106
107 public byte getIntegrityAlgorithmCode() {
108 return integrityAlgorithm;
109 }
110
111
112
113
114
115 public byte getConfidentialityAlgorithmCode() {
116 return confidentialityAlgorithm;
117 }
118
119
120
121
122
123
124
125
126
127 public CipherSuite(byte id, byte authenticationAlgorithm, byte confidentialityAlgorithm, byte integrityAlgorithm) {
128 this.id = id;
129 this.authenticationAlgorithm = authenticationAlgorithm;
130 this.confidentialityAlgorithm = confidentialityAlgorithm;
131 this.integrityAlgorithm = integrityAlgorithm;
132 }
133
134
135
136
137
138
139
140 public CipherSuite copy() {
141 return new CipherSuite(id, authenticationAlgorithm, confidentialityAlgorithm, integrityAlgorithm);
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 public void initializeAlgorithms(byte[] sik)
161 throws InvalidKeyException,
162 NoSuchAlgorithmException,
163 NoSuchPaddingException {
164 if (integrityAlgorithm == SecurityConstants.IA_MD5_128) {
165 throw new IllegalArgumentException(
166 "Integrity algorithm MD5-128 is keyed with the password: use initializeAlgorithms(sik, password)");
167 }
168 initializeAlgorithms(sik, null);
169 }
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 public void initializeAlgorithms(byte[] sik, byte[] password)
188 throws InvalidKeyException,
189 NoSuchAlgorithmException,
190 NoSuchPaddingException {
191 IntegrityAlgorithm integrity = getIntegrityAlgorithm();
192 integrity.initialize(integrity.getCode() == SecurityConstants.IA_MD5_128 ? password : sik);
193 getConfidentialityAlgorithm().initialize(sik, getAuthenticationAlgorithm());
194 }
195
196
197
198
199
200
201
202
203 public AuthenticationAlgorithm getAuthenticationAlgorithm() {
204 if (aa != null && aa.getCode() != authenticationAlgorithm) {
205 throw new IllegalArgumentException("Invalid authentication algorithm code");
206 }
207 switch (authenticationAlgorithm) {
208 case SecurityConstants.AA_RAKP_NONE:
209 return instantiateAuthenticationAlgorithm(AuthenticationRakpNone::new);
210 case SecurityConstants.AA_RAKP_HMAC_SHA1:
211 return instantiateAuthenticationAlgorithm(AuthenticationRakpHmacSha1::new);
212 case SecurityConstants.AA_RAKP_HMAC_MD5:
213 return instantiateAuthenticationAlgorithm(AuthenticationRakpHmacMd5::new);
214 case SecurityConstants.AA_RAKP_HMAC_SHA256:
215 return instantiateAuthenticationAlgorithm(AuthenticationRakpHmacSha256::new);
216 default:
217 throw new IllegalArgumentException("Invalid authentication algorithm.");
218 }
219 }
220
221
222
223
224
225
226
227 public IntegrityAlgorithm getIntegrityAlgorithm() {
228 if (ia != null && ia.getCode() != integrityAlgorithm) {
229 throw new IllegalArgumentException("Invalid integrity algorithm code");
230 }
231 switch (integrityAlgorithm) {
232 case SecurityConstants.IA_NONE:
233 return instantiateIntegrityAlgorithm(IntegrityNone::new);
234 case SecurityConstants.IA_HMAC_SHA1_96:
235 return instantiateIntegrityAlgorithm(IntegrityHmacSha1_96::new);
236 case SecurityConstants.IA_MD5_128:
237 return instantiateIntegrityAlgorithm(IntegrityMd5_128::new);
238 case SecurityConstants.IA_HMAC_MD5_128:
239 return instantiateIntegrityAlgorithm(IntegrityHmacMd5_128::new);
240 case SecurityConstants.IA_HMAC_SHA256_128:
241 return instantiateIntegrityAlgorithm(IntegrityHmacSha256_128::new);
242 default:
243 throw new IllegalArgumentException("Invalid integrity algorithm.");
244 }
245 }
246
247
248
249
250
251
252
253
254 public ConfidentialityAlgorithm getConfidentialityAlgorithm() {
255 if (ca != null && ca.getCode() != confidentialityAlgorithm) {
256 throw new IllegalArgumentException("Invalid confidentiality algorithm code");
257 }
258 switch (confidentialityAlgorithm) {
259 case SecurityConstants.CA_NONE:
260 if (ca == null) {
261 ca = new ConfidentialityNone();
262 }
263 return ca;
264 case SecurityConstants.CA_AES_CBC128:
265 if (ca == null) {
266 ca = new ConfidentialityAesCbc128();
267 }
268 return ca;
269 case SecurityConstants.CA_XRC4_40:
270
271
272 throw new IllegalArgumentException("Confidentiality algorithm XRC4-40 is not implemented.");
273 case SecurityConstants.CA_XRC4_128:
274 throw new IllegalArgumentException("Confidentiality algorithm XRC4-128 is not implemented.");
275 default:
276 throw new IllegalArgumentException("Invalid confidentiality algorithm.");
277
278 }
279 }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300 public static List<CipherSuite> getCipherSuites(byte[] bytes) {
301 ArrayList<CipherSuite> suites = new ArrayList<CipherSuite>();
302
303 int offset = 0;
304
305 while (offset < bytes.length) {
306 int start = TypeConverter.byteToInt(bytes[offset]);
307 if (start != 0xC0 && start != 0xC1) {
308 ++offset;
309 continue;
310 }
311 int tags = offset + (start == 0xC0 ? 2 : 5);
312 if (tags >= bytes.length) {
313 break;
314 }
315 int id = TypeConverter.byteToInt(bytes[offset + 1]);
316
317 offset = tags;
318 while (offset < bytes.length && (bytes[offset] & 0xC0) != 0xC0) {
319 ++offset;
320 }
321 if (start == 0xC0 && id < STANDARD_SUITES.length) {
322 byte[] algorithms = STANDARD_SUITES[id];
323 suites.add(new CipherSuite((byte) id, algorithms[0], algorithms[1], algorithms[2]));
324 }
325 }
326
327 return suites;
328 }
329
330
331
332
333
334
335
336
337 public boolean isSupported() {
338 try {
339 getAuthenticationAlgorithm();
340 getIntegrityAlgorithm();
341 getConfidentialityAlgorithm();
342 return true;
343 } catch (IllegalArgumentException e) {
344 return false;
345 }
346 }
347
348
349
350
351
352
353 public static CipherSuite getEmpty() {
354 return new CipherSuite((byte) 0, (byte) 0, (byte) 0, (byte) 0);
355 }
356
357
358
359
360
361
362
363
364
365 public static CipherSuite forId(int id) {
366 if (id < 0 || id >= STANDARD_SUITES.length) {
367 throw new IllegalArgumentException("Cipher suite " + id + " is not a standard suite (0 to 19)");
368 }
369 byte[] algorithms = STANDARD_SUITES[id];
370 return new CipherSuite((byte) id, algorithms[0], algorithms[1], algorithms[2]);
371 }
372
373
374
375
376
377
378
379 public static String listIds(List<CipherSuite> suites) {
380 StringBuilder ids = new StringBuilder();
381 if (suites != null) {
382 for (CipherSuite suite : suites) {
383 ids.append(ids.length() == 0 ? "" : ", ").append(suite.getId());
384 }
385 }
386 return ids.length() == 0 ? "none" : ids.toString();
387 }
388
389
390
391
392
393
394 private AuthenticationAlgorithm instantiateAuthenticationAlgorithm(
395 final Supplier<AuthenticationAlgorithm> constructor) {
396 if (aa == null) {
397 aa = constructor.get();
398 }
399 return aa;
400 }
401
402
403
404
405
406
407
408
409
410
411 public void wipeKeys() {
412 if (ia != null) {
413 ia.wipeKey();
414 }
415 }
416
417 private IntegrityAlgorithm instantiateIntegrityAlgorithm(final Supplier<IntegrityAlgorithm> constructor) {
418 if (ia == null) {
419 ia = constructor.get();
420 }
421 return ia;
422 }
423
424
425
426
427
428
429
430 public static Mac newMacInstance(final String algorithmName) {
431 if (algorithmName == null || algorithmName.trim().isEmpty()) {
432 return null;
433 }
434 try {
435 return Mac.getInstance(algorithmName);
436 } catch (NoSuchAlgorithmException e) {
437 throw new IllegalArgumentException("Algorithm " + algorithmName + " is not available", e);
438 }
439 }
440 }