blob: 80a1aaa5105970cb8167c4403fa0f60030ce551f [file] [log] [blame]
// Copyright 2021 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 fidl_fuchsia_wlan_policy as wlan_policy;
#[derive(Debug, PartialEq)]
pub enum SecurityType {
None,
Wep,
Wpa,
Wpa2,
Wpa3,
}
impl ::std::convert::From<SecurityType> for wlan_policy::SecurityType {
fn from(arg: SecurityType) -> Self {
match arg {
SecurityType::None => wlan_policy::SecurityType::None,
SecurityType::Wep => wlan_policy::SecurityType::Wep,
SecurityType::Wpa => wlan_policy::SecurityType::Wpa,
SecurityType::Wpa2 => wlan_policy::SecurityType::Wpa2,
SecurityType::Wpa3 => wlan_policy::SecurityType::Wpa3,
}
}
}
impl std::str::FromStr for SecurityType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"none" => Ok(SecurityType::None),
"wep" => Ok(SecurityType::Wep),
"wpa" => Ok(SecurityType::Wpa),
"wpa2" => Ok(SecurityType::Wpa2),
"wpa3" => Ok(SecurityType::Wpa3),
other => Err(anyhow::format_err!("could not parse security type: {}", other)),
}
}
}
#[derive(Debug, PartialEq)]
pub enum CredentialType {
None,
Psk,
Password,
}
impl std::str::FromStr for CredentialType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"none" => Ok(CredentialType::None),
"psk" => Ok(CredentialType::Psk),
"password" => Ok(CredentialType::Password),
other => Err(anyhow::format_err!("could not parse security type: {}", other)),
}
}
}
fn validate_credential(
security_type: &SecurityType,
credential_type: &CredentialType,
) -> Result<(), anyhow::Error> {
if *credential_type == CredentialType::None && *security_type != SecurityType::None {
return Err(anyhow::format_err!(
"When SecurityType != None, you must provide a CredentialType and Credential"
));
} else if *credential_type != CredentialType::None && *security_type == SecurityType::None {
return Err(anyhow::format_err!(
"When SecurityType == None, you must use CredentialType::None and may must not provide a Credential"
));
}
Ok(())
}
fn construct_credential(
credential_type: CredentialType,
credential: String,
) -> wlan_policy::Credential {
match credential_type {
CredentialType::r#None => wlan_policy::Credential::None(wlan_policy::Empty),
CredentialType::Psk => {
// The PSK is given in a 64 character hexadecimal string. Config args are safe to
// unwrap because the tool requires them to be present in the command.
let psk_arg = credential.as_bytes().to_vec();
let psk = hex::decode(psk_arg).expect(
"Error: PSK must be 64 hexadecimal characters.\
Example: \"123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234\"",
);
wlan_policy::Credential::Psk(psk)
}
CredentialType::Password => {
wlan_policy::Credential::Password(credential.as_bytes().to_vec())
}
}
}
pub fn id_from_args(ssid: String, security_type: SecurityType) -> wlan_policy::NetworkIdentifier {
wlan_policy::NetworkIdentifier {
ssid: ssid.as_bytes().to_vec(),
type_: wlan_policy::SecurityType::from(security_type),
}
}
pub fn config_from_args(
ssid: String,
security_type: SecurityType,
credential_type: CredentialType,
credential: String,
) -> wlan_policy::NetworkConfig {
validate_credential(&security_type, &credential_type).unwrap();
let credential = construct_credential(credential_type, credential);
let network_id = id_from_args(ssid, security_type);
wlan_policy::NetworkConfig {
id: Some(network_id),
credential: Some(credential),
..Default::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
pub struct NetworkConfig {
ssid: String,
security_type: SecurityType,
credential_type: CredentialType,
credential: String,
}
impl From<NetworkConfig> for wlan_policy::NetworkConfig {
fn from(arg: NetworkConfig) -> Self {
config_from_args(arg.ssid, arg.security_type, arg.credential_type, arg.credential)
}
}
/// Tests that a WEP network config will be correctly translated for save and remove network.
#[test]
fn test_construct_config_wep() {
test_construct_config_security(wlan_policy::SecurityType::Wep, SecurityType::Wep);
}
/// Tests that a WPA network config will be correctly translated for save and remove network.
#[test]
fn test_construct_config_wpa() {
test_construct_config_security(wlan_policy::SecurityType::Wpa, SecurityType::Wpa);
}
/// Tests that a WPA2 network config will be correctly translated for save and remove network.
#[test]
fn test_construct_config_wpa2() {
test_construct_config_security(wlan_policy::SecurityType::Wpa2, SecurityType::Wpa2);
}
/// Tests that a WPA3 network config will be correctly translated for save and remove network.
#[test]
fn test_construct_config_wpa3() {
test_construct_config_security(wlan_policy::SecurityType::Wpa3, SecurityType::Wpa3);
}
/// Tests that a config for an open network will be correctly translated to FIDL values for
/// save and remove network.
#[test]
fn test_construct_config_open() {
let open_config = NetworkConfig {
ssid: "some_ssid".to_string(),
security_type: SecurityType::None,
credential_type: CredentialType::None,
credential: "".to_string(),
};
let expected_cfg = wlan_policy::NetworkConfig {
id: Some(wlan_policy::NetworkIdentifier {
ssid: "some_ssid".as_bytes().to_vec(),
type_: wlan_policy::SecurityType::None,
}),
credential: Some(wlan_policy::Credential::None(wlan_policy::Empty {})),
..Default::default()
};
let result_cfg = wlan_policy::NetworkConfig::from(open_config);
assert_eq!(expected_cfg, result_cfg);
}
/// Tests that a config for an open network with a password will fail gracefully.
#[test]
#[should_panic]
fn test_construct_config_open_with_password() {
let malformed_open_config = NetworkConfig {
ssid: "some_ssid".to_string(),
security_type: SecurityType::None,
credential_type: CredentialType::Password,
credential: "".to_string(),
};
let _errmsg = wlan_policy::NetworkConfig::from(malformed_open_config);
}
/// Tests that a config for a protected network without a password will fail gracefully.
#[test]
#[should_panic]
fn test_construct_config_protected_without_password() {
let malformed_wpa2_config = NetworkConfig {
ssid: "some_ssid".to_string(),
security_type: SecurityType::Wpa2,
credential_type: CredentialType::None,
credential: "".to_string(),
};
let _ = wlan_policy::NetworkConfig::from(malformed_wpa2_config);
}
/// Test that a config with a PSK will be translated correctly, including a transfer from a
/// hex string to bytes.
#[test]
fn test_construct_config_psk() {
// Test PSK separately since it has a unique credential
const ASCII_ZERO: u8 = 49;
let psk =
String::from_utf8([ASCII_ZERO; 64].to_vec()).expect("Failed to create PSK test value");
let wpa_config = NetworkConfig {
ssid: "some_ssid".to_string(),
security_type: SecurityType::Wpa2,
credential_type: CredentialType::Psk,
credential: psk,
};
let expected_cfg = wlan_policy::NetworkConfig {
id: Some(wlan_policy::NetworkIdentifier {
ssid: "some_ssid".as_bytes().to_vec(),
type_: wlan_policy::SecurityType::Wpa2,
}),
credential: Some(wlan_policy::Credential::Psk([17; 32].to_vec())),
..Default::default()
};
let result_cfg = wlan_policy::NetworkConfig::from(wpa_config);
assert_eq!(expected_cfg, result_cfg);
}
/// Test that the given variant of security type with a password works when constructing
/// network configs as used by save and remove network.
fn test_construct_config_security(
fidl_type: wlan_policy::SecurityType,
tool_type: SecurityType,
) {
let wpa_config = NetworkConfig {
ssid: "some_ssid".to_string(),
security_type: tool_type,
credential_type: CredentialType::Password,
credential: "some_password_here".to_string(),
};
let expected_cfg = wlan_policy::NetworkConfig {
id: Some(wlan_policy::NetworkIdentifier {
ssid: "some_ssid".as_bytes().to_vec(),
type_: fidl_type,
}),
credential: Some(wlan_policy::Credential::Password(
"some_password_here".as_bytes().to_vec(),
)),
..Default::default()
};
let result_cfg = wlan_policy::NetworkConfig::from(wpa_config);
assert_eq!(expected_cfg, result_cfg);
}
}