passwords/manager.py

105 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 <command> <args>
Commands:
save <name>
read
readv1
auth
del <name>
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)