Merge branch '123-encrypted-crash-reports' into 'master'

Encrypted crash reports

Part of #123.

See merge request !128
This commit is contained in:
akwizgran
2016-04-25 10:17:53 +00:00
24 changed files with 715 additions and 53 deletions

View File

@@ -16,6 +16,9 @@ dependencies {
compile project(':briar-api')
compile project(':briar-core')
compile fileTree(dir: 'libs', include: '*.jar')
// This shouldn't be necessary; per section 23.4.4 of the Gradle docs:
// "file dependencies are included in transitive project dependencies within the same build".
compile files('../briar-core/libs/jsocks.jar')
compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4'

Binary file not shown.

View File

@@ -4,6 +4,8 @@
<string name="nav_drawer_close_description">Close the navigation drawer</string>
<string name="app_name">Briar</string>
<string name="crash_report_title">Briar Crash Report</string>
<string name="crash_report_saved">Crash report saved. It will be sent the next time you log into Briar.</string>
<string name="crash_report_not_saved">Could not save crash report to disk.</string>
<string name="ongoing_notification_title">Signed into Briar</string>
<string name="ongoing_notification_text">Touch to show the dashboard.</string>
<string name="setup_title">Briar Setup</string>
@@ -173,6 +175,8 @@
<string name="dialog_message_connect_panic_app">Are you sure that you want to allow %1$s to trigger destructive panic button actions?</string>
<string name="dialog_title_welcome">Welcome to Briar</string>
<string name="dialog_welcome_message">Add a contact to start communicating securely or press the icon in the upper left corner of the screen for more options.</string>
<string name="dialog_title_share_crash_report">Send to developers?</string>
<string name="dialog_message_share_crash_report">Would you like to send this crash report to the developers? It will be stored encrypted on your device until the next time you log into Briar, and then sent securely to the developers.</string>
<string name="dialog_button_ok">OK</string>
<string name="dialog_button_introduce">Introduce</string>
<string name="dialog_button_accept">Accept</string>

View File

@@ -37,6 +37,8 @@ import dagger.Component;
})
public interface AndroidComponent extends CoreEagerSingletons {
void inject(CrashReportActivity crashReportActivity);
void inject(SplashScreenActivity activity);
void inject(SetupActivity activity);

View File

@@ -4,13 +4,18 @@ import android.app.Application;
import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.PublicKey;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.reporting.DevConfig;
import org.briarproject.api.ui.UiCallback;
import org.briarproject.util.StringUtils;
import java.io.File;
import java.security.GeneralSecurityException;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -19,6 +24,8 @@ import dagger.Module;
import dagger.Provides;
import static android.content.Context.MODE_PRIVATE;
import static org.briarproject.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
import static org.briarproject.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
@Module
public class AppModule {
@@ -90,6 +97,28 @@ public class AppModule {
};
}
@Provides
@Singleton
public DevConfig provideDevConfig(final CryptoComponent crypto) {
return new DevConfig() {
@Override
public PublicKey getDevPublicKey() {
try {
return crypto.getMessageKeyParser().parsePublicKey(
StringUtils.fromHexString(DEV_PUBLIC_KEY_HEX));
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@Override
public String getDevOnionAddress() {
return DEV_ONION_ADDRESS;
}
};
}
@Provides
@Singleton
ReferenceManager provideReferenceManager() {

View File

@@ -2,7 +2,9 @@ package org.briarproject.android;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -18,12 +20,15 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.reporting.DevReporter;
import org.briarproject.util.StringUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -36,12 +41,10 @@ import java.util.Scanner;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.inject.Inject;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.content.Intent.ACTION_SEND;
import static android.content.Intent.EXTRA_EMAIL;
import static android.content.Intent.EXTRA_SUBJECT;
import static android.content.Intent.EXTRA_TEXT;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
@@ -59,6 +62,9 @@ public class CrashReportActivity extends AppCompatActivity
private LinearLayout status = null;
private View progress = null;
@Inject
protected DevReporter reporter;
private volatile String stack = null;
private volatile int pid = -1;
private volatile BluetoothAdapter bt = null;
@@ -68,6 +74,9 @@ public class CrashReportActivity extends AppCompatActivity
super.onCreate(state);
setContentView(R.layout.activity_crash);
((BriarApplication) getApplication()).getApplicationComponent()
.inject(this);
status = (LinearLayout) findViewById(R.id.crash_status);
progress = findViewById(R.id.progress_wheel);
@@ -94,7 +103,20 @@ public class CrashReportActivity extends AppCompatActivity
}
public void onClick(View view) {
share();
// TODO Encapsulate the dialog in a re-usable fragment
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.dialog_title_share_crash_report);
builder.setMessage(R.string.dialog_message_share_crash_report);
builder.setNegativeButton(R.string.cancel_button, null);
builder.setPositiveButton(R.string.send,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
saveCrashReport();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
private void refresh() {
@@ -324,7 +346,7 @@ public class CrashReportActivity extends AppCompatActivity
return Character.toUpperCase(first) + s.substring(1);
}
private void share() {
private void saveCrashReport() {
StringBuilder s = new StringBuilder();
for (Entry<String, String> e : getStatusMap().entrySet()) {
s.append(e.getKey());
@@ -332,12 +354,19 @@ public class CrashReportActivity extends AppCompatActivity
s.append(e.getValue());
s.append("\n\n");
}
String body = s.toString();
Intent i = new Intent(ACTION_SEND);
i.setType("message/rfc822");
i.putExtra(EXTRA_EMAIL, new String[] { "contact@briarproject.org" });
i.putExtra(EXTRA_SUBJECT, "Crash report");
i.putExtra(EXTRA_TEXT, body);
startActivity(Intent.createChooser(i, "Send to developers"));
final String crashReport = s.toString();
try {
reporter.encryptCrashReportToFile(
AndroidUtils.getCrashReportDir(this), crashReport);
Toast.makeText(this, R.string.crash_report_saved, Toast.LENGTH_LONG)
.show();
finish();
} catch (FileNotFoundException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, "Error while saving encrypted crash report",
e);
Toast.makeText(this, R.string.crash_report_not_saved,
Toast.LENGTH_SHORT).show();
}
}
}

View File

@@ -17,11 +17,15 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static android.content.Context.MODE_PRIVATE;
public class AndroidUtils {
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
private static final String STORED_CRASH_REPORTS = "crash-reports";
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
public static Collection<String> getSupportedArchitectures() {
@@ -84,4 +88,8 @@ public class AndroidUtils {
}
}
}
public static File getCrashReportDir(Context ctx) {
return ctx.getDir(STORED_CRASH_REPORTS, MODE_PRIVATE);
}
}

View File

@@ -10,6 +10,7 @@ import org.briarproject.api.plugins.BackoffFactory;
import org.briarproject.api.plugins.PluginConfig;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.plugins.simplex.SimplexPluginFactory;
import org.briarproject.api.reporting.DevReporter;
import org.briarproject.api.system.LocationUtils;
import org.briarproject.plugins.droidtooth.DroidtoothPluginFactory;
import org.briarproject.plugins.tcp.AndroidLanTcpPluginFactory;
@@ -31,12 +32,13 @@ public class AndroidPluginsModule {
public PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
AndroidExecutor androidExecutor,
SecureRandom random, BackoffFactory backoffFactory, Application app,
LocationUtils locationUtils, EventBus eventBus) {
LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth = new DroidtoothPluginFactory(ioExecutor,
androidExecutor, appContext, random, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, appContext,
locationUtils, eventBus);
locationUtils, reporter, eventBus);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
backoffFactory, appContext);
final Collection<DuplexPluginFactory> duplex =

View File

@@ -13,6 +13,7 @@ import net.freehaven.tor.control.TorControlConnection;
import net.sourceforge.jsocks.socks.Socks5Proxy;
import net.sourceforge.jsocks.socks.SocksSocket;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.PseudoRandom;
@@ -25,6 +26,7 @@ import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.reporting.DevReporter;
import org.briarproject.api.settings.Settings;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.LocationUtils;
@@ -83,6 +85,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
private final Executor ioExecutor;
private final Context appContext;
private final LocationUtils locationUtils;
private final DevReporter reporter;
private final Clock clock;
private final DuplexPluginCallback callback;
private final String architecture;
@@ -104,12 +107,13 @@ class TorPlugin implements DuplexPlugin, EventHandler,
private volatile BroadcastReceiver networkStateReceiver = null;
TorPlugin(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, Clock clock,
LocationUtils locationUtils, DevReporter reporter, Clock clock,
DuplexPluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, int pollingInterval) {
this.ioExecutor = ioExecutor;
this.appContext = appContext;
this.locationUtils = locationUtils;
this.reporter = reporter;
this.clock = clock;
this.callback = callback;
this.architecture = architecture;
@@ -172,13 +176,14 @@ class TorPlugin implements DuplexPlugin, EventHandler,
String torPath = torFile.getAbsolutePath();
String configPath = configFile.getAbsolutePath();
String pid = String.valueOf(android.os.Process.myPid());
String[] cmd = { torPath, "-f", configPath, OWNER, pid };
String[] env = { "HOME=" + torDirectory.getAbsolutePath() };
String[] cmd = {torPath, "-f", configPath, OWNER, pid};
String[] env = {"HOME=" + torDirectory.getAbsolutePath()};
Process torProcess;
try {
torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory);
} catch (SecurityException e1) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e1.toString(), e1);
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e1.toString(), e1);
return false;
}
// Log the process's standard output until it detaches
@@ -225,6 +230,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
if (phase != null && phase.contains("PROGRESS=100")) {
LOG.info("Tor has already bootstrapped");
bootstrapped = true;
sendCrashReports();
}
}
// Register to receive network status events
@@ -355,6 +361,16 @@ class TorPlugin implements DuplexPlugin, EventHandler,
}
}
private void sendCrashReports() {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
reporter.sendCrashReports(
AndroidUtils.getCrashReportDir(appContext), SOCKS_PORT);
}
});
}
private void bind() {
ioExecutor.execute(new Runnable() {
public void run() {
@@ -420,7 +436,8 @@ class TorPlugin implements DuplexPlugin, EventHandler,
obs.startWatching();
// Use the control connection to update the Tor config
List<String> config = Arrays.asList(
"HiddenServiceDir " + serviceDirectory.getAbsolutePath(),
"HiddenServiceDir " +
serviceDirectory.getAbsolutePath(),
"HiddenServicePort 80 127.0.0.1:" + port);
controlConnection.setConf(config);
controlConnection.saveConf();
@@ -593,20 +610,24 @@ class TorPlugin implements DuplexPlugin, EventHandler,
}
}
public void streamStatus(String status, String id, String target) {}
public void streamStatus(String status, String id, String target) {
}
public void orConnStatus(String status, String orName) {
if (LOG.isLoggable(INFO)) LOG.info("OR connection " + status);
}
public void bandwidthUsed(long read, long written) {}
public void bandwidthUsed(long read, long written) {
}
public void newDescriptors(List<String> orList) {}
public void newDescriptors(List<String> orList) {
}
public void message(String severity, String msg) {
if (LOG.isLoggable(INFO)) LOG.info(severity + " " + msg);
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
bootstrapped = true;
sendCrashReports();
if (isRunning()) callback.transportEnabled();
}
}
@@ -669,7 +690,7 @@ class TorPlugin implements DuplexPlugin, EventHandler,
} else if (blocked) {
LOG.info("Disabling network, country is blocked");
enableNetwork(false);
} else if (wifiOnly & !connectedToWifi){
} else if (wifiOnly & !connectedToWifi) {
LOG.info("Disabling network due to wifi setting");
enableNetwork(false);
} else {

View File

@@ -9,6 +9,7 @@ import org.briarproject.api.event.EventBus;
import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexPluginFactory;
import org.briarproject.api.reporting.DevReporter;
import org.briarproject.api.system.Clock;
import org.briarproject.api.system.LocationUtils;
import org.briarproject.system.SystemClock;
@@ -28,14 +29,17 @@ public class TorPluginFactory implements DuplexPluginFactory {
private final Executor ioExecutor;
private final Context appContext;
private final LocationUtils locationUtils;
private final DevReporter reporter;
private final EventBus eventBus;
private final Clock clock;
public TorPluginFactory(Executor ioExecutor, Context appContext,
LocationUtils locationUtils, EventBus eventBus) {
LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus) {
this.ioExecutor = ioExecutor;
this.appContext = appContext;
this.locationUtils = locationUtils;
this.reporter = reporter;
this.eventBus = eventBus;
clock = new SystemClock();
}
@@ -68,9 +72,10 @@ public class TorPluginFactory implements DuplexPluginFactory {
// Use position-independent executable for SDK >= 16
if (Build.VERSION.SDK_INT >= 16) architecture += "-pie";
TorPlugin plugin = new TorPlugin(ioExecutor, appContext, locationUtils,
clock, callback, architecture, MAX_LATENCY, MAX_IDLE_TIME,
POLLING_INTERVAL);
TorPlugin plugin =
new TorPlugin(ioExecutor, appContext, locationUtils, reporter,
clock, callback, architecture, MAX_LATENCY,
MAX_IDLE_TIME, POLLING_INTERVAL);
eventBus.addListener(plugin);
return plugin;
}