"""Encryption/decryption for email passwords and other server-side secrets. Uses AES-256-GCM with a key derived from the user's encryption key. The encryption key is passed from the frontend via X-Encryption-Key header and is itself derived from the user's login password using PBKDF2. """ import base64 import hashlib import os from cryptography.hazmat.primitives.ciphers.aead import AESGCM def _derive_key(user_key_b64: str) -> bytes: """Derive a 256-bit AES key from the user's base64-encoded key.""" try: raw = base64.b64decode(user_key_b64) except Exception: raw = user_key_b64.encode('utf-8') return hashlib.sha256(raw).digest() def encrypt_field(plaintext: str, user_key_b64: str) -> bytes: """Encrypt a string field. Returns nonce + ciphertext as bytes.""" key = _derive_key(user_key_b64) aesgcm = AESGCM(key) nonce = os.urandom(12) ciphertext = aesgcm.encrypt(nonce, plaintext.encode('utf-8'), None) return nonce + ciphertext def decrypt_field(encrypted: bytes, user_key_b64: str) -> str: """Decrypt a field. Input is nonce (12 bytes) + ciphertext.""" key = _derive_key(user_key_b64) aesgcm = AESGCM(key) nonce = encrypted[:12] ciphertext = encrypted[12:] plaintext = aesgcm.decrypt(nonce, ciphertext, None) return plaintext.decode('utf-8')