blob: c7cc852593d955ff045d7550849a6f54070fc556 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::client::{rsn::Rsna, ClientConfig},
anyhow::{format_err, Error},
fidl_fuchsia_wlan_common as fidl_common,
fidl_fuchsia_wlan_mlme::DeviceInfo,
std::convert::{TryFrom, TryInto},
wlan_common::{
bss::BssDescription,
ie::{
self,
rsn::rsne::{self, Rsne},
wpa::WpaIe,
},
security::{
wep::{self, WepKey},
wpa, SecurityAuthenticator,
},
},
wlan_rsn::{
self,
auth::{self, psk::ToPsk},
nonce::NonceReader,
NegotiatedProtection, ProtectionInfo,
},
};
#[derive(Debug)]
pub enum Protection {
Open,
Wep(WepKey),
// WPA1 is based off of a modified pre-release version of IEEE 802.11i. It is similar enough
// that we can reuse the existing RSNA implementation rather than duplicating large pieces of
// logic.
LegacyWpa(Rsna),
Rsna(Rsna),
}
impl Protection {
pub fn rsn_auth_method(&self) -> Option<auth::MethodName> {
let rsna = match self {
Self::LegacyWpa(rsna) => rsna,
Self::Rsna(rsna) => rsna,
// Neither WEP or Open use an RSN, so None is returned.
Self::Wep(_) | Self::Open => {
return None;
}
};
Some(rsna.supplicant.get_auth_method())
}
}
#[derive(Debug)]
pub enum ProtectionIe {
Rsne(Vec<u8>),
VendorIes(Vec<u8>),
}
/// Context for authentication.
///
/// This ephemeral type is used to query and derive various IEs and RSN entities based on
/// parameterized security data, the configured client, and the target BSS. This is exposed to
/// client code via `TryFrom` implementations, which allow a context to be converted into a
/// negotiated `Protection`. These conversions fail if the combination of an authenticator and a
/// network is incompatible.
///
/// The type parameter `C` represents the parameterized security data and is either a type
/// representing credential data or a `SecurityAuthenticator`.
///
/// # Examples
///
/// To derive a `Protection`, construct a `SecurityContext` using a `SecurityAuthenticator` and
/// perform a conversion.
///
/// ```rust,ignore
/// // See the documentation for `SecurityAuthenticator` for more details.
/// let authenticator = SecurityAuthenticator::try_from(authentication)?;
/// let protection = Protection::try_from(SecurityContext {
/// security: &authenticator,
/// device: &device, // Device information.
/// security_support: &security_support, // Security features.
/// config: &config, // Client configuration.
/// bss: &bss, // BSS description.
/// })?;
/// ```
#[derive(Clone, Copy, Debug)]
pub struct SecurityContext<'a, C> {
/// Contextual security data. This field has security-related
pub security: &'a C,
pub device: &'a DeviceInfo,
pub security_support: &'a fidl_common::SecuritySupport,
pub config: &'a ClientConfig,
pub bss: &'a BssDescription,
}
impl<'a, C> SecurityContext<'a, C> {
/// Gets a context with a subject replaced by the given subject. Other fields are unmodified.
fn map<U>(&self, subject: &'a U) -> SecurityContext<'a, U> {
SecurityContext {
security: subject,
device: self.device,
security_support: self.security_support,
config: self.config,
bss: self.bss,
}
}
}
impl<'a> SecurityContext<'a, wpa::Wpa1Credentials> {
/// Gets the authenticator and supplicant IEs for WPA1 from the associated BSS.
fn authenticator_supplicant_ie(&self) -> Result<(WpaIe, WpaIe), Error> {
let a_wpa_ie = self.bss.wpa_ie()?;
if !crate::client::wpa::is_legacy_wpa_compatible(&a_wpa_ie) {
return Err(format_err!("Legacy WPA requested but IE is incompatible: {:?}", a_wpa_ie));
}
let s_wpa_ie = crate::client::wpa::construct_s_wpa(&a_wpa_ie);
Ok((a_wpa_ie, s_wpa_ie))
}
/// Gets the PSK used to authenticate via WPA1.
fn authentication_config(&self) -> auth::Config {
auth::Config::ComputedPsk(self.security.to_psk(&self.bss.ssid).into())
}
}
impl<'a> SecurityContext<'a, wpa::Wpa2PersonalCredentials> {
/// Gets the authenticator and supplicant RSNEs for WPA2 Personal from the associated BSS.
fn authenticator_supplicant_rsne(&self) -> Result<(Rsne, Rsne), Error> {
let a_rsne_ie = self
.bss
.rsne()
.ok_or_else(|| format_err!("WPA2 requested but RSNE is not present in BSS."))?;
let (_, a_rsne) = rsne::from_bytes(a_rsne_ie)
.map_err(|error| format_err!("Invalid RSNE IE {:02x?}: {:?}", a_rsne_ie, error))?;
let s_rsne = a_rsne.derive_wpa2_s_rsne(&self.security_support)?;
Ok((a_rsne, s_rsne))
}
/// Gets the PSK used to authenticate via WPA2 Personal.
fn authentication_config(&self) -> auth::Config {
auth::Config::ComputedPsk(self.security.to_psk(&self.bss.ssid).into())
}
}
impl<'a> SecurityContext<'a, wpa::Wpa3PersonalCredentials> {
/// Gets the authenticator and supplicant RSNEs for WPA3 Personal from the associated BSS.
fn authenticator_supplicant_rsne(&self) -> Result<(Rsne, Rsne), Error> {
let a_rsne_ie = self
.bss
.rsne()
.ok_or_else(|| format_err!("WPA3 requested but RSNE is not present in BSS."))?;
let (_, a_rsne) = rsne::from_bytes(a_rsne_ie)
.map_err(|error| format_err!("Invalid RSNE IE {:02x?}: {:?}", a_rsne_ie, error))?;
let s_rsne = a_rsne.derive_wpa3_s_rsne(&self.security_support)?;
Ok((a_rsne, s_rsne))
}
/// Gets the SAE used to authenticate via WPA3 Personal.
fn authentication_config(&self) -> Result<auth::Config, Error> {
match self.security {
wpa::Wpa3PersonalCredentials::Passphrase(ref passphrase) => {
// Prefer SAE in SME.
if self.security_support.sae.sme_handler_supported {
Ok(auth::Config::Sae {
ssid: self.bss.ssid.clone(),
password: passphrase.clone().into(),
mac: self.device.sta_addr.clone(),
peer_mac: self.bss.bssid.0,
})
} else if self.security_support.sae.driver_handler_supported {
Ok(auth::Config::DriverSae { password: passphrase.clone().into() })
} else {
Err(format_err!(
"Failed to generate WPA3 authentication config: no SAE SME nor driver \
handler"
))
}
}
}
}
}
impl<'a> TryFrom<SecurityContext<'a, SecurityAuthenticator>> for Protection {
type Error = Error;
fn try_from(context: SecurityContext<'a, SecurityAuthenticator>) -> Result<Self, Self::Error> {
match context.security {
SecurityAuthenticator::Open => context
.bss
.is_open()
.then(|| Protection::Open)
.ok_or_else(|| format_err!("BSS is not configured for open authentication")),
SecurityAuthenticator::Wep(authenticator) => context.map(authenticator).try_into(),
SecurityAuthenticator::Wpa(wpa) => match wpa {
wpa::WpaAuthenticator::Wpa1 { credentials, .. } => {
context.map(credentials).try_into()
}
wpa::WpaAuthenticator::Wpa2 { authentication, .. } => match authentication {
wpa::Authentication::Personal(personal) => context.map(personal).try_into(),
// TODO(fxbug.dev/92693): Implement conversions for WPA Enterprise.
_ => Err(format_err!("WPA Enterprise is unsupported")),
},
wpa::WpaAuthenticator::Wpa3 { authentication, .. } => match authentication {
wpa::Authentication::Personal(personal) => context.map(personal).try_into(),
// TODO(fxbug.dev/92693): Implement conversions for WPA Enterprise.
_ => Err(format_err!("WPA Enterprise is unsupported")),
},
},
}
}
}
impl<'a> TryFrom<SecurityContext<'a, wep::WepAuthenticator>> for Protection {
type Error = Error;
fn try_from(context: SecurityContext<'a, wep::WepAuthenticator>) -> Result<Self, Self::Error> {
context
.bss
.has_wep_configured()
.then(|| Protection::Wep(context.security.key.clone()))
.ok_or_else(|| format_err!("BSS is not configured for WEP"))
}
}
impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa1Credentials>> for Protection {
type Error = Error;
fn try_from(context: SecurityContext<'a, wpa::Wpa1Credentials>) -> Result<Self, Self::Error> {
context
.bss
.has_wpa1_configured()
.then(|| -> Result<_, Self::Error> {
let (a_wpa_ie, s_wpa_ie) = context.authenticator_supplicant_ie()?;
let negotiated_protection = NegotiatedProtection::from_legacy_wpa(&s_wpa_ie)?;
let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
NonceReader::new(&context.device.sta_addr[..])?,
context.authentication_config(),
context.device.sta_addr,
ProtectionInfo::LegacyWpa(s_wpa_ie),
context.bss.bssid.0,
ProtectionInfo::LegacyWpa(a_wpa_ie),
)
.map_err(|error| format_err!("Failed to create ESS-SA: {:?}", error))?;
Ok(Protection::LegacyWpa(Rsna {
negotiated_protection,
supplicant: Box::new(supplicant),
}))
})
.transpose()?
.ok_or_else(|| format_err!("BSS is not configured for WPA1"))
}
}
impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa2PersonalCredentials>> for Protection {
type Error = Error;
fn try_from(
context: SecurityContext<'a, wpa::Wpa2PersonalCredentials>,
) -> Result<Self, Self::Error> {
context
.bss
.has_wpa2_personal_configured()
.then(|| -> Result<_, Self::Error> {
let (a_rsne, s_rsne) = context.authenticator_supplicant_rsne()?;
let negotiated_protection = NegotiatedProtection::from_rsne(&s_rsne)?;
let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
NonceReader::new(&context.device.sta_addr[..])?,
context.authentication_config(),
context.device.sta_addr,
ProtectionInfo::Rsne(s_rsne),
context.bss.bssid.0,
ProtectionInfo::Rsne(a_rsne),
)
.map_err(|error| format_err!("Failed to creat ESS-SA: {:?}", error))?;
Ok(Protection::Rsna(Rsna {
negotiated_protection,
supplicant: Box::new(supplicant),
}))
})
.transpose()?
.ok_or_else(|| format_err!("BSS is not configured for WPA2 Personal"))
}
}
impl<'a> TryFrom<SecurityContext<'a, wpa::Wpa3PersonalCredentials>> for Protection {
type Error = Error;
fn try_from(
context: SecurityContext<'a, wpa::Wpa3PersonalCredentials>,
) -> Result<Self, Self::Error> {
context
.bss
.has_wpa3_personal_configured()
.then(|| -> Result<_, Self::Error> {
if !context.config.wpa3_supported {
return Err(format_err!("WPA3 requested but client does not support WPA3"));
}
let (a_rsne, s_rsne) = context.authenticator_supplicant_rsne()?;
let negotiated_protection = NegotiatedProtection::from_rsne(&s_rsne)?;
let supplicant = wlan_rsn::Supplicant::new_wpa_personal(
NonceReader::new(&context.device.sta_addr[..])?,
context.authentication_config()?,
context.device.sta_addr,
ProtectionInfo::Rsne(s_rsne),
context.bss.bssid.0,
ProtectionInfo::Rsne(a_rsne),
)
.map_err(|error| format_err!("Failed to create ESS-SA: {:?}", error))?;
Ok(Protection::Rsna(Rsna {
negotiated_protection,
supplicant: Box::new(supplicant),
}))
})
.transpose()?
.ok_or_else(|| format_err!("BSS is not configured for WPA3 Personal"))
}
}
/// Based on the type of protection, derive either RSNE or Vendor IEs:
/// No Protection or WEP: Neither
/// WPA2: RSNE
/// WPA1: Vendor IEs
pub(crate) fn build_protection_ie(protection: &Protection) -> Result<Option<ProtectionIe>, Error> {
match protection {
Protection::Open | Protection::Wep(_) => Ok(None),
Protection::LegacyWpa(rsna) => {
let s_protection = rsna.negotiated_protection.to_full_protection();
let s_wpa = match s_protection {
ProtectionInfo::Rsne(_) => {
return Err(format_err!("found RSNE protection inside a WPA1 association..."));
}
ProtectionInfo::LegacyWpa(wpa) => wpa,
};
let mut buf = vec![];
// Writing an RSNE into a Vector can never fail as a Vector can be grown when more
// space is required. If this panic ever triggers, something is clearly broken
// somewhere else.
ie::write_wpa1_ie(&mut buf, &s_wpa).unwrap();
Ok(Some(ProtectionIe::VendorIes(buf)))
}
Protection::Rsna(rsna) => {
let s_protection = rsna.negotiated_protection.to_full_protection();
let s_rsne = match s_protection {
ProtectionInfo::Rsne(rsne) => rsne,
ProtectionInfo::LegacyWpa(_) => {
return Err(format_err!("found WPA protection inside an RSNA..."));
}
};
let mut buf = Vec::with_capacity(s_rsne.len());
// Writing an RSNE into a Vector can never fail as a Vector can be grown when more
// space is required. If this panic ever triggers, something is clearly broken
// somewhere else.
let () = s_rsne.write_into(&mut buf).unwrap();
Ok(Some(ProtectionIe::Rsne(buf)))
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::client::{self, rsn::Rsna},
wlan_common::{
assert_variant, fake_bss_description,
ie::{
fake_ies::fake_wpa_ie,
rsn::fake_rsnes::{fake_wpa2_s_rsne, fake_wpa3_s_rsne},
},
security::{
wep::{WEP104_KEY_BYTES, WEP40_KEY_BYTES},
wpa::credential::PSK_SIZE_BYTES,
},
test_utils::fake_features::{fake_security_support, fake_security_support_empty},
},
wlan_rsn::{rsna::NegotiatedProtection, ProtectionInfo},
};
#[test]
fn rsn_auth_method() {
// Open
let protection = Protection::Open;
assert!(protection.rsn_auth_method().is_none());
// Wep
let protection = Protection::Wep(WepKey::parse(&[1; 5]).expect("unable to parse WEP key"));
assert!(protection.rsn_auth_method().is_none());
// WPA1
let protection_info = ProtectionInfo::LegacyWpa(fake_wpa_ie());
let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
.expect("could create mocked WPA1 NegotiatedProtection");
let protection = Protection::LegacyWpa(Rsna {
negotiated_protection,
supplicant: Box::new(client::test_utils::mock_psk_supplicant().0),
});
assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Psk));
// WPA2
let protection_info = ProtectionInfo::Rsne(fake_wpa2_s_rsne());
let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
.expect("could create mocked WPA2 NegotiatedProtection");
let protection = Protection::Rsna(Rsna {
negotiated_protection,
supplicant: Box::new(client::test_utils::mock_psk_supplicant().0),
});
assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Psk));
// WPA3
let protection_info = ProtectionInfo::Rsne(fake_wpa3_s_rsne());
let negotiated_protection = NegotiatedProtection::from_protection(&protection_info)
.expect("could create mocked WPA3 NegotiatedProtection");
let protection = Protection::Rsna(Rsna {
negotiated_protection,
supplicant: Box::new(client::test_utils::mock_sae_supplicant().0),
});
assert_eq!(protection.rsn_auth_method(), Some(auth::MethodName::Sae));
}
#[test]
fn protection_from_wep40() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wep);
let authenticator = wep::WepAuthenticator { key: WepKey::from([1u8; WEP40_KEY_BYTES]) };
let protection = Protection::try_from(SecurityContext {
security: &authenticator,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.unwrap();
assert!(matches!(protection, Protection::Wep(_)));
}
#[test]
fn protection_from_wep104() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wep);
let authenticator = wep::WepAuthenticator { key: WepKey::from([1u8; WEP104_KEY_BYTES]) };
let protection = Protection::try_from(SecurityContext {
security: &authenticator,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.unwrap();
assert!(matches!(protection, Protection::Wep(_)));
}
#[test]
fn protection_from_wpa1_psk() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wpa1);
let credentials = wpa::Wpa1Credentials::Psk([1u8; PSK_SIZE_BYTES].into());
let protection = Protection::try_from(SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.unwrap();
assert!(matches!(protection, Protection::LegacyWpa(_)));
}
#[test]
fn protection_from_wpa2_personal_psk() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wpa2);
let credentials = wpa::Wpa2PersonalCredentials::Psk([1u8; PSK_SIZE_BYTES].into());
let protection = Protection::try_from(SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.unwrap();
assert!(matches!(protection, Protection::Rsna(_)));
}
#[test]
fn protection_from_wpa2_personal_passphrase() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wpa2);
let credentials =
wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
let protection = Protection::try_from(SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.unwrap();
assert!(matches!(protection, Protection::Rsna(_)));
}
#[test]
fn protection_from_wpa2_personal_tkip_only_passphrase() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wpa2TkipOnly);
let credentials =
wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
let protection = Protection::try_from(SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.unwrap();
assert!(matches!(protection, Protection::Rsna(_)));
}
#[test]
fn protection_from_wpa3_personal_passphrase() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = ClientConfig { wpa3_supported: true, ..Default::default() };
let bss = fake_bss_description!(Wpa3);
let credentials =
wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
let protection = Protection::try_from(SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.unwrap();
assert!(matches!(protection, Protection::Rsna(_)));
}
#[test]
fn protection_from_wpa1_passphrase_with_open_bss() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Open);
let credentials =
wpa::Wpa1Credentials::Passphrase("password".as_bytes().try_into().unwrap());
Protection::try_from(SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.expect_err("incorrectly accepted WPA1 passphrase credentials with open BSS");
}
#[test]
fn protection_from_open_authenticator_with_wpa1_bss() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wpa1);
// Note that there is no credentials type associated with an open authenticator, so an
// authenticator is used here instead.
let authenticator = SecurityAuthenticator::Open;
Protection::try_from(SecurityContext {
security: &authenticator,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.expect_err("incorrectly accepted open authenticator with WPA1 BSS");
}
#[test]
fn protection_from_wpa2_personal_passphrase_with_wpa3_bss() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = ClientConfig { wpa3_supported: true, ..Default::default() };
let bss = fake_bss_description!(Wpa3);
let credentials =
wpa::Wpa2PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
Protection::try_from(SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
})
.expect_err("incorrectly accepted WPA2 passphrase credentials with WPA3 BSS");
}
#[test]
fn wpa1_psk_rsna() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wpa1);
let credentials = wpa::Wpa1Credentials::Psk([1u8; PSK_SIZE_BYTES].into());
let context = SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
};
assert!(context.authenticator_supplicant_ie().is_ok());
assert!(matches!(context.authentication_config(), auth::Config::ComputedPsk(_)));
let protection = Protection::try_from(context).unwrap();
assert_variant!(protection, Protection::LegacyWpa(rsna) => {
assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Psk);
});
}
#[test]
fn wpa2_personal_psk_rsna() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = Default::default();
let bss = fake_bss_description!(Wpa2);
let credentials = wpa::Wpa2PersonalCredentials::Psk([1u8; PSK_SIZE_BYTES].into());
let context = SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
};
assert!(context.authenticator_supplicant_rsne().is_ok());
assert!(matches!(context.authentication_config(), auth::Config::ComputedPsk(_)));
let protection = Protection::try_from(context).unwrap();
assert_variant!(protection, Protection::Rsna(rsna) => {
assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Psk);
});
}
#[test]
fn wpa3_personal_passphrase_rsna_sme_auth() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support();
let config = ClientConfig { wpa3_supported: true, ..Default::default() };
let bss = fake_bss_description!(Wpa3);
let credentials =
wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
let context = SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
};
assert!(context.authenticator_supplicant_rsne().is_ok());
assert!(matches!(context.authentication_config(), Ok(auth::Config::Sae { .. })));
let protection = Protection::try_from(context).unwrap();
assert_variant!(protection, Protection::Rsna(rsna) => {
assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Sae);
});
}
#[test]
fn wpa3_personal_passphrase_rsna_driver_auth() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let mut security_support = fake_security_support_empty();
security_support.mfp.supported = true;
security_support.sae.driver_handler_supported = true;
let config = ClientConfig { wpa3_supported: true, ..Default::default() };
let bss = fake_bss_description!(Wpa3);
let credentials =
wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
let context = SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
};
assert!(context.authenticator_supplicant_rsne().is_ok());
assert!(matches!(context.authentication_config(), Ok(auth::Config::DriverSae { .. })));
let protection = Protection::try_from(context).unwrap();
assert_variant!(protection, Protection::Rsna(rsna) => {
assert_eq!(rsna.supplicant.get_auth_method(), auth::MethodName::Sae);
});
}
#[test]
fn wpa3_personal_passphrase_prefer_sme_auth() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let mut security_support = fake_security_support_empty();
security_support.mfp.supported = true;
security_support.sae.driver_handler_supported = true;
security_support.sae.sme_handler_supported = true;
let config = ClientConfig { wpa3_supported: true, ..Default::default() };
let bss = fake_bss_description!(Wpa3);
let credentials =
wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
let context = SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
};
assert!(matches!(context.authentication_config(), Ok(auth::Config::Sae { .. })));
}
#[test]
fn wpa3_personal_passphrase_no_security_support_features() {
let device = crate::test_utils::fake_device_info([1u8; 6]);
let security_support = fake_security_support_empty();
let config = Default::default();
let bss = fake_bss_description!(Wpa3);
let credentials =
wpa::Wpa3PersonalCredentials::Passphrase("password".as_bytes().try_into().unwrap());
let context = SecurityContext {
security: &credentials,
device: &device,
security_support: &security_support,
config: &config,
bss: &bss,
};
context
.authentication_config()
.expect_err("created WPA3 auth config for incompatible device");
}
}