apple_security/
import_export.rs

1//! Security Framework type import/export support.
2
3use core_foundation::array::CFArray;
4use core_foundation::base::{CFType, TCFType};
5use core_foundation::data::CFData;
6use core_foundation::dictionary::CFDictionary;
7use core_foundation::string::CFString;
8use apple_security_sys::import_export::*;
9use std::ptr;
10
11use crate::base::Result;
12use crate::certificate::SecCertificate;
13use crate::cvt;
14use crate::identity::SecIdentity;
15#[cfg(target_os = "macos")]
16use crate::os::macos::access::SecAccess;
17#[cfg(target_os = "macos")]
18use crate::os::macos::keychain::SecKeychain;
19use crate::trust::SecTrust;
20
21/// Information about an imported identity.
22pub struct ImportedIdentity {
23    /// The label of the identity.
24    pub label: Option<String>,
25    /// The ID of the identity. Typically the SHA-1 hash of the public key.
26    pub key_id: Option<Vec<u8>>,
27    /// A `SecTrust` object set up to validate this identity.
28    pub trust: Option<SecTrust>,
29    /// A certificate chain validating this identity.
30    pub cert_chain: Option<Vec<SecCertificate>>,
31    /// The identity itself.
32    pub identity: Option<SecIdentity>,
33    _p: (),
34}
35
36/// A builder type to import an identity from PKCS#12 formatted data.
37#[derive(Default)]
38pub struct Pkcs12ImportOptions {
39    passphrase: Option<CFString>,
40    #[cfg(target_os = "macos")]
41    keychain: Option<SecKeychain>,
42    #[cfg(target_os = "macos")]
43    access: Option<SecAccess>,
44}
45
46#[cfg(target_os = "macos")]
47impl crate::Pkcs12ImportOptionsInternals for Pkcs12ImportOptions {
48    #[inline(always)]
49    fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
50        self.keychain = Some(keychain);
51        self
52    }
53
54    #[inline(always)]
55    fn access(&mut self, access: SecAccess) -> &mut Self {
56        self.access = Some(access);
57        self
58    }
59}
60
61impl Pkcs12ImportOptions {
62    /// Creates a new builder with default options.
63    #[inline(always)]
64    #[must_use]
65    pub fn new() -> Self {
66        Self::default()
67    }
68
69    /// Specifies the passphrase to be used to decrypt the data.
70    ///
71    /// This must be specified, as unencrypted PKCS#12 data is not supported.
72    #[inline]
73    pub fn passphrase(&mut self, passphrase: &str) -> &mut Self {
74        self.passphrase = Some(CFString::new(passphrase));
75        self
76    }
77
78    /// Imports identities from PKCS#12 encoded data.
79    pub fn import(&self, pkcs12_data: &[u8]) -> Result<Vec<ImportedIdentity>> {
80        unsafe {
81            let pkcs12_data = CFData::from_buffer(pkcs12_data);
82
83            let mut options = vec![];
84
85            if let Some(ref passphrase) = self.passphrase {
86                options.push((
87                    CFString::wrap_under_get_rule(kSecImportExportPassphrase),
88                    passphrase.as_CFType(),
89                ));
90            }
91
92            self.import_setup(&mut options);
93
94            let options = CFDictionary::from_CFType_pairs(&options);
95
96            let mut raw_items = ptr::null();
97            cvt(SecPKCS12Import(
98                pkcs12_data.as_concrete_TypeRef(),
99                options.as_concrete_TypeRef(),
100                &mut raw_items,
101            ))?;
102            let raw_items = CFArray::<CFDictionary<CFString, *const _>>::wrap_under_create_rule(raw_items);
103
104            let mut items = vec![];
105
106            for raw_item in &raw_items {
107                let label = raw_item
108                    .find(kSecImportItemLabel)
109                    .map(|label| CFString::wrap_under_get_rule((*label).cast()).to_string());
110                let key_id = raw_item
111                    .find(kSecImportItemKeyID)
112                    .map(|key_id| CFData::wrap_under_get_rule((*key_id).cast()).to_vec());
113                let trust = raw_item
114                    .find(kSecImportItemTrust)
115                    .map(|trust| SecTrust::wrap_under_get_rule(*trust as *mut _));
116                let cert_chain = raw_item.find(kSecImportItemCertChain.cast()).map(
117                    |cert_chain| {
118                        CFArray::<SecCertificate>::wrap_under_get_rule((*cert_chain).cast())
119                            .iter()
120                            .map(|c| c.clone())
121                            .collect()
122                    },
123                );
124                let identity = raw_item
125                    .find(kSecImportItemIdentity)
126                    .map(|identity| SecIdentity::wrap_under_get_rule(*identity as *mut _));
127
128                items.push(ImportedIdentity {
129                    label,
130                    key_id,
131                    trust,
132                    cert_chain,
133                    identity,
134                    _p: (),
135                });
136            }
137
138            Ok(items)
139        }
140    }
141
142    #[cfg(target_os = "macos")]
143    fn import_setup(&self, options: &mut Vec<(CFString, CFType)>) {
144        unsafe {
145            if let Some(ref keychain) = self.keychain {
146                options.push((
147                    CFString::wrap_under_get_rule(kSecImportExportKeychain),
148                    keychain.as_CFType(),
149                ));
150            }
151
152            if let Some(ref access) = self.access {
153                options.push((
154                    CFString::wrap_under_get_rule(kSecImportExportAccess),
155                    access.as_CFType(),
156                ));
157            }
158        }
159    }
160
161    #[cfg(not(target_os = "macos"))]
162    fn import_setup(&self, _: &mut Vec<(CFString, CFType)>) {}
163}
164
165#[cfg(test)]
166mod test {
167    use super::*;
168
169    #[test]
170    fn missing_passphrase() {
171        let data = include_bytes!("../test/server.p12");
172        assert!(Pkcs12ImportOptions::new().import(data).is_err());
173    }
174}