use std::ptr::{self, null};
use core_foundation::string::CFString;
use core_foundation::base::{TCFType, CFOptionFlags, kCFAllocatorDefault};
use security_framework_sys::access_control::{
SecAccessControlGetTypeID, SecAccessControlCreateWithFlags,
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAttrAccessibleWhenUnlocked,
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly,
kSecAttrAccessibleAfterFirstUnlock
};
use security_framework_sys::base::{SecAccessControlRef, errSecParam};
use crate::base::{Error, Result};
declare_TCFType! {
SecAccessControl, SecAccessControlRef
}
impl_TCFType!(
SecAccessControl,
SecAccessControlRef,
SecAccessControlGetTypeID
);
unsafe impl Sync for SecAccessControl {}
unsafe impl Send for SecAccessControl {}
pub enum ProtectionMode {
AccessibleWhenPasscodeSetThisDeviceOnly,
AccessibleWhenUnlockedThisDeviceOnly,
AccessibleWhenUnlocked,
AccessibleAfterFirstUnlockThisDeviceOnly,
AccessibleAfterFirstUnlock,
}
impl SecAccessControl {
pub fn create_with_flags(flags: CFOptionFlags) -> Result<Self> {
Self::create_with_protection(None, flags)
}
pub fn create_with_protection(protection: Option<ProtectionMode>, flags: CFOptionFlags) -> Result<Self> {
let protection_val = protection.map(|v| {
match v {
ProtectionMode::AccessibleWhenPasscodeSetThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly) },
ProtectionMode::AccessibleWhenUnlockedThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenUnlockedThisDeviceOnly) },
ProtectionMode::AccessibleWhenUnlocked => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleWhenUnlocked) },
ProtectionMode::AccessibleAfterFirstUnlockThisDeviceOnly => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) },
ProtectionMode::AccessibleAfterFirstUnlock => unsafe { CFString::wrap_under_get_rule(kSecAttrAccessibleAfterFirstUnlock) },
}
});
unsafe {
let access_control = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
protection_val.map(|v| v.as_CFTypeRef()).unwrap_or(null()),
flags,
ptr::null_mut(),
);
if access_control.is_null() {
Err(Error::from_code(errSecParam))
} else {
Ok(Self::wrap_under_create_rule(access_control))
}
}
}
}