apple_security_framework/
import_export.rs

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