Mostly finished with scanAPI refactoring

android-scanner
Tal Moran 2017-06-25 16:20:28 +03:00
parent cb1d2343c4
commit 6f35f105c5
19 changed files with 260 additions and 851 deletions

View File

@ -53,6 +53,11 @@ public class GenericBulletinBoardSignature implements BulletinBoardSignature {
signer.loadVerificationCertificate(cert);
}
@Override
public ByteString computeCertificateFingerprint(Crypto.SignatureVerificationKey cert) throws CertificateException {
return signer.computeCertificateFingerprint(cert);
}
@Override
public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
signer.loadVerificationCertificates(certStream);

View File

@ -34,6 +34,13 @@ public interface DigitalSignature {
throws CertificateException;
/**
* Compute a fingerprint (SHA256 hash) of a certificate.
* @param cert
* @return
*/
public ByteString computeCertificateFingerprint(SignatureVerificationKey cert) throws CertificateException;
/**
* Load a set of certificates from an input stream.
* This will consume the entire stream.

View File

@ -20,6 +20,7 @@ import java.io.InputStream;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.security.interfaces.ECPrivateKey;
@ -68,26 +69,21 @@ public class ECDSADeterministicSignature extends ECDSASignature {
}
@Override
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
throws CertificateException, UnrecoverableKeyException, IOException {
super.loadSigningCertificate(keyStoreBuilder);
public void loadSigningCertificate(Certificate cert, PrivateKey key) throws InvalidKeyException, CertificateException {
super.loadSigningCertificate(cert, key);
if (!(loadedSigningKey instanceof ECPrivateKey)) {
logger.error("Wrong private key type (expected ECPrivateKey, got {})", loadedSigningKey.getClass());
throw new CertificateException("Wrong signing key type!");
}
ECPrivateKey key = (ECPrivateKey) loadedSigningKey;
ECPrivateKey eckey = (ECPrivateKey) loadedSigningKey;
AsymmetricKeyParameter baseParams;
try {
baseParams = ECUtil.generatePrivateKeyParameter(key);
} catch (InvalidKeyException e) {
throw new UnrecoverableKeyException("Couldn't convert private key");
}
baseParams = ECUtil.generatePrivateKeyParameter(eckey);
if (!(baseParams instanceof ECPrivateKeyParameters)) {
logger.error("Error converting to bouncycastle type! (got {})", baseParams.getClass());
throw new UnrecoverableKeyException("Wrong signing key type!");
throw new InvalidKeyException("Wrong signing key type!");
}
ECPrivateKeyParameters params = (ECPrivateKeyParameters) baseParams;

View File

@ -135,11 +135,33 @@ public class ECDSASignature implements DigitalSignatureGenerator {
loadVerificationCertificates(certStream);
}
public Collection<? extends Certificate> deserializeCertificates(InputStream certStream)
throws CertificateException {
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509);
Collection<? extends Certificate> certs = certificateFactory.generateCertificates(certStream);
return certs;
}
public Collection<? extends Certificate> deserializeCertificates(Crypto.SignatureVerificationKey cert)
throws CertificateException {
ByteArrayInputStream certStream = new ByteArrayInputStream(cert.getData().toByteArray());
return deserializeCertificates(certStream);
}
@Override
public ByteString computeCertificateFingerprint(Crypto.SignatureVerificationKey cert)
throws CertificateException {
Collection<? extends Certificate> certs = deserializeCertificates(cert);
return computeCertificateFingerprint(certs.iterator().next());
}
@Override
public void loadVerificationCertificates(InputStream certStream)
throws CertificateException {
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509);
Collection<? extends Certificate> certs = certificateFactory.generateCertificates(certStream);
Collection<? extends Certificate> certs = deserializeCertificates(certStream);
for (Certificate cert : certs) {
loadVerificationCertificate(cert);
}
@ -321,6 +343,8 @@ public class ECDSASignature implements DigitalSignatureGenerator {
}
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
if (key instanceof PrivateKey) {
// Here is where we *actually* load the private key
loadSigningCertificate(cert, (PrivateKey) key);
return;
} else {

View File

@ -11,16 +11,13 @@ import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB
* Created by Laura on 3/20/2017.
*/
public class PollingStationClientToyRun {
private static final String PSC_URL = "http://192.168.70.10:8880/";
public static void main(String [] args) {
int channel = 10;
System.out.println("Message to send: "+channel);
byte[] data = ByteBuffer.allocate(4).putInt(channel).array();
// byte[] data = {(byte) 1, (byte) 2};
PollingStation.ScannedData scannedData = PollingStation.ScannedData.newBuilder()
PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder()
.setChannel(ByteString.copyFrom(data))
.build();
@ -29,9 +26,10 @@ public class PollingStationClientToyRun {
// System.out.println("Channel int initial:"+channel);
// System.out.println("Channel int retrieved:"+retrieved);
ScannerClientAPI scannerClient = new ScannerClientAPI(PSC_URL);
boolean sent = scannerClient.sendScan(scannedData);
PollingStationScanner.ScannerClient scannerClient = new ScannerClientAPI();
System.out.println("Message sent successfully: "+sent);
// boolean sent = scannerClient.sendScan(scannedData);
//
// System.out.println("Message sent successfully: "+sent);
}
}

View File

@ -4,10 +4,7 @@ package meerkat.pollingstation;
* Created by Arbel on 5/31/2016.
*/
import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.BoolValue;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.concrete.ECDSASignature;
import meerkat.protobuf.PollingStation;
import javax.annotation.PostConstruct;
@ -43,7 +40,7 @@ public class PollingStationScannerWebApp implements PollingStationScanner.Pollin
@PostConstruct
@SuppressWarnings("unchecked")
public void init() throws Exception {
Object context = servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME);
Object context = servletContext.getAttribute(PollingStationWebScanner.SCAN_HANDLER_ATTRIBUTE_NAME);
try {
requestHandler = (PollingStationWebScanner.ScanRequestHandler) context;

View File

@ -8,14 +8,14 @@ import org.slf4j.LoggerFactory;
* Created by Laura on 3/20/2017.
*/
public class PollingStationToyRun {
final Logger logger = LoggerFactory.getLogger(getClass());
final static Logger logger = LoggerFactory.getLogger(PollingStation.class);
private static PollingStationScanner.PollingStationServer scanner;
private static final String CONTEXT_PATH = "/scan";
private static final int PORT = 8080;
private static final int PORT = 0;
public static void main(String [] args) throws Exception {
System.err.println("Setting up Scanner WebApp!");
logger.debug("Setting up Scanner WebApp!");
// scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
//
@ -27,19 +27,12 @@ public class PollingStationToyRun {
// }
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
PollingStation.ConnectionServerData serverData = scanner.start(false);
PollingStation.ConnectionServerData serverData = scanner.start(true);
PollingStationMainController controller = new PollingStationMainController();
controller.init(scanner);
Thread controllerThread = new Thread(controller);
controllerThread.setName("Meerkat PS-Controller Thread");
// Thread serverThread = new Thread(serverController);
// serverThread.setName("Meerkat PS-ServerScanner Thread");
controllerThread.start();
serverThread.start();
Thread.currentThread().setName("Meerkat PS-Controller Thread");
controller.run();
}
}

View File

@ -11,6 +11,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.concrete.ECDSASignature;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.*;
@ -28,7 +29,7 @@ import org.slf4j.LoggerFactory;
*/
public class PollingStationWebScanner implements PollingStationScanner.PollingStationServer {
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
public final static String SCAN_HANDLER_ATTRIBUTE_NAME = "controller";
final Logger logger = LoggerFactory.getLogger(getClass());
@ -59,6 +60,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
*
*/
DigitalSignature verifier;
ByteString connectedScannerID;
private final Server server;
@ -75,17 +77,35 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
return false;
}
}
try {
if (verifySignatures) {
if (verifySignatures) {
try {
if (data.getIdCase() != PollingStation.ConnectionClientData.IdCase.SCANNERPK)
throw new CertificateException("Connection request doesn't include certificate!");
verifier.loadVerificationCertificate(data.getScannerPK());
} catch (CertificateException e) {
logger.warn("Scanner connection with bad certificate: {}", e);
return false;
Crypto.SignatureVerificationKey pk = data.getScannerPK();
connectedScannerID = verifier.computeCertificateFingerprint(pk);
verifier.loadVerificationCertificate(pk);
} else {
// If we don't verify certs, we can still get a public key, in which case
// the ID will be the hash of the public key.
switch (data.getIdCase()) {
case SCANNERID:
connectedScannerID = data.getScannerId();
break;
case SCANNERPK:
connectedScannerID = verifier.computeCertificateFingerprint(data.getScannerPK());
break;
default:
logger.warn("Scanner connection with no ID or certificate!");
return false;
}
}
} catch (CertificateException e) {
logger.warn("Scanner connection with bad certificate: {}", e);
return false;
}
return true;
@ -95,7 +115,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
ScannedData scannedData = data.getData();
if (verifySignatures) {
ByteString scannerID = scannedData.getScannerId();
if (!scannerID.equals(verifier.getSignerID())) {
if (!scannerID.equals(connectedScannerID)) {
logger.warn("Scanner ID doesn't match connection public key");
return false;
}
@ -141,7 +161,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
server = new Server(port);
ServletContextHandler servletContextHandler = new ServletContextHandler(server, contextPath);
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, callbacks);
servletContextHandler.setAttribute(SCAN_HANDLER_ATTRIBUTE_NAME, scanRequestHandler);
ResourceConfig resourceConfig = new ResourceConfig(PollingStationScannerWebApp.class);
resourceConfig.register(ProtobufMessageBodyReader.class);
@ -153,14 +173,16 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
}
@Override
public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
this.verifyNonce = this.verifySignatures = verifyScanner;
nonce = new SecureRandom().nextLong();
server.start();
return null;
return PollingStation.ConnectionServerData.newBuilder()
.setNonce(nonce)
.setServerUrl(server.getURI().toString())
.build();
}
@Override

View File

@ -1,130 +0,0 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.pollingstation.controller.callbacks.ScanCallback;
import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
import meerkat.pollingstation.controller.callbacks.ScanErrorCallback;
import meerkat.pollingstation.controller.commands.PollingStationCommand;
import meerkat.pollingstation.controller.commands.ReceivedScanCommand;
import meerkat.protobuf.PollingStation;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Created by Laura on 3/20/2017.
*/
public class ReceiverScanHandler implements PollingStationScanner.PollingStationServer, Runnable {
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
private final Server server;
private final List<FutureCallback<PollingStation.ScannedData>> callbacks;
private LinkedBlockingQueue<ScanCallback> queue;
private LinkedBlockingQueue<PollingStationCommand> controllerQueue;
private volatile boolean shutDownHasBeenCalled;
public ReceiverScanHandler(int port, String subAddress) {
callbacks = new LinkedList<>();
server = new Server(port);
queue = new LinkedBlockingQueue<>();
shutDownHasBeenCalled = false;
ServletContextHandler servletContextHandler = new ServletContextHandler(server, subAddress);
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, (LinkedBlockingQueue<ScanCallback>) queue);
ResourceConfig resourceConfig = new ResourceConfig(ReceiverWebAPI.class);
resourceConfig.register(ProtobufMessageBodyReader.class);
resourceConfig.register(ProtobufMessageBodyWriter.class);
ServletHolder servletHolder = new ServletHolder(new ServletContainer(resourceConfig));
servletContextHandler.addServlet(servletHolder, "/*");
}
public void setControllerQueue(LinkedBlockingQueue<PollingStationCommand> q) {
this.controllerQueue = q;
}
private boolean wasShutDownCalled () {
return shutDownHasBeenCalled;
}
@Override
public void run() {
try {
start();
} catch (Exception e) {
e.printStackTrace();
System.err.println("Could not start server: " + e.getMessage());
}
}
@Override
public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
server.start();
while (! wasShutDownCalled()) {
try {
handleSingleCallback(queue.take());
}
catch (InterruptedException e) {
System.err.println("Interrupted while reading from command queue " + e);
}
}
}
@Override
public void stop() throws Exception {
server.stop();
}
@Override
public void subscribe(FutureCallback<PollingStation.ScannedData> scanCallback) {
callbacks.add(scanCallback);
}
private void handleSingleCallback(ScanCallback callback) {
if (callback instanceof ScanDataCallback) {
doScannedData((ScanDataCallback) callback);
}
else if (callback instanceof ScanErrorCallback) {
doScanError((ScanErrorCallback) callback);
}
else {
System.out.println("in the else");
// logger.error("handleSingleCommand: unknown type of PollingStationCommand received: " + command.getClass().getName());
// doReportErrorAndForceRestart(systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE));
// doReportErrorAndForceRestart("error message to define");
}
}
// TODO: 4/27/2017 set requestID properly
private void doScannedData(ScanDataCallback cb) {
PollingStation.ScannedData scan = cb.getScannedData();
// String channel = scan.getChannel().toStringUtf8();
int requestID = 1;
long serialNumber = scan.getSignedEncryptedBallot().getEncryptedBallot().getSerialNumber();
controllerQueue.add(new ReceivedScanCommand(requestID, serialNumber));
}
private void doScanError(ScanErrorCallback cb) {
}
}

View File

@ -1,134 +0,0 @@
package meerkat.pollingstation;
import com.google.protobuf.BoolValue;
import meerkat.pollingstation.controller.callbacks.ScanCallback;
import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
import meerkat.pollingstation.controller.callbacks.ScanErrorCallback;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import java.util.concurrent.LinkedBlockingQueue;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_CONNECT_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF;
/**
* Implements a Web-App interface for {@link PollingStationScanner.ScannerClient}
* This class depends on {@link meerkat.pollingstation.ReceiverScanHandler} and works in conjunction with it
*/
@Path("/")
public class ReceiverWebAPI implements PollingStationScanner.ScannerClient {
@Context
ServletContext servletContext;
LinkedBlockingQueue<ScanCallback> callbacks;
/**
* This method is called by the Jetty engine when instantiating the servlet
*/
@PostConstruct
@SuppressWarnings("unchecked")
public void init() throws Exception {
Object context = servletContext.getAttribute(ReceiverScanHandler.CALLBACKS_ATTRIBUTE_NAME);
try {
callbacks = (LinkedBlockingQueue<ScanCallback>) context;
} catch (ClassCastException e) {
throw e;
}
}
@POST
@Path(POLLING_STATION_WEB_SCANNER_CONNECT_PATH)
@Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF)
@Override
public BoolValue connect(PollingStation.ConnectionClientData clientData) {
boolean sent = false;
try {
ScanDataCallback callback = new ScanDataCallback(scannedData);
callbacks.add(callback);
sent = true;
} catch (Exception e) {
e.printStackTrace();
}
return BoolValue.newBuilder()
.setValue(sent)
.build();
}
@POST
@Path(POLLING_STATION_WEB_SCANNER_SCAN_PATH)
@Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF)
@Override
public BoolValue newScan(PollingStation.ScannedData scannedData, Crypto.Signature scannedDataSignature) {
boolean sent = false;
// for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
//
// callback.onSuccess(scannedData);
// handled = true;
//
// }
try {
ScanDataCallback callback = new ScanDataCallback(scannedData);
// scanner.subscribe(callback);
callbacks.add(callback);
sent = true;
} catch (Exception e) {
e.printStackTrace();
}
return BoolValue.newBuilder()
.setValue(sent)
.build();
}
@POST
@Path(POLLING_STATION_WEB_SCANNER_ERROR_PATH)
@Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF)
@Override
public BoolValue reportScanError(PollingStation.ErrorMsg errorMsg, Crypto.Signature errorMsgSignature) {
boolean handled = false;
// for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
//
// callback.onFailure(new IOException(errorMsg.getMsg()));
// handled = true;
//
// }
ScanErrorCallback callback = new ScanErrorCallback(errorMsg);
// scanner.subscribe(callback);
callbacks.add(callback);
return BoolValue.newBuilder()
.setValue(handled)
.build();
}
}

View File

@ -1,8 +0,0 @@
package meerkat.pollingstation.controller.callbacks;
/**
* Created by Laura on 3/20/2017.
*/
public interface ScanCallback {
}

View File

@ -1,38 +0,0 @@
package meerkat.pollingstation.controller.callbacks;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.PollingStation;
/**
* Created by Laura on 3/20/2017.
*/
public class ScanDataCallback implements ScanCallback, FutureCallback<PollingStation.ScannedData> {
private final PollingStation.ScannedData expectedData;
private boolean dataIsAsExpected;
private Throwable thrown;
public ScanDataCallback(PollingStation.ScannedData expectedData) {
this.expectedData = expectedData;
}
public PollingStation.ScannedData getScannedData() {
return this.expectedData;
}
@Override
public void onSuccess(PollingStation.ScannedData result) {
dataIsAsExpected = result.getBallot().getChannel().equals(expectedData.getBallot().getChannel());
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = false;
thrown = t;
}
public void doNothing() {
System.out.println("do nothing hit");
}
}

View File

@ -1,28 +0,0 @@
package meerkat.pollingstation.controller.callbacks;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.PollingStation;
/**
* Created by Laura on 3/20/2017.
*/
public class ScanErrorCallback extends ScanCallback implements FutureCallback<PollingStation.ErrorMsg> {
private final PollingStation.ErrorMsg expectedErrorMessage;
public ScanErrorCallback(PollingStation.ErrorMsg expectedErrorMessage) {
this.expectedErrorMessage = expectedErrorMessage;
}
@Override
public void onSuccess(PollingStation.ErrorMsg msg) {
// dataIsAsExpected = false;
// semaphore.release();
}
@Override
public void onFailure(Throwable t) {
// dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
// semaphore.release();
}
}

View File

@ -0,0 +1,158 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignatureGenerator;
import meerkat.crypto.concrete.ECDSADeterministicSignature;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation;
import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.protobuf.Voting;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
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";
/**
* Waiting time for transmission in ms.
*/
final static long TIMEOUT = 500;
PollingStation.ConnectionServerData serverData = null;
DigitalSignatureGenerator clientSigner;
BlockingQueue<ScannedData> results = new ArrayBlockingQueue<ScannedData>(1);
private Throwable thrown;
private class ScanHandler implements FutureCallback<ScannedData> {
@Override
public void onSuccess(ScannedData result) {
results.add(result);
}
@Override
public void onFailure(Throwable t) {
thrown = t;
}
}
@Before
public void init() {
clientSigner = new ECDSADeterministicSignature();
clientSigner.generateSigningCertificate(BigInteger.ONE, new Date(System.currentTimeMillis()),
new Date(System.currentTimeMillis() + 1000*60*60*24*365), "testing");
logger.debug("Setting up Scanner WebApp!");
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
scanner.subscribe(new ScanHandler());
thrown = null;
try {
serverData = scanner.start(true);
} catch (Exception e) {
fail("Could not start server: " + e.getMessage());
}
logger.info("Started scanner web service. API endpoint: {}", serverData.getServerUrl());
}
PollingStationScanner.ScannerClient connectClient() throws InterruptedException {
PollingStationScanner.ScannerClient scannerClient =
new ScannerClientAPI(clientSigner);
boolean connectOk = scannerClient.connect(serverData);
assertTrue("Connection to Scanner server failed!", connectOk);
return scannerClient;
}
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());
}
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());
}
@Test
public void testSuccessfulScan() throws Exception {
byte[] data = {(byte) 1, (byte) 2};
PollingStation.ScannedBallot ballot = PollingStation.ScannedBallot.newBuilder()
.setChannel(ByteString.copyFrom(data))
.setSignedEncryptedBallot(Voting.SignedEncryptedBallot.newBuilder()
.setEncryptedBallot(Voting.EncryptedBallot.newBuilder()
.setSerialNumber(0)
.setData(Crypto.RerandomizableEncryptedMessage.newBuilder()
.setData(ByteString.copyFromUtf8("Testing"))
)
)
)
.build();
PollingStationScanner.ScannerClient client = connectClient();
boolean scanOk = client.newScan(ballot);
assertTrue("Scan transmission failed", scanOk);
checkResult(ballot, TIMEOUT);
}
@Test
public void testErroneousScan() throws Exception {
PollingStation.ScanError errorMsg = PollingStation.ScanError.newBuilder()
.setMsg("!Error Message!")
.build();
PollingStationScanner.ScannerClient client = connectClient();
boolean scanOk = client.reportError(errorMsg);
assertTrue("Error report transmission failed", scanOk);
checkResult(errorMsg, TIMEOUT);
}
@After
public void shutDown() {
logger.debug("{} shutting down...", scanner.getClass().getCanonicalName());
try {
scanner.stop();
} catch (Exception e) {
fail("Could not stop server: " + e.getMessage());
}
}
}

View File

@ -1,162 +0,0 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation.*;
import meerkat.rest.Constants;
import meerkat.rest.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.util.concurrent.Semaphore;
import static meerkat.pollingstation.PollingStationConstants.*;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Created by Arbel on 25/05/2016.
*/
public class PollingStationWebScannerTest {
private PollingStationScanner.PollingStationServer scanner;
private static final String ADDRESS = "http://localhost";
private static final String SUB_ADDRESS = "";
private static final int PORT = 8080;
private Semaphore semaphore;
private Throwable thrown;
private boolean dataIsAsExpected;
private class ScanHandler implements FutureCallback<ScannedData> {
private final ScannedData expectedData;
public ScanHandler(ScannedData expectedData) {
this.expectedData = expectedData;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = false;
thrown = t;
semaphore.release();
}
}
private class ErrorHandler implements FutureCallback<ScannedData> {
private final String expectedErrorMessage;
public ErrorHandler(String expectedErrorMessage) {
this.expectedErrorMessage = expectedErrorMessage;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = false;
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
semaphore.release();
}
}
@Before
public void init() {
System.err.println("Setting up Scanner WebApp!");
scanner = new PollingStationWebScanner(PORT, SUB_ADDRESS);
semaphore = new Semaphore(0);
thrown = null;
try {
scanner.start();
} catch (Exception e) {
assertThat("Could not start server: " + e.getMessage(), false);
}
}
@Test
public void testSuccessfulScan() throws InterruptedException {
Client client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH);
byte[] data = {(byte) 1, (byte) 2};
ScannedData scannedData = ScannedData.newBuilder()
.setChannel(ByteString.copyFrom(data))
.build();
scanner.subscribe(new ScanHandler(scannedData));
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
response.close();
semaphore.acquire();
assertThat("Scanner has thrown an error", thrown == null);
assertThat("Scanned data received was incorrect", dataIsAsExpected);
}
@Test
public void testErroneousScan() throws InterruptedException {
Client client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_ERROR_PATH);
ErrorMsg errorMsg = ErrorMsg.newBuilder()
.setMsg("!Error Message!")
.build();
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF));
response.close();
semaphore.acquire();
assertThat("Scanner error received was incorrect", dataIsAsExpected);
}
@After
public void close() {
System.err.println("Scanner WebApp shutting down...");
try {
scanner.stop();
} catch (Exception e) {
assertThat("Could not stop server: " + e.getMessage(), false);
}
}
}

View File

@ -1,162 +0,0 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation.ErrorMsg;
import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader;
import meerkat.rest.ProtobufMessageBodyWriter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.util.concurrent.Semaphore;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* Created by Arbel on 25/05/2016.
*/
public class ReceiverTest {
private PollingStationScanner.PollingStationServer scanner;
private static final String ADDRESS = "http://localhost";
private static final String SUB_ADDRESS = "";
private static final int PORT = 8080;
private Semaphore semaphore;
private Throwable thrown;
private boolean dataIsAsExpected;
private class ScanHandler implements FutureCallback<ScannedData> {
private final ScannedData expectedData;
public ScanHandler(ScannedData expectedData) {
this.expectedData = expectedData;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = false;
thrown = t;
semaphore.release();
}
}
private class ErrorHandler implements FutureCallback<ScannedData> {
private final String expectedErrorMessage;
public ErrorHandler(String expectedErrorMessage) {
this.expectedErrorMessage = expectedErrorMessage;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = false;
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
semaphore.release();
}
}
@Before
public void init() {
System.err.println("Setting up Scanner WebApp!");
scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
semaphore = new Semaphore(0);
thrown = null;
try {
scanner.start();
} catch (Exception e) {
assertThat("Could not start server: " + e.getMessage(), false);
}
}
@Test
public void testSuccessfulScan() throws InterruptedException {
Client client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH);
byte[] data = {(byte) 1, (byte) 2};
ScannedData scannedData = ScannedData.newBuilder()
.setChannel(ByteString.copyFrom(data))
.build();
scanner.subscribe(new ScanHandler(scannedData));
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
response.close();
semaphore.acquire();
assertThat("Scanner has thrown an error", thrown == null);
assertThat("Scanned data received was incorrect", dataIsAsExpected);
}
@Test
public void testErroneousScan() throws InterruptedException {
Client client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_ERROR_PATH);
ErrorMsg errorMsg = ErrorMsg.newBuilder()
.setMsg("!Error Message!")
.build();
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF));
response.close();
semaphore.acquire();
assertThat("Scanner error received was incorrect", dataIsAsExpected);
}
@After
public void close() {
System.err.println("ReceiverScanHandler shutting down...");
try {
scanner.stop();
} catch (Exception e) {
assertThat("Could not stop server: " + e.getMessage(), false);
}
}
}

View File

@ -1,133 +0,0 @@
package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation;
import meerkat.protobuf.PollingStation.ErrorMsg;
import meerkat.protobuf.PollingStation.ScannedData;
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 static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
import static org.hamcrest.MatcherAssert.assertThat;
public class Receiver_ClientTest {
final Logger logger = LoggerFactory.getLogger(getClass());
private PollingStationScanner.PollingStationServer scanner;
private static final String CONTEXT_PATH = "/scan";
private Semaphore semaphore;
private Throwable thrown;
private boolean dataIsAsExpected;
PollingStation.ConnectionServerData serverData = null;
private class ScanHandler implements FutureCallback<ScannedData> {
private final ScannedData expectedData;
public ScanHandler(ScannedData expectedData) {
this.expectedData = expectedData;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = false;
thrown = t;
semaphore.release();
}
}
@Before
public void init() {
System.err.println("Setting up Scanner WebApp!");
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
semaphore = new Semaphore(0);
thrown = null;
PollingStation.ConnectionServerData serverData = null;
try {
serverData = scanner.start(false);
} catch (Exception e) {
assertThat("Could not start server: " + e.getMessage(), false);
}
logger.info("Started scanner web service. API endpoint: {}", serverData.getServerUrl());
}
@Test
public void testSuccessfulScan() throws InterruptedException {
byte[] data = {(byte) 1, (byte) 2};
ScannedData scannedData = ScannedData.newBuilder()
.setBallot(PollingStation.ScannedBallot.newBuilder()
.setChannel(ByteString.copyFrom(data))
)
.build();
scanner.subscribe(new ScanHandler(scannedData));
PollingStationScanner.ScannerClient scannerClient = new ScannerClientAPI()
ScannerClientAPI(PSC_URL + POLLING_STATION_WEB_SCANNER_SCAN_PATH);
scannerClient.newScan(scannedData, null);
semaphore.acquire();
assertThat("Scanner has thrown an error", thrown == null);
assertThat("Scanned data received was incorrect", dataIsAsExpected);
}
@Test
public void testErroneousScan() throws InterruptedException {
ErrorMsg errorMsg = ErrorMsg.newBuilder()
.setMsg("!Error Message!")
.build();
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
ScannerClientAPI scannerClient = new ScannerClientAPI();
PSC_URL + POLLING_STATION_WEB_SCANNER_ERROR_PATH);
scannerClient.sendError(errorMsg);
semaphore.acquire();
assertThat("Scanner error received was incorrect", dataIsAsExpected);
}
@After
public void close() {
System.err.println("ReceiverScanHandler shutting down...");
try {
scanner.stop();
} catch (Exception e) {
assertThat("Could not stop server: " + e.getMessage(), false);
}
}
}

View File

@ -7,8 +7,7 @@ public interface PollingStationConstants {
// Relative addresses for Scanner operations
public static final String POLLING_STATION_WEB_SCANNER_SCAN_PATH = "/scan";
public static final String POLLING_STATION_WEB_SCANNER_ERROR_PATH = "/error";
public static final String POLLING_STATION_WEB_SCANNER_SCAN_PATH = "/data";
public static final String POLLING_STATION_WEB_SCANNER_CONNECT_PATH = "/connect";
}

View File

@ -2,7 +2,7 @@ package meerkat.pollingstation;
import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignatureGenerator;
import meerkat.crypto.DigitalSignature;
import meerkat.protobuf.PollingStation;
import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader;
@ -17,16 +17,19 @@ import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.security.SignatureException;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_CONNECT_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
/**
* Created by Laura on 3/20/2017.
*/
public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
final Logger logger = LoggerFactory.getLogger(getClass());
DigitalSignatureGenerator signer;
DigitalSignature signer;
ByteString scannerID;
Client client;
WebTarget webTarget;
WebTarget baseWebTarget;
/**
* Serial number of request (zeroed on new connection to server)
@ -55,7 +58,7 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
* Constructor for client that signs messages.
* @param signer
*/
public ScannerClientAPI(DigitalSignatureGenerator signer) {
public ScannerClientAPI(DigitalSignature signer) {
this();
assert (signer != null && signer.getSignerID() != null); // Use the default constructor instead
@ -71,7 +74,7 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
@Override
public boolean connect(PollingStation.ConnectionServerData serverData) {
serial = 0;
webTarget = client.target(serverData.getServerUrl());
baseWebTarget = client.target(serverData.getServerUrl());
PollingStation.ConnectionClientData.Builder clientData = PollingStation.ConnectionClientData.newBuilder()
.setNonce(serverData.getNonce());
@ -81,7 +84,8 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
clientData.setScannerId(scannerID);
}
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
Response response = baseWebTarget.path(POLLING_STATION_WEB_SCANNER_CONNECT_PATH)
.request(Constants.MEDIATYPE_PROTOBUF)
.post(Entity.entity(clientData.build(), Constants.MEDIATYPE_PROTOBUF));
BoolValue res = response.readEntity(BoolValue.class);
response.close();
@ -106,7 +110,8 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
}
}
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
Response response = baseWebTarget.path(POLLING_STATION_WEB_SCANNER_SCAN_PATH)
.request(Constants.MEDIATYPE_PROTOBUF)
.post(Entity.entity(signedDataBuilder.build(), Constants.MEDIATYPE_PROTOBUF));
BoolValue res = response.readEntity(BoolValue.class);
response.close();