apple_security/os/macos/
certificate.rs1use 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
22pub trait SecCertificateExt {
24 fn common_name(&self) -> Result<String, Error>;
26
27 #[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 fn properties(&self, keys: Option<&[CertificateOid]>)
36 -> Result<CertificateProperties, CFError>;
37
38 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 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
118pub struct CertificateProperties(CFDictionary);
120
121impl CertificateProperties {
122 #[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
132pub struct CertificateProperty(CFDictionary);
134
135impl CertificateProperty {
136 #[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 #[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
165pub struct PropertySection(CFArray<CFDictionary>);
169
170impl PropertySection {
171 #[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
189pub 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
206pub enum PropertyType {
208 Section(PropertySection),
210 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}