95 lines
4.5 KiB
HTML
95 lines
4.5 KiB
HTML
<!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>
|
|
<br /><br /><br /><br />
|
|
<p>
|
|
<b>
|
|
WARNING: I have partially rolled my own crypto for this project. Please don't use it for anything serious!
|
|
</b>
|
|
</p>
|
|
<p class="leftjust">
|
|
How does this work: the simplified analogy is that we build a deck of cards numbered from 1 up to the number
|
|
of participants. This deck is shuffled, and each participant draws a card. The cards determine the secret
|
|
santa order: The participant who draws card 1 is the secret santa for the participant who draws card 2, who
|
|
is secret santa for the participant who draws card 3, and so forth.
|
|
</p>
|
|
<p>
|
|
In this analogy, once the participants have drawn their cards, they each anonymously put up a mailbox that
|
|
is marked with number of the card they drew. Each participant then puts their name and address (secretly)
|
|
into the mailbox with the number of their secret santa. Finally, they secretly open their mailboxes to find
|
|
the name and address of the recipient of their gifts.
|
|
</p>
|
|
<p>
|
|
More technically: the shuffling and drawing of the cards is done using the
|
|
<a href="https://people.csail.mit.edu/rivest/pubs/SRA81.pdf">mental poker algorithm described by Shamir,
|
|
Rivest, and Adleman.</a> This algorithm depends on a commutative encryption algorithm. Here, I have used the
|
|
SRA algorithm, which is a modification of the RSA algorithm where the modulus used to encrypt and decrypt
|
|
values is public, but the both the encryption and decryption keys are kept private. I couldn't find any
|
|
Python libraries that implement this algorithm, so I have implemented it on my own. I make no guarantees
|
|
that this is secure in any way.
|
|
</p>
|
|
<p>
|
|
In the analogy above, the mailboxes represent public key cryptography. It is possible to anonymously announce
|
|
a participant's public key using the same mental poker algorithm from above: each participant publishes a
|
|
"card" containing, in encrypted form, their public key and the card number they drew in the previous step.
|
|
Each other participant then encrypts this card with their own key. Once all the cards have been encrypted
|
|
in this way, each participant then removes their key, shuffles the deck, and applies a new key (which should
|
|
make it impossible to correlate cards before and after shuffling). Finally, each participant removes their
|
|
key from each of the cards. This should give a deck of cards containing participant numbers and public keys,
|
|
shuffled in such a way that it is impossible to know which card originated from which participant.
|
|
</p>
|
|
<p>
|
|
Finally, each participant publishes their name and address, encrypted so that only their secret santa can read
|
|
it. Each participant must then try to decrypt all of these encrypted messages until their gift receiver is found.
|
|
</p>
|
|
<p><a href="https://gitea.martinserver.no/martin/DecentraSanta">The source code for this web site can be found here!</a></p>
|
|
</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> |