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