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, PweMethod};
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
103struct OweAuthenticator;
104
105impl SecurityContext<'_, OweAuthenticator> {
106 fn authenticator_supplicant_rsne(&self) -> Result<(Rsne, Rsne), Error> {
108 let a_rsne_ie = self
109 .bss
110 .rsne()
111 .ok_or_else(|| format_err!("OWE requested but RSNE is not present in BSS."))?;
112 let (_, a_rsne) = rsne::from_bytes(a_rsne_ie)
113 .map_err(|error| format_err!("Invalid RSNE IE {:02x?}: {:?}", a_rsne_ie, error))?;
114 let s_rsne = a_rsne.derive_owe_s_rsne(self.security_support)?;
115 Ok((a_rsne, s_rsne))
116 }
117
118 fn authentication_config(&self) -> Result<auth::Config, Error> {
119 Ok(auth::Config::Owe)
120 }
121}
122
123impl SecurityContext<'_, wpa::Wpa1Credentials> {
124 fn authenticator_supplicant_ie(&self) -> Result<(WpaIe, WpaIe), Error> {
126 let a_wpa_ie = self.bss.wpa_ie()?;
127 if !crate::client::wpa::is_legacy_wpa_compatible(&a_wpa_ie) {
128 return Err(format_err!("Legacy WPA requested but IE is incompatible: {:?}", a_wpa_ie));
129 }
130 let s_wpa_ie = crate::client::wpa::construct_s_wpa(&a_wpa_ie);
131 Ok((a_wpa_ie, s_wpa_ie))
132 }
133
134 fn authentication_config(&self) -> auth::Config {
136 auth::Config::ComputedPsk(self.security.to_psk(&self.bss.ssid).into())
137 }
138}
139
140impl SecurityContext<'_, wpa::Wpa2PersonalCredentials> {
141 fn authenticator_supplicant_rsne(&self) -> Result<(Rsne, Rsne), Error> {
143 let a_rsne_ie = self
144 .bss
145 .rsne()
146 .ok_or_else(|| format_err!("WPA2 requested but RSNE is not present in BSS."))?;
147 let (_, a_rsne) = rsne::from_bytes(a_rsne_ie)
148 .map_err(|error| format_err!("Invalid RSNE IE {:02x?}: {:?}", a_rsne_ie, error))?;
149 let s_rsne = a_rsne.derive_wpa2_s_rsne(self.security_support)?;
150 Ok((a_rsne, s_rsne))
151 }
152
153 fn authentication_config(&self) -> auth::Config {
155 auth::Config::ComputedPsk(self.security.to_psk(&self.bss.ssid).into())
156 }
157}
158
159impl SecurityContext<'_, wpa::Wpa3PersonalCredentials> {
160 fn authenticator_supplicant_rsne(&self) -> Result<(Rsne, Rsne), Error> {
162 let a_rsne_ie = self
163 .bss
164 .rsne()
165 .ok_or_else(|| format_err!("WPA3 requested but RSNE is not present in BSS."))?;
166 let (_, a_rsne) = rsne::from_bytes(a_rsne_ie)
167 .map_err(|error| format_err!("Invalid RSNE IE {:02x?}: {:?}", a_rsne_ie, error))?;
168 let s_rsne = a_rsne.derive_wpa3_s_rsne(self.security_support)?;
169 Ok((a_rsne, s_rsne))
170 }
171
172 fn authentication_config(&self) -> Result<auth::Config, Error> {
174 match self.security {
175 wpa::Wpa3PersonalCredentials::Passphrase(passphrase) => {
176 if self
178 .security_support
179 .sae
180 .as_ref()
181 .and_then(|sae| sae.sme_handler_supported)
182 .unwrap_or(false)
183 {
184 let direct_hash_supported_by_peer = self
185 .bss
186 .rsnxe()
187 .and_then(|rsnxe| rsnxe.rsnxe_octet_1)
188 .map(|octet| octet.sae_hash_to_element())
189 .unwrap_or(false);
190 let direct_hash_supported_by_driver = self
191 .security_support
192 .sae
193 .as_ref()
194 .and_then(|sae| sae.hash_to_element_supported)
195 .unwrap_or(false);
196 Ok(auth::Config::Sae {
197 ssid: self.bss.ssid.clone(),
198 password: passphrase.clone().into(),
199 mac: self.device.sta_addr.into(),
200 peer_mac: self.bss.bssid.into(),
201 pwe_method: if direct_hash_supported_by_peer
202 && direct_hash_supported_by_driver
203 {
204 PweMethod::Direct
205 } else {
206 PweMethod::Loop
207 },
208 })
209 } else if self
210 .security_support
211 .sae
212 .as_ref()
213 .and_then(|sae| sae.driver_handler_supported)
214 .unwrap_or(false)
215 {
216 Ok(auth::Config::DriverSae { password: passphrase.clone().into() })
217 } else {
218 Err(format_err!(
219 "Failed to generate WPA3 authentication config: no SAE SME nor driver \
220 handler"
221 ))
222 }
223 }
224 }
225 }
226}
227
228impl<'a> TryFrom<SecurityContext<'a, SecurityAuthenticator>> for Protection {
229 type Error = Error;
230
231 fn try_from(context: SecurityContext<'a, SecurityAuthenticator>) -> Result<Self, Self::Error> {
232 match context.security {
233 SecurityAuthenticator::Open => context
234 .bss
235 .is_open()
236 .then(|| Protection::Open)
237 .ok_or_else(|| format_err!("BSS is not configured for open authentication")),
238 SecurityAuthenticator::Owe => context.map(&OweAuthenticator).try_into(),
239 SecurityAuthenticator::Wep(authenticator) => context.map(authenticator).try_into(),
240 SecurityAuthenticator::Wpa(wpa) => match wpa {
241 wpa::WpaAuthenticator::Wpa1 { credentials, .. } => {
242 context.map(credentials).try_into()
243 }
244 wpa::WpaAuthenticator::Wpa2 { authentication, .. } => match authentication {
245 wpa::Authentication::Personal(personal) => context.map(personal).try_into(),
246 _ => Err(format_err!("WPA Enterprise is unsupported")),
248 },
249 wpa::WpaAuthenticator::Wpa3 { authentication, .. } => match authentication {
250 wpa::Authentication::Personal(personal) => context.map(personal).try_into(),
251 _ => Err(format_err!("WPA Enterprise is unsupported")),
253 },
254 },
255 }
256 }
257}
258
259impl<'a> TryFrom<SecurityContext<'a, OweAuthenticator>> for Protection {
260 type Error = Error;
261
262 fn try_from(context: SecurityContext<'a, OweAuthenticator>) -> Result<Self, Self::Error> {
263 context
264 .bss
265 .has_owe_configured()
266 .then(|| -> Result<_, Self::Error> {
267 let sta_addr = context.device.sta_addr.into();
268 if !context.config.owe_supported {
269 return Err(format_err!("OWE requested but client does not support OWE"));
270 }
271 let (a_rsne, s_rsne) = context.authenticator_supplicant_rsne()?;
272 let negotiated_protection = NegotiatedProtection::from_rsne(&s_rsne)?;
273 let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
274 NonceReader::new(&sta_addr)?,
275 context.authentication_config()?,
276 sta_addr,
277 ProtectionInfo::Rsne(s_rsne),
278 context.bss.bssid.into(),
279 ProtectionInfo::Rsne(a_rsne),
280 )
281 .map_err(|error| format_err!("Failed to create ESS-SA: {:?}", error))?;
282 Ok(Protection::Rsna(Rsna {
283 negotiated_protection,
284 supplicant: Box::new(supplicant),
285 }))
286 })
287 .transpose()?
288 .ok_or_else(|| format_err!("BSS is not configured for OWE"))
289 }
290}
291
292impl<'a> TryFrom<SecurityContext<'a, wep::WepAuthenticator>> for Protection {
293 type Error = Error;
294
295 fn try_from(context: SecurityContext<'a, wep::WepAuthenticator>) -> Result<Self, Self::Error> {
296 context
297 .bss
298 .has_wep_configured()
299 .then(|| Protection::Wep(context.security.key.clone()))
300 .ok_or_else(|| format_err!("BSS is not configured for WEP"))
301 }
302}
303
304impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa1Credentials>> for Protection {
305 type Error = Error;
306
307 fn try_from(context: SecurityContext<'a, wpa::Wpa1Credentials>) -> Result<Self, Self::Error> {
308 context
309 .bss
310 .has_wpa1_configured()
311 .then(|| -> Result<_, Self::Error> {
312 let sta_addr = context.device.sta_addr.into();
313 let (a_wpa_ie, s_wpa_ie) = context.authenticator_supplicant_ie()?;
314 let negotiated_protection = NegotiatedProtection::from_legacy_wpa(&s_wpa_ie)?;
315 let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
316 NonceReader::new(&sta_addr)?,
317 context.authentication_config(),
318 sta_addr,
319 ProtectionInfo::LegacyWpa(s_wpa_ie),
320 context.bss.bssid.into(),
321 ProtectionInfo::LegacyWpa(a_wpa_ie),
322 )
323 .map_err(|error| format_err!("Failed to create ESS-SA: {:?}", error))?;
324 Ok(Protection::LegacyWpa(Rsna {
325 negotiated_protection,
326 supplicant: Box::new(supplicant),
327 }))
328 })
329 .transpose()?
330 .ok_or_else(|| format_err!("BSS is not configured for WPA1"))
331 }
332}
333
334impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa2PersonalCredentials>> for Protection {
335 type Error = Error;
336
337 fn try_from(
338 context: SecurityContext<'a, wpa::Wpa2PersonalCredentials>,
339 ) -> Result<Self, Self::Error> {
340 context
341 .bss
342 .has_wpa2_personal_configured()
343 .then(|| -> Result<_, Self::Error> {
344 let sta_addr = context.device.sta_addr.into();
345 let (a_rsne, s_rsne) = context.authenticator_supplicant_rsne()?;
346 let negotiated_protection = NegotiatedProtection::from_rsne(&s_rsne)?;
347 let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
348 NonceReader::new(&sta_addr)?,
349 context.authentication_config(),
350 sta_addr,
351 ProtectionInfo::Rsne(s_rsne),
352 context.bss.bssid.into(),
353 ProtectionInfo::Rsne(a_rsne),
354 )
355 .map_err(|error| format_err!("Failed to creat ESS-SA: {:?}", error))?;
356 Ok(Protection::Rsna(Rsna {
357 negotiated_protection,
358 supplicant: Box::new(supplicant),
359 }))
360 })
361 .transpose()?
362 .ok_or_else(|| format_err!("BSS is not configured for WPA2 Personal"))
363 }
364}
365
366impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa3PersonalCredentials>> for Protection {
367 type Error = Error;
368
369 fn try_from(
370 context: SecurityContext<'a, wpa::Wpa3PersonalCredentials>,
371 ) -> Result<Self, Self::Error> {
372 context
373 .bss
374 .has_wpa3_personal_configured()
375 .then(|| -> Result<_, Self::Error> {
376 let sta_addr = context.device.sta_addr.into();
377 if !context.config.wpa3_supported {
378 return Err(format_err!("WPA3 requested but client does not support WPA3"));
379 }
380 let (a_rsne, s_rsne) = context.authenticator_supplicant_rsne()?;
381 let negotiated_protection = NegotiatedProtection::from_rsne(&s_rsne)?;
382 let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
383 NonceReader::new(&sta_addr)?,
384 context.authentication_config()?,
385 sta_addr,
386 ProtectionInfo::Rsne(s_rsne),
387 context.bss.bssid.into(),
388 ProtectionInfo::Rsne(a_rsne),
389 )
390 .map_err(|error| format_err!("Failed to create ESS-SA: {:?}", error))?;
391 Ok(Protection::Rsna(Rsna {
392 negotiated_protection,
393 supplicant: Box::new(supplicant),
394 }))
395 })
396 .transpose()?
397 .ok_or_else(|| format_err!("BSS is not configured for WPA3 Personal"))
398 }
399}
400
401pub(crate) fn build_protection_ie(protection: &Protection) -> Result<Option<ProtectionIe>, Error> {
406 match protection {
407 Protection::Open | Protection::Wep(_) => Ok(None),
408 Protection::LegacyWpa(rsna) => {
409 let s_protection = rsna.negotiated_protection.to_full_protection();
410 let s_wpa = match s_protection {
411 ProtectionInfo::Rsne(_) => {
412 return Err(format_err!("found RSNE protection inside a WPA1 association..."));
413 }
414 ProtectionInfo::LegacyWpa(wpa) => wpa,
415 };
416 let mut buf = vec![];
417 #[expect(clippy::unwrap_used)]
418 ie::write_wpa1_ie(&mut buf, &s_wpa).unwrap(); Ok(Some(ProtectionIe::VendorIes(buf)))
420 }
421 Protection::Rsna(rsna) => {
422 let s_protection = rsna.negotiated_protection.to_full_protection();
423 let s_rsne = match s_protection {
424 ProtectionInfo::Rsne(rsne) => rsne,
425 ProtectionInfo::LegacyWpa(_) => {
426 return Err(format_err!("found WPA protection inside an RSNA..."));
427 }
428 };
429 let mut buf = Vec::with_capacity(s_rsne.len());
430 #[expect(clippy::unwrap_used)]
434 let () = s_rsne.write_into(&mut buf).unwrap();
435 Ok(Some(ProtectionIe::Rsne(buf)))
436 }
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443 use crate::client::{self};
444 use assert_matches::assert_matches;
445 use test_case::test_case;
446 use wlan_common::fake_bss_description;
447 use wlan_common::ie::fake_ies::fake_wpa_ie;
448 use wlan_common::ie::rsn::fake_rsnes::{fake_wpa2_s_rsne, fake_wpa3_s_rsne};
449 use wlan_common::security::wep::{WEP40_KEY_BYTES, WEP104_KEY_BYTES};
450 use wlan_common::security::wpa::credential::PSK_SIZE_BYTES;
451 use wlan_common::test_utils::fake_features::{
452 fake_security_support, fake_security_support_empty,
453 };
454
455 #[test]
456 fn rsn_auth_method() {
457 let protection = Protection::Open;
459 assert!(protection.rsn_auth_method().is_none());
460
461 let protection = Protection::Wep(WepKey::parse([1; 5]).expect("unable to parse WEP key"));
463 assert!(protection.rsn_auth_method().is_none());
464
465 let protection_info = ProtectionInfo::LegacyWpa(fake_wpa_ie());
467 let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
468 .expect("could create mocked WPA1 NegotiatedProtection");
469 let protection = Protection::LegacyWpa(Rsna {
470 negotiated_protection,
471 supplicant: Box::new(client::test_utils::mock_psk_supplicant().0),
472 });
473 assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Psk));
474
475 let protection_info = ProtectionInfo::Rsne(fake_wpa2_s_rsne());
477 let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
478 .expect("could create mocked WPA2 NegotiatedProtection");
479 let protection = Protection::Rsna(Rsna {
480 negotiated_protection,
481 supplicant: Box::new(client::test_utils::mock_psk_supplicant().0),
482 });
483 assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Psk));
484
485 let protection_info = ProtectionInfo::Rsne(fake_wpa3_s_rsne());
487 let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
488 .expect("could create mocked WPA3 NegotiatedProtection");
489 let protection = Protection::Rsna(Rsna {
490 negotiated_protection,
491 supplicant: Box::new(client::test_utils::mock_sae_supplicant().0),
492 });
493 assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Sae));
494
495 let protection_info = ProtectionInfo::Rsne(Rsne::common_owe_rsne());
497 let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
498 .expect("could create mocked OWE NegotiatedProtection");
499 let protection = Protection::Rsna(Rsna {
500 negotiated_protection,
501 supplicant: Box::new(client::test_utils::mock_owe_supplicant().0),
502 });
503 assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Owe));
504 }
505
506 #[test]
507 fn protection_from_wep40() {
508 let device = crate::test_utils::fake_device_info([1u8; 6].into());
509 let security_support = fake_security_support();
510 let config = Default::default();
511 let bss = fake_bss_description!(Wep);
512 let authenticator = wep::WepAuthenticator { key: WepKey::from([1u8; WEP40_KEY_BYTES]) };
513 let protection = Protection::try_from(SecurityContext {
514 security: &authenticator,
515 device: &device,
516 security_support: &security_support,
517 config: &config,
518 bss: &bss,
519 })
520 .unwrap();
521 assert!(matches!(protection, Protection::Wep(_)));
522 }
523
524 #[test]
525 fn protection_from_wep104() {
526 let device = crate::test_utils::fake_device_info([1u8; 6].into());
527 let security_support = fake_security_support();
528 let config = Default::default();
529 let bss = fake_bss_description!(Wep);
530 let authenticator = wep::WepAuthenticator { key: WepKey::from([1u8; WEP104_KEY_BYTES]) };
531 let protection = Protection::try_from(SecurityContext {
532 security: &authenticator,
533 device: &device,
534 security_support: &security_support,
535 config: &config,
536 bss: &bss,
537 })
538 .unwrap();
539 assert!(matches!(protection, Protection::Wep(_)));
540 }
541
542 #[test]
543 fn protection_from_wpa1_psk() {
544 let device = crate::test_utils::fake_device_info([1u8; 6].into());
545 let security_support = fake_security_support();
546 let config = Default::default();
547 let bss = fake_bss_description!(Wpa1);
548 let credentials = wpa::Wpa1Credentials::Psk([1u8; PSK_SIZE_BYTES].into());
549 let protection = Protection::try_from(SecurityContext {
550 security: &credentials,
551 device: &device,
552 security_support: &security_support,
553 config: &config,
554 bss: &bss,
555 })
556 .unwrap();
557 assert!(matches!(protection, Protection::LegacyWpa(_)));
558 }
559
560 #[test]
561 fn protection_from_wpa2_personal_psk() {
562 let device = crate::test_utils::fake_device_info([1u8; 6].into());
563 let security_support = fake_security_support();
564 let config = Default::default();
565 let bss = fake_bss_description!(Wpa2);
566 let credentials = wpa::Wpa2PersonalCredentials::Psk([1u8; PSK_SIZE_BYTES].into());
567 let protection = Protection::try_from(SecurityContext {
568 security: &credentials,
569 device: &device,
570 security_support: &security_support,
571 config: &config,
572 bss: &bss,
573 })
574 .unwrap();
575 assert!(matches!(protection, Protection::Rsna(_)));
576 }
577
578 #[test]
579 fn protection_from_wpa2_personal_passphrase() {
580 let device = crate::test_utils::fake_device_info([1u8; 6].into());
581 let security_support = fake_security_support();
582 let config = Default::default();
583 let bss = fake_bss_description!(Wpa2);
584 let credentials =
585 wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
586 let protection = Protection::try_from(SecurityContext {
587 security: &credentials,
588 device: &device,
589 security_support: &security_support,
590 config: &config,
591 bss: &bss,
592 })
593 .unwrap();
594 assert!(matches!(protection, Protection::Rsna(_)));
595 }
596
597 #[test]
598 fn protection_from_wpa2_personal_tkip_only_passphrase() {
599 let device = crate::test_utils::fake_device_info([1u8; 6].into());
600 let security_support = fake_security_support();
601 let config = Default::default();
602 let bss = fake_bss_description!(Wpa2TkipOnly);
603 let credentials =
604 wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
605 let protection = Protection::try_from(SecurityContext {
606 security: &credentials,
607 device: &device,
608 security_support: &security_support,
609 config: &config,
610 bss: &bss,
611 })
612 .unwrap();
613 assert!(matches!(protection, Protection::Rsna(_)));
614 }
615
616 #[test]
617 fn protection_from_wpa3_personal_passphrase() {
618 let device = crate::test_utils::fake_device_info([1u8; 6].into());
619 let security_support = fake_security_support();
620 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
621 let bss = fake_bss_description!(Wpa3);
622 let credentials =
623 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
624 let protection = Protection::try_from(SecurityContext {
625 security: &credentials,
626 device: &device,
627 security_support: &security_support,
628 config: &config,
629 bss: &bss,
630 })
631 .unwrap();
632 assert!(matches!(protection, Protection::Rsna(_)));
633 }
634
635 #[test]
636 fn protection_from_owe() {
637 let device = crate::test_utils::fake_device_info([1u8; 6].into());
638 let security_support = fake_security_support();
639 let config = ClientConfig { owe_supported: true, ..Default::default() };
640 let bss = fake_bss_description!(Owe);
641 let security = SecurityAuthenticator::Owe;
642 let protection = Protection::try_from(SecurityContext {
643 security: &security,
644 device: &device,
645 security_support: &security_support,
646 config: &config,
647 bss: &bss,
648 })
649 .unwrap();
650 assert!(matches!(protection, Protection::Rsna(_)));
651 }
652
653 #[test]
654 fn protection_from_owe_no_security_support_features() {
655 let device = crate::test_utils::fake_device_info([1u8; 6].into());
656 let security_support = fake_security_support_empty();
657 let config = Default::default();
658 let bss = fake_bss_description!(Owe);
659 let security = SecurityAuthenticator::Owe;
660 let protection = Protection::try_from(SecurityContext {
661 security: &security,
662 device: &device,
663 security_support: &security_support,
664 config: &config,
665 bss: &bss,
666 });
667 let _ = protection.expect_err("created OWE auth config for incompatible device");
668 }
669
670 #[test]
671 fn protection_from_wpa1_passphrase_with_open_bss() {
672 let device = crate::test_utils::fake_device_info([1u8; 6].into());
673 let security_support = fake_security_support();
674 let config = Default::default();
675 let bss = fake_bss_description!(Open);
676 let credentials =
677 wpa::Wpa1Credentials::Passphrase("password".as_bytes().try_into().unwrap());
678 let _ = Protection::try_from(SecurityContext {
679 security: &credentials,
680 device: &device,
681 security_support: &security_support,
682 config: &config,
683 bss: &bss,
684 })
685 .expect_err("incorrectly accepted WPA1 passphrase credentials with open BSS");
686 }
687
688 #[test]
689 fn protection_from_open_authenticator_with_wpa1_bss() {
690 let device = crate::test_utils::fake_device_info([1u8; 6].into());
691 let security_support = fake_security_support();
692 let config = Default::default();
693 let bss = fake_bss_description!(Wpa1);
694 let authenticator = SecurityAuthenticator::Open;
697 let _ = Protection::try_from(SecurityContext {
698 security: &authenticator,
699 device: &device,
700 security_support: &security_support,
701 config: &config,
702 bss: &bss,
703 })
704 .expect_err("incorrectly accepted open authenticator with WPA1 BSS");
705 }
706
707 #[test]
708 fn protection_from_wpa2_personal_passphrase_with_wpa3_bss() {
709 let device = crate::test_utils::fake_device_info([1u8; 6].into());
710 let security_support = fake_security_support();
711 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
712 let bss = fake_bss_description!(Wpa3);
713 let credentials =
714 wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
715 let _ = Protection::try_from(SecurityContext {
716 security: &credentials,
717 device: &device,
718 security_support: &security_support,
719 config: &config,
720 bss: &bss,
721 })
722 .expect_err("incorrectly accepted WPA2 passphrase credentials with WPA3 BSS");
723 }
724
725 #[test]
726 fn wpa1_psk_rsna() {
727 let device = crate::test_utils::fake_device_info([1u8; 6].into());
728 let security_support = fake_security_support();
729 let config = Default::default();
730 let bss = fake_bss_description!(Wpa1);
731 let credentials = wpa::Wpa1Credentials::Psk([1u8; PSK_SIZE_BYTES].into());
732 let context = SecurityContext {
733 security: &credentials,
734 device: &device,
735 security_support: &security_support,
736 config: &config,
737 bss: &bss,
738 };
739 assert!(context.authenticator_supplicant_ie().is_ok());
740 assert!(matches!(context.authentication_config(), auth::Config::ComputedPsk(_)));
741
742 let protection = Protection::try_from(context).unwrap();
743 assert_matches!(protection, Protection::LegacyWpa(rsna) => {
744 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Psk);
745 });
746 }
747
748 #[test]
749 fn wpa2_personal_psk_rsna() {
750 let device = crate::test_utils::fake_device_info([1u8; 6].into());
751 let security_support = fake_security_support();
752 let config = Default::default();
753 let bss = fake_bss_description!(Wpa2);
754 let credentials = wpa::Wpa2PersonalCredentials::Psk([1u8; PSK_SIZE_BYTES].into());
755 let context = SecurityContext {
756 security: &credentials,
757 device: &device,
758 security_support: &security_support,
759 config: &config,
760 bss: &bss,
761 };
762 assert!(context.authenticator_supplicant_rsne().is_ok());
763 assert!(matches!(context.authentication_config(), auth::Config::ComputedPsk(_)));
764
765 let protection = Protection::try_from(context).unwrap();
766 assert_matches!(protection, Protection::Rsna(rsna) => {
767 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Psk);
768 });
769 }
770
771 #[test]
772 fn wpa3_personal_passphrase_rsna_sme_auth() {
773 let device = crate::test_utils::fake_device_info([1u8; 6].into());
774 let security_support = fake_security_support();
775 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
776 let bss = fake_bss_description!(Wpa3);
777 let credentials =
778 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
779 let context = SecurityContext {
780 security: &credentials,
781 device: &device,
782 security_support: &security_support,
783 config: &config,
784 bss: &bss,
785 };
786 assert!(context.authenticator_supplicant_rsne().is_ok());
787 assert!(matches!(context.authentication_config(), Ok(auth::Config::Sae { .. })));
788
789 let protection = Protection::try_from(context).unwrap();
790 assert_matches!(protection, Protection::Rsna(rsna) => {
791 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Sae);
792 });
793 }
794
795 #[test_case(false, fake_bss_description!(Wpa3), PweMethod::Loop; "looping")]
796 #[test_case(true, fake_bss_description!(Wpa3), PweMethod::Loop; "only driver supports direct")]
797 #[test_case(false, fake_bss_description!(Wpa3, sae_hash_to_element: true), PweMethod::Loop; "only peer supports direct")]
798 #[test_case(true, fake_bss_description!(Wpa3, sae_hash_to_element: true), PweMethod::Direct; "direct")]
799 fn wpa3_personal_passphrase_rsna_sme_auth_hash_to_element(
800 driver_supports_h2e: bool,
801 bss: BssDescription,
802 expected_pwe_method: PweMethod,
803 ) {
804 let device = crate::test_utils::fake_device_info([1u8; 6].into());
805 let mut security_support = fake_security_support();
806 if driver_supports_h2e {
807 security_support.sae.as_mut().unwrap().hash_to_element_supported = Some(true);
808 }
809 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
810 let credentials =
811 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
812 let context = SecurityContext {
813 security: &credentials,
814 device: &device,
815 security_support: &security_support,
816 config: &config,
817 bss: &bss,
818 };
819 assert!(context.authenticator_supplicant_rsne().is_ok());
820 let pwe_method = assert_matches!(
821 context.authentication_config(),
822 Ok(auth::Config::Sae { pwe_method, .. }) => pwe_method
823 );
824 assert_eq!(pwe_method, expected_pwe_method);
825
826 let protection = Protection::try_from(context).unwrap();
827 assert_matches!(protection, Protection::Rsna(rsna) => {
828 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Sae);
829 });
830 }
831
832 #[test]
833 fn wpa3_personal_passphrase_rsna_driver_auth() {
834 let device = crate::test_utils::fake_device_info([1u8; 6].into());
835 let mut security_support = fake_security_support_empty();
836 security_support.mfp.get_or_insert_with(Default::default).supported = Some(true);
837 security_support.sae.get_or_insert_with(Default::default).driver_handler_supported =
838 Some(true);
839 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
840 let bss = fake_bss_description!(Wpa3);
841 let credentials =
842 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
843 let context = SecurityContext {
844 security: &credentials,
845 device: &device,
846 security_support: &security_support,
847 config: &config,
848 bss: &bss,
849 };
850 assert!(context.authenticator_supplicant_rsne().is_ok());
851 assert!(matches!(context.authentication_config(), Ok(auth::Config::DriverSae { .. })));
852
853 let protection = Protection::try_from(context).unwrap();
854 assert_matches!(protection, Protection::Rsna(rsna) => {
855 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Sae);
856 });
857 }
858
859 #[test]
860 fn wpa3_personal_passphrase_prefer_sme_auth() {
861 let device = crate::test_utils::fake_device_info([1u8; 6].into());
862 let mut security_support = fake_security_support_empty();
863 security_support.mfp.get_or_insert_with(Default::default).supported = Some(true);
864 security_support.sae.get_or_insert_with(Default::default).driver_handler_supported =
865 Some(true);
866 security_support.sae.get_or_insert_with(Default::default).sme_handler_supported =
867 Some(true);
868 let config = ClientConfig { wpa3_supported: true, ..Default::default() };
869 let bss = fake_bss_description!(Wpa3);
870 let credentials =
871 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
872 let context = SecurityContext {
873 security: &credentials,
874 device: &device,
875 security_support: &security_support,
876 config: &config,
877 bss: &bss,
878 };
879 assert!(matches!(context.authentication_config(), Ok(auth::Config::Sae { .. })));
880 }
881
882 #[test]
883 fn wpa3_personal_passphrase_no_security_support_features() {
884 let device = crate::test_utils::fake_device_info([1u8; 6].into());
885 let security_support = fake_security_support_empty();
886 let config = Default::default();
887 let bss = fake_bss_description!(Wpa3);
888 let credentials =
889 wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
890 let context = SecurityContext {
891 security: &credentials,
892 device: &device,
893 security_support: &security_support,
894 config: &config,
895 bss: &bss,
896 };
897 let _ = context
898 .authentication_config()
899 .expect_err("created WPA3 auth config for incompatible device");
900 }
901
902 #[test]
903 fn owe_rsna_sme_auth() {
904 let device = crate::test_utils::fake_device_info([1u8; 6].into());
905 let security_support = fake_security_support();
906 let config = ClientConfig { owe_supported: true, ..Default::default() };
907 let bss = fake_bss_description!(Owe);
908 let security = OweAuthenticator;
909 let context = SecurityContext {
910 security: &security,
911 device: &device,
912 security_support: &security_support,
913 config: &config,
914 bss: &bss,
915 };
916 assert!(context.authenticator_supplicant_rsne().is_ok());
917 assert!(matches!(context.authentication_config(), Ok(auth::Config::Owe)));
918
919 let protection = Protection::try_from(context).unwrap();
920 assert_matches!(protection, Protection::Rsna(rsna) => {
921 assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Owe);
922 });
923 }
924}