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