-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathencryption.py
More file actions
83 lines (61 loc) · 2.52 KB
/
encryption.py
File metadata and controls
83 lines (61 loc) · 2.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import base64
import hashlib
import os
import re
from argon2 import PasswordHasher
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from dotenv import load_dotenv
__all__ = [
"decrypt_email",
"encrypt_email",
"hash_email",
"hash_password",
"mask_email",
"verify_password",
]
load_dotenv()
ph = PasswordHasher()
AES_KEY = bytes.fromhex(os.getenv("AES_SECRET_KEY", os.urandom(32).hex()))
PEPPER = os.getenv("PEPPER", "SuperSecretPepper").encode("utf-8")
def decrypt_email(encrypted_email: str) -> str:
"""Decrypts an AES-256 encrypted email."""
encrypted_data = base64.b64decode(encrypted_email)
iv, ciphertext = encrypted_data[:16], encrypted_data[16:]
cipher = Cipher(algorithms.AES(AES_KEY), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
decrypted_email = decryptor.update(ciphertext) + decryptor.finalize()
return decrypted_email.strip().decode()
def encrypt_email(email: str) -> str:
"""Encrypts an email using AES-256."""
iv = os.urandom(16) # Generate a random IV
cipher = Cipher(algorithms.AES(AES_KEY), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
# Pad email to 16-byte blocks
padded_email = email + (16 - len(email) % 16) * " "
ciphertext = encryptor.update(padded_email.encode()) + encryptor.finalize()
# Store IV + ciphertext (Base64 encoded)
return base64.b64encode(iv + ciphertext).decode()
def hash_email(email: str) -> str:
"""Generate a SHA-256 hash of the email (used for fast lookup)."""
return hashlib.sha256(email.encode()).hexdigest()
def hash_password(password: str) -> tuple[str, bytes]:
peppered_password = password.encode("utf-8") + PEPPER
return ph.hash(peppered_password) # Argon2 applies salt automatically
def mask_email(email: str) -> str:
local, domain = email.split("@")
domain_parts = domain.split(".")
def mask_part(part: str) -> str:
if len(part) <= 2:
return part
return part[0] + "*" * (len(part) - 2) + part[-1]
masked_local = re.sub(
r"(\w)(\w+)(\w)",
lambda m: m.group(1) + "*" * len(m.group(2)) + m.group(3),
local,
)
masked_domain = ".".join(mask_part(part) for part in domain_parts)
return masked_local + "@" + masked_domain
def verify_password(password, stored_password):
peppered_password = password.encode("utf-8") + PEPPER
return ph.verify(stored_password, peppered_password)