1 package org.metricshub.ipmi.core.coding.protocol.encoder;
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.common.TypeConverter;
30
31 import java.security.InvalidKeyException;
32
33
34
35
36 public class Protocolv20Encoder extends ProtocolEncoder {
37
38
39
40
41
42
43
44
45
46
47 @Override
48 public byte[] encode(IpmiMessage ipmiMessage) throws InvalidKeyException {
49 if (!(ipmiMessage instanceof Ipmiv20Message)) {
50 throw new IllegalArgumentException(
51 "IPMIMessage must be in 2.0 version.");
52 }
53 Ipmiv20Message message = (Ipmiv20Message) ipmiMessage;
54
55 byte[] payload = message.getPayload().getEncryptedPayload();
56
57 if (payload == null) {
58 message.getPayload().encryptPayload(
59 message.getConfidentialityAlgorithm());
60 payload = message.getPayload().getEncryptedPayload();
61 }
62
63 byte[] raw = new byte[getMessageLength(message)];
64
65 if (message.getAuthenticationType() != AuthenticationType.RMCPPlus) {
66 throw new IllegalArgumentException(
67 "Authentication type must be RMCP+ for IPMI v2.0");
68 }
69
70 raw[0] = encodeAuthenticationType(message.getAuthenticationType());
71
72 int offset = 1;
73
74 raw[offset] = encodePayloadType(message.isPayloadEncrypted(),
75 message.isPayloadAuthenticated(), message.getPayloadType());
76
77 ++offset;
78
79 if (message.getPayloadType() == PayloadType.Oem) {
80 encodeOEMIANA(message.getOemIANA(), raw, offset);
81 offset += 4;
82
83 encodeOEMPayloadId(message.getOemPayloadID(), raw, offset);
84 offset += 2;
85 }
86
87 encodeSessionId(message.getSessionID(), raw, offset);
88 offset += 4;
89
90 encodeSessionSequenceNumber(message.getSessionSequenceNumber(), raw,
91 offset);
92 offset += 4;
93
94 encodePayloadLength(payload.length, raw, offset);
95 offset += 2;
96
97 offset = encodePayload(payload, raw, offset);
98
99 if (message.isPayloadAuthenticated() && message.getSessionID() != 0) {
100 encodeSessionTrailer(message.getAuthCode(), raw, offset);
101 }
102
103 return raw;
104 }
105
106
107
108
109
110
111
112 private int getMessageLength(Ipmiv20Message ipmiMessage) {
113 int length = 12
114 + ipmiMessage.getConfidentialityAlgorithm()
115 .getConfidentialityOverheadSize(
116 ipmiMessage.getPayloadLength())
117 + ipmiMessage.getPayloadLength();
118
119 if (ipmiMessage.getPayloadType() == PayloadType.Oem) {
120 length += 6;
121 }
122
123 if (ipmiMessage.isPayloadAuthenticated()
124 && ipmiMessage.getSessionID() != 0) {
125 if (ipmiMessage.getAuthCode() != null) {
126 if ((length + ipmiMessage.getAuthCode().length + 2) % 4 != 0) {
127 length += 4 - (length + ipmiMessage.getAuthCode().length + 2) % 4;
128 }
129 length += ipmiMessage.getAuthCode().length;
130 }
131 length += 2;
132 }
133
134 return length;
135 }
136
137 private byte encodePayloadType(boolean isEncrypted,
138 boolean isAuthenticated, PayloadType payloadType) {
139 byte result = 0;
140
141 if (isEncrypted) {
142 result |= TypeConverter.intToByte(0x80);
143 }
144
145 if (isAuthenticated) {
146 result |= TypeConverter.intToByte(0x40);
147 }
148
149 result |= TypeConverter.intToByte(payloadType.getCode());
150
151 return result;
152 }
153
154
155
156
157
158
159
160
161
162
163
164 private void encodeOEMIANA(int value, byte[] message, int offset) {
165 encodeInt(value, message, offset);
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 protected void encodeOEMPayloadId(Object value, byte[] message, int offset) {
183 byte[] oemId = null;
184 try {
185 oemId = (byte[]) value;
186 } catch (Exception e) {
187 throw new IllegalArgumentException("Value is corrupted", e);
188 }
189 if (oemId.length != 2) {
190 throw new IllegalArgumentException("Value has invalid length");
191 }
192 if (oemId.length + offset > message.length) {
193 throw new IndexOutOfBoundsException("Message is too short");
194 }
195
196 System.arraycopy(oemId, 0, message, offset, 2);
197 }
198
199 @Override
200 protected void encodePayloadLength(int value, byte[] message, int offset) {
201 byte[] payloadLength = TypeConverter.intToLittleEndianByteArray(value);
202 message[offset] = payloadLength[0];
203 message[offset + 1] = payloadLength[1];
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 private int encodeSessionTrailer(final byte[] authCode, final byte[] message, final int offset) {
223 int pad = 0;
224
225 if (authCode != null && authCode.length + offset > message.length) {
226 throw new IndexOutOfBoundsException("Message is too short");
227 }
228
229 if (authCode != null) {
230 pad = (offset + authCode.length + 2) % 4;
231 }
232
233 if (pad > 0) {
234 pad = 4 - pad;
235 } else {
236 pad = 0;
237 }
238
239 int currentOffset = offset;
240
241 for (int i = 0; i < pad; ++i) {
242 message[currentOffset] = TypeConverter.intToByte(0xff);
243 ++currentOffset;
244 }
245
246 message[currentOffset] = TypeConverter.intToByte(pad);
247 ++currentOffset;
248
249
250 message[currentOffset] = TypeConverter.intToByte(0x07);
251 ++currentOffset;
252 if (authCode != null) {
253 System.arraycopy(authCode, 0, message, currentOffset, authCode.length);
254 currentOffset += authCode.length;
255 }
256
257 return currentOffset;
258 }
259 }