blob: 5441a4b06e27470efe3b533c4406ffd4e144c829 [file] [log] [blame]
//! OSX specific extensions to certificate functionality.
use core_foundation::array::{CFArray, CFArrayIterator};
use core_foundation::base::TCFType;
use core_foundation::base::ToVoid;
use core_foundation::dictionary::CFDictionary;
use core_foundation::error::CFError;
use core_foundation::string::CFString;
use std::os::raw::c_void;
use security_framework_sys::certificate::*;
use std::ptr;
use base::Error;
use certificate::SecCertificate;
use cvt;
use key::SecKey;
use os::macos::certificate_oids::CertificateOid;
/// An extension trait adding OSX specific functionality to `SecCertificate`.
pub trait SecCertificateExt {
/// Returns the common name associated with the certificate.
fn common_name(&self) -> Result<String, Error>;
/// Returns the public key associated with the certificate.
fn public_key(&self) -> Result<SecKey, Error>;
/// Returns the set of properties associated with the certificate.
///
/// The `keys` argument can optionally be used to filter the properties loaded to an explicit
/// subset.
fn properties(&self, keys: Option<&[CertificateOid]>)
-> Result<CertificateProperties, CFError>;
}
impl SecCertificateExt for SecCertificate {
fn common_name(&self) -> Result<String, Error> {
unsafe {
let mut string = ptr::null();
cvt(SecCertificateCopyCommonName(
self.as_concrete_TypeRef(),
&mut string,
))?;
Ok(CFString::wrap_under_create_rule(string).to_string())
}
}
fn public_key(&self) -> Result<SecKey, Error> {
unsafe {
let mut key = ptr::null_mut();
cvt(SecCertificateCopyPublicKey(
self.as_concrete_TypeRef(),
&mut key,
))?;
Ok(SecKey::wrap_under_create_rule(key))
}
}
fn properties(
&self,
keys: Option<&[CertificateOid]>,
) -> Result<CertificateProperties, CFError> {
unsafe {
let keys = keys.map(|oids| {
let oids = oids.iter().map(|oid| oid.to_str()).collect::<Vec<_>>();
CFArray::from_CFTypes(&oids)
});
let keys = match keys {
Some(ref keys) => keys.as_concrete_TypeRef(),
None => ptr::null_mut(),
};
let mut error = ptr::null_mut();
let dictionary = SecCertificateCopyValues(self.as_concrete_TypeRef(), keys, &mut error);
if error.is_null() {
Ok(CertificateProperties(CFDictionary::wrap_under_create_rule(
dictionary,
)))
} else {
Err(CFError::wrap_under_create_rule(error))
}
}
}
}
/// Properties associated with a certificate.
pub struct CertificateProperties(CFDictionary);
impl CertificateProperties {
/// Retrieves a specific property identified by its OID.
pub fn get(&self, oid: CertificateOid) -> Option<CertificateProperty> {
unsafe {
self.0.find(oid.as_ptr() as *const c_void).map(|value| {
CertificateProperty(CFDictionary::wrap_under_get_rule(*value as *mut _))
})
}
}
}
/// A property associated with a certificate.
pub struct CertificateProperty(CFDictionary);
impl CertificateProperty {
/// Returns the label of this property.
pub fn label(&self) -> CFString {
unsafe {
CFString::wrap_under_get_rule(*self.0.get(kSecPropertyKeyLabel.to_void()) as *const _)
}
}
/// Returns an enum of the underlying data for this property.
pub fn get(&self) -> PropertyType {
unsafe {
let type_ = CFString::wrap_under_get_rule(
*self.0.get(kSecPropertyKeyType.to_void()) as *mut _,
);
let value = self.0.get(kSecPropertyKeyValue.to_void());
if type_ == CFString::wrap_under_get_rule(kSecPropertyTypeSection) {
PropertyType::Section(PropertySection(CFArray::wrap_under_get_rule(
*value as *const _,
)))
} else if type_ == CFString::wrap_under_get_rule(kSecPropertyTypeString) {
PropertyType::String(CFString::wrap_under_get_rule(*value as *const _))
} else {
PropertyType::__Unknown
}
}
}
}
/// A "section" property.
///
/// Sections are sequences of other properties.
pub struct PropertySection(CFArray<CFDictionary>);
impl PropertySection {
/// Returns an iterator over the properties in this section.
pub fn iter<'a>(&'a self) -> PropertySectionIter<'a> {
PropertySectionIter(self.0.iter())
}
}
impl<'a> IntoIterator for &'a PropertySection {
type IntoIter = PropertySectionIter<'a>;
type Item = CertificateProperty;
fn into_iter(self) -> PropertySectionIter<'a> {
self.iter()
}
}
/// An iterator over the properties in a section.
pub struct PropertySectionIter<'a>(CFArrayIterator<'a, CFDictionary>);
impl<'a> Iterator for PropertySectionIter<'a> {
type Item = CertificateProperty;
fn next(&mut self) -> Option<CertificateProperty> {
self.0.next().map(|t| CertificateProperty(t.clone()))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
/// An enum of the various types of properties.
pub enum PropertyType {
/// A section.
Section(PropertySection),
/// A string.
String(CFString),
#[doc(hidden)]
__Unknown,
}
#[cfg(test)]
mod test {
use super::*;
use os::macos::certificate_oids::CertificateOid;
use std::collections::HashMap;
use test::certificate;
#[test]
fn common_name() {
let certificate = certificate();
assert_eq!("foobar.com", p!(certificate.common_name()).to_string());
}
#[test]
fn public_key() {
let certificate = certificate();
p!(certificate.public_key());
}
#[test]
fn signature_algorithm() {
let certificate = certificate();
let properties = certificate
.properties(Some(&[CertificateOid::x509_v1_signature_algorithm()]))
.unwrap();
let value = properties
.get(CertificateOid::x509_v1_signature_algorithm())
.unwrap();
let section = match value.get() {
PropertyType::Section(section) => section,
_ => panic!(),
};
let properties = section
.iter()
.map(|p| (p.label().to_string(), p.get()))
.collect::<HashMap<_, _>>();
let algorithm = match properties["Algorithm"] {
PropertyType::String(ref s) => s.to_string(),
_ => panic!(),
};
assert_eq!(algorithm, "1.2.840.113549.1.1.5");
}
}