Sign packets
This commit is contained in:
parent
ab629a78b4
commit
96fe20503a
@ -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 };
|
||||||
|
@ -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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(data) {
|
||||||
|
return new RsaPubKey(BigInt(data.n), BigInt(data.e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrivKey {
|
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 };
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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",
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user