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 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}