From 2d4cca0e80a6fdcf32d723057b28ad573dbcb9ff Mon Sep 17 00:00:00 2001 From: andrei-mihnea-cerbu Date: Fri, 19 Jan 2024 04:05:05 +0200 Subject: [PATCH] encryptare + adaugare de imagini --- .../v1/endpoints/encryption_key/__init__.py | 18 ++++ .../encryption_key/get_private_key.py | 6 ++ .../app/__pycache__/main.cpython-310.pyc | Bin 519 -> 634 bytes messenger_app/app/main.py | 2 + messenger_gui/app/encryption/AESEncryptor.py | 30 ++++++ messenger_gui/app/encryption/__init__.py | 1 + messenger_gui/app/http/request_builder.py | 1 - messenger_gui/app/main_application.py | 27 ++++++ messenger_gui/app/views/chat.py | 34 ++++++- messenger_gui/app/views/main_page.py | 10 +- messenger_gui/app/views/message.py | 86 ++++++++++-------- 11 files changed, 167 insertions(+), 48 deletions(-) create mode 100644 messenger_app/api/v1/endpoints/encryption_key/__init__.py create mode 100644 messenger_app/api/v1/endpoints/encryption_key/get_private_key.py create mode 100644 messenger_gui/app/encryption/AESEncryptor.py create mode 100644 messenger_gui/app/encryption/__init__.py diff --git a/messenger_app/api/v1/endpoints/encryption_key/__init__.py b/messenger_app/api/v1/endpoints/encryption_key/__init__.py new file mode 100644 index 0000000..48109bd --- /dev/null +++ b/messenger_app/api/v1/endpoints/encryption_key/__init__.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter, Header, HTTPException +from .get_private_key import get_private_key + +''' +The 'messages' packages contains the router configuration for the 'Messages' endpoints. +''' + +router = APIRouter() + + +@router.get("/get_private_key") +async def get_private_key_router(authorization_key: str = Header('Authorization_Key', convert_underscores=False)): + expected_value = 'messenger' + if not authorization_key == expected_value: + raise HTTPException(status_code=401, detail="Unauthorized: Incorrect header value") + return get_private_key() + +__all__ = ["router"] diff --git a/messenger_app/api/v1/endpoints/encryption_key/get_private_key.py b/messenger_app/api/v1/endpoints/encryption_key/get_private_key.py new file mode 100644 index 0000000..ef5af0d --- /dev/null +++ b/messenger_app/api/v1/endpoints/encryption_key/get_private_key.py @@ -0,0 +1,6 @@ + +def get_private_key(): + # Your implementation to retrieve the private key goes here + # For testing purposes, return a dummy private key + return {"private_key": '8f0240aeb3fe8e16'} + diff --git a/messenger_app/app/__pycache__/main.cpython-310.pyc b/messenger_app/app/__pycache__/main.cpython-310.pyc index 5d74ec0bbb11e825c1d6af1065942e48a01d2555..b6b0edc7596a706ee6e3b991ff5604576460b90f 100644 GIT binary patch delta 248 zcmZo?`NhJQ&&$ij00h%^uT1r2n#d=^*f&wzI3a~0g(-(KmpO`yks+0_nIVchl{=T&j*r@}dYK>2@N2J(ponR;c0dZ~FS1^JnICB;A%OnrQIYUM3aRQ}`! kMtL?-pkInaCog66lMn$ic^G&YA&`wph)IZ1h*69Y0HD1;&Hw-a delta 141 zcmeyx($2z{&&$ij00d$W7pAUZoX97`STIrB*eR80syZ@8&LoN diff --git a/messenger_app/app/main.py b/messenger_app/app/main.py index ec34906..dc1d55c 100644 --- a/messenger_app/app/main.py +++ b/messenger_app/app/main.py @@ -2,9 +2,11 @@ from fastapi import FastAPI from api.v1.endpoints.users import router as users_router from api.v1.endpoints.messages import router as messages_router from api.v1.endpoints.auth import router as auth_router +from api.v1.endpoints.encryption_key import router as encryption_key_router app = FastAPI() app.include_router(users_router, tags=["Users"], prefix="/users") app.include_router(messages_router, tags=["Messages"], prefix="/messages") app.include_router(auth_router, tags=["Auth"], prefix="/auth") +app.include_router(encryption_key_router, tags=["Encryption"], prefix="/encryption") diff --git a/messenger_gui/app/encryption/AESEncryptor.py b/messenger_gui/app/encryption/AESEncryptor.py new file mode 100644 index 0000000..96db79a --- /dev/null +++ b/messenger_gui/app/encryption/AESEncryptor.py @@ -0,0 +1,30 @@ +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives import padding + + +class AESEncryptor: + def __init__(self, key): + if not isinstance(key, bytes): + key = key.encode('utf-8') # Assuming UTF-8 encoding, adjust if needed + if len(key) != 16: + raise ValueError("AES key must be 16 bytes in length") + self.key = key + + def encrypt(self, plaintext): + cipher = Cipher(algorithms.AES(self.key), modes.ECB(), backend=default_backend()) + encryptor = cipher.encryptor() + plaintext = plaintext.encode('utf-8') + padder = padding.PKCS7(algorithms.AES.block_size).padder() + padded_plaintext = padder.update(plaintext) + padder.finalize() + ciphertext = encryptor.update(padded_plaintext) + encryptor.finalize() + return ciphertext.hex() + + def decrypt(self, ciphertext): + cipher = Cipher(algorithms.AES(self.key), modes.ECB(), backend=default_backend()) + decryptor = cipher.decryptor() + ciphertext = bytes.fromhex(ciphertext) + decrypted_data = decryptor.update(ciphertext) + decryptor.finalize() + unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() + unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() + return unpadded_data.decode('utf-8') diff --git a/messenger_gui/app/encryption/__init__.py b/messenger_gui/app/encryption/__init__.py new file mode 100644 index 0000000..8352254 --- /dev/null +++ b/messenger_gui/app/encryption/__init__.py @@ -0,0 +1 @@ +from .AESEncryptor import AESEncryptor diff --git a/messenger_gui/app/http/request_builder.py b/messenger_gui/app/http/request_builder.py index 5d7dbfa..397e107 100644 --- a/messenger_gui/app/http/request_builder.py +++ b/messenger_gui/app/http/request_builder.py @@ -13,7 +13,6 @@ class RequestBuilder: __method (str): HTTP method (GET, POST, ...) """ - def __init__(self): self.__url = "" self.__headers = {} diff --git a/messenger_gui/app/main_application.py b/messenger_gui/app/main_application.py index b0b66d4..f76e2b8 100644 --- a/messenger_gui/app/main_application.py +++ b/messenger_gui/app/main_application.py @@ -1,9 +1,13 @@ import json import tkinter as tk + +from app.http import request_builder from app.views.login import LoginView from app.views.initial import InitialView from app.views.signup import SignupView from app.views.main_page import MainPageView +from app.encryption import AESEncryptor +import json class MainApplication(tk.Tk): @@ -20,9 +24,21 @@ class MainApplication(tk.Tk): self.current_logged_user = {} + self.define_http_params() + self.encryptor = self.initialize_encryptor() + self.current_view = None self.change_to_initial_view() + def define_http_params(self): + self.url = "http://localhost:8000/encryption/get_private_key" + self.headers = { + "Content-Type": "application/json", + "Authorization_Key": "messenger" + } + self.method = "GET" + self.params = None + def change_to_initial_view(self): self.change_view(InitialView(self)) @@ -51,3 +67,14 @@ class MainApplication(tk.Tk): self.current_logged_user['id'] = data['info']['id'] self.current_logged_user['username'] = data['info']['username'] self.current_logged_user['email'] = data['info']['email'] + + def initialize_encryptor(self): + request_builder.set_url(self.url) + request_builder.set_headers(self.headers) + request_builder.set_method(self.method) + + result = request_builder.build().make_request() + json_data = json.loads(result.text) + private_key = json_data.get("private_key", None) + + return AESEncryptor(private_key) diff --git a/messenger_gui/app/views/chat.py b/messenger_gui/app/views/chat.py index a8a9b4b..a0464e4 100644 --- a/messenger_gui/app/views/chat.py +++ b/messenger_gui/app/views/chat.py @@ -1,7 +1,10 @@ +import base64 import json +import os import tkinter as tk from app.http import request_builder from app.views.message import MessageView +import tkinter.filedialog class ChatWindow(tk.Toplevel): @@ -9,13 +12,16 @@ class ChatWindow(tk.Toplevel): This view is opened in a separate window and takes care of the communication between the two users. It receives both the users' information and retrieves the conversation_url. """ - def __init__(self, master=None, user_data=None, current_logged_user=None): + + def __init__(self, master=None, encryptor=None, user_data=None, current_logged_user=None): super().__init__(master) self.user_data = user_data self.current_logged_user = current_logged_user self.define_get_conversation_url_params() + self.encryptor = encryptor + self.geometry("700x700") self.conversation_url = self.get_conversation_url() @@ -46,7 +52,7 @@ class ChatWindow(tk.Toplevel): self.params = {} self.body = { 'conversation_url': self.conversation_url, - 'message': message, + 'message': self.encryptor.encrypt(message), 'message_type': message_type, 'sender': self.current_logged_user['id'] } @@ -59,6 +65,7 @@ class ChatWindow(tk.Toplevel): # Display existing conversation for message in conversation: + message['message'] = self.encryptor.decrypt(message['message']) message_view = MessageView(self.messages_frame, message, self.current_logged_user, self.user_data) message_view.pack() @@ -74,6 +81,28 @@ class ChatWindow(tk.Toplevel): self.send_button = tk.Button(self.input_frame, text="Send", command=lambda: self.send_message()) self.send_button.pack(side=tk.RIGHT) + self.select_photo_button = tk.Button(self.input_frame, text="Select Photo", command=self.select_photo) + self.select_photo_button.pack(side=tk.LEFT) + + def select_photo(self): + initial_dir = os.path.expanduser("~") + file_path = tkinter.filedialog.askopenfilename( + initialdir=initial_dir, filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif;*.bmp")]) + with open(file_path, 'rb') as file: + file_content = file.read() + image_base64 = base64.b64encode(file_content).decode('utf-8') + self.define_send_message_params(image_base64, "image") + + request_builder.set_url(self.url) + request_builder.set_body(self.body) + request_builder.set_headers(self.headers) + request_builder.set_method(self.method) + request_builder.set_params(self.params) + + request_builder.build().make_request() + + self.update_chat() + def update_chat(self): """ This function is scheduled each second to make a request to the API to retrieve the latest chat between the @@ -85,6 +114,7 @@ class ChatWindow(tk.Toplevel): self.define_get_conversation_params() for message in self.get_conversation(): + message['message'] = self.encryptor.decrypt(message['message']) message_view = MessageView(self.messages_frame, message, self.current_logged_user, self.user_data) message_view.pack() diff --git a/messenger_gui/app/views/main_page.py b/messenger_gui/app/views/main_page.py index 0bac8f0..695f93e 100644 --- a/messenger_gui/app/views/main_page.py +++ b/messenger_gui/app/views/main_page.py @@ -26,11 +26,11 @@ class MainPageView(tk.Frame): def create_widgets(self): """Takes care of the creation of the UI""" # Create a main frame to center the user frame and button frame - main_frame = tk.Frame(self.master) - main_frame.pack(expand=True) + self.main_frame = tk.Frame(self) + self.main_frame.pack(expand=True) # Create a frame to hold the user frames - user_frame_container = tk.Frame(main_frame) + user_frame_container = tk.Frame(self.main_frame) user_frame_container.pack(side=tk.TOP, fill=tk.BOTH, expand=True) users_data = self.get_user_list() @@ -49,7 +49,7 @@ class MainPageView(tk.Frame): user_frame_instance.grid(row=row, column=column, padx=10, pady=10) # Create a frame to hold the buttons - button_frame = tk.Frame(main_frame) + button_frame = tk.Frame(self.main_frame) button_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True) # Create buttons in the button frame @@ -64,7 +64,7 @@ class MainPageView(tk.Frame): def open_chat(self, user_data): """Opens a chat in a different window""" # Pass both the clicked user data and the current logged user info to ChatWindow - chat_window = ChatWindow(self, user_data, self.master.current_logged_user) + chat_window = ChatWindow(self, self.master.encryptor, user_data, self.master.current_logged_user) chat_window.mainloop() # Main loop for the new window def get_user_list(self): diff --git a/messenger_gui/app/views/message.py b/messenger_gui/app/views/message.py index 874611a..8ea370b 100644 --- a/messenger_gui/app/views/message.py +++ b/messenger_gui/app/views/message.py @@ -1,15 +1,10 @@ import tkinter as tk - -import emoji -from PIL import Image, ImageTk +from PIL import Image, ImageTk, UnidentifiedImageError import io import base64 - +import emoji class MessageView(tk.Frame): - """ - This view is the message that appears in main windows of the chat. This is configured to hold text and images. - """ def __init__(self, master, message, current_logged_user, user_data, *args, **kwargs): super().__init__(master, *args, **kwargs) self.current_logged_user = current_logged_user @@ -18,49 +13,49 @@ class MessageView(tk.Frame): def create_widgets(self, message): is_sent_by_current_user = 1 - sender_name = None - if is_sent_by_current_user: - sender_name = self.current_logged_user['username'] - else: - sender_name = self.user_data['username'] - - message_frame = tk.Frame(self) # Set a background color for the message frame - message_label = None - image_label = None + sender_name = ( + self.current_logged_user["username"] + if is_sent_by_current_user + else self.user_data["username"] + ) + message_frame = tk.Frame(self) pack = None - match message['message_type']: - case 'text': - decoded_message = self.decode_emoji(f"sender: {sender_name}\nmessage: {message['message']}") - pack = tk.Label(message_frame, text=decoded_message, bg='white') - case 'image': - decoded_image = self.decode_image(message['message']) + match message["message_type"]: + case "text": + decoded_message = self.decode_emoji( + f"sender: {sender_name}\nmessage: {message['message']}" + ) + pack = tk.Label(message_frame, text=decoded_message, bg="white") + self.decoded_message = decoded_message # Keep a reference to prevent garbage collection + case "image": + decoded_image = self.decode_image(message["message"]) pack = tk.Label(message_frame, image=decoded_image) pack.image = decoded_image # Keep a reference to prevent garbage collection pack.pack() if is_sent_by_current_user: - alignment = 'right' + alignment = "right" else: - alignment = 'left' + alignment = "left" message_frame.pack(side=alignment, padx=5, pady=5) def decode_emoji(self, message): emoji_mapping = { - ':)': '😊', - ':(': 'â˜šī¸', - ':D': '😃', - ':P': '😛', - ';)': '😉', - 'XD': '😆', - '<3': 'â¤ī¸', - 'O:)': '😇', - ':/': '😕', - ':*': '😘', - '8)': '😎', + ":)": "😊", + ":(": "â˜šī¸", + ":D": "😃", + ":P": "😛", + ";)": "😉", + "XD": "😆", + "<3": "â¤ī¸", + "O:)": "😇", + ":/": "😕", + ":*": "😘", + "8)": "😎", } for emoji_string, unicode_character in emoji_mapping.items(): @@ -68,8 +63,19 @@ class MessageView(tk.Frame): return message def decode_image(self, encoded_image): - # Assume encoded_image is a base64-encoded image - decoded_image = base64.b64decode(encoded_image) - image = Image.open(io.BytesIO(decoded_image)) - image = ImageTk.PhotoImage(image) - return image \ No newline at end of file + try: + decoded_image = base64.b64decode(encoded_image) + image_io = io.BytesIO(decoded_image) + + original_image = Image.open(image_io) + resized_image = original_image.resize((100, 100), Image.ADAPTIVE) + + self.image = resized_image + image = ImageTk.PhotoImage(self.image) + return image + except UnidentifiedImageError as e: + print(f"Error decoding image: {e}") + return None + except Exception as e: + print(f"Unexpected error: {e}") + return None