Sign packets
This commit is contained in:
parent
ab629a78b4
commit
96fe20503a
@ -1,4 +1,4 @@
|
||||
import { generate_keypair } from "./paillier.js";
|
||||
import { generate_rsa_keypair } from "./rsa.js";
|
||||
import { generate_rsa_keypair, RsaPubKey } from "./rsa.js";
|
||||
|
||||
export { generate_keypair, generate_rsa_keypair };
|
||||
export { generate_keypair, generate_rsa_keypair, RsaPubKey };
|
||||
|
@ -3,18 +3,29 @@ import { mod_exp, mod_inv } from "./math.js";
|
||||
|
||||
let p, q, pubKey, privKey;
|
||||
|
||||
class PubKey {
|
||||
constructor(p, q) {
|
||||
this.n = p * q;
|
||||
this.e = 65537n;
|
||||
export class RsaPubKey {
|
||||
constructor(n, e) {
|
||||
this.n = n;
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
encrypt(m) {
|
||||
return mod_exp(m, this.e, this.n);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
n: "0x" + this.n.toString(16),
|
||||
e: "0x" + this.e.toString(16),
|
||||
};
|
||||
}
|
||||
|
||||
static fromJSON(data) {
|
||||
return new RsaPubKey(BigInt(data.n), BigInt(data.e));
|
||||
}
|
||||
}
|
||||
|
||||
class PrivKey {
|
||||
class RsaPrivKey {
|
||||
constructor(p, q) {
|
||||
this.n = p * q;
|
||||
this.d = mod_inv(65537n, (q - 1n) * (p - 1n));
|
||||
@ -40,8 +51,8 @@ export function generate_rsa_keypair() {
|
||||
q = BigInt(window.sessionStorage.getItem("rsa_q"));
|
||||
}
|
||||
|
||||
pubKey = new PubKey(p, q);
|
||||
privKey = new PrivKey(p, q);
|
||||
pubKey = new RsaPubKey(p * q, 65537n);
|
||||
privKey = new RsaPrivKey(p, q);
|
||||
|
||||
return { pubKey, privKey };
|
||||
}
|
||||
|
@ -36,11 +36,11 @@ export class Game {
|
||||
return Object.values(this.players).filter((p) => p.isPlaying)[0];
|
||||
}
|
||||
|
||||
addPlayer(id, is_us) {
|
||||
addPlayer(id, is_us, pubkey) {
|
||||
let is_new = this.players[id] === undefined;
|
||||
|
||||
if (this.isWaiting()) {
|
||||
this.players[id] = new Player(id, is_us);
|
||||
this.players[id] = new Player(id, is_us, pubkey);
|
||||
if (is_us) {
|
||||
this.us = this.players[id];
|
||||
}
|
||||
|
@ -26,14 +26,29 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
socket.on("connect", () => {
|
||||
window.console.log("Connected!");
|
||||
window.console.log(`We are: ${ID}`);
|
||||
socket.emit("message", Packet.createAnnounce());
|
||||
game.addPlayer(ID, true);
|
||||
socket.emit("message", Packet.createAnnounce(window.rsa));
|
||||
game.addPlayer(ID, true, window.rsa.pubKey);
|
||||
});
|
||||
|
||||
socket.on("message", async (data) => {
|
||||
socket.on("message", async (packet) => {
|
||||
let data = packet.payload;
|
||||
window.console.log(data);
|
||||
|
||||
// todo validate signature
|
||||
const sender = game.players[data.author];
|
||||
if (sender === undefined) {
|
||||
window.console.warn(
|
||||
`Not able to verify signature on packet ${data.id} as the sender is unknown.`
|
||||
);
|
||||
} else {
|
||||
let sig = BigInt(packet.sig);
|
||||
// decrypt and compare signature
|
||||
let dehash = sender.rsaPubKey.encrypt(sig).toString(16);
|
||||
let hash = CryptoJS.SHA3(JSON.stringify(data)).toString();
|
||||
if (dehash !== hash) {
|
||||
window.console.error(`Signature invalid! Ignoring packet ${data.id}.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case "RANDOM":
|
||||
@ -67,11 +82,11 @@ document.addEventListener("ANNOUNCE", (ev) => {
|
||||
const data = ev.detail;
|
||||
if (data.author === ID) return;
|
||||
|
||||
let is_new = game.addPlayer(data.author, false);
|
||||
let is_new = game.addPlayer(data.author, false, data.rsaPubKey);
|
||||
|
||||
// When a new player is seen, all announce to ensure they know all players.
|
||||
if (is_new) {
|
||||
socket.emit("message", Packet.createAnnounce());
|
||||
socket.emit("message", Packet.createAnnounce(window.rsa));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -9,30 +9,51 @@ export class Packet {
|
||||
};
|
||||
}
|
||||
|
||||
static createAnnounce() {
|
||||
static _sign(data) {
|
||||
// compute hash of data
|
||||
let hash = CryptoJS.SHA3(JSON.stringify(data));
|
||||
let res = 0n;
|
||||
for (let word32 of hash.words) {
|
||||
// remove any sign
|
||||
let bi = BigInt.asUintN(32, BigInt(word32));
|
||||
res = (res << 32n) | bi;
|
||||
}
|
||||
|
||||
// sign hash
|
||||
let sig = window.rsa.privKey.decrypt(res);
|
||||
|
||||
// return new packet
|
||||
return {
|
||||
...this._createBase("ANNOUNCE"),
|
||||
sig: "0x" + sig.toString(16),
|
||||
payload: data,
|
||||
};
|
||||
}
|
||||
|
||||
static createAnnounce(rsaKeyPair) {
|
||||
return this._sign({
|
||||
...this._createBase("ANNOUNCE"),
|
||||
rsaPubKey: rsaKeyPair.pubKey.toJSON(),
|
||||
});
|
||||
}
|
||||
|
||||
static createDisconnect() {
|
||||
return this._createBase("DISCONNECT");
|
||||
return this._sign(this._createBase("DISCONNECT"));
|
||||
}
|
||||
|
||||
static createKeepAlive() {
|
||||
return this._createBase("KEEPALIVE");
|
||||
return this._sign(this._createBase("KEEPALIVE"));
|
||||
}
|
||||
|
||||
static createSetReady(nowReady) {
|
||||
return {
|
||||
return this._sign({
|
||||
...this._createBase("ACT"),
|
||||
action: "READY",
|
||||
ready: nowReady,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static createBarrierSignal() {
|
||||
return this._createBase("BARRIER");
|
||||
return this._sign(this._createBase("BARRIER"));
|
||||
}
|
||||
|
||||
static createRegionClaim(region) {
|
||||
@ -43,27 +64,27 @@ export class Packet {
|
||||
}
|
||||
|
||||
static createAction(action, startRegion, endRegion, amount) {
|
||||
return {
|
||||
return this._sign({
|
||||
...this._createBase("ACT"),
|
||||
startRegion: startRegion,
|
||||
endRegion: endRegion,
|
||||
strength: amount,
|
||||
action: action,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static createDefense(amount) {
|
||||
return {
|
||||
return this._sign({
|
||||
...this._createBase("ACT"),
|
||||
action: "DEFENSE",
|
||||
amount: amount,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static createEndTurn() {
|
||||
return {
|
||||
return this._sign({
|
||||
...this._createBase("ACT"),
|
||||
action: "END",
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Packet } from "./packet.js";
|
||||
import { socket } from "./main.js";
|
||||
import { RsaPubKey } from "../crypto/rsa.js";
|
||||
|
||||
// Timeout to consider a player disconnected
|
||||
const TIMEOUT = 30_000;
|
||||
@ -11,10 +12,11 @@ const PHASE_FORTIFY = 3;
|
||||
let totalDice = 0;
|
||||
|
||||
export class Player {
|
||||
constructor(id, local) {
|
||||
constructor(id, local, pubkey) {
|
||||
this.timeout = null;
|
||||
this.id = id;
|
||||
this.ready = false;
|
||||
this.rsaPubKey = RsaPubKey.fromJSON(pubkey);
|
||||
|
||||
// Data which is reset every turn
|
||||
this.isPlaying = false;
|
||||
|
Loading…
Reference in New Issue
Block a user