Merge branch 'master' of http://cs.idc.ac.il/rhodecode/meerkat/meerkat-java into voting-booth-gui

android-scanner
Vladimir Eliezer Tokarev 2016-09-10 14:00:14 +03:00
commit c6f2ffff9b
114 changed files with 7394 additions and 74 deletions

View File

@ -259,5 +259,3 @@ publishing {
}
}

View File

@ -33,7 +33,7 @@ public class Protocol<T> extends VerifiableSecretSharing<T> {
Waiting,
/**
* Party gave invalid answer to conplaint.
* Party gave invalid answer to complaint.
*/
Disqualified,
@ -79,6 +79,7 @@ public class Protocol<T> extends VerifiableSecretSharing<T> {
* it must be chosen such that computing discrete logarithms is hard in this group.
* @param encoder Encode/Decode group elements (of type T) to/from byte array
*/
//TODO: why the use of regular Random? Should it be changed?
public Protocol(int t, int n, BigInteger zi, Random random, BigInteger q, T g
, Group<T> group, int id, ByteEncoder<T> encoder) {
super(t, n, zi, random, q, g,group);

View File

@ -436,7 +436,7 @@ public class User<T> implements Runnable {
/**
* complaint message is valid if:
* 1. it was received in broadcast chanel
* 2. the sender didn't complained against id before
* 2. the sender didn't complain against id before
*/
protected boolean isValidComplaintMessage(int sender, boolean isBroadcast, DKG.IDMessage complaintMessage){
int i = sender;

View File

@ -198,6 +198,7 @@ public class Polynomial implements Comparable<Polynomial> {
@Override
public boolean equals(Object obj) {
//TODO: is this implementation correct? cannot understand its logic (hai)
if(!super.equals(obj))
return false;
Point other = (Point)obj;

View File

@ -10,6 +10,7 @@ import meerkat.crypto.secretsharing.shamir.Polynomial;
import meerkat.crypto.secretsharing.shamir.SecretSharing;
import meerkat.crypto.utils.BigIntegerByteEncoder;
import meerkat.crypto.utils.GenerateRandomPrime;
import meerkat.protobuf.Crypto;
import org.factcenter.qilin.primitives.Group;
import org.factcenter.qilin.primitives.concrete.Zpstar;
import org.factcenter.qilin.util.ByteEncoder;
@ -20,10 +21,7 @@ import org.junit.internal.runners.statements.Fail;
import static org.junit.Assert.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@ -42,58 +40,57 @@ public class SDKGTest {
BigInteger q = p.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
Group<BigInteger> group = new Zpstar(p);
Arithmetic<BigInteger> arithmetic = new Fp(q);
int t = 1;
int n = 20;
int initialT = 17;
int initialN = 20;
Random rand = new Random(1);
public void oneTest(Testable testable) throws Exception {
for (int i = 0; i < testable.sdkgs.length ; i++){
for (int i = 0; i < testable.sdkgs.length; i++){
testable.futures[i] = executorService.submit(testable.sdkgs[i]);
}
for (int i = 0; i < testable.futures.length ; i++){
testable.futures[i].get();
for (Future ftr : testable.futures) {
ftr.get();
}
// got the right public value
BigInteger publicValue = group.multiply(testable.g,testable.secret);
for (int i: testable.valids){
assert (testable.sdkgs[i - 1].getPublicValue().equals(publicValue));
BigInteger publicValue = group.multiply(testable.g, testable.secret);
for (int i : testable.valids){
assertEquals (testable.sdkgs[i-1].getPublicValue(), publicValue);
}
// assert valid verification values
BigInteger expected,verification;
for (int i: testable.valids){
for (int i : testable.valids){
expected = group.multiply(testable.g, testable.sdkgs[i - 1].getShare().y);
verification = VerifiableSecretSharing.computeVerificationValue(i, testable.sdkgs[i - 1].getCommitments(), group);
assert (expected.equals(verification));
assertEquals (expected, verification);
}
// restore the secret from shares
ArrayList<Polynomial.Point> sharesList = new ArrayList<Polynomial.Point>();
ArrayList<Polynomial.Point> sharesList = new ArrayList<>();
for (int i: testable.valids){
for (int i : testable.valids){
sharesList.add(testable.sdkgs[i - 1].getShare());
}
Polynomial.Point[] shares = new Polynomial.Point[sharesList.size()];
for (int i = 0; i < shares.length; i ++){
shares[i] = sharesList.get(i);
}
shares = sharesList.toArray(shares);
BigInteger calculatedSecret = SecretSharing.recoverSecret(shares,arithmetic);
assert (calculatedSecret.equals(testable.secret));
BigInteger calculatedSecret = SecretSharing.recoverSecret(shares, arithmetic);
assertEquals (calculatedSecret, testable.secret);
}
@Test
public void test() throws Exception {
public void runSharingProtocol() throws Exception {
Testable testable;
for (int i = 0; i < NUM_TESTS; i++) {
testable = new Testable(n, t, group, q, rand);
testable = new Testable(initialN+i, initialT+i, group, q, rand);
oneTest(testable);
}
}
static class Testable {
Set<Integer> valids;
Set<Integer> QUAL;
@ -118,14 +115,14 @@ public class SDKGTest {
this.q = q;
this.random = random;
this.sdkgs = new User[n];
this.valids = new HashSet<Integer>();
this.QUAL = new HashSet<Integer>();
this.aborted = new HashSet<Integer>();
this.malicious = new HashSet<Integer>();
this.valids = new HashSet<>();
this.QUAL = new HashSet<>();
this.aborted = new HashSet<>();
this.malicious = new HashSet<>();
this.futures = new Future[n];
this.g = sampleGenerator(random);
this.h = group.multiply(g,randomIntModQ(random));
ArrayList<Integer> ids = new ArrayList<Integer>();
this.h = group.multiply(g, randomIntModQ(random));
List<Integer> ids = new ArrayList<>();
for (int id = 1; id<= n ; id++){
ids.add(id);
}
@ -139,8 +136,8 @@ public class SDKGTest {
id = ids.remove(random.nextInt(ids.size()));
s = randomIntModQ(random);
channel = channels.getChannel(id);
sdkg = new Protocol<BigInteger>(t, n, s, random, q, g , h, group, id,encoder);
sdkgs[id - 1] = randomSDKGUser(id,channel,sdkg);
sdkg = new Protocol<>(t, n, s, random, q, g , h, group, id, encoder);
sdkgs[id - 1] = randomSDKGUser(id, channel, sdkg);
if(QUAL.contains(id)){
this.secret = this.secret.add(s).mod(q);
}
@ -159,7 +156,7 @@ public class SDKGTest {
case HONEST:
valids.add(id);
QUAL.add(id);
return new User<BigInteger>(sdkg,channel);
return new User<>(sdkg,channel);
case FAILSTOP:
int abortStage = random.nextInt(3) + 1; // 1 or 2 or 3
@ -167,13 +164,13 @@ public class SDKGTest {
if (abortStage > 1){
QUAL.add(id);
}
return new SDKGUserImplAbort(sdkg,channel,abortStage);
return new SDKGUserImplAbort(sdkg, channel, abortStage);
case MALICIOUS:
malicious.add(id);
Set<Integer> falls = DKGMaliciousUser.selectFallsRandomly(valids,random);
Protocol<BigInteger> maliciousSDKG = SDKGMaliciousUserImpl.generateMaliciousSDKG(sdkg,channel,random);
return new SDKGMaliciousUserImpl(sdkg,maliciousSDKG,channel,falls);
Set<Integer> falls = DKGMaliciousUser.selectFallsRandomly(valids, random);
Protocol<BigInteger> maliciousSDKG = SDKGMaliciousUserImpl.generateMaliciousSDKG(sdkg, channel, random);
return new SDKGMaliciousUserImpl(sdkg, maliciousSDKG, channel, falls);
}
fail("Unknown user type");
@ -203,4 +200,6 @@ public class SDKGTest {
}
}
}

View File

@ -0,0 +1,11 @@
<<<<<<< HEAD
#Sun Mar 20 15:13:00 IST 2016
=======
#Tue Aug 05 03:26:05 IDT 2014
>>>>>>> e8e511d9ce636a127bb33d70ebfd9b2f230c6e1d
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip
distributionSha256Sum=4647967f8de78d6d6d8093cdac50f368f8c2b8038f41a5afe1c3bce4c69219a9

View File

@ -50,7 +50,7 @@ dependencies {
compile 'com.google.guava:guava:15.0'
// Crypto
compile 'org.factcenter.qilin:qilin:1.2+'
compile 'org.factcenter.qilin:qilin:1.2.+'
compile 'org.bouncycastle:bcprov-jdk15on:1.53'
testCompile 'junit:junit:4.+'

View File

@ -47,7 +47,7 @@ public class ECDSASignature implements DigitalSignature {
ByteBuffer lenBuf = ByteBuffer.allocate(4);
Map<ByteString, Certificate> loadedCertificates = new HashMap<>();
Map<ByteString, Certificate> loadedCertificates = new HashMap<ByteString, Certificate>();
/**
* Signature currently loaded (will be used in calls to {@link #verify()}).

View File

@ -107,6 +107,15 @@ public class ECElGamalEncryption implements Encryption {
.build();
}
public static ConcreteCrypto.ElGamalCiphertext RerandomizableEncryptedMessage2ElGamalCiphertext(Crypto.RerandomizableEncryptedMessage msg) throws InvalidProtocolBufferException {
return ConcreteCrypto.ElGamalCiphertext.parseFrom(msg.getData());
}
public static Crypto.RerandomizableEncryptedMessage elGamalCiphertext2RerandomizableEncryptedMessage(ConcreteCrypto.ElGamalCiphertext msg) {
return Crypto.RerandomizableEncryptedMessage.newBuilder()
.setData(msg.toByteString()).build();
}
@Override
public Crypto.RerandomizableEncryptedMessage rerandomize(Crypto.RerandomizableEncryptedMessage msg, Crypto.EncryptionRandomness rnd) throws InvalidProtocolBufferException {
BigInteger rndInt = BigIntegers.fromUnsignedByteArray(rnd.getData().toByteArray());
@ -135,4 +144,8 @@ public class ECElGamalEncryption implements Encryption {
return retval;
}
public BigInteger extractRandomness(Crypto.EncryptionRandomness encryptionRandomness){
return new BigInteger(1,encryptionRandomness.getData().toByteArray());
}
}

View File

@ -1,5 +1,6 @@
package meerkat.crypto.mixnet;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Mixing;
@ -11,8 +12,9 @@ public interface Mix2ZeroKnowledgeProver {
Crypto.RerandomizableEncryptedMessage in2,
Crypto.RerandomizableEncryptedMessage out1,
Crypto.RerandomizableEncryptedMessage out2,
boolean switched,
boolean switched,int i,int j, int layer, // switch info
Crypto.EncryptionRandomness r1,
Crypto.EncryptionRandomness r2);
Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException;
}

View File

@ -1,5 +1,6 @@
package meerkat.crypto.mixnet;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Mixing;
@ -19,5 +20,5 @@ public interface Mix2ZeroKnowledgeVerifier {
Crypto.RerandomizableEncryptedMessage in2,
Crypto.RerandomizableEncryptedMessage out1,
Crypto.RerandomizableEncryptedMessage out2,
Mixing.ZeroKnowledgeProof proof);
Mixing.ZeroKnowledgeProof proof) throws InvalidProtocolBufferException;
}

View File

@ -1,11 +1,15 @@
package meerkat.crypto.mixnet;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.protobuf.Crypto;
import java.util.List;
import static meerkat.protobuf.Voting.*;
import java.util.Random;
/**
* Created by talm on 25/10/15.
*/
public interface Mixer {
public List<EncryptedBallot> mix(List<EncryptedBallot> ballots);
public MixerOutput mix(List<Crypto.RerandomizableEncryptedMessage> ciphertexts,Random random)
throws InvalidProtocolBufferException;
}

View File

@ -0,0 +1,13 @@
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

@ -7,6 +7,14 @@ option java_package = "meerkat.protobuf";
import 'meerkat/crypto.proto';
import 'google/protobuf/timestamp.proto';
message BoolMsg {
bool value = 1;
}
message IntMsg {
int32 value = 1;
}
message MessageID {
// The ID of a message for unique retrieval.
// Note that it is assumed that this ID is a function of the message itself.

View File

@ -2,11 +2,16 @@ syntax = "proto3";
package meerkat;
import "meerkat/voting.proto";
option java_package = "meerkat.protobuf";
// Container for scanned data
message ScannedData {
bytes data = 1;
bytes channel = 1;
SignedEncryptedBallot signed_encrypted_ballot = 2;
}
// Container for error messages

View File

@ -6,7 +6,57 @@ option java_package = "meerkat.protobuf";
import 'meerkat/crypto.proto';
// TODO:
message ZeroKnowledgeProof {
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

@ -6,11 +6,49 @@ import 'meerkat/crypto.proto';
option java_package = "meerkat.protobuf";
// A ballot question. This is an opaque
// data type that is parsed by the UI to display
// the question.
// Type of the element data to be presented by UI
enum UIElementDataType {
TEXT = 0;
IMAGE = 1;
VOICE = 2;
}
// Type of question
enum QuestionType {
MULTIPLE_CHOICE = 0;
MULTIPLE_SELECTION = 1;
ORDER = 2;
}
// An element to be presented by UI
message UIElement {
UIElementDataType type = 1;
bytes data = 2;
}
// A question in the ballot
// is_mandatory determines whether the question may be skipped with no answer
// description might hold information/guidlines for the voter
message BallotQuestion {
bytes data = 1;
bool is_mandatory = 1;
UIElement question = 2;
UIElement description = 3;
repeated UIElement answer = 4;
}
message QuestionCluster {
UIElement cluster_description = 1;
repeated int32 question_index = 2;
}
message Channel {
UIElement channel_description = 1;
repeated int32 cluster_index = 2;
}
// An answer to a specific ballot question.
@ -22,17 +60,22 @@ message BallotAnswer {
}
message PlaintextBallot {
uint64 serialNumber = 1; // Ballot serial number
repeated BallotAnswer answers = 2;
uint64 serial_number = 1; // Ballot serial number
bytes channel_identifier = 2;
repeated BallotAnswer answers = 3;
}
message EncryptedBallot {
uint64 serialNumber = 1; // Ballot serial number
uint64 serial_number = 1; // Ballot serial number
RerandomizableEncryptedMessage data = 2;
}
message SignedEncryptedBallot {
EncryptedBallot encrypted_ballot = 1;
Signature signature = 2;
}
message BallotSecrets {
PlaintextBallot plaintext_ballot = 1;
@ -45,6 +88,10 @@ message BoothParams {
}
message BoothSystemMessages {
map<string, UIElement> system_message = 1;
}
// A table to translate to and from compactly encoded answers
// and their human-understandable counterparts.
// This should be parsable by the UI
@ -79,12 +126,29 @@ message ElectionParams {
// How many mixers must participate for the mixing to be considered valid
uint32 mixerThreshold = 5;
// Candidate list (or other question format)
repeated BallotQuestion questions = 6;
// questions to first indicate the voter's channel
repeated BallotQuestion channel_choice_questions = 6;
// Translation table between answers and plaintext encoding
BallotAnswerTranslationTable answerTranslationTable = 7;
// translating the channel-choice answers to the voter's channel
SimpleCategoriesSelectionData selection_data = 7;
// Candidate list (or other question format)
repeated BallotQuestion race_questions = 8;
// Data required in order to access the Bulletin Board Servers
BulletinBoardClientParams bulletinBoardClientParams = 8;
BulletinBoardClientParams bulletinBoardClientParams = 9;
}
message Category {
repeated uint32 questionIndex = 1;
}
message CategoryChooser {
repeated Category category = 1;
}
message SimpleCategoriesSelectionData {
Category shared_defaults = 1;
repeated CategoryChooser categoryChooser = 2;
}

View File

@ -52,7 +52,7 @@ public class ECElGamalUtils {
.setSubjectPublicKeyInfo(ByteString.copyFrom(javaPk.getEncoded())).build();
return serializedPk;
} catch (NoSuchAlgorithmException|InvalidKeySpecException e) {
} catch (Exception e) {
logger.error("Should never happen!", e);
throw new RuntimeException("Error converting public key!", e);
}
@ -80,7 +80,7 @@ public class ECElGamalUtils {
try {
java.lang.reflect.Method newBuilder = plaintextMessageType.getMethod("newBuilder");
GeneratedMessage.Builder<?> builder = (GeneratedMessage.Builder<?>) newBuilder.invoke(plaintextMessageType);
Message.Builder builder = (Message.Builder) newBuilder.invoke(plaintextMessageType);
builder.mergeDelimitedFrom(in);
return plaintextMessageType.cast(builder.build());
} catch (Exception e) {

208
mixer/build.gradle Normal file
View File

@ -0,0 +1,208 @@
plugins {
id "us.kirchmeier.capsule" version "1.0.1"
id 'com.google.protobuf' version '0.7.0'
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'maven-publish'
// Uncomment the lines below to define an application
// (this will also allow you to build a "fatCapsule" which includes
// the entire application, including all dependencies in a single jar)
//apply plugin: 'application'
//mainClassName='your.main.ApplicationClass'
// Is this a snapshot version?
ext { isSnapshot = false }
ext {
groupId = 'org.factcenter.meerkat'
nexusRepository = "https://cs.idc.ac.il/nexus/content/groups/${isSnapshot ? 'unstable' : 'public'}/"
// Credentials for IDC nexus repositories (needed only for using unstable repositories and publishing)
// Should be set in ${HOME}/.gradle/gradle.properties
nexusUser = project.hasProperty('nexusUser') ? project.property('nexusUser') : ""
nexusPassword = project.hasProperty('nexusPassword') ? project.property('nexusPassword') : ""
}
description = "TODO: Add a description"
// Your project version
version = "0.0"
version += "${isSnapshot ? '-SNAPSHOT' : ''}"
dependencies {
// Meerkat common
compile project(':meerkat-common')
// Logging
compile 'org.slf4j:slf4j-api:1.7.7'
runtime 'ch.qos.logback:logback-classic:1.1.2'
runtime 'ch.qos.logback:logback-core:1.1.2'
// Google protobufs
compile 'com.google.protobuf:protobuf-java:3.+'
// Crypto
compile 'org.factcenter.qilin:qilin:1.2.+'
testCompile 'junit:junit:4.+'
runtime 'org.codehaus.groovy:groovy:2.4.+'
}
/*==== You probably don't have to edit below this line =======*/
// The run task added by the application plugin
// is also of type JavaExec.
tasks.withType(JavaExec) {
// Assign all Java system properties from
// the command line to the JavaExec task.
systemProperties System.properties
}
protobuf {
// Configure the protoc executable
protoc {
// Download from repositories
artifact = 'com.google.protobuf:protoc:3.+'
}
}
idea {
module {
project.sourceSets.each { sourceSet ->
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
println "Adding $srcDir"
// add protobuf generated sources to generated source dir.
if ("test".equals(sourceSet.name)) {
testSourceDirs += file(srcDir)
} else {
sourceDirs += file(srcDir)
}
generatedSourceDirs += file(srcDir)
}
// Don't exclude build directory
excludeDirs -= file(buildDir)
}
}
/*===================================
* "Fat" Build targets
*===================================*/
if (project.hasProperty('mainClassName') && (mainClassName != null)) {
task mavenCapsule(type: MavenCapsule) {
description = "Generate a capsule jar that automatically downloads and caches dependencies when run."
applicationClass mainClassName
destinationDir = buildDir
}
task fatCapsule(type: FatCapsule) {
description = "Generate a single capsule jar containing everything. Use -Pfatmain=... to override main class"
destinationDir = buildDir
def fatMain = hasProperty('fatmain') ? fatmain : mainClassName
applicationClass fatMain
def testJar = hasProperty('test')
if (hasProperty('fatmain')) {
appendix = "fat-${fatMain}"
} else {
appendix = "fat"
}
if (testJar) {
from sourceSets.test.output
}
}
}
/*===================================
* Repositories
*===================================*/
repositories {
// Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
maven {
url nexusRepository
if (isSnapshot) {
credentials { username
password
username nexusUser
password nexusPassword
}
}
}
// Use local maven repository
mavenLocal()
// Use 'maven central' for other dependencies.
mavenCentral()
}
task "info" << {
println "Project: ${project.name}"
println "Description: ${project.description}"
println "--------------------------"
println "GroupId: $groupId"
println "Version: $version (${isSnapshot ? 'snapshot' : 'release'})"
println ""
}
info.description 'Print some information about project parameters'
/*===================================
* Publishing
*===================================*/
publishing {
publications {
mavenJava(MavenPublication) {
groupId project.groupId
pom.withXml {
asNode().appendNode('description', project.description)
}
from project.components.java
}
}
repositories {
maven {
url "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
credentials { username
password
username nexusUser
password nexusPassword
}
}
}
}

View File

@ -0,0 +1,118 @@
package meerkat.mixer.main;
import com.google.protobuf.ByteString;
import meerkat.crypto.mixnet.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
*/
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
* @param mixerOutput
* @return meerkat.mixer.mixing output as list of batch data
*/
public List<BulletinBoardAPI.BatchChunk> MixerOutput2BatchChunk(MixerOutput mixerOutput) {
List<BulletinBoardAPI.BatchChunk> result = new ArrayList<BulletinBoardAPI.BatchChunk>();
result.add(BulletinBoardAPI.BatchChunk.newBuilder()
.setData(Integer2ByteString(n))
.build());
for (Mixing.ZeroKnowledgeProof[] zkpLayer : mixerOutput.getProofs()) {
for (Mixing.ZeroKnowledgeProof zkp : zkpLayer) {
result.add(BulletinBoardAPI.BatchChunk.newBuilder()
.setData(zkp.toByteString())
.build());
}
}
for (Crypto.RerandomizableEncryptedMessage[] encryptionLayer : mixerOutput.getEncryptedMessages()) {
for (Crypto.RerandomizableEncryptedMessage encryption : encryptionLayer) {
result.add(BulletinBoardAPI.BatchChunk.newBuilder()
.setData(encryption.toByteString())
.build());
}
}
return result;
}
/**
* convert batch data list to meerkat.mixer.mixing output
* @param batchChunkList
* @return batch data list as MixerOutput
* @throws Exception
*/
public MixerOutput BatchChunkList2MixerOutput
(List<BulletinBoardAPI.BatchChunk> batchChunkList) throws Exception {
if (n != ByteString2Integer(batchChunkList.remove(0).getData())){
throw new Exception();
}
int nDiv2 = n >>1;
Mixing.ZeroKnowledgeProof[][] proofs = new Mixing.ZeroKnowledgeProof[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());
}
}
Crypto.RerandomizableEncryptedMessage[][] encryptions
= new Crypto.RerandomizableEncryptedMessage[layers + 1][n];
for (int layer = 0; layer < layers + 1; layer++)
{
for (int encryptionIndex = 0 ; encryptionIndex < n ; encryptionIndex ++)
{
encryptions[layer][encryptionIndex] = Crypto.RerandomizableEncryptedMessage
.parseFrom(batchChunkList.remove(0).getData());
}
}
return new meerkat.mixer.mixing.MixerOutput(n,layers,proofs,encryptions);
}
}

View File

@ -0,0 +1,104 @@
package meerkat.mixer.main;
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier;
import meerkat.crypto.mixnet.MixerOutput;
import meerkat.protobuf.Crypto;
import meerkat.mixer.necessary.AsyncBulletinBoardClient;
import meerkat.mixer.necessary.CompleteBatch;
import meerkat.mixer.verifier.VerifyTable;
import java.util.Arrays;
import java.util.List;
/**
* Created by Tzlil on 12/17/2015.
* implements AsyncBulletinBoardClient.ClientCallback<CompleteBatch>
*/
public class BatchHandler implements AsyncBulletinBoardClient.ClientCallback<CompleteBatch> {
private MixerOutput mixerOutput;
private boolean msgReceived;
private Throwable t;
private CompleteBatch msg;
private final int n, layers;
private final Mix2ZeroKnowledgeVerifier verifier;
/**
* constructor
* @param n
* @param layers
* @param verifier
*/
public BatchHandler(int n, int layers, Mix2ZeroKnowledgeVerifier verifier) {
this.mixerOutput = null;
this.n = n;
this.layers = layers;
this.msgReceived = false;
this.t = null;
this.verifier = verifier;
}
@Override
public void handleCallback(CompleteBatch msg) {
this.msg = msg;
synchronized (this) {
this.msgReceived = true;
notifyAll();
}
}
@Override
public void handleFailure(Throwable t) {
this.t = t;
synchronized (this) {
this.msgReceived = true;
notifyAll();
}
}
/**
* return true iff msg was received
* @return msgReceived
*/
public boolean isMsgReceived() {
return msgReceived;
}
/**
* convert batch data to meerkat.mixer.mixing output
* @throws Exception
*/
private void convertMessage() throws Exception {
BatchConverter batchConverter = new BatchConverter(n,layers);
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
* @throws Exception
*/
public boolean verifyTable() throws Exception {
if (mixerOutput == null) {
convertMessage();
}
return VerifyTable.verifyTable(verifier, n, mixerOutput);
}
/**
* extract input for meerkat.mixer.mixing 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 {
if (t != null) {
throw t;
}
if(!verifyTable()){
throw new Exception("in valid table");
}
return Arrays.asList(mixerOutput.getEncryptedMessages()[layers]);//there are layers + 1
}
}

View File

@ -0,0 +1,107 @@
package meerkat.mixer.main;
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeVerifier;
import meerkat.crypto.mixnet.Mixer;
import meerkat.crypto.mixnet.MixerOutput;
import meerkat.protobuf.BulletinBoardAPI;
import meerkat.protobuf.Crypto;
import meerkat.mixer.mixing.MixNetwork;
import meerkat.mixer.necessary.AsyncBulletinBoardClient;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by Tzlil on 12/17/2015.
* this class define all the operation meerkat.mixer.mixing 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
*/
public class MainMixing {
private final Mixer mixer;
private final Mix2ZeroKnowledgeVerifier verifier;
private final int n, layers;
private final AsyncBulletinBoardClient asyncBulletinBoardClient;
private final byte[] id;
/**
* constructor
* @param mixer
* @param verifier
* @param n
* @param asyncBulletinBoardClient
* @param id
*/
public MainMixing(Mixer mixer, Mix2ZeroKnowledgeVerifier verifier, int n
, AsyncBulletinBoardClient asyncBulletinBoardClient, byte[] id) {
this.mixer = mixer;
this.verifier = verifier;
this.n = n;
this.layers = MixNetwork.numberOfLayers(n);
this.asyncBulletinBoardClient = asyncBulletinBoardClient;
this.id = id;
}
/**
*
* @param prevBatchIds
* @param batchId
* @param random
* @param callback
* @throws Throwable
*/
public void main(List<Integer> prevBatchIds, int batchId, 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);
asyncBulletinBoardClient.readBatch(id, prevBatchId,currentBatchHandler);
batchHandlers.add(currentBatchHandler);
}
// check all handlers messages were received
for (BatchHandler batchHandler : batchHandlers) {
synchronized (batchHandler){
if(!batchHandler.isMsgReceived())
batchHandler.wait();
}
}
// assert all handlers succeeded
for (BatchHandler batchHandler : batchHandlers) {
if(!batchHandler.verifyTable()){
throw new Exception("invalid input");
}
}
BatchHandler lastBatchHandler = batchHandlers.get(batchHandlers.size() - 1);
mixerInput = lastBatchHandler.getInputForMixer();
MixerOutput mixerOutput = mixer.mix(mixerInput,random);
updateBB(mixerOutput, batchId, callback);
}
/**
* send meerkat.mixer.mixing output to BB
* @param mixerOutput
* @param batchId
* @param callback
*/
private void updateBB(MixerOutput mixerOutput
, int batchId, AsyncBulletinBoardClient.ClientCallback<?> callback) {
BatchConverter batchConverter = new BatchConverter(n,layers);
List<BulletinBoardAPI.BatchChunk> batchChunkList = batchConverter.MixerOutput2BatchChunk(mixerOutput);
asyncBulletinBoardClient.postBatch(id, batchId, batchChunkList, callback);
}
}

View File

@ -0,0 +1,214 @@
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

@ -0,0 +1,193 @@
package meerkat.mixer.mixing;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.Encryption;
import meerkat.crypto.mixnet.Mix2ZeroKnowledgeProver;
import meerkat.crypto.mixnet.MixerOutput;
import meerkat.protobuf.Crypto.EncryptionRandomness;
import meerkat.protobuf.Crypto.RerandomizableEncryptedMessage;
import meerkat.protobuf.Mixing.ZeroKnowledgeProof;
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
* 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 {
private final Mix2ZeroKnowledgeProver prover;
private final Encryption encryptor;
/**
* constructor
* @param prover
* @param encryptor
*/
public Mixer(Mix2ZeroKnowledgeProver prover, Encryption encryptor) {
this.prover = prover;
this.encryptor = encryptor;
}
/**
* return True iff n == 2 ^ k
* @param n
* @return
*/
public static boolean isPowerOfTwo(int n){
return (n & (n - 1)) == 0;
}
/**
* initialize encryption table of size (layers + 1)* n
* @param n number of votes
* @param layers
* @param ciphertexts encrypted votes
* @return an initialized encryption table s.t first layer == given encrypted votes
*/
private RerandomizableEncryptedMessage[][] initializeEncryptionTable(int n,int layers,List<RerandomizableEncryptedMessage> ciphertexts){
// set first level of encryption
RerandomizableEncryptedMessage[][] encryptionTable = new RerandomizableEncryptedMessage[layers + 1][n];
for (int j = 0; j < n; j++) {
encryptionTable[0][j] = ciphertexts.get(j);
}
return encryptionTable;
}
/**
* generate randomness for all rerandomize operations
* @param n number of votes
* @param layers
* @return an initialized randomness table of size layers * n
*/
private EncryptionRandomness[][] generateRandomnessesForRerandomize(int n,int layers,Random random){
EncryptionRandomness[][] randomnesses = new EncryptionRandomness[layers][n];
for (int layer = 0; layer < layers; layer++)
{
for (int i = 0; i < n; i++) {
randomnesses[layer][i] = encryptor.generateRandomness(random);;
}
}
return randomnesses;
}
/**
* generate new random mix network
* @param n number of votes
* @param random
* @return new random mix network
*/
private MixNetwork generateMixNetwork(int n,Random random){
return new MixNetwork(new RandomPermutation(n,random));
}
/**
* fills the encryption table with rerandomize encrypted votes.
* @param layers
* @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;
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];
if (!sw.value) {
encryptionTable[layer + 1][index1] = encryptor.rerandomize(e1, r1);
encryptionTable[layer + 1][index2] = encryptor.rerandomize(e2, r2);
} else {
encryptionTable[layer + 1][index1] = encryptor.rerandomize(e2, r2);
encryptionTable[layer + 1][index2] = encryptor.rerandomize(e1, r1);
}
}
}
}
/**
* 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
, RerandomizableEncryptedMessage[][] encryptionTable
, EncryptionRandomness[][] randomnesses) throws InvalidProtocolBufferException {
Switch[] switchesLayer;
int index1,index2;
int switchIndex = 0;
int nDiv2 = n >> 1;
ZeroKnowledgeProof[][] proofsTable = new ZeroKnowledgeProof[layers][nDiv2];
RerandomizableEncryptedMessage e1,e2;
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];
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);
switchIndex = (switchIndex + 1) % nDiv2;
}
}
return proofsTable;
}
/**
* mix given encrypted votes using random
* @param ciphertexts encrypted votes
* @param random
* @return meerkat.mixer.mixing result
* @throws InvalidProtocolBufferException
*/
public MixerOutput mix(List<RerandomizableEncryptedMessage> ciphertexts,Random random) throws InvalidProtocolBufferException {
int n = ciphertexts.size();
assert (n > 1 && isPowerOfTwo(n));
int layers = MixNetwork.numberOfLayers(n); // layers = 2logn -1
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);
}
}

View File

@ -0,0 +1,130 @@
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

@ -0,0 +1,45 @@
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

@ -0,0 +1,36 @@
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,80 @@
package meerkat.mixer.necessary;
import meerkat.protobuf.BulletinBoardAPI.*;
import java.util.List;
/**
* Created by Arbel Deutsch Peled on 14-Dec-15.
*
* ToDo: this should be replaced by AsyncBulletinBoardClient at BB project
*/
public interface AsyncBulletinBoardClient extends BulletinBoardClient {
public interface ClientCallback<T> {
void handleCallback(T msg);
void handleFailure(Throwable t);
}
public interface MessageHandler {
void handleNewMessages(List<BulletinBoardMessage> messageList);
}
/**
* Post a message to the bulletin board in an asynchronous manner
* @param msg is the message to be posted
* @param callback is a class containing methods to handle the result of the operation
* @return a unique message ID for the message, that can be later used to retrieve the batch
*/
public MessageID postMessage(BulletinBoardMessage msg, ClientCallback<?> callback);
/**
* This method allows for sending large messages as a batch to the bulletin board
* @param signerId is the canonical form for the ID of the sender of this batch
* @param batchId is a unique (per signer) ID for this batch
* @param batchChunkList is the (canonically ordered) list of data comprising the batch message
* @param startPosition is the location (in the batch) of the first entry in batchChunkList (optionally used to continue interrupted post operations)
* @param callback is a callback function class for handling results of the operation
* @return a unique message ID for the entire message, that can be later used to retrieve the batch
*/
public MessageID postBatch(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, int startPosition, ClientCallback<?> callback);
/**
* Overloading of the postBatch method in which startPosition is set to the default value 0
*/
public MessageID postBatch(byte[] signerId, int batchId, List<BatchChunk> batchChunkList, ClientCallback<?> callback);
/**
* Check how "safe" a given message is in an asynchronous manner
* The result of the computation is a rank between 0.0 and 1.0 indicating the fraction of servers containing the message
* @param id is the unique message identifier for retrieval
* @param callback is a callback function class for handling results of the operation
*/
public void getRedundancy(MessageID id, ClientCallback<Float> callback);
/**
* Read all messages posted matching the given filter in an asynchronous manner
* Note that if messages haven't been "fully posted", this might return a different
* set of messages in different calls. However, messages that are fully posted
* are guaranteed to be included.
* @param filterList return only messages that match the filters (null means no filtering).
* @param callback is a callback function class for handling results of the operation
*/
public void readMessages(MessageFilterList filterList, ClientCallback<List<BulletinBoardMessage>> callback);
/**
* Read a given batch message from the bulletin board
* @param signerId is the ID of the signer (sender) of the batch message
* @param batchId is the unique (per signer) ID of the batch
* @param callback is a callback class for handling the result of the operation
*/
public void readBatch(byte[] signerId, int batchId, ClientCallback<CompleteBatch> callback);
/**
* Subscribes to a notifier that will return any new messages on the server that match the given filters
* @param filterList defines the set of filters for message retrieval
* @param messageHandler defines the handler for new messages received
*/
public void subscribe(MessageFilterList filterList, MessageHandler messageHandler);
}

View File

@ -0,0 +1,61 @@
package meerkat.mixer.necessary;
import meerkat.comm.CommunicationException;
import meerkat.protobuf.Voting.BulletinBoardClientParams;
import java.util.List;
import static meerkat.protobuf.BulletinBoardAPI.*;
/**
* Created by talm on 24/10/15.
*
* ToDo: this should be replaced by BulletinBoardClient at BB project
*/
public interface BulletinBoardClient {
interface ClientCallback<T> {
void handleCallback(T msg);
void handleFailure(Throwable t);
}
/**
* Initialize the client to use some specified servers
* @param clientParams contains the parameters required for the client setup
*/
void init(BulletinBoardClientParams clientParams);
/**
* Post a message to the bulletin board in a synchronous manner
* @param msg is the message to be posted
* @return a unique message ID for the message, that can be later used to retrieve the batch
* @throws CommunicationException
*/
MessageID postMessage(BulletinBoardMessage msg) throws CommunicationException;
/**
* Check how "safe" a given message is in a synchronous manner
* @param id is the unique message identifier for retrieval
* @return a normalized "redundancy score" from 0 (local only) to 1 (fully published)
*/
float getRedundancy(MessageID id);
/**
* Read all messages posted matching the given filter in a synchronous manner
* Note that if messages haven't been "fully posted", this might return a different
* set of messages in different calls. However, messages that are fully posted
* are guaranteed to be included.
* @param filterList return only messages that match the filters (null means no filtering).
* @return the list of messages
*/
List<BulletinBoardMessage> readMessages(MessageFilterList filterList);
/**
* Closes all connections, if any.
* This is msgRecived in a synchronous (blocking) way.
*/
void close();
}

View File

@ -0,0 +1,70 @@
package meerkat.mixer.necessary;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.*;
import java.util.LinkedList;
import java.util.List;
/**
* Created by Arbel Deutsch Peled on 14-Dec-15.
*
* A data structure for holding a complete batch message along with its signature
*
* ToDo: this should be replaced by CompleteBatch at BB project
*/
public class CompleteBatch {
private BeginBatchMessage beginBatchMessage;
private List<BatchChunk> batchChunkList;
private Signature signature;
public CompleteBatch() {
batchChunkList = new LinkedList<BatchChunk>();
}
public CompleteBatch(BeginBatchMessage newBeginBatchMessage) {
this();
beginBatchMessage = newBeginBatchMessage;
}
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchChunk> newChunkList) {
this(newBeginBatchMessage);
appendBatchChunk(newChunkList);
}
public CompleteBatch(BeginBatchMessage newBeginBatchMessage, List<BatchChunk> newChunkList, Signature newSignature) {
this(newBeginBatchMessage, newChunkList);
signature = newSignature;
}
public BeginBatchMessage getBeginBatchMessage() {
return beginBatchMessage;
}
public List<BatchChunk> getBatchChunkList() {
return batchChunkList;
}
public Signature getSignature() {
return signature;
}
public void setBeginBatchMessage(BeginBatchMessage beginBatchMessage) {
this.beginBatchMessage = beginBatchMessage;
}
public void appendBatchChunk(BatchChunk newBatchChunk) {
batchChunkList.add(newBatchChunk);
}
public void appendBatchChunk(List<BatchChunk> newBatchChunkList) {
batchChunkList.addAll(newBatchChunkList);
}
public void setSignature(Signature newSignature) {
signature = newSignature;
}
}

View File

@ -0,0 +1,303 @@
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

@ -0,0 +1,216 @@
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

@ -0,0 +1,95 @@
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

@ -0,0 +1,91 @@
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

@ -0,0 +1,95 @@
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,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");
}
}

View File

@ -0,0 +1,46 @@
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

@ -0,0 +1,92 @@
package meerkat.mixer;
/**
* Created by Tzlil on 12/30/2015.
*/
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.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;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MixingTest {
ECElGamalEncryption encryptor;
ECGroup group;
Random random,randomMixer,randomProver;
RandomOracle randomOracle;
Mix2ZeroKnowledgeVerifier verifier;
Prover 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 = 8; // + 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;
}
@Test
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");
long startTime = System.currentTimeMillis();
meerkat.crypto.mixnet.MixerOutput mixerOutput = mixer.mix(mixerInput,randomMixer);
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println("start verification");
startTime = System.currentTimeMillis();
assert (VerifyTable.verifyTable(verifier,n,mixerOutput));
finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
}
}

View File

@ -0,0 +1,90 @@
package meerkat.mixer;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Voting;
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 java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/17/2016.
*/
public class RerandomizeTest {
Random rand;
ECElGamal.SK key;
ECGroup group;
ECElGamalEncryption enc;
ConcreteCrypto.ElGamalPublicKey serializedPk;
ECPoint g ;
ECPoint h ;
@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();
g = group.getGenerator();
h = enc.getElGamalPK().getPK();
}
private ECPoint convert2ECPoint(ByteString bs){
return group.decode(bs.toByteArray());
}
public void oneRerandomizeTest() throws InvalidProtocolBufferException {
Voting.PlaintextBallot msg = Utils.genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15.
Crypto.EncryptionRandomness r = enc.generateRandomness(rand);
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));
ConcreteCrypto.ElGamalCiphertext eElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(e);
ConcreteCrypto.ElGamalCiphertext eNewElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(eNew);
ECPoint expected1 = g.multiply(enc.extractRandomness(r));
ECPoint result1 = group.add(convert2ECPoint(eNewElGamal.getC1()),group.negate(convert2ECPoint(eElGamal.getC1())));
expected1.normalize();
result1.normalize();
assert (expected1.equals(result1));
ECPoint expected2 = h.multiply(enc.extractRandomness(r));
ECPoint result2 = group.add(convert2ECPoint(eNewElGamal.getC2()), group.negate(convert2ECPoint(eElGamal.getC2())));
expected2.normalize();
result2.normalize();
assert (expected2.equals(result2));
}
@Test
public void rerandomizeTest() throws InvalidProtocolBufferException {
int tests = 1000;
for (int i = 0; i < tests; i ++){
System.out.println("re-randomize test #" + i);
oneRerandomizeTest();
}
}
}

View File

@ -0,0 +1,89 @@
package meerkat.mixer;
import org.bouncycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import org.junit.Before;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 3/20/2016.
*/
public class SimpleRerandomizeTest {
Random rand;
ECGroup group;
ECPoint g ;
ECPoint h ;
BigInteger sk;
@Before
public void setup() throws Exception {
rand = new Random();
group = new ECGroup("secp256k1");
g = group.getGenerator();
sk = random();
h = g.multiply(sk);
}
public BigInteger random(){
return new BigInteger(256,rand).mod(group.orderUpperBound());
}
public ECPoint randomMessage(){
return group.sample(rand);
}
public Encryption encrypt(ECPoint message,BigInteger randomness){
return new Encryption(g.multiply(randomness), message.add(h.multiply(randomness)));
}
public Encryption rerandomize(Encryption encryption,BigInteger randomness){
return new Encryption(encryption.a.add(g.multiply(randomness)),encryption.b.add(h.multiply(randomness)));
}
public ECPoint decrypt(Encryption encryption){
return encryption.a.multiply(sk).negate().add(encryption.b);
}
public class Encryption{
ECPoint a,b;
Encryption(ECPoint a, ECPoint b){
this.a = a;
this.b = b;
}
}
public void oneRerandomizeTest(){
ECPoint message = randomMessage();
Encryption e = encrypt(message,random());
BigInteger r = random();
Encryption eNew = rerandomize(e,r);
assert (decrypt(e).equals(message));
assert (decrypt(eNew).equals(message));
ECPoint expected1 = g.multiply(r);
ECPoint result1 = group.add(eNew.a,e.a.negate());
expected1.normalize();
result1.normalize();
assert (expected1.equals(result1));
ECPoint expected2 = h.multiply(r);
ECPoint result2 = group.add(eNew.b,e.b.negate());
expected2.normalize();
result2.normalize();
assert (expected2.equals(result2));
}
@org.junit.Test
public void rerandomizeTest(){
int tests = 1000;
for (int i = 0; i < tests; i ++){
System.out.println("rerandomize test #" + i);
oneRerandomizeTest();
}
}
}

View File

@ -0,0 +1,103 @@
package meerkat.mixer;
import com.google.protobuf.ByteString;
import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.crypto.concrete.GlobalCryptoSetup;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Voting;
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.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();
public static Voting.PlaintextBallot genRandomBallot(int numQuestions, int numAnswers, int maxAnswer) {
Voting.PlaintextBallot.Builder ballot = Voting.PlaintextBallot.newBuilder();
ballot.setSerialNumber(random.nextInt(1000000));
for (int i = 0; i < numQuestions; ++i) {
Voting.BallotAnswer.Builder answers = ballot.addAnswersBuilder();
for (int j = 0; j < numAnswers; ++j) {
answers.addAnswer(random.nextInt(maxAnswer));
}
}
return ballot.build();
}
}

View File

@ -0,0 +1,105 @@
package meerkat.mixer;
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.protobuf.ConcreteCrypto;
import meerkat.protobuf.Crypto;
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 ;
@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);
}
private ECPoint convert2ECPoint(ByteString bs){
return group.decode(bs.toByteArray());
}
public void oneZKPTest() throws InvalidProtocolBufferException {
Voting.PlaintextBallot msg1 = Utils.genRandomBallot(2,3,16); // 2 questions with 3 answers each, in range 0-15.
Voting.PlaintextBallot msg2 = Utils.genRandomBallot(2,3,16);
Crypto.EncryptionRandomness r1 = enc.generateRandomness(rand);
Crypto.EncryptionRandomness r2 = enc.generateRandomness(rand);
Crypto.RerandomizableEncryptedMessage e1 = enc.encrypt(msg1, enc.generateRandomness(rand));
Crypto.RerandomizableEncryptedMessage e2 = enc.encrypt(msg2, enc.generateRandomness(rand));
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);
ECPoint g = group.getGenerator();
ECPoint h = enc.getElGamalPK().getPK();
ConcreteCrypto.ElGamalCiphertext e1ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(e1);
ConcreteCrypto.ElGamalCiphertext e2ElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(e2);
ConcreteCrypto.ElGamalCiphertext e1TagElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(e1New);
ConcreteCrypto.ElGamalCiphertext e2TagElGamal = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(e2New);
assertEquals (g.multiply(enc.extractRandomness(r1)),
group.add(convert2ECPoint(e1TagElGamal.getC1()),group.negate(convert2ECPoint(e1ElGamal.getC1()))));
assertEquals (h.multiply(enc.extractRandomness(r1)),
group.add(convert2ECPoint(e1TagElGamal.getC2()),group.negate(convert2ECPoint(e1ElGamal.getC2()))));
assertEquals (g.multiply(enc.extractRandomness(r2)),
group.add(convert2ECPoint(e2TagElGamal.getC1()),group.negate(convert2ECPoint(e2ElGamal.getC1()))));
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)));
}
@Test
public void zeroKnowledgeProofTest() throws InvalidProtocolBufferException {
int tests = 1000;
for (int i = 0; i < tests; i ++){
System.out.println("ZKP test #" + i);
oneZKPTest();
}
}
}

View File

@ -0,0 +1,63 @@
package profiling.BigInteger;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.mixer.Utils;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/21/2016.
*/
public class AddSub {
int tests;
BigInteger[] randoms;
BigInteger[] randoms2;
public void setup() throws Exception {
Random rand = new Random();
tests = 1 << 17;
randoms = new BigInteger[tests];
rand = new Random();
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);
ECElGamalEncryption enc = new ECElGamalEncryption();
enc.init(serializedPk);
for (int i =0 ; i < tests ; i++){
randoms[i] = new BigInteger(enc.generateRandomness(rand).getData().toByteArray());
}
randoms2 = new BigInteger[tests];
for (int i =0 ; i < tests ; i++){
randoms2[i] = new BigInteger(enc.generateRandomness(rand).getData().toByteArray());
}
}
public void AddSubProfiling() throws InvalidProtocolBufferException {
System.out.println("AddSub");
System.out.println("#" + tests + " tests");
long startTime = System.currentTimeMillis();
int i = 0;
while (i < randoms.length / 2) {
randoms[i].add(randoms2[i]);
i++;
}
while (i < randoms.length) {
randoms[i].subtract(randoms2[i]);
i++;
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of "+((double)(finishTime-startTime))/tests + " ms");
}
}

View File

@ -0,0 +1,46 @@
package profiling.BigInteger;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.mixer.Utils;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/25/2016.
*/
public class GenerateRandomness {
int tests;
ECElGamalEncryption enc;
Random rand;
public void setup() throws Exception {
rand = new Random();
ECGroup group = new ECGroup("secp256k1");
tests = 1<<18;
BigInteger sk = ECElGamal.generateSecretKey(group, rand);
ECElGamal.SK key = new ECElGamal.SK(group, sk);
ConcreteCrypto.ElGamalPublicKey serializedPk = Utils.serializePk(group, key);
enc = new ECElGamalEncryption();
enc.init(serializedPk);
}
public void GenerateRandomnessProfiling() throws InvalidProtocolBufferException {
System.out.println("GenerateRandomnessProfiling");
System.out.println("#" + tests + " tests");
long startTime = System.currentTimeMillis();
for (int i =0 ; i < tests ; i++){
enc.generateRandomness(rand).getData().toByteArray();
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of "+((double)(finishTime-startTime))/tests + " ms");
}
}

View File

@ -0,0 +1,50 @@
package profiling.BigInteger;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.mixer.Utils;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/21/2016.
*/
public class Modulo {
int tests;
BigInteger[] randoms;
BigInteger orderUpperBound;
public void setup() throws Exception {
Random rand = new Random();
ECGroup group = new ECGroup("secp256k1");
orderUpperBound = group.orderUpperBound();
tests = 1<<17;
randoms = new BigInteger[tests];
BigInteger sk = ECElGamal.generateSecretKey(group, rand);
ECElGamal.SK key = new ECElGamal.SK(group, sk);
ConcreteCrypto.ElGamalPublicKey serializedPk = Utils.serializePk(group, key);
ECElGamalEncryption enc = new ECElGamalEncryption();
enc.init(serializedPk);
for (int i =0 ; i < tests ; i++){
randoms[i] = new BigInteger(enc.generateRandomness(rand).getData().toByteArray());
}
}
public void ModuloProfiling() throws InvalidProtocolBufferException {
System.out.println("ModuloProfiling");
System.out.println("#" + tests + " tests");
long startTime = System.currentTimeMillis();
for (int i = 0 ; i < randoms.length;i++) {
randoms[i].mod(orderUpperBound);
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of "+((double)(finishTime-startTime))/tests + " ms");
}
}

View File

@ -0,0 +1,59 @@
package profiling.BigInteger;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.protobuf.ConcreteCrypto;
import meerkat.mixer.Utils;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/25/2016.
*/
public class Mul {
int tests;
BigInteger[] randoms;
BigInteger[] randoms2;
public void setup() throws Exception {
Random rand = new Random();
tests = 1 << 17;
randoms = new BigInteger[tests];
rand = new Random();
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);
ECElGamalEncryption enc = new ECElGamalEncryption();
enc.init(serializedPk);
for (int i =0 ; i < tests ; i++){
randoms[i] = new BigInteger(enc.generateRandomness(rand).getData().toByteArray());
}
randoms2 = new BigInteger[tests];
for (int i =0 ; i < tests ; i++){
randoms2[i] = new BigInteger(enc.generateRandomness(rand).getData().toByteArray());
}
}
public void MulProfiling() throws InvalidProtocolBufferException {
System.out.println("Mul");
System.out.println("#" + tests + " tests");
long startTime = System.currentTimeMillis();
int i = 0;
while (i < randoms.length) {
randoms[i].multiply(randoms2[i]);
i++;
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of "+((double)(finishTime-startTime))/tests + " ms");
}
}

View File

@ -0,0 +1,46 @@
package profiling.BigInteger;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.protobuf.Voting;
import org.factcenter.qilin.primitives.RandomOracle;
import org.factcenter.qilin.primitives.concrete.DigestOracle;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/25/2016.
*/
public class SHA256 {
final int LENGTH = 420;
int tests;
BigInteger[] randoms;
RandomOracle randomOracle;
public void setup() throws Exception {
Random rand = new Random();
randomOracle = new DigestOracle();
tests = 1<<15;
Voting.PlaintextBallot msg;
randoms = new BigInteger[tests];
byte[] arr = new byte[LENGTH];
for (int i =0 ; i < tests ; i++){
rand.nextBytes(arr);
randoms[i] = new BigInteger(arr);
}
}
public void SHA256Profiling() throws InvalidProtocolBufferException {
System.out.println("SHA256Profiling");
System.out.println("#" + tests + " tests");
long startTime = System.currentTimeMillis();
for (int i = 0 ; i < randoms.length;i++) {
byte[] arr = randoms[i].toByteArray();
new BigInteger(this.randomOracle.hash(arr, arr.length));
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of "+((double)(finishTime-startTime))/tests + " ms");
}
}

View File

@ -0,0 +1,68 @@
package profiling.Convert;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
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;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/25/2016.
*/
public class ByteString2ECPoint {
Random rand;
ECElGamal.SK key;
ECGroup group;
ECElGamalEncryption enc;
ConcreteCrypto.ElGamalPublicKey serializedPk;
int tests;
ConcreteCrypto.ElGamalCiphertext[] encryptedMessage;
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);
tests = 1024 * 19;
encryptedMessage = new ConcreteCrypto.ElGamalCiphertext[tests];
Voting.PlaintextBallot msg;
for (int i = 0; i < tests; i ++){
msg = Utils.genRandomBallot(2,3,16);
encryptedMessage[i] = ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext
(enc.encrypt(msg, enc.generateRandomness(rand)));
}
}
private ECPoint convert2ECPoint(ByteString bs){
return group.decode(bs.toByteArray());
}
public void ByteString2ECPointProfiling() throws InvalidProtocolBufferException {
System.out.println("ByteString2ECPointProfiling");
System.out.println("#"+ tests + " tests");
System.out.println("start tests operations");
long startTime = System.currentTimeMillis();
for (int i = 0; i < tests; i ++){
convert2ECPoint(encryptedMessage[i].getC1());
convert2ECPoint(encryptedMessage[i].getC2());
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of "+((double)(finishTime-startTime))/ (2 * tests) + " ms");
}
}

View File

@ -0,0 +1,62 @@
package profiling.Convert;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
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;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/25/2016.
*/
public class RerandomizableEncryptedMessage2ElGamalCiphertext {
Random rand;
ECElGamal.SK key;
ECGroup group;
ECElGamalEncryption enc;
ConcreteCrypto.ElGamalPublicKey serializedPk;
int tests;
Crypto.RerandomizableEncryptedMessage[] encryptedMessage;
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);
tests = 1024 * 18;
encryptedMessage = new Crypto.RerandomizableEncryptedMessage[tests];
Voting.PlaintextBallot msg;
for (int i = 0; i < tests; i ++){
msg = Utils.genRandomBallot(2,3,16);
encryptedMessage[i] = enc.encrypt(msg, enc.generateRandomness(rand));
}
}
public void RerandomizableEncryptedMessage2ElGamalCiphertext() throws InvalidProtocolBufferException {
System.out.println("RerandomizableEncryptedMessage2ElGamalCiphertext");
System.out.println("#"+ tests + " tests");
System.out.println("start tests operations");
long startTime = System.currentTimeMillis();
for (int i = 0; i < tests; i ++){
ECElGamalEncryption.RerandomizableEncryptedMessage2ElGamalCiphertext(encryptedMessage[i]);
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of "+((double)(finishTime-startTime))/ tests + " ms");
}
}

View File

@ -0,0 +1,61 @@
package profiling.ECGroup;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.mixer.Utils;
import org.bouncycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by Tzlil on 1/20/2016.
*/
public class Add {
ECElGamalEncryption encryptor;
ECGroup group;
Random random;
private int n;
List<ECPoint> members1;
List<ECPoint> members2;
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))));
// generate n;
int sqrtn = 128;
n = sqrtn*sqrtn;
members1 = new ArrayList<ECPoint>(sqrtn);
members2 = new ArrayList<ECPoint>(sqrtn);
for (int i = 0 ; i < sqrtn; i ++){
members1.add(group.sample(random));
members2.add(group.sample(random));
}
}
public void addProfiling() throws InvalidProtocolBufferException {
System.out.println("AddSub");
System.out.println("n is : " + n);
System.out.println("start n operations");
long startTime = System.currentTimeMillis();
for (ECPoint member1: members1) {
for (ECPoint member2: members2) {
group.add(member1,member2);
}
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of"+((double)(finishTime-startTime))/(n)+ " ms");
}
}

View File

@ -0,0 +1,57 @@
package profiling.ECGroup;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.mixer.Utils;
import org.bouncycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by Tzlil on 1/20/2016.
*/
public class Encode {
ECElGamalEncryption encryptor;
ECGroup group;
Random random;
private int n;
List<ECPoint> members;
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))));
// generate n;
int sqrtn = 128;
n = sqrtn*sqrtn;
members = new ArrayList<ECPoint>(n);
for (int i = 0 ; i < n; i ++){
members.add(group.sample(random));
}
}
public void encodeProfiling() throws InvalidProtocolBufferException {
System.out.println("Encode");
System.out.println("n is : " + n);
System.out.println("start n operations");
long startTime = System.currentTimeMillis();
for (ECPoint member: members) {
group.encode(member);
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of"+((double)(finishTime-startTime))/(n)+ " ms");
}
}

View File

@ -0,0 +1,69 @@
package profiling.ECGroup;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.mixer.Utils;
import org.bouncycastle.math.ec.ECPoint;
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.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by Tzlil on 1/19/2016.
*/
public class Mul {
ECElGamalEncryption encryptor;
ECGroup group;
Random random;
private int n;
List<ECPoint> members;
List<BigInteger> randomnesses;
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))));
// generate n
int sqrtn = 128;
n = sqrtn*sqrtn;
members = new ArrayList<ECPoint>(sqrtn);
randomnesses = new ArrayList<BigInteger>(sqrtn);
byte[] arr = new byte[256];
for (int i = 0 ; i < sqrtn; i ++){
members.add(group.sample(random));
random.nextBytes(arr);
randomnesses.add(new BigInteger(arr));
}
}
public void mulProfiling() throws InvalidProtocolBufferException {
System.out.println("Multiply");
System.out.println("n is : " + n);
System.out.println("start n operations");
long startTime = System.currentTimeMillis();
for(ECPoint member:members) {
for (BigInteger rand : randomnesses) {
group.multiply(member, rand);
}
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of"+((double)(finishTime-startTime))/(n)+ " ms");
}
}

View File

@ -0,0 +1,56 @@
package profiling.ECGroup;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
import meerkat.mixer.Utils;
import org.bouncycastle.math.ec.ECPoint;
import org.factcenter.qilin.primitives.concrete.ECElGamal;
import org.factcenter.qilin.primitives.concrete.ECGroup;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Created by Tzlil on 1/20/2016.
*/
public class Negate {
ECElGamalEncryption encryptor;
ECGroup group;
Random random;
private int n;
List<ECPoint> members;
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))));
// generate n;
int sqrtn = 128;
n = sqrtn*sqrtn;
members = new ArrayList<ECPoint>(n);
for (int i = 0 ; i < n; i ++){
members.add(group.sample(random));
}
}
public void negProfiling() throws InvalidProtocolBufferException {
System.out.println("Neg");
System.out.println("n is : " + n);
System.out.println("start n operations");
long startTime = System.currentTimeMillis();
for (ECPoint member: members) {
group.negate(member);
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of"+((double)(finishTime-startTime))/(n)+ " ms");
}
}

View File

@ -0,0 +1,67 @@
package profiling;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.crypto.concrete.ECElGamalEncryption;
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;
import java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/20/2016.
*/
public class Rerandomize {
Random rand;
ECElGamal.SK key;
ECGroup group;
ECElGamalEncryption enc;
ConcreteCrypto.ElGamalPublicKey serializedPk;
int n;
Crypto.EncryptionRandomness[] randomnesses;
Crypto.RerandomizableEncryptedMessage[] encryptedMessage;
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);
int LogVotes = 10;
int layers = 2*LogVotes - 1;
n = layers * (1<<LogVotes) / 2;
randomnesses = new Crypto.EncryptionRandomness[n];
encryptedMessage = new Crypto.RerandomizableEncryptedMessage[n];
Voting.PlaintextBallot msg;
for (int i = 0; i < n ; i ++){
msg = Utils.genRandomBallot(2,3,16);
randomnesses[i] = enc.generateRandomness(rand);
encryptedMessage[i] = enc.encrypt(msg, enc.generateRandomness(rand));
}
}
public void RerandomizeProfiling() throws InvalidProtocolBufferException {
System.out.println("Rerandomiz");
System.out.println("n is : " + n);
System.out.println("start n operations");
long startTime = System.currentTimeMillis();
for (int i = 0; i < n ; i ++){
enc.rerandomize(encryptedMessage[i],randomnesses[i]);
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of"+((double)(finishTime-startTime))/n + " ms");
}
}

View File

@ -0,0 +1,85 @@
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.protobuf.ConcreteCrypto;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Voting;
import meerkat.mixer.Utils;
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 java.math.BigInteger;
import java.util.Random;
/**
* Created by Tzlil on 1/20/2016.
*/
public class ZeroKnowledgeProof {
Random rand;
ECElGamal.SK key;
ECGroup group;
ECElGamalEncryption enc;
ConcreteCrypto.ElGamalPublicKey serializedPk;
Mix2ZeroKnowledgeProver prover ;
int n;
Crypto.EncryptionRandomness[] randomnesses;
Crypto.RerandomizableEncryptedMessage[] encryptedMessage;
Crypto.RerandomizableEncryptedMessage[] reencryptedMessage;
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();
prover = new Prover(new Random(),enc,randomOracle);
int LogVotes = 12;
int layers = 2*LogVotes - 1;
n = layers * (1<<LogVotes) / 2;
randomnesses = new Crypto.EncryptionRandomness[n*2];
encryptedMessage = new Crypto.RerandomizableEncryptedMessage[n*2];
reencryptedMessage = new Crypto.RerandomizableEncryptedMessage[n*2];
Voting.PlaintextBallot msg;
for (int i = 0; i < n*2 ; i ++){
msg = Utils.genRandomBallot(2,3,16);
randomnesses[i] = enc.generateRandomness(rand);
encryptedMessage[i] = enc.encrypt(msg, enc.generateRandomness(rand));
reencryptedMessage[i] = enc.rerandomize(encryptedMessage[i], randomnesses[i]);
}
}
private ECPoint convert2ECPoint(ByteString bs){
return group.decode(bs.toByteArray());
}
public void zeroKnowledgeProofTest() throws InvalidProtocolBufferException {
System.out.println("Prove");
System.out.println("n is : " + n);
System.out.println("start n proves");
long startTime = System.currentTimeMillis();
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]);
}
long finishTime = System.currentTimeMillis();
System.out.println(" that took: "+(finishTime-startTime)+ " ms");
System.out.println(" avg of"+((double)(finishTime-startTime))/n + " ms");
}
}

View File

@ -46,7 +46,7 @@ public class PollingStationWebScannerTest {
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = result.getData().equals(expectedData.getData());
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
semaphore.release();
}
@ -109,7 +109,7 @@ public class PollingStationWebScannerTest {
byte[] data = {(byte) 1, (byte) 2};
ScannedData scannedData = ScannedData.newBuilder()
.setData(ByteString.copyFrom(data))
.setChannel(ByteString.copyFrom(data))
.build();
scanner.subscribe(new ScanHandler(scannedData));

View File

@ -32,7 +32,7 @@ public class ProtobufMessageBodyReader implements MessageBodyReader<Message> {
InputStream entityStream) throws IOException, WebApplicationException {
try {
Method newBuilder = type.getMethod("newBuilder");
GeneratedMessage.Builder<?> builder = (GeneratedMessage.Builder<?>) newBuilder.invoke(type);
Message.Builder builder = (Message.Builder) newBuilder.invoke(type);
return builder.mergeFrom(entityStream).build();
} catch (Exception e) {
throw new WebApplicationException(e);

View File

@ -3,6 +3,6 @@ include 'voting-booth'
include 'bulletin-board-server'
include 'polling-station'
include 'restful-api-common'
include 'mixer'
include 'bulletin-board-client'
include 'distributed-key-generation'

11
settings.gradle.orig Normal file
View File

@ -0,0 +1,11 @@
include 'meerkat-common'
include 'voting-booth'
include 'bulletin-board-server'
include 'polling-station'
include 'restful-api-common'
<<<<<<< HEAD
include 'mixer'
=======
include 'bulletin-board-client'
>>>>>>> e8e511d9ce636a127bb33d70ebfd9b2f230c6e1d

View File

@ -1,4 +1,3 @@
plugins {
id "us.kirchmeier.capsule" version "1.0.1"
id 'com.google.protobuf' version '0.7.0'
@ -40,6 +39,15 @@ version += "${isSnapshot ? '-SNAPSHOT' : ''}"
dependencies {
// Meerkat common
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'
@ -52,6 +60,10 @@ dependencies {
testCompile 'junit:junit:4.+'
runtime 'org.codehaus.groovy:groovy:2.4.+'
// Meerkat polling station
compile project(':polling-station')
}
@ -188,6 +200,3 @@ publishing {
}
}
}

View File

@ -0,0 +1,46 @@
package meerkat.voting;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import meerkat.crypto.Encryption;
import meerkat.protobuf.Crypto.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;
/**
* Created by hai on 07/06/16.
*/
public class ToyEncryption implements Encryption {
@Override
public RerandomizableEncryptedMessage encrypt(Message plaintext, EncryptionRandomness rnd) throws IOException {
ByteString cipher = ByteString.copyFromUtf8("Encryption(")
.concat(plaintext.toByteString())
.concat(ByteString.copyFromUtf8(", Random("))
.concat(rnd.getData())
.concat(ByteString.copyFromUtf8("))"));
return RerandomizableEncryptedMessage.newBuilder()
.setData(cipher)
.build();
}
@Override
public RerandomizableEncryptedMessage rerandomize
(RerandomizableEncryptedMessage msg, EncryptionRandomness rnd) throws InvalidProtocolBufferException {
throw new UnsupportedOperationException();
}
@Override
public EncryptionRandomness generateRandomness(Random rand) {
ByteBuffer b = ByteBuffer.allocate(4);
b.putInt(rand.nextInt());
byte[] bArr = b.array();
ByteString bs = ByteString.copyFrom(bArr);
return EncryptionRandomness.newBuilder()
.setData(bs)
.build();
}
}

View File

@ -0,0 +1,90 @@
package meerkat.voting;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import meerkat.crypto.DigitalSignature;
import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.Crypto.Signature;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateException;
/**
* Created by hai on 07/06/16.
*/
public class ToySignature implements DigitalSignature {
private final ByteString signerID;
private ByteString msgByteString;
public ToySignature(String signerID) {
this.signerID = ByteString.copyFromUtf8(signerID);
}
@Override
public ByteString getSignerID() {
return signerID;
}
@Override
public void updateContent(Message msg) throws SignatureException {
msgByteString = msg.toByteString();
}
@Override
public Signature sign() throws SignatureException {
ByteString signature = ByteString.copyFromUtf8("Signature(")
.concat(msgByteString)
.concat(ByteString.copyFromUtf8(")"));
return Signature.newBuilder()
.setType(SignatureType.ECDSA)
.setData(signature)
.setSignerId(getSignerID())
.build();
}
@Override
public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
throw new UnsupportedOperationException();
}
@Override
public void clearVerificationCertificates() {
throw new UnsupportedOperationException();
}
@Override
public void updateContent(byte[] data) throws SignatureException {
throw new UnsupportedOperationException();
}
@Override
public void initVerify(Signature sig) throws CertificateException, InvalidKeyException {
throw new UnsupportedOperationException();
}
@Override
public boolean verify() {
throw new UnsupportedOperationException();
}
@Override
public KeyStore.Builder getPKCS12KeyStoreBuilder(InputStream keyStream, char[] password) throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
throw new UnsupportedOperationException();
}
@Override
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder) throws IOException, CertificateException, UnrecoverableKeyException {
throw new UnsupportedOperationException();
}
@Override
public void clearSigningKey() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,290 @@
package meerkat.voting;
import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.Encryption;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.*;
import meerkat.voting.output.*;
import meerkat.voting.storage.*;
import meerkat.voting.encryptor.*;
import meerkat.voting.ui.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
/**
* Created by hai on 26/04/16.
*/
public class VotingBoothToyRun {
public static void main(String[] args) {
try {
generateSystemMessages();
generateDemoQuestions();
}
catch (Exception e) {
return;
}
Random rand = new Random();
Encryption enc = new ToyEncryption();
DigitalSignature sig = new ToySignature("MY_SIGNER_ID");
StorageManager storageManager = new StorageManagerMockup();
SystemConsoleOutputDevice outputDevice = new SystemConsoleOutputDevice();
VBCryptoManager cryptoManager = new VBCryptoManagerImpl(rand, enc, sig);
SystemConsoleUI ui = new SystemConsoleUI ();
VotingBoothImpl controller = new VotingBoothImpl();
try {
controller.init(outputDevice, cryptoManager, ui, storageManager);
}
catch (Exception e) {
System.err.println("init failed");
return;
}
// create threads
Thread controllerThread = new Thread(controller);
controllerThread.setName("Meerkat VB-Controller Thread");
Thread uiThread = new Thread(ui);
uiThread.setName("Meerkat VB-UI Thread");
Thread outputThread = new Thread(outputDevice);
outputThread.setName("Meerkat VB-Output Thread");
uiThread.start();
controllerThread.start();
outputThread.start();
}
private static void generateDemoQuestions() throws IOException {
ElectionParams electionParams = ElectionParams.newBuilder()
.addAllRaceQuestions(generateBallotQuestions())
.addAllChannelChoiceQuestions(generateChannelChoiceQuestions())
.setSelectionData(generateSelectionData())
.build();
try {
FileOutputStream output = new FileOutputStream(StorageManagerMockup.electionParamFullFilename);
electionParams.writeTo(output);
output.close();
System.out.println("Successfully wrote election parameter protobuf to a file");
}
catch (IOException e) {
System.err.println("Could not write to the election parameter file: '" + StorageManagerMockup.electionParamFullFilename + "'.");
throw e;
}
}
private static List<BallotQuestion> generateChannelChoiceQuestions() {
ArrayList<BallotQuestion> channelChoiceQuestions = new ArrayList();
String[] ans1 = {"Red", "Blue", "Green"};
BallotQuestion ccquestion1 = generateBallotQuestion("What is your favorite color?", "Pick one answer", ans1);
channelChoiceQuestions.add(ccquestion1);
String[] ans2 = {"Yes", "No"};
BallotQuestion ccquestion2 = generateBallotQuestion("Are you a republican?", "Pick one answer", ans2);
channelChoiceQuestions.add(ccquestion2);
return channelChoiceQuestions;
}
private static List<BallotQuestion> generateBallotQuestions() {
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));
String[] answers2 = {"Miranda Kerr", "Doutzen Kroes", "Moran Atias", "Roslana Rodina", "Adriana Lima"};
allBallotQuestions.add(generateBallotQuestion("question 2: Which model do you like", "Mark as many as you want", answers2));
allBallotQuestions.add(generateBallotQuestion("question 3. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 4. Asking something...", "Pick one answer", answers1));
String[] answers5 = {"Clint Eastwood", "Ninja", "Sonic", "Tai-chi", "Diablo", "Keanu"};
allBallotQuestions.add(generateBallotQuestion("question 5: Good name for a cat", "Pick the best one", answers5));
allBallotQuestions.add(generateBallotQuestion("question 6. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 7. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 8. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 9. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 10. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 11. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 12. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 13. Asking something...", "Pick one answer", answers1));
allBallotQuestions.add(generateBallotQuestion("question 14. Asking something...", "Pick one answer", answers1));
return allBallotQuestions;
}
private static BallotQuestion generateBallotQuestion(String questionStr, String descriptionStr, String[] answers) {
UIElement question = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(stringToBytes(questionStr))
.build();
UIElement description = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(stringToBytes(descriptionStr))
.build();
BallotQuestion.Builder bqb = BallotQuestion.newBuilder();
bqb.setIsMandatory(false);
bqb.setQuestion(question);
bqb.setDescription(description);
for (String answerStr : answers) {
UIElement answer = UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(stringToBytes(answerStr))
.build();
bqb.addAnswer(answer);
}
return bqb.build();
}
private static SimpleCategoriesSelectionData generateSelectionData() {
Category sharedDefaults = Category.newBuilder()
.addQuestionIndex(0)
.addQuestionIndex(5)
.addQuestionIndex(9)
.build();
Category cat00 = Category.newBuilder()
.addQuestionIndex(1)
.addQuestionIndex(4)
.addQuestionIndex(6)
.addQuestionIndex(7)
.build();
Category cat01 = Category.newBuilder()
.addQuestionIndex(2)
.addQuestionIndex(4)
.addQuestionIndex(8)
.build();
Category cat02 = Category.newBuilder()
.addQuestionIndex(3)
.addQuestionIndex(8)
.build();
Category cat10 = Category.newBuilder()
.addQuestionIndex(10)
.addQuestionIndex(11)
.build();
Category cat11 = Category.newBuilder()
.addQuestionIndex(12)
.addQuestionIndex(13)
.build();
CategoryChooser catChooser0 = CategoryChooser.newBuilder()
.addCategory(cat00)
.addCategory(cat01)
.addCategory(cat02)
.build();
CategoryChooser catChooser1 = CategoryChooser.newBuilder()
.addCategory(cat10)
.addCategory(cat11)
.build();
return SimpleCategoriesSelectionData.newBuilder()
.setSharedDefaults(sharedDefaults)
.addCategoryChooser(catChooser0)
.addCategoryChooser(catChooser1)
.build();
}
private static ByteString stringToBytes (String s) {
return ByteString.copyFromUtf8(s);
}
private static void generateSystemMessages() throws IOException{
Map<String, UIElement> systemMessageMap = new HashMap();
systemMessageMap.put(StorageManager.WAIT_FOR_COMMIT_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while committing to ballot"))
.build());
systemMessageMap.put(StorageManager.WAIT_FOR_AUDIT_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while auditing your ballot"))
.build());
systemMessageMap.put(StorageManager.WAIT_FOR_CAST_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Please wait while finalizing your ballot for voting"))
.build());
systemMessageMap.put(StorageManager.RESTART_VOTING_BUTTON, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Restart voting"))
.build());
systemMessageMap.put(StorageManager.UNRECOGNIZED_FINALIZE_RESPONSE_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Could not understand response for Cast or Audit. Force restarting."))
.build());
systemMessageMap.put(StorageManager.UNSUCCESSFUL_CHANNEL_CHOICE_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Choice of channel was unsuccessful. Force restarting."))
.build());
systemMessageMap.put(StorageManager.OUTPUT_DEVICE_FAILURE_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Ballot output device failure. Force restarting."))
.build());
systemMessageMap.put(StorageManager.UNSUCCESSFUL_VOTING_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Voting was unsuccessful. Force restarting."))
.build());
systemMessageMap.put(StorageManager.SOMETHING_WRONG_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Something was terribly wrong. Force restarting."))
.build());
systemMessageMap.put(StorageManager.ENCRYPTION_FAILED_MESSAGE, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Encryption failed for some unknown reason."))
.build());
systemMessageMap.put(StorageManager.RETRY_BUTTON, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Retry"))
.build());
systemMessageMap.put(StorageManager.CANCEL_VOTE_BUTTON, UIElement.newBuilder()
.setType(UIElementDataType.TEXT)
.setData(ByteString.copyFromUtf8("Cancel Vote"))
.build());
BoothSystemMessages systemMessages = BoothSystemMessages.newBuilder().putAllSystemMessage(systemMessageMap).build();
try {
FileOutputStream output = new FileOutputStream(StorageManagerMockup.systemMessagesFilename);
systemMessages.writeTo(output);
output.close();
System.out.println("Successfully wrote system messages protobuf to a file");
}
catch (IOException e) {
System.err.println("Could not write to the system messages file: '" + StorageManagerMockup.systemMessagesFilename + "'.");
throw e;
}
}
}

View File

@ -0,0 +1,34 @@
package meerkat.voting.controller;
import meerkat.voting.encryptor.VBCryptoManager;
import meerkat.voting.ui.VotingBoothUI;
import meerkat.voting.output.BallotOutputDevice;
import meerkat.voting.storage.StorageManager;
import java.io.IOException;
/**
* An interface for the controller component of the voting booth
*/
public interface VotingBoothController extends Runnable {
/**
* initialize by setting all the different components of the Voting Booth to be recognized by this controller
* @param outputDevice the ballot output device. Naturally a printer and/or ethernet connection
* @param vbCrypto the crypto module
* @param vbUI User interface in which the voter chooses his answers
* @param vbStorageManager storage component for handling files and USB sticks
*/
public void init (BallotOutputDevice outputDevice,
VBCryptoManager vbCrypto,
VotingBoothUI vbUI,
StorageManager vbStorageManager) throws IOException;
/**
* an asynchronous call from Admin Console (If there is such one implemented) to shut down the system
*/
public void callShutDown();
}

View File

@ -0,0 +1,473 @@
package meerkat.voting.controller;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.*;
import meerkat.voting.controller.commands.*;
import meerkat.voting.controller.selector.QuestionSelector;
import meerkat.voting.controller.selector.SimpleListCategoriesSelector;
import meerkat.voting.encryptor.VBCryptoManager;
import meerkat.voting.encryptor.VBCryptoManager.EncryptionAndSecrets;
import meerkat.voting.output.BallotOutputDevice;
import meerkat.voting.storage.StorageManager;
import meerkat.voting.ui.VotingBoothUI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.SignatureException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
/**
* An asynchronous implementation of the VotingBoothController.
* This implementation binds the other components (storage, ui, output device, and crypto manager),
* and runs as its own thread controlling the whole VB process.
* The high level details are that it has a queue of commands to handle in order, and a State object which keeps
* all data from previous tasks which is necessary for the next task.
* It calls executions in the UI and output device asynchronously.
*/
public class VotingBoothImpl implements VotingBoothController {
private final Logger logger = LoggerFactory.getLogger(VotingBoothImpl.class);
// the component interfaces of the Voting Booth
private BallotOutputDevice outputDevice;
private VBCryptoManager crypto;
private VotingBoothUI ui;
private StorageManager storageManager;
// election details and info
private List<BallotQuestion> questionsForChoosingChannel;
private QuestionSelector questionSelector;
private Map<String, UIElement> systemMessages;
// state
private ControllerState state;
private volatile boolean shutDownHasBeenCalled;
private LinkedBlockingQueue<ControllerCommand> queue;
protected final int MAX_REQUEST_IDENTIFIER = 100000;
private static int requestCounter = 0;
// a simple constructor
public VotingBoothImpl () {
logger.info("A VotingBoothImpl is constructed");
shutDownHasBeenCalled = false;
queue = new LinkedBlockingQueue<>();
state = new ControllerState();
}
@Override
public void init(BallotOutputDevice outputDevice,
VBCryptoManager vbCrypto,
VotingBoothUI vbUI,
StorageManager vbStorageManager) throws IOException {
logger.info("init is called");
// keep pointers to the VB components
this.outputDevice = outputDevice;
this.crypto = vbCrypto;
this.ui = vbUI;
this.storageManager = vbStorageManager;
// store election details and info
ElectionParams electionParams;
try {
logger.info("init: reading election params");
electionParams = storageManager.readElectionParams();
logger.info("init: reading system messages");
systemMessages = storageManager.readSystemMessages();
}
catch (IOException e) {
logger.error("init could not read info from a file. Exception is: " + e);
throw e;
}
logger.info("init: setting the election parameters");
this.questionsForChoosingChannel = electionParams.getChannelChoiceQuestionsList();
List<BallotQuestion> questions = electionParams.getRaceQuestionsList();
this.questionSelector = new SimpleListCategoriesSelector(questions, electionParams.getSelectionData());
logger.info("init: setting finished");
}
@Override
public void run() {
logger.info("run command has been called");
runVotingFlow();
doShutDown();
}
/**
* a method for running the Voting flow of the VB (in contrast to the admin-setup flow
* It simply loops: takes the next command in its inner queue and handles it
*/
private void runVotingFlow () {
logger.info("entered the voting flow");
queue.add(new RestartVotingCommand(generateRequestIdentifier(), state.currentBallotSerialNumber));
while (! wasShutDownCalled()) {
try {
ControllerCommand Command = queue.take();
handleSingleCommand(Command);
}
catch (InterruptedException e) {
logger.warn ("Interrupted while reading from command queue " + e);
}
}
}
@Override
public void callShutDown() {
logger.info("callShutDown command has been called");
shutDownHasBeenCalled = true;
queue.clear();
ui.callShutDown();
outputDevice.callShutDown();
}
/**
* this method decides upon a given command if to ignore it (if it has an old serial number) or to handle it
* If we choose to handle it, then it simply calls the matching method which handles this type of command
* @param command a command to handle next (probably from the inner command queue)
*/
private void handleSingleCommand(ControllerCommand command) {
// check if the command is old and should be ignored
if (command.getBallotSerialNumber() != state.currentBallotSerialNumber && !(command instanceof RestartVotingCommand)) {
// probably an old command relating to some old ballot serial number. Simply log it and ignore it.
String errorMessage = "handleSingleCommand: received a task too old. " +
command.getBallotSerialNumber() + " " + state.currentBallotSerialNumber;
logger.debug(errorMessage);
return;
}
// decide which method to run according to the command type
if (command instanceof RestartVotingCommand) {
doRestartVoting ();
}
else if (command instanceof ChannelChoiceCommand) {
doChooseChannel();
}
else if (command instanceof ChannelDeterminedCommand) {
doSetChannelAndAskQuestions ((ChannelDeterminedCommand)command);
}
else if (command instanceof ChooseFinalizeOptionCommand) {
doChooseFinalizeOption();
}
else if (command instanceof CastCommand) {
doFinalize(false);
}
else if (command instanceof AuditCommand) {
doFinalize(true);
}
else if (command instanceof EncryptAndCommitBallotCommand) {
doCommit ((EncryptAndCommitBallotCommand)command);
}
else if (command instanceof ReportErrorCommand) {
doReportErrorAndForceRestart((ReportErrorCommand)command);
}
else {
logger.error("handleSingleCommand: unknown type of ControllerCommand received: " + command.getClass().getName());
doReportErrorAndForceRestart(systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE));
}
}
private boolean wasShutDownCalled () {
return shutDownHasBeenCalled;
}
private void doShutDown () {
logger.info("running callShutDown");
state.clearAndResetState(VBState.SHUT_DOWN);
//TODO: add commands to actually shut down the machine
}
/**
* a method to execute a Restart Voting Command
*/
private void doRestartVoting () {
queue.clear();
state.clearAndResetState(VBState.NEW_VOTER);
ui.startNewVoterSession(new NewVoterCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
/**
* a (overloaded) method to execute a Report Error Command.
* It actually just runs the overloaded version of this method with the error message inside the command
* @param command the command has the info of the error message to report
*/
private void doReportErrorAndForceRestart(ReportErrorCommand command) {
doReportErrorAndForceRestart(command.getErrorMessage());
}
/**
* a (overloaded) method to report an error message to the voter
* @param errorMessage message to show the voter
*/
private void doReportErrorAndForceRestart(UIElement errorMessage) {
queue.clear();
state.clearAndResetState(VBState.FATAL_ERROR_FORCE_NEW_VOTER);
ui.showErrorMessageWithButtons(errorMessage,
new UIElement[]{systemMessages.get(StorageManager.RESTART_VOTING_BUTTON)},
new ErrorMessageRestartCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue));
}
/**
* a method to execute a Channel Choice Command
* it notifies the UI to present the channel choice questions to the voter
*/
private void doChooseChannel () {
if (state.stateIdentifier == VBState.NEW_VOTER) {
logger.debug("doing chooseChannel");
state.stateIdentifier = VBState.CHOOSE_CHANNEL;
ui.chooseChannel(this.questionsForChoosingChannel,
new ChannelChoiceCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.UNSUCCESSFUL_CHANNEL_CHOICE_MESSAGE)));
}
else {
logger.debug("doChooseChannel: current state is " + state.stateIdentifier);
// ignore this request
}
}
/**
* a method to execute a Channel Determined Command
* (this actually sets the channel now after the voter has answered the channel choice questions)
* It then determines the race questions for the voter, and notifies the UI to present them to the voter
* @param command details of the voter's answers on the channel choice questions
*/
private void doSetChannelAndAskQuestions (ChannelDeterminedCommand command) {
if (state.stateIdentifier == VBState.CHOOSE_CHANNEL) {
logger.debug("doing set channel and ask questions");
state.stateIdentifier = VBState.ANSWER_QUESTIONS;
List<BallotAnswer> channelChoiceAnswers = command.channelChoiceAnswers;
state.channelIdentifier = questionSelector.getChannelIdentifier(channelChoiceAnswers);
state.channelSpecificQuestions = questionSelector.selectQuestionsForVoter(state.channelIdentifier);
ui.askVoterQuestions(state.channelSpecificQuestions,
new VotingCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.UNSUCCESSFUL_VOTING_MESSAGE)));
}
else {
logger.debug("doSetChannelAndAskQuestions: current state is " + state.stateIdentifier);
// ignore this request
}
}
/**
* a method to execute a Do-Finalzie-Option Command
* notifies the UI to present the cast-or-audit question to the voter
*/
private void doChooseFinalizeOption() {
if (state.stateIdentifier == VBState.COMMITTING_TO_BALLOT) {
logger.debug("doChooseFinalizeOption");
state.stateIdentifier = VBState.CAST_OR_AUDIT;
ui.castOrAudit(new CastOrAuditCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.UNRECOGNIZED_FINALIZE_RESPONSE_MESSAGE)));
}
else {
logger.debug("doChooseFinalizeOption: current state is " + state.stateIdentifier);
// ignore this request
}
}
/**
* a method to execute a Encrypt-and-Commit Command
* It sends a notification to commit to the output device
* @param command details of the voter's answers on the ballot questions
*/
private void doCommit (EncryptAndCommitBallotCommand command) {
if (state.stateIdentifier == VBState.ANSWER_QUESTIONS) {
logger.debug("doing commit");
try {
setBallotData(command);
ui.notifyVoterToWaitForFinish(systemMessages.get(StorageManager.WAIT_FOR_COMMIT_MESSAGE),
new WaitForFinishCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE)));
outputDevice.commitToBallot(state.plaintextBallot,
state.signedEncryptedBallot,
new OutputDeviceCommitCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.OUTPUT_DEVICE_FAILURE_MESSAGE)));
state.stateIdentifier = VBState.COMMITTING_TO_BALLOT;
}
catch (SignatureException | IOException e) {
logger.error("doCommit: encryption failed. exception: " + e);
// in case the encryption failed for some unknown reason, we send the UI a notification
// to ask the voter whether he wants to retry or cancel the ballot
UIElement errorMessage = systemMessages.get(StorageManager.ENCRYPTION_FAILED_MESSAGE);
UIElement[] buttons = new UIElement[]{
systemMessages.get(StorageManager.RETRY_BUTTON),
systemMessages.get(StorageManager.CANCEL_VOTE_BUTTON)};
EncryptionFailedCallback callback = new EncryptionFailedCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue);
ui.showErrorMessageWithButtons(errorMessage, buttons, callback);
}
}
else {
logger.debug("doCommit: current state is " + state.stateIdentifier);
// ignore this request
}
}
/**
* encrypt the ballot, and keep all info (plaintext, encryption and secrets) in the state's attributes
* @param command either an EncryptAndCommitBallotCommand if we encrypt the plaintext for the first time, or a RetryEncryptAndCommitBallotCommand if we already got here but encryption failed before
* @throws IOException problems in the encryption process
* @throws SignatureException problems in the digital signature process
*/
private void setBallotData (EncryptAndCommitBallotCommand command) throws IOException, SignatureException{
// a Retry command is given only if we first got here, and later the encryption failed but the voter chose to retry
// in such a case the plaintext is already set from previous attempt
if (! (command instanceof RetryEncryptAndCommitBallotCommand)) {
// this is not a retry attempt, so the plaintext is not set yet
// otherwise, we have the plaintext from the previous encryption attempt
state.plaintextBallot = PlaintextBallot.newBuilder()
.setSerialNumber(command.getBallotSerialNumber())
.addAllAnswers(command.getVotingAnswers())
.build();
}
// keep the encryption and the secrets we used for it
EncryptionAndSecrets encryptionAndSecrets = crypto.encrypt(state.plaintextBallot);
state.signedEncryptedBallot = encryptionAndSecrets.getSignedEncryptedBallot();
state.secrets = encryptionAndSecrets.getSecrets();
}
/**
* a method to execute a Cast Command or an Audit Command
* according to the flag, chooses which finalize ballot task to send to the output device
* @param auditRequested true if we wish to finalize by auditing. false if we finalize by casting the ballot
*/
private void doFinalize (boolean auditRequested) {
if (state.stateIdentifier == VBState.CAST_OR_AUDIT) {
logger.debug("finalizing");
state.stateIdentifier = VBState.FINALIZING;
if (auditRequested) {
// finalize by auditing
ui.notifyVoterToWaitForFinish(systemMessages.get(StorageManager.WAIT_FOR_AUDIT_MESSAGE),
new WaitForFinishCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE)));
outputDevice.audit(state.secrets,
new OutputDeviceFinalizeCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.OUTPUT_DEVICE_FAILURE_MESSAGE)));
}
else {
// finalize by casting the ballot
ui.notifyVoterToWaitForFinish(systemMessages.get(StorageManager.WAIT_FOR_CAST_MESSAGE),
new WaitForFinishCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE)));
outputDevice.castBallot(
new OutputDeviceFinalizeCallback(generateRequestIdentifier(),
state.currentBallotSerialNumber,
this.queue,
systemMessages.get(StorageManager.OUTPUT_DEVICE_FAILURE_MESSAGE)));
}
}
else {
logger.debug("doFinalize: current state is " + state.stateIdentifier);
// ignore this request
}
}
// an enum to keep the step (of the voting process) in which the VB is currently in
private enum VBState {
NEW_VOTER,
CHOOSE_CHANNEL,
ANSWER_QUESTIONS,
COMMITTING_TO_BALLOT,
CAST_OR_AUDIT,
FINALIZING,
FATAL_ERROR_FORCE_NEW_VOTER,
SHUT_DOWN
}
/**
* a class to keep and directly access all the details of the VB controller state.
* naming:
* - the (enum) state identifier of the current step
* - the chosen channel
* - all details of the ballot (both plaintext and encryption)
* - last request identifier (to one of the other component interfaces)
* - serial number of the current ballot
*/
private class ControllerState {
public VBState stateIdentifier;
public byte[] channelIdentifier;
public List<BallotQuestion> channelSpecificQuestions;
public PlaintextBallot plaintextBallot;
public SignedEncryptedBallot signedEncryptedBallot;
public BallotSecrets secrets;
public int lastRequestIdentifier;
public long currentBallotSerialNumber;
public ControllerState () {
plaintextBallot = null;
signedEncryptedBallot = null;
secrets = null;
lastRequestIdentifier = -1;
channelIdentifier = null;
channelSpecificQuestions = null;
currentBallotSerialNumber = 0;
}
private void clearPlaintext () {
plaintextBallot = null;
}
private void clearCiphertext () {
signedEncryptedBallot = null;
secrets = null;
}
public void clearAndResetState(VBState newStateIdentifier) {
state.clearPlaintext();
state.clearCiphertext();
state.stateIdentifier = newStateIdentifier;
state.currentBallotSerialNumber += 1;
}
}
/**
* Creates a new request identifier to identify any call to one of the other component interfaces.
* We limit its value to MAX_REQUEST_IDENTIFIER, so the identifier is kept short.
* @return a new request identifier
*/
private int generateRequestIdentifier() {
++requestCounter;
if (requestCounter >= MAX_REQUEST_IDENTIFIER) {
requestCounter = 1;
}
return requestCounter;
}
}

View File

@ -0,0 +1,52 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.*;
import meerkat.voting.ui.VotingBoothUI.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the cast-or-audit request to the UI.
* Upon getting a FinalizeBallotChoice response from the voter, the callback then registers a new command
* to the controller queue, either a CastCommand or an AuditCommand according to the voter's choice
*/
public class CastOrAuditCallback extends ControllerCallback<FinalizeBallotChoice> {
protected final static Logger logger = LoggerFactory.getLogger(CastOrAuditCallback.class);
protected final UIElement unrecognizedFinalizeResponseMessage;
public CastOrAuditCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue,
UIElement unrecognizedFinalizeResponseMessage) {
super(requestId, ballotSerialNumber, controllerQueue);
this.unrecognizedFinalizeResponseMessage = unrecognizedFinalizeResponseMessage;
}
@Override
public void onSuccess(FinalizeBallotChoice result) {
if (result == FinalizeBallotChoice.CAST) {
enqueueCommand(new CastCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else if (result == FinalizeBallotChoice.AUDIT) {
enqueueCommand(new AuditCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else {
logger.error("CastOrAuditCallback got an unrecognized response: " + result);
onFailure(new IllegalArgumentException("CastOrAuditCallback got an unknown result (" + result + ")"));
}
}
@Override
public void onFailure(Throwable t) {
logger.error("CastOrAuditCallback got a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
unrecognizedFinalizeResponseMessage));
}
}

View File

@ -0,0 +1,50 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the channel-choice request to the UI.
* Upon receiving the answers for the channel-choice questions, the callback registers a new ChannelDeterminedCommand
* to the controller queue, so the controller can then process the answers and set the channel.
* If voter cancelled during the process, a cancelling exception is thrown and a RestartVotingCommand is
* registered through the onFailure() method
*/
public class ChannelChoiceCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(ChannelChoiceCallback.class);
protected final UIElement unsuccessfulChannelChoiceMessage;
public ChannelChoiceCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue,
UIElement unsuccessfulChannelChoiceMessage) {
super(requestId, ballotSerialNumber, controllerQueue);
this.unsuccessfulChannelChoiceMessage = unsuccessfulChannelChoiceMessage;
}
@Override
public void onSuccess(List<BallotAnswer> result) {
logger.debug("callback for channel choice returned success");
// register the chosen BallotAnswers to a command in the controller queue
enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
@Override
public void onFailure(Throwable t) {
if (t instanceof VoterCancelThrowable) {
// voter has cancelled during the UI channel choice process. A VoterCancelThrowable is thrown
logger.debug("ChannelChoiceCallback got a cancellation response");
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else {
logger.error("channel choice initiated a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
unsuccessfulChannelChoiceMessage));
}
}
}

View File

@ -0,0 +1,41 @@
package meerkat.voting.controller.callbacks;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.voting.controller.commands.ControllerCommand;
import java.util.concurrent.LinkedBlockingQueue;
/**
* The base (abstract) class of all callbacks for requests sent by the controller to other components (ui, output-device)
* It implements the FutureCallback interface
* Its members are:
* - requestIdentifier - uniquely identifies the request which this callback responds
* - ballotSerialNumber - number of ballot which was currently active when request was sent
* - controllerQueue - so the callback can issue and register a new command to the controller, once the request handling is finished
*/
public abstract class ControllerCallback<T> implements FutureCallback<T> {
private final int requestIdentifier;
private final long ballotSerialNumber;
private LinkedBlockingQueue<ControllerCommand> controllerQueue;
protected ControllerCallback (int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
this.requestIdentifier = requestId;
this.ballotSerialNumber = ballotSerialNumber;
this.controllerQueue = controllerQueue;
}
protected int getRequestIdentifier () {
return requestIdentifier;
}
protected long getBallotSerialNumber () {
return ballotSerialNumber;
}
protected void enqueueCommand (ControllerCommand command) {
controllerQueue.add(command);
}
}

View File

@ -0,0 +1,45 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* This is quite a special callback. It is not issued in a normal flow of the voting.
* This callback is made only for a request to the UI to choose handling of failure in encryption.
* When encryption/signature fails the voter is asked in the UI whether to retry or abort.
* This specific callback decides, upon the answer to this request, which command to register in the controller's queue
*/
public class EncryptionFailedCallback extends ControllerCallback<Integer> {
protected final static Logger logger = LoggerFactory.getLogger(EncryptionFailedCallback.class);
public EncryptionFailedCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
super(requestId, ballotSerialNumber, controllerQueue);
}
@Override
public void onSuccess(Integer result) {
logger.debug("callback for encryption-failed request is initiated successfully");
int res = result.intValue();
if (res == 0) {
logger.debug("voter chose to retry encryption");
enqueueCommand(new RetryEncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else if (res == 1) {
logger.debug("voter chose to abort the vote");
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else {
onFailure(new IllegalArgumentException("EncryptionFailedCallback got an unknown result (" + res + ")"));
}
}
@Override
public void onFailure(Throwable t) {
logger.error("Error message execution initiated a failure: " + t);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}

View File

@ -0,0 +1,33 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* This is quite a special callback. It is not issued in a normal flow of the voting.
* This callback is made only for a request to the UI to show the voter an error message.
* Upon approval of the voter, the method onSuccess() of this callback is called, and the voting
* is reset through a command to the controller's queue
*/
public class ErrorMessageRestartCallback extends ControllerCallback<Integer> {
protected final static Logger logger = LoggerFactory.getLogger(ErrorMessageRestartCallback.class);
public ErrorMessageRestartCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
super(requestId, ballotSerialNumber, controllerQueue);
}
@Override
public void onSuccess(Integer i) {
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
@Override
public void onFailure(Throwable t) {
logger.error("Error message execution initiated a failure: " + t);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}

View File

@ -0,0 +1,34 @@
package meerkat.voting.controller.callbacks;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the StartSession request to the UI.
* Upon approval of the voter, it registers a new ChannelChoiceCommand to the controller queue (which
* then starts the channel choice process)
*/
public class NewVoterCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(NewVoterCallback.class);
public NewVoterCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue) {
super(requestId, ballotSerialNumber, controllerQueue);
}
@Override
public void onSuccess(Void v) {
logger.debug("callback for new voting returned success");
enqueueCommand(new ChannelChoiceCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
@Override
public void onFailure(Throwable t) {
logger.error("New voting session got a failure: " + t);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}

View File

@ -0,0 +1,39 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the Commit request to the output-device.
* When committing is done, the callback's onSuccess() method is called to register a new ChooseFinalizeOptionCommand
* to the controller
*/
public class OutputDeviceCommitCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(OutputDeviceCommitCallback.class);
protected final UIElement outputDeviceFailureMessage;
public OutputDeviceCommitCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue,
UIElement outputDeviceFailureMessage) {
super(requestId, ballotSerialNumber, controllerQueue);
this.outputDeviceFailureMessage = outputDeviceFailureMessage;
}
@Override
public void onSuccess(Void v) {
logger.debug("callback for output device commit success");
enqueueCommand(new ChooseFinalizeOptionCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
@Override
public void onFailure(Throwable t) {
logger.error("OutputDeviceCommitCallback got a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
outputDeviceFailureMessage));
}
}

View File

@ -0,0 +1,39 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the Finalize request to the output-device.
* When finalizing (either cast or audit) is done,
* the callback's onSuccess() method is called to register a new command to the controller to restart the voting process
*/
public class OutputDeviceFinalizeCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(OutputDeviceFinalizeCallback.class);
protected final UIElement outputDeviceFailureMessage;
public OutputDeviceFinalizeCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue,
UIElement outputDeviceFailureMessage) {
super(requestId, ballotSerialNumber, controllerQueue);
this.outputDeviceFailureMessage = outputDeviceFailureMessage;
}
@Override
public void onSuccess(Void v) {
logger.debug("callback for output device finalize success");
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
@Override
public void onFailure(Throwable t) {
logger.error("OutputDeviceFinalizeCallback got a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
outputDeviceFailureMessage));
}
}

View File

@ -0,0 +1,8 @@
package meerkat.voting.controller.callbacks;
/**
* Just a simple unique exception to throw when a voter aborts/cancels the voting during the voting process
*/
public class VoterCancelThrowable extends Throwable {
//
}

View File

@ -0,0 +1,48 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* A controller callback for the race-voting request to the UI.
* Upon receiving the answers for the race questions, the callback registers a new command to process
* the voter's answers (encrypt and then commit) into the controller's queue.
* If voter cancelled during the process, a cancelling exception is thrown and a RestartVotingCommand is
* registered through the onFailure() method
*/
public class VotingCallback extends ControllerCallback<List<BallotAnswer>> {
protected final static Logger logger = LoggerFactory.getLogger(VotingCallback.class);
protected final UIElement unsuccessfulVotingMessage;
public VotingCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue,
UIElement unsuccessfulVotingMessage) {
super(requestId, ballotSerialNumber, controllerQueue);
this.unsuccessfulVotingMessage = unsuccessfulVotingMessage;
}
@Override
public void onSuccess(List<BallotAnswer> result) {
logger.debug("callback for voting returned success");
enqueueCommand(new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
@Override
public void onFailure(Throwable t) {
if (t instanceof VoterCancelThrowable) {
logger.debug("VotingCallback got a cancellation response");
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else {
logger.error("voting initiated a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
unsuccessfulVotingMessage));
}
}
}

View File

@ -0,0 +1,37 @@
package meerkat.voting.controller.callbacks;
import meerkat.protobuf.Voting.UIElement;
import meerkat.voting.controller.commands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.LinkedBlockingQueue;
/**
* This callback is attached to requests to UI which ask the voter to wait for some process to finish.
* It actually asks nothing in the UI, and it is simply attached to the UI request as a place-holder.
* Therefore its onSuccess() method is empty
*/
public class WaitForFinishCallback extends ControllerCallback<Void> {
protected final static Logger logger = LoggerFactory.getLogger(WaitForFinishCallback.class);
protected final UIElement somethingWrongMessage;
public WaitForFinishCallback(int requestId,
long ballotSerialNumber,
LinkedBlockingQueue<ControllerCommand> controllerQueue,
UIElement somethingWrongMessage) {
super(requestId, ballotSerialNumber, controllerQueue);
this.somethingWrongMessage = somethingWrongMessage;
}
@Override
public void onSuccess(Void v) {
}
@Override
public void onFailure(Throwable t) {
logger.error("WaitForFinishCallback got a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
somethingWrongMessage));
}
}

View File

@ -0,0 +1,10 @@
package meerkat.voting.controller.commands;
/**
* a command to audit the ballot
*/
public class AuditCommand extends ControllerCommand {
public AuditCommand(int requestIdentifier, long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber);
}
}

View File

@ -0,0 +1,10 @@
package meerkat.voting.controller.commands;
/**
* a command to cast the ballot
*/
public class CastCommand extends ControllerCommand {
public CastCommand(int requestIdentifier, long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber);
}
}

View File

@ -0,0 +1,10 @@
package meerkat.voting.controller.commands;
/**
* a command to initiate the channel choice flow at the beginning of the voting
*/
public class ChannelChoiceCommand extends ControllerCommand {
public ChannelChoiceCommand(int requestIdentifier, long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber);
}
}

View File

@ -0,0 +1,16 @@
package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting.*;
import java.util.List;
/**
* This command is registered in the controller right after the voter answered all the channel choice questions
*/
public class ChannelDeterminedCommand extends ControllerCommand {
public List<BallotAnswer> channelChoiceAnswers;
public ChannelDeterminedCommand(int requestIdentifier, long ballotSerialNumber, List<BallotAnswer> answers) {
super(requestIdentifier, ballotSerialNumber);
channelChoiceAnswers = answers;
}
}

View File

@ -0,0 +1,10 @@
package meerkat.voting.controller.commands;
/**
* a command to initiate asking the voter how to finalize (cast-or-audit) the ballot
*/
public class ChooseFinalizeOptionCommand extends ControllerCommand {
public ChooseFinalizeOptionCommand(int requestIdentifier, long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber);
}
}

View File

@ -0,0 +1,23 @@
package meerkat.voting.controller.commands;
/**
* This is the base class for the controller commands.
* These commands are registered in a command queue of the controller.
*/
public abstract class ControllerCommand {
protected final int requestIdentifier;
protected final long ballotSerialNumber;
protected ControllerCommand(int requestIdentifier, long ballotSerialNumber) {
this.requestIdentifier = requestIdentifier;
this.ballotSerialNumber = ballotSerialNumber;
}
public long getBallotSerialNumber () {
return this.ballotSerialNumber;
}
public int getRequestIdentifier () {
return this.requestIdentifier;
}
}

View File

@ -0,0 +1,24 @@
package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting.BallotAnswer;
import java.util.List;
/**
* a command registered after voter answered all ballot questions.
* The controller then initiates an encryption-signature-commit flow
*/
public class EncryptAndCommitBallotCommand extends ControllerCommand {
private final List<BallotAnswer> votingAnswers;
public EncryptAndCommitBallotCommand(int requestIdentifier,
long ballotSerialNumber,
List<BallotAnswer> answers) {
super(requestIdentifier, ballotSerialNumber);
votingAnswers = answers;
}
public List<BallotAnswer> getVotingAnswers() {
return votingAnswers;
}
}

View File

@ -0,0 +1,20 @@
package meerkat.voting.controller.commands;
import meerkat.protobuf.Voting.*;
/**
* This command is not a part of the normal flow of the controller.
* It asks the controller to handle (report to voter) some error message
*/
public class ReportErrorCommand extends ControllerCommand {
private final UIElement errorMessage;
public ReportErrorCommand(int requestIdentifier, long ballotSerialNumber, UIElement errorMessage) {
super(requestIdentifier, ballotSerialNumber);
this.errorMessage = errorMessage;
}
public UIElement getErrorMessage() {
return errorMessage;
}
}

View File

@ -0,0 +1,11 @@
package meerkat.voting.controller.commands;
/**
* a command to restart a voting flow (for a new voter)
*/
public class RestartVotingCommand extends ControllerCommand {
public RestartVotingCommand(int requestIdentifier, long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber);
}
}

View File

@ -0,0 +1,15 @@
package meerkat.voting.controller.commands;
/**
* This is quite a special command not part of the normal voting flow.
* It extends the base EncryptAndCommitBallotCommand for occasions where first attempt of encryption failed
* and the voter asks to re-try encrypting and committing.
*/
public class RetryEncryptAndCommitBallotCommand extends EncryptAndCommitBallotCommand {
public RetryEncryptAndCommitBallotCommand(int requestIdentifier,
long ballotSerialNumber) {
super(requestIdentifier, ballotSerialNumber, null);
}
}

View File

@ -0,0 +1,29 @@
package meerkat.voting.controller.selector;
import meerkat.protobuf.Voting.*;
import java.util.List;
/**
* An interface for the question-selection component.
* This component handles the connection between the channel choice questions and the race questions.
* It gets the answers for the channel choice questions and determines which race question to put in the voter's ballot.
* It also creates an identifier for this chosen channel. This identifier should appear in the plaintext of the ballot.
* The channel identifier does not identify a specific voter, but rather it identifies a specific voting channel
*/
public interface QuestionSelector {
/**
* determines an identifier for the channel of the voter
* @param channelChoiceAnswers The answers given by the voter to the channel choice questions
* @return an identifier of the channel. To be used by selectQuestionsForVoter(). This identifier should also appear on the plaintext of the ballot
*/
public byte[] getChannelIdentifier (List<BallotAnswer> channelChoiceAnswers);
/**
* determines which race questions to present to the voter according to its channel
* @param channelIdentifier the identifier of this specific channel
* @return the race questions (to present to the voter)
*/
public List<BallotQuestion> selectQuestionsForVoter (byte[] channelIdentifier);
}

View File

@ -0,0 +1,176 @@
package meerkat.voting.controller.selector;
import meerkat.protobuf.Voting.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.lang.Math;
/**
* A simple implementation of a QuestionSelector.
* This implementation simply regards every single answer in the channel choice phase as an identifier of a category
* Every category is an array of ballot race questions.
* Data of categories is initialized and stored by a SimpleCategoriesSelectionData protobuf.
* After receiving the answers from a channel choice phase, this class simply gathers all the categories
* chosen and compiles the list of ballot questions to include in the ballot for this voter (a question
* is included in the ballot if its index appears in any chosen category, or in the default category shared by all voters)
*/
public class SimpleListCategoriesSelector implements QuestionSelector {
protected final static Logger logger = LoggerFactory.getLogger(SimpleListCategoriesSelector.class);
// all the possible race questions
private final BallotQuestion[] allBallotQuestions;
// this category is presented to any voter (regardless of his answers to the channel choice questions)
private final int[] sharedDefaults;
// all the categories.
// first index is the channel choice question number
// second index is a possible answer to this question
// categoryChoosers[questionNumber][answerNumber] is an array of indices (to the ballotQuestions array).
// This category of questions is included in the ballot if voter answered this specific answer to this channel choice question
private final int[][][] categoryChoosers;
private final static byte QUESTION_SELECTED = (byte)1;
private final static byte QUESTION_NOT_SELECTED = (byte)0;
/**
* A very straight-forward constructor for the SimpleListCategoriesSelector
* @param allBallotQuestions all possible race questions for this election
* @param data a protobuf containing all the index categories
*/
public SimpleListCategoriesSelector(List<BallotQuestion> allBallotQuestions, SimpleCategoriesSelectionData data) {
// copies the ballot race question list into a member array
this.allBallotQuestions = new BallotQuestion[allBallotQuestions.size()];
allBallotQuestions.toArray(this.allBallotQuestions);
// copies the shared category list (as appears in the protobuf data) into a member array
sharedDefaults = listToIntArray(data.getSharedDefaults().getQuestionIndexList());
// copies the category lists (as appear in the protobuf data) into a 3-dimensional member array
int[][][] selectionDataTmp = new int[data.getCategoryChooserList().size()][][];
int channelChoiceQuestionNumber = 0;
for (CategoryChooser catChooser: data.getCategoryChooserList()) {
selectionDataTmp[channelChoiceQuestionNumber] = new int[catChooser.getCategoryList().size()][];
int channelChoiceAnswerNumber = 0;
for (Category category: catChooser.getCategoryList()) {
selectionDataTmp[channelChoiceQuestionNumber][channelChoiceAnswerNumber] = listToIntArray(category.getQuestionIndexList());
++channelChoiceAnswerNumber;
}
++channelChoiceQuestionNumber;
}
categoryChoosers = selectionDataTmp;
// verifies in advance that there are not very suspicious indices in the selection data
assertDataValid();
}
/**
* asserts that the selection data does not contain a question index which is beyond the length of
* the ballot race questions array. Otherwise, throws an IndexOutOfBoundsException
*/
private void assertDataValid () {
// find the maximum question index in the selection data
int maxQuestionIndex = -1;
for (int index: sharedDefaults) {
maxQuestionIndex = Math.max(maxQuestionIndex, index);
}
for (int[][] categoryChooser: categoryChoosers) {
for (int[] category: categoryChooser) {
for (int index: category) {
maxQuestionIndex = Math.max(maxQuestionIndex, index);
}
}
}
// asserts that the maximal question index in the selection data does not overflow the ballot race questions array
int questionsLength = allBallotQuestions.length;
if (maxQuestionIndex >= questionsLength) {
String errorMessage = "Selection data refers to question index " + maxQuestionIndex + " while we have only " + questionsLength + " questions totally";
logger.error(errorMessage);
throw new IndexOutOfBoundsException(errorMessage);
}
}
/**
* an implementation of the QuestionSelector interface method.
* In this selector class the identifier simply marks all the ballot race questions which appear in at least one
* category of the categories chosen by the voter (or in the shared defaults category) in the channel choice round.
* @param channelChoiceAnswers The answers given by the voter to the channel choice questions
* @return the channel identifier
*/
@Override
public byte[] getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) {
/*
* Currently, this implementation of the QuestionSelector interface returns an over-simplified identifier which
* is merely an array of booleans (which flags the questions to appear in the ballot)
* For elections with more than one possible channel we should return a more printable and recognizable
* identifier to be put in the plaintext of the ballot
*/
byte[] isSelected = new byte[allBallotQuestions.length];
java.util.Arrays.fill(isSelected, QUESTION_NOT_SELECTED);
for (int i: sharedDefaults) {
isSelected[i] = QUESTION_SELECTED;
}
int channelChoiceQuestionNumber = 0;
for (BallotAnswer ballotAnswer: channelChoiceAnswers) {
assertAnswerLengthIsOne(ballotAnswer, channelChoiceQuestionNumber);
for (int i: categoryChoosers[channelChoiceQuestionNumber][(int)ballotAnswer.getAnswer(0)]) {
isSelected[i] = QUESTION_SELECTED;
}
}
return isSelected;
}
/**
* Verifies that the ballot answer is of length 1. (We do not yet handle multi-choice questions in the channel choice round).
* Otherwise, throws an exception.
* @param ballotAnswer the answer to verify whose length is one
* @param questionNumber the number of the question (needed only for error message strings)
*/
private void assertAnswerLengthIsOne (BallotAnswer ballotAnswer, int questionNumber) {
if (ballotAnswer.getAnswerCount() != 1) {
String errorMessage = "SimpleListCategoriesSelector expects a single answer for every channel choice question\n";
errorMessage += "Answer to question number " + (questionNumber+1) + " is";
for (long i : ballotAnswer.getAnswerList()) {
errorMessage += " " + i;
}
logger.error(errorMessage);
throw new IllegalArgumentException(errorMessage);
}
}
@Override
public List<BallotQuestion> selectQuestionsForVoter(byte[] channelIdentifier) {
List<BallotQuestion> selectedQuestions = new ArrayList<>();
for (int i = 0; i < channelIdentifier.length; ++i) {
if (channelIdentifier[i] == QUESTION_SELECTED) {
selectedQuestions.add(allBallotQuestions[i]);
}
}
return selectedQuestions;
}
/**
* copies a List of Integers into an int[] array of same length
* @param l a list of Integers
* @return an array of ints
*/
private int[] listToIntArray(List<Integer> l) {
int[] res = new int[l.size()];
int index = 0;
for (Integer i: l) {
res[index] = i;
++index;
}
return res;
}
}

View File

@ -0,0 +1,44 @@
package meerkat.voting.encryptor;
import meerkat.protobuf.Voting.*;
import java.io.IOException;
import java.security.SignatureException;
/**
* An interface for the encryptor component of the voting booth
* It handles both the encryption and the digital signature
*/
public interface VBCryptoManager {
/**
* A simple class for pairing EncrypedBallot together with its matching BallotSecrets
*/
public class EncryptionAndSecrets {
private final SignedEncryptedBallot signedEncryptedBallot;
private final BallotSecrets secrets;
public EncryptionAndSecrets (SignedEncryptedBallot encryptedBallot, BallotSecrets secrets) {
this.signedEncryptedBallot = encryptedBallot;
this.secrets = secrets;
}
public SignedEncryptedBallot getSignedEncryptedBallot() {
return signedEncryptedBallot;
}
public BallotSecrets getSecrets() {
return secrets;
}
}
/**
* This function encrypts the plaintext ballot using the booth's keys
* @param plaintextBallot - all plaintext ballot info of the voter
* @return an encryption of the ballot
*/
// TODO: do we seed the random here?
public EncryptionAndSecrets encrypt (PlaintextBallot plaintextBallot) throws SignatureException, IOException;
}

View File

@ -0,0 +1,68 @@
package meerkat.voting.encryptor;
import meerkat.crypto.*;
import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.Voting.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.SignatureException;
import java.util.Random;
/**
* A basic implementation of the VBCryptoManager interface
*/
public class VBCryptoManagerImpl implements VBCryptoManager {
protected final static Logger logger = LoggerFactory.getLogger(VBCryptoManagerImpl.class);
private final Random random; //TODO: Random object should be more cryptographycally secure
private final Encryption encryption;
private final DigitalSignature digitalSignature;
public VBCryptoManagerImpl (Random rand, Encryption encryption, DigitalSignature digitalSignature) {
this.random = rand;
this.encryption = encryption;
this.digitalSignature = digitalSignature;
}
@Override
public EncryptionAndSecrets encrypt(PlaintextBallot plaintextBallot) throws SignatureException, IOException {
// TODO: do we seed the random here?
try {
EncryptionRandomness encryptionRandomness = encryption.generateRandomness(random);
BallotSecrets secrets = BallotSecrets.newBuilder()
.setPlaintextBallot(plaintextBallot)
.setEncryptionRandomness(encryptionRandomness)
.build();
RerandomizableEncryptedMessage encryptedMessage = encryption.encrypt(plaintextBallot, encryptionRandomness);
EncryptedBallot encBallot = EncryptedBallot.newBuilder()
.setSerialNumber(plaintextBallot.getSerialNumber())
.setData(encryptedMessage)
.build();
digitalSignature.updateContent(encBallot);
SignedEncryptedBallot signedEncryptedBallot = SignedEncryptedBallot.newBuilder()
.setEncryptedBallot(encBallot)
.setSignature(digitalSignature.sign())
.build();
// TODO: still has to supply RandomnessGenerationProof as well
return new EncryptionAndSecrets(signedEncryptedBallot, secrets);
}
catch (IOException e) {
logger.error("encrypt: the encryption component has thrown an exception: " + e);
throw e;
}
catch (SignatureException e) {
logger.error("encrypt: the signature component has thrown an exception: " + e);
throw e;
}
}
}

View File

@ -0,0 +1,140 @@
package meerkat.voting.output;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.Voting.BallotSecrets;
import meerkat.protobuf.Voting.PlaintextBallot;
import meerkat.protobuf.Voting.SignedEncryptedBallot;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.controller.callbacks.OutputDeviceCommitCallback;
import meerkat.voting.controller.callbacks.OutputDeviceFinalizeCallback;
import meerkat.voting.output.outputcommands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ArrayBlockingQueue;
/**
* This is a base class for simple OutputDevices which run asynchronously (as a separate thread).
* The methods of the BallotOutputDevice simply register a matching OutputCommand in the instance's queue
* The Runnable.run method simply takes the next registered command and calls the matching (abstract) method
*/
public abstract class AsyncRunnableOutputDevice implements BallotOutputDevice, Runnable {
private Logger logger;
private ArrayBlockingQueue<OutputCommand> queue;
private volatile boolean shutDownHasBeenCalled;
public AsyncRunnableOutputDevice() {
logger = LoggerFactory.getLogger(AsyncRunnableOutputDevice.class);
logger.info("AsyncRunnableOutputDevice is constructed");
queue = new ArrayBlockingQueue<>(1);
shutDownHasBeenCalled = false;
}
@Override
public void run () {
logger.info("starts running");
while (! wasShutDownCalled()) {
try {
OutputCommand command = queue.take();
handleSingleCommand(command);
}
catch (InterruptedException e) {
logger.warn("Interrupted while reading from command queue " + e);
}
}
}
private boolean wasShutDownCalled () {
return shutDownHasBeenCalled;
}
@Override
public void callShutDown() {
logger.info("callShutDown command has been called");
shutDownHasBeenCalled = true;
queue.clear();
}
/**
* chooses the next method to run according to the type of the given OutputCommand
* @param command any valid OutputCommand
*/
private void handleSingleCommand(OutputCommand command) {
if (command instanceof CommitOutputCommand) {
doCommitToBallot((CommitOutputCommand)command);
}
else if (command instanceof AuditOutputCommand) {
doAudit((AuditOutputCommand)command);
}
else if (command instanceof CastOutputCommand) {
doCastBallot((CastOutputCommand)command);
}
else if (command instanceof CancelOutputCommand) {
doCancel((CancelOutputCommand)command);
}
else {
String errorMessage = "handleSingleCommand: unknown type of OutputCommand received: " +
command.getClass().getName();
logger.error(errorMessage);
throw new RuntimeException(errorMessage);
}
}
@Override
public void commitToBallot(PlaintextBallot plaintextBallot,
SignedEncryptedBallot signedEncryptedBallot,
FutureCallback<Void> callback) {
logger.debug("Output interface call to commit to ballot");
queue.clear();
queue.add(new CommitOutputCommand(plaintextBallot, signedEncryptedBallot, (OutputDeviceCommitCallback)callback));
}
@Override
public void audit(BallotSecrets ballotSecrets, FutureCallback<Void> callback) {
logger.debug("an interface call to audit");
queue.clear();
queue.add(new AuditOutputCommand(ballotSecrets, (OutputDeviceFinalizeCallback)callback));
}
@Override
public void castBallot(FutureCallback<Void> callback) {
logger.debug("an interface call to cast ballot");
queue.clear();
queue.add(new CastOutputCommand((OutputDeviceFinalizeCallback)callback));
}
@Override
public void cancelBallot(FutureCallback<Void> callback) {
logger.debug("an interface call to cancel the output");
queue.clear();
queue.add(new CancelOutputCommand((ControllerCallback<Void>)callback));
}
/**
* This method should be filled by an extending class. It should have the details of how to commit to a ballot
* @param command a CommitOutputCommand with the details and the callback
*/
abstract void doCommitToBallot(CommitOutputCommand command);
/**
* This method should be filled by an extending class. It should have the details of how to audit the ballot
* @param command a AuditOutputCommand with the details and the callback
*/
abstract void doAudit(AuditOutputCommand command);
/**
* This method should be filled by an extending class. It should have the details of how to cast the ballot
* @param command a CastOutputCommand with the details and the callback
*/
abstract void doCastBallot(CastOutputCommand command);
/**
* This method should be filled by an extending class. It should have the details of how to cancel the ballot output
* @param command a CancelOutputCommand with the details and the callback
*/
abstract void doCancel(CancelOutputCommand command);
}

View File

@ -0,0 +1,44 @@
package meerkat.voting.output;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.Voting.*;
/**
* An interface for the device in which we output the ballots.
* Probably going to be a printer or an ethernet connection, or both.
*/
public interface BallotOutputDevice {
/**
* Output the encrypted ballot. This is a commitment before voter chooses casting or auditing
* @param encryptedBallot - the encrypted ballot to commit to
* @param callback - a callback object which expects no return value
*/
public void commitToBallot(PlaintextBallot plaintextBallot,
SignedEncryptedBallot encryptedBallot,
FutureCallback<Void> callback);
/**
* Voter chose 'audit'. Output the ballot secrets to prove correctness of the encryption.
* @param ballotSecrets - the secrets of the encryption
* @param callback - a callback object which expects no return value
*/
public void audit(BallotSecrets ballotSecrets, FutureCallback<Void> callback);
/**
* Voter chose 'cast'. Finalize the ballot for use in the polling station
* @param callback - a callback object which expects no return value
*/
public void castBallot(FutureCallback<Void> callback);
/**
* Cancelling the current ballot. This clears the state of the OutputDevice if the implementation has any such state.
* @param callback - a callback object which expects no return value
*/
public void cancelBallot(FutureCallback<Void> callback);
/**
* A method for shutting down the Output Device
*/
public void callShutDown();
}

View File

@ -0,0 +1,108 @@
package meerkat.voting.output;
import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.protobuf.Voting.SignedEncryptedBallot;
import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
import meerkat.voting.output.outputcommands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.client.*;
import javax.ws.rs.core.Response;
import java.io.IOException;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
/**
* A ballot output device for the network. It simply sends details over the wire
*/
public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice {
private static final Logger logger = LoggerFactory.getLogger(NetworkVirtualPrinter.class);
private ByteString channelIdentifier;
private SignedEncryptedBallot signedEncryptedBallot;
private final WebTarget successfulPrintTarget;
public NetworkVirtualPrinter(String address) {
super();
logger.info("A NetworkVirtualPrinter is constructed");
Client client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
successfulPrintTarget = client.target(address).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH);
resetState();
}
/**
* The NetworkVirtualPrinter actually does nothing for committing.
* It simply keeps the ballot details for later.
* When the voter chooses to Cast the ballot, these details are sent over the wire.
* @param command a CommitOutputCommand with the signed encryption of the ballot
*/
public void doCommitToBallot(CommitOutputCommand command) {
logger.debug("entered method doCommitToBallot");
channelIdentifier = command.getChannelIdentifierByteString();
signedEncryptedBallot = command.getSignedEncryptedBallot();
command.getCallback().onSuccess(null);
}
/**
* The NetworkVirtualPrinter actually does nothing for auditing.
* @param command a AuditOutputCommand with the details and the callback
*/
public void doAudit(AuditOutputCommand command) {
logger.debug("entered method doAudit");
resetState();
command.getCallback().onSuccess(null);
}
/**
* This is where the magic happens. The signed encrypted ballot is transmitted over the wire
* @param command a CastOutputCommand with the details and the callback
*/
public void doCastBallot(CastOutputCommand command) {
logger.debug("entered method doCastBallot");
ScannedData scannedData = ScannedData.newBuilder()
.setChannel(channelIdentifier)
.setSignedEncryptedBallot(this.signedEncryptedBallot)
.build();
Response response = successfulPrintTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
BoolValue b = response.readEntity(BoolValue.class);
response.close();
resetState();
if (b.getValue()) {
command.getCallback().onSuccess(null);
}
else {
command.getCallback().onFailure(new IOException());
}
}
/**
* The NetworkVirtualPrinter actually does nothing for canceling.
* @param command a CancelOutputCommand with the callback
*/
public void doCancel(CancelOutputCommand command) {
logger.debug("entered method doCancel");
resetState();
command.getCallback().onSuccess(null);
}
private void resetState() {
channelIdentifier = null;
signedEncryptedBallot = null;
}
}

View File

@ -0,0 +1,106 @@
package meerkat.voting.output;
import com.google.protobuf.ByteString;
import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.Voting.*;
import meerkat.voting.output.outputcommands.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A toy OutputDevice class
* outputs everything simply to the System console
*/
public class SystemConsoleOutputDevice extends AsyncRunnableOutputDevice {
private static final Logger logger = LoggerFactory.getLogger(SystemConsoleOutputDevice.class);
public SystemConsoleOutputDevice () {
super();
logger.info("A SystemConsoleOutputDevice is constructed");
}
/**
* Committing to the ballot.
* Simply prints to the output stream all the details in the CommitOutputCommand.
* @param command details to commit to, and the callback to call when finished
*/
public void doCommitToBallot(CommitOutputCommand command) {
logger.debug("entered method doCommitToBallot");
PlaintextBallot plaintextBallot = command.getPlaintext();
long plaintextSerialNumber = plaintextBallot.getSerialNumber();
System.out.println("Commitment of Ballot #" + plaintextSerialNumber);
System.out.println("(channel): ");
System.out.println(bytesToString(command.getChannelIdentifierByteString()));
System.out.println("(plaintext): ");
System.out.println(plaintextBallot);
SignedEncryptedBallot signedEncryptedBallot = command.getSignedEncryptedBallot();
long encryptedSerialNumber = signedEncryptedBallot.getEncryptedBallot().getSerialNumber();
System.out.println("Commitment of Ballot #" + encryptedSerialNumber + " (ciphertext):");
if (plaintextSerialNumber != encryptedSerialNumber) {
logger.error("plaintext and encryption serial numbers do not match!! plaintext# = " +
plaintextSerialNumber + ", ciphertext# = " + encryptedSerialNumber);
}
ByteString encryptedData = signedEncryptedBallot.getEncryptedBallot().getData().getData();
System.out.println(bytesToString(encryptedData));
command.getCallback().onSuccess(null);
}
/**
* auditing the ballot.
* prints to the output stream the ballot secrets (the encryption randomness and its proof of random generation)
* @param command An auditing command with the callback to finally call
*/
public void doAudit(AuditOutputCommand command) {
logger.debug("entered method doAudit");
System.out.println("Auditing");
BallotSecrets ballotSecrets = command.getBallotSecrets();
printEncryptionRandomness(ballotSecrets.getEncryptionRandomness());
printRandomnessGenerationProof (ballotSecrets.getProof());
command.getCallback().onSuccess(null);
}
/**
* Casting the ballot (actually does nothing new)
* @param command a CastOutputCommand with the details and the callback
*/
public void doCastBallot(CastOutputCommand command) {
logger.debug("entered method doCastBallot");
System.out.println("Ballot finalized for casting!");
command.getCallback().onSuccess(null);
}
/**
* Canceling the ballot (actually does nothing new)
* @param command a CancelOutputCommand with the details and the callback
*/
public void doCancel(CancelOutputCommand command) {
logger.debug("entered method doCancel");
System.out.println("Ballot cancelled!");
command.getCallback().onSuccess(null);
}
private void printEncryptionRandomness (EncryptionRandomness encryptionRandomness) {
System.out.println("Encryption Randomness = ");
ByteString data = encryptionRandomness.getData();
System.out.println(bytesToString(data));
}
private void printRandomnessGenerationProof (RandomnessGenerationProof proof) {
System.out.println("Proof of randomness generation:");
ByteString data = proof.getData();
System.out.println(bytesToString(data));
}
/*
* Returns the UTF8 decoding of byte-string data
*/
private static String bytesToString(ByteString data) {
return data.toStringUtf8();
}
}

View File

@ -0,0 +1,22 @@
package meerkat.voting.output.outputcommands;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
/**
* This OutputCommand supplies the necessary details for outputting Audit information
*/
public class AuditOutputCommand extends OutputCommand<Void> {
private final BallotSecrets ballotSecrets;
public AuditOutputCommand(BallotSecrets ballotSecrets, ControllerCallback<Void> callback) {
super(callback);
this.ballotSecrets = ballotSecrets;
}
public BallotSecrets getBallotSecrets() {
return ballotSecrets;
}
}

View File

@ -0,0 +1,14 @@
package meerkat.voting.output.outputcommands;
import meerkat.voting.controller.callbacks.ControllerCallback;
/**
* This OutputCommand signals the output-device that it should Cancel the rest of the ballot output
*/
public class CancelOutputCommand extends OutputCommand<Void> {
public CancelOutputCommand(ControllerCallback<Void> callback) {
super(callback);
}
}

View File

@ -0,0 +1,14 @@
package meerkat.voting.output.outputcommands;
import meerkat.voting.controller.callbacks.ControllerCallback;
/**
* This OutputCommand signals the output-device that the voter wishes to Cast the ballot
*/
public class CastOutputCommand extends OutputCommand<Void> {
public CastOutputCommand(ControllerCallback<Void> callback) {
super(callback);
}
}

View File

@ -0,0 +1,34 @@
package meerkat.voting.output.outputcommands;
import com.google.protobuf.ByteString;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
/**
* This OutputCommand supplies the necessary details for outputting a commit to the ballot
*/
public class CommitOutputCommand extends OutputCommand<Void> {
private final PlaintextBallot plaintextBallot;
private final SignedEncryptedBallot signedEncryptedBallot;
public CommitOutputCommand(PlaintextBallot plaintextBallot,
SignedEncryptedBallot signedEncryptedBallot,
ControllerCallback<Void> callback) {
super(callback);
this.plaintextBallot = plaintextBallot;
this.signedEncryptedBallot = signedEncryptedBallot;
}
public ByteString getChannelIdentifierByteString() {
return plaintextBallot.getChannelIdentifier();
}
public PlaintextBallot getPlaintext() {
return plaintextBallot;
}
public SignedEncryptedBallot getSignedEncryptedBallot() {
return signedEncryptedBallot;
}
}

View File

@ -0,0 +1,18 @@
package meerkat.voting.output.outputcommands;
import meerkat.voting.controller.callbacks.ControllerCallback;
/**
* Base class for the commands to put in the output-device queue
*/
public abstract class OutputCommand<T> {
protected final ControllerCallback<T> callback;
protected OutputCommand(ControllerCallback<T> callback) {
this.callback = callback;
}
public ControllerCallback<T> getCallback () {
return callback;
}
}

View File

@ -0,0 +1,49 @@
package meerkat.voting.storage;
import meerkat.protobuf.Voting.*;
import java.io.IOException;
import java.util.Map;
/**
* An interface for the storage component of the voting booth
*/
public interface StorageManager {
/**
* Detect whether an administration key is inserted to the machine. This determines if we gointo the set-up flow or the voting session flow.
* @return True is a hardware key is inserted. False if not.
*/
public boolean isAdminHardwareKeyInserted();
/**
* load the election params from the storage.
* @return the current election params
* @throws IOException
*/
public ElectionParams readElectionParams () throws IOException;
/**
* write the election parameters protobuf to the storage
* @param params ElectionParams protobuf to save
* @throws IOException
*/
public void writeElectionParams(ElectionParams params) throws IOException;
public Map<String, UIElement> readSystemMessages() throws IOException;
// These are just static key identifiers for accessing the matching System Messages in the message map
public final static String WAIT_FOR_COMMIT_MESSAGE = "waitForCommit";
public final static String WAIT_FOR_AUDIT_MESSAGE = "waitForAudit";
public final static String WAIT_FOR_CAST_MESSAGE = "waitForCast";
public final static String RESTART_VOTING_BUTTON = "restartVotingButton";
public final static String UNRECOGNIZED_FINALIZE_RESPONSE_MESSAGE = "unrecognizedFinalizeResponse";
public final static String UNSUCCESSFUL_CHANNEL_CHOICE_MESSAGE = "unsuccessfulChannelChoice";
public final static String OUTPUT_DEVICE_FAILURE_MESSAGE = "outputDeviceFailure";
public final static String UNSUCCESSFUL_VOTING_MESSAGE = "unsuccessfulVoting";
public final static String SOMETHING_WRONG_MESSAGE = "somethingWrong";
public final static String ENCRYPTION_FAILED_MESSAGE = "encryptionFailed";
public final static String RETRY_BUTTON = "retryButton";
public final static String CANCEL_VOTE_BUTTON = "cancelVoteButton";
}

Some files were not shown because too many files have changed in this diff Show More