174 lines
5.5 KiB
Rust
174 lines
5.5 KiB
Rust
//! 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());
|
|
}
|
|
}
|