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; RerandomizableEncryptedMessage data = 2;
} }
message SignedEncryptedBallot {
EncryptedBallot encrypted_ballot = 1;
bytes signer_id = 2;
Signature signature = 3;
}
message BallotSecrets { message BallotSecrets {
PlaintextBallot plaintext_ballot = 1; PlaintextBallot plaintext_ballot = 1;
@ -126,3 +132,16 @@ message ElectionParams {
// Data required in order to access the Bulletin Board Servers // Data required in order to access the Bulletin Board Servers
BulletinBoardClientParams bulletinBoardClientParams = 8; 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.protobuf.Voting.*;
import meerkat.voting.controller.selector.QuestionSelector; import meerkat.voting.controller.selector.QuestionSelector;
import meerkat.voting.encryptor.VBCryptoManager;
import meerkat.voting.ui.VotingBoothUI; import meerkat.voting.ui.VotingBoothUI;
import meerkat.voting.encryptor.VotingBoothEncryptor;
import meerkat.voting.output.BallotOutputDevice; import meerkat.voting.output.BallotOutputDevice;
import meerkat.voting.storage.StorageManager; import meerkat.voting.storage.StorageManager;
import java.util.ArrayList;
import java.util.List; 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 * 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 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 vbUI User interface in which the voter chooses his answers
* @param vbStorageManager storage component for handling files and USB sticks * @param vbStorageManager storage component for handling files and USB sticks
*/ */
public void init (BallotOutputDevice outputDevice, public void init (BallotOutputDevice outputDevice,
VotingBoothEncryptor vbEncryptor, VBCryptoManager vbCrypto,
VotingBoothUI vbUI, VotingBoothUI vbUI,
StorageManager vbStorageManager); StorageManager vbStorageManager);

View File

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

View File

@ -23,21 +23,21 @@ public class ChannelChoiceCallback extends ControllerCallback<List<BallotAnswer>
@Override @Override
public void onSuccess(List<BallotAnswer> result) { public void onSuccess(List<BallotAnswer> result) {
if (result.size() != 0) {
logger.debug("callback for channel choice returned success"); logger.debug("callback for channel choice returned success");
enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result)); enqueueCommand(new ChannelDeterminedCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
} }
else {
logger.debug("ChannelChoiceCallback got a cancellation response: " + result);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
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); logger.error("channel choice initiated a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(), enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(), getBallotSerialNumber(),
SystemMessages.getUnsuccessfulChannelChoiceMessage())); 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 @Override
public void onSuccess(List<BallotAnswer> result) { public void onSuccess(List<BallotAnswer> result) {
if (result.size() != 0) {
logger.debug("callback for voting returned success"); logger.debug("callback for voting returned success");
enqueueCommand(new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), result)); enqueueCommand(new EncryptAndCommitBallotCommand(getRequestIdentifier(), getBallotSerialNumber(), result));
} }
else {
logger.debug("VotingCallback got a cancellation response: " + result);
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
}
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
if (t instanceof VoterCancelException) {
logger.debug("VotingCallback got a cancellation response");
enqueueCommand(new RestartVotingCommand(getRequestIdentifier(), getBallotSerialNumber()));
}
else {
logger.error("voting initiated a failure: " + t); logger.error("voting initiated a failure: " + t);
enqueueCommand(new ReportErrorCommand(getRequestIdentifier(), enqueueCommand(new ReportErrorCommand(getRequestIdentifier(),
getBallotSerialNumber(), getBallotSerialNumber(),
SystemMessages.getUnsuccessfulVotingMessage())); 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 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; package meerkat.voting.controller.selector;
import meerkat.protobuf.Voting.BallotAnswer; import meerkat.protobuf.Voting.*;
import meerkat.protobuf.Voting.BallotQuestion;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.lang.Math;
/** /**
* A simple implementation of a QuestionSelector. * A simple implementation of a QuestionSelector.
* This implementation simply regards every single answer in the channel choice phase as an identifier of a category * 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. * 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 * 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 * 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) * is included if its index appears in any chosen category, or in the default category shared by all voters)
*/ */
public class SimpleListCategoriesSelector implements QuestionSelector { public class SimpleListCategoriesSelector implements QuestionSelector {
protected final static Logger logger = LoggerFactory.getLogger(SimpleListCategoriesSelector.class); protected final static Logger logger = LoggerFactory.getLogger(SimpleListCategoriesSelector.class);
private BallotQuestion[] allBallotQuestions;
private SimpleCategoriesData selectionData; private final BallotQuestion[] allBallotQuestions;
private boolean[] isSelected; 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; public SimpleListCategoriesSelector(List<BallotQuestion> allBallotQuestions, SimpleCategoriesSelectionData data) {
this.selectionData = null; 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;
@Override
public void setAllBallotQuestions(List<BallotQuestion> allBallotQuestions) {
this.allBallotQuestions = (BallotQuestion[])allBallotQuestions.toArray();
isSelected = new boolean[this.allBallotQuestions.length];
assertDataValid();
} }
categoryChoosers = selectionDataTmp;
@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);
}
this.selectionData = (SimpleCategoriesData)data;
assertDataValid(); assertDataValid();
} }
private void assertDataValid () { private void assertDataValid () {
if (selectionData != null && allBallotQuestions != null) {
int questionsLength = allBallotQuestions.length; int questionsLength = allBallotQuestions.length;
int maxQuestionNumber = selectionData.getMaxQuestionNumber(); int maxQuestionIndex = -1;
if (maxQuestionNumber >= questionsLength) { for (int[][] categoryChooser: categoryChoosers) {
String errorMessage = "Selection data refers to question index " + maxQuestionNumber + " while we have only " + questionsLength + " questions totally"; 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); logger.error(errorMessage);
throw new IndexOutOfBoundsException(errorMessage); throw new IndexOutOfBoundsException(errorMessage);
} }
} }
}
@Override @Override
public int getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) { public byte[] getChannelIdentifier(List<BallotAnswer> channelChoiceAnswers) {
return 0; byte[] isSelected = new byte[allBallotQuestions.length];
java.util.Arrays.fill(isSelected, QUESTION_NOT_SELECTED);
for (int i: sharedDefaults) {
isSelected[i] = QUESTION_SELECTED;
} }
@Override int channelChoiceQuestionNumber = 0;
public List<BallotQuestion> selectQuestionsForVoter(List<BallotAnswer> channelChoiceAnswers) { for (BallotAnswer ballotAnswer: channelChoiceAnswers) {
java.util.Arrays.fill(isSelected, false); assertAnswerLengthIsOne(ballotAnswer, channelChoiceQuestionNumber);
markSelected(selectionData.getSharedDefaults()); for (int i: categoryChoosers[channelChoiceQuestionNumber][(int)ballotAnswer.getAnswer(0)]) {
markSelectionsOfVoterChannelChoice ((BallotAnswer[])channelChoiceAnswers.toArray()); isSelected[i] = QUESTION_SELECTED;
int[] questionIndices = extractSelectedIndices();
List<BallotQuestion> selectedQuestions = new ArrayList<>();
for (int questionIndex: questionIndices) {
selectedQuestions.add(allBallotQuestions[questionIndex]);
} }
return selectedQuestions;
} }
private void markSelectionsOfVoterChannelChoice(BallotAnswer[] channelChoiceAnswers) { return isSelected;
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)));
}
} }
private void assertAnswerLengthIsOne (BallotAnswer ballotAnswer, int questionNumber) { private void assertAnswerLengthIsOne (BallotAnswer ballotAnswer, int questionNumber) {
@ -98,35 +98,23 @@ public class SimpleListCategoriesSelector implements QuestionSelector {
} }
} }
private void assertKeyInSelectionData(int questionNumber, int answerNumber) { @Override
int[] questionListToAdd = selectionData.getItem(questionNumber, answerNumber); public List<BallotQuestion> selectQuestionsForVoter(byte[] channelIdentifier) {
if (null == questionListToAdd) { List<BallotQuestion> selectedQuestions = new ArrayList<>();
String errorMessage = "SimpleListCategoriesSelector could not resolve answer number " + answerNumber + " for question number " + questionNumber; for (int i = 0; i < channelIdentifier.length; ++i) {
logger.error(errorMessage); if (channelIdentifier[i] == QUESTION_SELECTED) {
throw new IllegalArgumentException(errorMessage); selectedQuestions.add(allBallotQuestions[i]);
} }
} }
return selectedQuestions;
}
private void markSelected (int[] questionIndices) { private int[] listToIntArray(List<Integer> l) {
for (int i: questionIndices) { int[] res = new int[l.size()];
isSelected[i] = true; int index = 0;
} for (Integer i: l) {
} res[index] = i;
++index;
private int[] extractSelectedIndices() {
int nSelected = 0;
for (boolean b: isSelected) {
if (b) {
nSelected++;
}
}
int[] res = new int[nSelected];
int currIndex = 0;
for (int questionNumber = 0; questionNumber < isSelected.length; ++questionNumber) {
if (isSelected[questionNumber]) {
res[currIndex] = questionNumber;
currIndex++;
}
} }
return res; return res;
} }

View File

@ -2,25 +2,27 @@ package meerkat.voting.encryptor;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.*;
import java.io.IOException;
import java.security.SignatureException;
/** /**
* An interface for the encryptor component of the voting booth * 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 * A simple class for pairing EncrypedBallot together with its matching BallotSecrets
*/ */
public class EncryptionAndSecrets { public class EncryptionAndSecrets {
private final EncryptedBallot encryptedBallot; private final SignedEncryptedBallot signedEncryptedBallot;
private final BallotSecrets secrets; private final BallotSecrets secrets;
public EncryptionAndSecrets (EncryptedBallot encryptedBallot, BallotSecrets secrets) { public EncryptionAndSecrets (SignedEncryptedBallot encryptedBallot, BallotSecrets secrets) {
this.encryptedBallot = encryptedBallot; this.signedEncryptedBallot = encryptedBallot;
this.secrets = secrets; this.secrets = secrets;
} }
public EncryptedBallot getEncryptedBallot () { public SignedEncryptedBallot getSignedEncryptedBallot() {
return encryptedBallot; return signedEncryptedBallot;
} }
public BallotSecrets getSecrets() { public BallotSecrets getSecrets() {
@ -34,8 +36,7 @@ public interface VotingBoothEncryptor {
* @param plaintextBallot - all plaintext ballot info of the voter * @param plaintextBallot - all plaintext ballot info of the voter
* @return an encryption of the ballot * @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 * @param callback - a callback object which expects no return value
*/ */
public void commitToBallot(PlaintextBallot plaintextBallot, public void commitToBallot(PlaintextBallot plaintextBallot,
EncryptedBallot encryptedBallot, SignedEncryptedBallot encryptedBallot,
FutureCallback<Void> callback); FutureCallback<Void> callback);
/** /**

View File

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

View File

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

View File

@ -16,6 +16,11 @@ public interface VotingBoothUI {
AUDIT 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 * 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 * @param callback - a boolean future callback to return when done