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