diff --git a/android-scanner/build.gradle b/android-scanner/build.gradle
index 1e33557..ba8b498 100644
--- a/android-scanner/build.gradle
+++ b/android-scanner/build.gradle
@@ -56,23 +56,18 @@ dependencies {
compile project(':meerkat-common')
compile project(':scanner-api-common')
- // compile 'com.android.support.constraint:constraint-layout:1.0.0-beta3'
-
- //compile 'com.android.support:appcompat-v7:23.1.0'
- // Google protobufs
- // Retrofit
-// compile 'com.squareup.retrofit2:retrofit:2.1.0'
-// compile 'com.squareup.retrofit2:converter-protobuf:2.2.0'
-
- // androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
-// exclude group: 'com.android.support', module: 'support-annotations'
-// })
-
compile 'com.android.support:appcompat-v7:26.0.+'
compile 'com.google.protobuf:protobuf-java:3.+'
compile 'com.android.support:support-v4:26.0.+'
compile 'com.android.support:support-vector-drawable:26.0.+'
testCompile 'junit:junit:4.12'
+
+ // Android logging
+ compile 'com.github.tony19:logback-android-core:1.1.1-6'
+ compile('com.github.tony19:logback-android-classic:1.1.1-6') {
+ // workaround issue #73
+ exclude group: 'com.google.android', module: 'android'
+ }
}
repositories {
diff --git a/android-scanner/src/main/assets/logback.xml b/android-scanner/src/main/assets/logback.xml
new file mode 100644
index 0000000..5a9f909
--- /dev/null
+++ b/android-scanner/src/main/assets/logback.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ %logger{12}
+
+
+ [%-20thread] %msg
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/MainActivity.java b/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/MainActivity.java
index e6aad8b..8ef376f 100644
--- a/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/MainActivity.java
+++ b/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/MainActivity.java
@@ -4,7 +4,9 @@ package com.meerkat.laura.fakescannerapp;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.preference.Preference;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
@@ -16,7 +18,9 @@ import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
+import ch.qos.logback.classic.android.BasicLogcatConfigurator;
import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.Message;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import meerkat.crypto.DigitalSignatureGenerator;
@@ -31,7 +35,12 @@ import java.util.Date;
import static com.meerkat.laura.fakescannerapp.R.xml.preferences;
-public class MainActivity extends AppCompatActivity implements View.OnClickListener {
+public class MainActivity extends AppCompatActivity implements View.OnClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
+
+ static {
+ BasicLogcatConfigurator.configureDefaultContext();
+ }
+
final public static String SCANNER_NAME = "AndroidScanner";
SharedPreferences sharedPref;
@@ -40,6 +49,90 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
PollingStationScanner.ScannerClient scannerClient;
DigitalSignatureGenerator signer;
+ class AsyncScanConnect extends AsyncTask {
+ PollingStation.ConnectionServerData serverData;
+
+ public AsyncScanConnect(PollingStation.ConnectionServerData connectionServerData) {
+ this.serverData = connectionServerData;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ serverStatus.setTextColor(Color.BLUE);
+ serverStatus.setText("Connecting to " + serverData.getServerUrl());
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... foo) {
+ try {
+ return scannerClient.connect(serverData);
+ } catch (RuntimeException e) {
+ Log.e("MainActivity", "Exception during connect: " + e.toString());
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean res) {
+ super.onPostExecute(res);
+ if (res) {
+ serverStatus.setTextColor(Color.GREEN);
+ serverStatus.setText("Connected to " + serverData.getServerUrl());
+ } else {
+ serverStatus.setTextColor(Color.RED);
+ serverStatus.setText("Connection failed: " + serverData.getServerUrl());
+ }
+ }
+ }
+
+ class AsyncScanSend extends AsyncTask {
+ Message data;
+
+ public AsyncScanSend(Message data) {
+ this.data = data;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ serverStatus.setTextColor(Color.BLUE);
+ serverStatus.setText("Sending to server...");
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... foo) {
+ try {
+ if (data instanceof PollingStation.ScannedBallot) {
+ return scannerClient.newScan((PollingStation.ScannedBallot) data);
+ } else if (data instanceof PollingStation.ScanError) {
+ return scannerClient.reportError((PollingStation.ScanError) data);
+ } else {
+ Log.e("MainActivity", "Trying to send invalid message to scanner");
+ return false;
+ }
+ } catch (RuntimeException e) {
+ Log.e("MainActivity", "Exception during send: " + e.toString());
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean res) {
+ super.onPostExecute(res);
+ if (res) {
+ serverStatus.setTextColor(Color.GREEN);
+ serverStatus.setText("Sent successfully");
+ Log.i("MainActivity", "post submitted to API.");
+ } else {
+ serverStatus.setTextColor(Color.RED);
+ serverStatus.setText("Send failed");
+ Log.e("MainActivity", "Unable to submit post to API.");
+ }
+ }
+ }
+
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
@@ -61,60 +154,52 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
}
void setNewScannerClientAPI(SharedPreferences sharedPref) {
- String pscUrlString = sharedPref.getString(SettingsActivity.PSC_URL, "");
+ String pscUrlString = sharedPref.getString(SettingsFragment.PSC_URL, "");
+ long nonce = 0;
- scannerClient = new ScannerClientAPI(signer);
+ try {
+ nonce = Long.parseLong(sharedPref.getString(SettingsFragment.PSC_NONCE, "0"));
+ } catch (NumberFormatException e) {
+ // Ignore
+ }
- serverStatus.setTextColor(Color.BLUE);
- serverStatus.setText("Connecting to " + pscUrlString);
+ if (pscUrlString.isEmpty())
+ return;
PollingStation.ConnectionServerData serverData = PollingStation.ConnectionServerData.newBuilder()
.setServerUrl(pscUrlString)
- .setNonce(0)
+ .setNonce(nonce)
.build();
- boolean res = scannerClient.connect(serverData);
- if (res) {
- serverStatus.setTextColor(Color.GREEN);
- serverStatus.setText("Connected to " + pscUrlString);
- } else {
- serverStatus.setTextColor(Color.RED);
- serverStatus.setText("Connection failed: " + pscUrlString);
- }
-
+ new AsyncScanConnect(serverData).execute();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- PreferenceManager.setDefaultValues(this, preferences, false);
- sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
- sharedPref.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() {
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
- if (s.equals(SettingsActivity.PSC_URL))
- setNewScannerClientAPI(sharedPreferences);
- }
- });
-
- scanBtn = (Button)findViewById(R.id.scan_button);
- formatTxt = (TextView)findViewById(R.id.scan_format);
- contentTxt = (TextView)findViewById(R.id.scan_content);
- responseTxt = (TextView)findViewById(R.id.server_response);
- serverStatus = (TextView) findViewById(R.id.serverStatus);
-
signer = new ECDSADeterministicSignature();
signer.generateSigningCertificate(BigInteger.ONE, new Date(System.currentTimeMillis()),
- new Date(System.currentTimeMillis() + 1000*60*60*24*365), SCANNER_NAME);
+ new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 365), SCANNER_NAME);
+ scannerClient = new ScannerClientAPI(signer);
+
+ PreferenceManager.setDefaultValues(this, preferences, false);
+ sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+ sharedPref.registerOnSharedPreferenceChangeListener(this);
+
+ scanBtn = (Button) findViewById(R.id.scan_button);
+ formatTxt = (TextView) findViewById(R.id.scan_format);
+ contentTxt = (TextView) findViewById(R.id.scan_content);
+ responseTxt = (TextView) findViewById(R.id.server_response);
+ serverStatus = (TextView) findViewById(R.id.serverStatus);
setNewScannerClientAPI(sharedPref);
scanBtn.setOnClickListener(this);
}
- public void onClick(View v){
- if(v.getId()==R.id.scan_button){
+ public void onClick(View v) {
+ if (v.getId() == R.id.scan_button) {
IntentIntegrator scanIntegrator = new IntentIntegrator(this);
formatTxt.setText("Initiating scan...");
scanIntegrator.initiateScan();
@@ -131,28 +216,24 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
contentTxt.setText("CONTENT: " + scanContent + "sending data to server...");
// sendPost(rawBytes);
sendPost(scanContent);
- } else{
+ } else {
Toast toast = Toast.makeText(getApplicationContext(),
"No scan data received!", Toast.LENGTH_SHORT);
toast.show();
}
}
-// public void sendPost(byte[] body) {
-public void sendPost(String scanContent) {
+ // public void sendPost(byte[] body) {
+ public void sendPost(String scanContent) {
try {
PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.parseFrom(Base64.decode(scanContent.getBytes(), Base64.DEFAULT));
- //
- // PollingStation.ScannedData scannedData = PollingStation.ScannedData.newBuilder()
- // .setChannel(ByteString.copyFrom(body))
- // .build();
+ //
+ // PollingStation.ScannedData scannedData = PollingStation.ScannedData.newBuilder()
+ // .setChannel(ByteString.copyFrom(body))
+ // .build();
- if (scannerClient.newScan(scannedBallot)) {
- Log.i("MainActivity", "post submitted to API.");
- } else {
- Log.e("MainActivity", "Unable to submit post to API.");
- }
+ new AsyncScanSend(scannedBallot).execute();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
@@ -162,4 +243,10 @@ public void sendPost(String scanContent) {
responseTxt.setText(response);
}
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key){
+ setNewScannerClientAPI(sharedPreferences);
+ }
+
}
diff --git a/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/SettingsActivity.java b/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/SettingsActivity.java
index 4e0af64..56660ad 100644
--- a/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/SettingsActivity.java
+++ b/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/SettingsActivity.java
@@ -1,14 +1,26 @@
package com.meerkat.laura.fakescannerapp;
+import android.app.Activity;
+import android.content.SharedPreferences;
import android.os.Bundle;
-import android.preference.PreferenceActivity;
+import android.preference.PreferenceManager;
-public class SettingsActivity extends PreferenceActivity {
- public final static String PSC_URL = "psc_url";
+/**
+ * Created by talm on 25/06/17.
+ */
+public class SettingsActivity extends Activity {
+ SettingsFragment settings;
@Override
- public void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.preferences);
+
+ settings = new SettingsFragment();
+ PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(settings);
+
+ // Display the fragment as the main content.
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, settings)
+ .commit();
}
-}
\ No newline at end of file
+}
diff --git a/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/SettingsFragment.java b/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/SettingsFragment.java
new file mode 100644
index 0000000..0adaa66
--- /dev/null
+++ b/android-scanner/src/main/java/com/meerkat/laura/fakescannerapp/SettingsFragment.java
@@ -0,0 +1,34 @@
+package com.meerkat.laura.fakescannerapp;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+
+public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
+ public final static String PSC_URL = "psc_url";
+ public final static String PSC_NONCE = "psc_nonce";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ Preference pref = findPreference(key);
+ if (pref == null)
+ return;
+ switch(key) {
+ case SettingsFragment.PSC_URL:
+ pref.setSummary(sharedPreferences.getString(key, ""));
+ break;
+ case SettingsFragment.PSC_NONCE:
+ pref.setSummary(sharedPreferences.getString(key, ""));
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/android-scanner/src/main/res/values/strings.xml b/android-scanner/src/main/res/values/strings.xml
index 3dd1e94..d84450c 100644
--- a/android-scanner/src/main/res/values/strings.xml
+++ b/android-scanner/src/main/res/values/strings.xml
@@ -17,7 +17,7 @@
Scanner Service URL
- http://192.168.70.10:8880/
+ http://192.168.70.10:8880/scan
Add friends to messages
diff --git a/android-scanner/src/main/res/xml/preferences.xml b/android-scanner/src/main/res/xml/preferences.xml
index 909e7af..a3876df 100644
--- a/android-scanner/src/main/res/xml/preferences.xml
+++ b/android-scanner/src/main/res/xml/preferences.xml
@@ -12,6 +12,15 @@
android:maxLines="1"
android:inputType="textUri" />
+
+
diff --git a/meerkat-common/build.gradle b/meerkat-common/build.gradle
index 2c29a9e..8ba3dc7 100644
--- a/meerkat-common/build.gradle
+++ b/meerkat-common/build.gradle
@@ -42,7 +42,7 @@ version += "${isSnapshot ? '-SNAPSHOT' : ''}"
dependencies {
// Logging
- compile 'org.slf4j:slf4j-api:1.7.7'
+ compile 'org.slf4j:slf4j-api:1.7.21'
runtime 'ch.qos.logback:logback-classic:1.1.2'
runtime 'ch.qos.logback:logback-core:1.1.2'
@@ -53,12 +53,18 @@ dependencies {
compile 'com.google.protobuf:protobuf-java:3.+'
// ListeningExecutor
- compile 'com.google.guava:guava:15.0'
+ compile 'com.google.guava:guava:19.0'
// Crypto
compile 'org.factcenter.qilin:qilin:1.2.+'
- compile 'org.bouncycastle:bcprov-jdk15on:1.57'
- compile 'org.bouncycastle:bcpkix-jdk15on:1.57' // For certificate generation
+
+ // Use SpongyCastle instead of bouncycastle for android compatibility
+// compile 'org.bouncycastle:bcprov-jdk15on:1.57'
+// compile 'org.bouncycastle:bcpkix-jdk15on:1.57' // For certificate generation
+
+ compile 'com.madgag.spongycastle:prov:1.56.0.0'
+ compile 'com.madgag.spongycastle:bcpkix-jdk15on:1.56.0.0' // For certificate generation
+
testCompile 'junit:junit:4.+'
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 4bafe3d..287b481 100644
--- a/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java
+++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/ECDSASignature.java
@@ -6,17 +6,17 @@ import meerkat.crypto.DigitalSignatureGenerator;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Crypto.Signature;
import meerkat.util.Hex;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.*;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.spongycastle.asn1.ASN1EncodableVector;
+import org.spongycastle.asn1.DERSequence;
+import org.spongycastle.asn1.x500.X500Name;
+import org.spongycastle.asn1.x509.*;
+import org.spongycastle.asn1.x509.Extension;
+import org.spongycastle.cert.X509v3CertificateBuilder;
+import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.spongycastle.cert.jcajce.JcaX509ExtensionUtils;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.operator.ContentSigner;
+import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/meerkat-common/src/main/java/meerkat/crypto/concrete/GlobalCryptoSetup.java b/meerkat-common/src/main/java/meerkat/crypto/concrete/GlobalCryptoSetup.java
index 5333a25..67ec7b4 100644
--- a/meerkat-common/src/main/java/meerkat/crypto/concrete/GlobalCryptoSetup.java
+++ b/meerkat-common/src/main/java/meerkat/crypto/concrete/GlobalCryptoSetup.java
@@ -1,6 +1,6 @@
package meerkat.crypto.concrete;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/meerkat-common/src/main/resources/logback.groovy b/meerkat-common/src/main/resources/logback.groovy
index 076b6c4..9bf5095 100644
--- a/meerkat-common/src/main/resources/logback.groovy
+++ b/meerkat-common/src/main/resources/logback.groovy
@@ -1,9 +1,7 @@
-
-
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.filter.ThresholdFilter
-import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.util.Duration
+
import static ch.qos.logback.classic.Level.*
if (System.getProperty("log.debug") != null) {
@@ -41,6 +39,4 @@ if (haveBeagle) {
appenders += ["SOCKET"]
}
-root(LOG_LEVEL, appenders)
-
-
+root(LOG_LEVEL, appenders)
\ No newline at end of file
diff --git a/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java b/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java
index c274794..b5d082d 100644
--- a/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java
+++ b/polling-station/src/main/java/meerkat/pollingstation/PollingStationToyRun.java
@@ -29,7 +29,7 @@ public class PollingStationToyRun {
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
PollingStation.ConnectionServerData serverData = scanner.start(true);
- logger.info("Started polling station web scanner on {}", serverData.getServerUrl());
+ logger.info("Started polling station web scanner on {} (nonce: {})", serverData.getServerUrl(), Long.toUnsignedString(serverData.getNonce()));
PollingStationMainController controller = new PollingStationMainController();
controller.init(scanner);
diff --git a/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java b/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java
index a733867..34bd315 100644
--- a/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java
+++ b/polling-station/src/main/java/meerkat/pollingstation/PollingStationWebScanner.java
@@ -242,7 +242,8 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
@Override
public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
this.verifyNonce = this.verifySignatures = verifyScanner;
- nonce = new SecureRandom().nextLong();
+ // Use a 40-bit nonce (should be enough, since you can't do offline verification).
+ nonce = new SecureRandom().nextLong() & 0xffffffffffL;
server.start();
diff --git a/scanner-api-common/build.gradle b/scanner-api-common/build.gradle
index 70701b1..e4c02b9 100644
--- a/scanner-api-common/build.gradle
+++ b/scanner-api-common/build.gradle
@@ -26,7 +26,7 @@ sourceCompatibility = '1.7'
ext { isSnapshot = false }
ext {
- groupId = 'org.factcenter.meerkat'
+ groupId = 'org.factcenter.meerkat'
// Credentials for publishing repositories
publishRepository = "https://cs.idc.ac.il/nexus/content/repositories/${project.isSnapshot ? 'snapshots' : 'releases'}"
publishUser = project.hasProperty('publishUser') ? project.property('publishUser') : ""
@@ -161,21 +161,6 @@ if (project.hasProperty('mainClassName') && (mainClassName != null)) {
*===================================*/
repositories {
-
- // Prefer the local nexus repository (it may have 3rd party artifacts not found in mavenCentral)
- maven {
- url nexusRepository
-
- if (isSnapshot) {
- credentials { username
- password
-
- username nexusUser
- password nexusPassword
- }
- }
- }
-
// Use local maven repository
mavenLocal()