apple_security/os/macos/
certificate.rs

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