prove and verify rounds
This commit is contained in:
parent
0f8ad2a0a8
commit
35dbf321e9
@ -52,18 +52,25 @@ class Ciphertext {
|
||||
this.pubKey = key;
|
||||
this.plainText = plainText;
|
||||
|
||||
while (this.plainText < 0n) {
|
||||
this.plainText += key.n2;
|
||||
}
|
||||
|
||||
this.readOnly = false;
|
||||
}
|
||||
|
||||
update(c) {
|
||||
this.cipherText = (this.cipherText * c.cipherText) % this.pubKey.n2;
|
||||
this.r = (this.r * c.r) % this.pubKey.n2;
|
||||
this.plainText += c.plainText;
|
||||
this.plainText = (this.plainText + c.plainText) % this.pubKey.n2;
|
||||
|
||||
// Force into range
|
||||
while (this.cipherText < 0n) {
|
||||
this.cipherText += this.pubKey.n2;
|
||||
}
|
||||
while (this.plainText < 0n) {
|
||||
this.plainText += this.pubKey.n2;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
@ -195,7 +202,11 @@ export class ReadOnlyCiphertext {
|
||||
BigInt(statement.challenge)
|
||||
);
|
||||
|
||||
return verifier.verify(BigInt(statement.proof));
|
||||
if (verifier.verify(BigInt(statement.proof))) {
|
||||
return BigInt(statement.plainText);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
clone() {
|
||||
|
@ -20,9 +20,9 @@ export function cryptoRandom(bits) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random integer of length 2048 bits.
|
||||
* Generate random integer of length N bits.
|
||||
*
|
||||
* We generate between 2^2047 and 2^2048 - 1 by adding differences.
|
||||
* We generate between 2^(N - 1) and 2^N - 1 by adding differences.
|
||||
*/
|
||||
function generate_bigint() {
|
||||
let intRepr = cryptoRandom();
|
||||
|
@ -21,31 +21,12 @@ function cryptoShuffle(l) {
|
||||
return out;
|
||||
}
|
||||
|
||||
window.cryptoShuffle = cryptoShuffle;
|
||||
|
||||
const ROUNDS = 24;
|
||||
|
||||
function proveRegions(regions) {
|
||||
// Construct prover coins
|
||||
let coins = [];
|
||||
|
||||
let regionNames = Object.keys(regions);
|
||||
for (let x = 0; x < ROUNDS; x++) {
|
||||
let psi = cryptoShuffle(structuredClone(regionNames)).join("");
|
||||
let newRegions = structuredClone(regions);
|
||||
// rearrange keys
|
||||
for (let index = 0; index < regionNames.length; index++) {
|
||||
let c = regions[psi[index]].clone();
|
||||
// re-blind
|
||||
c.update(c.pubKey.encrypt(0n));
|
||||
newRegions[regionNames[index]] = c;
|
||||
}
|
||||
coins.push(newRegions);
|
||||
}
|
||||
|
||||
function getCoins(text) {
|
||||
// Construct verifier coins
|
||||
let hasher = new jsSHA("SHA3-256", "TEXT");
|
||||
hasher.update(JSON.stringify(coins));
|
||||
hasher.update(text);
|
||||
let hash = hasher.getHash("UINT8ARRAY");
|
||||
|
||||
let verifierCoins = [];
|
||||
@ -57,14 +38,129 @@ function proveRegions(regions) {
|
||||
}
|
||||
}
|
||||
|
||||
return verifierCoins;
|
||||
}
|
||||
|
||||
window.cryptoShuffle = cryptoShuffle;
|
||||
|
||||
function proveRegions(regions) {
|
||||
// Construct prover coins
|
||||
let proofs = [];
|
||||
let privateInputs = [];
|
||||
|
||||
let regionNames = Object.keys(regions).sort();
|
||||
for (let x = 0; x < ROUNDS; x++) {
|
||||
let psi = cryptoShuffle(structuredClone(regionNames)).join("");
|
||||
let newRegions = structuredClone(regions);
|
||||
|
||||
// Rearrange keys
|
||||
for (let index = 0; index < regionNames.length; index++) {
|
||||
newRegions[regionNames[index]] = regions[psi[index]].pubKey.encrypt(
|
||||
regions[psi[index]].plainText * -1n
|
||||
);
|
||||
}
|
||||
|
||||
proofs.push(newRegions);
|
||||
privateInputs.push(psi);
|
||||
}
|
||||
|
||||
let verifierCoins = getCoins(JSON.stringify(proofs));
|
||||
|
||||
let verifications = [];
|
||||
|
||||
// Construct prover proofs
|
||||
for (let coin of verifierCoins) {
|
||||
for (let i = 0; i < ROUNDS; i++) {
|
||||
let coin = verifierCoins[i];
|
||||
let proof = proofs[i];
|
||||
let privateInput = privateInputs[i];
|
||||
|
||||
if (coin === 1) {
|
||||
// Reveal bijection and proof for zero
|
||||
let zeroProofs = {};
|
||||
|
||||
for (let i = 0; i < regionNames.length; i++) {
|
||||
let name = regionNames[i];
|
||||
let psiName = privateInput[i];
|
||||
|
||||
let c = proof[name].clone();
|
||||
c.update(regions[psiName]);
|
||||
zeroProofs[name] = c.proveNI();
|
||||
}
|
||||
|
||||
let ver = {
|
||||
psi: privateInput,
|
||||
zeroProofs: zeroProofs,
|
||||
};
|
||||
verifications.push(ver);
|
||||
} else {
|
||||
// Reveal proof for plaintext
|
||||
let valueProofs = {};
|
||||
|
||||
for (let name of regionNames) {
|
||||
valueProofs[name] = proof[name].proveNI();
|
||||
}
|
||||
|
||||
verifications.push({ valueProofs: valueProofs });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
regions: regions,
|
||||
proofs: proofs,
|
||||
verifications: verifications,
|
||||
};
|
||||
}
|
||||
|
||||
window.proveRegions = proveRegions;
|
||||
|
||||
export function verifyRegions(obj, key) {
|
||||
let verifierCoins = getCoins(JSON.stringify(obj.proofs));
|
||||
let regions = obj.regions;
|
||||
let regionNames = Object.keys(regions).sort();
|
||||
|
||||
for (let i = 0; i < ROUNDS; i++) {
|
||||
let proof = obj.proofs[i];
|
||||
let verification = obj.verifications[i];
|
||||
|
||||
if (verifierCoins[i] === 1) {
|
||||
for (let regionName of regionNames) {
|
||||
// Undo psi
|
||||
let originalRegion = proof[regionName];
|
||||
|
||||
// Compute product
|
||||
let c = new ReadOnlyCiphertext(key, BigInt(regions[regionName]));
|
||||
c.update(new ReadOnlyCiphertext(key, BigInt(originalRegion)));
|
||||
|
||||
// Check ciphertext is zero
|
||||
let plaintext = c.verifyNI(verification.zeroProofs[regionName]);
|
||||
|
||||
if (plaintext !== 0n) {
|
||||
console.log(plaintext);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let foundOne = false;
|
||||
|
||||
for (let name of Object.keys(verification.valueProofs)) {
|
||||
let ciphertext = new ReadOnlyCiphertext(key, BigInt(proof[name]));
|
||||
let plaintext = ciphertext.verifyNI(verification.valueProofs[name]);
|
||||
if (plaintext === null) {
|
||||
return false;
|
||||
} else if (plaintext === 1n) {
|
||||
if (foundOne) {
|
||||
return false;
|
||||
} else {
|
||||
foundOne = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
window.verifyRegions = verifyRegions;
|
||||
|
||||
// verifyRegions(proveRegions({A:paillier.pubKey.encrypt(0n),B:paillier.pubKey.encrypt(1n),C:paillier.pubKey.encrypt(0n),D:paillier.pubKey.encrypt(0n),E:paillier.pubKey.encrypt(0n)}), paillier.pubKey)
|
||||
|
Binary file not shown.
@ -566,7 +566,7 @@ Timing results versus RSA are backed experimentally by my implementation. The fo
|
||||
console.log(performance.measure("duration", "start", "end").duration)
|
||||
\end{minted}
|
||||
|
||||
Performing 250 Paillier encrypts required 48,800ms. On the other hand, performing 250 RSA encrypts required just 60ms.
|
||||
Performing 250 Paillier encrypts required 47,000ms. On the other hand, performing 250 RSA encrypts required just 40ms.
|
||||
|
||||
The speed of decryption is considerably less important in this circumstance, as Paillier ciphertexts are not decrypted during the execution of the program.
|
||||
|
||||
@ -580,7 +580,9 @@ Taking this idea further, one may simply cache $r^n$ for a number of randomly ge
|
||||
|
||||
\textbf{Alternative Paillier scheme.} \cite{Jurik2003ExtensionsTT} presents an optimised encryption scheme based on the subgroup of elements with Jacobi symbol $+1$. This forms a group as the Jacobi symbol is multiplicative, being a generalisation of the Legendre symbol.
|
||||
|
||||
I used this scheme to reduce the time to encrypt to half. Greater optimisations are possible through pre-computation of fixed-base exponentials, but this takes a considerable amount of time, and I found it infeasible within my implementation, since keypairs are only used for a single session.
|
||||
Using this scheme alone reduced the time to encrypt by a half. Greater optimisations are possible through pre-computation of fixed-base exponentials, but this takes a considerable amount of time, and I found it infeasible within my implementation, since keypairs are only used for a single session.
|
||||
|
||||
Furthermore, in practice gains were closer to a reduction by a third, since in the modified scheme additional computation must be performed to attain the $r$ that would work with normal Paillier, in order to perform the zero-knowledge proofs from before.
|
||||
|
||||
\textbf{Smaller key size.} The complexity of Paillier encryption increases with key size. Using a smaller key could considerably reduce the time taken \cite{paillier1999public}.
|
||||
|
||||
@ -599,12 +601,12 @@ The other proofs do not translate so trivially to this structure however. In fac
|
||||
All measurements taken on Brave 1.50.114 (Chromium 112.0.5615.49) 64-bit, using a Ryzen 5 3600 CPU.
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{|c|c|c|}
|
||||
\begin{tabular}{|c|c|c|c|}
|
||||
\hline
|
||||
Modulus size & Na\"ive encrypt & Jacobi encrypt \\\hline
|
||||
$n = 1024$ & cell5 & 4ms \\
|
||||
$n = 2048$ & cell8 & 22ms \\
|
||||
$n = 4096$ & cell8 & 128ms \\
|
||||
Modulus size & Na\"ive encrypt & Jacobi encrypt & RSA encrypt \\\hline
|
||||
$|n| = 1024$ & 6ms & 4ms & <1ms \\
|
||||
$|n| = 2048$ & 34ms & 22ms & <1ms \\
|
||||
$|n| = 4096$ & 189ms & 128ms & <1ms \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
Loading…
Reference in New Issue
Block a user