2022-08-26 08:11:35 +00:00
|
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
2022-11-09 22:43:26 +00:00
|
|
|
import 'package:isar/isar.dart';
|
|
|
|
import 'package:stack_wallet_backup/secure_storage.dart';
|
2022-11-09 23:48:43 +00:00
|
|
|
import 'package:stackwallet/models/isar/models/encrypted_string_value.dart';
|
2022-11-12 22:04:16 +00:00
|
|
|
import 'package:stackwallet/utilities/stack_file_system.dart';
|
2022-08-26 08:11:35 +00:00
|
|
|
|
2022-11-09 23:48:43 +00:00
|
|
|
abstract class SecureStorageInterface {
|
2022-11-10 21:40:23 +00:00
|
|
|
dynamic get store;
|
|
|
|
|
2022-08-26 08:11:35 +00:00
|
|
|
Future<void> write({
|
|
|
|
required String key,
|
|
|
|
required String? value,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
});
|
|
|
|
|
|
|
|
Future<String?> read({
|
|
|
|
required String key,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
});
|
|
|
|
|
|
|
|
Future<void> delete({
|
|
|
|
required String key,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
});
|
2022-12-05 19:59:07 +00:00
|
|
|
|
|
|
|
Future<void> deleteAll({
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
});
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 23:48:43 +00:00
|
|
|
class DesktopSecureStore {
|
2022-11-09 22:43:26 +00:00
|
|
|
final StorageCryptoHandler handler;
|
|
|
|
late final Isar isar;
|
|
|
|
|
2022-11-09 23:48:43 +00:00
|
|
|
DesktopSecureStore(this.handler);
|
2022-11-09 22:43:26 +00:00
|
|
|
|
2022-11-09 23:48:43 +00:00
|
|
|
Future<void> init() async {
|
|
|
|
isar = await Isar.open(
|
|
|
|
[EncryptedStringValueSchema],
|
2022-11-12 22:04:16 +00:00
|
|
|
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
2022-11-09 23:48:43 +00:00
|
|
|
inspector: false,
|
2022-11-10 21:40:23 +00:00
|
|
|
name: "desktopStore",
|
2022-11-09 23:48:43 +00:00
|
|
|
);
|
|
|
|
}
|
2022-11-09 22:43:26 +00:00
|
|
|
|
2022-12-05 19:59:07 +00:00
|
|
|
Future<void> close() async {
|
|
|
|
await isar.close();
|
|
|
|
}
|
|
|
|
|
2022-11-09 22:43:26 +00:00
|
|
|
Future<String?> read({
|
|
|
|
required String key,
|
|
|
|
}) async {
|
2022-11-09 23:48:43 +00:00
|
|
|
final value =
|
|
|
|
await isar.encryptedStringValues.filter().keyEqualTo(key).findFirst();
|
|
|
|
|
|
|
|
// value does not exist;
|
|
|
|
if (value == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2022-11-09 22:43:26 +00:00
|
|
|
|
2022-11-09 23:48:43 +00:00
|
|
|
return await handler.decryptValue(key, value.value);
|
2022-11-09 22:43:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> write({
|
|
|
|
required String key,
|
|
|
|
required String? value,
|
|
|
|
}) async {
|
2022-11-09 23:48:43 +00:00
|
|
|
if (value == null) {
|
|
|
|
// here we assume that a value is to be deleted
|
2022-11-10 21:40:23 +00:00
|
|
|
await isar.writeTxn(() async {
|
|
|
|
await isar.encryptedStringValues.deleteByKey(key);
|
|
|
|
});
|
2022-11-09 23:48:43 +00:00
|
|
|
} else {
|
|
|
|
// otherwise created encrypted object value
|
|
|
|
final object = EncryptedStringValue();
|
|
|
|
object.key = key;
|
|
|
|
object.value = await handler.encryptValue(key, value);
|
|
|
|
|
|
|
|
// store object value
|
2022-11-10 21:40:23 +00:00
|
|
|
await isar.writeTxn(() async {
|
|
|
|
await isar.encryptedStringValues.put(object);
|
|
|
|
});
|
2022-11-09 23:48:43 +00:00
|
|
|
}
|
2022-11-09 22:43:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> delete({
|
|
|
|
required String key,
|
|
|
|
}) async {
|
2022-11-10 21:40:23 +00:00
|
|
|
await isar.writeTxn(() async {
|
|
|
|
await isar.encryptedStringValues.deleteByKey(key);
|
|
|
|
});
|
2022-11-09 22:43:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// all *Options params ignored on desktop
|
2022-11-09 23:48:43 +00:00
|
|
|
class SecureStorageWrapper implements SecureStorageInterface {
|
2022-11-09 22:43:26 +00:00
|
|
|
final dynamic _store;
|
|
|
|
final bool _isDesktop;
|
2022-08-26 08:11:35 +00:00
|
|
|
|
2022-11-10 21:40:23 +00:00
|
|
|
@override
|
|
|
|
dynamic get store => _store;
|
|
|
|
|
2022-11-09 22:43:26 +00:00
|
|
|
const SecureStorageWrapper({
|
|
|
|
required dynamic store,
|
|
|
|
required bool isDesktop,
|
|
|
|
}) : assert(isDesktop
|
2022-11-09 23:48:43 +00:00
|
|
|
? store is DesktopSecureStore
|
2022-11-09 22:43:26 +00:00
|
|
|
: store is FlutterSecureStorage),
|
|
|
|
_store = store,
|
|
|
|
_isDesktop = isDesktop;
|
2022-08-26 08:11:35 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
Future<String?> read({
|
|
|
|
required String key,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
2022-11-09 22:43:26 +00:00
|
|
|
}) async {
|
|
|
|
if (_isDesktop) {
|
2022-11-09 23:48:43 +00:00
|
|
|
return await (_store as DesktopSecureStore).read(key: key);
|
2022-11-09 22:43:26 +00:00
|
|
|
} else {
|
|
|
|
return await (_store as FlutterSecureStorage).read(
|
|
|
|
key: key,
|
|
|
|
iOptions: iOptions,
|
|
|
|
aOptions: aOptions,
|
|
|
|
lOptions: lOptions,
|
|
|
|
webOptions: webOptions,
|
|
|
|
mOptions: mOptions,
|
|
|
|
wOptions: wOptions,
|
|
|
|
);
|
|
|
|
}
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> write({
|
|
|
|
required String key,
|
|
|
|
required String? value,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
2022-11-09 22:43:26 +00:00
|
|
|
}) async {
|
|
|
|
if (_isDesktop) {
|
2022-11-09 23:48:43 +00:00
|
|
|
return await (_store as DesktopSecureStore).write(key: key, value: value);
|
2022-11-09 22:43:26 +00:00
|
|
|
} else {
|
|
|
|
return await (_store as FlutterSecureStorage).write(
|
|
|
|
key: key,
|
|
|
|
value: value,
|
|
|
|
iOptions: iOptions,
|
|
|
|
aOptions: aOptions,
|
|
|
|
lOptions: lOptions,
|
|
|
|
webOptions: webOptions,
|
|
|
|
mOptions: mOptions,
|
|
|
|
wOptions: wOptions,
|
|
|
|
);
|
|
|
|
}
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> delete({
|
|
|
|
required String key,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
}) async {
|
2022-11-09 22:43:26 +00:00
|
|
|
if (_isDesktop) {
|
2022-11-09 23:48:43 +00:00
|
|
|
return (_store as DesktopSecureStore).delete(key: key);
|
2022-11-09 22:43:26 +00:00
|
|
|
} else {
|
|
|
|
return await (_store as FlutterSecureStorage).delete(
|
|
|
|
key: key,
|
|
|
|
iOptions: iOptions,
|
|
|
|
aOptions: aOptions,
|
|
|
|
lOptions: lOptions,
|
|
|
|
webOptions: webOptions,
|
|
|
|
mOptions: mOptions,
|
|
|
|
wOptions: wOptions,
|
|
|
|
);
|
|
|
|
}
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
2022-12-05 19:59:07 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> deleteAll({
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
}) async {
|
|
|
|
if (_isDesktop) {
|
|
|
|
// return (_store as DesktopSecureStore).deleteAll();
|
|
|
|
throw UnimplementedError();
|
|
|
|
} else {
|
|
|
|
return await (_store as FlutterSecureStorage).deleteAll(
|
|
|
|
iOptions: iOptions,
|
|
|
|
aOptions: aOptions,
|
|
|
|
lOptions: lOptions,
|
|
|
|
webOptions: webOptions,
|
|
|
|
mOptions: mOptions,
|
|
|
|
wOptions: wOptions,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Mock class for testing purposes
|
2022-11-09 23:48:43 +00:00
|
|
|
class FakeSecureStorage implements SecureStorageInterface {
|
2022-08-26 08:11:35 +00:00
|
|
|
final Map<String, String?> _store = {};
|
|
|
|
int _interactions = 0;
|
|
|
|
int get interactions => _interactions;
|
|
|
|
int _writes = 0;
|
|
|
|
int get writes => _writes;
|
|
|
|
int _reads = 0;
|
|
|
|
int get reads => _reads;
|
|
|
|
int _deletes = 0;
|
|
|
|
int get deletes => _deletes;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<String?> read({
|
|
|
|
required String key,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
}) async {
|
|
|
|
_interactions++;
|
|
|
|
_reads++;
|
|
|
|
return _store[key];
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> write({
|
|
|
|
required String key,
|
|
|
|
required String? value,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
}) async {
|
|
|
|
_interactions++;
|
|
|
|
_writes++;
|
|
|
|
_store[key] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> delete({
|
|
|
|
required String key,
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
}) async {
|
|
|
|
_interactions++;
|
|
|
|
_deletes++;
|
|
|
|
_store.remove(key);
|
|
|
|
}
|
2022-11-10 21:40:23 +00:00
|
|
|
|
2022-12-05 19:59:07 +00:00
|
|
|
@override
|
|
|
|
Future<void> deleteAll({
|
|
|
|
IOSOptions? iOptions,
|
|
|
|
AndroidOptions? aOptions,
|
|
|
|
LinuxOptions? lOptions,
|
|
|
|
WebOptions? webOptions,
|
|
|
|
MacOsOptions? mOptions,
|
|
|
|
WindowsOptions? wOptions,
|
|
|
|
}) async {
|
|
|
|
_interactions++;
|
|
|
|
_deletes++;
|
|
|
|
_store.clear();
|
|
|
|
}
|
|
|
|
|
2022-11-10 21:40:23 +00:00
|
|
|
@override
|
|
|
|
dynamic get store => throw UnimplementedError();
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|