diff --git a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java index b556bf9..3899e0d 100644 --- a/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java +++ b/meerkat-common/src/main/java/meerkat/bulletinboard/GenericBulletinBoardSignature.java @@ -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); diff --git a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java index b6f0415..2f1f3bd 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/DigitalSignature.java @@ -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. diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSADeterministicSignature.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSADeterministicSignature.java index 0afbdd7..23a70b1 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSADeterministicSignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSADeterministicSignature.java @@ -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; diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java index eef377c..4bafe3d 100644 --- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java +++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java @@ -135,11 +135,33 @@ public class ECDSASignature implements DigitalSignatureGenerator { loadVerificationCertificates(certStream); } + + public Collection deserializeCertificates(InputStream certStream) + throws CertificateException { + CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509); + + Collection certs = certificateFactory.generateCertificates(certStream); + return certs; + } + + public Collection 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 certs = deserializeCertificates(cert); + return computeCertificateFingerprint(certs.iterator().next()); + } + + @Override public void loadVerificationCertificates(InputStream certStream) throws CertificateException { - CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509); - Collection certs = certificateFactory.generateCertificates(certStream); + Collection 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 { diff --git a/polling-station/src/main/java/meerkat/pollingstation/PollingStationClientToyRun.java b/polling-station/src/main/java/meerkat/pollingstation/PollingStationClientToyRun.java index 953df29..82df7ae 100644 --- a/polling-station/src/main/java/meerkat/pollingstation/PollingStationClientToyRun.java +++ b/polling-station/src/main/java/meerkat/pollingstation/PollingStationClientToyRun.java @@ -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); } } diff --git a/polling-station/src/main/java/meerkat/pollingstation/PollingStationScannerWebApp.java b/polling-station/src/main/java/meerkat/pollingstation/PollingStationScannerWebApp.java index 3208aad..c16d839 100644 --- a/polling-station/src/main/java/meerkat/pollingstation/PollingStationScannerWebApp.java +++ b/polling-station/src/main/java/meerkat/pollingstation/PollingStationScannerWebApp.java @@ -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; diff --git a/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java b/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java index 424f41a..391c377 100644 --- a/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java +++ b/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java @@ -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(); } } diff --git a/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java b/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java index 6eee87c..8b459d7 100644 --- a/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java +++ b/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java @@ -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 diff --git a/polling-station/src/main/java/meerkat/pollingstation/ReceiverScanHandler.java b/polling-station/src/main/java/meerkat/pollingstation/ReceiverScanHandler.java deleted file mode 100644 index 3436eb6..0000000 --- a/polling-station/src/main/java/meerkat/pollingstation/ReceiverScanHandler.java +++ /dev/null @@ -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> callbacks; - - private LinkedBlockingQueue queue; - private LinkedBlockingQueue 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) 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 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 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) { - - } - -} \ No newline at end of file diff --git a/polling-station/src/main/java/meerkat/pollingstation/ReceiverWebAPI.java b/polling-station/src/main/java/meerkat/pollingstation/ReceiverWebAPI.java deleted file mode 100644 index 29a7487..0000000 --- a/polling-station/src/main/java/meerkat/pollingstation/ReceiverWebAPI.java +++ /dev/null @@ -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 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) 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 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 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(); - - } - - -} diff --git a/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanCallback.java b/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanCallback.java deleted file mode 100644 index 3900fa8..0000000 --- a/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanCallback.java +++ /dev/null @@ -1,8 +0,0 @@ -package meerkat.pollingstation.controller.callbacks; - -/** - * Created by Laura on 3/20/2017. - */ -public interface ScanCallback { - -} diff --git a/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanDataCallback.java b/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanDataCallback.java deleted file mode 100644 index 7176e3a..0000000 --- a/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanDataCallback.java +++ /dev/null @@ -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 { - - 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"); - } -} diff --git a/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanErrorCallback.java b/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanErrorCallback.java deleted file mode 100644 index 58aa16e..0000000 --- a/polling-station/src/main/java/meerkat/pollingstation/controller/callbacks/ScanErrorCallback.java +++ /dev/null @@ -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 { - - 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(); - } -} diff --git a/polling-station/src/test/java/meerkat/pollingstation/PollingStationScannerTest.java b/polling-station/src/test/java/meerkat/pollingstation/PollingStationScannerTest.java new file mode 100644 index 0000000..a152fd3 --- /dev/null +++ b/polling-station/src/test/java/meerkat/pollingstation/PollingStationScannerTest.java @@ -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 results = new ArrayBlockingQueue(1); + + + private Throwable thrown; + + private class ScanHandler implements FutureCallback { + @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()); + } + + } + +} \ No newline at end of file diff --git a/polling-station/src/test/java/meerkat/pollingstation/PollingStationWebScannerTest.java b/polling-station/src/test/java/meerkat/pollingstation/PollingStationWebScannerTest.java deleted file mode 100644 index 39c9ff0..0000000 --- a/polling-station/src/test/java/meerkat/pollingstation/PollingStationWebScannerTest.java +++ /dev/null @@ -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 { - - 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 { - - 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); - } - - } - -} \ No newline at end of file diff --git a/polling-station/src/test/java/meerkat/pollingstation/ReceiverTest.java b/polling-station/src/test/java/meerkat/pollingstation/ReceiverTest.java deleted file mode 100644 index a5b138b..0000000 --- a/polling-station/src/test/java/meerkat/pollingstation/ReceiverTest.java +++ /dev/null @@ -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 { - - 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 { - - 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); - } - - } - -} \ No newline at end of file diff --git a/polling-station/src/test/java/meerkat/pollingstation/Receiver_ClientTest.java b/polling-station/src/test/java/meerkat/pollingstation/Receiver_ClientTest.java deleted file mode 100644 index 762784c..0000000 --- a/polling-station/src/test/java/meerkat/pollingstation/Receiver_ClientTest.java +++ /dev/null @@ -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 { - - 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); - } - - } - -} \ No newline at end of file diff --git a/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationConstants.java b/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationConstants.java index fa3e863..c00dd1a 100644 --- a/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationConstants.java +++ b/scanner-api-common/src/main/java/meerkat/pollingstation/PollingStationConstants.java @@ -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"; } diff --git a/scanner-api-common/src/main/java/meerkat/pollingstation/ScannerClientAPI.java b/scanner-api-common/src/main/java/meerkat/pollingstation/ScannerClientAPI.java index e39317e..9c8f31b 100644 --- a/scanner-api-common/src/main/java/meerkat/pollingstation/ScannerClientAPI.java +++ b/scanner-api-common/src/main/java/meerkat/pollingstation/ScannerClientAPI.java @@ -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();