1use bitflags::bitflags;
2use foreign_types::{ForeignType, ForeignTypeRef};
3use libc::c_int;
4use std::mem;
5use std::ptr;
6
7use crate::bio::{MemBio, MemBioSlice};
8use crate::error::ErrorStack;
9use crate::pkey::{HasPrivate, PKeyRef};
10use crate::stack::{Stack, StackRef};
11use crate::symm::Cipher;
12use crate::x509::store::X509StoreRef;
13use crate::x509::{X509Ref, X509};
14use crate::{cvt, cvt_p};
15use openssl_macros::corresponds;
16
17foreign_type_and_impl_send_sync! {
18    type CType = ffi::PKCS7;
19    fn drop = ffi::PKCS7_free;
20
21    /// A PKCS#7 structure.
22    ///
23    /// Contains signed and/or encrypted data.
24    pub struct Pkcs7;
25
26    /// Reference to `Pkcs7`
27    pub struct Pkcs7Ref;
28}
29
30bitflags! {
31    pub struct Pkcs7Flags: c_int {
32        const TEXT = ffi::PKCS7_TEXT;
33        const NOCERTS = ffi::PKCS7_NOCERTS;
34        const NOSIGS = ffi::PKCS7_NOSIGS;
35        const NOCHAIN = ffi::PKCS7_NOCHAIN;
36        const NOINTERN = ffi::PKCS7_NOINTERN;
37        const NOVERIFY = ffi::PKCS7_NOVERIFY;
38        const DETACHED = ffi::PKCS7_DETACHED;
39        const BINARY = ffi::PKCS7_BINARY;
40        const NOATTR = ffi::PKCS7_NOATTR;
41        const NOSMIMECAP = ffi::PKCS7_NOSMIMECAP;
42        const NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE;
43        const CRLFEOL = ffi::PKCS7_CRLFEOL;
44        const STREAM = ffi::PKCS7_STREAM;
45        const NOCRL = ffi::PKCS7_NOCRL;
46        const PARTIAL = ffi::PKCS7_PARTIAL;
47        const REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST;
48        #[cfg(not(any(ossl101, ossl102, libressl)))]
49        const NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT;
50    }
51}
52
53impl Pkcs7 {
54    from_pem! {
55        /// Deserializes a PEM-encoded PKCS#7 signature
56        ///
57        /// The input should have a header of `-----BEGIN PKCS7-----`.
58        #[corresponds(PEM_read_bio_PKCS7)]
59        from_pem,
60        Pkcs7,
61        ffi::PEM_read_bio_PKCS7
62    }
63
64    from_der! {
65        /// Deserializes a DER-encoded PKCS#7 signature
66        #[corresponds(d2i_PKCS7)]
67        from_der,
68        Pkcs7,
69        ffi::d2i_PKCS7
70    }
71
72    /// Parses a message in S/MIME format.
73    ///
74    /// Returns the loaded signature, along with the cleartext message (if
75    /// available).
76    #[corresponds(SMIME_read_PKCS7)]
77    pub fn from_smime(input: &[u8]) -> Result<(Pkcs7, Option<Vec<u8>>), ErrorStack> {
78        ffi::init();
79
80        let input_bio = MemBioSlice::new(input)?;
81        let mut bcont_bio = ptr::null_mut();
82        unsafe {
83            let pkcs7 =
84                cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio)).map(Pkcs7)?;
85            let out = if !bcont_bio.is_null() {
86                let bcont_bio = MemBio::from_ptr(bcont_bio);
87                Some(bcont_bio.get_buf().to_vec())
88            } else {
89                None
90            };
91            Ok((pkcs7, out))
92        }
93    }
94
95    /// Creates and returns a PKCS#7 `envelopedData` structure.
96    ///
97    /// `certs` is a list of recipient certificates. `input` is the content to be
98    /// encrypted. `cipher` is the symmetric cipher to use. `flags` is an optional
99    /// set of flags.
100    #[corresponds(PKCS7_encrypt)]
101    pub fn encrypt(
102        certs: &StackRef<X509>,
103        input: &[u8],
104        cipher: Cipher,
105        flags: Pkcs7Flags,
106    ) -> Result<Pkcs7, ErrorStack> {
107        let input_bio = MemBioSlice::new(input)?;
108
109        unsafe {
110            cvt_p(ffi::PKCS7_encrypt(
111                certs.as_ptr(),
112                input_bio.as_ptr(),
113                cipher.as_ptr(),
114                flags.bits,
115            ))
116            .map(Pkcs7)
117        }
118    }
119
120    /// Creates and returns a PKCS#7 `signedData` structure.
121    ///
122    /// `signcert` is the certificate to sign with, `pkey` is the corresponding
123    /// private key. `certs` is an optional additional set of certificates to
124    /// include in the PKCS#7 structure (for example any intermediate CAs in the
125    /// chain).
126    #[corresponds(PKCS7_sign)]
127    pub fn sign<PT>(
128        signcert: &X509Ref,
129        pkey: &PKeyRef<PT>,
130        certs: &StackRef<X509>,
131        input: &[u8],
132        flags: Pkcs7Flags,
133    ) -> Result<Pkcs7, ErrorStack>
134    where
135        PT: HasPrivate,
136    {
137        let input_bio = MemBioSlice::new(input)?;
138        unsafe {
139            cvt_p(ffi::PKCS7_sign(
140                signcert.as_ptr(),
141                pkey.as_ptr(),
142                certs.as_ptr(),
143                input_bio.as_ptr(),
144                flags.bits,
145            ))
146            .map(Pkcs7)
147        }
148    }
149}
150
151impl Pkcs7Ref {
152    /// Converts PKCS#7 structure to S/MIME format
153    #[corresponds(SMIME_write_PKCS7)]
154    pub fn to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result<Vec<u8>, ErrorStack> {
155        let input_bio = MemBioSlice::new(input)?;
156        let output = MemBio::new()?;
157        unsafe {
158            cvt(ffi::SMIME_write_PKCS7(
159                output.as_ptr(),
160                self.as_ptr(),
161                input_bio.as_ptr(),
162                flags.bits,
163            ))
164            .map(|_| output.get_buf().to_owned())
165        }
166    }
167
168    to_pem! {
169        /// Serializes the data into a PEM-encoded PKCS#7 structure.
170        ///
171        /// The output will have a header of `-----BEGIN PKCS7-----`.
172        #[corresponds(PEM_write_bio_PKCS7)]
173        to_pem,
174        ffi::PEM_write_bio_PKCS7
175    }
176
177    to_der! {
178        /// Serializes the data into a DER-encoded PKCS#7 structure.
179        #[corresponds(i2d_PKCS7)]
180        to_der,
181        ffi::i2d_PKCS7
182    }
183
184    /// Decrypts data using the provided private key.
185    ///
186    /// `pkey` is the recipient's private key, and `cert` is the recipient's
187    /// certificate.
188    ///
189    /// Returns the decrypted message.
190    #[corresponds(PKCS7_decrypt)]
191    pub fn decrypt<PT>(
192        &self,
193        pkey: &PKeyRef<PT>,
194        cert: &X509Ref,
195        flags: Pkcs7Flags,
196    ) -> Result<Vec<u8>, ErrorStack>
197    where
198        PT: HasPrivate,
199    {
200        let output = MemBio::new()?;
201
202        unsafe {
203            cvt(ffi::PKCS7_decrypt(
204                self.as_ptr(),
205                pkey.as_ptr(),
206                cert.as_ptr(),
207                output.as_ptr(),
208                flags.bits,
209            ))
210            .map(|_| output.get_buf().to_owned())
211        }
212    }
213
214    /// Verifies the PKCS#7 `signedData` structure contained by `&self`.
215    ///
216    /// `certs` is a set of certificates in which to search for the signer's
217    /// certificate. `store` is a trusted certificate store (used for chain
218    /// verification). `indata` is the signed data if the content is not present
219    /// in `&self`. The content is written to `out` if it is not `None`.
220    #[corresponds(PKCS7_verify)]
221    pub fn verify(
222        &self,
223        certs: &StackRef<X509>,
224        store: &X509StoreRef,
225        indata: Option<&[u8]>,
226        out: Option<&mut Vec<u8>>,
227        flags: Pkcs7Flags,
228    ) -> Result<(), ErrorStack> {
229        let out_bio = MemBio::new()?;
230
231        let indata_bio = match indata {
232            Some(data) => Some(MemBioSlice::new(data)?),
233            None => None,
234        };
235        let indata_bio_ptr = indata_bio.as_ref().map_or(ptr::null_mut(), |p| p.as_ptr());
236
237        unsafe {
238            cvt(ffi::PKCS7_verify(
239                self.as_ptr(),
240                certs.as_ptr(),
241                store.as_ptr(),
242                indata_bio_ptr,
243                out_bio.as_ptr(),
244                flags.bits,
245            ))
246            .map(|_| ())?
247        }
248
249        if let Some(data) = out {
250            data.clear();
251            data.extend_from_slice(out_bio.get_buf());
252        }
253
254        Ok(())
255    }
256
257    /// Retrieve the signer's certificates from the PKCS#7 structure without verifying them.
258    #[corresponds(PKCS7_get0_signers)]
259    pub fn signers(
260        &self,
261        certs: &StackRef<X509>,
262        flags: Pkcs7Flags,
263    ) -> Result<Stack<X509>, ErrorStack> {
264        unsafe {
265            let ptr = cvt_p(ffi::PKCS7_get0_signers(
266                self.as_ptr(),
267                certs.as_ptr(),
268                flags.bits,
269            ))?;
270
271            // The returned stack is owned by the caller, but the certs inside are not! Our stack interface can't deal
272            // with that, so instead we just manually bump the refcount of the certs so that the whole stack is properly
273            // owned.
274            let stack = Stack::<X509>::from_ptr(ptr);
275            for cert in &stack {
276                mem::forget(cert.to_owned());
277            }
278
279            Ok(stack)
280        }
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use crate::hash::MessageDigest;
287    use crate::pkcs7::{Pkcs7, Pkcs7Flags};
288    use crate::pkey::PKey;
289    use crate::stack::Stack;
290    use crate::symm::Cipher;
291    use crate::x509::store::X509StoreBuilder;
292    use crate::x509::X509;
293
294    #[test]
295    fn encrypt_decrypt_test() {
296        let cert = include_bytes!("../test/certs.pem");
297        let cert = X509::from_pem(cert).unwrap();
298        let mut certs = Stack::new().unwrap();
299        certs.push(cert.clone()).unwrap();
300        let message: String = String::from("foo");
301        let cipher = Cipher::des_ede3_cbc();
302        let flags = Pkcs7Flags::STREAM;
303        let pkey = include_bytes!("../test/key.pem");
304        let pkey = PKey::private_key_from_pem(pkey).unwrap();
305
306        let pkcs7 =
307            Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed");
308
309        let encrypted = pkcs7
310            .to_smime(message.as_bytes(), flags)
311            .expect("should succeed");
312
313        let (pkcs7_decoded, _) = Pkcs7::from_smime(encrypted.as_slice()).expect("should succeed");
314
315        let decoded = pkcs7_decoded
316            .decrypt(&pkey, &cert, Pkcs7Flags::empty())
317            .expect("should succeed");
318
319        assert_eq!(decoded, message.into_bytes());
320    }
321
322    #[test]
323    fn sign_verify_test_detached() {
324        let cert = include_bytes!("../test/cert.pem");
325        let cert = X509::from_pem(cert).unwrap();
326        let certs = Stack::new().unwrap();
327        let message = "foo";
328        let flags = Pkcs7Flags::STREAM | Pkcs7Flags::DETACHED;
329        let pkey = include_bytes!("../test/key.pem");
330        let pkey = PKey::private_key_from_pem(pkey).unwrap();
331        let mut store_builder = X509StoreBuilder::new().expect("should succeed");
332
333        let root_ca = include_bytes!("../test/root-ca.pem");
334        let root_ca = X509::from_pem(root_ca).unwrap();
335        store_builder.add_cert(root_ca).expect("should succeed");
336
337        let store = store_builder.build();
338
339        let pkcs7 =
340            Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
341
342        let signed = pkcs7
343            .to_smime(message.as_bytes(), flags)
344            .expect("should succeed");
345        println!("{:?}", String::from_utf8(signed.clone()).unwrap());
346        let (pkcs7_decoded, content) =
347            Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
348
349        let mut output = Vec::new();
350        pkcs7_decoded
351            .verify(
352                &certs,
353                &store,
354                Some(message.as_bytes()),
355                Some(&mut output),
356                flags,
357            )
358            .expect("should succeed");
359
360        assert_eq!(output, message.as_bytes());
361        assert_eq!(content.expect("should be non-empty"), message.as_bytes());
362    }
363
364    /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2
365    #[test]
366    #[cfg_attr(all(libressl360, not(libressl361)), ignore)]
367    fn sign_verify_test_normal() {
368        let cert = include_bytes!("../test/cert.pem");
369        let cert = X509::from_pem(cert).unwrap();
370        let certs = Stack::new().unwrap();
371        let message = "foo";
372        let flags = Pkcs7Flags::STREAM;
373        let pkey = include_bytes!("../test/key.pem");
374        let pkey = PKey::private_key_from_pem(pkey).unwrap();
375        let mut store_builder = X509StoreBuilder::new().expect("should succeed");
376
377        let root_ca = include_bytes!("../test/root-ca.pem");
378        let root_ca = X509::from_pem(root_ca).unwrap();
379        store_builder.add_cert(root_ca).expect("should succeed");
380
381        let store = store_builder.build();
382
383        let pkcs7 =
384            Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
385
386        let signed = pkcs7
387            .to_smime(message.as_bytes(), flags)
388            .expect("should succeed");
389
390        let (pkcs7_decoded, content) =
391            Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
392
393        let mut output = Vec::new();
394        pkcs7_decoded
395            .verify(&certs, &store, None, Some(&mut output), flags)
396            .expect("should succeed");
397
398        assert_eq!(output, message.as_bytes());
399        assert!(content.is_none());
400    }
401
402    /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2
403    #[test]
404    #[cfg_attr(all(libressl360, not(libressl361)), ignore)]
405    fn signers() {
406        let cert = include_bytes!("../test/cert.pem");
407        let cert = X509::from_pem(cert).unwrap();
408        let cert_digest = cert.digest(MessageDigest::sha256()).unwrap();
409        let certs = Stack::new().unwrap();
410        let message = "foo";
411        let flags = Pkcs7Flags::STREAM;
412        let pkey = include_bytes!("../test/key.pem");
413        let pkey = PKey::private_key_from_pem(pkey).unwrap();
414        let mut store_builder = X509StoreBuilder::new().expect("should succeed");
415
416        let root_ca = include_bytes!("../test/root-ca.pem");
417        let root_ca = X509::from_pem(root_ca).unwrap();
418        store_builder.add_cert(root_ca).expect("should succeed");
419
420        let pkcs7 =
421            Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
422
423        let signed = pkcs7
424            .to_smime(message.as_bytes(), flags)
425            .expect("should succeed");
426
427        let (pkcs7_decoded, _) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
428
429        let empty_certs = Stack::new().unwrap();
430        let signer_certs = pkcs7_decoded
431            .signers(&empty_certs, flags)
432            .expect("should succeed");
433        assert_eq!(empty_certs.len(), 0);
434        assert_eq!(signer_certs.len(), 1);
435        let signer_digest = signer_certs[0].digest(MessageDigest::sha256()).unwrap();
436        assert_eq!(*cert_digest, *signer_digest);
437    }
438
439    #[test]
440    fn invalid_from_smime() {
441        let input = String::from("Invalid SMIME Message");
442        let result = Pkcs7::from_smime(input.as_bytes());
443
444        assert!(result.is_err());
445    }
446}
447