42 lines
1.4 KiB
Python
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()
|