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