1 //! OSX specific extensions to certificate functionality. 2 3 use core_foundation::array::{CFArray, CFArrayIterator}; 4 use core_foundation::base::TCFType; 5 use core_foundation::base::ToVoid; 6 use core_foundation::dictionary::CFDictionary; 7 use core_foundation::error::CFError; 8 use core_foundation::string::CFString; 9 use security_framework_sys::certificate::*; 10 use std::os::raw::c_void; 11 use std::ptr; 12 13 use crate::base::Error; 14 use crate::certificate::SecCertificate; 15 use crate::cvt; 16 use crate::key::SecKey; 17 use crate::os::macos::certificate_oids::CertificateOid; 18 19 /// An extension trait adding OSX specific functionality to `SecCertificate`. 20 pub trait SecCertificateExt { 21 /// Returns the common name associated with the certificate. common_name(&self) -> Result<String, Error>22 fn common_name(&self) -> Result<String, Error>; 23 24 /// Returns the public key associated with the certificate. public_key(&self) -> Result<SecKey, Error>25 fn public_key(&self) -> Result<SecKey, Error>; 26 27 /// Returns the set of properties associated with the certificate. 28 /// 29 /// The `keys` argument can optionally be used to filter the properties loaded to an explicit 30 /// subset. properties(&self, keys: Option<&[CertificateOid]>) -> Result<CertificateProperties, CFError>31 fn properties(&self, keys: Option<&[CertificateOid]>) 32 -> Result<CertificateProperties, CFError>; 33 } 34 35 impl SecCertificateExt for SecCertificate { common_name(&self) -> Result<String, Error>36 fn common_name(&self) -> Result<String, Error> { 37 unsafe { 38 let mut string = ptr::null(); 39 cvt(SecCertificateCopyCommonName( 40 self.as_concrete_TypeRef(), 41 &mut string, 42 ))?; 43 Ok(CFString::wrap_under_create_rule(string).to_string()) 44 } 45 } 46 public_key(&self) -> Result<SecKey, Error>47 fn public_key(&self) -> Result<SecKey, Error> { 48 unsafe { 49 let mut key = ptr::null_mut(); 50 cvt(SecCertificateCopyPublicKey( 51 self.as_concrete_TypeRef(), 52 &mut key, 53 ))?; 54 Ok(SecKey::wrap_under_create_rule(key)) 55 } 56 } 57 properties( &self, keys: Option<&[CertificateOid]>, ) -> Result<CertificateProperties, CFError>58 fn properties( 59 &self, 60 keys: Option<&[CertificateOid]>, 61 ) -> Result<CertificateProperties, CFError> { 62 unsafe { 63 let keys = keys.map(|oids| { 64 let oids = oids.iter().map(|oid| oid.to_str()).collect::<Vec<_>>(); 65 CFArray::from_CFTypes(&oids) 66 }); 67 68 let keys = match keys { 69 Some(ref keys) => keys.as_concrete_TypeRef(), 70 None => ptr::null_mut(), 71 }; 72 73 let mut error = ptr::null_mut(); 74 75 let dictionary = SecCertificateCopyValues(self.as_concrete_TypeRef(), keys, &mut error); 76 77 if error.is_null() { 78 Ok(CertificateProperties(CFDictionary::wrap_under_create_rule( 79 dictionary, 80 ))) 81 } else { 82 Err(CFError::wrap_under_create_rule(error)) 83 } 84 } 85 } 86 } 87 88 /// Properties associated with a certificate. 89 pub struct CertificateProperties(CFDictionary); 90 91 impl CertificateProperties { 92 /// Retrieves a specific property identified by its OID. get(&self, oid: CertificateOid) -> Option<CertificateProperty>93 pub fn get(&self, oid: CertificateOid) -> Option<CertificateProperty> { 94 unsafe { 95 self.0.find(oid.as_ptr() as *const c_void).map(|value| { 96 CertificateProperty(CFDictionary::wrap_under_get_rule(*value as *mut _)) 97 }) 98 } 99 } 100 } 101 102 /// A property associated with a certificate. 103 pub struct CertificateProperty(CFDictionary); 104 105 impl CertificateProperty { 106 /// Returns the label of this property. label(&self) -> CFString107 pub fn label(&self) -> CFString { 108 unsafe { 109 CFString::wrap_under_get_rule(*self.0.get(kSecPropertyKeyLabel.to_void()) as *const _) 110 } 111 } 112 113 /// Returns an enum of the underlying data for this property. get(&self) -> PropertyType114 pub fn get(&self) -> PropertyType { 115 unsafe { 116 let type_ = 117 CFString::wrap_under_get_rule(*self.0.get(kSecPropertyKeyType.to_void()) as *mut _); 118 let value = self.0.get(kSecPropertyKeyValue.to_void()); 119 120 if type_ == CFString::wrap_under_get_rule(kSecPropertyTypeSection) { 121 PropertyType::Section(PropertySection(CFArray::wrap_under_get_rule( 122 *value as *const _, 123 ))) 124 } else if type_ == CFString::wrap_under_get_rule(kSecPropertyTypeString) { 125 PropertyType::String(CFString::wrap_under_get_rule(*value as *const _)) 126 } else { 127 PropertyType::__Unknown 128 } 129 } 130 } 131 } 132 133 /// A "section" property. 134 /// 135 /// Sections are sequences of other properties. 136 pub struct PropertySection(CFArray<CFDictionary>); 137 138 impl PropertySection { 139 /// Returns an iterator over the properties in this section. 140 #[inline(always)] iter(&self) -> PropertySectionIter<'_>141 pub fn iter(&self) -> PropertySectionIter<'_> { 142 PropertySectionIter(self.0.iter()) 143 } 144 } 145 146 impl<'a> IntoIterator for &'a PropertySection { 147 type IntoIter = PropertySectionIter<'a>; 148 type Item = CertificateProperty; 149 150 #[inline(always)] into_iter(self) -> PropertySectionIter<'a>151 fn into_iter(self) -> PropertySectionIter<'a> { 152 self.iter() 153 } 154 } 155 156 /// An iterator over the properties in a section. 157 pub struct PropertySectionIter<'a>(CFArrayIterator<'a, CFDictionary>); 158 159 impl<'a> Iterator for PropertySectionIter<'a> { 160 type Item = CertificateProperty; 161 next(&mut self) -> Option<CertificateProperty>162 fn next(&mut self) -> Option<CertificateProperty> { 163 self.0.next().map(|t| CertificateProperty(t.clone())) 164 } 165 166 #[inline(always)] size_hint(&self) -> (usize, Option<usize>)167 fn size_hint(&self) -> (usize, Option<usize>) { 168 self.0.size_hint() 169 } 170 } 171 172 /// An enum of the various types of properties. 173 pub enum PropertyType { 174 /// A section. 175 Section(PropertySection), 176 /// A string. 177 String(CFString), 178 #[doc(hidden)] 179 __Unknown, 180 } 181 182 #[cfg(test)] 183 mod test { 184 use super::*; 185 use crate::os::macos::certificate_oids::CertificateOid; 186 use crate::test::certificate; 187 use std::collections::HashMap; 188 189 #[test] common_name()190 fn common_name() { 191 let certificate = certificate(); 192 assert_eq!("foobar.com", p!(certificate.common_name())); 193 } 194 195 #[test] public_key()196 fn public_key() { 197 let certificate = certificate(); 198 p!(certificate.public_key()); 199 } 200 201 #[test] signature_algorithm()202 fn signature_algorithm() { 203 let certificate = certificate(); 204 let properties = certificate 205 .properties(Some(&[CertificateOid::x509_v1_signature_algorithm()])) 206 .unwrap(); 207 let value = properties 208 .get(CertificateOid::x509_v1_signature_algorithm()) 209 .unwrap(); 210 let section = match value.get() { 211 PropertyType::Section(section) => section, 212 _ => panic!(), 213 }; 214 let properties = section 215 .iter() 216 .map(|p| (p.label().to_string(), p.get())) 217 .collect::<HashMap<_, _>>(); 218 let algorithm = match properties["Algorithm"] { 219 PropertyType::String(ref s) => s.to_string(), 220 _ => panic!(), 221 }; 222 assert_eq!(algorithm, "1.2.840.113549.1.1.5"); 223 } 224 } 225