feat(sha): add hmac
This commit is contained in:
parent
38f023e465
commit
e658bde37b
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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::*;
|
|
@ -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
|
Loading…
Reference in New Issue