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