1use crate::client::ClientConfig;
6use crate::client::rsn::Rsna;
7use anyhow::{Error, format_err};
8use fidl_fuchsia_wlan_common as fidl_common;
9use fidl_fuchsia_wlan_mlme::DeviceInfo;
10use wlan_common::bss::BssDescription;
11use wlan_common::ie::rsn::rsne::{self, Rsne};
12use wlan_common::ie::wpa::WpaIe;
13use wlan_common::ie::{self};
14use wlan_common::security::wep::{self, WepKey};
15use wlan_common::security::{SecurityAuthenticator, wpa};
16use wlan_rsn::auth::psk::ToPsk;
17use wlan_rsn::auth::{self};
18use wlan_rsn::nonce::NonceReader;
19use wlan_rsn::{NegotiatedProtection, ProtectionInfo};
20
21#[derive(Debug)]
22pub enum Protection {
23 Open,
24 Wep(WepKey),
25 LegacyWpa(Rsna),
29 Rsna(Rsna),
30}
31
32impl Protection {
33 pub fn rsn_auth_method(&self) -> Option<auth::MethodName> {
34 let rsna = match self {
35 Self::LegacyWpa(rsna) => rsna,
36 Self::Rsna(rsna) => rsna,
37 Self::Wep(_) | Self::Open => {
39 return None;
40 }
41 };
42
43 Some(rsna.supplicant.get_auth_method())
44 }
45}
46
47#[derive(Debug)]
48pub enum ProtectionIe {
49 Rsne(Vec<u8>),
50 VendorIes(Vec<u8>),
51}
52
53#[derive(Clone, Copy, Debug)]
81pub struct SecurityContext<'a, C> {
82 pub security: &'a C,
84 pub device: &'a DeviceInfo,
85 pub security_support: &'a fidl_common::SecuritySupport,
86 pub config: &'a ClientConfig,
87 pub bss: &'a BssDescription,
88}
89
90impl<'a, C> SecurityContext<'a, C> {
91 fn map<U>(&self, subject: &'a U) -> SecurityContext<'a, U> {
93 SecurityContext {
94 security: subject,
95 device: self.device,
96 security_support: self.security_support,
97 config: self.config,
98 bss: self.bss,
99 }
100 }
101}
102
103impl SecurityContext<'_, wpa::Wpa1Credentials> {
104 fn authenticator_supplicant_ie(&self) -> Result<(WpaIe, WpaIe), Error> {
106 let a_wpa_ie = self.bss.wpa_ie()?;
107 if !crate::client::wpa::is_legacy_wpa_compatible(&a_wpa_ie) {
108 return Err(format_err!("Legacy WPA requested but IE is incompatible: {:?}", a_wpa_ie));
109 }
110 let s_wpa_ie = crate::client::wpa::construct_s_wpa(&a_wpa_ie);
111 Ok((a_wpa_ie, s_wpa_ie))
112 }
113
114 fn authentication_config(&self) -> auth::Config {
116 auth::Config::ComputedPsk(self.security.to_psk(&self.bss.ssid).into())
117 }
118}
119
120impl SecurityContext<'_, wpa::Wpa2PersonalCredentials> {
121 fn authenticator_supplicant_rsne(&self) -> Result<(Rsne, Rsne), Error> {
123 let a_rsne_ie = self
124 .bss
125 .rsne()
126 .ok_or_else(|| format_err!("WPA2 requested but RSNE is not present in BSS."))?;
127 let (_, a_rsne) = rsne::from_bytes(a_rsne_ie)
128 .map_err(|error| format_err!("Invalid RSNE IE {:02x?}: {:?}", a_rsne_ie, error))?;
129 let s_rsne = a_rsne.derive_wpa2_s_rsne(self.security_support)?;
130 Ok((a_rsne, s_rsne))
131 }
132
133 fn authentication_config(&self) -> auth::Config {
135 auth::Config::ComputedPsk(self.security.to_psk(&self.bss.ssid).into())
136 }
137}
138
139impl SecurityContext<'_, wpa::Wpa3PersonalCredentials> {
140 fn authenticator_supplicant_rsne(&self) -> Result<(Rsne, Rsne), Error> {
142 let a_rsne_ie = self
143 .bss
144 .rsne()
145 .ok_or_else(|| format_err!("WPA3 requested but RSNE is not present in BSS."))?;
146 let (_, a_rsne) = rsne::from_bytes(a_rsne_ie)
147 .map_err(|error| format_err!("Invalid RSNE IE {:02x?}: {:?}", a_rsne_ie, error))?;
148 let s_rsne = a_rsne.derive_wpa3_s_rsne(self.security_support)?;
149 Ok((a_rsne, s_rsne))
150 }
151
152 fn authentication_config(&self) -> Result<auth::Config, Error> {
154 match self.security {
155 wpa::Wpa3PersonalCredentials::Passphrase(passphrase) => {
156 if self.security_support.sae.sme_handler_supported {
158 Ok(auth::Config::Sae {
159 ssid: self.bss.ssid.clone(),
160 password: passphrase.clone().into(),
161 mac: self.device.sta_addr.into(),
162 peer_mac: self.bss.bssid.into(),
163 })
164 } else if self.security_support.sae.driver_handler_supported {
165 Ok(auth::Config::DriverSae { password: passphrase.clone().into() })
166 } else {
167 Err(format_err!(
168 "Failed to generate WPA3 authentication config: no SAE SME nor driver \
169 handler"
170 ))
171 }
172 }
173 }
174 }
175}
176
177impl<'a> TryFrom<SecurityContext<'a, SecurityAuthenticator>> for Protection {
178 type Error = Error;
179
180 fn try_from(context: SecurityContext<'a, SecurityAuthenticator>) -> Result<Self, Self::Error> {
181 match context.security {
182 SecurityAuthenticator::Open => context
183 .bss
184 .is_open()
185 .then(|| Protection::Open)
186 .ok_or_else(|| format_err!("BSS is not configured for open authentication")),
187 SecurityAuthenticator::Wep(authenticator) => context.map(authenticator).try_into(),
188 SecurityAuthenticator::Wpa(wpa) => match wpa {
189 wpa::WpaAuthenticator::Wpa1 { credentials, .. } => {
190 context.map(credentials).try_into()
191 }
192 wpa::WpaAuthenticator::Wpa2 { authentication, .. } => match authentication {
193 wpa::Authentication::Personal(personal) => context.map(personal).try_into(),
194 _ => Err(format_err!("WPA Enterprise is unsupported")),
196 },
197 wpa::WpaAuthenticator::Wpa3 { authentication, .. } => match authentication {
198 wpa::Authentication::Personal(personal) => context.map(personal).try_into(),
199 _ => Err(format_err!("WPA Enterprise is unsupported")),
201 },
202 },
203 }
204 }
205}
206
207impl<'a> TryFrom<SecurityContext<'a, wep::WepAuthenticator>> for Protection {
208 type Error = Error;
209
210 fn try_from(context: SecurityContext<'a, wep::WepAuthenticator>) -> Result<Self, Self::Error> {
211 context
212 .bss
213 .has_wep_configured()
214 .then(|| Protection::Wep(context.security.key.clone()))
215 .ok_or_else(|| format_err!("BSS is not configured for WEP"))
216 }
217}
218
219impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa1Credentials>> for Protection {
220 type Error = Error;
221
222 fn try_from(context: SecurityContext<'a, wpa::Wpa1Credentials>) -> Result<Self, Self::Error> {
223 context
224 .bss
225 .has_wpa1_configured()
226 .then(|| -> Result<_, Self::Error> {
227 let sta_addr = context.device.sta_addr.into();
228 let (a_wpa_ie, s_wpa_ie) = context.authenticator_supplicant_ie()?;
229 let negotiated_protection = NegotiatedProtection::from_legacy_wpa(&s_wpa_ie)?;
230 let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
231 NonceReader::new(&sta_addr)?,
232 context.authentication_config(),
233 sta_addr,
234 ProtectionInfo::LegacyWpa(s_wpa_ie),
235 context.bss.bssid.into(),
236 ProtectionInfo::LegacyWpa(a_wpa_ie),
237 )
238 .map_err(|error| format_err!("Failed to create ESS-SA: {:?}", error))?;
239 Ok(Protection::LegacyWpa(Rsna {
240 negotiated_protection,
241 supplicant: Box::new(supplicant),
242 }))
243 })
244 .transpose()?
245 .ok_or_else(|| format_err!("BSS is not configured for WPA1"))
246 }
247}
248
249impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa2PersonalCredentials>> for Protection {
250 type Error = Error;
251
252 fn try_from(
253 context: SecurityContext<'a, wpa::Wpa2PersonalCredentials>,
254 ) -> Result<Self, Self::Error> {
255 context
256 .bss
257 .has_wpa2_personal_configured()
258 .then(|| -> Result<_, Self::Error> {
259 let sta_addr = context.device.sta_addr.into();
260 let (a_rsne, s_rsne) = context.authenticator_supplicant_rsne()?;
261 let negotiated_protection = NegotiatedProtection::from_rsne(&s_rsne)?;
262 let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
263 NonceReader::new(&sta_addr)?,
264 context.authentication_config(),
265 sta_addr,
266 ProtectionInfo::Rsne(s_rsne),
267 context.bss.bssid.into(),
268 ProtectionInfo::Rsne(a_rsne),
269 )
270 .map_err(|error| format_err!("Failed to creat ESS-SA: {:?}", error))?;
271 Ok(Protection::Rsna(Rsna {
272 negotiated_protection,
273 supplicant: Box::new(supplicant),
274 }))
275 })
276 .transpose()?
277 .ok_or_else(|| format_err!("BSS is not configured for WPA2 Personal"))
278 }
279}
280
281impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa3PersonalCredentials>> for Protection {
282 type Error = Error;
283
284 fn try_from(
285 context: SecurityContext<'a, wpa::Wpa3PersonalCredentials>,
286 ) -> Result<Self, Self::Error> {
287 context
288 .bss
289 .has_wpa3_personal_configured()
290 .then(|| -> Result<_, Self::Error> {
291 let sta_addr = context.device.sta_addr.into();
292 if !context.config.wpa3_supported {
293 return Err(format_err!("WPA3 requested but client does not support WPA3"));
294 }
295 let (a_rsne, s_rsne) = context.authenticator_supplicant_rsne()?;
296 let negotiated_protection = NegotiatedProtection::from_rsne(&s_rsne)?;
297 let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
298 NonceReader::new(&sta_addr)?,
299 context.authentication_config()?,
300 sta_addr,
301 ProtectionInfo::Rsne(s_rsne),
302 context.bss.bssid.into(),
303 ProtectionInfo::Rsne(a_rsne),
304 )
305 .map_err(|error| format_err!("Failed to create ESS-SA: {:?}", error))?;
306 Ok(Protection::Rsna(Rsna {
307 negotiated_protection,
308 supplicant: Box::new(supplicant),
309 }))
310 })
311 .transpose()?
312 .ok_or_else(|| format_err!("BSS is not configured for WPA3 Personal"))
313 }
314}
315
316pub(crate) fn build_protection_ie(protection: &Protection) -> Result<Option<ProtectionIe>, Error> {
321 match protection {
322 Protection::Open | Protection::Wep(_) => Ok(None),
323 Protection::LegacyWpa(rsna) => {
324 let s_protection = rsna.negotiated_protection.to_full_protection();
325 let s_wpa = match s_protection {
326 ProtectionInfo::Rsne(_) => {
327 return Err(format_err!("found RSNE protection inside a WPA1 association..."));
328 }
329 ProtectionInfo::LegacyWpa(wpa) => wpa,
330 };
331 let mut buf = vec![];
332 #[expect(clippy::unwrap_used)]
333 ie::write_wpa1_ie(&mut buf, &s_wpa).unwrap(); Ok(Some(ProtectionIe::VendorIes(buf)))
335 }
336 Protection::Rsna(rsna) => {
337 let s_protection = rsna.negotiated_protection.to_full_protection();
338 let s_rsne = match s_protection {
339 ProtectionInfo::Rsne(rsne) => rsne,
340 ProtectionInfo::LegacyWpa(_) => {
341 return Err(format_err!("found WPA protection inside an RSNA..."));
342 }
343 };
344 let mut buf = Vec::with_capacity(s_rsne.len());
345 #[expect(clippy::unwrap_used)]
349 let () = s_rsne.write_into(&mut buf).unwrap();
350 Ok(Some(ProtectionIe::Rsne(buf)))
351 }
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358 use crate::client::{self};
359 use assert_matches::assert_matches;
360 use wlan_common::fake_bss_description;
361 use wlan_common::ie::fake_ies::fake_wpa_ie;
362 use wlan_common::ie::rsn::fake_rsnes::{fake_wpa2_s_rsne, fake_wpa3_s_rsne};
363 use wlan_common::security::wep::{WEP40_KEY_BYTES, WEP104_KEY_BYTES};
364 use wlan_common::security::wpa::credential::PSK_SIZE_BYTES;
365 use wlan_common::test_utils::fake_features::{
366 fake_security_support, fake_security_support_empty,
367 };
368
369 #[test]
370 fn rsn_auth_method() {
371 let protection = Protection::Open;
373 assert!(protection.rsn_auth_method().is_none());
374
375 let protection = Protection::Wep(WepKey::parse([1; 5]).expect("unable to parse WEP key"));
377 assert!(protection.rsn_auth_method().is_none());
378
379 let protection_info = ProtectionInfo::LegacyWpa(fake_wpa_ie());
381 let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
382 .expect("could create mocked WPA1 NegotiatedProtection");
383 let protection = Protection::LegacyWpa(Rsna {
384 negotiated_protection,
385 supplicant: Box::new(client::test_utils::mock_psk_supplicant().0),
386 });
387 assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Psk));
388
389 let protection_info = ProtectionInfo::Rsne(fake_wpa2_s_rsne());
391 let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
392 .expect("could create mocked WPA2 NegotiatedProtection");
393 let protection = Protection::Rsna(Rsna {
394 negotiated_protection,
395 supplicant: Box::new(client::test_utils::mock_psk_supplicant().0),
396 });
397 assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Psk));
398
399 let protection_info = ProtectionInfo::Rsne(fake_wpa3_s_rsne());
401 let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
402 .expect("could create mocked WPA3 NegotiatedProtection");
403 let protection = Protection::Rsna(Rsna {
404 negotiated_protection,
405 supplicant: Box::new(client::test_utils::mock_sae_supplicant().0),
406 });
407 assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Sae));
408 }
409
410 #[test]
411 fn protection_from_wep40() {
412 let device = crate::test_utils::fake_device_info([1u8; 6].into());
413 let security_support = fake_security_support();
414 let config = Default::default();
415 let bss = fake_bss_description!(Wep);
416 let authenticator = wep::WepAuthenticator { key: WepKey::from([1u8; WEP40_KEY_BYTES]) };
417 let protection = Protection::try_from(SecurityContext {
418 security: &authenticator,
419 device: &device,
420 security_support: &security_support,
421 config: &config,
422 bss: &bss,
423 })
424 .unwrap();
425 assert!(matches!(protection, Protection::Wep(_)));
426 }
427
428 #[test]
429 fn protection_from_wep104() {
430 let device = crate::test_utils::fake_device_info([1u8; 6].into());
431 let security_support = fake_security_support();
432 let config = Default::default();
433 let bss = fake_bss_description!(Wep);
434 let authenticator = wep::WepAuthenticator { key: WepKey::from([1u8; WEP104_KEY_BYTES]) };
435 let protection = Protection::try_from(SecurityContext {
436 security: &authenticator,
437 device: &device,
438 security_support: &security_support,
439 config: &config,
440 bss: &bss,
441 })
442 .unwrap();
443 assert!(matches!(protection, Protection::Wep(_)));
444 }
445
446 #[test]
447 fn protection_from_wpa1_psk() {
448 let device = crate::test_utils::fake_device_info([1u8; 6].into());
449 let security_support = fake_security_support();
450 let config = Default::default();
451 let bss = fake_bss_description!(Wpa1);
452 let credentials = wpa::Wpa1Credentials::Psk([1u8; PSK_SIZE_BYTES].into());
453 let protection = Protection::try_from(SecurityContext {
454 security: &credentials,
455 device: &device,
456 security_support: &security_support,
457 config: &config,
458 bss: &bss,
459 })
460 .unwrap();
461 assert!(matches!(protection, Protection::LegacyWpa(_)));
462 }
463
464 #[test]
465 fn protection_from_wpa2_personal_psk() {
466 let device = crate::test_utils::fake_device_info([1u8; 6].into());
467 let security_support = fake_security_support();
468 let config = Default::default();
469 let bss = fake_bss_description!(Wpa2);
470 let credentials = wpa::Wpa2PersonalCredentials::Psk([1u8; PSK_SIZE_BYTES].into());
471 let protection = Protection::try_from(SecurityContext {
472 security: &credentials,
473 device: &device,
474 security_support: &security_support,
475 config: &config,
476 bss: &bss,
477 })
478 .unwrap();
479 assert!(matches!(protection, Protection::Rsna(_)));
480 }
481
482 #[test]
483 fn protection_from_wpa2_personal_passphrase() {
484 let device = crate::test_utils::fake_device_info([1u8; 6].into());
485 let security_support = fake_security_support();
486 let config = Default::default();
487 let bss = fake_bss_description!(Wpa2);
488 let credentials =
489 wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
490 let protection = Protection::try_from(SecurityContext {
491 security: &credentials,
492 device: &device,
493 security_support: &security_support,
494 config: &config,
495 bss: &bss,
496 })
497 .unwrap();
498 assert!(matches!(protection, Protection::Rsna(_)));
499 }
500
501 #[test]
502 fn protection_from_wpa2_personal_tkip_only_passphrase() {
503 let device = crate::test_utils::fake_device_info([1u8; 6].into());
504 let security_support = fake_security_support();
505 let config = Default::default();
506 let bss = fake_bss_description!(Wpa2TkipOnly);
507 let credentials =
508 wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
509 let protection = Protection::try_from(SecurityContext {
510 security: &credentials,
511 device: &device,
512 security_support: &security_support,
513 config: &config,
514 bss: &bss,
515 })
516 .unwrap();
517 assert!(matches!(protection, Protection::Rsna(_)));
518 }
519
520 #[test]
521 fn protection_from_wpa3_personal_passphrase() {
522 let device = crate::test_utils::fake_device_info([1u8; 6].into());
523 let security_support = fake_security_support();
524 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
525 let bss = fake_bss_description!(Wpa3);
526 let credentials =
527 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
528 let protection = Protection::try_from(SecurityContext {
529 security: &credentials,
530 device: &device,
531 security_support: &security_support,
532 config: &config,
533 bss: &bss,
534 })
535 .unwrap();
536 assert!(matches!(protection, Protection::Rsna(_)));
537 }
538
539 #[test]
540 fn protection_from_wpa1_passphrase_with_open_bss() {
541 let device = crate::test_utils::fake_device_info([1u8; 6].into());
542 let security_support = fake_security_support();
543 let config = Default::default();
544 let bss = fake_bss_description!(Open);
545 let credentials =
546 wpa::Wpa1Credentials::Passphrase("password".as_bytes().try_into().unwrap());
547 let _ = Protection::try_from(SecurityContext {
548 security: &credentials,
549 device: &device,
550 security_support: &security_support,
551 config: &config,
552 bss: &bss,
553 })
554 .expect_err("incorrectly accepted WPA1 passphrase credentials with open BSS");
555 }
556
557 #[test]
558 fn protection_from_open_authenticator_with_wpa1_bss() {
559 let device = crate::test_utils::fake_device_info([1u8; 6].into());
560 let security_support = fake_security_support();
561 let config = Default::default();
562 let bss = fake_bss_description!(Wpa1);
563 let authenticator = SecurityAuthenticator::Open;
566 let _ = Protection::try_from(SecurityContext {
567 security: &authenticator,
568 device: &device,
569 security_support: &security_support,
570 config: &config,
571 bss: &bss,
572 })
573 .expect_err("incorrectly accepted open authenticator with WPA1 BSS");
574 }
575
576 #[test]
577 fn protection_from_wpa2_personal_passphrase_with_wpa3_bss() {
578 let device = crate::test_utils::fake_device_info([1u8; 6].into());
579 let security_support = fake_security_support();
580 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
581 let bss = fake_bss_description!(Wpa3);
582 let credentials =
583 wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
584 let _ = Protection::try_from(SecurityContext {
585 security: &credentials,
586 device: &device,
587 security_support: &security_support,
588 config: &config,
589 bss: &bss,
590 })
591 .expect_err("incorrectly accepted WPA2 passphrase credentials with WPA3 BSS");
592 }
593
594 #[test]
595 fn wpa1_psk_rsna() {
596 let device = crate::test_utils::fake_device_info([1u8; 6].into());
597 let security_support = fake_security_support();
598 let config = Default::default();
599 let bss = fake_bss_description!(Wpa1);
600 let credentials = wpa::Wpa1Credentials::Psk([1u8; PSK_SIZE_BYTES].into());
601 let context = SecurityContext {
602 security: &credentials,
603 device: &device,
604 security_support: &security_support,
605 config: &config,
606 bss: &bss,
607 };
608 assert!(context.authenticator_supplicant_ie().is_ok());
609 assert!(matches!(context.authentication_config(), auth::Config::ComputedPsk(_)));
610
611 let protection = Protection::try_from(context).unwrap();
612 assert_matches!(protection, Protection::LegacyWpa(rsna) => {
613 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Psk);
614 });
615 }
616
617 #[test]
618 fn wpa2_personal_psk_rsna() {
619 let device = crate::test_utils::fake_device_info([1u8; 6].into());
620 let security_support = fake_security_support();
621 let config = Default::default();
622 let bss = fake_bss_description!(Wpa2);
623 let credentials = wpa::Wpa2PersonalCredentials::Psk([1u8; PSK_SIZE_BYTES].into());
624 let context = SecurityContext {
625 security: &credentials,
626 device: &device,
627 security_support: &security_support,
628 config: &config,
629 bss: &bss,
630 };
631 assert!(context.authenticator_supplicant_rsne().is_ok());
632 assert!(matches!(context.authentication_config(), auth::Config::ComputedPsk(_)));
633
634 let protection = Protection::try_from(context).unwrap();
635 assert_matches!(protection, Protection::Rsna(rsna) => {
636 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Psk);
637 });
638 }
639
640 #[test]
641 fn wpa3_personal_passphrase_rsna_sme_auth() {
642 let device = crate::test_utils::fake_device_info([1u8; 6].into());
643 let security_support = fake_security_support();
644 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
645 let bss = fake_bss_description!(Wpa3);
646 let credentials =
647 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
648 let context = SecurityContext {
649 security: &credentials,
650 device: &device,
651 security_support: &security_support,
652 config: &config,
653 bss: &bss,
654 };
655 assert!(context.authenticator_supplicant_rsne().is_ok());
656 assert!(matches!(context.authentication_config(), Ok(auth::Config::Sae { .. })));
657
658 let protection = Protection::try_from(context).unwrap();
659 assert_matches!(protection, Protection::Rsna(rsna) => {
660 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Sae);
661 });
662 }
663
664 #[test]
665 fn wpa3_personal_passphrase_rsna_driver_auth() {
666 let device = crate::test_utils::fake_device_info([1u8; 6].into());
667 let mut security_support = fake_security_support_empty();
668 security_support.mfp.supported = true;
669 security_support.sae.driver_handler_supported = true;
670 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
671 let bss = fake_bss_description!(Wpa3);
672 let credentials =
673 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
674 let context = SecurityContext {
675 security: &credentials,
676 device: &device,
677 security_support: &security_support,
678 config: &config,
679 bss: &bss,
680 };
681 assert!(context.authenticator_supplicant_rsne().is_ok());
682 assert!(matches!(context.authentication_config(), Ok(auth::Config::DriverSae { .. })));
683
684 let protection = Protection::try_from(context).unwrap();
685 assert_matches!(protection, Protection::Rsna(rsna) => {
686 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Sae);
687 });
688 }
689
690 #[test]
691 fn wpa3_personal_passphrase_prefer_sme_auth() {
692 let device = crate::test_utils::fake_device_info([1u8; 6].into());
693 let mut security_support = fake_security_support_empty();
694 security_support.mfp.supported = true;
695 security_support.sae.driver_handler_supported = true;
696 security_support.sae.sme_handler_supported = true;
697 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
698 let bss = fake_bss_description!(Wpa3);
699 let credentials =
700 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
701 let context = SecurityContext {
702 security: &credentials,
703 device: &device,
704 security_support: &security_support,
705 config: &config,
706 bss: &bss,
707 };
708 assert!(matches!(context.authentication_config(), Ok(auth::Config::Sae { .. })));
709 }
710
711 #[test]
712 fn wpa3_personal_passphrase_no_security_support_features() {
713 let device = crate::test_utils::fake_device_info([1u8; 6].into());
714 let security_support = fake_security_support_empty();
715 let config = Default::default();
716 let bss = fake_bss_description!(Wpa3);
717 let credentials =
718 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
719 let context = SecurityContext {
720 security: &credentials,
721 device: &device,
722 security_support: &security_support,
723 config: &config,
724 bss: &bss,
725 };
726 let _ = context
727 .authentication_config()
728 .expect_err("created WPA3 auth config for incompatible device");
729 }
730}