| | import os |
| | import json |
| | import base64 |
| | from cryptography.hazmat.primitives import hashes |
| | from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC |
| | from cryptography.hazmat.primitives.ciphers.aead import AESGCM |
| |
|
| | |
| | def generate_key(password: str, salt: bytes) -> bytes: |
| | kdf = PBKDF2HMAC( |
| | algorithm=hashes.SHA256(), |
| | length=32, |
| | salt=salt, |
| | iterations=100000, |
| | ) |
| | return kdf.derive(password.encode()) |
| |
|
| | |
| | def encrypt_password(password: str, key: bytes): |
| | |
| | nonce = os.urandom(12) |
| | aesgcm = AESGCM(key) |
| | |
| | ciphertext = aesgcm.encrypt(nonce, password.encode(), None) |
| | |
| | return base64.b64encode(nonce + ciphertext).decode() |
| |
|
| | |
| | def decrypt_password(encrypted_password: str, key: bytes): |
| | raw = base64.b64decode(encrypted_password) |
| | nonce, ciphertext = raw[:12], raw[12:] |
| | aesgcm = AESGCM(key) |
| | |
| | return aesgcm.decrypt(nonce, ciphertext, None).decode() |
| |
|
| | |
| | def store_password(account: str, username: str, password: str, master_password: str): |
| | salt = os.urandom(16) |
| | key = generate_key(master_password, salt) |
| | encrypted_pw = encrypt_password(password, key) |
| | data = {"account": account, "username": username, "password": encrypted_pw, "salt": base64.b64encode(salt).decode()} |
| | |
| | try: |
| | with open("passwords.json", "r") as f: |
| | records = json.load(f) |
| | except FileNotFoundError: |
| | records = [] |
| |
|
| | records = [r for r in records if r["account"] != account] |
| | records.append(data) |
| |
|
| | with open("passwords.json", "w") as f: |
| | json.dump(records, f, indent=4) |
| | print(f"[+] Authenticated GCM password stored for: {account}") |
| |
|
| | |
| | def retrieve_password(account: str, master_password: str): |
| | try: |
| | with open("passwords.json", "r") as f: |
| | records = json.load(f) |
| | except FileNotFoundError: |
| | return None |
| |
|
| | for r in records: |
| | if r["account"] == account: |
| | salt = base64.b64decode(r["salt"]) |
| | key = generate_key(master_password, salt) |
| | try: |
| | return decrypt_password(r["password"], key) |
| | except Exception: |
| | print("[-] Master password incorrect or data tampered with!") |
| | return None |
| | return None |
| |
|
| | |
| | def encrypt_file(file_path: str, account: str, master_password: str): |
| | password = retrieve_password(account, master_password) |
| | if not password: return |
| |
|
| | try: |
| | with open(file_path, "rb") as f: |
| | content = f.read() |
| | except FileNotFoundError: return |
| |
|
| | salt = os.urandom(16) |
| | nonce = os.urandom(12) |
| | key = generate_key(password, salt) |
| | |
| | aesgcm = AESGCM(key) |
| | ciphertext = aesgcm.encrypt(nonce, content, None) |
| |
|
| | enc_file = file_path + ".enc" |
| | with open(enc_file, "wb") as f: |
| | f.write(ciphertext) |
| |
|
| | meta = { |
| | "salt": base64.b64encode(salt).decode(), |
| | "nonce": base64.b64encode(nonce).decode(), |
| | "account": account |
| | } |
| | with open(enc_file + ".meta", "w") as f: |
| | json.dump(meta, f) |
| |
|
| | print(f"[+] File encrypted with AES-GCM: {enc_file}") |
| |
|
| | |
| | def decrypt_file(enc_file: str, master_password: str): |
| | meta_file = enc_file + ".meta" |
| | if not os.path.exists(meta_file): return |
| |
|
| | with open(meta_file, "r") as f: |
| | meta = json.load(f) |
| |
|
| | account = meta["account"] |
| | salt = base64.b64decode(meta["salt"]) |
| | nonce = base64.b64decode(meta["nonce"]) |
| |
|
| | password = retrieve_password(account, master_password) |
| | if not password: return |
| |
|
| | key = generate_key(password, salt) |
| | with open(enc_file, "rb") as f: |
| | ciphertext = f.read() |
| |
|
| | aesgcm = AESGCM(key) |
| | try: |
| | data = aesgcm.decrypt(nonce, ciphertext, None) |
| | dec_file = enc_file.replace(".enc", "_decrypted") |
| | with open(dec_file, "wb") as f: |
| | f.write(data) |
| | print(f"[+] Authenticated decryption successful: {dec_file}") |
| | except Exception: |
| | print("[-] Decryption failed: Invalid password or corrupted data.") |