You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
9.0 KiB
Python
215 lines
9.0 KiB
Python
try:
|
|
import base64
|
|
import pickle
|
|
from tabulate import tabulate
|
|
from pysqlcipher3 import dbapi2 as sc
|
|
from pyotp import TOTP
|
|
from time import time
|
|
from math import log2
|
|
from password_strength import PasswordStats
|
|
from getpass import getpass as gp
|
|
from secrets import token_urlsafe
|
|
from random import randint as rint, SystemRandom as sr
|
|
from atexit import register
|
|
from os import urandom, path, remove
|
|
except ModuleNotFoundError:
|
|
print("You have not installed the required modules. Follow these steps to do so:\n\n1. Open the terminal (Linux/MacOS) or command prompt (Windows).\n2. Navigate to this directory and then to the files directory.\n3. Type 'pip install -r dependencies.txt'.\n4. Restart the program.\n\nIf you have followed all the steps correctly, keyvault will work on the next start.")
|
|
exit()
|
|
|
|
def database_enc():
|
|
global conn
|
|
global cursor
|
|
conn = sc.connect("database/keyvault.db")
|
|
cursor = conn.cursor()
|
|
if path.isfile("database/keyvault.db"):
|
|
for _ in range(3):
|
|
try:
|
|
password = gp(prompt = "Enter master password: ")
|
|
conn.execute(f"PRAGMA key = {password}")
|
|
conn.execute('''
|
|
CREATE TABLE IF NOT EXISTS data (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
service TEXT,
|
|
username TEXT,
|
|
email TEXT,
|
|
password TEXT,
|
|
website TEXT,
|
|
category TEXT,
|
|
notes TEXT,
|
|
totp TEXT )
|
|
''')
|
|
passCorrect = True
|
|
break
|
|
except sc.DatabaseError:
|
|
print("Incorrect password.\n")
|
|
passCorrect = False
|
|
|
|
if not passCorrect:
|
|
print("You have entered a wrong password three times. Please restart the program to try again.")
|
|
exit()
|
|
|
|
else:
|
|
print("You have not setup a master password yet. Please set one below.\n")
|
|
while True:
|
|
mp, mp2 = gp(prompt = "Enter a secure master password (hidden for privacy!): "), gp(prompt = "Please enter it again: ")
|
|
if mp == mp2:
|
|
if len(mp) < 8:
|
|
print("\nThe master password you have set is too weak. Please set another one.")
|
|
else:
|
|
break
|
|
else:
|
|
print("Both of the passwords are different. Please enter the same password.")
|
|
conn.execute(f"PRAGMA key = {mp}")
|
|
conn.commit()
|
|
|
|
def gen():
|
|
userpass = input("Type 'u' to generate usernames or 'p' for passwords: ").lower()
|
|
if userpass == 'u':
|
|
with open("files/generation/wordlist.txt", "r") as f:
|
|
words = f.readlines()
|
|
word1, word2 = rint(0, 8874), rint(0, 8874)
|
|
username = f"{words[word1][0:-1]}{words[word2][0:-1]}{rint(0, 100000)}"
|
|
print(username)
|
|
elif userpass == 'p':
|
|
while True:
|
|
length = int(input("Enter password length (above 8 only): "))
|
|
if length <= 7:
|
|
print("The password is too short. Please enter it again.")
|
|
else:
|
|
break
|
|
pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~@#$%^&*()-_=+]}[{\"';:.>,</?"
|
|
strength = ''.join(sr().choice(pool) for i in range(length))
|
|
print(strength)
|
|
else:
|
|
print("Incorrect input.")
|
|
|
|
def strength():
|
|
password = gp(prompt = "\nEnter the password to check its strength (hidden for privacy!): ")
|
|
if password in common_passwords:
|
|
print("Your password is in the list of 1000 of the most common passwords. Change your password immediately.")
|
|
else:
|
|
poolsize = 0
|
|
for l in password:
|
|
if l.islower():
|
|
poolsize += 26
|
|
elif l.isupper():
|
|
poolsize += 26
|
|
elif l.isdigit():
|
|
poolsize += 10
|
|
else:
|
|
poolsize += 32
|
|
entropy = log2(poolsize**len(password))
|
|
passstrength = PasswordStats(strength).strength()
|
|
if passstrength < 0.33:
|
|
print(f"[PASSWORD STRENGTH]: {passstrength}\n[PASSWORD ENTROPY]: {entropy} bits\nYour password is extremely weak. Please change it.\n")
|
|
elif passstrength >= 0.33 and passstrength < 0.66:
|
|
print(f"[PASSWORD STRENGTH]: {passstrength}\n[PASSWORD ENTROPY]: {entropy} bits\nYour password is of fair strength. Please consider changing it to a stronger one.\n")
|
|
elif passstrength >= 0.66 and passstrength < 0.80:
|
|
print(f"[PASSWORD STRENGTH]: {passstrength}\n[PASSWORD ENTROPY]: {entropy} bits\nYour password is strong.\n")
|
|
elif passstrength >= 0.80 and passstrength < 0.9:
|
|
print(f"[PASSWORD STRENGTH]: {passstrength}\n[PASSWORD ENTROPY]: {entropy} bits\nYour password is incredibly strong.\n")
|
|
else:
|
|
print(f"[PASSWORD STRENGTH]: {passstrength}\n[PASSWORD ENTROPY]: {entropy} bits\nYour password is practically uncrackable.\n")
|
|
|
|
def add():
|
|
service = username = email = password = website = category = notes = totp = " "
|
|
print("For any of these fields, don't type anything to make it an empty field.")
|
|
service = input("Enter service: ")
|
|
username = input("Enter username: ")
|
|
email = input("Enter email: ")
|
|
password = gp(prompt = "Enter password (hidden for privacy): ")
|
|
website = input("Enter website: ")
|
|
category = input("Enter category: ")
|
|
notes = input("Enter notes: ")
|
|
totp = input("Enter TOTP secret: ")
|
|
data = [service, username, email, password, website, category, notes, totp]
|
|
for _ in range(len(data)):
|
|
if data[_] == "":
|
|
data[_] = None
|
|
cursor.execute("INSERT INTO data(service, username, email, password, website, category, notes, totp) VALUES(?, ?, ?, ?, ?, ?, ?, ?)", tuple(data))
|
|
conn.commit()
|
|
|
|
def ls(cmd="SELECT * FROM data"):
|
|
cursor.execute(cmd)
|
|
data = list(cursor.fetchall())
|
|
if data == []:
|
|
print("The database is empty.")
|
|
else:
|
|
for _ in range(len(data)):
|
|
data[_] = list(data[_])
|
|
if data[_][-1] != None:
|
|
data[_].append(TOTP(str(data[_][-1])).now())
|
|
else:
|
|
data[_].append(None)
|
|
|
|
head = ["ID", "Service", "Username", "Email", "Password", "Website", "Category", "Notes", "TOTP Secret", "TOTP Code"]
|
|
print(tabulate(data, headers = head, tablefmt = "grid"))
|
|
return data
|
|
|
|
def rm():
|
|
ls()
|
|
delid = int(input("Enter the ID of the record you want to delete: "))
|
|
cursor.execute("DELETE FROM data WHERE id=?", (delid,))
|
|
conn.commit()
|
|
|
|
def find():
|
|
print("The following search criteria are available: id, service, username, email, website, category")
|
|
criterian = input("Enter your criterian here: ").lower()
|
|
if criterian not in ['id', 'service', 'username', 'email', 'website', 'category']:
|
|
print("You have selected an invalid criterian.")
|
|
else:
|
|
srchquery = input("Enter your search query: ")
|
|
sqlquery = f"SELECT * FROM data WHERE {criterian}='{srchquery}'"
|
|
ls(sqlquery)
|
|
|
|
def edit():
|
|
print("The following edit criteria are available: id, service, username, email, password, website, category, notes, totp")
|
|
criterian = input("Enter your criterian here: ").lower()
|
|
if criterian not in ['id', 'service', 'username', 'email', 'password', 'website', 'category', 'notes', 'totp']:
|
|
print("You have selected an invalid criterian.")
|
|
else:
|
|
idsearch = input("Enter the ID of the entry to edit: ")
|
|
data = ls(f"SELECT * FROM data WHERE id={idsearch}")
|
|
if data == []:
|
|
print("This entry does not exist.")
|
|
else:
|
|
confirmation = input("Is this the correct entry? [y/n] > ").lower()
|
|
if confirmation == 'y':
|
|
newvalue = input("Enter the new value: ")
|
|
cursor.execute(f"UPDATE data SET {criterian}='{newvalue}' WHERE id={idsearch}")
|
|
else:
|
|
print("You have cancelled the entry edit process.")
|
|
|
|
database_enc()
|
|
|
|
with open("files/strength/common-passwords.txt", "r") as f:
|
|
common_passwords = f.read()
|
|
|
|
print("\nkeyvault initialized.")
|
|
|
|
print("keyvault is ready to use! Type 'help' for a list of commands.\n")
|
|
|
|
while True:
|
|
command = input("> ").lower()
|
|
if command == 'help':
|
|
print("\nUsage:\n\nadd - add an entry\nls - list entires\nrm - remove entry\ngen - generate a password or username\nstrength - check password strength\nedit - edit an entry\nfind/search - open search wizard\nhelp - display this message\nversion - print current version\n")
|
|
elif command == 'search' or command == 'find':
|
|
find()
|
|
elif command == 'ls':
|
|
ls()
|
|
elif command == 'add':
|
|
add()
|
|
elif command == 'rm':
|
|
rm()
|
|
elif command == 'edit':
|
|
edit()
|
|
elif command == 'version':
|
|
print("keyvault: v1.0.0")
|
|
elif command == 'gen':
|
|
gen()
|
|
elif command == 'strength':
|
|
strength()
|
|
elif command == 'exit':
|
|
print("Thank for you using keyvault!")
|
|
exit()
|