1 package org.metricshub.ipmi.core.coding.protocol.decoder;
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.protocol.IpmiMessage;
26 import org.metricshub.ipmi.core.coding.protocol.Ipmiv20Message;
27 import org.metricshub.ipmi.core.coding.protocol.PayloadType;
28 import org.metricshub.ipmi.core.coding.rmcp.RmcpMessage;
29 import org.metricshub.ipmi.core.coding.security.CipherSuite;
30 import org.metricshub.ipmi.core.coding.security.ConfidentialityNone;
31 import org.metricshub.ipmi.core.coding.security.SecurityConstants;
32 import org.metricshub.ipmi.core.common.TypeConverter;
33
34 import java.security.InvalidKeyException;
35 import java.security.MessageDigest;
36 import java.util.Arrays;
37
38
39
40
41 public class Protocolv20Decoder extends ProtocolDecoder {
42
43
44
45
46
47 private static final int HEADER_LENGTH = 12;
48
49 private CipherSuite cipherSuite;
50
51
52
53
54
55
56
57 public Protocolv20Decoder(CipherSuite cipherSuite) {
58 super();
59 this.cipherSuite = cipherSuite;
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 @Override
77 public IpmiMessage decode(RmcpMessage rmcpMessage) throws InvalidKeyException {
78 Ipmiv20Message message = new Ipmiv20Message(cipherSuite.getConfidentialityAlgorithm());
79
80 byte[] raw = rmcpMessage.getData();
81
82 checkLength(raw, HEADER_LENGTH);
83
84 message.setAuthenticationType(decodeAuthenticationType(raw[0]));
85
86 message.setPayloadEncrypted(decodeEncryption(raw[1]));
87
88 message.setPayloadAuthenticated(decodeAuthentication(raw[1]));
89
90 message.setPayloadType(decodePayloadType(raw[1]));
91
92 int offset = 2;
93
94 if (message.getPayloadType() == PayloadType.Oem) {
95 checkLength(raw, HEADER_LENGTH + 6);
96
97 message.setOemIANA(decodeOEMIANA(raw));
98 offset += 4;
99
100 message.setOemPayloadID(decodeOEMPayloadId(raw, offset));
101 offset += 2;
102 }
103
104 message.setSessionID(decodeSessionID(raw, offset));
105 offset += 4;
106
107 message.setSessionSequenceNumber(decodeSessionSequenceNumber(raw, offset));
108 offset += 4;
109
110 int payloadLength = decodePayloadLength(raw, offset);
111 offset += 2;
112
113 checkLength(raw, offset + payloadLength);
114
115
116
117 if (message.getSessionID() != 0 && cipherSuite.getIntegrityAlgorithm().getCode() != SecurityConstants.IA_NONE) {
118 if (!message.isPayloadAuthenticated()) {
119 throw new IllegalArgumentException("Unauthenticated message in a session with integrity protection");
120 }
121 int authCodeOffset = skipIntegrityPAD(raw, offset + payloadLength);
122 if (!validateAuthCode(raw, authCodeOffset)) {
123 throw new IllegalArgumentException("Integrity check failed");
124 }
125 message.setAuthCode(decodeAuthCode(raw, authCodeOffset));
126 }
127
128 if (message.isPayloadEncrypted()) {
129 message.setPayload(
130 decodePayload(raw, offset, payloadLength, message.getConfidentialityAlgorithm(), message.getPayloadType()));
131 } else {
132 message
133 .setPayload(decodePayload(raw, offset, payloadLength, new ConfidentialityNone(), message.getPayloadType()));
134 }
135
136 return message;
137 }
138
139
140
141
142
143
144
145
146 private static void checkLength(byte[] rawMessage, int length) {
147 if (rawMessage.length < length) {
148 throw new IllegalArgumentException("Message is truncated: " + rawMessage.length + " bytes, expected " + length);
149 }
150 }
151
152
153
154
155
156
157
158 private boolean decodeEncryption(byte payloadType) {
159 return (payloadType & TypeConverter.intToByte(0x80)) != 0;
160 }
161
162
163
164
165
166
167
168 public boolean decodeAuthentication(byte payloadType) {
169 return (payloadType & TypeConverter.intToByte(0x40)) != 0;
170 }
171
172 public static PayloadType decodePayloadType(byte payloadType) {
173 return PayloadType.parseInt(TypeConverter.intToByte(payloadType & TypeConverter.intToByte(0x3f)));
174 }
175
176
177
178
179
180
181
182
183 private int decodeOEMIANA(byte[] rawMessage) {
184 byte[] oemIANA = new byte[4];
185
186 System.arraycopy(rawMessage, 2, oemIANA, 0, 3);
187 oemIANA[3] = 0;
188
189 return TypeConverter.littleEndianByteArrayToInt(oemIANA);
190 }
191
192
193
194
195
196
197
198
199
200
201
202 protected Object decodeOEMPayloadId(byte[] rawMessage, int offset) {
203 byte[] oemPayload = new byte[2];
204
205 System.arraycopy(rawMessage, offset, oemPayload, 0, 2);
206
207 return oemPayload;
208 }
209
210 @Override
211 protected int decodePayloadLength(byte[] rawData, int offset) {
212 byte[] payloadLength = new byte[4];
213 System.arraycopy(rawData, offset, payloadLength, 0, 2);
214 payloadLength[2] = 0;
215 payloadLength[3] = 0;
216
217 return TypeConverter.littleEndianByteArrayToInt(payloadLength);
218 }
219
220
221
222
223
224
225
226
227
228
229
230
231
232 private int skipIntegrityPAD(final byte[] rawMessage, final int offset) {
233
234 int skip = 0;
235 while (skip < 3
236 && offset + skip < rawMessage.length
237 && TypeConverter.byteToInt(rawMessage[offset + skip]) == 0xff) {
238 ++skip;
239 }
240
241 int currentOffset = offset + skip + 2;
242 if (currentOffset >= rawMessage.length || TypeConverter.byteToInt(rawMessage[offset + skip]) != skip) {
243 throw new IllegalArgumentException("Message is corrupted.");
244 }
245 return currentOffset;
246 }
247
248
249
250
251
252
253
254
255
256
257 private byte[] decodeAuthCode(byte[] rawMessage, int offset) {
258 byte[] authCode = new byte[rawMessage.length - offset];
259 System.arraycopy(rawMessage, offset, authCode, 0, authCode.length);
260 return authCode;
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274 private boolean validateAuthCode(byte[] rawMessage, int offset) {
275 return MessageDigest.isEqual(
276 decodeAuthCode(rawMessage, offset),
277 cipherSuite.getIntegrityAlgorithm().generateAuthCode(Arrays.copyOf(rawMessage, offset)));
278 }
279
280
281
282
283
284
285
286
287 public static int decodeSessionID(RmcpMessage message) {
288 int offset = 2;
289 if (decodePayloadType(message.getData()[1]) == PayloadType.Oem) {
290 offset += 6;
291 }
292 return decodeSessionID(message.getData(), offset);
293 }
294 }