RSA keygen. some other stuff

This commit is contained in:
jude 2023-03-04 00:25:54 +00:00
parent cf0c9135e1
commit ed171bf77f
10 changed files with 169 additions and 99 deletions

View File

@ -1,2 +1,4 @@
import { generate_keypair } from "./paillier.js"; import { generate_keypair } from "./paillier.js";
export { generate_keypair }; import { generate_rsa_keypair } from "./rsa.js";
export { generate_keypair, generate_rsa_keypair };

View File

@ -12,3 +12,24 @@ export function mod_exp(a, b, n) {
return res; return res;
} }
export function mod_inv(a, n) {
let t = 0n;
let new_t = 1n;
let r = n;
let new_r = a;
while (new_r !== 0n) {
let quotient = r / new_r;
t = new_t;
new_t = t - quotient * new_t;
r = new_r;
new_r = r - quotient * new_r;
}
if (t < 0) {
t = t + n;
}
return t;
}

View File

@ -0,0 +1,47 @@
import { generate_prime } from "./random_primes.js";
import { mod_exp, mod_inv } from "./math.js";
let p, q, pubKey, privKey;
class PubKey {
constructor(p, q) {
this.n = p * q;
this.e = 65537;
}
decrypt(m) {
return mod_exp(m, this.e, this.n);
}
}
class PrivKey {
constructor(p, q) {
this.n = p * q;
this.d = mod_inv(65537, (q - 1) * (p - 1));
}
encrypt(c) {
return mod_exp(c, this.d, this.n);
}
}
export function generate_rsa_keypair() {
if (window.sessionStorage.getItem("rsa_p") === null) {
p = generate_prime();
window.sessionStorage.setItem("rsa_p", p);
} else {
p = BigInt(window.sessionStorage.getItem("rsa_p"));
}
if (window.sessionStorage.getItem("rsa_q") === null) {
q = generate_prime();
window.sessionStorage.setItem("rsa_q", q);
} else {
q = BigInt(window.sessionStorage.getItem("rsa_q"));
}
pubKey = new PubKey(p, q);
privKey = new PrivKey(p, q);
return { pubKey, privKey };
}

View File

@ -1,4 +1,4 @@
import { socket, players } from "./main.js"; import { socket, game } from "./main.js";
import { Packet } from "./packet.js"; import { Packet } from "./packet.js";
/** /**
@ -25,7 +25,7 @@ export class Barrier {
resolve(data) { resolve(data) {
this.hits.add(data.author); this.hits.add(data.author);
if (this.hits.size === Object.keys(players).length - 1) { if (this.hits.size === Object.keys(game.players).length - 1) {
this.hits = new Set(); this.hits = new Set();
this.resolver(); this.resolver();
} }

View File

@ -1,15 +1,4 @@
import { import { game, socket, ID } from "./main.js";
gameState,
WAITING,
PRE_GAME,
PLAYING,
us,
socket,
players,
ID,
allPlayersReady,
startPregame,
} from "./main.js";
import { Region } from "./map.js"; import { Region } from "./map.js";
import { Packet } from "./packet.js"; import { Packet } from "./packet.js";
@ -31,28 +20,18 @@ export function lockMapDom() {
document.querySelectorAll(".actions").forEach((e) => e.classList.add("hidden")); document.querySelectorAll(".actions").forEach((e) => e.classList.add("hidden"));
} }
export function updateDom() {
if (gameState !== WAITING) {
document.querySelector("#ready-button").style.display = "none";
}
updatePlayerDom();
showRemainingReinforcements();
updateMapDom();
}
function updateMapDom() { function updateMapDom() {
if (us.isPlaying) { if (game.us.isPlaying) {
unlockMapDom(); unlockMapDom();
} else { } else {
lockMapDom(); lockMapDom();
} }
if (gameState === PRE_GAME) { if (game.isPregame()) {
document.querySelectorAll(".fortify, .attack").forEach((e) => { document.querySelectorAll(".fortify, .attack").forEach((e) => {
e.classList.add("hidden"); e.classList.add("hidden");
}); });
} else if (gameState === PLAYING) { } else if (game.isPlaying()) {
document.querySelectorAll(".node button").forEach((e) => { document.querySelectorAll(".node button").forEach((e) => {
e.classList.remove("hidden"); e.classList.remove("hidden");
}); });
@ -72,16 +51,23 @@ function updateMapDom() {
} }
} }
document.addEventListener("gameStateUpdate", () => {
if (!game.isWaiting()) {
document.querySelector("#ready-button").style.display = "none";
}
});
document.addEventListener("gameStateUpdate", updateMapDom);
function updatePlayerDom() { function updatePlayerDom() {
let list = document.querySelector("#playerList"); let list = document.querySelector("#playerList");
list.replaceChildren(); list.replaceChildren();
for (let playerId of Object.keys(players).sort()) { for (let playerId of Object.keys(game.players).sort()) {
let player = players[playerId]; let player = game.players[playerId];
let statusSpan = document.createElement("div"); let statusSpan = document.createElement("div");
statusSpan.classList.add("status-span"); statusSpan.classList.add("status-span");
if (gameState === WAITING) { if (game.isWaiting()) {
if (player.ready) { if (player.ready) {
statusSpan.textContent = "R"; statusSpan.textContent = "R";
statusSpan.classList.add("ready"); statusSpan.classList.add("ready");
@ -110,21 +96,19 @@ function updatePlayerDom() {
} }
} }
document.addEventListener("addPlayer", updatePlayerDom);
document.addEventListener("removePlayer", updatePlayerDom);
document.addEventListener("updatePlayer", updatePlayerDom);
document.addEventListener("gameStateUpdate", updatePlayerDom);
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#ready-button").addEventListener("click", async (ev) => { document.querySelector("#ready-button").addEventListener("click", async (ev) => {
let nowReady = ev.target.textContent === "Not ready"; let nowReady = ev.target.textContent === "Not ready";
us.ready = nowReady;
ev.target.classList.toggle("active"); ev.target.classList.toggle("active");
ev.target.textContent = nowReady ? "Ready" : "Not ready"; ev.target.textContent = nowReady ? "Ready" : "Not ready";
socket.emit("message", Packet.createSetReady(nowReady)); socket.emit("message", Packet.createSetReady(nowReady));
updatePlayerDom();
if (allPlayersReady()) {
await startPregame();
}
}); });
document.querySelector("#end-turn").addEventListener("click", async (ev) => { document.querySelector("#end-turn").addEventListener("click", async (ev) => {
@ -212,7 +196,7 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
document.querySelector("#shuffleColors").addEventListener("click", () => { document.querySelector("#shuffleColors").addEventListener("click", () => {
Object.values(players).forEach((player) => { Object.values(game.players).forEach((player) => {
player.resetColor(); player.resetColor();
}); });
updatePlayerDom(); updatePlayerDom();
@ -220,11 +204,11 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
function showRemainingReinforcements() { function showRemainingReinforcements() {
if (gameState === PRE_GAME) { if (game.isPregame()) {
document.querySelector( document.querySelector(
"#remaining-reinforcements" "#remaining-reinforcements"
).innerHTML = `<span>Remaining placements: ${reinforcementsRemaining()}</span>`; ).innerHTML = `<span>Remaining placements: ${reinforcementsRemaining()}</span>`;
} else if (gameState === PLAYING) { } else if (game.isPlaying()) {
document.querySelector( document.querySelector(
"#remaining-reinforcements" "#remaining-reinforcements"
).innerHTML = `<span>Remaining placements: ${ ).innerHTML = `<span>Remaining placements: ${

View File

@ -1,4 +1,4 @@
import { Player } from "./player"; import { Player } from "./player.js";
const WAITING = 0; const WAITING = 0;
const PRE_GAME = 1; const PRE_GAME = 1;
@ -25,18 +25,23 @@ export class Game {
incrementState() { incrementState() {
this.state += 1; this.state += 1;
const event = new CustomEvent("gameStateUpdate", {
detail: { newState: this.state },
});
document.dispatchEvent(event);
} }
currentPlayer() { currentPlayer() {
return Object.values(this.players).filter((p) => p.isPlaying)[0]; return Object.values(this.players).filter((p) => p.isPlaying)[0];
} }
addPlayer(id, name, is_us) { addPlayer(id, is_us) {
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, name, is_us); this.players[id] = new Player(id, is_us);
if (is_us === true) { if (is_us) {
this.us = this.players[id]; this.us = this.players[id];
} }
} }
@ -64,7 +69,10 @@ export class Game {
} }
setReady(id, ready) { setReady(id, ready) {
this.players[id].readyState = ready; this.players[id].ready = ready;
const event = new CustomEvent("updatePlayer");
document.dispatchEvent(event);
if (this._allPlayersReady()) { if (this._allPlayersReady()) {
this.incrementState(); this.incrementState();
@ -73,7 +81,7 @@ export class Game {
_allPlayersReady() { _allPlayersReady() {
for (let player of Object.values(this.players)) { for (let player of Object.values(this.players)) {
if (!player.readyState) { if (!player.ready) {
return false; return false;
} }
} }

View File

@ -2,17 +2,16 @@ import { generate_keypair } from "../crypto/main.js";
import { Random } from "./random.js"; import { Random } from "./random.js";
import { Barrier } from "./barrier.js"; import { Barrier } from "./barrier.js";
import { Packet } from "./packet.js"; import { Packet } from "./packet.js";
import { updateDom } from "./dom.js"; import { Game } from "./game.js";
import "./dom.js";
export const ID = window.crypto.randomUUID(); export const ID = window.crypto.randomUUID();
export let us = null;
export const game = new Game(); export const game = new Game();
export let socket; export let socket;
let random; let random;
let barrier; let barrier;
let keys; const paillier = generate_keypair();
const rsa = generate_rsa_keypair();
// Not totally reliable but better than nothing. // Not totally reliable but better than nothing.
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
@ -23,15 +22,17 @@ document.addEventListener("DOMContentLoaded", () => {
socket = io(); socket = io();
random = new Random(); random = new Random();
barrier = new Barrier(); barrier = new Barrier();
keys = generate_keypair();
socket.on("connect", () => { socket.on("connect", () => {
window.console.log("Connected!"); window.console.log("Connected!");
window.console.log(`We are: ${ID}`);
socket.emit("message", Packet.createAnnounce()); socket.emit("message", Packet.createAnnounce());
game.addPlayer(ID, name, true); game.addPlayer(ID, true);
}); });
socket.on("message", async (data) => { socket.on("message", async (data) => {
window.console.log(data);
// todo validate signature // todo validate signature
switch (data.type) { switch (data.type) {
@ -62,10 +63,11 @@ document.addEventListener("DOMContentLoaded", () => {
* *
* @param data Packet received * @param data Packet received
*/ */
document.addEventListener("ANNOUNCE", (data) => { document.addEventListener("ANNOUNCE", (ev) => {
const data = ev.detail;
if (data.author === ID) return; if (data.author === ID) return;
let is_new = game.addPlayer(data.author, data.name, false); let is_new = game.addPlayer(data.author, false);
// 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) {
@ -73,7 +75,8 @@ document.addEventListener("ANNOUNCE", (data) => {
} }
}); });
document.addEventListener("DISCONNECT", (data) => { document.addEventListener("DISCONNECT", (ev) => {
const data = ev.detail;
game.removePlayer(data.author); game.removePlayer(data.author);
}); });
@ -82,54 +85,61 @@ document.addEventListener("DISCONNECT", (data) => {
* *
* @param data Packet received * @param data Packet received
*/ */
document.addEventListener("KEEPALIVE", (data) => { document.addEventListener("KEEPALIVE", (ev) => {
const data = ev.detail;
game.keepAlive(data.author); game.keepAlive(data.author);
}); });
document.addEventListener("ACT", async (data) => { document.addEventListener("ACT", async (ev) => {
if (data.author !== game.currentPlayer().id) { const data = ev.detail;
if (data.action === "DEFENSE") {
await game.players[data.author].setDefense(data.amount);
}
return;
}
if (game.isWaiting()) { if (game.isWaiting()) {
game.setReady(data.author, data.ready); game.setReady(data.author, data.ready);
} else if (game.isPregame()) { } else {
if (!Region.allRegionsClaimed()) { if (data.author !== game.currentPlayer().id) {
// Claim a region in the pregame. if (data.action === "DEFENSE") {
if (game.currentPlayer().claim(data)) { await game.players[data.author].setDefense(data.amount);
// Increment to next player.
game.currentPlayer().endTurn();
}
} else if (!Region.allReinforcementsPlaced()) {
if (game.currentPlayer().reinforce(data)) {
game.currentPlayer().endTurn();
} }
return;
} }
if (Region.allReinforcementsPlaced()) { if (game.isPregame()) {
game.incrementState(); if (!Region.allRegionsClaimed()) {
} // Claim a region in the pregame.
} else { if (game.currentPlayer().claim(data)) {
if (await game.currentPlayer().act(data)) { // Increment to next player.
game.currentPlayer().endTurn(); game.currentPlayer().endTurn();
}
} else if (!Region.allReinforcementsPlaced()) {
if (game.currentPlayer().reinforce(data)) {
game.currentPlayer().endTurn();
}
}
if (Region.allReinforcementsPlaced()) {
game.incrementState();
}
} else {
if (await game.currentPlayer().act(data)) {
game.currentPlayer().endTurn();
}
} }
} }
}); });
export async function startPregame() { document.addEventListener("gameStateUpdate", async () => {
gameState = PRE_GAME; if (game.isPregame()) {
let firstPlayerIndex = await random.get(
Object.keys(game.players).length,
"first-player"
);
let firstPlayerIndex = await random.get(Object.keys(players).length, "first-player"); let firstPlayer = Object.values(game.players).sort((a, b) =>
a.id < b.id ? -1 : 1
)[firstPlayerIndex];
let firstPlayer = Object.values(players).sort((a, b) => (a.id < b.id ? -1 : 1))[ firstPlayer.isPlaying = true;
firstPlayerIndex await barrier.wait();
]; }
});
firstPlayer.isPlaying = true;
await barrier.wait();
updateDom();
}

View File

@ -12,7 +12,6 @@ export class Packet {
static createAnnounce() { static createAnnounce() {
return { return {
...this._createBase("ANNOUNCE"), ...this._createBase("ANNOUNCE"),
name: "",
}; };
} }

View File

@ -1,6 +1,5 @@
import { Packet } from "./packet.js"; import { Packet } from "./packet.js";
import { socket } from "./main.js"; import { socket } from "./main.js";
import { updateDom } from "./dom.js";
// Timeout to consider a player disconnected // Timeout to consider a player disconnected
const TIMEOUT = 30_000; const TIMEOUT = 30_000;
@ -34,7 +33,7 @@ export class Player {
// Emit keepalive messages to inform other players we are still here // Emit keepalive messages to inform other players we are still here
window.setInterval(() => { window.setInterval(() => {
socket.emit("message", Packet.createKeepAlive()); socket.emit("message", Packet.createKeepAlive());
}, TIMEOUT / 5); }, TIMEOUT / 2);
} }
} }

View File

@ -1,4 +1,4 @@
import { socket, ID, players } from "./main.js"; import { socket, ID, game } from "./main.js";
class RandomSession { class RandomSession {
constructor(range) { constructor(range) {
@ -87,7 +87,7 @@ export class Random {
if ( if (
Object.keys(session.cipherTexts).length === Object.keys(session.cipherTexts).length ===
Object.keys(players).length - 1 Object.keys(game.players).length - 1
) { ) {
// Step 3: release our key once all players have sent a ciphertext // Step 3: release our key once all players have sent a ciphertext
socket.emit("message", { socket.emit("message", {
@ -104,7 +104,7 @@ export class Random {
// Step 4: get final random value // Step 4: get final random value
if ( if (
Object.keys(session.cipherKeys).length === Object.keys(session.cipherKeys).length ===
Object.keys(players).length - 1 Object.keys(game.players).length - 1
) { ) {
// Lock out wait calls as they may resolve to never-ending promises. // Lock out wait calls as they may resolve to never-ending promises.
await navigator.locks.request(`random-${data.session}`, () => { await navigator.locks.request(`random-${data.session}`, () => {