import 'dart:ffi'; import 'dart:io'; import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:sqlcipher_flutter_libs/sqlcipher_flutter_libs.dart'; import 'package:sqlite3/open.dart'; import 'package:sqlite3/sqlite3.dart'; /// Routes package:sqlite3 to the **SQLCipher** build instead of plain SQLite. /// /// - Android: the bundled SQLCipher `.so` (from sqlcipher_flutter_libs). /// - Linux: the system `libsqlcipher.so` (dev machines / CI install it). /// - iOS & macOS: SQLCipher is linked into the app binary — no override needed. void useSqlCipher() { open ..overrideFor(OperatingSystem.android, openCipherOnAndroid) ..overrideFor(OperatingSystem.linux, _openLinuxCipher); } DynamicLibrary _openLinuxCipher() { // The dev package ships `libsqlcipher.so`; the runtime package only the // versioned `libsqlcipher.so.0`. Accept either. try { return DynamicLibrary.open('libsqlcipher.so'); } on ArgumentError { return DynamicLibrary.open('libsqlcipher.so.0'); } } /// Opens [file] as an encrypted database using the raw 256-bit [keyHex]. /// /// Verifies SQLCipher is actually linked (`PRAGMA cipher_version`) and refuses /// to fall back to plaintext — enforcing "no plaintext at rest, ever". QueryExecutor openEncryptedExecutor(File file, String keyHex) { return LazyDatabase(() async { if (Platform.isAndroid) { await applyWorkaroundToOpenSqlCipherOnOldAndroidVersions(); } return NativeDatabase.createInBackground( file, isolateSetup: useSqlCipher, setup: (db) => applyKeyAndVerify(db, keyHex), ); }); } /// Applies the SQLCipher key to [db] and asserts encryption is active. /// /// `x'…'` passes the key as raw bytes, skipping the KDF (our key is already a /// random 256-bit value from the OS keystore). Exposed for the security test. void applyKeyAndVerify(Database db, String keyHex) { db.execute('PRAGMA key = "x\'$keyHex\'";'); final cipher = db.select('PRAGMA cipher_version;'); if (cipher.isEmpty) { throw StateError( 'SQLCipher is not linked: PRAGMA cipher_version is empty. Encryption at ' 'rest is mandatory — refusing to open a plaintext database.', ); } }