After almost completely redoing the first version I think I finally have it. I still have a few things to add/change, but I think it's pretty good and safe. I've improved security features as well as overall functionality.
I haven't run this program through pycharm yet, so there might be a few PEP 8 errors. It took me a few weeks just trying to learn about cryptography (not an easy subject). For @GarethRees post, I really didn't even start on version 2 until I implemented some sort of encryption that worked perfectly on Version 1. I tried using gpg as he recommended, but could never figure it out. I even tried it again after figuring out how to use pycrypto.
If there are any bugs just point them out you don't have to write a full review just whatever you think should be changed or added post it. I really appreciate feedback and will upvote all. If you want a .exe version just ask and I'll post a link. Hope everyone likes it.
#Programmer: DeliriousSyntax
#Date: 12-9-15
#File: AccountKeeperV2.py
#This program lets you store and create passwords
import CustomFunctions as CF
import pyperclip
import random
import shelve
import EcstaticCryption
import string
#Encrypting and Decrypting is done with pycrypto
"""
Task List:
Hide key after hitting enter
Create a settings GUI
Let user continuously generate a password untill satisfied
When changing password let user pick a new random password
Use string.punctuation in character list without messing up printing
Add Exclude similar characters option
Let user save settings without opening script
"""
class main:
LOWER = True
UPPER = True
NUMBERS = True
SYMBOLS = True
COPY = True
File = "Keeper.dat"
template = ('\n- Account: {} '
'- Username: {} '
'- Password: {} '
' -')
key = input('Key: ')
EC = EcstaticCryption.AESCipher(key)
@property
def CHARACTERS(self):
CHAR = []
if self.LOWER:CHAR.append(string.ascii_lowercase)
if self.UPPER:CHAR.append(string.ascii_uppercase)
if self.SYMBOLS:CHAR.append('!#$%&()*+,-.:;<=>?@[]^_`{|}~')
if self.NUMBERS:CHAR.append(string.digits)
return CHAR
def all_accounts(self):
with shelve.open(self.File) as f:
print('\n')
for account in f:
print(account, end=' ')
print('\n')
def all_users(self, account):
with shelve.open(self.File) as f:
print('\n')
for user in f[account]:
print(user, end=' ')
print('\n')
def check_account(self, account):
list_of_accounts = []
with shelve.open(self.File) as f:
if account in f:
return True
else:
return False
def check_username(self, account, username):
list_of_users = []
with shelve.open(self.File) as f:
try:
if username in f[account]:
return True
else:
pass
except KeyError:
pass
return False
def generate_password(self, digits_in_pass):
holder = []
for _ in range(digits_in_pass):
temp = random.SystemRandom().choice(self.CHARACTERS)
holder.append(random.SystemRandom().choice(temp))
return ''.join(holder)
def save_account(self, entry):
with shelve.open(self.File) as f:
try:
holder_account = f[entry[0]]
except KeyError:
holder_account = {}
holder_account[entry[1]] = entry
f[entry[0]] = holder_account
def new_account(self, log=True):
"""Creates a new random password"""
print('\n')
account = input("Account: ")
with shelve.open(self.File) as f:
while True:
username = input("Username: ")
existing_username = self.check_username(account, username)
if not existing_username:
break
print("This account already exists!")
password = input("Password (Type \"random\" for random password): ")
if password.lower() == 'random':
digits_in_pass = CF.valid_int("Length of password: ")
password = self.generate_password(digits_in_pass)
encrypted_password = self.EC.encrypt(password)
entry = [account, username, encrypted_password]
self.save_account(entry)
if self.COPY:
pyperclip.copy(password)
if log:
print(self.template.format(entry[0], entry[1], password))
def print_account(self, account, username):
with shelve.open(self.File) as f:
entry = f[account][username]
password = self.EC.decrypt(entry[2])
print(self.template.format(entry[0], entry[1], password))
if self.COPY:
pyperclip.copy(password)
def change_username(self, account, username, log=True):
new_username = input('\nEnter new username: \n ->')
with shelve.open(self.File) as f:
account_holder = f[account]
account_holder[new_username] = account_holder.pop(username)
account_holder[new_username][1] = new_username
f[account] = account_holder
if log:
entry = f[account][new_username]
password = self.EC.decrypt(entry[2])
print(self.template.format(entry[0], entry[1], password))
def change_password(self, account, username, log=True):
new_password = input('Enter new password: \n ->')
with shelve.open(self.File) as f:
f[account][username][2] = self.EC.encrypt(new_password)
if log:
entry = f[account][username]
print(self.template.format(entry[0], entry[1], new_password))
def delete_account(self, account, username):
confirmation = input("\nType 'DELETE' to confirm deletion of this account...\n ->")
if confirmation.lower() == 'delete':
with shelve.open(self.File) as f:
account_holder = f[account]
try:
del account_holder[username]
f[account] = account_holder
print('\nAccount deleted...')
except KeyError:
print('Error deleting account!')
def account_menu(self, account, username):
print("\nAccount Found! What's next?\n"
" 1) Print account\n"
" 2) Change username\n"
" 3) Change password\n"
" 4) Delete account\n"
" 5) Cancel")
account_choice = input(" ->")
if account_choice == '1':
self.print_account(account, username)
elif account_choice == '2':
self.change_username(account, username)
elif account_choice == '3':
self.change_password(account, username)
elif account_choice == '4':
self.delete_account(account, username)
else:
pass
def find_account(self):
account = input('\n\nAccount: ')
while account == 'all accounts':
self.all_accounts()
account = input('Account: ')
existing_account = self.check_account(account)
if existing_account:
username = input('Username: ')
while username == 'all users':
self.all_users(account)
username = input('Username: ')
existing_user = self.check_username(account, username)
if existing_user:
self.account_menu(account, username)
else:
print('\nWe could not find your account.')
else:
print('\nCould not find any {} accounts.'.format(account))
def program_start(self):
"""MAIN"""
choice = None
while True:
print("\n\n\nMenu:\n"
" 1) Add an account\n"
" 2) Search for an existing account\n"
" 3) Exit")
choice = input(" ->")
if choice == "1":
self.new_account()
elif choice == "2":
self.find_account()
else:
break
if __name__ == '__main__':
print("Welcome to Account Keeper V2")
m = main()
m.program_start()