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
21#[derive(Clone)]
23#[non_exhaustive]
24pub struct ImportedIdentity {
25 pub label: Option<String>,
27 pub key_id: Option<Vec<u8>>,
29 pub trust: Option<SecTrust>,
31 pub cert_chain: Option<Vec<SecCertificate>>,
33 pub identity: Option<SecIdentity>,
35}
36
37#[derive(Default)]
39pub struct Pkcs12ImportOptions {
40 passphrase: Option<CFString>,
41 #[cfg(target_os = "macos")]
42 keychain: Option<SecKeychain>,
43 #[cfg(target_os = "macos")]
44 access: Option<SecAccess>,
45}
46
47#[cfg(target_os = "macos")]
48impl Pkcs12ImportOptions {
49 #[inline(always)]
53 pub fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
54 self.keychain = Some(keychain);
55 self
56 }
57
58 #[inline(always)]
60 pub fn access(&mut self, access: SecAccess) -> &mut Self {
61 self.access = Some(access);
62 self
63 }
64}
65
66impl Pkcs12ImportOptions {
67 #[inline(always)]
69 #[must_use]
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 #[inline]
78 pub fn passphrase(&mut self, passphrase: &str) -> &mut Self {
79 self.passphrase = Some(CFString::new(passphrase));
80 self
81 }
82
83 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(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 = CFArray::<CFDictionary<CFString, *const _>>::wrap_under_create_rule(raw_items);
108
109 let mut items = vec![];
110
111 for raw_item in &raw_items {
112 let label = raw_item
113 .find(kSecImportItemLabel)
114 .map(|label| CFString::wrap_under_get_rule((*label).cast()).to_string());
115 let key_id = raw_item
116 .find(kSecImportItemKeyID)
117 .map(|key_id| CFData::wrap_under_get_rule((*key_id).cast()).to_vec());
118 let trust = raw_item
119 .find(kSecImportItemTrust)
120 .map(|trust| SecTrust::wrap_under_get_rule(*trust as *mut _));
121 let cert_chain = raw_item
122 .find(kSecImportItemCertChain)
123 .map(|cert_chain| {
124 CFArray::<SecCertificate>::wrap_under_get_rule((*cert_chain).cast())
125 .iter()
126 .map(|c| c.clone())
127 .collect()
128 });
129 let identity = raw_item
130 .find(kSecImportItemIdentity)
131 .map(|identity| SecIdentity::wrap_under_get_rule(*identity as *mut _));
132
133 items.push(ImportedIdentity {
134 label,
135 key_id,
136 trust,
137 cert_chain,
138 identity,
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(keychain) = &self.keychain {
150 options.push((
151 CFString::wrap_under_get_rule(kSecImportExportKeychain),
152 keychain.as_CFType(),
153 ));
154 }
155
156 if let Some(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}