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