Fixed the Selector classes.
Fixed the crypto classes to handle signatures as well. Some more other fixesvbdev2
parent
94f3920e6d
commit
76d3fdeac2
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package meerkat.voting.controller.callbacks;
|
||||
|
||||
/**
|
||||
* Created by hai on 06/06/16.
|
||||
*/
|
||||
public class VoterCancelException extends Exception{
|
||||
//
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package meerkat.voting.controller.selector;
|
||||
|
||||
/**
|
||||
* Created by hai on 11/05/16.
|
||||
*/
|
||||
public class SelectionData {
|
||||
}
|
|
@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue