Skip to content

Add some additional CMS methods #1034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openssl-sys/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ extern "C" {
stack!(stack_st_ASN1_OBJECT);

extern "C" {
pub fn OBJ_txt2obj(oid: *const c_char, no_name: c_int) -> *mut ASN1_OBJECT;
pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING;
#[cfg(any(ossl110, libressl273))]
pub fn ASN1_STRING_get0_data(x: *const ASN1_STRING) -> *const c_uchar;
Expand Down
47 changes: 43 additions & 4 deletions openssl-sys/src/cms.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use libc::*;

pub enum CMS_ContentInfo {}
pub enum CMS_SignerInfo {}

extern "C" {
#[cfg(ossl101)]
Expand Down Expand Up @@ -55,9 +56,17 @@ pub const CMS_KEY_PARAM: c_uint = 0x40000;
pub const CMS_ASCIICRLF: c_uint = 0x80000;

extern "C" {
#[cfg(ossl101)]
pub fn CMS_decrypt(
cms: *mut ::CMS_ContentInfo,
pkey: *mut ::EVP_PKEY,
cert: *mut ::X509,
dcont: *mut ::BIO,
out: *mut ::BIO,
flags: c_uint,
) -> c_int;
#[cfg(ossl101)]
pub fn SMIME_read_CMS(bio: *mut ::BIO, bcont: *mut *mut ::BIO) -> *mut ::CMS_ContentInfo;

#[cfg(ossl101)]
pub fn CMS_sign(
signcert: *mut ::X509,
Expand All @@ -66,14 +75,44 @@ extern "C" {
data: *mut ::BIO,
flags: c_uint,
) -> *mut ::CMS_ContentInfo;

#[cfg(ossl101)]
pub fn CMS_decrypt(
pub fn CMS_add1_signer(
cms: *mut ::CMS_ContentInfo,
signcert: *mut ::X509,
pkey: *mut ::EVP_PKEY,
cert: *mut ::X509,
md: *const ::EVP_MD,
flags: c_uint,
) -> *mut ::CMS_SignerInfo;
#[cfg(ossl101)]
pub fn CMS_final(
cms: *mut ::CMS_ContentInfo,
data: *mut ::BIO,
dcont: *mut ::BIO,
flags: c_uint,
) -> c_int;
#[cfg(ossl101)]
pub fn CMS_verify(
cms: *mut ::CMS_ContentInfo,
certs: *mut ::stack_st_X509,
store: *mut ::X509_STORE,
indata: *mut ::BIO,
out: *mut ::BIO,
flags: c_uint,
) -> c_int;
#[cfg(ossl101)]
pub fn d2i_CMS_ContentInfo(
a: *mut *mut ::CMS_ContentInfo,
ppin: *mut *const c_uchar,
length: c_long,
) -> *mut ::CMS_ContentInfo;
#[cfg(ossl101)]
pub fn CMS_signed_add1_attr_by_OBJ(
si: *mut ::CMS_SignerInfo,
obj: *const ::ASN1_OBJECT,
bytes_type: c_int,
bytes: *const c_void,
len: c_int,
) -> c_int;
#[cfg(ossl101)]
pub fn CMS_SignerInfo_sign(si: *mut ::CMS_SignerInfo) -> c_int;
}
11 changes: 11 additions & 0 deletions openssl/src/asn1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use ffi;
use foreign_types::{ForeignType, ForeignTypeRef};
use libc::{c_char, c_int, c_long};
use std::ffi::CString;
use std::fmt;
use std::ptr;
use std::slice;
Expand Down Expand Up @@ -273,6 +274,16 @@ foreign_type_and_impl_send_sync! {
pub struct Asn1ObjectRef;
}

impl Asn1Object {
/// Returns an ASN1_object from a given OID in text form.
pub fn from_txt(oid: &str, no_name: bool) -> Asn1Object {
unsafe {
let asn1 = ffi::OBJ_txt2obj(CString::new(oid).unwrap().as_ptr(), no_name as c_int);
Asn1Object::from_ptr(asn1)
}
}
}

impl Asn1ObjectRef {
/// Returns the NID associated with this OID.
pub fn nid(&self) -> Nid {
Expand Down
200 changes: 196 additions & 4 deletions openssl/src/cms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,67 @@
//! Data accepted by this module will be smime type `enveloped-data`.

use ffi;
use foreign_types::{ForeignType, ForeignTypeRef};
use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
use libc::c_void;
use std::ptr;

use asn1::Asn1Object;
use bio::{MemBio, MemBioSlice};
use error::ErrorStack;
use hash::MessageDigest;
use libc::c_uint;
use pkey::{HasPrivate, PKeyRef};
use stack::StackRef;
use x509::{X509, X509Ref};
use {cvt, cvt_p};
use stack::{Stack, StackRef};
use x509::store::X509Store;
use x509::{X509Ref, X509};
use {cvt, cvt_n, cvt_p};

pub struct CmsSignerInfoRef(Opaque);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to use the macro to create these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I'm unable to figure out the correct macro for this.


impl ForeignTypeRef for CmsSignerInfoRef {
type CType = ffi::CMS_SignerInfo;
}

pub struct CmsSignerInfo(*mut ffi::CMS_SignerInfo);

impl ForeignType for CmsSignerInfo {
type CType = ffi::CMS_SignerInfo;
type Ref = CmsSignerInfoRef;

unsafe fn from_ptr(ptr: *mut ffi::CMS_SignerInfo) -> CmsSignerInfo {
CmsSignerInfo(ptr)
}

fn as_ptr(&self) -> *mut ffi::CMS_SignerInfo {
self.0
}
}

impl CmsSignerInfo {
/// Add an attribute to this CmsSignerInfo with the ASN1 type obj.
///
/// `data_type` is an opaque integer.
pub fn add_attr(
&mut self,
obj: &Asn1Object,
data_type: i32,
data: &[u8],
) -> Result<i32, ErrorStack> {
unsafe {
cvt(ffi::CMS_signed_add1_attr_by_OBJ(
self.as_ptr(),
obj.as_ptr(),
data_type,
data.as_ptr() as *const c_void,
data.len() as i32,
))
}
}

pub fn sign(&mut self) -> i32 {
unsafe { ffi::CMS_SignerInfo_sign(self.as_ptr()) }
}
}

bitflags! {
pub struct CMSOptions : c_uint {
Expand Down Expand Up @@ -104,6 +155,120 @@ impl CmsContentInfoRef {
to_der,
ffi::i2d_CMS_ContentInfo
}

from_der! {
/// Deserializes this CmsContentInfo using DER.
///
/// OpenSSL documentation at [`d2i_CMS_ContentInfo`]
///
/// [`i2d_CMS_ContentInfo`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_CMS_ContentInfo.html
from_der,
CmsContentInfo,
ffi::d2i_CMS_ContentInfo
}

/// Add a signer to this CmsContentInfo. This is useful when the CmsContentInfo is
/// created using CmsContentInfo::partial and additional configuration is required
/// for the keypair or digest.
///
/// OpenSSL documentation at [`CMS_add1_signer`]
///
/// [`CMS_add1_signer`]: https://www.openssl.org/docs/man1.1.0/man3/CMS_add1_signer.html
pub fn add_signer<T: HasPrivate>(
&mut self,
signcert: &X509,
pkey: &PKeyRef<T>,
digest: Option<MessageDigest>,
flags: CMSOptions,
) -> Result<CmsSignerInfo, ErrorStack> {
let md_ptr = match digest {
Some(md) => md.as_ptr(),
None => ptr::null_mut(),
};

unsafe {
Ok(ForeignType::from_ptr(cvt_p(ffi::CMS_add1_signer(
self.as_ptr(),
signcert.as_ptr(),
pkey.as_ptr(),
md_ptr,
flags.bits(),
))?))
}
}

pub fn finalize(
&mut self,
data: &[u8],
dcont: Option<&MemBioSlice>,
flags: CMSOptions,
) -> Result<(), ErrorStack> {
unsafe {
let bio = MemBioSlice::new(data)?;
let dcont_ptr = match dcont {
Some(p) => p.as_ptr(),
None => ptr::null_mut(),
};

cvt(ffi::CMS_final(
self.as_ptr(),
bio.as_ptr(),
dcont_ptr,
flags.bits(),
))?;

Ok(())
}
}

/// Verify this CmsContentInfo's signature, given a stack of certificates
/// in certs, an X509 store in store. If the signature is detached, the
/// data can be passed in data. The data sans signature will be copied
/// into output_data if it is present.
///
/// OpenSSL documentation at [`CMS_verify`]
///
/// [`CMS_verify`]: https://www.openssl.org/docs/manmaster/man3/CMS_verify.html
pub fn verify(
&mut self,
certs: Option<&Stack<X509>>,
store: Option<&X509Store>,
data: Option<&[u8]>,
output_data: Option<&mut Vec<u8>>,
flags: CMSOptions,
) -> Result<bool, ErrorStack> {
unsafe {
let certs = match certs {
Some(certs) => certs.as_ptr(),
None => ptr::null_mut(),
};
let store = match store {
Some(store) => store.as_ptr(),
None => ptr::null_mut(),
};
let in_data = match data {
Some(data) => MemBioSlice::new(data)?.as_ptr(),
None => ptr::null_mut(),
};
let out_bio = MemBio::new()?;

let is_valid = cvt_n(ffi::CMS_verify(
self.as_ptr(),
certs,
store,
in_data,
out_bio.as_ptr(),
flags.bits(),
))? == 1;

if let Some(out_data) = output_data {
out_data.clear();
out_data.extend_from_slice(out_bio.get_buf());
};

Ok(is_valid)
}
}
}

impl CmsContentInfo {
Expand Down Expand Up @@ -161,4 +326,31 @@ impl CmsContentInfo {
Ok(CmsContentInfo::from_ptr(cms))
}
}

/// Create a partial CmsContentInfo by only specifying the flags. This is
/// used in conjunction with CmsContentInfo::add_signer to customize the
/// keypair, message digest and flags.
///
/// OpenSSL documentation at [`CMS_sign`]
///
/// [`CMS_sign`]: https://www.openssl.org/docs/manmaster/man3/CMS_sign.html
pub fn partial(
certs: Option<&Stack<X509>>,
flags: CMSOptions,
) -> Result<CmsContentInfo, ErrorStack> {
let certs = match certs {
Some(certs) => certs.as_ptr(),
None => ptr::null_mut(),
};

unsafe {
Ok(CmsContentInfo::from_ptr(cvt_p(ffi::CMS_sign(
ptr::null_mut(),
ptr::null_mut(),
certs,
ptr::null_mut(),
flags.bits(),
))?))
}
}
}