//! Support for password options, to be used with the passwords module use core_foundation::{string::CFString, base::{CFType, TCFType, CFOptionFlags}, number::CFNumber}; use security_framework_sys::{keychain::{SecProtocolType, SecAuthenticationType}, access_control::*}; use security_framework_sys::item::{ kSecAttrAccessControl, kSecAttrAccount, kSecAttrAuthenticationType, kSecAttrPath, kSecAttrPort, kSecAttrProtocol, kSecAttrSecurityDomain, kSecAttrServer, kSecAttrService, kSecClass, kSecClassGenericPassword, kSecClassInternetPassword, }; use crate::access_control::SecAccessControl; /// `PasswordOptions` constructor pub struct PasswordOptions { /// query built for the keychain request pub query: Vec<(CFString, CFType)>, } bitflags::bitflags! { /// The option flags used to configure the evaluation of a `SecAccessControl`. pub struct AccessControlOptions: CFOptionFlags { /** Constraint to access an item with either biometry or passcode. */ const USER_PRESENCE = kSecAccessControlUserPresence; #[cfg(feature = "OSX_10_13")] /** Constraint to access an item with Touch ID for any enrolled fingers, or Face ID. */ const BIOMETRY_ANY = kSecAccessControlBiometryAny; #[cfg(feature = "OSX_10_13")] /** Constraint to access an item with Touch ID for currently enrolled fingers, or from Face ID with the currently enrolled user. */ const BIOMETRY_CURRENT_SET = kSecAccessControlBiometryCurrentSet; /** Constraint to access an item with a passcode. */ const DEVICE_PASSCODE = kSecAccessControlDevicePasscode; #[cfg(feature = "OSX_10_15")] /** Constraint to access an item with a watch. */ const WATCH = kSecAccessControlWatch; /** Indicates that at least one constraint must be satisfied. */ const OR = kSecAccessControlOr; /** Indicates that all constraints must be satisfied. */ const AND = kSecAccessControlAnd; /** Enable a private key to be used in signing a block of data or verifying a signed block. */ const PRIVATE_KEY_USAGE = kSecAccessControlPrivateKeyUsage; /** Option to use an application-provided password for data encryption key generation. */ const APPLICATION_PASSWORD = kSecAccessControlApplicationPassword; } } impl PasswordOptions { /// Create a new generic password options /// Generic passwords are identified by service and account. They have other /// attributes, but this interface doesn't allow specifying them. #[must_use] pub fn new_generic_password(service: &str, account: &str) -> Self { let query = vec![ ( unsafe { CFString::wrap_under_get_rule(kSecClass) }, unsafe { CFString::wrap_under_get_rule(kSecClassGenericPassword).into_CFType() }, ), ( unsafe { CFString::wrap_under_get_rule(kSecAttrService) }, CFString::from(service).into_CFType(), ), ( unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, CFString::from(account).into_CFType(), ), ]; Self { query } } /// Create a new internet password options /// Internet passwords are identified by a number of attributes. /// They can have others, but this interface doesn't allow specifying them. #[must_use] pub fn new_internet_password( server: &str, security_domain: Option<&str>, account: &str, path: &str, port: Option, protocol: SecProtocolType, authentication_type: SecAuthenticationType, ) -> Self { let mut query = vec![ ( unsafe { CFString::wrap_under_get_rule(kSecClass) }, unsafe { CFString::wrap_under_get_rule(kSecClassInternetPassword) }.into_CFType(), ), ( unsafe { CFString::wrap_under_get_rule(kSecAttrServer) }, CFString::from(server).into_CFType(), ), ( unsafe { CFString::wrap_under_get_rule(kSecAttrPath) }, CFString::from(path).into_CFType(), ), ( unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) }, CFString::from(account).into_CFType(), ), ( unsafe { CFString::wrap_under_get_rule(kSecAttrProtocol) }, CFNumber::from(protocol as i32).into_CFType(), ), ( unsafe { CFString::wrap_under_get_rule(kSecAttrAuthenticationType) }, CFNumber::from(authentication_type as i32).into_CFType(), ), ]; if let Some(domain) = security_domain { query.push(( unsafe { CFString::wrap_under_get_rule(kSecAttrSecurityDomain) }, CFString::from(domain).into_CFType(), )) } if let Some(port) = port { query.push(( unsafe { CFString::wrap_under_get_rule(kSecAttrPort) }, CFNumber::from(i32::from(port)).into_CFType(), )) } Self { query } } /// Add access control to the password pub fn set_access_control_options(&mut self, options: AccessControlOptions) { self.query.push(( unsafe { CFString::wrap_under_get_rule(kSecAttrAccessControl) }, SecAccessControl::create_with_flags(options.bits()) .unwrap() .into_CFType(), )) } }