ente/mobile/lib/utils/lock_screen_settings.dart
Ashil 627a34a231
[mob][auth] Lockscreen option auth (#2465)
### Pull Request Description

*Features Added:*

1. *App Lock Options:*
- *Device Lock*: Users can set the app lock to utilize the device's
default lock mechanism (e.g., fingerprint, face recognition, or device
PIN/password).
- *Custom PIN/Password*: Users have the option to set a custom PIN or
password for app lock.

2. *Authentication for App Lock Settings:*
- When users attempt to change the app lock settings, they must
authenticate using the currently set PIN/password or, if none is set,
the device lock.

3. *App Lock on Background and Quit:*
- The app will prompt for authentication when it is brought back to the
foreground after being in the background or when the app is restarted.
4. *Incremental Lock Timer:*
- A timer mechanism is implemented which starts at 30 seconds after few
incorrect unlock attempts.
- The lockout time doubles after each incorrect attempt, increasing
delay penalties.
- After 10 incorrect attempts, the user is automatically logged out.
2024-07-31 12:43:15 +05:30

155 lines
4.7 KiB
Dart

import "dart:convert";
import "package:flutter/foundation.dart";
import "package:flutter_secure_storage/flutter_secure_storage.dart";
import "package:flutter_sodium/flutter_sodium.dart";
import "package:photos/utils/crypto_util.dart";
import "package:privacy_screen/privacy_screen.dart";
import "package:shared_preferences/shared_preferences.dart";
class LockScreenSettings {
LockScreenSettings._privateConstructor();
static final LockScreenSettings instance =
LockScreenSettings._privateConstructor();
static const password = "ls_password";
static const pin = "ls_pin";
static const saltKey = "ls_salt";
static const keyInvalidAttempts = "ls_invalid_attempts";
static const lastInvalidAttemptTime = "ls_last_invalid_attempt_time";
static const keyHideAppContent = "ls_hide_app_content";
static const autoLockTime = "ls_auto_lock_time";
late FlutterSecureStorage _secureStorage;
late SharedPreferences _preferences;
static const List<Duration> autoLockDurations = [
Duration(seconds: 0),
Duration(seconds: 5),
Duration(seconds: 15),
Duration(minutes: 1),
Duration(minutes: 5),
Duration(minutes: 30),
];
void init(SharedPreferences prefs) async {
_secureStorage = const FlutterSecureStorage();
_preferences = prefs;
///Workaround for privacyScreen not working when app is killed and opened.
await setHideAppContent(getShouldHideAppContent());
}
Future<void> setHideAppContent(bool hideContent) async {
!hideContent
? await PrivacyScreen.instance.disable()
: await PrivacyScreen.instance.enable(
iosOptions: const PrivacyIosOptions(
enablePrivacy: true,
lockTrigger: IosLockTrigger.didEnterBackground,
),
androidOptions: const PrivacyAndroidOptions(
enableSecure: true,
),
blurEffect: PrivacyBlurEffect.extraLight,
);
await _preferences.setBool(keyHideAppContent, hideContent);
}
bool getShouldHideAppContent() {
return _preferences.getBool(keyHideAppContent) ?? false;
}
Future<void> setAutoLockTime(Duration duration) async {
await _preferences.setInt(autoLockTime, duration.inMilliseconds);
}
int getAutoLockTime() {
return _preferences.getInt(autoLockTime) ?? 5000;
}
Future<void> setLastInvalidAttemptTime(int time) async {
await _preferences.setInt(lastInvalidAttemptTime, time);
}
int getlastInvalidAttemptTime() {
return _preferences.getInt(lastInvalidAttemptTime) ?? 0;
}
int getInvalidAttemptCount() {
return _preferences.getInt(keyInvalidAttempts) ?? 0;
}
Future<void> setInvalidAttemptCount(int count) async {
await _preferences.setInt(keyInvalidAttempts, count);
}
static Uint8List _generateSalt() {
return Sodium.randombytesBuf(Sodium.cryptoPwhashSaltbytes);
}
Future<void> setPin(String userPin) async {
await _secureStorage.delete(key: saltKey);
final salt = _generateSalt();
final hash = cryptoPwHash({
"password": utf8.encode(userPin),
"salt": salt,
"opsLimit": Sodium.cryptoPwhashOpslimitInteractive,
"memLimit": Sodium.cryptoPwhashMemlimitInteractive,
});
final String saltPin = base64Encode(salt);
final String hashedPin = base64Encode(hash);
await _secureStorage.write(key: saltKey, value: saltPin);
await _secureStorage.write(key: pin, value: hashedPin);
await _secureStorage.delete(key: password);
return;
}
Future<Uint8List?> getSalt() async {
final String? salt = await _secureStorage.read(key: saltKey);
if (salt == null) return null;
return base64Decode(salt);
}
Future<String?> getPin() async {
return _secureStorage.read(key: pin);
}
Future<void> setPassword(String pass) async {
await _secureStorage.delete(key: saltKey);
final salt = _generateSalt();
final hash = cryptoPwHash({
"password": utf8.encode(pass),
"salt": salt,
"opsLimit": Sodium.cryptoPwhashOpslimitInteractive,
"memLimit": Sodium.cryptoPwhashMemlimitInteractive,
});
await _secureStorage.write(key: saltKey, value: base64Encode(salt));
await _secureStorage.write(key: password, value: base64Encode(hash));
await _secureStorage.delete(key: pin);
return;
}
Future<String?> getPassword() async {
return _secureStorage.read(key: password);
}
Future<void> removePinAndPassword() async {
await _secureStorage.delete(key: saltKey);
await _secureStorage.delete(key: pin);
await _secureStorage.delete(key: password);
}
Future<bool> isPinSet() async {
return await _secureStorage.containsKey(key: pin);
}
Future<bool> isPasswordSet() async {
return await _secureStorage.containsKey(key: password);
}
}