Address comments.

This commit is contained in:
Sam Scott 2018-10-02 22:51:49 -04:00
parent 2dd3736444
commit 8ae761063c
3 changed files with 127 additions and 133 deletions

View File

@ -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) MemBio(bio)
} }
} }

View File

@ -6,112 +6,49 @@ use error::ErrorStack;
use stack::Stack; use stack::Stack;
use foreign_types::ForeignType; use foreign_types::ForeignType;
use symm::Cipher; use symm::Cipher;
use pkey::{HasPrivate, Public, PKeyRef}; use pkey::{HasPrivate, PKeyRef};
use libc::c_int; use libc::c_int;
use std::ptr::null_mut; use std::ptr::null_mut;
use foreign_types::ForeignTypeRef; use foreign_types::ForeignTypeRef;
use {cvt, cvt_p}; use {cvt, cvt_p};
generic_foreign_type_and_impl_send_sync! { foreign_type_and_impl_send_sync! {
type CType = ffi::PKCS7; type CType = ffi::PKCS7;
fn drop = ffi::PKCS7_free; fn drop = ffi::PKCS7_free;
/// A PKCS#7 structure. /// A PKCS#7 structure.
/// ///
/// Contains signed and/or encrypted data. /// Contains signed and/or encrypted data.
pub struct Pkcs7<T>; pub struct Pkcs7;
/// Reference to `Pkcs7` /// Reference to `Pkcs7`
pub struct Pkcs7Ref<T>; pub struct Pkcs7Ref;
} }
bitflags! { bitflags! {
pub struct PKCS7Flags: c_int { pub struct Pkcs7Flags: c_int {
const PKCS7_TEXT = ffi::PKCS7_TEXT; const TEXT = ffi::PKCS7_TEXT;
const PKCS7_NOCERTS = ffi::PKCS7_NOCERTS; const NOCERTS = ffi::PKCS7_NOCERTS;
const PKCS7_NOSIGS = ffi::PKCS7_NOSIGS; const NOSIGS = ffi::PKCS7_NOSIGS;
const PKCS7_NOCHAIN = ffi::PKCS7_NOCHAIN; const NOCHAIN = ffi::PKCS7_NOCHAIN;
const PKCS7_NOINTERN = ffi::PKCS7_NOINTERN; const NOINTERN = ffi::PKCS7_NOINTERN;
const PKCS7_NOVERIFY = ffi::PKCS7_NOVERIFY; const NOVERIFY = ffi::PKCS7_NOVERIFY;
const PKCS7_DETACHED = ffi::PKCS7_DETACHED; const DETACHED = ffi::PKCS7_DETACHED;
const PKCS7_BINARY = ffi::PKCS7_BINARY; const BINARY = ffi::PKCS7_BINARY;
const PKCS7_NOATTR = ffi::PKCS7_NOATTR; const NOATTR = ffi::PKCS7_NOATTR;
const PKCS7_NOSMIMECAP = ffi::PKCS7_NOSMIMECAP; const NOSMIMECAP = ffi::PKCS7_NOSMIMECAP;
const PKCS7_NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE; const NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE;
const PKCS7_CRLFEOL = ffi::PKCS7_CRLFEOL; const CRLFEOL = ffi::PKCS7_CRLFEOL;
const PKCS7_STREAM = ffi::PKCS7_STREAM; const STREAM = ffi::PKCS7_STREAM;
const PKCS7_NOCRL = ffi::PKCS7_NOCRL; const NOCRL = ffi::PKCS7_NOCRL;
const PKCS7_PARTIAL = ffi::PKCS7_PARTIAL; const PARTIAL = ffi::PKCS7_PARTIAL;
const PKCS7_REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST; const REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST;
#[cfg(not(any(ossl101, ossl102, libressl)))] #[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<Public> { 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<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! { from_pem! {
/// Deserializes a PEM-encoded PKCS#7 signature /// Deserializes a PEM-encoded PKCS#7 signature
/// ///
@ -121,31 +58,32 @@ impl Pkcs7<Public> {
/// ///
/// [`PEM_read_bio_PKCS7`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PKCS7.html /// [`PEM_read_bio_PKCS7`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PKCS7.html
from_pem, from_pem,
Pkcs7<Public>, Pkcs7,
ffi::PEM_read_bio_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 /// This corresponds to [`SMIME_read_PKCS7`].
/// certificate.
/// ///
/// Returns the decrypted message. /// [`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<Vec<u8>>), ErrorStack> {
/// 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(); ffi::init();
let output = MemBio::new()?; let input_bio = MemBioSlice::new(input)?;
let mut bcont_bio = null_mut();
unsafe { unsafe {
cvt(ffi::PKCS7_decrypt(self.0, pkey.as_ptr(), cert.as_ptr(), output.as_ptr(), 0)) let pkcs7= cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio))?;
.and(Ok(output.get_buf().to_owned())) 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<Public> {
/// This corresponds to [`PKCS7_encrypt`]. /// This corresponds to [`PKCS7_encrypt`].
/// ///
/// [`PKCS7_encrypt`]: https://www.openssl.org/docs/man1.0.2/crypto/PKCS7_encrypt.html /// [`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> { pub fn encrypt(certs: &Stack<X509>, input: &[u8], cipher: Cipher, flags: Pkcs7Flags) -> Result<Self, ErrorStack> {
ffi::init(); ffi::init();
let input_bio = MemBioSlice::new(input)?; let input_bio = MemBioSlice::new(input)?;
@ -188,7 +126,7 @@ impl Pkcs7<Public> {
pkey: &PKeyRef<PT>, pkey: &PKeyRef<PT>,
certs: &Stack<X509>, certs: &Stack<X509>,
input: &[u8], input: &[u8],
flags: PKCS7Flags flags: Pkcs7Flags
) -> Result<Self, ErrorStack> ) -> Result<Self, ErrorStack>
where where
PT: HasPrivate PT: HasPrivate
@ -206,6 +144,72 @@ impl Pkcs7<Public> {
).map(|p| Pkcs7::from_ptr(p)) ).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<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.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<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.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`. /// Verifies the PKCS#7 `signedData` structure contained by `&self`.
/// ///
@ -223,8 +227,8 @@ impl Pkcs7<Public> {
store: &X509Store, store: &X509Store,
indata: Option<&[u8]>, indata: Option<&[u8]>,
out: Option<&mut Vec<u8>>, out: Option<&mut Vec<u8>>,
flags: PKCS7Flags flags: Pkcs7Flags
) -> Result<bool, ErrorStack> { ) -> Result<(), ErrorStack> {
ffi::init(); ffi::init();
let out_bio = MemBio::new()?; let out_bio = MemBio::new()?;
@ -237,13 +241,13 @@ impl Pkcs7<Public> {
let result = unsafe { let result = unsafe {
cvt(ffi::PKCS7_verify( cvt(ffi::PKCS7_verify(
self.0, self.as_ptr(),
certs.as_ptr(), certs.as_ptr(),
store.as_ptr(), store.as_ptr(),
indata_bio_ptr, indata_bio_ptr,
out_bio.as_ptr(), out_bio.as_ptr(),
flags.bits)) flags.bits))
.map(|r| r == 1) .map(|_r| ())
}; };
if let Some(data) = out { if let Some(data) = out {
@ -260,8 +264,8 @@ mod tests {
use x509::X509; use x509::X509;
use x509::store::X509StoreBuilder; use x509::store::X509StoreBuilder;
use symm::Cipher; use symm::Cipher;
use pkcs7::{Pkcs7, PKCS7Flags}; use pkcs7::{Pkcs7, Pkcs7Flags};
use pkey::{PKey, Public}; use pkey::PKey;
use stack::Stack; use stack::Stack;
#[test] #[test]
@ -272,7 +276,7 @@ mod tests {
certs.push(cert.clone()).unwrap(); certs.push(cert.clone()).unwrap();
let message: String = String::from("foo"); let message: String = String::from("foo");
let cypher = Cipher::des_ede3_cbc(); let cypher = Cipher::des_ede3_cbc();
let flags = PKCS7Flags::PKCS7_STREAM; let flags = Pkcs7Flags::STREAM;
let pkey = include_bytes!("../test/key.pem"); let pkey = include_bytes!("../test/key.pem");
let pkey = PKey::private_key_from_pem(pkey).unwrap(); 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 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()).expect("should succeed");
let pkcs7_decoded = Pkcs7::from_smime(encrypted.as_slice(), &mut bcount).expect("should succeed");
let decoded = pkcs7_decoded.decrypt(&pkey, &cert).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 cert = X509::from_pem(cert).unwrap();
let certs = Stack::new().unwrap(); let certs = Stack::new().unwrap();
let message: String = String::from("foo"); 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 = include_bytes!("../test/key.pem");
let pkey = PKey::private_key_from_pem(pkey).unwrap(); let pkey = PKey::private_key_from_pem(pkey).unwrap();
let mut store_builder = X509StoreBuilder::new().expect("should succeed"); 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"); let signed = pkcs7.to_smime(message.as_bytes(), flags).expect("should succeed");
println!("{:?}", String::from_utf8(signed.clone()).unwrap()); println!("{:?}", String::from_utf8(signed.clone()).unwrap());
let mut bcount = Vec::new(); let (pkcs7_decoded, content) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
let pkcs7_decoded = Pkcs7::from_smime(signed.as_slice(), &mut bcount).expect("should succeed");
let mut output = Vec::new(); 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"); .expect("should succeed");
assert!(result);
assert_eq!(message.clone().into_bytes(), output); 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] #[test]
@ -327,7 +328,7 @@ mod tests {
let cert = X509::from_pem(cert).unwrap(); let cert = X509::from_pem(cert).unwrap();
let certs = Stack::new().unwrap(); let certs = Stack::new().unwrap();
let message: String = String::from("foo"); let message: String = String::from("foo");
let flags = PKCS7Flags::PKCS7_STREAM; let flags = Pkcs7Flags::STREAM;
let pkey = include_bytes!("../test/key.pem"); let pkey = include_bytes!("../test/key.pem");
let pkey = PKey::private_key_from_pem(pkey).unwrap(); let pkey = PKey::private_key_from_pem(pkey).unwrap();
let mut store_builder = X509StoreBuilder::new().expect("should succeed"); 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 signed = pkcs7.to_smime(message.as_bytes(), flags).expect("should succeed");
let mut bcount = Vec::new(); let (pkcs7_decoded, content) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed");
let pkcs7_decoded = Pkcs7::<Public>::from_smime(signed.as_slice(), &mut bcount).expect("should succeed");
let mut output = Vec::new(); 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); assert_eq!(message.clone().into_bytes(), output);
let empty: Vec<u8> = Vec::new(); assert!(content.is_none());
assert_eq!(empty, bcount);
} }
#[test] #[test]
fn invalid_from_smime() { fn invalid_from_smime() {
let input = String::from("Invalid SMIME Message"); let input = String::from("Invalid SMIME Message");
let mut bcount = Vec::new(); let result = Pkcs7::from_smime(input.as_bytes());
let result = Pkcs7::from_smime(input.as_bytes(), &mut bcount);
assert_eq!(result.is_err(), true) assert_eq!(result.is_err(), true)
} }

View File

@ -94,8 +94,6 @@ fn main() {
s == "PasswordCallback" || s == "pem_password_cb" || s == "bio_info_cb" || s.starts_with("CRYPTO_EX_") s == "PasswordCallback" || s == "pem_password_cb" || s == "bio_info_cb" || s.starts_with("CRYPTO_EX_")
}); });
cfg.skip_struct(|s| s == "ProbeResult"); 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| { cfg.skip_fn(move |s| {
s == "CRYPTO_memcmp" || // uses volatile s == "CRYPTO_memcmp" || // uses volatile