rust_ipmi/
ipmi_client.rs

1use core::time;
2use std::{
3    collections::HashMap,
4    net::{ToSocketAddrs, UdpSocket},
5    time::Duration,
6};
7
8use crate::{
9    commands::{
10        AuthVersion, Command, GetChannelAuthCapabilitiesRequest,
11        GetChannelAuthCapabilitiesResponse, GetChannelCipherSuitesRequest,
12        GetChannelCipherSuitesResponse, Privilege,
13    },
14    err::{IPMIClientError, PacketError},
15    helpers::utils::{append_u128_to_vec, append_u32_to_vec, hash_hmac_sha_256},
16    parser::{
17        ipmi_payload::{IpmiPayload, NetFn},
18        ipmi_payload_response::{CompletionCode, IpmiPayloadResponse},
19        ipmi_raw_request::IpmiPayloadRawRequest,
20        rakp::{RAKPMessage1, RAKPMessage2, RAKPMessage3, RAKP},
21        rmcp_open_session::{
22            AuthAlgorithm, ConfidentialityAlgorithm, IntegrityAlgorithm, RMCPPlusOpenSession,
23            RMCPPlusOpenSessionRequest, StatusCode,
24        },
25        AuthType, Packet, Payload, PayloadType,
26    },
27};
28
29pub type Result<T> = core::result::Result<T, IPMIClientError>;
30
31#[derive(Debug)]
32pub struct IPMIClient {
33    client_socket: UdpSocket,
34    auth_state: AuthState,
35    privilege: Privilege,
36    session_seq_number: u32,
37    auth_algorithm: Option<AuthAlgorithm>,
38    integrity_algorithm: Option<IntegrityAlgorithm>,
39    confidentiality_algorithm: Option<ConfidentialityAlgorithm>,
40    managed_system_session_id: Option<u32>,
41    managed_system_random_number: Option<u128>,
42    managed_system_guid: Option<u128>,
43    remote_console_session_id: Option<u32>,
44    remote_console_random_number: u128,
45    username: Option<String>,
46    password_mac_key: Option<Vec<u8>>,
47    channel_auth_capabilities: Option<GetChannelAuthCapabilitiesResponse>,
48    cipher_suite_bytes: Option<Vec<u8>>,
49    cipher_list_index: u8,
50    sik: Option<[u8; 32]>,
51    k1: Option<[u8; 32]>,
52    k2: Option<[u8; 32]>,
53}
54impl IPMIClient {
55    /// Creates client for running IPMI commands against a BMC.
56    ///
57    /// # Arguments
58    /// * `ipmi_server_addr` - Socket address of the IPMI server (or BMC LAN controller). Default port for IPMI RMCP is 623 UDP.
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use rust_ipmi::ipmi_client::{IPMIClient, IPMIClientError};
64    ///
65    /// let ipmi_server = "192.168.1.10:623"
66    /// let ipmi_client: Result<IPMIClient, IPMIClientError> = IPMIClient::new(ipmi_server)
67    ///     .expect("Failed to connect to the IPMI server");
68    /// ```
69    pub fn new<A: ToSocketAddrs>(ipmi_server_addr: A) -> Result<IPMIClient> {
70        let client_socket =
71            UdpSocket::bind("0.0.0.0:0").map_err(|e| IPMIClientError::FailedBind(e))?;
72        let _ = client_socket
73            .set_read_timeout(Some(time::Duration::from_secs(20)))
74            .map_err(|e| IPMIClientError::FailedSetSocketReadTimeout(e));
75        client_socket
76            .connect(ipmi_server_addr)
77            .map_err(|e| IPMIClientError::ConnectToIPMIServer(e))?;
78        Ok(IPMIClient {
79            client_socket,
80            auth_state: AuthState::Discovery,
81            session_seq_number: 0x0000000a,
82            auth_algorithm: None,
83            integrity_algorithm: None,
84            confidentiality_algorithm: None,
85            managed_system_session_id: None,
86            managed_system_guid: None,
87            remote_console_random_number: rand::random::<u128>(),
88            username: None,
89            password_mac_key: None,
90            sik: None,
91            k1: None,
92            k2: None,
93            channel_auth_capabilities: None,
94            cipher_suite_bytes: None,
95            cipher_list_index: 0,
96            managed_system_random_number: None,
97            remote_console_session_id: None,
98            privilege: Privilege::Callback,
99        })
100    }
101
102    /// Set the read timeout on the ipmi client UDP socket. Default timeout for the socket is set to 20 seconds
103    ///
104    /// # Arguments
105    /// * `duration` - The timeout duration. If set to 5 seconds, the ipmi client will wait up to 5 seconds for a response from the BMC until timing out
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use rust_ipmi::ipmi_client::IPMIClient;
111    ///
112    /// let ipmi_server = "192.168.1.10:623"
113    /// let ipmi_client = IPMIClient::new(ipmi_server).expect("Failed to connect to the IPMI server");
114    /// ipmi_client.set_read_timeout(Some(time::Duration::from_secs(10))).expect("Failed to set the timeout");
115    /// ```
116    pub fn set_read_timeout(&self, duration: Option<Duration>) -> Result<()> {
117        self.client_socket
118            .set_read_timeout(duration)
119            .map_err(|e| IPMIClientError::SetReadTimeOutError(e))
120    }
121
122    /// Authenticates and establishes a session with the BMC.
123    ///
124    /// # Arguments
125    /// * `username` - username used to authenticate against the BMC.
126    /// * `password` - password for the username provided.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// use rust_ipmi::{IPMIClient, IPMIClientError};
132    ///
133    /// let ipmi_server = "192.168.1.10:623";
134    /// let mut ipmi_client: IPMIClient = IPMIClient::new(ipmi_server)
135    ///     .expect("Failed to connect to the server");
136    ///
137    /// let username = "my-username";
138    /// ipmi_client.establish_connection(username, "password")
139    ///     .map_err(|e: IPMIClientError| println!("Failed to establish session with BMC: {}", e));
140    ///
141    /// ```
142    pub fn establish_connection<S: ToString>(&mut self, username: S, password: S) -> Result<()> {
143        // if let Some(u) = username {
144        // };
145        self.username = Some(username.to_string());
146        // if let Some(u) = password {
147        // };
148        let binding = password.to_string();
149        let rakp2_mac_key = binding.as_bytes();
150        self.password_mac_key = Some(rakp2_mac_key.into());
151
152        self.discovery_request()?; // Get channel auth capabilites and set cipher
153        self.authenticate()?; // rmcp open session and authenticate
154        Ok(())
155    }
156
157    /// Run a raw command to the BMC. See "Appendix G - Command Assignments" of the IPMIv2 spec
158    /// to see a comprehensive list of default IPMI commands.
159    ///
160    /// # Arguments
161    /// * `net_fn` - NetFn of your request.
162    /// * `command_code` - u8: Command code of your request.
163    /// * `data` - Data associated with the command.
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use rust_ipmi::{IPMIClient, Payload, IPMIClientError, Command, NetFn};
169    ///
170    /// let ipmi_server = "192.168.1.10:623";
171    /// let mut ipmi_client: IPMIClient = IPMIClient::new(ipmi_server)
172    ///     .expect("Failed to connect to the server");
173
174    ///
175    /// ipmi_client.establish_connection("username", "password")
176    ///     .map_err(|e: IPMIClientError| println!("Failed to establish session with BMC: {}", e));
177    ///
178    /// let response = ipmi_client
179    ///     .send_raw_request(NetFn::App, Command::SetSessionPrivilegeLevel.into(), Some(vec![0x4]))
180    ///     .map_err(|e: IPMIClientError| println!("{}", e));
181    /// ```
182    pub fn send_raw_request<C: Into<Option<Vec<u8>>>, T: TryInto<NetFn>>(
183        &mut self,
184        net_fn: T,
185        command_code: u8,
186        data: C,
187    ) -> Result<IpmiPayloadResponse> {
188        // must establish session first
189        if self.auth_state != AuthState::Established {
190            Err(IPMIClientError::SessionNotEstablishedYet)?
191        };
192
193        // Set session privilege level to ADMIN
194        if self.privilege != Privilege::Administrator {
195            let set_session = IpmiPayloadRawRequest::new(
196                NetFn::App,
197                (0x3b, 0x6.into()).try_into()?,
198                Some(vec![Privilege::Administrator.into()]),
199            )
200            .create_packet(
201                self.managed_system_session_id.clone().unwrap(),
202                self.session_seq_number,
203            );
204
205            let set_session_response: Packet = self.send_packet(set_session)?;
206            self.session_seq_number += 1;
207            if let Some(Payload::Ipmi(IpmiPayload::Response(payload))) =
208                set_session_response.payload
209            {
210                if payload.completion_code == CompletionCode::CompletedNormally {
211                    self.privilege = Privilege::Administrator;
212                }
213            }
214        }
215
216        let raw_request: Packet;
217
218        let netfn: NetFn = net_fn
219            .try_into()
220            .map_err(|_e: <T as TryInto<NetFn>>::Error| {
221                IPMIClientError::NetFnError(crate::err::NetFnError::UnknownNetFn(0))
222            })?;
223        // let netfn: u8 = net_fn.into();
224        let command: Command = (command_code, netfn)
225            .try_into()
226            .map_err(|e| IPMIClientError::CommandError(e))?;
227
228        match data.into() {
229            None => {
230                raw_request = IpmiPayloadRawRequest::new(netfn, command, None).create_packet(
231                    self.managed_system_session_id.clone().unwrap(),
232                    self.session_seq_number,
233                );
234            }
235            Some(x) => {
236                raw_request = IpmiPayloadRawRequest::new(netfn, command, Some(x.into()))
237                    .create_packet(
238                        self.managed_system_session_id.clone().unwrap(),
239                        self.session_seq_number,
240                    );
241            }
242        }
243
244        let response: Packet = self.send_packet(raw_request)?;
245        self.session_seq_number += 1;
246        if let Some(Payload::Ipmi(IpmiPayload::Response(payload))) = response.payload {
247            return Ok(payload);
248        }
249        Err(IPMIClientError::NoResponse)?
250        // Ok(None)
251    }
252
253    fn discovery_request(&mut self) -> Result<()> {
254        let channel_packet =
255            GetChannelAuthCapabilitiesRequest::new(true, 0xE, Privilege::Administrator)
256                .create_packet(AuthType::None, 0x0, 0x0, None);
257        self.send_packet(channel_packet)?;
258
259        // Get the Channel Cipher Suites
260        let cipher_packet = GetChannelCipherSuitesRequest::default().create_packet();
261        self.send_packet(cipher_packet)?;
262        Ok(())
263    }
264
265    fn authenticate(&mut self) -> Result<()> {
266        // RMCP+ Open Session Request
267        let rmcp_open_packet: Packet = RMCPPlusOpenSessionRequest::new(
268            0,
269            Privilege::Administrator,
270            0xa0a2a3a4,
271            self.auth_algorithm.clone().unwrap(),
272            self.integrity_algorithm.clone().unwrap(),
273            self.confidentiality_algorithm.clone().unwrap(),
274        )
275        .into();
276        self.send_packet(rmcp_open_packet)?;
277
278        // RAKP Message 1
279        let rakp1_packet: Packet = RAKPMessage1::new(
280            0x0,
281            self.managed_system_session_id.unwrap(),
282            self.remote_console_random_number,
283            true,
284            Privilege::Administrator,
285            self.username.clone().unwrap(),
286        )
287        .into();
288        self.send_packet(rakp1_packet)?;
289        self.create_session_keys()?;
290
291        // RAKP Message 3
292        let mut rakp3_input_buffer = Vec::new();
293        append_u128_to_vec(
294            &mut rakp3_input_buffer,
295            self.managed_system_random_number.unwrap(),
296        );
297        append_u32_to_vec(
298            &mut rakp3_input_buffer,
299            self.remote_console_session_id.unwrap(),
300        );
301        rakp3_input_buffer.push(0x14);
302        rakp3_input_buffer.push(self.username.clone().unwrap().len().try_into().unwrap());
303        self.username
304            .clone()
305            .unwrap()
306            .as_bytes()
307            .iter()
308            .for_each(|char| rakp3_input_buffer.push(char.clone()));
309
310        let rakp3_auth_code =
311            hash_hmac_sha_256(self.password_mac_key.clone().unwrap(), rakp3_input_buffer);
312        let rakp3_packet: Packet = RAKPMessage3::new(
313            0x0,
314            StatusCode::NoErrors,
315            self.managed_system_session_id.clone().unwrap(),
316            Some(rakp3_auth_code.into()),
317        )
318        .into();
319        self.send_packet(rakp3_packet)?;
320
321        Ok(())
322    }
323
324    fn send_packet(&mut self, request_packet: Packet) -> Result<Packet> {
325        match self.auth_state {
326            AuthState::Established => {
327                self.client_socket
328                    .send(
329                        &request_packet
330                            .to_encrypted_bytes(&self.k1.unwrap(), &self.k2.unwrap())
331                            .unwrap(),
332                    )
333                    .map_err(|e| IPMIClientError::FailedSend(e))?;
334            }
335            _ => {
336                let x: Vec<u8> = request_packet.into();
337                self.client_socket
338                    .send(&x)
339                    .map_err(|e| IPMIClientError::FailedSend(e))?;
340            }
341        }
342
343        let mut recv_buff = [0; 8092];
344
345        if let Ok((n_bytes, _addr)) = self.client_socket.recv_from(&mut recv_buff) {
346            let response_slice = &recv_buff[..n_bytes];
347            let response_packet: Packet;
348            match self.k2.clone() {
349                None => response_packet = Packet::try_from(response_slice)?,
350                Some(k2) => response_packet = Packet::try_from((response_slice, &k2))?,
351            }
352            if let Some(payload) = response_packet.clone().payload {
353                match payload {
354                    Payload::Ipmi(IpmiPayload::Request(_)) => {
355                        return Err(IPMIClientError::MisformedResponse)
356                    }
357                    Payload::Ipmi(IpmiPayload::Response(payload)) => {
358                        self.handle_completion_code(payload)?;
359                    }
360                    Payload::RMCP(RMCPPlusOpenSession::Request(_)) => {
361                        return Err(IPMIClientError::MisformedResponse)
362                    }
363                    Payload::RMCP(RMCPPlusOpenSession::Response(payload)) => self
364                        .handle_status_code(Payload::RMCP(RMCPPlusOpenSession::Response(
365                            payload,
366                        )))?,
367                    Payload::RAKP(RAKP::Message2(payload)) => {
368                        self.handle_status_code(Payload::RAKP(RAKP::Message2(payload)))?
369                    }
370                    Payload::RAKP(RAKP::Message4(payload)) => {
371                        self.handle_status_code(Payload::RAKP(RAKP::Message4(payload)))?
372                    }
373                    _ => Err(IPMIClientError::MisformedResponse)?,
374                }
375                return Ok(response_packet);
376            }
377            Ok(response_packet)
378        } else {
379            return Err(IPMIClientError::NoResponse);
380        }
381    }
382
383    // result bool: true if packet should be returned in the top function
384    fn handle_completion_code(&mut self, payload: IpmiPayloadResponse) -> Result<()> {
385        match payload.completion_code {
386            CompletionCode::CompletedNormally => match payload.command {
387                Command::GetChannelAuthCapabilities => {
388                    self.handle_channel_auth_capabilities(payload)?
389                }
390                Command::GetChannelCipherSuites => {
391                    while let AuthState::Discovery = self.auth_state {
392                        self.cipher_list_index += 1;
393                        self.handle_cipher_suites(payload.clone(), self.cipher_list_index)?;
394                    }
395                }
396                _ => return Ok(()),
397            },
398            _ => {
399                println!("{:?}", payload.completion_code);
400                return Ok(());
401            }
402        }
403        Ok(())
404    }
405
406    fn handle_status_code(&mut self, payload: Payload) -> Result<()> {
407        if let Payload::RMCP(RMCPPlusOpenSession::Response(response)) = payload.clone() {
408            match response.rmcp_plus_status_code {
409                StatusCode::NoErrors => {
410                    self.managed_system_session_id =
411                        Some(response.managed_system_session_id.clone());
412                }
413                _ => Err(IPMIClientError::FailedToOpenSession(
414                    response.rmcp_plus_status_code,
415                ))?,
416            }
417        }
418
419        if let Payload::RAKP(RAKP::Message2(response)) = payload.clone() {
420            match response.rmcp_plus_status_code {
421                StatusCode::NoErrors => {
422                    self.managed_system_guid = Some(response.managed_system_guid);
423                    self.remote_console_session_id = Some(response.remote_console_session_id);
424                    self.managed_system_random_number = Some(response.managed_system_random_number);
425                    // validate BMC auth code
426                    self.validate_rakp2(response)?;
427                }
428                _ => Err(IPMIClientError::FailedToOpenSession(
429                    response.rmcp_plus_status_code,
430                ))?,
431            }
432        }
433        if let Payload::RAKP(RAKP::Message4(response)) = payload.clone() {
434            match response.rmcp_plus_status_code {
435                StatusCode::NoErrors => {
436                    // println!("rak4: {:x?}", payload.integrity_check_value.unwrap());
437                    let mut rakp4_input_buffer: Vec<u8> = Vec::new();
438                    append_u128_to_vec(
439                        &mut rakp4_input_buffer,
440                        self.remote_console_random_number.clone(),
441                    );
442                    append_u32_to_vec(
443                        &mut rakp4_input_buffer,
444                        self.managed_system_session_id.clone().unwrap(),
445                    );
446                    append_u128_to_vec(
447                        &mut rakp4_input_buffer,
448                        self.managed_system_guid.clone().unwrap(),
449                    );
450                    let auth_code =
451                        hash_hmac_sha_256(self.sik.clone().unwrap().into(), rakp4_input_buffer);
452
453                    if response.integrity_check_value.clone().unwrap() == auth_code[..16] {
454                        // println!("Ses!!!");
455                        self.auth_state = AuthState::Established;
456                    } else {
457                        Err(IPMIClientError::MismatchedKeyExchangeAuthCode)?
458                    }
459                }
460                _ => Err(IPMIClientError::FailedToOpenSession(
461                    response.rmcp_plus_status_code,
462                ))?,
463            }
464        }
465
466        Ok(())
467    }
468
469    fn create_session_keys(&mut self) -> Result<()> {
470        let mut sik_input = Vec::new();
471        append_u128_to_vec(&mut sik_input, self.remote_console_random_number);
472        append_u128_to_vec(&mut sik_input, self.managed_system_random_number.unwrap());
473        sik_input.push(0x14);
474        sik_input.push(
475            self.username
476                .clone()
477                .unwrap()
478                .len()
479                .try_into()
480                .map_err(|e| IPMIClientError::UsernameOver255InLength(e))?,
481        );
482        self.username
483            .clone()
484            .unwrap()
485            .as_bytes()
486            .iter()
487            .for_each(|char| sik_input.push(char.clone()));
488
489        self.sik = Some(hash_hmac_sha_256(
490            self.password_mac_key.clone().unwrap(),
491            sik_input,
492        ));
493        self.k1 = Some(hash_hmac_sha_256(self.sik.unwrap().into(), [1; 20].into()));
494        self.k2 = Some(hash_hmac_sha_256(self.sik.unwrap().into(), [2; 20].into()));
495
496        Ok(())
497    }
498
499    fn validate_rakp2(&self, response: RAKPMessage2) -> Result<()> {
500        if let None = response.clone().key_exchange_auth_code {
501            return Ok(());
502        }
503        let mut rakp2_input_buffer: Vec<u8> = Vec::new();
504        append_u32_to_vec(
505            &mut rakp2_input_buffer,
506            self.remote_console_session_id.unwrap(),
507        );
508        append_u32_to_vec(
509            &mut rakp2_input_buffer,
510            self.managed_system_session_id.unwrap(),
511        );
512        append_u128_to_vec(&mut rakp2_input_buffer, self.remote_console_random_number);
513        append_u128_to_vec(
514            &mut rakp2_input_buffer,
515            self.managed_system_random_number.unwrap(),
516        );
517        append_u128_to_vec(&mut rakp2_input_buffer, self.managed_system_guid.unwrap());
518        rakp2_input_buffer.push(0x14);
519        rakp2_input_buffer.push(
520            self.username
521                .clone()
522                .unwrap()
523                .len()
524                .try_into()
525                .map_err(|e| IPMIClientError::UsernameOver255InLength(e))?,
526        );
527        self.username
528            .clone()
529            .unwrap()
530            .as_bytes()
531            .iter()
532            .for_each(|char| rakp2_input_buffer.push(char.clone()));
533
534        let manual_auth_code =
535            hash_hmac_sha_256(self.password_mac_key.clone().unwrap(), rakp2_input_buffer);
536        let mut vec_auth_code = Vec::new();
537        vec_auth_code.extend_from_slice(manual_auth_code.as_slice());
538        if vec_auth_code != response.key_exchange_auth_code.unwrap() {
539            Err(IPMIClientError::FailedToValidateRAKP2)?
540        }
541        Ok(())
542    }
543
544    fn handle_channel_auth_capabilities(&mut self, payload: IpmiPayloadResponse) -> Result<()> {
545        let response: GetChannelAuthCapabilitiesResponse = payload
546            .data
547            .unwrap()
548            .try_into()
549            .map_err(|e| PacketError::IPMIPayload(e))?;
550        // Currently don't support IPMI v1.5
551        if let AuthVersion::IpmiV1_5 = response.auth_version {
552            return Err(IPMIClientError::UnsupportedVersion);
553        }
554        self.channel_auth_capabilities = Some(response);
555        Ok(())
556    }
557
558    fn handle_cipher_suites(
559        &mut self,
560        payload: IpmiPayloadResponse,
561        cipher_list_index: u8,
562    ) -> Result<()> {
563        let response: GetChannelCipherSuitesResponse = payload
564            .data
565            .unwrap()
566            .try_into()
567            .map_err(|e| PacketError::IPMIPayload(e))?;
568        // update total cipher bytes for the ipmi client object
569        if let Some(mut old_bytes) = self.cipher_suite_bytes.clone() {
570            response
571                .cypher_suite_record_data_bytes
572                .iter()
573                .for_each(|byte| old_bytes.push(*byte));
574            self.cipher_suite_bytes = Some(old_bytes);
575        } else {
576            self.cipher_suite_bytes = Some(response.cypher_suite_record_data_bytes.clone());
577        }
578
579        match response.is_last() {
580            false => {
581                let cipher_packet = GetChannelCipherSuitesRequest::new(
582                    0xe,
583                    PayloadType::IPMI,
584                    true,
585                    cipher_list_index,
586                )
587                .create_packet();
588                self.send_packet(cipher_packet)?;
589                Ok(())
590            }
591            true => {
592                // parse through cipher suite records
593                self.choose_ciphers();
594
595                // set new state - beginning authentication
596                self.auth_state = AuthState::Authentication;
597                Ok(())
598            }
599        }
600    }
601
602    fn choose_ciphers(&mut self) {
603        let mut priority_map: HashMap<
604            u8,
605            (
606                u8,
607                (AuthAlgorithm, IntegrityAlgorithm, ConfidentialityAlgorithm),
608            ),
609        > = HashMap::new();
610        if let Some(bytes) = self.cipher_suite_bytes.clone() {
611            bytes.split(|x| *x == 0xc0).for_each(|bytes| {
612                if bytes.len() != 4 {
613                    return;
614                }
615                let auth_value: (u8, AuthAlgorithm) = match bytes[1] {
616                    0x01 => (2, AuthAlgorithm::RakpHmacSha1),
617                    0x02 => (1, AuthAlgorithm::RakpHmacMd5),
618                    0x03 => (3, AuthAlgorithm::RakpHmacSha256),
619                    _ => (0, AuthAlgorithm::RakpNone),
620                };
621                let integ_value: (u8, IntegrityAlgorithm) = match bytes[2] {
622                    0x41 => (2, IntegrityAlgorithm::HmacSha196),
623                    0x42 => (3, IntegrityAlgorithm::HmacMd5128),
624                    0x43 => (1, IntegrityAlgorithm::Md5128),
625                    0x44 => (4, IntegrityAlgorithm::HmacSha256128),
626                    _ => (0, IntegrityAlgorithm::None),
627                };
628                let confid_value: (u8, ConfidentialityAlgorithm) = match bytes[3] {
629                    0x81 => (3, ConfidentialityAlgorithm::AesCbc128),
630                    0x82 => (2, ConfidentialityAlgorithm::XRc4128),
631                    0x83 => (1, ConfidentialityAlgorithm::XRc440),
632                    _ => (0, ConfidentialityAlgorithm::None),
633                };
634                priority_map.insert(
635                    bytes[0],
636                    (
637                        auth_value.0 + integ_value.0 + confid_value.0,
638                        (auth_value.1, integ_value.1, confid_value.1),
639                    ),
640                );
641            });
642            let id_to_use = priority_map.iter().max_by_key(|entry| entry.1 .0).unwrap();
643            self.auth_algorithm = Some(id_to_use.1 .1 .0.clone());
644            self.integrity_algorithm = Some(id_to_use.1 .1 .1.clone());
645            self.confidentiality_algorithm = Some(id_to_use.1 .1 .2.clone());
646        } else {
647            self.auth_algorithm = Some(AuthAlgorithm::RakpNone);
648            self.integrity_algorithm = Some(IntegrityAlgorithm::None);
649            self.confidentiality_algorithm = Some(ConfidentialityAlgorithm::None);
650        }
651    }
652}
653
654#[derive(Debug, PartialEq)]
655enum AuthState {
656    Discovery,
657    Authentication,
658    Established,
659}