Refactored and fixed NetworkVirtualPrinter

android-scanner
Tal Moran 2017-06-25 17:51:25 +03:00
parent 6f35f105c5
commit 324a079a90
7 changed files with 149 additions and 154 deletions

View File

@ -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<ScannedData> results = new ArrayBlockingQueue<ScannedData>(1);
private Throwable thrown;
private class ScanHandler implements FutureCallback<ScannedData> {
@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());
}

View File

@ -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

View File

@ -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();

View File

@ -86,31 +86,27 @@ public abstract class AsyncRunnableOutputDevice implements BallotOutputDevice, R
@Override
public void commitToBallot(PlaintextBallot plaintextBallot,
SignedEncryptedBallot signedEncryptedBallot,
FutureCallback<Void> callback) {
ControllerCallback<Void> 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<Void> callback) {
public void audit(BallotSecrets ballotSecrets, ControllerCallback<Void> 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<Void> callback) {
public void castBallot(ControllerCallback<Void> 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<Void> callback) {
public void cancelBallot(ControllerCallback<Void> callback) {
logger.debug("an interface call to cancel the output");
queue.clear();
queue.add(new CancelOutputCommand((ControllerCallback<Void>)callback));
queue.add(new CancelOutputCommand(callback));
}

View File

@ -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<Void> callback);
ControllerCallback<Void> 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<Void> callback);
public void audit(BallotSecrets ballotSecrets, ControllerCallback<Void> 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<Void> callback);
public void castBallot(ControllerCallback<Void> 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<Void> callback);
public void cancelBallot(ControllerCallback<Void> callback);
/**
* A method for shutting down the Output Device

View File

@ -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();

View File

@ -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<ScannedData> {
PollingStation.ConnectionServerData serverData = null;
DigitalSignatureGenerator clientSigner;
private final ScannedData expectedData;
public ScanHandler(ScannedData expectedData) {
this.expectedData = expectedData;
BlockingQueue<ScannedData> results = new ArrayBlockingQueue<ScannedData>(1);
class CommandCallbackHandler extends ControllerCallback<Void> {
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<ScannedData> {
@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();