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