blob: 5fd6136eaae36d600571ff1ad9487ac74c4fec52 [file] [log] [blame]
//! Security Framework type import/export support.
use core_foundation::array::CFArray;
use core_foundation::base::{CFType, TCFType};
use core_foundation::data::CFData;
use core_foundation::dictionary::CFDictionary;
use core_foundation::string::CFString;
use security_framework_sys::import_export::*;
use std::ptr;
use crate::base::Result;
use crate::certificate::SecCertificate;
use crate::cvt;
use crate::identity::SecIdentity;
#[cfg(target_os = "macos")]
use crate::os::macos::access::SecAccess;
#[cfg(target_os = "macos")]
use crate::os::macos::keychain::SecKeychain;
use crate::trust::SecTrust;
/// Information about an imported identity.
pub struct ImportedIdentity {
/// The label of the identity.
pub label: Option<String>,
/// The ID of the identity. Typically the SHA-1 hash of the public key.
pub key_id: Option<Vec<u8>>,
/// A `SecTrust` object set up to validate this identity.
pub trust: Option<SecTrust>,
/// A certificate chain validating this identity.
pub cert_chain: Option<Vec<SecCertificate>>,
/// The identity itself.
pub identity: Option<SecIdentity>,
_p: (),
}
/// A builder type to import an identity from PKCS#12 formatted data.
#[derive(Default)]
pub struct Pkcs12ImportOptions {
passphrase: Option<CFString>,
#[cfg(target_os = "macos")]
keychain: Option<SecKeychain>,
#[cfg(target_os = "macos")]
access: Option<SecAccess>,
}
#[cfg(target_os = "macos")]
impl crate::Pkcs12ImportOptionsInternals for Pkcs12ImportOptions {
#[inline(always)]
fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
self.keychain = Some(keychain);
self
}
#[inline(always)]
fn access(&mut self, access: SecAccess) -> &mut Self {
self.access = Some(access);
self
}
}
impl Pkcs12ImportOptions {
/// Creates a new builder with default options.
#[inline(always)]
pub fn new() -> Self {
Self::default()
}
/// Specifies the passphrase to be used to decrypt the data.
///
/// This must be specified, as unencrypted PKCS#12 data is not supported.
#[inline]
pub fn passphrase(&mut self, passphrase: &str) -> &mut Self {
self.passphrase = Some(CFString::new(passphrase));
self
}
/// Imports identities from PKCS#12 encoded data.
pub fn import(&self, pkcs12_data: &[u8]) -> Result<Vec<ImportedIdentity>> {
unsafe {
let pkcs12_data = CFData::from_buffer(pkcs12_data);
let mut options = vec![];
if let Some(ref passphrase) = self.passphrase {
options.push((
CFString::wrap_under_get_rule(kSecImportExportPassphrase),
passphrase.as_CFType(),
));
}
self.import_setup(&mut options);
let options = CFDictionary::from_CFType_pairs(&options);
let mut raw_items = ptr::null();
cvt(SecPKCS12Import(
pkcs12_data.as_concrete_TypeRef(),
options.as_concrete_TypeRef(),
&mut raw_items,
))?;
let raw_items =
CFArray::<CFDictionary<CFString, *const _>>::wrap_under_create_rule(raw_items);
let mut items = vec![];
for raw_item in &raw_items {
let label = raw_item
.find(kSecImportItemLabel)
.map(|label| CFString::wrap_under_get_rule(*label as *const _).to_string());
let key_id = raw_item
.find(kSecImportItemKeyID)
.map(|key_id| CFData::wrap_under_get_rule(*key_id as *const _).to_vec());
let trust = raw_item
.find(kSecImportItemTrust)
.map(|trust| SecTrust::wrap_under_get_rule(*trust as *mut _));
let cert_chain =
raw_item.find(kSecImportItemCertChain as *const _).map(|cert_chain| {
CFArray::<SecCertificate>::wrap_under_get_rule(*cert_chain as *const _)
.iter()
.map(|c| c.clone())
.collect()
});
let identity = raw_item
.find(kSecImportItemIdentity)
.map(|identity| SecIdentity::wrap_under_get_rule(*identity as *mut _));
items.push(ImportedIdentity { label, key_id, trust, cert_chain, identity, _p: () });
}
Ok(items)
}
}
#[cfg(target_os = "macos")]
fn import_setup(&self, options: &mut Vec<(CFString, CFType)>) {
unsafe {
if let Some(ref keychain) = self.keychain {
options.push((
CFString::wrap_under_get_rule(kSecImportExportKeychain),
keychain.as_CFType(),
));
}
if let Some(ref access) = self.access {
options.push((
CFString::wrap_under_get_rule(kSecImportExportAccess),
access.as_CFType(),
));
}
}
}
#[cfg(not(target_os = "macos"))]
fn import_setup(&self, _: &mut Vec<(CFString, CFType)>) {}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn missing_passphrase() {
let data = include_bytes!("../test/server.p12");
assert!(Pkcs12ImportOptions::new().import(data).is_err());
}
}