-
-
Notifications
You must be signed in to change notification settings - Fork 269
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: unify desktop and mobile databases (#764)
* chore: unify desktop and mobile databases - migrate `package:sqflite_flutter` to `sqlcipher_flutter_libs` - use FFI for all SQLite operations - use `SQfLiteEncryptionHelper` for database encryption - enforce encryption for new SQLite datbase implementation - migrate existing SQLite databases - encrypt unencrypted ones - migrate database locations to unified approach - drop dependency on sqlite Signed-off-by: The one with the braid <[email protected]> * chore: add sqlcipher to macos CI Signed-off-by: The one with the braid <[email protected]> --------- Signed-off-by: The one with the braid <[email protected]>
- Loading branch information
1 parent
3e9ff75
commit 3c532f9
Showing
16 changed files
with
207 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,7 +67,7 @@ jobs: | |
flutter-version: ${{ env.FLUTTER_VERSION }} | ||
cache: true | ||
- name: Install dependencies | ||
run: sudo apt-get update && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 -y | ||
run: sudo apt-get update && sudo apt-get install curl clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev libjsoncpp-dev cmake-data libsecret-1-dev libsecret-1-0 librhash0 libssl-dev -y | ||
- run: flutter pub get | ||
- run: flutter build linux --target-platform linux-x64 | ||
|
||
|
@@ -84,5 +84,6 @@ jobs: | |
uses: maxim-lobanov/[email protected] | ||
with: | ||
xcode-version: latest | ||
- run: brew install sqlcipher | ||
- run: flutter pub get | ||
- run: flutter build ios --no-codesign |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:flutter/foundation.dart'; | ||
|
||
import 'package:flutter_gen/gen_l10n/l10n.dart'; | ||
import 'package:matrix/matrix.dart'; | ||
import 'package:path/path.dart'; | ||
import 'package:path_provider/path_provider.dart'; | ||
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; | ||
import 'package:universal_html/html.dart' as html; | ||
|
||
import 'package:fluffychat/config/app_config.dart'; | ||
import 'package:fluffychat/utils/client_manager.dart'; | ||
import 'package:fluffychat/utils/matrix_sdk_extensions/flutter_hive_collections_database.dart'; | ||
import 'package:fluffychat/utils/platform_infos.dart'; | ||
import 'cipher.dart'; | ||
|
||
import 'sqlcipher_stub.dart' | ||
if (dart.library.io) 'package:sqlcipher_flutter_libs/sqlcipher_flutter_libs.dart'; | ||
|
||
Future<DatabaseApi> flutterMatrixSdkDatabaseBuilder(Client client) async { | ||
MatrixSdkDatabase? database; | ||
try { | ||
database = await _constructDatabase(client); | ||
await database.open(); | ||
return database; | ||
} catch (e) { | ||
// Try to delete database so that it can created again on next init: | ||
database?.delete().catchError( | ||
(e, s) => Logs().w( | ||
'Unable to delete database, after failed construction', | ||
e, | ||
s, | ||
), | ||
); | ||
|
||
// Send error notification: | ||
final l10n = lookupL10n(PlatformDispatcher.instance.locale); | ||
ClientManager.sendInitNotification( | ||
l10n.initAppError, | ||
l10n.databaseBuildErrorBody( | ||
AppConfig.newIssueUrl.toString(), | ||
e.toString(), | ||
), | ||
); | ||
|
||
return FlutterHiveCollectionsDatabase.databaseBuilder(client); | ||
} | ||
} | ||
|
||
Future<MatrixSdkDatabase> _constructDatabase(Client client) async { | ||
if (kIsWeb) { | ||
html.window.navigator.storage?.persist(); | ||
return MatrixSdkDatabase(client.clientName); | ||
} | ||
|
||
final cipher = await getDatabaseCipher(); | ||
|
||
final fileStoragePath = PlatformInfos.isIOS || PlatformInfos.isMacOS | ||
? await getLibraryDirectory() | ||
: await getApplicationSupportDirectory(); | ||
|
||
final path = join(fileStoragePath.path, '${client.clientName}.sqlite'); | ||
|
||
// fix dlopen for old Android | ||
await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions(); | ||
// import the SQLite / SQLCipher shared objects / dynamic libraries | ||
final factory = | ||
createDatabaseFactoryFfi(ffiInit: SQfLiteEncryptionHelper.ffiInit); | ||
|
||
// migrate from potential previous SQLite database path to current one | ||
await _migrateLegacyLocation(path, client.clientName); | ||
|
||
// required for [getDatabasesPath] | ||
databaseFactory = factory; | ||
|
||
// in case we got a cipher, we use the encryption helper | ||
// to manage SQLite encryption | ||
final helper = SQfLiteEncryptionHelper( | ||
factory: factory, | ||
path: path, | ||
cipher: cipher, | ||
); | ||
|
||
// check whether the DB is already encrypted and otherwise do so | ||
await helper.ensureDatabaseFileEncrypted(); | ||
|
||
final database = await factory.openDatabase( | ||
path, | ||
options: OpenDatabaseOptions( | ||
version: 1, | ||
// most important : apply encryption when opening the DB | ||
onConfigure: helper.applyPragmaKey, | ||
), | ||
); | ||
|
||
return MatrixSdkDatabase( | ||
client.clientName, | ||
database: database, | ||
maxFileSize: 1024 * 1024 * 10, | ||
fileStoragePath: fileStoragePath, | ||
deleteFilesAfterDuration: const Duration(days: 30), | ||
); | ||
} | ||
|
||
Future<void> _migrateLegacyLocation( | ||
String sqlFilePath, | ||
String clientName, | ||
) async { | ||
final oldPath = PlatformInfos.isDesktop | ||
? (await getApplicationSupportDirectory()).path | ||
: await getDatabasesPath(); | ||
|
||
final oldFilePath = join(oldPath, clientName); | ||
if (oldFilePath == sqlFilePath) return; | ||
|
||
final maybeOldFile = File(oldFilePath); | ||
if (await maybeOldFile.exists()) { | ||
Logs().i( | ||
'Migrate legacy location for database from "$oldFilePath" to "$sqlFilePath"', | ||
); | ||
await maybeOldFile.copy(sqlFilePath); | ||
await maybeOldFile.delete(); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import 'dart:convert'; | ||
import 'dart:math'; | ||
|
||
import 'package:flutter/services.dart'; | ||
|
||
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; | ||
import 'package:matrix/matrix.dart'; | ||
|
||
const _passwordStorageKey = 'database_password'; | ||
|
||
Future<String> getDatabaseCipher() async { | ||
String? password; | ||
|
||
try { | ||
const secureStorage = FlutterSecureStorage(); | ||
final containsEncryptionKey = | ||
await secureStorage.read(key: _passwordStorageKey) != null; | ||
if (!containsEncryptionKey) { | ||
final rng = Random.secure(); | ||
final list = Uint8List(32); | ||
list.setAll(0, Iterable.generate(list.length, (i) => rng.nextInt(256))); | ||
final newPassword = base64UrlEncode(list); | ||
await secureStorage.write( | ||
key: _passwordStorageKey, | ||
value: newPassword, | ||
); | ||
} | ||
// workaround for if we just wrote to the key and it still doesn't exist | ||
password = await secureStorage.read(key: _passwordStorageKey); | ||
if (password == null) throw MissingPluginException(); | ||
} on MissingPluginException catch (_) { | ||
const FlutterSecureStorage() | ||
.delete(key: _passwordStorageKey) | ||
.catchError((_) {}); | ||
Logs().i('Database encryption is not supported on this platform'); | ||
} catch (e, s) { | ||
const FlutterSecureStorage() | ||
.delete(key: _passwordStorageKey) | ||
.catchError((_) {}); | ||
Logs().w('Unable to init database encryption', e, s); | ||
} | ||
|
||
// with the new database, we should no longer allow unencrypted storage | ||
// secure_storage now supports all platforms we support | ||
assert(password != null); | ||
|
||
return password!; | ||
} |
1 change: 1 addition & 0 deletions
1
lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/sqlcipher_stub.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Future<void> applyWorkaroundToOpenSqlCipherOnOldAndroidVersions() async {} |
107 changes: 0 additions & 107 deletions
107
lib/utils/matrix_sdk_extensions/flutter_matrix_sdk_database_builder.dart
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.