import sys import os from getpass import getpass from hashlib import md5, sha256 from base64 import b64encode from os.path import expanduser import pyperclip import nacl.secret def show_usage_error(error_message): raise Exception(f"""Invalid usage: {error_message} Syntax: python3 manager.py Commands: save read readv1 auth del list""") def read_passwords_list(): passwords_list_path = '.ps_list' if not os.path.exists(passwords_list_path): save_passwords_list([]) with open(passwords_list_path, 'r') as file: return file.read().splitlines() def save_passwords_list(passwords_list): with open('.ps_list', 'w') as file: file.writelines('\n'.join(passwords_list)) def append_to_passwords_list(new_entries): current_list = read_passwords_list() updated_list = current_list + new_entries save_passwords_list(list(set(updated_list))) def decrypt_master_password(protect_pass=None): master_pass_path = f'{expanduser("~")}/.master_pass' with open(master_pass_path, 'rb') as file: encrypted_data = file.read() if not protect_pass: protect_pass = getpass("Protect password: ") protect_hash = sha256(protect_pass.encode('utf-8')).digest() box = nacl.secret.SecretBox(protect_hash) nonce = encrypted_data[:nacl.secret.SecretBox.NONCE_SIZE] encrypted = encrypted_data[nacl.secret.SecretBox.NONCE_SIZE:] return box.decrypt(encrypted, nonce) def process_command(args): if len(args) < 2: show_usage_error("No command provided.") command = args[1] if command in ['save', 'del'] and len(args) < 3: show_usage_error("Missing arguments for command.") if command == 'save': entry_name = args[2] comment = input("Enter comment for this password: ") entry = f"{entry_name} – {comment}" if comment else entry_name append_to_passwords_list([entry]) print("Entry saved.") elif command == 'auth': master_pass = getpass("Master-password: ").encode('utf-8') protect_pass = getpass("Protect password: ") protect_hash = sha256(protect_pass.encode('utf-8')).digest() box = nacl.secret.SecretBox(protect_hash) encrypted = box.encrypt(master_pass) with open(f'{expanduser("~")}/.master_pass', 'wb') as file: file.write(encrypted) print("Master-password saved.") elif command == 'read': master_pass = decrypt_master_password() postfix = getpass("Password postfix: ") hash_digest = sha256(master_pass + postfix.encode('utf-8')).digest() password = b64encode(hash_digest).decode().replace('=', '.').replace('+', '-').replace('/', '_') pyperclip.copy(password) print("Password copied to clipboard.") elif command == 'readv1': master_pass = decrypt_master_password() postfix = getpass("Password postfix: ") hash_digest = md5(master_pass + postfix.encode('utf-8')).digest() password = b64encode(hash_digest).decode().replace('=', '') pyperclip.copy(password) print("Password (v1) copied to clipboard.") elif command == 'del': entry_name = args[2] updated_list = [entry for entry in read_passwords_list() if not entry.startswith(entry_name)] save_passwords_list(updated_list) print("Entry deleted.") elif command == 'list': print("Known passwords:") for entry in read_passwords_list(): print(entry) else: show_usage_error("Unknown command.") if __name__ == '__main__': try: process_command(sys.argv) except Exception as e: print(f"Error: {e}") sys.exit(1)