This commit is contained in:
2023-04-10 19:05:10 +01:00
parent 8c5b6dbac9
commit 1647615595
8 changed files with 154 additions and 34 deletions

View File

@ -16,11 +16,11 @@ class Ciphertext {
let gm = (1n + key.n * plainText) % key.n2;
// Compute g^m r^n from crt.
this.cyphertext = (gm * mod_exp(r, key.n, key.n2)) % key.n2;
this.cipherText = (gm * mod_exp(r, key.n, key.n2)) % key.n2;
// Force into range.
while (this.cyphertext < 0n) {
this.cyphertext += key.n2;
while (this.cipherText < 0n) {
this.cipherText += key.n2;
}
this.r = r;
@ -31,30 +31,69 @@ class Ciphertext {
}
update(c) {
this.cyphertext = (this.cyphertext * c.cyphertext) % this.pubKey.n2;
this.cipherText = (this.cipherText * c.cipherText) % this.pubKey.n2;
this.r = (this.r * c.r) % this.pubKey.n2;
this.plainText += c.plainText;
// Force into range
while (this.cyphertext < 0n) {
this.cyphertext += this.pubKey.n2;
while (this.cipherText < 0n) {
this.cipherText += this.pubKey.n2;
}
}
toString() {
return "0x" + this.cyphertext.toString(16);
return "0x" + this.cipherText.toString(16);
}
prove() {
return new ZeroProofSessionProver(this);
return new ValueProofSessionProver(this);
}
asReadOnlyCyphertext() {
return new ReadOnlyCiphertext(this.pubKey, this.cyphertext);
// Construct a non-interactive proof
proveNI() {
let rp = cryptoRandom(4096);
while (rp >= this.pubKey.n) {
rp = cryptoRandom(4096);
}
let a = mod_exp(rp, this.pubKey.n, this.pubKey.n2);
let hasher = new jsSHA("SHAKE256", "HEX");
let plainText = this.plainText.toString(16);
if (plainText.length % 2 !== 0) {
plainText = "0" + plainText;
}
let aStr = a.toString(16);
if (aStr.length % 2 !== 0) {
aStr = "0" + aStr;
}
hasher.update(this.pubKey.g.toString(16));
hasher.update(plainText);
hasher.update(aStr);
let challenge = BigInt("0x" + hasher.getHash("HEX", { outputLen: 2048 }));
return {
plainText: "0x" + this.plainText.toString(16),
a: "0x" + a.toString(16),
challenge: "0x" + challenge.toString(16),
proof:
"0x" +
(
((rp % this.pubKey.n) * mod_exp(this.r, challenge, this.pubKey.n)) %
this.pubKey.n
).toString(16),
};
}
asReadOnlyCiphertext() {
return new ReadOnlyCiphertext(this.pubKey, this.cipherText);
}
}
class ZeroProofSessionProver {
class ValueProofSessionProver {
constructor(cipherText) {
this.cipherText = cipherText;
@ -82,46 +121,63 @@ class ZeroProofSessionProver {
asVerifier() {
return this.cipherText
.asReadOnlyCyphertext()
.asReadOnlyCiphertext()
.prove(this.cipherText.plainText, this.noise());
}
}
window.Cyphertext = Ciphertext;
window.Ciphertext = Ciphertext;
export class ReadOnlyCiphertext {
constructor(key, cyphertext) {
this.cyphertext = cyphertext;
constructor(key, cipherText) {
this.cipherText = cipherText;
this.pubKey = key;
this.readOnly = true;
}
update(c) {
this.cyphertext = (this.cyphertext * c.cyphertext) % this.pubKey.n2;
this.cipherText = (this.cipherText * c.cipherText) % this.pubKey.n2;
// Force into range
while (this.cyphertext < 0n) {
this.cyphertext += this.pubKey.n2;
while (this.cipherText < 0n) {
this.cipherText += this.pubKey.n2;
}
}
prove(plainText, a) {
return new ZeroProofSessionVerifier(this, plainText, a);
return new ValueProofSessionVerifier(this, plainText, a);
}
verifyNI(statement) {
let verifier = new ValueProofSessionVerifier(
this,
BigInt(statement.plainText),
BigInt(statement.a),
BigInt(statement.challenge)
);
return verifier.verify(BigInt(statement.proof));
}
clone() {
return new ReadOnlyCiphertext(this.pubKey, this.cyphertext);
return new ReadOnlyCiphertext(this.pubKey, this.cipherText);
}
}
class ZeroProofSessionVerifier {
constructor(cipherText, plainText, a) {
class ValueProofSessionVerifier {
constructor(cipherText, plainText, a, challenge) {
// Clone, otherwise the update below will mutate the original value
this.cipherText = cipherText.clone();
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;
if (challenge === undefined) {
// Shift the challenge down by 1 to ensure it is smaller than either prime factor.
this.challenge = cryptoRandom(2048) << 1n;
} else {
this.challenge = challenge;
}
this.a = a;
this.plainText = plainText;
@ -130,14 +186,14 @@ class ZeroProofSessionVerifier {
verify(proof) {
// check coprimality
if (gcd(proof, this.cipherText.pubKey.n) !== 1n) return -1;
if (gcd(this.cipherText.cyphertext, this.cipherText.pubKey.n) !== 1n) return -2;
if (gcd(this.cipherText.cipherText, this.cipherText.pubKey.n) !== 1n) return -2;
if (gcd(this.a, this.cipherText.pubKey.n) !== 1n) return -3;
// check exp
return mod_exp(proof, this.cipherText.pubKey.n, this.cipherText.pubKey.n2) ===
(this.a *
mod_exp(
this.cipherText.cyphertext,
this.cipherText.cipherText,
this.challenge,
this.cipherText.pubKey.n2
)) %
@ -147,7 +203,7 @@ class ZeroProofSessionVerifier {
}
}
window.ReadOnlyCyphertext = ReadOnlyCiphertext;
window.ReadOnlyCiphertext = ReadOnlyCiphertext;
export class PaillierPubKey {
constructor(n) {