security_framework/
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 security_framework_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 Pkcs12ImportOptions {
48 #[inline(always)]
52 pub fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
53 self.keychain = Some(keychain);
54 self
55 }
56
57 #[inline(always)]
59 pub fn access(&mut self, access: SecAccess) -> &mut Self {
60 self.access = Some(access);
61 self
62 }
63}
64
65impl Pkcs12ImportOptions {
66 #[inline(always)]
68 #[must_use]
69 pub fn new() -> Self {
70 Self::default()
71 }
72
73 #[inline]
77 pub fn passphrase(&mut self, passphrase: &str) -> &mut Self {
78 self.passphrase = Some(CFString::new(passphrase));
79 self
80 }
81
82 pub fn import(&self, pkcs12_data: &[u8]) -> Result<Vec<ImportedIdentity>> {
84 unsafe {
85 let pkcs12_data = CFData::from_buffer(pkcs12_data);
86
87 let mut options = vec![];
88
89 if let Some(ref passphrase) = self.passphrase {
90 options.push((
91 CFString::wrap_under_get_rule(kSecImportExportPassphrase),
92 passphrase.as_CFType(),
93 ));
94 }
95
96 self.import_setup(&mut options);
97
98 let options = CFDictionary::from_CFType_pairs(&options);
99
100 let mut raw_items = ptr::null();
101 cvt(SecPKCS12Import(
102 pkcs12_data.as_concrete_TypeRef(),
103 options.as_concrete_TypeRef(),
104 &mut raw_items,
105 ))?;
106 let raw_items = CFArray::<CFDictionary<CFString, *const _>>::wrap_under_create_rule(raw_items);
107
108 let mut items = vec![];
109
110 for raw_item in &raw_items {
111 let label = raw_item
112 .find(kSecImportItemLabel)
113 .map(|label| CFString::wrap_under_get_rule((*label).cast()).to_string());
114 let key_id = raw_item
115 .find(kSecImportItemKeyID)
116 .map(|key_id| CFData::wrap_under_get_rule((*key_id).cast()).to_vec());
117 let trust = raw_item
118 .find(kSecImportItemTrust)
119 .map(|trust| SecTrust::wrap_under_get_rule(*trust as *mut _));
120 let cert_chain = raw_item
121 .find(kSecImportItemCertChain.cast())
122 .map(|cert_chain| {
123 CFArray::<SecCertificate>::wrap_under_get_rule((*cert_chain).cast())
124 .iter()
125 .map(|c| c.clone())
126 .collect()
127 });
128 let identity = raw_item
129 .find(kSecImportItemIdentity)
130 .map(|identity| SecIdentity::wrap_under_get_rule(*identity as *mut _));
131
132 items.push(ImportedIdentity {
133 label,
134 key_id,
135 trust,
136 cert_chain,
137 identity,
138 _p: (),
139 });
140 }
141
142 Ok(items)
143 }
144 }
145
146 #[cfg(target_os = "macos")]
147 fn import_setup(&self, options: &mut Vec<(CFString, CFType)>) {
148 unsafe {
149 if let Some(ref keychain) = self.keychain {
150 options.push((
151 CFString::wrap_under_get_rule(kSecImportExportKeychain),
152 keychain.as_CFType(),
153 ));
154 }
155
156 if let Some(ref access) = self.access {
157 options.push((
158 CFString::wrap_under_get_rule(kSecImportExportAccess),
159 access.as_CFType(),
160 ));
161 }
162 }
163 }
164
165 #[cfg(not(target_os = "macos"))]
166 fn import_setup(&self, _: &mut Vec<(CFString, CFType)>) {}
167}
168
169#[cfg(test)]
170mod test {
171 use super::*;
172
173 #[test]
174 fn missing_passphrase() {
175 let data = include_bytes!("../test/server.p12");
176 assert!(Pkcs12ImportOptions::new().import(data).is_err());
177 }
178}