stack_wallet/lib/utilities/desktop_password_service.dart

224 lines
5.9 KiB
Dart
Raw Normal View History

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
*
*/
2024-06-10 22:50:26 +00:00
import 'package:hive/hive.dart' show Box;
2022-11-04 19:32:02 +00:00
import 'package:stack_wallet_backup/secure_storage.dart';
2024-05-27 23:56:22 +00:00
2024-06-10 22:50:26 +00:00
import '../db/hive/db.dart';
import 'logger.dart';
2022-11-04 19:32:02 +00:00
const String kBoxNameDesktopData = "desktopData";
2022-11-04 19:32:02 +00:00
const String _kKeyBlobKey = "swbKeyBlobKeyStringID";
const String _kKeyBlobVersionKey = "swbKeyBlobVersionKeyStringID";
const int kLatestBlobVersion = 2;
2022-11-04 19:32:02 +00:00
String _getMessageFromException(Object exception) {
if (exception is IncorrectPassphraseOrVersion) {
2022-11-04 19:32:02 +00:00
return exception.errMsg();
}
if (exception is BadDecryption) {
return exception.errMsg();
}
if (exception is InvalidLength) {
return exception.errMsg();
}
if (exception is EncodingError) {
return exception.errMsg();
}
if (exception is VersionError) {
return exception.errMsg();
}
2022-11-04 19:32:02 +00:00
return exception.toString();
}
class DPS {
StorageCryptoHandler? _handler;
StorageCryptoHandler get handler {
if (_handler == null) {
throw Exception(
2024-05-27 23:56:22 +00:00
"DPS: attempted to access handler without proper authentication",
);
2022-11-04 19:32:02 +00:00
}
return _handler!;
}
2022-11-09 22:43:26 +00:00
DPS();
2022-11-04 19:32:02 +00:00
Future<void> initFromNew(String passphrase) async {
if (_handler != null) {
throw Exception("DPS: attempted to re initialize with new passphrase");
}
try {
_handler = await StorageCryptoHandler.fromNewPassphrase(
passphrase,
kLatestBlobVersion,
);
2022-11-09 22:43:26 +00:00
await _put(key: _kKeyBlobKey, value: await _handler!.getKeyBlob());
await _updateStoredKeyBlobVersion(kLatestBlobVersion);
2022-11-04 19:32:02 +00:00
} catch (e, s) {
Logging.instance.log(
"${_getMessageFromException(e)}\n$s",
level: LogLevel.Error,
);
rethrow;
}
}
Future<void> initFromExisting(String passphrase) async {
if (_handler != null) {
throw Exception(
2024-05-27 23:56:22 +00:00
"DPS: attempted to re initialize with existing passphrase",
);
2022-11-04 19:32:02 +00:00
}
2022-11-09 22:43:26 +00:00
2022-11-04 19:32:02 +00:00
try {
final keyBlob = await _get(key: _kKeyBlobKey);
if (keyBlob == null) {
throw Exception(
2024-05-27 23:56:22 +00:00
"DPS: failed to find keyBlob while attempting to initialize with existing passphrase",
);
}
final blobVersion = await _getStoredKeyBlobVersion();
_handler = await StorageCryptoHandler.fromExisting(
passphrase,
keyBlob,
blobVersion,
);
if (blobVersion < kLatestBlobVersion) {
// update blob
await _handler!.resetPassphrase(passphrase, kLatestBlobVersion);
await _put(key: _kKeyBlobKey, value: await _handler!.getKeyBlob());
await _updateStoredKeyBlobVersion(kLatestBlobVersion);
}
2022-11-04 19:32:02 +00:00
} catch (e, s) {
Logging.instance.log(
"${_getMessageFromException(e)}\n$s",
level: LogLevel.Error,
);
throw Exception(_getMessageFromException(e));
2022-11-04 19:32:02 +00:00
}
}
Future<bool> verifyPassphrase(String passphrase) async {
try {
final keyBlob = await _get(key: _kKeyBlobKey);
if (keyBlob == null) {
// no passphrase key blob found so any passphrase is technically bad
return false;
}
final blobVersion = await _getStoredKeyBlobVersion();
await StorageCryptoHandler.fromExisting(passphrase, keyBlob, blobVersion);
// existing passphrase matches key blob
return true;
} catch (e, s) {
Logging.instance.log(
"${_getMessageFromException(e)}\n$s",
level: LogLevel.Warning,
);
// password is wrong or some other error
return false;
}
}
Future<bool> changePassphrase(
String passphraseOld,
String passphraseNew,
) async {
try {
final keyBlob = await _get(key: _kKeyBlobKey);
if (keyBlob == null) {
// no passphrase key blob found so any passphrase is technically bad
return false;
}
if (!(await verifyPassphrase(passphraseOld))) {
return false;
}
final blobVersion = await _getStoredKeyBlobVersion();
await _handler!.resetPassphrase(passphraseNew, blobVersion);
await _put(
key: _kKeyBlobKey,
value: await _handler!.getKeyBlob(),
);
await _updateStoredKeyBlobVersion(blobVersion);
// successfully updated passphrase
return true;
} catch (e, s) {
Logging.instance.log(
"${_getMessageFromException(e)}\n$s",
level: LogLevel.Warning,
);
return false;
}
}
2022-11-04 19:32:02 +00:00
Future<bool> hasPassword() async {
final keyBlob = await _get(key: _kKeyBlobKey);
2022-11-09 22:43:26 +00:00
return keyBlob != null;
2022-11-04 19:32:02 +00:00
}
Future<int> _getStoredKeyBlobVersion() async {
final keyBlobVersionString = await _get(key: _kKeyBlobVersionKey);
return int.tryParse(keyBlobVersionString ?? "1") ?? 1;
}
Future<void> _updateStoredKeyBlobVersion(int version) async {
await _put(key: _kKeyBlobVersionKey, value: version.toString());
}
Future<void> _put({required String key, required String value}) async {
Box<String>? box;
try {
2024-06-10 22:50:26 +00:00
box = await DB.instance.hive.openBox<String>(kBoxNameDesktopData);
await box.put(key, value);
} catch (e, s) {
Logging.instance.log(
"DPS failed put($key): $e\n$s",
level: LogLevel.Fatal,
);
} finally {
await box?.close();
}
}
Future<String?> _get({required String key}) async {
String? value;
Box<String>? box;
try {
2024-06-10 22:50:26 +00:00
box = await DB.instance.hive.openBox<String>(kBoxNameDesktopData);
value = box.get(key);
} catch (e, s) {
Logging.instance.log(
"DPS failed get($key): $e\n$s",
level: LogLevel.Fatal,
);
} finally {
await box?.close();
}
return value;
}
2023-07-03 21:12:24 +00:00
/// Dangerous. Used in one place and should not be called anywhere else.
@Deprecated("Don't use this if at all possible")
Future<void> deleteBox() async {
2024-06-10 22:50:26 +00:00
await DB.instance.hive.deleteBoxFromDisk(kBoxNameDesktopData);
2023-07-03 21:12:24 +00:00
}
2022-11-04 19:32:02 +00:00
}