2023-05-26 21:21:16 +00:00
|
|
|
/*
|
|
|
|
* This file is part of Stack Wallet.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2023 Cypher Stack
|
|
|
|
* All Rights Reserved.
|
|
|
|
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
|
|
|
* Generated by Cypher Stack on 2023-05-26
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
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';
|
2024-05-27 23:56:22 +00:00
|
|
|
|
2024-05-23 00:37:06 +00:00
|
|
|
import '../models/isar/models/encrypted_string_value.dart';
|
|
|
|
import '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,
|
|
|
|
});
|
2023-01-28 00:18:58 +00:00
|
|
|
|
|
|
|
Future<List<String>> get keys;
|
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",
|
2023-02-11 00:41:21 +00:00
|
|
|
maxSizeMiB: 512,
|
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
|
|
|
}
|
2023-01-28 00:18:58 +00:00
|
|
|
|
|
|
|
Future<List<String>> get keys async {
|
|
|
|
return await isar.encryptedStringValues.where().keyProperty().findAll();
|
|
|
|
}
|
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,
|
2024-05-27 23:56:22 +00:00
|
|
|
}) : assert(
|
|
|
|
isDesktop
|
|
|
|
? store is DesktopSecureStore
|
|
|
|
: store is FlutterSecureStorage,
|
|
|
|
),
|
2022-11-09 22:43:26 +00:00
|
|
|
_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,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-01-28 00:18:58 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
Future<List<String>> get keys async {
|
|
|
|
if (_isDesktop) {
|
|
|
|
return (_store as DesktopSecureStore).keys;
|
|
|
|
} else {
|
|
|
|
return (await (_store as FlutterSecureStorage).readAll()).keys.toList();
|
|
|
|
}
|
|
|
|
}
|
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();
|
2023-01-28 00:18:58 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
Future<List<String>> get keys => Future(() => _store.keys.toList());
|
2022-08-26 08:11:35 +00:00
|
|
|
}
|