paillier integration sort of

This commit is contained in:
jude
2023-03-17 10:42:11 +00:00
parent a6961e1900
commit 2d72cdd87b
11 changed files with 244 additions and 92 deletions

View File

@ -135,14 +135,14 @@ document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll(".claim").forEach((el) =>
el.addEventListener("click", (ev) => {
let region = ev.target.closest(".node").dataset.name;
socket.emit("message", Packet.createRegionClaim(region));
game.us.sendClaim(region);
})
);
document.querySelectorAll(".reinforce").forEach((el) =>
el.addEventListener("click", (ev) => {
let region = ev.target.closest(".node").dataset.name;
socket.emit("message", Packet.createReinforce(region));
game.us.sendReinforce(region);
})
);
@ -231,7 +231,7 @@ function showRemainingReinforcements() {
if (game.isPregame()) {
document.querySelector(
"#remaining-reinforcements"
).innerHTML = `<span>Remaining placements: ${Region.reinforcementsRemaining()}</span>`;
).innerHTML = `<span>Remaining placements: ${game.reinforcementsRemaining()}</span>`;
} else if (game.isPlaying()) {
document.querySelector(
"#remaining-reinforcements"

View File

@ -1,5 +1,8 @@
import { Player } from "./player.js";
// In standard Risk, this is 5
const _REINFORCEMENT_MULTIPLIER = 1;
const WAITING = 0;
const PRE_GAME = 1;
const PLAYING = 2;
@ -9,6 +12,8 @@ export class Game {
this.us = null;
this.players = {};
this.state = WAITING;
this.allPlaced = false;
}
isWaiting() {
@ -91,4 +96,33 @@ export class Game {
}
return true;
}
reinforcementsRemaining() {
if (this.allPlaced) {
return 0;
} else {
return (
_REINFORCEMENT_MULTIPLIER * (10 - this.playerCount()) -
this.us.totalStrength
);
}
}
allReinforcementsPlaced() {
if (this.allPlaced) {
return true;
} else {
let totalStrength = Object.values(this.players).reduce(
(counter, player) => counter + player.totalStrength,
0
);
this.allPlaced =
totalStrength >=
this.playerCount() *
_REINFORCEMENT_MULTIPLIER *
(10 - this.playerCount());
return this.allPlaced;
}
}
}

View File

@ -121,6 +121,11 @@ document.addEventListener("ACT", async (ev) => {
if (game.isWaiting()) {
game.setReady(data.author, data.ready);
} else {
// Throw out our own packets
if (data.author === game.us) {
return;
}
if (data.author !== game.currentPlayer().id) {
if (data.action === "DEFENSE") {
await game.players[data.author].setDefense(data.amount);
@ -136,13 +141,13 @@ document.addEventListener("ACT", async (ev) => {
// Increment to next player.
game.currentPlayer().endTurn();
}
} else if (!Region.allReinforcementsPlaced()) {
} else if (!game.allReinforcementsPlaced()) {
if (game.currentPlayer().reinforce(data)) {
game.currentPlayer().endTurn();
}
}
if (Region.allReinforcementsPlaced()) {
if (game.allReinforcementsPlaced()) {
game.incrementState();
}
} else {

View File

@ -1,10 +1,3 @@
import { game } from "./main.js";
let allPlaced = false;
// In standard Risk, this is 5
const _REINFORCEMENT_MULTIPLIER = 1;
const REGIONS = {};
class Continent {
@ -15,16 +8,16 @@ class Continent {
}
class Strength {
constructor(cyphertext) {
this.cyphertext = cyphertext;
constructor(cipherText) {
this.cipherText = cipherText;
this.assumedStrength = null;
}
update(amount) {
if (this.cyphertext === null) {
this.cyphertext = amount;
update(cipherText) {
if (this.cipherText === null) {
this.cipherText = cipherText;
} else {
this.cyphertext *= amount;
this.cipherText.update(cipherText);
}
this.assumedStrength = null;
@ -53,40 +46,6 @@ export class Region {
);
}
// todo fix
static reinforcementsRemaining() {
if (allPlaced) {
return 0;
} else {
let totalStrength = Object.values(REGIONS)
.filter((region) => region.owner === game.us)
.reduce(
(counter, region) => counter + region.strength.assumedStrength,
0
);
return _REINFORCEMENT_MULTIPLIER * (10 - game.playerCount()) - totalStrength;
}
}
static allReinforcementsPlaced() {
if (allPlaced) {
return true;
} else {
let totalStrength = Object.values(REGIONS).reduce(
(counter, region) => counter + region.strength.assumedStrength,
0
);
allPlaced =
totalStrength >=
game.playerCount() *
_REINFORCEMENT_MULTIPLIER *
(10 - game.playerCount());
return allPlaced;
}
}
static getRegion(name) {
return REGIONS[name];
}
@ -95,25 +54,25 @@ export class Region {
return Object.values(REGIONS);
}
claim(player) {
claim(player, cipherText) {
this.owner = player;
this.strength.update(player.paillierPubKey.encrypt(1n));
this.strength.update(cipherText);
this.strength.assumedStrength = 1;
}
reinforce(amount) {
this.strength.update(amount);
reinforce(cipherText) {
this.strength.update(cipherText);
}
isClaimed() {
return this.strength.cyphertext !== null;
return this.strength.cipherText !== null;
}
displayStrength() {
if (this.owner === null) {
return "";
} else if (this.owner === game.us) {
return window.paillier.privKey.decrypt(this.strength.cyphertext).toString();
} else if (!this.strength.cipherText.readOnly) {
return this.strength.cipherText.plainText;
} else if (this.strength.assumedStrength !== null) {
return `${this.strength.assumedStrength}`;
} else {

View File

@ -77,29 +77,18 @@ export class Packet {
return this._sign(this._createBase("BARRIER"));
}
static createRegionClaim(region) {
static createRegionClaim(region, text) {
return this._sign({
...this._createBase("ACT"),
region: region,
cipherText: text,
});
}
static createReinforce(region) {
// todo cache some of these, possibly by pregenerating cyphertexts in a web worker
let regions = {};
for (let ourRegion of game.us.getRegions()) {
if (ourRegion.name !== region) {
regions[ourRegion.name] =
"0x" + game.us.paillierPubKey.encrypt(0n).toString(16);
} else {
regions[ourRegion.name] =
"0x" + game.us.paillierPubKey.encrypt(1n).toString(16);
}
}
static createReinforce(regionCyphertexts) {
return this._sign({
...this._createBase("ACT"),
regions: regions,
regions: regionCyphertexts,
});
}

View File

@ -1,7 +1,7 @@
import { Packet } from "./packet.js";
import { socket, game, random } from "./main.js";
import { RsaPubKey } from "../crypto/rsa.js";
import { PaillierPubKey } from "../crypto/paillier.js";
import { PaillierPubKey, ReadOnlyCyphertext } from "../crypto/paillier.js";
import { Region } from "./map.js";
import { showDefenseDom } from "./dom.js";
@ -16,9 +16,13 @@ let totalDice = 0;
export class Player {
constructor(id, local, rsa_key, paillier_key) {
// Game state
this.totalStrength = 0;
this.ready = false;
// Protocol state
this.timeout = null;
this.id = id;
this.ready = false;
this.rsaPubKey = RsaPubKey.fromJSON(rsa_key);
this.paillierPubKey = PaillierPubKey.fromJSON(paillier_key);
this.lastPacket = 0;
@ -82,7 +86,13 @@ export class Player {
let region = Region.getRegion(data.region);
if (region.owner === null) {
region.claim(this);
region.claim(
this,
new ReadOnlyCyphertext(this.paillierPubKey, data.cipherText)
);
this.totalStrength += 1;
return true;
} else {
return false;
@ -105,14 +115,48 @@ export class Player {
}
}
this.totalStrength += 1;
return true;
}
sendClaim(region) {
let cipherText = this.paillierPubKey.encrypt(1n);
Region.getRegion(region).claim(this, cipherText);
socket.emit("message", Packet.createRegionClaim(region, cipherText.toString()));
this.totalStrength += 1;
this.endTurn();
}
sendReinforce(region) {
let regions = {};
for (let ourRegion of this.getRegions()) {
let cipherText;
if (ourRegion.name !== region) {
cipherText = this.paillierPubKey.encrypt(0n);
} else {
cipherText = this.paillierPubKey.encrypt(1n);
}
regions[ourRegion.name] = cipherText.toString();
ourRegion.reinforce(cipherText);
}
socket.emit("message", Packet.createReinforce(regions));
this.totalStrength += 1;
this.endTurn();
}
/**
* Process a generic action packet representing a player's move.
*
* @param data Data received via socket
* @returns {boolean} Whether this player's turn has ended or not.
* @returns {boolean} Whether this player's turn should now end or not.
*/
async act(data) {
if (this.turnPhase === PHASE_REINFORCE) {