""" AES-256-GCM 加解密工具 密钥从 JWT_SECRET 派生,用于加密 WebDAV 密码等敏感信息 """ import base64 import os import hashlib from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.primitives import hashes from app.config import JWT_SECRET _SALT = b"elysia-todo-sync-v1" _NONCE_SIZE = 12 # AES-GCM 标准 nonce 长度 def _derive_key() -> bytes: """从 JWT_SECRET 派生 256-bit AES 密钥""" kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=_SALT, iterations=480000, ) return kdf.derive(JWT_SECRET.encode("utf-8")) def encrypt(plaintext: str) -> str: """AES-256-GCM 加密,返回 base64(iv + ciphertext + tag)""" if not plaintext: return "" key = _derive_key() nonce = os.urandom(_NONCE_SIZE) aesgcm = AESGCM(key) ciphertext = aesgcm.encrypt(nonce, plaintext.encode("utf-8"), None) return base64.b64encode(nonce + ciphertext).decode("ascii") def decrypt(encrypted: str) -> str | None: """AES-256-GCM 解密,解密失败返回 None""" if not encrypted: return None try: key = _derive_key() raw = base64.b64decode(encrypted) nonce = raw[:_NONCE_SIZE] ciphertext = raw[_NONCE_SIZE:] aesgcm = AESGCM(key) plaintext = aesgcm.decrypt(nonce, ciphertext, None) return plaintext.decode("utf-8") except Exception: return None