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