Refactor to match style and add documentation.
This commit is contained in:
parent
cd1d1955d9
commit
2dd3736444
|
|
@ -25,6 +25,7 @@ pub use object::*;
|
||||||
pub use ocsp::*;
|
pub use ocsp::*;
|
||||||
pub use ossl_typ::*;
|
pub use ossl_typ::*;
|
||||||
pub use pem::*;
|
pub use pem::*;
|
||||||
|
pub use pkcs7::*;
|
||||||
pub use pkcs12::*;
|
pub use pkcs12::*;
|
||||||
pub use rand::*;
|
pub use rand::*;
|
||||||
pub use rsa::*;
|
pub use rsa::*;
|
||||||
|
|
@ -61,6 +62,7 @@ mod object;
|
||||||
mod ocsp;
|
mod ocsp;
|
||||||
mod ossl_typ;
|
mod ossl_typ;
|
||||||
mod pem;
|
mod pem;
|
||||||
|
mod pkcs7;
|
||||||
mod pkcs12;
|
mod pkcs12;
|
||||||
mod rand;
|
mod rand;
|
||||||
mod rsa;
|
mod rsa;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ pub enum ASN1_BIT_STRING {}
|
||||||
pub enum ASN1_TIME {}
|
pub enum ASN1_TIME {}
|
||||||
pub enum ASN1_TYPE {}
|
pub enum ASN1_TYPE {}
|
||||||
pub enum ASN1_OBJECT {}
|
pub enum ASN1_OBJECT {}
|
||||||
|
pub enum ASN1_OCTET_STRING {}
|
||||||
|
|
||||||
pub enum bio_st {} // FIXME remove
|
pub enum bio_st {} // FIXME remove
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,15 @@ extern "C" {
|
||||||
cb: pem_password_cb,
|
cb: pem_password_cb,
|
||||||
u: *mut c_void,
|
u: *mut c_void,
|
||||||
) -> *mut EVP_PKEY;
|
) -> *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;
|
pub const PEM_R_NO_START_LINE: c_int = 108;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
pub mod pk7_smime;
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -52,6 +52,7 @@ pub mod nid;
|
||||||
pub mod ocsp;
|
pub mod ocsp;
|
||||||
pub mod pkcs12;
|
pub mod pkcs12;
|
||||||
pub mod pkcs5;
|
pub mod pkcs5;
|
||||||
|
pub mod pkcs7;
|
||||||
pub mod pkey;
|
pub mod pkey;
|
||||||
pub mod rand;
|
pub mod rand;
|
||||||
pub mod rsa;
|
pub mod rsa;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue