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. iter(&self) -> PropertySectionIter<'_>140 pub fn iter(&self) -> PropertySectionIter<'_> { 141 PropertySectionIter(self.0.iter()) 142 } 143 } 144 145 impl<'a> IntoIterator for &'a PropertySection { 146 type IntoIter = PropertySectionIter<'a>; 147 type Item = CertificateProperty; 148 into_iter(self) -> PropertySectionIter<'a>149 fn into_iter(self) -> PropertySectionIter<'a> { 150 self.iter() 151 } 152 } 153 154 /// An iterator over the properties in a section. 155 pub struct PropertySectionIter<'a>(CFArrayIterator<'a, CFDictionary>); 156 157 impl<'a> Iterator for PropertySectionIter<'a> { 158 type Item = CertificateProperty; 159 next(&mut self) -> Option<CertificateProperty>160 fn next(&mut self) -> Option<CertificateProperty> { 161 self.0.next().map(|t| CertificateProperty(t.clone())) 162 } 163 size_hint(&self) -> (usize, Option<usize>)164 fn size_hint(&self) -> (usize, Option<usize>) { 165 self.0.size_hint() 166 } 167 } 168 169 /// An enum of the various types of properties. 170 pub enum PropertyType { 171 /// A section. 172 Section(PropertySection), 173 /// A string. 174 String(CFString), 175 #[doc(hidden)] 176 __Unknown, 177 } 178 179 #[cfg(test)] 180 mod test { 181 use super::*; 182 use crate::os::macos::certificate_oids::CertificateOid; 183 use crate::test::certificate; 184 use std::collections::HashMap; 185 186 #[test] common_name()187 fn common_name() { 188 let certificate = certificate(); 189 assert_eq!("foobar.com", p!(certificate.common_name())); 190 } 191 192 #[test] public_key()193 fn public_key() { 194 let certificate = certificate(); 195 p!(certificate.public_key()); 196 } 197 198 #[test] signature_algorithm()199 fn signature_algorithm() { 200 let certificate = certificate(); 201 let properties = certificate 202 .properties(Some(&[CertificateOid::x509_v1_signature_algorithm()])) 203 .unwrap(); 204 let value = properties 205 .get(CertificateOid::x509_v1_signature_algorithm()) 206 .unwrap(); 207 let section = match value.get() { 208 PropertyType::Section(section) => section, 209 _ => panic!(), 210 }; 211 let properties = section 212 .iter() 213 .map(|p| (p.label().to_string(), p.get())) 214 .collect::<HashMap<_, _>>(); 215 let algorithm = match properties["Algorithm"] { 216 PropertyType::String(ref s) => s.to_string(), 217 _ => panic!(), 218 }; 219 assert_eq!(algorithm, "1.2.840.113549.1.1.5"); 220 } 221 } 222