From 76d3fdeac290254da0e8d249fb02acba1a7f28ff Mon Sep 17 00:00:00 2001 From: Hai Brenner Date: Tue, 7 Jun 2016 16:15:08 +0300 Subject: [PATCH] Fixed the Selector classes. Fixed the crypto classes to handle signatures as well. Some more other fixes --- .../src/main/proto/meerkat/voting.proto | 19 +++ .../controller/VotingBoothController.java | 7 +- .../voting/controller/VotingBoothImpl.java | 46 +++--- .../callbacks/ChannelChoiceCallback.java | 24 +-- .../callbacks/VoterCancelException.java | 8 + .../controller/callbacks/VotingCallback.java | 24 +-- .../controller/commands/ShutDownCommand.java | 10 -- .../controller/selector/QuestionSelector.java | 7 +- .../controller/selector/SelectionData.java | 7 - .../selector/SimpleCategoriesData.java | 97 ------------ .../SimpleListCategoriesSelector.java | 144 ++++++++---------- ...othEncryptor.java => VBCryptoManager.java} | 19 +-- .../voting/encryptor/VBCryptoManagerImpl.java | 67 ++++++++ .../voting/encryptor/VBEncryptorImpl.java | 14 -- .../voting/output/BallotOutputDevice.java | 2 +- .../output/SystemConsoleOutputDevice.java | 6 +- .../meerkat/voting/ui/SystemConsoleUI.java | 31 +++- .../java/meerkat/voting/ui/VotingBoothUI.java | 5 + 18 files changed, 259 insertions(+), 278 deletions(-) create mode 100644 voting-booth/src/main/java/meerkat/voting/controller/callbacks/VoterCancelException.java delete mode 100644 voting-booth/src/main/java/meerkat/voting/controller/commands/ShutDownCommand.java delete mode 100644 voting-booth/src/main/java/meerkat/voting/controller/selector/SelectionData.java delete mode 100644 voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleCategoriesData.java rename voting-booth/src/main/java/meerkat/voting/encryptor/{VotingBoothEncryptor.java => VBCryptoManager.java} (60%) create mode 100644 voting-booth/src/main/java/meerkat/voting/encryptor/VBCryptoManagerImpl.java delete mode 100644 voting-booth/src/main/java/meerkat/voting/encryptor/VBEncryptorImpl.java diff --git a/meerkat-common/src/main/proto/meerkat/voting.proto b/meerkat-common/src/main/proto/meerkat/voting.proto index 8ece676..6c0b1e2 100644 --- a/meerkat-common/src/main/proto/meerkat/voting.proto +++ b/meerkat-common/src/main/proto/meerkat/voting.proto @@ -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; +} diff --git a/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothController.java b/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothController.java index 057f13d..6b706f3 100644 --- a/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothController.java +++ b/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothController.java @@ -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); diff --git a/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothImpl.java b/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothImpl.java index cd53f65..2d79d33 100644 --- a/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothImpl.java +++ b/voting-booth/src/main/java/meerkat/voting/controller/VotingBoothImpl.java @@ -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 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 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 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; } } diff --git a/voting-booth/src/main/java/meerkat/voting/controller/callbacks/ChannelChoiceCallback.java b/voting-booth/src/main/java/meerkat/voting/controller/callbacks/ChannelChoiceCallback.java index f6abd5c..ea8094c 100644 --- a/voting-booth/src/main/java/meerkat/voting/controller/callbacks/ChannelChoiceCallback.java +++ b/voting-booth/src/main/java/meerkat/voting/controller/callbacks/ChannelChoiceCallback.java @@ -23,21 +23,21 @@ public class ChannelChoiceCallback extends ControllerCallback @Override public void onSuccess(List 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())); + } } } diff --git a/voting-booth/src/main/java/meerkat/voting/controller/callbacks/VoterCancelException.java b/voting-booth/src/main/java/meerkat/voting/controller/callbacks/VoterCancelException.java new file mode 100644 index 0000000..4c0d411 --- /dev/null +++ b/voting-booth/src/main/java/meerkat/voting/controller/callbacks/VoterCancelException.java @@ -0,0 +1,8 @@ +package meerkat.voting.controller.callbacks; + +/** + * Created by hai on 06/06/16. + */ +public class VoterCancelException extends Exception{ + // +} diff --git a/voting-booth/src/main/java/meerkat/voting/controller/callbacks/VotingCallback.java b/voting-booth/src/main/java/meerkat/voting/controller/callbacks/VotingCallback.java index 5317374..4b6f2f7 100644 --- a/voting-booth/src/main/java/meerkat/voting/controller/callbacks/VotingCallback.java +++ b/voting-booth/src/main/java/meerkat/voting/controller/callbacks/VotingCallback.java @@ -23,21 +23,21 @@ public class VotingCallback extends ControllerCallback> { @Override public void onSuccess(List 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())); + } } } diff --git a/voting-booth/src/main/java/meerkat/voting/controller/commands/ShutDownCommand.java b/voting-booth/src/main/java/meerkat/voting/controller/commands/ShutDownCommand.java deleted file mode 100644 index ee3c0f2..0000000 --- a/voting-booth/src/main/java/meerkat/voting/controller/commands/ShutDownCommand.java +++ /dev/null @@ -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); - } -} diff --git a/voting-booth/src/main/java/meerkat/voting/controller/selector/QuestionSelector.java b/voting-booth/src/main/java/meerkat/voting/controller/selector/QuestionSelector.java index e409e36..e9decae 100644 --- a/voting-booth/src/main/java/meerkat/voting/controller/selector/QuestionSelector.java +++ b/voting-booth/src/main/java/meerkat/voting/controller/selector/QuestionSelector.java @@ -8,11 +8,8 @@ import java.util.List; */ public interface QuestionSelector { - public void setAllBallotQuestions (List allBallotQuestions); + public byte[] getChannelIdentifier (List channelChoiceAnswers); - public void setSelectionData(SelectionData data); + public List selectQuestionsForVoter (byte[] channelIdentifier); - public int getChannelIdentifier (List channelChoiceAnswers); - - public List selectQuestionsForVoter (List channelChoiceAnswers); } diff --git a/voting-booth/src/main/java/meerkat/voting/controller/selector/SelectionData.java b/voting-booth/src/main/java/meerkat/voting/controller/selector/SelectionData.java deleted file mode 100644 index 24a1125..0000000 --- a/voting-booth/src/main/java/meerkat/voting/controller/selector/SelectionData.java +++ /dev/null @@ -1,7 +0,0 @@ -package meerkat.voting.controller.selector; - -/** - * Created by hai on 11/05/16. - */ -public class SelectionData { -} diff --git a/voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleCategoriesData.java b/voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleCategoriesData.java deleted file mode 100644 index 544a86d..0000000 --- a/voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleCategoriesData.java +++ /dev/null @@ -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 + "]"; - } - -} diff --git a/voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleListCategoriesSelector.java b/voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleListCategoriesSelector.java index d953246..6181f05 100644 --- a/voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleListCategoriesSelector.java +++ b/voting-booth/src/main/java/meerkat/voting/controller/selector/SimpleListCategoriesSelector.java @@ -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 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 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 channelChoiceAnswers) { - return 0; - } + public byte[] getChannelIdentifier(List channelChoiceAnswers) { + byte[] isSelected = new byte[allBallotQuestions.length]; + java.util.Arrays.fill(isSelected, QUESTION_NOT_SELECTED); - @Override - public List selectQuestionsForVoter(List channelChoiceAnswers) { - java.util.Arrays.fill(isSelected, false); - markSelected(selectionData.getSharedDefaults()); - markSelectionsOfVoterChannelChoice ((BallotAnswer[])channelChoiceAnswers.toArray()); - int[] questionIndices = extractSelectedIndices(); - List 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 selectQuestionsForVoter(byte[] channelIdentifier) { + List 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 l) { + int[] res = new int[l.size()]; + int index = 0; + for (Integer i: l) { + res[index] = i; + ++index; } return res; } diff --git a/voting-booth/src/main/java/meerkat/voting/encryptor/VotingBoothEncryptor.java b/voting-booth/src/main/java/meerkat/voting/encryptor/VBCryptoManager.java similarity index 60% rename from voting-booth/src/main/java/meerkat/voting/encryptor/VotingBoothEncryptor.java rename to voting-booth/src/main/java/meerkat/voting/encryptor/VBCryptoManager.java index e9a885f..e664607 100644 --- a/voting-booth/src/main/java/meerkat/voting/encryptor/VotingBoothEncryptor.java +++ b/voting-booth/src/main/java/meerkat/voting/encryptor/VBCryptoManager.java @@ -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 } diff --git a/voting-booth/src/main/java/meerkat/voting/encryptor/VBCryptoManagerImpl.java b/voting-booth/src/main/java/meerkat/voting/encryptor/VBCryptoManagerImpl.java new file mode 100644 index 0000000..3f3a1b2 --- /dev/null +++ b/voting-booth/src/main/java/meerkat/voting/encryptor/VBCryptoManagerImpl.java @@ -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; + } + } +} diff --git a/voting-booth/src/main/java/meerkat/voting/encryptor/VBEncryptorImpl.java b/voting-booth/src/main/java/meerkat/voting/encryptor/VBEncryptorImpl.java deleted file mode 100644 index 51ffef0..0000000 --- a/voting-booth/src/main/java/meerkat/voting/encryptor/VBEncryptorImpl.java +++ /dev/null @@ -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(); - } -} diff --git a/voting-booth/src/main/java/meerkat/voting/output/BallotOutputDevice.java b/voting-booth/src/main/java/meerkat/voting/output/BallotOutputDevice.java index 8d1f7f1..afbe223 100644 --- a/voting-booth/src/main/java/meerkat/voting/output/BallotOutputDevice.java +++ b/voting-booth/src/main/java/meerkat/voting/output/BallotOutputDevice.java @@ -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 callback); /** diff --git a/voting-booth/src/main/java/meerkat/voting/output/SystemConsoleOutputDevice.java b/voting-booth/src/main/java/meerkat/voting/output/SystemConsoleOutputDevice.java index e1556d6..5d8446e 100644 --- a/voting-booth/src/main/java/meerkat/voting/output/SystemConsoleOutputDevice.java +++ b/voting-booth/src/main/java/meerkat/voting/output/SystemConsoleOutputDevice.java @@ -29,18 +29,18 @@ public class SystemConsoleOutputDevice implements BallotOutputDevice { @Override public void commitToBallot(PlaintextBallot plaintextBallot, - EncryptedBallot encryptedBallot, + SignedEncryptedBallot signedEncryptedBallot, FutureCallback 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); } diff --git a/voting-booth/src/main/java/meerkat/voting/ui/SystemConsoleUI.java b/voting-booth/src/main/java/meerkat/voting/ui/SystemConsoleUI.java index 1ab1574..2f30a80 100644 --- a/voting-booth/src/main/java/meerkat/voting/ui/SystemConsoleUI.java +++ b/voting-booth/src/main/java/meerkat/voting/ui/SystemConsoleUI.java @@ -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 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 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 askVoterForAnswers(List questions) throws IOException { + private List askVoterForAnswers(List 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; + } + + } diff --git a/voting-booth/src/main/java/meerkat/voting/ui/VotingBoothUI.java b/voting-booth/src/main/java/meerkat/voting/ui/VotingBoothUI.java index 192dd1c..fccb28e 100644 --- a/voting-booth/src/main/java/meerkat/voting/ui/VotingBoothUI.java +++ b/voting-booth/src/main/java/meerkat/voting/ui/VotingBoothUI.java @@ -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