Working more on web server
This commit is contained in:
parent
8652ab2eec
commit
14cdefea69
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,2 +1,10 @@
|
|||||||
.idea/*
|
.idea/*
|
||||||
**/__pycache__/**
|
**/__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/
|
||||||
|
|||||||
34
WebServer/server.py
Normal file
34
WebServer/server.py
Normal file
@ -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('/<exchange>')
|
||||||
|
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)
|
||||||
28
WebServer/static/exchange_client.py
Normal file
28
WebServer/static/exchange_client.py
Normal file
@ -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)
|
||||||
23
WebServer/static/exchange_worker.js
Normal file
23
WebServer/static/exchange_worker.js
Normal file
@ -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');
|
||||||
|
});
|
||||||
38
WebServer/static/style.css
Normal file
38
WebServer/static/style.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
49
WebServer/templates/exchange.html
Normal file
49
WebServer/templates/exchange.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Exchange</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
|
||||||
|
<script src="{{ url_for('static', filename='socket.io.min.js') }}" integrity="sha384-mkQ3/7FUtcGyoppY6bz/PORYoGqOl7/aSUMn2ymDOJcapfS6PHqxhRTMh1RR0Q6+" crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Santa exchange</h1>
|
||||||
|
<label for="own_name">Your name: </label><br /><input type="text" id="own_name" /><br />
|
||||||
|
<br />
|
||||||
|
<label for="info_for_santa">Info for your santa (e.g. shipping address or similar):</label><br />
|
||||||
|
<textarea id="info_for_santa"></textarea><br />
|
||||||
|
<button id="submit_info_button">Submit your info</button>
|
||||||
|
<br /><br /><br />
|
||||||
|
<p id="statusParagraph"></p>
|
||||||
|
<button>Start with current participants</button>
|
||||||
|
<p>Current participants:</p>
|
||||||
|
<div id='participantList'></div>
|
||||||
|
<!--<script type="text/javascript">
|
||||||
|
const socket = io();
|
||||||
|
const client_id = Math.floor(Math.random() * 2**64)
|
||||||
|
socket.emit('join', {room: window.location.pathname, 'client_id': client_id})
|
||||||
|
|
||||||
|
socket.on('message', data => {
|
||||||
|
// All messages are broadcast to everyone, so discard messages that were actually sent from ourselves
|
||||||
|
data = JSON.parse(data);
|
||||||
|
if (data['client_id'] != client_id) {
|
||||||
|
console.log(data);
|
||||||
|
console.log(data['message']);
|
||||||
|
window.pass_message_to_python(JSON.stringify(data['message']));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>-->
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
const exchangeWorker = new Worker("{{ url_for('static', filename='exchange_worker.js') }}", { type: 'module' });
|
||||||
|
|
||||||
|
exchangeWorker.onmessage = (e) => {
|
||||||
|
console.log('Received message:');
|
||||||
|
console.log(e);
|
||||||
|
document.getElementById('statusParagraph').innerHTML = e.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
exchangeWorker.postMessage('Nothing');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
52
WebServer/templates/index.html
Normal file
52
WebServer/templates/index.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>DecentraSanta</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"/>
|
||||||
|
<script type="text/javascript" src="{{ url_for('static', filename='pyodide.js') }}"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="bodyDiv">
|
||||||
|
<h1>DecentraSanta</h1>
|
||||||
|
<p>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.</p>
|
||||||
|
|
||||||
|
<p>You can either start a new secret santa exchange, or join an existing one.</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<button id="start_new" onclick="onNewExchange();">Start new exchange</button>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<input type="text" id="existing_address" />
|
||||||
|
<br/>
|
||||||
|
<button id="join_existing" onclick="onJoinExchange();">Join existing exchange</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
function onNewExchange() {
|
||||||
|
const byteArray = new Uint8Array(8);
|
||||||
|
self.crypto.getRandomValues(byteArray);
|
||||||
|
let randomHexString = Array.from(byteArray).map(x => x.toString(16)).reduce((a, c) => a + c);
|
||||||
|
var urlString = window.location.href;
|
||||||
|
if (!urlString.endsWith('/')) {
|
||||||
|
urlString = urlString + '/';
|
||||||
|
}
|
||||||
|
urlString = urlString + randomHexString;
|
||||||
|
window.location.href = urlString;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onJoinExchange() {
|
||||||
|
const existingExchange = document.getElementById('existing_address').value;
|
||||||
|
var urlString = window.location.href;
|
||||||
|
if (!urlString.endsWith('/')) {
|
||||||
|
urlString = urlString + '/';
|
||||||
|
}
|
||||||
|
urlString = urlString + existingExchange;
|
||||||
|
window.location.href = urlString;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
13
main.py
13
main.py
@ -1,9 +1,9 @@
|
|||||||
from Crypto.PublicKey.ECC import EccKey
|
from Crypto.PublicKey.ECC import EccKey
|
||||||
|
|
||||||
from classes.Message import Message
|
from src.SantaExchange.Message import Message
|
||||||
from classes.MessageHandler import MessageHandler
|
from src.SantaExchange.MessageHandler import MessageHandler
|
||||||
from classes.SantasBrain import Brain
|
from src.SantaExchange.SantasBrain import Brain
|
||||||
from classes.UserInterface import UserInterface
|
from src.SantaExchange.UserInterface import UserInterface
|
||||||
|
|
||||||
participants: list[tuple[MessageHandler, UserInterface]] = []
|
participants: list[tuple[MessageHandler, UserInterface]] = []
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ class TestUserInterface(UserInterface):
|
|||||||
def announce_recipient(self, name: str, other_info: str):
|
def announce_recipient(self, name: str, other_info: str):
|
||||||
print(f'{self.own_name}: Received {name}, {other_info}')
|
print(f'{self.own_name}: Received {name}, {other_info}')
|
||||||
|
|
||||||
number_of_participants = 20
|
number_of_participants = 3
|
||||||
|
|
||||||
for i in range(number_of_participants):
|
for i in range(number_of_participants):
|
||||||
test_message_handler = TestMessageHandler()
|
test_message_handler = TestMessageHandler()
|
||||||
@ -48,5 +48,4 @@ for i in range(number_of_participants):
|
|||||||
|
|
||||||
for i in range(number_of_participants):
|
for i in range(number_of_participants):
|
||||||
handler, interface = participants[i]
|
handler, interface = participants[i]
|
||||||
interface.lets_go()
|
interface.lets_go()
|
||||||
|
|
||||||
@ -1 +1,4 @@
|
|||||||
pycryptodome>=3.21.0
|
pycryptodome>=3.21.0
|
||||||
|
flask>=3.1.0
|
||||||
|
flask-socketio>=5.5.1
|
||||||
|
setuptools>=78.1.0
|
||||||
24
setup-server.sh
Executable file
24
setup-server.sh
Executable file
@ -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/
|
||||||
@ -8,11 +8,11 @@ from json import JSONDecodeError
|
|||||||
from Crypto.PublicKey import ECC
|
from Crypto.PublicKey import ECC
|
||||||
from Crypto.PublicKey.ECC import EccKey
|
from Crypto.PublicKey.ECC import EccKey
|
||||||
|
|
||||||
from classes.Message import Message
|
from src.SantaExchange.Message import Message
|
||||||
from classes.MessageTypes.Announcement import AnnouncementMessage
|
from src.SantaExchange.MessageTypes.Announcement import AnnouncementMessage
|
||||||
from classes.MessageTypes.Introduction import IntroductionMessage
|
from src.SantaExchange.MessageTypes.Introduction import IntroductionMessage
|
||||||
from classes.MessageTypes.Ready import ReadyMessage
|
from src.SantaExchange.MessageTypes.Ready import ReadyMessage
|
||||||
from classes.MessageTypes.Shuffle import ShuffleMessage
|
from src.SantaExchange.MessageTypes.Shuffle import ShuffleMessage
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ class MessageHandler:
|
|||||||
self.receivers: list[Callable[[Message], None]] = []
|
self.receivers: list[Callable[[Message], None]] = []
|
||||||
|
|
||||||
def send_message(self, message: Message, signing_key: EccKey):
|
def send_message(self, message: Message, signing_key: EccKey):
|
||||||
# Must be implemented by child classes
|
# Must be implemented by child SantaExchange
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add_message_receiver(self, message_receiver: Callable[[Message], None]):
|
def add_message_receiver(self, message_receiver: Callable[[Message], None]):
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
from classes.Message import Message
|
from src.SantaExchange.Message import Message
|
||||||
|
|
||||||
|
|
||||||
class AnnouncementMessage(Message):
|
class AnnouncementMessage(Message):
|
||||||
@ -3,7 +3,7 @@ import base64
|
|||||||
from Crypto.PublicKey import ECC
|
from Crypto.PublicKey import ECC
|
||||||
from Crypto.PublicKey.ECC import EccKey
|
from Crypto.PublicKey.ECC import EccKey
|
||||||
|
|
||||||
from classes.Message import Message
|
from src.SantaExchange.Message import Message
|
||||||
|
|
||||||
|
|
||||||
class IntroductionMessage(Message):
|
class IntroductionMessage(Message):
|
||||||
@ -3,7 +3,7 @@ import base64
|
|||||||
from Crypto.PublicKey import ECC
|
from Crypto.PublicKey import ECC
|
||||||
from Crypto.PublicKey.ECC import EccKey
|
from Crypto.PublicKey.ECC import EccKey
|
||||||
|
|
||||||
from classes.Message import Message
|
from src.SantaExchange.Message import Message
|
||||||
|
|
||||||
|
|
||||||
class ReadyMessage(Message):
|
class ReadyMessage(Message):
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
from classes.Message import Message
|
from src.SantaExchange.Message import Message
|
||||||
|
|
||||||
|
|
||||||
class ShuffleMessage(Message):
|
class ShuffleMessage(Message):
|
||||||
@ -14,15 +14,15 @@ from Crypto.Protocol.DH import key_agreement
|
|||||||
from Crypto.PublicKey import ECC
|
from Crypto.PublicKey import ECC
|
||||||
from Crypto.PublicKey.ECC import EccKey
|
from Crypto.PublicKey.ECC import EccKey
|
||||||
|
|
||||||
from classes import Message
|
from src.SantaExchange import Message
|
||||||
from classes.Crypto.CSPRNG import CSPRNG
|
from src.SantaExchange.Crypto.CSPRNG import CSPRNG
|
||||||
from classes.Crypto.CommutativeCipher import CommutativeCipher
|
from src.SantaExchange.Crypto.CommutativeCipher import CommutativeCipher
|
||||||
from classes.MessageHandler import MessageHandler
|
from src.SantaExchange.MessageHandler import MessageHandler
|
||||||
from classes.MessageTypes.Announcement import AnnouncementMessage
|
from src.SantaExchange.MessageTypes.Announcement import AnnouncementMessage
|
||||||
from classes.MessageTypes.Introduction import IntroductionMessage
|
from src.SantaExchange.MessageTypes.Introduction import IntroductionMessage
|
||||||
from classes.MessageTypes.Ready import ReadyMessage
|
from src.SantaExchange.MessageTypes.Ready import ReadyMessage
|
||||||
from classes.MessageTypes.Shuffle import ShuffleMessage
|
from src.SantaExchange.MessageTypes.Shuffle import ShuffleMessage
|
||||||
from classes.UserInterface import UserInterface
|
from src.SantaExchange.UserInterface import UserInterface
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -583,11 +583,11 @@ class Brain:
|
|||||||
|
|
||||||
# If we are the last participant, everything should now be decrypted
|
# If we are the last participant, everything should now be decrypted
|
||||||
if own_index == len(all_participants) - 1:
|
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
|
return shuffle_message
|
||||||
|
|
||||||
def get_anonymous_keys(self, message: ShuffleMessage):
|
def get_anonymous_keys(self, message: ShuffleMessage, number_of_participants: int):
|
||||||
anonymous_keys = {}
|
anonymous_keys = {}
|
||||||
cards = message.get_cards()
|
cards = message.get_cards()
|
||||||
for card in 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.')
|
logger.critical(f'Received card {card} could not be decoded as JSON. Secret santa process failed.')
|
||||||
self.process_failed = True
|
self.process_failed = True
|
||||||
return
|
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
|
self.anonymous_keys = anonymous_keys
|
||||||
|
|
||||||
def build_announcement(self, all_participants: list[str]) -> Optional[AnnouncementMessage]:
|
def build_announcement(self, all_participants: list[str]) -> Optional[AnnouncementMessage]:
|
||||||
@ -614,7 +624,7 @@ class Brain:
|
|||||||
if message is None:
|
if message is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.get_anonymous_keys(message)
|
self.get_anonymous_keys(message, len(all_participants))
|
||||||
|
|
||||||
if self.anonymous_keys is None:
|
if self.anonymous_keys is None:
|
||||||
return None
|
return None
|
||||||
0
src/SantaExchange/__init__.py
Normal file
0
src/SantaExchange/__init__.py
Normal file
8
src/setup.py
Normal file
8
src/setup.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='SantaExchange',
|
||||||
|
version="0.1",
|
||||||
|
packages=find_packages()
|
||||||
|
)
|
||||||
Loading…
x
Reference in New Issue
Block a user