From 14cdefea69df8d2527f0a2d176af3f8e8b700534 Mon Sep 17 00:00:00 2001 From: Martin Asprusten Date: Sat, 19 Apr 2025 16:42:46 +0200 Subject: [PATCH] Working more on web server --- .gitignore | 8 +++ {classes/Crypto => WebServer}/__init__.py | 0 WebServer/server.py | 34 ++++++++++++ WebServer/static/exchange_client.py | 28 ++++++++++ WebServer/static/exchange_worker.js | 23 ++++++++ WebServer/static/style.css | 38 ++++++++++++++ WebServer/templates/exchange.html | 49 +++++++++++++++++ WebServer/templates/index.html | 52 +++++++++++++++++++ main.py | 13 +++-- requirements.txt | 5 +- setup-server.sh | 24 +++++++++ .../SantaExchange}/Crypto/CSPRNG.py | 0 .../Crypto/CommutativeCipher.py | 0 .../SantaExchange/Crypto}/__init__.py | 0 {classes => src/SantaExchange}/Message.py | 0 .../SantaExchange}/MessageHandler.py | 12 ++--- .../MessageTypes/Announcement.py | 2 +- .../MessageTypes/Introduction.py | 2 +- .../SantaExchange}/MessageTypes/Ready.py | 2 +- .../SantaExchange}/MessageTypes/Shuffle.py | 2 +- .../SantaExchange/MessageTypes}/__init__.py | 0 {classes => src/SantaExchange}/SantasBrain.py | 34 +++++++----- .../SantaExchange}/UserInterface.py | 0 src/SantaExchange/__init__.py | 0 src/setup.py | 8 +++ 25 files changed, 306 insertions(+), 30 deletions(-) rename {classes/Crypto => WebServer}/__init__.py (100%) create mode 100644 WebServer/server.py create mode 100644 WebServer/static/exchange_client.py create mode 100644 WebServer/static/exchange_worker.js create mode 100644 WebServer/static/style.css create mode 100644 WebServer/templates/exchange.html create mode 100644 WebServer/templates/index.html create mode 100755 setup-server.sh rename {classes => src/SantaExchange}/Crypto/CSPRNG.py (100%) rename {classes => src/SantaExchange}/Crypto/CommutativeCipher.py (100%) rename {classes/MessageTypes => src/SantaExchange/Crypto}/__init__.py (100%) rename {classes => src/SantaExchange}/Message.py (100%) rename {classes => src/SantaExchange}/MessageHandler.py (93%) rename {classes => src/SantaExchange}/MessageTypes/Announcement.py (95%) rename {classes => src/SantaExchange}/MessageTypes/Introduction.py (95%) rename {classes => src/SantaExchange}/MessageTypes/Ready.py (96%) rename {classes => src/SantaExchange}/MessageTypes/Shuffle.py (94%) rename {classes => src/SantaExchange/MessageTypes}/__init__.py (100%) rename {classes => src/SantaExchange}/SantasBrain.py (95%) rename {classes => src/SantaExchange}/UserInterface.py (100%) create mode 100644 src/SantaExchange/__init__.py create mode 100644 src/setup.py diff --git a/.gitignore b/.gitignore index d436ecc..67e0b84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,10 @@ .idea/* **/__pycache__/** +WebServer/static/pyodide* +WebServer/static/socket.* +WebServer/static/python_stdlib.zip +WebServer/static/**/*.whl +pyodide.tar.bz2 +src/build +src/dist +src/SantaExchange.egg-info/ diff --git a/classes/Crypto/__init__.py b/WebServer/__init__.py similarity index 100% rename from classes/Crypto/__init__.py rename to WebServer/__init__.py diff --git a/WebServer/server.py b/WebServer/server.py new file mode 100644 index 0000000..b9cbe47 --- /dev/null +++ b/WebServer/server.py @@ -0,0 +1,34 @@ +import json +from collections import defaultdict + +from flask import Flask, render_template +from flask_socketio import SocketIO, join_room, leave_room, send + +app = Flask(__name__) +socketio = SocketIO(app) + +if __name__ == 'main': + socketio.run(app) + +@app.route('/') +def return_index_page(): + return render_template('index.html') + +@app.route('/') +def return_exchange_page(exchange): + return render_template('exchange.html') + +@socketio.on('join') +def on_join(data): + room = data['room'] + join_room(room) + +@socketio.on('leave') +def on_leave(data): + room = data['room'] + leave_room(room) + +@socketio.on('message') +def on_message(data): + room = data['room'] + send(data, to=room) \ No newline at end of file diff --git a/WebServer/static/exchange_client.py b/WebServer/static/exchange_client.py new file mode 100644 index 0000000..8f6adbe --- /dev/null +++ b/WebServer/static/exchange_client.py @@ -0,0 +1,28 @@ +from Crypto.PublicKey.ECC import EccKey + +from src.SantaExchange.Message import Message +from src.SantaExchange.MessageHandler import MessageHandler +from src.SantaExchange.SantasBrain import Brain +from src.SantaExchange.UserInterface import UserInterface + + +class ExchangeClient(MessageHandler, UserInterface): + def __init__(self, send_message_function, receive_user_function, announce_recipient_function): + MessageHandler.__init__(self) + UserInterface.__init__(self) + + self.send_message_function = send_message_function + self.receive_user_function = receive_user_function + self.announce_recipient_function = announce_recipient_function + + self.brain = Brain(self, self) + + def send_message(self, message: Message, signing_key: EccKey): + message_string = message.generate_and_sign(signing_key) + self.send_message_function(message_string) + + def receive_user(self, name: str): + self.receive_user_function(name) + + def announce_recipient(self, name: str, other_info: str): + self.announce_recipient_function(name, other_info) \ No newline at end of file diff --git a/WebServer/static/exchange_worker.js b/WebServer/static/exchange_worker.js new file mode 100644 index 0000000..208bc2f --- /dev/null +++ b/WebServer/static/exchange_worker.js @@ -0,0 +1,23 @@ +import "./pyodide.js"; + +async function prepare() { + let pyodide = await loadPyodide(); + await pyodide.loadPackage("pycryptodome"); + await pyodide.loadPackage("./santaexchange-0.1-py3-none-any.whl") + pyodide.runPython(` + class Test: + def calculate(self): + return 15 + + a = Test() + `); + + console.log('Running python function'); + console.log(pyodide.globals.get('a').calculate()); +} +prepare(); + +addEventListener('message', e => { + self.postMessage('Loading pyodide'); + self.postMessage('Loaded pyodide'); +}); \ No newline at end of file diff --git a/WebServer/static/style.css b/WebServer/static/style.css new file mode 100644 index 0000000..bd42c2e --- /dev/null +++ b/WebServer/static/style.css @@ -0,0 +1,38 @@ +body { + background-color: #fff7f7; + color: #000000; + text-align: center; + font-family: 'Courier New'; + font-size: 20px; +} + +h1 { + color: #d46a64; + -webkit-text-stroke-width: 2px; + -webkit-text-stroke-color: var(--header-stroke-color); + font-size: 80px; + font-family: 'Helvetica'; + margin-bottom: 20px; +} + +.bodyDiv { + width: 80vw; + margin-left: auto; + margin-right: auto; +} + +button { + font-family: 'Courier New'; + font-size: 20px; + margin-top: 10px; + margin-bottom: 5px; +} + +input { + font-size: 20px; +} + +textarea { + width: 30vw; + height: 20vh; +} \ No newline at end of file diff --git a/WebServer/templates/exchange.html b/WebServer/templates/exchange.html new file mode 100644 index 0000000..9e40e07 --- /dev/null +++ b/WebServer/templates/exchange.html @@ -0,0 +1,49 @@ + + + + + Exchange + + + + +

Santa exchange

+

+
+
+
+ +


+

+ +

Current participants:

+
+ + + + + \ No newline at end of file diff --git a/WebServer/templates/index.html b/WebServer/templates/index.html new file mode 100644 index 0000000..7b2ccd3 --- /dev/null +++ b/WebServer/templates/index.html @@ -0,0 +1,52 @@ + + + + + DecentraSanta + + + + +
+

DecentraSanta

+

Welcome to the decentralised secret santa exchange. This website uses cryptography to distribute secret santas + in such a way that no one but you, not even the server the communication goes through, can know who you're + giving a + gift to.

+ +

You can either start a new secret santa exchange, or join an existing one.

+ +
+ +
+
+ +
+ +
+ + + + \ No newline at end of file diff --git a/main.py b/main.py index ba9a082..f8d09cc 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,9 @@ from Crypto.PublicKey.ECC import EccKey -from classes.Message import Message -from classes.MessageHandler import MessageHandler -from classes.SantasBrain import Brain -from classes.UserInterface import UserInterface +from src.SantaExchange.Message import Message +from src.SantaExchange.MessageHandler import MessageHandler +from src.SantaExchange.SantasBrain import Brain +from src.SantaExchange.UserInterface import UserInterface participants: list[tuple[MessageHandler, UserInterface]] = [] @@ -36,7 +36,7 @@ class TestUserInterface(UserInterface): def announce_recipient(self, name: str, other_info: str): print(f'{self.own_name}: Received {name}, {other_info}') -number_of_participants = 20 +number_of_participants = 3 for i in range(number_of_participants): test_message_handler = TestMessageHandler() @@ -48,5 +48,4 @@ for i in range(number_of_participants): for i in range(number_of_participants): handler, interface = participants[i] - interface.lets_go() - + interface.lets_go() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 75a967c..aab002f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,4 @@ -pycryptodome>=3.21.0 \ No newline at end of file +pycryptodome>=3.21.0 +flask>=3.1.0 +flask-socketio>=5.5.1 +setuptools>=78.1.0 \ No newline at end of file diff --git a/setup-server.sh b/setup-server.sh new file mode 100755 index 0000000..1f19837 --- /dev/null +++ b/setup-server.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# A lot of this stuff should probably be done with npm, but I'm not a big fan of Node +echo "Checking if pyodide is installed" +if ! test -f ./WebServer/static/pyodide.js; then + if ! test -f ./pyodide.tar.bz2; then + curl -L --output pyodide.tar.bz2 https://github.com/pyodide/pyodide/releases/download/0.27.5/pyodide-0.27.5.tar.bz2 + fi + tar -xvf pyodide.tar.bz2 -C ./WebServer/static --strip-components=1 pyodide/pyodide.asm.js pyodide/pyodide.asm.wasm pyodide/pyodide.js pyodide/pyodide-lock.json pyodide/python_stdlib.zip pyodide/pycryptodome-3.20.0-cp35-abi3-pyodide_2024_0_wasm32.whl +fi + +echo "Pyodide is installed" + +echo "Checking if socket.io is installed" +if ! test -f ./WebServer/static/socket.io.min.js; then + curl -L --output ./WebServer/static/socket.io.min.js https://cdn.socket.io/4.8.1/socket.io.min.js +fi +echo "Socket.io is installed" + +echo "Building wheel file of SantaExchange package" +cd src/ +python setup.py bdist_wheel +cd ../ +cp ./src/dist/santaexchange-0.1-py3-none-any.whl ./WebServer/static/ diff --git a/classes/Crypto/CSPRNG.py b/src/SantaExchange/Crypto/CSPRNG.py similarity index 100% rename from classes/Crypto/CSPRNG.py rename to src/SantaExchange/Crypto/CSPRNG.py diff --git a/classes/Crypto/CommutativeCipher.py b/src/SantaExchange/Crypto/CommutativeCipher.py similarity index 100% rename from classes/Crypto/CommutativeCipher.py rename to src/SantaExchange/Crypto/CommutativeCipher.py diff --git a/classes/MessageTypes/__init__.py b/src/SantaExchange/Crypto/__init__.py similarity index 100% rename from classes/MessageTypes/__init__.py rename to src/SantaExchange/Crypto/__init__.py diff --git a/classes/Message.py b/src/SantaExchange/Message.py similarity index 100% rename from classes/Message.py rename to src/SantaExchange/Message.py diff --git a/classes/MessageHandler.py b/src/SantaExchange/MessageHandler.py similarity index 93% rename from classes/MessageHandler.py rename to src/SantaExchange/MessageHandler.py index c4a2702..098641f 100644 --- a/classes/MessageHandler.py +++ b/src/SantaExchange/MessageHandler.py @@ -8,11 +8,11 @@ from json import JSONDecodeError from Crypto.PublicKey import ECC from Crypto.PublicKey.ECC import EccKey -from classes.Message import Message -from classes.MessageTypes.Announcement import AnnouncementMessage -from classes.MessageTypes.Introduction import IntroductionMessage -from classes.MessageTypes.Ready import ReadyMessage -from classes.MessageTypes.Shuffle import ShuffleMessage +from src.SantaExchange.Message import Message +from src.SantaExchange.MessageTypes.Announcement import AnnouncementMessage +from src.SantaExchange.MessageTypes.Introduction import IntroductionMessage +from src.SantaExchange.MessageTypes.Ready import ReadyMessage +from src.SantaExchange.MessageTypes.Shuffle import ShuffleMessage logger = logging.getLogger(__name__) @@ -22,7 +22,7 @@ class MessageHandler: self.receivers: list[Callable[[Message], None]] = [] def send_message(self, message: Message, signing_key: EccKey): - # Must be implemented by child classes + # Must be implemented by child SantaExchange pass def add_message_receiver(self, message_receiver: Callable[[Message], None]): diff --git a/classes/MessageTypes/Announcement.py b/src/SantaExchange/MessageTypes/Announcement.py similarity index 95% rename from classes/MessageTypes/Announcement.py rename to src/SantaExchange/MessageTypes/Announcement.py index bf729ac..38adda3 100644 --- a/classes/MessageTypes/Announcement.py +++ b/src/SantaExchange/MessageTypes/Announcement.py @@ -1,6 +1,6 @@ import base64 -from classes.Message import Message +from src.SantaExchange.Message import Message class AnnouncementMessage(Message): diff --git a/classes/MessageTypes/Introduction.py b/src/SantaExchange/MessageTypes/Introduction.py similarity index 95% rename from classes/MessageTypes/Introduction.py rename to src/SantaExchange/MessageTypes/Introduction.py index 639f264..6faa26a 100644 --- a/classes/MessageTypes/Introduction.py +++ b/src/SantaExchange/MessageTypes/Introduction.py @@ -3,7 +3,7 @@ import base64 from Crypto.PublicKey import ECC from Crypto.PublicKey.ECC import EccKey -from classes.Message import Message +from src.SantaExchange.Message import Message class IntroductionMessage(Message): diff --git a/classes/MessageTypes/Ready.py b/src/SantaExchange/MessageTypes/Ready.py similarity index 96% rename from classes/MessageTypes/Ready.py rename to src/SantaExchange/MessageTypes/Ready.py index c801b9d..a8f9eea 100644 --- a/classes/MessageTypes/Ready.py +++ b/src/SantaExchange/MessageTypes/Ready.py @@ -3,7 +3,7 @@ import base64 from Crypto.PublicKey import ECC from Crypto.PublicKey.ECC import EccKey -from classes.Message import Message +from src.SantaExchange.Message import Message class ReadyMessage(Message): diff --git a/classes/MessageTypes/Shuffle.py b/src/SantaExchange/MessageTypes/Shuffle.py similarity index 94% rename from classes/MessageTypes/Shuffle.py rename to src/SantaExchange/MessageTypes/Shuffle.py index c4b2136..982c2ea 100644 --- a/classes/MessageTypes/Shuffle.py +++ b/src/SantaExchange/MessageTypes/Shuffle.py @@ -1,6 +1,6 @@ import base64 -from classes.Message import Message +from src.SantaExchange.Message import Message class ShuffleMessage(Message): diff --git a/classes/__init__.py b/src/SantaExchange/MessageTypes/__init__.py similarity index 100% rename from classes/__init__.py rename to src/SantaExchange/MessageTypes/__init__.py diff --git a/classes/SantasBrain.py b/src/SantaExchange/SantasBrain.py similarity index 95% rename from classes/SantasBrain.py rename to src/SantaExchange/SantasBrain.py index 9594860..08550c6 100644 --- a/classes/SantasBrain.py +++ b/src/SantaExchange/SantasBrain.py @@ -14,15 +14,15 @@ from Crypto.Protocol.DH import key_agreement from Crypto.PublicKey import ECC from Crypto.PublicKey.ECC import EccKey -from classes import Message -from classes.Crypto.CSPRNG import CSPRNG -from classes.Crypto.CommutativeCipher import CommutativeCipher -from classes.MessageHandler import MessageHandler -from classes.MessageTypes.Announcement import AnnouncementMessage -from classes.MessageTypes.Introduction import IntroductionMessage -from classes.MessageTypes.Ready import ReadyMessage -from classes.MessageTypes.Shuffle import ShuffleMessage -from classes.UserInterface import UserInterface +from src.SantaExchange import Message +from src.SantaExchange.Crypto.CSPRNG import CSPRNG +from src.SantaExchange.Crypto.CommutativeCipher import CommutativeCipher +from src.SantaExchange.MessageHandler import MessageHandler +from src.SantaExchange.MessageTypes.Announcement import AnnouncementMessage +from src.SantaExchange.MessageTypes.Introduction import IntroductionMessage +from src.SantaExchange.MessageTypes.Ready import ReadyMessage +from src.SantaExchange.MessageTypes.Shuffle import ShuffleMessage +from src.SantaExchange.UserInterface import UserInterface logger = logging.getLogger(__name__) @@ -583,11 +583,11 @@ class Brain: # If we are the last participant, everything should now be decrypted if own_index == len(all_participants) - 1: - self.get_anonymous_keys(shuffle_message) + self.get_anonymous_keys(shuffle_message, len(all_participants)) return shuffle_message - def get_anonymous_keys(self, message: ShuffleMessage): + def get_anonymous_keys(self, message: ShuffleMessage, number_of_participants: int): anonymous_keys = {} cards = message.get_cards() for card in cards: @@ -598,6 +598,16 @@ class Brain: logger.critical(f'Received card {card} could not be decoded as JSON. Secret santa process failed.') self.process_failed = True return + + # Check that there are as many different cards as there are participants + for i in range(number_of_participants): + if i not in anonymous_keys: + self.process_failed = True + logger.critical( + f'Not all cards seem to have been drawn. It is possible someone is trying to cheat. ' + f'Stopping secret santa exchange' + ) + return self.anonymous_keys = anonymous_keys def build_announcement(self, all_participants: list[str]) -> Optional[AnnouncementMessage]: @@ -614,7 +624,7 @@ class Brain: if message is None: return None - self.get_anonymous_keys(message) + self.get_anonymous_keys(message, len(all_participants)) if self.anonymous_keys is None: return None diff --git a/classes/UserInterface.py b/src/SantaExchange/UserInterface.py similarity index 100% rename from classes/UserInterface.py rename to src/SantaExchange/UserInterface.py diff --git a/src/SantaExchange/__init__.py b/src/SantaExchange/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/setup.py b/src/setup.py new file mode 100644 index 0000000..2daa505 --- /dev/null +++ b/src/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup, find_packages +from pathlib import Path + +setup( + name='SantaExchange', + version="0.1", + packages=find_packages() +) \ No newline at end of file