mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 20:29:52 +01:00
WIP: Add SQLite DB backend using sqlite-jdbc-crypt.
This commit is contained in:
@@ -39,7 +39,8 @@ class H2Database extends JdbcDatabase {
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INT NOT NULL AUTO_INCREMENT PRIMARY KEY";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||
|
||||
@@ -38,8 +38,8 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
private static final String HASH_TYPE = "BINARY(32)";
|
||||
private static final String SECRET_TYPE = "BINARY(32)";
|
||||
private static final String BINARY_TYPE = "BINARY";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||
private static final String COUNTER_TYPE = "INTEGER NOT NULL"
|
||||
+ " PRIMARY KEY GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||
|
||||
@@ -143,8 +143,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " handshakePublicKey _BINARY," // Null if key is unknown
|
||||
+ " localAuthorId _HASH NOT NULL,"
|
||||
+ " verified BOOLEAN NOT NULL,"
|
||||
// FIXME: SQLite interprets default '00' as string
|
||||
+ " syncVersions _BINARY DEFAULT '00' NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId),"
|
||||
+ " FOREIGN KEY (localAuthorId)"
|
||||
+ " REFERENCES localAuthors (authorId)"
|
||||
+ " ON DELETE CASCADE)";
|
||||
@@ -295,11 +295,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " active BOOLEAN NOT NULL,"
|
||||
+ " rootKey _SECRET," // Null for rotation keys
|
||||
+ " alice BOOLEAN," // Null for rotation keys
|
||||
+ " PRIMARY KEY (transportId, keySetId),"
|
||||
// FIXME: Primary key has changed, migration needed
|
||||
+ " FOREIGN KEY (transportId)"
|
||||
+ " REFERENCES transports (transportId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
+ " UNIQUE (keySetId),"
|
||||
// FIXME: Unique constraint removed, migration needed
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
+ " REFERENCES contacts (contactId)"
|
||||
+ " ON DELETE CASCADE,"
|
||||
@@ -358,6 +358,11 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
"CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
|
||||
+ " ON messages (cleanupDeadline)";
|
||||
|
||||
// FIXME: Migration needs to add new index
|
||||
private static final String INDEX_OUTGOING_KEYS_BY_TRANSPORT_ID_KEYSET_ID =
|
||||
"CREATE INDEX IF NOT EXISTS outgoingKeysByTransportIdKeysetId"
|
||||
+ " ON outgoingKeys (transportId, keySetId)";
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(JdbcDatabase.class.getName());
|
||||
|
||||
@@ -564,6 +569,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
|
||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TX_COUNT_TIMESTAMP);
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_CLEANUP_DEADLINE);
|
||||
s.executeUpdate(INDEX_OUTGOING_KEYS_BY_TRANSPORT_ID_KEYSET_ID);
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
@@ -2597,6 +2603,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PublicKey publicKey = new AgreementPublicKey(rs.getBytes(1));
|
||||
String alias = rs.getString(2);
|
||||
long timestamp = rs.getLong(3);
|
||||
if (rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
return new PendingContact(p, publicKey, alias, timestamp);
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.db.DbClosedException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.sqlite.mc.SQLiteMCSqlCipherConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.IoUtils.isNonEmptyDirectory;
|
||||
|
||||
/**
|
||||
* Contains all the SQLite-specific code for the database.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
class SqliteDatabase extends JdbcDatabase {
|
||||
|
||||
private static final Logger LOG = getLogger(SqliteDatabase.class.getName());
|
||||
|
||||
private static final String HASH_TYPE = "BLOB";
|
||||
private static final String SECRET_TYPE = "BLOB";
|
||||
private static final String BINARY_TYPE = "BLOB";
|
||||
private static final String COUNTER_TYPE =
|
||||
"INTEGER PRIMARY KEY AUTOINCREMENT";
|
||||
private static final String STRING_TYPE = "VARCHAR";
|
||||
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||
|
||||
private final DatabaseConfig config;
|
||||
private final String url;
|
||||
|
||||
@Nullable
|
||||
private volatile Properties properties = null;
|
||||
|
||||
@Inject
|
||||
SqliteDatabase(DatabaseConfig config, MessageFactory messageFactory,
|
||||
Clock clock) {
|
||||
super(dbTypes, messageFactory, clock);
|
||||
this.config = config;
|
||||
File dir = config.getDatabaseDirectory();
|
||||
String path = new File(dir, "db").getAbsolutePath();
|
||||
url = "jdbc:sqlite:" + path + "?cipher=sqlcipher";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||
throws DbException {
|
||||
properties = SQLiteMCSqlCipherConfig.getDefault()
|
||||
.withHexKey(key.getBytes())
|
||||
.build()
|
||||
.toProperties();
|
||||
File dir = config.getDatabaseDirectory();
|
||||
boolean reopen = isNonEmptyDirectory(dir);
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Reopening DB: " + reopen);
|
||||
if (!reopen && dir.mkdirs()) LOG.info("Created database directory");
|
||||
super.open("org.sqlite.JDBC", reopen, key, listener);
|
||||
return reopen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws DbException {
|
||||
Connection c = null;
|
||||
try {
|
||||
c = createConnection();
|
||||
setDirty(c, false);
|
||||
c.close();
|
||||
closeAllConnections();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(c, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection createConnection() throws DbException, SQLException {
|
||||
Properties properties = this.properties;
|
||||
if (properties == null) throw new DbClosedException();
|
||||
Connection c = DriverManager.getConnection(url, properties);
|
||||
Statement s = null;
|
||||
try {
|
||||
s = c.createStatement();
|
||||
s.execute("PRAGMA foreign_keys = ON");
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
tryToClose(c, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compactAndClose() throws DbException {
|
||||
close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class H2SqliteDatabasePerformanceComparisonTest
|
||||
extends DatabasePerformanceComparisonTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
assumeTrue(isOptionalTestEnabled(
|
||||
H2SqliteDatabasePerformanceComparisonTest.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
Database<Connection> createDatabase(boolean conditionA,
|
||||
DatabaseConfig databaseConfig, MessageFactory messageFactory,
|
||||
Clock clock) {
|
||||
if (conditionA) {
|
||||
return new H2Database(databaseConfig, messageFactory, clock);
|
||||
} else {
|
||||
return new SqliteDatabase(databaseConfig, messageFactory, clock);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.junit.Before;
|
||||
|
||||
import static org.briarproject.bramble.test.TestUtils.isCryptoStrengthUnlimited;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class SqliteDatabaseTest extends JdbcDatabaseTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
assumeTrue(isCryptoStrengthUnlimited());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JdbcDatabase createDatabase(DatabaseConfig config,
|
||||
MessageFactory messageFactory, Clock clock) {
|
||||
return new SqliteDatabase(config, messageFactory, clock);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user