Merge branch 'master' of https://vcs.factcenter.org/meerkat/meerkat-java into voting-booth-gui

android-scanner
Laura Radaelli 2017-01-25 21:45:50 +02:00
commit 17d7d5eca4
98 changed files with 3579 additions and 2202 deletions

View File

@ -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}/"
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

1
bulletin-board-client/gradlew vendored Symbolic link
View File

@ -0,0 +1 @@
../gradlew

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -104,7 +104,7 @@ public class Protocol<T> extends VerifiableSecretSharing<T> {
* setter
* @param parties
*/
protected void setParties(Party[] parties){
protected void setParties(Party<T>[] parties){
this.parties = parties;
}
@ -112,7 +112,7 @@ public class Protocol<T> extends VerifiableSecretSharing<T> {
* getter
* @return
*/
protected Party[] getParties(){
protected Party<T>[] getParties(){
return parties;
}

View File

@ -77,7 +77,7 @@ public class User<T> implements Runnable {
* All parties participating in key generation.
* parties[id-1] has my info.
*/
protected final Party[] parties;
protected final Party<T>[] parties;
/**
* set of all non-disqualified parties

View File

@ -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

View File

@ -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
}
}
}

View File

@ -15,11 +15,14 @@ import java.util.List;
*/
public class MessageInputStream<T extends Message> implements Iterable<T>{
Class<T> type;
private T.Builder builder;
private InputStream in;
MessageInputStream(InputStream in, Class<T> 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<T extends Message> implements Iterable<T>{
@Override
public T next() {
try {
return readMessage();
return readMessage(type);
} catch (IOException e) {
return null;
}
@ -65,12 +68,10 @@ public class MessageInputStream<T extends Message> implements Iterable<T>{
}
public T readMessage() throws IOException{
public T readMessage(Class<T> 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<T extends Message> implements Iterable<T>{
List<T> list = new LinkedList<>();
while (isAvailable()){
list.add(readMessage());
list.add(readMessage(type));
}
return list;

View File

@ -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<ECPoint,ECPoint> 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<ECPoint,ECPoint> randomizer = elGamalPK.encrypt(curve.getInfinity(), rndInt);
ConcreteCrypto.ElGamalCiphertext originalEncodedCipher= ConcreteCrypto.ElGamalCiphertext.parseFrom(msg.getData());
Pair<ECPoint,ECPoint> originalCipher = new Pair<ECPoint, ECPoint>(
curve.decodePoint(originalEncodedCipher.getC1().toByteArray()),
curve.decodePoint(originalEncodedCipher.getC2().toByteArray()));
decodeElement(originalEncodedCipher.getC1()),
decodeElement(originalEncodedCipher.getC2()));
Pair<ECPoint,ECPoint> 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());
}
}

View File

@ -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<ECPoint> 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 extends Message> T decrypt(Class<T> 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<ECPoint, ECPoint>(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");
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<Crypto.RerandomizableEncryptedMessage> ciphertexts,Random random)
throws InvalidProtocolBufferException;
}

View File

@ -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();
}

View File

@ -1,7 +0,0 @@
package meerkat.crypto.mixnet;
/**
* Created by talm on 25/10/15.
*/
public class Trustee {
}

View File

@ -1,7 +0,0 @@
package meerkat.crypto.mixnet;
/**
* Created by talm on 25/10/15.
*/
public class Verifier {
}

View File

@ -10,4 +10,4 @@ message BroadcastMessage {
bool is_private = 3;
bytes payload = 5;
}
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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 extends Message> T decrypt(Class<T> 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<ECPoint, ECPoint>(c1, c2));
ECPoint plaintextEncoded = secretKey.decrypt(new Pair<>(c1, c2));
byte[] plaintext = group.injectiveDecode(plaintextEncoded);

View File

@ -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
}
}
}

View File

@ -0,0 +1 @@
../../meerkat-common/src/main/proto/meerkat/concrete_crypto.proto

1
mixer/docs/crypto.proto Symbolic link
View File

@ -0,0 +1 @@
../../meerkat-common/src/main/proto/meerkat/crypto.proto

104
mixer/docs/mixer.md Normal file
View File

@ -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

1
mixer/docs/mixing.proto Symbolic link
View File

@ -0,0 +1 @@
../src/main/proto/meerkat/mixing.proto

1
mixer/gradlew vendored Symbolic link
View File

@ -0,0 +1 @@
../gradlew

View File

@ -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<RerandomizableEncryptedMessage> 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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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<BulletinBoardAPI.BatchChunk> MixerOutput2BatchChunk(MixerOutput mixerOutput) {
List<BulletinBoardAPI.BatchChunk> result = new ArrayList<BulletinBoardAPI.BatchChunk>();
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<BulletinBoardAPI.BatchChunk> 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);
}

View File

@ -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<CompleteBatch>
*/
* Handles callback for receiving a complete mix proof table from the bulletin board.
*/
public class BatchHandler implements AsyncBulletinBoardClient.ClientCallback<CompleteBatch> {
private MixerOutput mixerOutput;
@ -21,19 +21,14 @@ public class BatchHandler implements AsyncBulletinBoardClient.ClientCallback<Com
private Throwable t;
private CompleteBatch msg;
private final int n, layers;
private final Mix2ZeroKnowledgeVerifier verifier;
private final Mix2nizk.Verifier verifier;
/**
* constructor
* @param n
* @param layers
* @param verifier
*/
public BatchHandler(int n, int layers, Mix2ZeroKnowledgeVerifier verifier) {
public BatchHandler(Mix2nizk.Verifier verifier) {
this.mixerOutput = null;
this.n = n;
this.layers = layers;
this.msgReceived = false;
this.t = null;
this.verifier = verifier;
@ -66,39 +61,39 @@ public class BatchHandler implements AsyncBulletinBoardClient.ClientCallback<Com
}
/**
* convert batch data to meerkat.mixer.mixing output
* convert batch data to meerkat.mixProverVerifier.network output
* @throws Exception
*/
private void convertMessage() throws Exception {
BatchConverter batchConverter = new BatchConverter(n,layers);
BatchConverter batchConverter = new BatchConverter();
this.mixerOutput = batchConverter.BatchChunkList2MixerOutput(msg.getBatchChunkList());
}
/**
* call convert message, and if succeed verify the table
* @return return true iff the given batch message is valid meerkat.mixer.mixing output
* @return return true iff the given batch message is valid meerkat.mixProverVerifier.network output
* @throws Exception
*/
public boolean verifyTable() throws Exception {
public boolean verifyTable(boolean strict) throws Exception {
if (mixerOutput == null) {
convertMessage();
}
return VerifyTable.verifyTable(verifier, n, mixerOutput);
return MixVerifier.verifyTable(verifier, mixerOutput, strict);
}
/**
* extract input for meerkat.mixer.mixing from previous mixers output
* extract input for meerkat.mixProverVerifier.network from previous mixers output
* @return last layer of encrypted votes as list
* @throws Throwable in case if failure
*/
public List<Crypto.RerandomizableEncryptedMessage> getInputForMixer() throws Throwable {
public List<Crypto.RerandomizableEncryptedMessage> 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
}
}

View File

@ -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<Integer> prevBatchIds, int batchId, Random random, AsyncBulletinBoardClient.ClientCallback<?> callback) throws Throwable {
public void main(List<Integer> prevBatchIds, int batchId, boolean strictVerification, Random random, AsyncBulletinBoardClient.ClientCallback<?> callback) throws Throwable {
List<Crypto.RerandomizableEncryptedMessage> mixerInput;
List<BatchHandler> batchHandlers = new ArrayList<BatchHandler>(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<BulletinBoardAPI.BatchChunk> batchChunkList = batchConverter.MixerOutput2BatchChunk(mixerOutput);
asyncBulletinBoardClient.postBatch(id, batchId, batchChunkList, callback);
}

View File

@ -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<Crypto.RerandomizableEncryptedMessage> 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<String> 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<Void> OPT_HELP = parser.accepts("help", "Print help");
final OptionSpec<Void> OPT_GENKEY = parser.accepts("genkey", "Generate a key-pair (write into key file)");
final OptionSpec<Void> OPT_DECRYPT = parser.accepts("decrypt", "Decrypt using given keypair");
final OptionSpec<File> OPT_KEYFILE = parser.accepts("keys", "File containing public key (or keypair for decryption)").withRequiredArg().ofType(File.class);
final OptionSpec<Void> OPT_ENCRYPT = parser.accepts("encrypt", "Decrypt using given keypair");
final OptionSpec<Void> OPT_STRICT = parser.accepts("strict", "Use strict verification (verify that network matches our Benes implementation)");
final OptionSpec<File> 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<File> 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();
}
}
}

View File

@ -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<int[]> permutationsQueue = new ArrayBlockingQueue<int[]>(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<Edge> edges;
private boolean value;
private boolean set;
public Node() {
edges = new ArrayList<Edge>(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;
}
}
}
}

View File

@ -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();
}
}

View File

@ -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<Integer> numbers= new ArrayList<Integer>(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;
}
}

View File

@ -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 +
'}';
}
}

View File

@ -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 <a href="http://grid.cs.gsu.edu/~wkim/index_files/permutation_network.pdf">paper</a>)
* @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<Integer> 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];
}
}

View File

@ -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);
}

View File

@ -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 <T> 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;
}
}
}

View File

@ -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 <T> type of values.
*/
public static <T> 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 <T> 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 <T> 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);
}
}
}

View File

@ -0,0 +1,21 @@
package meerkat.mixer.proofs;
/**
* Created by talm on 11/01/17.
*/
public interface Concatenator {
public interface Pair<OutType, InType1, InType2>{
public OutType concatenate(InType1 msg1, InType2 msg2);
public InType1 getMsg1(OutType msg);
public InType2 getMsg2(OutType msg);
}
public interface Triplet<OutType, InType1, InType2, InType3>{
public OutType concatenate(InType1 msg1, InType2 msg2, InType3 msg3);
public InType1 getMsg1of3(OutType msg);
public InType2 getMsg2of3(OutType msg);
public InType3 getMsg3of3(OutType msg);
}
}

View File

@ -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;
}
}

View File

@ -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 <FirstMsgType, FinalMessageType> {
public FirstMsgType getFirstMessage();
public FinalMessageType getFinalMessage(BigInteger challenge);
}
public interface Simulator <FirstMsgType, FinalMessageType> {
public FirstMsgType getFirstMessage(BigInteger challenge);
public FinalMessageType getFinalMessage();
}
public interface Verifier <FirstMsgType, FinalMessageType> {
public boolean verify(FirstMsgType firstMessage, BigInteger challenge, FinalMessageType finalMessage);
}
}

View File

@ -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<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> {
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<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> {
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<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> {
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]));
}
}
}

View File

@ -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<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.FinalMessage, Mixing.Mix2Proof.AndProof.FinalMessage> {
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<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.FinalMessage, Mixing.Mix2Proof.AndProof.FinalMessage> {
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]));
}
}
}

View File

@ -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<Mixing.Mix2Proof, Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.FinalMessage> 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);
}
}

View File

@ -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<Mixing.Mix2Proof.AndProof.FirstMessage,Mixing.Mix2Proof.DlogProof.FirstMessage,Mixing.Mix2Proof.DlogProof.FirstMessage> {
@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<Mixing.Mix2Proof.AndProof.FinalMessage,Mixing.Mix2Proof.DlogProof.FinalMessage,Mixing.Mix2Proof.DlogProof.FinalMessage> {
@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<Mixing.Mix2Proof.FirstMessage,Mixing.Mix2Proof.AndProof.FirstMessage,Mixing.Mix2Proof.AndProof.FirstMessage> {
@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<Mixing.Mix2Proof.FinalMessage,BigInteger,Mixing.Mix2Proof.AndProof.FinalMessage,Mixing.Mix2Proof.AndProof.FinalMessage> {
@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<Mixing.Mix2Proof, Mixing.Mix2Proof.FirstMessage,Mixing.Mix2Proof.FinalMessage> {
@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();
}
}
}

View File

@ -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<Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> {
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<Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> {
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<Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> {
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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<NIZKMsgType, FirstMsgType extends Message, FinalMessageType> {
final static String DIGEST_ALG = "SHA-256";
final MessageDigest md;
final Concatenator.Pair<NIZKMsgType, FirstMsgType, FinalMessageType> concat;
public SigmaFiatShamir(Concatenator.Pair<NIZKMsgType, FirstMsgType, FinalMessageType> 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);
}
}
/**
* FiatShamir 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<FirstMsgType, FinalMessageType> 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<FirstMsgType, FinalMessageType> verifier) {
FirstMsgType firstMessage = concat.getMsg1(NIZK);
BigInteger challenge = hash(firstMessage);
return verifier.verify(firstMessage, challenge, concat.getMsg2(NIZK));
}
}

View File

@ -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 <FirstMessageOut, FirstMessageIn, FinalMessageOut, FinalMessageIn>
implements SigmaProtocol.Prover<FirstMessageOut, FinalMessageOut> {
final SigmaProtocol.Prover<FirstMessageIn, FinalMessageIn>[] provers;
final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
final Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;
@SafeVarargs
public Prover(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
SigmaProtocol.Prover<FirstMessageIn, FinalMessageIn>... 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<FirstMessageOut, FirstMessageIn,
FinalMessageOut, FinalMessageIn>
implements SigmaProtocol.Verifier<FirstMessageOut, FinalMessageOut> {
final SigmaProtocol.Verifier<FirstMessageIn, FinalMessageIn>[] verifiers;
final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
final Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;
@SafeVarargs
public Verifier(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
SigmaProtocol.Verifier<FirstMessageIn, FinalMessageIn>... 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 <FirstMessageOut, FirstMessageIn, FinalMessageOut, FinalMessageIn>
implements SigmaProtocol.Simulator<FirstMessageOut, FinalMessageOut> {
final SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn>[] simulators;
final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
final Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;
@SafeVarargs
public Simulator(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
Concatenator.Pair<FinalMessageOut, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn>... 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());
}
}
}

View File

@ -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 <FirstMessageOut, FirstMessageIn, FinalMessageOut, FinalMessageIn>
implements SigmaProtocol.Prover<FirstMessageOut, FinalMessageOut> {
final SigmaProtocol.Prover<FirstMessageIn, FinalMessageIn> prover;
final SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn> simulator;
final int proverIdx;
final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
final Concatenator.Triplet<FinalMessageOut, BigInteger, FinalMessageIn, FinalMessageIn> 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<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
Concatenator.Triplet<FinalMessageOut, BigInteger, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
ChallengeGenerator challengeGenerator,
SigmaProtocol.Prover<FirstMessageIn, FinalMessageIn> prover,
SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn> 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 <FirstMessageOut, FirstMessageIn, FinalMessageOut, FinalMessageIn>
implements SigmaProtocol.Verifier<FirstMessageOut, FinalMessageOut> {
final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
final Concatenator.Triplet<FinalMessageOut, BigInteger, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;
final ChallengeGenerator challengeGenerator;
final SigmaProtocol.Verifier<FirstMessageIn, FinalMessageIn>[] verifiers;
@SafeVarargs
public Verifier(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
Concatenator.Triplet<FinalMessageOut, BigInteger, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
ChallengeGenerator challengeGenerator,
SigmaProtocol.Verifier<FirstMessageIn, FinalMessageIn>... 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<FirstMessageOut, FirstMessageIn, FinalMessageOut, FinalMessageIn>
implements SigmaProtocol.Simulator<FirstMessageOut, FinalMessageOut> {
final Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator;
final Concatenator.Triplet<FinalMessageOut, BigInteger, FinalMessageIn, FinalMessageIn> finalMessageConcatenator;
final ChallengeGenerator challengeGenerator;
final SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn>[] simulators;
BigInteger simChallenge0;
@SafeVarargs
public Simulator(Concatenator.Pair<FirstMessageOut, FirstMessageIn, FirstMessageIn> firstMessageConcatenator,
Concatenator.Triplet<FinalMessageOut, BigInteger, FinalMessageIn, FinalMessageIn> finalMessageConcatenator,
ChallengeGenerator challengeGenerator, SigmaProtocol.Simulator<FirstMessageIn, FinalMessageIn>... 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());
}
}
}

View File

@ -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);
}
}
}

View File

@ -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 FiatShamir 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;
}
/**
* FiatShamir 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();
}
}

View File

@ -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)));
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}

View File

@ -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<Crypto.RerandomizableEncryptedMessage> generateMixerInput(){
List<Crypto.RerandomizableEncryptedMessage> result = new ArrayList<Crypto.RerandomizableEncryptedMessage>();
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<Crypto.RerandomizableEncryptedMessage> 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<Crypto.RerandomizableEncryptedMessage> 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<Crypto.RerandomizableEncryptedMessage> generateMixerInput(){
// List<Crypto.RerandomizableEncryptedMessage> result = new ArrayList<Crypto.RerandomizableEncryptedMessage>();
// 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<Crypto.RerandomizableEncryptedMessage> 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<Crypto.RerandomizableEncryptedMessage> 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");
// }
//}

View File

@ -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);
}
}
}

View File

@ -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));
}
}

View File

@ -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<Crypto.RerandomizableEncryptedMessage> generateMixerInput(){
public List<Crypto.RerandomizableEncryptedMessage> generateMixerInput(int n){
List<Crypto.RerandomizableEncryptedMessage> result = new ArrayList<Crypto.RerandomizableEncryptedMessage>();
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<Crypto.RerandomizableEncryptedMessage> mixerInput = generateMixerInput();
System.out.println(" start mixing");
List<Crypto.RerandomizableEncryptedMessage> 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");

View File

@ -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);

View File

@ -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<ECPoint> 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 extends Message> T decrypt(Class<T> 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<ECPoint, ECPoint>(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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<Integer> getSequenceSet(int N) {
Set<Integer> 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<Integer> 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);
}
}
}

View File

@ -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<Crypto.BigInteger,BigInteger> {
/**
* 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<Crypto.BigInteger,BigInteger> {
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<Crypto.BigInteger,BigInteger> {
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;
}
}
}

View File

@ -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<Crypto.BigInteger, BigInteger> {
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<Crypto.BigInteger, BigInteger> getNewProver() {
return new DummySigmaProof.Prover(x, y, z, new BigInteger(100, rand));
}
@Override
protected SigmaProtocol.Verifier<Crypto.BigInteger, BigInteger> getNewVerifier() {
return new DummySigmaProof.Verifier(x, y, z);
}
@Override
protected SigmaProtocol.Simulator<Crypto.BigInteger, BigInteger> getNewSimulator() {
return new DummySigmaProof.Simulator(x, y, z);
}
@Override
protected BigInteger getChallengeModulus() {
return new BigInteger(100, rand);
}
}

View File

@ -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<M1 extends Message, M2> {
public final int NUM_REPEAT = 10;
abstract protected void generateRandomTrueStatement();
abstract protected void generateRandomFalseStatement();
abstract protected SigmaProtocol.Prover<M1, M2> getNewProver();
abstract protected SigmaProtocol.Verifier<M1, M2> getNewVerifier();
abstract protected SigmaProtocol.Simulator<M1, M2> getNewSimulator();
public class NIZKConcat implements Concatenator.Pair<Pair<M1,M2>, M1, M2> {
@Override
public Pair<M1, M2> concatenate(M1 msg1, M2 msg2) { return new Pair<M1, M2>(msg1, msg2); }
@Override
public M1 getMsg1(Pair<M1,M2> msg) { return msg.a; }
@Override
public M2 getMsg2(Pair<M1,M2> msg) { return msg.b; }
}
final protected NIZKConcat nizkConcat = new NIZKConcat();
abstract protected BigInteger getChallengeModulus();
SigmaProtocol.Prover<M1, M2> prover;
SigmaProtocol.Verifier<M1, M2> verifier;
SigmaProtocol.Simulator<M1, M2> 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<Pair<M1,M2>, M1, M2> fiatShamir = new SigmaFiatShamir<Pair<M1,M2>, M1, M2>(nizkConcat);
prover = getNewProver();
Pair<M1,M2> nizk = fiatShamir.generateNizk(prover);
verifier = getNewVerifier();
boolean nizkOk = fiatShamir.verifyNizk(nizk, verifier);
assertTrue(String.format("NIZK Verification error in iteration %d", i), nizkOk);
}
}
}

View File

@ -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<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage> {
final DlogStatementSchnorrSigmaTest dlogtest;
Statements.DlogStatement s1, s2;
Statements.DlogStatementWitness w1, w2;
public DlogAndStatementSigmaTest() {
this.dlogtest = new DlogStatementSchnorrSigmaTest();
}
@Override
protected void generateRandomTrueStatement() {
Pair<Statements.DlogStatement, Statements.DlogStatementWitness> s1w1 = dlogtest.returnRandomTrueStatement();
s1 = s1w1.a; w1 = s1w1.b;
Pair<Statements.DlogStatement, Statements.DlogStatementWitness> s2w2 = dlogtest.returnRandomTrueStatement();
s2 = s2w2.a; w2 = s2w2.b;
}
@Override
protected void generateRandomFalseStatement() {
s1 = dlogtest.returnRandomFalseStatement();
s2 = dlogtest.returnRandomFalseStatement();
}
@Override
protected SigmaProtocol.Prover<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage> 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<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage> 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<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage> 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();
}
}

View File

@ -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<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.FinalMessage> {
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<Statements.DlogStatement, Statements.DlogStatementWitness> s1w1 = dlogtest.returnRandomTrueStatement();
Pair<Statements.DlogStatement, Statements.DlogStatementWitness> 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<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.FinalMessage> getNewProver() {
SigmaProtocol.Prover<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage> andProver = new DlogConjunction.Prover(dlogtest.encryptor, rand, statements[trueStatementIndex], w);
SigmaProtocol.Simulator<Mixing.Mix2Proof.AndProof.FirstMessage, Mixing.Mix2Proof.AndProof.FinalMessage> 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<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.FinalMessage> getNewVerifier() {
Mix2.ChallengeGenerator gen = new Mix2.ChallengeGenerator(dlogtest.encryptor, rand);
return new SigmaProtocolOr2.Verifier<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.AndProof.FirstMessage,
Mixing.Mix2Proof.FinalMessage, Mixing.Mix2Proof.AndProof.FinalMessage>(ProtobufConcatenators.concatMix1, ProtobufConcatenators.concatMix2,
gen, new DlogConjunction.Verifier(dlogtest.encryptor, statements[0]), new DlogConjunction.Verifier(dlogtest.encryptor, statements[1]));
}
@Override
protected SigmaProtocol.Simulator<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.FinalMessage> getNewSimulator() {
Mix2.ChallengeGenerator gen = new Mix2.ChallengeGenerator(dlogtest.encryptor, rand);
return new SigmaProtocolOr2.Simulator<Mixing.Mix2Proof.FirstMessage, Mixing.Mix2Proof.AndProof.FirstMessage,
Mixing.Mix2Proof.FinalMessage, Mixing.Mix2Proof.AndProof.FinalMessage>(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();
}
}

View File

@ -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<Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> {
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<Statements.DlogStatement, Statements.DlogStatementWitness> 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<Statements.DlogStatement, Statements.DlogStatementWitness> sw = returnRandomTrueStatement();
this.statement = sw.a;
this.witness = sw.b;
}
@Override
protected void generateRandomFalseStatement() {
witness = null;
statement = returnRandomFalseStatement();
}
@Override
protected SigmaProtocol.Prover<Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> getNewProver() {
prover = new SchnorrDlogEquivalence.Prover(encryptor, rand, statement, witness);
return prover;
}
@Override
protected SigmaProtocol.Verifier<Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> getNewVerifier() {
verifier = new SchnorrDlogEquivalence.Verifier(encryptor, statement);
return verifier;
}
@Override
protected SigmaProtocol.Simulator<Mixing.Mix2Proof.DlogProof.FirstMessage, Mixing.Mix2Proof.DlogProof.FinalMessage> getNewSimulator() {
simulator = new SchnorrDlogEquivalence.Simulator(encryptor, rand, statement);
return simulator;
}
@Override
protected BigInteger getChallengeModulus() {
return group.orderUpperBound();
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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++){

View File

@ -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);

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<<LogVotes) / 2;
@ -75,7 +74,7 @@ public class ZeroKnowledgeProof {
for (int i = 0; i < n*2 ; i +=2){
prover.prove(encryptedMessage[i],encryptedMessage[i+1],reencryptedMessage[i],reencryptedMessage[i+1],
false,0,0,0,randomnesses[i],randomnesses[i+1]);
false,0,0,0, 0, randomnesses[i],randomnesses[i+1]);
}
long finishTime = System.currentTimeMillis();

View File

@ -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 = "Meerkat polling-station application"
@ -43,21 +42,10 @@ 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 '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.+'
@ -137,21 +125,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()
@ -159,13 +132,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'
@ -187,12 +162,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
}
}
}

View File

@ -37,8 +37,16 @@ public class PollingStationScannerWebApp implements PollingStationScanner.Produc
* This method is called by the Jetty engine when instantiating the servlet
*/
@PostConstruct
public void init() throws Exception{
callbacks = (Iterable<FutureCallback<PollingStation.ScannedData>>) servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME);
@SuppressWarnings("unchecked")
public void init() throws Exception {
Object context = servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME);
try {
callbacks = (Iterable<FutureCallback<PollingStation.ScannedData>>) context;
} catch (ClassCastException e) {
throw e;
}
}
@POST

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -90,7 +90,7 @@ public class VotingBoothToyRun {
private static List<BallotQuestion> generateChannelChoiceQuestions() {
ArrayList<BallotQuestion> channelChoiceQuestions = new ArrayList();
ArrayList<BallotQuestion> 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<BallotQuestion> generateBallotQuestions() {
ArrayList<BallotQuestion> allBallotQuestions = new ArrayList();
ArrayList<BallotQuestion> 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<String, UIElement> systemMessageMap = new HashMap();
Map<String, UIElement> systemMessageMap = new HashMap<>();
systemMessageMap.put(StorageManager.WAIT_FOR_COMMIT_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)

View File

@ -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();
}
}