chore: add encrypted text column support

This commit is contained in:
Kingkor Roy Tirtho 2024-06-16 20:58:54 +06:00
parent bf6cec8d69
commit a799ca55bc
7 changed files with 149 additions and 1 deletions

View File

@ -27,6 +27,7 @@ import 'package:spotube/provider/palette_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/cli/cli.dart'; import 'package:spotube/services/cli/cli.dart';
import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
import 'package:spotube/services/kv_store/kv_store.dart'; import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/wm_tools/wm_tools.dart'; import 'package:spotube/services/wm_tools/wm_tools.dart';
@ -76,6 +77,7 @@ Future<void> main(List<String> rawArgs) async {
} }
await KVStoreService.initialize(); await KVStoreService.initialize();
await EncryptedKvStoreService.initialize();
final hiveCacheDir = final hiveCacheDir =
kIsWeb ? null : (await getApplicationSupportDirectory()).path; kIsWeb ? null : (await getApplicationSupportDirectory()).path;

View File

@ -4,11 +4,14 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:encrypt/encrypt.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
import 'package:flutter/material.dart' hide Table; import 'package:flutter/material.dart' hide Table, Key;
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart'; import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
import 'package:drift/native.dart'; import 'package:drift/native.dart';
import 'package:sqlite3/sqlite3.dart'; import 'package:sqlite3/sqlite3.dart';
@ -24,6 +27,7 @@ part 'tables/blacklist.dart';
part 'typeconverters/color.dart'; part 'typeconverters/color.dart';
part 'typeconverters/locale.dart'; part 'typeconverters/locale.dart';
part 'typeconverters/string_list.dart'; part 'typeconverters/string_list.dart';
part 'typeconverters/encrypted_text.dart';
@DriftDatabase( @DriftDatabase(
tables: [ tables: [

View File

@ -0,0 +1,39 @@
part of '../database.dart';
class DecryptedText {
final String value;
const DecryptedText(this.value);
static Encrypter? _encrypter;
factory DecryptedText.decrypted(String value) {
_encrypter ??= Encrypter(
Salsa20(
Key.fromUtf8(EncryptedKvStoreService.encryptionKeySync),
),
);
return DecryptedText(
_encrypter!.decrypt(
Encrypted.fromBase64(value),
iv: KVStoreService.ivKey,
),
);
}
String encrypt() {
return _encrypter!.encrypt(value, iv: KVStoreService.ivKey).base64;
}
}
class EncryptedTextConverter extends TypeConverter<DecryptedText, String> {
@override
DecryptedText fromSql(String fromDb) {
return DecryptedText.decrypted(fromDb);
}
@override
String toSql(DecryptedText value) {
return value.encrypt();
}
}

View File

@ -0,0 +1,43 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:uuid/uuid.dart';
abstract class EncryptedKvStoreService {
static const _storage = FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
),
);
static late final String _encryptionKeySync;
static Future<void> initialize() async {
_encryptionKeySync = await encryptionKey;
}
static String get encryptionKeySync => _encryptionKeySync;
static Future<String> get encryptionKey async {
try {
final value = await _storage.read(key: 'encryption');
final key = const Uuid().v4();
if (value == null) {
await setEncryptionKey(key);
return key;
}
return value;
} catch (e) {
return KVStoreService.encryptionKey;
}
}
static Future<void> setEncryptionKey(String key) async {
try {
await _storage.write(key: 'encryption', value: key);
} catch (e) {
await KVStoreService.setEncryptionKey(key);
}
}
}

View File

@ -1,7 +1,9 @@
import 'dart:convert'; import 'dart:convert';
import 'package:encrypt/encrypt.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotube/services/wm_tools/wm_tools.dart'; import 'package:spotube/services/wm_tools/wm_tools.dart';
import 'package:uuid/uuid.dart';
abstract class KVStoreService { abstract class KVStoreService {
static SharedPreferences? _sharedPreferences; static SharedPreferences? _sharedPreferences;
@ -43,4 +45,37 @@ abstract class KVStoreService {
value.toJson(), value.toJson(),
), ),
); );
static String get encryptionKey {
final value = sharedPreferences.getString('encryption');
final key = const Uuid().v4();
if (value == null) {
setEncryptionKey(key);
return key;
}
return value;
}
static Future<void> setEncryptionKey(String key) async {
await sharedPreferences.setString('encryption', key);
}
static IV get ivKey {
final iv = sharedPreferences.getString('iv');
final value = IV.fromSecureRandom(8);
if (iv == null) {
setIVKey(value);
return value;
}
return IV.fromBase64(iv);
}
static Future<void> setIVKey(IV iv) async {
await sharedPreferences.setString('iv', iv.base64);
}
} }

View File

@ -57,6 +57,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.0" version: "2.5.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda"
url: "https://pub.dev"
source: hosted
version: "1.5.3"
async: async:
dependency: "direct main" dependency: "direct main"
description: description:
@ -539,6 +547,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.13" version: "3.0.13"
encrypt:
dependency: "direct main"
description:
name: encrypt
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
url: "https://pub.dev"
source: hosted
version: "5.0.3"
envied: envied:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1670,6 +1686,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.dev"
source: hosted
version: "3.9.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:

View File

@ -124,6 +124,7 @@ dependencies:
drift: ^2.18.0 drift: ^2.18.0
sqlite3_flutter_libs: ^0.5.23 sqlite3_flutter_libs: ^0.5.23
sqlite3: ^2.4.3 sqlite3: ^2.4.3
encrypt: ^5.0.3
dev_dependencies: dev_dependencies:
build_runner: ^2.4.11 build_runner: ^2.4.11