1 //! SMIME implementation using CMS
3 //! CMS (PKCS#7) is an encyption standard. It allows signing and ecrypting data using
4 //! X.509 certificates. The OpenSSL implementation of CMS is used in email encryption
5 //! generated from a `Vec` of bytes. This `Vec` follows the smime protocol standards.
6 //! Data accepted by this module will be smime type `enveloped-data`.
9 use foreign_types
::{ForeignType, ForeignTypeRef}
;
12 use bio
::{MemBio, MemBioSlice}
;
13 use error
::ErrorStack
;
15 use pkey
::{HasPrivate, PKeyRef}
;
18 use x509
::{X509Ref, X509}
;
22 pub struct CMSOptions
: c_uint
{
23 const TEXT
= ffi
::CMS_TEXT
;
24 const CMS_NOCERTS
= ffi
::CMS_NOCERTS
;
25 const NO_CONTENT_VERIFY
= ffi
::CMS_NO_CONTENT_VERIFY
;
26 const NO_ATTR_VERIFY
= ffi
::CMS_NO_ATTR_VERIFY
;
27 const NOSIGS
= ffi
::CMS_NOSIGS
;
28 const NOINTERN
= ffi
::CMS_NOINTERN
;
29 const NO_SIGNER_CERT_VERIFY
= ffi
::CMS_NO_SIGNER_CERT_VERIFY
;
30 const NOVERIFY
= ffi
::CMS_NOVERIFY
;
31 const DETACHED
= ffi
::CMS_DETACHED
;
32 const BINARY
= ffi
::CMS_BINARY
;
33 const NOATTR
= ffi
::CMS_NOATTR
;
34 const NOSMIMECAP
= ffi
::CMS_NOSMIMECAP
;
35 const NOOLDMIMETYPE
= ffi
::CMS_NOOLDMIMETYPE
;
36 const CRLFEOL
= ffi
::CMS_CRLFEOL
;
37 const STREAM
= ffi
::CMS_STREAM
;
38 const NOCRL
= ffi
::CMS_NOCRL
;
39 const PARTIAL
= ffi
::CMS_PARTIAL
;
40 const REUSE_DIGEST
= ffi
::CMS_REUSE_DIGEST
;
41 const USE_KEYID
= ffi
::CMS_USE_KEYID
;
42 const DEBUG_DECRYPT
= ffi
::CMS_DEBUG_DECRYPT
;
43 #[cfg(all(not(libressl), not(ossl101)))]
44 const KEY_PARAM
= ffi
::CMS_KEY_PARAM
;
45 #[cfg(all(not(libressl), not(ossl101), not(ossl102)))]
46 const ASCIICRLF
= ffi
::CMS_ASCIICRLF
;
50 foreign_type_and_impl_send_sync
! {
51 type CType
= ffi
::CMS_ContentInfo
;
52 fn drop
= ffi
::CMS_ContentInfo_free
;
54 /// High level CMS wrapper
56 /// CMS supports nesting various types of data, including signatures, certificates,
57 /// encrypted data, smime messages (encrypted email), and data digest. The ContentInfo
58 /// content type is the encapsulation of all those content types. [`RFC 5652`] describes
59 /// CMS and OpenSSL follows this RFC's implmentation.
61 /// [`RFC 5652`]: https://tools.ietf.org/html/rfc5652#page-6
62 pub struct CmsContentInfo
;
63 /// Reference to [`CMSContentInfo`]
65 /// [`CMSContentInfo`]:struct.CmsContentInfo.html
66 pub struct CmsContentInfoRef
;
69 impl CmsContentInfoRef
{
70 /// Given the sender's private key, `pkey` and the recipient's certificiate, `cert`,
71 /// decrypt the data in `self`.
73 /// OpenSSL documentation at [`CMS_decrypt`]
75 /// [`CMS_decrypt`]: https://www.openssl.org/docs/man1.1.0/crypto/CMS_decrypt.html
76 pub fn decrypt
<T
>(&self, pkey
: &PKeyRef
<T
>, cert
: &X509
) -> Result
<Vec
<u8>, ErrorStack
>
81 let pkey
= pkey
.as_ptr();
82 let cert
= cert
.as_ptr();
83 let out
= MemBio
::new()?
;
94 Ok(out
.get_buf().to_owned())
99 /// Serializes this CmsContentInfo using DER.
101 /// OpenSSL documentation at [`i2d_CMS_ContentInfo`]
103 /// [`i2d_CMS_ContentInfo`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_CMS_ContentInfo.html
105 ffi
::i2d_CMS_ContentInfo
109 /// Serializes this CmsContentInfo using DER.
111 /// OpenSSL documentation at [`PEM_write_bio_CMS`]
113 /// [`PEM_write_bio_CMS`]: https://www.openssl.org/docs/man1.1.0/man3/PEM_write_bio_CMS.html
115 ffi
::PEM_write_bio_CMS
119 impl CmsContentInfo
{
120 /// Parses a smime formatted `vec` of bytes into a `CmsContentInfo`.
122 /// OpenSSL documentation at [`SMIME_read_CMS`]
124 /// [`SMIME_read_CMS`]: https://www.openssl.org/docs/man1.0.2/crypto/SMIME_read_CMS.html
125 pub fn smime_read_cms(smime
: &[u8]) -> Result
<CmsContentInfo
, ErrorStack
> {
127 let bio
= MemBioSlice
::new(smime
)?
;
129 let cms
= cvt_p(ffi
::SMIME_read_CMS(bio
.as_ptr(), ptr
::null_mut()))?
;
131 Ok(CmsContentInfo
::from_ptr(cms
))
136 /// Deserializes a DER-encoded ContentInfo structure.
138 /// This corresponds to [`d2i_CMS_ContentInfo`].
140 /// [`d2i_CMS_ContentInfo`]: https://www.openssl.org/docs/manmaster/man3/d2i_X509.html
143 ffi
::d2i_CMS_ContentInfo
147 /// Deserializes a PEM-encoded ContentInfo structure.
149 /// This corresponds to [`PEM_read_bio_CMS`].
151 /// [`PEM_read_bio_CMS`]: https://www.openssl.org/docs/man1.1.0/man3/PEM_read_bio_CMS.html
154 ffi
::PEM_read_bio_CMS
157 /// Given a signing cert `signcert`, private key `pkey`, a certificate stack `certs`,
158 /// data `data` and flags `flags`, create a CmsContentInfo struct.
160 /// All arguments are optional.
162 /// OpenSSL documentation at [`CMS_sign`]
164 /// [`CMS_sign`]: https://www.openssl.org/docs/manmaster/man3/CMS_sign.html
166 signcert
: Option
<&X509Ref
>,
167 pkey
: Option
<&PKeyRef
<T
>>,
168 certs
: Option
<&StackRef
<X509
>>,
171 ) -> Result
<CmsContentInfo
, ErrorStack
>
176 let signcert
= signcert
.map_or(ptr
::null_mut(), |p
| p
.as_ptr());
177 let pkey
= pkey
.map_or(ptr
::null_mut(), |p
| p
.as_ptr());
178 let data_bio
= match data
{
179 Some(data
) => Some(MemBioSlice
::new(data
)?
),
182 let data_bio_ptr
= data_bio
.as_ref().map_or(ptr
::null_mut(), |p
| p
.as_ptr());
183 let certs
= certs
.map_or(ptr
::null_mut(), |p
| p
.as_ptr());
185 let cms
= cvt_p(ffi
::CMS_sign(
193 Ok(CmsContentInfo
::from_ptr(cms
))
197 /// Given a certificate stack `certs`, data `data`, cipher `cipher` and flags `flags`,
198 /// create a CmsContentInfo struct.
200 /// OpenSSL documentation at [`CMS_encrypt`]
202 /// [`CMS_encrypt`]: https://www.openssl.org/docs/manmaster/man3/CMS_encrypt.html
204 certs
: &StackRef
<X509
>,
208 ) -> Result
<CmsContentInfo
, ErrorStack
> {
210 let data_bio
= MemBioSlice
::new(data
)?
;
212 let cms
= cvt_p(ffi
::CMS_encrypt(
219 Ok(CmsContentInfo
::from_ptr(cms
))
232 fn cms_encrypt_decrypt() {
233 // load cert with public key only
234 let pub_cert_bytes
= include_bytes
!("../test/cms_pubkey.der");
235 let pub_cert
= X509
::from_der(pub_cert_bytes
).expect("failed to load pub cert");
237 // load cert with private key
238 let priv_cert_bytes
= include_bytes
!("../test/cms.p12");
239 let priv_cert
= Pkcs12
::from_der(priv_cert_bytes
).expect("failed to load priv cert");
240 let priv_cert
= priv_cert
242 .expect("failed to parse priv cert");
244 // encrypt cms message using public key cert
245 let input
= String
::from("My Message");
246 let mut cert_stack
= Stack
::new().expect("failed to create stack");
249 .expect("failed to add pub cert to stack");
251 let encrypt
= CmsContentInfo
::encrypt(
254 Cipher
::des_ede3_cbc(),
257 .expect("failed create encrypted cms");
259 // decrypt cms message using private key cert (DER)
261 let encrypted_der
= encrypt
.to_der().expect("failed to create der from cms");
263 CmsContentInfo
::from_der(&encrypted_der
).expect("failed read cms from der");
264 let decrypt
= decrypt
265 .decrypt(&priv_cert
.pkey
, &priv_cert
.cert
)
266 .expect("failed to decrypt cms");
268 String
::from_utf8(decrypt
).expect("failed to create string from cms content");
269 assert_eq
!(input
, decrypt
);
272 // decrypt cms message using private key cert (PEM)
274 let encrypted_pem
= encrypt
.to_pem().expect("failed to create pem from cms");
276 CmsContentInfo
::from_pem(&encrypted_pem
).expect("failed read cms from pem");
277 let decrypt
= decrypt
278 .decrypt(&priv_cert
.pkey
, &priv_cert
.cert
)
279 .expect("failed to decrypt cms");
281 String
::from_utf8(decrypt
).expect("failed to create string from cms content");
282 assert_eq
!(input
, decrypt
);