apple_security/
certificate.rs

1//! Certificate support.
2
3use 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    /// A type representing a certificate.
37    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    /// Creates a `SecCertificate` from DER encoded certificate data.
55    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    /// Returns DER encoded data describing this certificate.
69    #[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    /// Adds a certificate to a keychain.
78    #[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    /// Returns a human readable summary of this certificate.
90    #[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    /// Returns a vector of email addresses for the subject of the certificate.
99    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    /// Returns DER encoded X.509 distinguished name of the certificate issuer.
114    #[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    /// Returns DER encoded X.509 distinguished name of the certificate subject.
124    #[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    /// Returns DER encoded serial number of the certificate.
134    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    /// Use `BigUint::from_bytes_be(serial_number_bytes())` instead
147    #[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    /// Returns DER encoded subjectPublicKeyInfo of certificate if available. This can be used
155    /// for certificate pinning.
156    pub fn public_key_info_der(&self) -> Result<Option<Vec<u8>>> {
157        // Imported from TrustKit
158        // https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/TSKSPKIHashCache.m
159        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    /// Get public key from certificate
189    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    /// Translates to `SecItemDelete`, passing in the `SecCertificateRef`
208    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}