From 20d3f52b5e12ef7206ccdfa2f0d48f9752d38db4 Mon Sep 17 00:00:00 2001 From: p7mj Date: Thu, 9 Apr 2026 07:40:39 -0400 Subject: [PATCH] Bug fixes and login additions --- CHATTER/server.py | 79 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/CHATTER/server.py b/CHATTER/server.py index bbff6c2..2362b1a 100644 --- a/CHATTER/server.py +++ b/CHATTER/server.py @@ -1,17 +1,60 @@ import time from pathlib import Path +import hashlib +import os # Setup paths base_path = Path(__file__).parent + req_path = base_path / "requests" user_path = base_path / "users" chat_path = base_path / "single_chats" +status_path = base_path / "requests" / "status" req_path.mkdir(exist_ok=True) print("Server online. Monitoring requests...") +def hash_password(password: str) -> str: + # Generate a random 16-byte salt + salt = os.urandom(16) + + # Hash the password using scrypt + # n=16384 (CPU/Memory cost), r=8 (Block size), p=1 (Parallelization) + password_hash = hashlib.scrypt( + password.encode(), + salt=salt, + n=16384, + r=8, + p=1 + ) + + # Store the salt and hash together as hex strings so they can be saved in a file + return f"{salt.hex()}:{password_hash.hex()}" + +def verify_password(stored_full_hash: str, provided_password: str) -> bool: + try: + # Split the stored string back into the salt and the hash + salt_hex, original_hash_hex = stored_full_hash.split(":") + salt = bytes.fromhex(salt_hex) + original_hash = bytes.fromhex(original_hash_hex) + + # Hash the provided password using the same salt and parameters + new_hash = hashlib.scrypt( + provided_password.encode(), + salt=salt, + n=16384, + r=8, + p=1 + ) + + # Use hmac.compare_digest to prevent "timing attacks" + import hmac + return hmac.compare_digest(new_hash, original_hash) + except Exception: + return False + while True: # 1. Grab everything in the folder # .iterdir() is efficient for scanning @@ -39,27 +82,35 @@ while True: parts = content.split(":", 2) # FAILSAFE 3: Check if we have enough parts (Command:User:Pass) - if len(parts) < 3: - print(f"Incomplete data in {file_path.name}, Deleting.") - # TODO Make failed request file - file_path.unlink() + if len(parts) != 3: + print(f"Corrupted data in {file_path.name}, Deleting.") + file_path.rename(status_path / f"fail_{file_path.name}") continue - + command, user, data = parts + + # Validity check for one last time if not command.isalnum() or not user.isalnum() or not data.isalnum(): print(f"Invalid parameters in {file_path.name}, Deleting.") - # TODO Make failed request file + file_path.rename(status_path / f"fail_{file_path.name}") continue print(f"Processing {command} for {user}...") if command == "LOGIN": + username_path = user_path / f"{user}" # TODO - #1. check if user exists as a folder - #2. hash password and check against user hash.txt if exist - #3. Make success request file - #4. Fail file as try/except - pass - if command == "CREATEUSER": + if username_path.is_dir(): + with open(username_path / "pass.txt", "r") as f: + password = f.readline().strip() + + # Now password is the entire sequence + if verify_password(password, data): + file_path.rename(status_path / f"success_{file_path.name}") + # move and rename to success_filename.txt in status + else: + file_path.rename(status_path / f"fail_{file_path.name}") + + elif command == "CREATEUSER": # TODO #1. Make a new folder with the name #2. Hash password and store in hash.txt @@ -68,8 +119,8 @@ while True: #5. Fail file as try/except pass - # 4. Clean up: Delete the request after successful processing - file_path.unlink() + elif file_path.is_file(): + file_path.unlink() except Exception as e: # FAILSAFE 4: Catch-all for weird errors (like file being locked)