View Javadoc
1   package org.metricshub.ipmi.core.coding;
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.commands.IpmiVersion;
26  import org.metricshub.ipmi.core.coding.commands.ResponseData;
27  import org.metricshub.ipmi.core.coding.payload.IpmiPayload;
28  import org.metricshub.ipmi.core.coding.payload.lan.IPMIException;
29  import org.metricshub.ipmi.core.coding.protocol.AuthenticationType;
30  import org.metricshub.ipmi.core.coding.protocol.IpmiMessage;
31  import org.metricshub.ipmi.core.coding.protocol.Ipmiv15Message;
32  import org.metricshub.ipmi.core.coding.protocol.Ipmiv20Message;
33  import org.metricshub.ipmi.core.coding.protocol.PayloadType;
34  import org.metricshub.ipmi.core.coding.protocol.encoder.Protocolv20Encoder;
35  import org.metricshub.ipmi.core.coding.security.CipherSuite;
36  import org.metricshub.ipmi.core.coding.security.SecurityConstants;
37  
38  import java.security.InvalidKeyException;
39  import java.security.NoSuchAlgorithmException;
40  
41  /**
42   * Base class for {@link IpmiPayload} coders, used for encoding pyloads inside {@link IpmiMessage}s.
43   */
44  public abstract class PayloadCoder {
45  
46  	private IpmiVersion ipmiVersion;
47  
48  	/**
49  	 * Authentication type used. Default == None;
50  	 */
51  	private AuthenticationType authenticationType;
52  
53  	private CipherSuite cipherSuite;
54  
55  	public void setIpmiVersion(IpmiVersion ipmiVersion) {
56  		this.ipmiVersion = ipmiVersion;
57  	}
58  
59  	public IpmiVersion getIpmiVersion() {
60  		return ipmiVersion;
61  	}
62  
63  	public void setAuthenticationType(AuthenticationType authenticationType) {
64  		this.authenticationType = authenticationType;
65  	}
66  
67  	public AuthenticationType getAuthenticationType() {
68  		return authenticationType;
69  	}
70  
71  	public void setCipherSuite(CipherSuite cipherSuite) {
72  		this.cipherSuite = cipherSuite;
73  	}
74  
75  	public CipherSuite getCipherSuite() {
76  		return cipherSuite;
77  	}
78  
79  	public PayloadCoder() {
80  		setSessionParameters(IpmiVersion.V20, CipherSuite.getEmpty(), AuthenticationType.RMCPPlus);
81  	}
82  
83  	public PayloadCoder(IpmiVersion version, CipherSuite cipherSuite, AuthenticationType authenticationType) {
84  		setSessionParameters(version, cipherSuite, authenticationType);
85  	}
86  
87  	/**
88  	 * Sets session parameters.
89  	 *
90  	 * @param version
91  	 *        - IPMI version of the command.
92  	 * @param sessionCipherSuite
93  	 *        - {@link CipherSuite} containing authentication,
94  	 *        confidentiality and integrity algorithms for this session.
95  	 * @param sessionAuthenticationType
96  	 *        - Type of authentication used. Must be RMCPPlus for IPMI v2.0.
97  	 */
98  	public void setSessionParameters(
99  			IpmiVersion version,
100 			CipherSuite sessionCipherSuite,
101 			AuthenticationType sessionAuthenticationType) {
102 
103 		if (version == IpmiVersion.V20 && sessionAuthenticationType != AuthenticationType.RMCPPlus) {
104 			throw new IllegalArgumentException("Authentication Type must be RMCPPlus for IPMI v2.0 messages");
105 		}
106 
107 		setIpmiVersion(version);
108 		setAuthenticationType(sessionAuthenticationType);
109 		setCipherSuite(sessionCipherSuite);
110 	}
111 
112 	/**
113 	 * Prepares an IPMI request message containing class-specific payload.
114 	 *
115 	 * @param messageSequenceNumber
116 	 *        - A generated sequence number used for matching request and
117 	 *        response. For all IPMI messages,
118 	 *        messageSequenceNumber is used as a IPMI LAN Message sequence
119 	 *        number and as an IPMI payload message tag.
120 	 * @param sessionSequenceNumber
121 	 *        - If IPMI message is sent in a session, it is used as
122 	 *        a Session Sequence Number
123 	 * @param sessionId
124 	 *        - ID of the managed system's session message is being sent in.
125 	 *        For sessionless commands should b set to 0.
126 	 * @return IPMI message
127 	 * @throws NoSuchAlgorithmException
128 	 *         - when authentication, confidentiality or integrity algorithm
129 	 *         fails.
130 	 * @throws InvalidKeyException
131 	 *         - when creating of the algorithm key fails
132 	 */
133 	public IpmiMessage encodePayload(int messageSequenceNumber, int sessionSequenceNumber, int sessionId)
134 			throws NoSuchAlgorithmException,
135 			InvalidKeyException {
136 		if (getIpmiVersion() == IpmiVersion.V15) {
137 			return encodeV15Payload(messageSequenceNumber, sessionSequenceNumber, sessionId);
138 		} else /* IPMI version 2.0 */ {
139 			return encodeV20Payload(messageSequenceNumber, sessionSequenceNumber, sessionId);
140 		}
141 	}
142 
143 	private Ipmiv15Message encodeV15Payload(int messageSequenceNumber, int sessionSequenceNumber, int sessionId)
144 			throws NoSuchAlgorithmException,
145 			InvalidKeyException {
146 		Ipmiv15Message message = new Ipmiv15Message();
147 
148 		message.setAuthenticationType(getAuthenticationType());
149 
150 		message.setSessionID(sessionId);
151 
152 		message.setSessionSequenceNumber(sessionSequenceNumber);
153 
154 		message.setPayload(preparePayload(messageSequenceNumber));
155 
156 		return message;
157 	}
158 
159 	private Ipmiv20Message encodeV20Payload(int messageSequenceNumber, int sessionSequenceNumber, int sessionId)
160 			throws NoSuchAlgorithmException,
161 			InvalidKeyException {
162 		Ipmiv20Message message = new Ipmiv20Message(getCipherSuite().getConfidentialityAlgorithm());
163 
164 		message.setAuthenticationType(getAuthenticationType());
165 
166 		message.setSessionID(sessionId);
167 
168 		message.setSessionSequenceNumber(sessionSequenceNumber);
169 
170 		message.setPayloadType(getSupportedPayloadType());
171 
172 		message.setPayloadAuthenticated(getCipherSuite().getIntegrityAlgorithm().getCode() != SecurityConstants.IA_NONE);
173 
174 		message.setPayloadEncrypted(getCipherSuite().getConfidentialityAlgorithm().getCode() != SecurityConstants.CA_NONE);
175 
176 		message.setPayload(preparePayload(messageSequenceNumber));
177 
178 		message.setAuthCode(
179 				getCipherSuite().getIntegrityAlgorithm()
180 						.generateAuthCode(message.getIntegrityAlgorithmBase(new Protocolv20Encoder())));
181 
182 		return message;
183 	}
184 
185 	public abstract PayloadType getSupportedPayloadType();
186 
187 	/**
188 	 * Prepares {@link IpmiPayload} to be encoded. Called from
189 	 * {@link #encodePayload(int, int, int)}
190 	 *
191 	 * @param sequenceNumber
192 	 *        - sequenceNumber is used as an IPMI payload message tag
193 	 * @return IPMI payload
194 	 * @throws NoSuchAlgorithmException
195 	 *         - when authentication, confidentiality or integrity algorithm
196 	 *         fails.
197 	 * @throws InvalidKeyException
198 	 *         - when creating of the algorithm key fails
199 	 */
200 	protected abstract IpmiPayload preparePayload(int sequenceNumber)
201 			throws NoSuchAlgorithmException,
202 			InvalidKeyException;
203 
204 	/**
205 	 * Retrieves payload-specific response data from IPMI message
206 	 *
207 	 * @param message
208 	 *        - IPMI message
209 	 * @return response data
210 	 * @throws IllegalArgumentException
211 	 *         when message is not a response for class-specific command or
212 	 *         response has invalid length.
213 	 * @throws IPMIException
214 	 *         when response completion code isn't OK.
215 	 * @throws NoSuchAlgorithmException
216 	 *         when authentication, confidentiality or integrity algorithm
217 	 *         fails.
218 	 * @throws InvalidKeyException
219 	 *         when creating of the authentication algorithm key fails
220 	 */
221 	public abstract ResponseData getResponseData(IpmiMessage message)
222 			throws IPMIException,
223 			NoSuchAlgorithmException,
224 			InvalidKeyException;
225 
226 }