blob: 756224ec41d32918c2f8a9240c679ccc855e7b2f [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 base::Result;
use certificate::SecCertificate;
use cvt;
use identity::SecIdentity;
#[cfg(target_os = "macos")]
use os::macos::access::SecAccess;
#[cfg(target_os = "macos")]
use os::macos::keychain::SecKeychain;
use 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 ::Pkcs12ImportOptionsInternals for Pkcs12ImportOptions {
fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
self.keychain = Some(keychain);
self
}
fn access(&mut self, access: SecAccess) -> &mut Self {
self.access = Some(access);
self
}
}
impl Pkcs12ImportOptions {
/// Creates a new builder with default options.
pub fn new() -> Pkcs12ImportOptions {
Self::default()
}
/// Specifies the passphrase to be used to decrypt the data.
///
/// This must be specified, as unencrypted PKCS#12 data is not supported.
pub fn passphrase(&mut self, passphrase: &str) -> &mut Self {
self.passphrase = Some(CFString::new(passphrase));
self
}
/// Deprecated
///
/// Replaced by `os::macos::import_export::Pkcs12ImportOptionsExt::keychain`.
#[cfg(target_os = "macos")]
pub fn keychain(&mut self, keychain: SecKeychain) -> &mut Self {
self.keychain = Some(keychain);
self
}
/// Deprecated
///
/// Replaced by `os::macos::import_export::Pkcs12ImportOptionsExt::access`.
#[cfg(target_os = "macos")]
pub fn access(&mut self, access: SecAccess) -> &mut Self {
self.access = Some(access);
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>::wrap_under_create_rule(raw_items);
let mut items = vec![];
for raw_item in &raw_items {
let label = raw_item
.find(kSecImportItemLabel as *const _)
.map(|label| CFString::wrap_under_get_rule(*label as *const _).to_string());
let key_id = raw_item
.find(kSecImportItemKeyID as *const _)
.map(|key_id| CFData::wrap_under_get_rule(*key_id as *const _).to_vec());
let trust = raw_item
.find(kSecImportItemTrust as *const _)
.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 as *const _)
.map(|identity| SecIdentity::wrap_under_get_rule(*identity as *mut _));
items.push(ImportedIdentity {
label: label,
key_id: key_id,
trust: trust,
cert_chain: cert_chain,
identity: 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());
}
}