Skip to content

Commit

Permalink
Fix sqlcipher bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert-Stackflow committed Aug 31, 2024
1 parent b2d21ae commit e8e6f5a
Show file tree
Hide file tree
Showing 23 changed files with 441 additions and 263 deletions.
44 changes: 27 additions & 17 deletions lib/Database/database_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class DatabaseManager {
if (lib != null) open.overrideForAll(() => lib!);
});
static DatabaseFactory _currentDbFactory = cipherDbFactory;
static bool _isSqlcipherLoaded = false;
static bool isDatabaseEncrypted = false;

static bool get initialized => _database != null;

Expand All @@ -46,26 +46,24 @@ class DatabaseManager {

static Future<void> initDataBase(String password) async {
if (_database == null) {
_currentDbFactory = cipherDbFactory;
String path = join(await FileUtil.getDatabaseDir(), _dbName);
File file = File(path);
bool isEncrypted = false;
if (file.existsSync()) {
final stream = file.openRead(0, _unencrypedFileHeader.length);
String content = String.fromCharCodes(await stream.fold<List<int>>(
[], (previous, element) => previous..addAll(element)));
if (content == _unencrypedFileHeader) {
isEncrypted = false;
isDatabaseEncrypted = false;
_currentDbFactory = dbFactory;
ILogger.info(
"Database is an unencrypted SQLite database. File header is $content");
} else {
isEncrypted = true;
isDatabaseEncrypted = true;
_currentDbFactory = cipherDbFactory;
ILogger.info("Database is an encrypted SQLite database.");
}
} else {
isEncrypted = true;
isDatabaseEncrypted = true;
_currentDbFactory = cipherDbFactory;
password = await HiveUtil.regeneratePassword();
ILogger.info("Database not exist and new password is $password");
Expand All @@ -78,7 +76,7 @@ class DatabaseManager {
version: _dbVersion,
singleInstance: true,
onConfigure: (db) async {
_onConfigure(db, password, isEncrypted);
_onConfigure(db, password);
},
onUpgrade: _onUpgrade,
onCreate: _onCreate,
Expand All @@ -90,18 +88,32 @@ class DatabaseManager {

static Future<bool> changePassword(String password) async {
if (_database != null) {
List<Map<String, Object?>> res =
await _database!.rawQuery("PRAGMA rekey='$password'");
if (res.isNotEmpty) {
return true;
if (isDatabaseEncrypted) {
List<Map<String, Object?>> res =
await _database!.rawQuery("PRAGMA rekey='$password'");
ILogger.info("Change database password result is $res");
if (res.isNotEmpty) {
return true;
}
} else {
try {
await _database!.rawQuery(
"ATTACH DATABASE 'encrypted.db' AS tmp KEY '$password'");
await _database!.rawQuery("SELECT sqlcipher_export('tmp')");
await _database!.rawQuery("DETACH DATABASE tmp");
return true;
} catch (e) {
ILogger.error("Failed to change database password", e);
return false;
}
}
return false;
}
return false;
}

static Future<void> _onConfigure(
Database db, String password, bool isEncrypted) async {
if (isEncrypted) {
static Future<void> _onConfigure(Database db, String password) async {
if (isDatabaseEncrypted) {
List<Map<String, Object?>> res =
await db.rawQuery("PRAGMA KEY='$password'");
if (res.isNotEmpty) {
Expand Down Expand Up @@ -248,12 +260,10 @@ class DatabaseManager {
lib = DynamicLibrary.open('/usr/lib/libsqlite3.dylib');
}
if (Platform.isWindows) {
lib = DynamicLibrary.open('sqlcipher.dll');
lib = DynamicLibrary.open('sqlite_sqlcipher.dll');
}
_isSqlcipherLoaded = true;
return lib;
} catch (e) {
_isSqlcipherLoaded = false;
return null;
}
}
Expand Down
4 changes: 4 additions & 0 deletions lib/Resources/fonts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "package:cloudotp/Utils/Tuple/tuple.dart";
import "package:cloudotp/Utils/responsive_util.dart";
import "package:flutter/cupertino.dart";

import "../Utils/app_provider.dart";
import "../Utils/font_util.dart";
import "../Utils/hive_util.dart";
import "../Utils/itoast.dart";
Expand Down Expand Up @@ -117,8 +118,11 @@ enum FontEnum {
await HiveUtil.put(HiveUtil.fontFamilyKey, item.index);
await FontEnum.downloadFont(
context: context,
showToast: false,
onFinished: (value) {
dialog.dismiss();
appProvider.darkTheme = appProvider.darkTheme;
appProvider.lightTheme = appProvider.lightTheme;
if (autoRestartApp) {
ResponsiveUtil.restartApp(context);
}
Expand Down
73 changes: 57 additions & 16 deletions lib/Screens/Lock/database_decrypt_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import '../../Utils/ilogger.dart';
import '../../Utils/responsive_util.dart';
import '../../Utils/uri_util.dart';
import '../../Widgets/Item/input_item.dart';
import '../../Widgets/Window/window_caption.dart';
import '../../generated/l10n.dart';
import '../main_screen.dart';

Expand All @@ -31,6 +30,22 @@ class DatabaseDecryptScreenState extends State<DatabaseDecryptScreen>
final FocusNode _focusNode = FocusNode();
late InputValidateAsyncController validateAsyncController;
GlobalKey<FormState> formKey = GlobalKey<FormState>();
bool _isMaximized = false;
bool _isStayOnTop = false;

@override
void onWindowMaximize() {
setState(() {
_isMaximized = true;
});
}

@override
void onWindowUnmaximize() {
setState(() {
_isMaximized = false;
});
}

@override
Future<void> onWindowResized() async {
Expand Down Expand Up @@ -82,17 +97,33 @@ class DatabaseDecryptScreenState extends State<DatabaseDecryptScreen>
Widget build(BuildContext context) {
return MyScaffold(
backgroundColor: MyTheme.getBackground(context),
appBar: ResponsiveUtil.isDesktop()
? PreferredSize(
preferredSize: const Size(0, 82),
child: ItemBuilder.buildWindowTitle(
context,
forceClose: true,
backgroundColor: MyTheme.getBackground(context),
isStayOnTop: _isStayOnTop,
isMaximized: _isMaximized,
onStayOnTopTap: () {
setState(() {
_isStayOnTop = !_isStayOnTop;
windowManager.setAlwaysOnTop(_isStayOnTop);
});
},
),
)
: null,
bottomNavigationBar: Container(
height: 82,
color: MyTheme.getBackground(context),
),
body: SafeArea(
right: false,
child: Stack(
children: [
if (ResponsiveUtil.isDesktop()) const WindowMoveHandle(),
Center(
child: DatabaseManager.lib != null
? _buildBody()
: _buildFailedBody(),
),
],
child: Center(
child:
DatabaseManager.lib != null ? _buildBody() : _buildFailedBody(),
),
),
);
Expand Down Expand Up @@ -173,13 +204,23 @@ class DatabaseDecryptScreenState extends State<DatabaseDecryptScreen>
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(S.current.loadSqlcipherFailed,
style: Theme.of(context).textTheme.titleLarge),
IgnorePointer(
child: Text(
S.current.loadSqlcipherFailed,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
),
const SizedBox(height: 30),
SizedBox(
width: min(MediaQuery.sizeOf(context).width - 40, 500),
child: Text(S.current.loadSqlcipherFailedMessage,
style: Theme.of(context).textTheme.titleMedium),
IgnorePointer(
child: SizedBox(
width: min(MediaQuery.sizeOf(context).width - 40, 500),
child: Text(
S.current.loadSqlcipherFailedMessage,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleMedium,
),
),
),
const SizedBox(height: 30),
ItemBuilder.buildRoundButton(
Expand Down
138 changes: 86 additions & 52 deletions lib/Screens/Lock/pin_verify_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import 'package:cloudotp/Utils/route_util.dart';
import 'package:cloudotp/Utils/utils.dart';
import 'package:cloudotp/Widgets/General/Unlock/gesture_notifier.dart';
import 'package:cloudotp/Widgets/General/Unlock/gesture_unlock_view.dart';
import 'package:cloudotp/Widgets/Window/window_caption.dart';
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';

import '../../Resources/theme.dart';
import '../../Utils/hive_util.dart';
import '../../Utils/responsive_util.dart';
import '../../Widgets/Item/item_builder.dart';
Expand Down Expand Up @@ -40,6 +40,22 @@ class PinVerifyScreenState extends State<PinVerifyScreen> with WindowListener {
late final GestureNotifier _notifier = GestureNotifier(
status: GestureStatus.verify, gestureText: S.current.verifyGestureLock);
final GlobalKey<GestureState> _gestureUnlockView = GlobalKey();
bool _isMaximized = false;
bool _isStayOnTop = false;

@override
void onWindowMaximize() {
setState(() {
_isMaximized = true;
});
}

@override
void onWindowUnmaximize() {
setState(() {
_isMaximized = false;
});
}

@override
Future<void> onWindowResized() async {
Expand Down Expand Up @@ -86,63 +102,81 @@ class PinVerifyScreenState extends State<PinVerifyScreen> with WindowListener {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: MyTheme.getBackground(context),
appBar: ResponsiveUtil.isDesktop() && widget.jumpToMain
? PreferredSize(
preferredSize: const Size(0, 82),
child: ItemBuilder.buildWindowTitle(
context,
forceClose: true,
backgroundColor: MyTheme.getBackground(context),
isStayOnTop: _isStayOnTop,
isMaximized: _isMaximized,
onStayOnTopTap: () {
setState(() {
_isStayOnTop = !_isStayOnTop;
windowManager.setAlwaysOnTop(_isStayOnTop);
});
},
),
)
: null,
bottomNavigationBar: Container(
height: widget.jumpToMain ? 82 : 0,
color: MyTheme.getBackground(context),
),
body: SafeArea(
right: false,
child: Stack(
children: [
if (ResponsiveUtil.isDesktop()) const WindowMoveHandle(),
Center(
child: PopScope(
canPop: !widget.isModal,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 50),
Text(
_notifier.gestureText,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 30),
Flexible(
child: GestureUnlockView(
key: _gestureUnlockView,
size: min(MediaQuery.sizeOf(context).width, 400),
padding: 60,
roundSpace: 40,
defaultColor: Colors.grey.withOpacity(0.5),
selectedColor: Theme.of(context).primaryColor,
failedColor: Colors.redAccent,
disableColor: Colors.grey,
solidRadiusRatio: 0.3,
lineWidth: 2,
touchRadiusRatio: 0.3,
onCompleted: _gestureComplete,
),
),
Visibility(
visible: _isUseBiometric,
child: GestureDetector(
onTap: () {
auth();
},
child: ItemBuilder.buildClickItem(
Text(
ResponsiveUtil.isWindows()
? S.current.biometricVerifyPin
: S.current.biometricVerifyFingerprint,
style: Theme.of(context).textTheme.titleSmall,
),
),
child: Center(
child: PopScope(
canPop: !widget.isModal,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 50),
Text(
_notifier.gestureText,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 30),
Flexible(
child: GestureUnlockView(
key: _gestureUnlockView,
size: min(MediaQuery.sizeOf(context).width, 400),
padding: 60,
roundSpace: 40,
defaultColor: Colors.grey.withOpacity(0.5),
selectedColor: Theme.of(context).primaryColor,
failedColor: Colors.redAccent,
disableColor: Colors.grey,
solidRadiusRatio: 0.3,
lineWidth: 2,
touchRadiusRatio: 0.3,
onCompleted: _gestureComplete,
),
),
Visibility(
visible: _isUseBiometric,
child: GestureDetector(
onTap: () {
auth();
},
child: ItemBuilder.buildClickItem(
Text(
ResponsiveUtil.isWindows()
? S.current.biometricVerifyPin
: S.current.biometricVerifyFingerprint,
style: Theme.of(context).textTheme.titleSmall,
),
),
const SizedBox(height: 50),
],
),
),
),
const SizedBox(height: 50),
],
),
],
),
),
),
);
Expand Down
Loading

0 comments on commit e8e6f5a

Please sign in to comment.