summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--openssl-sys/src/lib.rs2
-rw-r--r--openssl-sys/src/ossl_typ.rs1
-rw-r--r--openssl-sys/src/pem.rs9
-rw-r--r--openssl-sys/src/pkcs7.rs74
-rw-r--r--openssl/src/crypto/mod.rs8
-rw-r--r--openssl/src/crypto/pkcs7/mod.rs1
-rw-r--r--openssl/src/crypto/pkcs7/pk7_smime.rs257
-rw-r--r--openssl/src/lib.rs1
-rw-r--r--openssl/src/pkcs7.rs366
9 files changed, 453 insertions, 266 deletions
diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs
index 4a05ae0b..d400c89f 100644
--- a/openssl-sys/src/lib.rs
+++ b/openssl-sys/src/lib.rs
@@ -25,6 +25,7 @@ pub use object::*;
pub use ocsp::*;
pub use ossl_typ::*;
pub use pem::*;
+pub use pkcs7::*;
pub use pkcs12::*;
pub use rand::*;
pub use rsa::*;
@@ -61,6 +62,7 @@ mod object;
mod ocsp;
mod ossl_typ;
mod pem;
+mod pkcs7;
mod pkcs12;
mod rand;
mod rsa;
diff --git a/openssl-sys/src/ossl_typ.rs b/openssl-sys/src/ossl_typ.rs
index 96c4d002..3fcfc5e0 100644
--- a/openssl-sys/src/ossl_typ.rs
+++ b/openssl-sys/src/ossl_typ.rs
@@ -10,6 +10,7 @@ pub enum ASN1_BIT_STRING {}
pub enum ASN1_TIME {}
pub enum ASN1_TYPE {}
pub enum ASN1_OBJECT {}
+pub enum ASN1_OCTET_STRING {}
pub enum bio_st {} // FIXME remove
cfg_if! {
diff --git a/openssl-sys/src/pem.rs b/openssl-sys/src/pem.rs
index 1a06b969..1639e643 100644
--- a/openssl-sys/src/pem.rs
+++ b/openssl-sys/src/pem.rs
@@ -132,6 +132,15 @@ extern "C" {
cb: pem_password_cb,
u: *mut c_void,
) -> *mut EVP_PKEY;
+
+ pub fn PEM_read_bio_PKCS7(
+ bio: *mut BIO,
+ out: *mut *mut PKCS7,
+ cb: pem_password_cb,
+ u: *mut c_void,
+ ) -> *mut PKCS7;
+
+ pub fn PEM_write_bio_PKCS7(bp: *mut BIO, x: *mut PKCS7) -> c_int;
}
pub const PEM_R_NO_START_LINE: c_int = 108;
diff --git a/openssl-sys/src/pkcs7.rs b/openssl-sys/src/pkcs7.rs
new file mode 100644
index 00000000..454a04df
--- /dev/null
+++ b/openssl-sys/src/pkcs7.rs
@@ -0,0 +1,74 @@
+use libc::*;
+
+use *;
+
+pub enum PKCS7_SIGNED {}
+pub enum PKCS7_ENVELOPE {}
+pub enum PKCS7_SIGN_ENVELOPE {}
+pub enum PKCS7_DIGEST {}
+pub enum PKCS7_ENCRYPT {}
+pub enum PKCS7 {}
+
+pub const PKCS7_TEXT: c_int = 0x1;
+pub const PKCS7_NOCERTS: c_int = 0x2;
+pub const PKCS7_NOSIGS: c_int = 0x4;
+pub const PKCS7_NOCHAIN: c_int = 0x8;
+pub const PKCS7_NOINTERN: c_int = 0x10;
+pub const PKCS7_NOVERIFY: c_int = 0x20;
+pub const PKCS7_DETACHED: c_int = 0x40;
+pub const PKCS7_BINARY: c_int = 0x80;
+pub const PKCS7_NOATTR: c_int = 0x100;
+pub const PKCS7_NOSMIMECAP: c_int = 0x200;
+pub const PKCS7_NOOLDMIMETYPE: c_int = 0x400;
+pub const PKCS7_CRLFEOL: c_int = 0x800;
+pub const PKCS7_STREAM: c_int = 0x1000;
+pub const PKCS7_NOCRL: c_int = 0x2000;
+pub const PKCS7_PARTIAL: c_int = 0x4000;
+pub const PKCS7_REUSE_DIGEST: c_int = 0x8000;
+#[cfg(not(any(ossl101, ossl102, libressl)))]
+pub const PKCS7_NO_DUAL_CONTENT: c_int = 0x10000;
+
+extern "C" {
+ pub fn PKCS7_encrypt(
+ certs: *mut stack_st_X509,
+ b: *mut BIO,
+ cipher: *const EVP_CIPHER,
+ flags: c_int,
+ ) -> *mut PKCS7;
+
+ pub fn PKCS7_verify(
+ pkcs7: *mut PKCS7,
+ certs: *mut stack_st_X509,
+ store: *mut X509_STORE,
+ indata: *mut BIO,
+ out: *mut BIO,
+ flags: c_int,
+ ) -> c_int;
+
+ pub fn PKCS7_sign(
+ signcert: *mut X509,
+ pkey: *mut EVP_PKEY,
+ certs: *mut stack_st_X509,
+ data: *mut BIO,
+ flags: c_int,
+ ) -> *mut PKCS7;
+
+ pub fn PKCS7_decrypt(
+ pkcs7: *mut PKCS7,
+ pkey: *mut EVP_PKEY,
+ cert: *mut X509,
+ data: *mut BIO,
+ flags: c_int,
+ ) -> c_int;
+
+ pub fn PKCS7_free(pkcs7: *mut PKCS7);
+
+ pub fn SMIME_write_PKCS7(
+ out: *mut BIO,
+ pkcs7: *mut PKCS7,
+ data: *mut BIO,
+ flags: c_int,
+ ) -> c_int;
+
+ pub fn SMIME_read_PKCS7(bio: *mut BIO, bcont: *mut *mut BIO) -> *mut PKCS7;
+}
diff --git a/openssl/src/crypto/mod.rs b/openssl/src/crypto/mod.rs
deleted file mode 100644
index 1fb6e08c..00000000
--- a/openssl/src/crypto/mod.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-#![doc(hidden)]
-#![deprecated(since = "0.9.20")]
-use string::OpensslString;
-
-#[deprecated(note = "renamed to OpensslString", since = "0.9.7")]
-pub type CryptoString = OpensslString;
-
-pub mod pkcs7;
diff --git a/openssl/src/crypto/pkcs7/mod.rs b/openssl/src/crypto/pkcs7/mod.rs
deleted file mode 100644
index de7a3d2d..00000000
--- a/openssl/src/crypto/pkcs7/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod pk7_smime;
diff --git a/openssl/src/crypto/pkcs7/pk7_smime.rs b/openssl/src/crypto/pkcs7/pk7_smime.rs
deleted file mode 100644
index 6cca691c..00000000
--- a/openssl/src/crypto/pkcs7/pk7_smime.rs
+++ /dev/null
@@ -1,257 +0,0 @@
-use x509::{X509, X509Ref};
-use x509::store::X509Store;
-use ffi;
-use bio::{MemBio, MemBioSlice};
-use error::ErrorStack;
-use stack::Stack;
-use foreign_types::ForeignType;
-use symm::Cipher;
-use pkey::PKeyRef;
-use libc::c_int;
-use std::ptr::null_mut;
-use foreign_types::ForeignTypeRef;
-
-pub struct PKCS7(*mut ffi::pkcs7_st);
-
-bitflags! {
- pub struct PKCS7Flags: c_int {
- const PKCS7_TEXT = ffi::PKCS7_TEXT;
- const PKCS7_NOCERTS = ffi::PKCS7_NOCERTS;
- const PKCS7_NOSIGS = ffi::PKCS7_NOSIGS;
- const PKCS7_NOCHAIN = ffi::PKCS7_NOCHAIN;
- const PKCS7_NOINTERN = ffi::PKCS7_NOINTERN;
- const PKCS7_NOVERIFY = ffi::PKCS7_NOVERIFY;
- const PKCS7_DETACHED = ffi::PKCS7_DETACHED;
- const PKCS7_BINARY = ffi::PKCS7_BINARY;
- const PKCS7_NOATTR = ffi::PKCS7_NOATTR;
- const PKCS7_NOSMIMECAP = ffi::PKCS7_NOSMIMECAP;
- const PKCS7_NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE;
- const PKCS7_CRLFEOL = ffi::PKCS7_CRLFEOL;
- const PKCS7_STREAM = ffi::PKCS7_STREAM;
- const PKCS7_NOCRL = ffi::PKCS7_NOCRL;
- const PKCS7_PARTIAL = ffi::PKCS7_PARTIAL;
- const PKCS7_REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST;
- #[cfg(not(any(ossl101, ossl102, libressl)))]
- const PKCS7_NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT;
- }
-}
-
-impl PKCS7 {
- pub fn smime_write(&self, input: &[u8], flags: PKCS7Flags) -> Result<Vec<u8>, ErrorStack> {
- ffi::init();
-
- unsafe {
- let input_bio = MemBioSlice::new(input)?;
-
- let output = MemBio::new()?;
-
- if ffi::SMIME_write_PKCS7(output.as_ptr(), self.0, input_bio.as_ptr(), flags.bits) == 1 {
- Ok(output.get_buf().to_owned())
- } else {
- Err(ErrorStack::get())
- }
- }
- }
-
- pub fn smime_read(input: &[u8], bcount: &mut Vec<u8>) -> Result<PKCS7, ErrorStack> {
- ffi::init();
-
- let input_bio = MemBioSlice::new(input)?;
-
- let mut bcount_bio = null_mut();
-
- let pkcs7 = unsafe { ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcount_bio) };
-
- bcount.clear();
-
- if !bcount_bio.is_null() {
- let bcount_bio = MemBio::from_ptr(bcount_bio);
- bcount.append(&mut bcount_bio.get_buf().to_vec());
- }
-
- if pkcs7.is_null() {
- Err(ErrorStack::get())
- } else {
- Ok(PKCS7(pkcs7))
- }
- }
-
- pub fn decrypt(&self, pkey: &PKeyRef, cert: &X509Ref) -> Result<Vec<u8>, ErrorStack> {
- ffi::init();
-
- let output = MemBio::new()?;
-
- unsafe {
- if ffi::PKCS7_decrypt(self.0, pkey.as_ptr(), cert.as_ptr(), output.as_ptr(), 0) == 1 {
- Ok(output.get_buf().to_owned())
- } else {
- Err(ErrorStack::get())
- }
- }
- }
-
- pub fn encrypt(certs: &Stack<X509>, input: &[u8], cypher: Cipher, flags: PKCS7Flags) -> Result<PKCS7, ErrorStack> {
- ffi::init();
-
- let input_bio = MemBioSlice::new(input)?;
-
- let pkcs7 = unsafe { ffi::PKCS7_encrypt(certs.as_ptr(), input_bio.as_ptr(), cypher.as_ptr(), flags.bits) };
-
- if pkcs7.is_null() {
- Err(ErrorStack::get())
- } else {
- Ok(PKCS7(pkcs7))
- }
- }
-
- pub fn sign(signcert: &X509Ref, pkey: &PKeyRef, certs: &Stack<X509>, input: &[u8], flags: PKCS7Flags) -> Result<PKCS7, ErrorStack> {
- ffi::init();
-
- let input_bio = MemBioSlice::new(input)?;
-
- let pkcs7 = unsafe { ffi::PKCS7_sign(signcert.as_ptr(), pkey.as_ptr(), certs.as_ptr(), input_bio.as_ptr(), flags.bits) };
-
- if pkcs7.is_null() {
- Err(ErrorStack::get())
- } else {
- Ok(PKCS7(pkcs7))
- }
- }
-
- pub fn verify(&self, certs: &Stack<X509>, store: &X509Store, indata: Option<&[u8]>, out: Option<&mut Vec<u8>>, flags: PKCS7Flags) -> Result<bool, ErrorStack> {
- ffi::init();
-
- let out_bio = MemBio::new()?;
-
- let result = match indata {
- Some(data) => {
- let indata_bio = MemBioSlice::new(data)?;
- unsafe { ffi::PKCS7_verify(self.0, certs.as_ptr(), store.as_ptr(), indata_bio.as_ptr(), out_bio.as_ptr(), flags.bits) }
- },
- None => unsafe { ffi::PKCS7_verify(self.0, certs.as_ptr(), store.as_ptr(), null_mut(), out_bio.as_ptr(), flags.bits) }
- };
-
- if let Some(data) = out {
- data.clear();
- data.append(&mut out_bio.get_buf().to_vec());
- }
-
- if result == 1 {
- Ok(true)
- } else {
- Err(ErrorStack::get())
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use x509::X509;
- use x509::store::X509StoreBuilder;
- use symm::Cipher;
- use crypto::pkcs7::pk7_smime::PKCS7_STREAM;
- use crypto::pkcs7::pk7_smime::PKCS7_DETACHED;
- use crypto::pkcs7::pk7_smime::PKCS7;
- use pkey::PKey;
- use stack::Stack;
-
- #[test]
- fn encrypt_decrypt_test() {
- let cert = include_bytes!("../../../test/certs.pem");
- let cert = X509::from_pem(cert).unwrap();
- let mut certs = Stack::new().unwrap();
- certs.push(cert.clone()).unwrap();
- let message: String = String::from("foo");
- let cypher = Cipher::des_ede3_cbc();
- let flags = PKCS7_STREAM;
- let pkey = include_bytes!("../../../test/key.pem");
- let pkey = PKey::private_key_from_pem(pkey).unwrap();
-
- let pkcs7 = PKCS7::encrypt(&certs, message.as_bytes(), cypher, flags).expect("should succeed");
-
- let encrypted = pkcs7.smime_write(message.as_bytes(), flags).expect("should succeed");
-
- let mut bcount = Vec::new();
- let pkcs7_decoded = PKCS7::smime_read(encrypted.as_slice(), &mut bcount).expect("should succeed");
-
- let decoded = pkcs7_decoded.decrypt(&pkey, &cert).expect("should succeed");
-
- assert_eq!(decoded, message.into_bytes());
- }
-
- #[test]
- fn sign_verify_test_detached() {
- let cert = include_bytes!("../../../test/cert.pem");
- let cert = X509::from_pem(cert).unwrap();
- let certs = Stack::new().unwrap();
- let message: String = String::from("foo");
- let flags = PKCS7_STREAM | PKCS7_DETACHED;
- let pkey = include_bytes!("../../../test/key.pem");
- let pkey = PKey::private_key_from_pem(pkey).unwrap();
- let mut store_builder = X509StoreBuilder::new().expect("should succeed");
-
- let root_ca = include_bytes!("../../../test/root-ca.pem");
- let root_ca = X509::from_pem(root_ca).unwrap();
- store_builder.add_cert(root_ca).expect("should succeed");
-
- let store = store_builder.build();
-
- let pkcs7 = PKCS7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
-
- let signed = pkcs7.smime_write(message.as_bytes(), flags).expect("should succeed");
-
- let mut bcount = Vec::new();
- let pkcs7_decoded = PKCS7::smime_read(signed.as_slice(), &mut bcount).expect("should succeed");
-
- let mut output = Vec::new();
- let result = pkcs7_decoded.verify(&certs, &store, Some(message.as_bytes()), Some(&mut output), flags)
- .expect("should succeed");
-
- assert!(result);
- assert_eq!(message.clone().into_bytes(), output);
- assert_eq!(message.clone().into_bytes(), bcount);
- }
-
- #[test]
- fn sign_verify_test_normal() {
- let cert = include_bytes!("../../../test/cert.pem");
- let cert = X509::from_pem(cert).unwrap();
- let certs = Stack::new().unwrap();
- let message: String = String::from("foo");
- let flags = PKCS7_STREAM;
- let pkey = include_bytes!("../../../test/key.pem");
- let pkey = PKey::private_key_from_pem(pkey).unwrap();
- let mut store_builder = X509StoreBuilder::new().expect("should succeed");
-
- let root_ca = include_bytes!("../../../test/root-ca.pem");
- let root_ca = X509::from_pem(root_ca).unwrap();
- store_builder.add_cert(root_ca).expect("should succeed");
-
- let store = store_builder.build();
-
- let pkcs7 = PKCS7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
-
- let signed = pkcs7.smime_write(message.as_bytes(), flags).expect("should succeed");
-
- let mut bcount = Vec::new();
- let pkcs7_decoded = PKCS7::smime_read(signed.as_slice(), &mut bcount).expect("should succeed");
-
- let mut output = Vec::new();
- let result = pkcs7_decoded.verify(&certs, &store, None, Some(&mut output), flags).expect("should succeed");
-
- assert!(result);
- assert_eq!(message.clone().into_bytes(), output);
- let empty: Vec<u8> = Vec::new();
- assert_eq!(empty, bcount);
- }
-
- #[test]
- fn invalid_smime_read() {
- let input = String::from("Invalid SMIME Message");
- let mut bcount = Vec::new();
-
- let result = PKCS7::smime_read(input.as_bytes(), &mut bcount);
-
- assert_eq!(result.is_err(), true)
- }
-}
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index e6455482..c4d6eeeb 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -52,6 +52,7 @@ pub mod nid;
pub mod ocsp;
pub mod pkcs12;
pub mod pkcs5;
+pub mod pkcs7;
pub mod pkey;
pub mod rand;
pub mod rsa;
diff --git a/openssl/src/pkcs7.rs b/openssl/src/pkcs7.rs
new file mode 100644
index 00000000..40569eaf
--- /dev/null
+++ b/openssl/src/pkcs7.rs
@@ -0,0 +1,366 @@
+use x509::{X509, X509Ref};
+use x509::store::X509Store;
+use ffi;
+use bio::{MemBio, MemBioSlice};
+use error::ErrorStack;
+use stack::Stack;
+use foreign_types::ForeignType;
+use symm::Cipher;
+use pkey::{HasPrivate, Public, PKeyRef};
+use libc::c_int;
+use std::ptr::null_mut;
+use foreign_types::ForeignTypeRef;
+use {cvt, cvt_p};
+
+generic_foreign_type_and_impl_send_sync! {
+ type CType = ffi::PKCS7;
+ fn drop = ffi::PKCS7_free;
+
+ /// A PKCS#7 structure.
+ ///
+ /// Contains signed and/or encrypted data.
+ pub struct Pkcs7<T>;
+
+ /// Reference to `Pkcs7`
+ pub struct Pkcs7Ref<T>;
+}
+
+bitflags! {
+ pub struct PKCS7Flags: c_int {
+ const PKCS7_TEXT = ffi::PKCS7_TEXT;
+ const PKCS7_NOCERTS = ffi::PKCS7_NOCERTS;
+ const PKCS7_NOSIGS = ffi::PKCS7_NOSIGS;
+ const PKCS7_NOCHAIN = ffi::PKCS7_NOCHAIN;
+ const PKCS7_NOINTERN = ffi::PKCS7_NOINTERN;
+ const PKCS7_NOVERIFY = ffi::PKCS7_NOVERIFY;
+ const PKCS7_DETACHED = ffi::PKCS7_DETACHED;
+ const PKCS7_BINARY = ffi::PKCS7_BINARY;
+ const PKCS7_NOATTR = ffi::PKCS7_NOATTR;
+ const PKCS7_NOSMIMECAP = ffi::PKCS7_NOSMIMECAP;
+ const PKCS7_NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE;
+ const PKCS7_CRLFEOL = ffi::PKCS7_CRLFEOL;
+ const PKCS7_STREAM = ffi::PKCS7_STREAM;
+ const PKCS7_NOCRL = ffi::PKCS7_NOCRL;
+ const PKCS7_PARTIAL = ffi::PKCS7_PARTIAL;
+ const PKCS7_REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST;
+ #[cfg(not(any(ossl101, ossl102, libressl)))]
+ const PKCS7_NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT;
+ }
+}
+
+impl Pkcs7<Public> {
+ /// Converts PKCS#7 structure to S/MIME format
+ ///
+ /// This corresponds to [`SMIME_write_PKCS7`].
+ ///
+ /// [`SMIME_write_PKCS7`]: https://www.openssl.org/docs/man1.1.0/crypto/SMIME_write_PKCS7.html
+ pub fn to_smime(
+ &self,
+ input: &[u8],
+ flags: PKCS7Flags
+ ) -> Result<Vec<u8>, ErrorStack>
+ {
+ ffi::init();
+
+ let input_bio = MemBioSlice::new(input)?;
+ let output = MemBio::new()?;
+ unsafe {
+ cvt(
+ ffi::SMIME_write_PKCS7(
+ output.as_ptr(),
+ self.0,
+ input_bio.as_ptr(),
+ flags.bits)
+ ).and(
+ Ok(output.get_buf().to_owned())
+ )
+ }
+ }
+
+ /// Parses a message in S/MIME format.
+ ///
+ /// This corresponds to [`SMIME_read_PKCS7`].
+ ///
+ /// [`SMIME_read_PKCS7`]: https://www.openssl.org/docs/man1.1.0/crypto/SMIME_read_PKCS7.html
+ pub fn from_smime(input: &[u8], bcont: &mut Vec<u8>) -> Result<Self, ErrorStack> {
+ ffi::init();
+
+ let input_bio = MemBioSlice::new(input)?;
+ let mut bcount_bio = null_mut();
+ let pkcs7 = unsafe {
+ cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcount_bio))?
+ };
+ bcont.clear();
+ if !bcount_bio.is_null() {
+ let bcount_bio = MemBio::from_ptr(bcount_bio);
+ bcont.append(&mut bcount_bio.get_buf().to_vec());
+ }
+ unsafe {
+ Ok(Pkcs7::from_ptr(pkcs7))
+ }
+ }
+
+ to_pem! {
+ /// Serializes the data into a PEM-encoded PKCS#7 structure.
+ ///
+ /// The output will have a header of `-----BEGIN PKCS7-----`.
+ ///
+ /// This corresponds to [`PEM_write_bio_PKCS7`].
+ ///
+ /// [`PEM_write_bio_PKCS7`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS7.html
+ to_pem,
+ ffi::PEM_write_bio_PKCS7
+ }
+
+ from_pem! {
+ /// Deserializes a PEM-encoded PKCS#7 signature
+ ///
+ /// The input should have a header of `-----BEGIN PKCS7-----`.
+ ///
+ /// This corresponds to [`PEM_read_bio_PKCS7`].
+ ///
+ /// [`PEM_read_bio_PKCS7`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PKCS7.html
+ from_pem,
+ Pkcs7<Public>,
+ ffi::PEM_read_bio_PKCS7
+ }
+
+ /// Decrypts data using the provided private key.
+ ///
+ /// `pkey` is the recipient's private key, and `cert` is the recipient's
+ /// certificate.
+ ///
+ /// Returns the decrypted message.
+ ///
+ /// This corresponds to [`PKCS7_decrypt`].
+ ///
+ /// [`PKCS7_decrypt`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_decrypt.html
+ pub fn decrypt<PT>(&self, pkey: &PKeyRef<PT>, cert: &X509Ref) -> Result<Vec<u8>, ErrorStack>
+ where
+ PT: HasPrivate
+ {
+ ffi::init();
+
+ let output = MemBio::new()?;
+
+ unsafe {
+ cvt(ffi::PKCS7_decrypt(self.0, pkey.as_ptr(), cert.as_ptr(), output.as_ptr(), 0))
+ .and(Ok(output.get_buf().to_owned()))
+ }
+ }
+
+ /// Creates and returns a PKCS#7 `envelopedData` structure.
+ ///
+ /// `certs` is a list of recipient certificates. `input` is the content to be
+ /// encrypted. `cipher` is the symmetric cipher to use. `flags` is an optional
+ /// set of flags.
+ ///
+ /// This corresponds to [`PKCS7_encrypt`].
+ ///
+ /// [`PKCS7_encrypt`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_encrypt.html
+ pub fn encrypt(certs: &Stack<X509>, input: &[u8], cipher: Cipher, flags: PKCS7Flags) -> Result<Self, ErrorStack> {
+ ffi::init();
+
+ let input_bio = MemBioSlice::new(input)?;
+
+ unsafe {
+ cvt_p(ffi::PKCS7_encrypt(
+ certs.as_ptr(),
+ input_bio.as_ptr(),
+ cipher.as_ptr(),
+ flags.bits)
+ ).map(|p| Pkcs7::from_ptr(p))
+ }
+ }
+
+ /// Creates and returns a PKCS#7 `signedData` structure.
+ ///
+ /// `signcert` is the certificate to sign with, `pkey` is the corresponding
+ /// private key. `certs` is an optional additional set of certificates to
+ /// include in the PKCS#7 structure (for example any intermediate CAs in the
+ /// chain).
+ ///
+ /// This corresponds to [`PKCS7_sign`].
+ ///
+ /// [`PKCS7_sign`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_sign.html
+ pub fn sign<PT>(
+ signcert: &X509Ref,
+ pkey: &PKeyRef<PT>,
+ certs: &Stack<X509>,
+ input: &[u8],
+ flags: PKCS7Flags
+ ) -> Result<Self, ErrorStack>
+ where
+ PT: HasPrivate
+ {
+ ffi::init();
+
+ let input_bio = MemBioSlice::new(input)?;
+ unsafe {
+ cvt_p(ffi::PKCS7_sign(
+ signcert.as_ptr(),
+ pkey.as_ptr(),
+ certs.as_ptr(),
+ input_bio.as_ptr(),
+ flags.bits)
+ ).map(|p| Pkcs7::from_ptr(p))
+ }
+ }
+
+ /// Verifies the PKCS#7 `signedData` structure contained by `&self`.
+ ///
+ /// `certs` is a set of certificates in which to search for the signer's
+ /// certificate. `store` is a trusted certificate store (used for chain
+ /// verification). `indata` is the signed data if the content is not present
+ /// in `&self`. The content is written to `out` if it is not `None`.
+ ///
+ /// This corresponds to [`PKCS7_verify`].
+ ///
+ /// [`PKCS7_verify`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_verify.html
+ pub fn verify(
+ &self,
+ certs: &Stack<X509>,
+ store: &X509Store,
+ indata: Option<&[u8]>,
+ out: Option<&mut Vec<u8>>,
+ flags: PKCS7Flags
+ ) -> Result<bool, ErrorStack> {
+ ffi::init();
+
+ let out_bio = MemBio::new()?;
+
+ let indata_bio = match indata {
+ Some(data) => Some(MemBioSlice::new(data)?),
+ None => None,
+ };
+ let indata_bio_ptr = indata_bio.as_ref().map_or(null_mut(), |p| p.as_ptr());
+
+ let result = unsafe {
+ cvt(ffi::PKCS7_verify(
+ self.0,
+ certs.as_ptr(),
+ store.as_ptr(),
+ indata_bio_ptr,
+ out_bio.as_ptr(),
+ flags.bits))
+ .map(|r| r == 1)
+ };
+
+ if let Some(data) = out {
+ data.clear();
+ data.append(&mut out_bio.get_buf().to_vec());
+ }
+
+ result
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use x509::X509;
+ use x509::store::X509StoreBuilder;
+ use symm::Cipher;
+ use pkcs7::{Pkcs7, PKCS7Flags};
+ use pkey::{PKey, Public};
+ use stack::Stack;
+
+ #[test]
+ fn encrypt_decrypt_test() {
+ let cert = include_bytes!("../test/certs.pem");
+ let cert = X509::from_pem(cert).unwrap();
+ let mut certs = Stack::new().unwrap();
+ certs.push(cert.clone()).unwrap();
+ let message: String = String::from("foo");
+ let cypher = Cipher::des_ede3_cbc();
+ let flags = PKCS7Flags::PKCS7_STREAM;
+ let pkey = include_bytes!("../test/key.pem");
+ let pkey = PKey::private_key_from_pem(pkey).unwrap();
+
+ let pkcs7 = Pkcs7::encrypt(&certs, message.as_bytes(), cypher, flags).expect("should succeed");
+
+ let encrypted = pkcs7.to_smime(message.as_bytes(), flags).expect("should succeed");
+
+ let mut bcount = Vec::new();
+ let pkcs7_decoded = Pkcs7::from_smime(encrypted.as_slice(), &mut bcount).expect("should succeed");
+
+ let decoded = pkcs7_decoded.decrypt(&pkey, &cert).expect("should succeed");
+
+ assert_eq!(decoded, message.into_bytes());
+ }
+
+ #[test]
+ fn sign_verify_test_detached() {
+ let cert = include_bytes!("../test/cert.pem");
+ let cert = X509::from_pem(cert).unwrap();
+ let certs = Stack::new().unwrap();
+ let message: String = String::from("foo");
+ let flags = PKCS7Flags::PKCS7_STREAM | PKCS7Flags::PKCS7_DETACHED;
+ let pkey = include_bytes!("../test/key.pem");
+ let pkey = PKey::private_key_from_pem(pkey).unwrap();
+ let mut store_builder = X509StoreBuilder::new().expect("should succeed");
+
+ let root_ca = include_bytes!("../test/root-ca.pem");
+ let root_ca = X509::from_pem(root_ca).unwrap();
+ store_builder.add_cert(root_ca).expect("should succeed");
+
+ let store = store_builder.build();
+
+ let pkcs7 = Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
+
+ let signed = pkcs7.to_smime(message.as_bytes(), flags).expect("should succeed");
+ println!("{:?}", String::from_utf8(signed.clone()).unwrap());
+ let mut bcount = Vec::new();
+ let pkcs7_decoded = Pkcs7::from_smime(signed.as_slice(), &mut bcount).expect("should succeed");
+
+ let mut output = Vec::new();
+ let result = pkcs7_decoded.verify(&certs, &store, Some(message.as_bytes()), Some(&mut output), flags)
+ .expect("should succeed");
+
+ assert!(result);
+ assert_eq!(message.clone().into_bytes(), output);
+ assert_eq!(message.clone().into_bytes(), bcount);
+ }
+
+ #[test]
+ fn sign_verify_test_normal() {
+ let cert = include_bytes!("../test/cert.pem");
+ let cert = X509::from_pem(cert).unwrap();
+ let certs = Stack::new().unwrap();
+ let message: String = String::from("foo");
+ let flags = PKCS7Flags::PKCS7_STREAM;
+ let pkey = include_bytes!("../test/key.pem");
+ let pkey = PKey::private_key_from_pem(pkey).unwrap();
+ let mut store_builder = X509StoreBuilder::new().expect("should succeed");
+
+ let root_ca = include_bytes!("../test/root-ca.pem");
+ let root_ca = X509::from_pem(root_ca).unwrap();
+ store_builder.add_cert(root_ca).expect("should succeed");
+
+ let store = store_builder.build();
+
+ let pkcs7 = Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
+
+ let signed = pkcs7.to_smime(message.as_bytes(), flags).expect("should succeed");
+
+ let mut bcount = Vec::new();
+ let pkcs7_decoded = Pkcs7::<Public>::from_smime(signed.as_slice(), &mut bcount).expect("should succeed");
+
+ let mut output = Vec::new();
+ let result = pkcs7_decoded.verify(&certs, &store, None, Some(&mut output), flags).expect("should succeed");
+
+ assert!(result);
+ assert_eq!(message.clone().into_bytes(), output);
+ let empty: Vec<u8> = Vec::new();
+ assert_eq!(empty, bcount);
+ }
+
+ #[test]
+ fn invalid_from_smime() {
+ let input = String::from("Invalid SMIME Message");
+ let mut bcount = Vec::new();
+
+ let result = Pkcs7::from_smime(input.as_bytes(), &mut bcount);
+
+ assert_eq!(result.is_err(), true)
+ }
+}