rewriting mixing proofs
parent
aac7a50a94
commit
b7ef2c10e1
|
@ -11,43 +11,37 @@ message Plaintext {
|
||||||
bytes data = 1;
|
bytes data = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ZeroKnowledgeProof {
|
message Mix2Proof {
|
||||||
message OrProof {
|
// Proof that log_g(a) = log_h(b) = x
|
||||||
|
message DlogProof {
|
||||||
message FirstMessage {
|
message FirstMessage {
|
||||||
GroupElement g1 = 1;
|
GroupElement gr = 1; // g^r
|
||||||
GroupElement h1 = 2;
|
GroupElement hr = 2; // h^r
|
||||||
GroupElement g2 = 3;
|
}
|
||||||
GroupElement h2 = 4;
|
message FinalMessage {
|
||||||
GroupElement g1Tag = 5;
|
BigInteger xcr = 1; // xc+r, where c is the challenge
|
||||||
GroupElement h1Tag = 6;
|
}
|
||||||
GroupElement g2Tag = 7;
|
|
||||||
GroupElement h2Tag = 8;
|
|
||||||
GroupElement u = 9;
|
|
||||||
GroupElement v = 10;
|
|
||||||
GroupElement uTag = 11;
|
|
||||||
GroupElement vTag = 12;
|
|
||||||
}
|
}
|
||||||
//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;
|
|
||||||
|
|
||||||
//calc: u, v, uTag, vTag;
|
message AndProof {
|
||||||
GroupElement u = 9;
|
message FirstMessage {
|
||||||
GroupElement v = 10;
|
DlogProof.FirstMessage clause0 = 1;
|
||||||
GroupElement uTag = 11;
|
DlogProof.FirstMessage clause1 = 2;
|
||||||
GroupElement vTag = 12;
|
}
|
||||||
|
message FinalMessage {
|
||||||
|
DlogProof.FinalMessage clause0 = 1;
|
||||||
|
DlogProof.FinalMessage clause1 = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//generated: c1,c2,z,zTag
|
message FirstMessage {
|
||||||
BigInteger c1 = 13;
|
AndProof.FirstMessage clause0 = 1;
|
||||||
BigInteger c2 = 14;
|
AndProof.FirstMessage clause1 = 2;
|
||||||
BigInteger z = 15;
|
}
|
||||||
BigInteger zTag = 16;
|
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{
|
message Location{
|
||||||
|
@ -56,9 +50,7 @@ message ZeroKnowledgeProof {
|
||||||
int32 layer = 3;
|
int32 layer = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
OrProof first = 1;
|
FirstMessage firstMessage = 1;
|
||||||
OrProof second = 2;
|
FinalMessage finalMessage = 2;
|
||||||
OrProof third = 3;
|
|
||||||
OrProof fourth = 4;
|
|
||||||
Location location = 5;
|
Location location = 5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.google.protobuf.GeneratedMessage;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import meerkat.protobuf.ConcreteCrypto;
|
import meerkat.protobuf.ConcreteCrypto;
|
||||||
|
import meerkat.protobuf.ConcreteCrypto.GroupElement;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||||
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
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)
|
public static <T extends Message> T decrypt(Class<T> plaintextMessageType, ECElGamal.SK secretKey, ECGroup group, Crypto.RerandomizableEncryptedMessage opaqueCipher)
|
||||||
throws InvalidProtocolBufferException {
|
throws InvalidProtocolBufferException {
|
||||||
ConcreteCrypto.ElGamalCiphertext cipherText = ConcreteCrypto.ElGamalCiphertext.parseFrom(opaqueCipher.getData());
|
ConcreteCrypto.ElGamalCiphertext cipherText = ConcreteCrypto.ElGamalCiphertext.parseFrom(opaqueCipher.getData());
|
||||||
ByteString c1encoded = cipherText.getC1();
|
GroupElement c1encoded = cipherText.getC1();
|
||||||
ByteString c2encoded = cipherText.getC2();
|
GroupElement c2encoded = cipherText.getC2();
|
||||||
|
|
||||||
ECPoint c1 = group.decode(c1encoded.toByteArray());
|
ECPoint c1 = group.decode(c1encoded.toByteArray());
|
||||||
ECPoint c2 = group.decode(c2encoded.toByteArray());
|
ECPoint c2 = group.decode(c2encoded.toByteArray());
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
package meerkat.mixer.proofs;
|
package meerkat.mixer.proofs;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import meerkat.crypto.concrete.ECElGamalEncryption;
|
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;
|
||||||
|
import meerkat.protobuf.Crypto.EncryptionRandomness;
|
||||||
import org.bouncycastle.math.ec.ECPoint;
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
import org.factcenter.qilin.primitives.concrete.ECGroup;
|
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
|
* both meerkat.mixer.proofs and meerkat.mixer.verifier implementation use it
|
||||||
*/
|
*/
|
||||||
public class ECElGamalMixParams {
|
public class ECElGamalMixParams {
|
||||||
|
|
||||||
private final ECElGamalEncryption encryptor;
|
final ECElGamalEncryption encryptor;
|
||||||
private final ECGroup group;
|
final ECGroup group;
|
||||||
private final ECPoint g;
|
final ECPoint g;
|
||||||
private final ECPoint h;
|
final ECPoint h;
|
||||||
|
|
||||||
// Cache encoded formats to save multiple re-encodings
|
// Cache encoded formats to save multiple re-encodings
|
||||||
private final ConcreteCrypto.GroupElement gEncoded;
|
private final GroupElement gEncoded;
|
||||||
private final ConcreteCrypto.GroupElement hEncoded;
|
private final GroupElement hEncoded;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param encryptor encryptor used for encoding/decoding serialized ciphertexts. The group, default generator and
|
* @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
|
* 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
|
public Mix2Statement createStatement(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2,
|
||||||
, Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2) throws InvalidProtocolBufferException {
|
Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2) throws InvalidProtocolBufferException {
|
||||||
|
|
||||||
ConcreteCrypto.ElGamalCiphertext in1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in1);
|
ElGamalCiphertext in1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in1);
|
||||||
ConcreteCrypto.ElGamalCiphertext in2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in2);
|
ElGamalCiphertext in2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(in2);
|
||||||
ConcreteCrypto.ElGamalCiphertext out1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out1);
|
ElGamalCiphertext out1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out1);
|
||||||
ConcreteCrypto.ElGamalCiphertext out2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out2);
|
ElGamalCiphertext out2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(out2);
|
||||||
return new Statement(in1ElGamal, in2ElGamal, out1ElGamal, out2ElGamal);
|
return new Mix2Statement(in1ElGamal, in2ElGamal, out1ElGamal, out2ElGamal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* convert each encrypted message to ElGamalCiphertext
|
* 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
|
* ElGamalCiphertext
|
||||||
*/
|
*/
|
||||||
protected ProverStatement createProverStatement(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2
|
protected Mix2StatementWitness createMix2Witness(EncryptionRandomness r1, EncryptionRandomness r2, boolean switched) {
|
||||||
, Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2
|
// if (!switched), dlogW1 is witness for a ~ c, dlogW2 is witness for b ~ d,
|
||||||
, Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched)
|
// otherwise, dlogW1 is witness for a ~ d, dlogW2 is witness for b ~ c
|
||||||
throws InvalidProtocolBufferException {
|
DlogStatementWitness dlogW1 = new DlogStatementWitness(encryptor.extractRandomness(r1));
|
||||||
|
DlogStatementWitness dlogW2 = new DlogStatementWitness(encryptor.extractRandomness(r2));
|
||||||
|
|
||||||
//convert RerandomizableEncryptedMessage to ElGamalCiphertext
|
AndStatementWitness andW = new AndStatementWitness(dlogW1, dlogW2);
|
||||||
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 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.
|
* 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 {
|
||||||
|
|
||||||
/**
|
public final AndStatement[] clauses = new AndStatement[2];
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
private Mix2Statement(ElGamalCiphertext a, ElGamalCiphertext b,
|
||||||
* See {@link #c1_e1NDive1}
|
ElGamalCiphertext c, ElGamalCiphertext d){
|
||||||
*/
|
|
||||||
ECPoint c1_e1NDive2;
|
|
||||||
|
|
||||||
/**
|
ECPoint a1 = encryptor.decodeElement(a.getC1());
|
||||||
* See {@link #c1_e1NDive1}
|
ECPoint a2 = encryptor.decodeElement(a.getC2());
|
||||||
*/
|
ECPoint b1 = encryptor.decodeElement(b.getC1());
|
||||||
ECPoint c1_e2NDive2;
|
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());
|
||||||
|
|
||||||
/**
|
ECPoint c1_div_a1 = group.add(c1, group.negate(a1));
|
||||||
* See {@link #c1_e1NDive1}
|
ECPoint d1_div_a1 = group.add(d1, group.negate(a1));
|
||||||
*/
|
ECPoint c1_div_b1 = group.add(c1, group.negate(b1));
|
||||||
ECPoint c2_e1NDive1;
|
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));
|
||||||
|
|
||||||
/**
|
// "Straight assignment" (a ~ c, b ~ d)
|
||||||
* See {@link #c1_e1NDive1}
|
DlogStatement s0_0 = new DlogStatement(c1_div_a1, c2_div_a2);
|
||||||
*/
|
DlogStatement s0_1 = new DlogStatement(d1_div_b1, d2_div_b2);
|
||||||
ECPoint c2_e2NDive1;
|
clauses[0] = new AndStatement(s0_0, s0_1);
|
||||||
|
|
||||||
/**
|
// "Switched assignment" (a ~ d, b ~ c)
|
||||||
* See {@link #c1_e1NDive1}
|
DlogStatement s1_0 = new DlogStatement(d1_div_a1, d2_div_a2);
|
||||||
*/
|
DlogStatement s1_1 = new DlogStatement(c1_div_b1, c2_div_b2);
|
||||||
ECPoint c2_e1NDive2;
|
clauses[1] = new AndStatement(s1_0, s1_1);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate and set the four substatements.
|
|
||||||
*/
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Statement(ConcreteCrypto.ElGamalCiphertext e1, ConcreteCrypto.ElGamalCiphertext e2,
|
|
||||||
ConcreteCrypto.ElGamalCiphertext e1New, ConcreteCrypto.ElGamalCiphertext e2New){
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A statement with additional witness information that enables us to generate a ZK proof.
|
|
||||||
*/
|
|
||||||
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;
|
/**
|
||||||
|
* Witness information that enables us to generate a ZK proof for the 2-ciphertext mix
|
||||||
|
*/
|
||||||
|
public class Mix2StatementWitness {
|
||||||
|
/**
|
||||||
|
* Which of the two clauses is the true one
|
||||||
|
*/
|
||||||
|
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 ProverStatement(ConcreteCrypto.ElGamalCiphertext e1, ConcreteCrypto.ElGamalCiphertext e2,
|
|
||||||
ConcreteCrypto.ElGamalCiphertext e1New, ConcreteCrypto.ElGamalCiphertext e2New,
|
/**
|
||||||
Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched) {
|
* Statement consisting of a conjunction of two {@link DlogStatement}s
|
||||||
super(e1, e2, e1New, e2New);
|
*/
|
||||||
this.r1 = r1;
|
public class AndStatement {
|
||||||
this.r2 = r2;
|
public final DlogStatement[] clauses = new DlogStatement[2];
|
||||||
this.switched = switched;
|
|
||||||
|
private AndStatement(DlogStatement clause0, DlogStatement clause1) {
|
||||||
|
this.clauses[0] = clause0;
|
||||||
|
this.clauses[1] = clause1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AndStatementWitness {
|
||||||
|
public final DlogStatementWitness[] witnesses = new DlogStatementWitness[2];
|
||||||
|
|
||||||
|
private AndStatementWitness(DlogStatementWitness witness0, DlogStatementWitness witness1) {
|
||||||
|
this.witnesses[0] = witness0;
|
||||||
|
this.witnesses[1] = witness1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The statement of a single disjunction to be proved:
|
* The parameters for a statement of discrete-log equality
|
||||||
* 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}
|
|
||||||
*
|
*
|
||||||
|
* log_{g}(a)==log_{h}(b)
|
||||||
*/
|
*/
|
||||||
public class OrStatement {
|
public class DlogStatement {
|
||||||
public final ECPoint g1;
|
public final ECPoint g;
|
||||||
public final ECPoint h1;
|
public final ECPoint a;
|
||||||
public final ECPoint g2;
|
public final ECPoint h;
|
||||||
public final ECPoint h2;
|
public final ECPoint b;
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 {
|
public class DlogStatementWitness {
|
||||||
|
/**
|
||||||
protected final Crypto.EncryptionRandomness x;
|
* The actual discrete logarithm (i.e., a=g^x, b=h^x)
|
||||||
protected final TrueCouple flag;
|
*/
|
||||||
|
BigInteger x;
|
||||||
// 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 DlogStatementWitness(BigInteger x) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.flag = flag;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,34 +46,34 @@ public class Prover implements Mix2ZeroKnowledgeProver {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param in1
|
* @param a
|
||||||
* @param in2
|
* @param b
|
||||||
* @param out1 - if switched then out1 = rerandomize(in2,r2) else out1 = rerandomize(in1,r1)
|
* @param c - if switched then c = rerandomize(b,r2) else c = rerandomize(a,r1)
|
||||||
* @param out2 - if switched then out2 = rerandomize(in1,r1) else out1 = rerandomize(in2,r2)
|
* @param d - if switched then d = rerandomize(a,r1) else d = rerandomize(b,r2)
|
||||||
* @param switched - flag
|
* @param switched - trueClauseIndex
|
||||||
* @param i - column of in1 and out1 in encryption table
|
* @param i - column of a and c in encryption table
|
||||||
* @param j - column of in2 and out2 in encryption table
|
* @param j - column of b and d in encryption table
|
||||||
* @param layer - row of in1,in2 in encryption table
|
* @param layer - row of a,b in encryption table
|
||||||
* @param r1
|
* @param r1
|
||||||
* @param r2
|
* @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
|
* @throws InvalidProtocolBufferException
|
||||||
*/
|
*/
|
||||||
public Mixing.ZeroKnowledgeProof prove(Crypto.RerandomizableEncryptedMessage in1,
|
public Mixing.Mix2Proof prove(Crypto.RerandomizableEncryptedMessage a,
|
||||||
Crypto.RerandomizableEncryptedMessage in2,
|
Crypto.RerandomizableEncryptedMessage b,
|
||||||
Crypto.RerandomizableEncryptedMessage out1,
|
Crypto.RerandomizableEncryptedMessage c,
|
||||||
Crypto.RerandomizableEncryptedMessage out2,
|
Crypto.RerandomizableEncryptedMessage d,
|
||||||
boolean switched,int i,int j, int layer,
|
boolean switched,int i,int j, int layer,
|
||||||
Crypto.EncryptionRandomness r1,
|
Crypto.EncryptionRandomness r1,
|
||||||
Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException {
|
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));
|
first = createOrProof(statement.getDlogStatement(0));
|
||||||
second = createOrProof(statement.getOrStatement(1));
|
second = createOrProof(statement.getDlogStatement(1));
|
||||||
third = createOrProof(statement.getOrStatement(2));
|
third = createOrProof(statement.getDlogStatement(2));
|
||||||
fourth = createOrProof(statement.getOrStatement(3));
|
fourth = createOrProof(statement.getDlogStatement(3));
|
||||||
|
|
||||||
Mixing.ZeroKnowledgeProof.Location location = Mixing.ZeroKnowledgeProof.Location.newBuilder()
|
Mixing.ZeroKnowledgeProof.Location location = Mixing.ZeroKnowledgeProof.Location.newBuilder()
|
||||||
.setI(i)
|
.setI(i)
|
||||||
|
@ -98,18 +98,36 @@ public class Prover implements Mix2ZeroKnowledgeProver {
|
||||||
* @param randomOracle
|
* @param randomOracle
|
||||||
* @return randomOracle.hash(input)
|
* @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();
|
byte[] arr = input.toByteArray();
|
||||||
return new BigInteger(1,randomOracle.hash(arr,arr.length));
|
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>
|
* <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>Verifier chooses a random c and sends c</li>
|
||||||
* <li>Prover computes </li>
|
* <li>Prover computes </li>
|
||||||
* </ol>
|
* </ol>
|
||||||
|
@ -140,7 +158,7 @@ public class Prover implements Mix2ZeroKnowledgeProver {
|
||||||
Mixing.ZeroKnowledgeProof.OrProof.FirstMessage firstMessage;
|
Mixing.ZeroKnowledgeProof.OrProof.FirstMessage firstMessage;
|
||||||
|
|
||||||
|
|
||||||
switch (orStatement.flag) {
|
switch (orStatement.trueClauseIndex) {
|
||||||
case left:
|
case left:
|
||||||
c2 = encryptor.generateRandomExponent(rand);
|
c2 = encryptor.generateRandomExponent(rand);
|
||||||
zTag = encryptor.generateRandomExponent(rand);
|
zTag = encryptor.generateRandomExponent(rand);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,13 +2,17 @@ package meerkat.mixer.proofs;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import meerkat.crypto.concrete.ECElGamalEncryption;
|
import meerkat.crypto.concrete.ECElGamalEncryption;
|
||||||
|
import meerkat.crypto.concrete.Util;
|
||||||
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier;
|
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier;
|
||||||
|
import meerkat.protobuf.ConcreteCrypto.GroupElement;
|
||||||
import meerkat.protobuf.Crypto;
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.protobuf.Mixing;
|
import meerkat.protobuf.Mixing;
|
||||||
import org.bouncycastle.math.ec.ECPoint;
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
import org.factcenter.qilin.primitives.RandomOracle;
|
import org.factcenter.qilin.primitives.RandomOracle;
|
||||||
import org.factcenter.qilin.primitives.concrete.ECGroup;
|
import org.factcenter.qilin.primitives.concrete.ECGroup;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* implements Mix2ZeroKnowledgeVerifier
|
* implements Mix2ZeroKnowledgeVerifier
|
||||||
*/
|
*/
|
||||||
|
@ -52,42 +56,76 @@ public class Verifier implements Mix2ZeroKnowledgeVerifier {
|
||||||
Crypto.RerandomizableEncryptedMessage out1,
|
Crypto.RerandomizableEncryptedMessage out1,
|
||||||
Crypto.RerandomizableEncryptedMessage out2,
|
Crypto.RerandomizableEncryptedMessage out2,
|
||||||
Mixing.ZeroKnowledgeProof proof) throws InvalidProtocolBufferException {
|
Mixing.ZeroKnowledgeProof proof) throws InvalidProtocolBufferException {
|
||||||
ECElGamalMixParams.Statement statement = mixParams.createStatement(in1,in2,out1,out2);
|
ECElGamalMixParams.Mix2Statement statement = mixParams.createStatement(in1,in2,out1,out2);
|
||||||
return verifyElGamaOrProof(statement.getOrStatement(0), proof.getFirst())&&
|
return verifyFiatShamirOrProof(statement, proof.getFirst(), proof.getSecond());
|
||||||
verifyElGamaOrProof(statement.getOrStatement(1), proof.getSecond())&&
|
|
||||||
verifyElGamaOrProof(statement.getOrStatement(2), proof.getThird())&&
|
|
||||||
verifyElGamaOrProof(statement.getOrStatement(3), proof.getFourth());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Verify a Schnorr proof of discrete log equality, given the entire transcript of the ZKP.
|
||||||
* @param orStatement
|
* @param statement
|
||||||
* @param orProof
|
* @param firstMessage
|
||||||
* @return verify single or proof
|
* @param challenge
|
||||||
|
* @param finalMessage
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean verifyElGamaOrProof(ECElGamalMixParams.OrStatement orStatement,
|
boolean verifyInteractiveDlogProof(ECElGamalMixParams.DlogStatement statement,
|
||||||
Mixing.ZeroKnowledgeProof.OrProof orProof) {
|
Mixing.ZeroKnowledgeProof.DlogProof.FirstMessage firstMessage,
|
||||||
ZeroKnowledgeOrProofParser.ZeroKnowledgeOrProofContainer container = parser.parseOrProof(orProof);
|
BigInteger challenge,
|
||||||
return container.g1.equals(orStatement.g1) &&
|
Mixing.ZeroKnowledgeProof.DlogProof.FinalMessage finalMessage) {
|
||||||
container.h1.equals(orStatement.h1) &&
|
GroupElement grEncoded = firstMessage.getGr();
|
||||||
container.g2.equals(orStatement.g2) &&
|
ECPoint gr = group.decode(grEncoded.toByteArray());
|
||||||
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)));
|
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue