105 lines
3.7 KiB
Python
105 lines
3.7 KiB
Python
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) |