diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/security-framework/src/certificate.rs | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/security-framework/src/certificate.rs')
-rw-r--r-- | vendor/security-framework/src/certificate.rs | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/vendor/security-framework/src/certificate.rs b/vendor/security-framework/src/certificate.rs new file mode 100644 index 000000000..e33168a75 --- /dev/null +++ b/vendor/security-framework/src/certificate.rs @@ -0,0 +1,320 @@ +//! Certificate support. + +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{TCFType, ToVoid}; +use core_foundation::data::CFData; +use core_foundation::dictionary::CFMutableDictionary; +use core_foundation::string::CFString; +use core_foundation_sys::base::kCFAllocatorDefault; +#[cfg(target_os = "ios")] +use security_framework_sys::base::{errSecNotTrusted, errSecSuccess}; +use security_framework_sys::base::{errSecParam, SecCertificateRef}; +use security_framework_sys::certificate::*; +use security_framework_sys::keychain_item::SecItemDelete; +use std::fmt; +use std::ptr; + +use crate::base::{Error, Result}; +use crate::cvt; +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +use crate::key; +#[cfg(target_os = "macos")] +use crate::os::macos::keychain::SecKeychain; +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +use core_foundation::base::FromVoid; +#[cfg(any(feature = "OSX_10_13", target_os = "ios"))] +use core_foundation::error::{CFError, CFErrorRef}; +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +use core_foundation::number::CFNumber; +#[cfg(feature = "serial-number-bigint")] +use num_bigint::BigUint; +use security_framework_sys::item::kSecValueRef; +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +use std::ops::Deref; + +declare_TCFType! { + /// A type representing a certificate. + SecCertificate, SecCertificateRef +} +impl_TCFType!(SecCertificate, SecCertificateRef, SecCertificateGetTypeID); + +unsafe impl Sync for SecCertificate {} +unsafe impl Send for SecCertificate {} + +impl fmt::Debug for SecCertificate { + #[cold] + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("SecCertificate") + .field("subject", &self.subject_summary()) + .finish() + } +} + +impl SecCertificate { + /// Creates a `SecCertificate` from DER encoded certificate data. + pub fn from_der(der_data: &[u8]) -> Result<Self> { + let der_data = CFData::from_buffer(der_data); + unsafe { + let certificate = + SecCertificateCreateWithData(kCFAllocatorDefault, der_data.as_concrete_TypeRef()); + if certificate.is_null() { + Err(Error::from_code(errSecParam)) + } else { + Ok(Self::wrap_under_create_rule(certificate)) + } + } + } + + /// Returns DER encoded data describing this certificate. + #[must_use] + pub fn to_der(&self) -> Vec<u8> { + unsafe { + let der_data = SecCertificateCopyData(self.0); + CFData::wrap_under_create_rule(der_data).to_vec() + } + } + + /// Adds a certificate to a keychain. + #[cfg(target_os="macos")] + pub fn add_to_keychain(&self, keychain: Option<SecKeychain>) -> Result<()> { + let kch = match keychain { + Some(kch) => kch, + _ => SecKeychain::default()?, + }; + cvt(unsafe { + SecCertificateAddToKeychain(self.as_CFTypeRef() as *mut _, kch.as_CFTypeRef() as *mut _) + }) + } + + /// Returns a human readable summary of this certificate. + #[must_use] + pub fn subject_summary(&self) -> String { + unsafe { + let summary = SecCertificateCopySubjectSummary(self.0); + CFString::wrap_under_create_rule(summary).to_string() + } + } + + /// Returns a vector of email addresses for the subject of the certificate. + pub fn email_addresses(&self) -> Result<Vec<String>, Error> { + let mut array: CFArrayRef = ptr::null(); + unsafe { + cvt(SecCertificateCopyEmailAddresses( + self.as_concrete_TypeRef(), + &mut array, + ))?; + + let array = CFArray::<CFString>::wrap_under_create_rule(array); + Ok(array.into_iter().map(|p| p.to_string()).collect()) + } + } + + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + /// Returns DER encoded X.509 distinguished name of the certificate issuer. + #[must_use] + pub fn issuer(&self) -> Vec<u8> { + unsafe { + let issuer = SecCertificateCopyNormalizedIssuerSequence(self.0); + CFData::wrap_under_create_rule(issuer).to_vec() + } + } + + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + /// Returns DER encoded X.509 distinguished name of the certificate subject. + #[must_use] + pub fn subject(&self) -> Vec<u8> { + unsafe { + let subject = SecCertificateCopyNormalizedSubjectSequence(self.0); + CFData::wrap_under_create_rule(subject).to_vec() + } + } + + #[cfg(any(feature = "OSX_10_13", target_os = "ios"))] + /// Returns DER encoded serial number of the certificate. + pub fn serial_number_bytes(&self) -> Result<Vec<u8>, CFError> { + unsafe { + let mut error: CFErrorRef = ptr::null_mut(); + let serial_number = SecCertificateCopySerialNumberData(self.0, &mut error); + if error.is_null() { + Ok(CFData::wrap_under_create_rule(serial_number).to_vec()) + } else { + Err(CFError::wrap_under_create_rule(error)) + } + } + } + + /// Use `BigUint::from_bytes_be(serial_number_bytes())` instead + #[deprecated(note = "use serial_number_bytes()")] + #[cfg(feature = "serial-number-bigint")] + pub fn serial_number(&self) -> Result<BigUint, CFError> { + Ok(BigUint::from_bytes_be(&self.serial_number_bytes()?)) + } + + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + /// Returns DER encoded subjectPublicKeyInfo of certificate if available. This can be used + /// for certificate pinning. + pub fn public_key_info_der(&self) -> Result<Option<Vec<u8>>> { + // Imported from TrustKit + // https://github.com/datatheorem/TrustKit/blob/master/TrustKit/Pinning/TSKSPKIHashCache.m + let public_key = self.public_key()?; + Ok(self.pk_to_der(public_key)) + } + + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + #[must_use] + fn pk_to_der(&self, public_key: key::SecKey) -> Option<Vec<u8>> { + use security_framework_sys::item::kSecAttrKeyType; + use security_framework_sys::item::kSecAttrKeySizeInBits; + + let public_key_attributes = public_key.attributes(); + let public_key_type = public_key_attributes + .find(unsafe { kSecAttrKeyType }.cast::<std::os::raw::c_void>())?; + let public_keysize = public_key_attributes + .find(unsafe { kSecAttrKeySizeInBits }.cast::<std::os::raw::c_void>())?; + let public_keysize = unsafe { CFNumber::from_void(*public_keysize.deref()) }; + let public_keysize_val = public_keysize.to_i64()? as u32; + let hdr_bytes = get_asn1_header_bytes( + unsafe { CFString::wrap_under_get_rule(*public_key_type.deref() as _) }, + public_keysize_val, + )?; + let public_key_data = public_key.external_representation()?; + let mut out = Vec::with_capacity(hdr_bytes.len() + public_key_data.len() as usize); + out.extend_from_slice(hdr_bytes); + out.extend_from_slice(public_key_data.bytes()); + Some(out) + } + + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + /// Get public key from certificate + pub fn public_key(&self) -> Result<key::SecKey> { + use crate::policy::SecPolicy; + use crate::trust::SecTrust; + use std::slice::from_ref; + + let policy = SecPolicy::create_x509(); + let mut trust = SecTrust::create_with_certificates(from_ref(self), from_ref(&policy))?; + #[allow(deprecated)] + #[cfg(not(target_os = "ios"))] + trust.evaluate()?; + #[cfg(target_os = "ios")] + cvt(match trust.evaluate_with_error() { + Ok(_) => errSecSuccess, + Err(_) => errSecNotTrusted, + })?; + trust.copy_public_key() + } + + /// Translates to `SecItemDelete`, passing in the `SecCertificateRef` + pub fn delete(&self) -> Result<(), Error> { + let query = CFMutableDictionary::from_CFType_pairs(&[( + unsafe { kSecValueRef }.to_void(), + self.to_void(), + )]); + + cvt(unsafe { SecItemDelete(query.as_concrete_TypeRef()) }) + } +} + +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +fn get_asn1_header_bytes(pkt: CFString, ksz: u32) -> Option<&'static [u8]> { + use security_framework_sys::item::kSecAttrKeyTypeRSA; + use security_framework_sys::item::kSecAttrKeyTypeECSECPrimeRandom; + + if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 2048 { + return Some(&RSA_2048_ASN1_HEADER); + } + if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeRSA) } && ksz == 4096 { + return Some(&RSA_4096_ASN1_HEADER); + } + if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) } + && ksz == 256 + { + return Some(&EC_DSA_SECP_256_R1_ASN1_HEADER); + } + if pkt == unsafe { CFString::wrap_under_get_rule(kSecAttrKeyTypeECSECPrimeRandom) } + && ksz == 384 + { + return Some(&EC_DSA_SECP_384_R1_ASN1_HEADER); + } + None +} + +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +const RSA_2048_ASN1_HEADER: [u8; 24] = [ + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, +]; + +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +const RSA_4096_ASN1_HEADER: [u8; 24] = [ + 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, +]; + +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +const EC_DSA_SECP_256_R1_ASN1_HEADER: [u8; 26] = [ + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, +]; + +#[cfg(any(feature = "OSX_10_12", target_os = "ios"))] +const EC_DSA_SECP_384_R1_ASN1_HEADER: [u8; 23] = [ + 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, + 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, +]; + +#[cfg(test)] +mod test { + use crate::test::certificate; + #[cfg(feature = "serial-number-bigint")] + use num_bigint::BigUint; + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + use x509_parser::prelude::*; + + #[test] + fn subject_summary() { + let cert = certificate(); + assert_eq!("foobar.com", cert.subject_summary()); + } + + #[test] + fn email_addresses() { + let cert = certificate(); + assert_eq!(Vec::<String>::new(), cert.email_addresses().unwrap()); + } + + #[test] + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + fn issuer() { + let cert = certificate(); + let issuer = cert.issuer(); + let (_, name) = X509Name::from_der(&issuer).unwrap(); + let name_str = name.to_string_with_registry(oid_registry()).unwrap(); + assert_eq!( + "C=US, ST=CALIFORNIA, L=PALO ALTO, O=FOOBAR LLC, OU=DEV LAND, CN=FOOBAR.COM", + name_str + ); + } + + #[test] + #[cfg(any(feature = "OSX_10_12", target_os = "ios"))] + fn subject() { + let cert = certificate(); + let subject = cert.subject(); + let (_, name) = X509Name::from_der(&subject).unwrap(); + let name_str = name.to_string_with_registry(oid_registry()).unwrap(); + assert_eq!( + "C=US, ST=CALIFORNIA, L=PALO ALTO, O=FOOBAR LLC, OU=DEV LAND, CN=FOOBAR.COM", + name_str + ); + } + + #[test] + #[cfg(feature = "serial-number-bigint")] + #[allow(deprecated)] + fn serial_number() { + let cert = certificate(); + let serial_number = cert.serial_number().unwrap(); + assert_eq!(BigUint::from(16452297291294946383_u128), serial_number); + } +} |