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