summaryrefslogtreecommitdiff
path: root/vendor/security-framework/src/trust.rs
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
committermo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
commit01959b16a21b22b5df5f16569c2a8e8f92beecef (patch)
tree32afa5d747c5466345c59ec52161a7cba3d6d755 /vendor/security-framework/src/trust.rs
parentff30574117a996df332e23d1fb6f65259b316b5b (diff)
chore: vendor dependencies
Diffstat (limited to 'vendor/security-framework/src/trust.rs')
-rw-r--r--vendor/security-framework/src/trust.rs393
1 files changed, 393 insertions, 0 deletions
diff --git a/vendor/security-framework/src/trust.rs b/vendor/security-framework/src/trust.rs
new file mode 100644
index 00000000..79181d9f
--- /dev/null
+++ b/vendor/security-framework/src/trust.rs
@@ -0,0 +1,393 @@
+//! Trust evaluation support.
+
+use core_foundation::array::CFArray;
+#[cfg(target_os = "macos")]
+use core_foundation::array::CFArrayRef;
+use core_foundation::base::TCFType;
+use core_foundation::data::CFData;
+use core_foundation::date::CFDate;
+use core_foundation_sys::base::{Boolean, CFIndex};
+
+use security_framework_sys::trust::*;
+use std::ptr;
+
+use crate::base::Result;
+use crate::certificate::SecCertificate;
+use crate::cvt;
+use crate::key::SecKey;
+use crate::policy::SecPolicy;
+use core_foundation::error::{CFError, CFErrorRef};
+
+/// The result of trust evaluation.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct TrustResult(SecTrustResultType);
+
+impl TrustResult {
+ /// An invalid setting or result.
+ pub const INVALID: Self = Self(kSecTrustResultInvalid);
+
+ /// You may proceed.
+ pub const PROCEED: Self = Self(kSecTrustResultProceed);
+
+ /// Indicates a denial by the user, do not proceed.
+ pub const DENY: Self = Self(kSecTrustResultDeny);
+
+ /// The certificate is implicitly trusted.
+ pub const UNSPECIFIED: Self = Self(kSecTrustResultUnspecified);
+
+ /// Indicates a trust policy failure that the user can override.
+ pub const RECOVERABLE_TRUST_FAILURE: Self = Self(kSecTrustResultRecoverableTrustFailure);
+
+ /// Indicates a trust policy failure that the user cannot override.
+ pub const FATAL_TRUST_FAILURE: Self = Self(kSecTrustResultFatalTrustFailure);
+
+ /// An error not related to trust validation.
+ pub const OTHER_ERROR: Self = Self(kSecTrustResultOtherError);
+}
+
+impl TrustResult {
+ /// Returns true if the result is "successful" - specifically `PROCEED` or `UNSPECIFIED`.
+ #[inline]
+ #[must_use]
+ pub fn success(self) -> bool {
+ matches!(self, Self::PROCEED | Self::UNSPECIFIED)
+ }
+}
+
+declare_TCFType! {
+ /// A type representing a trust evaluation for a certificate.
+ SecTrust, SecTrustRef
+}
+impl_TCFType!(SecTrust, SecTrustRef, SecTrustGetTypeID);
+
+unsafe impl Sync for SecTrust {}
+unsafe impl Send for SecTrust {}
+
+#[cfg(target_os = "macos")]
+bitflags::bitflags! {
+ /// The option flags used to configure the evaluation of a `SecTrust`.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ pub struct TrustOptions: SecTrustOptionFlags {
+ /// Allow expired certificates (except for the root certificate).
+ const ALLOW_EXPIRED = kSecTrustOptionAllowExpired;
+ /// Allow CA certificates as leaf certificates.
+ const LEAF_IS_CA = kSecTrustOptionLeafIsCA;
+ /// Allow network downloads of CA certificates.
+ const FETCH_ISSUER_FROM_NET = kSecTrustOptionFetchIssuerFromNet;
+ /// Allow expired root certificates.
+ const ALLOW_EXPIRED_ROOT = kSecTrustOptionAllowExpiredRoot;
+ /// Require a positive revocation check for each certificate.
+ const REQUIRE_REVOCATION_PER_CERT = kSecTrustOptionRequireRevPerCert;
+ /// Use TrustSettings instead of anchors.
+ const USE_TRUST_SETTINGS = kSecTrustOptionUseTrustSettings;
+ /// Treat properly self-signed certificates as anchors implicitly.
+ const IMPLICIT_ANCHORS = kSecTrustOptionImplicitAnchors;
+ }
+}
+
+impl SecTrust {
+ /// Creates a `SecTrustRef` that is configured with a certificate chain, for validating
+ /// that chain against a collection of policies.
+ pub fn create_with_certificates(
+ certs: &[SecCertificate],
+ policies: &[SecPolicy],
+ ) -> Result<Self> {
+ let cert_array = CFArray::from_CFTypes(certs);
+ let policy_array = CFArray::from_CFTypes(policies);
+ let mut trust = ptr::null_mut();
+ unsafe {
+ cvt(SecTrustCreateWithCertificates(
+ cert_array.as_CFTypeRef(),
+ policy_array.as_CFTypeRef(),
+ &mut trust,
+ ))?;
+ Ok(Self(trust))
+ }
+ }
+
+ /// Sets the date and time against which the certificates in this trust object
+ /// are verified.
+ #[inline]
+ pub fn set_trust_verify_date(&mut self, date: &CFDate) -> Result<()> {
+ unsafe { cvt(SecTrustSetVerifyDate(self.0, date.as_concrete_TypeRef())) }
+ }
+
+ /// Sets additional anchor certificates used to validate trust.
+ pub fn set_anchor_certificates(&mut self, certs: &[SecCertificate]) -> Result<()> {
+ let certs = CFArray::from_CFTypes(certs);
+
+ unsafe {
+ cvt(SecTrustSetAnchorCertificates(
+ self.0,
+ certs.as_concrete_TypeRef(),
+ ))
+ }
+ }
+
+ /// Retrieves the anchor (root) certificates stored by macOS
+ #[cfg(target_os = "macos")]
+ pub fn copy_anchor_certificates() -> Result<Vec<SecCertificate>> {
+ let mut array: CFArrayRef = ptr::null();
+
+ unsafe {
+ cvt(SecTrustCopyAnchorCertificates(&mut array))?;
+ }
+
+ if array.is_null() {
+ return Ok(vec![]);
+ }
+
+ let array = unsafe { CFArray::<SecCertificate>::wrap_under_create_rule(array) };
+ Ok(array.into_iter().map(|c| c.clone()).collect())
+ }
+
+ /// If set to `true`, only the certificates specified by
+ /// `set_anchor_certificates` will be trusted, but not globally trusted
+ /// certificates.
+ #[inline]
+ pub fn set_trust_anchor_certificates_only(&mut self, only: bool) -> Result<()> {
+ unsafe { cvt(SecTrustSetAnchorCertificatesOnly(self.0, Boolean::from(only))) }
+ }
+
+ /// Sets the policy used to evaluate trust.
+ #[inline]
+ pub fn set_policy(&mut self, policy: &SecPolicy) -> Result<()> {
+ unsafe { cvt(SecTrustSetPolicies(self.0, policy.as_CFTypeRef())) }
+ }
+
+ /// Sets option flags for customizing evaluation of a trust object.
+ #[cfg(target_os = "macos")]
+ #[inline]
+ pub fn set_options(&mut self, options: TrustOptions) -> Result<()> {
+ unsafe { cvt(SecTrustSetOptions(self.0, options.bits())) }
+ }
+
+ /// Indicates whether this trust object is permitted to
+ /// fetch missing intermediate certificates from the network.
+ pub fn get_network_fetch_allowed(&mut self) -> Result<bool> {
+ let mut allowed = 0;
+
+ unsafe { cvt(SecTrustGetNetworkFetchAllowed(self.0, &mut allowed))? };
+
+ Ok(allowed != 0)
+ }
+
+ /// Specifies whether this trust object is permitted to
+ /// fetch missing intermediate certificates from the network.
+ #[inline]
+ pub fn set_network_fetch_allowed(&mut self, allowed: bool) -> Result<()> {
+ unsafe { cvt(SecTrustSetNetworkFetchAllowed(self.0, allowed as u8)) }
+ }
+
+ /// Attaches Online Certificate Status Protocol (OSCP) response data
+ /// to this trust object.
+ pub fn set_trust_ocsp_response<I: Iterator<Item = impl AsRef<[u8]>>>(
+ &mut self,
+ ocsp_response: I,
+ ) -> Result<()> {
+ let response: Vec<CFData> = ocsp_response
+ .into_iter()
+ .map(|bytes| CFData::from_buffer(bytes.as_ref()))
+ .collect();
+
+ let response = CFArray::from_CFTypes(&response);
+
+ unsafe { cvt(SecTrustSetOCSPResponse(self.0, response.as_CFTypeRef())) }
+ }
+
+ /// Attaches signed certificate timestamp data to this trust object.
+ #[cfg(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
+ pub fn set_signed_certificate_timestamps<I: Iterator<Item = impl AsRef<[u8]>>>(
+ &mut self,
+ scts: I,
+ ) -> Result<()> {
+ let scts: Vec<CFData> = scts
+ .into_iter()
+ .map(|bytes| CFData::from_buffer(bytes.as_ref()))
+ .collect();
+
+ let scts = CFArray::from_CFTypes(&scts);
+
+ unsafe { cvt(SecTrustSetSignedCertificateTimestamps(self.0, scts.as_concrete_TypeRef())) }
+ }
+
+ /// Returns the public key for a leaf certificate after it has been evaluated.
+ #[inline]
+ pub fn copy_public_key(&mut self) -> Result<SecKey> {
+ unsafe {
+ Ok(SecKey::wrap_under_create_rule(SecTrustCopyPublicKey(
+ self.0,
+ )))
+ }
+ }
+
+ /// Evaluates trust.
+ #[deprecated(note = "use evaluate_with_error")]
+ pub fn evaluate(&self) -> Result<TrustResult> {
+ #[allow(deprecated)]
+ unsafe {
+ let mut result = kSecTrustResultInvalid;
+ cvt(SecTrustEvaluate(self.0, &mut result))?;
+ Ok(TrustResult(result))
+ }
+ }
+
+ /// Evaluates trust. Requires macOS 10.14 or iOS, otherwise it just calls `evaluate()`
+ pub fn evaluate_with_error(&self) -> Result<(), CFError> {
+ #[cfg(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos"))]
+ unsafe {
+ let mut error: CFErrorRef = ::std::ptr::null_mut();
+ if !SecTrustEvaluateWithError(self.0, &mut error) {
+ assert!(!error.is_null());
+ let error = CFError::wrap_under_create_rule(error);
+ return Err(error);
+ }
+ Ok(())
+ }
+ #[cfg(not(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))]
+ #[allow(deprecated)]
+ {
+ use security_framework_sys::base::errSecNotTrusted;
+ use security_framework_sys::base::errSecTrustSettingDeny;
+
+ let code = match self.evaluate() {
+ Ok(res) if res.success() => return Ok(()),
+ Ok(TrustResult::DENY) => errSecTrustSettingDeny,
+ Ok(_) => errSecNotTrusted,
+ Err(err) => err.code(),
+ };
+ Err(cferror_from_osstatus(code))
+ }
+ }
+
+ /// Returns the number of certificates in an evaluated certificate chain.
+ ///
+ /// Note: evaluate must first be called on the `SecTrust`.
+ #[inline(always)]
+ #[must_use]
+ pub fn certificate_count(&self) -> CFIndex {
+ unsafe { SecTrustGetCertificateCount(self.0) }
+ }
+
+ /// Returns a specific certificate from the certificate chain used to evaluate trust.
+ ///
+ /// Note: evaluate must first be called on the `SecTrust`.
+ #[deprecated(note = "deprecated by Apple")]
+ #[must_use]
+ pub fn certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate> {
+ #[allow(deprecated)]
+ unsafe {
+ if self.certificate_count() <= ix {
+ None
+ } else {
+ let certificate = SecTrustGetCertificateAtIndex(self.0, ix);
+ Some(SecCertificate::wrap_under_get_rule(certificate.cast()))
+ }
+ }
+ }
+}
+
+#[cfg(not(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))]
+extern "C" {
+ fn CFErrorCreate(allocator: core_foundation_sys::base::CFAllocatorRef, domain: core_foundation_sys::string::CFStringRef, code: CFIndex, userInfo: core_foundation_sys::dictionary::CFDictionaryRef) -> CFErrorRef;
+}
+
+#[cfg(not(any(feature = "OSX_10_14", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "visionos")))]
+fn cferror_from_osstatus(code: core_foundation_sys::base::OSStatus) -> CFError {
+ unsafe {
+ let error = CFErrorCreate(ptr::null_mut(), core_foundation_sys::error::kCFErrorDomainOSStatus, code as _, ptr::null_mut());
+ assert!(!error.is_null());
+ CFError::wrap_under_create_rule(error)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::policy::SecPolicy;
+ use crate::secure_transport::SslProtocolSide;
+ use crate::test::certificate;
+ use crate::trust::SecTrust;
+
+ #[test]
+ #[allow(deprecated)]
+ fn create_with_certificates() {
+ let cert = certificate();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
+ let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
+ assert!(!trust.evaluate().unwrap().success());
+ }
+
+ #[test]
+ fn create_with_certificates_new() {
+ let cert = certificate();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
+ let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
+ assert!(trust.evaluate_with_error().is_err());
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn certificate_count_and_at_index() {
+ let cert = certificate();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
+ let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
+ trust.evaluate().unwrap();
+
+ let count = trust.certificate_count();
+ assert_eq!(count, 1);
+
+ let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
+ assert_eq!(cert_bytes, certificate().to_der());
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn certificate_count_and_at_index_new() {
+ let cert = certificate();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
+ let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
+ assert!(trust.evaluate_with_error().is_err());
+
+ let count = trust.certificate_count();
+ assert_eq!(count, 1);
+
+ let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
+ assert_eq!(cert_bytes, certificate().to_der());
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn certificate_at_index_out_of_bounds() {
+ let cert = certificate();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
+
+ let trust = SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap();
+ trust.evaluate().unwrap();
+ assert!(trust.certificate_at_index(1).is_none());
+
+ let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
+ assert!(trust.evaluate_with_error().is_err());
+ assert!(trust.certificate_at_index(1).is_none());
+ }
+
+ #[test]
+ #[allow(deprecated)]
+ fn set_policy() {
+ let cert = certificate();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
+ let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
+ trust.set_policy(&ssl_policy).unwrap();
+ assert!(!trust.evaluate().unwrap().success());
+ }
+
+ #[test]
+ fn set_policy_new() {
+ let cert = certificate();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
+ let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
+ let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
+ trust.set_policy(&ssl_policy).unwrap();
+ assert!(trust.evaluate_with_error().is_err());
+ }
+}