encryptare + adaugare de imagini

This commit is contained in:
andrei-mihnea-cerbu
2024-01-19 04:05:05 +02:00
parent 67b7a241d7
commit 2d4cca0e80
11 changed files with 167 additions and 48 deletions
@@ -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"]
@@ -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'}
Binary file not shown.
+2
View File
@@ -2,9 +2,11 @@ from fastapi import FastAPI
from api.v1.endpoints.users import router as users_router from api.v1.endpoints.users import router as users_router
from api.v1.endpoints.messages import router as messages_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.auth import router as auth_router
from api.v1.endpoints.encryption_key import router as encryption_key_router
app = FastAPI() app = FastAPI()
app.include_router(users_router, tags=["Users"], prefix="/users") app.include_router(users_router, tags=["Users"], prefix="/users")
app.include_router(messages_router, tags=["Messages"], prefix="/messages") app.include_router(messages_router, tags=["Messages"], prefix="/messages")
app.include_router(auth_router, tags=["Auth"], prefix="/auth") app.include_router(auth_router, tags=["Auth"], prefix="/auth")
app.include_router(encryption_key_router, tags=["Encryption"], prefix="/encryption")
@@ -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')
+1
View File
@@ -0,0 +1 @@
from .AESEncryptor import AESEncryptor
@@ -13,7 +13,6 @@ class RequestBuilder:
__method (str): HTTP method (GET, POST, ...) __method (str): HTTP method (GET, POST, ...)
""" """
def __init__(self): def __init__(self):
self.__url = "" self.__url = ""
self.__headers = {} self.__headers = {}
+27
View File
@@ -1,9 +1,13 @@
import json import json
import tkinter as tk import tkinter as tk
from app.http import request_builder
from app.views.login import LoginView from app.views.login import LoginView
from app.views.initial import InitialView from app.views.initial import InitialView
from app.views.signup import SignupView from app.views.signup import SignupView
from app.views.main_page import MainPageView from app.views.main_page import MainPageView
from app.encryption import AESEncryptor
import json
class MainApplication(tk.Tk): class MainApplication(tk.Tk):
@@ -20,9 +24,21 @@ class MainApplication(tk.Tk):
self.current_logged_user = {} self.current_logged_user = {}
self.define_http_params()
self.encryptor = self.initialize_encryptor()
self.current_view = None self.current_view = None
self.change_to_initial_view() 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): def change_to_initial_view(self):
self.change_view(InitialView(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['id'] = data['info']['id']
self.current_logged_user['username'] = data['info']['username'] self.current_logged_user['username'] = data['info']['username']
self.current_logged_user['email'] = data['info']['email'] 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)
+32 -2
View File
@@ -1,7 +1,10 @@
import base64
import json import json
import os
import tkinter as tk import tkinter as tk
from app.http import request_builder from app.http import request_builder
from app.views.message import MessageView from app.views.message import MessageView
import tkinter.filedialog
class ChatWindow(tk.Toplevel): 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 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. 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) super().__init__(master)
self.user_data = user_data self.user_data = user_data
self.current_logged_user = current_logged_user self.current_logged_user = current_logged_user
self.define_get_conversation_url_params() self.define_get_conversation_url_params()
self.encryptor = encryptor
self.geometry("700x700") self.geometry("700x700")
self.conversation_url = self.get_conversation_url() self.conversation_url = self.get_conversation_url()
@@ -46,7 +52,7 @@ class ChatWindow(tk.Toplevel):
self.params = {} self.params = {}
self.body = { self.body = {
'conversation_url': self.conversation_url, 'conversation_url': self.conversation_url,
'message': message, 'message': self.encryptor.encrypt(message),
'message_type': message_type, 'message_type': message_type,
'sender': self.current_logged_user['id'] 'sender': self.current_logged_user['id']
} }
@@ -59,6 +65,7 @@ class ChatWindow(tk.Toplevel):
# Display existing conversation # Display existing conversation
for message in 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 = MessageView(self.messages_frame, message, self.current_logged_user, self.user_data)
message_view.pack() 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 = tk.Button(self.input_frame, text="Send", command=lambda: self.send_message())
self.send_button.pack(side=tk.RIGHT) 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): def update_chat(self):
""" """
This function is scheduled each second to make a request to the API to retrieve the latest chat between the 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() self.define_get_conversation_params()
for message in self.get_conversation(): 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 = MessageView(self.messages_frame, message, self.current_logged_user, self.user_data)
message_view.pack() message_view.pack()
+5 -5
View File
@@ -26,11 +26,11 @@ class MainPageView(tk.Frame):
def create_widgets(self): def create_widgets(self):
"""Takes care of the creation of the UI""" """Takes care of the creation of the UI"""
# Create a main frame to center the user frame and button frame # Create a main frame to center the user frame and button frame
main_frame = tk.Frame(self.master) self.main_frame = tk.Frame(self)
main_frame.pack(expand=True) self.main_frame.pack(expand=True)
# Create a frame to hold the user frames # 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) user_frame_container.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
users_data = self.get_user_list() 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) user_frame_instance.grid(row=row, column=column, padx=10, pady=10)
# Create a frame to hold the buttons # 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) button_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
# Create buttons in the button frame # Create buttons in the button frame
@@ -64,7 +64,7 @@ class MainPageView(tk.Frame):
def open_chat(self, user_data): def open_chat(self, user_data):
"""Opens a chat in a different window""" """Opens a chat in a different window"""
# Pass both the clicked user data and the current logged user info to ChatWindow # 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 chat_window.mainloop() # Main loop for the new window
def get_user_list(self): def get_user_list(self):
+44 -38
View File
@@ -1,15 +1,10 @@
import tkinter as tk import tkinter as tk
from PIL import Image, ImageTk, UnidentifiedImageError
import emoji
from PIL import Image, ImageTk
import io import io
import base64 import base64
import emoji
class MessageView(tk.Frame): 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): def __init__(self, master, message, current_logged_user, user_data, *args, **kwargs):
super().__init__(master, *args, **kwargs) super().__init__(master, *args, **kwargs)
self.current_logged_user = current_logged_user self.current_logged_user = current_logged_user
@@ -18,49 +13,49 @@ class MessageView(tk.Frame):
def create_widgets(self, message): def create_widgets(self, message):
is_sent_by_current_user = 1 is_sent_by_current_user = 1
sender_name = None sender_name = (
if is_sent_by_current_user: self.current_logged_user["username"]
sender_name = self.current_logged_user['username'] if is_sent_by_current_user
else: else self.user_data["username"]
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
message_frame = tk.Frame(self)
pack = None pack = None
match message['message_type']: match message["message_type"]:
case 'text': case "text":
decoded_message = self.decode_emoji(f"sender: {sender_name}\nmessage: {message['message']}") decoded_message = self.decode_emoji(
pack = tk.Label(message_frame, text=decoded_message, bg='white') f"sender: {sender_name}\nmessage: {message['message']}"
case 'image': )
decoded_image = self.decode_image(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 = tk.Label(message_frame, image=decoded_image)
pack.image = decoded_image # Keep a reference to prevent garbage collection pack.image = decoded_image # Keep a reference to prevent garbage collection
pack.pack() pack.pack()
if is_sent_by_current_user: if is_sent_by_current_user:
alignment = 'right' alignment = "right"
else: else:
alignment = 'left' alignment = "left"
message_frame.pack(side=alignment, padx=5, pady=5) message_frame.pack(side=alignment, padx=5, pady=5)
def decode_emoji(self, message): def decode_emoji(self, message):
emoji_mapping = { emoji_mapping = {
':)': '😊', ":)": "😊",
':(': '☹️', ":(": "☹️",
':D': '😃', ":D": "😃",
':P': '😛', ":P": "😛",
';)': '😉', ";)": "😉",
'XD': '😆', "XD": "😆",
'<3': '❤️', "<3": "❤️",
'O:)': '😇', "O:)": "😇",
':/': '😕', ":/": "😕",
':*': '😘', ":*": "😘",
'8)': '😎', "8)": "😎",
} }
for emoji_string, unicode_character in emoji_mapping.items(): for emoji_string, unicode_character in emoji_mapping.items():
@@ -68,8 +63,19 @@ class MessageView(tk.Frame):
return message return message
def decode_image(self, encoded_image): def decode_image(self, encoded_image):
# Assume encoded_image is a base64-encoded image try:
decoded_image = base64.b64decode(encoded_image) decoded_image = base64.b64decode(encoded_image)
image = Image.open(io.BytesIO(decoded_image)) image_io = io.BytesIO(decoded_image)
image = ImageTk.PhotoImage(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 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