• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use openssl_macros::corresponds;
16 
17 foreign_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 
30 bitflags! {
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 
53 impl 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)]
from_smime(input: &[u8]) -> Result<(Pkcs7, Option<Vec<u8>>), ErrorStack>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)]
encrypt( certs: &StackRef<X509>, input: &[u8], cipher: Cipher, flags: Pkcs7Flags, ) -> Result<Pkcs7, ErrorStack>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)]
sign<PT>( signcert: &X509Ref, pkey: &PKeyRef<PT>, certs: &StackRef<X509>, input: &[u8], flags: Pkcs7Flags, ) -> Result<Pkcs7, ErrorStack> where PT: HasPrivate,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 
151 impl Pkcs7Ref {
152     /// Converts PKCS#7 structure to S/MIME format
153     #[corresponds(SMIME_write_PKCS7)]
to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result<Vec<u8>, ErrorStack>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)]
decrypt<PT>( &self, pkey: &PKeyRef<PT>, cert: &X509Ref, flags: Pkcs7Flags, ) -> Result<Vec<u8>, ErrorStack> where PT: HasPrivate,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)]
verify( &self, certs: &StackRef<X509>, store: &X509StoreRef, indata: Option<&[u8]>, out: Option<&mut Vec<u8>>, flags: Pkcs7Flags, ) -> Result<(), ErrorStack>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)]
signers( &self, certs: &StackRef<X509>, flags: Pkcs7Flags, ) -> Result<Stack<X509>, ErrorStack>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)]
285 mod 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]
encrypt_decrypt_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]
sign_verify_test_detached()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)]
sign_verify_test_normal()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)]
signers()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]
invalid_from_smime()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