apple_security/
passwords_options.rs

1//! Support for password options, to be used with the passwords module
2
3use core_foundation::{string::CFString, base::{CFType, TCFType, CFOptionFlags}, number::CFNumber};
4use apple_security_sys::{keychain::{SecProtocolType, SecAuthenticationType}, access_control::*};
5use apple_security_sys::item::{
6    kSecAttrAccessControl, kSecAttrAccount, kSecAttrAuthenticationType, kSecAttrPath, kSecAttrPort, kSecAttrProtocol,
7    kSecAttrSecurityDomain, kSecAttrServer, kSecAttrService, kSecClass, kSecClassGenericPassword,
8    kSecClassInternetPassword,
9};
10use crate::access_control::SecAccessControl;
11
12/// `PasswordOptions` constructor
13pub struct PasswordOptions {
14    /// query built for the keychain request
15    pub query: Vec<(CFString, CFType)>,
16}
17
18bitflags::bitflags! {
19    /// The option flags used to configure the evaluation of a `SecAccessControl`.
20    pub struct AccessControlOptions: CFOptionFlags {
21        /** Constraint to access an item with either biometry or passcode. */
22        const USER_PRESENCE = kSecAccessControlUserPresence;
23        #[cfg(feature = "OSX_10_13")]
24        /** Constraint to access an item with Touch ID for any enrolled fingers, or Face ID. */
25        const BIOMETRY_ANY = kSecAccessControlBiometryAny;
26        #[cfg(feature = "OSX_10_13")]
27        /** Constraint to access an item with Touch ID for currently enrolled fingers, or from Face ID with the currently enrolled user. */
28        const BIOMETRY_CURRENT_SET = kSecAccessControlBiometryCurrentSet;
29        /** Constraint to access an item with a passcode. */
30        const DEVICE_PASSCODE = kSecAccessControlDevicePasscode;
31        #[cfg(feature = "OSX_10_15")]
32        /** Constraint to access an item with a watch. */
33        const WATCH = kSecAccessControlWatch;
34        /** Indicates that at least one constraint must be satisfied. */
35        const OR = kSecAccessControlOr;
36        /** Indicates that all constraints must be satisfied. */
37        const AND = kSecAccessControlAnd;
38        /** Enable a private key to be used in signing a block of data or verifying a signed block. */
39        const PRIVATE_KEY_USAGE = kSecAccessControlPrivateKeyUsage;
40        /** Option to use an application-provided password for data encryption key generation. */
41        const APPLICATION_PASSWORD = kSecAccessControlApplicationPassword;
42    }
43}
44
45impl PasswordOptions {
46    /// Create a new generic password options
47    /// Generic passwords are identified by service and account.  They have other
48    /// attributes, but this interface doesn't allow specifying them.
49    #[must_use] pub fn new_generic_password(service: &str, account: &str) -> Self {
50        let query = vec![
51            (
52                unsafe { CFString::wrap_under_get_rule(kSecClass) },
53                unsafe { CFString::wrap_under_get_rule(kSecClassGenericPassword).into_CFType() },
54            ),
55            (
56                unsafe { CFString::wrap_under_get_rule(kSecAttrService) },
57                CFString::from(service).into_CFType(),
58            ),
59            (
60                unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) },
61                CFString::from(account).into_CFType(),
62            ),
63        ];
64        Self { query }
65    }
66
67    /// Create a new internet password options
68    /// Internet passwords are identified by a number of attributes.
69    /// They can have others, but this interface doesn't allow specifying them.
70    #[must_use] pub fn new_internet_password(
71        server: &str,
72        security_domain: Option<&str>,
73        account: &str,
74        path: &str,
75        port: Option<u16>,
76        protocol: SecProtocolType,
77        authentication_type: SecAuthenticationType,
78    ) -> Self {
79        let mut query = vec![
80            (
81                unsafe { CFString::wrap_under_get_rule(kSecClass) },
82                unsafe { CFString::wrap_under_get_rule(kSecClassInternetPassword) }.into_CFType(),
83            ),
84            (
85                unsafe { CFString::wrap_under_get_rule(kSecAttrServer) },
86                CFString::from(server).into_CFType(),
87            ),
88            (
89                unsafe { CFString::wrap_under_get_rule(kSecAttrPath) },
90                CFString::from(path).into_CFType(),
91            ),
92            (
93                unsafe { CFString::wrap_under_get_rule(kSecAttrAccount) },
94                CFString::from(account).into_CFType(),
95            ),
96            (
97                unsafe { CFString::wrap_under_get_rule(kSecAttrProtocol) },
98                CFNumber::from(protocol as i32).into_CFType(),
99            ),
100            (
101                unsafe { CFString::wrap_under_get_rule(kSecAttrAuthenticationType) },
102                CFNumber::from(authentication_type as i32).into_CFType(),
103            ),
104        ];
105        if let Some(domain) = security_domain {
106            query.push((
107                unsafe { CFString::wrap_under_get_rule(kSecAttrSecurityDomain) },
108                CFString::from(domain).into_CFType(),
109            ))
110        }
111        if let Some(port) = port {
112            query.push((
113                unsafe { CFString::wrap_under_get_rule(kSecAttrPort) },
114                CFNumber::from(i32::from(port)).into_CFType(),
115            ))
116        }
117        Self { query }
118    }
119
120    /// Add access control to the password
121    pub fn set_access_control_options(&mut self, options: AccessControlOptions) {
122        self.query.push((
123            unsafe { CFString::wrap_under_get_rule(kSecAttrAccessControl) },
124            SecAccessControl::create_with_flags(options.bits())
125                .unwrap()
126                .into_CFType(),
127        ))
128    }
129}