mod aes;
mod hmac;
mod jwt;
mod openssl;
mod password_hasher;
mod secure_token;
use std::sync::{Arc, RwLock};
use hmac::HmacSigner;
use secrecy::SecretString;
pub use aes::Aes;
pub use jwt::JwtSigner;
pub use openssl::Openssl;
pub use password_hasher::{Argon2Password, BcryptPassword, Hashable};
pub use secure_token::SecureToken;
use crate::{
config::{AnzarConfiguration, AuthStrategy, HashingAlgorithm, JwtConfig},
domain::model::SigningKey,
error::{AuthError, CoreError, InternalError, Result},
};
#[derive(Clone)]
pub struct Crypto {
pub password_hasher: Arc<dyn Hashable>,
pub token: SecureToken,
pub hmac: HmacSigner,
pub aes: Aes,
pub openssl: Openssl,
jwt: Arc<RwLock<Option<JwtSigner>>>,
}
impl Default for Crypto {
fn default() -> Self {
Self {
password_hasher: Arc::new(Argon2Password::default()),
token: SecureToken::default(),
hmac: HmacSigner::default(),
aes: Aes::default(),
openssl: Openssl::default(),
jwt: Arc::new(RwLock::new(None)),
}
}
}
impl Crypto {
pub fn with_argon(m_cost: u32, t_cost: u32, p_cost: u32) -> Self {
Self {
password_hasher: Arc::new(Argon2Password::new(m_cost, t_cost, p_cost)),
..Self::default()
}
}
pub fn with_bcrypt(cost: u32) -> Self {
Self {
password_hasher: Arc::new(BcryptPassword::new(cost)),
..Self::default()
}
}
}
impl Crypto {
pub fn with_initializations(mut self, configuration: &AnzarConfiguration) -> Self {
let key = &configuration.security.secret_key;
self.hmac = HmacSigner::new(&SecretString::from(key.clone()));
self.aes = Aes::new(&SecretString::from(key.clone()));
if let Ok(conf) = configuration.auth.jwt() {
self.openssl = Openssl::new(&conf.algorithm);
}
self
}
}
impl Crypto {
pub fn with_jwt(
mut self,
private: &str,
signing_key: &SigningKey,
jwt_config: &JwtConfig,
) -> Self {
self.jwt = Arc::new(RwLock::new(Some(JwtSigner::new(
private,
signing_key,
jwt_config,
))));
self
}
}
impl Crypto {
pub fn with_token_size32(mut self) -> Self {
self.token = SecureToken::with_size32();
self
}
pub fn with_token_size64(mut self) -> Self {
self.token = SecureToken::with_size64();
self
}
}
impl Crypto {
pub fn from_configuration(configuration: &AnzarConfiguration) -> Self {
match configuration.auth.password.algorithm {
HashingAlgorithm::Argon2 {
memory_kib,
iterations,
parallelism,
} => Crypto::with_argon(memory_kib, iterations, parallelism),
HashingAlgorithm::Bcrypt { cost } => Crypto::with_bcrypt(cost),
}
.with_initializations(configuration)
.with_token_size64()
}
}
impl Crypto {
pub fn validate(self, strategy: &AuthStrategy) -> Result<Self> {
if matches!(strategy, AuthStrategy::Jwt(..)) && self.jwt().is_err() {
return Err(CoreError::Internal(InternalError::MissingConfiguration(
"JWT strategy requires a JWT signer, but none was configured".into(),
)));
}
match self.hmac.secret_key.len() {
0 => {
return Err(CoreError::Internal(InternalError::MissingConfiguration(
"HMAC secret key is missing".into(),
)));
}
n if n < 32 => {
return Err(CoreError::Internal(InternalError::MissingConfiguration(
format!("HMAC secret key is too short ({n} bytes), minimum is 32 bytes"),
)));
}
_ => {}
}
Ok(self)
}
pub fn jwt(&self) -> Result<JwtSigner> {
let jwt = self.jwt.read().unwrap();
jwt.clone()
.ok_or(CoreError::Unauthenticated(AuthError::JwtNotConfigured))
}
pub fn rotate_jwt(&self, new_signer: JwtSigner) {
let mut jwt = self.jwt.write().unwrap();
*jwt = Some(new_signer);
}
}