Feat: encrypt all data
This commit is contained in:
parent
640852c206
commit
e909952fa4
2 changed files with 68 additions and 14 deletions
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue