Sign packets

This commit is contained in:
jude 2023-03-04 14:19:26 +00:00
parent ab629a78b4
commit 96fe20503a
6 changed files with 80 additions and 31 deletions

View File

@ -1,4 +1,4 @@
import { generate_keypair } from "./paillier.js"; 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 };

View File

@ -3,18 +3,29 @@ import { mod_exp, mod_inv } from "./math.js";
let p, q, pubKey, privKey; let p, q, pubKey, privKey;
class PubKey { export class RsaPubKey {
constructor(p, q) { constructor(n, e) {
this.n = p * q; this.n = n;
this.e = 65537n; this.e = e;
} }
encrypt(m) { encrypt(m) {
return mod_exp(m, this.e, this.n); return mod_exp(m, this.e, this.n);
} }
toJSON() {
return {
n: "0x" + this.n.toString(16),
e: "0x" + this.e.toString(16),
};
} }
class PrivKey { static fromJSON(data) {
return new RsaPubKey(BigInt(data.n), BigInt(data.e));
}
}
class RsaPrivKey {
constructor(p, q) { constructor(p, q) {
this.n = p * q; this.n = p * q;
this.d = mod_inv(65537n, (q - 1n) * (p - 1n)); 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")); q = BigInt(window.sessionStorage.getItem("rsa_q"));
} }
pubKey = new PubKey(p, q); pubKey = new RsaPubKey(p * q, 65537n);
privKey = new PrivKey(p, q); privKey = new RsaPrivKey(p, q);
return { pubKey, privKey }; return { pubKey, privKey };
} }

View File

@ -36,11 +36,11 @@ export class Game {
return Object.values(this.players).filter((p) => p.isPlaying)[0]; 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; let is_new = this.players[id] === undefined;
if (this.isWaiting()) { if (this.isWaiting()) {
this.players[id] = new Player(id, is_us); this.players[id] = new Player(id, is_us, pubkey);
if (is_us) { if (is_us) {
this.us = this.players[id]; this.us = this.players[id];
} }

View File

@ -26,14 +26,29 @@ document.addEventListener("DOMContentLoaded", () => {
socket.on("connect", () => { socket.on("connect", () => {
window.console.log("Connected!"); window.console.log("Connected!");
window.console.log(`We are: ${ID}`); window.console.log(`We are: ${ID}`);
socket.emit("message", Packet.createAnnounce()); socket.emit("message", Packet.createAnnounce(window.rsa));
game.addPlayer(ID, true); 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); 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) { switch (data.type) {
case "RANDOM": case "RANDOM":
@ -67,11 +82,11 @@ document.addEventListener("ANNOUNCE", (ev) => {
const data = ev.detail; const data = ev.detail;
if (data.author === ID) return; 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. // When a new player is seen, all announce to ensure they know all players.
if (is_new) { if (is_new) {
socket.emit("message", Packet.createAnnounce()); socket.emit("message", Packet.createAnnounce(window.rsa));
} }
}); });

View File

@ -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 { 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() { static createDisconnect() {
return this._createBase("DISCONNECT"); return this._sign(this._createBase("DISCONNECT"));
} }
static createKeepAlive() { static createKeepAlive() {
return this._createBase("KEEPALIVE"); return this._sign(this._createBase("KEEPALIVE"));
} }
static createSetReady(nowReady) { static createSetReady(nowReady) {
return { return this._sign({
...this._createBase("ACT"), ...this._createBase("ACT"),
action: "READY", action: "READY",
ready: nowReady, ready: nowReady,
}; });
} }
static createBarrierSignal() { static createBarrierSignal() {
return this._createBase("BARRIER"); return this._sign(this._createBase("BARRIER"));
} }
static createRegionClaim(region) { static createRegionClaim(region) {
@ -43,27 +64,27 @@ export class Packet {
} }
static createAction(action, startRegion, endRegion, amount) { static createAction(action, startRegion, endRegion, amount) {
return { return this._sign({
...this._createBase("ACT"), ...this._createBase("ACT"),
startRegion: startRegion, startRegion: startRegion,
endRegion: endRegion, endRegion: endRegion,
strength: amount, strength: amount,
action: action, action: action,
}; });
} }
static createDefense(amount) { static createDefense(amount) {
return { return this._sign({
...this._createBase("ACT"), ...this._createBase("ACT"),
action: "DEFENSE", action: "DEFENSE",
amount: amount, amount: amount,
}; });
} }
static createEndTurn() { static createEndTurn() {
return { return this._sign({
...this._createBase("ACT"), ...this._createBase("ACT"),
action: "END", action: "END",
}; });
} }
} }

View File

@ -1,5 +1,6 @@
import { Packet } from "./packet.js"; import { Packet } from "./packet.js";
import { socket } from "./main.js"; import { socket } from "./main.js";
import { RsaPubKey } from "../crypto/rsa.js";
// Timeout to consider a player disconnected // Timeout to consider a player disconnected
const TIMEOUT = 30_000; const TIMEOUT = 30_000;
@ -11,10 +12,11 @@ const PHASE_FORTIFY = 3;
let totalDice = 0; let totalDice = 0;
export class Player { export class Player {
constructor(id, local) { constructor(id, local, pubkey) {
this.timeout = null; this.timeout = null;
this.id = id; this.id = id;
this.ready = false; this.ready = false;
this.rsaPubKey = RsaPubKey.fromJSON(pubkey);
// Data which is reset every turn // Data which is reset every turn
this.isPlaying = false; this.isPlaying = false;