geekorm_core/utils/crypto/
hashing.rs

1#[cfg(feature = "hash-argon2")]
2use argon2::Argon2;
3#[cfg(feature = "hash-pbkdf2")]
4use pbkdf2::Pbkdf2;
5#[cfg(feature = "hash-sha512")]
6use sha_crypt::{Sha512Params, sha512_check, sha512_simple};
7// Password Hashing Library
8use password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng};
9
10use crate::utils::crypto::HashingAlgorithm;
11
12/// Generate a hash for a given string
13#[allow(unreachable_patterns)]
14pub fn generate_hash(data: String, alg: HashingAlgorithm) -> Result<String, crate::Error> {
15    match alg {
16        #[cfg(feature = "hash-pbkdf2")]
17        HashingAlgorithm::Pbkdf2 => generate_hash_pdkdf2(data),
18        #[cfg(feature = "hash-argon2")]
19        HashingAlgorithm::Argon2 => generate_hash_argon2(data),
20        #[cfg(feature = "hash-sha512")]
21        HashingAlgorithm::Sha512 => generate_hash_sha512(data),
22        _ => Err(crate::Error::HashingError(
23            "Invalid hashing algorithm".to_string(),
24        )),
25    }
26}
27
28/// Verify a hash for a given string
29///
30/// ```rust
31/// use geekorm_core::utils::{verify_hash, generate_hash, crypto::HashingAlgorithm};
32///
33/// let data = "password".to_string();
34/// let hash = generate_hash(data.clone(), HashingAlgorithm::Pbkdf2).unwrap();
35///
36/// if verify_hash(data, hash, HashingAlgorithm::Pbkdf2).unwrap() {
37///     println!("Password is correct");
38/// } else {
39///     println!("Password is incorrect");
40/// }
41/// ```
42#[cfg(feature = "hash")]
43pub fn verify_hash(
44    data: String,
45    hash: String,
46    alg: HashingAlgorithm,
47) -> Result<bool, crate::Error> {
48    match alg {
49        HashingAlgorithm::Pbkdf2 => verify_hash_pbkdf2(data, hash),
50        #[cfg(feature = "hash-argon2")]
51        HashingAlgorithm::Argon2 => {
52            let hasher = PasswordHash::new(&hash).map_err(|e| {
53                crate::Error::HashingError(format!("Error parsing password hash: {}", e))
54            })?;
55            match Argon2::default().verify_password(data.as_bytes(), &hasher) {
56                Ok(_) => Ok(true),
57                Err(_) => Ok(false),
58            }
59        }
60        #[cfg(feature = "hash-sha512")]
61        HashingAlgorithm::Sha512 => match sha512_check(data.as_str(), hash.as_str()) {
62            Ok(_) => Ok(true),
63            Err(_) => Ok(false),
64        },
65    }
66}
67
68/// Generate a hash using PBKDF2
69#[cfg(feature = "hash")]
70pub(crate) fn generate_hash_pdkdf2(data: String) -> Result<String, crate::Error> {
71    // Salt
72    let salt = SaltString::generate(&mut OsRng);
73    // Hash
74    match Pbkdf2.hash_password(data.as_bytes(), &salt) {
75        Ok(hash) => Ok(hash.to_string()),
76        Err(e) => Err(crate::Error::HashingError(format!(
77            "Error hashing password: {}",
78            e
79        ))),
80    }
81}
82
83#[cfg(feature = "hash")]
84pub(crate) fn verify_hash_pbkdf2(data: String, hash: String) -> Result<bool, crate::Error> {
85    let parsed_hash = match PasswordHash::new(&hash) {
86        Ok(h) => h,
87        Err(e) => {
88            return Err(crate::Error::HashingError(format!(
89                "Error parsing password hash: {}",
90                e
91            )));
92        }
93    };
94
95    match Pbkdf2.verify_password(data.as_bytes(), &parsed_hash) {
96        Ok(_) => Ok(true),
97        Err(_) => Ok(false),
98    }
99}
100
101/// Generate a hash using Argon2
102#[cfg(feature = "hash-argon2")]
103pub(crate) fn generate_hash_argon2(data: String) -> Result<String, crate::Error> {
104    // Salt
105    let salt = SaltString::generate(&mut OsRng);
106    // Hash
107    let argon2 = Argon2::default();
108
109    match argon2.hash_password(data.as_bytes(), &salt) {
110        Ok(hash) => Ok(hash.to_string()),
111        Err(e) => Err(crate::Error::HashingError(format!(
112            "Error hashing password: {}",
113            e
114        ))),
115    }
116}
117
118/// Generate a hash using SHA512 + Rounds
119#[cfg(feature = "hash-sha512")]
120pub(crate) fn generate_hash_sha512(data: String) -> Result<String, crate::Error> {
121    let params = match Sha512Params::new(100_000) {
122        Ok(p) => p,
123        Err(_) => {
124            return Err(crate::Error::HashingError(String::from(
125                "Error creating params for sha512",
126            )));
127        }
128    };
129    match sha512_simple(data.as_str(), &params) {
130        Ok(hash) => Ok(hash),
131        Err(_) => Err(crate::Error::HashingError(
132            "Error hashing password using SHA512".to_string(),
133        )),
134    }
135}