use crate::access_control::SecAccessControl;
use core_foundation::base::{CFOptionFlags, CFType, TCFType};
#[allow(unused_imports)]
use core_foundation::boolean::CFBoolean;
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::CFNumber;
use core_foundation::string::{CFString, CFStringRef};
use security_framework_sys::access_control::*;
use security_framework_sys::item::{
kSecAttrAccessControl, kSecAttrAccessGroup, kSecAttrAccount, kSecAttrAuthenticationType,
kSecAttrComment, kSecAttrDescription, kSecAttrLabel,
kSecAttrPath, kSecAttrPort, kSecAttrProtocol, kSecAttrSecurityDomain, kSecAttrServer,
kSecAttrService, kSecClass, kSecClassGenericPassword, kSecClassInternetPassword,
};
#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
use security_framework_sys::item::kSecAttrSynchronizable;
#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
use security_framework_sys::item::kSecAttrSynchronizableAny;
#[cfg(any(feature = "OSX_10_15", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
use security_framework_sys::item::kSecUseDataProtectionKeychain;
use security_framework_sys::keychain::{SecAuthenticationType, SecProtocolType};
pub struct PasswordOptions {
#[deprecated(note = "This field should have been private. Please use setters that don't expose CFType")]
pub query: Vec<(CFString, CFType)>,
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AccessControlOptions: CFOptionFlags {
const USER_PRESENCE = kSecAccessControlUserPresence;
#[cfg(feature = "OSX_10_13")]
const BIOMETRY_ANY = kSecAccessControlBiometryAny;
#[cfg(feature = "OSX_10_13")]
const BIOMETRY_CURRENT_SET = kSecAccessControlBiometryCurrentSet;
const DEVICE_PASSCODE = kSecAccessControlDevicePasscode;
#[cfg(feature = "OSX_10_15")]
const WATCH = kSecAccessControlWatch;
const OR = kSecAccessControlOr;
const AND = kSecAccessControlAnd;
const PRIVATE_KEY_USAGE = kSecAccessControlPrivateKeyUsage;
const APPLICATION_PASSWORD = kSecAccessControlApplicationPassword;
}
}
impl PasswordOptions {
#[must_use]
pub fn new_generic_password(service: &str, account: &str) -> Self {
#[allow(deprecated)]
Self { 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()),
] }
}
#[must_use]
pub fn new_internet_password(
server: &str,
security_domain: Option<&str>,
account: &str,
path: &str,
port: Option<u16>,
protocol: SecProtocolType,
authentication_type: SecAuthenticationType,
) -> Self {
#[allow(deprecated)]
let mut this = Self { 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 {
unsafe {
this.push_query(kSecAttrSecurityDomain, CFString::from(domain));
}
}
if let Some(port) = port {
unsafe {
this.push_query(kSecAttrPort, CFNumber::from(i32::from(port)));
}
}
this
}
pub fn set_access_control_options(&mut self, options: AccessControlOptions) {
unsafe {
self.push_query(kSecAttrAccessControl, SecAccessControl::create_with_flags(options.bits()).unwrap());
}
}
pub fn set_access_control(&mut self, access_control: SecAccessControl) {
unsafe {
self.push_query(kSecAttrAccessControl, access_control);
}
}
pub fn set_access_group(&mut self, group: &str) {
unsafe {
self.push_query(kSecAttrAccessGroup, CFString::from(group));
}
}
#[cfg(any(feature = "OSX_10_12", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub fn set_access_synchronized(&mut self, synchronized: Option<bool>) {
unsafe {
if let Some(synchronizable) = synchronized {
self.push_query(kSecAttrSynchronizable, CFBoolean::from(synchronizable));
} else {
let either = CFString::wrap_under_get_rule(kSecAttrSynchronizableAny);
self.push_query(kSecAttrSynchronizable, either);
}
}
}
pub fn set_comment(&mut self, comment: &str) {
unsafe {
self.push_query(kSecAttrComment, CFString::from(comment));
}
}
pub fn set_description(&mut self, description: &str) {
unsafe {
self.push_query(kSecAttrDescription, CFString::from(description));
}
}
pub fn set_label(&mut self, label: &str) {
unsafe {
self.push_query(kSecAttrLabel, CFString::from(label));
}
}
#[cfg(any(feature = "OSX_10_15", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
pub fn use_protected_keychain(&mut self) {
unsafe {
self.push_query(kSecUseDataProtectionKeychain, CFBoolean::from(true));
}
}
pub(crate) unsafe fn push_query(&mut self, static_key_constant: CFStringRef, value: impl TCFType) {
#[allow(deprecated)]
self.query.push((
unsafe { CFString::wrap_under_get_rule(static_key_constant) },
value.into_CFType(),
));
}
pub(crate) fn to_dictionary(&self) -> CFDictionary<CFString, CFType> {
#[allow(deprecated)]
CFDictionary::from_CFType_pairs(&self.query[..])
}
}