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 -->
|
<!-- next-header -->
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Features
|
||||||
|
- **sha**: Added HMAC Sha1, HMAC Sha256 and HMAC Sha512
|
||||||
|
|
||||||
## [0.3.0] - 2022-06-23
|
## [0.3.0] - 2022-06-23
|
||||||
- updated dependencies
|
- updated dependencies
|
||||||
|
|
|
@ -86,10 +86,12 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"hex",
|
"hex",
|
||||||
|
"hmac",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"serde",
|
"serde",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -100,6 +102,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -118,6 +121,15 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
@ -332,6 +344,12 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.98"
|
version = "1.0.98"
|
||||||
|
|
|
@ -13,15 +13,17 @@ edition = "2021"
|
||||||
[features]
|
[features]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
full = ["sha", "jwt"]
|
full = ["sha", "jwt"]
|
||||||
sha = ["sha1", "sha2"]
|
sha = ["sha1", "sha2", "hmac"]
|
||||||
jwt = ["chrono", "serde", "jsonwebtoken"]
|
jwt = ["chrono", "serde", "jsonwebtoken"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sha1 = { version = "0.10.1", optional = true }
|
sha1 = { version = "0.10.1", optional = true }
|
||||||
sha2 = { version = "0.10.2", optional = true }
|
sha2 = { version = "0.10.2", optional = true }
|
||||||
|
hmac = { version = "0.12.1", optional = true }
|
||||||
chrono = { version = "0.4.19", optional = true }
|
chrono = { version = "0.4.19", optional = true }
|
||||||
serde = { version = "1.0.137", optional = true }
|
serde = { version = "1.0.137", optional = true }
|
||||||
jsonwebtoken = { version = "8.1.1", optional = true }
|
jsonwebtoken = { version = "8.1.1", optional = true }
|
||||||
|
thiserror = "1.0.31"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.58"
|
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 sha1::{Digest, Sha1};
|
||||||
use sha2::{Sha256, Sha512};
|
use sha2::{Sha256, Sha512};
|
||||||
|
|
||||||
|
@ -144,12 +129,12 @@ impl CryptographicHash {
|
||||||
/// use crypto_utils::sha::{Algorithm, CryptographicHash};
|
/// use crypto_utils::sha::{Algorithm, CryptographicHash};
|
||||||
///
|
///
|
||||||
/// // compute hash
|
/// // 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
|
/// // 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> {
|
pub fn hash(algo: Algorithm, input: &[u8]) -> Vec<u8> {
|
||||||
// create hasher
|
// create hasher
|
Loading…
Reference in New Issue