Commit 0bdb0766 authored by Benjamin REED's avatar Benjamin REED

working build, implemented csr

parent 0a804230
......@@ -12,7 +12,14 @@ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
DEFAULT_DATABASE_PATH = "db.txt"
USAGES = {
"new": "new <length> [ <label> <character set> ] ( 'length' = num characters in password, 'label' = optional name for the password 'charset' = characters that can be in the password (use '-<characters>' to exclude a list of characters))",
"new":
"new <mode> [ <key> ] <length> [ <label> <character set> ]\
\n 'mode' = csr or rsa, mode of encryption\
\n 'key' = the key to use when making a caesar cipher, must be a number. Not necessary for RSA!\
\n 'length' = num characters in password \
\n 'label' = optional name for the password \
\n 'charset' = characters that can be in the password (use '-<characters>' to exclude a list of characters))\
",
"echo": "echo <string content> ( 'string_content' = any text to be echoed )",
"dbg": "dbg ( Prints a debug output of the manager's state )",
"list": "list [ <label> <password> ](Prints the passwords currently stored 'label' = the password you want to decrypt)",
......@@ -76,6 +83,43 @@ def encrypt(message: bytes, key: bytes) -> bytes:
def decrypt(token: bytes, key: bytes) -> bytes:
return Fernet(key).decrypt(token)
class Password:
def __init__(self, password, key, mode):
self.password = password
self.key = key
self.mode = mode
def decrypt(password, key, mode):
if mode == "csr":
s = []
for l in password:
s.append(chr(ord(l) - int(key)))
return "".join(s)
if mode == "rsa":
print("TODO: decrypt RSA")
def encrypt(self):
match self.mode:
case "rsa":
return self.rsa_encrypt()
case "csr":
return self.csr_encrypt()
case _:
raise Exception
def serialize(self):
return f"{self.key},{self.encrypt()}"
def rsa_encrypt(self):
print("Todo! RSA encryption.")
return self.password.encode()
def csr_encrypt(self):
s = []
for l in self.password:
s.append(chr(ord(l) + int(self.key)))
return "".join(s)
class State:
def __init__(self, filepath, password):
self.filepath = filepath
......@@ -88,15 +132,15 @@ class State:
lines = content.split('\n')
head = lines[0]
# try:
# key, decrypt = password_decrypt(head, password)
# except:
# print("Password does not match for this database, and as such it cannot be decrypted.")
# raise Exception
try:
self.sha_key, decrypt = password_decrypt(head, password)
except:
print("Password does not match for this database, and as such it cannot be decrypted.")
raise Exception
self.passwords = {}
self.to_remove = []
self.key = bytes(0)
self.rsa_key = None
self.overwritten = []
self.head = head
......@@ -104,68 +148,95 @@ class State:
file.close()
return
mode = None
key = None
for i, line in enumerate(lines):
if i == 0:
continue
if line == "":
continue
parts = line.split(",")
if len(parts) != 2:
print(f"Error parsing password on line {i}: {line}\n ^^^^ -> Expected comma.")
if 'mode:' in line:
parts = line.split(':')
if len(parts) != 3:
print(f"Error parsing mode switch on line {i}: {line}\n ^^^^ -> Expected three parts, got {len(parts)}")
continue
mode = parts[1]
key = parts[2]
if mode == "rsa":
self.rsa_key = key.encode()
continue
# Expect: key,label,encrypted_password
parts = line.split(",", 1)
# if len(parts) != 3:
# print(f"Error parsing password on line {i}: {line}\n ^^^^ -> Expected three parts, got {len(parts)}.")
# continue
if mode is None or key is None:
print(f"Error parsing password on line {i}: {line}\n ^^^^ -> Missing mode description above.")
continue
# Expect: old(label) or rm(label)
l = parts[0].split("(")
if len(l) != 1 and l[0] == "rm":
part = parts[0].split("(")[1][:-1]
self.passwords[part] = parts[1].encode()
self.to_remove.append(part)
label = parts[0].split("(")[1][:-1] # trim ending parenthesis
self.passwords[label] = Password(Password.decrypt(parts[1], key, mode), key, mode)
self.to_remove.append(label)
elif len(l) != 1 and l[0] == "old":
self.overwritten.append(l[1][:-1])
self.passwords[parts[0]] = parts[1].encode()
self.passwords[parts[0]] = Password(Password.decrypt(parts[1], key, mode), key, mode)
else:
self.passwords[parts[0]] = parts[1].encode()
self.passwords[parts[0]] = Password(Password.decrypt(parts[1], key, mode), key, mode)
file.close()
def add_password(self, label, password_in, password):
if label == "":
def add_password(self, label, password_in, mode, key=None):
if label is None:
label = str(len(self.passwords) + 1)
try:
(key, _) = password_decrypt(self.head, password)
self.passwords[label] = password_encrypt(password_in.encode(), password)
self.key = key
except:
if self.key:
self.passwords[label] = Fernet(self.key).encrypt(password_in.encode())
if self.rsa_key is None:
print("TODO: generate RSA key")
if key is None:
p = Password(password_in, self.rsa_key, mode)
self.passwords[label] = p
return
raise Exception
p = Password(password_in, key, mode)
self.passwords[label] = p
#self.passwords[label] = encrypt(password.encode, self.key)
def get_password(self, label, password):
def get_password(self, label):
if label in self.passwords:
try:
dec = Fernet(self.key).decrypt(self.passwords[label])
return dec
except:
(key, decoded) = password_decrypt(self.passwords[label], password)
self.key = key
return decoded
return self.passwords[label].password
else:
return b"[ERROR: no such label found]"
def serialize_password(self, key, value):
if key in self.to_remove:
return f"rm({key}),{value.decode('utf-8')}"
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"{key},{value.decode('utf-8')}"
return f"{label},{password.encrypt()}"
def save(self):
content = self.head + "\n" + "\n".join(map(lambda x: self.serialize_password(x[0], x[1]), self.passwords.items()))
content = self.head + "\n"
for l, p in self.passwords.items():
if p.mode == "csr":
content += f"mode:csr:{p.key}\n"
content += f"{l},{p.encrypt()}\n"
if self.rsa_key is not None:
content += f"mode:rsa:{self.rsa_key.encode('utf-8')}\n"
file = open(self.filepath, 'w')
file.write(content)
......@@ -178,20 +249,30 @@ def generate_password(length, charset):
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
def main():
filepath = ""
password = ""
if len(sys.argv) < 2:
print(f"No database path supplied. Usage: {sys.argv[0]} <database path> <password>")
return
else:
filepath = sys.argv[1]
if len(sys.argv) < 3:
print(f"No password supplied. Usage: {sys.argv[0]} <database path> <password>")
return
else:
password = sys.argv[2]
try:
state = handle_args(filepath, "TODO: REMOVE THIS SHEISSE")
state = handle_args(filepath, password)
except FileNotFoundError:
print(f"Error while opening database file, {filepath} does not exist or cannot be opened.")
ask = input(f"Create {filepath}? (Y/N)")
......@@ -371,20 +452,48 @@ def main():
for over in state.overwritten:
print(f"{over}: [...]", end=' ')
print("")
elif command == "new": # Form: new <length> [ <label> ]
elif command == "new": # Form: 0:new 1:<mode> 2:<key> 3:<length> [ 4:<label> ]
if len(args) < 2:
print(f"Too few arguments for {command}, usage: {USAGES['new']}")
continue
label = None
s_length = None
key = None
charset = "all"
mode = args[1]
if mode == 'csr':
if len(args) < 4:
print(f"Too few arguments for new caesar password. Usage: {USAGES['new']}")
continue
key = args[2]
s_length = args[3]
if len(args) > 4:
label = args[4]
if len(args) > 5:
charset = args[5]
elif mode == 'rsa':
if len(args) < 3:
print(f"Too few arguments for new rsa password: Usage: {USAGES['new']}");
continue
key = None
s_length = args[2]
if len(args) > 3:
label = args[3]
if len(args) > 4:
charset = args[4]
else:
print(f"Mode '{mode}' is unknown. Modes are 'rsa' and 'csr'")
continue
try:
length = int(args[1])
length = int(s_length)
except:
print(f"Error parsing length as number, usage: {USAGES['new']}")
continue
if len(args) >= 3:
label = args[2]
else:
label = ""
if label in state.passwords:
print(f"Error generating new password, label '{label}' is already in use.")
......@@ -400,13 +509,8 @@ def main():
else:
continue
charset = "all"
chars = set()
if len(args) == 4:
charset = args[3]
if charset == "all":
chars = LOWERCASE_ALPHABET | UPPERCASE_ALPHABET | NUMBERS | SPECIAL
else:
......@@ -419,10 +523,7 @@ def main():
password = generate_password(length, chars)
print(str(password))
try:
state.add_password(label, password, input("Please input the database password: "))
except:
print("Incorrect password")
state.add_password(label, password, mode, key)
elif command == "list" or command == "ls":
if len(args) == 1:
# if state.passwords.len() >= 10:
......@@ -434,14 +535,7 @@ def main():
print("(Hint: type 'list <key>' to decrypt your chosen password")
elif len(args) == 2:
label = args[1].strip().lower()
try:
print(state.get_password(args[1], "").decode('utf-8'))
except:
print("Please input the password to decrypt this item.")
elif len(args) == 3:
label = args[1].strip().lower()
password = args[2]
print(state.get_password(args[1], password).decode('utf-8'))
print(state.get_password(args[1]))
else:
print(f"Too few args for {command}. Usage: {USAGES['list']}")
......
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