Encrypt logs on disk, store encryption key in DB.

This commit is contained in:
akwizgran
2018-10-23 14:18:31 +01:00
parent 61407c3e06
commit d9b4c013bb
12 changed files with 190 additions and 29 deletions

View File

@@ -1,9 +1,12 @@
package org.briarproject.briar.logging;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.briar.api.logging.PersistentLogManager;
import java.util.logging.Formatter;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@@ -16,8 +19,11 @@ public class LoggingModule {
}
@Provides
@Singleton
PersistentLogManager providePersistentLogManager(
PersistentLogManagerImpl logManager) {
return logManager;
LifecycleManager lifecycleManager,
PersistentLogManagerImpl persistentLogManager) {
lifecycleManager.registerOpenDatabaseHook(persistentLogManager);
return persistentLogManager;
}
}

View File

@@ -1,14 +1,26 @@
package org.briarproject.briar.logging;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.briar.api.logging.PersistentLogManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
@@ -21,16 +33,20 @@ import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@ThreadSafe
@NotNullByDefault
class PersistentLogManagerImpl implements PersistentLogManager {
class PersistentLogManagerImpl implements PersistentLogManager,
OpenDatabaseHook {
private static final Logger LOG =
Logger.getLogger(PersistentLogManagerImpl.class.getName());
@@ -42,16 +58,47 @@ class PersistentLogManagerImpl implements PersistentLogManager {
private final ScheduledExecutorService scheduler;
private final Executor ioExecutor;
private final ShutdownManager shutdownManager;
private final DatabaseComponent db;
private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory;
private final Formatter formatter;
private final SecretKey logKey;
@Nullable
private volatile SecretKey oldLogKey = null;
@Inject
PersistentLogManagerImpl(@Scheduler ScheduledExecutorService scheduler,
@IoExecutor Executor ioExecutor, ShutdownManager shutdownManager,
Formatter formatter) {
PersistentLogManagerImpl(
@Scheduler ScheduledExecutorService scheduler,
@IoExecutor Executor ioExecutor,
ShutdownManager shutdownManager,
DatabaseComponent db,
StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory,
Formatter formatter,
CryptoComponent crypto) {
this.scheduler = scheduler;
this.ioExecutor = ioExecutor;
this.shutdownManager = shutdownManager;
this.db = db;
this.streamReaderFactory = streamReaderFactory;
this.streamWriterFactory = streamWriterFactory;
this.formatter = formatter;
logKey = crypto.generateSecretKey();
}
@Override
public void onDatabaseOpened(Transaction txn) throws DbException {
Settings s = db.getSettings(txn, LOG_SETTINGS_NAMESPACE);
// Load the old log key, if any
byte[] oldKeyBytes = s.getBytes(LOG_KEY_KEY);
if (oldKeyBytes != null && oldKeyBytes.length == SecretKey.LENGTH) {
LOG.info("Loaded old log key");
oldLogKey = new SecretKey(oldKeyBytes);
}
// Store the current log key
s.putBytes(LOG_KEY_KEY, logKey.getBytes());
db.mergeSettings(txn, s, LOG_SETTINGS_NAMESPACE);
}
@Override
@@ -64,11 +111,24 @@ class PersistentLogManagerImpl implements PersistentLogManager {
LOG.warning("Failed to rename log file");
try {
OutputStream out = new FileOutputStream(logFile);
StreamHandler handler = new StreamHandler(out, formatter);
StreamWriter writer =
streamWriterFactory.createLogStreamWriter(out, logKey);
StreamHandler handler =
new StreamHandler(writer.getOutputStream(), formatter);
// Flush the log periodically in case we're killed without getting
// the chance to run shutdown hooks
scheduler.scheduleWithFixedDelay(() ->
ioExecutor.execute(handler::flush),
FLUSH_INTERVAL_MS, FLUSH_INTERVAL_MS, MILLISECONDS);
shutdownManager.addShutdownHook(handler::flush);
// Flush the log and terminate the stream at shutdown
shutdownManager.addShutdownHook(() -> {
handler.flush();
try {
writer.sendEndOfStream();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
});
return handler;
} catch (SecurityException e) {
throw new IOException(e);
@@ -77,14 +137,23 @@ class PersistentLogManagerImpl implements PersistentLogManager {
@Override
public Collection<String> getPersistedLog(File dir) throws IOException {
SecretKey oldLogKey = this.oldLogKey;
if (oldLogKey == null) {
LOG.info("Old log key has not been loaded");
return emptyList();
}
File oldLogFile = new File(dir, OLD_LOG_FILE);
if (oldLogFile.exists()) {
LOG.info("Reading old log file");
List<String> lines = new ArrayList<>();
Scanner s = new Scanner(oldLogFile);
while (s.hasNextLine()) lines.add(s.nextLine());
s.close();
return lines;
try (InputStream in = new FileInputStream(oldLogFile)) {
InputStream reader = streamReaderFactory
.createLogStreamReader(in, oldLogKey);
Scanner s = new Scanner(reader);
while (s.hasNextLine()) lines.add(s.nextLine());
s.close();
return lines;
}
} else {
LOG.info("Old log file does not exist");
return emptyList();