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