Feat: encrypt all data

This commit is contained in:
Dorian Zedler 2023-01-05 18:40:06 +01:00
parent 640852c206
commit e909952fa4
Signed by: dorian
GPG key ID: 989DE36109AFA354
2 changed files with 68 additions and 14 deletions

View file

@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script> <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script> <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
<script src="https://unpkg.com/crypto-js@4.1.1/crypto-js.js"></script>
<script src="js/index.js"></script> <script src="js/index.js"></script>
<script src="js/localState.js"></script> <script src="js/localState.js"></script>
<script src="js/remoteState.js"></script> <script src="js/remoteState.js"></script>
@ -67,7 +68,7 @@
<summary>This room</summary> <summary>This room</summary>
<label for="roomForm_interval">Interval (seconds):</label> <label for="roomForm_interval">Interval (seconds):</label>
<input id="roomForm_interval" type="number" x-model.throttle.1000ms="$store.remoteState.interval" <input id="roomForm_interval" type="number" x-model.throttle.500ms="$store.remoteState.interval"
placeholder="Interval" /> placeholder="Interval" />
<p> <p>

View file

@ -11,6 +11,8 @@ document.addEventListener("alpine:init", () => {
_currentPlayerTopic: null, _currentPlayerTopic: null,
_gameStateTopic: null, _gameStateTopic: null,
_lastGameState: null, _lastGameState: null,
_key: null,
_iv: null,
init() { init() {
Alpine.effect(() => { Alpine.effect(() => {
@ -64,12 +66,32 @@ document.addEventListener("alpine:init", () => {
connect() { connect() {
const that = this; const that = this;
const url = "wss://broker.emqx.io:8084/mqtt"; const url = "wss://broker.emqx.io:8084/mqtt";
const keySize = 512;
const ivSize = 128;
// derive key from room name
this._key = CryptoJS.PBKDF2(Alpine.store("localState").room, url, {
keySize: keySize / 32,
iterations: 1000,
});
// random iv
this._iv = CryptoJS.PBKDF2(Alpine.store("localState").room, url, {
keySize: ivSize / 32,
iterations: 5000,
});
const topicPrefix = `im.dorian.whos-turn-is-it.${btoa( const topicPrefix = `im.dorian.whos-turn-is-it.${btoa(
Alpine.store("localState").room Alpine.store("localState").room
)}`; )}`;
this._gameStateTopic = CryptoJS.SHA256(
this._encrypt(`${topicPrefix}.gameState`)
).toString();
this._currentPlayerTopic = CryptoJS.SHA256(
this._encrypt(`${topicPrefix}.currentPlayer`)
).toString();
this._gameStateTopic = `${topicPrefix}.gameState`; console.log("Connecting to MQTT broker...");
this._currentPlayerTopic = `${topicPrefix}.currentPlayer`; console.log("Game state topic:", this._gameStateTopic);
console.log("Current player topic:", this._currentPlayerTopic);
const options = { const options = {
// Clean session // Clean session
@ -95,10 +117,15 @@ document.addEventListener("alpine:init", () => {
this._client.on("message", (topic, message) => { this._client.on("message", (topic, message) => {
// message is Buffer // message is Buffer
console.log(topic, message.toString()); message = that._decrypt(message.toString());
const data = JSON.parse(JSON.parse(message));
if (topic === that._gameStateTopic) { if (topic === that._gameStateTopic) {
const data = JSON.parse(message.toString()); if (data.version !== 1 || !data.players || !data.interval) {
console.log("Invalid game state, resetting...");
that.clear();
return;
}
if (!that.connected) { if (!that.connected) {
that.connected = true; that.connected = true;
} }
@ -106,7 +133,14 @@ document.addEventListener("alpine:init", () => {
that.players = data.players; that.players = data.players;
that.interval = data.interval; that.interval = data.interval;
} else if (topic === that._currentPlayerTopic) { } else if (topic === that._currentPlayerTopic) {
const data = JSON.parse(message.toString()); if (!data.id || !data.since) {
console.log("Invalid current player, resetting...");
that.clear();
return;
}
if (data.since < that.lastPlayerSwitch) {
return;
}
that.currentPlayer = data.id; that.currentPlayer = data.id;
that.lastPlayerSwitch = data.since; that.lastPlayerSwitch = data.since;
} }
@ -162,21 +196,40 @@ document.addEventListener("alpine:init", () => {
JSON.stringify(this._lastGameState) === JSON.stringify(newGameState) JSON.stringify(this._lastGameState) === JSON.stringify(newGameState)
) )
return; return;
this._client.publish(this._gameStateTopic, JSON.stringify(newGameState), {
qos: 1, console.log("Updating game state:", newGameState);
retain: true, this._client.publish(
}); this._gameStateTopic,
this._encrypt(JSON.stringify(newGameState)),
{
qos: 1,
retain: true,
}
);
}, },
_updateCurrentPlayer(id) { _updateCurrentPlayer(id) {
this._client.publish( this._client.publish(
this._currentPlayerTopic, this._currentPlayerTopic,
JSON.stringify({ this._encrypt(
id: id, JSON.stringify({
since: new Date().getTime(), id: id,
}), since: new Date().getTime(),
})
),
{ qos: 1, retain: true } { qos: 1, retain: true }
); );
}, },
_encrypt(data) {
return CryptoJS.AES.encrypt(JSON.stringify(data), this._key, {
iv: this._iv,
}).toString();
},
_decrypt(data) {
const decrypted = CryptoJS.AES.decrypt(data, this._key, { iv: this._iv });
return decrypted.toString(CryptoJS.enc.Utf8);
},
}); });
}); });