1use std::num::NonZeroU32;
2
3use ipmi_rs_core::app::auth::{
4 AuthenticationAlgorithm, ConfidentialityAlgorithm, IntegrityAlgorithm,
5};
6
7use crate::app::auth::PrivilegeLevel;
8
9use super::RakpErrorStatusCode;
10
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum AlgorithmPayloadError {
13 IncorrectDataLen,
14 IncorrectPayloadLenValue,
15 UnknownAuthAlgorithm(u8),
16 UnknownIntegrityAlgorithm(u8),
17 UnknownConfidentialityAlgorithm(u8),
18 UnknownPayloadType(u8),
19}
20
21#[derive(Debug, Clone, Copy)]
22pub enum AlgorithmPayload {
23 Authentication(AuthenticationAlgorithm),
24 Integrity(IntegrityAlgorithm),
25 Confidentiality(ConfidentialityAlgorithm),
26}
27
28impl AlgorithmPayload {
29 pub fn write(&self, null_byte: bool, buffer: &mut Vec<u8>) {
30 let (ty, value) = match *self {
31 Self::Authentication(a) => (0x00, u8::from(a)),
32 Self::Integrity(i) => (0x01, u8::from(i)),
33 Self::Confidentiality(c) => (0x02, u8::from(c)),
34 };
35
36 assert!((value & 0xC0) == 0);
38
39 buffer.push(ty);
41
42 buffer.extend_from_slice(&[0x00, 0x00]);
44
45 if null_byte {
47 buffer.push(0x00);
48 } else {
49 buffer.push(0x08);
50 }
51
52 buffer.push(value);
54
55 buffer.extend_from_slice(&[0x00, 0x00, 0x00]);
57 }
58
59 pub fn from_data(data: &[u8]) -> Result<Self, AlgorithmPayloadError> {
60 use AlgorithmPayloadError::{
61 IncorrectDataLen, IncorrectPayloadLenValue, UnknownAuthAlgorithm,
62 UnknownConfidentialityAlgorithm, UnknownIntegrityAlgorithm, UnknownPayloadType,
63 };
64
65 if data.len() != 8 {
66 return Err(IncorrectDataLen);
67 }
68
69 let ty = data[0];
70 let payload_len = data[3];
71
72 if payload_len != 8 {
73 return Err(IncorrectPayloadLenValue);
74 }
75
76 let algo = data[4];
77
78 match ty {
79 0x00 => {
80 let algo = AuthenticationAlgorithm::try_from(algo)
81 .map_err(|_| UnknownAuthAlgorithm(algo))?;
82 Ok(Self::Authentication(algo))
83 }
84 0x01 => {
85 let algo = IntegrityAlgorithm::try_from(algo)
86 .map_err(|_| UnknownIntegrityAlgorithm(algo))?;
87 Ok(Self::Integrity(algo))
88 }
89 0x02 => {
90 let algo = ConfidentialityAlgorithm::try_from(algo)
91 .map_err(|_| UnknownConfidentialityAlgorithm(algo))?;
92 Ok(Self::Confidentiality(algo))
93 }
94 _ => Err(UnknownPayloadType(ty)),
95 }
96 }
97}
98
99#[derive(Debug, Clone)]
100pub struct OpenSessionRequest {
101 pub message_tag: u8,
102 pub requested_max_privilege: Option<PrivilegeLevel>,
103 pub remote_console_session_id: NonZeroU32,
104 pub authentication_algorithms: AuthenticationAlgorithm,
108 pub integrity_algorithms: IntegrityAlgorithm,
109 pub confidentiality_algorithms: ConfidentialityAlgorithm,
110}
111
112impl OpenSessionRequest {
113 pub fn write_data(&self, buffer: &mut Vec<u8>) {
114 buffer.push(self.message_tag);
115 buffer.push(self.requested_max_privilege.map_or(0, Into::into));
116
117 buffer.push(0);
119 buffer.push(0);
120
121 buffer.extend_from_slice(&self.remote_console_session_id.get().to_le_bytes());
122
123 AlgorithmPayload::Authentication(self.authentication_algorithms).write(false, buffer);
124 AlgorithmPayload::Integrity(self.integrity_algorithms).write(false, buffer);
125 AlgorithmPayload::Confidentiality(self.confidentiality_algorithms).write(false, buffer);
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq)]
130pub enum ParseSessionResponseError {
131 NotEnoughData,
132 HaveErrorCode(Result<OpenSessionResponseErrorStatusCode, u8>),
133 ZeroRemoteConsoleSessionId,
134 ZeroManagedSystemSessionId,
135 InvalidPrivilegeLevel(u8),
136 InvalidAlgorithmPayload,
137 AuthPayloadWasNonAuthAlgorithm,
138 IntegrityPayloadWasNonIntegrityAlgorithm,
139 ConfidentialityPayloadWasNonConfidentialityAlgorithm,
140 AlgorithmPayloadError(AlgorithmPayloadError),
141}
142
143#[derive(Debug, Clone, PartialEq)]
144pub struct OpenSessionResponse {
145 pub message_tag: u8,
146 pub maximum_privilege_level: PrivilegeLevel,
147 pub remote_console_session_id: NonZeroU32,
148 pub managed_system_session_id: NonZeroU32,
149 pub authentication_payload: AuthenticationAlgorithm,
150 pub integrity_payload: IntegrityAlgorithm,
151 pub confidentiality_payload: ConfidentialityAlgorithm,
152}
153
154impl OpenSessionResponse {
155 pub fn from_data(data: &[u8]) -> Result<Self, ParseSessionResponseError> {
156 use ParseSessionResponseError::{
157 AlgorithmPayloadError, AuthPayloadWasNonAuthAlgorithm,
158 ConfidentialityPayloadWasNonConfidentialityAlgorithm, HaveErrorCode,
159 IntegrityPayloadWasNonIntegrityAlgorithm, InvalidPrivilegeLevel, NotEnoughData,
160 ZeroManagedSystemSessionId, ZeroRemoteConsoleSessionId,
161 };
162
163 if data.len() < 2 {
164 return Err(NotEnoughData);
165 }
166
167 let message_tag = data[0];
168 let status_code = data[1];
169
170 if status_code != 00 {
171 return Err(HaveErrorCode(
172 OpenSessionResponseErrorStatusCode::try_from(status_code).map_err(|_| status_code),
173 ));
174 }
175
176 if data.len() != 36 {
177 return Err(NotEnoughData);
178 }
179
180 let max_privilege_level =
181 PrivilegeLevel::try_from(data[2]).map_err(|_| InvalidPrivilegeLevel(data[2]))?;
182
183 let remote_console_session_id = u32::from_le_bytes(data[4..8].try_into().unwrap());
184 let remote_console_session_id =
185 NonZeroU32::new(remote_console_session_id).ok_or(ZeroRemoteConsoleSessionId)?;
186 let managed_system_session_id = u32::from_le_bytes(data[8..12].try_into().unwrap());
187 let managed_system_session_id =
188 NonZeroU32::new(managed_system_session_id).ok_or(ZeroManagedSystemSessionId)?;
189
190 let authentication_payload =
191 match AlgorithmPayload::from_data(&data[12..20]).map_err(AlgorithmPayloadError)? {
192 AlgorithmPayload::Authentication(a) => a,
193 _ => return Err(AuthPayloadWasNonAuthAlgorithm),
194 };
195
196 let integrity_payload =
197 match AlgorithmPayload::from_data(&data[20..28]).map_err(AlgorithmPayloadError)? {
198 AlgorithmPayload::Integrity(i) => i,
199 _ => return Err(IntegrityPayloadWasNonIntegrityAlgorithm),
200 };
201
202 let confidentiality_payload =
203 match AlgorithmPayload::from_data(&data[28..36]).map_err(AlgorithmPayloadError)? {
204 AlgorithmPayload::Confidentiality(c) => c,
205 _ => return Err(ConfidentialityPayloadWasNonConfidentialityAlgorithm),
206 };
207
208 Ok(Self {
209 message_tag,
210 maximum_privilege_level: max_privilege_level,
211 remote_console_session_id,
212 managed_system_session_id,
213 authentication_payload,
214 integrity_payload,
215 confidentiality_payload,
216 })
217 }
218}
219
220#[derive(Debug, Clone, Copy, PartialEq)]
221#[repr(u8)]
222pub enum OpenSessionResponseErrorStatusCode {
223 CommonRakp(RakpErrorStatusCode),
224 InvalidPayloadType = 0x03,
225 InvalidAuthenticationAlgorithm = 0x04,
226 InvalidIntegrityAlgorithm = 0x05,
227 InvalidConfidentialityAlgorithm = 0x10,
228 NoMatchingAuthenticationPayload = 0x06,
229 NoMatchingIntegrityPayload = 0x07,
230 NoMatchingCipherSuite = 0x011,
231 InvalidRole = 0x09,
232}
233
234impl TryFrom<u8> for OpenSessionResponseErrorStatusCode {
235 type Error = ();
236
237 fn try_from(value: u8) -> Result<Self, Self::Error> {
238 use OpenSessionResponseErrorStatusCode::*;
239
240 if let Ok(common) = TryFrom::try_from(value) {
241 return Ok(CommonRakp(common));
242 }
243
244 let value = match value {
245 0x03 => InvalidPayloadType,
246 0x04 => InvalidAuthenticationAlgorithm,
247 0x05 => InvalidIntegrityAlgorithm,
248 0x10 => InvalidConfidentialityAlgorithm,
249 0x06 => NoMatchingAuthenticationPayload,
250 0x07 => NoMatchingIntegrityPayload,
251 0x11 => NoMatchingCipherSuite,
252 0x09 => InvalidRole,
253 _ => return Err(()),
254 };
255
256 Ok(value)
257 }
258}
259
260#[test]
261pub fn from_data() {
262 let data = [
263 0x00, 0x00, 0x04, 0x00, 0xa4, 0xa3, 0xa2, 0x0a, 0xe0, 0x34, 0x71, 0x4a, 0x00, 0x00, 0x00,
264 0x08, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00,
265 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
266 ];
267
268 let message = OpenSessionResponse::from_data(&data).unwrap();
269
270 let expected = OpenSessionResponse {
271 message_tag: 0x00,
272 maximum_privilege_level: PrivilegeLevel::Administrator,
273 remote_console_session_id: NonZeroU32::new(0x0aa2a3a4).unwrap(),
274 managed_system_session_id: NonZeroU32::new(0x4a7134e0).unwrap(),
275 authentication_payload: AuthenticationAlgorithm::RakpHmacSha1,
276 integrity_payload: IntegrityAlgorithm::HmacSha1_96,
277 confidentiality_payload: ConfidentialityAlgorithm::None,
278 };
279
280 assert_eq!(message, expected);
281}