From 12ed7a679dfd7ad70912e0197348fc848348090c Mon Sep 17 00:00:00 2001 From: "tzlil.gon" Date: Tue, 24 Nov 2015 15:39:39 +0200 Subject: [PATCH] mixer code --- .../src/main/proto/meerkat/mixing.proto | 42 +++- mixer/build.gradle | 205 ++++++++++++++++++ mixer/src/main/java/mixer/Graph.java | 100 +++++++++ mixer/src/main/java/mixer/Mixer.java | 172 +++++++++++++++ settings.gradle | 1 + 5 files changed, 518 insertions(+), 2 deletions(-) create mode 100644 mixer/build.gradle create mode 100644 mixer/src/main/java/mixer/Graph.java create mode 100644 mixer/src/main/java/mixer/Mixer.java diff --git a/meerkat-common/src/main/proto/meerkat/mixing.proto b/meerkat-common/src/main/proto/meerkat/mixing.proto index 50cffc7..8adade9 100644 --- a/meerkat-common/src/main/proto/meerkat/mixing.proto +++ b/meerkat-common/src/main/proto/meerkat/mixing.proto @@ -6,7 +6,45 @@ option java_package = "meerkat.protobuf"; import 'meerkat/crypto.proto'; -// TODO: message ZeroKnowledgeProof { bytes data = 1; -} \ No newline at end of file +} + +//message ZeroKnowledgeProof { +// +// message OrProof{ +// message GroupMember{ +// required bytes data = 1; +// } +// message BigIntegerMsg{ +// required bytes data = 1; +// } +// //input : g1,h1, g2, h2, g1Tag, h1Tag, g2Tag, h2Tag; +// required GroupMember g1 = 1; +// required GroupMember h1 = 2; +// required GroupMember g2 = 3; +// required GroupMember h2 = 4; +// required GroupMember g1Tag = 5; +// required GroupMember h1Tag = 6; +// required GroupMember g2Tag = 7; +// required GroupMember h2Tag = 8; +// +// //calc: u, v, uTag, vTag; +// required GroupMember g2 = 9; +// required GroupMember h2 = 10; +// required GroupMember g1Tag = 11; +// required GroupMember h1Tag = 12; +// +// //generated: c1,c2,z,zTag +// required BigIntegerMsg c1 = 13; +// required BigIntegerMsg c2 = 14; +// required BigIntegerMsg z = 15; +// required BigIntegerMsg zTag = 16; +// } +// +// required OrProof first = 1; +// required OrProof second = 2; +// required OrProof third = 3; +// required OrProof fourth = 4; +//} +// \ No newline at end of file diff --git a/mixer/build.gradle b/mixer/build.gradle new file mode 100644 index 0000000..14cb9dd --- /dev/null +++ b/mixer/build.gradle @@ -0,0 +1,205 @@ + +plugins { + id "us.kirchmeier.capsule" version "1.0.1" + id 'com.google.protobuf' version '0.7.0' +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' + +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' + +// 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') : "" +} + +description = "TODO: Add a description" + +// Your project version +version = "0.0" + +version += "${isSnapshot ? '-SNAPSHOT' : ''}" + + +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.+' + + testCompile 'junit:junit:4.+' + + runtime 'org.codehaus.groovy:groovy:2.4.+' +} + + +/*==== You probably don't have to edit below this line =======*/ + +// The run task added by the application plugin +// is also of type JavaExec. +tasks.withType(JavaExec) { + // Assign all Java system properties from + // the command line to the JavaExec task. + systemProperties System.properties +} + + +protobuf { + // Configure the protoc executable + protoc { + // Download from repositories + artifact = 'com.google.protobuf:protoc:3.+' + } +} + + +idea { + module { + project.sourceSets.each { sourceSet -> + + def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" + + println "Adding $srcDir" + // add protobuf generated sources to generated source dir. + if ("test".equals(sourceSet.name)) { + testSourceDirs += file(srcDir) + } else { + sourceDirs += file(srcDir) + } + generatedSourceDirs += file(srcDir) + + } + + // Don't exclude build directory + excludeDirs -= file(buildDir) + } +} + + +/*=================================== + * "Fat" Build targets + *===================================*/ + + +if (project.hasProperty('mainClassName') && (mainClassName != null)) { + + task mavenCapsule(type: MavenCapsule) { + description = "Generate a capsule jar that automatically downloads and caches dependencies when run." + applicationClass mainClassName + destinationDir = buildDir + } + + task fatCapsule(type: FatCapsule) { + description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class" + + destinationDir = buildDir + + def fatMain = hasProperty('fatmain') ? fatmain : mainClassName + + applicationClass fatMain + + def testJar = hasProperty('test') + + if (hasProperty('fatmain')) { + appendix = "fat-${fatMain}" + } else { + appendix = "fat" + } + + if (testJar) { + from sourceSets.test.output + } + } +} + + +/*=================================== + * Repositories + *===================================*/ + +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() + + // 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 "" +} +info.description 'Print some information about project parameters' + + +/*=================================== + * Publishing + *===================================*/ + +publishing { + publications { + mavenJava(MavenPublication) { + groupId project.groupId + pom.withXml { + asNode().appendNode('description', project.description) + } + from project.components.java + + } + } + repositories { + maven { + url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}" + credentials { username + password + + username nexusUser + password nexusPassword + } + } + } +} + + + diff --git a/mixer/src/main/java/mixer/Graph.java b/mixer/src/main/java/mixer/Graph.java new file mode 100644 index 0000000..b600be8 --- /dev/null +++ b/mixer/src/main/java/mixer/Graph.java @@ -0,0 +1,100 @@ +package mixer; + +import java.util.ArrayList; +import java.util.List; + +class Graph +{ + private int n; + private Node[] nodes; + protected Graph(int[] permutation){ + n = permutation.length; // n = 2^k + createNodes(); + createEdges(permutation); + setSwitches(); + } + + protected boolean getSwitchValue(int index,boolean up) + { + // index must be less then n/2 + return up ? nodes[index].on : nodes[index + n / 2].on; + } + + private void createNodes() + { + nodes = new Node[n]; + for (int i = 0; i < n / 2; i++) + { + nodes[i] = new Node(i, i + n / 2, true); + nodes[i + n / 2] = new Node(i, i + n / 2, false); + } + } + + private void createEdges(int[] permutation) + { + int pi1, pi2; + for (int i = 0; i < n / 2; i++) + { + pi1 = (permutation[i] < n / 2) ? permutation[i] + (n / 2) : permutation[i]; + pi2 = (permutation[i + n / 2] < n / 2) ? permutation[i + n / 2] + (n / 2) : permutation[i + n / 2]; + + nodes[i].edges.add(new Edge(nodes[pi1], (permutation[i] >= n / 2))); + nodes[pi1].edges.add(new Edge(nodes[i], (permutation[i] >= n / 2))); + + nodes[i].edges.add(new Edge(nodes[pi2], (permutation[i + n / 2] < n / 2))); + nodes[pi2].edges.add(new Edge(nodes[i], (permutation[i + n / 2] < n / 2))); + } + } + + private void setSwitches() + { + for (int i = 0; i < n / 2; i++) + { + Node node = nodes[i]; + if (node.set) + continue; + boolean v = false; + while (true) + { + node.set = true; + node.on = v; + + if (node.edges.get(0).nighbor.set && node.edges.get(1).nighbor.set) + break; + v ^= (!node.edges.get(0).nighbor.set) ? node.edges.get(0).broken : node.edges.get(1).broken; + node = (!node.edges.get(0).nighbor.set) ? node.edges.get(0).nighbor : node.edges.get(1).nighbor; + } + } + } + + private class Node + { + public boolean up; + public List edges; + public int i, j; + public boolean on; + public boolean set; + public Node(int i, int j,boolean up) + { + this.i = i; + this.j = j; + this.up = up; + edges = new ArrayList(); + set = false; + } + } + + private class Edge + { + public Node nighbor; + public boolean broken; + public Edge(Node nighbor, boolean broken) + { + this.nighbor = nighbor; + this.broken = broken; + } + } + + +} + diff --git a/mixer/src/main/java/mixer/Mixer.java b/mixer/src/main/java/mixer/Mixer.java new file mode 100644 index 0000000..c4d2952 --- /dev/null +++ b/mixer/src/main/java/mixer/Mixer.java @@ -0,0 +1,172 @@ +package mixer; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; + +import com.google.protobuf.InvalidProtocolBufferException; + +import javafx.util.Pair; + +import java.util.Random; + +import meerkat.protobuf.Crypto.*; +import meerkat.protobuf.Mixing.*; + +import meerkat.crypto.Encryption; +import meerkat.crypto.concrete.ECElGamalEncryption; +import meerkat.crypto.mixnet.Mix2ZeroKnowledgeProver; + +public class Mixer{ + + private Random random; + private Mix2ZeroKnowledgeProver prover; + private Encryption encryptor; + + public Mixer(Random rand,Mix2ZeroKnowledgeProver prov,Encryption enc) { + this.random = rand; + this.prover = prov; + this.encryptor = enc; + + } + + public Pair mix(List ciphertexts) throws InvalidProtocolBufferException{ + + int n = ciphertexts.size(); + // assert n = 2^k and n > 1 + if( n <= 1 || ((n & (n-1)) != 0)) + return null; + + //initialization + int layers = 0; + for (int i = n; i > 1; i>>=1) { + layers++; + } + layers<<=1; + layers--; + RerandomizableEncryptedMessage[][] encryptionsTable = new RerandomizableEncryptedMessage[layers][n]; + ZeroKnowledgeProof[][] proofsTable= new ZeroKnowledgeProof[layers][n/2]; + boolean[][] mixnet = createMixNet(n,layers); + int index1, index2, switchIndex = 0; + EncryptionRandomness r1 ,r2; + RerandomizableEncryptedMessage e1, e2; + boolean half = true; + + //set first level of encryptions + for (int j = 0; j < n; j++) + { + encryptionsTable[0][j] = ciphertexts.get(j); + } + + // main loop + for (int i = n, layer = 0; layer < layers; layer++) // i == permutation size + { + for (int j = 0; j < n; j += i) // + { + for (int k = 0; k < i / 2; k++) + { + index1 = k + j; + index2 = k + j + i / 2; + e1 = encryptionsTable[layer][index1]; + e2 = encryptionsTable[layer][index2]; + r1 = encryptor.generateRandomness(random); + r2 = encryptor.generateRandomness(random); + if (!mixnet[layer][switchIndex]) + { + encryptionsTable[layer+1][index1] = encryptor.rerandomize(e1, r1); + encryptionsTable[layer+1][index2] = encryptor.rerandomize(e2,r2); + + } + else + { + encryptionsTable[layer+1][index1] = encryptor.rerandomize(e2,r2); + encryptionsTable[layer+1][index2] = encryptor.rerandomize(e1,r1); + } + proofsTable[layer][switchIndex] = + prover.prove(e1, e2, encryptionsTable[layer + 1][index1], + encryptionsTable[layer + 1][index2], + mixnet[layer][switchIndex], r1,r2); + + switchIndex = (switchIndex + 1) % (n / 2); + } + } + if (half) + { + i >>= 1; + if (i == 1) + { + half = false; + i = 4; + } + } + else + { + i <<= 1; + } + } + return new Pair(proofsTable, encryptionsTable); + } + + private int[] randomPermutation(int n){ + 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; + } + + private boolean[][] createMixNet(int n,int layers) + { + int[] permutaion = randomPermutation(n); + int[] pi, piL, piR; + Queue permutaions = new ArrayBlockingQueue(n); + Graph graph; + boolean[][] mixnet = new boolean[layers][n>>1]; + + permutaions.add(permutaion); + + for (int i = n, layer = 0; i > 1; i >>= 1, layer++) // i == permutation size + { + for (int j = 0; j < n / 2; j += i / 2) // + { + pi = permutaions.remove(); + graph = new Graph(pi); + piL = new int[i / 2]; + piR = new int[i / 2]; + for (int k = 0; k < i / 2; k++) + { + mixnet[layers - layer - 1][k + j] = graph.getSwitchValue(k, true); + mixnet[layer][k + j] = graph.getSwitchValue(k, false); + + if (!mixnet[layers - layer - 1][k + j]) + { + piL[k] = pi[k] % (i / 2); + piR[k] = pi[k + i / 2] % (i / 2); + } + else + { + piL[k] = pi[k + i / 2] % (i / 2); + piR[k] = pi[k] % (i / 2); + } + } + permutaions.add(piL); + permutaions.add(piR); + } + } + return mixnet; + } + +} diff --git a/settings.gradle b/settings.gradle index e4ef054..043ce64 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,3 +3,4 @@ include 'voting-booth' include 'bulletin-board-server' include 'polling-station' include 'restful-api-common' +include 'mixer' \ No newline at end of file