From 324a079a901872315e34288c293b5d156553b842 Mon Sep 17 00:00:00 2001 From: Tal Moran Date: Sun, 25 Jun 2017 17:51:25 +0300 Subject: [PATCH] Refactored and fixed NetworkVirtualPrinter --- .../PollingStationScannerTest.java | 17 +- .../pollingstation/PollingStationScanner.java | 3 + .../java/meerkat/voting/ToySignature.java | 16 ++ .../output/AsyncRunnableOutputDevice.java | 20 +- .../voting/output/BallotOutputDevice.java | 9 +- .../voting/output/NetworkVirtualPrinter.java | 32 ++- .../voting/NetworkVirtualPrinterTest.java | 206 ++++++++---------- 7 files changed, 149 insertions(+), 154 deletions(-) diff --git a/polling-station/src/test/java/meerkat/pollingstation/PollingStationScannerTest.java b/polling-station/src/test/java/meerkat/pollingstation/PollingStationScannerTest.java index a152fd3..f9141bf 100644 --- a/polling-station/src/test/java/meerkat/pollingstation/PollingStationScannerTest.java +++ b/polling-station/src/test/java/meerkat/pollingstation/PollingStationScannerTest.java @@ -26,7 +26,7 @@ import static org.junit.Assert.*; public class PollingStationScannerTest { final Logger logger = LoggerFactory.getLogger(getClass()); private PollingStationScanner.PollingStationServer scanner; - private static final String CONTEXT_PATH = "/scan"; + private static final String SCAN_CONTEXT_PATH = "/scan"; /** * Waiting time for transmission in ms. @@ -39,9 +39,6 @@ public class PollingStationScannerTest { BlockingQueue results = new ArrayBlockingQueue(1); - - private Throwable thrown; - private class ScanHandler implements FutureCallback { @Override public void onSuccess(ScannedData result) { @@ -49,9 +46,7 @@ public class PollingStationScannerTest { } @Override - public void onFailure(Throwable t) { - thrown = t; - } + public void onFailure(Throwable t) { } } @@ -63,11 +58,9 @@ public class PollingStationScannerTest { logger.debug("Setting up Scanner WebApp!"); - scanner = new PollingStationWebScanner(0, CONTEXT_PATH); + scanner = new PollingStationWebScanner(0, SCAN_CONTEXT_PATH); scanner.subscribe(new ScanHandler()); - thrown = null; - try { serverData = scanner.start(true); } catch (Exception e) { @@ -94,13 +87,13 @@ public class PollingStationScannerTest { void checkResult(PollingStation.ScannedBallot ballot, long timeout) throws Exception { ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS); assertEquals("Expecting ballot in received scan", ScannedData.DataCase.BALLOT, arrivedData.getDataCase()); - assertEquals("Recieved scan does not match transmission", ballot, arrivedData.getBallot()); + assertEquals("Received scan does not match transmission", ballot, arrivedData.getBallot()); } void checkResult(PollingStation.ScanError error, long timeout) throws Exception { ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS); assertEquals("Expecting error in received scan", ScannedData.DataCase.ERROR, arrivedData.getDataCase()); - assertEquals("Recieved scan does not match transmission", error, arrivedData.getError()); + assertEquals("Received scan does not match transmission", error, arrivedData.getError()); } diff --git a/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationScanner.java b/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationScanner.java index dd62388..eccad9a 100644 --- a/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationScanner.java +++ b/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationScanner.java @@ -4,6 +4,9 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.protobuf.BoolValue; import meerkat.protobuf.Crypto; import meerkat.protobuf.PollingStation.*; + +import java.util.concurrent.Future; + /** * Created by Arbel on 05/05/2016. * An interface for the scanner used by the Polling Station Committee diff --git a/voting-booth/src/main/java/meerkat/voting/ToySignature.java b/voting-booth/src/main/java/meerkat/voting/ToySignature.java index 89bfc58..1546d18 100644 --- a/voting-booth/src/main/java/meerkat/voting/ToySignature.java +++ b/voting-booth/src/main/java/meerkat/voting/ToySignature.java @@ -3,6 +3,7 @@ package meerkat.voting; import com.google.protobuf.ByteString; import com.google.protobuf.Message; import meerkat.crypto.DigitalSignature; +import meerkat.protobuf.Crypto; import meerkat.protobuf.Crypto.*; import meerkat.protobuf.Crypto.Signature; @@ -29,6 +30,11 @@ public class ToySignature implements DigitalSignature { return signerID; } + @Override + public SignatureVerificationKey getSignerPublicKey() { + return null; + } + @Override public void updateContent(Message msg) throws SignatureException { msgByteString = msg.toByteString(); @@ -47,6 +53,16 @@ public class ToySignature implements DigitalSignature { } + @Override + public void loadVerificationCertificate(SignatureVerificationKey cert) throws CertificateException { + throw new UnsupportedOperationException(); + } + + @Override + public ByteString computeCertificateFingerprint(SignatureVerificationKey cert) throws CertificateException { + return null; + } + @Override public void loadVerificationCertificates(InputStream certStream) throws CertificateException { throw new UnsupportedOperationException(); diff --git a/voting-booth/src/main/java/meerkat/voting/output/AsyncRunnableOutputDevice.java b/voting-booth/src/main/java/meerkat/voting/output/AsyncRunnableOutputDevice.java index d7b2b60..633e68c 100644 --- a/voting-booth/src/main/java/meerkat/voting/output/AsyncRunnableOutputDevice.java +++ b/voting-booth/src/main/java/meerkat/voting/output/AsyncRunnableOutputDevice.java @@ -86,31 +86,27 @@ public abstract class AsyncRunnableOutputDevice implements BallotOutputDevice, R @Override public void commitToBallot(PlaintextBallot plaintextBallot, SignedEncryptedBallot signedEncryptedBallot, - FutureCallback callback) { + ControllerCallback callback) { logger.debug("Output interface call to commit to ballot"); - queue.clear(); - queue.add(new CommitOutputCommand(plaintextBallot, signedEncryptedBallot, (OutputDeviceCommitCallback)callback)); + queue.add(new CommitOutputCommand(plaintextBallot, signedEncryptedBallot, callback)); } @Override - public void audit(BallotSecrets ballotSecrets, FutureCallback callback) { + public void audit(BallotSecrets ballotSecrets, ControllerCallback callback) { logger.debug("an interface call to audit"); - queue.clear(); - queue.add(new AuditOutputCommand(ballotSecrets, (OutputDeviceFinalizeCallback)callback)); + queue.add(new AuditOutputCommand(ballotSecrets, callback)); } @Override - public void castBallot(FutureCallback callback) { + public void castBallot(ControllerCallback callback) { logger.debug("an interface call to cast ballot"); - queue.clear(); - queue.add(new CastOutputCommand((OutputDeviceFinalizeCallback)callback)); + queue.add(new CastOutputCommand(callback)); } @Override - public void cancelBallot(FutureCallback callback) { + public void cancelBallot(ControllerCallback callback) { logger.debug("an interface call to cancel the output"); - queue.clear(); - queue.add(new CancelOutputCommand((ControllerCallback)callback)); + queue.add(new CancelOutputCommand(callback)); } 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 3909ce3..9536d6b 100644 --- a/voting-booth/src/main/java/meerkat/voting/output/BallotOutputDevice.java +++ b/voting-booth/src/main/java/meerkat/voting/output/BallotOutputDevice.java @@ -2,6 +2,7 @@ package meerkat.voting.output; import com.google.common.util.concurrent.FutureCallback; import meerkat.protobuf.Voting.*; +import meerkat.voting.controller.callbacks.ControllerCallback; /** * An interface for the device in which we output the ballots. @@ -16,26 +17,26 @@ public interface BallotOutputDevice { */ public void commitToBallot(PlaintextBallot plaintextBallot, SignedEncryptedBallot encryptedBallot, - FutureCallback callback); + ControllerCallback callback); /** * Voter chose 'audit'. Output the ballot secrets to prove correctness of the encryption. * @param ballotSecrets - the secrets of the encryption * @param callback - a callback object which expects no return value */ - public void audit(BallotSecrets ballotSecrets, FutureCallback callback); + public void audit(BallotSecrets ballotSecrets, ControllerCallback callback); /** * Voter chose 'cast'. Finalize the ballot for use in the polling station * @param callback - a callback object which expects no return value */ - public void castBallot(FutureCallback callback); + public void castBallot(ControllerCallback callback); /** * Cancelling the current ballot. This clears the state of the OutputDevice if the implementation has any such state. * @param callback - a callback object which expects no return value */ - public void cancelBallot(FutureCallback callback); + public void cancelBallot(ControllerCallback callback); /** * A method for shutting down the Output Device diff --git a/voting-booth/src/main/java/meerkat/voting/output/NetworkVirtualPrinter.java b/voting-booth/src/main/java/meerkat/voting/output/NetworkVirtualPrinter.java index 74f9970..661b18c 100644 --- a/voting-booth/src/main/java/meerkat/voting/output/NetworkVirtualPrinter.java +++ b/voting-booth/src/main/java/meerkat/voting/output/NetworkVirtualPrinter.java @@ -2,6 +2,11 @@ package meerkat.voting.output; import com.google.protobuf.BoolValue; import com.google.protobuf.ByteString; +import meerkat.crypto.DigitalSignature; +import meerkat.crypto.DigitalSignatureGenerator; +import meerkat.pollingstation.PollingStationScanner; +import meerkat.pollingstation.ScannerClientAPI; +import meerkat.protobuf.PollingStation; import meerkat.protobuf.PollingStation.ScannedData; import meerkat.protobuf.Voting.SignedEncryptedBallot; import meerkat.rest.Constants; @@ -25,15 +30,18 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice { private static final Logger logger = LoggerFactory.getLogger(NetworkVirtualPrinter.class); private ByteString channelIdentifier; private SignedEncryptedBallot signedEncryptedBallot; - private final WebTarget successfulPrintTarget; - public NetworkVirtualPrinter(String address) { + PollingStationScanner.ScannerClient scannerClient; + + public NetworkVirtualPrinter(PollingStation.ConnectionServerData serverData, DigitalSignature printerSigner) { super(); logger.info("A NetworkVirtualPrinter is constructed"); - Client client = ClientBuilder.newClient(); - client.register(ProtobufMessageBodyReader.class); - client.register(ProtobufMessageBodyWriter.class); - successfulPrintTarget = client.target(address).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH); + scannerClient = new ScannerClientAPI(printerSigner); + if (!scannerClient.connect(serverData)) { + logger.error("Couldn't connect to polling station scan server!"); + // TODO: Use a checked exception? + throw new RuntimeException("Connection Error"); + } resetState(); } @@ -44,6 +52,7 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice { * When the voter chooses to Cast the ballot, these details are sent over the wire. * @param command a CommitOutputCommand with the signed encryption of the ballot */ + @Override public void doCommitToBallot(CommitOutputCommand command) { logger.debug("entered method doCommitToBallot"); channelIdentifier = command.getChannelIdentifierByteString(); @@ -56,6 +65,7 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice { * The NetworkVirtualPrinter actually does nothing for auditing. * @param command a AuditOutputCommand with the details and the callback */ + @Override public void doAudit(AuditOutputCommand command) { logger.debug("entered method doAudit"); resetState(); @@ -67,20 +77,19 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice { * This is where the magic happens. The signed encrypted ballot is transmitted over the wire * @param command a CastOutputCommand with the details and the callback */ + @Override public void doCastBallot(CastOutputCommand command) { logger.debug("entered method doCastBallot"); - ScannedData scannedData = ScannedData.newBuilder() + PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder() .setChannel(channelIdentifier) .setSignedEncryptedBallot(this.signedEncryptedBallot) .build(); - Response response = successfulPrintTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF)); - BoolValue b = response.readEntity(BoolValue.class); - response.close(); + boolean b = scannerClient.newScan(scannedBallot); resetState(); - if (b.getValue()) { + if (b) { command.getCallback().onSuccess(null); } else { @@ -93,6 +102,7 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice { * The NetworkVirtualPrinter actually does nothing for canceling. * @param command a CancelOutputCommand with the callback */ + @Override public void doCancel(CancelOutputCommand command) { logger.debug("entered method doCancel"); resetState(); diff --git a/voting-booth/src/test/java/meerkat/voting/NetworkVirtualPrinterTest.java b/voting-booth/src/test/java/meerkat/voting/NetworkVirtualPrinterTest.java index 4a467ac..358dfd3 100644 --- a/voting-booth/src/test/java/meerkat/voting/NetworkVirtualPrinterTest.java +++ b/voting-booth/src/test/java/meerkat/voting/NetworkVirtualPrinterTest.java @@ -2,26 +2,35 @@ package meerkat.voting; import com.google.common.util.concurrent.FutureCallback; - -import meerkat.protobuf.Crypto.*; -import meerkat.protobuf.PollingStation.*; - import com.google.protobuf.ByteString; +import meerkat.crypto.DigitalSignatureGenerator; +import meerkat.crypto.concrete.ECDSADeterministicSignature; import meerkat.pollingstation.PollingStationScanner; import meerkat.pollingstation.PollingStationWebScanner; - -import meerkat.protobuf.Voting.*; -import meerkat.voting.controller.callbacks.OutputDeviceCommitCallback; -import meerkat.voting.controller.callbacks.OutputDeviceFinalizeCallback; +import meerkat.protobuf.Crypto.RerandomizableEncryptedMessage; +import meerkat.protobuf.Crypto.Signature; +import meerkat.protobuf.Crypto.SignatureType; +import meerkat.protobuf.PollingStation; +import meerkat.protobuf.PollingStation.ScannedData; +import meerkat.protobuf.Voting.EncryptedBallot; +import meerkat.protobuf.Voting.PlaintextBallot; +import meerkat.protobuf.Voting.SignedEncryptedBallot; +import meerkat.voting.controller.callbacks.ControllerCallback; import meerkat.voting.output.NetworkVirtualPrinter; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.concurrent.Semaphore; +import java.math.BigInteger; +import java.util.Date; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.*; /** * A test for the NetworkVirtualPrinter @@ -31,127 +40,104 @@ import static org.hamcrest.MatcherAssert.assertThat; public class NetworkVirtualPrinterTest { + final Logger logger = LoggerFactory.getLogger(getClass()); + private static final String SCAN_CONTEXT_PATH = "/scan"; - private PollingStationScanner.PollingStationServer scanner; - private static final String ADDRESS = "http://localhost"; - private static final String SUB_ADDRESS = ""; - private static final int PORT = 8080; + /** + * Waiting time for transmission in ms. + */ + final static long TIMEOUT = 500; + + private PollingStationScanner.PollingStationServer scannerServer; - private Semaphore semaphore0; - private Semaphore semaphore1; - private Semaphore semaphore2; - private Throwable thrown; - private boolean dataIsAsExpected; private NetworkVirtualPrinter networkPrinter; - private class ScanHandler implements FutureCallback { + PollingStation.ConnectionServerData serverData = null; + DigitalSignatureGenerator clientSigner; - private final ScannedData expectedData; - public ScanHandler(ScannedData expectedData) { - this.expectedData = expectedData; + BlockingQueue results = new ArrayBlockingQueue(1); + + class CommandCallbackHandler extends ControllerCallback { + boolean ok; + CountDownLatch isFlagSet; + + public CommandCallbackHandler(int requestId, long serial) { + super(requestId, serial,null); + this.ok = false; + isFlagSet = new CountDownLatch(1); } + private void done(boolean isOk) { + ok = isOk; + isFlagSet.countDown(); + } + public boolean isOk(long timeout) throws InterruptedException { + isFlagSet.await(timeout, TimeUnit.MILLISECONDS); + return ok; + } + + @Override + public void onSuccess(Void result) { done(true); } + + @Override + public void onFailure(Throwable t) { + logger.error("Commit to ballot failed " + t.getMessage()); + done(false); + } + } + + private class ScanHandler implements FutureCallback { @Override public void onSuccess(ScannedData result) { - dataIsAsExpected = result.equals(expectedData); - semaphore2.release(); + results.add(result); } @Override - public void onFailure(Throwable t) { - dataIsAsExpected = false; - thrown = t; - semaphore2.release(); - } + public void onFailure(Throwable t) { } } - private class CommitHandler extends OutputDeviceCommitCallback { - - private boolean success; - public String errorMessage; - - public CommitHandler(int requestId, long serialNumber) { - super(requestId, serialNumber, null, null); - errorMessage = null; - success = false; - } - - @Override - public void onSuccess(Void v) { - System.out.println("CommitHandler success"); - success = true; - semaphore0.release(); - } - - @Override - public void onFailure(Throwable t) { - errorMessage = "Commit to ballot failed " + t.getMessage(); - semaphore0.release(); - } - - public boolean gotSuccess() { - return success; - } + void checkScanResult(PollingStation.ScannedBallot ballot, long timeout) throws Exception { + ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS); + assertEquals("Expecting ballot in received scan", ScannedData.DataCase.BALLOT, arrivedData.getDataCase()); + assertEquals("Received scan does not match transmission", ballot, arrivedData.getBallot()); } - private class CastHandler extends OutputDeviceFinalizeCallback { - - private boolean success; - public String errorMessage; - - public CastHandler(int requestId, long serialNumber) { - super(requestId, serialNumber, null, null); - errorMessage = null; - success = false; - } - - @Override - public void onSuccess(Void v) { - System.out.println("CastHandler success"); - success = true; - semaphore1.release(); - } - - @Override - public void onFailure(Throwable t) { - errorMessage = "Cast to ballot failed " + t.getMessage(); - semaphore1.release(); - } - - public boolean gotSuccess() { - return success; - } + void checkSCanResult(PollingStation.ScanError error, long timeout) throws Exception { + ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS); + assertEquals("Expecting error in received scan", ScannedData.DataCase.ERROR, arrivedData.getDataCase()); + assertEquals("Received scan does not match transmission", error, arrivedData.getError()); } @Before public void init() { - System.err.println("Setting up Scanner WebApp!"); + clientSigner = new ECDSADeterministicSignature(); + clientSigner.generateSigningCertificate(BigInteger.ONE, new Date(System.currentTimeMillis()), + new Date(System.currentTimeMillis() + 1000*60*60*24*365), "testing"); - scanner = new PollingStationWebScanner(PORT, SUB_ADDRESS); + logger.debug("Setting up Scanner WebApp!"); - semaphore0 = new Semaphore(0); - semaphore1 = new Semaphore(0); - semaphore2 = new Semaphore(0); - thrown = null; + scannerServer = new PollingStationWebScanner(0, SCAN_CONTEXT_PATH); + + scannerServer.subscribe(new ScanHandler()); try { - scanner.start(); + serverData = scannerServer.start(true); + logger.info("Scanner server started at {}", serverData.getServerUrl()); } catch (Exception e) { - assertThat("Could not start server: " + e.getMessage(), false); + fail("Could not start server: " + e.getMessage()); } - - networkPrinter = new NetworkVirtualPrinter(ADDRESS + ":" + PORT); + networkPrinter = new NetworkVirtualPrinter(serverData, clientSigner); Thread outputThread = new Thread(networkPrinter); outputThread.setName("Meerkat VB-Output Thread"); outputThread.start(); } @Test - public void testSuccessfulScan() throws InterruptedException { + public void testSuccessfulScan() throws Exception { // create scannedData @@ -186,47 +172,37 @@ public class NetworkVirtualPrinterTest { .setSignature(signature) .build(); - ScannedData scannedData = ScannedData.newBuilder() + PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder() .setChannel(ByteString.copyFrom(channel)) .setSignedEncryptedBallot(signedEncryptedBallot) .build(); - scanner.subscribe(new ScanHandler(scannedData)); //Send scan - CommitHandler commitHandler = new CommitHandler(0, serialNumber); + CommandCallbackHandler commitHandler = new CommandCallbackHandler(0, 1); networkPrinter.commitToBallot(plaintextBallot, signedEncryptedBallot, commitHandler); - semaphore0.acquire(); - - CastHandler castHandler = new CastHandler(1, serialNumber); + CommandCallbackHandler castHandler = new CommandCallbackHandler(1, 1); networkPrinter.castBallot(castHandler); - semaphore1.acquire(); - - semaphore2.acquire(); - // Make sure the response was valid + assertTrue("Commit to ballot callback did not receive success", commitHandler.isOk(TIMEOUT)); + assertTrue("Cast ballot callback did not receive success", castHandler.isOk(TIMEOUT)); - assertThat("Commit to ballot callback did not receive success", commitHandler.gotSuccess()); - assertThat("Cast ballot callback did not receive success", castHandler.gotSuccess()); - - assertThat("Scanner has thrown an error", thrown == null); - assertThat("Scanned data received was incorrect", dataIsAsExpected); - + checkScanResult(scannedBallot, TIMEOUT); } @After public void close() { - System.err.println("Scanner WebApp shutting down..."); + logger.debug("Scanner WebApp shutting down..."); try { - scanner.stop(); + scannerServer.stop(); } catch (Exception e) { - assertThat("Could not stop server: " + e.getMessage(), false); + fail("Could not stop scan server: " + e.getMessage()); } networkPrinter.callShutDown();