secure/generators.py
import secrets
import string
import pyotp
_LOWER = string.ascii_lowercase
_UPPER = string.ascii_uppercase
_DIGITS = string.digits
_SYMBOLS = "!@#$%^&*-_=+"
_PASSWORD_POOL = _LOWER + _UPPER + _DIGITS + _SYMBOLS
_ALIAS_CHARS = string.ascii_letters + string.digits + "_"
def new_password(length: int = 20) -> str:
if length < 4:
raise ValueError("length must be >= 4 to fit one of each required class")
required = [
secrets.choice(_LOWER),
secrets.choice(_UPPER),
secrets.choice(_DIGITS),
secrets.choice(_SYMBOLS),
]
filler = [secrets.choice(_PASSWORD_POOL) for _ in range(length - len(required))]
chars = required + filler
secrets.SystemRandom().shuffle(chars)
return "".join(chars)
def new_alias() -> str:
length = secrets.randbelow(14) + 3
return "".join(secrets.choice(_ALIAS_CHARS) for _ in range(length))
def new_totp_secret() -> str:
return pyotp.random_base32()
def totp_now(secret: str) -> str:
return pyotp.TOTP(secret).now()