use ring::digest;
use std::borrow::Cow;
pub enum Hmac {
SHA1,
SHA256,
SHA384,
SHA512,
}
impl Hmac {
fn blocksize(&self) -> usize {
match *self {
Hmac::SHA1 => 64,
Hmac::SHA256 => 64,
Hmac::SHA384 => 128,
Hmac::SHA512 => 128,
}
}
fn hash(&self, data: &[u8]) -> digest::Digest {
let method = match *self {
Hmac::SHA1 => &digest::SHA1,
Hmac::SHA256 => &digest::SHA256,
Hmac::SHA384 => &digest::SHA384,
Hmac::SHA512 => &digest::SHA512,
};
digest::digest(method, data)
}
fn pad_key<'a>(&self, key: &'a [u8]) -> Cow<'a, [u8]> {
let mut key = Cow::from(key);
if key.len() > self.blocksize() {
key = self.hash(&key).as_ref().to_vec().into();
}
if key.len() < self.blocksize() {
let mut resized_key = key.into_owned();
resized_key.resize(self.blocksize(), 0x00);
key = resized_key.into();
}
key
}
pub fn hmac_compute(&self, key: &[u8], message: &[u8]) -> Vec<u8> {
let key = self.pad_key(key);
let make_padded_key = |byte: u8| {
let mut pad = key.to_vec();
for i in &mut pad { *i ^= byte };
pad
};
let mut ipad = make_padded_key(0x36);
let mut opad = make_padded_key(0x5C);
ipad.extend_from_slice(message);
opad.extend_from_slice(self.hash(&ipad).as_ref());
self.hash(&opad).as_ref().to_vec()
}
pub fn hmac_validate(&self, key: &[u8], message: &[u8], hmac: &Vec<u8>) -> bool {
let check = self.hmac_compute(&key, &message);
if &check == hmac {
true
} else {
false
}
}
}
#[cfg(test)]
mod test {
use hmac::Hmac;
use ring::test;
use functions;
#[test]
fn test_pad_key_sha1() {
let rand_k: Vec<u8> = functions::gen_rand_key(67);
let rand_k2: Vec<u8> = functions::gen_rand_key(130);
let rand_k3: Vec<u8> = functions::gen_rand_key(34);
assert_eq!(Hmac::SHA1.pad_key(&rand_k).len(), Hmac::SHA1.blocksize());
assert_eq!(Hmac::SHA1.pad_key(&rand_k2).len(), Hmac::SHA1.blocksize());
assert_eq!(Hmac::SHA1.pad_key(&rand_k3).len(), Hmac::SHA1.blocksize());
}
#[test]
fn test_pad_key_sha256() {
let rand_k: Vec<u8> = functions::gen_rand_key(67);
let rand_k2: Vec<u8> = functions::gen_rand_key(130);
let rand_k3: Vec<u8> = functions::gen_rand_key(34);
assert_eq!(Hmac::SHA256.pad_key(&rand_k).len(), Hmac::SHA256.blocksize());
assert_eq!(Hmac::SHA256.pad_key(&rand_k2).len(), Hmac::SHA256.blocksize());
assert_eq!(Hmac::SHA256.pad_key(&rand_k3).len(), Hmac::SHA256.blocksize());
}
#[test]
fn test_pad_key_sha384() {
let rand_k: Vec<u8> = functions::gen_rand_key(67);
let rand_k2: Vec<u8> = functions::gen_rand_key(130);
let rand_k3: Vec<u8> = functions::gen_rand_key(34);
assert_eq!(Hmac::SHA384.pad_key(&rand_k).len(), Hmac::SHA384.blocksize());
assert_eq!(Hmac::SHA384.pad_key(&rand_k2).len(), Hmac::SHA384.blocksize());
assert_eq!(Hmac::SHA384.pad_key(&rand_k3).len(), Hmac::SHA384.blocksize());
}
#[test]
fn test_pad_key_sha512() {
let rand_k: Vec<u8> = functions::gen_rand_key(67);
let rand_k2: Vec<u8> = functions::gen_rand_key(130);
let rand_k3: Vec<u8> = functions::gen_rand_key(34);
assert_eq!(Hmac::SHA512.pad_key(&rand_k).len(), Hmac::SHA512.blocksize());
assert_eq!(Hmac::SHA512.pad_key(&rand_k2).len(), Hmac::SHA512.blocksize());
assert_eq!(Hmac::SHA512.pad_key(&rand_k3).len(), Hmac::SHA512.blocksize());
}
#[test]
fn test_hmac_compute_result() {
let key = vec![0x61; 5];
let message = vec![0x61; 5];
let actual_sha1 = Hmac::SHA1.hmac_compute(&key, &message);
let actual_sha256 = Hmac::SHA256.hmac_compute(&key, &message);
let actual_sha384 = Hmac::SHA384.hmac_compute(&key, &message);
let actual_sha512 = Hmac::SHA512.hmac_compute(&key, &message);
let expected_sha1 = test::from_hex("40a50a7b74cf6099ee7082e3b4e2fd51f002f29d").unwrap();
let expected_sha256 = test::from_hex("c960dd5485480f51044c1afa312fecc5ab58548f9f108a5062a3bc229fd02359").unwrap();
let expected_sha384 = test::from_hex("6b0d10e1f341c5d9d9c3fb59431ee2ba155b5fa75e25a73bcd418d8a8a45c9562741a1214537fc33b08db20a1d52e037").unwrap();
let expected_sha512 = test::from_hex("aaffe2e33265ab09d1f971dc8ee821a996e57264658a805317caabeb5b93321e4e4dacb366670fb34867a4d0359b07f5e9ee7e681c650c7301cc9bf89f4a1adf").unwrap();
assert_eq!(actual_sha1, expected_sha1);
assert_eq!(actual_sha256, expected_sha256);
assert_eq!(actual_sha384, expected_sha384);
assert_eq!(actual_sha512, expected_sha512);
}
#[test]
fn test_hmac_validate() {
let key = vec![0x61; 5];
let message = vec![0x62; 5];
let wrong_key = vec![0x67; 5];
let recieved_sha1 = Hmac::SHA1.hmac_compute(&key, &message);
let recieved_sha256 = Hmac::SHA256.hmac_compute(&key, &message);
let recieved_sha384 = Hmac::SHA384.hmac_compute(&key, &message);
let recieved_sha512 = Hmac::SHA512.hmac_compute(&key, &message);
assert_eq!(Hmac::SHA1.hmac_validate(&key, &message, &recieved_sha1), true);
assert_eq!(Hmac::SHA1.hmac_validate(&wrong_key, &message, &recieved_sha1), false);
assert_eq!(Hmac::SHA256.hmac_validate(&key, &message, &recieved_sha256), true);
assert_eq!(Hmac::SHA256.hmac_validate(&wrong_key, &message, &recieved_sha256), false);
assert_eq!(Hmac::SHA384.hmac_validate(&key, &message, &recieved_sha384), true);
assert_eq!(Hmac::SHA384.hmac_validate(&wrong_key, &message, &recieved_sha384), false);
assert_eq!(Hmac::SHA512.hmac_validate(&key, &message, &recieved_sha512), true);
assert_eq!(Hmac::SHA512.hmac_validate(&wrong_key, &message, &recieved_sha512), false);
}
}