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