apple_security_framework/
passwords_options.rs

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