1 //! Certificate support.
2 
3 use core_foundation::base::TCFType;
4 use core_foundation::data::CFData;
5 use core_foundation::string::CFString;
6 use core_foundation_sys::base::kCFAllocatorDefault;
7 use security_framework_sys::base::{errSecParam, SecCertificateRef};
8 use security_framework_sys::certificate::*;
9 use std::fmt;
10 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
11 use std::ptr;
12 
13 use crate::base::{Error, Result};
14 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
15 use core_foundation::base::FromVoid;
16 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
17 use core_foundation::number::CFNumber;
18 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
19 use core_foundation_sys::base::CFRelease;
20 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
21 use security_framework_sys::base::SecPolicyRef;
22 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
23 use security_framework_sys::item::*;
24 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
25 use security_framework_sys::policy::SecPolicyCreateBasicX509;
26 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
27 use security_framework_sys::trust::{
28     SecTrustCopyPublicKey, SecTrustCreateWithCertificates, SecTrustEvaluate, SecTrustRef,
29     SecTrustResultType,
30 };
31 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
32 use std::ops::Deref;
33 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
34 use {cvt, key};
35 
36 declare_TCFType! {
37     /// A type representing a certificate.
38     SecCertificate, SecCertificateRef
39 }
40 impl_TCFType!(SecCertificate, SecCertificateRef, SecCertificateGetTypeID);
41 
42 unsafe impl Sync for SecCertificate {}
43 unsafe impl Send for SecCertificate {}
44 
45 impl fmt::Debug for SecCertificate {
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 
53 impl SecCertificate {
54     /// Creates a `SecCertificate` from DER encoded certificate data.
55     pub fn from_der(der_data: &[u8]) -> Result<SecCertificate> {
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(SecCertificate::wrap_under_create_rule(certificate))
64             }
65         }
66     }
67 
68     /// Returns DER encoded data describing this certificate.
69     pub fn to_der(&self) -> Vec<u8> {
70         unsafe {
71             let der_data = SecCertificateCopyData(self.0);
72             CFData::wrap_under_create_rule(der_data).to_vec()
73         }
74     }
75 
76     /// Returns a human readable summary of this certificate.
77     pub fn subject_summary(&self) -> String {
78         unsafe {
79             let summary = SecCertificateCopySubjectSummary(self.0);
80             CFString::wrap_under_create_rule(summary).to_string()
81         }
82     }
83 
84     #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
85     /// Returns DER encoded subjectPublicKeyInfo of certificate if available. This can be used
86     /// for certificate pinning.
87     pub fn public_key_info_der(&self) -> Result<Option<Vec<u8>>> {
88         // Imported from TrustKit
89         // https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/TSKSPKIHashCache.m
90         let public_key = self.public_key()?;
91         Ok(self.pk_to_der(public_key))
92     }
93 
94     #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
95     fn pk_to_der(&self, public_key: key::SecKey) -> Option<Vec<u8>> {
96         let public_key_attributes = public_key.attributes();
97         let public_key_type = public_key_attributes
98             .find(unsafe { kSecAttrKeyType } as *const ::std::os::raw::c_void)?;
99         let public_keysize = public_key_attributes
100             .find(unsafe { kSecAttrKeySizeInBits } as *const ::std::os::raw::c_void)?;
101         let public_keysize = unsafe { CFNumber::from_void(*public_keysize.deref()) };
102         let public_keysize_val = public_keysize.to_i64()? as u32;
103         let hdr_bytes = get_asn1_header_bytes(
104             unsafe { CFString::wrap_under_get_rule(*public_key_type.deref() as _) },
105             public_keysize_val,
106         )?;
107         let public_key_data = public_key.external_representation()?;
108         let mut out = Vec::with_capacity(hdr_bytes.len() + public_key_data.len() as usize);
109         out.extend_from_slice(hdr_bytes);
110         out.extend_from_slice(public_key_data.bytes());
111         Some(out)
112     }
113 
114     #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
115     /// Get public key from certificate
116     pub fn public_key(&self) -> Result<key::SecKey> {
117         unsafe {
118             // Create an X509 trust using the using the certificate
119             let mut trust: SecTrustRef = ptr::null_mut();
120             let policy: SecPolicyRef = SecPolicyCreateBasicX509();
121             cvt(SecTrustCreateWithCertificates(
122                 self.as_concrete_TypeRef() as _,
123                 policy as _,
124                 &mut trust,
125             ))?;
126 
127             // Get a public key reference for the certificate from the trust
128             let mut result: SecTrustResultType = 0;
129             cvt(SecTrustEvaluate(trust, &mut result))?;
130             let public_key = SecTrustCopyPublicKey(trust);
131             CFRelease(policy as _);
132             CFRelease(trust as _);
133 
134             Ok(key::SecKey::wrap_under_create_rule(public_key))
135         }
136     }
137 }
138 
139 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
140 fn get_asn1_header_bytes(pkt: CFString, ksz: u32) -> Option<&'static [u8]> {
141     if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 2048 {
142         return Some(&RSA_2048_ASN1_HEADER);
143     }
144     if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 4096 {
145         return Some(&RSA_4096_ASN1_HEADER);
146     }
147     if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) }
148         && ksz == 256
149     {
150         return Some(&EC_DSA_SECP_256_R1_ASN1_HEADER);
151     }
152     if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) }
153         && ksz == 384
154     {
155         return Some(&EC_DSA_SECP_384_R1_ASN1_HEADER);
156     }
157     None
158 }
159 
160 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
161 const RSA_2048_ASN1_HEADER: [u8; 24] = [
162     0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
163     0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
164 ];
165 
166 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
167 const RSA_4096_ASN1_HEADER: [u8; 24] = [
168     0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
169     0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00,
170 ];
171 
172 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
173 const EC_DSA_SECP_256_R1_ASN1_HEADER: [u8; 26] = [
174     0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
175     0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
176 ];
177 
178 #[cfg(any(feature = "OSX_10_12", target_os = "ios"))]
179 const EC_DSA_SECP_384_R1_ASN1_HEADER: [u8; 23] = [
180     0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b,
181     0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00,
182 ];
183 
184 #[cfg(test)]
185 mod test {
186     use crate::test::certificate;
187 
188     #[test]
189     fn subject_summary() {
190         let cert = certificate();
191         assert_eq!("foobar.com", cert.subject_summary());
192     }
193 }
194