Commit 6d7da466 authored by Benjamin REED's avatar Benjamin REED

cleanup and doc comments

parent ad9e425c
......@@ -49,6 +49,9 @@ CHARSET_MAP = {
SAFEST = 50
"""
Simple function that prints a prompt message to get a confirmation message.
"""
def confirm(msg, yes, no):
i = input(msg + f" ({yes}/{no})")
i.strip()
......@@ -90,6 +93,16 @@ def encrypt(message: bytes, key: bytes) -> bytes:
def decrypt(token: bytes, key: bytes) -> bytes:
return Fernet(key).decrypt(token)
"""
Password class contains:
- key => The key used to encrypt and decrypt the password, 'int' for caesar, 'Tuple(bigint, bigint)' for RSA
- mode => 'rsa' or 'csr', the encryption method used for the password
- password => the unencrypted password
Note: it is probably a bad idea to have the program open on a multi-user device, as the passwords
are stored in Python interpreter memory unobfuscated, and as such can be found using simple C function calls.
Memory obfuscation is possibly a feature to add in the future or to use as the consumer of this program.
"""
class Password:
def __init__(self, password, key, mode):
self.password = password
......@@ -102,6 +115,12 @@ class Password:
def __repr__(self):
return self.__str__()
"""
Accept a key and an encrypted password and return the decrypted password.
NOTE: this uses an unbeleivably dangerous loophole with 'eval' that should
not be allowed to release into the final version
"""
def decrypt(password, key, mode):
if mode == "csr":
s = []
......@@ -116,6 +135,9 @@ class Password:
password = eval(password)
return rsa.decrypt_data(key.q, password)
"""
Returns the encrypted bytes of the password depending on password type.
"""
def encrypt(self):
match self.mode:
case "rsa":
......@@ -125,18 +147,31 @@ class Password:
case _:
raise Exception
"""Returns serialized string representing this password for saving.
"""
def serialize(self):
return f"{self.key},{self.encrypt()}"
"""
Encrypt a password with helper functions from the rsa.py module.
"""
def rsa_encrypt(self):
return rsa.encrypt_data(self.key.p, self.password)
"""
Increment the index of each character by one.
"""
def csr_encrypt(self):
s = []
for l in str(self.password):
s.append(chr(ord(l) + int(self.key)))
return "".join(s)
"""
State of password manager, passwords, keys, and path of database file.
NOTE: the class's constructor is also its load function.
"""
class State:
def __init__(self, filepath, password):
if not os.path.isfile(filepath):
......@@ -218,6 +253,11 @@ class State:
file.close()
"""
Add a password to the state depending on what type of password it is.
NOTE: an empty string as a label will cause the label to be the number of passwords + 1
"""
def add_password(self, label, password_in, mode, key=None):
if label is None:
label = str(len(self.passwords) + 1)
......@@ -233,8 +273,10 @@ class State:
p = Password(password_in, key, mode)
self.passwords[label] = p
#self.passwords[label] = encrypt(password.encode, self.key)
"""
Returns password as bytes.
"""
def get_password(self, label):
if label in self.passwords:
p = self.passwords[label]
......@@ -245,16 +287,25 @@ class State:
else:
return b"[ERROR: no such label found]"
def serialize_password(self, label, password):
if label in self.to_remove:
return f"rm({label}),{password.encrypt()}"
# elif key in self.overwritten:
# del self.overwritten[self.overwritten.index(key)]
# return f"old({key}),{value.decode('utf-8')}"
else:
return f"{label},{password.encrypt()}"
"""
Saves the State to State.filepath.
NOTE: the database file currently contains no version data, making incompatibility un unnoticed error.
FORMAT:
<head> <-- verification that inputted password is correct using Fernet encryption
<rsa key> <-- RSA key corresponding to this state
<foreach p in passwords>
if p is caesar:
'mode:csr:<p.key>' <-- each caesar password has its own key
'<p.label>,<p.encrypted_password>' <-- comma separated
<end>
'mode:rsa:_' <-- _ means the key is defined at the top of the file
<foreach p in passwords>
if p is rsa:
'<p.label>,<p.encrypted_password>' <-- same format as caesar, just in a different section
<end>
"""
def save(self):
content = self.head + "\n"
content += f"{self.rsa_key}" + "\n"
......@@ -277,16 +328,15 @@ class State:
file.write(content)
file.close()
"""
Generate a password given a 'set' object of characters to choose from.
"""
def generate_password(length, charset):
s = []
for i in range(0, length):
s.append(random.choice(list(charset)))
return ''.join(s)
def aes_256(inp):
pass
def handle_args(db: str, password: str):
state = State(db, password)
return state
......@@ -352,7 +402,6 @@ def main():
print(f"Error parsing database {filepath}: {e}")
return
# TODO: make this serialized (somehow... :[)
cfg = {
"hints": True
}
......@@ -470,7 +519,7 @@ def main():
print("You will have to rename this password to restore it.")
label_in = input("New label (If left empty, a number will be used): ")
if label_in == "":
label = str(len(state.passwords) + 1) # oh so hacky, i hate python and i want my damn state to be operated on by its own damn functions. I hate this.
label = str(len(state.passwords) + 1)
else:
label_in.strip()
label = label_in.lower()
......@@ -589,7 +638,6 @@ def main():
state.add_password(label, password.encode(), mode, key)
elif command == "list" or command == "ls":
if len(args) == 1:
# if state.passwords.len() >= 10:
# TODO: make it possible to scroll thru a list of passwords
for l in state.passwords:
if l not in state.to_remove and "old" not in l:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment