Skip to main content

esp_radio/wifi/sta/
eap.rs

1//! Wi-Fi extensible authentication protocol.
2
3use alloc::string::String;
4use core::fmt;
5
6use procmacros::BuilderLite;
7
8use super::ScanMethod;
9use crate::{
10    WifiError,
11    wifi::{AuthenticationMethod, Protocols, Ssid},
12};
13
14/// Configuration for EAP-FAST authentication protocol.
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17#[instability::unstable]
18pub struct EapFastConfig {
19    /// Specifies the provisioning mode for EAP-FAST.
20    pub fast_provisioning: u8,
21    /// The maximum length of the PAC (Protected Access Credentials) list.
22    pub fast_max_pac_list_len: u8,
23    /// Indicates whether the PAC file is in binary format.
24    pub fast_pac_format_binary: bool,
25}
26
27/// Phase 2 authentication methods
28#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[instability::unstable]
31pub enum TtlsPhase2Method {
32    /// EAP (Extensible Authentication Protocol).
33    Eap,
34    /// MSCHAPv2 (Microsoft Challenge Handshake Authentication Protocol 2).
35    Mschapv2,
36    /// MSCHAP (Microsoft Challenge Handshake Authentication Protocol).
37    Mschap,
38    /// PAP (Password Authentication Protocol).
39    Pap,
40    /// CHAP (Challenge Handshake Authentication Protocol).
41    Chap,
42}
43
44impl TtlsPhase2Method {
45    /// Maps the phase 2 method to a raw `u32` representation.
46    pub(crate) fn to_raw(self) -> u32 {
47        match self {
48            TtlsPhase2Method::Eap => {
49                crate::sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_EAP
50            }
51            TtlsPhase2Method::Mschapv2 => {
52                crate::sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAPV2
53            }
54            TtlsPhase2Method::Mschap => {
55                crate::sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_MSCHAP
56            }
57            TtlsPhase2Method::Pap => {
58                crate::sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_PAP
59            }
60            TtlsPhase2Method::Chap => {
61                crate::sys::include::esp_eap_ttls_phase2_types_ESP_EAP_TTLS_PHASE2_CHAP
62            }
63        }
64    }
65}
66
67type CertificateAndKey = (&'static [u8], &'static [u8], Option<&'static [u8]>);
68
69/// Configuration for an EAP (Extensible Authentication Protocol) station.
70#[derive(BuilderLite, Clone, PartialEq, Eq, Hash)]
71#[instability::unstable]
72pub struct EapStationConfig {
73    /// The SSID of the network the station is connecting to.
74    #[builder_lite(skip_setter)]
75    pub(crate) ssid: Ssid,
76    /// The BSSID (MAC Address) of the specific access point.
77    pub(crate) bssid: Option<[u8; 6]>,
78    /// The authentication method used for EAP.
79    pub(crate) auth_method: AuthenticationMethod,
80    /// The identity used during authentication.
81    #[builder_lite(reference)]
82    pub(crate) identity: Option<String>,
83    /// The username used for inner authentication.
84    /// Some EAP methods require a username for authentication.
85    #[builder_lite(reference)]
86    pub(crate) username: Option<String>,
87    /// The password used for inner authentication.
88    #[builder_lite(reference)]
89    pub(crate) password: Option<String>,
90    /// A new password to be set during the authentication process.
91    /// Some methods support password changes during authentication.
92    #[builder_lite(reference)]
93    pub(crate) new_password: Option<String>,
94    /// Configuration for EAP-FAST.
95    #[builder_lite(reference)]
96    pub(crate) eap_fast_config: Option<EapFastConfig>,
97    /// A PAC (Protected Access Credential) file for EAP-FAST.
98    pub(crate) pac_file: Option<&'static [u8]>,
99    /// A boolean flag indicating whether time checking is enforced during
100    /// authentication.
101    pub(crate) time_check: bool,
102    /// A CA (Certificate Authority) certificate for validating the
103    /// authentication server's certificate.
104    pub(crate) ca_cert: Option<&'static [u8]>,
105    /// A tuple containing the station's certificate, private key, and an
106    /// intermediate certificate.
107    pub(crate) certificate_and_key: Option<CertificateAndKey>,
108    /// The Phase 2 authentication method used for EAP-TTLS.
109    #[builder_lite(reference)]
110    pub(crate) ttls_phase2_method: Option<TtlsPhase2Method>,
111    /// The specific Wi-Fi channel to use for the connection.
112    pub(crate) channel: Option<u8>,
113    /// The set of protocols supported by the access point.
114    pub(crate) protocols: Protocols,
115    /// Interval for station to listen to beacon from access point.
116    ///
117    /// The unit of listen interval is one beacon interval.
118    /// For example, if beacon interval is 100 ms and listen interval is 3,
119    /// the interval for station to listen to beacon is 300 ms
120    #[builder_lite(unstable)]
121    pub(crate) listen_interval: u16,
122    /// Time to disconnect from access point if no data is received.
123    ///
124    /// Must be between 6 and 31.
125    #[builder_lite(unstable)]
126    pub(crate) beacon_timeout: u16,
127    /// Number of connection retries station will do before moving to next access point.
128    ///
129    /// `scan_method` should be set as [`ScanMethod::AllChannels`] to use this config.
130    ///
131    /// Note: Enabling this may cause connection time to increase in case the best access point
132    /// doesn't behave properly.
133    #[builder_lite(unstable)]
134    pub(crate) failure_retry_cnt: u8,
135    /// Scan method.
136    #[builder_lite(unstable)]
137    pub(crate) scan_method: ScanMethod,
138}
139
140impl EapStationConfig {
141    /// Set the SSID of the access point.
142    #[instability::unstable]
143    pub fn with_ssid(mut self, ssid: impl Into<Ssid>) -> Self {
144        self.ssid = ssid.into();
145        self
146    }
147
148    pub(crate) fn validate(&self) -> Result<(), WifiError> {
149        if self.ssid.len() > 32 {
150            return Err(WifiError::InvalidArguments);
151        }
152
153        if self.identity.as_ref().unwrap_or(&String::new()).len() > 128 {
154            return Err(WifiError::InvalidArguments);
155        }
156
157        if self.username.as_ref().unwrap_or(&String::new()).len() > 128 {
158            return Err(WifiError::InvalidArguments);
159        }
160
161        if self.password.as_ref().unwrap_or(&String::new()).len() > 64 {
162            return Err(WifiError::InvalidArguments);
163        }
164
165        if self.new_password.as_ref().unwrap_or(&String::new()).len() > 64 {
166            return Err(WifiError::InvalidArguments);
167        }
168
169        if !(6..=31).contains(&self.beacon_timeout) {
170            return Err(WifiError::InvalidArguments);
171        }
172
173        Ok(())
174    }
175}
176
177impl Default for EapStationConfig {
178    fn default() -> Self {
179        EapStationConfig {
180            ssid: Ssid::default(),
181            bssid: None,
182            auth_method: AuthenticationMethod::Wpa2Enterprise,
183            identity: None,
184            username: None,
185            password: None,
186            channel: None,
187            eap_fast_config: None,
188            time_check: false,
189            new_password: None,
190            pac_file: None,
191            ca_cert: None,
192            certificate_and_key: None,
193            ttls_phase2_method: None,
194            protocols: Protocols::default(),
195            listen_interval: 3,
196            beacon_timeout: 6,
197            failure_retry_cnt: 1,
198            scan_method: ScanMethod::Fast,
199        }
200    }
201}
202
203impl fmt::Debug for EapStationConfig {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        f.debug_struct("EapStationConfig")
206            .field("ssid", &self.ssid)
207            .field("bssid", &self.bssid)
208            .field("auth_method", &self.auth_method)
209            .field("channel", &self.channel)
210            .field("identity", &self.identity)
211            .field("username", &self.username)
212            .field("password", &"**REDACTED**")
213            .field("new_password", &"**REDACTED**")
214            .field("eap_fast_config", &self.eap_fast_config)
215            .field("time_check", &self.time_check)
216            .field("pac_file set", &self.pac_file.is_some())
217            .field("ca_cert set", &self.ca_cert.is_some())
218            .field("certificate_and_key set", &"**REDACTED**")
219            .field("ttls_phase2_method", &self.ttls_phase2_method)
220            .field("protocols", &self.protocols)
221            .field("listen_interval", &self.listen_interval)
222            .field("beacon_timeout", &self.beacon_timeout)
223            .field("failure_retry_cnt", &self.failure_retry_cnt)
224            .field("scan_method", &self.scan_method)
225            .finish()
226    }
227}
228
229#[cfg(feature = "defmt")]
230impl defmt::Format for EapStationConfig {
231    fn format(&self, fmt: defmt::Formatter<'_>) {
232        defmt::write!(
233            fmt,
234            "EapStationConfig {{\
235            ssid: {}, \
236            bssid: {:?}, \
237            auth_method: {:?}, \
238            channel: {:?}, \
239            identity: {:?}, \
240            username: {:?}, \
241            password: **REDACTED**, \
242            new_password: **REDACTED**, \
243            eap_fast_config: {:?}, \
244            time_check: {}, \
245            pac_file: {}, \
246            ca_cert: {}, \
247            certificate_and_key: **REDACTED**, \
248            ttls_phase2_method: {:?}, \
249            protocols: {}, \
250            listen_interval: {}, \
251            beacon_timeout: {}, \
252            failure_retry_cnt: {}, \
253            scan_method: {},
254            }}",
255            self.ssid.as_str(),
256            self.bssid,
257            self.auth_method,
258            self.channel,
259            &self.identity.as_ref().map_or("", |v| v.as_str()),
260            &self.username.as_ref().map_or("", |v| v.as_str()),
261            self.eap_fast_config,
262            self.time_check,
263            self.pac_file,
264            self.ca_cert,
265            self.ttls_phase2_method,
266            self.protocols,
267            self.listen_interval,
268            self.beacon_timeout,
269            self.failure_retry_cnt,
270            self.scan_method
271        )
272    }
273}