security_framework/os/macos/
certificate.rsuse core_foundation::array::{CFArray, CFArrayIterator};
use core_foundation::base::TCFType;
use core_foundation::base::ToVoid;
use core_foundation::data::CFData;
use core_foundation::dictionary::CFDictionary;
use core_foundation::error::CFError;
use core_foundation::string::CFString;
use security_framework_sys::certificate::*;
use std::os::raw::c_void;
use std::ptr;
use crate::base::Error;
use crate::certificate::SecCertificate;
use crate::cvt;
use crate::key::SecKey;
use crate::os::macos::certificate_oids::CertificateOid;
use crate::os::macos::digest_transform::{Builder, DigestType};
pub trait SecCertificateExt {
fn common_name(&self) -> Result<String, Error>;
#[cfg_attr(not(feature = "OSX_10_14"), deprecated(note = "Uses deprecated SecCertificateCopyPublicKey. Enable OSX_10_14 feature to avoid it"))]
fn public_key(&self) -> Result<SecKey, Error>;
fn properties(&self, keys: Option<&[CertificateOid]>)
-> Result<CertificateProperties, CFError>;
fn fingerprint(&self) -> Result<[u8; 32], CFError> { unimplemented!() }
}
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())
}
}
#[cfg(feature = "OSX_10_14")]
fn public_key(&self) -> Result<SecKey, Error> {
unsafe {
let key = SecCertificateCopyKey(self.as_concrete_TypeRef());
if key.is_null() {
return Err(Error::from_code(-26275));
}
Ok(SecKey::wrap_under_create_rule(key))
}
}
#[cfg(not(feature = "OSX_10_14"))]
fn public_key(&self) -> Result<SecKey, Error> {
#[allow(deprecated)]
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))
}
}
}
fn fingerprint(&self) -> Result<[u8; 32], CFError> {
let data = CFData::from_buffer(&self.to_der());
let hash = Builder::new()
.type_(DigestType::sha2())
.length(256)
.execute(&data)?;
Ok(hash.bytes().try_into().unwrap())
}
}
pub struct CertificateProperties(CFDictionary);
impl CertificateProperties {
#[must_use] pub fn get(&self, oid: CertificateOid) -> Option<CertificateProperty> {
unsafe {
self.0.find(oid.as_ptr().cast::<c_void>()).map(|value| {
CertificateProperty(CFDictionary::wrap_under_get_rule(*value as *mut _))
})
}
}
}
pub struct CertificateProperty(CFDictionary);
impl CertificateProperty {
#[must_use]
pub fn label(&self) -> CFString {
unsafe {
CFString::wrap_under_get_rule((*self.0.get(kSecPropertyKeyLabel.to_void())).cast())
}
}
#[must_use]
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).cast(),
)))
} else if type_ == CFString::wrap_under_get_rule(kSecPropertyTypeString) {
PropertyType::String(CFString::wrap_under_get_rule((*value).cast()))
} else {
PropertyType::__Unknown
}
}
}
}
pub struct PropertySection(CFArray<CFDictionary>);
impl PropertySection {
#[inline(always)]
#[must_use]
pub fn iter(&self) -> PropertySectionIter<'_> {
PropertySectionIter(self.0.iter())
}
}
impl<'a> IntoIterator for &'a PropertySection {
type IntoIter = PropertySectionIter<'a>;
type Item = CertificateProperty;
#[inline(always)]
fn into_iter(self) -> PropertySectionIter<'a> {
self.iter()
}
}
pub struct PropertySectionIter<'a>(CFArrayIterator<'a, CFDictionary>);
impl<'a> Iterator for PropertySectionIter<'a> {
type Item = CertificateProperty;
#[inline]
fn next(&mut self) -> Option<CertificateProperty> {
self.0.next().map(|t| CertificateProperty(t.clone()))
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
pub enum PropertyType {
Section(PropertySection),
String(CFString),
#[doc(hidden)]
__Unknown,
}
#[cfg(test)]
mod test {
use super::*;
use crate::test::certificate;
use std::collections::HashMap;
#[test]
fn common_name() {
let certificate = certificate();
assert_eq!("foobar.com", p!(certificate.common_name()));
}
#[test]
#[allow(deprecated)]
fn public_key() {
let certificate = certificate();
p!(certificate.public_key());
}
#[test]
fn fingerprint() {
let certificate = certificate();
let fingerprint = p!(certificate.fingerprint());
assert_eq!(
"af9dd180a326ae08b37e6398f9262f8b9d4c55674a233a7c84975024f873655d",
hex::encode(fingerprint)
);
}
#[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");
}
}