summaryrefslogtreecommitdiffstats
path: root/vendor/security-framework/src/certificate.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/security-framework/src/certificate.rs
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-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.rs320
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);
+ }
+}