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