74 lines
1.8 KiB
Python
74 lines
1.8 KiB
Python
"""Encryption utilities for sensitive data like tokens."""
|
|
|
|
import logging
|
|
import os
|
|
|
|
from cryptography.fernet import Fernet, InvalidToken
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class EncryptionKeyMissingError(ValueError):
|
|
"""Raised when FERNET_KEY environment variable is not set."""
|
|
|
|
|
|
def _get_encryption_key() -> bytes:
|
|
"""Get or derive the encryption key from environment variable.
|
|
|
|
Uses FERNET_KEY env var if set (must be 32 url-safe base64 bytes).
|
|
|
|
Returns:
|
|
32-byte Fernet-compatible key
|
|
|
|
Raises:
|
|
EncryptionKeyMissingError: If FERNET_KEY is not set
|
|
"""
|
|
explicit_key = os.environ.get("FERNET_KEY")
|
|
if not explicit_key:
|
|
raise EncryptionKeyMissingError
|
|
|
|
return explicit_key.encode()
|
|
|
|
|
|
def encrypt_token(token: str) -> str:
|
|
"""Encrypt a token for safe storage.
|
|
|
|
Args:
|
|
token: The plaintext token to encrypt
|
|
|
|
Returns:
|
|
Base64-encoded encrypted token
|
|
"""
|
|
if not token:
|
|
return ""
|
|
|
|
key = _get_encryption_key()
|
|
f = Fernet(key)
|
|
encrypted = f.encrypt(token.encode())
|
|
return encrypted.decode()
|
|
|
|
|
|
def decrypt_token(encrypted_token: str) -> str:
|
|
"""Decrypt an encrypted token.
|
|
|
|
Args:
|
|
encrypted_token: The base64-encoded encrypted token
|
|
|
|
Returns:
|
|
The plaintext token, or empty string if decryption fails
|
|
"""
|
|
if not encrypted_token:
|
|
return ""
|
|
|
|
try:
|
|
key = _get_encryption_key()
|
|
f = Fernet(key)
|
|
decrypted = f.decrypt(encrypted_token.encode())
|
|
return decrypted.decode()
|
|
except InvalidToken:
|
|
logger.warning("Failed to decrypt token: invalid token")
|
|
return ""
|
|
except EncryptionKeyMissingError:
|
|
logger.warning("Failed to decrypt token: encryption key not set")
|
|
return ""
|