Merge branch 'soru/update-database' into 'master'

switch to moor_ffi w/ sqlcipher

See merge request ChristianPauly/fluffychat-flutter!104
This commit is contained in:
Christian Pauly 2020-07-20 15:33:52 +00:00
commit 9ae4597ab1
10 changed files with 239 additions and 70 deletions

View File

@ -1,7 +1,11 @@
# Version 0.16.0 - 2020-07-?? # Version 0.16.0 - 2020-07-??
### Features ### Features
- Implement web notifications - Implement web notifications
### Changes
- Various performance improvements
- Added languages: Galician, Croatian, Japanese, Russian - Added languages: Galician, Croatian, Japanese, Russian
### Fixes:
- Various fixes, including key verification fixes
# Version 0.15.1 - 2020-06-26 # Version 0.15.1 - 2020-06-26
### Fixes: ### Fixes:

View File

@ -84,6 +84,7 @@ dependencies {
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation "net.zetetic:android-database-sqlcipher:4.4.0" // needed for moor_ffi w/ sqlcipher
} }
apply plugin: "com.google.gms.google-services" apply plugin: "com.google.gms.google-services"

View File

@ -63,6 +63,7 @@ target 'Runner' do
# Keep pod path relative so it can be checked into Podfile.lock. # Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter' pod 'Flutter', :path => 'Flutter'
pod 'SQLCipher'
# Plugin Pods # Plugin Pods
@ -90,4 +91,4 @@ post_install do |installer|
end end
# add pods for desired Firebase products # add pods for desired Firebase products
# https://firebase.google.com/docs/ios/setup#available-pods # https://firebase.google.com/docs/ios/setup#available-pods

View File

@ -0,0 +1,121 @@
// file from https://gist.github.com/simolus3/5097bbd80ce59f9b957961fe851fd95a#file-cipher_db-dart
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:math';
import 'package:moor/backends.dart';
import 'package:moor/moor.dart';
import 'package:moor_ffi/moor_ffi.dart';
import 'package:moor_ffi/open_helper.dart';
/// Tells `moor_ffi` to use `sqlcipher` instead of the regular `sqlite3`.
///
/// This needs to be called before using `moor`, for instance in the `main`
/// method.
void init() {
const sharedLibraryName = 'libsqlcipher.so';
open.overrideFor(OperatingSystem.android, () {
try {
return DynamicLibrary.open(sharedLibraryName);
} catch (_) {
// On some (especially old) Android devices, we somehow can't dlopen
// libraries shipped with the apk. We need to find the full path of the
// library (/data/data/<id>/lib/libsqlite3.so) and open that one.
// For details, see https://github.com/simolus3/moor/issues/420
final appIdAsBytes = File('/proc/self/cmdline').readAsBytesSync();
// app id ends with the first \0 character in here.
final endOfAppId = max(appIdAsBytes.indexOf(0), 0);
final appId = String.fromCharCodes(appIdAsBytes.sublist(0, endOfAppId));
return DynamicLibrary.open('/data/data/$appId/lib/$sharedLibraryName');
}
});
open.overrideFor(OperatingSystem.iOS, () => DynamicLibrary.executable());
}
class VmDatabaseEncrypted extends DelegatedDatabase {
/// Creates a database that will store its result in the [file], creating it
/// if it doesn't exist.
factory VmDatabaseEncrypted(
File file, {
String password = '',
bool logStatements = false,
}) {
final vmDatabase = VmDatabase(file, logStatements: logStatements);
return VmDatabaseEncrypted._(vmDatabase, password);
}
factory VmDatabaseEncrypted.memory({
String password = '',
bool logStatements = false,
}) {
final vmDatabase = VmDatabase.memory(logStatements: logStatements);
return VmDatabaseEncrypted._(vmDatabase, password);
}
VmDatabaseEncrypted._(
VmDatabase vmDatabase,
String password,
) : super(
_VmEncryptedDelegate(vmDatabase.delegate, password),
logStatements: vmDatabase.logStatements,
isSequential: vmDatabase.isSequential,
);
}
class _VmEncryptedDelegate extends DatabaseDelegate {
final String password;
final DatabaseDelegate delegate;
_VmEncryptedDelegate(
this.delegate,
this.password,
);
@override
Future<void> open(QueryExecutorUser db) async {
await delegate.open(db);
final keyLiteral = const StringType().mapToSqlConstant(password);
await delegate.runCustom('PRAGMA KEY = $keyLiteral', const []);
return Future.value();
}
@override
FutureOr<bool> get isOpen => delegate.isOpen;
@override
Future<void> runBatched(BatchedStatements statements) {
return delegate.runBatched(statements);
}
@override
Future<void> runCustom(String statement, List args) {
return delegate.runCustom(statement, args);
}
@override
Future<int> runInsert(String statement, List args) {
return delegate.runInsert(statement, args);
}
@override
Future<QueryResult> runSelect(String statement, List args) {
return delegate.runSelect(statement, args);
}
@override
Future<int> runUpdate(String statement, List args) {
return delegate.runUpdate(statement, args);
}
@override
TransactionDelegate get transactionDelegate => delegate.transactionDelegate;
@override
DbVersionDelegate get versionDelegate => delegate.versionDelegate;
}

View File

@ -1,14 +1,63 @@
import 'dart:io';
import 'dart:isolate';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:encrypted_moor/encrypted_moor.dart'; import 'package:sqflite/sqflite.dart' show getDatabasesPath;
import 'package:path/path.dart' as p;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:moor/moor.dart';
import 'package:moor/isolate.dart';
import 'cipher_db.dart' as cipher;
Database constructDb( bool _inited = false;
// see https://moor.simonbinder.eu/docs/advanced-features/isolates/
void _startBackground(_IsolateStartRequest request) {
// this is the entry point from the background isolate! Let's create
// the database from the path we received
if (!_inited) {
cipher.init();
_inited = true;
}
final executor = cipher.VmDatabaseEncrypted(File(request.targetPath),
password: request.password, logStatements: request.logStatements);
// we're using MoorIsolate.inCurrent here as this method already runs on a
// background isolate. If we used MoorIsolate.spawn, a third isolate would be
// started which is not what we want!
final moorIsolate = MoorIsolate.inCurrent(
() => DatabaseConnection.fromExecutor(executor),
);
// inform the starting isolate about this, so that it can call .connect()
request.sendMoorIsolate.send(moorIsolate);
}
// used to bundle the SendPort and the target path, since isolate entry point
// functions can only take one parameter.
class _IsolateStartRequest {
final SendPort sendMoorIsolate;
final String targetPath;
final String password;
final bool logStatements;
_IsolateStartRequest(
this.sendMoorIsolate, this.targetPath, this.password, this.logStatements);
}
Future<Database> constructDb(
{bool logStatements = false, {bool logStatements = false,
String filename = 'database.sqlite', String filename = 'database.sqlite',
String password = ''}) { String password = ''}) async {
debugPrint('[Moor] using encrypted moor'); debugPrint('[Moor] using encrypted moor');
return Database(EncryptedExecutor( final dbFolder = await getDatabasesPath();
path: filename, password: password, logStatements: logStatements)); final targetPath = p.join(dbFolder, filename);
final receivePort = ReceivePort();
await Isolate.spawn(
_startBackground,
_IsolateStartRequest(
receivePort.sendPort, targetPath, password, logStatements),
);
final isolate = (await receivePort.first as MoorIsolate);
return Database.connect(await isolate.connect());
} }
Future<String> getLocalstorage(String key) async { Future<String> getLocalstorage(String key) async {

View File

@ -1,9 +1,9 @@
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
Database constructDb( Future<Database> constructDb(
{bool logStatements = false, {bool logStatements = false,
String filename = 'database.sqlite', String filename = 'database.sqlite',
String password = ''}) { String password = ''}) async {
throw 'Platform not supported'; throw 'Platform not supported';
} }

View File

@ -3,10 +3,10 @@ import 'package:moor/moor_web.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:html'; import 'dart:html';
Database constructDb( Future<Database> constructDb(
{bool logStatements = false, {bool logStatements = false,
String filename = 'database.sqlite', String filename = 'database.sqlite',
String password = ''}) { String password = ''}) async {
debugPrint('[Moor] Using moor web'); debugPrint('[Moor] Using moor web');
return Database(WebDatabase.withStorage( return Database(WebDatabase.withStorage(
MoorWebStorage.indexedDbIfSupported(filename), MoorWebStorage.indexedDbIfSupported(filename),

View File

@ -12,27 +12,36 @@ import 'package:olm/olm.dart' as olm; // needed for migration
import 'package:random_string/random_string.dart'; import 'package:random_string/random_string.dart';
Future<Database> getDatabase(Client client) async { Future<Database> getDatabase(Client client) async {
if (_db != null) return _db; while (_generateDatabaseLock) {
final store = Store(); await Future.delayed(Duration(milliseconds: 50));
var password = await store.getItem('database-password');
var needMigration = false;
if (password == null || password.isEmpty) {
needMigration = true;
password = randomString(255);
} }
_db = constructDb( _generateDatabaseLock = true;
logStatements: false, try {
filename: 'moor.sqlite', if (_db != null) return _db;
password: password, final store = Store();
); var password = await store.getItem('database-password');
if (needMigration) { var needMigration = false;
await migrate(client.clientName, _db, store); if (password == null || password.isEmpty) {
await store.setItem('database-password', password); needMigration = true;
password = randomString(255);
}
_db = await constructDb(
logStatements: false,
filename: 'moor.sqlite',
password: password,
);
if (needMigration) {
await migrate(client.clientName, _db, store);
await store.setItem('database-password', password);
}
return _db;
} finally {
_generateDatabaseLock = false;
} }
return _db;
} }
Database _db; Database _db;
bool _generateDatabaseLock = false;
Future<void> migrate(String clientName, Database db, Store store) async { Future<void> migrate(String clientName, Database db, Store store) async {
debugPrint('[Store] attempting old migration to moor...'); debugPrint('[Store] attempting old migration to moor...');

View File

@ -148,15 +148,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
encrypted_moor:
dependency: "direct main"
description:
path: "extras/encryption"
ref: HEAD
resolved-ref: "6f930b011577e5bc8a5e5511691c8fcc43869a1c"
url: "https://github.com/simolus3/moor.git"
source: git
version: "1.0.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -168,8 +159,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: ac720df3d26985faef0b6d6a86a8013f44c5c6e3 ref: f4c8cfe992ceed937721af87abbd13fce7700ea5
resolved-ref: ac720df3d26985faef0b6d6a86a8013f44c5c6e3 resolved-ref: f4c8cfe992ceed937721af87abbd13fce7700ea5
url: "https://gitlab.com/famedly/famedlysdk.git" url: "https://gitlab.com/famedly/famedlysdk.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
@ -252,7 +243,14 @@ packages:
name: flutter_matrix_html name: flutter_matrix_html
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.1" version: "0.1.2"
flutter_olm:
dependency: "direct main"
description:
name: flutter_olm
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@ -434,12 +432,10 @@ packages:
matrix_file_e2ee: matrix_file_e2ee:
dependency: transitive dependency: transitive
description: description:
path: "." name: matrix_file_e2ee
ref: "1.x.y" url: "https://pub.dartlang.org"
resolved-ref: "32edeff765369a7a77a0822f4b19302ca24a017b" source: hosted
url: "https://gitlab.com/famedly/libraries/matrix_file_e2ee.git" version: "1.0.4"
source: git
version: "1.0.3"
memoryfilepicker: memoryfilepicker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -475,6 +471,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.2" version: "3.0.2"
moor_ffi:
dependency: "direct main"
description:
name: moor_ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
multi_server_socket: multi_server_socket:
dependency: transitive dependency: transitive
description: description:
@ -506,12 +509,10 @@ packages:
olm: olm:
dependency: transitive dependency: transitive
description: description:
path: "." name: olm
ref: "1.x.y" url: "https://pub.dartlang.org"
resolved-ref: "8e4fcccff7a2d4d0bd5142964db092bf45061905" source: hosted
url: "https://gitlab.com/famedly/libraries/dart-olm.git" version: "1.2.1"
source: git
version: "1.2.0"
open_file: open_file:
dependency: "direct main" dependency: "direct main"
description: description:
@ -706,20 +707,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
sqflite_sqlcipher:
dependency: transitive
description:
name: sqflite_sqlcipher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0+6"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:

View File

@ -27,14 +27,13 @@ dependencies:
famedlysdk: famedlysdk:
git: git:
url: https://gitlab.com/famedly/famedlysdk.git url: https://gitlab.com/famedly/famedlysdk.git
ref: ac720df3d26985faef0b6d6a86a8013f44c5c6e3 ref: f4c8cfe992ceed937721af87abbd13fce7700ea5
localstorage: ^3.0.1+4 localstorage: ^3.0.1+4
bubble: ^1.1.9+1 bubble: ^1.1.9+1
memoryfilepicker: ^0.1.1 memoryfilepicker: ^0.1.1
url_launcher: ^5.4.1 url_launcher: ^5.4.1
url_launcher_web: ^0.1.0 url_launcher_web: ^0.1.0
sqflite: ^1.2.0
flutter_advanced_networkimage: any flutter_advanced_networkimage: any
firebase_messaging: ^6.0.13 firebase_messaging: ^6.0.13
flutter_local_notifications: ^1.4.3 flutter_local_notifications: ^1.4.3
@ -63,10 +62,8 @@ dependencies:
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
encrypted_moor: moor_ffi: ^0.5.0 # 0.6.0 and up have a bug that it doesn't build with --profile, see https://github.com/simolus3/moor/issues/581
git: sqflite: ^1.1.7 # Still used to obtain the database location
url: https://github.com/simolus3/moor.git
path: extras/encryption
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: