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