Fix some stuff and sort of implement proving

This commit is contained in:
jude 2023-03-24 12:41:54 +00:00
parent 6de13d3b70
commit 07b1080b3d
7 changed files with 137 additions and 21 deletions

View File

@ -86,7 +86,7 @@ export class ReadOnlyCyphertext {
this.cyphertext = (this.cyphertext * c.cyphertext) % this.pubKey.n ** 2n; this.cyphertext = (this.cyphertext * c.cyphertext) % this.pubKey.n ** 2n;
} }
prove(plainText, a) { prove(tag, plainText, a) {
return new ProofSessionVerifier(this, plainText, a); return new ProofSessionVerifier(this, plainText, a);
} }
} }

View File

@ -157,6 +157,17 @@ document.addEventListener("ACT", async (ev) => {
} }
}); });
// todo has to filter by player
document.addEventListener("PROOF", async (ev) => {
const data = ev.detail;
if (data.stage === "CONJECTURE") {
// find the relevant entity
let region = Region.getRegion(data.region);
region.prove(data.plainText, data.noise());
}
});
document.addEventListener("endTurn", () => { document.addEventListener("endTurn", () => {
if (game.isPregame() && game.allReinforcementsPlaced()) { if (game.isPregame() && game.allReinforcementsPlaced()) {
game.incrementState(); game.incrementState();

View File

@ -1,3 +1,5 @@
import { Packet } from "./packet.js";
const REGIONS = {}; const REGIONS = {};
class Continent { class Continent {
@ -22,6 +24,71 @@ class Strength {
this.assumedStrength = null; this.assumedStrength = null;
} }
prove(region) {
if (this.cipherText.readOnly) {
return;
}
const controller = new AbortController();
let proofSessionProver = this.cipherText.prove();
document.addEventListener(
"PROOF",
(ev) => {
const data = ev.detail;
if (data.region === region && data.stage === "CHALLENGE") {
let z = proofSessionProver.prove(data.challenge);
socket.emit("message", Packet.createProof(region, z));
controller.abort();
}
},
{ signal: controller.signal }
);
socket.emit(
"message",
Packet.createProofConjecture(
region,
this.cipherText.plainText,
proofSessionProver.a
)
);
}
verify(region, plainText, a) {
if (!this.cipherText.readOnly) {
return;
}
const controller = new AbortController();
let proofSessionVerifier = this.cipherText.prove(plainText, a);
document.addEventListener(
"PROOF",
(ev) => {
const data = ev.detail;
if (data.region === region && data.stage === "PROOF") {
if (proofSessionVerifier.verify(data.z)) {
console.log("verified");
this.assumedStrength = plainText;
controller.abort();
} else {
console.warn("Failed to verify ciphertext!");
}
}
},
{ signal: controller.signal }
);
socket.emit(
"message",
Packet.createProofChallenge(region, proofSessionVerifier.challenge)
);
}
} }
export class Region { export class Region {
@ -72,13 +139,19 @@ export class Region {
if (this.owner === null) { if (this.owner === null) {
return ""; return "";
} else if (!this.strength.cipherText.readOnly) { } else if (!this.strength.cipherText.readOnly) {
return this.strength.cipherText.plainText; return this.strength.cipherText.plainText.toString();
} else if (this.strength.assumedStrength !== null) { } else if (this.strength.assumedStrength !== null) {
return `${this.strength.assumedStrength}`; return this.strength.assumedStrength.toString();
} else { } else {
return "?"; return "?";
} }
} }
prove() {}
verify(plainText, a) {
this.strength.verify(this.name, plainText, a);
}
} }
const EAST = new Continent("East"); const EAST = new Continent("East");

View File

@ -1,4 +1,4 @@
import { ID, game } from "./main.js"; import { ID } from "./main.js";
export class Packet { export class Packet {
static _createBase(name) { static _createBase(name) {
@ -85,10 +85,10 @@ export class Packet {
}); });
} }
static createReinforce(regionCyphertexts) { static createReinforce(regionCipherTexts) {
return this._sign({ return this._sign({
...this._createBase("ACT"), ...this._createBase("ACT"),
regions: regionCyphertexts, regions: regionCipherTexts,
}); });
} }
@ -116,4 +116,32 @@ export class Packet {
action: "END", action: "END",
}); });
} }
static createProofConjecture(region, plainText, a) {
return this._sign({
...this._createBase("PROOF"),
stage: "CONJECTURE",
plainText: plainText,
a: a,
region: region,
});
}
static createProofChallenge(region, challenge) {
return this._sign({
...this._createBase("PROOF"),
stage: "CHALLENGE",
challenge: challenge,
region: region,
});
}
static createProof(region, z) {
return this._sign({
...this._createBase("PROOF"),
stage: "PROOF",
z: z,
region: region,
});
}
} }

View File

@ -15,7 +15,7 @@ const PHASE_FORTIFY = 3;
let totalDice = 0; let totalDice = 0;
export class Player { export class Player {
constructor(id, local, rsa_key, paillier_key) { constructor(id, local, rsaKey, paillierKey) {
// Game state // Game state
this.totalStrength = 0; this.totalStrength = 0;
this.ready = false; this.ready = false;
@ -23,8 +23,8 @@ export class Player {
// Protocol state // Protocol state
this.timeout = null; this.timeout = null;
this.id = id; this.id = id;
this.rsaPubKey = RsaPubKey.fromJSON(rsa_key); this.rsaPubKey = RsaPubKey.fromJSON(rsaKey);
this.paillierPubKey = PaillierPubKey.fromJSON(paillier_key); this.paillierPubKey = PaillierPubKey.fromJSON(paillierKey);
this.lastPacket = 0; this.lastPacket = 0;
// Data which is reset every turn // Data which is reset every turn
@ -88,7 +88,7 @@ export class Player {
if (region.owner === null) { if (region.owner === null) {
region.claim( region.claim(
this, this,
new ReadOnlyCyphertext(this.paillierPubKey, data.cipherText) new ReadOnlyCyphertext(this.paillierPubKey, BigInt(data.cipherText))
); );
this.totalStrength += 1; this.totalStrength += 1;
@ -111,7 +111,12 @@ export class Player {
let region = Region.getRegion(regionName); let region = Region.getRegion(regionName);
if (region.owner === this) { if (region.owner === this) {
region.reinforce(BigInt(data.regions[regionName])); region.reinforce(
new ReadOnlyCyphertext(
this.paillierPubKey,
BigInt(data.regions[regionName])
)
);
} }
} }

Binary file not shown.

View File

@ -192,7 +192,7 @@ A similar issue appears in the proposed system: a cheating player could update t
\subsubsection{Additive homomorphic cryptosystems} \subsubsection{Additive homomorphic cryptosystems}
Some cryptosystems admit an additive homomorphic property: that is, given the public key and two encrypted values $\sigma_1 = E(m_1), \sigma_2 = E(m_2)$, the value $\sigma_1 + \sigma_2 = E(m_1 + m_2)$ is the cyphertext of the underlying operation. Some cryptosystems admit an additive homomorphic property: that is, given the public key and two encrypted values $\sigma_1 = E(m_1), \sigma_2 = E(m_2)$, the value $\sigma_1 + \sigma_2 = E(m_1 + m_2)$ is the ciphertext of the underlying operation.
\cite{paillier1999public} defined a cryptosystem based on residuosity classes, which expresses this property. \cite{damgaard2010generalization} demonstrates an honest-verifier zero-knowledge proof for proving a given value is 0. Hence, clearly, proving a summation $a + b = v$ can be performed by proving $v - a - b = 0$ in an additive homomorphic cryptosystem. \cite{paillier1999public} defined a cryptosystem based on residuosity classes, which expresses this property. \cite{damgaard2010generalization} demonstrates an honest-verifier zero-knowledge proof for proving a given value is 0. Hence, clearly, proving a summation $a + b = v$ can be performed by proving $v - a - b = 0$ in an additive homomorphic cryptosystem.
@ -316,7 +316,7 @@ The selection of such $g$ is ideal, as the binomial expansion property helps to
\subsection{Encryption} \subsection{Encryption}
The cyphertext is, in general, computed as $c = g^m r^n \mod n^2$ for $r < n$ some random secret value. To make this easier to compute, we compute the equivalent value $c = (r^n \mod n^2) \cdot (g^m \mod n^2) \mod n^2$. The ciphertext is, in general, computed as $c = g^m r^n \mod n^2$ for $r < n$ some random secret value. To make this easier to compute, we compute the equivalent value $c = (r^n \mod n^2) \cdot (g^m \mod n^2) \mod n^2$.
\subsection{Private key} \subsection{Private key}
@ -326,13 +326,13 @@ We are also interested in the ability to compute $\mu = \lambda^{-1} \mod n$ as
\subsection{Decryption} \subsection{Decryption}
Let $c$ be the cyphertext. The corresponding plaintext is computed as $m = L(c^\lambda \mod n^2) \cdot \mu \mod n$, where $L(x) = \frac{x - 1}{n}$. This is relatively simple to compute in JavaScript. Let $c$ be the ciphertext. The corresponding plaintext is computed as $m = L(c^\lambda \mod n^2) \cdot \mu \mod n$, where $L(x) = \frac{x - 1}{n}$. This is relatively simple to compute in JavaScript.
\subsection{Proof system} \subsection{Proof system}
The proof system is that of \cite{damgard2003}. The authors give a method to prove knowledge of the encrypted value. The importance of using a zero-knowledge method for this is that it verifies knowledge to a single party. This party should be an honest verifier: this is an assumption we have made of the context, but in general this is not true, and so this provides an attack surface for colluding parties. The proof system is that of \cite{damgard2003}. The authors give a method to prove knowledge of an encrypted value. The importance of using a zero-knowledge method for this is that it verifies knowledge to a single party. This party should be an honest verifier: this is an assumption we have made of the context, but in general this is not true, and so this provides an attack surface for colluding parties.
The proof system presented is an interactive proof for a given cyphertext $c$ being an encryption of 0. The proof system presented is an interactive proof for a given ciphertext $c$ being an encryption of 0.
\begin{center} \begin{center}
\begin{tikzpicture}[every node/.append style={very thick,rounded corners=0.1mm}] \begin{tikzpicture}[every node/.append style={very thick,rounded corners=0.1mm}]
@ -360,14 +360,13 @@ The proof system presented is an interactive proof for a given cyphertext $c$ be
\end{tikzpicture} \end{tikzpicture}
\end{center} \end{center}
Then, a proof for the following homologous problem can be trivially constructed: given some cyphertext $c = g^mr^n \mod n^2$, prove that the text $cg^{-m} \mod n^2$ is an encryption of 0. A proof for the following homologous problem can be trivially constructed: given some ciphertext $c = g^mr^n \mod n^2$, prove that the text $cg^{-m} \mod n^2$ is an encryption of 0. The text $cg^{-m}$ is constructed by the verifier. The prover then proceeds with the proof as normal, since $cg^{-m}$ is an encryption of 0 under the same noise as the encryption of $m$ given.
% Furthermore, the above protocol can be made non-interactive using the Fiat-Shamir heuristic \citep{fiatshamir}. (this contradicts the lit review) % Furthermore, the above protocol can be made non-interactive using the Fiat-Shamir heuristic \citep{fiatshamir}. (this contradicts the lit review)
\subsection{Implementation details} \subsection{Implementation details}
\subsection{Application to domain} \subsection{Application to domain}
Players should prove a number of properties of their game state to each other to ensure fair play. These are as follows. \begin{enumerate} Players should prove a number of properties of their game state to each other to ensure fair play. These are as follows. \begin{enumerate}
@ -384,7 +383,7 @@ Players should prove a number of properties of their game state to each other to
(4) and (5) can be generalised further as range proofs. (4) and (5) can be generalised further as range proofs.
For (1), we propose the following communication sequence. The player submits pairs $(R, c_R)$ for each region they control, where $R$ is the region and $c_R$ is a cyphertext encoding the number of reinforcements to add to the region (which may be 0). Each player computes $c_{R_1} \cdot \ldots \cdot c_{R_n}$. For (1), we propose the following communication sequence. The player submits pairs $(R, c_R)$ for each region they control, where $R$ is the region and $c_R$ is a ciphertext encoding the number of reinforcements to add to the region (which may be 0). Each player computes $c_{R_1} \cdot \ldots \cdot c_{R_n}$.
\subsection{Shared random values} \subsection{Shared random values}
@ -420,7 +419,7 @@ This is achieved through bit-commitment and properties of $\mathbb{Z}_n$. The pr
Depending on how $N_A + N_B$ is then turned into a random value within a range, this system may be manipulated by an attacker who has some knowledge of how participants are generating their noise. As a basic example, suppose a random value within range is generated by taking $N_A + N_B \mod 3$, and participants are producing 2-bit noises. An attacker could submit a 3-bit noise with the most-significant bit set, in which case the odds of getting a 1 are significantly higher than the odds of a 0 or a 2. To avoid this problem, peers should agree beforehand on the number of bits to transmit, and truncate any values in the final stage that exceed this limit. Depending on how $N_A + N_B$ is then turned into a random value within a range, this system may be manipulated by an attacker who has some knowledge of how participants are generating their noise. As a basic example, suppose a random value within range is generated by taking $N_A + N_B \mod 3$, and participants are producing 2-bit noises. An attacker could submit a 3-bit noise with the most-significant bit set, in which case the odds of getting a 1 are significantly higher than the odds of a 0 or a 2. To avoid this problem, peers should agree beforehand on the number of bits to transmit, and truncate any values in the final stage that exceed this limit.
The encryption function used must also guarantee the integrity of decrypted cyphertexts to prevent a malicious party creating a cyphertext which decrypts to multiple valid values through using different keys. The encryption function used must also guarantee the integrity of decrypted ciphertexts to prevent a malicious party creating a ciphertext which decrypts to multiple valid values through using different keys.
\begin{proposition} \begin{proposition}
The scheme shown is not manipulable by a single cheater. The scheme shown is not manipulable by a single cheater.
@ -436,7 +435,7 @@ The encryption function used must also guarantee the integrity of decrypted cyph
\subsection{Avoiding modular bias} \subsection{Avoiding modular bias}
The typical way to avoid modular bias is by resampling. To avoid excessive communication, resampling can be performed within the bit sequence by partitioning into blocks of $n$ bits and taking blocks until one falls within range. This is appropriate in the presented use case as random values need only be up to 6, so the likelihood of consuming over 63 bits of noise when resampling for a value in the range 0 to 5 is $\left(\frac{1}{4}\right)^{21} \approx 2.3 \times 10^{-13}$. The typical way to avoid modular bias is by resampling. To avoid excessive communication, resampling can be performed within the bit sequence by partitioning into blocks of $n$ bits and taking blocks until one falls within range. This is appropriate in the presented use case as random values need only be up to 6, so the probability of consuming over 63 bits of noise when resampling for a value in the range 0 to 5 is $\left(\frac{1}{4}\right)^{21} \approx 2.3 \times 10^{-13}$.
\bibliography{Dissertation} \bibliography{Dissertation}