1use 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#[derive(Debug, Copy, Clone, PartialEq, Eq)]
24pub struct TrustResult(SecTrustResultType);
25
26impl TrustResult {
27 pub const INVALID: Self = Self(kSecTrustResultInvalid);
29
30 pub const PROCEED: Self = Self(kSecTrustResultProceed);
32
33 pub const DENY: Self = Self(kSecTrustResultDeny);
35
36 pub const UNSPECIFIED: Self = Self(kSecTrustResultUnspecified);
38
39 pub const RECOVERABLE_TRUST_FAILURE: Self = Self(kSecTrustResultRecoverableTrustFailure);
41
42 pub const FATAL_TRUST_FAILURE: Self = Self(kSecTrustResultFatalTrustFailure);
44
45 pub const OTHER_ERROR: Self = Self(kSecTrustResultOtherError);
47}
48
49impl TrustResult {
50 #[inline]
52 #[must_use]
53 pub fn success(self) -> bool {
54 matches!(self, Self::PROCEED | Self::UNSPECIFIED)
55 }
56}
57
58declare_TCFType! {
59 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 pub struct TrustOptions: SecTrustOptionFlags {
71 const ALLOW_EXPIRED = kSecTrustOptionAllowExpired;
73 const LEAF_IS_CA = kSecTrustOptionLeafIsCA;
75 const FETCH_ISSUER_FROM_NET = kSecTrustOptionFetchIssuerFromNet;
77 const ALLOW_EXPIRED_ROOT = kSecTrustOptionAllowExpiredRoot;
79 const REQUIRE_REVOCATION_PER_CERT = kSecTrustOptionRequireRevPerCert;
81 const USE_TRUST_SETTINGS = kSecTrustOptionUseTrustSettings;
83 const IMPLICIT_ANCHORS = kSecTrustOptionImplicitAnchors;
85 }
86}
87
88impl SecTrust {
89 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 #[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 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 #[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 #[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 #[inline]
154 pub fn set_policy(&mut self, policy: &SecPolicy) -> Result<()> {
155 unsafe { cvt(SecTrustSetPolicies(self.0, policy.as_CFTypeRef())) }
156 }
157
158 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[inline(always)]
270 #[must_use]
271 pub fn certificate_count(&self) -> CFIndex {
272 unsafe { SecTrustGetCertificateCount(self.0) }
273 }
274
275 #[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}