apple_security/
trust.rs

1//! Trust evaluation support.
2
3use core_foundation::array::CFArray;
4#[cfg(target_os = "macos")]
5use core_foundation::array::CFArrayRef;
6use core_foundation::base::TCFType;
7#[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
8use core_foundation::data::CFData;
9use core_foundation::date::CFDate;
10use core_foundation_sys::base::{Boolean, CFIndex};
11
12use apple_security_sys::trust::*;
13use std::ptr;
14
15use crate::base::Result;
16use crate::certificate::SecCertificate;
17use crate::cvt;
18use crate::key::SecKey;
19use crate::policy::SecPolicy;
20use core_foundation::error::{CFError, CFErrorRef};
21
22/// The result of trust evaluation.
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
24pub struct TrustResult(SecTrustResultType);
25
26impl TrustResult {
27    /// An invalid setting or result.
28    pub const INVALID: Self = Self(kSecTrustResultInvalid);
29
30    /// You may proceed.
31    pub const PROCEED: Self = Self(kSecTrustResultProceed);
32
33    /// Indicates a denial by the user, do not proceed.
34    pub const DENY: Self = Self(kSecTrustResultDeny);
35
36    /// The certificate is implicitly trusted.
37    pub const UNSPECIFIED: Self = Self(kSecTrustResultUnspecified);
38
39    /// Indicates a trust policy failure that the user can override.
40    pub const RECOVERABLE_TRUST_FAILURE: Self = Self(kSecTrustResultRecoverableTrustFailure);
41
42    /// Indicates a trust policy failure that the user cannot override.
43    pub const FATAL_TRUST_FAILURE: Self = Self(kSecTrustResultFatalTrustFailure);
44
45    /// An error not related to trust validation.
46    pub const OTHER_ERROR: Self = Self(kSecTrustResultOtherError);
47}
48
49impl TrustResult {
50    /// Returns true if the result is "successful" - specifically `PROCEED` or `UNSPECIFIED`.
51    #[inline]
52    #[must_use]
53    pub fn success(self) -> bool {
54        matches!(self, Self::PROCEED | Self::UNSPECIFIED)
55    }
56}
57
58declare_TCFType! {
59    /// A type representing a trust evaluation for a certificate.
60    SecTrust, SecTrustRef
61}
62impl_TCFType!(SecTrust, SecTrustRef, SecTrustGetTypeID);
63
64unsafe impl Sync for SecTrust {}
65unsafe impl Send for SecTrust {}
66
67#[cfg(target_os = "macos")]
68bitflags::bitflags! {
69    /// The option flags used to configure the evaluation of a `SecTrust`.
70    pub struct TrustOptions: SecTrustOptionFlags {
71        /// Allow expired certificates (except for the root certificate).
72        const ALLOW_EXPIRED = kSecTrustOptionAllowExpired;
73        /// Allow CA certificates as leaf certificates.
74        const LEAF_IS_CA = kSecTrustOptionLeafIsCA;
75        /// Allow network downloads of CA certificates.
76        const FETCH_ISSUER_FROM_NET = kSecTrustOptionFetchIssuerFromNet;
77        /// Allow expired root certificates.
78        const ALLOW_EXPIRED_ROOT = kSecTrustOptionAllowExpiredRoot;
79        /// Require a positive revocation check for each certificate.
80        const REQUIRE_REVOCATION_PER_CERT =  kSecTrustOptionRequireRevPerCert;
81        /// Use TrustSettings instead of anchors.
82        const USE_TRUST_SETTINGS = kSecTrustOptionUseTrustSettings;
83        /// Treat properly self-signed certificates as anchors implicitly.
84        const IMPLICIT_ANCHORS =  kSecTrustOptionImplicitAnchors;
85    }
86}
87
88impl SecTrust {
89    /// Creates a `SecTrustRef` that is configured with a certificate chain, for validating
90    /// that chain against a collection of policies.
91    pub fn create_with_certificates(
92        certs: &[SecCertificate],
93        policies: &[SecPolicy],
94    ) -> Result<Self> {
95        let cert_array = CFArray::from_CFTypes(certs);
96        let policy_array = CFArray::from_CFTypes(policies);
97        let mut trust = ptr::null_mut();
98        unsafe {
99            cvt(SecTrustCreateWithCertificates(
100                cert_array.as_CFTypeRef(),
101                policy_array.as_CFTypeRef(),
102                &mut trust,
103            ))?;
104            Ok(Self(trust))
105        }
106    }
107
108    /// Sets the date and time against which the certificates in this trust object
109    /// are verified.
110    #[inline]
111    pub fn set_trust_verify_date(&mut self, date: &CFDate) -> Result<()> {
112        unsafe { cvt(SecTrustSetVerifyDate(self.0, date.as_concrete_TypeRef())) }
113    }
114
115    /// Sets additional anchor certificates used to validate trust.
116    pub fn set_anchor_certificates(&mut self, certs: &[SecCertificate]) -> Result<()> {
117        let certs = CFArray::from_CFTypes(certs);
118
119        unsafe {
120            cvt(SecTrustSetAnchorCertificates(
121                self.0,
122                certs.as_concrete_TypeRef(),
123            ))
124        }
125    }
126
127    /// Retrieves the anchor (root) certificates stored by macOS
128    #[cfg(target_os = "macos")]
129    pub fn copy_anchor_certificates() -> Result<Vec<SecCertificate>> {
130        let mut array: CFArrayRef = ptr::null();
131
132        unsafe {
133            cvt(SecTrustCopyAnchorCertificates(&mut array))?;
134        }
135
136        if array.is_null() {
137            return Ok(vec![]);
138        }
139
140        let array = unsafe { CFArray::<SecCertificate>::wrap_under_create_rule(array) };
141        Ok(array.into_iter().map(|c| c.clone()).collect())
142    }
143
144    /// If set to `true`, only the certificates specified by
145    /// `set_anchor_certificates` will be trusted, but not globally trusted
146    /// certificates.
147    #[inline]
148    pub fn set_trust_anchor_certificates_only(&mut self, only: bool) -> Result<()> {
149        unsafe { cvt(SecTrustSetAnchorCertificatesOnly(self.0, only as Boolean)) }
150    }
151
152    /// Sets the policy used to evaluate trust.
153    #[inline]
154    pub fn set_policy(&mut self, policy: &SecPolicy) -> Result<()> {
155        unsafe { cvt(SecTrustSetPolicies(self.0, policy.as_CFTypeRef())) }
156    }
157
158    /// Sets option flags for customizing evaluation of a trust object.
159    #[cfg(target_os = "macos")]
160    #[inline]
161    pub fn set_options(&mut self, options: TrustOptions) -> Result<()> {
162        unsafe { cvt(SecTrustSetOptions(self.0, options.bits())) }
163    }
164
165    /// Indicates whether this trust object is permitted to
166    /// fetch missing intermediate certificates from the network.
167    #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
168    pub fn get_network_fetch_allowed(&mut self) -> Result<bool> {
169        let mut allowed = 0;
170
171        unsafe { cvt(SecTrustGetNetworkFetchAllowed(self.0, &mut allowed))? };
172
173        Ok(allowed != 0)
174    }
175
176    /// Specifies whether this trust object is permitted to
177    /// fetch missing intermediate certificates from the network.
178    #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
179    #[inline]
180    pub fn set_network_fetch_allowed(&mut self, allowed: bool) -> Result<()> {
181        unsafe { cvt(SecTrustSetNetworkFetchAllowed(self.0, allowed as u8)) }
182    }
183
184    /// Attaches Online Certificate Status Protocol (OSCP) response data
185    /// to this trust object.
186    #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
187    pub fn set_trust_ocsp_response<I: Iterator<Item = impl AsRef<[u8]>>>(
188        &mut self,
189        ocsp_response: I,
190    ) -> Result<()> {
191        let response: Vec<CFData> = ocsp_response
192            .into_iter()
193            .map(|bytes| CFData::from_buffer(bytes.as_ref()))
194            .collect();
195
196        let response = CFArray::from_CFTypes(&response);
197
198        unsafe { cvt(SecTrustSetOCSPResponse(self.0, response.as_CFTypeRef())) }
199    }
200
201    /// Attaches signed certificate timestamp data to this trust object.
202    #[cfg(any(feature = "OSX_10_14", target_os = "ios"))]
203    pub fn set_signed_certificate_timestamps<I: Iterator<Item = impl AsRef<[u8]>>>(
204        &mut self,
205        scts: I,
206    ) -> Result<()> {
207        let scts: Vec<CFData> = scts
208            .into_iter()
209            .map(|bytes| CFData::from_buffer(bytes.as_ref()))
210            .collect();
211
212        let scts = CFArray::from_CFTypes(&scts);
213
214        unsafe { cvt(SecTrustSetSignedCertificateTimestamps(self.0, scts.as_concrete_TypeRef())) }
215    }
216
217    /// Returns the public key for a leaf certificate after it has been evaluated.
218    #[inline]
219    pub fn copy_public_key(&mut self) -> Result<SecKey> {
220        unsafe {
221            Ok(SecKey::wrap_under_create_rule(SecTrustCopyPublicKey(
222                self.0,
223            )))
224        }
225    }
226
227    /// Evaluates trust.
228    #[deprecated(note = "use evaluate_with_error")]
229    pub fn evaluate(&self) -> Result<TrustResult> {
230        #[allow(deprecated)]
231        unsafe {
232            let mut result = kSecTrustResultInvalid;
233            cvt(SecTrustEvaluate(self.0, &mut result))?;
234            Ok(TrustResult(result))
235        }
236    }
237
238    /// Evaluates trust. Requires macOS 10.14 or iOS, otherwise it just calls `evaluate()`
239    pub fn evaluate_with_error(&self) -> Result<(), CFError> {
240        #[cfg(any(feature = "OSX_10_14", target_os = "ios"))]
241        unsafe {
242            let mut error: CFErrorRef = ::std::ptr::null_mut();
243            if !SecTrustEvaluateWithError(self.0, &mut error) {
244                assert!(!error.is_null());
245                let error = CFError::wrap_under_create_rule(error);
246                return Err(error);
247            }
248            Ok(())
249        }
250        #[cfg(not(any(feature = "OSX_10_14", target_os = "ios")))]
251        #[allow(deprecated)]
252        {
253            use apple_security_sys::base::errSecNotTrusted;
254            use apple_security_sys::base::errSecTrustSettingDeny;
255
256            let code = match self.evaluate() {
257                Ok(res) if res.success() => return Ok(()),
258                Ok(TrustResult::DENY) => errSecTrustSettingDeny,
259                Ok(_) => errSecNotTrusted,
260                Err(err) => err.code(),
261            };
262            Err(cferror_from_osstatus(code))
263        }
264    }
265
266    /// Returns the number of certificates in an evaluated certificate chain.
267    ///
268    /// Note: evaluate must first be called on the `SecTrust`.
269    #[inline(always)]
270    #[must_use]
271    pub fn certificate_count(&self) -> CFIndex {
272        unsafe { SecTrustGetCertificateCount(self.0) }
273    }
274
275    /// Returns a specific certificate from the certificate chain used to evaluate trust.
276    ///
277    /// Note: evaluate must first be called on the `SecTrust`.
278    #[deprecated(note = "deprecated by Apple")]
279    #[must_use]
280    pub fn certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate> {
281        #[allow(deprecated)]
282        unsafe {
283            if self.certificate_count() <= ix {
284                None
285            } else {
286                let certificate = SecTrustGetCertificateAtIndex(self.0, ix);
287                Some(SecCertificate::wrap_under_get_rule(certificate.cast()))
288            }
289        }
290    }
291}
292
293#[cfg(not(any(feature = "OSX_10_14", target_os = "ios")))]
294extern "C" {
295    fn CFErrorCreate(allocator: core_foundation_sys::base::CFAllocatorRef, domain: core_foundation_sys::string::CFStringRef, code: CFIndex, userInfo: core_foundation_sys::dictionary::CFDictionaryRef) -> CFErrorRef;
296}
297
298#[cfg(not(any(feature = "OSX_10_14", target_os = "ios")))]
299fn cferror_from_osstatus(code: core_foundation_sys::base::OSStatus) -> CFError {
300    unsafe {
301        let error = CFErrorCreate(ptr::null_mut(), core_foundation_sys::error::kCFErrorDomainOSStatus, code as _, ptr::null_mut());
302        assert!(!error.is_null());
303        CFError::wrap_under_create_rule(error)
304    }
305}
306
307#[cfg(test)]
308mod test {
309    use crate::policy::SecPolicy;
310    use crate::secure_transport::SslProtocolSide;
311    use crate::test::certificate;
312    use crate::trust::SecTrust;
313
314    #[test]
315    #[allow(deprecated)]
316    fn create_with_certificates() {
317        let cert = certificate();
318        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
319        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
320        assert_eq!(trust.evaluate().unwrap().success(), false)
321    }
322
323    #[test]
324    fn create_with_certificates_new() {
325        let cert = certificate();
326        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
327        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
328        assert!(trust.evaluate_with_error().is_err());
329    }
330
331    #[test]
332    #[allow(deprecated)]
333    fn certificate_count_and_at_index() {
334        let cert = certificate();
335        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
336        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
337        trust.evaluate().unwrap();
338
339        let count = trust.certificate_count();
340        assert_eq!(count, 1);
341
342        let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
343        assert_eq!(cert_bytes, certificate().to_der());
344    }
345
346    #[test]
347    #[allow(deprecated)]
348    fn certificate_count_and_at_index_new() {
349        let cert = certificate();
350        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
351        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
352        assert!(trust.evaluate_with_error().is_err());
353
354        let count = trust.certificate_count();
355        assert_eq!(count, 1);
356
357        let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
358        assert_eq!(cert_bytes, certificate().to_der());
359    }
360
361    #[test]
362    #[allow(deprecated)]
363    fn certificate_at_index_out_of_bounds() {
364        let cert = certificate();
365        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
366
367        let trust = SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap();
368        trust.evaluate().unwrap();
369        assert!(trust.certificate_at_index(1).is_none());
370
371        let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
372        assert!(trust.evaluate_with_error().is_err());
373        assert!(trust.certificate_at_index(1).is_none());
374    }
375
376    #[test]
377    #[allow(deprecated)]
378    fn set_policy() {
379        let cert = certificate();
380        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
381        let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
382        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
383        trust.set_policy(&ssl_policy).unwrap();
384        assert_eq!(trust.evaluate().unwrap().success(), false)
385    }
386
387    #[test]
388    fn set_policy_new() {
389        let cert = certificate();
390        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
391        let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
392        let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
393        trust.set_policy(&ssl_policy).unwrap();
394        assert!(trust.evaluate_with_error().is_err());
395    }
396}