apple_security/
import_export.rs1use 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
21pub struct ImportedIdentity {
23 pub label: Option<String>,
25 pub key_id: Option<Vec<u8>>,
27 pub trust: Option<SecTrust>,
29 pub cert_chain: Option<Vec<SecCertificate>>,
31 pub identity: Option<SecIdentity>,
33 _p: (),
34}
35
36#[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 #[inline(always)]
64 #[must_use]
65 pub fn new() -> Self {
66 Self::default()
67 }
68
69 #[inline]
73 pub fn passphrase(&mut self, passphrase: &str) -> &mut Self {
74 self.passphrase = Some(CFString::new(passphrase));
75 self
76 }
77
78 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}