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 create(protection: AccessControlProtection, flags: AccessControlFlags) -> Result<Self> {
50 let protection = bridge::cstring(protection.as_bridge_name())?;
51 let mut status = 0;
52 let mut error = std::ptr::null_mut();
53 let raw = unsafe {
54 bridge::security_access_control_create(
55 protection.as_ptr(),
56 flags.bits(),
57 &mut status,
58 &mut error,
59 )
60 };
61 bridge::required_handle("security_access_control_create", raw, status, error)
62 .map(|handle| Self { handle })
63 }
64
65 pub fn is_valid(&self) -> bool {
66 !self.handle.as_ptr().is_null()
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
71pub struct KeychainEntry {
72 account: String,
73 service: String,
74}
75
76impl KeychainEntry {
77 pub fn new(account: impl Into<String>, service: impl Into<String>) -> Self {
78 Self {
79 account: account.into(),
80 service: service.into(),
81 }
82 }
83
84 pub fn account(&self) -> &str {
85 &self.account
86 }
87
88 pub fn service(&self) -> &str {
89 &self.service
90 }
91
92 pub fn set(&self, password: &str) -> Result<()> {
93 Keychain::set(&self.account, &self.service, password)
94 }
95
96 pub fn get(&self) -> Result<String> {
97 Keychain::get(&self.account, &self.service)
98 }
99
100 pub fn delete(&self) -> Result<()> {
101 Keychain::delete(&self.account, &self.service)
102 }
103}
104
105pub struct Keychain;
106
107impl Keychain {
108 pub fn entry(account: impl Into<String>, service: impl Into<String>) -> KeychainEntry {
109 KeychainEntry::new(account, service)
110 }
111
112 pub fn set(account: &str, service: &str, password: &str) -> Result<()> {
113 let account = bridge::cstring(account)?;
114 let service = bridge::cstring(service)?;
115 let password = bridge::cstring(password)?;
116 let mut error = std::ptr::null_mut();
117 let status = unsafe {
118 bridge::security_keychain_set_password(
119 account.as_ptr(),
120 service.as_ptr(),
121 password.as_ptr(),
122 &mut error,
123 )
124 };
125 bridge::status_result("security_keychain_set_password", status, error)
126 }
127
128 pub fn get(account: &str, service: &str) -> Result<String> {
129 let account = bridge::cstring(account)?;
130 let service = bridge::cstring(service)?;
131 let mut status = 0;
132 let mut error = std::ptr::null_mut();
133 let raw = unsafe {
134 bridge::security_keychain_get_password(
135 account.as_ptr(),
136 service.as_ptr(),
137 &mut status,
138 &mut error,
139 )
140 };
141 bridge::required_string("security_keychain_get_password", raw, status, error)
142 }
143
144 pub fn delete(account: &str, service: &str) -> Result<()> {
145 let account = bridge::cstring(account)?;
146 let service = bridge::cstring(service)?;
147 let mut error = std::ptr::null_mut();
148 let status = unsafe {
149 bridge::security_keychain_delete_password(
150 account.as_ptr(),
151 service.as_ptr(),
152 &mut error,
153 )
154 };
155 bridge::status_result("security_keychain_delete_password", status, error)
156 }
157
158 pub fn list_accounts(service: &str) -> Result<Vec<String>> {
159 let service = bridge::cstring(service)?;
160 let mut status = 0;
161 let mut error = std::ptr::null_mut();
162 let raw = unsafe {
163 bridge::security_keychain_list_accounts(service.as_ptr(), &mut status, &mut error)
164 };
165 bridge::required_json("security_keychain_list_accounts", raw, status, error)
166 }
167}