use super::Algorithm;
use std::io;
use std::ptr;
use winapi::shared::minwindef::DWORD;
use winapi::um::wincrypt::{
CryptAcquireContextW, CryptCreateHash, CryptDestroyHash, CryptGetHashParam, CryptHashData,
CryptReleaseContext, ALG_ID, CALG_MD5, CALG_SHA1, CALG_SHA_256, CALG_SHA_512, CRYPT_SILENT,
CRYPT_VERIFYCONTEXT, HCRYPTHASH, HCRYPTPROV, HP_HASHVAL, PROV_RSA_AES,
};
macro_rules! call {
($e:expr) => {{
if $e == 0 {
panic!("failed {}: {}", stringify!($e), io::Error::last_os_error())
}
}};
}
macro_rules! finish_algorithm {
($func_name: ident, $size: ident) => {
fn $func_name(&mut self) -> Vec<u8> {
let mut len = $size as u32;
let mut hash = [0u8; $size];
call!(unsafe {
CryptGetHashParam(self.hcrypthash, HP_HASHVAL, hash.as_mut_ptr(), &mut len, 0)
});
assert_eq!(len as usize, hash.len());
hash.to_vec()
}
}
}
const MD5_LENGTH: usize = 16;
const SHA1_LENGTH: usize = 20;
const SHA256_LENGTH: usize = 32;
const SHA512_LENGTH: usize = 64;
pub struct Hasher {
alg_id: ALG_ID,
hcryptprov: HCRYPTPROV,
hcrypthash: HCRYPTHASH,
}
impl Hasher {
pub fn new(algorithm: Algorithm) -> Hasher {
let mut hcp = 0;
call!(unsafe {
CryptAcquireContextW(
&mut hcp,
ptr::null(),
ptr::null(),
PROV_RSA_AES,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT,
)
});
let alg_id = match algorithm {
Algorithm::MD5 => CALG_MD5,
Algorithm::SHA1 => CALG_SHA1,
Algorithm::SHA256 => CALG_SHA_256,
Algorithm::SHA512 => CALG_SHA_512,
};
let mut hasher = Hasher {
alg_id,
hcryptprov: hcp,
hcrypthash: 0,
};
call!(unsafe {
CryptCreateHash(
hasher.hcryptprov,
hasher.alg_id,
0,
0,
&mut hasher.hcrypthash,
)
});
hasher
}
pub fn finish(&mut self) -> Vec<u8> {
match self.alg_id {
CALG_MD5 => self.finish_md5(),
CALG_SHA1 => self.finish_sha1(),
CALG_SHA_256 => self.finish_sha256(),
CALG_SHA_512 => self.finish_sha512(),
_ => panic!("Unknown algorithm {}", self.alg_id),
}
}
finish_algorithm!(finish_md5, MD5_LENGTH);
finish_algorithm!(finish_sha1, SHA1_LENGTH);
finish_algorithm!(finish_sha256, SHA256_LENGTH);
finish_algorithm!(finish_sha512, SHA512_LENGTH);
}
impl io::Write for Hasher {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
call!(unsafe {
CryptHashData(
self.hcrypthash,
buf.as_ptr() as *mut _,
buf.len() as DWORD,
0,
)
});
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Drop for Hasher {
fn drop(&mut self) {
if self.hcrypthash != 0 {
call!(unsafe { CryptDestroyHash(self.hcrypthash) });
}
call!(unsafe { CryptReleaseContext(self.hcryptprov, 0) });
}
}