From 6018cd383a6c8c551c44c32d47a88630eec006d2 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 7 Sep 2022 10:35:12 +0700 Subject: [PATCH] TF-886 Store `encryptionKey` in Hive database --- .../authentication_info_cache_client.dart | 4 +- lib/features/caching/caching_manager.dart | 5 + .../caching/config/hive_cache_client.dart | 7 + .../caching/config/hive_cache_config.dart | 41 +++--- .../caching/encryption_key_cache_client.dart | 124 ++++++++++++++++++ .../caching/utils/caching_constants.dart | 3 +- .../local/encryption_key_cache_manager.dart | 18 +++ .../data/model/encryption_key_cache.dart | 19 +++ .../login/data/utils/login_constant.dart | 1 - lib/main.dart | 1 + lib/main/bindings/local/local_bindings.dart | 7 +- 11 files changed, 209 insertions(+), 21 deletions(-) create mode 100644 lib/features/caching/encryption_key_cache_client.dart create mode 100644 lib/features/login/data/local/encryption_key_cache_manager.dart create mode 100644 lib/features/login/data/model/encryption_key_cache.dart diff --git a/lib/features/caching/authentication_info_cache_client.dart b/lib/features/caching/authentication_info_cache_client.dart index 51e110c15..b7335c4a7 100644 --- a/lib/features/caching/authentication_info_cache_client.dart +++ b/lib/features/caching/authentication_info_cache_client.dart @@ -1,6 +1,5 @@ import 'package:hive/hive.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart'; -import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; class AuthenticationInfoCacheClient extends HiveCacheClient { @@ -11,7 +10,6 @@ class AuthenticationInfoCacheClient extends HiveCacheClient clearAllData() { return Future.sync(() async { - await HiveCacheConfig.removeEncryptionKey(); final boxAuthenticationInfo = await openBox(); return boxAuthenticationInfo.clear(); }).catchError((error) { @@ -101,7 +99,7 @@ class AuthenticationInfoCacheClient extends HiveCacheClient> openBox() async { return Future.sync(() async { - final encryptionKey = await HiveCacheConfig.getEncryptionKey(); + final encryptionKey = await getEncryptionKey(); return Hive.openBox( tableName, encryptionCipher: HiveAesCipher(encryptionKey!)); diff --git a/lib/features/caching/caching_manager.dart b/lib/features/caching/caching_manager.dart index e2607e63d..aa7380136 100644 --- a/lib/features/caching/caching_manager.dart +++ b/lib/features/caching/caching_manager.dart @@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:tmail_ui_user/features/caching/account_cache_client.dart'; import 'package:tmail_ui_user/features/caching/authentication_info_cache_client.dart'; import 'package:tmail_ui_user/features/caching/email_cache_client.dart'; +import 'package:tmail_ui_user/features/caching/encryption_key_cache_client.dart'; import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart'; import 'package:tmail_ui_user/features/caching/recent_search_cache_client.dart'; import 'package:tmail_ui_user/features/caching/state_cache_client.dart'; @@ -16,6 +17,7 @@ class CachingManager { final RecentSearchCacheClient _recentSearchCacheClient; final TokenOidcCacheClient _tokenOidcCacheClient; final AccountCacheClient _accountCacheClient; + final EncryptionKeyCacheClient _encryptionKeyCacheClient; final AuthenticationInfoCacheClient _authenticationInfoCacheClient; CachingManager( @@ -25,6 +27,7 @@ class CachingManager { this._recentSearchCacheClient, this._tokenOidcCacheClient, this._accountCacheClient, + this._encryptionKeyCacheClient, this._authenticationInfoCacheClient, ); @@ -37,6 +40,7 @@ class CachingManager { _recentSearchCacheClient.clearAllData(), _tokenOidcCacheClient.clearAllData(), _accountCacheClient.clearAllData(), + _encryptionKeyCacheClient.clearAllData(), _authenticationInfoCacheClient.clearAllData(), ]); } else { @@ -47,6 +51,7 @@ class CachingManager { _recentSearchCacheClient.deleteBox(), _tokenOidcCacheClient.deleteBox(), _accountCacheClient.deleteBox(), + _encryptionKeyCacheClient.deleteBox(), _authenticationInfoCacheClient.deleteBox(), ]); } diff --git a/lib/features/caching/config/hive_cache_client.dart b/lib/features/caching/config/hive_cache_client.dart index f21399cd6..1cc2fba64 100644 --- a/lib/features/caching/config/hive_cache_client.dart +++ b/lib/features/caching/config/hive_cache_client.dart @@ -1,5 +1,8 @@ +import 'dart:typed_data'; + import 'package:hive/hive.dart'; +import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; abstract class HiveCacheClient { @@ -32,4 +35,8 @@ abstract class HiveCacheClient { } Future clearAllData(); + + Future getEncryptionKey() { + return HiveCacheConfig.getEncryptionKey(); + } } \ No newline at end of file diff --git a/lib/features/caching/config/hive_cache_config.dart b/lib/features/caching/config/hive_cache_config.dart index 8768a6d5e..dfb498bbf 100644 --- a/lib/features/caching/config/hive_cache_config.dart +++ b/lib/features/caching/config/hive_cache_config.dart @@ -6,11 +6,11 @@ import 'package:core/utils/app_logger.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart' as path_provider; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; +import 'package:tmail_ui_user/features/login/data/model/encryption_key_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; -import 'package:tmail_ui_user/features/login/data/utils/login_constant.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_cache.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_rights_cache.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/state_cache.dart'; @@ -37,22 +37,32 @@ class HiveCacheConfig { } } - static Future getEncryptionKey() async { - final sharedPreference = Get.find(); - final containsEncryptionKey = sharedPreference.containsKey(LoginConstant.keyHiveEncrypt); - if (!containsEncryptionKey) { - final key = Hive.generateSecureKey(); - await sharedPreference.setString(LoginConstant.keyHiveEncrypt, base64UrlEncode(key)); + static Future initializeEncryptionKey() async { + final encryptionKeyCacheManager = Get.find(); + final encryptionKeyCache = await encryptionKeyCacheManager.getEncryptionKeyStored(); + if (encryptionKeyCache == null) { + final secureKey = Hive.generateSecureKey(); + final secureKeyEncode = base64UrlEncode(secureKey); + log('HiveCacheConfig::_initializeEncryptionKey(): secureKeyEncode: $secureKeyEncode'); + await encryptionKeyCacheManager.storeEncryptionKey(EncryptionKeyCache(secureKeyEncode)); } - final keyStored = sharedPreference.getString(LoginConstant.keyHiveEncrypt) ?? ''; - final encryptionKey = base64Url.decode(keyStored); - log('HiveCacheConfig::getEncryptionKey(): Encryption key: $encryptionKey'); - return encryptionKey; } - static Future removeEncryptionKey() { - final sharedPreference = Get.find(); - return sharedPreference.remove(LoginConstant.keyHiveEncrypt); + static Future getEncryptionKey() async { + final encryptionKeyCacheManager = Get.find(); + var encryptionKeyCache = await encryptionKeyCacheManager.getEncryptionKeyStored(); + if (encryptionKeyCache == null) { + await initializeEncryptionKey(); + encryptionKeyCache = await encryptionKeyCacheManager.getEncryptionKeyStored(); + } + + if (encryptionKeyCache != null) { + log('HiveCacheConfig::getEncryptionKey(): encryptionKey: ${encryptionKeyCache.value}'); + final encryptionKey = base64Url.decode(encryptionKeyCache.value); + return encryptionKey; + } else { + return null; + } } void registerAdapter() { @@ -65,6 +75,7 @@ class HiveCacheConfig { Hive.registerAdapter(RecentSearchCacheAdapter()); Hive.registerAdapter(TokenOidcCacheAdapter()); Hive.registerAdapter(AccountCacheAdapter()); + Hive.registerAdapter(EncryptionKeyCacheAdapter()); Hive.registerAdapter(AuthenticationInfoCacheAdapter()); } diff --git a/lib/features/caching/encryption_key_cache_client.dart b/lib/features/caching/encryption_key_cache_client.dart new file mode 100644 index 000000000..42a6d227e --- /dev/null +++ b/lib/features/caching/encryption_key_cache_client.dart @@ -0,0 +1,124 @@ +import 'package:hive/hive.dart'; +import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart'; +import 'package:tmail_ui_user/features/login/data/model/encryption_key_cache.dart'; + +class EncryptionKeyCacheClient extends HiveCacheClient { + + @override + String get tableName => "EncryptionKeyCache"; + + @override + Future clearAllData() { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.clear(); + }).catchError((error) { + throw error; + }); + } + + @override + Future deleteItem(String key) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.delete(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future deleteMultipleItem(List listKey) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.deleteAll(listKey); + }).catchError((error) { + throw error; + }); + } + + @override + Future> getAll() { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.values.toList(); + }).catchError((error) { + throw error; + }); + } + + @override + Future getItem(String key) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.get(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future insertItem(String key, EncryptionKeyCache newObject) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.put(key, newObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future insertMultipleItem(Map mapObject) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.putAll(mapObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future isExistItem(String key) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.containsKey(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future isExistTable() { + return Future.sync(() async { + return Hive.boxExists(tableName); + }).catchError((error) { + throw error; + }); + } + + @override + Future> openBox() async { + return Hive.openBox(tableName); + } + + @override + Future updateItem(String key, EncryptionKeyCache newObject) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.put(key, newObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future updateMultipleItem(Map mapObject) { + return Future.sync(() async { + final boxEncryptionKey = await openBox(); + return boxEncryptionKey.putAll(mapObject); + }).catchError((error) { + throw error; + }); + } + +} \ No newline at end of file diff --git a/lib/features/caching/utils/caching_constants.dart b/lib/features/caching/utils/caching_constants.dart index 3e724706b..785b9766a 100644 --- a/lib/features/caching/utils/caching_constants.dart +++ b/lib/features/caching/utils/caching_constants.dart @@ -9,5 +9,6 @@ class CachingConstants { static const int RECENT_SEARCH_HIVE_CACHE_IDENTIFY = 7; static const int TOKEN_OIDC_HIVE_CACHE_IDENTIFY = 8; static const int ACCOUNT_HIVE_CACHE_IDENTIFY = 9; - static const int AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY = 10; + static const int ENCRYPTION_KEY_HIVE_CACHE_IDENTIFY = 10; + static const int AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY = 11; } \ No newline at end of file diff --git a/lib/features/login/data/local/encryption_key_cache_manager.dart b/lib/features/login/data/local/encryption_key_cache_manager.dart new file mode 100644 index 000000000..2a89b6b19 --- /dev/null +++ b/lib/features/login/data/local/encryption_key_cache_manager.dart @@ -0,0 +1,18 @@ +import 'package:tmail_ui_user/features/caching/encryption_key_cache_client.dart'; +import 'package:tmail_ui_user/features/login/data/model/encryption_key_cache.dart'; + +class EncryptionKeyCacheManager { + final EncryptionKeyCacheClient _encryptionKeyCacheClient; + + EncryptionKeyCacheManager(this._encryptionKeyCacheClient); + + Future storeEncryptionKey(EncryptionKeyCache encryptionKeyCache) { + return _encryptionKeyCacheClient.insertItem( + EncryptionKeyCache.keyCacheValue, + encryptionKeyCache); + } + + Future getEncryptionKeyStored() { + return _encryptionKeyCacheClient.getItem(EncryptionKeyCache.keyCacheValue); + } +} \ No newline at end of file diff --git a/lib/features/login/data/model/encryption_key_cache.dart b/lib/features/login/data/model/encryption_key_cache.dart new file mode 100644 index 000000000..2d3752ac1 --- /dev/null +++ b/lib/features/login/data/model/encryption_key_cache.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; +import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; + +part 'encryption_key_cache.g.dart'; + +@HiveType(typeId: CachingConstants.ENCRYPTION_KEY_HIVE_CACHE_IDENTIFY) +class EncryptionKeyCache extends HiveObject with EquatableMixin { + + static const String keyCacheValue = 'hiveEncryptionKey'; + + @HiveField(0) + final String value; + + EncryptionKeyCache(this.value); + + @override + List get props => [value]; +} \ No newline at end of file diff --git a/lib/features/login/data/utils/login_constant.dart b/lib/features/login/data/utils/login_constant.dart index 27e6268df..1a836e8e3 100644 --- a/lib/features/login/data/utils/login_constant.dart +++ b/lib/features/login/data/utils/login_constant.dart @@ -1,5 +1,4 @@ class LoginConstant { static const String keyBaseUrl = 'KEY_BASE_URL'; - static const String keyHiveEncrypt = 'KEY_HIVE_ENCRYPT'; } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 4f1101248..653b899a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,6 +24,7 @@ void main() async { )); await MainBindings().dependencies(); await HiveCacheConfig().setUp(); + await HiveCacheConfig.initializeEncryptionKey(); await Executor().warmUp(); await dotenv.load(fileName: 'env.file'); runApp(const TMailApp()); diff --git a/lib/main/bindings/local/local_bindings.dart b/lib/main/bindings/local/local_bindings.dart index 8903ad095..c436f4d8c 100644 --- a/lib/main/bindings/local/local_bindings.dart +++ b/lib/main/bindings/local/local_bindings.dart @@ -6,6 +6,7 @@ import 'package:tmail_ui_user/features/caching/account_cache_client.dart'; import 'package:tmail_ui_user/features/caching/authentication_info_cache_client.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/caching/email_cache_client.dart'; +import 'package:tmail_ui_user/features/caching/encryption_key_cache_client.dart'; import 'package:tmail_ui_user/features/caching/mailbox_cache_client.dart'; import 'package:tmail_ui_user/features/caching/recent_search_cache_client.dart'; import 'package:tmail_ui_user/features/caching/state_cache_client.dart'; @@ -13,6 +14,7 @@ import 'package:tmail_ui_user/features/caching/token_oidc_cache_client.dart'; import 'package:tmail_ui_user/features/cleanup/data/local/recent_search_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart'; +import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; @@ -42,8 +44,10 @@ class LocalBindings extends Bindings { Get.put(TokenOidcCacheClient()); Get.put(TokenOidcCacheManager(Get.find())); Get.put(AccountCacheClient()); - Get.put(AuthenticationInfoCacheClient()); Get.put(AccountCacheManager(Get.find())); + Get.put(EncryptionKeyCacheClient()); + Get.put(EncryptionKeyCacheManager(Get.find())); + Get.put(AuthenticationInfoCacheClient()); Get.put(AuthenticationInfoCacheManager(Get.find())); Get.put(OidcConfigurationCacheManager(Get.find())); Get.put(LanguageCacheManager(Get.find())); @@ -54,6 +58,7 @@ class LocalBindings extends Bindings { Get.find(), Get.find(), Get.find(), + Get.find(), Get.find(), )); }