pub mod keyderivation;
pub mod keymanagement;
pub mod signatures;
pub mod keyexchange;
pub mod hashing;
pub mod keywrap;
pub use keyderivation::{DerivedKey, Hkdf, PasswordStrength, Pbkdf2};
pub use keymanagement::{KeyMetadata, KeyStore, RotationPolicy};
pub use signatures::{Ed25519KeyPair, Ed25519PublicKey, HmacKey, SignatureSuite};
pub use keyexchange::{X25519KeyPair, X25519PublicKey, SharedSecret};
pub use hashing::{HashAlgorithm, Hasher, HashOutput};
pub use keywrap::{KeyWrapper, WrappedKey};
use aes_gcm::{
aead::{Aead, KeyInit, OsRng},
Aes256Gcm, Nonce,
};
use argon2::{
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Algorithm, Argon2, Params, Version,
};
use hmac::Hmac;
use rand::RngCore;
use sha2::Sha256;
use thiserror::Error;
use zeroize::{Zeroize, ZeroizeOnDrop};
type HmacSha256 = Hmac<Sha256>;
#[derive(Error, Debug)]
pub enum CryptoError {
#[error("Password hashing failed: {0}")]
HashingError(String),
#[error("Password verification failed")]
VerificationError,
#[error("Encryption failed: {0}")]
EncryptionError(String),
#[error("Decryption failed: {0}")]
DecryptionError(String),
#[error("Invalid key length")]
InvalidKeyLength,
#[error("HMAC generation failed: {0}")]
HmacError(String),
#[error("Weak password: {0}")]
WeakPassword(String),
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecurePassword {
password: Vec<u8>,
}
impl SecurePassword {
pub fn new(password: impl Into<Vec<u8>>) -> Self {
Self {
password: password.into(),
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.password
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecureKey {
key: Vec<u8>,
}
impl SecureKey {
pub fn new(key: Vec<u8>) -> Result<Self, CryptoError> {
if key.len() != 32 {
return Err(CryptoError::InvalidKeyLength);
}
Ok(Self { key })
}
pub fn generate() -> Self {
let mut key = vec![0u8; 32];
OsRng.fill_bytes(&mut key);
Self { key }
}
pub fn as_bytes(&self) -> &[u8] {
&self.key
}
}
pub mod password {
use super::*;
pub fn hash_password(password: &SecurePassword) -> Result<String, CryptoError> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = argon2
.hash_password(password.as_bytes(), &salt)
.map_err(|e| CryptoError::HashingError(e.to_string()))?;
Ok(password_hash.to_string())
}
pub fn verify_password(password: &SecurePassword, hash: &str) -> Result<bool, CryptoError> {
let parsed_hash =
PasswordHash::new(hash).map_err(|e| CryptoError::HashingError(e.to_string()))?;
let argon2 = Argon2::default();
match argon2.verify_password(password.as_bytes(), &parsed_hash) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
#[derive(Debug, Clone)]
pub struct PasswordStrength {
pub min_length: usize,
pub require_uppercase: bool,
pub require_lowercase: bool,
pub require_digit: bool,
pub require_special: bool,
}
impl Default for PasswordStrength {
fn default() -> Self {
Self {
min_length: 12,
require_uppercase: true,
require_lowercase: true,
require_digit: true,
require_special: true,
}
}
}
pub fn validate_password_strength(
password: &SecurePassword,
requirements: &PasswordStrength,
) -> Result<(), CryptoError> {
let password_str = std::str::from_utf8(password.as_bytes())
.map_err(|_| CryptoError::WeakPassword("Invalid UTF-8".to_string()))?;
if password_str.len() < requirements.min_length {
return Err(CryptoError::WeakPassword(format!(
"Password must be at least {} characters",
requirements.min_length
)));
}
if requirements.require_uppercase && !password_str.chars().any(|c| c.is_uppercase()) {
return Err(CryptoError::WeakPassword(
"Password must contain uppercase letters".to_string(),
));
}
if requirements.require_lowercase && !password_str.chars().any(|c| c.is_lowercase()) {
return Err(CryptoError::WeakPassword(
"Password must contain lowercase letters".to_string(),
));
}
if requirements.require_digit && !password_str.chars().any(|c| c.is_ascii_digit()) {
return Err(CryptoError::WeakPassword(
"Password must contain digits".to_string(),
));
}
if requirements.require_special
&& !password_str
.chars()
.any(|c| !c.is_alphanumeric() && !c.is_whitespace())
{
return Err(CryptoError::WeakPassword(
"Password must contain special characters".to_string(),
));
}
Ok(())
}
pub fn derive_key_from_password(
password: &SecurePassword,
salt: &[u8],
) -> Result<SecureKey, CryptoError> {
if salt.len() < 16 {
return Err(CryptoError::HashingError(
"Salt must be at least 16 bytes".to_string(),
));
}
let params = Params::new(65536, 3, 1, Some(32))
.map_err(|e| CryptoError::HashingError(e.to_string()))?;
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut key_bytes = vec![0u8; 32];
argon2
.hash_password_into(password.as_bytes(), salt, &mut key_bytes)
.map_err(|e| CryptoError::HashingError(e.to_string()))?;
Ok(SecureKey { key: key_bytes })
}
}
pub mod encryption {
use super::*;
pub struct EncryptedData {
pub ciphertext: Vec<u8>,
pub nonce: [u8; 12],
}
pub fn encrypt(key: &SecureKey, plaintext: &[u8]) -> Result<EncryptedData, CryptoError> {
let cipher = Aes256Gcm::new_from_slice(key.as_bytes())
.map_err(|e| CryptoError::EncryptionError(e.to_string()))?;
let mut nonce_bytes = [0u8; 12];
OsRng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher
.encrypt(nonce, plaintext)
.map_err(|e| CryptoError::EncryptionError(e.to_string()))?;
Ok(EncryptedData {
ciphertext,
nonce: nonce_bytes,
})
}
pub fn decrypt(key: &SecureKey, encrypted: &EncryptedData) -> Result<Vec<u8>, CryptoError> {
let cipher = Aes256Gcm::new_from_slice(key.as_bytes())
.map_err(|e| CryptoError::DecryptionError(e.to_string()))?;
let nonce = Nonce::from_slice(&encrypted.nonce);
let plaintext = cipher
.decrypt(nonce, encrypted.ciphertext.as_ref())
.map_err(|e| CryptoError::DecryptionError(e.to_string()))?;
Ok(plaintext)
}
}
pub mod random {
use super::*;
pub fn generate_random_bytes(length: usize) -> Vec<u8> {
let mut bytes = vec![0u8; length];
OsRng.fill_bytes(&mut bytes);
bytes
}
pub fn generate_random_hex(length: usize) -> String {
let bytes = generate_random_bytes(length);
hex::encode(bytes)
}
pub fn generate_salt() -> Vec<u8> {
generate_random_bytes(32)
}
}
pub mod hmac_ops {
use super::*;
use hmac::Mac;
pub fn compute_hmac(key: &SecureKey, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
let mut mac = <HmacSha256 as Mac>::new_from_slice(key.as_bytes())
.map_err(|e| CryptoError::HmacError(e.to_string()))?;
mac.update(message);
Ok(mac.finalize().into_bytes().to_vec())
}
pub fn verify_hmac(
key: &SecureKey,
message: &[u8],
expected_hmac: &[u8],
) -> Result<bool, CryptoError> {
let mut mac = <HmacSha256 as Mac>::new_from_slice(key.as_bytes())
.map_err(|e| CryptoError::HmacError(e.to_string()))?;
mac.update(message);
match mac.verify_slice(expected_hmac) {
Ok(()) => Ok(true),
Err(_) => Ok(false),
}
}
}
pub mod secure_compare {
pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
let mut result = 0u8;
for (byte_a, byte_b) in a.iter().zip(b.iter()) {
result |= byte_a ^ byte_b;
}
result == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_password_hashing() {
let password = SecurePassword::new(b"MySecurePassword123!".to_vec());
let hash = password::hash_password(&password).unwrap();
assert!(password::verify_password(&password, &hash).unwrap());
let wrong_password = SecurePassword::new(b"WrongPassword".to_vec());
assert!(!password::verify_password(&wrong_password, &hash).unwrap());
}
#[test]
fn test_encryption_decryption() {
let key = SecureKey::generate();
let plaintext = b"Sensitive financial data: Account 123456, Balance: $50,000";
let encrypted = encryption::encrypt(&key, plaintext).unwrap();
let decrypted = encryption::decrypt(&key, &encrypted).unwrap();
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
}
#[test]
fn test_encryption_with_wrong_key() {
let key1 = SecureKey::generate();
let key2 = SecureKey::generate();
let plaintext = b"Secret data";
let encrypted = encryption::encrypt(&key1, plaintext).unwrap();
let result = encryption::decrypt(&key2, &encrypted);
assert!(result.is_err());
}
#[test]
fn test_random_generation() {
let bytes1 = random::generate_random_bytes(32);
let bytes2 = random::generate_random_bytes(32);
assert_eq!(bytes1.len(), 32);
assert_eq!(bytes2.len(), 32);
assert_ne!(bytes1, bytes2); }
#[test]
fn test_random_hex() {
let hex = random::generate_random_hex(16);
assert_eq!(hex.len(), 32); }
#[test]
fn test_zeroization() {
let password_bytes = b"TestPassword123".to_vec();
{
let _secure_password = SecurePassword::new(password_bytes.clone());
}
}
#[test]
fn test_key_length_validation() {
let short_key = vec![0u8; 16]; let result = SecureKey::new(short_key);
assert!(result.is_err());
let valid_key = vec![0u8; 32];
let result = SecureKey::new(valid_key);
assert!(result.is_ok());
}
#[test]
fn test_password_strength_validation() {
let strong = SecurePassword::new(b"SecurePass123!@#".to_vec());
let requirements = password::PasswordStrength::default();
assert!(password::validate_password_strength(&strong, &requirements).is_ok());
let short = SecurePassword::new(b"Short1!".to_vec());
let result = password::validate_password_strength(&short, &requirements);
assert!(result.is_err());
let no_upper = SecurePassword::new(b"nouppercasehere123!".to_vec());
let result = password::validate_password_strength(&no_upper, &requirements);
assert!(result.is_err());
let no_special = SecurePassword::new(b"NoSpecialChars123".to_vec());
let result = password::validate_password_strength(&no_special, &requirements);
assert!(result.is_err());
}
#[test]
fn test_key_derivation_from_password() {
let password = SecurePassword::new(b"MyMasterPassword123!".to_vec());
let salt = random::generate_salt();
let key1 = password::derive_key_from_password(&password, &salt).unwrap();
let key2 = password::derive_key_from_password(&password, &salt).unwrap();
assert_eq!(key1.as_bytes(), key2.as_bytes());
let different_salt = random::generate_salt();
let key3 = password::derive_key_from_password(&password, &different_salt).unwrap();
assert_ne!(key1.as_bytes(), key3.as_bytes());
}
#[test]
fn test_hmac_generation_and_verification() {
let key = SecureKey::generate();
let message = b"Important financial transaction data";
let hmac_result = hmac_ops::compute_hmac(&key, message).unwrap();
assert_eq!(hmac_result.len(), 32);
assert!(hmac_ops::verify_hmac(&key, message, &hmac_result).unwrap());
let wrong_key = SecureKey::generate();
assert!(!hmac_ops::verify_hmac(&wrong_key, message, &hmac_result).unwrap());
let modified_message = b"Modified transaction data";
assert!(!hmac_ops::verify_hmac(&key, modified_message, &hmac_result).unwrap());
}
#[test]
fn test_constant_time_compare() {
let data1 = b"secret_data";
let data2 = b"secret_data";
let data3 = b"different_data";
assert!(secure_compare::constant_time_compare(data1, data2));
assert!(!secure_compare::constant_time_compare(data1, data3));
let short = b"short";
assert!(!secure_compare::constant_time_compare(data1, short));
}
#[test]
fn test_salt_generation() {
let salt1 = random::generate_salt();
let salt2 = random::generate_salt();
assert_eq!(salt1.len(), 32);
assert_eq!(salt2.len(), 32);
assert_ne!(salt1, salt2); }
#[test]
fn test_password_based_encryption() {
let password = SecurePassword::new(b"UserMasterPassword123!".to_vec());
let salt = random::generate_salt();
let key = password::derive_key_from_password(&password, &salt).unwrap();
let sensitive_data = b"Social Security Number: 123-45-6789";
let encrypted = encryption::encrypt(&key, sensitive_data).unwrap();
let decrypted = encryption::decrypt(&key, &encrypted).unwrap();
assert_eq!(sensitive_data.as_slice(), decrypted.as_slice());
}
#[test]
fn test_multiple_hmac_computations() {
let key = SecureKey::generate();
let message1 = b"Message 1";
let message2 = b"Message 2";
let hmac1 = hmac_ops::compute_hmac(&key, message1).unwrap();
let hmac2 = hmac_ops::compute_hmac(&key, message2).unwrap();
assert_ne!(hmac1, hmac2);
assert!(hmac_ops::verify_hmac(&key, message1, &hmac1).unwrap());
assert!(hmac_ops::verify_hmac(&key, message2, &hmac2).unwrap());
assert!(!hmac_ops::verify_hmac(&key, message1, &hmac2).unwrap());
assert!(!hmac_ops::verify_hmac(&key, message2, &hmac1).unwrap());
}
#[test]
fn test_encryption_with_derived_key() {
let password = SecurePassword::new(b"StrongPassword789!@#".to_vec());
let salt = random::generate_salt();
let key = password::derive_key_from_password(&password, &salt).unwrap();
let data = b"Encrypted with derived key";
let encrypted = encryption::encrypt(&key, data).unwrap();
let decrypted = encryption::decrypt(&key, &encrypted).unwrap();
assert_eq!(data.as_slice(), decrypted.as_slice());
let wrong_password = SecurePassword::new(b"WrongPassword123!".to_vec());
let wrong_key = password::derive_key_from_password(&wrong_password, &salt).unwrap();
let result = encryption::decrypt(&wrong_key, &encrypted);
assert!(result.is_err());
}
}