WIP: Add SQLite DB backend using sqlite-jdbc-crypt.

This commit is contained in:
akwizgran
2023-09-01 10:58:05 +01:00
parent 87ef5e58ee
commit 6734284585
10 changed files with 373 additions and 178 deletions

View File

@@ -104,3 +104,12 @@ mailbox integration test:
script:
- (cd briar-mailbox; git fetch; git reset --hard origin/main)
- MAILBOX_INTEGRATION_TESTS=true ./gradlew --info mailbox-integration-tests:test
db_performance_comparison_test:
extends: .base-test
stage: optional_tests
script:
- OPTIONAL_TESTS=org.briarproject.bramble.db.H2SqliteDatabasePerformanceComparisonTest ./gradlew --info -Djava.security.egd=file:/dev/urandom :bramble-core:test --tests H2SqliteDatabasePerformanceComparisonTest
rules:
- when: manual

View File

@@ -16,6 +16,7 @@ dependencies {
implementation "org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version"
//noinspection GradleDependency
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
implementation 'io.github.willena:sqlite-jdbc:3.41.2.1'
implementation 'org.bitlet:weupnp:0.1.4'
implementation 'net.i2p.crypto:eddsa:0.2.0'
implementation 'org.whispersystems:curve25519-java:0.5.0'

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -24,6 +24,7 @@ dependencyVerification {
'com.squareup.okio:okio-jvm:3.0.0:okio-jvm-3.0.0.jar:be64a0cc1f28ea9cd5c970dd7e7557af72c808d738c495b397bf897c9921e907',
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
'io.github.willena:sqlite-jdbc:3.41.2.1:sqlite-jdbc-3.41.2.1.jar:fb60e7137c1791db89240701338d31ca42a0bec5508c1aab1c1131cf885f2309',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',