diff --git a/static/js/modules/crypto/paillier.js b/static/js/modules/crypto/paillier.js index de2176c..dca1d08 100644 --- a/static/js/modules/crypto/paillier.js +++ b/static/js/modules/crypto/paillier.js @@ -1,14 +1,15 @@ -import { random2048, generate_prime } from "./random_primes.js"; +import { cryptoRandom, generate_prime } from "./random_primes.js"; import { mod_exp } from "./math.js"; class Cyphertext { - constructor(key, plainText) { - // Compute g^m r^n mod n^2 - let r = random2048(); + constructor(key, plainText, r) { + if (r === undefined) { + r = cryptoRandom(4096); - // Resample to avoid modulo bias. - while (r >= key.n) { - r = random2048(); + // Resample to avoid modulo bias. + while (r >= key.n) { + r = cryptoRandom(4096); + } } // Compute g^m by binomial theorem. @@ -21,13 +22,11 @@ class Cyphertext { this.plainText = plainText; this.readOnly = false; - - this.rp = null; } update(c) { - this.cyphertext *= c.cyphertext; - this.r *= c.r; + this.cyphertext = (this.cyphertext * c.cyphertext) % this.pubKey.n ** 2n; + this.r = (this.r * c.r) % this.pubKey.n ** 2n; this.plainText += c.plainText; } @@ -48,9 +47,9 @@ class ProofSessionProver { constructor(cipherText) { this.cipherText = cipherText; - this.rp = random2048(); + this.rp = cryptoRandom(4096); while (this.rp >= this.cipherText.pubKey.n) { - this.rp = random2048(); + this.rp = cryptoRandom(4096); } } @@ -59,12 +58,17 @@ class ProofSessionProver { } prove(challenge) { - return { - proof: - ((this.rp % this.cipherText.pubKey.n) * - mod_exp(this.cipherText.r, challenge, this.cipherText.pubKey.n)) % - this.cipherText.pubKey.n, - }; + return ( + ((this.rp % this.cipherText.pubKey.n) * + mod_exp(this.cipherText.r, challenge, this.cipherText.pubKey.n)) % + this.cipherText.pubKey.n + ); + } + + asVerifier() { + return this.cipherText + .asReadOnlyCyphertext() + .prove(this.cipherText.plainText, this.noise()); } } @@ -76,24 +80,23 @@ export class ReadOnlyCyphertext { this.pubKey = key; this.readOnly = true; - - this.proofPromise = null; } update(c) { - this.cyphertext *= c.cyphertext; + this.cyphertext = (this.cyphertext * c.cyphertext) % this.pubKey.n ** 2n; } - async prove() { - // request a proof - let promise = new Promise((res) => (this.proofPromise = res)); + prove(plainText, a) { + return new ProofSessionVerifier(this, plainText, a); } } class ProofSessionVerifier { - constructor(cipherText, a) { + constructor(cipherText, plainText, a) { this.cipherText = cipherText; - this.challenge = random2048(); + this.cipherText.update(this.cipherText.pubKey.encrypt(-1n * plainText, 1n)); + // Shift the challenge down by 1 to ensure it is smaller than either prime factor. + this.challenge = cryptoRandom(2048) << 1n; this.a = a; } @@ -126,8 +129,8 @@ export class PaillierPubKey { this.g = this.n + 1n; } - encrypt(m) { - return new Cyphertext(this, m); + encrypt(m, r) { + return new Cyphertext(this, m, r); } toJSON() { diff --git a/static/js/modules/crypto/paillier_proof.js b/static/js/modules/crypto/paillier_proof.js deleted file mode 100644 index 6d69872..0000000 --- a/static/js/modules/crypto/paillier_proof.js +++ /dev/null @@ -1,28 +0,0 @@ -import { random2048 } from "./random_primes.js"; -import { mod_exp } from "./math"; - -class PlaintextVerifier { - constructor(cyphertext, value, pub_key) { - this.proving = - (cyphertext * mod_exp(pub_key.g, value, pub_key.n ** 2)) % pub_key.n ** 2; - this.challenge = random2048(); - } - - verify(response) {} -} - -class PlaintextProver { - constructor(cyphertext, pub_key, priv_key) { - this.value = priv_key.decrypt(cyphertext.text); - this.mixin = random2048(); - - this.pubKey = pub_key; - } - - handleChallenge(challenge) { - return ( - (this.mixin * mod_exp(cyphertext.mixin, challenge, this.pubKey.n)) % - this.pubKey.n - ); - } -} diff --git a/static/js/modules/crypto/random_primes.js b/static/js/modules/crypto/random_primes.js index d2bbe02..8729502 100644 --- a/static/js/modules/crypto/random_primes.js +++ b/static/js/modules/crypto/random_primes.js @@ -1,7 +1,12 @@ import { mod_exp } from "./math.js"; -export function random2048() { - const byteArray = new BigUint64Array(32); +export function cryptoRandom(bits) { + if (bits === undefined) { + bits = 2048; + } + let length = bits / 64; + + const byteArray = new BigUint64Array(length); window.crypto.getRandomValues(byteArray); let intRepr = 0n; for (let int of byteArray) { @@ -18,7 +23,7 @@ export function random2048() { * We generate between 2^2047 and 2^2048 - 1 by adding differences. */ function generate_bigint() { - let intRepr = random2048(); + let intRepr = cryptoRandom(); // Drop the MSB to force into range from above intRepr >>= 1n; @@ -56,7 +61,7 @@ function miller_rabin(n, k) { } for (; k > 0; k--) { - let a = random2048(); + let a = cryptoRandom(); let x = mod_exp(a, d, n); if (x === 1n || x === n - 1n) {