Skip to main content

security/keychain/
mod.rs

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}