1use core_foundation::array::{CFArray, CFArrayRef};
3use core_foundation::base::{TCFType, ToVoid};
4use core_foundation::data::CFData;
5use core_foundation::dictionary::CFMutableDictionary;
6use core_foundation::string::CFString;
7use core_foundation::{declare_TCFType, impl_TCFType};
8use core_foundation_sys::base::kCFAllocatorDefault;
9#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
10use security_framework_sys::base::{errSecNotTrusted, errSecSuccess};
11use security_framework_sys::base::{errSecParam, SecCertificateRef};
12use security_framework_sys::certificate::*;
13use security_framework_sys::keychain_item::SecItemDelete;
14use std::fmt;
15use std::ptr;
16
17use crate::base::{Error, Result};
18use crate::cvt;
19use crate::key;
20#[cfg(target_os = "macos")]
21use crate::os::macos::keychain::SecKeychain;
22use core_foundation::base::FromVoid;
23use core_foundation::error::{CFError, CFErrorRef};
24use core_foundation::number::CFNumber;
25use security_framework_sys::item::kSecValueRef;
26
27declare_TCFType! {
28 SecCertificate, SecCertificateRef
30}
31impl_TCFType!(SecCertificate, SecCertificateRef, SecCertificateGetTypeID);
32
33unsafe impl Sync for SecCertificate {}
34unsafe impl Send for SecCertificate {}
35
36impl fmt::Debug for SecCertificate {
37 #[cold]
38 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
39 fmt.debug_struct("SecCertificate")
40 .field("subject", &self.subject_summary())
41 .finish()
42 }
43}
44
45impl SecCertificate {
46 pub fn from_der(der_data: &[u8]) -> Result<Self> {
48 let der_data = CFData::from_buffer(der_data);
49 unsafe {
50 let certificate = SecCertificateCreateWithData(kCFAllocatorDefault, der_data.as_concrete_TypeRef());
51 if certificate.is_null() {
52 Err(Error::from_code(errSecParam))
53 } else {
54 Ok(Self::wrap_under_create_rule(certificate))
55 }
56 }
57 }
58
59 #[must_use]
61 pub fn to_der(&self) -> Vec<u8> {
62 unsafe {
63 let der_data = SecCertificateCopyData(self.0);
64 CFData::wrap_under_create_rule(der_data).to_vec()
65 }
66 }
67
68 #[cfg(target_os = "macos")]
70 pub fn add_to_keychain(&self, keychain: Option<SecKeychain>) -> Result<()> {
71 let kch = match keychain {
72 Some(kch) => kch,
73 _ => SecKeychain::default()?,
74 };
75 cvt(unsafe {
76 SecCertificateAddToKeychain(self.as_CFTypeRef() as *mut _, kch.as_CFTypeRef() as *mut _)
77 })
78 }
79
80 #[must_use]
82 pub fn subject_summary(&self) -> String {
83 unsafe {
84 let summary = SecCertificateCopySubjectSummary(self.0);
85 CFString::wrap_under_create_rule(summary).to_string()
86 }
87 }
88
89 pub fn email_addresses(&self) -> Result<Vec<String>, Error> {
91 let mut array: CFArrayRef = ptr::null();
92 unsafe {
93 cvt(SecCertificateCopyEmailAddresses(
94 self.as_concrete_TypeRef(),
95 &mut array,
96 ))?;
97
98 let array = CFArray::<CFString>::wrap_under_create_rule(array);
99 Ok(array.into_iter().map(|p| p.to_string()).collect())
100 }
101 }
102
103 #[must_use]
105 pub fn issuer(&self) -> Vec<u8> {
106 unsafe {
107 let issuer = SecCertificateCopyNormalizedIssuerSequence(self.0);
108 CFData::wrap_under_create_rule(issuer).to_vec()
109 }
110 }
111
112 #[must_use]
114 pub fn subject(&self) -> Vec<u8> {
115 unsafe {
116 let subject = SecCertificateCopyNormalizedSubjectSequence(self.0);
117 CFData::wrap_under_create_rule(subject).to_vec()
118 }
119 }
120
121 pub fn serial_number_bytes(&self) -> Result<Vec<u8>, CFError> {
123 unsafe {
124 let mut error: CFErrorRef = ptr::null_mut();
125 let serial_number = SecCertificateCopySerialNumberData(self.0, &mut error);
126 if error.is_null() {
127 Ok(CFData::wrap_under_create_rule(serial_number).to_vec())
128 } else {
129 Err(CFError::wrap_under_create_rule(error))
130 }
131 }
132 }
133
134 pub fn public_key_info_der(&self) -> Result<Option<Vec<u8>>> {
137 let public_key = self.public_key()?;
140 Ok(self.pk_to_der(public_key))
141 }
142
143 #[must_use]
144 #[allow(clippy::unused_self)]
145 #[allow(clippy::needless_pass_by_value)]
146 fn pk_to_der(&self, public_key: key::SecKey) -> Option<Vec<u8>> {
147 use security_framework_sys::item::{kSecAttrKeySizeInBits, kSecAttrKeyType};
148
149 let public_key_attributes = public_key.attributes();
150 let public_key_type = public_key_attributes
151 .find(unsafe { kSecAttrKeyType }.cast::<std::os::raw::c_void>())?;
152 let public_keysize = public_key_attributes
153 .find(unsafe { kSecAttrKeySizeInBits }.cast::<std::os::raw::c_void>())?;
154 let public_keysize = unsafe { CFNumber::from_void(*public_keysize) };
155 let public_keysize_val = public_keysize.to_i64()? as u32;
156 let hdr_bytes = get_asn1_header_bytes(
157 unsafe { CFString::wrap_under_get_rule((*public_key_type).cast()) },
158 public_keysize_val,
159 )?;
160 let public_key_data = public_key.external_representation()?;
161 let mut out = Vec::with_capacity(hdr_bytes.len() + public_key_data.len() as usize);
162 out.extend_from_slice(hdr_bytes);
163 out.extend_from_slice(public_key_data.bytes());
164 Some(out)
165 }
166
167 pub fn public_key(&self) -> Result<key::SecKey> {
169 use crate::policy::SecPolicy;
170 use crate::trust::SecTrust;
171 use std::slice::from_ref;
172
173 let policy = SecPolicy::create_x509();
174 let mut trust = SecTrust::create_with_certificates(from_ref(self), from_ref(&policy))?;
175 #[allow(deprecated)]
176 #[cfg(not(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))]
177 trust.evaluate()?;
178 #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
179 cvt(match trust.evaluate_with_error() {
180 Ok(_) => errSecSuccess,
181 Err(_) => errSecNotTrusted,
182 })?;
183 trust.copy_public_key()
184 }
185
186 pub fn delete(&self) -> Result<(), Error> {
188 let query = CFMutableDictionary::from_CFType_pairs(&[(
189 unsafe { kSecValueRef }.to_void(),
190 self.to_void(),
191 )]);
192
193 cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) })
194 }
195}
196
197fn get_asn1_header_bytes(pkt: CFString, ksz: u32) -> Option<&'static [u8]> {
198 use security_framework_sys::item::{kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyTypeRSA};
199
200 if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 2048 {
201 return Some(&RSA_2048_ASN1_HEADER);
202 }
203 if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 4096 {
204 return Some(&RSA_4096_ASN1_HEADER);
205 }
206 if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) } && ksz == 256 {
207 return Some(&EC_DSA_SECP_256_R1_ASN1_HEADER);
208 }
209 if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) } && ksz == 384 {
210 return Some(&EC_DSA_SECP_384_R1_ASN1_HEADER);
211 }
212 None
213}
214
215const RSA_2048_ASN1_HEADER: [u8; 24] = [
216 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
217 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
218];
219
220const RSA_4096_ASN1_HEADER: [u8; 24] = [
221 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
222 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
223];
224
225const EC_DSA_SECP_256_R1_ASN1_HEADER: [u8; 26] = [
226 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
227 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
228];
229
230const EC_DSA_SECP_384_R1_ASN1_HEADER: [u8; 23] = [
231 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b,
232 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00,
233];
234
235#[cfg(test)]
236mod test {
237 use crate::test::certificate;
238 use x509_parser::prelude::*;
239
240 #[test]
241 fn subject_summary() {
242 let cert = certificate();
243 assert_eq!("foobar.com", cert.subject_summary());
244 }
245
246 #[test]
247 fn email_addresses() {
248 let cert = certificate();
249 assert_eq!(Vec::<String>::new(), cert.email_addresses().unwrap());
250 }
251
252 #[test]
253 fn issuer() {
254 let cert = certificate();
255 let issuer = cert.issuer();
256 let (_, name) = X509Name::from_der(&issuer).unwrap();
257 let name_str = name.to_string_with_registry(oid_registry()).unwrap();
258 assert_eq!(
259 "C=US, ST=California, L=Palo Alto, O=Foobar LLC, OU=Dev Land, CN=foobar.com",
260 name_str
261 );
262 }
263
264 #[test]
265 fn subject() {
266 let cert = certificate();
267 let subject = cert.subject();
268 let (_, name) = X509Name::from_der(&subject).unwrap();
269 let name_str = name.to_string_with_registry(oid_registry()).unwrap();
270 assert_eq!(
271 "C=US, ST=California, L=Palo Alto, O=Foobar LLC, OU=Dev Land, CN=foobar.com",
272 name_str
273 );
274 }
275}