import re
import math
from typing import Tuple, List
def validate_password_strength(password: str) -> Tuple[bool, int, List[str]]:
"""
Validates password strength and provides feedback.
Args:
password (str): Password to validate
Returns:
Tuple[bool, int, List[str]]: (is_valid, score, feedback)
- is_valid: True if password meets minimum requirements
- score: Password strength score (0-100)
- feedback: List of feedback messages
"""
feedback = []
score = 0
# Length checks
length = len(password)
if length < 8:
feedback.append("Password must be at least 8 characters long")
else:
score += min(25, length * 2) # Reward longer passwords up to 25 points
# Character variety checks
has_lower = bool(re.search(r'[a-z]', password))
has_upper = bool(re.search(r'[A-Z]', password))
has_digit = bool(re.search(r'[0-9]', password))
has_special = bool(re.search(r'[^a-zA-Z0-9]', password))
# Award points for character variety
variety_score = sum([has_lower, has_upper, has_digit, has_special]) * 10
score += variety_score
# Provide feedback for missing character types
if not has_lower:
feedback.append("Add lowercase letters")
if not has_upper:
feedback.append("Add uppercase letters")
if not has_digit:
feedback.append("Add numbers")
if not has_special:
feedback.append("Add special characters (!@#$%^&* etc.)")
# Check for common patterns (deduct points)
if re.search(r'(.)\1{2,}', password): # Repeated characters
score -= 15
feedback.append("Avoid repeated characters")
if re.search(r'(012|123|234|345|456|567|678|789|890|abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)', password.lower()):
score -= 10
feedback.append("Avoid sequential characters")
# Check for common passwords (simplified)
common_passwords = ['password', '12345678', 'qwertyui', 'admin123']
if password.lower() in common_passwords:
score -= 30
feedback.append("Do not use common passwords")
# Normalize score
score = max(0, min(100, score))
# Determine validity (minimum requirements)
is_valid = (length >= 8 and has_lower and has_upper and has_digit)
# Additional feedback based on score
if score < 30:
strength = "Very Weak"
elif score < 50:
strength = "Weak"
elif score < 70:
strength = "Fair"
elif score < 90:
strength = "Good"
else:
strength = "Strong"
feedback.insert(0, f"Password strength: {strength} ({score}/100)")
return (is_valid, score, feedback)
def estimate_crack_time(password: str) -> str:
"""
Estimates how long it would take to crack a password.
Args:
password (str): Password to analyze
Returns:
str: Human-readable time estimate
"""
# Simplified entropy calculation
charset_size = 0
if re.search(r'[a-z]', password):
charset_size += 26
if re.search(r'[A-Z]', password):
charset_size += 26
if re.search(r'[0-9]', password):
charset_size += 10
if re.search(r'[^a-zA-Z0-9]', password):
charset_size += 32 # Rough estimate for special chars
entropy = len(password) * math.log2(charset_size) if charset_size > 0 else 0
# Assume 10 billion attempts per second (modern GPU)
attempts_per_second = 10**10
total_combinations = 2**entropy
seconds_to_crack = total_combinations / (2 * attempts_per_second) # Average time
# Convert to human-readable format
if seconds_to_crack < 60:
return f"{seconds_to_crack:.2f} seconds"
elif seconds_to_crack < 3600:
return f"{seconds_to_crack/60:.2f} minutes"
elif seconds_to_crack < 86400:
return f"{seconds_to_crack/3600:.2f} hours"
elif seconds_to_crack < 31536000:
return f"{seconds_to_crack/86400:.2f} days"
else:
years = seconds_to_crack / 31536000
if years > 1000000:
return "millions of years"
return f"{years:.2f} years"
# Example usage
if __name__ == "__main__":
test_passwords = [
"password",
"Password123",
"MyP@ssw0rd123!",
"Tr0ub4dor&3",
"correcthorsebatterystaple",
"123456789",
"Qwerty123!"
]
for pwd in test_passwords:
print(f"\nAnalyzing: {pwd}")
is_valid, score, feedback = validate_password_strength(pwd)
crack_time = estimate_crack_time(pwd)
print(f"Valid: {is_valid}")
print(f"Crack time estimate: {crack_time}")
for item in feedback:
print(f" - {item}")
This password strength validator solves the common problem of helping users create secure passwords by providing real-time feedback on password quality. The code evaluates passwords based on multiple criteria:
The validator returns three key pieces of information:
To use this snippet, simply call validate_password_strength(password) with any password string. The function returns a tuple containing validation status, a score out of 100, and a list of feedback messages. For even more insight, use estimate_crack_time(password) to see how secure the password is against brute force attacks.
The code is designed to be integrated into signup forms, password change interfaces, or security auditing tools where providing immediate feedback on password strength is essential.