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