Merge branch 'master' of http://cs.idc.ac.il/rhodecode/meerkat/meerkat-java into voting-booth-gui
commit
c6f2ffff9b
|
@ -259,5 +259,3 @@ publishing {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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.+'
|
||||
|
|
|
@ -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()}).
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 Fiat–Shamir heuristic
|
||||
*/
|
||||
public Prover(Random rand,ECElGamalEncryption encryptor,RandomOracle randomOracle) {
|
||||
|
||||
this.rand = rand;
|
||||
this.encryptor = encryptor;
|
||||
this.randomOracle = randomOracle;
|
||||
this.group = this.encryptor.getGroup();
|
||||
this.g = group.getGenerator();
|
||||
this.h = this.encryptor.getElGamalPK().getPK();
|
||||
this.organizer = new ElGamalProofOrganizer(group,g,h);
|
||||
this.groupOrderUpperBound = group.orderUpperBound();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in1
|
||||
* @param in2
|
||||
* @param out1 - if sw then out1 = rerandomize(in2,r2) else out1 = rerandomize(in1,r1)
|
||||
* @param out2 - if sw then out2 = rerandomize(in1,r1) else out1 = rerandomize(in2,r2)
|
||||
* @param sw - flag
|
||||
* @param i - column of in1 and out1 in encryption table
|
||||
* @param j - column of in2 and out2 in encryption table
|
||||
* @param layer - row of in1,in2 in encryption table
|
||||
* @param r1
|
||||
* @param r2
|
||||
* @return - a valid ZKP that indeed out1,out2 calculated as required
|
||||
* @throws InvalidProtocolBufferException
|
||||
*/
|
||||
public Mixing.ZeroKnowledgeProof prove(Crypto.RerandomizableEncryptedMessage in1,
|
||||
Crypto.RerandomizableEncryptedMessage in2,
|
||||
Crypto.RerandomizableEncryptedMessage out1,
|
||||
Crypto.RerandomizableEncryptedMessage out2,
|
||||
boolean sw,int i,int j, int layer,
|
||||
Crypto.EncryptionRandomness r1,
|
||||
Crypto.EncryptionRandomness r2) throws InvalidProtocolBufferException {
|
||||
Mixing.ZeroKnowledgeProof.OrProof first,second,third,fourth;
|
||||
|
||||
ElGamalProofOrganizer.ElGamalProofInput input = organizer.createProofInput(in1,in2,out1,out2,r1,r2,sw);
|
||||
|
||||
first = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.first));
|
||||
second = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.second));
|
||||
third = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.third));
|
||||
fourth = createOrProofElGamal(input.getOrProofInput(ElGamalProofOrganizer.OrProofOrder.fourth));
|
||||
|
||||
Mixing.ZeroKnowledgeProof.Location location = Mixing.ZeroKnowledgeProof.Location.newBuilder()
|
||||
.setI(i)
|
||||
.setJ(j)
|
||||
.setLayer(layer)
|
||||
.build();
|
||||
|
||||
Mixing.ZeroKnowledgeProof result = Mixing.ZeroKnowledgeProof.newBuilder()
|
||||
.setFirst(first)
|
||||
.setSecond(second)
|
||||
.setThird(third)
|
||||
.setFourth(fourth)
|
||||
.setLocation(location)
|
||||
.build();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fiat–Shamir heuristic
|
||||
* @param input - protobuf contains all parameters from the first step of the current prove
|
||||
* @param randomOracle
|
||||
* @return randomOracle.hash(input)
|
||||
*/
|
||||
public static BigInteger hash(Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle input, RandomOracle randomOracle) {
|
||||
byte[] arr = input.toByteArray();
|
||||
return new BigInteger(1,randomOracle.hash(arr,arr.length));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param orProofInput
|
||||
* @return ZKP OrProof: there exists x s.t (g1 ^ x == h1 and g2 ^ x == h2) or (g1' ^ x == h1 and g2' ^ x == h2)
|
||||
* assuming DLog is hard in this.group then that proves x is known for the meerkat.mixer.prover
|
||||
*/
|
||||
private Mixing.ZeroKnowledgeProof.OrProof createOrProofElGamal(ElGamalProofOrganizer.OrProofInput orProofInput) {
|
||||
|
||||
ECPoint g1 = orProofInput.g1;
|
||||
ECPoint h1 = orProofInput.h1;
|
||||
ECPoint g2 = orProofInput.g2;
|
||||
ECPoint h2 = orProofInput.h2;
|
||||
|
||||
ECPoint g1Tag = orProofInput.g1Tag;
|
||||
ECPoint h1Tag = orProofInput.h1Tag;
|
||||
ECPoint g2Tag = orProofInput.g2Tag;
|
||||
ECPoint h2Tag = orProofInput.h2Tag;
|
||||
|
||||
BigInteger r = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound);
|
||||
BigInteger c1,c2,z,zTag;
|
||||
ECPoint u,v,uTag,vTag;
|
||||
Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle forRandomOracle;
|
||||
|
||||
|
||||
switch (orProofInput.flag) {
|
||||
case left:
|
||||
c2 = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound);
|
||||
zTag = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound);
|
||||
//step 1
|
||||
u = group.multiply(g1, r);
|
||||
v = group.multiply(g2, r);
|
||||
uTag = group.add(group.multiply(g1Tag, zTag), group.negate(group.multiply(h1Tag, c2)));
|
||||
vTag = group.add(group.multiply(g2Tag, zTag), group.negate(group.multiply(h2Tag, c2)));
|
||||
//step 2
|
||||
// c1 = (hash(input + step1) + group size - c2)% group size
|
||||
forRandomOracle =
|
||||
Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle.newBuilder()
|
||||
.setG1(ByteString.copyFrom(orProofInput.g1Encoded))
|
||||
.setH1(ByteString.copyFrom(orProofInput.h1Encoded))
|
||||
.setG2(ByteString.copyFrom(orProofInput.g2Encoded))
|
||||
.setH2(ByteString.copyFrom(orProofInput.h2Encoded))
|
||||
.setG1Tag(ByteString.copyFrom(orProofInput.g1TagEncoded))
|
||||
.setH1Tag(ByteString.copyFrom(orProofInput.h1TagEncoded))
|
||||
.setG2Tag(ByteString.copyFrom(orProofInput.g2TagEncoded))
|
||||
.setH2Tag(ByteString.copyFrom(orProofInput.h2TagEncoded))
|
||||
.setU(ByteString.copyFrom(group.encode(u)))
|
||||
.setV(ByteString.copyFrom(group.encode(v)))
|
||||
.setUTag(ByteString.copyFrom(group.encode(uTag)))
|
||||
.setVTag(ByteString.copyFrom(group.encode(vTag)))
|
||||
.build();
|
||||
c1 = hash(forRandomOracle,randomOracle).add(group.orderUpperBound().subtract(c2)).mod(groupOrderUpperBound);
|
||||
//step 3
|
||||
//z = (r + c1 * x) % group size;
|
||||
z = r.add(c1.multiply(encryptor.extractRandomness(orProofInput.x))).mod(groupOrderUpperBound);
|
||||
break;
|
||||
case right:
|
||||
c1 = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound);
|
||||
z = encryptor.extractRandomness(encryptor.generateRandomness(rand)).mod(groupOrderUpperBound);
|
||||
//step 1
|
||||
uTag = group.multiply(g1Tag, r);
|
||||
vTag = group.multiply(g2Tag, r);
|
||||
u = group.add(group.multiply(g1, z), group.negate(group.multiply(h1, c1)));
|
||||
v = group.add(group.multiply(g2, z), group.negate(group.multiply(h2, c1)));
|
||||
//step 2
|
||||
// c1 = (hash(input + step1) + group size - c1)% group size
|
||||
forRandomOracle =
|
||||
Mixing.ZeroKnowledgeProof.OrProof.ForRandomOracle.newBuilder()
|
||||
.setG1(ByteString.copyFrom(orProofInput.g1Encoded))
|
||||
.setH1(ByteString.copyFrom(orProofInput.h1Encoded))
|
||||
.setG2(ByteString.copyFrom(orProofInput.g2Encoded))
|
||||
.setH2(ByteString.copyFrom(orProofInput.h2Encoded))
|
||||
.setG1Tag(ByteString.copyFrom(orProofInput.g1TagEncoded))
|
||||
.setH1Tag(ByteString.copyFrom(orProofInput.h1TagEncoded))
|
||||
.setG2Tag(ByteString.copyFrom(orProofInput.g2TagEncoded))
|
||||
.setH2Tag(ByteString.copyFrom(orProofInput.h2TagEncoded))
|
||||
.setU(ByteString.copyFrom(group.encode(u)))
|
||||
.setV(ByteString.copyFrom(group.encode(v)))
|
||||
.setUTag(ByteString.copyFrom(group.encode(uTag)))
|
||||
.setVTag(ByteString.copyFrom(group.encode(vTag)))
|
||||
.build();
|
||||
c2 = hash(forRandomOracle,randomOracle).add(group.orderUpperBound().subtract(c1)).mod(groupOrderUpperBound);
|
||||
//step 3
|
||||
//zTag = (r + c2 * x) % group size;
|
||||
zTag = r.add(c2.multiply(encryptor.extractRandomness(orProofInput.x))).mod(groupOrderUpperBound);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return Mixing.ZeroKnowledgeProof.OrProof.newBuilder()
|
||||
.setG1(forRandomOracle.getG1())
|
||||
.setH1(forRandomOracle.getH1())
|
||||
.setG2(forRandomOracle.getG2())
|
||||
.setH2(forRandomOracle.getH2())
|
||||
.setG1Tag(forRandomOracle.getG1())
|
||||
.setH1Tag(forRandomOracle.getH1Tag())
|
||||
.setG2Tag(forRandomOracle.getG2Tag())
|
||||
.setH2Tag(forRandomOracle.getH2Tag())
|
||||
.setU(forRandomOracle.getU())
|
||||
.setV(forRandomOracle.getV())
|
||||
.setUTag(forRandomOracle.getUTag())
|
||||
.setVTag(forRandomOracle.getVTag())
|
||||
.setC1(ByteString.copyFrom(c1.toByteArray()))
|
||||
.setC2(ByteString.copyFrom(c2.toByteArray()))
|
||||
.setZ(ByteString.copyFrom(z.toByteArray()))
|
||||
.setZTag(ByteString.copyFrom(zTag.toByteArray()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
//
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue