diff --git a/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java b/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java index 62d7c6d..b657cfb 100644 --- a/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java +++ b/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java @@ -3,6 +3,8 @@ package meerkat.mixer.proofs; import com.google.protobuf.Message; import meerkat.crypto.concrete.ECElGamalEncryption; import meerkat.crypto.concrete.Util; +import meerkat.protobuf.ConcreteCrypto; +import meerkat.protobuf.ConcreteCrypto.GroupElement; import meerkat.protobuf.Crypto; import meerkat.protobuf.Mixing; import org.bouncycastle.math.ec.ECPoint; @@ -32,6 +34,7 @@ public class ECElGamalMixProtocols { h = params.h; } + public class DlogStatementSchnorrProver implements SigmaProtocol.Prover { ECElGamalMixParams.DlogStatement statement; ECElGamalMixParams.DlogStatementWitness witness; @@ -62,4 +65,232 @@ public class ECElGamalMixProtocols { return Util.encodeBigInteger(challenge.multiply(witness.x).add(r).mod(group.orderUpperBound())); } } + + public class DlogStatementSchnorrVerifier implements SigmaProtocol.Verifier { + final ECElGamalMixParams.DlogStatement statement; + + public DlogStatementSchnorrVerifier(ECElGamalMixParams.DlogStatement statement) { + this.statement = statement; + } + + @Override + public boolean verify(Message firstMessage, BigInteger challenge, Message finalMessage) { + assert(firstMessage instanceof Mixing.Mix2Proof.DlogProof.FirstMessage && + finalMessage instanceof Mixing.Mix2Proof.DlogProof.FinalMessage); + return verify((Mixing.Mix2Proof.DlogProof.FirstMessage) firstMessage, challenge, + (Mixing.Mix2Proof.DlogProof.FinalMessage) finalMessage); + } + + public boolean verify(Mixing.Mix2Proof.DlogProof.FirstMessage firstMessage, BigInteger challenge, + Mixing.Mix2Proof.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; + } + + } + + public class DlogStatementSchnorrSimulator implements SigmaProtocol.Simulator { + ECElGamalMixParams.DlogStatement statement; + BigInteger response = null; + + public DlogStatementSchnorrSimulator(ECElGamalMixParams.DlogStatement statement) { + this.statement = statement; + } + + @Override + public Mixing.Mix2Proof.DlogProof.FirstMessage getFirstMessage(BigInteger challenge) { + response = encryptor.generateRandomExponent(rand); + + ECPoint u = group.multiply(statement.g, response).subtract(group.multiply(statement.a,challenge)); + ECPoint v = group.multiply(statement.h, response).subtract(group.multiply(statement.b,challenge)); + return Mixing.Mix2Proof.DlogProof.FirstMessage.newBuilder() + .setGr(encryptor.encodeElement(u)) + .setHr(encryptor.encodeElement(v)) + .build(); + } + + @Override + public Crypto.BigInteger getFinalMessage() { + return Util.encodeBigInteger(response); + } + } + + /** + * Prover for plaintext equivalence of a pair of ciphertexts. + */ + public class CiphertextPairEquivalenceProver extends SigmaProtocolAnd.Prover { + public CiphertextPairEquivalenceProver(ECElGamalMixParams.AndStatement statement, + ECElGamalMixParams.AndStatementWitness witness) { + super(new DlogStatementSchnorrProver(statement.clauses[0], witness.witnesses[0]), + new DlogStatementSchnorrProver(statement.clauses[1], witness.witnesses[1])); + } + + @Override + protected Mixing.Mix2Proof.AndProof.FirstMessage buildConcatenatedFirstMessage(Message... firstMessages) { + assert (firstMessages.length == 2); + assert (firstMessages[0] instanceof Mixing.Mix2Proof.DlogProof.FirstMessage && + firstMessages[1] instanceof Mixing.Mix2Proof.DlogProof.FirstMessage); + + return Mixing.Mix2Proof.AndProof.FirstMessage.newBuilder() + .setClause0((Mixing.Mix2Proof.DlogProof.FirstMessage)firstMessages[0]) + .setClause1((Mixing.Mix2Proof.DlogProof.FirstMessage)firstMessages[1]) + .build(); + } + + @Override + protected Mixing.Mix2Proof.AndProof.FinalMessage buildConcatenatedFinalMessage(Message... finalMessages) { + assert(finalMessages.length == 2); + assert (finalMessages[0] instanceof Mixing.Mix2Proof.DlogProof.FinalMessage && + finalMessages[1] instanceof Mixing.Mix2Proof.DlogProof.FinalMessage); + + return Mixing.Mix2Proof.AndProof.FinalMessage.newBuilder() + .setClause0((Mixing.Mix2Proof.DlogProof.FinalMessage)finalMessages[0]) + .setClause1((Mixing.Mix2Proof.DlogProof.FinalMessage)finalMessages[1]) + .build(); + } + } + + public class CiphertextPairEquivalenceVerifier extends SigmaProtocolAnd.Verifier { + public CiphertextPairEquivalenceVerifier(ECElGamalMixParams.AndStatement statement) { + super(new DlogStatementSchnorrVerifier(statement.clauses[0]), new DlogStatementSchnorrVerifier(statement.clauses[1])); + } + + @Override + protected Mixing.Mix2Proof.DlogProof.FirstMessage getFirstMessage(Message concatenated, int verifier) { + assert(concatenated instanceof Mixing.Mix2Proof.AndProof.FirstMessage); + Mixing.Mix2Proof.AndProof.FirstMessage msg = (Mixing.Mix2Proof.AndProof.FirstMessage) concatenated; + if (verifier == 0) + return msg.getClause0(); + else + return msg.getClause1(); + } + + @Override + protected Mixing.Mix2Proof.DlogProof.FinalMessage getFinalMessage(Message concatenated, int verifier) { + assert(concatenated instanceof Mixing.Mix2Proof.AndProof.FinalMessage); + Mixing.Mix2Proof.AndProof.FinalMessage msg = (Mixing.Mix2Proof.AndProof.FinalMessage) concatenated; + if (verifier == 0) + return msg.getClause0(); + else + return msg.getClause1(); + } + } + + public class CiphertextPairEquivalenceSimulator extends SigmaProtocolAnd.Simulator { + public CiphertextPairEquivalenceSimulator(ECElGamalMixParams.AndStatement statement) { + super(new DlogStatementSchnorrSimulator(statement.clauses[0]), new DlogStatementSchnorrSimulator(statement.clauses[1])); + } + + @Override + protected Mixing.Mix2Proof.AndProof.FirstMessage buildConcatenatedFirstMessage(Message... firstMessages) { + assert (firstMessages.length == 2); + assert (firstMessages[0] instanceof Mixing.Mix2Proof.DlogProof.FirstMessage && + firstMessages[1] instanceof Mixing.Mix2Proof.DlogProof.FirstMessage); + + return Mixing.Mix2Proof.AndProof.FirstMessage.newBuilder() + .setClause0((Mixing.Mix2Proof.DlogProof.FirstMessage)firstMessages[0]) + .setClause1((Mixing.Mix2Proof.DlogProof.FirstMessage)firstMessages[1]) + .build(); + } + + @Override + protected Mixing.Mix2Proof.AndProof.FinalMessage buildConcatenatedFinalMessage(Message... finalMessages) { + assert(finalMessages.length == 2); + assert (finalMessages[0] instanceof Mixing.Mix2Proof.DlogProof.FinalMessage && + finalMessages[1] instanceof Mixing.Mix2Proof.DlogProof.FinalMessage); + + return Mixing.Mix2Proof.AndProof.FinalMessage.newBuilder() + .setClause0((Mixing.Mix2Proof.DlogProof.FinalMessage)finalMessages[0]) + .setClause1((Mixing.Mix2Proof.DlogProof.FinalMessage)finalMessages[1]) + .build(); + } + } + + public class Mix2Prover extends SigmaProtocolOr2.Prover { + public Mix2Prover(ECElGamalMixParams.Mix2Statement statement, ECElGamalMixParams.Mix2StatementWitness witness) { + super(new CiphertextPairEquivalenceProver(statement.clauses[witness.trueClauseIndex], witness.witness), + new CiphertextPairEquivalenceSimulator(statement.clauses[1 - witness.trueClauseIndex]), + witness.trueClauseIndex); + } + + @Override + protected Mixing.Mix2Proof.FirstMessage buildConcatenatedFirstMessage(Message... firstMessages) { + assert(firstMessages.length == 2); + + assert (firstMessages[0] instanceof Mixing.Mix2Proof.AndProof.FirstMessage && + firstMessages[1] instanceof Mixing.Mix2Proof.AndProof.FirstMessage); + + return Mixing.Mix2Proof.FirstMessage.newBuilder() + .setClause0((Mixing.Mix2Proof.AndProof.FirstMessage)firstMessages[0]) + .setClause1((Mixing.Mix2Proof.AndProof.FirstMessage)firstMessages[1]) + .build(); + } + + @Override + protected Mixing.Mix2Proof.FinalMessage buildConcatenatedFinalMessage(BigInteger firstChallenge, + Message... finalMessages) { + assert(finalMessages.length == 2); + assert (finalMessages[0] instanceof Mixing.Mix2Proof.AndProof.FinalMessage && + finalMessages[1] instanceof Mixing.Mix2Proof.AndProof.FinalMessage); + + return Mixing.Mix2Proof.FinalMessage.newBuilder() + .setClause0((Mixing.Mix2Proof.AndProof.FinalMessage)finalMessages[0]) + .setClause1((Mixing.Mix2Proof.AndProof.FinalMessage)finalMessages[1]) + .setC0(Util.encodeBigInteger(firstChallenge)) + .build(); + } + + @Override + protected BigInteger generateChallenge() { + return encryptor.generateRandomExponent(rand); + } + + @Override + protected BigInteger subtractChallenge(BigInteger c1, BigInteger c2) { + return c1.subtract(c2).mod(group.orderUpperBound()); + } + } + + public class Mix2Verifier extends SigmaProtocolOr2.Verifier { + public Mix2Verifier(ECElGamalMixParams.Mix2Statement statement) { + super(new CiphertextPairEquivalenceVerifier(statement.clauses[0]), new CiphertextPairEquivalenceVerifier(statement.clauses[1])); + } + + @Override + protected Mixing.Mix2Proof.AndProof.FirstMessage getFirstMessage(Message concatenated, int verifier) { + assert(concatenated instanceof Mixing.Mix2Proof.FirstMessage); + Mixing.Mix2Proof.FirstMessage msg = (Mixing.Mix2Proof.FirstMessage) concatenated; + if (verifier == 0) + return msg.getClause0(); + else + return msg.getClause1(); + } + + @Override + protected BigInteger getFirstChallenge(Message concatenated) { + assert(concatenated instanceof Mixing.Mix2Proof.FinalMessage); + Mixing.Mix2Proof.FinalMessage msg = (Mixing.Mix2Proof.FinalMessage) concatenated; + + return Util.decodeBigInteger(msg.getC0()); + } + + @Override + protected Mixing.Mix2Proof.AndProof.FinalMessage getFinalMessage(Message concatenated, int verifier) { + assert(concatenated instanceof Mixing.Mix2Proof.FinalMessage); + Mixing.Mix2Proof.FinalMessage msg = (Mixing.Mix2Proof.FinalMessage) concatenated; + if (verifier == 0) + return msg.getClause0(); + else + return msg.getClause1(); + } + } } diff --git a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java index c0e6c59..b3b09c6 100644 --- a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java +++ b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java @@ -15,8 +15,7 @@ public interface SigmaProtocol { } public interface Simulator { - public Message getFirstMessage(); - public BigInteger getChallenge(); + public Message getFirstMessage(BigInteger challenge); public Message getFinalMessage(); } diff --git a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java index 4dc2424..4837b75 100644 --- a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java +++ b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java @@ -8,7 +8,7 @@ import java.math.BigInteger; * Generic conjuction of sigma protocols */ public class SigmaProtocolAnd { - abstract public class Prover implements SigmaProtocol.Prover { + static abstract public class Prover implements SigmaProtocol.Prover { final SigmaProtocol.Prover[] provers; abstract protected Message buildConcatenatedFirstMessage(Message... firstMessages); @@ -37,7 +37,7 @@ public class SigmaProtocolAnd { } } - abstract public class Verifier implements SigmaProtocol.Verifier { + static abstract public class Verifier implements SigmaProtocol.Verifier { final SigmaProtocol.Verifier[] verifiers; abstract protected Message getFirstMessage(Message concatenated, int verifier); @@ -56,4 +56,36 @@ public class SigmaProtocolAnd { return ok; } } + + static abstract public class Simulator implements SigmaProtocol.Simulator { + final SigmaProtocol.Simulator[] simulators; + + abstract protected Message buildConcatenatedFirstMessage(Message... firstMessages); + abstract protected Message buildConcatenatedFinalMessage(Message... finalMessages); + + public Simulator(SigmaProtocol.Simulator... simulators) { + this.simulators = simulators; + } + + @Override + public Message getFirstMessage(BigInteger challenge) { + Message[] firstMessages = new Message[simulators.length]; + + for (int i = 0; i < firstMessages.length; ++i) { + firstMessages[i] = simulators[i].getFirstMessage(challenge); + } + return buildConcatenatedFirstMessage(firstMessages); + } + + @Override + public Message getFinalMessage() { + Message[] finalMessages = new Message[simulators.length]; + + for (int i = 0; i < finalMessages.length; ++i) { + finalMessages[i] = simulators[i].getFinalMessage(); + } + return buildConcatenatedFinalMessage(finalMessages); + } + } + } diff --git a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java index febb8af..889c51d 100644 --- a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java +++ b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java @@ -8,7 +8,7 @@ import java.math.BigInteger; * Disjunction of 2 Sigma protocol statements */ public class SigmaProtocolOr2 { - abstract public class Prover implements SigmaProtocol.Prover { + static abstract public class Prover implements SigmaProtocol.Prover { final SigmaProtocol.Prover prover; final SigmaProtocol.Simulator simulator; final int proverIdx; @@ -16,6 +16,15 @@ public class SigmaProtocolOr2 { abstract protected Message buildConcatenatedFirstMessage(Message... firstMessages); abstract protected Message buildConcatenatedFinalMessage(BigInteger firstChallenge, Message... finalMessages); + BigInteger simChallenge; + + /** + * Generate a random challenge in an appropriate way (e.g., mod group order) + * @return + */ + abstract protected BigInteger generateChallenge(); + + /** * Subtract two challenges in an appropriate way (e.g., mod group order) * @param c1 @@ -39,26 +48,27 @@ public class SigmaProtocolOr2 { @Override public Message getFirstMessage() { + simChallenge = generateChallenge(); if (proverIdx == 0) { - return buildConcatenatedFirstMessage(prover.getFirstMessage(), simulator.getFirstMessage()); + return buildConcatenatedFirstMessage(prover.getFirstMessage(), simulator.getFirstMessage(simChallenge)); } else { - return buildConcatenatedFirstMessage(simulator.getFirstMessage(), prover.getFirstMessage()); + return buildConcatenatedFirstMessage(simulator.getFirstMessage(simChallenge), prover.getFirstMessage()); } } @Override public Message getFinalMessage(BigInteger challenge) { - BigInteger realchallenge = subtractChallenge(challenge, simulator.getChallenge()); + BigInteger realchallenge = subtractChallenge(challenge, simChallenge); if (proverIdx == 0) { return buildConcatenatedFinalMessage(realchallenge, prover.getFinalMessage(realchallenge), simulator.getFinalMessage()); } else { - return buildConcatenatedFinalMessage(simulator.getChallenge(), simulator.getFinalMessage(), prover.getFinalMessage(realchallenge)); + return buildConcatenatedFinalMessage(simChallenge, simulator.getFinalMessage(), prover.getFinalMessage(realchallenge)); } } } - abstract public class Verifier implements SigmaProtocol.Verifier { + static abstract public class Verifier implements SigmaProtocol.Verifier { final SigmaProtocol.Verifier[] verifiers; abstract protected Message getFirstMessage(Message concatenated, int verifier);