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