| //! 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::data::CFData; | 
 | use core_foundation::dictionary::CFDictionary; | 
 | use core_foundation::error::CFError; | 
 | use core_foundation::string::CFString; | 
 | use security_framework_sys::certificate::*; | 
 | use std::convert::TryInto; | 
 | 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}; | 
 |  | 
 | /// 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. | 
 |     #[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>; | 
 |  | 
 |     /// 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>; | 
 |  | 
 |     /// Returns the SHA-256 fingerprint of the certificate. | 
 |     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)) | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     /// Returns the SHA-256 fingerprint of the certificate. | 
 |     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()) | 
 |     } | 
 | } | 
 |  | 
 | /// Properties associated with a certificate. | 
 | pub struct CertificateProperties(CFDictionary); | 
 |  | 
 | impl CertificateProperties { | 
 |     /// Retrieves a specific property identified by its OID. | 
 |     #[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 _)) | 
 |             }) | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /// A property associated with a certificate. | 
 | pub struct CertificateProperty(CFDictionary); | 
 |  | 
 | impl CertificateProperty { | 
 |     /// Returns the label of this property. | 
 |     #[must_use] | 
 |     pub fn label(&self) -> CFString { | 
 |         unsafe { | 
 |             CFString::wrap_under_get_rule((*self.0.get(kSecPropertyKeyLabel.to_void())).cast()) | 
 |         } | 
 |     } | 
 |  | 
 |     /// Returns an enum of the underlying data for this property. | 
 |     #[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 | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /// 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. | 
 |     #[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() | 
 |     } | 
 | } | 
 |  | 
 | /// An iterator over the properties in a section. | 
 | 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() | 
 |     } | 
 | } | 
 |  | 
 | /// 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 crate::os::macos::certificate_oids::CertificateOid; | 
 |     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"); | 
 |     } | 
 | } |