1use crate::connection::{
2 rmcp::{AuthenticationAlgorithm, ConfidentialityAlgorithm, IntegrityAlgorithm},
3 Channel, IpmiCommand, Message, NetFn, ParseResponseError,
4};
5
6#[derive(Debug, Clone)]
7pub struct GetChannelCipherSuites {
8 channel: Channel,
9 list_index: u8,
10}
11
12impl From<GetChannelCipherSuites> for Message {
13 fn from(value: GetChannelCipherSuites) -> Self {
14 Message::new_request(
15 NetFn::App,
16 0x54,
17 vec![value.channel.value(), 0x00, value.list_index],
18 )
19 }
20}
21
22impl GetChannelCipherSuites {
23 pub fn new(channel: Channel, list_index: u8) -> Option<Self> {
27 if list_index > 0x3F {
28 None
29 } else {
30 Some(Self {
31 channel,
32 list_index,
33 })
34 }
35 }
36}
37
38pub struct ChannelCipherSuites {
39 data_length: usize,
40 record_data: [u8; 16],
41}
42
43impl ChannelCipherSuites {
44 pub fn parse_full_data(data: &[u8]) -> impl Iterator<Item = CipherSuite> + '_ {
45 struct Iter<'a> {
46 inner: core::slice::Iter<'a, u8>,
47 failed: bool,
48 }
49
50 impl Iterator for Iter<'_> {
51 type Item = CipherSuite;
52
53 fn next(&mut self) -> Option<Self::Item> {
54 if self.failed {
55 return None;
56 }
57
58 let start_of_record = *self.inner.next()?;
59
60 if start_of_record == 0xC0 {
61 let id = if let Some(id) = self.inner.next() {
62 *id
63 } else {
64 log::warn!("Got correct start of record, but missing Cipher Suite ID.");
65 return None;
66 };
67
68 let suite = if let Some(suite) = CipherSuite::from_id(id) {
69 suite
70 } else {
71 log::warn!("Unknown cipher suite ID: 0x{id:02X?}");
72 return None;
73 };
74
75 if let Some(auth) = self.inner.next() {
76 *auth & 0x3F
77 } else {
78 log::warn!("Got correct start of record, but missing Authentication Algorithm Number.");
79 return None;
80 };
81
82 if let Some(integ) = self.inner.next() {
83 *integ & 0x3F
84 } else {
85 log::warn!(
86 "Got correct start of record, but missing Integrity Algorithm Number."
87 );
88 return None;
89 };
90
91 if let Some(conf) = self.inner.next() {
92 *conf & 0x3F
93 } else {
94 log::warn!("Got correct start of record, but missing Confidentiality Algorithm Number.");
95 return None;
96 };
97
98 Some(suite)
99 } else {
100 log::debug!("Got a non-standard start of record: {start_of_record:02X}");
101 self.failed = true;
102 None
103 }
104 }
105 }
106
107 Iter {
108 inner: data.iter(),
109 failed: false,
110 }
111 }
112}
113
114impl core::ops::Deref for ChannelCipherSuites {
115 type Target = [u8];
116
117 fn deref(&self) -> &Self::Target {
118 &self.record_data[..self.data_length]
119 }
120}
121
122impl IpmiCommand for GetChannelCipherSuites {
123 type Output = ChannelCipherSuites;
124
125 type Error = ();
126
127 fn parse_response(
128 completion_code: crate::connection::CompletionCode,
129 data: &[u8],
130 ) -> Result<Self::Output, ParseResponseError<Self::Error>> {
131 Self::check_cc_success(completion_code)?;
132
133 if data.len() > 16 {
134 return Err(ParseResponseError::Parse(()));
135 }
136
137 let mut record_data = [0u8; 16];
138 record_data[..data.len()].copy_from_slice(data);
139
140 Ok(ChannelCipherSuites {
141 record_data,
142 data_length: data.len(),
143 })
144 }
145}
146
147macro_rules ! cipher_suite {
148 ($([$id:ident, $id_value:literal, $auth:literal, $integrity:literal, $confidentiality:literal]),*) => {
149
150 #[derive(Debug, Clone, Copy, PartialEq)]
151 pub enum CipherSuite {
152 $(
153 $id,
154 )*
155 }
156
157 impl CipherSuite {
158 pub fn id(&self) -> u8 {
159 match self {
160 $(
161 Self::$id => $id_value,
162 )*
163 }
164 }
165
166 pub fn from_id(value: u8) -> Option<Self> {
167 match value {
168 $(
169 $id_value => Some(Self::$id),
170 )*
171 _ => None,
172 }
173 }
174
175 pub fn from_suite(suite: [u8; 3]) -> Option<Self> {
176 match suite {
177 $(
178 [$auth, $integrity, $confidentiality] => return Some(Self::$id),
179 )*
180 _ => return None,
181 }
182 }
183
184 pub fn into_suite(&self) -> [u8; 3] {
185 match self {
186 $(
187 Self::$id => [$auth, $integrity, $confidentiality],
188 )*
189 }
190 }
191
192 pub fn as_suite(&self) -> &'static [u8; 3] {
193 match self {
194 $(
195 Self::$id => &[$auth, $integrity, $confidentiality],
196 )*
197 }
198 }
199
200 pub fn authentication(&self) -> AuthenticationAlgorithm {
201 let auth = self.as_suite()[0];
202 TryFrom::try_from(auth).unwrap()
203 }
204
205 pub fn integrity(&self) -> IntegrityAlgorithm {
206 let integ = self.as_suite()[1];
207 TryFrom::try_from(integ).unwrap()
208 }
209
210 pub fn confidentiality(&self) -> ConfidentialityAlgorithm {
211 let conf = self.as_suite()[1];
212 TryFrom::try_from(conf).unwrap()
213 }
214 }
215 }
216}
217
218cipher_suite! {
219 [Id0, 0, 0x00, 0x00, 0x00],
220 [Id1, 1, 0x01, 0x00, 0x00],
221 [Id2, 2, 0x01, 0x01, 0x00],
222 [Id3, 3, 0x01, 0x01, 0x01],
223 [Id4, 4, 0x01, 0x01, 0x02],
224 [Id5, 5, 0x01, 0x01, 0x03],
225 [Id6, 6, 0x02, 0x00, 0x00],
226 [Id7, 7, 0x02, 0x02, 0x00],
227 [Id8, 8, 0x02, 0x02, 0x01],
228 [Id9, 9, 0x02, 0x02, 0x02],
229 [Id10, 10, 0x02, 0x02, 0x03],
230 [Id11, 11, 0x02, 0x03, 0x00],
231 [Id12, 12, 0x02, 0x03, 0x01],
232 [Id13, 13, 0x02, 0x03, 0x02],
233 [Id14, 14, 0x02, 0x03, 0x03],
234 [Id15, 15, 0x03, 0x00, 0x00],
235 [Id16, 16, 0x03, 0x04, 0x00],
236 [Id17, 17, 0x03, 0x04, 0x01],
237 [Id18, 18, 0x03, 0x04, 0x02],
238 [Id19, 19, 0x03, 0x04, 0x03]
239}