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