apple_security_framework/
trust.rs1use std::ptr;
4
5#[cfg(target_os = "macos")]
6use core_foundation::array::CFArrayRef;
7#[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
8use core_foundation::data::CFData;
9use core_foundation::{
10 array::CFArray,
11 base::TCFType,
12 date::CFDate,
13 error::{CFError, CFErrorRef},
14};
15use core_foundation_sys::base::{Boolean, CFIndex};
16use security_framework_sys::trust::*;
17
18use crate::{base::Result, certificate::SecCertificate, cvt, key::SecKey, policy::SecPolicy};
19
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
22pub struct TrustResult(SecTrustResultType);
23
24impl TrustResult {
25 pub const INVALID: Self = Self(kSecTrustResultInvalid);
27
28 pub const PROCEED: Self = Self(kSecTrustResultProceed);
30
31 pub const DENY: Self = Self(kSecTrustResultDeny);
33
34 pub const UNSPECIFIED: Self = Self(kSecTrustResultUnspecified);
36
37 pub const RECOVERABLE_TRUST_FAILURE: Self = Self(kSecTrustResultRecoverableTrustFailure);
39
40 pub const FATAL_TRUST_FAILURE: Self = Self(kSecTrustResultFatalTrustFailure);
42
43 pub const OTHER_ERROR: Self = Self(kSecTrustResultOtherError);
45}
46
47impl TrustResult {
48 #[inline]
50 #[must_use]
51 pub fn success(self) -> bool {
52 matches!(self, Self::PROCEED | Self::UNSPECIFIED)
53 }
54}
55
56declare_TCFType! {
57 SecTrust, SecTrustRef
59}
60impl_TCFType!(SecTrust, SecTrustRef, SecTrustGetTypeID);
61
62unsafe impl Sync for SecTrust {}
63unsafe impl Send for SecTrust {}
64
65#[cfg(target_os = "macos")]
66bitflags::bitflags! {
67 #[derive(Debug, Clone)]
69 pub struct TrustOptions: SecTrustOptionFlags {
70 const ALLOW_EXPIRED = kSecTrustOptionAllowExpired;
72 const LEAF_IS_CA = kSecTrustOptionLeafIsCA;
74 const FETCH_ISSUER_FROM_NET = kSecTrustOptionFetchIssuerFromNet;
76 const ALLOW_EXPIRED_ROOT = kSecTrustOptionAllowExpiredRoot;
78 const REQUIRE_REVOCATION_PER_CERT = kSecTrustOptionRequireRevPerCert;
80 const USE_TRUST_SETTINGS = kSecTrustOptionUseTrustSettings;
82 const IMPLICIT_ANCHORS = kSecTrustOptionImplicitAnchors;
84 }
85}
86
87impl SecTrust {
88 pub fn create_with_certificates(
91 certs: &[SecCertificate],
92 policies: &[SecPolicy],
93 ) -> Result<Self> {
94 let cert_array = CFArray::from_CFTypes(certs);
95 let policy_array = CFArray::from_CFTypes(policies);
96 let mut trust = ptr::null_mut();
97 unsafe {
98 cvt(SecTrustCreateWithCertificates(
99 cert_array.as_CFTypeRef(),
100 policy_array.as_CFTypeRef(),
101 &mut trust,
102 ))?;
103 Ok(Self(trust))
104 }
105 }
106
107 #[inline]
110 pub fn set_trust_verify_date(&mut self, date: &CFDate) -> Result<()> {
111 unsafe { cvt(SecTrustSetVerifyDate(self.0, date.as_concrete_TypeRef())) }
112 }
113
114 pub fn set_anchor_certificates(&mut self, certs: &[SecCertificate]) -> Result<()> {
116 let certs = CFArray::from_CFTypes(certs);
117
118 unsafe {
119 cvt(SecTrustSetAnchorCertificates(
120 self.0,
121 certs.as_concrete_TypeRef(),
122 ))
123 }
124 }
125
126 #[cfg(target_os = "macos")]
128 pub fn copy_anchor_certificates() -> Result<Vec<SecCertificate>> {
129 let mut array: CFArrayRef = ptr::null();
130
131 unsafe {
132 cvt(SecTrustCopyAnchorCertificates(&mut array))?;
133 }
134
135 if array.is_null() {
136 return Ok(vec![]);
137 }
138
139 let array = unsafe { CFArray::<SecCertificate>::wrap_under_create_rule(array) };
140 Ok(array.into_iter().map(|c| c.clone()).collect())
141 }
142
143 #[inline]
147 pub fn set_trust_anchor_certificates_only(&mut self, only: bool) -> Result<()> {
148 unsafe { cvt(SecTrustSetAnchorCertificatesOnly(self.0, only as Boolean)) }
149 }
150
151 #[inline]
153 pub fn set_policy(&mut self, policy: &SecPolicy) -> Result<()> {
154 unsafe { cvt(SecTrustSetPolicies(self.0, policy.as_CFTypeRef())) }
155 }
156
157 #[cfg(target_os = "macos")]
159 #[inline]
160 pub fn set_options(&mut self, options: TrustOptions) -> Result<()> {
161 unsafe { cvt(SecTrustSetOptions(self.0, options.bits())) }
162 }
163
164 #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
167 pub fn get_network_fetch_allowed(&mut self) -> Result<bool> {
168 let mut allowed = 0;
169
170 unsafe { cvt(SecTrustGetNetworkFetchAllowed(self.0, &mut allowed))? };
171
172 Ok(allowed != 0)
173 }
174
175 #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
178 #[inline]
179 pub fn set_network_fetch_allowed(&mut self, allowed: bool) -> Result<()> {
180 unsafe { cvt(SecTrustSetNetworkFetchAllowed(self.0, allowed as u8)) }
181 }
182
183 #[cfg(any(feature = "OSX_10_9", target_os = "ios"))]
186 pub fn set_trust_ocsp_response<I: Iterator<Item = impl AsRef<[u8]>>>(
187 &mut self,
188 ocsp_response: I,
189 ) -> Result<()> {
190 let response: Vec<CFData> = ocsp_response
191 .into_iter()
192 .map(|bytes| CFData::from_buffer(bytes.as_ref()))
193 .collect();
194
195 let response = CFArray::from_CFTypes(&response);
196
197 unsafe { cvt(SecTrustSetOCSPResponse(self.0, response.as_CFTypeRef())) }
198 }
199
200 #[cfg(any(feature = "OSX_10_14", target_os = "ios"))]
202 pub fn set_signed_certificate_timestamps<I: Iterator<Item = impl AsRef<[u8]>>>(
203 &mut self,
204 scts: I,
205 ) -> Result<()> {
206 let scts: Vec<CFData> = scts
207 .into_iter()
208 .map(|bytes| CFData::from_buffer(bytes.as_ref()))
209 .collect();
210
211 let scts = CFArray::from_CFTypes(&scts);
212
213 unsafe {
214 cvt(SecTrustSetSignedCertificateTimestamps(
215 self.0,
216 scts.as_concrete_TypeRef(),
217 ))
218 }
219 }
220
221 #[inline]
223 pub fn copy_public_key(&mut self) -> Result<SecKey> {
224 unsafe {
225 Ok(SecKey::wrap_under_create_rule(SecTrustCopyPublicKey(
226 self.0,
227 )))
228 }
229 }
230
231 #[deprecated(note = "use evaluate_with_error")]
233 pub fn evaluate(&self) -> Result<TrustResult> {
234 #[allow(deprecated)]
235 unsafe {
236 let mut result = kSecTrustResultInvalid;
237 cvt(SecTrustEvaluate(self.0, &mut result))?;
238 Ok(TrustResult(result))
239 }
240 }
241
242 pub fn evaluate_with_error(&self) -> Result<(), CFError> {
244 #[cfg(any(feature = "OSX_10_14", target_os = "ios"))]
245 unsafe {
246 let mut error: CFErrorRef = ::std::ptr::null_mut();
247 if !SecTrustEvaluateWithError(self.0, &mut error) {
248 assert!(!error.is_null());
249 let error = CFError::wrap_under_create_rule(error);
250 return Err(error);
251 }
252 Ok(())
253 }
254 #[cfg(not(any(feature = "OSX_10_14", target_os = "ios")))]
255 #[allow(deprecated)]
256 {
257 use security_framework_sys::base::{errSecNotTrusted, errSecTrustSettingDeny};
258
259 let code = match self.evaluate() {
260 Ok(res) if res.success() => return Ok(()),
261 Ok(TrustResult::DENY) => errSecTrustSettingDeny,
262 Ok(_) => errSecNotTrusted,
263 Err(err) => err.code(),
264 };
265 Err(cferror_from_osstatus(code))
266 }
267 }
268
269 #[inline(always)]
273 #[must_use]
274 pub fn certificate_count(&self) -> CFIndex {
275 unsafe { SecTrustGetCertificateCount(self.0) }
276 }
277
278 #[deprecated(note = "deprecated by Apple")]
282 #[must_use]
283 pub fn certificate_at_index(&self, ix: CFIndex) -> Option<SecCertificate> {
284 #[allow(deprecated)]
285 unsafe {
286 if self.certificate_count() <= ix {
287 None
288 } else {
289 let certificate = SecTrustGetCertificateAtIndex(self.0, ix);
290 Some(SecCertificate::wrap_under_get_rule(certificate.cast()))
291 }
292 }
293 }
294}
295
296#[cfg(not(any(feature = "OSX_10_14", target_os = "ios")))]
297extern "C" {
298 fn CFErrorCreate(
299 allocator: core_foundation_sys::base::CFAllocatorRef,
300 domain: core_foundation_sys::string::CFStringRef,
301 code: CFIndex,
302 userInfo: core_foundation_sys::dictionary::CFDictionaryRef,
303 ) -> CFErrorRef;
304}
305
306#[cfg(not(any(feature = "OSX_10_14", target_os = "ios")))]
307fn cferror_from_osstatus(code: core_foundation_sys::base::OSStatus) -> CFError {
308 unsafe {
309 let error = CFErrorCreate(
310 ptr::null_mut(),
311 core_foundation_sys::error::kCFErrorDomainOSStatus,
312 code as _,
313 ptr::null_mut(),
314 );
315 assert!(!error.is_null());
316 CFError::wrap_under_create_rule(error)
317 }
318}
319
320#[cfg(test)]
321mod test {
322 use crate::{
323 policy::SecPolicy, secure_transport::SslProtocolSide, test::certificate, trust::SecTrust,
324 };
325
326 #[test]
327 #[allow(deprecated)]
328 fn create_with_certificates() {
329 let cert = certificate();
330 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
331 let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
332 assert!(!trust.evaluate().unwrap().success())
333 }
334
335 #[test]
336 fn create_with_certificates_new() {
337 let cert = certificate();
338 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
339 let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
340 assert!(trust.evaluate_with_error().is_err());
341 }
342
343 #[test]
344 #[allow(deprecated)]
345 fn certificate_count_and_at_index() {
346 let cert = certificate();
347 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
348 let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
349 trust.evaluate().unwrap();
350
351 let count = trust.certificate_count();
352 assert_eq!(count, 1);
353
354 let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
355 assert_eq!(cert_bytes, certificate().to_der());
356 }
357
358 #[test]
359 #[allow(deprecated)]
360 fn certificate_count_and_at_index_new() {
361 let cert = certificate();
362 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
363 let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
364 assert!(trust.evaluate_with_error().is_err());
365
366 let count = trust.certificate_count();
367 assert_eq!(count, 1);
368
369 let cert_bytes = trust.certificate_at_index(0).unwrap().to_der();
370 assert_eq!(cert_bytes, certificate().to_der());
371 }
372
373 #[test]
374 #[allow(deprecated)]
375 fn certificate_at_index_out_of_bounds() {
376 let cert = certificate();
377 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
378
379 let trust =
380 SecTrust::create_with_certificates(&[cert.clone()], &[ssl_policy.clone()]).unwrap();
381 trust.evaluate().unwrap();
382 assert!(trust.certificate_at_index(1).is_none());
383
384 let trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
385 assert!(trust.evaluate_with_error().is_err());
386 assert!(trust.certificate_at_index(1).is_none());
387 }
388
389 #[test]
390 #[allow(deprecated)]
391 fn set_policy() {
392 let cert = certificate();
393 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
394 let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
395 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
396 trust.set_policy(&ssl_policy).unwrap();
397 assert!(!trust.evaluate().unwrap().success())
398 }
399
400 #[test]
401 fn set_policy_new() {
402 let cert = certificate();
403 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io.bogus"));
404 let mut trust = SecTrust::create_with_certificates(&[cert], &[ssl_policy]).unwrap();
405 let ssl_policy = SecPolicy::create_ssl(SslProtocolSide::CLIENT, Some("certifi.io"));
406 trust.set_policy(&ssl_policy).unwrap();
407 assert!(trust.evaluate_with_error().is_err());
408 }
409}