Encrypt logs before handing them to crash report process

This commit is contained in:
Torsten Grote
2021-02-04 10:08:59 -03:00
parent 700ea2b387
commit 21112ce092
23 changed files with 459 additions and 54 deletions

View File

@@ -31,6 +31,7 @@ public class AndroidUtils {
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
private static final String STORED_REPORTS = "dev-reports";
private static final String STORED_LOGCAT = "dev-logcat";
public static Collection<String> getSupportedArchitectures() {
List<String> abis = new ArrayList<>();
@@ -107,6 +108,10 @@ public class AndroidUtils {
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
}
public static File getLogcatFile(Context ctx) {
return new File(ctx.getFilesDir(), STORED_LOGCAT);
}
/**
* Returns an array of supported content types for image attachments.
* GIFs can't be compressed on API < 24 so they're not supported.

View File

@@ -13,4 +13,6 @@ public interface DevConfig {
String getDevOnionAddress();
File getReportDir();
File getLogcatFile();
}

View File

@@ -136,6 +136,7 @@ dependencies {
testImplementation "org.jmock:jmock:$jmockVersion"
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
testAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
androidTestImplementation 'androidx.test.ext:junit:1.1.2'

View File

@@ -34,6 +34,7 @@ import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.attachment.AttachmentModule;
import org.briarproject.briar.android.attachment.media.MediaModule;
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.view.EmojiTextInputView;
import org.briarproject.briar.api.android.AndroidNotificationManager;
@@ -179,6 +180,10 @@ public interface AndroidComponent
AndroidWakeLockManager wakeLockManager();
CachingLogHandler logHandler();
Thread.UncaughtExceptionHandler exceptionHandler();
void inject(SignInReminderReceiver briarService);
void inject(BriarService briarService);

View File

@@ -33,12 +33,13 @@ import org.briarproject.briar.android.account.SetupModule;
import org.briarproject.briar.android.contact.ContactListModule;
import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
import org.briarproject.briar.android.logging.LoggingModule;
import org.briarproject.briar.android.login.LoginModule;
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule;
import org.briarproject.briar.android.settings.SettingsModule;
import org.briarproject.briar.android.privategroup.list.GroupListModule;
import org.briarproject.briar.android.reporting.DevReportModule;
import org.briarproject.briar.android.settings.SettingsModule;
import org.briarproject.briar.android.sharing.SharingModule;
import org.briarproject.briar.android.test.TestAvatarCreatorImpl;
import org.briarproject.briar.android.viewmodel.ViewModelModule;
@@ -74,6 +75,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
SetupModule.class,
DozeHelperModule.class,
ContactExchangeModule.class,
LoggingModule.class,
LoginModule.class,
NavDrawerModule.class,
ViewModelModule.class,
@@ -192,6 +194,11 @@ public class AppModule {
public File getReportDir() {
return AndroidUtils.getReportDir(app.getApplicationContext());
}
@Override
public File getLogcatFile() {
return AndroidUtils.getLogcatFile(app.getApplicationContext());
}
};
return devConfig;
}

View File

@@ -6,9 +6,6 @@ import android.content.SharedPreferences;
import org.briarproject.bramble.BrambleApplication;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import java.util.Collection;
import java.util.logging.LogRecord;
/**
* This exists so that the Application object will not necessarily be cast
* directly to the Briar application object.
@@ -17,8 +14,6 @@ public interface BriarApplication extends BrambleApplication {
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
Collection<LogRecord> getRecentLogRecords();
AndroidComponent getApplicationComponent();
SharedPreferences getDefaultSharedPreferences();

View File

@@ -20,12 +20,10 @@ import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.R;
import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.reporting.BriarExceptionHandler;
import org.briarproject.briar.android.util.UiUtils;
import java.util.Collection;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import androidx.annotation.NonNull;
@@ -42,16 +40,11 @@ public class BriarApplicationImpl extends Application
private static final Logger LOG =
getLogger(BriarApplicationImpl.class.getName());
private final CachingLogHandler logHandler = new CachingLogHandler();
private final BriarExceptionHandler exceptionHandler =
new BriarExceptionHandler(this);
private AndroidComponent applicationComponent;
private volatile SharedPreferences prefs;
@Override
protected void attachBaseContext(Context base) {
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
if (prefs == null)
prefs = PreferenceManager.getDefaultSharedPreferences(base);
// Loading the language needs to be done here.
@@ -67,6 +60,11 @@ public class BriarApplicationImpl extends Application
if (IS_DEBUG_BUILD) enableStrictMode();
applicationComponent = createApplicationComponent();
UncaughtExceptionHandler exceptionHandler =
applicationComponent.exceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
Logger rootLogger = getLogger("");
Handler[] handlers = rootLogger.getHandlers();
// Disable the Android logger for release builds
@@ -78,12 +76,12 @@ public class BriarApplicationImpl extends Application
// Restore the default handlers after the level raising handler
for (Handler handler : handlers) rootLogger.addHandler(handler);
}
CachingLogHandler logHandler = applicationComponent.logHandler();
rootLogger.addHandler(logHandler);
rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO);
LOG.info("Created");
applicationComponent = createApplicationComponent();
EmojiManager.install(new GoogleEmojiProvider());
}
@@ -136,11 +134,6 @@ public class BriarApplicationImpl extends Application
return applicationComponent;
}
@Override
public Collection<LogRecord> getRecentLogRecords() {
return logHandler.getRecentLogRecords();
}
@Override
public AndroidComponent getApplicationComponent() {
return applicationComponent;

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.TimeZone;
import java.util.logging.Formatter;
@@ -17,6 +18,16 @@ import static java.util.Locale.US;
@NotNullByDefault
public class BriefLogFormatter extends Formatter {
public static String formatLog(Formatter formatter,
Collection<LogRecord> logRecords) {
StringBuilder sb = new StringBuilder();
for (LogRecord record : logRecords) {
String formatted = formatter.format(record);
sb.append(formatted).append('\n');
}
return sb.toString();
}
private final Object lock = new Object();
private final DateFormat dateFormat; // Locking: lock
private final Date date; // Locking: lock

View File

@@ -21,6 +21,10 @@ public class CachingLogHandler extends Handler {
// Locking: lock
private final Queue<LogRecord> recent = new LinkedList<>();
// package-private constructor
CachingLogHandler() {
}
@Override
public void publish(LogRecord record) {
synchronized (lock) {

View File

@@ -0,0 +1,16 @@
package org.briarproject.briar.android.logging;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.AndroidUtils;
import androidx.annotation.Nullable;
@NotNullByDefault
public interface LogDecrypter {
/**
* Returns decrypted log records from {@link AndroidUtils#getLogcatFile}
* or null if there was an error reading the logs.
*/
@Nullable
String decryptLogs(@Nullable byte[] logKey);
}

View File

@@ -0,0 +1,61 @@
package org.briarproject.briar.android.logging;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import java.util.logging.Logger;
import javax.inject.Inject;
import androidx.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class LogDecrypterImpl implements LogDecrypter {
private static final Logger LOG =
getLogger(LogDecrypterImpl.class.getName());
private final DevConfig devConfig;
private final StreamReaderFactory streamReaderFactory;
@Inject
LogDecrypterImpl(DevConfig devConfig,
StreamReaderFactory streamReaderFactory) {
this.devConfig = devConfig;
this.streamReaderFactory = streamReaderFactory;
}
@Nullable
@Override
public String decryptLogs(@Nullable byte[] logKey) {
if (logKey == null) return null;
SecretKey key = new SecretKey(logKey);
File logFile = devConfig.getLogcatFile();
try (InputStream in = new FileInputStream(logFile)) {
InputStream reader =
streamReaderFactory.createLogStreamReader(in, key);
Scanner s = new Scanner(reader);
StringBuilder sb = new StringBuilder();
while (s.hasNextLine()) sb.append(s.nextLine()).append("\n");
s.close();
return sb.toString();
} catch (IOException e) {
logException(LOG, WARNING, e);
return null;
} finally {
//noinspection ResultOfMethodCallIgnored
logFile.delete();
}
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.briar.android.logging;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.AndroidUtils;
import androidx.annotation.Nullable;
@NotNullByDefault
public interface LogEncrypter {
/**
* Writes encrypted log records to {@link AndroidUtils#getLogcatFile}
* and returns the encryption key if everything went fine.
*/
@Nullable
byte[] encryptLogs();
}

View File

@@ -0,0 +1,77 @@
package org.briarproject.briar.android.logging;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.inject.Inject;
import androidx.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class LogEncrypterImpl implements LogEncrypter {
private static final Logger LOG =
getLogger(LogEncrypterImpl.class.getName());
private final DevConfig devConfig;
private final CachingLogHandler logHandler;
private final CryptoComponent crypto;
private final StreamWriterFactory streamWriterFactory;
@Inject
LogEncrypterImpl(DevConfig devConfig,
CachingLogHandler logHandler,
CryptoComponent crypto,
StreamWriterFactory streamWriterFactory) {
this.devConfig = devConfig;
this.logHandler = logHandler;
this.crypto = crypto;
this.streamWriterFactory = streamWriterFactory;
}
@Nullable
@Override
public byte[] encryptLogs() {
SecretKey logKey = crypto.generateSecretKey();
File logFile = devConfig.getLogcatFile();
try (OutputStream out = new FileOutputStream(logFile)) {
StreamWriter streamWriter =
streamWriterFactory.createLogStreamWriter(out, logKey);
Writer writer =
new OutputStreamWriter(streamWriter.getOutputStream());
writeLogString(writer);
writer.close();
return logKey.getBytes();
} catch (IOException e) {
logException(LOG, WARNING, e);
return null;
}
}
private void writeLogString(Writer writer) throws IOException {
Formatter formatter = new BriefLogFormatter();
for (LogRecord record : logHandler.getRecentLogRecords()) {
String formatted = formatter.format(record);
writer.append(formatted).append('\n');
}
}
}

View File

@@ -0,0 +1,29 @@
package org.briarproject.briar.android.logging;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class LoggingModule {
@Provides
@Singleton
CachingLogHandler provideCachingLogHandler() {
return new CachingLogHandler();
}
@Provides
@Singleton
LogEncrypter provideLogEncrypter(LogEncrypterImpl logEncrypter) {
return logEncrypter;
}
@Provides
@Singleton
LogDecrypter provideLogDecrypter(LogDecrypterImpl logDecrypter) {
return logDecrypter;
}
}

View File

@@ -1,29 +1,40 @@
package org.briarproject.briar.android.reporting;
import android.content.Context;
import android.app.Application;
import android.os.Process;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.logging.LogEncrypter;
import java.lang.Thread.UncaughtExceptionHandler;
import javax.inject.Inject;
import static org.briarproject.briar.android.util.UiUtils.startDevReportActivity;
@NotNullByDefault
public class BriarExceptionHandler implements UncaughtExceptionHandler {
class BriarExceptionHandler implements UncaughtExceptionHandler {
private final Context ctx;
private final Application app;
private final LogEncrypter logEncrypter;
private final long appStartTime;
public BriarExceptionHandler(Context ctx) {
this.ctx = ctx;
this.appStartTime = System.currentTimeMillis();
@Inject
BriarExceptionHandler(Application app, LogEncrypter logEncrypter) {
this.app = app;
this.logEncrypter = logEncrypter;
appStartTime = System.currentTimeMillis();
}
@Override
public void uncaughtException(Thread t, Throwable e) {
// encrypt logs to disk before handing over to new process
// the intent has limited space, so we can't reliably store them there.
byte[] logKey = logEncrypter.encryptLogs();
// activity runs in its own process, so we can kill the old one
startDevReportActivity(ctx, CrashReportActivity.class, e, appStartTime);
startDevReportActivity(app.getApplicationContext(),
CrashReportActivity.class, e, appStartTime, logKey);
Process.killProcess(Process.myPid());
System.exit(10);
}

View File

@@ -25,8 +25,6 @@ import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.BuildConfig;
import org.briarproject.briar.R;
import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.logging.BriefLogFormatter;
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
@@ -41,8 +39,6 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import javax.annotation.concurrent.Immutable;
@@ -74,8 +70,8 @@ class BriarReportCollector {
this.ctx = ctx;
}
public ReportData collectReportData(@Nullable Throwable t,
long appStartTime) {
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
String logs) {
ReportData reportData = new ReportData()
.add(getBasicInfo(t))
.add(getDeviceInfo());
@@ -86,7 +82,7 @@ class BriarReportCollector {
.add(getStorage())
.add(getConnectivity())
.add(getBuildConfig())
.add(getLogcat())
.add(getLogcat(logs))
.add(getDeviceFeatures());
}
@@ -313,15 +309,8 @@ class BriarReportCollector {
buildConfig);
}
private ReportItem getLogcat() {
BriarApplication app = (BriarApplication) ctx.getApplicationContext();
StringBuilder sb = new StringBuilder();
Formatter formatter = new BriefLogFormatter();
for (LogRecord record : app.getRecentLogRecords()) {
sb.append(formatter.format(record)).append('\n');
}
return new ReportItem("Logcat", R.string.dev_report_logcat,
sb.toString());
private ReportItem getLogcat(String logs) {
return new ReportItem("Logcat", R.string.dev_report_logcat, logs);
}
private ReportItem getDeviceFeatures() {

View File

@@ -35,6 +35,7 @@ public class CrashReportActivity extends BaseActivity
public static final String EXTRA_THROWABLE = "throwable";
public static final String EXTRA_APP_START_TIME = "appStartTime";
public static final String EXTRA_APP_LOGCAT = "logcat";
@Inject
ViewModelProvider.Factory viewModelFactory;
@@ -56,7 +57,8 @@ public class CrashReportActivity extends BaseActivity
Intent intent = getIntent();
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
viewModel.init(t, appStartTime);
byte[] logKey = intent.getByteArrayExtra(EXTRA_APP_LOGCAT);
viewModel.init(t, appStartTime, logKey);
viewModel.getShowReport().observeEvent(this, show -> {
if (show) displayFragment(true);
});

View File

@@ -15,4 +15,8 @@ public abstract class DevReportModule {
@ViewModelKey(ReportViewModel.class)
abstract ViewModel bindReportViewModel(ReportViewModel reportViewModel);
@Binds
abstract Thread.UncaughtExceptionHandler bindUncaughtExceptionHandler(
BriarExceptionHandler handler);
}

View File

@@ -11,6 +11,9 @@ import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.logging.BriefLogFormatter;
import org.briarproject.briar.android.logging.CachingLogHandler;
import org.briarproject.briar.android.logging.LogDecrypter;
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
@@ -19,6 +22,7 @@ import org.json.JSONException;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.UUID;
import java.util.logging.Formatter;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -36,13 +40,16 @@ import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
@NotNullByDefault
public class ReportViewModel extends AndroidViewModel {
class ReportViewModel extends AndroidViewModel {
private static final Logger LOG =
getLogger(ReportViewModel.class.getName());
private final CachingLogHandler logHandler;
private final LogDecrypter logDecrypter;
private final BriarReportCollector collector;
private final DevReporter reporter;
private final PluginManager pluginManager;
@@ -58,18 +65,39 @@ public class ReportViewModel extends AndroidViewModel {
private boolean isFeedback;
@Inject
public ReportViewModel(@NonNull Application application,
DevReporter reporter, PluginManager pluginManager) {
ReportViewModel(@NonNull Application application,
CachingLogHandler logHandler,
LogDecrypter logDecrypter,
DevReporter reporter,
PluginManager pluginManager) {
super(application);
this.collector = new BriarReportCollector(application);
collector = new BriarReportCollector(application);
this.logHandler = logHandler;
this.logDecrypter = logDecrypter;
this.reporter = reporter;
this.pluginManager = pluginManager;
}
void init(@Nullable Throwable t, long appStartTime) {
void init(@Nullable Throwable t, long appStartTime,
@Nullable byte[] logKey) {
isFeedback = t == null;
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
ReportData data = collector.collectReportData(t, appStartTime);
String decryptedLogs;
if (isFeedback) {
Formatter formatter = new BriefLogFormatter();
decryptedLogs =
formatLog(formatter, logHandler.getRecentLogRecords());
} else {
decryptedLogs = logDecrypter.decryptLogs(logKey);
if (decryptedLogs == null) {
// error decrypting logs, get logs from this process
Formatter formatter = new BriefLogFormatter();
decryptedLogs = formatLog(formatter,
logHandler.getRecentLogRecords());
}
}
ReportData data =
collector.collectReportData(t, appStartTime, decryptedLogs);
reportData.postValue(data);
}).start();
}
@@ -110,8 +138,8 @@ public class ReportViewModel extends AndroidViewModel {
}
/**
* The content of the report
* that will be loaded after {@link #init(Throwable, long)} was called.
* The content of the report that will be loaded after
* {@link #init(Throwable, long, byte[])} was called.
*/
LiveData<ReportData> getReportData() {
return reportData;

View File

@@ -97,6 +97,7 @@ import static java.util.concurrent.TimeUnit.DAYS;
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT;
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
@@ -357,16 +358,17 @@ public class UiUtils {
}
public static void triggerFeedback(Context ctx) {
startDevReportActivity(ctx, FeedbackActivity.class, null, null);
startDevReportActivity(ctx, FeedbackActivity.class, null, null, null);
}
public static void startDevReportActivity(Context ctx,
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
@Nullable Long appStartTime) {
@Nullable Long appStartTime, @Nullable byte[] logKey) {
final Intent dialogIntent = new Intent(ctx, activity);
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
dialogIntent.putExtra(EXTRA_THROWABLE, t);
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
dialogIntent.putExtra(EXTRA_APP_LOGCAT, logKey);
ctx.startActivity(dialogIntent);
}

View File

@@ -0,0 +1,66 @@
package org.briarproject.briar.android.logging;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
import static org.junit.Assert.assertEquals;
public class LogEncryptionDecryptionTest extends BrambleMockTestCase {
@ClassRule
public static TemporaryFolder folder = new TemporaryFolder();
private final SecureRandom random;
private final CachingLogHandler cachingLogHandler;
private final LogEncrypter logEncrypter;
private final LogDecrypter logDecrypter;
private final BriefLogFormatter logFormatter = new BriefLogFormatter();
public LogEncryptionDecryptionTest() throws IOException {
LoggingComponent loggingComponent = DaggerLoggingComponent.builder()
.loggingTestModule(new LoggingTestModule(folder.newFile()))
.build();
random = loggingComponent.random();
logEncrypter = loggingComponent.logEncrypter();
logDecrypter = loggingComponent.logDecrypter();
cachingLogHandler = loggingComponent.cachingLogHandler();
}
@Test
public void testEncryptedMatchesDecrypted() {
ArrayList<LogRecord> logRecords =
new ArrayList<>(random.nextInt(99) + 1);
for (int i = 0; i < logRecords.size(); i++) {
LogRecord logRecord = getRandomLogRecord();
cachingLogHandler.publish(logRecord);
logRecords.add(logRecord);
}
byte[] logKey = logEncrypter.encryptLogs();
assertEquals(formatLog(logFormatter, logRecords),
logDecrypter.decryptLogs(logKey));
}
private LogRecord getRandomLogRecord() {
Level[] levels = {SEVERE, WARNING, INFO, FINE};
Level level = levels[random.nextInt(levels.length)];
LogRecord logRecord =
new LogRecord(level, getRandomString(random.nextInt(128) + 1));
logRecord.setLoggerName(getRandomString(random.nextInt(23) + 1));
return logRecord;
}
}

View File

@@ -0,0 +1,29 @@
package org.briarproject.briar.android.logging;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.test.TestSecureRandomModule;
import java.security.SecureRandom;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
BrambleCoreModule.class,
TestSecureRandomModule.class,
LoggingModule.class,
LoggingTestModule.class,
})
public interface LoggingComponent {
SecureRandom random();
CachingLogHandler cachingLogHandler();
LogEncrypter logEncrypter();
LogDecrypter logDecrypter();
}

View File

@@ -0,0 +1,52 @@
package org.briarproject.briar.android.logging;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.reporting.DevConfig;
import java.io.File;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
class LoggingTestModule {
private final File logFile;
LoggingTestModule(File logFile) {
this.logFile = logFile;
}
@Provides
@Singleton
DevConfig provideDevConfig() {
@NotNullByDefault
DevConfig devConfig = new DevConfig() {
@Override
public PublicKey getDevPublicKey() {
throw new UnsupportedOperationException();
}
@Override
public String getDevOnionAddress() {
throw new UnsupportedOperationException();
}
@Override
public File getReportDir() {
throw new UnsupportedOperationException();
}
@Override
public File getLogcatFile() {
return logFile;
}
};
return devConfig;
}
}