apple_security_framework/
certificate.rs

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