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