From cd1d1955d9375cb8b5dfe94972c601a24d51dee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20Ma=CC=88nnchen?= Date: Fri, 24 Nov 2017 16:19:34 +0100 Subject: [PATCH 1/4] PKCS7 Support (Rebased onto latest version) --- openssl/src/bio.rs | 4 + openssl/src/crypto/mod.rs | 8 + openssl/src/crypto/pkcs7/mod.rs | 1 + openssl/src/crypto/pkcs7/pk7_smime.rs | 257 ++++++++++++++++++++++++++ systest/build.rs | 2 + 5 files changed, 272 insertions(+) create mode 100644 openssl/src/crypto/mod.rs create mode 100644 openssl/src/crypto/pkcs7/mod.rs create mode 100644 openssl/src/crypto/pkcs7/pk7_smime.rs diff --git a/openssl/src/bio.rs b/openssl/src/bio.rs index 51724e3f..15f6e066 100644 --- a/openssl/src/bio.rs +++ b/openssl/src/bio.rs @@ -66,6 +66,10 @@ impl MemBio { slice::from_raw_parts(ptr as *const _ as *const _, len as usize) } } + + pub fn from_ptr(bio: *mut ffi::BIO) -> MemBio { + MemBio(bio) + } } cfg_if! { diff --git a/openssl/src/crypto/mod.rs b/openssl/src/crypto/mod.rs new file mode 100644 index 00000000..1fb6e08c --- /dev/null +++ b/openssl/src/crypto/mod.rs @@ -0,0 +1,8 @@ +#![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 new file mode 100644 index 00000000..de7a3d2d --- /dev/null +++ b/openssl/src/crypto/pkcs7/mod.rs @@ -0,0 +1 @@ +pub mod pk7_smime; diff --git a/openssl/src/crypto/pkcs7/pk7_smime.rs b/openssl/src/crypto/pkcs7/pk7_smime.rs new file mode 100644 index 00000000..6cca691c --- /dev/null +++ b/openssl/src/crypto/pkcs7/pk7_smime.rs @@ -0,0 +1,257 @@ +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, 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) -> Result { + 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, 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, input: &[u8], cypher: Cipher, flags: PKCS7Flags) -> Result { + 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, input: &[u8], flags: PKCS7Flags) -> Result { + 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, store: &X509Store, indata: Option<&[u8]>, out: Option<&mut Vec>, flags: PKCS7Flags) -> Result { + 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 = 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/systest/build.rs b/systest/build.rs index 84bdcbdb..fe76fae9 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -94,6 +94,8 @@ fn main() { s == "PasswordCallback" || s == "pem_password_cb" || s == "bio_info_cb" || s.starts_with("CRYPTO_EX_") }); cfg.skip_struct(|s| s == "ProbeResult"); + cfg.skip_field(|s, field| s == "pkcs7_st" && field == "d"); + cfg.skip_struct(|s| s == "pkcs7_st__d"); cfg.skip_fn(move |s| { s == "CRYPTO_memcmp" || // uses volatile From 2dd3736444781d0326ceb61a8aa8ea24deb3794d Mon Sep 17 00:00:00 2001 From: Sam Scott Date: Tue, 2 Oct 2018 17:22:53 -0400 Subject: [PATCH 2/4] Refactor to match style and add documentation. --- openssl-sys/src/lib.rs | 2 + openssl-sys/src/ossl_typ.rs | 1 + openssl-sys/src/pem.rs | 9 + openssl-sys/src/pkcs7.rs | 74 ++++++ openssl/src/crypto/mod.rs | 8 - openssl/src/crypto/pkcs7/mod.rs | 1 - openssl/src/crypto/pkcs7/pk7_smime.rs | 257 ------------------ openssl/src/lib.rs | 1 + openssl/src/pkcs7.rs | 366 ++++++++++++++++++++++++++ 9 files changed, 453 insertions(+), 266 deletions(-) create mode 100644 openssl-sys/src/pkcs7.rs delete mode 100644 openssl/src/crypto/mod.rs delete mode 100644 openssl/src/crypto/pkcs7/mod.rs delete mode 100644 openssl/src/crypto/pkcs7/pk7_smime.rs create mode 100644 openssl/src/pkcs7.rs 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, 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) -> Result { - 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, 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, input: &[u8], cypher: Cipher, flags: PKCS7Flags) -> Result { - 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, input: &[u8], flags: PKCS7Flags) -> Result { - 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, store: &X509Store, indata: Option<&[u8]>, out: Option<&mut Vec>, flags: PKCS7Flags) -> Result { - 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 = 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; + + /// Reference to `Pkcs7` + pub struct Pkcs7Ref; +} + +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 { + /// 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, 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) -> Result { + 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, + 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(&self, pkey: &PKeyRef, cert: &X509Ref) -> Result, 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, input: &[u8], cipher: Cipher, flags: PKCS7Flags) -> Result { + 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( + signcert: &X509Ref, + pkey: &PKeyRef, + certs: &Stack, + input: &[u8], + flags: PKCS7Flags + ) -> Result + 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, + store: &X509Store, + indata: Option<&[u8]>, + out: Option<&mut Vec>, + flags: PKCS7Flags + ) -> Result { + 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::::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 = 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) + } +} From 8ae761063cc93d14e60725c2bb1b8efa374de85e Mon Sep 17 00:00:00 2001 From: Sam Scott Date: Tue, 2 Oct 2018 22:51:49 -0400 Subject: [PATCH 3/4] Address comments. --- openssl/src/bio.rs | 2 +- openssl/src/pkcs7.rs | 256 +++++++++++++++++++++---------------------- systest/build.rs | 2 - 3 files changed, 127 insertions(+), 133 deletions(-) diff --git a/openssl/src/bio.rs b/openssl/src/bio.rs index 15f6e066..4c2f1ff2 100644 --- a/openssl/src/bio.rs +++ b/openssl/src/bio.rs @@ -67,7 +67,7 @@ impl MemBio { } } - pub fn from_ptr(bio: *mut ffi::BIO) -> MemBio { + pub unsafe fn from_ptr(bio: *mut ffi::BIO) -> MemBio { MemBio(bio) } } diff --git a/openssl/src/pkcs7.rs b/openssl/src/pkcs7.rs index 40569eaf..54c1638c 100644 --- a/openssl/src/pkcs7.rs +++ b/openssl/src/pkcs7.rs @@ -6,112 +6,49 @@ use error::ErrorStack; use stack::Stack; use foreign_types::ForeignType; use symm::Cipher; -use pkey::{HasPrivate, Public, PKeyRef}; +use pkey::{HasPrivate, 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! { +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; + pub struct Pkcs7; /// Reference to `Pkcs7` - pub struct Pkcs7Ref; + pub struct Pkcs7Ref; } 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; + pub struct Pkcs7Flags: c_int { + const TEXT = ffi::PKCS7_TEXT; + const NOCERTS = ffi::PKCS7_NOCERTS; + const NOSIGS = ffi::PKCS7_NOSIGS; + const NOCHAIN = ffi::PKCS7_NOCHAIN; + const NOINTERN = ffi::PKCS7_NOINTERN; + const NOVERIFY = ffi::PKCS7_NOVERIFY; + const DETACHED = ffi::PKCS7_DETACHED; + const BINARY = ffi::PKCS7_BINARY; + const NOATTR = ffi::PKCS7_NOATTR; + const NOSMIMECAP = ffi::PKCS7_NOSMIMECAP; + const NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE; + const CRLFEOL = ffi::PKCS7_CRLFEOL; + const STREAM = ffi::PKCS7_STREAM; + const NOCRL = ffi::PKCS7_NOCRL; + const PARTIAL = ffi::PKCS7_PARTIAL; + const REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST; #[cfg(not(any(ossl101, ossl102, libressl)))] - const PKCS7_NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT; + const NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT; } } -impl Pkcs7 { - /// 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, 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) -> Result { - 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 - } - +impl Pkcs7 { from_pem! { /// Deserializes a PEM-encoded PKCS#7 signature /// @@ -121,31 +58,32 @@ impl Pkcs7 { /// /// [`PEM_read_bio_PKCS7`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PKCS7.html from_pem, - Pkcs7, + Pkcs7, ffi::PEM_read_bio_PKCS7 } - /// Decrypts data using the provided private key. + /// Parses a message in S/MIME format. + /// + /// Returns the loaded signature, along with the cleartext message (if + /// available). /// - /// `pkey` is the recipient's private key, and `cert` is the recipient's - /// certificate. + /// This corresponds to [`SMIME_read_PKCS7`]. /// - /// 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(&self, pkey: &PKeyRef, cert: &X509Ref) -> Result, ErrorStack> - where - PT: HasPrivate - { + /// [`SMIME_read_PKCS7`]: https://www.openssl.org/docs/man1.1.0/crypto/SMIME_read_PKCS7.html + pub fn from_smime(input: &[u8]) -> Result<(Self, Option>), ErrorStack> { ffi::init(); - let output = MemBio::new()?; - + let input_bio = MemBioSlice::new(input)?; + let mut bcont_bio = null_mut(); unsafe { - cvt(ffi::PKCS7_decrypt(self.0, pkey.as_ptr(), cert.as_ptr(), output.as_ptr(), 0)) - .and(Ok(output.get_buf().to_owned())) + let pkcs7= cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio))?; + let out = if !bcont_bio.is_null() { + let bcont_bio = MemBio::from_ptr(bcont_bio); + Some(bcont_bio.get_buf().to_vec()) + } else { + None + }; + Ok((Pkcs7::from_ptr(pkcs7), out)) } } @@ -158,7 +96,7 @@ impl Pkcs7 { /// This corresponds to [`PKCS7_encrypt`]. /// /// [`PKCS7_encrypt`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_encrypt.html - pub fn encrypt(certs: &Stack, input: &[u8], cipher: Cipher, flags: PKCS7Flags) -> Result { + pub fn encrypt(certs: &Stack, input: &[u8], cipher: Cipher, flags: Pkcs7Flags) -> Result { ffi::init(); let input_bio = MemBioSlice::new(input)?; @@ -188,7 +126,7 @@ impl Pkcs7 { pkey: &PKeyRef, certs: &Stack, input: &[u8], - flags: PKCS7Flags + flags: Pkcs7Flags ) -> Result where PT: HasPrivate @@ -206,6 +144,72 @@ impl Pkcs7 { ).map(|p| Pkcs7::from_ptr(p)) } } +} + +impl Pkcs7Ref { + /// 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, ErrorStack> + { + ffi::init(); + + let input_bio = MemBioSlice::new(input)?; + let output = MemBio::new()?; + unsafe { + cvt( + ffi::SMIME_write_PKCS7( + output.as_ptr(), + self.as_ptr(), + input_bio.as_ptr(), + flags.bits) + ).and( + Ok(output.get_buf().to_owned()) + ) + } + } + + 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 + } + + /// 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(&self, pkey: &PKeyRef, cert: &X509Ref) -> Result, ErrorStack> + where + PT: HasPrivate + { + ffi::init(); + + let output = MemBio::new()?; + + unsafe { + cvt(ffi::PKCS7_decrypt(self.as_ptr(), pkey.as_ptr(), cert.as_ptr(), output.as_ptr(), 0)) + .and(Ok(output.get_buf().to_owned())) + } + } /// Verifies the PKCS#7 `signedData` structure contained by `&self`. /// @@ -223,8 +227,8 @@ impl Pkcs7 { store: &X509Store, indata: Option<&[u8]>, out: Option<&mut Vec>, - flags: PKCS7Flags - ) -> Result { + flags: Pkcs7Flags + ) -> Result<(), ErrorStack> { ffi::init(); let out_bio = MemBio::new()?; @@ -237,13 +241,13 @@ impl Pkcs7 { let result = unsafe { cvt(ffi::PKCS7_verify( - self.0, + self.as_ptr(), certs.as_ptr(), store.as_ptr(), indata_bio_ptr, out_bio.as_ptr(), flags.bits)) - .map(|r| r == 1) + .map(|_r| ()) }; if let Some(data) = out { @@ -260,8 +264,8 @@ mod tests { use x509::X509; use x509::store::X509StoreBuilder; use symm::Cipher; - use pkcs7::{Pkcs7, PKCS7Flags}; - use pkey::{PKey, Public}; + use pkcs7::{Pkcs7, Pkcs7Flags}; + use pkey::PKey; use stack::Stack; #[test] @@ -272,7 +276,7 @@ mod tests { certs.push(cert.clone()).unwrap(); let message: String = String::from("foo"); let cypher = Cipher::des_ede3_cbc(); - let flags = PKCS7Flags::PKCS7_STREAM; + let flags = Pkcs7Flags::STREAM; let pkey = include_bytes!("../test/key.pem"); let pkey = PKey::private_key_from_pem(pkey).unwrap(); @@ -280,8 +284,7 @@ mod tests { 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 (pkcs7_decoded, _) = Pkcs7::from_smime(encrypted.as_slice()).expect("should succeed"); let decoded = pkcs7_decoded.decrypt(&pkey, &cert).expect("should succeed"); @@ -294,7 +297,7 @@ mod tests { 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 flags = Pkcs7Flags::STREAM | Pkcs7Flags::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"); @@ -309,16 +312,14 @@ mod tests { 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 (pkcs7_decoded, content) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); let mut output = Vec::new(); - let result = pkcs7_decoded.verify(&certs, &store, Some(message.as_bytes()), Some(&mut output), flags) + 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); + assert_eq!(message.clone().into_bytes(), content.expect("should be non-empty")); } #[test] @@ -327,7 +328,7 @@ mod tests { let cert = X509::from_pem(cert).unwrap(); let certs = Stack::new().unwrap(); let message: String = String::from("foo"); - let flags = PKCS7Flags::PKCS7_STREAM; + let flags = Pkcs7Flags::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"); @@ -342,24 +343,19 @@ mod tests { let signed = pkcs7.to_smime(message.as_bytes(), flags).expect("should succeed"); - let mut bcount = Vec::new(); - let pkcs7_decoded = Pkcs7::::from_smime(signed.as_slice(), &mut bcount).expect("should succeed"); + let (pkcs7_decoded, content) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); let mut output = Vec::new(); - let result = pkcs7_decoded.verify(&certs, &store, None, Some(&mut output), flags).expect("should succeed"); + 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 = Vec::new(); - assert_eq!(empty, bcount); + assert!(content.is_none()); } #[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); + let result = Pkcs7::from_smime(input.as_bytes()); assert_eq!(result.is_err(), true) } diff --git a/systest/build.rs b/systest/build.rs index fe76fae9..84bdcbdb 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -94,8 +94,6 @@ fn main() { s == "PasswordCallback" || s == "pem_password_cb" || s == "bio_info_cb" || s.starts_with("CRYPTO_EX_") }); cfg.skip_struct(|s| s == "ProbeResult"); - cfg.skip_field(|s, field| s == "pkcs7_st" && field == "d"); - cfg.skip_struct(|s| s == "pkcs7_st__d"); cfg.skip_fn(move |s| { s == "CRYPTO_memcmp" || // uses volatile From 04ada473d1a6585ccc770d0bbfe8f8cd6bfb54b2 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 10 Oct 2018 21:25:29 -0700 Subject: [PATCH 4/4] Cleanup --- openssl/src/pkcs7.rs | 179 ++++++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 79 deletions(-) diff --git a/openssl/src/pkcs7.rs b/openssl/src/pkcs7.rs index 54c1638c..ef745bd1 100644 --- a/openssl/src/pkcs7.rs +++ b/openssl/src/pkcs7.rs @@ -1,15 +1,14 @@ -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, PKeyRef}; -use libc::c_int; -use std::ptr::null_mut; +use ffi; use foreign_types::ForeignTypeRef; +use libc::c_int; +use pkey::{HasPrivate, PKeyRef}; +use stack::StackRef; +use std::ptr; +use symm::Cipher; +use x509::store::X509StoreRef; +use x509::{X509Ref, X509}; use {cvt, cvt_p}; foreign_type_and_impl_send_sync! { @@ -63,27 +62,28 @@ impl Pkcs7 { } /// Parses a message in S/MIME format. - /// + /// /// Returns the loaded signature, along with the cleartext message (if /// available). /// /// 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]) -> Result<(Self, Option>), ErrorStack> { + pub fn from_smime(input: &[u8]) -> Result<(Pkcs7, Option>), ErrorStack> { ffi::init(); let input_bio = MemBioSlice::new(input)?; - let mut bcont_bio = null_mut(); + let mut bcont_bio = ptr::null_mut(); unsafe { - let pkcs7= cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio))?; + let pkcs7 = + cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio)).map(Pkcs7)?; let out = if !bcont_bio.is_null() { let bcont_bio = MemBio::from_ptr(bcont_bio); Some(bcont_bio.get_buf().to_vec()) } else { None }; - Ok((Pkcs7::from_ptr(pkcs7), out)) + Ok((pkcs7, out)) } } @@ -96,9 +96,12 @@ impl Pkcs7 { /// This corresponds to [`PKCS7_encrypt`]. /// /// [`PKCS7_encrypt`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_encrypt.html - pub fn encrypt(certs: &Stack, input: &[u8], cipher: Cipher, flags: Pkcs7Flags) -> Result { - ffi::init(); - + pub fn encrypt( + certs: &StackRef, + input: &[u8], + cipher: Cipher, + flags: Pkcs7Flags, + ) -> Result { let input_bio = MemBioSlice::new(input)?; unsafe { @@ -106,8 +109,8 @@ impl Pkcs7 { certs.as_ptr(), input_bio.as_ptr(), cipher.as_ptr(), - flags.bits) - ).map(|p| Pkcs7::from_ptr(p)) + flags.bits, + )).map(Pkcs7) } } @@ -124,15 +127,13 @@ impl Pkcs7 { pub fn sign( signcert: &X509Ref, pkey: &PKeyRef, - certs: &Stack, + certs: &StackRef, input: &[u8], - flags: Pkcs7Flags - ) -> Result + flags: Pkcs7Flags, + ) -> Result where - PT: HasPrivate + PT: HasPrivate, { - ffi::init(); - let input_bio = MemBioSlice::new(input)?; unsafe { cvt_p(ffi::PKCS7_sign( @@ -140,8 +141,8 @@ impl Pkcs7 { pkey.as_ptr(), certs.as_ptr(), input_bio.as_ptr(), - flags.bits) - ).map(|p| Pkcs7::from_ptr(p)) + flags.bits, + )).map(Pkcs7) } } } @@ -152,26 +153,16 @@ impl Pkcs7Ref { /// 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, ErrorStack> - { - ffi::init(); - + pub fn to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result, ErrorStack> { let input_bio = MemBioSlice::new(input)?; let output = MemBio::new()?; unsafe { - cvt( - ffi::SMIME_write_PKCS7( - output.as_ptr(), - self.as_ptr(), - input_bio.as_ptr(), - flags.bits) - ).and( - Ok(output.get_buf().to_owned()) - ) + cvt(ffi::SMIME_write_PKCS7( + output.as_ptr(), + self.as_ptr(), + input_bio.as_ptr(), + flags.bits, + )).map(|_| output.get_buf().to_owned()) } } @@ -197,17 +188,25 @@ impl Pkcs7Ref { /// This corresponds to [`PKCS7_decrypt`]. /// /// [`PKCS7_decrypt`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_decrypt.html - pub fn decrypt(&self, pkey: &PKeyRef, cert: &X509Ref) -> Result, ErrorStack> - where - PT: HasPrivate + pub fn decrypt( + &self, + pkey: &PKeyRef, + cert: &X509Ref, + flags: Pkcs7Flags, + ) -> Result, ErrorStack> + where + PT: HasPrivate, { - ffi::init(); - let output = MemBio::new()?; unsafe { - cvt(ffi::PKCS7_decrypt(self.as_ptr(), pkey.as_ptr(), cert.as_ptr(), output.as_ptr(), 0)) - .and(Ok(output.get_buf().to_owned())) + cvt(ffi::PKCS7_decrypt( + self.as_ptr(), + pkey.as_ptr(), + cert.as_ptr(), + output.as_ptr(), + flags.bits, + )).map(|_| output.get_buf().to_owned()) } } @@ -223,50 +222,48 @@ impl Pkcs7Ref { /// [`PKCS7_verify`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_verify.html pub fn verify( &self, - certs: &Stack, - store: &X509Store, + certs: &StackRef, + store: &X509StoreRef, indata: Option<&[u8]>, out: Option<&mut Vec>, - flags: Pkcs7Flags + flags: Pkcs7Flags, ) -> Result<(), 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 indata_bio_ptr = indata_bio.as_ref().map_or(ptr::null_mut(), |p| p.as_ptr()); - let result = unsafe { + unsafe { cvt(ffi::PKCS7_verify( self.as_ptr(), certs.as_ptr(), store.as_ptr(), indata_bio_ptr, out_bio.as_ptr(), - flags.bits)) - .map(|_r| ()) - }; + flags.bits, + )).map(|_| ())? + } if let Some(data) = out { data.clear(); - data.append(&mut out_bio.get_buf().to_vec()); + data.extend_from_slice(out_bio.get_buf()); } - result + Ok(()) } } #[cfg(test)] mod tests { - use x509::X509; - use x509::store::X509StoreBuilder; - use symm::Cipher; use pkcs7::{Pkcs7, Pkcs7Flags}; use pkey::PKey; use stack::Stack; + use symm::Cipher; + use x509::store::X509StoreBuilder; + use x509::X509; #[test] fn encrypt_decrypt_test() { @@ -280,13 +277,18 @@ mod tests { 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 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 encrypted = pkcs7 + .to_smime(message.as_bytes(), flags) + .expect("should succeed"); let (pkcs7_decoded, _) = Pkcs7::from_smime(encrypted.as_slice()).expect("should succeed"); - let decoded = pkcs7_decoded.decrypt(&pkey, &cert).expect("should succeed"); + let decoded = pkcs7_decoded + .decrypt(&pkey, &cert, Pkcs7Flags::empty()) + .expect("should succeed"); assert_eq!(decoded, message.into_bytes()); } @@ -308,18 +310,31 @@ mod tests { let store = store_builder.build(); - let pkcs7 = Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + 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 signed = pkcs7 + .to_smime(message.as_bytes(), flags) + .expect("should succeed"); println!("{:?}", String::from_utf8(signed.clone()).unwrap()); - let (pkcs7_decoded, content) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); + let (pkcs7_decoded, content) = + Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); let mut output = Vec::new(); - pkcs7_decoded.verify(&certs, &store, Some(message.as_bytes()), Some(&mut output), flags) - .expect("should succeed"); + pkcs7_decoded + .verify( + &certs, + &store, + Some(message.as_bytes()), + Some(&mut output), + flags, + ).expect("should succeed"); assert_eq!(message.clone().into_bytes(), output); - assert_eq!(message.clone().into_bytes(), content.expect("should be non-empty")); + assert_eq!( + message.clone().into_bytes(), + content.expect("should be non-empty") + ); } #[test] @@ -339,14 +354,20 @@ mod tests { let store = store_builder.build(); - let pkcs7 = Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + 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 signed = pkcs7 + .to_smime(message.as_bytes(), flags) + .expect("should succeed"); - let (pkcs7_decoded, content) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); + let (pkcs7_decoded, content) = + Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); let mut output = Vec::new(); - pkcs7_decoded.verify(&certs, &store, None, Some(&mut output), flags).expect("should succeed"); + pkcs7_decoded + .verify(&certs, &store, None, Some(&mut output), flags) + .expect("should succeed"); assert_eq!(message.clone().into_bytes(), output); assert!(content.is_none());