diff --git a/build.gradle b/build.gradle index 9f070e3..ed7d229 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,57 @@ +plugins { + id "com.google.osdetector" version "1.4.0" +} subprojects { proj -> proj.afterEvaluate { // Used to generate initial maven-dir layout - task "create-dirs" { description = "Create default maven directory structure" } << { - sourceSets*.java.srcDirs*.each { it.mkdirs() } - sourceSets*.resources.srcDirs*.each { it.mkdirs() } + task "create-dirs" { + description = "Create default maven directory structure" + doLast { + sourceSets*.java.srcDirs*.each { it.mkdirs() } + sourceSets*.resources.srcDirs*.each { it.mkdirs() } + } } } } + +// Script to create a copy of the protobuf compiler jar in the top-level build directory +// (will make it easier to configure the intellij protobuf plugin) +repositories { + mavenLocal(); + mavenCentral(); +} + + +project.ext.protocLocation = 'com.google.protobuf:protoc:3.+' + +// Copied from Google's protobuf plugin code +File getProtocDep(protocLocation) { + // create a project configuration dependency for the artifact + Configuration config = project.configurations.create("protobufToolsLocator") { + visible = false + transitive = false + extendsFrom = [] + } + + def groupId, artifact, version + (groupId, artifact, version) = protocLocation.split(":") + def notation = [group: groupId, + name: artifact, + version: version, + classifier: project.osdetector.classifier, + ext: 'exe'] + Dependency dep = project.dependencies.add(config.name, notation) + + File file = config.fileCollection(dep).singleFile + + return file +} + + +task getprotoc(type: Copy) { + from getProtocDep(project.ext.protocLocation) + rename { file -> return "protoc" } + into "${buildDir}/" +} + diff --git a/build.gradle-template b/build.gradle-template index 638c953..9832d46 100644 --- a/build.gradle-template +++ b/build.gradle-template @@ -25,8 +25,11 @@ ext { // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" } description = "TODO: Add a description" @@ -131,17 +134,19 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { destinationDir = buildDir - def fatMain = hasProperty('fatmain') ? fatmain : mainClassName + def fatMain + + if (this.hasProperty('fatmain')) { + fatMain = fatmain + appendix = "fat-${fatMain}" + } else { + fatMain = mainClassName + appendix = "fat" + } applicationClass fatMain - def testJar = hasProperty('test') - - if (hasProperty('fatmain')) { - appendix = "fat-${fatMain}" - } else { - appendix = "fat" - } + def testJar = this.hasProperty('test') if (testJar) { from sourceSets.test.output @@ -205,12 +210,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/bulletin-board-client/build.gradle b/bulletin-board-client/build.gradle index 8f00228..0365e0b 100644 --- a/bulletin-board-client/build.gradle +++ b/bulletin-board-client/build.gradle @@ -16,12 +16,11 @@ ext { isSnapshot = false } ext { groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" } description = "Meerkat Voting Common Library" @@ -38,23 +37,10 @@ dependencies { compile project(':meerkat-common') compile project(':restful-api-common') - // Jersey for RESTful API - compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.22.+' + + // Databases compile 'org.xerial:sqlite-jdbc:3.7.+' - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - - // Crypto - compile 'org.factcenter.qilin:qilin:1.1+' - compile 'org.bouncycastle:bcprov-jdk15on:1.53' - compile 'org.bouncycastle:bcpkix-jdk15on:1.53' - // Depend on test resources from meerkat-common testCompile project(path: ':meerkat-common', configuration: 'testOutput') @@ -178,34 +164,22 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { *===================================*/ repositories { - + // Use local repo if possible mavenLocal(); - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - // Use 'maven central' for other dependencies. mavenCentral() } -task "info" << { - println "Project: ${project.name}" - println "Description: ${project.description}" - println "--------------------------" - println "GroupId: $groupId" - println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" - println "" +task "info" { + doLast { + println "Project: ${project.name}" + println "Description: ${project.description}" + println "--------------------------" + println "GroupId: $groupId" + println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" + println "" + } } info.description 'Print some information about project parameters' @@ -227,12 +201,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/bulletin-board-client/gradlew b/bulletin-board-client/gradlew new file mode 120000 index 0000000..502f5a2 --- /dev/null +++ b/bulletin-board-client/gradlew @@ -0,0 +1 @@ +../gradlew \ No newline at end of file diff --git a/bulletin-board-server/build.gradle b/bulletin-board-server/build.gradle index bb28fb8..a7a0418 100644 --- a/bulletin-board-server/build.gradle +++ b/bulletin-board-server/build.gradle @@ -21,13 +21,12 @@ apply plugin: 'maven-publish' ext { isSnapshot = false } ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + groupId = 'org.factcenter.meerkat' + + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" } description = "Bulletin-board server web application" @@ -43,9 +42,6 @@ dependencies { compile project(':meerkat-common') compile project(':restful-api-common') - // Jersey for RESTful API - compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.22.+' - // JDBC connections compile 'org.springframework:spring-jdbc:4.2.+' compile 'org.xerial:sqlite-jdbc:3.8.+' @@ -56,14 +52,6 @@ dependencies { // Servlets compile 'javax.servlet:javax.servlet-api:3.0.+' - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - // Depend on test resources from meerkat-common testCompile project(path: ':meerkat-common', configuration: 'testOutput') @@ -196,37 +184,24 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { *===================================*/ repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - // Use local maven repository mavenLocal() - jcenter() + //jcenter() // Use 'maven central' for other dependencies. mavenCentral() } -task "info" << { +task "info" { + doLast { println "Project: ${project.name}" -println "Description: ${project.description}" + println "Description: ${project.description}" println "--------------------------" println "GroupId: $groupId" println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" println "" + } } info.description 'Print some information about project parameters' @@ -248,12 +223,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/distributed-key-generation/build.gradle b/distributed-key-generation/build.gradle index 6befbc4..84f3ad1 100644 --- a/distributed-key-generation/build.gradle +++ b/distributed-key-generation/build.gradle @@ -20,16 +20,16 @@ apply plugin: 'maven-publish' ext { isSnapshot = false } ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + groupId = 'org.factcenter.meerkat' + + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" + } -description = "TODO: Add a description" +description = "Distributed key generation code" // Your project version version = "0.0" @@ -41,14 +41,6 @@ dependencies { // Meerkat common compile project(':meerkat-common') - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - // Depend on test resources from meerkat-common testCompile project(path: ':meerkat-common', configuration: 'testOutput') @@ -99,7 +91,7 @@ idea { def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" - println "Adding $srcDir" +// println "Adding $srcDir" // add protobuf generated sources to generated source dir. if ("test".equals(sourceSet.name)) { testSourceDirs += file(srcDir) @@ -158,21 +150,6 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { *===================================*/ repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - // Use local maven repository mavenLocal() @@ -180,13 +157,15 @@ repositories { mavenCentral() } -task "info" << { +task "info" { + doLast { println "Project: ${project.name}" -println "Description: ${project.description}" + println "Description: ${project.description}" println "--------------------------" println "GroupId: $groupId" println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" println "" + } } info.description 'Print some information about project parameters' @@ -208,12 +187,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/Protocol.java b/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/Protocol.java index f612b07..781a585 100644 --- a/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/Protocol.java +++ b/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/Protocol.java @@ -104,7 +104,7 @@ public class Protocol extends VerifiableSecretSharing { * setter * @param parties */ - protected void setParties(Party[] parties){ + protected void setParties(Party[] parties){ this.parties = parties; } @@ -112,7 +112,7 @@ public class Protocol extends VerifiableSecretSharing { * getter * @return */ - protected Party[] getParties(){ + protected Party[] getParties(){ return parties; } diff --git a/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/User.java b/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/User.java index f7c4624..235dab6 100644 --- a/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/User.java +++ b/distributed-key-generation/src/main/java/meerkat/crypto/dkg/feldman/User.java @@ -77,7 +77,7 @@ public class User implements Runnable { * All parties participating in key generation. * parties[id-1] has my info. */ - protected final Party[] parties; + protected final Party[] parties; /** * set of all non-disqualified parties diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7047800..2f94d87 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,5 +3,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip -distributionSha256Sum=43be380834a13e28e9504c21f67fe1a8895ab54f314a6596601896dca7213482 +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionSha256Sum=71a787faed83c4ef21e8464cc8452b941b5fcd575043aa29d39d15d879be89f7 diff --git a/meerkat-common/build.gradle b/meerkat-common/build.gradle index 896a547..e019d59 100644 --- a/meerkat-common/build.gradle +++ b/meerkat-common/build.gradle @@ -19,13 +19,12 @@ mainClassName='Demo' ext { isSnapshot = false } ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + groupId = 'org.factcenter.meerkat' + + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" } description = "Meerkat Voting Common Library" @@ -39,10 +38,12 @@ version += "${isSnapshot ? '-SNAPSHOT' : ''}" dependencies { // Logging compile 'org.slf4j:slf4j-api:1.7.7' - compile 'javax.ws.rs:javax.ws.rs-api:2.0.+' runtime 'ch.qos.logback:logback-classic:1.1.2' runtime 'ch.qos.logback:logback-core:1.1.2' + // RESTful API + compile 'javax.ws.rs:javax.ws.rs-api:2.0.+' + // Google protobufs compile 'com.google.protobuf:protobuf-java:3.+' @@ -157,32 +158,20 @@ task fatCapsule(type: FatCapsule){ repositories { mavenLocal(); - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } // Use 'maven central' for other dependencies. mavenCentral() } -task "info" << { +task "info" { + doLast { println "Project: ${project.name}" -println "Description: ${project.description}" + println "Description: ${project.description}" println "--------------------------" println "GroupId: $groupId" println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" println "" + } } info.description 'Print some information about project parameters' @@ -204,12 +193,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/meerkat-common/src/main/java/meerkat/comm/MessageInputStream.java b/meerkat-common/src/main/java/meerkat/comm/MessageInputStream.java index b1e5255..bc7c118 100644 --- a/meerkat-common/src/main/java/meerkat/comm/MessageInputStream.java +++ b/meerkat-common/src/main/java/meerkat/comm/MessageInputStream.java @@ -15,11 +15,14 @@ import java.util.List; */ public class MessageInputStream implements Iterable{ + Class type; + private T.Builder builder; private InputStream in; MessageInputStream(InputStream in, Class type) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + this.type = type; this.in = in; this.builder = (T.Builder) type.getMethod("newBuilder").invoke(type); } @@ -41,7 +44,7 @@ public class MessageInputStream implements Iterable{ @Override public T next() { try { - return readMessage(); + return readMessage(type); } catch (IOException e) { return null; } @@ -65,12 +68,10 @@ public class MessageInputStream implements Iterable{ } - public T readMessage() throws IOException{ - + public T readMessage(Class msgType) throws IOException{ builder.clear(); builder.mergeDelimitedFrom(in); - return (T) builder.build(); - + return msgType.cast(builder.build()); } public boolean isAvailable() throws IOException { @@ -82,7 +83,7 @@ public class MessageInputStream implements Iterable{ List list = new LinkedList<>(); while (isAvailable()){ - list.add(readMessage()); + list.add(readMessage(type)); } return list; diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java index 31ad4c1..7264cd0 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECElGamalEncryption.java @@ -80,6 +80,45 @@ public class ECElGamalEncryption implements Encryption { elGamalPK = new ECElGamal.PK(group, ((ECPublicKeyParameters) keyParam).getQ()); } + /** + * Encode a group element into a protobuf. + * @param p + * @return + */ + public ConcreteCrypto.GroupElement encodeElement(ECPoint p) { + return encodeElement(group, p); + } + + + /** + * Encode a group element into a protobuf. + * + * @param group group to use for encoding + * @param p element to encode. + * @return + */ + public static ConcreteCrypto.GroupElement encodeElement(ECGroup group, ECPoint p) { + return ConcreteCrypto.GroupElement.newBuilder().setData(ByteString.copyFrom(p.getEncoded(true))).build(); + } + + /** + * Decode from the serialized representation to an {@link ECPoint} object. + * @param bs + * @return + */ + public ECPoint decodeElement(ConcreteCrypto.GroupElement bs) { + return decodeElement(group, bs); + } + + /** + * Decode from the serialized representation to an {@link ECPoint} object. + * @param group group to use for decoding. + * @param bs + * @return + */ + public static ECPoint decodeElement(ECGroup group, ConcreteCrypto.GroupElement bs) { + return group.decode(bs.getData().toByteArray()); + } @Override public Crypto.RerandomizableEncryptedMessage encrypt(Message plaintext, Crypto.EncryptionRandomness rnd) { @@ -95,11 +134,11 @@ public class ECElGamalEncryption implements Encryption { byte[] msg = out.toByteArray(); ECPoint encodedMsg = group.injectiveEncode(msg, new PRGRandom(msg)); - BigInteger rndInt = BigIntegers.fromUnsignedByteArray(rnd.getData().toByteArray()); + BigInteger rndInt = extractRandomness(rnd); Pair cipherText = elGamalPK.encrypt(encodedMsg, rndInt); ConcreteCrypto.ElGamalCiphertext encodedCipherText = ConcreteCrypto.ElGamalCiphertext.newBuilder() - .setC1(ByteString.copyFrom(cipherText.a.getEncoded(true))) - .setC2(ByteString.copyFrom(cipherText.b.getEncoded(true))) + .setC1(encodeElement(cipherText.a)) + .setC2(encodeElement(cipherText.b)) .build(); return Crypto.RerandomizableEncryptedMessage.newBuilder() @@ -118,34 +157,49 @@ public class ECElGamalEncryption implements Encryption { @Override public Crypto.RerandomizableEncryptedMessage rerandomize(Crypto.RerandomizableEncryptedMessage msg, Crypto.EncryptionRandomness rnd) throws InvalidProtocolBufferException { - BigInteger rndInt = BigIntegers.fromUnsignedByteArray(rnd.getData().toByteArray()); + BigInteger rndInt = extractRandomness(rnd); Pair randomizer = elGamalPK.encrypt(curve.getInfinity(), rndInt); ConcreteCrypto.ElGamalCiphertext originalEncodedCipher= ConcreteCrypto.ElGamalCiphertext.parseFrom(msg.getData()); Pair originalCipher = new Pair( - curve.decodePoint(originalEncodedCipher.getC1().toByteArray()), - curve.decodePoint(originalEncodedCipher.getC2().toByteArray())); + decodeElement(originalEncodedCipher.getC1()), + decodeElement(originalEncodedCipher.getC2())); Pair newCipher = elGamalPK.add(originalCipher, randomizer); return Crypto.RerandomizableEncryptedMessage.newBuilder() .setData( ConcreteCrypto.ElGamalCiphertext.newBuilder() - .setC1(ByteString.copyFrom(newCipher.a.getEncoded(true))) - .setC2(ByteString.copyFrom(newCipher.b.getEncoded(true))) + .setC1(encodeElement(newCipher.a)) + .setC2(encodeElement(newCipher.b)) .build().toByteString() ).build(); } @Override public Crypto.EncryptionRandomness generateRandomness(Random rand) { - BigInteger randomInt = new BigInteger(group.getCurveParams().getN().bitLength() - 1, rand); + BigInteger randomInt = generateRandomExponent(rand); Crypto.EncryptionRandomness retval = Crypto.EncryptionRandomness.newBuilder() .setData(ByteString.copyFrom(BigIntegers.asUnsignedByteArray(randomInt))).build(); return retval; } + /** + * Return the discrete log of a random element in the group (i.e., a random value in Z_{groupOrder}) + * @param rand + * @return + */ + public BigInteger generateRandomExponent(Random rand) { + return new BigInteger(group.getCurveParams().getN().bitLength() - 1, rand); + } + + /** + * Extract (deserialize) the random exponent from a {@link Crypto.EncryptionRandomness} protobuf. + * + * @param encryptionRandomness + * @return + */ public BigInteger extractRandomness(Crypto.EncryptionRandomness encryptionRandomness){ - return new BigInteger(1,encryptionRandomness.getData().toByteArray()); + return BigIntegers.fromUnsignedByteArray(encryptionRandomness.getData().toByteArray()); } } diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/Util.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/Util.java new file mode 100644 index 0000000..e3b83d1 --- /dev/null +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/Util.java @@ -0,0 +1,120 @@ +package meerkat.crypto.concrete; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import meerkat.protobuf.ConcreteCrypto; +import meerkat.protobuf.Crypto; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.math.ec.ECPoint; +import org.factcenter.qilin.primitives.concrete.ECElGamal; +import org.factcenter.qilin.primitives.concrete.ECGroup; +import org.factcenter.qilin.primitives.generic.ElGamal; +import org.factcenter.qilin.util.Pair; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; + +/** + * Created by talm on 02/11/16. + */ +public class Util { + + public final static String ENCRYPTION_KEY_ALGORITHM = ECElGamalEncryption.KEY_ALGORITHM; + + /** + * Decode a BigInteger from a protobuf + * @param i + * @return + */ + public static BigInteger decodeBigInteger(Crypto.BigInteger i) { + return new BigInteger(i.getData().toByteArray()); + } + + /** + * Encode a BigInteger in a protobuf. + * @param i + * @return + */ + public static Crypto.BigInteger encodeBigInteger(BigInteger i) { + return Crypto.BigInteger.newBuilder().setData(ByteString.copyFrom(i.toByteArray())).build(); + } + + /** + * Serialize an El-Gamal public key into a form acceptable by {@link ECElGamalEncryption} + * @param pk + * @return + */ + public static ConcreteCrypto.ElGamalPublicKey encodePK(ECGroup group, ElGamal.PK pk) { + ECPoint pkPoint = pk.getPK(); + ECParameterSpec params = group.getCurveParams(); + + ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(pkPoint, params); + + try { + KeyFactory fact = KeyFactory.getInstance(ENCRYPTION_KEY_ALGORITHM, + GlobalCryptoSetup.getInstance().getBouncyCastleProvider()); + PublicKey javaPk = fact.generatePublic(pubKeySpec); + ConcreteCrypto.ElGamalPublicKey serializedPk = ConcreteCrypto.ElGamalPublicKey.newBuilder() + .setSubjectPublicKeyInfo(ByteString.copyFrom(javaPk.getEncoded())).build(); + + return serializedPk; + } catch (Exception e) { + throw new RuntimeException("Error converting public key!", e); + } + } + + /** + * Deserialize an ECElGamal secret key. + * @param serializedPk + * @param serializedSk + * @return + * @throws InvalidKeySpecException + */ + public static ECElGamal.SK decodeSK(ConcreteCrypto.ElGamalPublicKey serializedPk, Crypto.BigInteger serializedSk) + throws InvalidKeySpecException { + ECElGamalEncryption enc = new ECElGamalEncryption(); + enc.init(serializedPk); + ECElGamal.SK sk = new ECElGamal.SK(enc.getGroup(), decodeBigInteger(serializedSk)); + return sk; + } + + /** + * Standard (non-threshold) decryption for testing purposes. + * @param secretKey + * @return + */ + public static T decrypt(Class plaintextMessageType, ECElGamal.SK secretKey, ECGroup group, Crypto.RerandomizableEncryptedMessage opaqueCipher) + throws InvalidProtocolBufferException { + ConcreteCrypto.ElGamalCiphertext cipherText = ConcreteCrypto.ElGamalCiphertext.parseFrom(opaqueCipher.getData()); + ConcreteCrypto.GroupElement c1encoded = cipherText.getC1(); + ConcreteCrypto.GroupElement c2encoded = cipherText.getC2(); + + ECPoint c1 = ECElGamalEncryption.decodeElement(group, c1encoded); + ECPoint c2 = ECElGamalEncryption.decodeElement(group, c2encoded); + + assert (group.contains(c1)); + assert (group.contains(c2)); + + ECPoint plaintextEncoded = secretKey.decrypt(new Pair(c1, c2)); + + byte[] plaintext = group.injectiveDecode(plaintextEncoded); + + ByteArrayInputStream in = new ByteArrayInputStream(plaintext); + + try { + java.lang.reflect.Method newBuilder = plaintextMessageType.getMethod("newBuilder"); + Message.Builder builder = (Message.Builder) newBuilder.invoke(plaintextMessageType); + builder.mergeDelimitedFrom(in); + return plaintextMessageType.cast(builder.build()); + } catch (Exception e) { + throw new InvalidProtocolBufferException("Plaintext protobuf error"); + } + } + + +} diff --git a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mix2ZeroKnowledgeProver.java b/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mix2ZeroKnowledgeProver.java deleted file mode 100644 index 487483e..0000000 --- a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mix2ZeroKnowledgeProver.java +++ /dev/null @@ -1,20 +0,0 @@ -package meerkat.crypto.mixnet; - -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.protobuf.Crypto; -import meerkat.protobuf.Mixing; - -/** - * Prove in zero knowledge that two ciphertexts are a mix of two original ciphertexts. - */ -public interface Mix2ZeroKnowledgeProver { - public Mixing.ZeroKnowledgeProof prove(Crypto.RerandomizableEncryptedMessage in1, - Crypto.RerandomizableEncryptedMessage in2, - Crypto.RerandomizableEncryptedMessage out1, - Crypto.RerandomizableEncryptedMessage out2, - boolean switched,int i,int j, int layer, // switch info - Crypto.EncryptionRandomness r1, - Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException; - - -} diff --git a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mix2ZeroKnowledgeVerifier.java b/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mix2ZeroKnowledgeVerifier.java deleted file mode 100644 index f98f0ac..0000000 --- a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mix2ZeroKnowledgeVerifier.java +++ /dev/null @@ -1,24 +0,0 @@ -package meerkat.crypto.mixnet; - -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.protobuf.Crypto; -import meerkat.protobuf.Mixing; - -/** - * Verify the two-ciphertext mix proof - */ -public interface Mix2ZeroKnowledgeVerifier { - /** - * Return true iff the proof is valid. - * @param in1 - * @param in2 - * @param out1 - * @param out2 - * @return - */ - boolean verify(Crypto.RerandomizableEncryptedMessage in1, - Crypto.RerandomizableEncryptedMessage in2, - Crypto.RerandomizableEncryptedMessage out1, - Crypto.RerandomizableEncryptedMessage out2, - Mixing.ZeroKnowledgeProof proof) throws InvalidProtocolBufferException; -} diff --git a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mixer.java b/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mixer.java deleted file mode 100644 index a9b61d7..0000000 --- a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Mixer.java +++ /dev/null @@ -1,15 +0,0 @@ -package meerkat.crypto.mixnet; - -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.protobuf.Crypto; - -import java.util.List; -import java.util.Random; - -/** - * Created by talm on 25/10/15. - */ -public interface Mixer { - public MixerOutput mix(List ciphertexts,Random random) - throws InvalidProtocolBufferException; -} diff --git a/meerkat-common/src/main/java/meerkat/crypto/mixnet/MixerOutput.java b/meerkat-common/src/main/java/meerkat/crypto/mixnet/MixerOutput.java deleted file mode 100644 index eb71e61..0000000 --- a/meerkat-common/src/main/java/meerkat/crypto/mixnet/MixerOutput.java +++ /dev/null @@ -1,13 +0,0 @@ -package meerkat.crypto.mixnet; - -import meerkat.protobuf.Crypto; -import meerkat.protobuf.Mixing; - -/** - * Created by Tzlil on 1/18/2016. - */ -public interface MixerOutput { - public Mixing.ZeroKnowledgeProof[][] getProofs(); - public Crypto.RerandomizableEncryptedMessage[][] getEncryptedMessages(); - public int getN(); -} diff --git a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Trustee.java b/meerkat-common/src/main/java/meerkat/crypto/mixnet/Trustee.java deleted file mode 100644 index e8044cd..0000000 --- a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Trustee.java +++ /dev/null @@ -1,7 +0,0 @@ -package meerkat.crypto.mixnet; - -/** - * Created by talm on 25/10/15. - */ -public class Trustee { -} diff --git a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Verifier.java b/meerkat-common/src/main/java/meerkat/crypto/mixnet/Verifier.java deleted file mode 100644 index b733317..0000000 --- a/meerkat-common/src/main/java/meerkat/crypto/mixnet/Verifier.java +++ /dev/null @@ -1,7 +0,0 @@ -package meerkat.crypto.mixnet; - -/** - * Created by talm on 25/10/15. - */ -public class Verifier { -} diff --git a/meerkat-common/src/main/proto/meerkat/comm.proto b/meerkat-common/src/main/proto/meerkat/comm.proto index 6808288..c509165 100644 --- a/meerkat-common/src/main/proto/meerkat/comm.proto +++ b/meerkat-common/src/main/proto/meerkat/comm.proto @@ -10,4 +10,4 @@ message BroadcastMessage { bool is_private = 3; bytes payload = 5; -} \ No newline at end of file +} diff --git a/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto b/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto index d8c40d3..4fa6857 100644 --- a/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto +++ b/meerkat-common/src/main/proto/meerkat/concrete_crypto.proto @@ -14,9 +14,13 @@ message ElGamalPublicKey { bytes subject_public_key_info = 1; } -// An El-Gamal ciphertext // Each group element should be an ASN.1 encoded curve point with compression. +message GroupElement { + bytes data = 1; +} + +// An El-Gamal ciphertext message ElGamalCiphertext { - bytes c1 = 1; // First group element - bytes c2 = 2; // Second group element + GroupElement c1 = 1; // First group element + GroupElement c2 = 2; // Second group element } \ No newline at end of file diff --git a/meerkat-common/src/main/proto/meerkat/mixing.proto b/meerkat-common/src/main/proto/meerkat/mixing.proto deleted file mode 100644 index 83c87d6..0000000 --- a/meerkat-common/src/main/proto/meerkat/mixing.proto +++ /dev/null @@ -1,62 +0,0 @@ -syntax = "proto3"; - -package meerkat; - -option java_package = "meerkat.protobuf"; - -import 'meerkat/crypto.proto'; - -message Plaintext{ - bytes data = 1; -} - -message ZeroKnowledgeProof { - message OrProof { - message ForRandomOracle{ - bytes g1 = 1; - bytes h1 = 2; - bytes g2 = 3; - bytes h2 = 4; - bytes g1Tag = 5; - bytes h1Tag = 6; - bytes g2Tag = 7; - bytes h2Tag = 8; - bytes u = 9; - bytes v = 10; - bytes uTag = 11; - bytes vTag = 12; - } - //input : g1,h1, g2, h2, g1Tag, h1Tag, g2Tag, h2Tag; - bytes g1 = 1; - bytes h1 = 2; - bytes g2 = 3; - bytes h2 = 4; - bytes g1Tag = 5; - bytes h1Tag = 6; - bytes g2Tag = 7; - bytes h2Tag = 8; - - //calc: u, v, uTag, vTag; - bytes u = 9; - bytes v = 10; - bytes uTag = 11; - bytes vTag = 12; - - //generated: c1,c2,z,zTag - bytes c1 = 13; - bytes c2 = 14; - bytes z = 15; - bytes zTag = 16; - } - message Location{ - int32 i = 1; - int32 j = 2; - int32 layer = 3; - } - - OrProof first = 1; - OrProof second = 2; - OrProof third = 3; - OrProof fourth = 4; - Location location = 5; -} diff --git a/meerkat-common/src/main/proto/meerkat/voting.proto b/meerkat-common/src/main/proto/meerkat/voting.proto index 6e03a5b..0907adc 100644 --- a/meerkat-common/src/main/proto/meerkat/voting.proto +++ b/meerkat-common/src/main/proto/meerkat/voting.proto @@ -123,7 +123,7 @@ message ElectionParams { // Verification keys for valid mixers. repeated SignatureVerificationKey mixerVerificationKeys = 4; - // How many mixers must participate for the mixing to be considered valid + // How many mixers must participate for the network to be considered valid uint32 mixerThreshold = 5; // questions to first indicate the voter's channel diff --git a/meerkat-common/src/test/java/meerkat/comm/MessageStreamTest.java b/meerkat-common/src/test/java/meerkat/comm/MessageStreamTest.java index 58dc7c1..27eeca3 100644 --- a/meerkat-common/src/test/java/meerkat/comm/MessageStreamTest.java +++ b/meerkat-common/src/test/java/meerkat/comm/MessageStreamTest.java @@ -81,7 +81,7 @@ public class MessageStreamTest { new ByteArrayInputStream(stream.toByteArray()), BulletinBoardMessage.class); - assertThat("Retrieved message was not identical to send message", comparator.compare(message, in.readMessage()), is(equalTo(0))); + assertThat("Retrieved message was not identical to send message", comparator.compare(message, in.readMessage(BulletinBoardMessage.class)), is(equalTo(0))); } catch (IOException e) { 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..e1368e5 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,13 +67,13 @@ 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()); + ECPoint c1 = ECElGamalEncryption.decodeElement(group, c1encoded); + ECPoint c2 = ECElGamalEncryption.decodeElement(group, c2encoded); - ECPoint plaintextEncoded = secretKey.decrypt(new Pair(c1, c2)); + ECPoint plaintextEncoded = secretKey.decrypt(new Pair<>(c1, c2)); byte[] plaintext = group.injectiveDecode(plaintextEncoded); diff --git a/mixer/build.gradle b/mixer/build.gradle index 2dab24c..2cbf45d 100644 --- a/mixer/build.gradle +++ b/mixer/build.gradle @@ -13,26 +13,25 @@ apply plugin: 'maven-publish' // Uncomment the lines below to define an application // (this will also allow you to build a "fatCapsule" which includes // the entire application, including all dependencies in a single jar) -//apply plugin: 'application' -//mainClassName='your.main.ApplicationClass' +apply plugin: 'application' +mainClassName='meerkat.mixer.main.Mix' // Is this a snapshot version? ext { isSnapshot = false } ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + groupId = 'org.factcenter.meerkat' + + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" } -description = "TODO: Add a description" +description = "Mix network implementation" // Your project version -version = "0.0" +version = "0.1" version += "${isSnapshot ? '-SNAPSHOT' : ''}" @@ -41,16 +40,8 @@ dependencies { // Meerkat common compile project(':meerkat-common') - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - - // Crypto - compile 'org.factcenter.qilin:qilin:1.2.+' + // Command-line parsing. + compile 'net.sf.jopt-simple:jopt-simple:6.+' testCompile 'junit:junit:4.+' @@ -84,7 +75,7 @@ idea { def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" - println "Adding $srcDir" +// println "Adding $srcDir" // add protobuf generated sources to generated source dir. if ("test".equals(sourceSet.name)) { testSourceDirs += file(srcDir) @@ -143,21 +134,6 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { *===================================*/ repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - // Use local maven repository mavenLocal() @@ -165,13 +141,15 @@ repositories { mavenCentral() } -task "info" << { +task "info" { + doLast { println "Project: ${project.name}" -println "Description: ${project.description}" + println "Description: ${project.description}" println "--------------------------" println "GroupId: $groupId" println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" println "" + } } info.description 'Print some information about project parameters' @@ -193,12 +171,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/mixer/docs/concrete_crypto.proto b/mixer/docs/concrete_crypto.proto new file mode 120000 index 0000000..971513e --- /dev/null +++ b/mixer/docs/concrete_crypto.proto @@ -0,0 +1 @@ +../../meerkat-common/src/main/proto/meerkat/concrete_crypto.proto \ No newline at end of file diff --git a/mixer/docs/crypto.proto b/mixer/docs/crypto.proto new file mode 120000 index 0000000..acd418e --- /dev/null +++ b/mixer/docs/crypto.proto @@ -0,0 +1 @@ +../../meerkat-common/src/main/proto/meerkat/crypto.proto \ No newline at end of file diff --git a/mixer/docs/mixer.md b/mixer/docs/mixer.md new file mode 100644 index 0000000..c668cd9 --- /dev/null +++ b/mixer/docs/mixer.md @@ -0,0 +1,104 @@ +#Mixer File Format +This document attempts to provide sufficient information to write a verifier for our mixnet implementation. + +## High-Level Overview. +The mixer implements Abe's Permutation-network-based mix. The on-disk format of the mixer's output consists of a sequence of protobif messages, written using their writeDelimitedTo() method: +1. A [MixBatchHeader](mixing.proto) message containing the number of switch layers and the number of ciphertexts in each layer (where the latter is given as the base-2 log of the number, since the actual number must be a power of 2). +2. A matrix of ciphertext messages. The matrix is given column by column; the total number of columns is the number of layers + 1 (the first column is the input to the mix, and the final column is the output). The first message in the sequence is ciphertext 0 in column 0, then ciphertext 1 in layer 0, finally ending with ciphertext $2^{logN}-1$ in column $layers$. +3. A matrix of proofs for each 2x2 switch. The matrix is given column by column; the total number of columns is the number of layers. The first message in the matrix sequence is switch 0 in column 0, then switch 1 in column 0, finally ending with switch $2^{logN-1}-1$ in column $layers-1$. + + +## Ciphertexts +For future compatibility, each ciphertext actually written to disk is encoded as an "opaque" [RerandomizableEncryptedMessage](crypto.proto). The data field of this message contains the serialized ElGamalCiphertext message (see below). + +### EC-ElGamal Ciphertexts +Ciphertexts are serialized using the [ElGamalCiphertext](concrete_crypto.proto) message, with fields "c1" and "c2" for the first and second group elements. + +### EC Group elements +Group elements use the [GroupElement](concrete_crypto.proto) message. It's only field is "data", which should be an ASN.1-encoded curve point with compression (see section 4.3.6 of [X9.62-1998](https://www.security-audit.com/files/x9-62-09-20-98.pdf) "Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)") + +### EC-ElGamal Key Format +The ECElGamal Key is stored in the [ElGamalPublicKey](concrete_crypto.proto) message that contains a standard DER-encoded SubjectPublicKeyInfo as in [RFC 3279](https://tools.ietf.org/html/rfc3279) (note that this encoding includes the elliptic-curve group parameters). + + + +## Proofs +Each mixing proof is a serialized [Mix2Proof](mixing.proto) message. This consists of a firstMessage field (containing the first message of the Sigma protocol) and a finalMessage field(containing the final message of the Sigma protocol). The challenge is derived from the *serialized form* of the firstMessage field by hashing (see [Fiat-Shamir](#fiat-shamir-random-oracle) below for the hash algorithm), a finalMessage field and a location field (see [Location](#location) below). + +### Proof Statement +The proof statement itself is a disjunction of two conjunctions of Schnorr proofs for discrete-log equivalence, constructed from the ciphertexts. Let $g$ be the group generator and $h$ the ElGamal public key. Given input ciphertexts $a=(a_1,a_2)$ and +$b=(b_1,b_2)$ and output ciphertexts $c=(c_1,c_2)$ and $d=(d_1,d_2)$, the statement proved is: +$$ +(\log_g \frac{c_1}{a_1} = \log_h\frac{c_2}{a_2})\text{ AND } (\log_g \frac{d_1}{b_1} = \log_h\frac{d_2}{b_2}) \\ +\text{OR}\\ +(\log_g \frac{d_1}{a_1} = \log_h\frac{d_2}{a_2}) \text{ AND } (\log_g \frac{c_1}{b_1} = \log_h\frac{c_2}{b_2}) +$$ + +The firstMessage and finalMessage fields are generated using the sigma-protocol disjunction composition of the sigma protocols for the two conjuction statements, and those in turn are generated using the sigma-protocol conjunction statement of the basic Schnorr proofs (more details in the [protobuf file](mixing.proto)). + +### Location +The location field gives the layer number (column) and switchIdx (index within the column) for the 2x2 switch which this proof references. The input ciphertexts to switch $s$ in layer $i$ are in positions $2s$, $2s+1$ in column $i$ of the ciphertext matrix. The indices of the output ciphertexts in column $i+1$ are given in the location field (out0 is the index of the first output ciphertext and out1 the second). + +### Integers +Integers are serialized using the [BigInteger](crypto.proto) protobuf message, whose data field contains the integer encoded as in Java's [BigInteger.toByteArray()](http://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html#toByteArray()) method. + + +### Fiat-Shamir "Random Oracle" +The Fiat-Shamir heuristic uses a "random oracle". We use the following java code as the implementation of the oracle. + + + public BigInteger hash(byte[] input) { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.reset(); + + byte[] digest = md.digest(input); + return new BigInteger(1,digest); + } + +Note that our Sigma-protocols use Integers as a challenge, so the random oracle returns a BigInteger. The input to the oracle is always a protobuf Message; we convert it to a byte array using its toByteArray() method. + +## The Mixer Command-line Utility +The Mixer application can generate keys, encrypt, mix, verify and decrypt. The jar file can be run using "java -jar mixer.jar" + +Run + + java -jar mixer.jar -h + +To get a summary of command line options. +### Examples + +#### Generate a new Key-Pair + + java -jar mixer.jar -g -k ecelgamal.key + +Generates the keys in the file ecelgamal.key + +#### Encrypt some plaintexts +(run after generating keys) + + java -jar mixer.jar -k ecelgamal.key -e -i infile.txt -o outfile.enc + +**infile.txt** should consist of multiple lines; each line is used as a plaintext. If you get a warning message about inputs being truncated, you must ensure the longest line is shorter than about 20 chars (each plaintext must fit into a single group element, and there is some extra protobuf overhead) + +**outfile.enc** will contain a 0-layer mixnet (i.e., a single column of ciphertexts). The plaintexts will be padded with empty lines to the nearest power of two. + +#### Run the mix network +(run after encrypting some plaintexts) + + java -jar mixer.jar -k ecelgamal.key -i outfile.enc -o mixed.enc + +**mixed.enc** will contain the mixnet output including proofs. + +#### Verify the mix + + java -jar mixer.jar -k ecelgamal.key -i mixed.enc + +This doesn't write output. You can add the "-s" flag for strict verification; strict verification +checks that the Benes network structure matches our implementation exactly (otherwise +the verifier just ensures that the output is a permutation of the input). + +#### Decrypt the output + + java -jar mixer.jar -k ecelgamal.key -d -i mixed.enc -o decrypted.txt + + diff --git a/mixer/docs/mixing.proto b/mixer/docs/mixing.proto new file mode 120000 index 0000000..0f877ce --- /dev/null +++ b/mixer/docs/mixing.proto @@ -0,0 +1 @@ +../src/main/proto/meerkat/mixing.proto \ No newline at end of file diff --git a/mixer/gradlew b/mixer/gradlew new file mode 120000 index 0000000..502f5a2 --- /dev/null +++ b/mixer/gradlew @@ -0,0 +1 @@ +../gradlew \ No newline at end of file diff --git a/mixer/src/main/java/meerkat/mixer/mixing/Mixer.java b/mixer/src/main/java/meerkat/mixer/MixGenerator.java similarity index 50% rename from mixer/src/main/java/meerkat/mixer/mixing/Mixer.java rename to mixer/src/main/java/meerkat/mixer/MixGenerator.java index fbe091c..9ba10c2 100644 --- a/mixer/src/main/java/meerkat/mixer/mixing/Mixer.java +++ b/mixer/src/main/java/meerkat/mixer/MixGenerator.java @@ -1,29 +1,30 @@ -package meerkat.mixer.mixing; +package meerkat.mixer; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.Encryption; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeProver; -import meerkat.crypto.mixnet.MixerOutput; +import meerkat.mixer.network.BenesNetwork; +import meerkat.mixer.network.PermutationNetwork; +import meerkat.mixer.network.RandomPermutation; +import meerkat.mixer.proofs.Mix2nizk; import meerkat.protobuf.Crypto.EncryptionRandomness; import meerkat.protobuf.Crypto.RerandomizableEncryptedMessage; -import meerkat.protobuf.Mixing.ZeroKnowledgeProof; +import meerkat.protobuf.Mixing.Mix2Proof; import java.util.List; import java.util.Random; /** - * an implementation of meerkat.crypto.mixnet.Mixer - * meerkat.mixer.mixing algorithm on set of n encrypted votes: - * 0. asset n is power of two - * 1. set switches according to benes network on random permutation + * Run Abe's permutation network mixing algorithm on set of n encrypted votes: + * 0. n is power of two + * 1. set switches according to Benes network on random permutation * 2. re encrypt and mix with respect to switches values (encryptor.rerandomize) * 3. generate zero knowledge proof on each re encrypted couple * 4. return proofs table + encryption table * */ -public class Mixer implements meerkat.crypto.mixnet.Mixer { +public class MixGenerator { - private final Mix2ZeroKnowledgeProver prover; + private final Mix2nizk.Prover prover; private final Encryption encryptor; /** @@ -31,7 +32,7 @@ public class Mixer implements meerkat.crypto.mixnet.Mixer { * @param prover * @param encryptor */ - public Mixer(Mix2ZeroKnowledgeProver prover, Encryption encryptor) { + public MixGenerator(Mix2nizk.Prover prover, Encryption encryptor) { this.prover = prover; this.encryptor = encryptor; } @@ -80,89 +81,92 @@ public class Mixer implements meerkat.crypto.mixnet.Mixer { /** * generate new random mix network - * @param n number of votes + * @param logN log number of votes * @param random * @return new random mix network */ - private MixNetwork generateMixNetwork(int n,Random random){ - return new MixNetwork(new RandomPermutation(n,random)); + private PermutationNetwork generateMixNetwork(int logN, Random random){ + BenesNetwork net = new BenesNetwork(logN); + RandomPermutation perm = new RandomPermutation(1 << logN, random); + net.setPermutation(perm.permutation); + return net; } + /** - * fills the encryption table with rerandomize encrypted votes. - * @param layers + * fills the encryption table with rerandomized encrypted votes. * @param mixNetwork switches table (boolean values) * @param encryptionTable an initialized encryption table s.t first layer == given encrypted votes * @param randomnesses an initialized randomness table of size layers * n, use for rerandomize operations * @throws InvalidProtocolBufferException */ - private void rerandomize(int layers,MixNetwork mixNetwork, RerandomizableEncryptedMessage[][] encryptionTable - ,EncryptionRandomness[][] randomnesses) throws InvalidProtocolBufferException { - Switch[] switchesLayer; - int index1,index2; - RerandomizableEncryptedMessage e1,e2; + private void rerandomize(PermutationNetwork mixNetwork, + RerandomizableEncryptedMessage[][] encryptionTable, + EncryptionRandomness[][] randomnesses) throws InvalidProtocolBufferException { + RerandomizableEncryptedMessage a,b; EncryptionRandomness r1,r2; - for (int layer = 0; layer < layers; layer++) - { - switchesLayer = mixNetwork.getSwitchesByLayer(layer); - for (Switch sw : switchesLayer) { - index1 = sw.i; - index2 = sw.j; - e1 = encryptionTable[layer][index1]; - e2 = encryptionTable[layer][index2]; + int layers = mixNetwork.getNumLayers(); - r1 = randomnesses[layer][index1]; - r2 = randomnesses[layer][index2]; - if (!sw.value) { - encryptionTable[layer + 1][index1] = encryptor.rerandomize(e1, r1); - encryptionTable[layer + 1][index2] = encryptor.rerandomize(e2, r2); + int numSwitches = mixNetwork.getNumInputs() >>> 1; + for (int layer = 0; layer < layers; layer++) { + for (int switchIdx = 0; switchIdx < numSwitches; ++switchIdx) { + boolean isCrossed = mixNetwork.isCrossed(layer, switchIdx); + + int out0 = mixNetwork.getInputIdxInNextLayer(layer, 2 * switchIdx); + int out1 = mixNetwork.getInputIdxInNextLayer(layer, 2 * switchIdx + 1); + + r1 = randomnesses[layer][2 * switchIdx]; + r2 = randomnesses[layer][2 * switchIdx + 1]; + + a = encryptionTable[layer][2 * switchIdx]; + b = encryptionTable[layer][2 * switchIdx + 1]; + + if (isCrossed) { + encryptionTable[layer + 1][out0] = encryptor.rerandomize(b, r2); + encryptionTable[layer + 1][out1] = encryptor.rerandomize(a, r1); } else { - encryptionTable[layer + 1][index1] = encryptor.rerandomize(e2, r2); - encryptionTable[layer + 1][index2] = encryptor.rerandomize(e1, r1); + encryptionTable[layer + 1][out0] = encryptor.rerandomize(a, r1); + encryptionTable[layer + 1][out1] = encryptor.rerandomize(b, r2); } + } } } /** * generate zero knowledge proof for each rerandomize encrypted votes couple in encryptionTable - * @param n number of votes - * @param layers * @param mixNetwork switches table (boolean values) used for set encryption table * @param encryptionTable full encryption table * @param randomnesses randomness table of size layers * n, used for set encryption table * @return zero knowledge proofs table * @throws InvalidProtocolBufferException */ - private ZeroKnowledgeProof[][] generateZeroKnowledgeProofTable(int n, int layers, MixNetwork mixNetwork + private Mix2Proof[][] generateMix2ProofTable(PermutationNetwork mixNetwork , RerandomizableEncryptedMessage[][] encryptionTable , EncryptionRandomness[][] randomnesses) throws InvalidProtocolBufferException { - Switch[] switchesLayer; - int index1,index2; - int switchIndex = 0; - int nDiv2 = n >> 1; - ZeroKnowledgeProof[][] proofsTable = new ZeroKnowledgeProof[layers][nDiv2]; + int layers = mixNetwork.getNumLayers(); + int n = mixNetwork.getNumInputs(); + int numSwitches = n >> 1; + Mix2Proof[][] proofsTable = new Mix2Proof[layers][numSwitches]; - RerandomizableEncryptedMessage e1,e2; + RerandomizableEncryptedMessage a,b,c,d; EncryptionRandomness r1,r2; - for (int layer = 0; layer < layers; layer++) - { - switchesLayer = mixNetwork.getSwitchesByLayer(layer); - for (Switch sw : switchesLayer) { - index1 = sw.i; - index2 = sw.j; - e1 = encryptionTable[layer][index1]; - e2 = encryptionTable[layer][index2]; - r1 = randomnesses[layer][index1]; - r2 = randomnesses[layer][index2]; + for (int layer = 0; layer < layers; layer++) { + for (int switchIdx = 0; switchIdx < numSwitches; ++switchIdx) { + boolean isCrossed = mixNetwork.isCrossed(layer, switchIdx); + int out0 = mixNetwork.getInputIdxInNextLayer(layer, 2 * switchIdx); + int out1 = mixNetwork.getInputIdxInNextLayer(layer, 2 * switchIdx + 1); - proofsTable[layer][switchIndex] = - prover.prove(e1, e2, encryptionTable[layer + 1][index1], - encryptionTable[layer + 1][index2], - sw.value, sw.i, sw.j, sw.layer, r1, r2); + a = encryptionTable[layer][2 * switchIdx]; + b = encryptionTable[layer][2 * switchIdx + 1]; + c = encryptionTable[layer + 1][out0]; + d = encryptionTable[layer + 1][out1]; + r1 = randomnesses[layer][2 * switchIdx]; + r2 = randomnesses[layer][2 * switchIdx + 1]; - switchIndex = (switchIndex + 1) % nDiv2; + proofsTable[layer][switchIdx] = + prover.prove(a, b, c, d, isCrossed, layer, switchIdx, out0, out1, r1, r2); } } return proofsTable; @@ -172,7 +176,7 @@ public class Mixer implements meerkat.crypto.mixnet.Mixer { * mix given encrypted votes using random * @param ciphertexts encrypted votes * @param random - * @return meerkat.mixer.mixing result + * @return meerkat.mixProverVerifier.network result * @throws InvalidProtocolBufferException */ public MixerOutput mix(List ciphertexts,Random random) throws InvalidProtocolBufferException { @@ -180,14 +184,17 @@ public class Mixer implements meerkat.crypto.mixnet.Mixer { int n = ciphertexts.size(); assert (n > 1 && isPowerOfTwo(n)); - int layers = MixNetwork.numberOfLayers(n); // layers = 2logn -1 + int logN = Integer.numberOfTrailingZeros(Integer.highestOneBit(n)); + + PermutationNetwork net = generateMixNetwork(logN,random); + + int layers = net.getNumLayers(); RerandomizableEncryptedMessage[][] encryptionTable = initializeEncryptionTable(n,layers,ciphertexts); EncryptionRandomness[][] randomnesses = generateRandomnessesForRerandomize(n,layers,random); - MixNetwork mixNetwork = generateMixNetwork(n,random); - rerandomize(layers,mixNetwork,encryptionTable,randomnesses); - ZeroKnowledgeProof[][] proofsTable = generateZeroKnowledgeProofTable(n,layers,mixNetwork,encryptionTable,randomnesses); - return new meerkat.mixer.mixing.MixerOutput(n,layers,proofsTable, encryptionTable); + rerandomize(net,encryptionTable,randomnesses); + Mix2Proof[][] proofsTable = generateMix2ProofTable(net,encryptionTable,randomnesses); + + return new meerkat.mixer.MixerOutput(logN, proofsTable, encryptionTable); } - } \ No newline at end of file diff --git a/mixer/src/main/java/meerkat/mixer/MixVerifier.java b/mixer/src/main/java/meerkat/mixer/MixVerifier.java new file mode 100644 index 0000000..677bd16 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/MixVerifier.java @@ -0,0 +1,129 @@ +package meerkat.mixer; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.mixer.network.BenesNetwork; +import meerkat.mixer.network.PermutationNetwork; +import meerkat.mixer.proofs.Mix2nizk; +import meerkat.protobuf.Crypto; +import meerkat.protobuf.Mixing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; + +/** + * Created by Tzlil on 12/30/2015. + * provide one operation - verify meerkat.mixProverVerifier.network output + */ +public final class MixVerifier { + + final static Logger logger = LoggerFactory.getLogger(MixVerifier.class); + /** + * constructor + * @param verifier + * @param mixerOutput + * @param strict if true, check that the network structure matches our implementation of the Benes network exactly. + * if false, only checks that the result is a permutation. + * @return true iff the meerkat.mixProverVerifier.network output is valid + * @throws InvalidProtocolBufferException + */ + public static boolean verifyTable(Mix2nizk.Verifier verifier, MixerOutput mixerOutput, boolean strict) + throws InvalidProtocolBufferException { + + PermutationNetwork net = null; + + int layers = mixerOutput.getNumLayers(); + int n = 1 << mixerOutput.getLogN(); + + if (strict) { + net = new BenesNetwork(mixerOutput.getLogN()); + } + + //initialize outputUsed table + // used to check BeneshNet validity + boolean[][] outputUsed = new boolean[layers][n]; + for (boolean[] outputUsedLayer: outputUsed) { + Arrays.fill(outputUsedLayer,false); + } + + boolean[][] switchUsed = new boolean[layers][n / 2]; + for (boolean[] switchUsedLayer: switchUsed) { + Arrays.fill(switchUsedLayer,false); + } + + Mixing.Mix2Proof[][] Mix2Proofs = mixerOutput.getProofs(); + Crypto.RerandomizableEncryptedMessage[][] rerandomizableEncryptedMessages = mixerOutput.getEncryptedMessages(); + + for (int i = 0; i < Mix2Proofs.length ; i++){ + for (int j = 0; j < Mix2Proofs[i].length ; j ++){ + int out0,out1,layer, switchIdx; + + Mixing.Mix2Proof zkp = Mix2Proofs[i][j]; + Mixing.Mix2Proof.Location location = zkp.getLocation(); + out0 = location.getOut0(); + out1 = location.getOut1(); + layer = location.getLayer(); + switchIdx = location.getSwitchIdx(); + + if (strict) { + // Check that location is correct + int expectedOut0 = net.getInputIdxInNextLayer(layer, 2 * switchIdx); + if (out0 != expectedOut0) { + logger.error("Input {} in layer {} goes to {} instead of {}", 2 * switchIdx, layer, out0, expectedOut0); + return false; + } + int expectedOut1 = net.getInputIdxInNextLayer(layer, 2 * switchIdx + 1); + if (out0 != expectedOut0) { + logger.error("Input {} in layer {} goes to {} instead of {}", 2 * switchIdx + 1, layer, out1, expectedOut1); + return false; + } + } + + // mark location in table + if (switchUsed[layer][switchIdx]) { + logger.error("Switch {} in layer {} used twice!", switchIdx, layer); + return false; + } + switchUsed[layer][switchIdx] = true; + + if (outputUsed[layer][out0]) { + logger.error("Output {} in layer {} used twice!", out0, layer); + return false; + } + outputUsed[layer][out0] = true; + + if (outputUsed[layer][out1]) { + logger.error("Output {} in layer {} used twice!", out1, layer); + return false; + } + outputUsed[layer][out1] = true; + + // verify proof + if(!verifier.verify(rerandomizableEncryptedMessages[layer][2 * switchIdx], + rerandomizableEncryptedMessages[layer][2 * switchIdx + 1], + rerandomizableEncryptedMessages[layer + 1][out0], + rerandomizableEncryptedMessages[layer + 1][out1], + zkp)) { + return false; + } + } + } + + // verify all meerkat.mixProverVerifier.necessary locations for BenesNet were proved + for (int layer = 0; layer < layers; ++layer) { + for (int switchIdx = 0; switchIdx < n / 2; ++switchIdx) { + if (!switchUsed[layer][switchIdx]) { + logger.error("Switch {} in layer {} was not used!", switchIdx, layer); + return false; + } + } + for (int i = 0; i < n / 2; ++i) { + if (!outputUsed[layer][i]) { + logger.error("Output {} in layer {} was not used!", i, layer); + return false; + } + } + } + return true; + } +} diff --git a/mixer/src/main/java/meerkat/mixer/MixerOutput.java b/mixer/src/main/java/meerkat/mixer/MixerOutput.java new file mode 100644 index 0000000..370fefe --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/MixerOutput.java @@ -0,0 +1,111 @@ +package meerkat.mixer; + +import meerkat.protobuf.Crypto; +import meerkat.protobuf.Mixing; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/** + * Created by Tzlil on 1/18/2016. + * container for meerkat.mixProverVerifier.network.mix result. + */ +public class MixerOutput { + private final Mixing.Mix2Proof[][] proofs; + private final Crypto.RerandomizableEncryptedMessage[][] encryptedMessages; + private final int logN; + + /** + * constructor + * @param logN log (base 2) of the number of votes + * @param encryptedMessages at level 0 , contains the original encrypted votes + * at each other level contains the re encrypted votes + * @param proofs in each cell (level,switch) contains the match zero knowledge proof. + * We allow null proofs as a special case -- this is used to store the input to the + * first mixProverVerifier (as a 0-layer mix). + */ + public MixerOutput(int logN, Mixing.Mix2Proof[][] proofs, + Crypto.RerandomizableEncryptedMessage[][] encryptedMessages) { + this.proofs = proofs; + this.encryptedMessages = encryptedMessages; + this.logN = logN; + } + + public Mixing.Mix2Proof[][] getProofs() { + return proofs; + } + + public Crypto.RerandomizableEncryptedMessage[][] getEncryptedMessages() { + return encryptedMessages; + } + + public int getLogN() { + return logN; + } + + public int getNumLayers() { + return proofs == null ? 0 : proofs.length; + } + + + /** + * Read from an InputStream. The data should have been written by {@link #writeDelimitedTo(OutputStream)} + * @param in + * @return + * @throws IOException + */ + public static MixerOutput parseDelimitedFrom(InputStream in) throws IOException { + Mixing.MixBatchHeader header = Mixing.MixBatchHeader.parseDelimitedFrom(in); + int n = 1 << header.getLogN(); + int layers = header.getLayers(); + + Crypto.RerandomizableEncryptedMessage[][] encryptedMessages = new Crypto.RerandomizableEncryptedMessage[layers + 1][n]; + Mixing.Mix2Proof[][] proofs = null; + if (layers > 0) + proofs = new Mixing.Mix2Proof[header.getLayers()][n / 2]; + + for (int i = 0; i < encryptedMessages.length; ++i) { + for (int j = 0; j < encryptedMessages[i].length; ++j) { + encryptedMessages[i][j] = Crypto.RerandomizableEncryptedMessage.parseDelimitedFrom(in); + } + } + if (layers > 0) { + for (int i = 0; i < proofs.length; ++i) { + for (int j = 0; j < proofs[i].length; ++j) { + proofs[i][j] = Mixing.Mix2Proof.parseDelimitedFrom(in); + } + } + } + return new MixerOutput(header.getLogN(), proofs, encryptedMessages); + } + + /** + * Write to an outputstream. + * This format can be parsed by {@link #parseDelimitedFrom(InputStream)} + * @param out + * @throws IOException + */ + public void writeDelimitedTo(OutputStream out) throws IOException { + Mixing.MixBatchHeader header = Mixing.MixBatchHeader.newBuilder() + .setLogN(getLogN()) + .setLayers(getNumLayers()) + .build(); + header.writeDelimitedTo(out); + + for (int i = 0; i < encryptedMessages.length; ++i) { + for (int j = 0; j < encryptedMessages[i].length; ++j) { + encryptedMessages[i][j].writeDelimitedTo(out); + } + } + if (proofs != null) { + for (int i = 0; i < proofs.length; ++i) { + for (int j = 0; j < proofs[i].length; ++j) { + proofs[i][j].writeDelimitedTo(out); + } + } + } + } + +} diff --git a/mixer/src/main/java/meerkat/mixer/main/BatchConverter.java b/mixer/src/main/java/meerkat/mixer/main/BatchConverter.java index 1ad8204..ea78f80 100644 --- a/mixer/src/main/java/meerkat/mixer/main/BatchConverter.java +++ b/mixer/src/main/java/meerkat/mixer/main/BatchConverter.java @@ -1,66 +1,38 @@ package meerkat.mixer.main; -import com.google.protobuf.ByteString; -import meerkat.crypto.mixnet.MixerOutput; +import meerkat.mixer.MixerOutput; import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.Crypto; import meerkat.protobuf.Mixing; -import java.math.BigInteger; import java.util.ArrayList; import java.util.List; /** * Created by Tzlil on 12/17/2015. - * provide convert operation from batch data to meerkat.mixer.mixing output and backwards + * provide convert operation from batch data to meerkat.mixProverVerifier.network output and backwards */ public class BatchConverter { - - private final int n,layers; - /** - * constructor - * @param n - * @param layers - */ - public BatchConverter(int n,int layers){ - this.n = n; - this.layers = layers; - } - - /** - * convert integer to byte string - * @param a - * @return a as byte string - */ - private ByteString Integer2ByteString(int a){ - return ByteString.copyFrom(BigInteger.valueOf(a).toByteArray()); - } - - /** - * convert byte string to integer - * @param bs - * @return bs as int - */ - private int ByteString2Integer(ByteString bs) { - return Integer.valueOf(bs.toString()); - } - - /** - * convert meerkat.mixer.mixing output to batch data + * convert meerkat.mixProverVerifier.network output to batch data * @param mixerOutput - * @return meerkat.mixer.mixing output as list of batch data + * @return meerkat.mixProverVerifier.network output as list of batch data */ public List MixerOutput2BatchChunk(MixerOutput mixerOutput) { List result = new ArrayList(); + Mixing.MixBatchHeader header = Mixing.MixBatchHeader.newBuilder() + .setLogN(mixerOutput.getLogN()) + .setLayers(mixerOutput.getNumLayers()) + .build(); + result.add(BulletinBoardAPI.BatchChunk.newBuilder() - .setData(Integer2ByteString(n)) + .setData(header.toByteString()) .build()); - for (Mixing.ZeroKnowledgeProof[] zkpLayer : mixerOutput.getProofs()) { - for (Mixing.ZeroKnowledgeProof zkp : zkpLayer) { + for (Mixing.Mix2Proof[] zkpLayer : mixerOutput.getProofs()) { + for (Mixing.Mix2Proof zkp : zkpLayer) { result.add(BulletinBoardAPI.BatchChunk.newBuilder() .setData(zkp.toByteString()) .build()); @@ -77,7 +49,7 @@ public class BatchConverter { } /** - * convert batch data list to meerkat.mixer.mixing output + * convert batch data list to meerkat.mixProverVerifier.network output * @param batchChunkList * @return batch data list as MixerOutput * @throws Exception @@ -85,17 +57,18 @@ public class BatchConverter { public MixerOutput BatchChunkList2MixerOutput (List batchChunkList) throws Exception { - if (n != ByteString2Integer(batchChunkList.remove(0).getData())){ - throw new Exception(); - } + Mixing.MixBatchHeader header = Mixing.MixBatchHeader.parseFrom(batchChunkList.remove(0).getData()); + int logN = header.getLogN(); + int n = 1 << logN; + int layers = header.getLayers(); int nDiv2 = n >>1; - Mixing.ZeroKnowledgeProof[][] proofs = new Mixing.ZeroKnowledgeProof[layers][nDiv2]; + Mixing.Mix2Proof[][] proofs = new Mixing.Mix2Proof[layers][nDiv2]; for (int layer = 0; layer < layers; layer++) { for (int proofIndex = 0 ; proofIndex < nDiv2 ; proofIndex ++) { - proofs[layer][proofIndex] = Mixing.ZeroKnowledgeProof.parseFrom(batchChunkList.remove(0).getData()); + proofs[layer][proofIndex] = Mixing.Mix2Proof.parseFrom(batchChunkList.remove(0).getData()); } } @@ -110,7 +83,7 @@ public class BatchConverter { } } - return new meerkat.mixer.mixing.MixerOutput(n,layers,proofs,encryptions); + return new meerkat.mixer.MixerOutput(logN, proofs,encryptions); } diff --git a/mixer/src/main/java/meerkat/mixer/main/BatchHandler.java b/mixer/src/main/java/meerkat/mixer/main/BatchHandler.java index 8356475..2fb9ef5 100644 --- a/mixer/src/main/java/meerkat/mixer/main/BatchHandler.java +++ b/mixer/src/main/java/meerkat/mixer/main/BatchHandler.java @@ -1,19 +1,19 @@ package meerkat.mixer.main; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; -import meerkat.crypto.mixnet.MixerOutput; +import meerkat.mixer.MixerOutput; +import meerkat.mixer.proofs.Mix2nizk; import meerkat.protobuf.Crypto; import meerkat.mixer.necessary.AsyncBulletinBoardClient; import meerkat.mixer.necessary.CompleteBatch; -import meerkat.mixer.verifier.VerifyTable; +import meerkat.mixer.MixVerifier; import java.util.Arrays; import java.util.List; /** * Created by Tzlil on 12/17/2015. - * implements AsyncBulletinBoardClient.ClientCallback - */ + * Handles callback for receiving a complete mix proof table from the bulletin board. + */ public class BatchHandler implements AsyncBulletinBoardClient.ClientCallback { private MixerOutput mixerOutput; @@ -21,19 +21,14 @@ public class BatchHandler implements AsyncBulletinBoardClient.ClientCallback getInputForMixer() throws Throwable { + public List getInputForMixer(boolean strictVerification) throws Throwable { if (t != null) { throw t; } - if(!verifyTable()){ - throw new Exception("in valid table"); + if(!verifyTable(strictVerification)){ + throw new Exception("invalid table"); } - return Arrays.asList(mixerOutput.getEncryptedMessages()[layers]);//there are layers + 1 + return Arrays.asList(mixerOutput.getEncryptedMessages()[mixerOutput.getNumLayers()]); //there are layers + 1 } } diff --git a/mixer/src/main/java/meerkat/mixer/main/MainMixing.java b/mixer/src/main/java/meerkat/mixer/main/MainMixing.java index 148352b..f32cb9f 100644 --- a/mixer/src/main/java/meerkat/mixer/main/MainMixing.java +++ b/mixer/src/main/java/meerkat/mixer/main/MainMixing.java @@ -1,11 +1,10 @@ package meerkat.mixer.main; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; -import meerkat.crypto.mixnet.Mixer; -import meerkat.crypto.mixnet.MixerOutput; +import meerkat.mixer.MixGenerator; +import meerkat.mixer.MixerOutput; +import meerkat.mixer.proofs.Mix2nizk; import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.Crypto; -import meerkat.mixer.mixing.MixNetwork; import meerkat.mixer.necessary.AsyncBulletinBoardClient; @@ -16,17 +15,16 @@ import java.util.Random; /** * Created by Tzlil on 12/17/2015. - * this class define all the operation meerkat.mixer.mixing party should do: + * this class define all the operation meerkat.mixProverVerifier.network party should do: * 1. receive previous mixers output (re encrypted votes + proofs) * 2. verify its input * 3. mix - * 4. send the meerkat.mixer.mixing output + * 4. send the meerkat.mixProverVerifier.network output */ public class MainMixing { - private final Mixer mixer; - private final Mix2ZeroKnowledgeVerifier verifier; - private final int n, layers; + private final MixGenerator mixer; + private final Mix2nizk.Verifier verifier; private final AsyncBulletinBoardClient asyncBulletinBoardClient; private final byte[] id; @@ -35,16 +33,13 @@ public class MainMixing { * constructor * @param mixer * @param verifier - * @param n * @param asyncBulletinBoardClient * @param id */ - public MainMixing(Mixer mixer, Mix2ZeroKnowledgeVerifier verifier, int n - , AsyncBulletinBoardClient asyncBulletinBoardClient, byte[] id) { + public MainMixing(MixGenerator mixer, Mix2nizk.Verifier verifier, + AsyncBulletinBoardClient asyncBulletinBoardClient, byte[] id) { this.mixer = mixer; this.verifier = verifier; - this.n = n; - this.layers = MixNetwork.numberOfLayers(n); this.asyncBulletinBoardClient = asyncBulletinBoardClient; this.id = id; } @@ -57,14 +52,14 @@ public class MainMixing { * @param callback * @throws Throwable */ - public void main(List prevBatchIds, int batchId, Random random, AsyncBulletinBoardClient.ClientCallback callback) throws Throwable { + public void main(List prevBatchIds, int batchId, boolean strictVerification, Random random, AsyncBulletinBoardClient.ClientCallback callback) throws Throwable { List mixerInput; List batchHandlers = new ArrayList(prevBatchIds.size()); BatchHandler currentBatchHandler; for (Integer prevBatchId : prevBatchIds) { - currentBatchHandler = new BatchHandler(n, layers,verifier); + currentBatchHandler = new BatchHandler(verifier); asyncBulletinBoardClient.readBatch(id, prevBatchId,currentBatchHandler); batchHandlers.add(currentBatchHandler); } @@ -77,13 +72,13 @@ public class MainMixing { } // assert all handlers succeeded for (BatchHandler batchHandler : batchHandlers) { - if(!batchHandler.verifyTable()){ + if(!batchHandler.verifyTable(strictVerification)){ throw new Exception("invalid input"); } } BatchHandler lastBatchHandler = batchHandlers.get(batchHandlers.size() - 1); - mixerInput = lastBatchHandler.getInputForMixer(); + mixerInput = lastBatchHandler.getInputForMixer(strictVerification); MixerOutput mixerOutput = mixer.mix(mixerInput,random); updateBB(mixerOutput, batchId, callback); @@ -91,7 +86,7 @@ public class MainMixing { } /** - * send meerkat.mixer.mixing output to BB + * send meerkat.mixProverVerifier.network output to BB * @param mixerOutput * @param batchId * @param callback @@ -99,7 +94,7 @@ public class MainMixing { private void updateBB(MixerOutput mixerOutput , int batchId, AsyncBulletinBoardClient.ClientCallback callback) { - BatchConverter batchConverter = new BatchConverter(n,layers); + BatchConverter batchConverter = new BatchConverter(); List batchChunkList = batchConverter.MixerOutput2BatchChunk(mixerOutput); asyncBulletinBoardClient.postBatch(id, batchId, batchChunkList, callback); } diff --git a/mixer/src/main/java/meerkat/mixer/main/Mix.java b/mixer/src/main/java/meerkat/mixer/main/Mix.java new file mode 100644 index 0000000..dc67dbd --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/main/Mix.java @@ -0,0 +1,296 @@ +package meerkat.mixer.main; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.StringValue; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; +import meerkat.mixer.MixGenerator; +import meerkat.mixer.MixVerifier; +import meerkat.mixer.MixerOutput; +import meerkat.mixer.proofs.concrete.Mix2nizk; +import meerkat.protobuf.ConcreteCrypto; +import meerkat.protobuf.Crypto; +import org.factcenter.qilin.primitives.concrete.ECElGamal; +import org.factcenter.qilin.primitives.concrete.ECGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import static java.lang.System.exit; + +/** + * Command-line mixProverVerifier and verifier. + */ +public class Mix { + final static String DEFAULT_ECGROUP = "secp256k1"; + final static Logger logger = LoggerFactory.getLogger(Mix.class); + + public Random rand; + public ECGroup group; + public ECElGamalEncryption enc; + public ConcreteCrypto.ElGamalPublicKey serializedPk; + public ECElGamal.SK secretKey; + public Mix2nizk mixProverVerifier; + + public MixerOutput inMix; + + public Mix() { + rand = new SecureRandom(); + enc = new ECElGamalEncryption(); + serializedPk = null; + secretKey = null; + } + + public void loadMix(File inFile) throws IOException { + + assert(serializedPk != null); + InputStream in = new FileInputStream(inFile); + inMix = MixerOutput.parseDelimitedFrom(in); + in.close(); + logger.info("Loaded mixnet with {} layers and logN={}", inMix.getNumLayers(), inMix.getLogN()); + } + + public boolean verify(boolean strict) { + try { + logger.info("Starting verification of {} ciphertexts", 1 << inMix.getLogN()); + long startTime = System.currentTimeMillis(); + boolean ok = MixVerifier.verifyTable(mixProverVerifier, inMix, strict); + long endTime = System.currentTimeMillis(); + logger.info("Verification took {} seconds", (endTime - startTime) / 1000f); + return ok; + } catch (InvalidProtocolBufferException e) { + logger.error("Badly formatted encryptions", e); + return false; + } + } + + public void mix(File outFile) throws IOException { + MixGenerator mixer = new MixGenerator(mixProverVerifier, enc); + + List encryptedMessages = Arrays.asList(inMix.getEncryptedMessages()[inMix.getNumLayers()]); + + logger.info("Starting mix of {} ciphertexts", encryptedMessages.size()); + long startTime = System.currentTimeMillis(); + MixerOutput outMix = mixer.mix(encryptedMessages, rand); + long endTime = System.currentTimeMillis(); + logger.info("Mix done (took {} seconds)", (endTime - startTime) / 1000f); + + OutputStream out = new FileOutputStream(outFile); + outMix.writeDelimitedTo(out); + out.close(); + } + + + // For testing purposes + + public void setPK(ConcreteCrypto.ElGamalPublicKey serializedPk) { + this.serializedPk = serializedPk; + + try { + enc.init(serializedPk); + group = enc.getGroup(); + } catch (InvalidKeySpecException e) { + logger.error("Invalid EC-ElGamal public key", e); + exit(-2); + } + + mixProverVerifier = new Mix2nizk(rand, enc); + } + + /** + * Create a new ECElGamal key pair and write it serialized to file. + * + * @param outFile + */ + public void createKeypair(File outFile) throws IOException { + group = new ECGroup(DEFAULT_ECGROUP); + BigInteger sk = ECElGamal.generateSecretKey(group, rand); + secretKey = new ECElGamal.SK(group, sk); + + serializedPk = Util.encodePK(group, secretKey); + setPK(serializedPk); + Crypto.BigInteger serializedSk = Util.encodeBigInteger(sk); + + OutputStream out = new FileOutputStream(outFile); + + serializedPk.writeDelimitedTo(out); + serializedSk.writeDelimitedTo(out); + + out.close(); + } + + /** + * Loads a public key and optionally a secret key from a file. + * @param inFile + * @throws IOException + * @throws InvalidKeySpecException + */ + public void loadKeypair(File inFile) throws IOException, InvalidKeySpecException { + InputStream in = new FileInputStream(inFile); + serializedPk = ConcreteCrypto.ElGamalPublicKey.parseDelimitedFrom(in); + + setPK(serializedPk); + + try { + Crypto.BigInteger serializedSk = Crypto.BigInteger.parseDelimitedFrom(in); + secretKey = Util.decodeSK(serializedPk, serializedSk); + } catch (EOFException e) { + logger.debug("File {} does not have a secret key", inFile); + } finally { + in.close(); + } + } + + + /** + * Decrypt the output of a mixnet + * Outputs to a file, one line per decoded element. + * + * @throws IOException + */ + public void decrypt(File outFile) throws IOException { + PrintStream out = new PrintStream(outFile); + + assert(inMix != null); + assert(inMix.getEncryptedMessages() != null); + + logger.info("Decrypting {} ciphertexts", inMix.getEncryptedMessages()[inMix.getNumLayers()].length); + for (Crypto.RerandomizableEncryptedMessage ciphertext : inMix.getEncryptedMessages()[inMix.getNumLayers()]) { + StringValue plaintext = Util.decrypt(StringValue.class, secretKey, group, ciphertext); + out.println(plaintext.getValue()); + } + + out.close(); + } + + public void encrypt(File inFile, File outFile) throws IOException { + BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(inFile))); + + ArrayList lines = new ArrayList<>(); + String line; + while ((line = in.readLine()) != null) { + lines.add(line); + } + in.close(); + + int n = lines.size(); + int logN = 32 - Integer.numberOfLeadingZeros(n - 1); + n = 1 << logN; // We need the smallest power of 2 greater than the size + + + Crypto.RerandomizableEncryptedMessage ciphers[][] = new Crypto.RerandomizableEncryptedMessage[1][n]; + + logger.info("Encrypting {} plaintexts (padded to {})", lines.size(), n); + for (int i = 0; i < lines.size(); ++i) { + ciphers[0][i] = enc.encrypt(StringValue.newBuilder().setValue(lines.get(i)).build(), enc.generateRandomness(rand)); + } + + // Pad with empty values + StringValue empty = StringValue.newBuilder().setValue("").build(); + for (int i = lines.size(); i < n; ++i) { + ciphers[0][i] = enc.encrypt(empty, enc.generateRandomness(rand)); + } + + inMix = new MixerOutput(logN, null, ciphers); + + OutputStream out = new FileOutputStream(outFile); + inMix.writeDelimitedTo(out); + out.close(); + logger.info("Wrote mixnet with {} layers and logN={}", inMix.getNumLayers(), inMix.getLogN()); + } + + + static void printHelp(OptionParser parser) { + printHelp(null, parser); + } + + static void printHelp(String msg, OptionParser parser) { + if (msg != null) + System.out.println(msg); + try { + parser.printHelpOn(System.out); + } catch (IOException e) { + // should never happen + } + } + + public static void main(String[] args) { + OptionParser parser = new OptionParser(); + final OptionSpec OPT_HELP = parser.accepts("help", "Print help"); + final OptionSpec OPT_GENKEY = parser.accepts("genkey", "Generate a key-pair (write into key file)"); + final OptionSpec OPT_DECRYPT = parser.accepts("decrypt", "Decrypt using given keypair"); + final OptionSpec OPT_KEYFILE = parser.accepts("keys", "File containing public key (or keypair for decryption)").withRequiredArg().ofType(File.class); + final OptionSpec OPT_ENCRYPT = parser.accepts("encrypt", "Decrypt using given keypair"); + final OptionSpec OPT_STRICT = parser.accepts("strict", "Use strict verification (verify that network matches our Benes implementation)"); + final OptionSpec OPT_INPUTFILE = parser.accepts("infile", "Input file (if mixing, should contain ciphertext or mixProverVerifier output; if verifying, mixProverVerifier output)").withRequiredArg().ofType(File.class); + final OptionSpec OPT_OUTPUTFILE = parser.accepts("outfile", "Output file. If given, operate in Mix mode; otherwise in verify mode.").withRequiredArg().ofType(File.class); + + OptionSet options = parser.parse(args); + if (options.has(OPT_HELP)) { + printHelp(parser); + return; + } + + File keyFile = options.valueOf(OPT_KEYFILE); + if (keyFile == null) { + printHelp("Must specify key file", parser); + return; + } + + File inFile = options.valueOf(OPT_INPUTFILE); + if ((inFile == null || !inFile.canRead()) && !options.has(OPT_GENKEY)) { + printHelp("Must specify input file except for key generation" + (inFile.canRead() ? "" : " (can't read inFile)"), parser); + return; + } + + + File outFile = options.valueOf(OPT_OUTPUTFILE); + + Mix mix = new Mix(); + + try { + if (options.has(OPT_GENKEY)) { + mix.createKeypair(keyFile); + } else { + mix.loadKeypair(keyFile); + if (options.has(OPT_ENCRYPT)) { + mix.encrypt(inFile, outFile); + } else if (options.has(OPT_DECRYPT)) { + mix.loadMix(inFile); + mix.decrypt(outFile); + } else if (options.has(OPT_OUTPUTFILE)) { + // Mix mode + mix.loadMix(inFile); + mix.mix(outFile); + } else { + mix.loadMix(inFile); + boolean ok = mix.verify(options.has(OPT_STRICT)); + if (ok) { + logger.info("Verification successful"); + } else { + logger.error("Verification failed!"); + exit(-1); + } + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + } + + } +} diff --git a/mixer/src/main/java/meerkat/mixer/mixing/MixNetwork.java b/mixer/src/main/java/meerkat/mixer/mixing/MixNetwork.java deleted file mode 100644 index e820ebd..0000000 --- a/mixer/src/main/java/meerkat/mixer/mixing/MixNetwork.java +++ /dev/null @@ -1,214 +0,0 @@ -package meerkat.mixer.mixing; - -import java.util.ArrayList; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.ArrayBlockingQueue; - -/** - * Created by Tzlil on 12/15/2015. - * contains benes mix network. - * the network is generated in the constructor and can't be change - */ -public class MixNetwork { - - private final Switch[][] switches; - - /** - * constructor - * @param randomPermutation - */ - public MixNetwork(RandomPermutation randomPermutation) { - this.switches = generateSwitchesValue(randomPermutation.permutation); - } - - /** - * implements benes mix network algorithm - * @param permutation - random permutation - * @return switches - */ - private Switch[][] generateSwitchesValue(int[] permutation){ - int n = permutation.length; - assert ((n & n-1) == 0); //n == 2^k - int layers = numberOfLayers(n); - - int[] pi, piL, piR; - Queue permutationsQueue = new ArrayBlockingQueue(n); - Graph graph; - int iDiv2; - int nDiv2 = n >> 1; - Switch[][] switches = new Switch[layers][nDiv2]; - int index1,index2; - - permutationsQueue.add(permutation); - for (int i = n, layer = 0; i > 1; i >>= 1, layer++) // i == permutation size - { - iDiv2 = i >> 1; - for (int j = 0; j < nDiv2; j += iDiv2) // j == permutation start index - { - pi = permutationsQueue.remove(); - graph = new Graph(pi); - piL = new int[iDiv2]; - piR = new int[iDiv2]; - for (int k = 0; k < iDiv2; k++){ // k == switch index in permutation j - - index1 = k + (j << 1); - index2 = index1 + iDiv2; - switches[layers - layer - 1][k + j] = new Switch(index1,index2,layers - layer - 1,graph.getSwitchValue(k, true)); - switches[layer][k + j] = new Switch(index1,index2,layer,graph.getSwitchValue(k, false)); - - if (!switches[layers - layer - 1][k + j].value) { - piL[k] = pi[k] % iDiv2; - piR[k] = pi[k + iDiv2] % iDiv2; - } else { - piL[k] = pi[k + iDiv2] % iDiv2; - piR[k] = pi[k] % iDiv2; - } - } - permutationsQueue.add(piL); - permutationsQueue.add(piR); - } - } - return switches; - } - - /** - * getter for switches value at layer - * @param layer - * @return switches[layer] - */ - public Switch[] getSwitchesByLayer(int layer) - { - return switches[layer]; - } - - /** - * calc number of layers for n values - * @param n number of votes - * @return layers - */ - public static int numberOfLayers(int n){ - return (int) (2 * Math.log(n) / Math.log(2)) - 1; - } - - /** - * inner class - * graph object, part of benes mix network algorithm - */ - private class Graph { - private int n; - private int nDiv2; - private Node[][] nodes; - protected Graph(int[] permutation){ - n = permutation.length; // n = 2^k - nDiv2 = n >> 1; - createNodes(); - createEdges(permutation); - setSwitches(); - } - - /** - * provide an access to algorithm result - * index must be less then n/2 - */ - protected boolean getSwitchValue(int index,boolean up) { - return up ? nodes[0][index].value : nodes[1][index].value; - } - - - - - - - /** - * create two lines of nodes size n/2 each - * the value of the i th node is (i,i+n/2) if i < n /2 (first line) - * otherwise its value is (i - n/2 , i) (second line) - */ - private void createNodes() { - nodes = new Node[2][nDiv2]; - for (int i = 0; i < nDiv2; i++) { - nodes[0][i] = new Node(); - nodes[1][i] = new Node(); - } - } - - /** create an edge between each pair of nodes i,j from different lines (i index of the first line) - * if exists k in i th node's value and t in j th node's value - * s.t permutation[k] == t - * the edge is broken if (k < n/2 and t >= n/2) or (k >= n/2 and t < n/2) - * Note: in purpose to avoid edge cases, each node has exactly two edges - */ - private void createEdges(int[] permutation) { - int j; - for (int i = 0; i < nDiv2; i++) { - j = permutation[i] % nDiv2; - nodes[0][i].edges.add(new Edge(nodes[1][j], (permutation[i] >= nDiv2))); - nodes[1][j].edges.add(new Edge(nodes[0][i], (permutation[i] >= nDiv2))); - - j = permutation[i + nDiv2] % nDiv2; - nodes[0][i].edges.add(new Edge(nodes[1][j], (permutation[i + nDiv2] < nDiv2))); - nodes[1][j].edges.add(new Edge(nodes[0][i], (permutation[i + nDiv2] < nDiv2))); - } - } - - /** - * set switch's value (on/off) for each switch (node) - * s.t if nodes i,j connected by edge e, i th switch's value - * must be equal to j's if e is broken or not equal if e is not broken - */ - private void setSwitches() { - Node node; - boolean v; - Edge e0,e1; - // iterate over first line of nodes - for (int i = 0; i < nDiv2; i++) { - node = nodes[0][i]; - if (node.set) - continue; - //select default value for first node in connected component - v = false; - // set value to all reachable nodes from node - while (true) { - node.set = true; - node.value = v; - e0 = node.edges.get(0); e1 = node.edges.get(1); - if (e0.neighbor.set && e1.neighbor.set) - break; - v ^= (!e0.neighbor.set) ? e0.broken : e1.broken; - node = (!e0.neighbor.set) ? e0.neighbor : e1.neighbor; - } - } - } - - /** - * inner class - * node object in graph - * there are exactly twp edges for each node - */ - private class Node { - public List edges; - private boolean value; - private boolean set; - public Node() { - edges = new ArrayList(2); - set = false; - } - } - - /** - * inner class - * edge object in graph - */ - private class Edge { - public Node neighbor; - public boolean broken; - public Edge(Node neighbor, boolean broken) { - this.neighbor = neighbor; - this.broken = broken; - } - } - } - - -} diff --git a/mixer/src/main/java/meerkat/mixer/mixing/MixerOutput.java b/mixer/src/main/java/meerkat/mixer/mixing/MixerOutput.java deleted file mode 100644 index 64636cc..0000000 --- a/mixer/src/main/java/meerkat/mixer/mixing/MixerOutput.java +++ /dev/null @@ -1,130 +0,0 @@ -package meerkat.mixer.mixing; - -import meerkat.protobuf.Crypto; -import meerkat.protobuf.Mixing; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - - -/** - * Created by Tzlil on 1/18/2016. - * implements meerkat.crypto.mixnet.MixerOutput interface - * container for meerkat.mixer.mixing.mix result. - */ -public class MixerOutput implements meerkat.crypto.mixnet.MixerOutput{ - private final Mixing.ZeroKnowledgeProof[][] proofs; - private final Crypto.RerandomizableEncryptedMessage[][] encryptedMessages; - private final int n; - private final int layers; - - /** - * constructor - * @param n number of votes - * @param layers - * @param encryptedMessages at level 0 , contains the original encrypted votes - * at each other level contains the re encrypted votes - * @param proofs in each cell (level,switch) contains the match zero knowledge proof - */ - public MixerOutput(int n,int layers,Mixing.ZeroKnowledgeProof[][] proofs - , Crypto.RerandomizableEncryptedMessage[][] encryptedMessages) { - this.proofs = proofs; - this.encryptedMessages = encryptedMessages; - this.n = n; - this.layers = layers; - } - - - - @Override - public Mixing.ZeroKnowledgeProof[][] getProofs() { - return proofs; - } - - @Override - public Crypto.RerandomizableEncryptedMessage[][] getEncryptedMessages() { - return encryptedMessages; - } - - @Override - public int getN() { - return n; - } - - /** - * print the output, encrypted messages and proofs, to folder - * @param dir - directory - * @throws IOException - */ - public void outToFolder(String dir) throws IOException { - - (new File(dir)).mkdirs(); - //create files - String proofsDir = dir + "/Proofs"; - String encDir = dir + "/EncryptedMessages"; - (new File(proofsDir)).mkdir(); - (new File(encDir)).mkdir(); - for (int layer = 0; layer < layers; layer++){ - (new File(proofsDir +"/layer" + layer )).mkdir(); - (new File(encDir +"/layer" + layer )).mkdir(); - } - (new File(encDir +"/input")).mkdir(); - - - for (int layer = 0; layer < layers; layer++){ - for(int i = 0; i < proofs[layer].length; i ++){ - writeProofToFile(proofsDir,proofs[layer][i]); - } - } - - for (int layer = 0; layer <= layers; layer++){ - for(int i = 0; i < encryptedMessages[layer].length; i ++){ - writeEncToFile(encDir,layer - 1, i,encryptedMessages[layer][i]); - } - } - } - - /** - * create new file contains single proof - * @param proofsDir - * @param proof - * @throws IOException - */ - private void writeProofToFile(String proofsDir, Mixing.ZeroKnowledgeProof proof) throws IOException { - Mixing.ZeroKnowledgeProof.Location location = proof.getLocation(); - int layer = location.getLayer(); - int i = location.getI(); - int j = location.getJ(); - String fileName = proofsDir+"/layer" + layer +"/" + i +"_" + j; - - File file = new File(fileName); - file.createNewFile(); - FileOutputStream fos = new FileOutputStream(file); - fos.write(proof.toByteArray()); - fos.close(); - } - - /** - * create new file contains single encrypted message - * @param encDir - * @param layer - * @param i - * @param enc - * @throws IOException - */ - private void writeEncToFile(String encDir,int layer,int i, Crypto.RerandomizableEncryptedMessage enc) throws IOException { - - String fileName; - if(layer >= 0) - fileName = encDir+"/layer" + layer +"/" + i; - else - fileName = encDir+"/input/" + i; - - File file = new File(fileName); - file.createNewFile(); - FileOutputStream fos = new FileOutputStream(file); - fos.write(enc.toByteArray()); - fos.close(); - } -} diff --git a/mixer/src/main/java/meerkat/mixer/mixing/RandomPermutation.java b/mixer/src/main/java/meerkat/mixer/mixing/RandomPermutation.java deleted file mode 100644 index 7f18530..0000000 --- a/mixer/src/main/java/meerkat/mixer/mixing/RandomPermutation.java +++ /dev/null @@ -1,45 +0,0 @@ -package meerkat.mixer.mixing; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Created by Tzlil on 12/17/2015. - * container for random permutation - * the permutation is sated in constructor and can't be change - */ -public class RandomPermutation { - public final int[] permutation; - - /** - * constructor - * @param n permutation size - * @param random - */ - public RandomPermutation(int n,Random random) { - this.permutation = generatePermutation(n,random); - } - - /** - * generate random permutation ovver [0,n) - * @param n permutation size - * @param random - * @return permutation - */ - private int[] generatePermutation(int n,Random random){ - List numbers= new ArrayList(n); - for (int i = 0; i < n; i++) { - numbers.add(i); - } - - int[] result = new int[n]; - int index; - for (int i = 0; i < n; i++) { - index = random.nextInt(n - i); - result[i] = numbers.get(index); - numbers.remove(index); - } - return result; - } -} diff --git a/mixer/src/main/java/meerkat/mixer/mixing/Switch.java b/mixer/src/main/java/meerkat/mixer/mixing/Switch.java deleted file mode 100644 index 5de522e..0000000 --- a/mixer/src/main/java/meerkat/mixer/mixing/Switch.java +++ /dev/null @@ -1,36 +0,0 @@ -package meerkat.mixer.mixing; - -/** - * Created by Tzlil on 12/15/2015. - * container for switch - */ -public class Switch{ - - public final int i, j, layer; - public final boolean value; - - /** - * constructor - * @param i - * @param j - * @param layer - * @param value the switch is on or off - */ - public Switch(int i, int j, int layer, boolean value) { - this.i = i; - this.j = j; - this.layer = layer; - this.value = value; - } - - - @Override - public String toString() { - return "Switch{" + - "i=" + i + - ", j=" + j + - ", layer=" + layer + - ", value=" + value + - '}'; - } -} diff --git a/mixer/src/main/java/meerkat/mixer/network/BenesNetwork.java b/mixer/src/main/java/meerkat/mixer/network/BenesNetwork.java new file mode 100644 index 0000000..e467fd8 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/network/BenesNetwork.java @@ -0,0 +1,233 @@ +package meerkat.mixer.network; + +import java.util.*; + +/** + * Generate the Benes Network + */ +public class BenesNetwork implements PermutationNetwork +{ + /** + * log (base 2) of number of inputs to Benes network (N = 2^{logN}) + */ + final int logN; + + /** + * Values of switches set for a specific permutation. + * switchValues[layer][switchNum] is true if the corresponding switch applies the identity permutation on its inputs. + * layer can be in the range [0, 2*logN-1), switchNum in the range (0, 2^{logN-1}-1) + */ + final boolean[][] switchValues; + + public BenesNetwork(int logN) { + this.logN = logN; + + switchValues = new boolean[2*logN - 1][]; + for (int i = 0; i < switchValues.length; ++i) + switchValues[i] = new boolean[1 << (logN - 1)]; + } + + /** + * Return the index of the switch output in the previous layer that connects to a specified input in the current layer. + * Each consecutive pair of outputs is from the same switch, and each consecutive pair of inputs goes to the same switch. + * (that is, switch j has inputs/outputs 2j and 2j+1) + * + * @param logN log (base 2) of number of inputs to Benes network (N = 2^{logN}) + * @param layer current layer index. Must be between 0 and 2*logN-1 (layer -1 is the inputs and layer 2*logN-1 is the outputs) + * @param inputIdx the input Idx for the current layer (must be between 0 and (1 << logN) - 1 + * + * @return the requested index + */ + public static int getOutputIdxInPreviousLayer(int logN, int layer, int inputIdx) { + assert (layer >= 0) && (layer <= 2*logN - 1); + assert (inputIdx >= 0) && (inputIdx < 1 << logN); + + if ((layer == 0) || (layer == 2*logN - 1) || (inputIdx & 1) == 0) { + // layer 0 inputs and all even inputs + // are connected straight "across" everywhere + return inputIdx; + } + + // --- Odd inputs are connected depending on layer --- + + int crossBit; + if (layer < logN) + crossBit = logN - layer; + else + crossBit = layer - logN + 1; + + return inputIdx ^ (1 << crossBit); + } + + /** + * Inverse of {@link #getOutputIdxInPreviousLayer(int, int, int)} + * @param logN + * @param layer + * @param outputIdx + * @return + */ + public static int getInputIdxInNextLayer(int logN, int layer, int outputIdx) { + return getOutputIdxInPreviousLayer(logN, layer + 1, outputIdx); + } + + @Override + public int getInputIdxInNextLayer(int layer, int outputIdx) { + return getInputIdxInNextLayer(logN, layer, outputIdx); + } + + @Override + public int getNumInputs() { + return 1 << logN; + } + + @Override + public int getNumLayers() { + return 2*logN - 1; + } + + @Override + public int getOutputIdxInPreviousLayer(int layer, int inputIdx) { + return getOutputIdxInPreviousLayer(logN, layer, inputIdx); + } + + + /** + * Find in array + * TODO: replace with more efficient data structure if this becomes a performance bottleneck + * @param val + * @param arr + * @return + */ + static private int find(int val, int[] arr) { + for (int i = 0; i < arr.length; ++i) { + if (arr[i] == val) + return i; + } + return -1; + } + + /** + * The recursive algorithm attributed to H. Stone by A. Waksman + * (see pg. 161 in paper) + * @param permOut + * @param level + */ + private void setInternalPermutation(int[] permIn, int[] permOut, int idx, int level) { + // Make sure idx is in the the range [0, 2^{level}) + assert (idx < (1 << level)); + assert (permOut.length == (1 << (logN - level))); + assert (permIn.length == permOut.length); + + final int Nover2 = 1 << (logN - level - 1); + + // Base case + if (level == logN - 1) { + if (permOut[0] == permIn[0]) + switchValues[level][idx] = false; + else + switchValues[level][idx] = true; + return; + } + + int nextLen = permOut.length >>> 1; + int[] upperPermIn = new int[nextLen]; + int[] lowerPermIn = new int[nextLen]; + + int[] upperPermOut = new int[nextLen]; + int[] lowerPermOut = new int[nextLen]; + + + SortedSet unmatchedIndices = new TreeSet<>(); + for (int i = 0; i < permIn.length; ++i) { + unmatchedIndices.add(i); + } + + /** + * Where the set of switches for this level and index actually starts in switchValues + */ + int blockStart = idx << (logN - level - 1); + int numSwitches = 1 << (logN - level - 1); + + while (!unmatchedIndices.isEmpty()) { + int i = unmatchedIndices.first(); + + do { + //assert ((i & 1) == 0); // Algorithm guarantees only even indices are unmatched at start of loop + unmatchedIndices.remove(i); + unmatchedIndices.remove(i ^ 1); + + int switchNum = i >>> 1; + + // index of outPerm[i] after the last switch layer. + int iSwitched; + + if (((i & 1) == 0) && (switchNum < numSwitches / 2) || ((i & 1) == 1) && (switchNum >= numSwitches / 2)) { + // i is in the upper half and even, or in the lower half and odd, so + // switch must be "straight" to get i to upper half. + switchValues[2 * logN - level - 2][blockStart + switchNum] = false; + iSwitched = i; + + } else { + switchValues[2 * logN - level - 2][blockStart + switchNum] = true; + iSwitched = i ^ 1; + } + + // Index of outPerm[i] in the output of the previous layer. + int iConnectedTo = getOutputIdxInPreviousLayer(logN - level, 2 * (logN - level) - 2, iSwitched); + int iPairConnectedTo = getOutputIdxInPreviousLayer(logN - level, 2 * (logN - level) - 2, iSwitched ^ 1); + + upperPermOut[iConnectedTo] = permOut[i]; + lowerPermOut[iPairConnectedTo - Nover2] = permOut[i ^ 1]; + + // Index of permOut[i] before the first switch layer. + int j = find(permOut[i], permIn); + int jSwitchNum = j >>> 1; + + // Index of permOut[i] after the first switch layer + int jSwitched; + + if ((((j & 1) == 0) && (jSwitchNum < numSwitches / 2)) || ((j & 1) == 1) && (jSwitchNum >= numSwitches / 2)) { + // Even output in the upper half, or odd output in the lower half + // so switch needs to be "straight" to get j to the upper half + switchValues[level][blockStart + jSwitchNum] = false; + jSwitched = j; + } else { + // Otherwise switch needs to be "crossed" to get j to upper half + switchValues[level][blockStart + jSwitchNum] = true; + jSwitched = j ^ 1; + } + + int jConnectedTo = getInputIdxInNextLayer(logN - level, 0, jSwitched); + // must be in upper half + assert(jConnectedTo < (1 << (logN - 1))); + upperPermIn[jConnectedTo] = permOut[i]; + + // Connect from j's pair to through the lower half + int jPairConnectedTo = getInputIdxInNextLayer(logN - level, 0, jSwitched ^ 1); + lowerPermIn[jPairConnectedTo - Nover2] = permIn[j ^ 1]; + + int iPairNext = find(permIn[j ^ 1], permOut); + i = iPairNext ^ 1; + } while (unmatchedIndices.contains(i)); + } + + setInternalPermutation(upperPermIn, upperPermOut, 2*idx, level + 1); + setInternalPermutation(lowerPermIn, lowerPermOut, 2*idx + 1, level + 1); + } + + @Override + public void setPermutation(int[] permutation) { + int level = 0; + int[] identity = new int[getNumInputs()]; + for (int i = 0; i < identity.length; ++i) + identity[i] = i; + + setInternalPermutation(identity, permutation, 0, 0); + } + + @Override + public boolean isCrossed(int layer, int switchIdx) { + return switchValues[layer][switchIdx]; + } + +} diff --git a/mixer/src/main/java/meerkat/mixer/network/PermutationNetwork.java b/mixer/src/main/java/meerkat/mixer/network/PermutationNetwork.java new file mode 100644 index 0000000..c4cad88 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/network/PermutationNetwork.java @@ -0,0 +1,53 @@ +package meerkat.mixer.network; + +/** + * A generic permutation network composed of 2-input switches. + */ +public interface PermutationNetwork { + /** + * Return the number of inputs supported by the network. + * @return + */ + int getNumInputs(); + + /** + * Return the number of layers supported by the network. + * @return + */ + int getNumLayers(); + + /** + * Return the index of the switch output in the previous layer that connects to a specified input in the current layer. + * Each consecutive pair of outputs is from the same switch, and each consecutive pair of inputs goes to the same switch. + * (that is, switch j has inputs/outputs 2j and 2j+1) + * + * @param layer current layer index. Must be in the range [0, numLayers) (layer 0's previous layer consists of the inputs themselves) + * @param inputIdx the input Idx for the current layer. Must be in the range [0, numInputs) + * + * @return the requested index + */ + int getOutputIdxInPreviousLayer(int layer, int inputIdx); + + /** + * The inverse of {@link }#getOutputIdxInPreviousLayer(int,int)}. + * @param layer + * @param inputIdx + * @return + */ + int getInputIdxInNextLayer(int layer, int inputIdx); + + /** + * Initialize switches to generate a specific permutation. + * @param permutation + */ + void setPermutation(int[] permutation); + + + /** + * Returns true iff the switch is crossed + * @param layer + * @return + */ + boolean isCrossed(int layer, int switchIdx); + +} diff --git a/mixer/src/main/java/meerkat/mixer/network/RandomPermutation.java b/mixer/src/main/java/meerkat/mixer/network/RandomPermutation.java new file mode 100644 index 0000000..55a491d --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/network/RandomPermutation.java @@ -0,0 +1,59 @@ +package meerkat.mixer.network; + +import java.util.Random; + +/** + * Created by Tzlil on 12/17/2015. + * container for random permutation + * the permutation is set in the constructor and can't be changed + */ +public class RandomPermutation { + public final int[] permutation; + + /** + * constructor + * @param n permutation size + * @param random + */ + public RandomPermutation(int n, Random random) { + this.permutation = generatePermutation(n,random); + } + + /** + * generate random permutation ovver [0,n) + * @param n permutation size + * @param random + * @return permutation + */ + public static int[] generatePermutation(int n, Random random){ + int[] result = new int[n]; + + // initialize and permute in one pass using "inside-out" Fisher-Yates + for (int i = 0; i < n; ++i) { + int j = random.nextInt(i+1); + + if (j != i) { + result[i] = result[j]; + } + + result[j] = i; + } + + return result; + } + + + /** + * randomly shuffle an array of elements in-place (using Fisher-Yates) + * @param perm array of elements to shuffle. + * @param random + */ + public static void permute(T[] perm, Random random){ + for (int i = perm.length - 1; i > 0; --i) { + int j = random.nextInt(i+1); + T tmp = perm[i]; + perm[i] = perm[j]; + perm[j] = tmp; + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/network/Util.java b/mixer/src/main/java/meerkat/mixer/network/Util.java new file mode 100644 index 0000000..90bae97 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/network/Util.java @@ -0,0 +1,43 @@ +package meerkat.mixer.network; + +/** + * Generic utilities + */ +public class Util { + /* + * Apply a single layer's switch permutations in place (as implied by {@link PermutationNetwork#setPermutation(int[])}) + * @param values values output by previous layxer + * @param layer layer to apply (layer is between 1 and numLayers; layer 0 is the input layer) + * @param type of values. + */ + public static void applyLayerSwitches(PermutationNetwork net, T[] values, int layer) { + int numSwitches = net.getNumInputs() >>> 1; + for (int i = 0; i < numSwitches; ++i) { + if (net.isCrossed(layer, i)) { + T tmp = values[i * 2]; + values[i * 2] = values[i * 2 + 1]; + values[i * 2 + 1] = tmp; + } + } + } + + + public static void convertOutputPermToInputPerm(PermutationNetwork net, T[] oldValues, T[] newValues, int layer) { + int numInputs = net.getNumInputs(); + assert(numInputs == oldValues.length); + assert(numInputs == newValues.length); + + for (int i = 0; i < numInputs; ++i) { + newValues[i] = oldValues[net.getOutputIdxInPreviousLayer(layer, i)]; + } + } + + + public static void permute(PermutationNetwork net, T[] oldValues, T[] newValues) { + for (int layer = 0; layer < net.getNumLayers(); ++layer) { + convertOutputPermToInputPerm(net, oldValues, newValues, layer); + applyLayerSwitches(net, newValues, layer); + System.arraycopy(newValues, 0, oldValues, 0, oldValues.length); + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/Concatenator.java b/mixer/src/main/java/meerkat/mixer/proofs/Concatenator.java new file mode 100644 index 0000000..470fced --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/Concatenator.java @@ -0,0 +1,21 @@ +package meerkat.mixer.proofs; + +/** + * Created by talm on 11/01/17. + */ + + +public interface Concatenator { + public interface Pair{ + public OutType concatenate(InType1 msg1, InType2 msg2); + public InType1 getMsg1(OutType msg); + public InType2 getMsg2(OutType msg); + } + + public interface Triplet{ + public OutType concatenate(InType1 msg1, InType2 msg2, InType3 msg3); + public InType1 getMsg1of3(OutType msg); + public InType2 getMsg2of3(OutType msg); + public InType3 getMsg3of3(OutType msg); + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/Mix2nizk.java b/mixer/src/main/java/meerkat/mixer/proofs/Mix2nizk.java new file mode 100644 index 0000000..a959a22 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/Mix2nizk.java @@ -0,0 +1,44 @@ +package meerkat.mixer.proofs; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.protobuf.Crypto; +import meerkat.protobuf.Mixing; + +/** + * Created by talm on 20/01/17. + */ +public interface Mix2nizk { + /** + * Prove in zero knowledge that two ciphertexts are a mix of two original ciphertexts. + */ + public interface Prover { + public Mixing.Mix2Proof prove(Crypto.RerandomizableEncryptedMessage in1, + Crypto.RerandomizableEncryptedMessage in2, + Crypto.RerandomizableEncryptedMessage out1, + Crypto.RerandomizableEncryptedMessage out2, + boolean switched, int layer, int switchIdx, int out0Idx, int out1Idx, // switch info + Crypto.EncryptionRandomness r1, + Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException; + + + } + + /** + * Verify the two-ciphertext mix proof + */ + public interface Verifier { + /** + * Return true iff the proof is valid. + * @param in1 + * @param in2 + * @param out1 + * @param out2 + * @return + */ + boolean verify(Crypto.RerandomizableEncryptedMessage in1, + Crypto.RerandomizableEncryptedMessage in2, + Crypto.RerandomizableEncryptedMessage out1, + Crypto.RerandomizableEncryptedMessage out2, + Mixing.Mix2Proof proof) throws InvalidProtocolBufferException; + } +} 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..d0640c5 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/SigmaProtocol.java @@ -0,0 +1,25 @@ +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 FirstMsgType getFirstMessage(); + public FinalMessageType getFinalMessage(BigInteger challenge); + } + + public interface Simulator { + public FirstMsgType getFirstMessage(BigInteger challenge); + public FinalMessageType getFinalMessage(); + } + + public interface Verifier { + public boolean verify(FirstMsgType firstMessage, BigInteger challenge, FinalMessageType finalMessage); + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/concrete/DlogConjunction.java b/mixer/src/main/java/meerkat/mixer/proofs/concrete/DlogConjunction.java new file mode 100644 index 0000000..b9a9263 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/concrete/DlogConjunction.java @@ -0,0 +1,40 @@ +package meerkat.mixer.proofs.concrete; + +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.mixer.proofs.generic.SigmaProtocolAnd2; +import meerkat.protobuf.Mixing; + +import java.util.Random; + +/** + * Sigma protocol for plaintext equivalence of a pair of ciphertexts. + */ +public class DlogConjunction { + + public static class Prover + extends SigmaProtocolAnd2.Prover { + + public Prover(ECElGamalEncryption encryptor, Random rand, Statements.AndStatement statement, + Statements.AndStatementWitness witness) { + super(ProtobufConcatenators.concatAnd1, ProtobufConcatenators.concatAnd2, new SchnorrDlogEquivalence.Prover(encryptor, rand, statement.clauses[0], witness.witnesses[0]), + new SchnorrDlogEquivalence.Prover(encryptor, rand, statement.clauses[1], witness.witnesses[1])); + + } + } + + public static class Simulator extends SigmaProtocolAnd2.Simulator { + public Simulator(ECElGamalEncryption encryptor, Random rand, Statements.AndStatement statement) { + super(ProtobufConcatenators.concatAnd1, ProtobufConcatenators.concatAnd2, + new SchnorrDlogEquivalence.Simulator(encryptor, rand, statement.clauses[0]), new SchnorrDlogEquivalence.Simulator(encryptor, rand, statement.clauses[1])); + } + } + + public static class Verifier + extends SigmaProtocolAnd2.Verifier { + + public Verifier(ECElGamalEncryption encryptor, Statements.AndStatement statement) { + super(ProtobufConcatenators.concatAnd1, ProtobufConcatenators.concatAnd2, + new SchnorrDlogEquivalence.Verifier(encryptor, statement.clauses[0]), new SchnorrDlogEquivalence.Verifier(encryptor, statement.clauses[1])); + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/concrete/Mix2.java b/mixer/src/main/java/meerkat/mixer/proofs/concrete/Mix2.java new file mode 100644 index 0000000..10ea69a --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/concrete/Mix2.java @@ -0,0 +1,48 @@ +package meerkat.mixer.proofs.concrete; + +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.mixer.proofs.generic.SigmaProtocolOr2; +import meerkat.protobuf.Mixing; +import org.factcenter.qilin.primitives.concrete.ECGroup; + +import java.math.BigInteger; +import java.util.Random; + +/** + * Sigma protocol for proving correctness of a 2x2 switch + */ +public class Mix2 { + public static class ChallengeGenerator implements SigmaProtocolOr2.ChallengeGenerator { + ECElGamalEncryption encryptor; + ECGroup group; + Random rand; + + public ChallengeGenerator(ECElGamalEncryption encryptor, Random rand) { + this.encryptor = encryptor; + this.rand = rand; + group = encryptor.getGroup(); + } + + @Override + public BigInteger generateChallenge() { return encryptor.generateRandomExponent(rand); } + + @Override + public BigInteger subtractChallenge(BigInteger c1, BigInteger c2) { return c1.subtract(c2).mod(group.orderUpperBound()); } + } + + public static class Prover extends SigmaProtocolOr2.Prover { + public Prover(ECElGamalEncryption encryptor, Random rand, Statements.Mix2Statement statement, Statements.Mix2StatementWitness witness) { + super(ProtobufConcatenators.concatMix1, ProtobufConcatenators.concatMix2, new ChallengeGenerator(encryptor, rand), + new DlogConjunction.Prover(encryptor, rand, statement.clauses[witness.trueClauseIndex], witness.witness), + new DlogConjunction.Simulator(encryptor, rand, statement.clauses[1 - witness.trueClauseIndex]), + witness.trueClauseIndex); + } + } + + public static class Verifier extends SigmaProtocolOr2.Verifier { + public Verifier(ECElGamalEncryption encryptor, Statements.Mix2Statement statement) { + super(ProtobufConcatenators.concatMix1, ProtobufConcatenators.concatMix2, new ChallengeGenerator(encryptor, null), + new DlogConjunction.Verifier(encryptor, statement.clauses[0]), new DlogConjunction.Verifier(encryptor, statement.clauses[1])); + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/concrete/Mix2nizk.java b/mixer/src/main/java/meerkat/mixer/proofs/concrete/Mix2nizk.java new file mode 100644 index 0000000..482b694 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/concrete/Mix2nizk.java @@ -0,0 +1,99 @@ +package meerkat.mixer.proofs.concrete; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.mixer.proofs.Mix2nizk.Prover; +import meerkat.mixer.proofs.Mix2nizk.Verifier; +import meerkat.mixer.proofs.generic.SigmaFiatShamir; +import meerkat.protobuf.Crypto; +import meerkat.protobuf.Mixing; +import org.factcenter.qilin.primitives.concrete.ECGroup; + +import java.util.Random; + + +/** + * implements of Prover and Verifier interfaces + * this implementation assumes that each RerandomizableEncryptedMessage is an ElGamalCiphertext + */ +public class Mix2nizk implements Prover, Verifier { +// + private final ECGroup group; + private final Random rand; + private final ECElGamalEncryption encryptor; + private final Statements mixParams; + final SigmaFiatShamir mix2NIZK; + + /** + * @param rand + * @param encryptor + */ + public Mix2nizk(Random rand, ECElGamalEncryption encryptor) { + + this.rand = rand; + this.encryptor = encryptor; + this.group = this.encryptor.getGroup(); + this.mixParams = new Statements(encryptor); + this.mix2NIZK = new SigmaFiatShamir<>(ProtobufConcatenators.concatNIZK); + } + + /** + * @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 layer - row of a,b in encryption table + * @param r1 + * @param r2 + * @return - a valid ZKP that indeed c,d calculated as required + * @throws InvalidProtocolBufferException + */ + public Mixing.Mix2Proof prove(Crypto.RerandomizableEncryptedMessage a, + Crypto.RerandomizableEncryptedMessage b, + Crypto.RerandomizableEncryptedMessage c, + Crypto.RerandomizableEncryptedMessage d, + boolean switched, int layer, int switchIdx, int out0Idx, int out1Idx, + Crypto.EncryptionRandomness r1, + Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException { + + + Statements.Mix2Statement statement = mixParams.createStatement(a, b, c, d); + Statements.Mix2StatementWitness witness = mixParams.createMix2Witness(r1, r2, switched); + + Mix2.Prover prover = new Mix2.Prover(encryptor, rand, statement, witness); + + Mixing.Mix2Proof.Location location = Mixing.Mix2Proof.Location.newBuilder() + .setLayer(layer) + .setSwitchIdx(switchIdx) + .setOut0(out0Idx) + .setOut1(out1Idx) + .build(); + return mix2NIZK.generateNizk(prover).toBuilder().setLocation(location).build(); + } + + /** + * verify zero knowledge proof + * @param in1 + * @param in2 + * @param out1 + * @param out2 + * @param proof out1 = rerandomize(in1) && out2 = rerandomize(in2) + * || + * out1 = rerandomize(in2) && out2 = rerandomize(in1) + * @return true iff all 4 or proofs are valid + * @throws InvalidProtocolBufferException + */ + public boolean verify(Crypto.RerandomizableEncryptedMessage in1, + Crypto.RerandomizableEncryptedMessage in2, + Crypto.RerandomizableEncryptedMessage out1, + Crypto.RerandomizableEncryptedMessage out2, + Mixing.Mix2Proof proof) throws InvalidProtocolBufferException { + + Statements.Mix2Statement statement = mixParams.createStatement(in1,in2,out1,out2); + Mix2.Verifier verifier = new Mix2.Verifier(encryptor, statement); + + return mix2NIZK.verifyNizk(proof, verifier); + } +} + diff --git a/mixer/src/main/java/meerkat/mixer/proofs/concrete/ProtobufConcatenators.java b/mixer/src/main/java/meerkat/mixer/proofs/concrete/ProtobufConcatenators.java new file mode 100644 index 0000000..5a2d833 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/concrete/ProtobufConcatenators.java @@ -0,0 +1,108 @@ +package meerkat.mixer.proofs.concrete; + +import meerkat.crypto.concrete.Util; +import meerkat.mixer.proofs.Concatenator; +import meerkat.protobuf.Mixing; + +import java.math.BigInteger; + +/** + * Created by talm on 12/01/17. + */ +public class ProtobufConcatenators { + public final static ConcatAnd1 concatAnd1 = new ConcatAnd1(); + public final static ConcatAnd2 concatAnd2 = new ConcatAnd2(); + public final static ConcatMix1 concatMix1 = new ConcatMix1(); + public final static ConcatMix2 concatMix2 = new ConcatMix2(); + public final static ConcatNIZK concatNIZK = new ConcatNIZK(); + + public static class ConcatAnd1 implements Concatenator.Pair { + @Override + public Mixing.Mix2Proof.AndProof.FirstMessage concatenate(Mixing.Mix2Proof.DlogProof.FirstMessage msg1, Mixing.Mix2Proof.DlogProof.FirstMessage msg2) { + return Mixing.Mix2Proof.AndProof.FirstMessage.newBuilder() + .setClause0(msg1) + .setClause1(msg2) + .build(); + } + + @Override + public Mixing.Mix2Proof.DlogProof.FirstMessage getMsg1(Mixing.Mix2Proof.AndProof.FirstMessage msg) { return msg.getClause0(); } + + @Override + public Mixing.Mix2Proof.DlogProof.FirstMessage getMsg2(Mixing.Mix2Proof.AndProof.FirstMessage msg) { return msg.getClause1(); } + } + + public static class ConcatAnd2 implements Concatenator.Pair { + @Override + public Mixing.Mix2Proof.AndProof.FinalMessage concatenate(Mixing.Mix2Proof.DlogProof.FinalMessage msg1, Mixing.Mix2Proof.DlogProof.FinalMessage msg2) { + return Mixing.Mix2Proof.AndProof.FinalMessage.newBuilder() + .setClause0(msg1) + .setClause1(msg2) + .build(); + } + + @Override + public Mixing.Mix2Proof.DlogProof.FinalMessage getMsg1(Mixing.Mix2Proof.AndProof.FinalMessage msg) { return msg.getClause0(); } + + @Override + public Mixing.Mix2Proof.DlogProof.FinalMessage getMsg2(Mixing.Mix2Proof.AndProof.FinalMessage msg) { return msg.getClause1(); } + } + + public static class ConcatMix1 implements Concatenator.Pair { + @Override + public Mixing.Mix2Proof.FirstMessage concatenate(Mixing.Mix2Proof.AndProof.FirstMessage msg1, Mixing.Mix2Proof.AndProof.FirstMessage msg2) { + return Mixing.Mix2Proof.FirstMessage.newBuilder() + .setClause0(msg1) + .setClause1(msg2) + .build(); + } + + @Override + public Mixing.Mix2Proof.AndProof.FirstMessage getMsg1(Mixing.Mix2Proof.FirstMessage msg) { return msg.getClause0(); } + + @Override + public Mixing.Mix2Proof.AndProof.FirstMessage getMsg2(Mixing.Mix2Proof.FirstMessage msg) { return msg.getClause1(); } + } + + public static class ConcatMix2 implements Concatenator.Triplet { + @Override + public Mixing.Mix2Proof.FinalMessage concatenate(BigInteger msg1, Mixing.Mix2Proof.AndProof.FinalMessage msg2, Mixing.Mix2Proof.AndProof.FinalMessage msg3) { + return Mixing.Mix2Proof.FinalMessage.newBuilder() + .setC0(Util.encodeBigInteger(msg1)) + .setClause0(msg2) + .setClause1(msg3) + .build(); + } + + @Override + public BigInteger getMsg1of3(Mixing.Mix2Proof.FinalMessage msg) { return Util.decodeBigInteger(msg.getC0()); } + + @Override + public Mixing.Mix2Proof.AndProof.FinalMessage getMsg2of3(Mixing.Mix2Proof.FinalMessage msg) { return msg.getClause0(); } + + @Override + public Mixing.Mix2Proof.AndProof.FinalMessage getMsg3of3(Mixing.Mix2Proof.FinalMessage msg) { return msg.getClause1(); } + } + + + public static class ConcatNIZK implements Concatenator.Pair { + + @Override + public Mixing.Mix2Proof concatenate(Mixing.Mix2Proof.FirstMessage msg1, Mixing.Mix2Proof.FinalMessage msg2) { + return Mixing.Mix2Proof.newBuilder() + .setFirstMessage(msg1) + .setFinalMessage(msg2) + .build(); + } + + @Override + public Mixing.Mix2Proof.FirstMessage getMsg1(Mixing.Mix2Proof msg) { + return msg.getFirstMessage(); + } + + @Override + public Mixing.Mix2Proof.FinalMessage getMsg2(Mixing.Mix2Proof msg) { + return msg.getFinalMessage(); + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/concrete/SchnorrDlogEquivalence.java b/mixer/src/main/java/meerkat/mixer/proofs/concrete/SchnorrDlogEquivalence.java new file mode 100644 index 0000000..4aec718 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/concrete/SchnorrDlogEquivalence.java @@ -0,0 +1,124 @@ +package meerkat.mixer.proofs.concrete; + +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; +import meerkat.mixer.proofs.SigmaProtocol; +import meerkat.protobuf.ConcreteCrypto; +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; + +/** + * Schnorr's ZK proof of Dlog equivlance + */ +public class SchnorrDlogEquivalence { + + public static class Verifier implements SigmaProtocol.Verifier { + ECElGamalEncryption encryptor; + ECGroup group; + final Statements.DlogStatement statement; + + public Verifier(ECElGamalEncryption encryptor, Statements.DlogStatement statement) { + this.encryptor = encryptor; + group = encryptor.getGroup(); + this.statement = statement; + } + + @Override + public boolean verify(Mixing.Mix2Proof.DlogProof.FirstMessage firstMessage, BigInteger challenge, + Mixing.Mix2Proof.DlogProof.FinalMessage finalMessage) { + ConcreteCrypto.GroupElement grEncoded = firstMessage.getGr(); + ECPoint gr = encryptor.decodeElement(grEncoded); + + ConcreteCrypto.GroupElement hrEncoded = firstMessage.getHr(); + ECPoint hr = encryptor.decodeElement(hrEncoded); + + 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 static class Simulator implements SigmaProtocol.Simulator { + ECElGamalEncryption encryptor; + ECGroup group; + Random rand; + + Statements.DlogStatement statement; + BigInteger response = null; + + public Simulator(ECElGamalEncryption encryptor, Random rand, Statements.DlogStatement statement) { + this.encryptor = encryptor; + group = encryptor.getGroup(); + this.rand = rand; + 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 Mixing.Mix2Proof.DlogProof.FinalMessage getFinalMessage() { + + return Mixing.Mix2Proof.DlogProof.FinalMessage.newBuilder() + .setXcr(Util.encodeBigInteger(response)) + .build(); + } + } + + public static class Prover implements SigmaProtocol.Prover { + ECElGamalEncryption encryptor; + ECGroup group; + Random rand; + Statements.DlogStatement statement; + Statements.DlogStatementWitness witness; + + BigInteger r = null; + + public Prover(ECElGamalEncryption encryptor, Random rand, + Statements.DlogStatement statement, Statements.DlogStatementWitness witness) { + this.encryptor = encryptor; + group = encryptor.getGroup(); + this.rand = rand; + 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 Mixing.Mix2Proof.DlogProof.FinalMessage getFinalMessage(BigInteger challenge) { + return Mixing.Mix2Proof.DlogProof.FinalMessage.newBuilder() + .setXcr(Util.encodeBigInteger(challenge.multiply(witness.x).add(r).mod(group.orderUpperBound()))) + .build(); + } + + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/concrete/Statements.java b/mixer/src/main/java/meerkat/mixer/proofs/concrete/Statements.java new file mode 100644 index 0000000..6c41b43 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/concrete/Statements.java @@ -0,0 +1,191 @@ +package meerkat.mixer.proofs.concrete; + +import com.google.protobuf.InvalidProtocolBufferException; +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.protobuf.ConcreteCrypto.ElGamalCiphertext; +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; + +/** + * used for organizing the input for each ZKP + * + * both meerkat.mixProverVerifier.proofs and meerkat.mixProverVerifier.verifier implementation use it + */ +public class Statements { + + final public ECElGamalEncryption encryptor; + final public ECGroup group; + final public ECPoint g; + final public ECPoint h; + + /** + * @param encryptor encryptor used for encoding/decoding serialized ciphertexts. The group, default generator and + * second base (h) are taken from the encryptor (second base is the public key) + */ + public Statements(ECElGamalEncryption encryptor){ + this.encryptor = encryptor; + this.group = encryptor.getGroup(); + this.g = group.getGenerator(); + this.h = encryptor.getElGamalPK().getPK(); + } + + /** + * can be used by anyone, e.g meerkat.mixer.verifier + * + * call to the meerkat.mixer.main overload with trueClauseIndex = false + */ + public Mix2Statement createStatement(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2, + Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2) throws InvalidProtocolBufferException { + + 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 one or more of the encrypted messages isn't + * ElGamalCiphertext + */ + 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)); + + AndStatementWitness andW = new AndStatementWitness(dlogW1, dlogW2); + + return new Mix2StatementWitness(switched ? 1 : 0, andW); + } + + + /** + * 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 Mix2Statement can be constructed only by calling the {@link #createStatement} factory method on an instance of ECElGamalMixStatementGenerator (all constructors are private) + * + */ + public class Mix2Statement { + + public final AndStatement[] clauses = new AndStatement[2]; + + Mix2Statement(ElGamalCiphertext a, ElGamalCiphertext b, + ElGamalCiphertext c, ElGamalCiphertext d){ + + 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()); + + 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)); + + // "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); + + // "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 { + /** + * Which of the two clauses is the true one + */ + public final int trueClauseIndex; + + public final AndStatementWitness witness; + + Mix2StatementWitness(int trueClauseIndex, AndStatementWitness witness) { + + this.trueClauseIndex = trueClauseIndex; + assert (trueClauseIndex >= 0 && trueClauseIndex < 2); + this.witness = witness; + } + } + + + /** + * Statement consisting of a conjunction of two {@link DlogStatement}s + */ + public class AndStatement { + public final DlogStatement[] clauses = new DlogStatement[2]; + + AndStatement(DlogStatement clause0, DlogStatement clause1) { + this.clauses[0] = clause0; + this.clauses[1] = clause1; + } + } + + public class AndStatementWitness { + public final DlogStatementWitness[] witnesses = new DlogStatementWitness[2]; + + AndStatementWitness(DlogStatementWitness witness0, DlogStatementWitness witness1) { + this.witnesses[0] = witness0; + this.witnesses[1] = witness1; + } + } + + /** + * The parameters for a statement of discrete-log equality + * + * log_{g}(a)==log_{h}(b) + */ + public class DlogStatement { + public final ECPoint g; + public final ECPoint a; + public final ECPoint h; + public final ECPoint b; + + + DlogStatement(ECPoint a, ECPoint b) { + this.g = Statements.this.g; + this.a = a; + this.h = Statements.this.h; + this.b = b; + } + } + + /** + * Witness for correctness of a {@link DlogStatement} + */ + public class DlogStatementWitness { + /** + * The actual discrete logarithm (i.e., a=g^x, b=h^x) + */ + BigInteger x; + + public DlogStatementWitness(BigInteger x) { + this.x = x; + } + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaFiatShamir.java b/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaFiatShamir.java new file mode 100644 index 0000000..9275fd0 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaFiatShamir.java @@ -0,0 +1,55 @@ +package meerkat.mixer.proofs.generic; + +import com.google.protobuf.Message; +import meerkat.mixer.proofs.Concatenator; +import meerkat.mixer.proofs.SigmaProtocol; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Transform a Sigma protocol into a NIZK using Fiat-Shamir + * We use SHA-256 explicitly to make it easier to verify in other codebases + */ +public class SigmaFiatShamir { + final static String DIGEST_ALG = "SHA-256"; + final MessageDigest md; + final Concatenator.Pair concat; + + public SigmaFiatShamir(Concatenator.Pair concat) { + this.concat = concat; + try { + md = MessageDigest.getInstance(DIGEST_ALG); + } catch (NoSuchAlgorithmException e) { + // Should never happen + throw new RuntimeException("Error in instantiating " + DIGEST_ALG + " digest", e); + } + } + + /** + * Fiat–Shamir heuristic + * @param input - protobuf contains all parameters from the first step of the current proof + * @return randomOracle.hash(input) + */ + public BigInteger hash(Message input) { + md.reset(); + + byte[] arr = input.toByteArray(); + byte[] digest = md.digest(arr); + return new BigInteger(1,digest); + } + + public NIZKMsgType generateNizk(SigmaProtocol.Prover prover) { + FirstMsgType firstMessage = prover.getFirstMessage(); + BigInteger challenge = hash(firstMessage); + FinalMessageType finalMessage = prover.getFinalMessage(challenge); + return concat.concatenate(firstMessage, finalMessage); + } + + public boolean verifyNizk(NIZKMsgType NIZK, SigmaProtocol.Verifier verifier) { + FirstMsgType firstMessage = concat.getMsg1(NIZK); + BigInteger challenge = hash(firstMessage); + return verifier.verify(firstMessage, challenge, concat.getMsg2(NIZK)); + } +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaProtocolAnd2.java b/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaProtocolAnd2.java new file mode 100644 index 0000000..c411ef6 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaProtocolAnd2.java @@ -0,0 +1,91 @@ +package meerkat.mixer.proofs.generic; + +import meerkat.mixer.proofs.Concatenator; +import meerkat.mixer.proofs.SigmaProtocol; + +import java.math.BigInteger; + +/** + * Generic conjuction of sigma protocols + */ +public class SigmaProtocolAnd2 { + static public class Prover + implements SigmaProtocol.Prover { + final SigmaProtocol.Prover[] provers; + + final Concatenator.Pair firstMessageConcatenator; + final Concatenator.Pair finalMessageConcatenator; + + @SafeVarargs + public Prover(Concatenator.Pair firstMessageConcatenator, + Concatenator.Pair finalMessageConcatenator, + SigmaProtocol.Prover... provers) { + this.firstMessageConcatenator = firstMessageConcatenator; + this.finalMessageConcatenator = finalMessageConcatenator; + this.provers = provers; + } + + @Override + public FirstMessageOut getFirstMessage() { + return firstMessageConcatenator.concatenate(provers[0].getFirstMessage(), provers[1].getFirstMessage()); + } + + @Override + public FinalMessageOut getFinalMessage(BigInteger challenge) { + return finalMessageConcatenator.concatenate(provers[0].getFinalMessage(challenge), provers[1].getFinalMessage(challenge)); + } + } + + static public class Verifier + implements SigmaProtocol.Verifier { + final SigmaProtocol.Verifier[] verifiers; + + final Concatenator.Pair firstMessageConcatenator; + final Concatenator.Pair finalMessageConcatenator; + + @SafeVarargs + public Verifier(Concatenator.Pair firstMessageConcatenator, + Concatenator.Pair finalMessageConcatenator, + SigmaProtocol.Verifier... verifiers) { + this.firstMessageConcatenator = firstMessageConcatenator; + this.finalMessageConcatenator = finalMessageConcatenator; + this.verifiers = verifiers; + } + + @Override + public boolean verify(FirstMessageOut firstMessage, BigInteger challenge, FinalMessageOut finalMessage) { + return verifiers[0].verify(firstMessageConcatenator.getMsg1(firstMessage), challenge, finalMessageConcatenator.getMsg1(finalMessage)) && + verifiers[1].verify(firstMessageConcatenator.getMsg2(firstMessage), challenge, finalMessageConcatenator.getMsg2(finalMessage)); + } + } + + static public class Simulator + implements SigmaProtocol.Simulator { + final SigmaProtocol.Simulator[] simulators; + + final Concatenator.Pair firstMessageConcatenator; + final Concatenator.Pair finalMessageConcatenator; + + @SafeVarargs + public Simulator(Concatenator.Pair firstMessageConcatenator, + Concatenator.Pair finalMessageConcatenator, + SigmaProtocol.Simulator... simulators) { + this.firstMessageConcatenator = firstMessageConcatenator; + this.finalMessageConcatenator = finalMessageConcatenator; + this.simulators = simulators; + } + + @Override + public FirstMessageOut getFirstMessage(BigInteger challenge) { + return firstMessageConcatenator.concatenate(simulators[0].getFirstMessage(challenge), simulators[1].getFirstMessage(challenge)); + } + + @Override + public FinalMessageOut getFinalMessage() { + return finalMessageConcatenator.concatenate(simulators[0].getFinalMessage(), simulators[1].getFinalMessage()); + } + + } + +} diff --git a/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaProtocolOr2.java b/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaProtocolOr2.java new file mode 100644 index 0000000..66dfbf4 --- /dev/null +++ b/mixer/src/main/java/meerkat/mixer/proofs/generic/SigmaProtocolOr2.java @@ -0,0 +1,152 @@ +package meerkat.mixer.proofs.generic; + +import meerkat.mixer.proofs.Concatenator; +import meerkat.mixer.proofs.SigmaProtocol; + +import java.math.BigInteger; + +/** + * Disjunction of 2 Sigma protocol statements + */ +public class SigmaProtocolOr2 { + public interface ChallengeGenerator { + + /** + * Generate a random challenge in an appropriate way (e.g., mod group order) + * @return + */ + public BigInteger generateChallenge(); + + /** + * Subtract two challenges in an appropriate way (e.g., mod group order) + * @param c1 + * @param c2 + * @return + */ + public BigInteger subtractChallenge(BigInteger c1, BigInteger c2); + } + + static public class Prover + implements SigmaProtocol.Prover { + final SigmaProtocol.Prover prover; + final SigmaProtocol.Simulator simulator; + final int proverIdx; + + final Concatenator.Pair firstMessageConcatenator; + final Concatenator.Triplet finalMessageConcatenator; + final ChallengeGenerator challengeGenerator; + + BigInteger simChallenge; + + /** + * + * @param prover + * @param simulator + * @param proverIdx Which index should the prover be inserted at (0 means first, 1 means second) + */ + public Prover(Concatenator.Pair firstMessageConcatenator, + Concatenator.Triplet finalMessageConcatenator, + ChallengeGenerator challengeGenerator, + SigmaProtocol.Prover prover, + SigmaProtocol.Simulator simulator, int proverIdx) { + this.firstMessageConcatenator = firstMessageConcatenator; + this.finalMessageConcatenator = finalMessageConcatenator; + this.challengeGenerator = challengeGenerator; + this.prover = prover; + this.simulator = simulator; + this.proverIdx = proverIdx; + assert (proverIdx >= 0 && proverIdx < 2); + } + + @Override + public FirstMessageOut getFirstMessage() { + simChallenge = challengeGenerator.generateChallenge(); + if (proverIdx == 0) { + return firstMessageConcatenator.concatenate(prover.getFirstMessage(), simulator.getFirstMessage(simChallenge)); + } else { + return firstMessageConcatenator.concatenate(simulator.getFirstMessage(simChallenge), prover.getFirstMessage()); + } + } + + + @Override + public FinalMessageOut getFinalMessage(BigInteger challenge) { + BigInteger realchallenge = challengeGenerator.subtractChallenge(challenge, simChallenge); + if (proverIdx == 0) { + return finalMessageConcatenator.concatenate(realchallenge, prover.getFinalMessage(realchallenge), simulator.getFinalMessage()); + } else { + return finalMessageConcatenator.concatenate(simChallenge, simulator.getFinalMessage(), prover.getFinalMessage(realchallenge)); + } + } + } + + static public class Verifier + implements SigmaProtocol.Verifier { + + final Concatenator.Pair firstMessageConcatenator; + final Concatenator.Triplet finalMessageConcatenator; + + final ChallengeGenerator challengeGenerator; + + final SigmaProtocol.Verifier[] verifiers; + + @SafeVarargs + public Verifier(Concatenator.Pair firstMessageConcatenator, + Concatenator.Triplet finalMessageConcatenator, + ChallengeGenerator challengeGenerator, + SigmaProtocol.Verifier... verifiers) { + this.firstMessageConcatenator = firstMessageConcatenator; + this.finalMessageConcatenator = finalMessageConcatenator; + this.challengeGenerator = challengeGenerator; + + this.verifiers = verifiers; + } + + public boolean verify(FirstMessageOut firstMessage, BigInteger challenge, FinalMessageOut finalMessage) { + BigInteger firstChallenge = finalMessageConcatenator.getMsg1of3(finalMessage); + BigInteger secondChallenge = challengeGenerator.subtractChallenge(challenge, firstChallenge); + return verifiers[0].verify(firstMessageConcatenator.getMsg1(firstMessage), firstChallenge, finalMessageConcatenator.getMsg2of3(finalMessage)) & + verifiers[1].verify(firstMessageConcatenator.getMsg2(firstMessage), secondChallenge, finalMessageConcatenator.getMsg3of3(finalMessage)); + } + } + + static public class Simulator + implements SigmaProtocol.Simulator { + final Concatenator.Pair firstMessageConcatenator; + final Concatenator.Triplet finalMessageConcatenator; + + final ChallengeGenerator challengeGenerator; + + final SigmaProtocol.Simulator[] simulators; + + + BigInteger simChallenge0; + + + @SafeVarargs + public Simulator(Concatenator.Pair firstMessageConcatenator, + Concatenator.Triplet finalMessageConcatenator, + ChallengeGenerator challengeGenerator, SigmaProtocol.Simulator... simulators) { + this.firstMessageConcatenator = firstMessageConcatenator; + this.finalMessageConcatenator = finalMessageConcatenator; + this.challengeGenerator = challengeGenerator; + this.simulators = simulators; + } + + + @Override + public FirstMessageOut getFirstMessage(BigInteger challenge) { + + simChallenge0 = challengeGenerator.generateChallenge(); + BigInteger simChallenge1 = challengeGenerator.subtractChallenge(challenge, simChallenge0); + + return firstMessageConcatenator.concatenate(simulators[0].getFirstMessage(simChallenge0), simulators[1].getFirstMessage(simChallenge1)); + } + + @Override + public FinalMessageOut getFinalMessage() { + return finalMessageConcatenator.concatenate(simChallenge0, simulators[0].getFinalMessage(), simulators[1].getFinalMessage()); + } + } + +} diff --git a/mixer/src/main/java/meerkat/mixer/prover/ElGamalProofOrganizer.java b/mixer/src/main/java/meerkat/mixer/prover/ElGamalProofOrganizer.java deleted file mode 100644 index 153dc12..0000000 --- a/mixer/src/main/java/meerkat/mixer/prover/ElGamalProofOrganizer.java +++ /dev/null @@ -1,303 +0,0 @@ -package meerkat.mixer.prover; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.protobuf.ConcreteCrypto; -import meerkat.protobuf.Crypto; -import org.bouncycastle.math.ec.ECPoint; -import org.factcenter.qilin.primitives.concrete.ECGroup; - -/** - * use for organize the input for each ZKP - * - * both meerkat.mixer.prover and meerkat.mixer.verifier implantation are using it - */ -public class ElGamalProofOrganizer { - - private final ECGroup group; - private final ECPoint g; - private final ECPoint h; - private final byte[] gEncoded; - private final byte[] hEncoded; - - /** - * @param group - * @param g - generator of group - * @param h - h = g ^ SecretKey - */ - public ElGamalProofOrganizer(ECGroup group, ECPoint g, ECPoint h){ - this.group = group; - this.g = g; - this.h = h; - this.gEncoded = group.encode(g); - this.hEncoded = group.encode(h); - } - - public enum OrProofOrder { - first, second, third, fourth - } - - public enum TrueCouple { - left, right, unknown - } - - /** - * can be used by meerkat.mixer.prover only - * - * call to the meerkat.mixer.main overload with flag = true - */ - protected ElGamalProofInput createProofInput(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2 - , Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2 - , Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched) throws InvalidProtocolBufferException { - - //boolean flag = true; - return createProofInput(in1,in2,out1,out2,r1,r2,switched,true); - } - - /** - * can be used by anyone, e.g meerkat.mixer.verifier - * - * call to the meerkat.mixer.main overload with flag = false - */ - public ElGamalProofInput createProofInput(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2 - , Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2) throws InvalidProtocolBufferException { - - // flag = false; - return createProofInput(in1,in2,out1,out2,null,null,false,false); - } - - /** - * inner method - * convert each encrypted message to ElGamalCiphertext - * - * @param flag - true if called by meerkat.mixer.prover ( r1,r2,switched are known) - * @return ElGamalProofInput - * @throws InvalidProtocolBufferException - in case that at least one of the encrypted messages isn't - * ElGamalCiphertext - */ - private ElGamalProofInput createProofInput(Crypto.RerandomizableEncryptedMessage in1, Crypto.RerandomizableEncryptedMessage in2 - , Crypto.RerandomizableEncryptedMessage out1, Crypto.RerandomizableEncryptedMessage out2 - , Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched,boolean flag) - throws InvalidProtocolBufferException { - - //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); - - if(flag) { - return new ElGamalProofInput(in1ElGamal, in2ElGamal, out1ElGamal, out2ElGamal, r1, r2, switched); - }else { - return new ElGamalProofInput(in1ElGamal, in2ElGamal, out1ElGamal, out2ElGamal); - } - } - - - /** - * can be construct by instance of organizer only by calling createProofInput method (all constructors are private) - * - * in construction it use for preparing the input for prove, while avoiding double converting or calculations - * - * use as a container for 4 OrProofInput. - */ - public class ElGamalProofInput { - - private final OrProofInput first; - private final OrProofInput second; - private final OrProofInput third; - private final OrProofInput fourth; - - private ECPoint convert2ECPoint(ByteString bs){ - return group.decode(bs.toByteArray()); - } - - - /** - * @param flag - true if called by meerkat.mixer.prover ( r1,r2,switched are known) - */ - private ElGamalProofInput(ConcreteCrypto.ElGamalCiphertext e1, ConcreteCrypto.ElGamalCiphertext e2 - , ConcreteCrypto.ElGamalCiphertext e1New, ConcreteCrypto.ElGamalCiphertext e2New - , Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched,boolean flag){ - - ECPoint e1c1 = convert2ECPoint(e1.getC1()); - ECPoint e1c2 = convert2ECPoint(e1.getC2()); - ECPoint e2c1 = convert2ECPoint(e2.getC1()); - ECPoint e2c2 = convert2ECPoint(e2.getC2()); - ECPoint e1Nc1 = convert2ECPoint(e1New.getC1()); - ECPoint e1Nc2 = convert2ECPoint(e1New.getC2()); - ECPoint e2Nc1 = convert2ECPoint(e2New.getC1()); - ECPoint e2Nc2 = convert2ECPoint(e2New.getC2()); - - ECPoint c1_e1NDive1 = group.add(e1Nc1, group.negate(e1c1)); - ECPoint c1_e2NDive1 = group.add(e2Nc1, group.negate(e1c1)); - ECPoint c1_e1NDive2 = group.add(e1Nc1, group.negate(e2c1)); - ECPoint c1_e2NDive2 = group.add(e2Nc1, group.negate(e2c1)); - - ECPoint c2_e1NDive1 = group.add(e1Nc2, group.negate(e1c2)); - ECPoint c2_e2NDive1 = group.add(e2Nc2, group.negate(e1c2)); - ECPoint c2_e1NDive2 = group.add(e1Nc2, group.negate(e2c2)); - ECPoint c2_e2NDive2 = group.add(e2Nc2, group.negate(e2c2)); - - - if(!flag){ - - this.first = new OrProofInput(c1_e1NDive1,c2_e1NDive1,c1_e1NDive2,c2_e1NDive2); - this.second = new OrProofInput(c1_e1NDive1,c2_e1NDive1,c1_e2NDive1,c2_e2NDive1); - this.third = new OrProofInput(c1_e1NDive2,c2_e1NDive2,c1_e2NDive2,c2_e2NDive2); - this.fourth = new OrProofInput(c1_e2NDive1,c2_e2NDive1,c1_e2NDive2,c2_e2NDive2); - - }else { - - byte[] c1_e1NDive1Encoded = group.encode(c1_e1NDive1); - byte[] c1_e2NDive1Encoded = group.encode(c1_e2NDive1); - byte[] c1_e1NDive2Encoded = group.encode(c1_e1NDive2); - byte[] c1_e2NDive2Encoded = group.encode(c1_e2NDive2); - byte[] c2_e1NDive1Encoded = group.encode(c2_e1NDive1); - byte[] c2_e2NDive1Encoded = group.encode(c2_e2NDive1); - byte[] c2_e1NDive2Encoded = group.encode(c2_e1NDive2); - byte[] c2_e2NDive2Encoded = group.encode(c2_e2NDive2); - - - if (!switched) { - this.first = new OrProofInput(c1_e1NDive1, c2_e1NDive1, c1_e1NDive2, c2_e1NDive2 - , c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e1NDive2Encoded, c2_e1NDive2Encoded - , r1, TrueCouple.left); - this.second = new OrProofInput(c1_e1NDive1, c2_e1NDive1, c1_e2NDive1, c2_e2NDive1 - , c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e2NDive1Encoded, c2_e2NDive1Encoded - , r1, TrueCouple.left); - this.third = new OrProofInput(c1_e1NDive2, c2_e1NDive2, c1_e2NDive2, c2_e2NDive2 - , c1_e1NDive2Encoded, c2_e1NDive2Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded - , r2, TrueCouple.right); - this.fourth = new OrProofInput(c1_e2NDive1, c2_e2NDive1, c1_e2NDive2, c2_e2NDive2 - , c1_e2NDive1Encoded, c2_e2NDive1Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded - , r2, TrueCouple.right); - } else { - this.first = new OrProofInput(c1_e1NDive1, c2_e1NDive1, c1_e1NDive2, c2_e1NDive2 - , c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e1NDive2Encoded, c2_e1NDive2Encoded - , r2, TrueCouple.right); - this.second = new OrProofInput(c1_e1NDive1, c2_e1NDive1, c1_e2NDive1, c2_e2NDive1 - , c1_e1NDive1Encoded, c2_e1NDive1Encoded, c1_e2NDive1Encoded, c2_e2NDive1Encoded - , r1, TrueCouple.right); - this.third = new OrProofInput(c1_e1NDive2, c2_e1NDive2, c1_e2NDive2, c2_e2NDive2 - , c1_e1NDive2Encoded, c2_e1NDive2Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded - , r2, TrueCouple.left); - this.fourth = new OrProofInput(c1_e2NDive1, c2_e2NDive1, c1_e2NDive2, c2_e2NDive2 - , c1_e2NDive1Encoded, c2_e2NDive1Encoded, c1_e2NDive2Encoded, c2_e2NDive2Encoded - , r1, TrueCouple.left); - } - } - } - - - /** - * used by the meerkat.mixer.prover - * call to the meerkat.mixer.main constructor with flag = true - */ - private ElGamalProofInput(ConcreteCrypto.ElGamalCiphertext e1, ConcreteCrypto.ElGamalCiphertext e2 - , ConcreteCrypto.ElGamalCiphertext e1New, ConcreteCrypto.ElGamalCiphertext e2New - , Crypto.EncryptionRandomness r1, Crypto.EncryptionRandomness r2, boolean switched){ - //flag = true; - this(e1,e2,e1New,e2New,r1,r2,switched,true); - } - - /** - * used by meerkat.mixer.prover - * call to the meerkat.mixer.main constructor with flag = true - */ - private ElGamalProofInput(ConcreteCrypto.ElGamalCiphertext e1, ConcreteCrypto.ElGamalCiphertext e2 - , ConcreteCrypto.ElGamalCiphertext e1New, ConcreteCrypto.ElGamalCiphertext e2New){ - //flag = false; - this(e1,e2,e1New,e2New,null,null,false,false); - } - - /** - * getter for all 4 OrProofInputs - * - * @param orProofOrder - * @return the required OrProof - */ - public OrProofInput getOrProofInput(OrProofOrder orProofOrder) { - switch (orProofOrder) { - - case first: - return this.first; - case second: - return this.second; - case third: - return this.third; - case fourth: - return this.fourth; - } - return null; - } - } - - - /** - * can't be constructed (all constructors are private) - * - * 4 instances will be constructed for each new ElGamalProofInput - * - * container for all meerkat.mixer.necessary inputs for single OrProof - */ - public class OrProofInput{ - 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; - - // can be access by meerkat.mixer.prover only - protected final byte[] g1Encoded; - protected final byte[] h1Encoded; - protected final byte[] g2Encoded; - protected final byte[] h2Encoded; - protected final byte[] g1TagEncoded; - protected final byte[] h1TagEncoded; - protected final byte[] g2TagEncoded; - protected final byte[] h2TagEncoded; - protected final Crypto.EncryptionRandomness x; - protected final TrueCouple flag; - - /** - * used by meerkat.mixer.prover only - */ - private OrProofInput(ECPoint h1, ECPoint h2, ECPoint h1Tag, ECPoint h2Tag - ,byte[] h1Encoded, byte[] h2Encoded, byte[] h1TagEncoded, byte[] h2TagEncoded - , Crypto.EncryptionRandomness x, TrueCouple flag) { - this.g1 = g; - this.h1 = h1; - this.g2 = h; - this.h2 = h2; - this.g1Tag = g; - this.h1Tag = h1Tag; - this.g2Tag = h; - this.h2Tag = 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; - - this.x = x; - this.flag = flag; - - } - - /** - * used by meerkat.mixer.verifier - */ - private OrProofInput(ECPoint h1, ECPoint h2, ECPoint h1Tag, ECPoint h2Tag) { - this(h1,h2,h1Tag,h2Tag,null,null,null,null,null,TrueCouple.unknown); - } - } -} diff --git a/mixer/src/main/java/meerkat/mixer/prover/Prover.java b/mixer/src/main/java/meerkat/mixer/prover/Prover.java deleted file mode 100644 index d540974..0000000 --- a/mixer/src/main/java/meerkat/mixer/prover/Prover.java +++ /dev/null @@ -1,216 +0,0 @@ -package meerkat.mixer.prover; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeProver; -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; -import java.util.Random; - - -/** - * implements of Mix2ZeroKnowledgeProver interface - * this implementation assumes that each RerandomizableEncryptedMessage is ElGamalCiphertext - */ -public class Prover implements Mix2ZeroKnowledgeProver { - - private final ECGroup group; - private final RandomOracle randomOracle; - private final Random rand; - private final ECElGamalEncryption encryptor; - private final ECPoint g,h; - private final BigInteger groupOrderUpperBound; - private final ElGamalProofOrganizer organizer; - - /** - * @param rand - * @param encryptor - * @param randomOracle - use for Fiat–Shamir heuristic - */ - public Prover(Random rand,ECElGamalEncryption encryptor,RandomOracle randomOracle) { - - this.rand = rand; - this.encryptor = encryptor; - this.randomOracle = randomOracle; - this.group = this.encryptor.getGroup(); - this.g = group.getGenerator(); - this.h = this.encryptor.getElGamalPK().getPK(); - this.organizer = new ElGamalProofOrganizer(group,g,h); - this.groupOrderUpperBound = group.orderUpperBound(); - } - - /** - * @param in1 - * @param in2 - * @param out1 - if sw then out1 = rerandomize(in2,r2) else out1 = rerandomize(in1,r1) - * @param out2 - if sw then out2 = rerandomize(in1,r1) else out1 = rerandomize(in2,r2) - * @param sw - 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 r1 - * @param r2 - * @return - a valid ZKP that indeed out1,out2 calculated as required - * @throws InvalidProtocolBufferException - */ - public Mixing.ZeroKnowledgeProof prove(Crypto.RerandomizableEncryptedMessage in1, - Crypto.RerandomizableEncryptedMessage in2, - Crypto.RerandomizableEncryptedMessage out1, - Crypto.RerandomizableEncryptedMessage out2, - boolean sw,int i,int j, int layer, - Crypto.EncryptionRandomness r1, - Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException { - Mixing.ZeroKnowledgeProof.OrProof first,second,third,fourth; - - ElGamalProofOrganizer.ElGamalProofInput input = organizer.createProofInput(in1,in2,out1,out2,r1,r2,sw); - - first = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.first)); - second = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.second)); - third = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.third)); - fourth = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.fourth)); - - Mixing.ZeroKnowledgeProof.Location location = Mixing.ZeroKnowledgeProof.Location.newBuilder() - .setI(i) - .setJ(j) - .setLayer(layer) - .build(); - - Mixing.ZeroKnowledgeProof result = Mixing.ZeroKnowledgeProof.newBuilder() - .setFirst(first) - .setSecond(second) - .setThird(third) - .setFourth(fourth) - .setLocation(location) - .build(); - return result; - } - - - /** - * Fiat–Shamir heuristic - * @param input - protobuf contains all parameters from the first step of the current prove - * @param randomOracle - * @return randomOracle.hash(input) - */ - public static BigInteger hash(Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle input, RandomOracle randomOracle) { - byte[] arr = input.toByteArray(); - return new BigInteger(1,randomOracle.hash(arr,arr.length)); - } - - - /** - * @param orProofInput - * @return ZKP OrProof: there exists x s.t (g1 ^ x == h1 and g2 ^ x == h2) or (g1' ^ x == h1 and g2' ^ x == h2) - * assuming DLog is hard in this.group then that proves x is known for the meerkat.mixer.prover - */ - private Mixing.ZeroKnowledgeProof.OrProof createOrProofElGamal(ElGamalProofOrganizer.OrProofInput orProofInput) { - - ECPoint g1 = orProofInput.g1; - ECPoint h1 = orProofInput.h1; - ECPoint g2 = orProofInput.g2; - ECPoint h2 = orProofInput.h2; - - ECPoint g1Tag = orProofInput.g1Tag; - ECPoint h1Tag = orProofInput.h1Tag; - ECPoint g2Tag = orProofInput.g2Tag; - ECPoint h2Tag = orProofInput.h2Tag; - - BigInteger r = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound); - BigInteger c1,c2,z,zTag; - ECPoint u,v,uTag,vTag; - Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle forRandomOracle; - - - switch (orProofInput.flag) { - case left: - c2 = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound); - zTag = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound); - //step 1 - u = group.multiply(g1, r); - v = group.multiply(g2, r); - uTag = group.add(group.multiply(g1Tag, zTag), group.negate(group.multiply(h1Tag, c2))); - vTag = group.add(group.multiply(g2Tag, zTag), group.negate(group.multiply(h2Tag, c2))); - //step 2 - // c1 = (hash(input + step1) + group size - c2)% group size - forRandomOracle = - Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle.newBuilder() - .setG1(ByteString.copyFrom(orProofInput.g1Encoded)) - .setH1(ByteString.copyFrom(orProofInput.h1Encoded)) - .setG2(ByteString.copyFrom(orProofInput.g2Encoded)) - .setH2(ByteString.copyFrom(orProofInput.h2Encoded)) - .setG1Tag(ByteString.copyFrom(orProofInput.g1TagEncoded)) - .setH1Tag(ByteString.copyFrom(orProofInput.h1TagEncoded)) - .setG2Tag(ByteString.copyFrom(orProofInput.g2TagEncoded)) - .setH2Tag(ByteString.copyFrom(orProofInput.h2TagEncoded)) - .setU(ByteString.copyFrom(group.encode(u))) - .setV(ByteString.copyFrom(group.encode(v))) - .setUTag(ByteString.copyFrom(group.encode(uTag))) - .setVTag(ByteString.copyFrom(group.encode(vTag))) - .build(); - c1 = hash(forRandomOracle,randomOracle).add(group.orderUpperBound().subtract(c2)).mod(groupOrderUpperBound); - //step 3 - //z = (r + c1 * x) % group size; - z = r.add(c1.multiply(encryptor.extractRandomness(orProofInput.x))).mod(groupOrderUpperBound); - break; - case right: - c1 = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound); - z = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound); - //step 1 - uTag = group.multiply(g1Tag, r); - vTag = group.multiply(g2Tag, r); - u = group.add(group.multiply(g1, z), group.negate(group.multiply(h1, c1))); - v = group.add(group.multiply(g2, z), group.negate(group.multiply(h2, c1))); - //step 2 - // c1 = (hash(input + step1) + group size - c1)% group size - forRandomOracle = - Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle.newBuilder() - .setG1(ByteString.copyFrom(orProofInput.g1Encoded)) - .setH1(ByteString.copyFrom(orProofInput.h1Encoded)) - .setG2(ByteString.copyFrom(orProofInput.g2Encoded)) - .setH2(ByteString.copyFrom(orProofInput.h2Encoded)) - .setG1Tag(ByteString.copyFrom(orProofInput.g1TagEncoded)) - .setH1Tag(ByteString.copyFrom(orProofInput.h1TagEncoded)) - .setG2Tag(ByteString.copyFrom(orProofInput.g2TagEncoded)) - .setH2Tag(ByteString.copyFrom(orProofInput.h2TagEncoded)) - .setU(ByteString.copyFrom(group.encode(u))) - .setV(ByteString.copyFrom(group.encode(v))) - .setUTag(ByteString.copyFrom(group.encode(uTag))) - .setVTag(ByteString.copyFrom(group.encode(vTag))) - .build(); - c2 = hash(forRandomOracle,randomOracle).add(group.orderUpperBound().subtract(c1)).mod(groupOrderUpperBound); - //step 3 - //zTag = (r + c2 * x) % group size; - zTag = r.add(c2.multiply(encryptor.extractRandomness(orProofInput.x))).mod(groupOrderUpperBound); - break; - default: - return null; - } - - - return Mixing.ZeroKnowledgeProof.OrProof.newBuilder() - .setG1(forRandomOracle.getG1()) - .setH1(forRandomOracle.getH1()) - .setG2(forRandomOracle.getG2()) - .setH2(forRandomOracle.getH2()) - .setG1Tag(forRandomOracle.getG1()) - .setH1Tag(forRandomOracle.getH1Tag()) - .setG2Tag(forRandomOracle.getG2Tag()) - .setH2Tag(forRandomOracle.getH2Tag()) - .setU(forRandomOracle.getU()) - .setV(forRandomOracle.getV()) - .setUTag(forRandomOracle.getUTag()) - .setVTag(forRandomOracle.getVTag()) - .setC1(ByteString.copyFrom(c1.toByteArray())) - .setC2(ByteString.copyFrom(c2.toByteArray())) - .setZ(ByteString.copyFrom(z.toByteArray())) - .setZTag(ByteString.copyFrom(zTag.toByteArray())) - .build(); - } -} - diff --git a/mixer/src/main/java/meerkat/mixer/verifier/Verifier.java b/mixer/src/main/java/meerkat/mixer/verifier/Verifier.java deleted file mode 100644 index ed08512..0000000 --- a/mixer/src/main/java/meerkat/mixer/verifier/Verifier.java +++ /dev/null @@ -1,95 +0,0 @@ -package meerkat.mixer.verifier; - -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; -import meerkat.protobuf.Crypto; -import meerkat.protobuf.Mixing; -import org.bouncycastle.math.ec.ECPoint; -import meerkat.mixer.prover.ElGamalProofOrganizer; -import meerkat.mixer.prover.Prover; -import org.factcenter.qilin.primitives.RandomOracle; -import org.factcenter.qilin.primitives.concrete.ECGroup; - -/** - * implements Mix2ZeroKnowledgeVerifier - */ -public class Verifier implements Mix2ZeroKnowledgeVerifier { - - - private final ECGroup group; - private final RandomOracle randomOracle; - private final ECPoint g,h; - private final ElGamalProofOrganizer organizer; - private final ZeroKnowledgeOrProofParser parser; - - /** - * constructor - * @param encryptor should be as the encryptor used by meerkat.mixer.prover - * @param randomOracle should be as the random oracle used by meerkat.mixer.prover - */ - public Verifier(ECElGamalEncryption encryptor, RandomOracle randomOracle) { - this.group = encryptor.getGroup(); - this.g = group.getGenerator(); - this.h = encryptor.getElGamalPK().getPK(); - this.randomOracle = randomOracle; - this.organizer = new ElGamalProofOrganizer(group,g,h); - this.parser = new ZeroKnowledgeOrProofParser(group); - } - - /** - * verify zero knowledge proof - * @param in1 - * @param in2 - * @param out1 - * @param out2 - * @param proof out1 = rerandomize(in1) && out2 = rerandomize(in2) - * || - * out1 = rerandomize(in2) && out2 = rerandomize(in1) - * @return true iff all 4 or proofs are valid - * @throws InvalidProtocolBufferException - */ - public boolean verify(Crypto.RerandomizableEncryptedMessage in1, - Crypto.RerandomizableEncryptedMessage in2, - Crypto.RerandomizableEncryptedMessage out1, - Crypto.RerandomizableEncryptedMessage out2, - Mixing.ZeroKnowledgeProof proof) throws InvalidProtocolBufferException { - ElGamalProofOrganizer.ElGamalProofInput input = organizer.createProofInput(in1,in2,out1,out2); - return verifyElGamaOrProof(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.first), proof.getFirst())&& - verifyElGamaOrProof(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.second), proof.getSecond())&& - verifyElGamaOrProof(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.third), proof.getThird())&& - verifyElGamaOrProof(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.fourth), proof.getFourth()); - - } - - - /** - * - * @param orProofInput - * @param orProof - * @return verify single or proof - */ - private boolean verifyElGamaOrProof(ElGamalProofOrganizer.OrProofInput orProofInput, - Mixing.ZeroKnowledgeProof.OrProof orProof) { - ZeroKnowledgeOrProofParser.ZeroKnowledgeOrProofContainer container = parser.parseOrProof(orProof); - return container.g1.equals(orProofInput.g1) && - container.h1.equals(orProofInput.h1) && - container.g2.equals(orProofInput.g2) && - container.h2.equals(orProofInput.h2) && - container.g1Tag.equals(orProofInput.g1Tag) && - container.h1Tag.equals(orProofInput.h1Tag) && - container.g2Tag.equals(orProofInput.g2Tag) && - container.h2Tag.equals(orProofInput.h2Tag) && - container.c1.add(container.c2).mod(group.orderUpperBound()) - .equals(Prover.hash(container.forRandomOracle,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))); - - } -} diff --git a/mixer/src/main/java/meerkat/mixer/verifier/VerifyTable.java b/mixer/src/main/java/meerkat/mixer/verifier/VerifyTable.java deleted file mode 100644 index 2bb3781..0000000 --- a/mixer/src/main/java/meerkat/mixer/verifier/VerifyTable.java +++ /dev/null @@ -1,91 +0,0 @@ -package meerkat.mixer.verifier; - -import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; -import meerkat.crypto.mixnet.MixerOutput; -import meerkat.protobuf.Crypto; -import meerkat.protobuf.Mixing; - -import java.util.Arrays; - -/** - * Created by Tzlil on 12/30/2015. - * provide one operation - verify meerkat.mixer.mixing output - */ -public final class VerifyTable { - /** - * constructor - * @param verifier - * @param n - * @param mixerOutput - * @return true iff the meerkat.mixer.mixing output is valid - * @throws InvalidProtocolBufferException - */ - public static boolean verifyTable(Mix2ZeroKnowledgeVerifier verifier,int n,MixerOutput mixerOutput) - throws InvalidProtocolBufferException { - int index1,index2,layer; - - //assert n = 2^k - if ( (n &(n-1)) != 0) - throw new IllegalArgumentException("n"); - - int layers = 2*(int)(Math.log(n) / Math.log(2)) - 1; - //initialize locationChecksum table - // use for check BeneshNet validity - boolean[][] locationChecksum = new boolean[layers][n]; - for (boolean[] locationChecksumLayer: locationChecksum) { - Arrays.fill(locationChecksumLayer,false); - } - - Mixing.ZeroKnowledgeProof[][] zeroKnowledgeProofs = mixerOutput.getProofs(); - Crypto.RerandomizableEncryptedMessage[][] rerandomizableEncryptedMessages = mixerOutput.getEncryptedMessages(); - - for (int i = 0; i < zeroKnowledgeProofs.length ; i++){ - for (int j = 0; j < zeroKnowledgeProofs[i].length ; j ++){ - Mixing.ZeroKnowledgeProof zkp = zeroKnowledgeProofs[i][j]; - Mixing.ZeroKnowledgeProof.Location location = zkp.getLocation(); - index1 = location.getI(); - index2 = location.getJ(); - layer = location.getLayer(); - - // check location validity - if (layer > layers >> 1) { - if (index2 - index1 != n >> (layers - layer)) - return false; - } - else{ - if (index2 - index1 != n >> (layer + 1)) - return false; - } - - // mark location in table - locationChecksum[layer][index1] = true; - locationChecksum[layer][index2] = true; - - // verify proof - if(!verifier.verify(rerandomizableEncryptedMessages[layer][index1], - rerandomizableEncryptedMessages[layer][index2], - rerandomizableEncryptedMessages[layer + 1][index1], - rerandomizableEncryptedMessages[layer + 1][index2], - zkp)) { - - verifier.verify(rerandomizableEncryptedMessages[layer][index1], - rerandomizableEncryptedMessages[layer][index2], - rerandomizableEncryptedMessages[layer + 1][index1], - rerandomizableEncryptedMessages[layer + 1][index2], - zkp); - return false; - } - } - } - - // verify all meerkat.mixer.necessary locations for BeneshNet were proved - for (boolean[] checksumLayer: locationChecksum) { - for (boolean locationBoolean: checksumLayer) { - if (!locationBoolean) - return false; - } - } - return true; - } -} diff --git a/mixer/src/main/java/meerkat/mixer/verifier/ZeroKnowledgeOrProofParser.java b/mixer/src/main/java/meerkat/mixer/verifier/ZeroKnowledgeOrProofParser.java deleted file mode 100644 index 01ff344..0000000 --- a/mixer/src/main/java/meerkat/mixer/verifier/ZeroKnowledgeOrProofParser.java +++ /dev/null @@ -1,95 +0,0 @@ -package meerkat.mixer.verifier; - -import com.google.protobuf.ByteString; -import meerkat.protobuf.Mixing; -import org.bouncycastle.math.ec.ECPoint; -import org.factcenter.qilin.primitives.concrete.ECGroup; - -import java.math.BigInteger; - -/** - * Created by Tzlil on 1/25/2016. - * zero knowledge proof parser - */ -public class ZeroKnowledgeOrProofParser { - - private final ECGroup group; - - /** - * parse or proof message and return the result in zero knowledge or proof container - * @param orProof - * @return zero knowledge or proof container - */ - public ZeroKnowledgeOrProofContainer parseOrProof(Mixing.ZeroKnowledgeProof.OrProof orProof){ - return new ZeroKnowledgeOrProofContainer(orProof); - } - - /** - * getter - * @param group - */ - public ZeroKnowledgeOrProofParser(ECGroup group) { - this.group = group; - } - - /** - * convert ByteString to ECPoint - * @param bs - * @return - */ - public ECPoint convert2ECPoint(ByteString bs){ - return group.decode(bs.toByteArray()); - } - - - /** - * inner class - * container for parsed zero knowledge or proof - * constructor is private, can be construct using ZeroKnowledgeOrProofParser.parseOrProof - */ - public class ZeroKnowledgeOrProofContainer{ - public final ECPoint g1,g2,h1,h2; - public final ECPoint g1Tag,g2Tag,h1Tag,h2Tag; - public final ECPoint u,v,uTag,vTag; - public final BigInteger c1,c2,z,zTag; - public final Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle forRandomOracle; - - /** - * constructor - * @param orProof - */ - private ZeroKnowledgeOrProofContainer(Mixing.ZeroKnowledgeProof.OrProof orProof){ - this.forRandomOracle = - Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle.newBuilder() - .setG1(orProof.getG1()) - .setH1(orProof.getH1()) - .setG2(orProof.getG2()) - .setH2(orProof.getH2()) - .setG1Tag(orProof.getG1Tag()) - .setH1Tag(orProof.getH1Tag()) - .setG2Tag(orProof.getG2Tag()) - .setH2Tag(orProof.getH2Tag()) - .setU(orProof.getU()) - .setV(orProof.getV()) - .setUTag(orProof.getUTag()) - .setVTag(orProof.getVTag()) - .build(); - this.g1 = convert2ECPoint(orProof.getG1()); - this.g2 = convert2ECPoint(orProof.getG2()); - this.h1 = convert2ECPoint(orProof.getH1()); - this.h2 = convert2ECPoint(orProof.getH2()); - this.g1Tag = convert2ECPoint(orProof.getG1Tag()); - this.g2Tag = convert2ECPoint(orProof.getG2Tag()); - this.h1Tag = convert2ECPoint(orProof.getH1Tag()); - this.h2Tag = convert2ECPoint(orProof.getH2Tag()); - this.u = convert2ECPoint(orProof.getU()); - this.v = convert2ECPoint(orProof.getV()); - this.uTag = convert2ECPoint(orProof.getUTag()); - this.vTag = convert2ECPoint(orProof.getVTag()); - this.c1 = new BigInteger(orProof.getC1().toByteArray()); - this.c2 = new BigInteger(orProof.getC2().toByteArray()); - this.z = new BigInteger(orProof.getZ().toByteArray()); - this.zTag = new BigInteger(orProof.getZTag().toByteArray()); - } - } -} diff --git a/mixer/src/main/proto/meerkat/mixing.proto b/mixer/src/main/proto/meerkat/mixing.proto new file mode 100644 index 0000000..0755948 --- /dev/null +++ b/mixer/src/main/proto/meerkat/mixing.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package meerkat; + +option java_package = "meerkat.protobuf"; + +import 'meerkat/crypto.proto'; +import 'meerkat/concrete_crypto.proto'; + +message MixBatchHeader { + int32 logN = 1; // log (base 2) of number of inputs to mix + int32 layers = 2; // Number of layers in mix +} + +message Plaintext { + bytes data = 1; +} + +message Mix2Proof { + // Proof that log_g(a) = log_h(b) = x + message DlogProof { + message FirstMessage { + GroupElement gr = 1; // g^r + GroupElement hr = 2; // h^r + } + message FinalMessage { + BigInteger xcr = 1; // xc+r, where c is the challenge + } + } + + message AndProof { + message FirstMessage { + DlogProof.FirstMessage clause0 = 1; + DlogProof.FirstMessage clause1 = 2; + } + message FinalMessage { + DlogProof.FinalMessage clause0 = 1; + DlogProof.FinalMessage clause1 = 2; + } + } + + 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 + } + + + // Location of the + message Location { + int32 layer = 1; // layer in which the switch is placed + int32 switchIdx = 2; // idx of the switch + int32 out0 = 3; // idx of the first output ciphertext (in layer+1 of the ciphertext matrix) + int32 out1 = 4; // idx of the second output ciphertext (in layer+1 of the ciphertext matrix) + } + + + FirstMessage firstMessage = 1; + FinalMessage finalMessage = 2; + Location location = 5; +} + + diff --git a/mixer/src/test/java/meerkat/mixer/CreateTestVector.java b/mixer/src/test/java/meerkat/mixer/CreateTestVector.java index fe45e1b..17df70a 100644 --- a/mixer/src/test/java/meerkat/mixer/CreateTestVector.java +++ b/mixer/src/test/java/meerkat/mixer/CreateTestVector.java @@ -1,100 +1,100 @@ -package meerkat.mixer; - -import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeProver; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; -import meerkat.mixer.mixing.Mixer; -import meerkat.mixer.mixing.MixerOutput; -import meerkat.mixer.prover.Prover; -import meerkat.mixer.verifier.Verifier; -import meerkat.mixer.verifier.VerifyTable; -import meerkat.protobuf.Crypto; -import meerkat.protobuf.Voting; -import org.factcenter.qilin.primitives.RandomOracle; -import org.factcenter.qilin.primitives.concrete.DigestOracle; -import org.factcenter.qilin.primitives.concrete.ECElGamal; -import org.factcenter.qilin.primitives.concrete.ECGroup; -import org.junit.Before; - -import java.io.IOException; -import java.security.spec.InvalidKeySpecException; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Created by Tzlil on 1/19/2016. - */ -public class CreateTestVector { - - - ECElGamalEncryption encryptor; - ECGroup group; - Random random,randomMixer,randomProver; - RandomOracle randomOracle; - Mix2ZeroKnowledgeVerifier verifier; - Mix2ZeroKnowledgeProver prover; - meerkat.crypto.mixnet.Mixer mixer; - private int layers; - private int n; - - - @Before - public void setup() throws InvalidKeySpecException { - // initialization - random = new Random(); - group = new ECGroup("secp256k1"); - encryptor = new ECElGamalEncryption(); - encryptor.init(Utils.serializePk(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); - randomMixer = new Random(); - randomProver = new Random(); - randomOracle = new DigestOracle(); - verifier = new Verifier(encryptor,randomOracle); - prover = new Prover(randomProver,encryptor,randomOracle); - mixer = new Mixer(prover,encryptor); - - // generate n - int logN = 10; // + random.nextInt(8) - layers = 2*logN - 1; - n = 1 << logN; - } - - public List generateMixerInput(){ - List result = new ArrayList(); - Voting.PlaintextBallot msg; - for (int i = 0; i < n ; i++){ - msg = Utils.genRandomBallot(2,3,16); - result.add(encryptor.encrypt(msg, encryptor.generateRandomness(random))); - } - return result; - } - //@SimpleRerandomizeTest - public void createValidTest() throws IOException { - - List mixerInput = generateMixerInput(); - System.out.println("start mixing"); - MixerOutput mixerOutput = (MixerOutput)mixer.mix(mixerInput,randomMixer); - System.out.println("mixing ended, start verification"); - assert (VerifyTable.verifyTable(verifier,n,mixerOutput)); - System.out.println("verification ended, start printing"); - mixerOutput.outToFolder("C:\\Users\\Tzlil\\Desktop\\TestVector\\Test3"); - System.out.println("all done"); - } - - //@SimpleRerandomizeTest - public void createInvalidTest() throws IOException { - - //Mix2ZeroKnowledgeVerifier corruptedVerifier = new Verifier(encryptor,randomOracle,true); - //Mix2ZeroKnowledgeProver corruptedProver = new Prover(randomProver,encryptor,randomOracle,true); - //mixer = new Mixer(randomMixer,corruptedProver,encryptor,corruptedVerifier); - - List mixerInput = generateMixerInput(); - System.out.println("start mixing"); - MixerOutput mixerOutput = (MixerOutput)mixer.mix(mixerInput,random); - System.out.println("mixing ended, start negative verification"); - assert (!VerifyTable.verifyTable(verifier,n,mixerOutput)); - System.out.println("verification ended, start printing"); - mixerOutput.outToFolder("C:\\Users\\Tzlil\\Desktop\\TestVector\\Test5"); - System.out.println("all done"); - } -} +//package meerkat.mixer; +// +//import meerkat.crypto.concrete.ECElGamalEncryption; +//import meerkat.mixProverVerifier.proofs.Mix2nizk.Prover; +//import meerkat.mixProverVerifier.proofs.Mix2nizk.Verifier; +//import meerkat.mixProverVerifier.Mixer; +//import meerkat.mixProverVerifier.MixerOutput; +//import meerkat.mixProverVerifier.proofs.Prover; +//import meerkat.mixProverVerifier.proofs.Verifier; +//import meerkat.mixProverVerifier.VerifyTable; +//import meerkat.protobuf.Crypto; +//import meerkat.protobuf.Voting; +//import org.factcenter.qilin.primitives.RandomOracle; +//import org.factcenter.qilin.primitives.concrete.DigestOracle; +//import org.factcenter.qilin.primitives.concrete.ECElGamal; +//import org.factcenter.qilin.primitives.concrete.ECGroup; +//import org.junit.Before; +// +//import java.io.IOException; +//import java.security.spec.InvalidKeySpecException; +//import java.util.ArrayList; +//import java.util.List; +//import java.util.Random; +// +///** +// * Created by Tzlil on 1/19/2016. +// */ +//public class CreateTestVector { +// +// +// ECElGamalEncryption encryptor; +// ECGroup group; +// Random random,randomMixer,randomProver; +// RandomOracle randomOracle; +// Verifier verifier; +// Prover prover; +// meerkat.crypto.mixnet.Mixer mixProverVerifier; +// private int layers; +// private int n; +// +// +// @Before +// public void setup() throws InvalidKeySpecException { +// // initialization +// random = new Random(); +// group = new ECGroup("secp256k1"); +// encryptor = new ECElGamalEncryption(); +// encryptor.init(Utils.encodePK(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); +// randomMixer = new Random(); +// randomProver = new Random(); +// randomOracle = new DigestOracle(); +// verifier = new Verifier(encryptor,randomOracle); +// prover = new Prover(randomProver,encryptor,randomOracle); +// mixProverVerifier = new Mixer(prover,encryptor); +// +// // generate n +// int logN = 10; // + random.nextInt(8) +// layers = 2*logN - 1; +// n = 1 << logN; +// } +// +// public List generateMixerInput(){ +// List result = new ArrayList(); +// Voting.PlaintextBallot msg; +// for (int i = 0; i < n ; i++){ +// msg = Utils.genRandomBallot(2,3,16); +// result.add(encryptor.encrypt(msg, encryptor.generateRandomness(random))); +// } +// return result; +// } +// //@SimpleRerandomizeTest +// public void createValidTest() throws IOException { +// +// List mixerInput = generateMixerInput(); +// System.out.println("start network"); +// MixerOutput mixerOutput = (MixerOutput)mixProverVerifier.mix(mixerInput,randomMixer); +// System.out.println("network ended, start verification"); +// assert (VerifyTable.verifyTable(verifier,n,mixerOutput)); +// System.out.println("verification ended, start printing"); +// mixerOutput.outToFolder("C:\\Users\\Tzlil\\Desktop\\TestVector\\Test3"); +// System.out.println("all done"); +// } +// +// //@SimpleRerandomizeTest +// public void createInvalidTest() throws IOException { +// +// //Verifier corruptedVerifier = new Verifier(enc,randomOracle,true); +// //Prover corruptedProver = new Prover(randomProver,enc,randomOracle,true); +// //mixProverVerifier = new Mixer(randomMixer,corruptedProver,enc,corruptedVerifier); +// +// List mixerInput = generateMixerInput(); +// System.out.println("start network"); +// MixerOutput mixerOutput = (MixerOutput)mixProverVerifier.mix(mixerInput,random); +// System.out.println("network ended, start negative verification"); +// assert (!VerifyTable.verifyTable(verifier,n,mixerOutput)); +// System.out.println("verification ended, start printing"); +// mixerOutput.outToFolder("C:\\Users\\Tzlil\\Desktop\\TestVector\\Test5"); +// System.out.println("all done"); +// } +//} diff --git a/mixer/src/test/java/meerkat/mixer/ECParamTestBase.java b/mixer/src/test/java/meerkat/mixer/ECParamTestBase.java new file mode 100644 index 0000000..23b9c62 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/ECParamTestBase.java @@ -0,0 +1,40 @@ +package meerkat.mixer; + +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; +import meerkat.protobuf.ConcreteCrypto; +import org.factcenter.qilin.primitives.RandomOracle; +import org.factcenter.qilin.primitives.concrete.DigestOracle; +import org.factcenter.qilin.primitives.concrete.ECElGamal; +import org.factcenter.qilin.primitives.concrete.ECGroup; + +import java.math.BigInteger; +import java.security.spec.InvalidKeySpecException; +import java.util.Random; + +/** + * Base class with common parameters. + */ +public class ECParamTestBase { + + public Random rand; + public ECElGamal.SK key; + public ECGroup group; + public ECElGamalEncryption enc; + public ConcreteCrypto.ElGamalPublicKey serializedPk; + + public ECParamTestBase() { + rand = new Random(1); + group = new ECGroup("secp256k1"); + BigInteger sk = ECElGamal.generateSecretKey(group, rand); + key = new ECElGamal.SK(group, sk); + serializedPk = Util.encodePK(group, key); + enc = new ECElGamalEncryption(); + try { + enc.init(serializedPk); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + assert(false); + } + } +} diff --git a/mixer/src/test/java/meerkat/mixer/MixNetworkTest.java b/mixer/src/test/java/meerkat/mixer/MixNetworkTest.java deleted file mode 100644 index 23eaea4..0000000 --- a/mixer/src/test/java/meerkat/mixer/MixNetworkTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package meerkat.mixer; - -/** - * Created by Tzlil on 12/17/2015. - */ - -import meerkat.mixer.mixing.MixNetwork; -import meerkat.mixer.mixing.RandomPermutation; -import meerkat.mixer.mixing.Switch; -import org.junit.Test; -import java.util.Arrays; -import java.util.Random; - -public class MixNetworkTest { - - @Test - public void testMixNetwork() throws Exception{ - - Random random = new Random(); - int logn = 10; - int n = 1 << logn; - int layers = 2*logn - 1; - RandomPermutation randomPermutation = new RandomPermutation(n,random); - MixNetwork mixNetwork = new MixNetwork(randomPermutation); - - //initialize arr s.t arr[i] = i - int[] arr = new int[n]; - for (int i = 0; i < n ;i ++) - arr[i] = i; - - // layer by layer swap between values - for (int layer = 0 ; layer< layers ; layer ++) { - for (Switch sw : mixNetwork.getSwitchesByLayer(layer)) { - if(sw.value) { - arr[sw.i] += arr[sw.j]; - arr[sw.j] = arr[sw.i] - arr[sw.j]; - arr[sw.i] -= arr[sw.j]; - } - } - } - - assert(Arrays.equals(arr,randomPermutation.permutation)); - - } - -} diff --git a/mixer/src/test/java/meerkat/mixer/MixingTest.java b/mixer/src/test/java/meerkat/mixer/MixingTest.java index f9ec6f8..d424a0b 100644 --- a/mixer/src/test/java/meerkat/mixer/MixingTest.java +++ b/mixer/src/test/java/meerkat/mixer/MixingTest.java @@ -5,18 +5,9 @@ package meerkat.mixer; */ import com.google.protobuf.InvalidProtocolBufferException; -import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; -import meerkat.mixer.mixing.Mixer; -import meerkat.mixer.prover.Prover; -import meerkat.mixer.verifier.Verifier; -import meerkat.mixer.verifier.VerifyTable; +import meerkat.mixer.proofs.concrete.Mix2nizk; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting; -import org.factcenter.qilin.primitives.RandomOracle; -import org.factcenter.qilin.primitives.concrete.DigestOracle; -import org.factcenter.qilin.primitives.concrete.ECElGamal; -import org.factcenter.qilin.primitives.concrete.ECGroup; import org.junit.Before; import org.junit.Test; @@ -25,45 +16,33 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; -public class MixingTest { - - ECElGamalEncryption encryptor; - ECGroup group; +public class MixingTest extends ECParamTestBase { Random random,randomMixer,randomProver; - RandomOracle randomOracle; - Mix2ZeroKnowledgeVerifier verifier; - Prover prover; - meerkat.crypto.mixnet.Mixer mixer; - private int layers; + Mix2nizk mix2nizk; + MixGenerator mixer; private int n; @Before public void setup() throws InvalidKeySpecException { // initialization - random = new Random(); - group = new ECGroup("secp256k1"); - encryptor = new ECElGamalEncryption(); - encryptor.init(Utils.serializePk(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); - randomMixer = new Random(); - randomProver = new Random(); - randomOracle = new DigestOracle(); - verifier = new Verifier(encryptor,randomOracle); - prover = new Prover(randomProver,encryptor,randomOracle); - mixer = new Mixer(prover,encryptor); + random = new Random(1); + randomMixer = new Random(2); + randomProver = new Random(3); + mix2nizk = new Mix2nizk(randomProver, enc); + mixer = new MixGenerator(mix2nizk, enc); // generate n - int logN = 8; // + random.nextInt(8) - layers = 2*logN - 1; + int logN = 5; // + random.nextInt(8) n = 1 << logN; } - public List generateMixerInput(){ + public List generateMixerInput(int n){ List result = new ArrayList(); Voting.PlaintextBallot msg; for (int i = 0; i < n ; i++){ msg = Utils.genRandomBallot(2,3,16); - result.add(encryptor.encrypt(msg, encryptor.generateRandomness(random))); + result.add(enc.encrypt(msg, enc.generateRandomness(random))); } return result; } @@ -72,11 +51,11 @@ public class MixingTest { public void mixingTest() throws InvalidProtocolBufferException { System.out.println("n is : " + n); System.out.println(" generating input"); - List mixerInput = generateMixerInput(); - System.out.println(" start mixing"); + List mixerInput = generateMixerInput(n); + System.out.println(" start network"); long startTime = System.currentTimeMillis(); - meerkat.crypto.mixnet.MixerOutput mixerOutput = mixer.mix(mixerInput,randomMixer); + MixerOutput mixerOutput = mixer.mix(mixerInput,randomMixer); long finishTime = System.currentTimeMillis(); System.out.println(" that took: "+(finishTime-startTime)+ " ms"); @@ -84,7 +63,7 @@ public class MixingTest { System.out.println("start verification"); startTime = System.currentTimeMillis(); - assert (VerifyTable.verifyTable(verifier,n,mixerOutput)); + assert (MixVerifier.verifyTable(mix2nizk, mixerOutput, true)); finishTime = System.currentTimeMillis(); System.out.println(" that took: "+(finishTime-startTime)+ " ms"); diff --git a/mixer/src/test/java/meerkat/mixer/RerandomizeTest.java b/mixer/src/test/java/meerkat/mixer/RerandomizeTest.java index dea0069..52a7482 100644 --- a/mixer/src/test/java/meerkat/mixer/RerandomizeTest.java +++ b/mixer/src/test/java/meerkat/mixer/RerandomizeTest.java @@ -1,8 +1,8 @@ package meerkat.mixer; -import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting; @@ -35,7 +35,7 @@ public class RerandomizeTest { group = new ECGroup("secp256k1"); BigInteger sk = ECElGamal.generateSecretKey(group, rand); key = new ECElGamal.SK(group, sk); - serializedPk = Utils.serializePk(group, key); + serializedPk = Util.encodePK(group, key); enc = new ECElGamalEncryption(); enc.init(serializedPk); RandomOracle randomOracle = new DigestOracle(); @@ -43,8 +43,8 @@ public class RerandomizeTest { h = enc.getElGamalPK().getPK(); } - private ECPoint convert2ECPoint(ByteString bs){ - return group.decode(bs.toByteArray()); + private ECPoint convert2ECPoint(ConcreteCrypto.GroupElement bs){ + return enc.decodeElement(bs); } public void oneRerandomizeTest() throws InvalidProtocolBufferException { @@ -56,8 +56,8 @@ public class RerandomizeTest { Crypto.RerandomizableEncryptedMessage e = enc.encrypt(msg, enc.generateRandomness(rand)); Crypto.RerandomizableEncryptedMessage eNew = enc.rerandomize(e, r); - assert (Utils.decrypt(Voting.PlaintextBallot.class, key, group, e).equals(msg)); - assert (Utils.decrypt(Voting.PlaintextBallot.class, key, group, eNew).equals(msg)); + assert (Util.decrypt(Voting.PlaintextBallot.class, key, group, e).equals(msg)); + assert (Util.decrypt(Voting.PlaintextBallot.class, key, group, eNew).equals(msg)); ConcreteCrypto.ElGamalCiphertext eElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(e); ConcreteCrypto.ElGamalCiphertext eNewElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(eNew); diff --git a/mixer/src/test/java/meerkat/mixer/Utils.java b/mixer/src/test/java/meerkat/mixer/Utils.java index 06329ef..3852c8d 100644 --- a/mixer/src/test/java/meerkat/mixer/Utils.java +++ b/mixer/src/test/java/meerkat/mixer/Utils.java @@ -27,67 +27,7 @@ import java.util.Random; * Created by Tzlil on 1/1/2016. */ public class Utils { - - - public final static String ENCRYPTION_KEY_ALGORITHM = "ECDH"; - /** - * Serialize an El-Gamal public key into a form acceptable by {@link ECElGamalEncryption} - * @param pk - * @return - */ - public static ConcreteCrypto.ElGamalPublicKey serializePk(ECGroup group, ElGamal.PK pk) { - ECPoint pkPoint = pk.getPK(); - ECParameterSpec params = group.getCurveParams(); - - ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(pkPoint, params); - - try { - KeyFactory fact = KeyFactory.getInstance(ENCRYPTION_KEY_ALGORITHM, - GlobalCryptoSetup.getInstance().getBouncyCastleProvider()); - PublicKey javaPk = fact.generatePublic(pubKeySpec); - ConcreteCrypto.ElGamalPublicKey serializedPk = ConcreteCrypto.ElGamalPublicKey.newBuilder() - .setSubjectPublicKeyInfo(ByteString.copyFrom(javaPk.getEncoded())).build(); - - return serializedPk; - } catch (Exception e) { - throw new RuntimeException("Error converting public key!", e); - } - } - - /** - * Standard (non-threshold) decryption for testing purposes. - * @param secretKey - * @return - */ - 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(); - - ECPoint c1 = group.decode(c1encoded.toByteArray()); - ECPoint c2 = group.decode(c2encoded.toByteArray()); - - assert (group.contains(c1)); - assert (group.contains(c2)); - - ECPoint plaintextEncoded = secretKey.decrypt(new Pair(c1, c2)); - - byte[] plaintext = group.injectiveDecode(plaintextEncoded); - - ByteArrayInputStream in = new ByteArrayInputStream(plaintext); - - try { - java.lang.reflect.Method newBuilder = plaintextMessageType.getMethod("newBuilder"); - Message.Builder builder = (Message.Builder) newBuilder.invoke(plaintextMessageType); - builder.mergeDelimitedFrom(in); - return plaintextMessageType.cast(builder.build()); - } catch (Exception e) { - throw new InvalidProtocolBufferException("Plaintext protobuf error"); - } - } - - static Random random = new Random(); + static Random random = new Random(0); public static Voting.PlaintextBallot genRandomBallot(int numQuestions, int numAnswers, int maxAnswer) { Voting.PlaintextBallot.Builder ballot = Voting.PlaintextBallot.newBuilder(); diff --git a/mixer/src/test/java/meerkat/mixer/main/MixTest.java b/mixer/src/test/java/meerkat/mixer/main/MixTest.java new file mode 100644 index 0000000..83e4c56 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/main/MixTest.java @@ -0,0 +1,120 @@ +package meerkat.mixer.main; + +import com.google.protobuf.StringValue; +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; +import meerkat.protobuf.Crypto; +import org.factcenter.qilin.primitives.concrete.ECElGamal; +import org.junit.Before; +import org.junit.Test; + +import java.io.*; +import java.util.Random; + +import static org.junit.Assert.*; + +/** + * Created by talm on 21/01/17. + */ +public class MixTest { + public Mix mix; + Random rand = new Random(1); + + @Before + public void setUp() throws Exception { + mix = new Mix(); + } + + @Test + public void testKeygen() throws Exception { + File tmpKeys = File.createTempFile("elgamal", "key"); + mix.createKeypair(tmpKeys); + + ECElGamal.SK secretKey = mix.secretKey; + assertNotNull(secretKey); + assertNotNull(mix.serializedPk); + + Mix newMix = new Mix(); + + newMix.loadKeypair(tmpKeys); + assertEquals(mix.serializedPk, newMix.serializedPk); + + StringValue tst = StringValue.newBuilder().setValue("test").build(); + + Crypto.RerandomizableEncryptedMessage cipher = mix.enc.encrypt(tst, mix.enc.generateRandomness(rand)); + StringValue newTst = Util.decrypt(StringValue.class, newMix.secretKey, newMix.enc.getGroup(), cipher); + + assertEquals(tst, newTst); + + } + + File createPlaintexts(int n) throws Exception { + File tmpData = File.createTempFile("plaintext", "txt"); + PrintStream out = new PrintStream(tmpData); + + for (int i = 0; i < n; ++i) { + out.println("Line " + i); + } + + out.close(); + + return tmpData; + } + + @Test + public void testEncryptDecrypt() throws Exception { + File tmpKeys = File.createTempFile("elgamal", "key"); + mix.createKeypair(tmpKeys); + + int n = 35; + File plaintexts = createPlaintexts(n); + File ciphertexts = File.createTempFile("ciphertexts", "bin"); + mix.encrypt(plaintexts, ciphertexts); + + Mix newMix = new Mix(); + newMix.loadKeypair(tmpKeys); + newMix.loadMix(ciphertexts); + File newPlaintexts = File.createTempFile("plaintexts2", "txt"); + newMix.decrypt(newPlaintexts); + + BufferedReader in1 = new BufferedReader(new FileReader(plaintexts)); + BufferedReader in2 = new BufferedReader(new FileReader(newPlaintexts)); + + int n2 = 1 << newMix.inMix.getLogN(); + assert(n2 >= n); + + for (int i = 0; i < n; ++i) { + String line1 = in1.readLine(); + String line2 = in2.readLine(); + assertEquals(line1, line2); + } + + for (int i = n; i < n2; ++i) { + String line2 = in2.readLine(); + assertEquals("", line2); + } + } + + @Test + public void testMixVerify() throws Exception { + File tmpKeys = File.createTempFile("elgamal", "key"); + mix.createKeypair(tmpKeys); + + int n = 35; + File plaintexts = createPlaintexts(n); + File ciphertexts = File.createTempFile("ciphertexts", "enc"); + mix.encrypt(plaintexts, ciphertexts); + + File mixedFile = File.createTempFile("mixed", "enc"); + Mix newMix1 = new Mix(); + newMix1.loadKeypair(tmpKeys); + newMix1.loadMix(ciphertexts); + newMix1.mix(mixedFile); + + Mix newMix2 = new Mix(); + newMix2.loadKeypair(tmpKeys); + newMix2.loadMix(mixedFile); + boolean ok = newMix2.verify(true); + assertTrue(ok); + } +} \ No newline at end of file diff --git a/mixer/src/test/java/meerkat/mixer/network/BenesNetworkTest.java b/mixer/src/test/java/meerkat/mixer/network/BenesNetworkTest.java new file mode 100644 index 0000000..84178af --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/network/BenesNetworkTest.java @@ -0,0 +1,13 @@ +package meerkat.mixer.network; + +/** + * Tests for Benes Network topology + */ +public class BenesNetworkTest extends PermutationNetworkTest { + final static int logN = 8; + + @Override + protected PermutationNetwork getNewNetwork() { + return new BenesNetwork(logN); + } +} \ No newline at end of file diff --git a/mixer/src/test/java/meerkat/mixer/network/PermutationNetworkTest.java b/mixer/src/test/java/meerkat/mixer/network/PermutationNetworkTest.java new file mode 100644 index 0000000..f3e143d --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/network/PermutationNetworkTest.java @@ -0,0 +1,109 @@ +package meerkat.mixer.network; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Random; +import java.util.Set; +import java.util.TreeSet; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Generically Test a permutation network + */ +abstract public class PermutationNetworkTest { + + public static final int NUM_REPS = 10; + Random rand; + + abstract protected PermutationNetwork getNewNetwork(); + + PermutationNetwork network; + + @Before + public void setup() { + network = getNewNetwork(); + rand = new Random(1); + } + + public static Set getSequenceSet(int N) { + Set set = new TreeSet<>(); + + for (int i = 0; i < N; ++i) { + set.add(i); + } + return set; + } + + public static Integer[] getSequenceArray(int N) { + Integer[] arr = new Integer[N]; + + for (int i = 0; i < N; ++i) { + arr[i] = i; + } + return arr; + } + + + /** + * Check if a given network is actually a permutation network (i.e., always + * implies a permutation regardless of 2x2 switch settings). + */ + @Test + public void isAlwaysAPermutation() { + int numLayers = network.getNumLayers(); + int N = network.getNumInputs(); + + for (int layer = 1; layer < numLayers; ++layer) { + Set unusedInputs = getSequenceSet(N); + for (int i = 0; i < N; ++i) { + unusedInputs.remove(network.getOutputIdxInPreviousLayer(layer, i)); + } + assertTrue("Not a permutation! Didn't use: " + unusedInputs, unusedInputs.isEmpty()); + } + } + + @Test + public void forwardEqualsBackwards() { + int numLayers = network.getNumLayers(); + int N = network.getNumInputs(); + + for (int layer = 1; layer < numLayers; ++layer) { + for (int i = 0; i < N; ++i) { + int j = network.getOutputIdxInPreviousLayer(layer, i); + assertEquals(String.format("Input %d in layer %d has problems", i, layer), i, + network.getInputIdxInNextLayer(layer - 1, j)); + } + } + } + + public static int[] convert(Integer[] in) { + int[] out = new int[in.length]; + for (int i = 0; i < in.length; ++i) + out[i] = in[i]; + + return out; + } + + @Test + public void testRandomPermutations() throws Exception { + for (int rep = 0; rep < NUM_REPS; ++rep) { + Integer[] target = getSequenceArray(network.getNumInputs()); + RandomPermutation.permute(target, rand); + + network.setPermutation(convert(target)); + + Integer[] id = getSequenceArray(network.getNumInputs()); + + Integer[] out = new Integer[target.length]; + + Util.permute(network, id, out); + + assertArrayEquals("Permutation mismatch: " + target + " != " + out, target, out); + + } + } +} diff --git a/mixer/src/test/java/meerkat/mixer/proofs/DummySigmaProof.java b/mixer/src/test/java/meerkat/mixer/proofs/DummySigmaProof.java new file mode 100644 index 0000000..82d2429 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/proofs/DummySigmaProof.java @@ -0,0 +1,81 @@ +package meerkat.mixer.proofs; + +import meerkat.crypto.concrete.Util; +import meerkat.protobuf.Crypto; + +import java.math.BigInteger; + +/** + * A dummy proof that can be used for testing + */ +public class DummySigmaProof { + public static class Prover implements SigmaProtocol.Prover { + + /** + * The "statement" to be proved: x + y = z + */ + + final BigInteger x,y,z; + final BigInteger r; + + public Prover(BigInteger x, BigInteger y, BigInteger z, BigInteger r) { + this.x = x; + this.y = y; + this.z = z; + this.r = r; + } + + @Override + public Crypto.BigInteger getFirstMessage() { + return Util.encodeBigInteger(r); + } + + @Override + public BigInteger getFinalMessage(BigInteger challenge) { + return challenge.add(r.multiply(x.add(y))); + } + } + public static class Verifier implements SigmaProtocol.Verifier { + + final BigInteger x,y,z; + + public Verifier(BigInteger x, BigInteger y, BigInteger z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean verify(Crypto.BigInteger firstMessage, BigInteger challenge, BigInteger finalMessage) { + BigInteger r = Util.decodeBigInteger(firstMessage); + return finalMessage.equals(challenge.add(r.multiply(z))); + } + } + + public static class Simulator implements SigmaProtocol.Simulator { + + final BigInteger x,y,z; + + BigInteger resp; + + public Simulator(BigInteger x, BigInteger y, BigInteger z) { + this.x = x; + this.y = y; + this.z = z; + } + + + @Override + public Crypto.BigInteger getFirstMessage(BigInteger challenge) { + BigInteger r = BigInteger.ONE; + resp = challenge.add(z); + return Util.encodeBigInteger(r); + } + + @Override + public BigInteger getFinalMessage() { + return resp; + } + } + +} diff --git a/mixer/src/test/java/meerkat/mixer/proofs/DummySigmaTest.java b/mixer/src/test/java/meerkat/mixer/proofs/DummySigmaTest.java new file mode 100644 index 0000000..ffcd495 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/proofs/DummySigmaTest.java @@ -0,0 +1,45 @@ +package meerkat.mixer.proofs; + +import meerkat.protobuf.Crypto; + +import java.math.BigInteger; + +/** + * Created by talm on 14/01/17. + */ +public class DummySigmaTest extends SigmaProtocolTest { + BigInteger seed; + BigInteger x,y,z; + + @Override + protected void generateRandomTrueStatement() { + x = new BigInteger(100, rand); y = new BigInteger(100, rand); z = x.add(y); + } + + @Override + protected void generateRandomFalseStatement() { + x = new BigInteger(100, rand); + y = new BigInteger(100, rand); + z = new BigInteger(100, rand); + } + + @Override + protected SigmaProtocol.Prover getNewProver() { + return new DummySigmaProof.Prover(x, y, z, new BigInteger(100, rand)); + } + + @Override + protected SigmaProtocol.Verifier getNewVerifier() { + return new DummySigmaProof.Verifier(x, y, z); + } + + @Override + protected SigmaProtocol.Simulator getNewSimulator() { + return new DummySigmaProof.Simulator(x, y, z); + } + + @Override + protected BigInteger getChallengeModulus() { + return new BigInteger(100, rand); + } +} diff --git a/mixer/src/test/java/meerkat/mixer/proofs/SigmaProtocolTest.java b/mixer/src/test/java/meerkat/mixer/proofs/SigmaProtocolTest.java new file mode 100644 index 0000000..2bcddf2 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/proofs/SigmaProtocolTest.java @@ -0,0 +1,109 @@ +package meerkat.mixer.proofs; + +import com.google.protobuf.Message; +import meerkat.mixer.proofs.generic.SigmaFiatShamir; +import org.factcenter.qilin.util.Pair; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; +import java.util.Random; + +import static org.junit.Assert.assertTrue; + +/** + * Generic test for Sigma Protocol + */ +abstract public class SigmaProtocolTest { + public final int NUM_REPEAT = 10; + + abstract protected void generateRandomTrueStatement(); + + abstract protected void generateRandomFalseStatement(); + + abstract protected SigmaProtocol.Prover getNewProver(); + abstract protected SigmaProtocol.Verifier getNewVerifier(); + abstract protected SigmaProtocol.Simulator getNewSimulator(); + + public class NIZKConcat implements Concatenator.Pair, M1, M2> { + @Override + public Pair concatenate(M1 msg1, M2 msg2) { return new Pair(msg1, msg2); } + + @Override + public M1 getMsg1(Pair msg) { return msg.a; } + + @Override + public M2 getMsg2(Pair msg) { return msg.b; } + } + + final protected NIZKConcat nizkConcat = new NIZKConcat(); + + abstract protected BigInteger getChallengeModulus(); + + SigmaProtocol.Prover prover; + SigmaProtocol.Verifier verifier; + SigmaProtocol.Simulator simulator; + BigInteger challengeModulus; + int challengeBits; + + protected Random rand = new Random(1);; + + @Before + public void setup() { + challengeModulus = getChallengeModulus(); + challengeBits = challengeModulus.bitLength() + 1; + } + + @Test + public void testProverCompleteness() { + for (int i = 0; i < NUM_REPEAT; ++i) { + generateRandomTrueStatement(); + + prover = getNewProver(); + verifier = getNewVerifier(); + + M1 msg1 = prover.getFirstMessage(); + BigInteger c = new BigInteger(challengeBits, rand).mod(challengeModulus); + M2 msg2 = prover.getFinalMessage(c); + + assertTrue(String.format("Verification error in iteration %d", i), verifier.verify(msg1, c, msg2)); + } + } + + + @Test + public void testSimulatorCompleteness() { + for (int i = 0; i < NUM_REPEAT; ++i) { + generateRandomFalseStatement(); + + simulator = getNewSimulator(); + verifier = getNewVerifier(); + + BigInteger c = new BigInteger(challengeBits, rand).mod(challengeModulus); + + M1 msg1 = simulator.getFirstMessage(c); + M2 msg2 = simulator.getFinalMessage(); + + assertTrue(String.format("Simulator Verification error in iteration %d", i), verifier.verify(msg1, c, msg2)); + } + } + + @Test + public void testNIZKCompleteness() { + for (int i = 0; i < NUM_REPEAT; ++i) { + generateRandomTrueStatement(); + + SigmaFiatShamir, M1, M2> fiatShamir = new SigmaFiatShamir, M1, M2>(nizkConcat); + + prover = getNewProver(); + Pair nizk = fiatShamir.generateNizk(prover); + + verifier = getNewVerifier(); + boolean nizkOk = fiatShamir.verifyNizk(nizk, verifier); + + assertTrue(String.format("NIZK Verification error in iteration %d", i), nizkOk); + } + } + + +} \ No newline at end of file diff --git a/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogAndStatementSigmaTest.java b/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogAndStatementSigmaTest.java new file mode 100644 index 0000000..3a8dad4 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogAndStatementSigmaTest.java @@ -0,0 +1,61 @@ +package meerkat.mixer.proofs.concrete; + +import meerkat.mixer.proofs.SigmaProtocol; +import meerkat.mixer.proofs.SigmaProtocolTest; +import meerkat.mixer.proofs.generic.SigmaProtocolAnd2; +import meerkat.protobuf.Mixing; +import org.factcenter.qilin.util.Pair; + +import java.math.BigInteger; + +/** + * Created by talm on 12/01/17. + */ +public class DlogAndStatementSigmaTest extends SigmaProtocolTest { + final DlogStatementSchnorrSigmaTest dlogtest; + + Statements.DlogStatement s1, s2; + Statements.DlogStatementWitness w1, w2; + + public DlogAndStatementSigmaTest() { + this.dlogtest = new DlogStatementSchnorrSigmaTest(); + } + + @Override + protected void generateRandomTrueStatement() { + Pair s1w1 = dlogtest.returnRandomTrueStatement(); + s1 = s1w1.a; w1 = s1w1.b; + + Pair s2w2 = dlogtest.returnRandomTrueStatement(); + s2 = s2w2.a; w2 = s2w2.b; + } + + @Override + protected void generateRandomFalseStatement() { + s1 = dlogtest.returnRandomFalseStatement(); + s2 = dlogtest.returnRandomFalseStatement(); + } + + @Override + protected SigmaProtocol.Prover getNewProver() { + return new SigmaProtocolAnd2.Prover<>(ProtobufConcatenators.concatAnd1, ProtobufConcatenators.concatAnd2, + new SchnorrDlogEquivalence.Prover(dlogtest.encryptor, rand, s1, w1), new SchnorrDlogEquivalence.Prover(dlogtest.encryptor, rand, s2, w2)); + } + + @Override + protected SigmaProtocol.Verifier getNewVerifier() { + return new SigmaProtocolAnd2.Verifier<>(ProtobufConcatenators.concatAnd1, ProtobufConcatenators.concatAnd2, + new SchnorrDlogEquivalence.Verifier(dlogtest.encryptor, s1), new SchnorrDlogEquivalence.Verifier(dlogtest.encryptor, s2)); + } + + @Override + protected SigmaProtocol.Simulator getNewSimulator() { + return new SigmaProtocolAnd2.Simulator<>(ProtobufConcatenators.concatAnd1, ProtobufConcatenators.concatAnd2, + new SchnorrDlogEquivalence.Simulator(dlogtest.encryptor, rand, s1), new SchnorrDlogEquivalence.Simulator(dlogtest.encryptor, rand, s2)); + } + + @Override + protected BigInteger getChallengeModulus() { + return dlogtest.getChallengeModulus(); + } +} diff --git a/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogOrStatementSigmaTest.java b/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogOrStatementSigmaTest.java new file mode 100644 index 0000000..a4cb333 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogOrStatementSigmaTest.java @@ -0,0 +1,87 @@ +package meerkat.mixer.proofs.concrete; + +import meerkat.mixer.proofs.SigmaProtocol; +import meerkat.mixer.proofs.SigmaProtocolTest; +import meerkat.mixer.proofs.generic.SigmaProtocolOr2; +import meerkat.protobuf.Mixing; +import org.factcenter.qilin.util.Pair; + +import java.math.BigInteger; + +/** + * Created by talm on 14/01/17. + */ +public class DlogOrStatementSigmaTest extends SigmaProtocolTest { + final DlogStatementSchnorrSigmaTest dlogtest; + + final Statements.AndStatement[] statements = new Statements.AndStatement[2]; + Statements.AndStatementWitness w; + int trueStatementIndex; + + + public DlogOrStatementSigmaTest() { + this.dlogtest = new DlogStatementSchnorrSigmaTest(); + } + + @Override + protected void generateRandomTrueStatement() { + trueStatementIndex = rand.nextInt(2); + Pair s1w1 = dlogtest.returnRandomTrueStatement(); + Pair s2w2 = dlogtest.returnRandomTrueStatement(); + Statements.AndStatement trueStatement = dlogtest.statementGenerator.new AndStatement(s1w1.a, s2w2.a); + w = dlogtest.statementGenerator.new AndStatementWitness(s1w1.b, s2w2.b); + statements[trueStatementIndex] = trueStatement; + + Statements.DlogStatement f1 = dlogtest.returnRandomFalseStatement(); + Statements.DlogStatement f2 = dlogtest.returnRandomFalseStatement(); + + Statements.AndStatement falseStatement = dlogtest.statementGenerator.new AndStatement(f1, f2); + + statements[1 - trueStatementIndex] = falseStatement; + } + + @Override + protected void generateRandomFalseStatement() { + Statements.DlogStatement f1 = dlogtest.returnRandomFalseStatement(); + Statements.DlogStatement f2 = dlogtest.returnRandomFalseStatement(); + + statements[0] = dlogtest.statementGenerator.new AndStatement(f1, f2); + + f1 = dlogtest.returnRandomFalseStatement(); + f2 = dlogtest.returnRandomFalseStatement(); + statements[1] = dlogtest.statementGenerator.new AndStatement(f1, f2); + } + + @Override + protected SigmaProtocol.Prover getNewProver() { + SigmaProtocol.Prover andProver = new DlogConjunction.Prover(dlogtest.encryptor, rand, statements[trueStatementIndex], w); + SigmaProtocol.Simulator andSimulator = new DlogConjunction.Simulator(dlogtest.encryptor, rand, statements[1 - trueStatementIndex]); + + Mix2.ChallengeGenerator gen = new Mix2.ChallengeGenerator(dlogtest.encryptor, rand); + return new SigmaProtocolOr2.Prover<>(ProtobufConcatenators.concatMix1, ProtobufConcatenators.concatMix2, + gen, andProver, andSimulator, trueStatementIndex); + } + + @Override + protected SigmaProtocol.Verifier getNewVerifier() { + Mix2.ChallengeGenerator gen = new Mix2.ChallengeGenerator(dlogtest.encryptor, rand); + return new SigmaProtocolOr2.Verifier(ProtobufConcatenators.concatMix1, ProtobufConcatenators.concatMix2, + gen, new DlogConjunction.Verifier(dlogtest.encryptor, statements[0]), new DlogConjunction.Verifier(dlogtest.encryptor, statements[1])); + } + + @Override + protected SigmaProtocol.Simulator getNewSimulator() { + + Mix2.ChallengeGenerator gen = new Mix2.ChallengeGenerator(dlogtest.encryptor, rand); + return new SigmaProtocolOr2.Simulator(ProtobufConcatenators.concatMix1, ProtobufConcatenators.concatMix2, + gen, new DlogConjunction.Simulator(dlogtest.encryptor, rand, statements[0]), + new DlogConjunction.Simulator(dlogtest.encryptor, rand, statements[1])); + } + + @Override + protected BigInteger getChallengeModulus() { + return dlogtest.getChallengeModulus(); + } +} diff --git a/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogStatementSchnorrSigmaTest.java b/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogStatementSchnorrSigmaTest.java new file mode 100644 index 0000000..2e5ebe5 --- /dev/null +++ b/mixer/src/test/java/meerkat/mixer/proofs/concrete/DlogStatementSchnorrSigmaTest.java @@ -0,0 +1,93 @@ +package meerkat.mixer.proofs.concrete; + +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.mixer.ECParamTestBase; +import meerkat.mixer.proofs.SigmaProtocol; +import meerkat.mixer.proofs.SigmaProtocolTest; +import meerkat.mixer.proofs.concrete.SchnorrDlogEquivalence; +import meerkat.mixer.proofs.concrete.Statements; +import meerkat.protobuf.Mixing; +import org.bouncycastle.math.ec.ECPoint; +import org.factcenter.qilin.primitives.concrete.ECGroup; +import org.factcenter.qilin.util.Pair; + +import java.math.BigInteger; + +/** + * Created by talm on 12/01/17. + */ +public class DlogStatementSchnorrSigmaTest extends + SigmaProtocolTest { + + ECParamTestBase ecParams; + Statements statementGenerator; + ECElGamalEncryption encryptor; + ECGroup group; + + Statements.DlogStatement statement; + Statements.DlogStatementWitness witness; + SchnorrDlogEquivalence.Prover prover; + SchnorrDlogEquivalence.Verifier verifier; + SchnorrDlogEquivalence.Simulator simulator; + + public DlogStatementSchnorrSigmaTest() { + ecParams = new ECParamTestBase(); + encryptor = ecParams.enc; + group = encryptor.getGroup(); + this.statementGenerator = new Statements(ecParams.enc); + } + + + Pair returnRandomTrueStatement() { + BigInteger x = encryptor.generateRandomExponent(rand); + ECPoint a = group.multiply(statementGenerator.g, x); + ECPoint b = group.multiply(statementGenerator.h, x); + Statements.DlogStatement statement = statementGenerator.new DlogStatement(a, b); + Statements.DlogStatementWitness witness = statementGenerator.new DlogStatementWitness(x); + + return new Pair<>(statement, witness); + } + + Statements.DlogStatement returnRandomFalseStatement() { + ECPoint a = group.sample(rand); + ECPoint b = group.sample(rand); + + return statementGenerator.new DlogStatement(a, b); + } + + @Override + protected void generateRandomTrueStatement() { + Pair sw = returnRandomTrueStatement(); + this.statement = sw.a; + this.witness = sw.b; + } + + @Override + protected void generateRandomFalseStatement() { + witness = null; + statement = returnRandomFalseStatement(); + } + + @Override + protected SigmaProtocol.Prover getNewProver() { + prover = new SchnorrDlogEquivalence.Prover(encryptor, rand, statement, witness); + return prover; + } + + @Override + protected SigmaProtocol.Verifier getNewVerifier() { + verifier = new SchnorrDlogEquivalence.Verifier(encryptor, statement); + return verifier; + } + + @Override + protected SigmaProtocol.Simulator getNewSimulator() { + simulator = new SchnorrDlogEquivalence.Simulator(encryptor, rand, statement); + return simulator; + } + + @Override + protected BigInteger getChallengeModulus() { + return group.orderUpperBound(); + } +} \ No newline at end of file diff --git a/mixer/src/test/java/meerkat/mixer/ZeroKnowledgeProofTest.java b/mixer/src/test/java/meerkat/mixer/proofs/concrete/Mix2ProofTest.java similarity index 59% rename from mixer/src/test/java/meerkat/mixer/ZeroKnowledgeProofTest.java rename to mixer/src/test/java/meerkat/mixer/proofs/concrete/Mix2ProofTest.java index 4f2daca..ff3cac9 100644 --- a/mixer/src/test/java/meerkat/mixer/ZeroKnowledgeProofTest.java +++ b/mixer/src/test/java/meerkat/mixer/proofs/concrete/Mix2ProofTest.java @@ -1,58 +1,38 @@ -package meerkat.mixer; +package meerkat.mixer.proofs.concrete; -import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeProver; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier; -import meerkat.mixer.prover.Prover; -import meerkat.mixer.verifier.Verifier; +import meerkat.crypto.concrete.Util; +import meerkat.mixer.ECParamTestBase; +import meerkat.mixer.Utils; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Crypto; +import meerkat.protobuf.Mixing; import meerkat.protobuf.Voting; //import meerkat.protobuf.Voting.PlaintextBallot; import org.bouncycastle.math.ec.ECPoint; -import org.factcenter.qilin.primitives.RandomOracle; -import org.factcenter.qilin.primitives.concrete.DigestOracle; -import org.factcenter.qilin.primitives.concrete.ECElGamal; -import org.factcenter.qilin.primitives.concrete.ECGroup; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.math.BigInteger; -import java.util.Random; - /** * Created by Tzlil on 12/31/2015. */ -public class ZeroKnowledgeProofTest { - - Random rand; - ECElGamal.SK key; - ECGroup group; - ECElGamalEncryption enc; - ConcreteCrypto.ElGamalPublicKey serializedPk; - Mix2ZeroKnowledgeVerifier verifier ; - Mix2ZeroKnowledgeProver prover ; +public class Mix2ProofTest extends ECParamTestBase { + Mix2nizk nizk; + meerkat.mixer.proofs.Mix2nizk.Verifier verifier ; + meerkat.mixer.proofs.Mix2nizk.Prover prover ; @Before public void setup() throws Exception { - rand = new Random(); - group = new ECGroup("secp256k1"); - BigInteger sk = ECElGamal.generateSecretKey(group, rand); - key = new ECElGamal.SK(group, sk); - serializedPk = Utils.serializePk(group, key); - enc = new ECElGamalEncryption(); - enc.init(serializedPk); - RandomOracle randomOracle = new DigestOracle(); - verifier = new Verifier(enc,randomOracle); - prover = new Prover(new Random(),enc,randomOracle); + nizk = new Mix2nizk(rand, enc); + verifier = nizk; + prover = nizk; } - private ECPoint convert2ECPoint(ByteString bs){ - return group.decode(bs.toByteArray()); + private ECPoint convert2ECPoint(ConcreteCrypto.GroupElement bs){ + return enc.decodeElement(bs); } public void oneZKPTest() throws InvalidProtocolBufferException { @@ -66,10 +46,10 @@ public class ZeroKnowledgeProofTest { Crypto.RerandomizableEncryptedMessage e1New = enc.rerandomize(e1, r1); Crypto.RerandomizableEncryptedMessage e2New = enc.rerandomize(e2, r2); - assertEquals (Utils.decrypt(Voting.PlaintextBallot.class, key, group, e1), msg1); - assertEquals (Utils.decrypt(Voting.PlaintextBallot.class, key, group, e1New), msg1); - assertEquals (Utils.decrypt(Voting.PlaintextBallot.class, key, group, e2), msg2); - assertEquals (Utils.decrypt(Voting.PlaintextBallot.class, key, group, e2New), msg2); + assertEquals (Util.decrypt(Voting.PlaintextBallot.class, key, group, e1), msg1); + assertEquals (Util.decrypt(Voting.PlaintextBallot.class, key, group, e1New), msg1); + assertEquals (Util.decrypt(Voting.PlaintextBallot.class, key, group, e2), msg2); + assertEquals (Util.decrypt(Voting.PlaintextBallot.class, key, group, e2New), msg2); ECPoint g = group.getGenerator(); ECPoint h = enc.getElGamalPK().getPK(); @@ -89,11 +69,14 @@ public class ZeroKnowledgeProofTest { assertEquals (h.multiply(enc.extractRandomness(r2)), group.add(convert2ECPoint(e2TagElGamal.getC2()),group.negate(convert2ECPoint(e2ElGamal.getC2())))); - assertTrue (verifier.verify(e1,e2,e1New,e2New,prover.prove(e1,e2,e1New,e2New,false,0,0,0,r1,r2))); + + Mixing.Mix2Proof proof = prover.prove(e1,e2,e1New,e2New,false,0,0,0, 0, r1,r2); + + assertTrue (verifier.verify(e1,e2,e1New,e2New, proof)); } @Test - public void zeroKnowledgeProofTest() throws InvalidProtocolBufferException { + public void Mix2ProofTest() throws InvalidProtocolBufferException { int tests = 1000; diff --git a/mixer/src/test/java/profiling/BigInteger/AddSub.java b/mixer/src/test/java/profiling/BigInteger/AddSub.java index 8e3b5c1..6a64b0b 100644 --- a/mixer/src/test/java/profiling/BigInteger/AddSub.java +++ b/mixer/src/test/java/profiling/BigInteger/AddSub.java @@ -2,6 +2,7 @@ package profiling.BigInteger; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; import meerkat.protobuf.ConcreteCrypto; import meerkat.mixer.Utils; import org.factcenter.qilin.primitives.concrete.ECElGamal; @@ -28,7 +29,7 @@ public class AddSub { ECGroup group = new ECGroup("secp256k1"); BigInteger sk = ECElGamal.generateSecretKey(group, rand); ECElGamal.SK key = new ECElGamal.SK(group, sk); - ConcreteCrypto.ElGamalPublicKey serializedPk = Utils.serializePk(group, key); + ConcreteCrypto.ElGamalPublicKey serializedPk = Util.encodePK(group, key); ECElGamalEncryption enc = new ECElGamalEncryption(); enc.init(serializedPk); diff --git a/mixer/src/test/java/profiling/BigInteger/GenerateRandomness.java b/mixer/src/test/java/profiling/BigInteger/GenerateRandomness.java index ebe80f0..954fed4 100644 --- a/mixer/src/test/java/profiling/BigInteger/GenerateRandomness.java +++ b/mixer/src/test/java/profiling/BigInteger/GenerateRandomness.java @@ -2,6 +2,7 @@ package profiling.BigInteger; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; import meerkat.protobuf.ConcreteCrypto; import meerkat.mixer.Utils; import org.factcenter.qilin.primitives.concrete.ECElGamal; @@ -25,7 +26,7 @@ public class GenerateRandomness { BigInteger sk = ECElGamal.generateSecretKey(group, rand); ECElGamal.SK key = new ECElGamal.SK(group, sk); - ConcreteCrypto.ElGamalPublicKey serializedPk = Utils.serializePk(group, key); + ConcreteCrypto.ElGamalPublicKey serializedPk = Util.encodePK(group, key); enc = new ECElGamalEncryption(); enc.init(serializedPk); diff --git a/mixer/src/test/java/profiling/BigInteger/Modulo.java b/mixer/src/test/java/profiling/BigInteger/Modulo.java index d0149b0..42190be 100644 --- a/mixer/src/test/java/profiling/BigInteger/Modulo.java +++ b/mixer/src/test/java/profiling/BigInteger/Modulo.java @@ -2,8 +2,8 @@ package profiling.BigInteger; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; import meerkat.protobuf.ConcreteCrypto; -import meerkat.mixer.Utils; import org.factcenter.qilin.primitives.concrete.ECElGamal; import org.factcenter.qilin.primitives.concrete.ECGroup; @@ -27,7 +27,7 @@ public class Modulo { BigInteger sk = ECElGamal.generateSecretKey(group, rand); ECElGamal.SK key = new ECElGamal.SK(group, sk); - ConcreteCrypto.ElGamalPublicKey serializedPk = Utils.serializePk(group, key); + ConcreteCrypto.ElGamalPublicKey serializedPk = Util.encodePK(group, key); ECElGamalEncryption enc = new ECElGamalEncryption(); enc.init(serializedPk); for (int i =0 ; i < tests ; i++){ diff --git a/mixer/src/test/java/profiling/BigInteger/Mul.java b/mixer/src/test/java/profiling/BigInteger/Mul.java index 4ac913a..9164755 100644 --- a/mixer/src/test/java/profiling/BigInteger/Mul.java +++ b/mixer/src/test/java/profiling/BigInteger/Mul.java @@ -2,6 +2,7 @@ package profiling.BigInteger; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; import meerkat.protobuf.ConcreteCrypto; import meerkat.mixer.Utils; import org.factcenter.qilin.primitives.concrete.ECElGamal; @@ -28,7 +29,7 @@ public class Mul { ECGroup group = new ECGroup("secp256k1"); BigInteger sk = ECElGamal.generateSecretKey(group, rand); ECElGamal.SK key = new ECElGamal.SK(group, sk); - ConcreteCrypto.ElGamalPublicKey serializedPk = Utils.serializePk(group, key); + ConcreteCrypto.ElGamalPublicKey serializedPk = Util.encodePK(group, key); ECElGamalEncryption enc = new ECElGamalEncryption(); enc.init(serializedPk); diff --git a/mixer/src/test/java/profiling/Convert/ByteString2ECPoint.java b/mixer/src/test/java/profiling/Convert/ByteString2ECPoint.java index f9b5b2e..5c587de 100644 --- a/mixer/src/test/java/profiling/Convert/ByteString2ECPoint.java +++ b/mixer/src/test/java/profiling/Convert/ByteString2ECPoint.java @@ -1,11 +1,11 @@ package profiling.Convert; -import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; +import meerkat.mixer.Utils; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Voting; -import meerkat.mixer.Utils; import org.bouncycastle.math.ec.ECPoint; import org.factcenter.qilin.primitives.concrete.ECElGamal; import org.factcenter.qilin.primitives.concrete.ECGroup; @@ -30,7 +30,7 @@ public class ByteString2ECPoint { group = new ECGroup("secp256k1"); BigInteger sk = ECElGamal.generateSecretKey(group, rand); key = new ECElGamal.SK(group, sk); - serializedPk = Utils.serializePk(group, key); + serializedPk = Util.encodePK(group, key); enc = new ECElGamalEncryption(); enc.init(serializedPk); tests = 1024 * 19; @@ -45,8 +45,8 @@ public class ByteString2ECPoint { (enc.encrypt(msg, enc.generateRandomness(rand))); } } - private ECPoint convert2ECPoint(ByteString bs){ - return group.decode(bs.toByteArray()); + private ECPoint convert2ECPoint(ConcreteCrypto.GroupElement bs){ + return enc.decodeElement(bs); } public void ByteString2ECPointProfiling() throws InvalidProtocolBufferException { diff --git a/mixer/src/test/java/profiling/Convert/RerandomizableEncryptedMessage2ElGamalCiphertext.java b/mixer/src/test/java/profiling/Convert/RerandomizableEncryptedMessage2ElGamalCiphertext.java index 3d7435f..943ad1d 100644 --- a/mixer/src/test/java/profiling/Convert/RerandomizableEncryptedMessage2ElGamalCiphertext.java +++ b/mixer/src/test/java/profiling/Convert/RerandomizableEncryptedMessage2ElGamalCiphertext.java @@ -2,6 +2,7 @@ package profiling.Convert; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting; @@ -29,7 +30,7 @@ public class RerandomizableEncryptedMessage2ElGamalCiphertext { group = new ECGroup("secp256k1"); BigInteger sk = ECElGamal.generateSecretKey(group, rand); key = new ECElGamal.SK(group, sk); - serializedPk = Utils.serializePk(group, key); + serializedPk = Util.encodePK(group, key); enc = new ECElGamalEncryption(); enc.init(serializedPk); tests = 1024 * 18; diff --git a/mixer/src/test/java/profiling/ECGroup/Add.java b/mixer/src/test/java/profiling/ECGroup/Add.java index e0b5406..34c4832 100644 --- a/mixer/src/test/java/profiling/ECGroup/Add.java +++ b/mixer/src/test/java/profiling/ECGroup/Add.java @@ -2,7 +2,7 @@ package profiling.ECGroup; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.mixer.Utils; +import meerkat.crypto.concrete.Util; import org.bouncycastle.math.ec.ECPoint; import org.factcenter.qilin.primitives.concrete.ECElGamal; import org.factcenter.qilin.primitives.concrete.ECGroup; @@ -29,7 +29,7 @@ public class Add { random = new Random(); group = new ECGroup("secp256k1"); encryptor = new ECElGamalEncryption(); - encryptor.init(Utils.serializePk(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); + encryptor.init(Util.encodePK(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); // generate n; int sqrtn = 128; n = sqrtn*sqrtn; diff --git a/mixer/src/test/java/profiling/ECGroup/Encode.java b/mixer/src/test/java/profiling/ECGroup/Encode.java index 59201e3..b69db71 100644 --- a/mixer/src/test/java/profiling/ECGroup/Encode.java +++ b/mixer/src/test/java/profiling/ECGroup/Encode.java @@ -2,6 +2,7 @@ package profiling.ECGroup; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; import meerkat.mixer.Utils; import org.bouncycastle.math.ec.ECPoint; import org.factcenter.qilin.primitives.concrete.ECElGamal; @@ -29,7 +30,7 @@ public class Encode { random = new Random(); group = new ECGroup("secp256k1"); encryptor = new ECElGamalEncryption(); - encryptor.init(Utils.serializePk(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); + encryptor.init(Util.encodePK(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); // generate n; int sqrtn = 128; n = sqrtn*sqrtn; diff --git a/mixer/src/test/java/profiling/ECGroup/Mul.java b/mixer/src/test/java/profiling/ECGroup/Mul.java index 836db0e..d1f9d1f 100644 --- a/mixer/src/test/java/profiling/ECGroup/Mul.java +++ b/mixer/src/test/java/profiling/ECGroup/Mul.java @@ -2,7 +2,7 @@ package profiling.ECGroup; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.mixer.Utils; +import meerkat.crypto.concrete.Util; import org.bouncycastle.math.ec.ECPoint; import org.factcenter.qilin.primitives.concrete.ECElGamal; import org.factcenter.qilin.primitives.concrete.ECGroup; @@ -30,7 +30,7 @@ public class Mul { random = new Random(); group = new ECGroup("secp256k1"); encryptor = new ECElGamalEncryption(); - encryptor.init(Utils.serializePk(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); + encryptor.init(Util.encodePK(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); // generate n int sqrtn = 128; n = sqrtn*sqrtn; diff --git a/mixer/src/test/java/profiling/ECGroup/Negate.java b/mixer/src/test/java/profiling/ECGroup/Negate.java index d190186..0666eba 100644 --- a/mixer/src/test/java/profiling/ECGroup/Negate.java +++ b/mixer/src/test/java/profiling/ECGroup/Negate.java @@ -2,7 +2,7 @@ package profiling.ECGroup; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.mixer.Utils; +import meerkat.crypto.concrete.Util; import org.bouncycastle.math.ec.ECPoint; import org.factcenter.qilin.primitives.concrete.ECElGamal; import org.factcenter.qilin.primitives.concrete.ECGroup; @@ -28,7 +28,7 @@ public class Negate { random = new Random(); group = new ECGroup("secp256k1"); encryptor = new ECElGamalEncryption(); - encryptor.init(Utils.serializePk(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); + encryptor.init(Util.encodePK(group, new ECElGamal.SK(group, ECElGamal.generateSecretKey(group, random)))); // generate n; int sqrtn = 128; n = sqrtn*sqrtn; diff --git a/mixer/src/test/java/profiling/Rerandomize.java b/mixer/src/test/java/profiling/Rerandomize.java index 5a113c9..6428fd6 100644 --- a/mixer/src/test/java/profiling/Rerandomize.java +++ b/mixer/src/test/java/profiling/Rerandomize.java @@ -2,10 +2,11 @@ package profiling; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.concrete.Util; +import meerkat.mixer.Utils; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting; -import meerkat.mixer.Utils; import org.factcenter.qilin.primitives.concrete.ECElGamal; import org.factcenter.qilin.primitives.concrete.ECGroup; @@ -31,7 +32,7 @@ public class Rerandomize { group = new ECGroup("secp256k1"); BigInteger sk = ECElGamal.generateSecretKey(group, rand); key = new ECElGamal.SK(group, sk); - serializedPk = Utils.serializePk(group, key); + serializedPk = Util.encodePK(group, key); enc = new ECElGamalEncryption(); enc.init(serializedPk); int LogVotes = 10; diff --git a/mixer/src/test/java/profiling/ZeroKnowledgeProof.java b/mixer/src/test/java/profiling/ZeroKnowledgeProof.java index 7d38bf1..b477a9f 100644 --- a/mixer/src/test/java/profiling/ZeroKnowledgeProof.java +++ b/mixer/src/test/java/profiling/ZeroKnowledgeProof.java @@ -3,8 +3,8 @@ package profiling; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import meerkat.crypto.concrete.ECElGamalEncryption; -import meerkat.crypto.mixnet.Mix2ZeroKnowledgeProver; -import meerkat.mixer.prover.Prover; +import meerkat.crypto.concrete.Util; +import meerkat.mixer.proofs.concrete.Mix2nizk; import meerkat.protobuf.ConcreteCrypto; import meerkat.protobuf.Crypto; import meerkat.protobuf.Voting; @@ -28,22 +28,21 @@ public class ZeroKnowledgeProof { ECGroup group; ECElGamalEncryption enc; ConcreteCrypto.ElGamalPublicKey serializedPk; - Mix2ZeroKnowledgeProver prover ; + meerkat.mixer.proofs.Mix2nizk.Prover prover ; int n; Crypto.EncryptionRandomness[] randomnesses; Crypto.RerandomizableEncryptedMessage[] encryptedMessage; Crypto.RerandomizableEncryptedMessage[] reencryptedMessage; public void setup() throws Exception { - rand = new Random(); + rand = new Random(1); group = new ECGroup("secp256k1"); BigInteger sk = ECElGamal.generateSecretKey(group, rand); key = new ECElGamal.SK(group, sk); - serializedPk = Utils.serializePk(group, key); + serializedPk = Util.encodePK(group, key); enc = new ECElGamalEncryption(); enc.init(serializedPk); - RandomOracle randomOracle = new DigestOracle(); - prover = new Prover(new Random(),enc,randomOracle); + prover = new Mix2nizk(rand,enc); int LogVotes = 12; int layers = 2*LogVotes - 1; n = layers * (1<>) servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME); + @SuppressWarnings("unchecked") + public void init() throws Exception { + Object context = servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME); + + try { + callbacks = (Iterable>) context; + } catch (ClassCastException e) { + throw e; + } + } @POST diff --git a/restful-api-common/build.gradle b/restful-api-common/build.gradle index a9c80b9..07ef0af 100644 --- a/restful-api-common/build.gradle +++ b/restful-api-common/build.gradle @@ -18,13 +18,12 @@ apply plugin: 'maven-publish' ext { isSnapshot = false } ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + groupId = 'org.factcenter.meerkat' + + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" } description = "Common classes for implementing Meerkat's RESTful API" @@ -42,14 +41,6 @@ dependencies { // Jersey for RESTful API compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.22.+' - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - testCompile 'junit:junit:4.+' runtime 'org.codehaus.groovy:groovy:2.4.+' @@ -95,21 +86,6 @@ idea { *===================================*/ repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - // Use local maven repository mavenLocal() @@ -117,13 +93,15 @@ repositories { mavenCentral() } -task "info" << { +task "info" { + doLast { println "Project: ${project.name}" -println "Description: ${project.description}" + println "Description: ${project.description}" println "--------------------------" println "GroupId: $groupId" println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" println "" + } } info.description 'Print some information about project parameters' @@ -145,12 +123,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/voting-booth/build.gradle b/voting-booth/build.gradle index 0920359..e5dd671 100644 --- a/voting-booth/build.gradle +++ b/voting-booth/build.gradle @@ -19,13 +19,12 @@ apply plugin: 'maven-publish' ext { isSnapshot = false } ext { - groupId = 'org.factcenter.meerkat' - nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/" - - // Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing) - // Should be set in ${HOME}/.gradle/gradle.properties - nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : "" - nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : "" + groupId = 'org.factcenter.meerkat' + + // Credentials for publishing repositories + publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : "" + publishPassword = project.hasProperty('publishPassword') ? project.property('publishPassword') : "" } description = "Meerkat voting booth application" @@ -41,22 +40,11 @@ dependencies { compile project(':meerkat-common') compile project(':restful-api-common') - // Jersey for RESTful API - compile 'org.glassfish.jersey.containers:jersey-container-servlet:2.5.+' - // Servlets compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '9.3.+' compile 'org.eclipse.jetty:jetty-server:9.3.+' compile 'org.eclipse.jetty:jetty-servlet:9.3.+' - // Logging - compile 'org.slf4j:slf4j-api:1.7.7' - runtime 'ch.qos.logback:logback-classic:1.1.2' - runtime 'ch.qos.logback:logback-core:1.1.2' - - // Google protobufs - compile 'com.google.protobuf:protobuf-java:3.+' - testCompile 'junit:junit:4.+' runtime 'org.codehaus.groovy:groovy:2.4.+' @@ -140,21 +128,6 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) { *===================================*/ repositories { - - // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral) - maven { - url nexusRepository - - if (isSnapshot) { - credentials { username - password - - username nexusUser - password nexusPassword - } - } - } - // Use local maven repository mavenLocal() @@ -162,13 +135,15 @@ repositories { mavenCentral() } -task "info" << { +task "info" { + doLast { println "Project: ${project.name}" -println "Description: ${project.description}" + println "Description: ${project.description}" println "--------------------------" println "GroupId: $groupId" println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})" println "" + } } info.description 'Print some information about project parameters' @@ -190,12 +165,12 @@ publishing { } repositories { maven { - url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + url publishRepository credentials { username password - username nexusUser - password nexusPassword + username publishUser + password publishPassword } } } diff --git a/voting-booth/src/main/java/meerkat/voting/VotingBoothToyRun.java b/voting-booth/src/main/java/meerkat/voting/VotingBoothToyRun.java index f872d83..f00c011 100644 --- a/voting-booth/src/main/java/meerkat/voting/VotingBoothToyRun.java +++ b/voting-booth/src/main/java/meerkat/voting/VotingBoothToyRun.java @@ -90,7 +90,7 @@ public class VotingBoothToyRun { private static List generateChannelChoiceQuestions() { - ArrayList channelChoiceQuestions = new ArrayList(); + ArrayList channelChoiceQuestions = new ArrayList<>(); String[] ans1 = {"Red", "Blue", "Green"}; BallotQuestion ccquestion1 = generateBallotQuestion("What is your favorite color?", "Pick one answer", ans1); @@ -105,7 +105,7 @@ public class VotingBoothToyRun { private static List generateBallotQuestions() { - ArrayList allBallotQuestions = new ArrayList(); + ArrayList allBallotQuestions = new ArrayList<>(); String[] answers1 = {"answer 1", "answer 2", "answer 3", "answer 4"}; allBallotQuestions.add(generateBallotQuestion("question 1. Asking something...", "Pick one answer", answers1)); @@ -222,7 +222,7 @@ public class VotingBoothToyRun { private static void generateSystemMessages() throws IOException{ - Map systemMessageMap = new HashMap(); + Map systemMessageMap = new HashMap<>(); systemMessageMap.put(StorageManager.WAIT_FOR_COMMIT_MESSAGE, UIElement.newBuilder() .setType(UIElementDataType.TEXT) diff --git a/voting-booth/src/main/java/meerkat/voting/storage/StorageManagerMockup.java b/voting-booth/src/main/java/meerkat/voting/storage/StorageManagerMockup.java index cf4983e..bf4b58e 100644 --- a/voting-booth/src/main/java/meerkat/voting/storage/StorageManagerMockup.java +++ b/voting-booth/src/main/java/meerkat/voting/storage/StorageManagerMockup.java @@ -86,7 +86,7 @@ public class StorageManagerMockup implements StorageManager { logger.error("Could not read from the systemMessages file: '" + systemMessagesFilename + "'."); throw e; } - return systemMessages.getSystemMessage(); + return systemMessages.getSystemMessageMap(); } }