1 //! Security Framework type import/export support.
2 
3 use core_foundation::array::CFArray;
4 use core_foundation::base::{CFType, TCFType};
5 use core_foundation::data::CFData;
6 use core_foundation::dictionary::CFDictionary;
7 use core_foundation::string::CFString;
8 use security_framework_sys::import_export::*;
9 use std::ptr;
10 
11 use crate::base::Result;
12 use crate::certificate::SecCertificate;
13 use crate::cvt;
14 use crate::identity::SecIdentity;
15 #[cfg(target_os = "macos")]
16 use crate::os::macos::access::SecAccess;
17 #[cfg(target_os = "macos")]
18 use crate::os::macos::keychain::SecKeychain;
19 use crate::trust::SecTrust;
20 
21 /// Information about an imported identity.
22 pub 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)]
38 pub 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")]
47 impl crate::Pkcs12ImportOptionsInternals for Pkcs12ImportOptions {
keychain(&mut self, keychain: SecKeychain) -> &mut Self48     fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
49         self.keychain = Some(keychain);
50         self
51     }
52 
access(&mut self, access: SecAccess) -> &mut Self53     fn access(&mut self, access: SecAccess) -> &mut Self {
54         self.access = Some(access);
55         self
56     }
57 }
58 
59 impl Pkcs12ImportOptions {
60     /// Creates a new builder with default options.
new() -> Self61     pub fn new() -> Self {
62         Self::default()
63     }
64 
65     /// Specifies the passphrase to be used to decrypt the data.
66     ///
67     /// This must be specified, as unencrypted PKCS#12 data is not supported.
passphrase(&mut self, passphrase: &str) -> &mut Self68     pub fn passphrase(&mut self, passphrase: &str) -> &mut Self {
69         self.passphrase = Some(CFString::new(passphrase));
70         self
71     }
72 
73     /// Deprecated
74     ///
75     /// Replaced by `os::macos::import_export::Pkcs12ImportOptionsExt::keychain`.
76     #[cfg(target_os = "macos")]
77     #[deprecated(note = "Replaced by `os::macos::import_export::Pkcs12ImportOptionsExt::keychain`")]
keychain(&mut self, keychain: SecKeychain) -> &mut Self78     pub fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
79         self.keychain = Some(keychain);
80         self
81     }
82 
83     /// Deprecated
84     ///
85     /// Replaced by `os::macos::import_export::Pkcs12ImportOptionsExt::access`.
86     #[cfg(target_os = "macos")]
87     #[deprecated(note = "Replaced by `os::macos::import_export::Pkcs12ImportOptionsExt::access`")]
access(&mut self, access: SecAccess) -> &mut Self88     pub fn access(&mut self, access: SecAccess) -> &mut Self {
89         self.access = Some(access);
90         self
91     }
92 
93     /// Imports identities from PKCS#12 encoded data.
import(&self, pkcs12_data: &[u8]) -> Result<Vec<ImportedIdentity>>94     pub fn import(&self, pkcs12_data: &[u8]) -> Result<Vec<ImportedIdentity>> {
95         unsafe {
96             let pkcs12_data = CFData::from_buffer(pkcs12_data);
97 
98             let mut options = vec![];
99 
100             if let Some(ref passphrase) = self.passphrase {
101                 options.push((
102                     CFString::wrap_under_get_rule(kSecImportExportPassphrase),
103                     passphrase.as_CFType(),
104                 ));
105             }
106 
107             self.import_setup(&mut options);
108 
109             let options = CFDictionary::from_CFType_pairs(&options);
110 
111             let mut raw_items = ptr::null();
112             cvt(SecPKCS12Import(
113                 pkcs12_data.as_concrete_TypeRef(),
114                 options.as_concrete_TypeRef(),
115                 &mut raw_items,
116             ))?;
117             let raw_items = CFArray::<CFDictionary<CFString, *const _>>::wrap_under_create_rule(raw_items);
118 
119             let mut items = vec![];
120 
121             for raw_item in &raw_items {
122                 let label = raw_item
123                     .find(kSecImportItemLabel)
124                     .map(|label| CFString::wrap_under_get_rule(*label as *const _).to_string());
125                 let key_id = raw_item
126                     .find(kSecImportItemKeyID)
127                     .map(|key_id| CFData::wrap_under_get_rule(*key_id as *const _).to_vec());
128                 let trust = raw_item
129                     .find(kSecImportItemTrust)
130                     .map(|trust| SecTrust::wrap_under_get_rule(*trust as *mut _));
131                 let cert_chain = raw_item.find(kSecImportItemCertChain as *const _).map(
132                     |cert_chain| {
133                         CFArray::<SecCertificate>::wrap_under_get_rule(*cert_chain as *const _)
134                             .iter()
135                             .map(|c| c.clone())
136                             .collect()
137                     },
138                 );
139                 let identity = raw_item
140                     .find(kSecImportItemIdentity)
141                     .map(|identity| SecIdentity::wrap_under_get_rule(*identity as *mut _));
142 
143                 items.push(ImportedIdentity {
144                     label,
145                     key_id,
146                     trust,
147                     cert_chain,
148                     identity,
149                     _p: (),
150                 });
151             }
152 
153             Ok(items)
154         }
155     }
156 
157     #[cfg(target_os = "macos")]
import_setup(&self, options: &mut Vec<(CFString, CFType)>)158     fn import_setup(&self, options: &mut Vec<(CFString, CFType)>) {
159         unsafe {
160             if let Some(ref keychain) = self.keychain {
161                 options.push((
162                     CFString::wrap_under_get_rule(kSecImportExportKeychain),
163                     keychain.as_CFType(),
164                 ));
165             }
166 
167             if let Some(ref access) = self.access {
168                 options.push((
169                     CFString::wrap_under_get_rule(kSecImportExportAccess),
170                     access.as_CFType(),
171                 ));
172             }
173         }
174     }
175 
176     #[cfg(not(target_os = "macos"))]
import_setup(&self, _: &mut Vec<(CFString, CFType)>)177     fn import_setup(&self, _: &mut Vec<(CFString, CFType)>) {}
178 }
179 
180 #[cfg(test)]
181 mod test {
182     use super::*;
183 
184     #[test]
missing_passphrase()185     fn missing_passphrase() {
186         let data = include_bytes!("../test/server.p12");
187         assert!(Pkcs12ImportOptions::new().import(data).is_err());
188     }
189 }
190