Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vault automated tests #970

Merged
merged 2 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 1 addition & 1 deletion lib/application/oracle/state.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/application/session/session.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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);
}
}
2 changes: 1 addition & 1 deletion lib/infrastructure/rpc/dto/rpc_request.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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
6 changes: 4 additions & 2 deletions lib/model/blockchain/keychain_secured_infos.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions lib/model/keychain_service_keypair.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/model/nft_category.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions lib/model/token_transfer_wallet.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/model/uco_transfer_wallet.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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