rust-crypto-utils/src/sha/mac.rs

207 lines
6.3 KiB
Rust

use hmac::{Hmac, Mac};
use sha1::Sha1;
use sha2::{Sha256, Sha512};
use super::{Error, Result};
/// HMAC hashing algorithms
pub enum AlgorithmMac {
/// Read about HMAC in [wikipedia](https://en.wikipedia.org/wiki/HMAC)
HmacSHA1,
/// Read about HMAC in [wikipedia](https://en.wikipedia.org/wiki/HMAC)
HmacSHA256,
/// Read about HMAC in [wikipedia](https://en.wikipedia.org/wiki/HMAC)
HmacSHA512,
}
/// Compute cryptographic hash from bytes (HMAC Sha1, HMAC Sha256, HMAC Sha512).
///
/// ## Method 1 (recommend)
/// ```
/// use crypto_utils::sha::{AlgorithmMac, CryptographicMac};
///
/// // compute hash
/// let hash_bytes: Vec<u8> = CryptographicMac::hash(AlgorithmMac::HmacSHA1, b"secret", b"input").unwrap();
///
/// // decode hash to a String
/// let hash: String = hex::encode(hash_bytes);
///
/// # assert_eq!(hash, "30440f36ddc2809bbd4c8b1f37a6e80d7588c303".to_string())
/// ```
///
/// ## Method 2
/// ```
/// use crypto_utils::sha::{AlgorithmMac, CryptographicMac};
///
/// // create a new hasher
/// let mut hasher = CryptographicMac::new(AlgorithmMac::HmacSHA1, b"secret").unwrap();
///
/// // set value in hasher
/// hasher.update(b"input");
///
/// // compute hash
/// let hash_bytes: Vec<u8> = hasher.finalize();
///
/// // decode hash to a String
/// let hash: String = hex::encode(hash_bytes);
///
/// # assert_eq!(hash, "30440f36ddc2809bbd4c8b1f37a6e80d7588c303".to_string())
/// ```
pub enum CryptographicMac {
/// HMAC Sha1 hasher
HmacSha1(Hmac<Sha1>),
/// HMAC Sha256 hasher
HmacSha256(Hmac<Sha256>),
/// HMAC Sha512 hasher
HmacSha512(Hmac<Sha512>),
}
impl CryptographicMac {
/// Create a new HMAC Sha hasher.
///
/// ```no_run
/// use crypto_utils::sha::{AlgorithmMac, CryptographicMac};
///
/// // Hmac Sha1
/// let mut hasher = CryptographicMac::new(AlgorithmMac::HmacSHA1, b"secret").unwrap();
///
/// // Hmac Sha256
/// let mut hasher = CryptographicMac::new(AlgorithmMac::HmacSHA256, b"secret").unwrap();
///
/// // Hmac Sha512
/// let mut hasher = CryptographicMac::new(AlgorithmMac::HmacSHA512, b"secret").unwrap();
/// ```
pub fn new(algo: AlgorithmMac, key: &[u8]) -> Result<Self> {
Ok(match algo {
AlgorithmMac::HmacSHA1 => {
Self::HmacSha1(Hmac::<Sha1>::new_from_slice(key).map_err(|_| Error::InvalidKey)?)
}
AlgorithmMac::HmacSHA256 => Self::HmacSha256(
Hmac::<Sha256>::new_from_slice(key).map_err(|_| Error::InvalidKey)?,
),
AlgorithmMac::HmacSHA512 => Self::HmacSha512(
Hmac::<Sha512>::new_from_slice(key).map_err(|_| Error::InvalidKey)?,
),
})
}
/// Set value in the hasher
///
/// ```no_run
/// # use crypto_utils::sha::{AlgorithmMac, CryptographicMac};
/// #
/// # let mut hasher = CryptographicMac::new(AlgorithmMac::HmacSHA1, b"secret").unwrap();
/// #
/// hasher.update(b"value");
/// ```
pub fn update(&mut self, input: &[u8]) {
match self {
// Sha1
Self::HmacSha1(sha1) => sha1.update(input),
// Sha256
Self::HmacSha256(sha256) => sha256.update(input),
// Sha512
Self::HmacSha512(sha512) => sha512.update(input),
}
}
/// Compute hash
///
/// ```no_run
/// # use crypto_utils::sha::{AlgorithmMac, CryptographicMac};
/// #
/// # let mut hasher = CryptographicMac::new(AlgorithmMac::HmacSHA1, b"secret").unwrap();
/// #
/// # hasher.update(b"value");
/// let hash: Vec<u8> = hasher.finalize();
/// let hash_str: String = hex::encode(hash);
/// ```
pub fn finalize(self) -> Vec<u8> {
match self {
// Sha1
Self::HmacSha1(sha1) => sha1.finalize().into_bytes().to_vec(),
// Sha256
Self::HmacSha256(sha256) => sha256.finalize().into_bytes().to_vec(),
// Sha512
Self::HmacSha512(sha512) => sha512.finalize().into_bytes().to_vec(),
}
}
/// Compute hash using a single function
///
/// ```
/// use crypto_utils::sha::{AlgorithmMac, CryptographicMac};
///
/// let hash_bytes: Vec<u8> = CryptographicMac::hash(AlgorithmMac::HmacSHA1, b"secret", b"P@ssw0rd").unwrap();
///
/// // decode hash to a String
/// let hash: String = hex::encode(hash_bytes);
///
/// # assert_eq!(hash, "20bbb9ec2d4574845911b13695b776097bd46e41".to_string())
/// ```
pub fn hash(algo: AlgorithmMac, secret: &[u8], input: &[u8]) -> Result<Vec<u8>> {
// create hasher
let mut hasher = Self::new(algo, secret)?;
// set value in hasher
hasher.update(input);
// compute hash
Ok(hasher.finalize())
}
}
#[cfg(test)]
mod tests {
use super::{AlgorithmMac, CryptographicMac};
const SECRET: &[u8] = b"secret";
const INPUT: &[u8] = b"input";
// expected hashes
const EXPECTED_HMAC_SHA1: &str = "30440f36ddc2809bbd4c8b1f37a6e80d7588c303";
const EXPECTED_HMAC_SHA256: &str =
"8d8985d04b7abd32cbaa3779a3daa019e0d269a22aec15af8e7296f702cc68c6";
const EXPECTED_HMAC_SHA512: &str =
"2ac95ed3717e042c7064a5fa7c318230cd36d85e06f8ff8373d04ca17e361629e09f46b7f151ff382a3f48c5b19121446e45c2588f0ff1de9f74b0400daef81f";
/// Test a HMAC Sha1 hasher
#[test]
fn hmac_sha1() {
// compute hash
let hash_bytes = CryptographicMac::hash(AlgorithmMac::HmacSHA1, SECRET, INPUT).unwrap();
// decode hash to a String
let hash = hex::encode(hash_bytes);
// validate hash
assert_eq!(hash, EXPECTED_HMAC_SHA1.to_string())
}
/// Test a HMAC Sha256 hasher
#[test]
fn hmac_sha256() {
// compute hash
let hash_bytes = CryptographicMac::hash(AlgorithmMac::HmacSHA256, SECRET, INPUT).unwrap();
// decode hash to a String
let hash = hex::encode(hash_bytes);
// validate hash
assert_eq!(hash, EXPECTED_HMAC_SHA256.to_string())
}
/// Test a HMAC Sha512 hasher
#[test]
fn hmac_sha512() {
// compute hash
let hash_bytes = CryptographicMac::hash(AlgorithmMac::HmacSHA512, SECRET, INPUT).unwrap();
// decode hash to a String
let hash = hex::encode(hash_bytes);
// validate hash
assert_eq!(hash, EXPECTED_HMAC_SHA512.to_string())
}
}