ipmi_rs/app/auth/
get_channel_cipher_suites.rs

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    /// Create a new `GetChannelCipherSuites`.
24    ///
25    /// Returns `None` if `list_index > 0x3F``
26    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}