Add bcdg range proof
This commit is contained in:
@ -30,6 +30,10 @@ function RSTransform(g, a, p) {
|
||||
|
||||
class Ciphertext {
|
||||
constructor(key, plainText, r, set) {
|
||||
while (plainText < 0n) {
|
||||
plainText += key.n2;
|
||||
}
|
||||
|
||||
if (set !== undefined) {
|
||||
this.pubKey = key;
|
||||
this.plainText = plainText;
|
||||
@ -76,10 +80,6 @@ class Ciphertext {
|
||||
this.pubKey = key;
|
||||
this.plainText = plainText;
|
||||
|
||||
while (this.plainText < 0n) {
|
||||
this.plainText += key.n2;
|
||||
}
|
||||
|
||||
this.readOnly = false;
|
||||
}
|
||||
|
||||
@ -183,6 +183,10 @@ window.Ciphertext = Ciphertext;
|
||||
|
||||
export class ReadOnlyCiphertext {
|
||||
constructor(key, cipherText) {
|
||||
if (typeof cipherText !== "bigint") {
|
||||
throw "ReadOnlyCiphertext must take BigInt parameter";
|
||||
}
|
||||
|
||||
this.cipherText = cipherText;
|
||||
this.pubKey = key;
|
||||
|
||||
@ -291,13 +295,20 @@ export class PaillierPubKey {
|
||||
this._h_cache = [];
|
||||
this._hn_cache = [];
|
||||
|
||||
for (let i = 0n; i < BigInt(KEY_SIZE); i++) {
|
||||
this._h_cache.push(mod_exp(this.h, 2n ** i, this.n));
|
||||
this._hn_cache.push(mod_exp(this.hn, 2n ** i, this.n2));
|
||||
// Browser dies on higher key sizes :P
|
||||
if (KEY_SIZE <= 1024) {
|
||||
for (let i = 0n; i < BigInt(KEY_SIZE); i++) {
|
||||
this._h_cache.push(mod_exp(this.h, 2n ** i, this.n));
|
||||
this._hn_cache.push(mod_exp(this.hn, 2n ** i, this.n2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h_exp(b) {
|
||||
if (KEY_SIZE > 1024) {
|
||||
return mod_exp(this.h, b, this.n);
|
||||
}
|
||||
|
||||
let ctr = 1n;
|
||||
let i = 0;
|
||||
while (b !== 0n) {
|
||||
@ -313,6 +324,10 @@ export class PaillierPubKey {
|
||||
}
|
||||
|
||||
hn_exp(b) {
|
||||
if (KEY_SIZE > 1024) {
|
||||
return mod_exp(this.hn, b, this.n2);
|
||||
}
|
||||
|
||||
let ctr = 1n;
|
||||
let i = 0;
|
||||
while (b !== 0n) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { mod_exp } from "./math.js";
|
||||
|
||||
export const KEY_SIZE = 512;
|
||||
export const KEY_SIZE = 2048;
|
||||
|
||||
export function cryptoRandom(bits) {
|
||||
if (bits === undefined) {
|
||||
@ -100,7 +100,8 @@ export function generate_prime() {
|
||||
export function generate_safe_prime() {
|
||||
while (true) {
|
||||
let n = generate_prime();
|
||||
if (small_prime_test((n - 1n) / 2n) && miller_rabin((n - 1n) / 2n, 40)) {
|
||||
// This does not generate safe primes! But it takes forever to find safe primes of size 1024, so this will do.
|
||||
if (small_prime_test((n - 1n) / 2n) /* && miller_rabin((n - 1n) / 2n, 40) */) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
import { cryptoRandom } from "../crypto/random_primes.js";
|
||||
|
||||
function cryptoRange(upper) {
|
||||
// This is ridiculous: why implement a BigInt primitive, have it behave like a number, and then _not_ offer
|
||||
// mathematical operations like ilog2? Thankfully JavaScript is, for some reason, relatively fast at processing
|
||||
// strings (that is relative to it processing anything else)
|
||||
//
|
||||
// Actual comment: subtract 1 is important as otherwise this overestimates the logarithm for powers of two.
|
||||
let bitLength = BigInt((upper - 1n).toString(2).length + 1);
|
||||
let mask = 2n ** bitLength - 1n;
|
||||
|
||||
let r = cryptoRandom(1024);
|
||||
|
||||
while ((r & mask) >= BigInt(upper)) {
|
||||
r >>= bitLength;
|
||||
}
|
||||
|
||||
return r & mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSPRNG Fisher-Yates shuffle.
|
||||
*
|
||||
@ -164,4 +184,118 @@ 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)
|
||||
|
||||
function proveRange() {}
|
||||
export function proveRange(cipherText, rangeUpper) {
|
||||
if (cipherText.readOnly) {
|
||||
throw "Cannot prove range of ReadOnlyCiphertext";
|
||||
}
|
||||
|
||||
let key = cipherText.pubKey;
|
||||
|
||||
let proofs = [];
|
||||
|
||||
// Construct \omega_1, \omega_2, ciphertexts
|
||||
for (let i = 0; i < ROUNDS; i++) {
|
||||
let o1 = cryptoRange(rangeUpper);
|
||||
let o2 = o1 - rangeUpper;
|
||||
|
||||
let cs = cryptoShuffle([key.encrypt(o1), key.encrypt(o2)]);
|
||||
|
||||
proofs.push({
|
||||
cs: cs,
|
||||
});
|
||||
}
|
||||
|
||||
let coins = getCoins(JSON.stringify(proofs));
|
||||
let verifications = [];
|
||||
|
||||
for (let i = 0; i < ROUNDS; i++) {
|
||||
let coin = coins[i];
|
||||
let proof = proofs[i];
|
||||
|
||||
if (coin === 1) {
|
||||
// Prove that ciphertexts are valid
|
||||
verifications.push({
|
||||
c1: proof.cs[0].proveNI(),
|
||||
c2: proof.cs[1].proveNI(),
|
||||
});
|
||||
} else {
|
||||
// Prove that one of the sums is valid
|
||||
if ((cipherText.plainText + proof.cs[0].plainText) % key.n2 <= rangeUpper) {
|
||||
let ct = cipherText.clone();
|
||||
ct.update(proof.cs[0]);
|
||||
|
||||
verifications.push({
|
||||
csIndex: 0,
|
||||
proof: ct.proveNI(),
|
||||
});
|
||||
} else {
|
||||
let ct = cipherText.clone();
|
||||
ct.update(proof.cs[1]);
|
||||
|
||||
verifications.push({
|
||||
csIndex: 1,
|
||||
proof: ct.proveNI(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cipherText: cipherText,
|
||||
rangeUpper: "0x" + rangeUpper.toString(16),
|
||||
proofs: proofs,
|
||||
verifications: verifications,
|
||||
};
|
||||
}
|
||||
|
||||
window.proveRange = proveRange;
|
||||
|
||||
export function verifyRange(obj, key) {
|
||||
let coins = getCoins(JSON.stringify(obj.proofs));
|
||||
let rangeUpper = BigInt(obj.rangeUpper);
|
||||
|
||||
for (let i = 0; i < ROUNDS; i++) {
|
||||
let coin = coins[i];
|
||||
let proof = obj.proofs[i];
|
||||
let verification = obj.verifications[i];
|
||||
|
||||
if (coin === 1) {
|
||||
let c1 = new ReadOnlyCiphertext(key, BigInt(proof.cs[0]));
|
||||
let c2 = new ReadOnlyCiphertext(key, BigInt(proof.cs[1]));
|
||||
let o1 = c1.verifyNI(verification.c1);
|
||||
let o2 = c2.verifyNI(verification.c2);
|
||||
let diff;
|
||||
|
||||
if (o1 < o2) {
|
||||
o2 -= key.n2;
|
||||
diff = o1 - o2;
|
||||
} else {
|
||||
o1 -= key.n2;
|
||||
diff = o2 - o1;
|
||||
}
|
||||
|
||||
if (diff !== rangeUpper) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
let c = new ReadOnlyCiphertext(key, BigInt(proof.cs[verification.csIndex]));
|
||||
c.update(new ReadOnlyCiphertext(key, BigInt(obj.cipherText)));
|
||||
let value = c.verifyNI(verification.proof);
|
||||
|
||||
if (value === null || value > rangeUpper) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
window.verifyRange = verifyRange;
|
||||
|
||||
/**
|
||||
* - We prove that the set contains |S| - 2 zeros, with the final pair summing to zero
|
||||
* - We also attach some form of adjacency guarantee: that is, we prove the sums on all adjacent pairs are zero
|
||||
* - We also attach a range proof for the new region values
|
||||
*/
|
||||
export function proveFortify() {}
|
||||
|
Reference in New Issue
Block a user