From b7ef2c10e19b9ed73abf0a7760866753f3d07018 Mon Sep 17 00:00:00 2001 From: Tal Moran Date: Wed, 11 Jan 2017 17:01:14 +0200 Subject: [PATCH] rewriting mixing proofs --- .../src/main/proto/meerkat/mixing.proto | 66 ++-- .../crypto/concrete/ECElGamalUtils.java | 5 +- .../mixer/proofs/ECElGamalMixParams.java | 358 ++++++------------ .../mixer/proofs/ECElGamalMixProtocols.java | 65 ++++ .../java/meerkat/mixer/proofs/Prover.java | 66 ++-- .../meerkat/mixer/proofs/SigmaProtocol.java | 26 ++ .../mixer/proofs/SigmaProtocolAnd.java | 59 +++ .../mixer/proofs/SigmaProtocolOr2.java | 82 ++++ .../java/meerkat/mixer/proofs/Verifier.java | 98 +++-- 9 files changed, 484 insertions(+), 341 deletions(-) create mode 100644 mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java create mode 100644 mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java create mode 100644 mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java create mode 100644 mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java diff --git a/meerkat-common/src/main/proto/meerkat/mixing.proto b/meerkat-common/src/main/proto/meerkat/mixing.proto index abbd0a1..eebce4b 100644 --- a/meerkat-common/src/main/proto/meerkat/mixing.proto +++ b/meerkat-common/src/main/proto/meerkat/mixing.proto @@ -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; } diff --git a/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java b/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java index e26e00b..c40128c 100644 --- a/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java +++ b/meerkat-common/src/test/java/meerkat/crypto/concrete/ECElGamalUtils.java @@ -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 decrypt(Class 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()); diff --git a/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixParams.java b/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixParams.java index 895253c..56c02d6 100644 --- a/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixParams.java +++ b/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixParams.java @@ -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; } } } diff --git a/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java b/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java new file mode 100644 index 0000000..62d7c6d --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/ECElGamalMixProtocols.java @@ -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())); + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/Prover.java b/mixer/src/main/java/meerkat/mixer/proofs/Prover.java index cfa4407..91a3c24 100644 --- a/mixer/src/main/java/meerkat/mixer/proofs/Prover.java +++ b/mixer/src/main/java/meerkat/mixer/proofs/Prover.java @@ -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)): *
    - *
  1. Prover chooses a random r, and sends g1^r, g2^r
  2. + *
  3. Prover chooses a random r, and sends g^r, h^r
  4. *
  5. Verifier chooses a random c and sends c
  6. *
  7. Prover computes
  8. *
@@ -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); diff --git a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java new file mode 100644 index 0000000..c0e6c59 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java @@ -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); + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java new file mode 100644 index 0000000..4dc2424 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolAnd.java @@ -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; + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java new file mode 100644 index 0000000..febb8af --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocolOr2.java @@ -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)); + } + } + +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/Verifier.java b/mixer/src/main/java/meerkat/mixer/proofs/Verifier.java index 92ca859..6c0034d 100644 --- a/mixer/src/main/java/meerkat/mixer/proofs/Verifier.java +++ b/mixer/src/main/java/meerkat/mixer/proofs/Verifier.java @@ -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); } }