Skip to content

Commit

Permalink
✅ Test Vault.
Browse files Browse the repository at this point in the history
  • Loading branch information
Chralu committed Jun 4, 2024
1 parent 1c3454e commit ae72f14
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 38 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
.svn/
dist/

# Test
test/tmp_data

# Fastlane

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ part of '../vault.dart';

/// Encryption key is AES encrypted before storage
class PasswordVaultCipher implements VaultCipher {
PasswordVaultCipher({required this.password});
PasswordVaultCipher({required this.passphrase});

final String password;
final String passphrase;

Uint8List? _key;

Expand All @@ -24,11 +24,11 @@ class PasswordVaultCipher implements VaultCipher {

final encryptionKey = await Hive.readEncryptedSecureKey(
secureStorage,
password,
passphrase,
) ??
await Hive.generateAndStoreEncryptedSecureKey(
secureStorage,
password,
passphrase,
);

return encryptionKey;
Expand Down
86 changes: 59 additions & 27 deletions lib/infrastructure/datasources/vault/vault.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,74 @@ part 'lib/vault.encrypted_securedkey_extension.dart';
part 'lib/vault.raw_securedkey_extension.dart';

abstract class VaultCipher {
factory VaultCipher(String password) {
return kIsWeb
? PasswordVaultCipher(password: password)
: SimpleVaultCipher();
}

static Future<bool> get isSetup async {
return kIsWeb ? PasswordVaultCipher.isSetup : SimpleVaultCipher.isSetup;
}

static Future<void> clear() async {
return kIsWeb ? PasswordVaultCipher.clear() : SimpleVaultCipher.clear();
}

Future<Uint8List> get();

Future<void> updateSecureKey(
String newPassword,
);
}

typedef VaultPasswordDelegate = Future<String> Function();
abstract class VaultCipherFactory {
factory VaultCipherFactory() =>
kIsWeb ? PasswordVaultCipherFactory() : SimpleVaultCipherFactory();

VaultCipher build(String password);

Future<bool> get isSetup;

Future<void> clear();
}

class PasswordVaultCipherFactory implements VaultCipherFactory {
@override
VaultCipher build(String password) => PasswordVaultCipher(
passphrase: password,
);

@override
Future<bool> get isSetup => PasswordVaultCipher.isSetup;

@override
Future<void> clear() => PasswordVaultCipher.clear();
}

class SimpleVaultCipherFactory implements VaultCipherFactory {
@override
VaultCipher build(String password) => SimpleVaultCipher();

@override
Future<void> clear() => SimpleVaultCipher.clear();

@override
Future<bool> get isSetup => SimpleVaultCipher.isSetup;
}

typedef VaultPassphraseDelegate = Future<String> Function();
typedef VaultAutolockDelegate = Future<bool> Function();

class Vault {
Vault._();
Vault._({VaultCipherFactory? cipherFactory}) {
_cipherFactory = cipherFactory ?? VaultCipherFactory();
}

factory Vault.instance() {
Vault._instance ??= Vault._();
factory Vault.instance({VaultCipherFactory? cipherFactory}) {
Vault._instance ??= Vault._(cipherFactory: cipherFactory);
return Vault._instance!;
}

@visibleForTesting
static Future<void> reset() async {
Vault._instance = null;
await Hive.deleteFromDisk();
}

late final VaultCipherFactory _cipherFactory;

static const _logName = 'Vault';

static Vault? _instance;

VaultPasswordDelegate? passwordDelegate;
VaultPassphraseDelegate? passphraseDelegate;
VaultAutolockDelegate? shouldBeLocked;
VaultCipher? _vaultCipher;

Expand All @@ -74,7 +105,7 @@ class Vault {
'Unlocking vault',
name: _logName,
);
_vaultCipher = VaultCipher(password);
_vaultCipher = _cipherFactory.build(password);

// Ensures we are able to retrieve the encryption key
await _vaultCipher!.get();
Expand All @@ -85,7 +116,7 @@ class Vault {
}

Future<bool> get isSetup async {
return VaultCipher.isSetup;
return _cipherFactory.isSetup;
}

Future<bool> boxExists(String name) {
Expand All @@ -109,11 +140,12 @@ class Vault {
'Clearing vault secure key',
name: _logName,
);
await VaultCipher.clear();
await _cipherFactory.clear();
await lock();
}

Future<void> updateSecureKey(
String newPassword,
String passphrase,
) async {
log(
'Updating vault secure key',
Expand All @@ -122,7 +154,7 @@ class Vault {
if (_vaultCipher == null) {
throw const Failure.locked();
}
await _vaultCipher!.updateSecureKey(newPassword);
await _vaultCipher!.updateSecureKey(passphrase);
}

Future<Box<E>> openBox<E>(
Expand Down Expand Up @@ -205,7 +237,7 @@ class Vault {
return;
}

if (passwordDelegate == null) {
if (passphraseDelegate == null) {
throw Exception(
'Vault.passwordDelegate must be set before opening Boxes.',
);
Expand All @@ -214,8 +246,8 @@ class Vault {
'Requesting user action to unlock',
name: _logName,
);
final password = await passwordDelegate!();
final passphrase = await passphraseDelegate!();

await unlock(password);
await unlock(passphrase);
}
}
8 changes: 4 additions & 4 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ class SplashState extends ConsumerState<Splash> with WidgetsBindingObserver {
.addListener(removeNativeSplash);
}

VaultPasswordDelegate? _passwordDelegate;
VaultPassphraseDelegate? _passwordDelegate;

@override
void initState() {
Expand All @@ -337,7 +337,7 @@ class SplashState extends ConsumerState<Splash> with WidgetsBindingObserver {
),
canCancel: false,
);
Vault.instance().passwordDelegate = _passwordDelegate;
Vault.instance().passphraseDelegate = _passwordDelegate;

WidgetsBinding.instance.addPostFrameCallback((_) async {
await initializeProviders();
Expand All @@ -349,8 +349,8 @@ class SplashState extends ConsumerState<Splash> with WidgetsBindingObserver {
void dispose() {
/// If some other screen updated the passwordDelegate,
/// then we should not reset it.
if (Vault.instance().passwordDelegate == _passwordDelegate) {
Vault.instance().passwordDelegate = null;
if (Vault.instance().passphraseDelegate == _passwordDelegate) {
Vault.instance().passphraseDelegate = null;
}

super.dispose();
Expand Down
4 changes: 2 additions & 2 deletions lib/ui/views/authenticate/auto_lock_guard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class _AutoLockGuardState extends ConsumerState<AutoLockGuard>
WidgetsBinding.instance.addObserver(this);

Vault.instance()
..passwordDelegate = _forceAuthent
..passphraseDelegate = _forceAuthent
..shouldBeLocked = _shouldBeLocked;
}

Expand All @@ -72,7 +72,7 @@ class _AutoLockGuardState extends ConsumerState<AutoLockGuard>
WidgetsBinding.instance.removeObserver(this);
LockMaskOverlay.instance().hide();
Vault.instance()
..passwordDelegate = null
..passphraseDelegate = null
..shouldBeLocked = null;

super.dispose();
Expand Down
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.0"
mockito:
dependency: "direct dev"
description:
name: mockito
sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917"
url: "https://pub.dev"
source: hosted
version: "5.4.4"
msix:
dependency: "direct dev"
description:
Expand Down
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ dev_dependencies:
hive_generator: ^2.0.1
# Automatically generate code for converting to and from JSON by annotating Dart classes.
json_serializable: ^6.7.0
# Mocking library (tests)
mockito: ^5.4.4
# A command-line tool that create Msix installer from your flutter windows-build files.
msix: ^3.16.7
# Simple yet powerful Flutter-native UI testing framework eliminating limitations of flutter_test, integration_test, and flutter_driver.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:pointycastle/pointycastle.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group(
'Vault',
'Vault - SecureStorage',
() {
setUp(() async {
FlutterSecureStorage.setMockInitialValues({});
Expand Down
Loading

0 comments on commit ae72f14

Please sign in to comment.