Fixed the Selector classes.

Fixed the crypto classes to handle signatures as well.
Some more other fixes
vbdev2
Hai Brenner 2016-06-07 16:15:08 +03:00
parent 94f3920e6d
commit 76d3fdeac2
18 changed files with 259 additions and 278 deletions

View File

@ -71,6 +71,12 @@ message EncryptedBallot {
RerandomizableEncryptedMessage data = 2;
}
message SignedEncryptedBallot {
EncryptedBallot encrypted_ballot = 1;
bytes signer_id = 2;
Signature signature = 3;
}
message BallotSecrets {
PlaintextBallot plaintext_ballot = 1;
@ -126,3 +132,16 @@ message ElectionParams {
// Data required in order to access the Bulletin Board Servers
BulletinBoardClientParams bulletinBoardClientParams = 8;
}
message Category {
repeated uint32 questionIndex = 1;
}
message CategoryChooser {
repeated Category category = 1;
}
message SimpleCategoriesSelectionData {
Category shared_defaults = 1;
repeated CategoryChooser categoryChooser = 2;
}

View File

@ -2,12 +2,11 @@ package meerkat.voting.controller;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.selector.QuestionSelector;
import meerkat.voting.encryptor.VBCryptoManager;
import meerkat.voting.ui.VotingBoothUI;
import meerkat.voting.encryptor.VotingBoothEncryptor;
import meerkat.voting.output.BallotOutputDevice;
import meerkat.voting.storage.StorageManager;
import java.util.ArrayList;
import java.util.List;
@ -19,12 +18,12 @@ 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 vbEncryptor the encryption module
* @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,
VotingBoothEncryptor vbEncryptor,
VBCryptoManager vbCrypto,
VotingBoothUI vbUI,
StorageManager vbStorageManager);

View File

@ -1,17 +1,19 @@
package meerkat.voting.controller;
import com.google.protobuf.ByteString;
import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.*;
import meerkat.voting.controller.commands.*;
import meerkat.voting.controller.selector.QuestionSelector;
import meerkat.voting.encryptor.VotingBoothEncryptor;
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.concurrent.LinkedBlockingQueue;
@ -21,7 +23,7 @@ import java.util.concurrent.LinkedBlockingQueue;
public class VotingBoothImpl implements VotingBoothController {
private BallotOutputDevice outputDevice;
private VotingBoothEncryptor encryptor;
private VBCryptoManager crypto;
private VotingBoothUI ui;
private StorageManager storageManager;
private List<BallotQuestion> questionsForChoosingChannel;
@ -49,12 +51,12 @@ public class VotingBoothImpl implements VotingBoothController {
@Override
public void init(BallotOutputDevice outputDevice,
VotingBoothEncryptor vbEncryptor,
VBCryptoManager vbCrypto,
VotingBoothUI vbUI,
StorageManager vbStorageManager) {
logger.info("init is called");
this.outputDevice = outputDevice;
this.encryptor = vbEncryptor;
this.crypto = vbCrypto;
this.ui = vbUI;
this.storageManager = vbStorageManager;
}
@ -105,7 +107,7 @@ public class VotingBoothImpl implements VotingBoothController {
logger.info("callShutDown command has been called");
shutDownHasBeenCalled = true;
queue.clear();
queue.add(new ShutDownCommand());
ui.callShutDown();
}
private void handleSingleTask (ControllerCommand task) {
@ -138,14 +140,6 @@ public class VotingBoothImpl implements VotingBoothController {
else if (task instanceof ReportErrorCommand) {
doReportErrorAndForceRestart((ReportErrorCommand)task);
}
else if (task instanceof ShutDownCommand) {
// wasShutDownCalled is now true. Do nothing.
// The program will not access the command queue anymore, and will exit normally
if (!wasShutDownCalled()) {
logger.warn("Received a ShutDownCommand command. At this point shutDownHasBeenCalled flag should have been True, but it's not...");
shutDownHasBeenCalled = true;
}
}
else {
logger.error("handleSingleTask: unknown type of ControllerCommand received: " + task.getClass().getName());
doReportErrorAndForceRestart(SystemMessages.getSomethingWrongMessage());
@ -208,7 +202,7 @@ public class VotingBoothImpl implements VotingBoothController {
state.stateIdentifier = VBState.ANSWER_QUESTIONS;
List<BallotAnswer> channelChoiceAnswers = task.channelChoiceAnswers;
state.channelIdentifier = questionSelector.getChannelIdentifier(channelChoiceAnswers);
state.channelSpecificQuestions = questionSelector.selectQuestionsForVoter(channelChoiceAnswers);
state.channelSpecificQuestions = questionSelector.selectQuestionsForVoter(state.channelIdentifier);
ui.askVoterQuestions(state.channelSpecificQuestions,
new VotingCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
@ -229,7 +223,7 @@ public class VotingBoothImpl implements VotingBoothController {
state.currentBallotSerialNumber,
this.queue));
outputDevice.commitToBallot(state.plaintextBallot,
state.encryptedBallot,
state.signedEncryptedBallot,
new OutputDeviceCallback(generateRequestIdentifier(), state.currentBallotSerialNumber, this.queue));
}
else {
@ -243,8 +237,14 @@ public class VotingBoothImpl implements VotingBoothController {
.setSerialNumber(task.getBallotSerialNumber())
.addAllAnswers(task.getVotingAnswers())
.build();
VotingBoothEncryptor.EncryptionAndSecrets encryptionAndSecrets = encryptor.encrypt(state.plaintextBallot);
state.encryptedBallot = encryptionAndSecrets.getEncryptedBallot();
EncryptionAndSecrets encryptionAndSecrets = null;
try {
encryptionAndSecrets = crypto.encrypt(state.plaintextBallot);
}
catch (SignatureException | IOException e) {
// TODO: handle exception
}
state.signedEncryptedBallot = encryptionAndSecrets.getSignedEncryptedBallot();
state.secrets = encryptionAndSecrets.getSecrets();
}
@ -287,20 +287,20 @@ public class VotingBoothImpl implements VotingBoothController {
private class ControllerState {
public VBState stateIdentifier;
public int channelIdentifier;
public byte[] channelIdentifier;
public List<BallotQuestion> channelSpecificQuestions;
public PlaintextBallot plaintextBallot;
public EncryptedBallot encryptedBallot;
public SignedEncryptedBallot signedEncryptedBallot;
public BallotSecrets secrets;
public int lastRequestIdentifier;
public long currentBallotSerialNumber;
public ControllerState () {
plaintextBallot = null;
encryptedBallot = null;
signedEncryptedBallot = null;
secrets = null;
lastRequestIdentifier = -1;
channelIdentifier = -1;
channelIdentifier = null;
channelSpecificQuestions = null;
currentBallotSerialNumber = 0;
}
@ -311,7 +311,7 @@ public class VotingBoothImpl implements VotingBoothController {
}
public void clearCiphertext () {
encryptedBallot = null;
signedEncryptedBallot = null;
secrets = null;
}
}

View File

@ -23,21 +23,21 @@ public class ChannelChoiceCallback extends ControllerCallback<List<BallotAnswer>
@Override
public void onSuccess(List<BallotAnswer> result) {
if (result.size() != 0) {
logger.debug("callback for channel choice returned success");
enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
else {
logger.debug("ChannelChoiceCallback got a cancellation response: " + result);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
logger.debug("callback for channel choice returned success");
enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
@Override
public void onFailure(Throwable t) {
logger.error("channel choice initiated a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnsuccessfulChannelChoiceMessage()));
if (t instanceof VoterCancelException) {
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(),
SystemMessages.getUnsuccessfulChannelChoiceMessage()));
}
}
}

View File

@ -0,0 +1,8 @@
package meerkat.voting.controller.callbacks;
/**
* Created by hai on 06/06/16.
*/
public class VoterCancelException extends Exception{
//
}

View File

@ -23,21 +23,21 @@ public class VotingCallback extends ControllerCallback<List<BallotAnswer>> {
@Override
public void onSuccess(List<BallotAnswer> result) {
if (result.size() != 0) {
logger.debug("callback for voting returned success");
enqueueCommand(new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
else {
logger.debug("VotingCallback got a cancellation response: " + result);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
logger.debug("callback for voting returned success");
enqueueCommand(new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
}
@Override
public void onFailure(Throwable t) {
logger.error("voting initiated a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(),
SystemMessages.getUnsuccessfulVotingMessage()));
if (t instanceof VoterCancelException) {
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(),
SystemMessages.getUnsuccessfulVotingMessage()));
}
}
}

View File

@ -1,10 +0,0 @@
package meerkat.voting.controller.commands;
/**
* Created by hai on 11/04/16.
*/
public class ShutDownCommand extends ControllerCommand {
public ShutDownCommand() {
super(0, 0);
}
}

View File

@ -8,11 +8,8 @@ import java.util.List;
*/
public interface QuestionSelector {
public void setAllBallotQuestions (List<BallotQuestion> allBallotQuestions);
public byte[] getChannelIdentifier (List<BallotAnswer> channelChoiceAnswers);
public void setSelectionData(SelectionData data);
public List<BallotQuestion> selectQuestionsForVoter (byte[] channelIdentifier);
public int getChannelIdentifier (List<BallotAnswer> channelChoiceAnswers);
public List<BallotQuestion> selectQuestionsForVoter (List<BallotAnswer> channelChoiceAnswers);
}

View File

@ -1,7 +0,0 @@
package meerkat.voting.controller.selector;
/**
* Created by hai on 11/05/16.
*/
public class SelectionData {
}

View File

@ -1,97 +0,0 @@
package meerkat.voting.controller.selector;
/**
* This class is the SelectionData used to initialize a SimpleListCategoriesSelector.
* It holds a table of question categories.
* Every answer in the channel choice phase dictates a category of questions to include in the ballot.
* This class is simply only the data class of these categories.
* Specifically the questionSelectionsByAnswer member is this categories table:
* questionSelectionsByAnswer[questionNumber][answerNumber] is an array of question indices to include in ballot
* whenever the voter answered answerNumber on question questionNumber in the channel choice phase.
* Also the sharedDefaults member is a category of question indices to include for EVERY voter,
* regardless of his channel choice answers
*/
public class SimpleCategoriesData extends SelectionData{
private int[] sharedDefaults; // a category of questions to include for every voter
// the categories
// first index is the question number, the second is the answer number. the value is an array of question indices in this category
private int[][][] questionSelectionsByAnswer;
private int maxQuestionNumber;
public SimpleCategoriesData() {
sharedDefaults = new int[0];
questionSelectionsByAnswer = new int[0][][];
maxQuestionNumber = -1;
}
public void setSharedDefaults(int[] sharedDefaultsIndices) {
this.sharedDefaults = sharedDefaultsIndices.clone();
maxQuestionNumber = Math.max(maxQuestionNumber, maxIntInArray(sharedDefaultsIndices));
}
public int[] getSharedDefaults() {
return sharedDefaults;
}
public void setItem(int questionNumber, int answerNumber, int[] questionIndices) {
if (questionNumber >= questionSelectionsByAnswer.length) {
int[][][] tmp = new int[questionNumber+1][][];
System.arraycopy(questionSelectionsByAnswer, 0, tmp, 0, questionSelectionsByAnswer.length);
tmp[questionNumber] = new int[0][];
questionSelectionsByAnswer = tmp;
}
if (answerNumber >= questionSelectionsByAnswer[questionNumber].length) {
int[][] tmp = new int[answerNumber+1][];
System.arraycopy(questionSelectionsByAnswer[questionNumber], 0,
tmp, 0, questionSelectionsByAnswer[questionNumber].length);
questionSelectionsByAnswer[questionNumber] = tmp;
}
questionSelectionsByAnswer[questionNumber][answerNumber] = questionIndices.clone();
maxQuestionNumber = Math.max(maxQuestionNumber, maxIntInArray(questionIndices));
}
public int[] getItem(int questionNumber, int answerNumber) {
return questionSelectionsByAnswer[questionNumber][answerNumber];
}
public int getMaxQuestionNumber() {
return maxQuestionNumber;
}
private static int maxIntInArray(int[] arr) {
int m = -1;
for (int i: arr) {
m = Math.max(i , m);
}
return m;
}
public String toString () {
String s = "SimpleCategoriesData:\n";
s += "shared : " + arrayToString(sharedDefaults) + "\n";
s += "map : \n";
for (int questionNumber = 0; questionNumber < questionSelectionsByAnswer.length; ++questionNumber) {
for (int answerNumber = 0; answerNumber < questionSelectionsByAnswer[questionNumber].length; ++answerNumber) {
s += "(" + questionNumber + ", " + answerNumber + ") -> " +
arrayToString(questionSelectionsByAnswer[questionNumber][answerNumber]) + "\n";
}
}
return s;
}
private String arrayToString (int[] a) {
if (a.length == 0) {
return "[]";
}
String s = "[" + a[0];
for (int i = 1; i < a.length; ++i) {
s += ", " + a[i];
}
return s + "]";
}
}

View File

@ -1,89 +1,89 @@
package meerkat.voting.controller.selector;
import meerkat.protobuf.Voting.BallotAnswer;
import meerkat.protobuf.Voting.BallotQuestion;
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 questions.
* Data of categories is initialized and stored by a SimpleCategoriesData object.
* 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 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);
private BallotQuestion[] allBallotQuestions;
private SimpleCategoriesData selectionData;
private boolean[] isSelected;
private final BallotQuestion[] allBallotQuestions;
private final int[] sharedDefaults;
private final int[][][] categoryChoosers;
private final static byte QUESTION_SELECTED = (byte)1;
private final static byte QUESTION_NOT_SELECTED = (byte)0;
public SimpleListCategoriesSelector() {
this.allBallotQuestions = null;
this.selectionData = null;
}
@Override
public void setAllBallotQuestions(List<BallotQuestion> allBallotQuestions) {
this.allBallotQuestions = (BallotQuestion[])allBallotQuestions.toArray();
isSelected = new boolean[this.allBallotQuestions.length];
assertDataValid();
}
@Override
public void setSelectionData(SelectionData data) {
if (! (data instanceof SimpleCategoriesData)) {
String errorMessage = "Initialization of SimpleListCategoriesSelector with wrong object type";
logger.error(errorMessage);
throw new RuntimeException(errorMessage);
public SimpleListCategoriesSelector(List<BallotQuestion> allBallotQuestions, SimpleCategoriesSelectionData data) {
this.allBallotQuestions = new BallotQuestion[allBallotQuestions.size()];
allBallotQuestions.toArray(this.allBallotQuestions);
sharedDefaults = listToIntArray(data.getSharedDefaults().getQuestionIndexList());
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;
}
this.selectionData = (SimpleCategoriesData)data;
categoryChoosers = selectionDataTmp;
assertDataValid();
}
private void assertDataValid () {
if (selectionData != null && allBallotQuestions != null) {
int questionsLength = allBallotQuestions.length;
int maxQuestionNumber = selectionData.getMaxQuestionNumber();
if (maxQuestionNumber >= questionsLength) {
String errorMessage = "Selection data refers to question index " + maxQuestionNumber + " while we have only " + questionsLength + " questions totally";
logger.error(errorMessage);
throw new IndexOutOfBoundsException(errorMessage);
int questionsLength = allBallotQuestions.length;
int maxQuestionIndex = -1;
for (int[][] categoryChooser: categoryChoosers) {
for (int[] category: categoryChooser) {
for (int index: category) {
maxQuestionIndex = Math.max(maxQuestionIndex, index);
}
}
}
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);
}
}
@Override
public int getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) {
return 0;
}
public byte[] getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) {
byte[] isSelected = new byte[allBallotQuestions.length];
java.util.Arrays.fill(isSelected, QUESTION_NOT_SELECTED);
@Override
public List<BallotQuestion> selectQuestionsForVoter(List<BallotAnswer> channelChoiceAnswers) {
java.util.Arrays.fill(isSelected, false);
markSelected(selectionData.getSharedDefaults());
markSelectionsOfVoterChannelChoice ((BallotAnswer[])channelChoiceAnswers.toArray());
int[] questionIndices = extractSelectedIndices();
List<BallotQuestion> selectedQuestions = new ArrayList<>();
for (int questionIndex: questionIndices) {
selectedQuestions.add(allBallotQuestions[questionIndex]);
for (int i: sharedDefaults) {
isSelected[i] = QUESTION_SELECTED;
}
return selectedQuestions;
}
private void markSelectionsOfVoterChannelChoice(BallotAnswer[] channelChoiceAnswers) {
for (int q = 0; q < channelChoiceAnswers.length; ++q) {
BallotAnswer ballotAnswer = channelChoiceAnswers[q];
assertAnswerLengthIsOne(ballotAnswer, q);
assertKeyInSelectionData(q, (int)ballotAnswer.getAnswer(0));
markSelected(selectionData.getItem(q, (int)ballotAnswer.getAnswer(0)));
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;
}
private void assertAnswerLengthIsOne (BallotAnswer ballotAnswer, int questionNumber) {
@ -98,35 +98,23 @@ public class SimpleListCategoriesSelector implements QuestionSelector {
}
}
private void assertKeyInSelectionData(int questionNumber, int answerNumber) {
int[] questionListToAdd = selectionData.getItem(questionNumber, answerNumber);
if (null == questionListToAdd) {
String errorMessage = "SimpleListCategoriesSelector could not resolve answer number " + answerNumber + " for question number " + questionNumber;
logger.error(errorMessage);
throw new IllegalArgumentException(errorMessage);
}
}
private void markSelected (int[] questionIndices) {
for (int i: questionIndices) {
isSelected[i] = true;
}
}
private int[] extractSelectedIndices() {
int nSelected = 0;
for (boolean b: isSelected) {
if (b) {
nSelected++;
@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]);
}
}
int[] res = new int[nSelected];
int currIndex = 0;
for (int questionNumber = 0; questionNumber < isSelected.length; ++questionNumber) {
if (isSelected[questionNumber]) {
res[currIndex] = questionNumber;
currIndex++;
}
return selectedQuestions;
}
private int[] listToIntArray(List<Integer> l) {
int[] res = new int[l.size()];
int index = 0;
for (Integer i: l) {
res[index] = i;
++index;
}
return res;
}

View File

@ -2,25 +2,27 @@ 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
*/
public interface VotingBoothEncryptor {
public interface VBCryptoManager {
/**
* A simple class for pairing EncrypedBallot together with its matching BallotSecrets
*/
public class EncryptionAndSecrets {
private final EncryptedBallot encryptedBallot;
private final SignedEncryptedBallot signedEncryptedBallot;
private final BallotSecrets secrets;
public EncryptionAndSecrets (EncryptedBallot encryptedBallot, BallotSecrets secrets) {
this.encryptedBallot = encryptedBallot;
public EncryptionAndSecrets (SignedEncryptedBallot encryptedBallot, BallotSecrets secrets) {
this.signedEncryptedBallot = encryptedBallot;
this.secrets = secrets;
}
public EncryptedBallot getEncryptedBallot () {
return encryptedBallot;
public SignedEncryptedBallot getSignedEncryptedBallot() {
return signedEncryptedBallot;
}
public BallotSecrets getSecrets() {
@ -34,8 +36,7 @@ public interface VotingBoothEncryptor {
* @param plaintextBallot - all plaintext ballot info of the voter
* @return an encryption of the ballot
*/
public EncryptionAndSecrets encrypt (PlaintextBallot plaintextBallot);
public EncryptionAndSecrets encrypt (PlaintextBallot plaintextBallot) throws SignatureException, IOException;
//TODO: probably needs some key generation methods as well
}

View File

@ -0,0 +1,67 @@
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;
/**
* Created by hai on 07/06/16.
*/
public class VBCryptoManagerImpl implements VBCryptoManager {
protected final static Logger logger = LoggerFactory.getLogger(VBCryptoManagerImpl.class);
private final Random random;
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)
.setSignerId(digitalSignature.getSignerID())
.setSignature(digitalSignature.sign())
.build();
return new EncryptionAndSecrets(signedEncryptedBallot, secrets);
}
catch (IOException e) {
logger.error("encrypt: the encryption component has thrown an exception: " + e);
throw e;
}
catch (SignatureException e) {
logger.error("encrypt: the signature component has thrown an exception: " + e);
throw e;
}
}
}

View File

@ -1,14 +0,0 @@
package meerkat.voting.encryptor;
import meerkat.protobuf.Voting.*;
/**
* Created by hai on 26/04/16.
*/
public class VBEncryptorImpl implements VotingBoothEncryptor {
@Override
public EncryptionAndSecrets encrypt(PlaintextBallot plaintextBallot) {
throw new UnsupportedOperationException();
}
}

View File

@ -15,7 +15,7 @@ public interface BallotOutputDevice {
* @param callback - a callback object which expects no return value
*/
public void commitToBallot(PlaintextBallot plaintextBallot,
EncryptedBallot encryptedBallot,
SignedEncryptedBallot encryptedBallot,
FutureCallback<Void> callback);
/**

View File

@ -29,18 +29,18 @@ public class SystemConsoleOutputDevice implements BallotOutputDevice {
@Override
public void commitToBallot(PlaintextBallot plaintextBallot,
EncryptedBallot encryptedBallot,
SignedEncryptedBallot signedEncryptedBallot,
FutureCallback<Void> callback) {
logger.debug("entered method commitToBallot");
long plaintextSerialNumber = plaintextBallot.getSerialNumber();
System.out.println("Commitment of Ballot #" + plaintextSerialNumber + " (plaintext):");
long encryptedSerialNumber = encryptedBallot.getSerialNumber();
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 = encryptedBallot.getData().getData();
ByteString encryptedData = signedEncryptedBallot.getEncryptedBallot().getData().getData();
System.out.println(bytesToString(encryptedData));
callback.onSuccess(null);
}

View File

@ -26,6 +26,9 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
private final int tickDurationInMillisec = 10;
private Date startWaitingTime;
private volatile boolean shutDownHasBeenCalled;
public SystemConsoleUI() {
logger = LoggerFactory.getLogger(SystemConsoleUI.class);
@ -36,12 +39,15 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
startWaitingTime = null;
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TickerTimerTask(queue), new Date(), tickDurationInMillisec);
shutDownHasBeenCalled = false;
}
@Override
public void run () {
logger.info("entered the voting flow");
while (true) {
while (! wasShutDownCalled()) {
try {
UICommand command = queue.take();
handleSingleCommand(command);
@ -53,6 +59,13 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
}
@Override
public void callShutDown() {
logger.info("callShutDown command has been called");
shutDownHasBeenCalled = true;
queue.clear();
}
private void handleSingleCommand(UICommand command) {
if (!(command instanceof TickCommand)) {
if (startWaitingTime != null) {
@ -142,6 +155,9 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
List<BallotAnswer> answers = askVoterForAnswers(command.getQuestions());
command.getCallback().onSuccess(answers);
}
catch (VoterCancelException e) {
command.getCallback().onFailure(e);
}
catch (IOException e) {
String errorMessage = "Channel choice failed due to IOException: " + e;
logger.error (errorMessage);
@ -163,6 +179,9 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
List<BallotAnswer> answers = askVoterForAnswers(command.getQuestions());
command.getCallback().onSuccess(answers);
}
catch (VoterCancelException e) {
command.getCallback().onFailure(e);
}
catch (IOException e) {
String errorMessage = "Asking voting questions failed due to IOException: " + e;
logger.error (errorMessage);
@ -320,7 +339,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
}
}
private List<BallotAnswer> askVoterForAnswers(List<BallotQuestion> questions) throws IOException {
private List<BallotAnswer> askVoterForAnswers(List<BallotQuestion> questions) throws VoterCancelException, IOException {
assertQuestionsAreValid (questions);
@ -335,7 +354,7 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
String s = readInputLine();
if ((s.equals("cancel") || s.equals("c")) || (index == 0 && (s.equals("back") || s.equals("b")))) {
return null;
throw new VoterCancelException();
}
else if (s.equals("back") || s.equals("b")) {
--index;
@ -400,4 +419,10 @@ public class SystemConsoleUI implements VotingBoothUI, Runnable {
return data.toStringUtf8();
}
private boolean wasShutDownCalled () {
return shutDownHasBeenCalled;
}
}

View File

@ -16,6 +16,11 @@ public interface VotingBoothUI {
AUDIT
}
/**
* shut down the UI
*/
public void callShutDown();
/**
* Starts a new session for a voter. Presents whatever initial info is decided to show at the beginning
* @param callback - a boolean future callback to return when done