Skip to main content

security/
cms.rs

1use std::time::{Duration, SystemTime, UNIX_EPOCH};
2
3use bitflags::bitflags;
4use serde_json::Value;
5
6use crate::bridge;
7use crate::certificate::Certificate;
8use crate::error::{Result, SecurityError};
9use crate::identity::Identity;
10use crate::policy::Policy;
11
12bitflags! {
13    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14    /// Mirrors signed-attribute bits used by the CMS encoder APIs.
15    pub struct CmsSignedAttributes: u32 {
16        /// Mirrors a CMS signed-attribute bit.
17        const NONE = 0;
18        /// Mirrors a CMS signed-attribute bit.
19        const SMIME_CAPABILITIES = 0x0001;
20        /// Mirrors a CMS signed-attribute bit.
21        const SMIME_ENCRYPTION_KEY_PREFS = 0x0002;
22        /// Mirrors a CMS signed-attribute bit.
23        const SMIME_MS_ENCRYPTION_KEY_PREFS = 0x0004;
24        /// Mirrors a CMS signed-attribute bit.
25        const SIGNING_TIME = 0x0008;
26        /// Mirrors a CMS signed-attribute bit.
27        const APPLE_CODESIGNING_HASH_AGILITY = 0x0010;
28        /// Mirrors a CMS signed-attribute bit.
29        const APPLE_CODESIGNING_HASH_AGILITY_V2 = 0x0020;
30        /// Mirrors a CMS signed-attribute bit.
31        const APPLE_EXPIRATION_TIME = 0x0040;
32    }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36#[repr(u32)]
37/// Mirrors certificate-chain modes used by `CMSEncoder`.
38pub enum CmsCertificateChainMode {
39    /// Mirrors a CMS certificate-chain mode constant.
40    None = 0,
41    /// Mirrors a CMS certificate-chain mode constant.
42    SignerOnly = 1,
43    /// Mirrors a CMS certificate-chain mode constant.
44    Chain = 2,
45    /// Mirrors a CMS certificate-chain mode constant.
46    ChainWithRoot = 3,
47    /// Mirrors a CMS certificate-chain mode constant.
48    ChainWithRootOrFail = 4,
49}
50
51impl CmsCertificateChainMode {
52    fn from_raw(raw: u32) -> Result<Self> {
53        match raw {
54            0 => Ok(Self::None),
55            1 => Ok(Self::SignerOnly),
56            2 => Ok(Self::Chain),
57            3 => Ok(Self::ChainWithRoot),
58            4 => Ok(Self::ChainWithRootOrFail),
59            _ => Err(SecurityError::InvalidArgument(format!(
60                "unexpected CMS certificate chain mode: {raw}"
61            ))),
62        }
63    }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67/// Mirrors digest selectors used by `CMSEncoderSetSignerAlgorithm`.
68pub enum CmsDigestAlgorithm {
69    /// Mirrors a CMS digest selector.
70    Sha1,
71    /// Mirrors a CMS digest selector.
72    Sha256,
73}
74
75impl CmsDigestAlgorithm {
76    const fn as_bridge_name(self) -> &'static str {
77        match self {
78            Self::Sha1 => "sha1",
79            Self::Sha256 => "sha256",
80        }
81    }
82}
83
84#[derive(Debug)]
85/// Wraps Security.framework CMS decoder state.
86pub struct CmsDecoder {
87    handle: bridge::Handle,
88}
89
90impl CmsDecoder {
91    fn from_handle(handle: bridge::Handle) -> Self {
92        Self { handle }
93    }
94
95    /// Wraps the corresponding Security.framework CMS decoder operation.
96    pub fn type_id() -> usize {
97        unsafe { bridge::security_cms_decoder_get_type_id() }
98    }
99
100    /// Wraps the corresponding Security.framework CMS decoder operation.
101    pub fn update_message(&mut self, data: &[u8]) -> Result<()> {
102        let mut error = std::ptr::null_mut();
103        let status = unsafe {
104            bridge::security_cms_decoder_update_message(
105                self.handle.as_ptr(),
106                data.as_ptr().cast(),
107                bridge::len_to_isize(data.len())?,
108                &mut error,
109            )
110        };
111        bridge::status_result("security_cms_decoder_update_message", status, error)
112    }
113
114    /// Wraps the corresponding Security.framework CMS decoder operation.
115    pub fn finalize_message(&mut self) -> Result<()> {
116        let mut error = std::ptr::null_mut();
117        let status = unsafe {
118            bridge::security_cms_decoder_finalize_message(self.handle.as_ptr(), &mut error)
119        };
120        bridge::status_result("security_cms_decoder_finalize_message", status, error)
121    }
122
123    /// Wraps the corresponding Security.framework CMS decoder operation.
124    pub fn set_detached_content(&mut self, data: &[u8]) -> Result<()> {
125        let mut error = std::ptr::null_mut();
126        let status = unsafe {
127            bridge::security_cms_decoder_set_detached_content(
128                self.handle.as_ptr(),
129                data.as_ptr().cast(),
130                bridge::len_to_isize(data.len())?,
131                &mut error,
132            )
133        };
134        bridge::status_result("security_cms_decoder_set_detached_content", status, error)
135    }
136
137    /// Wraps the corresponding Security.framework CMS decoder operation.
138    pub fn detached_content(&self) -> Result<Option<Vec<u8>>> {
139        let mut status = 0;
140        let mut error = std::ptr::null_mut();
141        let raw = unsafe {
142            bridge::security_cms_decoder_copy_detached_content(
143                self.handle.as_ptr(),
144                &mut status,
145                &mut error,
146            )
147        };
148        if status != 0 {
149            return Err(bridge::status_error(
150                "security_cms_decoder_copy_detached_content",
151                status,
152                error,
153            )?);
154        }
155        bridge::optional_data(raw)
156    }
157
158    /// Wraps the corresponding Security.framework CMS decoder operation.
159    pub fn num_signers(&self) -> Result<usize> {
160        let mut status = 0;
161        let mut error = std::ptr::null_mut();
162        let count = unsafe {
163            bridge::security_cms_decoder_get_num_signers(
164                self.handle.as_ptr(),
165                &mut status,
166                &mut error,
167            )
168        };
169        if status != 0 {
170            return Err(bridge::status_error(
171                "security_cms_decoder_get_num_signers",
172                status,
173                error,
174            )?);
175        }
176        usize::try_from(count).map_err(|_| {
177            SecurityError::Serialization("negative signer count from bridge".to_owned())
178        })
179    }
180
181    /// Wraps the corresponding Security.framework CMS decoder operation.
182    pub fn signer_status(
183        &self,
184        signer_index: usize,
185        policy: Option<&Policy>,
186        evaluate_sec_trust: bool,
187    ) -> Result<Value> {
188        let mut status = 0;
189        let mut error = std::ptr::null_mut();
190        let raw = unsafe {
191            bridge::security_cms_decoder_copy_signer_status(
192                self.handle.as_ptr(),
193                bridge::len_to_isize(signer_index)?,
194                policy.map_or(std::ptr::null_mut(), |value| value.handle().as_ptr()),
195                evaluate_sec_trust,
196                &mut status,
197                &mut error,
198            )
199        };
200        bridge::required_json(
201            "security_cms_decoder_copy_signer_status",
202            raw,
203            status,
204            error,
205        )
206    }
207
208    /// Wraps the corresponding Security.framework CMS decoder operation.
209    pub fn signer_email_address(&self, signer_index: usize) -> Result<Option<String>> {
210        let mut status = 0;
211        let mut error = std::ptr::null_mut();
212        let raw = unsafe {
213            bridge::security_cms_decoder_copy_signer_email_address(
214                self.handle.as_ptr(),
215                bridge::len_to_isize(signer_index)?,
216                &mut status,
217                &mut error,
218            )
219        };
220        if raw.is_null() && status == 0 {
221            Ok(None)
222        } else {
223            bridge::required_string(
224                "security_cms_decoder_copy_signer_email_address",
225                raw,
226                status,
227                error,
228            )
229            .map(Some)
230        }
231    }
232
233    /// Wraps the corresponding Security.framework CMS decoder operation.
234    pub fn signer_certificate(&self, signer_index: usize) -> Result<Certificate> {
235        let mut status = 0;
236        let mut error = std::ptr::null_mut();
237        let raw = unsafe {
238            bridge::security_cms_decoder_copy_signer_cert(
239                self.handle.as_ptr(),
240                bridge::len_to_isize(signer_index)?,
241                &mut status,
242                &mut error,
243            )
244        };
245        bridge::required_handle("security_cms_decoder_copy_signer_cert", raw, status, error)
246            .map(Certificate::from_handle)
247    }
248
249    /// Wraps the corresponding Security.framework CMS decoder operation.
250    pub fn is_content_encrypted(&self) -> Result<bool> {
251        let mut status = 0;
252        let mut error = std::ptr::null_mut();
253        let encrypted = unsafe {
254            bridge::security_cms_decoder_is_content_encrypted(
255                self.handle.as_ptr(),
256                &mut status,
257                &mut error,
258            )
259        };
260        if status != 0 {
261            return Err(bridge::status_error(
262                "security_cms_decoder_is_content_encrypted",
263                status,
264                error,
265            )?);
266        }
267        Ok(encrypted)
268    }
269
270    /// Wraps the corresponding Security.framework CMS decoder operation.
271    pub fn encapsulated_content_type(&self) -> Result<Option<Vec<u8>>> {
272        let mut status = 0;
273        let mut error = std::ptr::null_mut();
274        let raw = unsafe {
275            bridge::security_cms_decoder_copy_encapsulated_content_type(
276                self.handle.as_ptr(),
277                &mut status,
278                &mut error,
279            )
280        };
281        if status != 0 {
282            return Err(bridge::status_error(
283                "security_cms_decoder_copy_encapsulated_content_type",
284                status,
285                error,
286            )?);
287        }
288        bridge::optional_data(raw)
289    }
290
291    /// Wraps the corresponding Security.framework CMS decoder operation.
292    pub fn content(&self) -> Result<Option<Vec<u8>>> {
293        let mut status = 0;
294        let mut error = std::ptr::null_mut();
295        let raw = unsafe {
296            bridge::security_cms_decoder_copy_content(self.handle.as_ptr(), &mut status, &mut error)
297        };
298        if status != 0 {
299            return Err(bridge::status_error(
300                "security_cms_decoder_copy_content",
301                status,
302                error,
303            )?);
304        }
305        bridge::optional_data(raw)
306    }
307
308    /// Wraps the corresponding Security.framework CMS decoder operation.
309    pub fn signer_signing_time(&self, signer_index: usize) -> Result<Option<SystemTime>> {
310        decode_optional_cms_date("security_cms_decoder_copy_signer_signing_time", unsafe {
311            let mut status = 0;
312            let mut error = std::ptr::null_mut();
313            let raw = bridge::security_cms_decoder_copy_signer_signing_time(
314                self.handle.as_ptr(),
315                bridge::len_to_isize(signer_index)?,
316                &mut status,
317                &mut error,
318            );
319            (raw, status, error)
320        })
321    }
322
323    /// Wraps the corresponding Security.framework CMS decoder operation.
324    pub fn signer_timestamp(&self, signer_index: usize) -> Result<Option<SystemTime>> {
325        decode_optional_cms_date("security_cms_decoder_copy_signer_timestamp", unsafe {
326            let mut status = 0;
327            let mut error = std::ptr::null_mut();
328            let raw = bridge::security_cms_decoder_copy_signer_timestamp(
329                self.handle.as_ptr(),
330                bridge::len_to_isize(signer_index)?,
331                &mut status,
332                &mut error,
333            );
334            (raw, status, error)
335        })
336    }
337
338    /// Wraps the corresponding Security.framework CMS decoder operation.
339    pub fn signer_timestamp_with_policy(
340        &self,
341        policy: Option<&Policy>,
342        signer_index: usize,
343    ) -> Result<Option<SystemTime>> {
344        decode_optional_cms_date(
345            "security_cms_decoder_copy_signer_timestamp_with_policy",
346            unsafe {
347                let mut status = 0;
348                let mut error = std::ptr::null_mut();
349                let raw = bridge::security_cms_decoder_copy_signer_timestamp_with_policy(
350                    self.handle.as_ptr(),
351                    policy.map_or(std::ptr::null_mut(), |value| value.handle().as_ptr()),
352                    bridge::len_to_isize(signer_index)?,
353                    &mut status,
354                    &mut error,
355                );
356                (raw, status, error)
357            },
358        )
359    }
360
361    /// Wraps the corresponding Security.framework CMS decoder operation.
362    pub fn signer_timestamp_certificates(&self, signer_index: usize) -> Result<Value> {
363        let mut status = 0;
364        let mut error = std::ptr::null_mut();
365        let raw = unsafe {
366            bridge::security_cms_decoder_copy_signer_timestamp_certificates(
367                self.handle.as_ptr(),
368                bridge::len_to_isize(signer_index)?,
369                &mut status,
370                &mut error,
371            )
372        };
373        bridge::required_json(
374            "security_cms_decoder_copy_signer_timestamp_certificates",
375            raw,
376            status,
377            error,
378        )
379    }
380
381    /// Wraps the corresponding Security.framework CMS decoder operation.
382    pub fn all_certificates(&self) -> Result<Vec<Certificate>> {
383        let mut status = 0;
384        let mut error = std::ptr::null_mut();
385        let raw = unsafe {
386            bridge::security_cms_decode_all_certificates(
387                std::ptr::null(),
388                0,
389                &mut status,
390                &mut error,
391            )
392        };
393        let _ = raw;
394        Err(SecurityError::InvalidArgument(
395            "CmsDecoder::all_certificates is not available; use Cms::decode_all_certificates"
396                .to_owned(),
397        ))
398    }
399}
400
401#[derive(Debug)]
402/// Wraps Security.framework CMS encoder state.
403pub struct CmsEncoder {
404    handle: bridge::Handle,
405}
406
407impl CmsEncoder {
408    fn from_handle(handle: bridge::Handle) -> Self {
409        Self { handle }
410    }
411
412    /// Wraps the corresponding Security.framework CMS encoder operation.
413    pub fn type_id() -> usize {
414        unsafe { bridge::security_cms_encoder_get_type_id() }
415    }
416
417    /// Wraps the corresponding Security.framework CMS encoder operation.
418    pub fn set_signer_algorithm(&mut self, algorithm: CmsDigestAlgorithm) -> Result<()> {
419        let algorithm = bridge::cstring(algorithm.as_bridge_name())?;
420        let mut error = std::ptr::null_mut();
421        let status = unsafe {
422            bridge::security_cms_encoder_set_signer_algorithm(
423                self.handle.as_ptr(),
424                algorithm.as_ptr(),
425                &mut error,
426            )
427        };
428        bridge::status_result("security_cms_encoder_set_signer_algorithm", status, error)
429    }
430
431    /// Wraps the corresponding Security.framework CMS encoder operation.
432    pub fn add_signers(&mut self, signers: &[Identity]) -> Result<()> {
433        let handles = signers.iter().map(Identity::handle).collect::<Vec<_>>();
434        let pointers = bridge::handle_pointer_array(&handles);
435        let mut error = std::ptr::null_mut();
436        let status = unsafe {
437            bridge::security_cms_encoder_add_signers(
438                self.handle.as_ptr(),
439                pointers.as_ptr(),
440                bridge::len_to_isize(pointers.len())?,
441                &mut error,
442            )
443        };
444        bridge::status_result("security_cms_encoder_add_signers", status, error)
445    }
446
447    /// Wraps the corresponding Security.framework CMS encoder operation.
448    pub fn signers(&self) -> Result<Value> {
449        let mut status = 0;
450        let mut error = std::ptr::null_mut();
451        let raw = unsafe {
452            bridge::security_cms_encoder_copy_signers(self.handle.as_ptr(), &mut status, &mut error)
453        };
454        bridge::required_json("security_cms_encoder_copy_signers", raw, status, error)
455    }
456
457    /// Wraps the corresponding Security.framework CMS encoder operation.
458    pub fn add_recipients(&mut self, recipients: &[Certificate]) -> Result<()> {
459        let handles = recipients
460            .iter()
461            .map(Certificate::handle)
462            .collect::<Vec<_>>();
463        let pointers = bridge::handle_pointer_array(&handles);
464        let mut error = std::ptr::null_mut();
465        let status = unsafe {
466            bridge::security_cms_encoder_add_recipients(
467                self.handle.as_ptr(),
468                pointers.as_ptr(),
469                bridge::len_to_isize(pointers.len())?,
470                &mut error,
471            )
472        };
473        bridge::status_result("security_cms_encoder_add_recipients", status, error)
474    }
475
476    /// Wraps the corresponding Security.framework CMS encoder operation.
477    pub fn recipients(&self) -> Result<Value> {
478        let mut status = 0;
479        let mut error = std::ptr::null_mut();
480        let raw = unsafe {
481            bridge::security_cms_encoder_copy_recipients(
482                self.handle.as_ptr(),
483                &mut status,
484                &mut error,
485            )
486        };
487        bridge::required_json("security_cms_encoder_copy_recipients", raw, status, error)
488    }
489
490    /// Wraps the corresponding Security.framework CMS encoder operation.
491    pub fn set_has_detached_content(&mut self, detached_content: bool) -> Result<()> {
492        let mut error = std::ptr::null_mut();
493        let status = unsafe {
494            bridge::security_cms_encoder_set_has_detached_content(
495                self.handle.as_ptr(),
496                detached_content,
497                &mut error,
498            )
499        };
500        bridge::status_result(
501            "security_cms_encoder_set_has_detached_content",
502            status,
503            error,
504        )
505    }
506
507    /// Wraps the corresponding Security.framework CMS encoder operation.
508    pub fn has_detached_content(&self) -> Result<bool> {
509        let mut status = 0;
510        let mut error = std::ptr::null_mut();
511        let detached = unsafe {
512            bridge::security_cms_encoder_get_has_detached_content(
513                self.handle.as_ptr(),
514                &mut status,
515                &mut error,
516            )
517        };
518        if status != 0 {
519            return Err(bridge::status_error(
520                "security_cms_encoder_get_has_detached_content",
521                status,
522                error,
523            )?);
524        }
525        Ok(detached)
526    }
527
528    /// Wraps the corresponding Security.framework CMS encoder operation.
529    pub fn set_encapsulated_content_type_oid(&mut self, oid: &str) -> Result<()> {
530        let oid = bridge::cstring(oid)?;
531        let mut error = std::ptr::null_mut();
532        let status = unsafe {
533            bridge::security_cms_encoder_set_encapsulated_content_type_oid(
534                self.handle.as_ptr(),
535                oid.as_ptr(),
536                &mut error,
537            )
538        };
539        bridge::status_result(
540            "security_cms_encoder_set_encapsulated_content_type_oid",
541            status,
542            error,
543        )
544    }
545
546    /// Wraps the corresponding Security.framework CMS encoder operation.
547    pub fn encapsulated_content_type(&self) -> Result<Option<Vec<u8>>> {
548        let mut status = 0;
549        let mut error = std::ptr::null_mut();
550        let raw = unsafe {
551            bridge::security_cms_encoder_copy_encapsulated_content_type(
552                self.handle.as_ptr(),
553                &mut status,
554                &mut error,
555            )
556        };
557        if status != 0 {
558            return Err(bridge::status_error(
559                "security_cms_encoder_copy_encapsulated_content_type",
560                status,
561                error,
562            )?);
563        }
564        bridge::optional_data(raw)
565    }
566
567    /// Wraps the corresponding Security.framework CMS encoder operation.
568    pub fn add_supporting_certificates(&mut self, certificates: &[Certificate]) -> Result<()> {
569        let handles = certificates
570            .iter()
571            .map(Certificate::handle)
572            .collect::<Vec<_>>();
573        let pointers = bridge::handle_pointer_array(&handles);
574        let mut error = std::ptr::null_mut();
575        let status = unsafe {
576            bridge::security_cms_encoder_add_supporting_certs(
577                self.handle.as_ptr(),
578                pointers.as_ptr(),
579                bridge::len_to_isize(pointers.len())?,
580                &mut error,
581            )
582        };
583        bridge::status_result("security_cms_encoder_add_supporting_certs", status, error)
584    }
585
586    /// Wraps the corresponding Security.framework CMS encoder operation.
587    pub fn supporting_certificates(&self) -> Result<Value> {
588        let mut status = 0;
589        let mut error = std::ptr::null_mut();
590        let raw = unsafe {
591            bridge::security_cms_encoder_copy_supporting_certs(
592                self.handle.as_ptr(),
593                &mut status,
594                &mut error,
595            )
596        };
597        bridge::required_json(
598            "security_cms_encoder_copy_supporting_certs",
599            raw,
600            status,
601            error,
602        )
603    }
604
605    /// Wraps the corresponding Security.framework CMS encoder operation.
606    pub fn add_signed_attributes(&mut self, signed_attributes: CmsSignedAttributes) -> Result<()> {
607        let mut error = std::ptr::null_mut();
608        let status = unsafe {
609            bridge::security_cms_encoder_add_signed_attributes(
610                self.handle.as_ptr(),
611                signed_attributes.bits(),
612                &mut error,
613            )
614        };
615        bridge::status_result("security_cms_encoder_add_signed_attributes", status, error)
616    }
617
618    /// Wraps the corresponding Security.framework CMS encoder operation.
619    pub fn set_certificate_chain_mode(
620        &mut self,
621        chain_mode: CmsCertificateChainMode,
622    ) -> Result<()> {
623        let mut error = std::ptr::null_mut();
624        let status = unsafe {
625            bridge::security_cms_encoder_set_certificate_chain_mode(
626                self.handle.as_ptr(),
627                chain_mode as u32,
628                &mut error,
629            )
630        };
631        bridge::status_result(
632            "security_cms_encoder_set_certificate_chain_mode",
633            status,
634            error,
635        )
636    }
637
638    /// Wraps the corresponding Security.framework CMS encoder operation.
639    pub fn certificate_chain_mode(&self) -> Result<CmsCertificateChainMode> {
640        let mut status = 0;
641        let mut error = std::ptr::null_mut();
642        let mode = unsafe {
643            bridge::security_cms_encoder_get_certificate_chain_mode(
644                self.handle.as_ptr(),
645                &mut status,
646                &mut error,
647            )
648        };
649        if status != 0 {
650            return Err(bridge::status_error(
651                "security_cms_encoder_get_certificate_chain_mode",
652                status,
653                error,
654            )?);
655        }
656        CmsCertificateChainMode::from_raw(mode)
657    }
658
659    /// Wraps the corresponding Security.framework CMS encoder operation.
660    pub fn update_content(&mut self, data: &[u8]) -> Result<()> {
661        let mut error = std::ptr::null_mut();
662        let status = unsafe {
663            bridge::security_cms_encoder_update_content(
664                self.handle.as_ptr(),
665                data.as_ptr().cast(),
666                bridge::len_to_isize(data.len())?,
667                &mut error,
668            )
669        };
670        bridge::status_result("security_cms_encoder_update_content", status, error)
671    }
672
673    /// Wraps the corresponding Security.framework CMS encoder operation.
674    pub fn encoded_content(&self) -> Result<Vec<u8>> {
675        let mut status = 0;
676        let mut error = std::ptr::null_mut();
677        let raw = unsafe {
678            bridge::security_cms_encoder_copy_encoded_content(
679                self.handle.as_ptr(),
680                &mut status,
681                &mut error,
682            )
683        };
684        bridge::required_data(
685            "security_cms_encoder_copy_encoded_content",
686            raw,
687            status,
688            error,
689        )
690    }
691
692    /// Wraps the corresponding Security.framework CMS encoder operation.
693    pub fn signer_timestamp(&self, signer_index: usize) -> Result<Option<SystemTime>> {
694        decode_optional_cms_date("security_cms_encoder_copy_signer_timestamp", unsafe {
695            let mut status = 0;
696            let mut error = std::ptr::null_mut();
697            let raw = bridge::security_cms_encoder_copy_signer_timestamp(
698                self.handle.as_ptr(),
699                bridge::len_to_isize(signer_index)?,
700                &mut status,
701                &mut error,
702            );
703            (raw, status, error)
704        })
705    }
706
707    /// Wraps the corresponding Security.framework CMS encoder operation.
708    pub fn signer_timestamp_with_policy(
709        &self,
710        policy: Option<&Policy>,
711        signer_index: usize,
712    ) -> Result<Option<SystemTime>> {
713        decode_optional_cms_date(
714            "security_cms_encoder_copy_signer_timestamp_with_policy",
715            unsafe {
716                let mut status = 0;
717                let mut error = std::ptr::null_mut();
718                let raw = bridge::security_cms_encoder_copy_signer_timestamp_with_policy(
719                    self.handle.as_ptr(),
720                    policy.map_or(std::ptr::null_mut(), |value| value.handle().as_ptr()),
721                    bridge::len_to_isize(signer_index)?,
722                    &mut status,
723                    &mut error,
724                );
725                (raw, status, error)
726            },
727        )
728    }
729}
730
731/// Wraps convenience helpers around Security.framework CMS APIs.
732pub struct Cms;
733
734impl Cms {
735    /// Wraps the corresponding Security.framework CMS helper.
736    pub fn encoder() -> Result<CmsEncoder> {
737        let mut status = 0;
738        let mut error = std::ptr::null_mut();
739        let raw = unsafe { bridge::security_cms_encoder_create(&mut status, &mut error) };
740        bridge::required_handle("security_cms_encoder_create", raw, status, error)
741            .map(CmsEncoder::from_handle)
742    }
743
744    /// Wraps the corresponding Security.framework CMS helper.
745    pub fn decoder() -> Result<CmsDecoder> {
746        let mut status = 0;
747        let mut error = std::ptr::null_mut();
748        let raw = unsafe { bridge::security_cms_decoder_create(&mut status, &mut error) };
749        bridge::required_handle("security_cms_decoder_create", raw, status, error)
750            .map(CmsDecoder::from_handle)
751    }
752
753    /// Wraps the corresponding Security.framework CMS helper.
754    pub fn encode_supporting_certificates(certificates: &[Certificate]) -> Result<Vec<u8>> {
755        let mut encoder = Self::encoder()?;
756        encoder.add_supporting_certificates(certificates)?;
757        encoder.encoded_content()
758    }
759
760    /// Wraps the corresponding Security.framework CMS helper.
761    pub fn decode_all_certificates(data: &[u8]) -> Result<Vec<Certificate>> {
762        let mut status = 0;
763        let mut error = std::ptr::null_mut();
764        let raw = unsafe {
765            bridge::security_cms_decode_all_certificates(
766                data.as_ptr().cast(),
767                bridge::len_to_isize(data.len())?,
768                &mut status,
769                &mut error,
770            )
771        };
772        let array_handle =
773            bridge::required_handle("security_cms_decode_all_certificates", raw, status, error)?;
774        let count = usize::try_from(unsafe {
775            bridge::security_certificate_array_get_count(array_handle.as_ptr())
776        })
777        .unwrap_or_default();
778        let mut certificates = Vec::with_capacity(count);
779        for index in 0..count {
780            let mut status = 0;
781            let mut error = std::ptr::null_mut();
782            let raw = unsafe {
783                bridge::security_certificate_array_copy_item(
784                    array_handle.as_ptr(),
785                    bridge::len_to_isize(index)?,
786                    &mut status,
787                    &mut error,
788                )
789            };
790            let handle = bridge::required_handle(
791                "security_certificate_array_copy_item",
792                raw,
793                status,
794                error,
795            )?;
796            certificates.push(Certificate::from_handle(handle));
797        }
798        Ok(certificates)
799    }
800
801    /// Wraps the corresponding Security.framework CMS helper.
802    pub fn encode_content(
803        signers: &[Identity],
804        recipients: &[Certificate],
805        encapsulated_content_type_oid: Option<&str>,
806        detached_content: bool,
807        signed_attributes: CmsSignedAttributes,
808        content: &[u8],
809    ) -> Result<Vec<u8>> {
810        let signer_handles = signers.iter().map(Identity::handle).collect::<Vec<_>>();
811        let signer_pointers = bridge::handle_pointer_array(&signer_handles);
812        let recipient_handles = recipients
813            .iter()
814            .map(Certificate::handle)
815            .collect::<Vec<_>>();
816        let recipient_pointers = bridge::handle_pointer_array(&recipient_handles);
817        let encapsulated_content_type_oid = encapsulated_content_type_oid
818            .map(bridge::cstring)
819            .transpose()?;
820        let mut status = 0;
821        let mut error = std::ptr::null_mut();
822        let raw = unsafe {
823            bridge::security_cms_encode_content(
824                signer_pointers.as_ptr(),
825                bridge::len_to_isize(signer_pointers.len())?,
826                recipient_pointers.as_ptr(),
827                bridge::len_to_isize(recipient_pointers.len())?,
828                encapsulated_content_type_oid
829                    .as_ref()
830                    .map_or(std::ptr::null(), |value| value.as_ptr()),
831                detached_content,
832                signed_attributes.bits(),
833                content.as_ptr().cast(),
834                bridge::len_to_isize(content.len())?,
835                &mut status,
836                &mut error,
837            )
838        };
839        bridge::required_data("security_cms_encode_content", raw, status, error)
840    }
841}
842
843fn decode_optional_cms_date(
844    operation: &'static str,
845    result: (*mut std::ffi::c_void, i32, *mut std::ffi::c_void),
846) -> Result<Option<SystemTime>> {
847    let (raw, status, error) = result;
848    if status != 0 {
849        return Err(bridge::status_error(operation, status, error)?);
850    }
851    bridge::optional_json::<Value>(raw)?.map_or(Ok(None), |value| {
852        decode_cms_date(value, operation).map(Some)
853    })
854}
855
856fn decode_cms_date(value: Value, operation: &'static str) -> Result<SystemTime> {
857    let unix =
858        value
859            .get("unix")
860            .and_then(Value::as_f64)
861            .ok_or_else(|| SecurityError::UnexpectedType {
862                operation,
863                expected: "date JSON object",
864            })?;
865    let duration = Duration::from_secs_f64(unix.abs());
866    if unix >= 0.0 {
867        Ok(UNIX_EPOCH + duration)
868    } else {
869        UNIX_EPOCH.checked_sub(duration).ok_or_else(|| {
870            SecurityError::InvalidArgument("CMS date preceded UNIX_EPOCH by too much".to_owned())
871        })
872    }
873}