1 package org.metricshub.ipmi.core.coding.protocol.decoder;
2
3 /*-
4 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5 * IPMI Java Client
6 * ჻჻჻჻჻჻
7 * Copyright 2023 Verax Systems, MetricsHub
8 * ჻჻჻჻჻჻
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Lesser Public License for more details.
18 *
19 * You should have received a copy of the GNU General Lesser Public
20 * License along with this program. If not, see
21 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23 */
24
25 import org.metricshub.ipmi.core.coding.payload.IpmiPayload;
26 import org.metricshub.ipmi.core.coding.payload.lan.IpmiLanResponse;
27 import org.metricshub.ipmi.core.coding.payload.sol.SolInboundMessage;
28 import org.metricshub.ipmi.core.coding.protocol.AuthenticationType;
29 import org.metricshub.ipmi.core.coding.protocol.IpmiMessage;
30 import org.metricshub.ipmi.core.coding.protocol.PayloadType;
31 import org.metricshub.ipmi.core.coding.rmcp.RmcpClassOfMessage;
32 import org.metricshub.ipmi.core.coding.rmcp.RmcpMessage;
33 import org.metricshub.ipmi.core.coding.security.ConfidentialityAlgorithm;
34 import org.metricshub.ipmi.core.common.TypeConverter;
35
36 /**
37 * Decodes IPMI session header and retrieves encrypted payload. Payload must be
38 * IPMI LAN format message.
39 */
40 public abstract class ProtocolDecoder implements IpmiDecoder {
41
42 public ProtocolDecoder() {
43
44 }
45
46 /**
47 * Decodes IPMI message version independent fields.
48 *
49 * @param rmcpMessage
50 * - RMCP message to decode.
51 * @param message
52 * - A reference to message being decoded.
53 * @param sequenceNumberOffset
54 * - Protocol version specific offset to Session Sequence Number
55 * field in the header of the IPMI message.
56 * @param payloadLengthOffset
57 * - Protocol version specific offset to IPMI Payload Length
58 * field in the header of the IPMI message.
59 * @param payloadLengthLength
60 * - Length of the payload length field.
61 * @see IpmiMessage
62 * @return Offset to the session trailer.
63 * @throws IllegalArgumentException
64 * when delivered RMCP message does not contain encapsulated
65 * IPMI message.
66 * @deprecated this method is obolete. use {@link ProtocolDecoder#decode(RmcpMessage)} instead
67 */
68 @Deprecated
69 protected int decode(
70 RmcpMessage rmcpMessage,
71 IpmiMessage message,
72 int sequenceNumberOffset,
73 int payloadLengthOffset,
74 int payloadLengthLength) {
75
76 if (rmcpMessage.getClassOfMessage() != RmcpClassOfMessage.Ipmi) {
77 throw new IllegalArgumentException("This is not an IPMI message");
78 }
79
80 byte[] raw = rmcpMessage.getData();
81
82 message.setAuthenticationType(decodeAuthenticationType(raw[0]));
83
84 message.setSessionSequenceNumber(decodeSessionSequenceNumber(raw, sequenceNumberOffset));
85
86 message.setPayloadLength(decodePayloadLength(raw, payloadLengthOffset));
87
88 message.setPayload(
89 decodePayload(
90 raw,
91 payloadLengthOffset + payloadLengthLength,
92 message.getPayloadLength(),
93 message.getConfidentialityAlgorithm(),
94 PayloadType.Ipmi));
95
96 return payloadLengthOffset + payloadLengthLength + message.getPayloadLength();
97 }
98
99 protected static AuthenticationType decodeAuthenticationType(byte authenticationType) {
100 authenticationType &= TypeConverter.intToByte(0x0f);
101
102 return AuthenticationType.parseInt(TypeConverter.byteToInt(authenticationType));
103 }
104
105 /**
106 * Decodes {@link AuthenticationType} of the message so that the version of
107 * the IPMI protocol could be determined.
108 *
109 * @param message
110 * - RMCP message to decode.
111 * @return {@link AuthenticationType} of the message.
112 */
113 public static AuthenticationType decodeAuthenticationType(RmcpMessage message) {
114 return decodeAuthenticationType(message.getData()[0]);
115 }
116
117 /**
118 * Decodes int in a little endian convention from raw message at given
119 * offset
120 *
121 * @param rawMessage
122 * - Raw message to be decoded
123 * @param offset
124 * @return Decoded integer
125 */
126 protected static int decodeInt(byte[] rawMessage, int offset) {
127 byte[] result = new byte[4];
128
129 System.arraycopy(rawMessage, offset, result, 0, 4);
130
131 return TypeConverter.littleEndianByteArrayToInt(result);
132 }
133
134 /**
135 * Decodes session sequence number.
136 *
137 * @param rawMessage
138 * - Byte array holding whole message data.
139 * @param offset
140 * - Offset to session sequence number in header.
141 * @return Session Sequence number.
142 */
143 protected int decodeSessionSequenceNumber(byte[] rawMessage, int offset) {
144 return decodeInt(rawMessage, offset);
145 }
146
147 /**
148 * Decodes session ID.
149 *
150 * @param rawMessage
151 * - Byte array holding whole message data.
152 * @param offset
153 * - Offset to session ID in header.
154 * @return Session ID.
155 */
156 protected static int decodeSessionID(byte[] rawMessage, int offset) {
157 return decodeInt(rawMessage, offset);
158 }
159
160 /**
161 * Decodes payload length.
162 *
163 * @param rawData
164 * - Byte array holding whole message data.
165 * @param offset
166 * - Offset to payload length in header.
167 * @return payload length.
168 */
169 protected abstract int decodePayloadLength(byte[] rawData, int offset);
170
171 /**
172 * Copies the payload out of the message and decrypts it.
173 *
174 * @param rawData
175 * - Byte array holding whole message data.
176 * @param offset
177 * - Offset to payload.
178 * @param length
179 * - Length of the payload.
180 * @param confidentialityAlgorithm
181 * - {@link ConfidentialityAlgorithm} required to decrypt
182 * payload.
183 * @return the decrypted payload
184 * @throws IllegalArgumentException
185 * when the payload is empty or cannot be decrypted
186 */
187 protected static byte[] decryptPayload(
188 byte[] rawData,
189 int offset,
190 int length,
191 ConfidentialityAlgorithm confidentialityAlgorithm) {
192 if (length <= 0) {
193 throw new IllegalArgumentException("Empty payload");
194 }
195 byte[] payload = new byte[length];
196
197 System.arraycopy(rawData, offset, payload, 0, length);
198
199 return confidentialityAlgorithm.decrypt(payload);
200 }
201
202 /**
203 * Decodes payload.
204 *
205 * @param rawData
206 * - Byte array holding whole message data.
207 * @param offset
208 * - Offset to payload.
209 * @param length
210 * - Length of the payload.
211 * @param confidentialityAlgorithm
212 * - {@link ConfidentialityAlgorithm} required to decrypt
213 * payload.
214 * @return Payload decoded into {@link IpmiLanResponse}.
215 * @throws IllegalArgumentException
216 * when the payload is empty
217 */
218 protected IpmiPayload decodePayload(
219 byte[] rawData,
220 int offset,
221 int length,
222 ConfidentialityAlgorithm confidentialityAlgorithm,
223 PayloadType payloadType) {
224 byte[] payload = decryptPayload(rawData, offset, length, confidentialityAlgorithm);
225
226 if (payloadType == PayloadType.Sol) {
227 return new SolInboundMessage(payload);
228 } else {
229 return new IpmiLanResponse(payload);
230 }
231 }
232 }