#![allow(dead_code)]
use std::io;
use std::mem;
use std::ptr;
use winapi::shared::minwindef as winapi;
use winapi::shared::ntdef;
use winapi::um::wincrypt;
use cert_context::CertContext;
use Inner;
lazy_static! {
static ref szOID_OIWSEC_sha1: Vec<u8> =
wincrypt::szOID_OIWSEC_sha1.bytes().chain(Some(0)).collect();
}
pub struct CtlContext(wincrypt::PCCTL_CONTEXT);
unsafe impl Send for CtlContext {}
unsafe impl Sync for CtlContext {}
impl Drop for CtlContext {
fn drop(&mut self) {
unsafe {
wincrypt::CertFreeCTLContext(self.0);
}
}
}
impl Inner<wincrypt::PCCTL_CONTEXT> for CtlContext {
unsafe fn from_inner(t: wincrypt::PCCTL_CONTEXT) -> CtlContext {
CtlContext(t)
}
fn as_inner(&self) -> wincrypt::PCCTL_CONTEXT {
self.0
}
fn get_mut(&mut self) -> &mut wincrypt::PCCTL_CONTEXT {
&mut self.0
}
}
impl CtlContext {
pub fn builder() -> Builder {
Builder {
certificates: vec![],
usages: vec![],
}
}
}
pub struct Builder {
certificates: Vec<CertContext>,
usages: Vec<Vec<u8>>,
}
impl Builder {
pub fn certificate(&mut self, cert: CertContext) -> &mut Builder {
self.certificates.push(cert);
self
}
pub fn usage(&mut self, usage: &str) -> &mut Builder {
let mut usage = usage.as_bytes().to_owned();
usage.push(0);
self.usages.push(usage);
self
}
pub fn encode_and_sign(&self) -> io::Result<Vec<u8>> {
unsafe {
let encoding = wincrypt::X509_ASN_ENCODING | wincrypt::PKCS_7_ASN_ENCODING;
let mut usages = self.usages.iter().map(|u| u.as_ptr()).collect::<Vec<_>>();
let mut entry_data = vec![];
let mut entries = vec![];
for certificate in &self.certificates {
let data = try!(cert_entry(certificate));
entries.push(*(data.as_ptr() as *const wincrypt::CTL_ENTRY));
entry_data.push(data);
}
let mut ctl_info: wincrypt::CTL_INFO = mem::zeroed();
ctl_info.dwVersion = wincrypt::CTL_V1;
ctl_info.SubjectUsage.cUsageIdentifier = usages.len() as winapi::DWORD;
ctl_info.SubjectUsage.rgpszUsageIdentifier = usages.as_mut_ptr() as *mut ntdef::LPSTR;
ctl_info.SubjectAlgorithm.pszObjId = szOID_OIWSEC_sha1.as_ptr() as ntdef::LPSTR;
ctl_info.cCTLEntry = entries.len() as winapi::DWORD;
ctl_info.rgCTLEntry = entries.as_mut_ptr();
let mut sign_info: wincrypt::CMSG_SIGNED_ENCODE_INFO = mem::zeroed();
sign_info.cbSize = mem::size_of_val(&sign_info) as winapi::DWORD;
let mut encoded_certs = self.certificates
.iter()
.map(|c| {
wincrypt::CERT_BLOB {
cbData: (*c.as_inner()).cbCertEncoded,
pbData: (*c.as_inner()).pbCertEncoded,
}
})
.collect::<Vec<_>>();
sign_info.rgCertEncoded = encoded_certs.as_mut_ptr();
sign_info.cCertEncoded = encoded_certs.len() as winapi::DWORD;
let flags = wincrypt::CMSG_ENCODE_SORTED_CTL_FLAG |
wincrypt::CMSG_ENCODE_HASHED_SUBJECT_IDENTIFIER_FLAG;
let mut size = 0;
let res = wincrypt::CryptMsgEncodeAndSignCTL(encoding,
&mut ctl_info,
&mut sign_info,
flags,
ptr::null_mut(),
&mut size);
if res == winapi::FALSE {
return Err(io::Error::last_os_error())
}
let mut encoded = vec![0; size as usize];
let res = wincrypt::CryptMsgEncodeAndSignCTL(encoding,
&mut ctl_info,
&mut sign_info,
flags,
encoded.as_mut_ptr() as *mut winapi::BYTE,
&mut size);
if res == winapi::FALSE {
return Err(io::Error::last_os_error())
}
Ok(encoded)
}
}
}
fn cert_entry(cert: &CertContext) -> io::Result<Vec<u8>> {
const CTL_ENTRY_FROM_PROP_CHAIN_FLAG: winapi::DWORD = 1;
unsafe {
let mut size = 0;
let res = wincrypt::CertCreateCTLEntryFromCertificateContextProperties(
cert.as_inner(),
0,
ptr::null_mut(),
CTL_ENTRY_FROM_PROP_CHAIN_FLAG,
ptr::null_mut(),
ptr::null_mut(),
&mut size);
if res == winapi::FALSE {
return Err(io::Error::last_os_error());
}
let mut entry = vec![0u8; size as usize];
let res = wincrypt::CertCreateCTLEntryFromCertificateContextProperties(
cert.as_inner(),
0,
ptr::null_mut(),
CTL_ENTRY_FROM_PROP_CHAIN_FLAG,
ptr::null_mut(),
entry.as_mut_ptr() as wincrypt::PCTL_ENTRY,
&mut size);
if res == winapi::FALSE {
Err(io::Error::last_os_error())
} else {
Ok(entry)
}
}
}