use crate::buffer::Buffer;
use crate::helpers::{AlgoHandle, Handle, WindowsString};
use crate::property::{AlgorithmName, HashLength, InitializationVector, ObjectLength};
use crate::{Error, Result};
use std::convert::TryFrom;
use std::marker::PhantomData;
use std::ptr::null_mut;
use winapi::shared::bcrypt::*;
use winapi::shared::minwindef::{PUCHAR, ULONG};
pub trait AlgorithmKind {
fn to_str(&self) -> &'static str;
}
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq)]
pub enum HashAlgorithmId {
Sha1,
Sha256,
Sha384,
Sha512,
Md2,
Md4,
Md5,
}
impl AlgorithmKind for HashAlgorithmId {
fn to_str(&self) -> &'static str {
HashAlgorithmId::to_str(*self)
}
}
#[derive(Debug, Clone, Copy, PartialOrd, PartialEq)]
pub enum MacAlgorithmId {
AesCmac,
AesGmac,
}
impl AlgorithmKind for MacAlgorithmId {
fn to_str(&self) -> &'static str {
MacAlgorithmId::to_str(*self)
}
}
impl HashAlgorithmId {
pub fn to_str(self) -> &'static str {
match self {
Self::Sha1 => BCRYPT_SHA1_ALGORITHM,
Self::Sha256 => BCRYPT_SHA256_ALGORITHM,
Self::Sha384 => BCRYPT_SHA384_ALGORITHM,
Self::Sha512 => BCRYPT_SHA512_ALGORITHM,
Self::Md2 => BCRYPT_MD2_ALGORITHM,
Self::Md4 => BCRYPT_MD4_ALGORITHM,
Self::Md5 => BCRYPT_MD5_ALGORITHM,
}
}
}
impl<'a> TryFrom<&'a str> for HashAlgorithmId {
type Error = &'a str;
fn try_from(val: &'a str) -> Result<HashAlgorithmId, Self::Error> {
match val {
BCRYPT_SHA1_ALGORITHM => Ok(Self::Sha1),
BCRYPT_SHA256_ALGORITHM => Ok(Self::Sha256),
BCRYPT_SHA384_ALGORITHM => Ok(Self::Sha384),
BCRYPT_SHA512_ALGORITHM => Ok(Self::Sha512),
BCRYPT_MD2_ALGORITHM => Ok(Self::Md2),
BCRYPT_MD4_ALGORITHM => Ok(Self::Md4),
BCRYPT_MD5_ALGORITHM => Ok(Self::Md5),
val => Err(val),
}
}
}
impl MacAlgorithmId {
fn to_str(self) -> &'static str {
match self {
Self::AesCmac => BCRYPT_AES_CMAC_ALGORITHM,
Self::AesGmac => BCRYPT_AES_GMAC_ALGORITHM,
}
}
}
impl<'a> TryFrom<&'a str> for MacAlgorithmId {
type Error = &'a str;
fn try_from(val: &'a str) -> std::result::Result<MacAlgorithmId, Self::Error> {
match val {
BCRYPT_AES_CMAC_ALGORITHM => Ok(Self::AesCmac),
BCRYPT_AES_GMAC_ALGORITHM => Ok(Self::AesGmac),
val => Err(val),
}
}
}
pub struct HashAlgorithm<Kind: AlgorithmKind> {
handle: AlgoHandle,
_kind: PhantomData<Kind>,
}
impl<Kind: AlgorithmKind> HashAlgorithm<Kind> {
pub fn open(id: Kind) -> Result<Self> {
let handle = AlgoHandle::open(id.to_str())?;
Ok(Self {
handle,
_kind: PhantomData,
})
}
fn create_hash(&self, secret: Option<&[u8]>, iv: Option<&[u8]>) -> Result<Hash> {
let (sec_ptr, sec_len) = secret
.map(|x| (x.as_ptr(), x.len()))
.unwrap_or((std::ptr::null(), 0));
let object_size = self.handle.get_property::<ObjectLength>()?;
let mut hash_handle = HashHandle::new();
let mut object = Buffer::new(object_size as usize);
unsafe {
Error::check(BCryptCreateHash(
self.handle.as_ptr(),
hash_handle.as_mut_ptr(),
object.as_mut_ptr(),
object.len() as ULONG,
sec_ptr as *mut _,
sec_len as ULONG,
0,
))?;
};
if let Some(iv) = iv {
hash_handle.set_property::<InitializationVector>(iv)?;
}
Ok(Hash {
handle: hash_handle,
object,
})
}
}
impl HashAlgorithm<HashAlgorithmId> {
pub fn new_hash(&self) -> Result<Hash> {
self.create_hash(None, None)
}
}
impl HashAlgorithm<MacAlgorithmId> {
pub fn new_mac(&self, secret: &[u8], iv: Option<&[u8]>) -> Result<Hash> {
self.create_hash(Some(secret), iv)
}
}
struct HashHandle {
handle: BCRYPT_HASH_HANDLE,
}
unsafe impl Send for HashHandle {}
impl HashHandle {
pub fn new() -> Self {
Self { handle: null_mut() }
}
}
impl Drop for HashHandle {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe {
BCryptDestroyHash(self.handle);
}
}
}
}
impl Handle for HashHandle {
fn as_ptr(&self) -> BCRYPT_HASH_HANDLE {
self.handle
}
fn as_mut_ptr(&mut self) -> *mut BCRYPT_HASH_HANDLE {
&mut self.handle
}
}
pub struct Hash {
handle: HashHandle,
object: Buffer,
}
impl Hash {
pub fn hash(&mut self, data: &[u8]) -> Result<()> {
unsafe {
Error::check(BCryptHashData(
self.handle.as_ptr(),
data.as_ptr() as PUCHAR,
data.len() as ULONG,
0,
))
}
}
pub fn finish(self) -> Result<Buffer> {
let hash_size = self.hash_size()?;
let mut result = Buffer::new(hash_size);
unsafe {
Error::check(BCryptFinishHash(
self.handle.as_ptr(),
result.as_mut_ptr(),
result.len() as ULONG,
0,
))
.map(|_| result)
}
}
pub fn hash_size(&self) -> Result<usize> {
self.handle
.get_property::<HashLength>()
.map(|hash_size| hash_size as usize)
}
pub fn hash_algorithm(&self) -> Result<HashAlgorithmId> {
self.handle
.get_property_unsized::<AlgorithmName>()
.map(|name| {
WindowsString::from_bytes_with_nul(name.as_ref().into())
.expect("API to return 0-terminated wide string")
})
.map(|name| {
HashAlgorithmId::try_from(name.to_string().as_str())
.expect("Windows CNG API to return a correct algorithm name")
})
}
}
impl Clone for Hash {
fn clone(&self) -> Self {
let object_size = self.object.len();
let mut handle = HashHandle::new();
let mut object = Buffer::new(object_size);
Error::check(unsafe {
BCryptDuplicateHash(
self.handle.as_ptr(),
handle.as_mut_ptr(),
object.as_mut_ptr(),
object.len() as ULONG,
0,
)
})
.expect("to always be able to duplicate a valid hash object");
Self { handle, object }
}
}
#[cfg(test)]
mod tests {
use super::*;
const DATA: &'static str = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
#[test]
fn sha1() {
check_hash(
HashAlgorithmId::Sha1,
DATA.as_bytes(),
&[
0x2B, 0x44, 0x89, 0x60, 0x6A, 0x23, 0xFB, 0x31, 0xFC, 0xDC, 0x84, 0x9F, 0xA7, 0xE5,
0x77, 0xBA, 0x90, 0xF6, 0xD3, 0x9A,
],
)
}
#[test]
fn sha256() {
check_hash(
HashAlgorithmId::Sha256,
DATA.as_bytes(),
&[
0x0E, 0xA3, 0x7C, 0x24, 0x3F, 0x60, 0x97, 0x4B, 0x0D, 0x54, 0xC6, 0xB2, 0xD7, 0x6C,
0xEC, 0xE3, 0xF4, 0xC7, 0x42, 0x49, 0x2C, 0xCE, 0x48, 0xEA, 0xF8, 0x1F, 0x35, 0x79,
0x31, 0xD6, 0xD6, 0x9E,
],
)
}
#[test]
fn sha384() {
check_hash(
HashAlgorithmId::Sha384,
DATA.as_bytes(),
&[
0x2A, 0x10, 0x60, 0x89, 0x6A, 0xCB, 0xA9, 0xFA, 0x37, 0x11, 0xBF, 0x10, 0x9E, 0x90,
0x24, 0xEA, 0x19, 0xF5, 0xFC, 0x33, 0xAF, 0x0F, 0x47, 0x15, 0xC3, 0xE9, 0xD8, 0x63,
0xB3, 0x24, 0xA5, 0x08, 0x9F, 0xAB, 0x95, 0x36, 0xB2, 0xAC, 0x10, 0xF6, 0xC1, 0xE7,
0x31, 0x03, 0x09, 0x54, 0x18, 0x41,
],
)
}
#[test]
fn sha512() {
check_hash(
HashAlgorithmId::Sha512,
DATA.as_bytes(),
&[
0x39, 0x50, 0xAC, 0xCD, 0xFE, 0xF7, 0x46, 0x20, 0x71, 0x42, 0x78, 0x76, 0x5B, 0xBD,
0xCE, 0x04, 0xD4, 0x57, 0x90, 0x4B, 0x7C, 0xEA, 0x86, 0x31, 0x39, 0x6C, 0xBA, 0x6D,
0x8B, 0xCE, 0xFC, 0xE0, 0x30, 0x8F, 0xC4, 0x7C, 0xFB, 0x88, 0x5B, 0xC8, 0x9E, 0xBD,
0xF4, 0xFF, 0xA6, 0xF9, 0x8F, 0xC8, 0x51, 0x05, 0x54, 0x7C, 0xBD, 0xDF, 0x56, 0x57,
0xB6, 0xAD, 0xBD, 0xDD, 0xA3, 0x8C, 0xB9, 0xB5,
],
)
}
#[test]
fn md2() {
check_hash(
HashAlgorithmId::Md2,
DATA.as_bytes(),
&[
0x08, 0x18, 0x53, 0xA0, 0x5C, 0x1F, 0x58, 0xC6, 0xED, 0x43, 0x46, 0x4C, 0x79, 0x7D,
0x65, 0x26,
],
)
}
#[test]
fn md4() {
check_hash(
HashAlgorithmId::Md4,
DATA.as_bytes(),
&[
0x24, 0x3C, 0xDA, 0xF5, 0x91, 0x4A, 0xE8, 0x70, 0x91, 0xC7, 0x13, 0xB5, 0xFA, 0x9F,
0xA7, 0x98,
],
)
}
#[test]
fn md5() {
check_hash(
HashAlgorithmId::Md5,
DATA.as_bytes(),
&[
0xE8, 0x89, 0xD8, 0x2D, 0xD1, 0x11, 0xD6, 0x31, 0x5D, 0x7B, 0x1E, 0xDC, 0xE2, 0xB1,
0xB3, 0x0F,
],
)
}
fn check_hash(algo_id: HashAlgorithmId, data: &[u8], expected_hash: &[u8]) {
let algo = HashAlgorithm::open(algo_id).unwrap();
let mut hash = algo.new_hash().unwrap();
let hash_size = hash.hash_size().unwrap();
hash.hash(data).unwrap();
let result = hash.finish().unwrap();
assert_eq!(hash_size, expected_hash.len());
assert_eq!(result.as_slice(), expected_hash);
check_clone_impl(algo_id);
}
fn check_clone_impl(algo_id: HashAlgorithmId) {
let algo = HashAlgorithm::open(algo_id).unwrap();
let mut hash1 = algo.new_hash().unwrap();
hash1.hash(DATA.as_bytes()).unwrap();
let mut hash2 = hash1.clone();
assert_ne!(hash1.handle.as_ptr(), hash2.handle.as_ptr());
const AUX_DATA: &[u8] = &[0xE8, 0x91, 0xD9, 0x12];
hash1.hash(AUX_DATA).unwrap();
hash2.hash(AUX_DATA).unwrap();
let result1 = hash1.finish().unwrap();
let result2 = hash2.finish().unwrap();
assert_eq!(result1, result2);
}
trait HexSlice: std::borrow::Borrow<str> {
fn as_hex(&self) -> Vec<u8> {
let res: Vec<u8> = self
.borrow()
.as_bytes()
.rchunks(2)
.map(|slice| std::str::from_utf8(slice).unwrap())
.map(|chr| u8::from_str_radix(chr, 16).unwrap())
.rev()
.collect();
res
}
}
impl<'a> HexSlice for &'a str {}
#[test]
fn cmac() {
let test_vectors = vec![
("2b7e151628aed2a6abf7158809cf4f3c", "", "bb1d6929e95937287fa37d129b756746"),
("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "070a16b46b4d4144f79bdd9dd04a287c"),
("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411", "dfa66747de9ae63030ca32611497c827"),
("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", "51f0bebf7e3b9d92fc49741779363cfe")
];
for (key, msg, tag) in test_vectors {
let (key, msg, tag) = (&key.as_hex(), &msg.as_hex(), &tag.as_hex());
check_mac(MacAlgorithmId::AesCmac, msg, tag, key);
}
}
#[test]
fn gmac() {
let algo = HashAlgorithm::open(MacAlgorithmId::AesGmac).unwrap();
let key = &"ce8d1103100fa290f953fbb439efdee4".as_hex();
let iv = &"4874c6f8082366fc7e49b933".as_hex();
let mut gmac = algo.new_mac(key, Some(iv)).unwrap();
gmac.hash(&"d69d033c32029789263c689e11ff7e9e8eefc48ddbc4e10eeae1c9edbb44f04e7cc6471501eadda3940ab433d0a8c210".as_hex()).unwrap();
let digest = gmac.finish().unwrap();
assert_eq!(
digest.as_slice(),
&*"a5964b77af0b8aecd844d6adec8b7b1c".as_hex()
);
let key = &"4fedd84c9495e7ff81db48d367305d80".as_hex();
let iv = &"d82bfb016a35b5efa5e3438a".as_hex();
let mut gmac = algo.new_mac(key, Some(iv)).unwrap();
gmac.hash(&"0c80e282e64aeac2fba241686a9b33a6bdbac1230442e79fc5c0b6926158b0bf9b8562b570d784e749b69d64ed17f45e".as_hex()).unwrap();
let digest = gmac.finish().unwrap();
assert_eq!(
digest.as_slice(),
&*"aad8933fdce92b9a24c2a9c2cc367291".as_hex()
);
}
fn check_mac(algo_id: MacAlgorithmId, data: &[u8], expected_hash: &[u8], secret: &[u8]) {
let algo = HashAlgorithm::open(algo_id).unwrap();
let mut hash = algo.new_mac(secret, None).unwrap();
let hash_size = hash.hash_size().unwrap();
hash.hash(data).unwrap();
let result = hash.finish().unwrap();
assert_eq!(hash_size, expected_hash.len());
assert_eq!(result.as_slice(), expected_hash);
}
#[test]
fn send() {
use crate::helpers::assert_send;
assert_send::<HashHandle>();
assert_send::<Hash>();
assert_send::<HashAlgorithm<HashAlgorithmId>>();
}
}