2026-02-21 18:35:20 -06:00

62 lines
1.9 KiB
Python

import os
import sys
from pathlib import Path
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
sys.path.append(str(Path(__file__).resolve().parent.parent.parent))
from .config import (
SALT_SIZE,
KEY_SIZE,
AES_NONCE_SIZE,
AES_TAG_SIZE,
ITERATIONS,
)
def derive_key(password: str, salt: bytes) -> bytes:
"""Derives a key from a password and salt using PBKDF2-HMAC-SHA256."""
if not password:
raise ValueError("Password cannot be empty.")
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=KEY_SIZE,
salt=salt,
iterations=ITERATIONS,
backend=default_backend(),
)
return kdf.derive(password.encode("utf-8"))
def encrypt_data(data: bytes, password: str) -> bytes:
"""Encrypts data using AES-256 GCM."""
salt = os.urandom(SALT_SIZE)
nonce = os.urandom(AES_NONCE_SIZE)
key = derive_key(password, salt)
cipher = Cipher(algorithms.AES(key), modes.GCM(nonce), backend=default_backend())
encryptor = cipher.encryptor()
ciphertext = encryptor.update(data) + encryptor.finalize()
return salt + nonce + encryptor.tag + ciphertext
def decrypt_data(encrypted_data: bytes, password: str) -> bytes:
"""Decrypts data using AES-256 GCM."""
salt = encrypted_data[:SALT_SIZE]
nonce = encrypted_data[SALT_SIZE : SALT_SIZE + AES_NONCE_SIZE]
tag = encrypted_data[
SALT_SIZE + AES_NONCE_SIZE : SALT_SIZE + AES_NONCE_SIZE + AES_TAG_SIZE
]
ciphertext = encrypted_data[SALT_SIZE + AES_NONCE_SIZE + AES_TAG_SIZE :]
key = derive_key(password, salt)
cipher = Cipher(
algorithms.AES(key), modes.GCM(nonce, tag), backend=default_backend()
)
decryptor = cipher.decryptor()
return decryptor.update(ciphertext) + decryptor.finalize()