rewriting mixing proofs

mixer
Tal Moran 2017-01-11 17:01:14 +02:00
parent aac7a50a94
commit b7ef2c10e1
9 changed files with 484 additions and 341 deletions

View File

@ -11,43 +11,37 @@ message Plaintext {
bytes data = 1;
}
message ZeroKnowledgeProof {
message OrProof {
message Mix2Proof {
// Proof that log_g(a) = log_h(b) = x
message DlogProof {
message FirstMessage {
GroupElement g1 = 1;
GroupElement h1 = 2;
GroupElement g2 = 3;
GroupElement h2 = 4;
GroupElement g1Tag = 5;
GroupElement h1Tag = 6;
GroupElement g2Tag = 7;
GroupElement h2Tag = 8;
GroupElement u = 9;
GroupElement v = 10;
GroupElement uTag = 11;
GroupElement vTag = 12;
GroupElement gr = 1; // g^r
GroupElement hr = 2; // h^r
}
//statement parameters : g1,h1, g2, h2, g1Tag, h1Tag, g2Tag, h2Tag;
GroupElement g1 = 1;
GroupElement h1 = 2;
GroupElement g2 = 3;
GroupElement h2 = 4;
GroupElement g1Tag = 5;
GroupElement h1Tag = 6;
GroupElement g2Tag = 7;
GroupElement h2Tag = 8;
message FinalMessage {
BigInteger xcr = 1; // xc+r, where c is the challenge
}
}
//calc: u, v, uTag, vTag;
GroupElement u = 9;
GroupElement v = 10;
GroupElement uTag = 11;
GroupElement vTag = 12;
message AndProof {
message FirstMessage {
DlogProof.FirstMessage clause0 = 1;
DlogProof.FirstMessage clause1 = 2;
}
message FinalMessage {
DlogProof.FinalMessage clause0 = 1;
DlogProof.FinalMessage clause1 = 2;
}
}
//generated: c1,c2,z,zTag
BigInteger c1 = 13;
BigInteger c2 = 14;
BigInteger z = 15;
BigInteger zTag = 16;
message FirstMessage {
AndProof.FirstMessage clause0 = 1;
AndProof.FirstMessage clause1 = 2;
}
message FinalMessage {
AndProof.FinalMessage clause0 = 1;
AndProof.FinalMessage clause1 = 2;
BigInteger c0 = 3; // Challenge for clause 0; challenge for clause 1 is computed from real challenge and c0
}
message Location{
@ -56,9 +50,7 @@ message ZeroKnowledgeProof {
int32 layer = 3;
}
OrProof first = 1;
OrProof second = 2;
OrProof third = 3;
OrProof fourth = 4;
FirstMessage firstMessage = 1;
FinalMessage finalMessage = 2;
Location location = 5;
}

View File

@ -5,6 +5,7 @@ import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.protobuf.ConcreteCrypto.GroupElement;
import meerkat.protobuf.Crypto;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
@ -66,8 +67,8 @@ public class ECElGamalUtils {
public static <T extends Message> T decrypt(Class<T> plaintextMessageType, ECElGamal.SK secretKey, ECGroup group, Crypto.RerandomizableEncryptedMessage opaqueCipher)
throws InvalidProtocolBufferException {
ConcreteCrypto.ElGamalCiphertext cipherText = ConcreteCrypto.ElGamalCiphertext.parseFrom(opaqueCipher.getData());
ByteString c1encoded = cipherText.getC1();
ByteString c2encoded = cipherText.getC2();
GroupElement c1encoded = cipherText.getC1();
GroupElement c2encoded = cipherText.getC2();
ECPoint c1 = group.decode(c1encoded.toByteArray());
ECPoint c2 = group.decode(c2encoded.toByteArray());

View File

@ -1,28 +1,31 @@
package meerkat.mixer.proofs;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.protobuf.ConcreteCrypto.ElGamalCiphertext;
import meerkat.protobuf.ConcreteCrypto.GroupElement;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Crypto.EncryptionRandomness;
import org.bouncycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
/**
* use for organize the input for each ZKP
* used for organizing the input for each ZKP
*
* both meerkat.mixer.proofs and meerkat.mixer.verifier implementation use it
*/
public class ECElGamalMixParams {
private final ECElGamalEncryption encryptor;
private final ECGroup group;
private final ECPoint g;
private final ECPoint h;
final ECElGamalEncryption encryptor;
final ECGroup group;
final ECPoint g;
final ECPoint h;
// Cache encoded formats to save multiple re-encodings
private final ConcreteCrypto.GroupElement gEncoded;
private final ConcreteCrypto.GroupElement hEncoded;
private final GroupElement gEncoded;
private final GroupElement hEncoded;
/**
* @param encryptor encryptor used for encoding/decoding serialized ciphertexts. The group, default generator and
@ -45,297 +48,156 @@ public class ECElGamalMixParams {
/**
* can be used by anyone, e.g meerkat.mixer.verifier
*
* call to the meerkat.mixer.main overload with flag = false
* call to the meerkat.mixer.main overload with trueClauseIndex = false
*/
public Statement createStatement(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2
, Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2) throws InvalidProtocolBufferException {
public Mix2Statement createStatement(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2,
Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2) throws InvalidProtocolBufferException {
ConcreteCrypto.ElGamalCiphertext in1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in1);
ConcreteCrypto.ElGamalCiphertext in2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in2);
ConcreteCrypto.ElGamalCiphertext out1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out1);
ConcreteCrypto.ElGamalCiphertext out2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out2);
return new Statement(in1ElGamal, in2ElGamal, out1ElGamal, out2ElGamal);
ElGamalCiphertext in1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in1);
ElGamalCiphertext in2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in2);
ElGamalCiphertext out1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out1);
ElGamalCiphertext out2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out2);
return new Mix2Statement(in1ElGamal, in2ElGamal, out1ElGamal, out2ElGamal);
}
/**
* convert each encrypted message to ElGamalCiphertext
*
* @throws InvalidProtocolBufferException - in case that at least one of the encrypted messages isn't
* @throws InvalidProtocolBufferException - in case one or more of the encrypted messages isn't
* ElGamalCiphertext
*/
protected ProverStatement createProverStatement(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2
, Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2
, Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched)
throws InvalidProtocolBufferException {
protected Mix2StatementWitness createMix2Witness(EncryptionRandomness r1, EncryptionRandomness r2, boolean switched) {
// if (!switched), dlogW1 is witness for a ~ c, dlogW2 is witness for b ~ d,
// otherwise, dlogW1 is witness for a ~ d, dlogW2 is witness for b ~ c
DlogStatementWitness dlogW1 = new DlogStatementWitness(encryptor.extractRandomness(r1));
DlogStatementWitness dlogW2 = new DlogStatementWitness(encryptor.extractRandomness(r2));
//convert RerandomizableEncryptedMessage to ElGamalCiphertext
ConcreteCrypto.ElGamalCiphertext in1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in1);
ConcreteCrypto.ElGamalCiphertext in2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in2);
ConcreteCrypto.ElGamalCiphertext out1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out1);
ConcreteCrypto.ElGamalCiphertext out2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out2);
AndStatementWitness andW = new AndStatementWitness(dlogW1, dlogW2);
return new ProverStatement(in1ElGamal, in2ElGamal, out1ElGamal, out2ElGamal, r1, r2, switched);
return new Mix2StatementWitness(switched ? 1 : 0, andW);
}
/**
* Statement to be proved.
* Mix2Statement to be proved.
*
* The actual stored data is a cached representation for use in proofs and verification. This consists of the four substatements that are ORs of the DLOG equality.
*
* A Statement can be constructed only by calling the {@link #createStatement} factory method on an instance of ECElGamalMixParams (all constructors are private)
* A Mix2Statement can be constructed only by calling the {@link #createStatement} factory method on an instance of ECElGamalMixParams (all constructors are private)
*
*/
public class Statement {
public class Mix2Statement {
/**
* Denote the first ciphertext pair e1=(e1c1,e1c2), e2=(e2c1,e2c2) and the second
* e1N=(e1Nc1, e1Nc2) and e2N=(e2Nc1,e2Nc2).
* Then ci_ejNDivek = ejNci/ekci
*/
ECPoint c1_e1NDive1;
/**
* See {@link #c1_e1NDive1}
*/
ECPoint c1_e2NDive1;
public final AndStatement[] clauses = new AndStatement[2];
/**
* See {@link #c1_e1NDive1}
*/
ECPoint c1_e1NDive2;
private Mix2Statement(ElGamalCiphertext a, ElGamalCiphertext b,
ElGamalCiphertext c, ElGamalCiphertext d){
/**
* See {@link #c1_e1NDive1}
*/
ECPoint c1_e2NDive2;
ECPoint a1 = encryptor.decodeElement(a.getC1());
ECPoint a2 = encryptor.decodeElement(a.getC2());
ECPoint b1 = encryptor.decodeElement(b.getC1());
ECPoint b2 = encryptor.decodeElement(b.getC2());
ECPoint c1 = encryptor.decodeElement(c.getC1());
ECPoint c2 = encryptor.decodeElement(c.getC2());
ECPoint d1 = encryptor.decodeElement(d.getC1());
ECPoint d2 = encryptor.decodeElement(d.getC2());
/**
* See {@link #c1_e1NDive1}
*/
ECPoint c2_e1NDive1;
ECPoint c1_div_a1 = group.add(c1, group.negate(a1));
ECPoint d1_div_a1 = group.add(d1, group.negate(a1));
ECPoint c1_div_b1 = group.add(c1, group.negate(b1));
ECPoint d1_div_b1 = group.add(d1, group.negate(b1));
ECPoint c2_div_a2 = group.add(c2, group.negate(a2));
ECPoint d2_div_a2 = group.add(d2, group.negate(a2));
ECPoint c2_div_b2 = group.add(c2, group.negate(b2));
ECPoint d2_div_b2 = group.add(d2, group.negate(b2));
/**
* See {@link #c1_e1NDive1}
*/
ECPoint c2_e2NDive1;
// "Straight assignment" (a ~ c, b ~ d)
DlogStatement s0_0 = new DlogStatement(c1_div_a1, c2_div_a2);
DlogStatement s0_1 = new DlogStatement(d1_div_b1, d2_div_b2);
clauses[0] = new AndStatement(s0_0, s0_1);
/**
* See {@link #c1_e1NDive1}
*/
ECPoint c2_e1NDive2;
/**
* See {@link #c1_e1NDive1}
*/
ECPoint c2_e2NDive2;
private final OrStatement orStatements[] = new OrStatement[4];
public OrStatement getOrStatement(int num) {
if (orStatements[0] == null)
generateOrStatements();
return orStatements[num];
}
public void setOrStatement(int num, OrStatement orStatement) {
orStatements[num] = orStatement;
// "Switched assignment" (a ~ d, b ~ c)
DlogStatement s1_0 = new DlogStatement(d1_div_a1, d2_div_a2);
DlogStatement s1_1 = new DlogStatement(c1_div_b1, c2_div_b2);
clauses[1] = new AndStatement(s1_0, s1_1);
}
}
/**
* Witness information that enables us to generate a ZK proof for the 2-ciphertext mix
*/
public class Mix2StatementWitness {
/**
* Generate and set the four substatements.
* Which of the two clauses is the true one
*/
protected void generateOrStatements() {
setOrStatement(0, new OrStatement(c1_e1NDive1,c2_e1NDive1,c1_e1NDive2,c2_e1NDive2));
setOrStatement(1, new OrStatement(c1_e1NDive1,c2_e1NDive1,c1_e2NDive1,c2_e2NDive1));
setOrStatement(2, new OrStatement(c1_e1NDive2,c2_e1NDive2,c1_e2NDive2,c2_e2NDive2));
setOrStatement(3, new OrStatement(c1_e2NDive1,c2_e2NDive1,c1_e2NDive2,c2_e2NDive2));
public final int trueClauseIndex;
public final AndStatementWitness witness;
private Mix2StatementWitness(int trueClauseIndex, AndStatementWitness witness) {
this.trueClauseIndex = trueClauseIndex;
assert (trueClauseIndex >= 0 && trueClauseIndex < 2);
this.witness = witness;
}
}
private Statement(ConcreteCrypto.ElGamalCiphertext e1, ConcreteCrypto.ElGamalCiphertext e2,
ConcreteCrypto.ElGamalCiphertext e1New, ConcreteCrypto.ElGamalCiphertext e2New){
/**
* Statement consisting of a conjunction of two {@link DlogStatement}s
*/
public class AndStatement {
public final DlogStatement[] clauses = new DlogStatement[2];
ECPoint e1c1 = encryptor.decodeElement(e1.getC1());
ECPoint e1c2 = encryptor.decodeElement(e1.getC2());
ECPoint e2c1 = encryptor.decodeElement(e2.getC1());
ECPoint e2c2 = encryptor.decodeElement(e2.getC2());
ECPoint e1Nc1 = encryptor.decodeElement(e1New.getC1());
ECPoint e1Nc2 = encryptor.decodeElement(e1New.getC2());
ECPoint e2Nc1 = encryptor.decodeElement(e2New.getC1());
ECPoint e2Nc2 = encryptor.decodeElement(e2New.getC2());
private AndStatement(DlogStatement clause0, DlogStatement clause1) {
this.clauses[0] = clause0;
this.clauses[1] = clause1;
}
}
c1_e1NDive1 = group.add(e1Nc1, group.negate(e1c1));
c1_e2NDive1 = group.add(e2Nc1, group.negate(e1c1));
c1_e1NDive2 = group.add(e1Nc1, group.negate(e2c1));
c1_e2NDive2 = group.add(e2Nc1, group.negate(e2c1));
c2_e1NDive1 = group.add(e1Nc2, group.negate(e1c2));
c2_e2NDive1 = group.add(e2Nc2, group.negate(e1c2));
c2_e1NDive2 = group.add(e1Nc2, group.negate(e2c2));
c2_e2NDive2 = group.add(e2Nc2, group.negate(e2c2));
public class AndStatementWitness {
public final DlogStatementWitness[] witnesses = new DlogStatementWitness[2];
private AndStatementWitness(DlogStatementWitness witness0, DlogStatementWitness witness1) {
this.witnesses[0] = witness0;
this.witnesses[1] = witness1;
}
}
/**
* A statement with additional witness information that enables us to generate a ZK proof.
* The parameters for a statement of discrete-log equality
*
* log_{g}(a)==log_{h}(b)
*/
public class ProverStatement extends Statement {
/**
* True iff the ciphertexts were switched (i.e., decrypt(e1N) == decrypt(e2) and decrypt(e2N) == decrypt(e1)
*/
boolean switched;
/**
* Encryption randomness for e1 rerandomization
*/
Crypto.EncryptionRandomness r1;
/**
* Encryption randomnesss for e2 rerandomization
*/
Crypto.EncryptionRandomness r2;
@Override
protected void generateOrStatements() {
ConcreteCrypto.GroupElement c1_e1NDive1Encoded = encryptor.encodeElement(c1_e1NDive1);
ConcreteCrypto.GroupElement c1_e2NDive1Encoded = encryptor.encodeElement(c1_e2NDive1);
ConcreteCrypto.GroupElement c1_e1NDive2Encoded = encryptor.encodeElement(c1_e1NDive2);
ConcreteCrypto.GroupElement c1_e2NDive2Encoded = encryptor.encodeElement(c1_e2NDive2);
ConcreteCrypto.GroupElement c2_e1NDive1Encoded = encryptor.encodeElement(c2_e1NDive1);
ConcreteCrypto.GroupElement c2_e2NDive1Encoded = encryptor.encodeElement(c2_e2NDive1);
ConcreteCrypto.GroupElement c2_e1NDive2Encoded = encryptor.encodeElement(c2_e1NDive2);
ConcreteCrypto.GroupElement c2_e2NDive2Encoded = encryptor.encodeElement(c2_e2NDive2);
if (!switched) {
setOrStatement(0, new OrProverStatement(c1_e1NDive1, c2_e1NDive1, c1_e1NDive2, c2_e1NDive2
, c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e1NDive2Encoded, c2_e1NDive2Encoded
, r1, TrueCouple.left));
setOrStatement(1, new OrProverStatement(c1_e1NDive1, c2_e1NDive1, c1_e2NDive1, c2_e2NDive1
, c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e2NDive1Encoded, c2_e2NDive1Encoded
, r1, TrueCouple.left));
setOrStatement(2, new OrProverStatement(c1_e1NDive2, c2_e1NDive2, c1_e2NDive2, c2_e2NDive2
, c1_e1NDive2Encoded, c2_e1NDive2Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded
, r2, TrueCouple.right));
setOrStatement(3, new OrProverStatement(c1_e2NDive1, c2_e2NDive1, c1_e2NDive2, c2_e2NDive2
, c1_e2NDive1Encoded, c2_e2NDive1Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded
, r2, TrueCouple.right));
} else {
setOrStatement(0, new OrProverStatement(c1_e1NDive1, c2_e1NDive1, c1_e1NDive2, c2_e1NDive2
, c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e1NDive2Encoded, c2_e1NDive2Encoded
, r2, TrueCouple.right));
setOrStatement(1, new OrProverStatement(c1_e1NDive1, c2_e1NDive1, c1_e2NDive1, c2_e2NDive1
, c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e2NDive1Encoded, c2_e2NDive1Encoded
, r1, TrueCouple.right));
setOrStatement(2, new OrProverStatement(c1_e1NDive2, c2_e1NDive2, c1_e2NDive2, c2_e2NDive2
, c1_e1NDive2Encoded, c2_e1NDive2Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded
, r2, TrueCouple.left));
setOrStatement(3, new OrProverStatement(c1_e2NDive1, c2_e2NDive1, c1_e2NDive2, c2_e2NDive2
, c1_e2NDive1Encoded, c2_e2NDive1Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded
, r1, TrueCouple.left));
}
}
@Override
public OrProverStatement getOrStatement(int num) {
OrStatement orStatement = super.getOrStatement(num);
if (!(orStatement instanceof OrProverStatement))
throw new RuntimeException("ProverStatement should always have OrProverStatement substatements!!");
return (OrProverStatement) orStatement;
}
private ProverStatement(ConcreteCrypto.ElGamalCiphertext e1, ConcreteCrypto.ElGamalCiphertext e2,
ConcreteCrypto.ElGamalCiphertext e1New, ConcreteCrypto.ElGamalCiphertext e2New,
Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched) {
super(e1, e2, e1New, e2New);
this.r1 = r1;
this.r2 = r2;
this.switched = switched;
}
}
/**
* The statement of a single disjunction to be proved:
* Either there exists x s.t (g1 ^ x == h1 and g2 ^ x == h2) or there exists x s.t. (g1' ^ x == h1 and g2' ^ x == h2)
*
* The statement can't be constructed externally (all constructors are private)
*
* 4 instances will be constructed for each new {@link ECElGamalMixParams.Statement}
*
*/
public class OrStatement {
public final ECPoint g1;
public final ECPoint h1;
public final ECPoint g2;
public final ECPoint h2;
public final ECPoint g1Tag;
public final ECPoint h1Tag;
public final ECPoint g2Tag;
public final ECPoint h2Tag;
/**
* used by meerkat.mixer.proofs only
*/
private OrStatement(ECPoint h1, ECPoint h2, ECPoint h1Tag, ECPoint h2Tag) {
this.g1 = g;
this.h1 = h1;
this.g2 = h;
this.h2 = h2;
this.g1Tag = g;
this.h1Tag = h1Tag;
this.g2Tag = h;
this.h2Tag = h2Tag;
public class DlogStatement {
public final ECPoint g;
public final ECPoint a;
public final ECPoint h;
public final ECPoint b;
private DlogStatement(ECPoint a, ECPoint b) {
this.g = ECElGamalMixParams.this.g;
this.a = a;
this.h = ECElGamalMixParams.this.h;
this.b = b;
}
}
/**
* An {@link OrStatement} with additional info needed to generate a ZK proof.
* Witness for correctness of a {@link DlogStatement}
*/
public class OrProverStatement extends OrStatement {
protected final Crypto.EncryptionRandomness x;
protected final TrueCouple flag;
// We cache the encoded versions since we need them as input to the random oracle
// when using the Fiat-Shamir heuristic.
protected final ConcreteCrypto.GroupElement g1Encoded;
protected final ConcreteCrypto.GroupElement h1Encoded;
protected final ConcreteCrypto.GroupElement g2Encoded;
protected final ConcreteCrypto.GroupElement h2Encoded;
protected final ConcreteCrypto.GroupElement g1TagEncoded;
protected final ConcreteCrypto.GroupElement h1TagEncoded;
protected final ConcreteCrypto.GroupElement g2TagEncoded;
protected final ConcreteCrypto.GroupElement h2TagEncoded;
private OrProverStatement(ECPoint h1, ECPoint h2, ECPoint h1Tag, ECPoint h2Tag,
ConcreteCrypto.GroupElement h1Encoded, ConcreteCrypto.GroupElement h2Encoded, ConcreteCrypto.GroupElement h1TagEncoded, ConcreteCrypto.GroupElement h2TagEncoded,
Crypto.EncryptionRandomness x, TrueCouple flag) {
super(h1, h2, h1Tag, h2Tag);
this.g1Encoded = gEncoded;
this.h1Encoded = h1Encoded;
this.g2Encoded = hEncoded;
this.h2Encoded = h2Encoded;
this.g1TagEncoded = gEncoded;
this.h1TagEncoded = h1TagEncoded;
this.g2TagEncoded = hEncoded;
this.h2TagEncoded = h2TagEncoded;
public class DlogStatementWitness {
/**
* The actual discrete logarithm (i.e., a=g^x, b=h^x)
*/
BigInteger x;
public DlogStatementWitness(BigInteger x) {
this.x = x;
this.flag = flag;
}
}
}

View File

@ -0,0 +1,65 @@
package meerkat.mixer.proofs;
import com.google.protobuf.Message;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.crypto.concrete.Util;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Mixing;
import org.bouncycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
import java.util.Random;
/**
* Prover, Simulator and Verifier
*/
public class ECElGamalMixProtocols {
final ECElGamalMixParams params;
final ECGroup group;
final ECElGamalEncryption encryptor;
final ECPoint g;
final ECPoint h;
final Random rand;
public ECElGamalMixProtocols(ECElGamalMixParams params, Random rand) {
this.params = params;
this.rand = rand;
group = params.group;
encryptor = params.encryptor;
g = params.g;
h = params.h;
}
public class DlogStatementSchnorrProver implements SigmaProtocol.Prover {
ECElGamalMixParams.DlogStatement statement;
ECElGamalMixParams.DlogStatementWitness witness;
BigInteger r = null;
public DlogStatementSchnorrProver(ECElGamalMixParams.DlogStatement statement, ECElGamalMixParams.DlogStatementWitness witness) {
this.statement = statement;
this.witness = witness;
}
@Override
public Mixing.Mix2Proof.DlogProof.FirstMessage getFirstMessage() {
r = encryptor.generateRandomExponent(rand);
ECPoint gr = group.multiply(statement.g, r);
ECPoint hr = group.multiply(statement.h, r);
Mixing.Mix2Proof.DlogProof.FirstMessage firstMessage = Mixing.Mix2Proof.DlogProof.FirstMessage.newBuilder()
.setGr(encryptor.encodeElement(gr))
.setHr(encryptor.encodeElement(hr))
.build();
return firstMessage;
}
@Override
public Crypto.BigInteger getFinalMessage(BigInteger challenge) {
return Util.encodeBigInteger(challenge.multiply(witness.x).add(r).mod(group.orderUpperBound()));
}
}
}

View File

@ -46,34 +46,34 @@ public class Prover implements Mix2ZeroKnowledgeProver {
}
/**
* @param in1
* @param in2
* @param out1 - if switched then out1 = rerandomize(in2,r2) else out1 = rerandomize(in1,r1)
* @param out2 - if switched then out2 = rerandomize(in1,r1) else out1 = rerandomize(in2,r2)
* @param switched - flag
* @param i - column of in1 and out1 in encryption table
* @param j - column of in2 and out2 in encryption table
* @param layer - row of in1,in2 in encryption table
* @param a
* @param b
* @param c - if switched then c = rerandomize(b,r2) else c = rerandomize(a,r1)
* @param d - if switched then d = rerandomize(a,r1) else d = rerandomize(b,r2)
* @param switched - trueClauseIndex
* @param i - column of a and c in encryption table
* @param j - column of b and d in encryption table
* @param layer - row of a,b in encryption table
* @param r1
* @param r2
* @return - a valid ZKP that indeed out1,out2 calculated as required
* @return - a valid ZKP that indeed c,d calculated as required
* @throws InvalidProtocolBufferException
*/
public Mixing.ZeroKnowledgeProof prove(Crypto.RerandomizableEncryptedMessage in1,
Crypto.RerandomizableEncryptedMessage in2,
Crypto.RerandomizableEncryptedMessage out1,
Crypto.RerandomizableEncryptedMessage out2,
public Mixing.Mix2Proof prove(Crypto.RerandomizableEncryptedMessage a,
Crypto.RerandomizableEncryptedMessage b,
Crypto.RerandomizableEncryptedMessage c,
Crypto.RerandomizableEncryptedMessage d,
boolean switched,int i,int j, int layer,
Crypto.EncryptionRandomness r1,
Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException {
Mixing.ZeroKnowledgeProof.OrProof first,second,third,fourth;
Mixing.Mix2Proof first,second,third,fourth;
ECElGamalMixParams.ProverStatement statement = mixParams.createProverStatement(in1,in2,out1,out2,r1,r2,switched);
ECElGamalMixParams.MixStatementWitness statement = mixParams.createProverStatement(a,b,c,d,r1,r2,switched);
first = createOrProof(statement.getOrStatement(0));
second = createOrProof(statement.getOrStatement(1));
third = createOrProof(statement.getOrStatement(2));
fourth = createOrProof(statement.getOrStatement(3));
first = createOrProof(statement.getDlogStatement(0));
second = createOrProof(statement.getDlogStatement(1));
third = createOrProof(statement.getDlogStatement(2));
fourth = createOrProof(statement.getDlogStatement(3));
Mixing.ZeroKnowledgeProof.Location location = Mixing.ZeroKnowledgeProof.Location.newBuilder()
.setI(i)
@ -98,18 +98,36 @@ public class Prover implements Mix2ZeroKnowledgeProver {
* @param randomOracle
* @return randomOracle.hash(input)
*/
public static BigInteger hash(Mixing.ZeroKnowledgeProof.OrProof.FirstMessage input, RandomOracle randomOracle) {
public static BigInteger hash(Mixing.Mix2Proof.FirstMessage input, RandomOracle randomOracle) {
byte[] arr = input.toByteArray();
return new BigInteger(1,randomOracle.hash(arr,arr.length));
}
Mixing.Mix2Proof.DlogProof.FirstMessage createDlogProof(ECElGamalMixParams.DlogStatement statement, ECElGamalMixParams.DlogStatementWitness witness) {
BigInteger r = encryptor.generateRandomExponent(rand);
ECPoint gr = group.multiply(statement.g, r);
ECPoint hr = group.multiply(statement.h, r);
Mixing.Mix2Proof.DlogProof.FirstMessage firstMessage = Mixing.Mix2Proof.DlogProof.FirstMessage.newBuilder()
.setGr(group.encode(gr))
.setHr(group.encode(hr))
.build();
BigInteger challenge = Prover.hash()
}
/**
* Generate a ZK proof that there exists x s.t (g1 ^ x == h1 and g2 ^ x == h2) or (g1' ^ x == h1 and g2' ^ x == h2).
* Generate a ZK proof that there exists x s.t (g ^ x == a and h ^ x == b) or (g' ^ x == a and h' ^ x == b).
*
* For each clause of the disjunction, we use the following sigma-protocol for DLOG equality (i.e. log_{g1}(h1)==log_{g1}(h2)):
* For each clause of the disjunction, we use the following sigma-protocol for DLOG equality (i.e. log_{g}(a)==log_{g}(b)):
* <ol>
* <li>Prover chooses a random r, and sends g1^r, g2^r </li>
* <li>Prover chooses a random r, and sends g^r, h^r </li>
* <li>Verifier chooses a random c and sends c</li>
* <li>Prover computes </li>
* </ol>
@ -140,7 +158,7 @@ public class Prover implements Mix2ZeroKnowledgeProver {
Mixing.ZeroKnowledgeProof.OrProof.FirstMessage firstMessage;
switch (orStatement.flag) {
switch (orStatement.trueClauseIndex) {
case left:
c2 = encryptor.generateRandomExponent(rand);
zTag = encryptor.generateRandomExponent(rand);

View File

@ -0,0 +1,26 @@
package meerkat.mixer.proofs;
import com.google.protobuf.Message;
import java.math.BigInteger;
/**
* Generic Sigma Protocol.
* The challenge is always a {@link java.math.BigInteger}.
*/
public interface SigmaProtocol {
public interface Prover {
public Message getFirstMessage();
public Message getFinalMessage(BigInteger challenge);
}
public interface Simulator {
public Message getFirstMessage();
public BigInteger getChallenge();
public Message getFinalMessage();
}
public interface Verifier {
public boolean verify(Message firstMessage, BigInteger challenge, Message finalMessage);
}
}

View File

@ -0,0 +1,59 @@
package meerkat.mixer.proofs;
import com.google.protobuf.Message;
import java.math.BigInteger;
/**
* Generic conjuction of sigma protocols
*/
public class SigmaProtocolAnd {
abstract public class Prover implements SigmaProtocol.Prover {
final SigmaProtocol.Prover[] provers;
abstract protected Message buildConcatenatedFirstMessage(Message... firstMessages);
abstract protected Message buildConcatenatedFinalMessage(Message... finalMessages);
public Prover(SigmaProtocol.Prover... provers) {
this.provers = provers;
}
@Override
public Message getFirstMessage() {
Message[] firstMessages = new Message[provers.length];
for (int i = 0; i < firstMessages.length; ++i) {
firstMessages[i] = provers[i].getFirstMessage();
}
return buildConcatenatedFirstMessage(firstMessages);
}
@Override
public Message getFinalMessage(BigInteger challenge) {
Message[] finalMessages = new Message[provers.length];
for (int i = 0; i < finalMessages.length; ++i) {
finalMessages[i] = provers[i].getFinalMessage(challenge);
}
return buildConcatenatedFinalMessage(finalMessages);
}
}
abstract public class Verifier implements SigmaProtocol.Verifier {
final SigmaProtocol.Verifier[] verifiers;
abstract protected Message getFirstMessage(Message concatenated, int verifier);
abstract protected Message getFinalMessage(Message concatenated, int verifier);
public Verifier(SigmaProtocol.Verifier... verifiers) {
this.verifiers = verifiers;
}
public boolean verify(Message firstMessage, BigInteger challenge, Message finalMessage) {
boolean ok = true;
for (int i = 0; i < verifiers.length; ++i) {
ok &= verifiers[i].verify(getFirstMessage(firstMessage, i), challenge, getFinalMessage(finalMessage, i));
}
return ok;
}
}
}

View File

@ -0,0 +1,82 @@
package meerkat.mixer.proofs;
import com.google.protobuf.Message;
import java.math.BigInteger;
/**
* Disjunction of 2 Sigma protocol statements
*/
public class SigmaProtocolOr2 {
abstract public class Prover implements SigmaProtocol.Prover {
final SigmaProtocol.Prover prover;
final SigmaProtocol.Simulator simulator;
final int proverIdx;
abstract protected Message buildConcatenatedFirstMessage(Message... firstMessages);
abstract protected Message buildConcatenatedFinalMessage(BigInteger firstChallenge, Message... finalMessages);
/**
* Subtract two challenges in an appropriate way (e.g., mod group order)
* @param c1
* @param c2
* @return
*/
abstract protected BigInteger subtractChallenge(BigInteger c1, BigInteger c2);
/**
*
* @param prover
* @param simulator
* @param proverIdx Which index should the prover be inserted at (0 means first, 1 means second)
*/
public Prover(SigmaProtocol.Prover prover, SigmaProtocol.Simulator simulator, int proverIdx) {
this.prover = prover;
this.simulator = simulator;
this.proverIdx = proverIdx;
assert (proverIdx >= 0 && proverIdx < 2);
}
@Override
public Message getFirstMessage() {
if (proverIdx == 0) {
return buildConcatenatedFirstMessage(prover.getFirstMessage(), simulator.getFirstMessage());
} else {
return buildConcatenatedFirstMessage(simulator.getFirstMessage(), prover.getFirstMessage());
}
}
@Override
public Message getFinalMessage(BigInteger challenge) {
BigInteger realchallenge = subtractChallenge(challenge, simulator.getChallenge());
if (proverIdx == 0) {
return buildConcatenatedFinalMessage(realchallenge, prover.getFinalMessage(realchallenge), simulator.getFinalMessage());
} else {
return buildConcatenatedFinalMessage(simulator.getChallenge(), simulator.getFinalMessage(), prover.getFinalMessage(realchallenge));
}
}
}
abstract public class Verifier implements SigmaProtocol.Verifier {
final SigmaProtocol.Verifier[] verifiers;
abstract protected Message getFirstMessage(Message concatenated, int verifier);
abstract protected BigInteger getFirstChallenge(Message concatenated);
abstract protected Message getFinalMessage(Message concatenated, int verifier);
public Verifier(SigmaProtocol.Verifier... verifiers) {
this.verifiers = verifiers;
}
public boolean verify(Message firstMessage, BigInteger challenge, Message finalMessage) {
BigInteger firstChallenge = getFirstChallenge(finalMessage);
BigInteger secondChallenge = challenge.subtract(firstChallenge);
return verifiers[0].verify(getFirstMessage(firstMessage, 0), firstChallenge, getFinalMessage(finalMessage, 0)) &
verifiers[0].verify(getFirstMessage(firstMessage, 1), secondChallenge, getFinalMessage(finalMessage, 1));
}
}
}

View File

@ -2,13 +2,17 @@ package meerkat.mixer.proofs;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.crypto.concrete.Util;
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier;
import meerkat.protobuf.ConcreteCrypto.GroupElement;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Mixing;
import org.bouncycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.RandomOracle;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
/**
* implements Mix2ZeroKnowledgeVerifier
*/
@ -52,42 +56,76 @@ public class Verifier implements Mix2ZeroKnowledgeVerifier {
Crypto.RerandomizableEncryptedMessage out1,
Crypto.RerandomizableEncryptedMessage out2,
Mixing.ZeroKnowledgeProof proof) throws InvalidProtocolBufferException {
ECElGamalMixParams.Statement statement = mixParams.createStatement(in1,in2,out1,out2);
return verifyElGamaOrProof(statement.getOrStatement(0), proof.getFirst())&&
verifyElGamaOrProof(statement.getOrStatement(1), proof.getSecond())&&
verifyElGamaOrProof(statement.getOrStatement(2), proof.getThird())&&
verifyElGamaOrProof(statement.getOrStatement(3), proof.getFourth());
ECElGamalMixParams.Mix2Statement statement = mixParams.createStatement(in1,in2,out1,out2);
return verifyFiatShamirOrProof(statement, proof.getFirst(), proof.getSecond());
}
/**
*
* @param orStatement
* @param orProof
* @return verify single or proof
* Verify a Schnorr proof of discrete log equality, given the entire transcript of the ZKP.
* @param statement
* @param firstMessage
* @param challenge
* @param finalMessage
* @return
*/
private boolean verifyElGamaOrProof(ECElGamalMixParams.OrStatement orStatement,
Mixing.ZeroKnowledgeProof.OrProof orProof) {
ZeroKnowledgeOrProofParser.ZeroKnowledgeOrProofContainer container = parser.parseOrProof(orProof);
return container.g1.equals(orStatement.g1) &&
container.h1.equals(orStatement.h1) &&
container.g2.equals(orStatement.g2) &&
container.h2.equals(orStatement.h2) &&
container.g1Tag.equals(orStatement.g1Tag) &&
container.h1Tag.equals(orStatement.h1Tag) &&
container.g2Tag.equals(orStatement.g2Tag) &&
container.h2Tag.equals(orStatement.h2Tag) &&
container.c1.add(container.c2).mod(group.orderUpperBound())
.equals(Prover.hash(container.firstMessage,randomOracle).mod(group.orderUpperBound())) &&
group.multiply(container.g1, container.z)
.equals(group.add(container.u, group.multiply(container.h1,container.c1))) &&
group.multiply(container.g2, container.z)
.equals(group.add(container.v, group.multiply(container.h2,container.c1))) &&
group.multiply(container.g1Tag, container.zTag)
.equals(group.add(container.uTag, group.multiply(container.h1Tag,container.c2))) &&
group.multiply(container.g2Tag, container.zTag)
.equals(group.add(container.vTag, group.multiply(container.h2Tag,container.c2)));
boolean verifyInteractiveDlogProof(ECElGamalMixParams.DlogStatement statement,
Mixing.ZeroKnowledgeProof.DlogProof.FirstMessage firstMessage,
BigInteger challenge,
Mixing.ZeroKnowledgeProof.DlogProof.FinalMessage finalMessage) {
GroupElement grEncoded = firstMessage.getGr();
ECPoint gr = group.decode(grEncoded.toByteArray());
GroupElement hrEncoded = firstMessage.getHr();
ECPoint hr = group.decode(hrEncoded.toByteArray());
BigInteger xcr = Util.decodeBigInteger(finalMessage.getXcr());
boolean gGood = group.add(gr, group.multiply(statement.a,challenge)).equals(group.multiply(statement.g,xcr));
boolean hGood = group.add(hr, group.multiply(statement.b,challenge)).equals(group.multiply(statement.h,xcr));
return gGood && hGood;
}
/**
* Verify a conjuction of two Dlog Proofs, given a complete transcript of the ZKP
* @param statement
* @param firstMessage
* @param challenge
* @param finalMessage
* @return
*/
boolean verifyInteractiveAndProof(ECElGamalMixParams.AndStatement statement,
Mixing.ZeroKnowledgeProof.AndProof.FirstMessage firstMessage,
BigInteger challenge,
Mixing.ZeroKnowledgeProof.AndProof.FinalMessage finalMessage) {
return verifyInteractiveDlogProof(statement.clauses[0], firstMessage.getClause0(), challenge, finalMessage.getClause0()) &&
verifyInteractiveDlogProof(statement.clauses[1], firstMessage.getClause1(), challenge, finalMessage.getClause1());
}
/**
* Verify a disjunction of two Dlog-equality statement conjuctions, given the entire transcript of the ZKP.
* @param statement
* @param firstMessage
* @param challenge
* @param finalMessage
* @return
*/
boolean verifyInteractiveMix2Proof(ECElGamalMixParams.Mix2Statement statement,
Mixing.ZeroKnowledgeProof.OrProof.FirstMessage firstMessage,
BigInteger challenge,
Mixing.ZeroKnowledgeProof.OrProof.FinalMessage finalMessage) {
BigInteger c0 = Util.decodeBigInteger(finalMessage.getC0());
BigInteger c1 = challenge.subtract(c0).mod(group.orderUpperBound());
return verifyInteractiveAndProof(statement.clauses[0], firstMessage.getClause0(), c0, finalMessage.getClause0()) &&
verifyInteractiveAndProof(statement.clauses[1], firstMessage.getClause1(), c1, finalMessage.getClause1());
}
boolean verifyFiatShamirOrProof(ECElGamalMixParams.Mix2Statement statement, Mixing.ZeroKnowledgeProof.OrProof.FirstMessage firstMessage,
Mixing.ZeroKnowledgeProof.OrProof.FinalMessage finalMessage) {
BigInteger challenge = Prover.hash(firstMessage, randomOracle).mod(group.orderUpperBound());
return verifyInteractiveMix2Proof(statement, firstMessage, challenge, finalMessage);
}
}