62 lines
1.9 KiB
Python
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()
|