diligence/backend/app/services/crypto.py

42 lines
1.4 KiB
Python

"""Fernet encryption for integration credentials.
Derives a Fernet-compatible key from the app SECRET_KEY using HKDF.
The SECRET_KEY can be any format (hex string, random bytes, passphrase).
"""
from __future__ import annotations
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import base64
_fernet_instance: Fernet | None = None
def _derive_fernet_key(secret_key: str) -> bytes:
"""Derive a Fernet-compatible key from the app SECRET_KEY."""
hkdf = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=b"diligence-integration-encryption",
info=b"fernet-key",
)
raw = hkdf.derive(secret_key.encode())
return base64.urlsafe_b64encode(raw)
def get_fernet(secret_key: str) -> Fernet:
global _fernet_instance
if _fernet_instance is None:
_fernet_instance = Fernet(_derive_fernet_key(secret_key))
return _fernet_instance
def encrypt_value(secret_key: str, plaintext: str) -> str:
"""Encrypt a plaintext string. Returns base64-encoded ciphertext."""
return get_fernet(secret_key).encrypt(plaintext.encode()).decode()
def decrypt_value(secret_key: str, ciphertext: str) -> str:
"""Decrypt a ciphertext string. Returns plaintext."""
return get_fernet(secret_key).decrypt(ciphertext.encode()).decode()