Karl 79a2f6e944 feat(security): implement AES-GCM for password encryption
Replaces the `pyeasyencrypt` library with a more robust and standard
encryption implementation using `cryptography.hazmat`.

This commit introduces AES-256-GCM for encrypting and decrypting user
account passwords. The `add_account` endpoint now properly encrypts
passwords before database insertion.

Error handling has been added to the `get_user_accounts` endpoint to
manage decryption failures for legacy passwords, which will be returned
as "DECRYPTION_FAILED".

BREAKING CHANGE: The password encryption algorithm has been changed.
All previously stored passwords are now invalid and cannot be decrypted.
2025-07-14 11:12:13 +01:00

31 lines
932 B
Python

import os
import hashlib
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
SECRET = "BBLBTV-DNS-PASSWORDS"
KEY = hashlib.sha256(SECRET.encode()).digest()
ALGORITHM = "aes-256-gcm"
IV_LENGTH = 16
AUTH_TAG_LENGTH = 16
def encrypt_password(clear_string):
iv = os.urandom(IV_LENGTH)
aesgcm = AESGCM(KEY)
ciphertext_and_tag = aesgcm.encrypt(iv, clear_string.encode(), None)
ciphertext = ciphertext_and_tag[:-AUTH_TAG_LENGTH]
tag = ciphertext_and_tag[-AUTH_TAG_LENGTH:]
return (iv + tag + ciphertext).hex()
def decrypt_password(encrypted_string):
data = bytes.fromhex(encrypted_string)
iv = data[:IV_LENGTH]
tag = data[IV_LENGTH:IV_LENGTH + AUTH_TAG_LENGTH]
ciphertext = data[IV_LENGTH + AUTH_TAG_LENGTH:]
aesgcm = AESGCM(KEY)
decrypted_bytes = aesgcm.decrypt(iv, ciphertext + tag, None)
return decrypted_bytes.decode()