1use bitflags::bitflags;
2
3use crate::bridge::{self, Handle};
4use crate::error::Result;
5
6bitflags! {
7 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8 pub struct AccessControlFlags: u64 {
9 const DEFAULTS = 0;
10 const USER_PRESENCE = 1 << 0;
11 const BIOMETRY_ANY = 1 << 1;
12 const BIOMETRY_CURRENT_SET = 1 << 3;
13 const DEVICE_PASSCODE = 1 << 4;
14 const COMPANION = 1 << 5;
15 const OR = 1 << 14;
16 const AND = 1 << 15;
17 const PRIVATE_KEY_USAGE = 1 << 30;
18 const APPLICATION_PASSWORD = 1 << 31;
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum AccessControlProtection {
24 WhenUnlocked,
25 AfterFirstUnlock,
26 WhenPasscodeSetThisDeviceOnly,
27 WhenUnlockedThisDeviceOnly,
28 AfterFirstUnlockThisDeviceOnly,
29}
30
31impl AccessControlProtection {
32 const fn as_bridge_name(self) -> &'static str {
33 match self {
34 Self::WhenUnlocked => "when_unlocked",
35 Self::AfterFirstUnlock => "after_first_unlock",
36 Self::WhenPasscodeSetThisDeviceOnly => "when_passcode_set_this_device_only",
37 Self::WhenUnlockedThisDeviceOnly => "when_unlocked_this_device_only",
38 Self::AfterFirstUnlockThisDeviceOnly => "after_first_unlock_this_device_only",
39 }
40 }
41}
42
43#[derive(Debug)]
44pub struct AccessControl {
45 handle: Handle,
46}
47
48impl AccessControl {
49 pub fn type_id() -> usize {
50 unsafe { bridge::security_access_control_get_type_id() }
51 }
52
53 pub fn create(protection: AccessControlProtection, flags: AccessControlFlags) -> Result<Self> {
54 let protection = bridge::cstring(protection.as_bridge_name())?;
55 let mut status = 0;
56 let mut error = std::ptr::null_mut();
57 let raw = unsafe {
58 bridge::security_access_control_create(
59 protection.as_ptr(),
60 flags.bits(),
61 &mut status,
62 &mut error,
63 )
64 };
65 bridge::required_handle("security_access_control_create", raw, status, error)
66 .map(|handle| Self { handle })
67 }
68
69 pub fn is_valid(&self) -> bool {
70 !self.handle.as_ptr().is_null()
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Hash)]
75pub struct KeychainEntry {
76 account: String,
77 service: String,
78}
79
80impl KeychainEntry {
81 pub fn new(account: impl Into<String>, service: impl Into<String>) -> Self {
82 Self {
83 account: account.into(),
84 service: service.into(),
85 }
86 }
87
88 pub fn account(&self) -> &str {
89 &self.account
90 }
91
92 pub fn service(&self) -> &str {
93 &self.service
94 }
95
96 pub fn set(&self, password: &str) -> Result<()> {
97 Keychain::set(&self.account, &self.service, password)
98 }
99
100 pub fn get(&self) -> Result<String> {
101 Keychain::get(&self.account, &self.service)
102 }
103
104 pub fn delete(&self) -> Result<()> {
105 Keychain::delete(&self.account, &self.service)
106 }
107}
108
109pub struct Keychain;
110
111impl Keychain {
112 pub fn entry(account: impl Into<String>, service: impl Into<String>) -> KeychainEntry {
113 KeychainEntry::new(account, service)
114 }
115
116 pub fn set(account: &str, service: &str, password: &str) -> Result<()> {
117 let account = bridge::cstring(account)?;
118 let service = bridge::cstring(service)?;
119 let password = bridge::cstring(password)?;
120 let mut error = std::ptr::null_mut();
121 let status = unsafe {
122 bridge::security_keychain_set_password(
123 account.as_ptr(),
124 service.as_ptr(),
125 password.as_ptr(),
126 &mut error,
127 )
128 };
129 bridge::status_result("security_keychain_set_password", status, error)
130 }
131
132 pub fn get(account: &str, service: &str) -> Result<String> {
133 let account = bridge::cstring(account)?;
134 let service = bridge::cstring(service)?;
135 let mut status = 0;
136 let mut error = std::ptr::null_mut();
137 let raw = unsafe {
138 bridge::security_keychain_get_password(
139 account.as_ptr(),
140 service.as_ptr(),
141 &mut status,
142 &mut error,
143 )
144 };
145 bridge::required_string("security_keychain_get_password", raw, status, error)
146 }
147
148 pub fn delete(account: &str, service: &str) -> Result<()> {
149 let account = bridge::cstring(account)?;
150 let service = bridge::cstring(service)?;
151 let mut error = std::ptr::null_mut();
152 let status = unsafe {
153 bridge::security_keychain_delete_password(
154 account.as_ptr(),
155 service.as_ptr(),
156 &mut error,
157 )
158 };
159 bridge::status_result("security_keychain_delete_password", status, error)
160 }
161
162 pub fn list_accounts(service: &str) -> Result<Vec<String>> {
163 let service = bridge::cstring(service)?;
164 let mut status = 0;
165 let mut error = std::ptr::null_mut();
166 let raw = unsafe {
167 bridge::security_keychain_list_accounts(service.as_ptr(), &mut status, &mut error)
168 };
169 bridge::required_json("security_keychain_list_accounts", raw, status, error)
170 }
171}