zero4rs 2.0.0

zero4rs is a powerful, pragmatic, and extremely fast web framework for Rust
Documentation
use argon2::password_hash::{Salt, SaltString};
use argon2::{Algorithm, Argon2, Params, PasswordHash, PasswordHasher, PasswordVerifier, Version};
use base64::{engine::general_purpose, Engine as _};

use hmac::{Hmac, Mac};
use rand::RngCore;
use sha3::Digest;

use crate::core::error2::Error;
use crate::core::error2::Result;

pub fn base64encode(s: &str) -> String {
    general_purpose::STANDARD.encode(s)
}

pub fn base64decode(s: &str) -> Result<String> {
    let decoded_bytes = general_purpose::STANDARD
        .decode(s)
        .map_err(Error::run_time)?;

    let s = String::from_utf8(decoded_bytes).map_err(Error::run_time)?;

    Ok(s)
}

pub fn urlencoded<T>(s: &T) -> String
where
    T: serde::Serialize,
{
    serde_urlencoded::to_string(s).unwrap()
}

pub fn sha3_256(s: String) -> String {
    let password_hash = sha3::Sha3_256::digest(s.as_bytes());
    format!("{:x}", password_hash)
}

pub fn encrypt_sha_256(key: &str, plaintext: &str) -> String {
    let mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(key.as_bytes()).unwrap();

    mac.update(plaintext.as_bytes());

    general_purpose::STANDARD.encode(mac.finalize().into_bytes())
}

pub fn compute_password_hash(password: &str) -> Result<String> {
    let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
    let mut rng = rand::rng();
    rng.fill_bytes(&mut bytes);

    let salt = SaltString::encode_b64(&bytes).expect("Invalid salt encoding");

    let password_hash = Argon2::new(
        Algorithm::Argon2id,
        Version::V0x13,
        Params::new(15000, 2, 1, None).unwrap(),
    )
    .hash_password(password.as_bytes(), &salt)
    .map_err(Error::run_time)?
    .to_string();

    Ok(password_hash)
}

pub fn verify_password(password: String, expected_password_hash: String) -> Result<bool> {
    let expected_password_hash =
        PasswordHash::new(expected_password_hash.as_str()).map_err(Error::run_time)?;

    if Argon2::default()
        .verify_password(password.as_bytes(), &expected_password_hash)
        .is_ok()
    {
        return Ok(true);
    }

    Ok(false)
}

pub fn html_escape(s: &str) -> String {
    htmlescape::encode_minimal(s)
}

#[rustfmt::skip]
pub fn hmac_tag(query_string: &str, secret: &str) -> Result<String> {
    let mut mac = Hmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()).map_err(Error::run_time)?;
    mac.update(query_string.as_bytes());
    Ok(hex::encode(mac.finalize().into_bytes()))
}

#[rustfmt::skip]
pub fn hmac_tag_verify(query_string: &str, secret: &str, tag: &str) -> Result<bool> {
    let _tag = hex::decode(tag).map_err(Error::run_time)?;
    let mut mac = Hmac::<sha2::Sha256>::new_from_slice(secret.as_bytes()).map_err(Error::run_time)?;

    mac.update(query_string.as_bytes());
    mac.verify_slice(&_tag).map_err(Error::run_time)?;

    Ok(true)
}