41 lines
1.2 KiB
Python
41 lines
1.2 KiB
Python
|
|
"""Fernet-based encryption service for sensitive fields.
|
||
|
|
|
||
|
|
Key is read from the MIMIC_ENCRYPTION_KEY env var (Fernet base64-urlsafe 32-byte key).
|
||
|
|
When the key is absent the service raises C2Disabled so callers can return 503.
|
||
|
|
The key is never logged or returned in any response.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import os
|
||
|
|
|
||
|
|
from cryptography.fernet import Fernet, InvalidToken
|
||
|
|
|
||
|
|
|
||
|
|
class C2Disabled(Exception):
|
||
|
|
"""Raised when MIMIC_ENCRYPTION_KEY is not set."""
|
||
|
|
|
||
|
|
|
||
|
|
def _get_fernet() -> Fernet:
|
||
|
|
key = os.environ.get("MIMIC_ENCRYPTION_KEY")
|
||
|
|
if not key:
|
||
|
|
raise C2Disabled("C2 disabled: MIMIC_ENCRYPTION_KEY not set")
|
||
|
|
return Fernet(key.encode() if isinstance(key, str) else key)
|
||
|
|
|
||
|
|
|
||
|
|
def encrypt(plaintext: str) -> str:
|
||
|
|
"""Encrypt *plaintext* and return a Fernet token (str)."""
|
||
|
|
f = _get_fernet()
|
||
|
|
return f.encrypt(plaintext.encode()).decode()
|
||
|
|
|
||
|
|
|
||
|
|
def decrypt(ciphertext: str) -> str:
|
||
|
|
"""Decrypt a Fernet token and return the plaintext string."""
|
||
|
|
f = _get_fernet()
|
||
|
|
try:
|
||
|
|
return f.decrypt(ciphertext.encode()).decode()
|
||
|
|
except InvalidToken as exc:
|
||
|
|
raise ValueError("Invalid ciphertext") from exc
|
||
|
|
|
||
|
|
|
||
|
|
__all__ = ["C2Disabled", "encrypt", "decrypt"]
|