Web exchange seems to work now
This commit is contained in:
parent
14cdefea69
commit
564e449e33
@ -1,10 +1,11 @@
|
|||||||
|
from typing import Optional, Callable
|
||||||
|
|
||||||
from Crypto.PublicKey.ECC import EccKey
|
from Crypto.PublicKey.ECC import EccKey
|
||||||
|
|
||||||
from src.SantaExchange.Message import Message
|
from SantaExchange.Message import Message
|
||||||
from src.SantaExchange.MessageHandler import MessageHandler
|
from SantaExchange.MessageHandler import MessageHandler
|
||||||
from src.SantaExchange.SantasBrain import Brain
|
from SantaExchange.SantasBrain import Brain
|
||||||
from src.SantaExchange.UserInterface import UserInterface
|
from SantaExchange.UserInterface import UserInterface
|
||||||
|
|
||||||
|
|
||||||
class ExchangeClient(MessageHandler, UserInterface):
|
class ExchangeClient(MessageHandler, UserInterface):
|
||||||
def __init__(self, send_message_function, receive_user_function, announce_recipient_function):
|
def __init__(self, send_message_function, receive_user_function, announce_recipient_function):
|
||||||
|
|||||||
@ -1,23 +1,56 @@
|
|||||||
import "./pyodide.js";
|
import "./pyodide.js";
|
||||||
|
|
||||||
async function prepare() {
|
async function prepare() {
|
||||||
|
self.postMessage({type: 'status', stage: 'initialize', text: 'Initializing...'});
|
||||||
let pyodide = await loadPyodide();
|
let pyodide = await loadPyodide();
|
||||||
await pyodide.loadPackage("pycryptodome");
|
await pyodide.loadPackage("pycryptodome");
|
||||||
await pyodide.loadPackage("./santaexchange-0.1-py3-none-any.whl")
|
await pyodide.loadPackage("./santaexchange-0.1-py3-none-any.whl");
|
||||||
pyodide.runPython(`
|
let response = await fetch('./exchange_client.py');
|
||||||
class Test:
|
pyodide.runPython(await response.text())
|
||||||
def calculate(self):
|
|
||||||
return 15
|
|
||||||
|
|
||||||
a = Test()
|
function sendMessage(messageString) {
|
||||||
|
self.postMessage({type: 'message', message: messageString})
|
||||||
|
}
|
||||||
|
|
||||||
|
function receiveUser(username) {
|
||||||
|
self.postMessage({type: 'new_user', username: username})
|
||||||
|
}
|
||||||
|
|
||||||
|
function announceRecipient(recipient_name, recipient_info) {
|
||||||
|
self.postMessage({type: 'status', stage: 'finished', text: `Your secret santa receiver is \n${recipient_name}\n${recipient_info}`})
|
||||||
|
}
|
||||||
|
|
||||||
|
function printLogs(record) {
|
||||||
|
console.log(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
pyodide.globals.set('send_message', sendMessage);
|
||||||
|
pyodide.globals.set('receive_user', receiveUser);
|
||||||
|
pyodide.globals.set('announce_recipient', announceRecipient);
|
||||||
|
pyodide.globals.set('print_logs_function', printLogs);
|
||||||
|
|
||||||
|
pyodide.runPython(`
|
||||||
|
exchange_worker = ExchangeClient(send_message, receive_user, announce_recipient)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
console.log('Running python function');
|
addEventListener('message', e => {
|
||||||
console.log(pyodide.globals.get('a').calculate());
|
if (e.data.type == 'message') {
|
||||||
|
pyodide.globals.get('exchange_worker').decode_received_message(e.data.message);
|
||||||
|
} else if (e.data.type == 'set_user') {
|
||||||
|
let username = e.data.username;
|
||||||
|
let userinfo = e.data.userinfo;
|
||||||
|
pyodide.globals.get('exchange_worker').set_user_info(username, userinfo);
|
||||||
|
postMessage({type: 'status', stage: 'wait_for_start', text: 'Waiting for other participants'});
|
||||||
|
} else if (e.data.type == 'start') {
|
||||||
|
postMessage({
|
||||||
|
type: 'status',
|
||||||
|
stage: 'exchanging',
|
||||||
|
text: 'Performing exchange when all users have pressed start. This may take a minute or so.'
|
||||||
|
});
|
||||||
|
pyodide.globals.get('exchange_worker').start_exchange(e.data.users);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.postMessage({type: 'status', stage: 'wait_for_user', text: 'Initialized'})
|
||||||
}
|
}
|
||||||
prepare();
|
prepare();
|
||||||
|
|
||||||
addEventListener('message', e => {
|
|
||||||
self.postMessage('Loading pyodide');
|
|
||||||
self.postMessage('Loaded pyodide');
|
|
||||||
});
|
|
||||||
@ -35,4 +35,5 @@ input {
|
|||||||
textarea {
|
textarea {
|
||||||
width: 30vw;
|
width: 30vw;
|
||||||
height: 20vh;
|
height: 20vh;
|
||||||
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
@ -12,38 +12,70 @@
|
|||||||
<br />
|
<br />
|
||||||
<label for="info_for_santa">Info for your santa (e.g. shipping address or similar):</label><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 />
|
<textarea id="info_for_santa"></textarea><br />
|
||||||
<button id="submit_info_button">Submit your info</button>
|
<button id="submit_info_button" onclick="submitInfo();">Submit your info</button>
|
||||||
<br /><br /><br />
|
<br /><br /><br />
|
||||||
<p id="statusParagraph"></p>
|
<pre id="statusParagraph"></pre>
|
||||||
<button>Start with current participants</button>
|
<button id="start_exchange_button" onclick="submitStart();">Start with current participants</button>
|
||||||
<p>Current participants:</p>
|
<p>Current participants:</p>
|
||||||
<div id='participantList'></div>
|
<ul id='participantList'>
|
||||||
<!--<script type="text/javascript">
|
|
||||||
|
</ul>
|
||||||
|
<script type="text/javascript">
|
||||||
const socket = io();
|
const socket = io();
|
||||||
const client_id = Math.floor(Math.random() * 2**64)
|
const client_id = Math.floor(Math.random() * 2**64)
|
||||||
socket.emit('join', {room: window.location.pathname, 'client_id': client_id})
|
|
||||||
|
|
||||||
socket.on('message', data => {
|
const other_users = []
|
||||||
// All messages are broadcast to everyone, so discard messages that were actually sent from ourselves
|
|
||||||
data = JSON.parse(data);
|
let submitButton = document.getElementById('submit_info_button');
|
||||||
if (data['client_id'] != client_id) {
|
let startButton = document.getElementById('start_exchange_button');
|
||||||
console.log(data);
|
|
||||||
console.log(data['message']);
|
submitButton.disabled = true;
|
||||||
window.pass_message_to_python(JSON.stringify(data['message']));
|
startButton.disabled = true;
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>-->
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
const exchangeWorker = new Worker("{{ url_for('static', filename='exchange_worker.js') }}", { type: 'module' });
|
const exchangeWorker = new Worker("{{ url_for('static', filename='exchange_worker.js') }}", { type: 'module' });
|
||||||
|
|
||||||
exchangeWorker.onmessage = (e) => {
|
exchangeWorker.onmessage = (e) => {
|
||||||
console.log('Received message:');
|
if (e.data.type == 'status') {
|
||||||
console.log(e);
|
document.getElementById('statusParagraph').innerHTML = e.data.text;
|
||||||
document.getElementById('statusParagraph').innerHTML = e.data;
|
if (e.data.stage == 'wait_for_user') {
|
||||||
|
submitButton.disabled = false;
|
||||||
|
startButton.disabled = true;
|
||||||
|
} else if (e.data.stage == 'wait_for_start') {
|
||||||
|
submitButton.disabled = true;
|
||||||
|
startButton.disabled = false;
|
||||||
|
} else {
|
||||||
|
submitButton.disabled = true;
|
||||||
|
startButton.disabled = true;
|
||||||
|
}
|
||||||
|
} else if (e.data.type == 'message') {
|
||||||
|
socket.emit('message', {client_id: client_id, room: window.location.pathname, message: e.data.message});
|
||||||
|
} else if (e.data.type == 'new_user') {
|
||||||
|
let list = document.getElementById('participantList');
|
||||||
|
const childNode = document.createElement("li");
|
||||||
|
childNode.innerHTML = e.data.username;
|
||||||
|
other_users.push(e.data.username);
|
||||||
|
list.appendChild(childNode);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exchangeWorker.postMessage('Nothing');
|
socket.on('message', data => {
|
||||||
|
// All messages are broadcast to everyone, so discard messages that were actually sent from ourselves
|
||||||
|
if (data['client_id'] != client_id) {
|
||||||
|
exchangeWorker.postMessage({type: 'message', message: data['message']})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function submitInfo() {
|
||||||
|
let name = document.getElementById('own_name').value;
|
||||||
|
let info = document.getElementById('info_for_santa').value;
|
||||||
|
exchangeWorker.postMessage({type: 'set_user', username: name, userinfo: info});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitStart() {
|
||||||
|
exchangeWorker.postMessage({type: 'start', users: other_users});
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('join', {room: window.location.pathname, client_id: client_id})
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -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 src.SantaExchange.Message import Message
|
from SantaExchange.Message import Message
|
||||||
from src.SantaExchange.MessageTypes.Announcement import AnnouncementMessage
|
from SantaExchange.MessageTypes.Announcement import AnnouncementMessage
|
||||||
from src.SantaExchange.MessageTypes.Introduction import IntroductionMessage
|
from SantaExchange.MessageTypes.Introduction import IntroductionMessage
|
||||||
from src.SantaExchange.MessageTypes.Ready import ReadyMessage
|
from SantaExchange.MessageTypes.Ready import ReadyMessage
|
||||||
from src.SantaExchange.MessageTypes.Shuffle import ShuffleMessage
|
from SantaExchange.MessageTypes.Shuffle import ShuffleMessage
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -21,9 +21,9 @@ class MessageHandler:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.receivers: list[Callable[[Message], None]] = []
|
self.receivers: list[Callable[[Message], None]] = []
|
||||||
|
|
||||||
|
# Must be implemented by child class
|
||||||
def send_message(self, message: Message, signing_key: EccKey):
|
def send_message(self, message: Message, signing_key: EccKey):
|
||||||
# Must be implemented by child SantaExchange
|
raise NotImplementedError
|
||||||
pass
|
|
||||||
|
|
||||||
def add_message_receiver(self, message_receiver: Callable[[Message], None]):
|
def add_message_receiver(self, message_receiver: Callable[[Message], None]):
|
||||||
self.receivers.append(message_receiver)
|
self.receivers.append(message_receiver)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
from src.SantaExchange.Message import Message
|
from 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 src.SantaExchange.Message import Message
|
from 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 src.SantaExchange.Message import Message
|
from SantaExchange.Message import Message
|
||||||
|
|
||||||
|
|
||||||
class ReadyMessage(Message):
|
class ReadyMessage(Message):
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
from src.SantaExchange.Message import Message
|
from 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 src.SantaExchange import Message
|
from SantaExchange import Message
|
||||||
from src.SantaExchange.Crypto.CSPRNG import CSPRNG
|
from SantaExchange.Crypto.CSPRNG import CSPRNG
|
||||||
from src.SantaExchange.Crypto.CommutativeCipher import CommutativeCipher
|
from SantaExchange.Crypto.CommutativeCipher import CommutativeCipher
|
||||||
from src.SantaExchange.MessageHandler import MessageHandler
|
from SantaExchange.MessageHandler import MessageHandler
|
||||||
from src.SantaExchange.MessageTypes.Announcement import AnnouncementMessage
|
from SantaExchange.MessageTypes.Announcement import AnnouncementMessage
|
||||||
from src.SantaExchange.MessageTypes.Introduction import IntroductionMessage
|
from SantaExchange.MessageTypes.Introduction import IntroductionMessage
|
||||||
from src.SantaExchange.MessageTypes.Ready import ReadyMessage
|
from SantaExchange.MessageTypes.Ready import ReadyMessage
|
||||||
from src.SantaExchange.MessageTypes.Shuffle import ShuffleMessage
|
from SantaExchange.MessageTypes.Shuffle import ShuffleMessage
|
||||||
from src.SantaExchange.UserInterface import UserInterface
|
from SantaExchange.UserInterface import UserInterface
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -129,6 +129,10 @@ class Brain:
|
|||||||
self.chosen_participants = sorted([name for name, key in confirmed_existing])
|
self.chosen_participants = sorted([name for name, key in confirmed_existing])
|
||||||
self.message_handler.send_message(ready_message, self.signing_key)
|
self.message_handler.send_message(ready_message, self.signing_key)
|
||||||
|
|
||||||
|
# Might as well start building ciphers and preparing immediately. Also, if the first user in the list happens
|
||||||
|
# to be the last person to send the start message, something needs to trigger the santa loop
|
||||||
|
self.santa_loop_and_send()
|
||||||
|
|
||||||
def receive_message(self, message: Message):
|
def receive_message(self, message: Message):
|
||||||
# If this is an introduction message, it needs special handling
|
# If this is an introduction message, it needs special handling
|
||||||
if isinstance(message, IntroductionMessage):
|
if isinstance(message, IntroductionMessage):
|
||||||
@ -149,14 +153,13 @@ class Brain:
|
|||||||
self.received_messages[name].append(message)
|
self.received_messages[name].append(message)
|
||||||
|
|
||||||
# Each received message triggers the main function of this class
|
# Each received message triggers the main function of this class
|
||||||
messages_to_send = self.santa_loop()
|
self.santa_loop_and_send()
|
||||||
for message in messages_to_send:
|
|
||||||
self.message_handler.send_message(message, self.signing_key)
|
|
||||||
|
|
||||||
def receive_introduction_message(self, message: IntroductionMessage):
|
def receive_introduction_message(self, message: IntroductionMessage):
|
||||||
discovered_new_participant = False
|
discovered_new_participant = False
|
||||||
name = message.get_name()
|
name = message.get_name()
|
||||||
key = message.get_key()
|
key = message.get_key()
|
||||||
|
logger.debug(f'Receiving introduction from user {name}')
|
||||||
|
|
||||||
with self.thread_lock:
|
with self.thread_lock:
|
||||||
if name in self.known_participants and self.known_participants[name][0] != key:
|
if name in self.known_participants and self.known_participants[name][0] != key:
|
||||||
@ -174,11 +177,21 @@ class Brain:
|
|||||||
# Send an introduction message, if we are ready for that
|
# Send an introduction message, if we are ready for that
|
||||||
if (
|
if (
|
||||||
discovered_new_participant
|
discovered_new_participant
|
||||||
and self.introduction_message is not None
|
|
||||||
and self.signing_key is not None
|
|
||||||
):
|
):
|
||||||
|
logger.debug(f'Received new user {name}')
|
||||||
self.user_interface.receive_user(name)
|
self.user_interface.receive_user(name)
|
||||||
self.message_handler.send_message(self.introduction_message, self.signing_key)
|
if (
|
||||||
|
self.introduction_message is not None
|
||||||
|
and self.signing_key is not None
|
||||||
|
):
|
||||||
|
logger.debug('Sending own introduction message')
|
||||||
|
self.message_handler.send_message(self.introduction_message, self.signing_key)
|
||||||
|
|
||||||
|
|
||||||
|
def santa_loop_and_send(self):
|
||||||
|
messages_to_send = self.santa_loop()
|
||||||
|
for message in messages_to_send:
|
||||||
|
self.message_handler.send_message(message, self.signing_key)
|
||||||
|
|
||||||
# Santa's brain will be driven by receiving messages. We'll call this main method each time we receive a message.
|
# Santa's brain will be driven by receiving messages. We'll call this main method each time we receive a message.
|
||||||
# This is probably inefficient, but it makes it easier to follow what the code is doing
|
# This is probably inefficient, but it makes it easier to follow what the code is doing
|
||||||
@ -203,6 +216,7 @@ class Brain:
|
|||||||
or self.announcement_build_cipher is None
|
or self.announcement_build_cipher is None
|
||||||
or self.announcement_shuffle_cipher is None
|
or self.announcement_shuffle_cipher is None
|
||||||
):
|
):
|
||||||
|
logger.debug('Building ciphers')
|
||||||
should_continue = self.build_ciphers()
|
should_continue = self.build_ciphers()
|
||||||
if not should_continue:
|
if not should_continue:
|
||||||
return messages_to_send
|
return messages_to_send
|
||||||
@ -212,6 +226,7 @@ class Brain:
|
|||||||
|
|
||||||
# Shuffle cards
|
# Shuffle cards
|
||||||
if not self.sent_card_shuffling:
|
if not self.sent_card_shuffling:
|
||||||
|
logger.debug('Shuffling vards')
|
||||||
shuffle_message = self.build_shuffle_message(all_participants)
|
shuffle_message = self.build_shuffle_message(all_participants)
|
||||||
if shuffle_message is None:
|
if shuffle_message is None:
|
||||||
return messages_to_send
|
return messages_to_send
|
||||||
@ -220,6 +235,7 @@ class Brain:
|
|||||||
|
|
||||||
# Decrypt shuffled cards
|
# Decrypt shuffled cards
|
||||||
if not self.sent_card_decryption:
|
if not self.sent_card_decryption:
|
||||||
|
logger.debug('Decrypting shuffled cards')
|
||||||
decrypt_cards_message = self.build_decrypt_cards_message(all_participants)
|
decrypt_cards_message = self.build_decrypt_cards_message(all_participants)
|
||||||
if decrypt_cards_message is None:
|
if decrypt_cards_message is None:
|
||||||
return messages_to_send
|
return messages_to_send
|
||||||
@ -432,6 +448,7 @@ class Brain:
|
|||||||
return ShuffleMessage(self.own_name, decrypted_cards, DECRYPT_CARDS_STAGE)
|
return ShuffleMessage(self.own_name, decrypted_cards, DECRYPT_CARDS_STAGE)
|
||||||
|
|
||||||
def decrypt_card_value(self, card: bytes):
|
def decrypt_card_value(self, card: bytes):
|
||||||
|
logger.debug('Decrypting own drawn card')
|
||||||
decrypted_card_bytes = self.card_exchange_cipher.decode(card)
|
decrypted_card_bytes = self.card_exchange_cipher.decode(card)
|
||||||
if decrypted_card_bytes not in self.card_values:
|
if decrypted_card_bytes not in self.card_values:
|
||||||
logging.critical(f'Received an invalid card after shuffling. Secret santa exchange failed!')
|
logging.critical(f'Received an invalid card after shuffling. Secret santa exchange failed!')
|
||||||
|
|||||||
@ -6,11 +6,13 @@ class UserInterface:
|
|||||||
self.user_info_listeners: list[Callable[[str, str], None]] = []
|
self.user_info_listeners: list[Callable[[str, str], None]] = []
|
||||||
self.start_listeners: list[Callable[[list[str]], None]] = []
|
self.start_listeners: list[Callable[[list[str]], None]] = []
|
||||||
|
|
||||||
|
# Must be implemented by child class
|
||||||
def receive_user(self, name: str):
|
def receive_user(self, name: str):
|
||||||
pass
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# Must be implemented by child class
|
||||||
def announce_recipient(self, name: str, other_info: str):
|
def announce_recipient(self, name: str, other_info: str):
|
||||||
pass
|
raise NotImplementedError
|
||||||
|
|
||||||
def set_user_info(self, name: str, info_for_santa: str):
|
def set_user_info(self, name: str, info_for_santa: str):
|
||||||
for listener in self.user_info_listeners:
|
for listener in self.user_info_listeners:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user