import os from datetime import datetime, timedelta from typing import Any, Union from jose import jwt from passlib.context import CryptContext from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives.serialization import load_pem_public_key, load_pem_private_key import base64 SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key-change-in-production") ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def create_access_token( subject: Union[str, Any], expires_delta: timedelta = None ) -> str: if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta( minutes=ACCESS_TOKEN_EXPIRE_MINUTES ) to_encode = {"exp": expire, "sub": str(subject)} encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) -> str: return pwd_context.hash(password) # E2E Encryption utilities def generate_rsa_key_pair(): """Generate RSA key pair for E2E encryption""" private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, ) public_key = private_key.public_key() # Serialize keys private_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ) public_pem = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) return private_pem.decode(), public_pem.decode() def encrypt_message(message: str, public_key_pem: str) -> str: """Encrypt message using recipient's public key""" try: public_key = load_pem_public_key(public_key_pem.encode()) encrypted = public_key.encrypt( message.encode(), padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return base64.b64encode(encrypted).decode() except Exception: return message # Fallback to unencrypted if encryption fails def decrypt_message(encrypted_message: str, private_key_pem: str) -> str: """Decrypt message using recipient's private key""" try: private_key = load_pem_private_key(private_key_pem.encode(), password=None) encrypted_bytes = base64.b64decode(encrypted_message.encode()) decrypted = private_key.decrypt( encrypted_bytes, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return decrypted.decode() except Exception: return encrypted_message # Fallback to encrypted if decryption fails