mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Move encrypted key, account deletion into AccountManager.
This commit is contained in:
@@ -1,21 +1,140 @@
|
||||
package org.briarproject.bramble.account;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class AccountManagerImpl implements AccountManager {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AccountManagerImpl.class.getName());
|
||||
|
||||
private static final String DB_KEY_FILENAME = "db.key";
|
||||
private static final String DB_KEY_BACKUP_FILENAME = "db.key.bak";
|
||||
|
||||
private final DatabaseConfig databaseConfig;
|
||||
private final File dbKeyFile, dbKeyBackupFile;
|
||||
|
||||
@Nullable
|
||||
private volatile SecretKey databaseKey = null;
|
||||
|
||||
@Inject
|
||||
AccountManagerImpl(DatabaseConfig databaseConfig) {
|
||||
this.databaseConfig = databaseConfig;
|
||||
File keyDir = databaseConfig.getDatabaseKeyDirectory();
|
||||
dbKeyFile = new File(keyDir, DB_KEY_FILENAME);
|
||||
dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDatabaseKey() {
|
||||
return databaseConfig.getEncryptionKey() != null;
|
||||
return databaseKey != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public SecretKey getDatabaseKey() {
|
||||
return databaseKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDatabaseKey(SecretKey k) {
|
||||
databaseKey = k;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getEncryptedDatabaseKey() {
|
||||
String key = readDbKeyFromFile(dbKeyFile);
|
||||
if (key == null) {
|
||||
LOG.info("No database key in primary file");
|
||||
key = readDbKeyFromFile(dbKeyBackupFile);
|
||||
if (key == null) LOG.info("No database key in backup file");
|
||||
else LOG.warning("Found database key in backup file");
|
||||
} else {
|
||||
LOG.info("Found database key in primary file");
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String readDbKeyFromFile(File f) {
|
||||
if (!f.exists()) {
|
||||
LOG.info("Key file does not exist");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), "UTF-8"));
|
||||
String key = reader.readLine();
|
||||
reader.close();
|
||||
return key;
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean storeEncryptedDatabaseKey(String hex) {
|
||||
LOG.info("Storing database key in file");
|
||||
// Create the directory if necessary
|
||||
if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
|
||||
LOG.info("Created database key directory");
|
||||
// If only the backup file exists, rename it so we don't overwrite it
|
||||
if (dbKeyBackupFile.exists() && !dbKeyFile.exists()) {
|
||||
if (dbKeyBackupFile.renameTo(dbKeyFile))
|
||||
LOG.info("Renamed old backup");
|
||||
else LOG.warning("Failed to rename old backup");
|
||||
}
|
||||
try {
|
||||
// Write to the backup file
|
||||
writeDbKeyToFile(hex, dbKeyBackupFile);
|
||||
LOG.info("Stored database key in backup file");
|
||||
// Delete the old primary file, if it exists
|
||||
if (dbKeyFile.exists()) {
|
||||
if (dbKeyFile.delete()) LOG.info("Deleted primary file");
|
||||
else LOG.warning("Failed to delete primary file");
|
||||
}
|
||||
// The backup file becomes the new primary
|
||||
if (dbKeyBackupFile.renameTo(dbKeyFile)) {
|
||||
LOG.info("Renamed backup file to primary");
|
||||
} else {
|
||||
LOG.warning("Failed to rename backup file to primary");
|
||||
return false; // Don't overwrite our only copy
|
||||
}
|
||||
// Write a second copy to the backup file
|
||||
writeDbKeyToFile(hex, dbKeyBackupFile);
|
||||
LOG.info("Stored second copy of database key in backup file");
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(key.getBytes("UTF-8"));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
@@ -48,7 +49,8 @@ interface Database<T> {
|
||||
* @throws DataTooOldException if the data uses an older schema than the
|
||||
* current code and cannot be migrated
|
||||
*/
|
||||
boolean open(@Nullable MigrationListener listener) throws DbException;
|
||||
boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Prevents new transactions from starting, waits for all current
|
||||
@@ -641,7 +643,7 @@ interface Database<T> {
|
||||
* Marks the given transport keys as usable for outgoing streams.
|
||||
*/
|
||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Updates the transmission count and expiry time of the given message
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.ContactExistsException;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
@@ -103,9 +104,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(@Nullable MigrationListener listener)
|
||||
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
boolean reopened = db.open(listener);
|
||||
boolean reopened = db.open(key, listener);
|
||||
shutdown.addShutdownHook(() -> {
|
||||
try {
|
||||
close();
|
||||
|
||||
@@ -32,6 +32,9 @@ class H2Database extends JdbcDatabase {
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Nullable
|
||||
private volatile SecretKey key = null;
|
||||
|
||||
@Inject
|
||||
H2Database(DatabaseConfig config, Clock clock) {
|
||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
@@ -44,11 +47,12 @@ class H2Database extends JdbcDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(@Nullable MigrationListener listener)
|
||||
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
this.key = key;
|
||||
boolean reopen = config.databaseExists();
|
||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
super.open("org.h2.Driver", reopen, listener);
|
||||
super.open("org.h2.Driver", reopen, key, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
@@ -63,7 +67,7 @@ class H2Database extends JdbcDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFreeSpace() throws DbException {
|
||||
public long getFreeSpace() {
|
||||
File dir = config.getDatabaseDirectory();
|
||||
long maxSize = config.getMaxSize();
|
||||
long free = dir.getFreeSpace();
|
||||
@@ -88,7 +92,7 @@ class H2Database extends JdbcDatabase {
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws SQLException {
|
||||
SecretKey key = config.getEncryptionKey();
|
||||
SecretKey key = this.key;
|
||||
if (key == null) throw new IllegalStateException();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("user", "user");
|
||||
|
||||
@@ -33,6 +33,9 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Nullable
|
||||
private volatile SecretKey key = null;
|
||||
|
||||
@Inject
|
||||
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
|
||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||
@@ -46,10 +49,12 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(@Nullable MigrationListener listener) throws DbException {
|
||||
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
this.key = key;
|
||||
boolean reopen = config.databaseExists();
|
||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, listener);
|
||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
@@ -93,7 +98,7 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws SQLException {
|
||||
SecretKey key = config.getEncryptionKey();
|
||||
SecretKey key = this.key;
|
||||
if (key == null) throw new IllegalStateException();
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
||||
|
||||
@@ -328,7 +328,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
protected void open(String driverClass, boolean reopen,
|
||||
protected void open(String driverClass, boolean reopen, SecretKey key,
|
||||
@Nullable MigrationListener listener) throws DbException {
|
||||
// Load the JDBC driver
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.briarproject.bramble.lifecycle;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
@@ -55,6 +57,7 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
private final List<Service> services;
|
||||
private final List<Client> clients;
|
||||
private final List<ExecutorService> executors;
|
||||
private final AccountManager accountManager;
|
||||
private final IdentityManager identityManager;
|
||||
private final Semaphore startStopSemaphore = new Semaphore(1);
|
||||
private final CountDownLatch dbLatch = new CountDownLatch(1);
|
||||
@@ -65,9 +68,10 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
|
||||
@Inject
|
||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
|
||||
IdentityManager identityManager) {
|
||||
AccountManager accountManager, IdentityManager identityManager) {
|
||||
this.db = db;
|
||||
this.eventBus = eventBus;
|
||||
this.accountManager = accountManager;
|
||||
this.identityManager = identityManager;
|
||||
services = new CopyOnWriteArrayList<>();
|
||||
clients = new CopyOnWriteArrayList<>();
|
||||
@@ -104,7 +108,9 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
LOG.info("Starting services");
|
||||
long start = now();
|
||||
|
||||
boolean reopened = db.open(this);
|
||||
SecretKey key = accountManager.getDatabaseKey();
|
||||
if (key == null) throw new IllegalStateException();
|
||||
boolean reopened = db.open(key, this);
|
||||
if (reopened) logDuration(LOG, "Reopening database", start);
|
||||
else logDuration(LOG, "Creating database", start);
|
||||
identityManager.storeLocalAuthor();
|
||||
|
||||
Reference in New Issue
Block a user