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