feat(sha): add hmac

This commit is contained in:
MedzikUser 2022-06-28 22:50:19 +02:00
parent 38f023e465
commit e658bde37b
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
6 changed files with 230 additions and 19 deletions

View File

@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
<!-- next-header -->
## [Unreleased]
### Features
- **sha**: Added HMAC Sha1, HMAC Sha256 and HMAC Sha512
## [0.3.0] - 2022-06-23
- updated dependencies

18
Cargo.lock generated
View File

@ -86,10 +86,12 @@ dependencies = [
"anyhow",
"chrono",
"hex",
"hmac",
"jsonwebtoken",
"serde",
"sha1",
"sha2",
"thiserror",
]
[[package]]
@ -100,6 +102,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -118,6 +121,15 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "itoa"
version = "1.0.2"
@ -332,6 +344,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.98"

View File

@ -13,15 +13,17 @@ edition = "2021"
[features]
default = ["full"]
full = ["sha", "jwt"]
sha = ["sha1", "sha2"]
sha = ["sha1", "sha2", "hmac"]
jwt = ["chrono", "serde", "jsonwebtoken"]
[dependencies]
sha1 = { version = "0.10.1", optional = true }
sha2 = { version = "0.10.2", optional = true }
hmac = { version = "0.12.1", optional = true }
chrono = { version = "0.4.19", optional = true }
serde = { version = "1.0.137", optional = true }
jsonwebtoken = { version = "8.1.1", optional = true }
thiserror = "1.0.31"
[dev-dependencies]
anyhow = "1.0.58"

184
src/sha/mac.rs Normal file
View File

@ -0,0 +1,184 @@
use hmac::{Hmac, Mac};
use sha1::Sha1;
use sha2::{Sha256, Sha512};
use thiserror::Error;
/// Custom error type
#[derive(Debug, Error)]
pub enum Error {
/// Invalid HMAC Key
#[error("invalid key")]
InvalidKey,
}
/// Alias to a `Resuly<T, Error>` with the cutom [Error].
pub type Result<T> = std::result::Result<T, Error>;
/// 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).
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())
}
}

20
src/sha/mod.rs Normal file
View File

@ -0,0 +1,20 @@
//! Module for creating sha1, sha256 and sha512 hashes.
//!
//! ```no_run
//! use crypto_utils::sha::{Algorithm, CryptographicHash};
//!
//! // sha1
//! CryptographicHash::hash(Algorithm::SHA1, b"P@ssw0rd");
//!
//! // sha256
//! CryptographicHash::hash(Algorithm::SHA256, b"P@ssw0rd");
//!
//! // sha512
//! CryptographicHash::hash(Algorithm::SHA512, b"P@ssw0rd");
//! ```
mod mac;
mod sha;
pub use mac::*;
pub use sha::*;

View File

@ -1,18 +1,3 @@
//! Module for creating sha1, sha256 and sha512 hashes.
//!
//! ```no_run
//! use crypto_utils::sha::{Algorithm, CryptographicHash};
//!
//! // sha1
//! CryptographicHash::hash(Algorithm::SHA1, b"P@ssw0rd");
//!
//! // sha256
//! CryptographicHash::hash(Algorithm::SHA256, b"P@ssw0rd");
//!
//! // sha512
//! CryptographicHash::hash(Algorithm::SHA512, b"P@ssw0rd");
//! ```
use sha1::{Digest, Sha1};
use sha2::{Sha256, Sha512};
@ -144,12 +129,12 @@ impl CryptographicHash {
/// use crypto_utils::sha::{Algorithm, CryptographicHash};
///
/// // compute hash
/// let mut hash_bytes = CryptographicHash::hash(Algorithm::SHA1, b"P@ssw0rd");
/// let hash_bytes: Vec<u8> = CryptographicHash::hash(Algorithm::SHA1, b"P@ssw0rd");
///
/// // decode hash to a String
/// let hash = hex::encode(hash_bytes);
/// let hash: String = hex::encode(hash_bytes);
///
/// assert_eq!(hash, "21bd12dc183f740ee76f27b78eb39c8ad972a757".to_string())
/// # assert_eq!(hash, "21bd12dc183f740ee76f27b78eb39c8ad972a757".to_string())
/// ```
pub fn hash(algo: Algorithm, input: &[u8]) -> Vec<u8> {
// create hasher