use crate::error::{Result, SmartPassError};
use rand::Rng;
use sha2::{Digest, Sha256};
pub const CHARS: &str =
"!@#$%^&*()_+-=[]{};:,.<>?/ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
const MIN_SECRET_LEN: usize = 12;
const MIN_PASSWORD_LEN: usize = 12;
const MAX_PASSWORD_LEN: usize = 100;
const MIN_CODE_LEN: usize = 4;
const MAX_CODE_LEN: usize = 100;
fn sha256_hash(text: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(text.as_bytes());
let result = hasher.finalize();
hex::encode(result)
}
fn validate_secret(secret: &str) -> Result<()> {
if secret.len() < MIN_SECRET_LEN {
return Err(SmartPassError::SecretTooShort(secret.len()));
}
Ok(())
}
fn validate_password_length(length: usize) -> Result<()> {
if length < MIN_PASSWORD_LEN {
return Err(SmartPassError::InvalidPasswordLength {
min: MIN_PASSWORD_LEN,
max: MAX_PASSWORD_LEN,
current: length,
});
}
if length > MAX_PASSWORD_LEN {
return Err(SmartPassError::InvalidPasswordLength {
min: MIN_PASSWORD_LEN,
max: MAX_PASSWORD_LEN,
current: length,
});
}
Ok(())
}
fn validate_code_length(length: usize) -> Result<()> {
if length < MIN_CODE_LEN {
return Err(SmartPassError::InvalidCodeLength(length));
}
if length > MAX_CODE_LEN {
return Err(SmartPassError::InvalidCodeLength(length));
}
Ok(())
}
fn get_steps_from_secret(
secret: &str,
min_steps: usize,
max_steps: usize,
salt: &str,
) -> Result<usize> {
let hash_value = sha256_hash(&format!("{}:{}", secret, salt));
let hash_int = u64::from_str_radix(&hash_value[0..8], 16)
.map_err(|_| SmartPassError::CryptoError("Failed to parse hash".to_string()))?;
let steps = min_steps + (hash_int as usize % (max_steps - min_steps + 1));
Ok(steps)
}
fn generate_key(secret: &str, steps: usize, salt: &str) -> Result<String> {
validate_secret(secret)?;
let mut hash = sha256_hash(&format!("{}:{}", secret, salt));
for i in 0..steps {
hash = sha256_hash(&format!("{}:{}", hash, i));
}
Ok(hash)
}
pub fn generate_private_key(secret: &str) -> Result<String> {
validate_secret(secret)?;
let steps = get_steps_from_secret(secret, 15, 30, "private")?;
generate_key(secret, steps, "private")
}
pub fn generate_public_key(secret: &str) -> Result<String> {
validate_secret(secret)?;
let steps = get_steps_from_secret(secret, 45, 60, "public")?;
generate_key(secret, steps, "public")
}
pub fn verify_secret(secret: &str, public_key: &str) -> Result<bool> {
let computed = generate_public_key(secret)?;
Ok(computed == public_key)
}
fn hex_to_bytes(hex_str: &str) -> Result<Vec<u8>> {
hex::decode(hex_str).map_err(SmartPassError::from)
}
fn generate_password_from_private_key(private_key: &str, length: usize) -> Result<String> {
validate_password_length(length)?;
let mut result = String::with_capacity(length);
let mut counter = 0;
let chars = CHARS.as_bytes();
while result.len() < length {
let hash_hex = sha256_hash(&format!("{}:{}", private_key, counter));
let hash_bytes = hex_to_bytes(&hash_hex)?;
for &byte in &hash_bytes {
if result.len() >= length {
break;
}
let idx = (byte as usize) % chars.len();
result.push(chars[idx] as char);
}
counter += 1;
}
Ok(result)
}
pub fn generate_smart_password_sync(secret: &str, length: usize) -> Result<String> {
validate_secret(secret)?;
validate_password_length(length)?;
let private_key = generate_private_key(secret)?;
generate_password_from_private_key(&private_key, length)
}
pub fn generate_strong_password(length: usize) -> Result<String> {
validate_password_length(length)?;
let mut rng = rand::thread_rng();
let chars = CHARS.as_bytes();
let mut result = String::with_capacity(length);
for _ in 0..length {
let idx = rng.gen_range(0..chars.len());
result.push(chars[idx] as char);
}
Ok(result)
}
pub fn generate_base_password(length: usize) -> Result<String> {
generate_strong_password(length)
}
pub fn generate_code(length: usize) -> Result<String> {
validate_code_length(length)?;
let mut rng = rand::thread_rng();
let chars = CHARS.as_bytes();
let mut result = String::with_capacity(length);
for _ in 0..length {
let idx = rng.gen_range(0..chars.len());
result.push(chars[idx] as char);
}
Ok(result)
}