Skip to content

Commit

Permalink
Secrets Management Page
Browse files Browse the repository at this point in the history
  • Loading branch information
Ameasere committed Apr 29, 2024
1 parent 29b8067 commit 74f5d5f
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 22 deletions.
2 changes: 1 addition & 1 deletion UI/main.ui
Original file line number Diff line number Diff line change
Expand Up @@ -2877,7 +2877,7 @@ QPushButton:pressed {
<string>Secrets</string>
</property>
</widget>
<widget class="QPlainTextEdit" name="send_pte_2">
<widget class="QPlainTextEdit" name="secret_value">
<property name="geometry">
<rect>
<x>40</x>
Expand Down
187 changes: 177 additions & 10 deletions hsm/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.fernet import Fernet
from Crypto.Cipher import DES, DES3, Blowfish
from twofish import Twofish
from Crypto.Util.Padding import pad
Expand All @@ -26,6 +29,7 @@
valid_hashes = hashlib.algorithms_available
valid_encryption_decryption_algorithms = ["aes", "rsa", "des", "3des", "blowfish", "twofish"]
valid_secret_generators = ["uuid4", "random", "randomstring", "randomnumber"]
salt = open("/etc/polaris/salt", "r").read()


def check_if_db_exists():
Expand Down Expand Up @@ -126,35 +130,91 @@ def protocol(data):
data = data[10:]
data = data.split("/")

if len(data) != 4:
if len(data) < 4 or len(data) > 5:
print(f"[{time.ctime()}] Invalid data received: {data} | Does not have the correct number of elements.")
sys.stdout.flush()
logger(f"Invalid data received: {data} | Does not have the correct number of elements.")
return False

# Replace the data[0] element with the lower of itself
action = data[0].lower().split(":")[0]
mid = data[1].split(":")[1]
mp = data[2].split(":")[1]
username = data[3].split(":")[1]
secret_value = ""
secret_label = ""
if action != "store" and action != "retrieve":
mid = data[1].split(":")[1]
mp = data[2].split(":")[1]
username = data[3].split(":")[1]
elif action == "store":
try:
secret_value = data[0].split(":")[2]
secret_label = data[0].split(":")[1]
mid = data[1].split(":")[1]
mp = data[2].split(":")[1]
username = data[3].split(":")[1]
except IndexError as e:
print(f"[{time.ctime()}] Invalid data received: {data} | Indexes misaligned: {repr(e)}")
sys.stdout.flush()
logger(f"Invalid data received: {data} | Indexes misaligned: {repr(e)}")
return False
elif action == "retrieve":
mid = data[1].split(":")[1]
mp = data[2].split(":")[1]
username = data[3].split(":")[1]
try:
secret_label = data[0].split(":")[1]
except IndexError as e:
print(f"[{time.ctime()}] Invalid data received: {data} | Indexes misaligned: {repr(e)}")
sys.stdout.flush()
logger(f"Invalid data received: {data} | Indexes misaligned: {repr(e)}")
return False
elif action == "retrieveall":
mid = data[1].split(":")[1]
mp = data[2].split(":")[1]
username = data[3].split(":")[1]

if data[1].split(":")[0] != "mid" or data[2].split(":")[0] != "mp" or data[3].split(":")[0] != "username":
print(f"[{time.ctime()}] Invalid data received: {data} | One or more elements are not in the correct format.")
sys.stdout.flush()
logger(f"Invalid data received: {data} | One or more elements are not in the correct format.")
return False

elif mid == "" or mp == "" or username == "":
print(f"[{time.ctime()}] Invalid data received: {data} | One or more elements are empty.")
sys.stdout.flush()
logger(f"Invalid data received: {data} | One or more elements are empty.")
return False

elif action in valid_hashes or action in valid_encryption_decryption_algorithms or action in valid_secret_generators:
print(f"[{time.ctime()}] Valid data received: {data}")
sys.stdout.flush()
logger(f"Valid data received: {data}")
return True

elif action == "store" and secret_label != "" and secret_value != "":
print(f"[{time.ctime()}] Valid data received: {data}")
sys.stdout.flush()
logger(f"Valid data received: {data}")
return True
elif action == "retrieve" and secret_label != "":
print(f"[{time.ctime()}] Valid data received: {data}")
sys.stdout.flush()
logger(f"Valid data received: {data}")
return True
elif action == "retrieveall":
print(f"[{time.ctime()}] Valid data received: {data}")
sys.stdout.flush()
logger(f"Valid data received: {data}")
return True
return False


def protocol_handler(data, shared_key):
data = data[10:]
backup_data = data.split("/") if "/" in data else data
data = data.split(":")
data[0] = data[0].lower()
print(f"[{time.ctime()}] Protocol handler received: {data}")
sys.stdout.flush()
if data[0] in valid_hashes:
h = hashlib.new(data[0])
h.update(data[1].encode())
Expand All @@ -171,8 +231,10 @@ def protocol_handler(data, shared_key):
return data
elif data[0] == "rsa":
print(f"[{time.ctime()}] Generating RSA key pair, this may take a while...")
(pub, priv) = rsa.newkeys(2048) # Poolsize omitted, daemonic process not allowed. THIS IS A FUTURE TODO!
sys.stdout.flush()
(pub, priv) = rsa.newkeys(2048) # Poolsize omitted, daemonic process not allowed. THIS IS A FUTURE TODO!
print(f"[{time.ctime()}] RSA key pair generated.")
sys.stdout.flush()
pub = base64.b64encode(pub.save_pkcs1()).decode()
priv = base64.b64encode(priv.save_pkcs1()).decode()
data = "polaris://" + data[0] + ":[pub]" + pub + ":[priv]" + priv
Expand Down Expand Up @@ -215,16 +277,111 @@ def protocol_handler(data, shared_key):
return data

elif data[0] == "store":
print(f"[{time.ctime()}] Storing secret: {backup_data}")
secret_value = backup_data[0].split(":")[2]
secret_label = backup_data[0].split(":")[1]
# Encrypt the secret value using master password and store it in the database
global salt
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt.encode(),
iterations=100000,
backend=default_backend()
)
mid = backup_data[1].split(":")[1]
mp = backup_data[2].split(":")[1]
key = kdf.derive(mp.encode())
encoded_key = base64.urlsafe_b64encode(key)
cipher = Fernet(encoded_key)
username = backup_data[3].split(":")[1]
logger("Store command received.")
# SQL query to check if the provided information is correct
conn = sqlite3.connect("/etc/polaris/polaris.db")
c = conn.cursor()
salt = open("/etc/polaris/salt", "r").read()
mp_hash = hashlib.sha256((mp + salt).encode()).hexdigest()
secret_value = base64.b64encode(cipher.encrypt(secret_value.encode())).decode()
print(f"[{time.ctime()}] Checking if the combination of: {username}, {mp_hash}, {mid} exists.")
users_combo = c.execute("SELECT * FROM users WHERE username = ? AND password = ? AND mid = ?", (username, mp_hash, mid)).fetchall()
if len(users_combo) == 0:
print(f"[{time.ctime()}] Invalid combination of username, password and machine id.")
logger("Invalid combination of username, password and machine id.")
return "polaris://store:failure"
check_secret_exists = c.execute("SELECT * FROM secrets WHERE uid = ? AND label = ?", (users_combo[0][0], data[1])).fetchall()
if len(check_secret_exists) > 0:
print(f"[{time.ctime()}] Secret with label {data[1]} already exists.")
logger(f"Secret with label {data[1]} already exists.")
return "polaris://store:failure"
else:
c.execute("INSERT INTO secrets (uid, label, secret) VALUES (?, ?, ?)", (users_combo[0][0], secret_label, secret_value))
conn.commit()
conn.close()
return "polaris://store:success"
elif data[0] == "retrieve":
logger("Retrieve command received.")
print(f"[{time.ctime()}] Retrieving secret with label: {data}")
mid = backup_data[1].split(":")[1]
mp = backup_data[2].split(":")[1]
username = backup_data[3].split(":")[1]
conn = sqlite3.connect("/etc/polaris/polaris.db")
c = conn.cursor()
salt = open("/etc/polaris/salt", "r").read()
mp_hash = hashlib.sha256((mp + salt).encode()).hexdigest()
users_combo = c.execute("SELECT * FROM users WHERE username = ? AND password = ? AND mid = ?", (username, mp_hash, mid)).fetchall()
if len(users_combo) == 0:
print(f"[{time.ctime()}] Invalid combination of username, password and machine id.")
logger("Invalid combination of username, password and machine id.")
return "polaris://retrieve:failure"
secret = c.execute("SELECT secret FROM secrets WHERE uid = ? AND label = ?", (users_combo[0][0], backup_data[0].split(":")[1])).fetchall()
conn.close()
if len(secret) == 0:
return "polaris://retrieve:failure"
else:
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt.encode(),
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(mp.encode()))
cipher = Fernet(key)
secret_value = cipher.decrypt(base64.b64decode(secret[0][0])).decode()
return "polaris://retrieve:" + secret_value
elif backup_data[0] == "retrieveall":
logger("Retrieve all command received.")
print(f"[{time.ctime()}] Retrieving all secrets.")
mid = backup_data[1].split(":")[1]
mp = backup_data[2].split(":")[1]
username = backup_data[3].split(":")[1]
conn = sqlite3.connect("/etc/polaris/polaris.db")
c = conn.cursor()
salt = open("/etc/polaris/salt", "r").read()
mp_hash = hashlib.sha256((mp + salt).encode()).hexdigest()
users_combo = c.execute("SELECT * FROM users WHERE username = ? AND password = ? AND mid = ?", (username, mp_hash, mid)).fetchall()
if len(users_combo) == 0:
print(f"[{time.ctime()}] Invalid combination of username, password and machine id.")
logger("Invalid combination of username, password and machine id.")
return "polaris://retrieveall:failure"
secrets = c.execute("SELECT label FROM secrets WHERE uid = ?", (users_combo[0][0],)).fetchall()
conn.close()
to_return = "polaris://"
if len(secrets) == 0:
return to_return
else:
# Each secret label in the list must be separated by a slash
for secret in secrets:
to_return = to_return + secret[0] + "/"
return to_return


def handle_client(client_socket):
shared_key = key_exchange(client_socket)
encrypted_data = client_socket.recv(8192)
if len(encrypted_data) == 0:
print(f"[{time.ctime()}] Connection closed by {client_socket.getpeername()}")
sys.stdout.flush()
logger(f"Connection closed by {client_socket.getpeername()}")
client_socket.close()
return
Expand All @@ -241,6 +398,7 @@ def handle_client(client_socket):
if decrypted_data == "Hello, HSM":
print(f"[{time.ctime()}] This looks like a handshake.")
logger(f"This looks like a handshake.")
sys.stdout.flush()
encrypted_data = encrypt_data(shared_key, "Hello, Client")
client_socket.send(encrypted_data)
print(f"[{time.ctime()}] Sent encrypted data: Hello, Client")
Expand All @@ -252,10 +410,11 @@ def handle_client(client_socket):
elif decrypted_data.startswith("polaris://mid:") and len(
decrypted_data.replace("polaris://", "").split("/")) == 3:
print(f"[{time.ctime()}] Client has initiated setup.")
sys.stdout.flush()
logger(f"Client has initiated setup.")
mid = decrypted_data.replace("polaris://mid:", "").split("/")[0]
mp = decrypted_data.replace("polaris://mid:", "").split("/")[1]
username = decrypted_data.replace("polaris://mid:", "").split("/")[2]
mp = decrypted_data.replace("polaris://mid:", "").split("/")[1].split(":")[1]
username = decrypted_data.replace("polaris://mid:", "").split("/")[2].split(":")[1]
print(f"[{time.ctime()}] Received: {mid}, {mp}, {username}")
logger(f"Received: {mid}, {mp}, {username} from {client_socket.getpeername()}")
sys.stdout.flush() # Journalctl Prints
Expand All @@ -265,8 +424,10 @@ def handle_client(client_socket):
conn = sqlite3.connect("/etc/polaris/polaris.db")
c = conn.cursor()
salt = open("/etc/polaris/salt", "r").read()
users_combo = c.execute("SELECT * FROM users WHERE username = ? AND password = ? AND mid = ?", (username, hashlib.sha256((mp + salt).encode()).hexdigest(), mid)).fetchall()
machines_combo = c.execute("SELECT * FROM machines WHERE mid = ? AND ip = ?", (mid, client_socket.getpeername()[0])).fetchall()
users_combo = c.execute("SELECT * FROM users WHERE username = ? AND password = ? AND mid = ?",
(username, hashlib.sha256((mp + salt).encode()).hexdigest(), mid)).fetchall()
machines_combo = c.execute("SELECT * FROM machines WHERE mid = ? AND ip = ?",
(mid, client_socket.getpeername()[0])).fetchall()
if len(users_combo) > 0 or len(machines_combo) > 0:
encrypted_data = encrypt_data(shared_key, "polaris://mid:failure")
client_socket.send(encrypted_data)
Expand Down Expand Up @@ -295,10 +456,12 @@ def handle_client(client_socket):

elif protocol(decrypted_data):
print(f"[{time.ctime()}] Protocol validation successful: {decrypted_data}")
sys.stdout.flush()
logger(f"Protocol validation successful: {decrypted_data}")
data_to_return = protocol_handler(decrypted_data, shared_key)
encrypted_data = encrypt_data(shared_key, data_to_return)
print(f"[{time.ctime()}] Sending encrypted data: {encrypted_data}")
sys.stdout.flush()
client_socket.send(encrypted_data)
print(f"[{time.ctime()}] Sent encrypted data: {data_to_return}")
logger(f"Sent encrypted data: {data_to_return} to {client_socket.getpeername()}")
Expand All @@ -307,6 +470,9 @@ def handle_client(client_socket):

else:
print(f"[{time.ctime()}] Invalid data received: {decrypted_data}")
sys.stdout.flush()
print(f"Protocol Status: {protocol(decrypted_data)}")
sys.stdout.flush()
logger(f"Invalid data received: {decrypted_data}")
encrypted_data = encrypt_data(shared_key, "polaris://error")
client_socket.send(encrypted_data)
Expand Down Expand Up @@ -347,6 +513,7 @@ def handle_statistics_client(client_socket):
def start_server():
try:
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Reuse the address after the server is stopped
server.bind(('0.0.0.0', 26555))
server.listen(5)

Expand All @@ -364,7 +531,7 @@ def start_server():
print(f"[{time.ctime()}] Server stopped by the user.")
sys.stdout.flush()
logger(f"Server stopped by the user.")
server.close()
server.close() if server else None
sys.exit(0)
except Exception as e:
print(f"[{time.ctime()}] An error occurred: {e}")
Expand Down
14 changes: 7 additions & 7 deletions modules/ui_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1518,13 +1518,13 @@ def setupUi(self, MainWindow):
self.hsmnamelabel_6.setObjectName(u"hsmnamelabel_6")
self.hsmnamelabel_6.setGeometry(QRect(600, 50, 181, 21))
self.hsmnamelabel_6.setStyleSheet(u"font: 600 10pt \"Inter Medium\";")
self.send_pte_2 = QPlainTextEdit(self.secrets)
self.send_pte_2.setObjectName(u"send_pte_2")
self.send_pte_2.setGeometry(QRect(40, 200, 361, 201))
self.send_pte_2.setMinimumSize(QSize(200, 90))
self.send_pte_2.setStyleSheet(u"background-color: rgb(33, 37, 43);\n"
self.secret_value = QPlainTextEdit(self.secrets)
self.secret_value.setObjectName(u"secret_value")
self.secret_value.setGeometry(QRect(40, 200, 361, 201))
self.secret_value.setMinimumSize(QSize(200, 90))
self.secret_value.setStyleSheet(u"background-color: rgb(33, 37, 43);\n"
"font: 600 10pt \"Inter Medium\";")
self.send_pte_2.setReadOnly(False)
self.secret_value.setReadOnly(False)
self.btn_storesecret = QPushButton(self.secrets)
self.btn_storesecret.setObjectName(u"btn_storesecret")
self.btn_storesecret.setGeometry(QRect(130, 130, 186, 30))
Expand Down Expand Up @@ -1945,7 +1945,7 @@ def retranslateUi(self, MainWindow):
self.secret_response.setPlaceholderText(QCoreApplication.translate("MainWindow", u"The server will respond here...", None))
self.btn_retrievesecret.setText(QCoreApplication.translate("MainWindow", u" Send", None))
self.hsmnamelabel_6.setText(QCoreApplication.translate("MainWindow", u"Secrets", None))
self.send_pte_2.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Enter your secret here...", None))
self.secret_value.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Enter your secret here...", None))
self.btn_storesecret.setText(QCoreApplication.translate("MainWindow", u" Send", None))
self.secret_label.setText("")
self.secret_label.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Give your secret a label...", None))
Expand Down
Loading

0 comments on commit 74f5d5f

Please sign in to comment.