mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 20:09:23 +00:00
dirty payment code obfuscation
This commit is contained in:
parent
61ad20e919
commit
3985674525
3 changed files with 83 additions and 10 deletions
|
@ -1302,6 +1302,7 @@ class BitcoinWallet extends CoinServiceAPI
|
||||||
coin: coin,
|
coin: coin,
|
||||||
db: db,
|
db: db,
|
||||||
electrumXClient: electrumXClient,
|
electrumXClient: electrumXClient,
|
||||||
|
secureStorage: secureStore,
|
||||||
getMnemonic: () => mnemonic,
|
getMnemonic: () => mnemonic,
|
||||||
getChainHeight: () => chainHeight,
|
getChainHeight: () => chainHeight,
|
||||||
getCurrentChangeAddress: () => currentChangeAddress,
|
getCurrentChangeAddress: () => currentChangeAddress,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:bip32/bip32.dart' as bip32;
|
import 'package:bip32/bip32.dart' as bip32;
|
||||||
|
@ -18,6 +19,7 @@ import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
|
||||||
import 'package:stackwallet/utilities/bip32_utils.dart';
|
import 'package:stackwallet/utilities/bip32_utils.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
@ -32,6 +34,7 @@ mixin PaynymWalletInterface {
|
||||||
late final Coin _coin;
|
late final Coin _coin;
|
||||||
late final MainDB _db;
|
late final MainDB _db;
|
||||||
late final ElectrumX _electrumXClient;
|
late final ElectrumX _electrumXClient;
|
||||||
|
late final SecureStorageInterface _secureStorage;
|
||||||
|
|
||||||
// passed in wallet functions
|
// passed in wallet functions
|
||||||
late final Future<List<String>> Function() _getMnemonic;
|
late final Future<List<String>> Function() _getMnemonic;
|
||||||
|
@ -75,6 +78,7 @@ mixin PaynymWalletInterface {
|
||||||
required Coin coin,
|
required Coin coin,
|
||||||
required MainDB db,
|
required MainDB db,
|
||||||
required ElectrumX electrumXClient,
|
required ElectrumX electrumXClient,
|
||||||
|
required SecureStorageInterface secureStorage,
|
||||||
required Future<List<String>> Function() getMnemonic,
|
required Future<List<String>> Function() getMnemonic,
|
||||||
required Future<int> Function() getChainHeight,
|
required Future<int> Function() getChainHeight,
|
||||||
required Future<String> Function() getCurrentChangeAddress,
|
required Future<String> Function() getCurrentChangeAddress,
|
||||||
|
@ -120,6 +124,7 @@ mixin PaynymWalletInterface {
|
||||||
_coin = coin;
|
_coin = coin;
|
||||||
_db = db;
|
_db = db;
|
||||||
_electrumXClient = electrumXClient;
|
_electrumXClient = electrumXClient;
|
||||||
|
_secureStorage = secureStorage;
|
||||||
_getMnemonic = getMnemonic;
|
_getMnemonic = getMnemonic;
|
||||||
_getChainHeight = getChainHeight;
|
_getChainHeight = getChainHeight;
|
||||||
_getCurrentChangeAddress = getCurrentChangeAddress;
|
_getCurrentChangeAddress = getCurrentChangeAddress;
|
||||||
|
@ -137,12 +142,15 @@ mixin PaynymWalletInterface {
|
||||||
btc_dart.NetworkType get networkType => _network;
|
btc_dart.NetworkType get networkType => _network;
|
||||||
|
|
||||||
Future<Address> currentReceivingPaynymAddress(PaymentCode sender) async {
|
Future<Address> currentReceivingPaynymAddress(PaymentCode sender) async {
|
||||||
|
final key = await lookupKey(sender.toString());
|
||||||
final address = await _db
|
final address = await _db
|
||||||
.getAddresses(_walletId)
|
.getAddresses(_walletId)
|
||||||
.filter()
|
.filter()
|
||||||
.subTypeEqualTo(AddressSubType.paynymReceive)
|
.subTypeEqualTo(AddressSubType.paynymReceive)
|
||||||
.and()
|
.and()
|
||||||
.otherDataEqualTo(sender.toString())
|
.otherDataEqualTo(key)
|
||||||
|
.and()
|
||||||
|
.otherDataIsNotNull()
|
||||||
.sortByDerivationIndexDesc()
|
.sortByDerivationIndexDesc()
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
|
||||||
|
@ -232,8 +240,9 @@ mixin PaynymWalletInterface {
|
||||||
DerivePathType derivePathType,
|
DerivePathType derivePathType,
|
||||||
) async {
|
) async {
|
||||||
final address = await getMyNotificationAddress(derivePathType);
|
final address = await getMyNotificationAddress(derivePathType);
|
||||||
|
final pCodeString = await paymentCodeStringByKey(address.otherData!);
|
||||||
final paymentCode = PaymentCode.fromPaymentCode(
|
final paymentCode = PaymentCode.fromPaymentCode(
|
||||||
address.otherData!,
|
pCodeString!,
|
||||||
_network,
|
_network,
|
||||||
);
|
);
|
||||||
return paymentCode;
|
return paymentCode;
|
||||||
|
@ -287,12 +296,15 @@ mixin PaynymWalletInterface {
|
||||||
const maxCount = 2147483647;
|
const maxCount = 2147483647;
|
||||||
|
|
||||||
for (int i = startIndex; i < maxCount; i++) {
|
for (int i = startIndex; i < maxCount; i++) {
|
||||||
|
final key = await lookupKey(pCode.toString());
|
||||||
final address = await _db
|
final address = await _db
|
||||||
.getAddresses(_walletId)
|
.getAddresses(_walletId)
|
||||||
.filter()
|
.filter()
|
||||||
.subTypeEqualTo(AddressSubType.paynymSend)
|
.subTypeEqualTo(AddressSubType.paynymSend)
|
||||||
.and()
|
.and()
|
||||||
.otherDataEqualTo(pCode.toString())
|
.otherDataEqualTo(key)
|
||||||
|
.and()
|
||||||
|
.otherDataIsNotNull()
|
||||||
.and()
|
.and()
|
||||||
.derivationIndexEqualTo(i)
|
.derivationIndexEqualTo(i)
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
@ -311,7 +323,7 @@ mixin PaynymWalletInterface {
|
||||||
).getSendAddressKeyPair();
|
).getSendAddressKeyPair();
|
||||||
|
|
||||||
// add address to local db
|
// add address to local db
|
||||||
final address = generatePaynymSendAddressFromKeyPair(
|
final address = await generatePaynymSendAddressFromKeyPair(
|
||||||
pair: pair,
|
pair: pair,
|
||||||
derivationIndex: i,
|
derivationIndex: i,
|
||||||
derivePathType: DerivePathType.bip44,
|
derivePathType: DerivePathType.bip44,
|
||||||
|
@ -770,7 +782,7 @@ mixin PaynymWalletInterface {
|
||||||
i, // index to use
|
i, // index to use
|
||||||
);
|
);
|
||||||
final pair = paymentAddressSending.getSendAddressKeyPair();
|
final pair = paymentAddressSending.getSendAddressKeyPair();
|
||||||
final address = generatePaynymSendAddressFromKeyPair(
|
final address = await generatePaynymSendAddressFromKeyPair(
|
||||||
pair: pair,
|
pair: pair,
|
||||||
derivationIndex: i,
|
derivationIndex: i,
|
||||||
derivePathType: DerivePathType.bip44,
|
derivePathType: DerivePathType.bip44,
|
||||||
|
@ -815,12 +827,12 @@ mixin PaynymWalletInterface {
|
||||||
await _db.updateOrPutAddresses(addresses);
|
await _db.updateOrPutAddresses(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
Address generatePaynymSendAddressFromKeyPair({
|
Future<Address> generatePaynymSendAddressFromKeyPair({
|
||||||
required btc_dart.ECPair pair,
|
required btc_dart.ECPair pair,
|
||||||
required int derivationIndex,
|
required int derivationIndex,
|
||||||
required DerivePathType derivePathType,
|
required DerivePathType derivePathType,
|
||||||
required PaymentCode toPaymentCode,
|
required PaymentCode toPaymentCode,
|
||||||
}) {
|
}) async {
|
||||||
final data = btc_dart.PaymentData(pubkey: pair.publicKey);
|
final data = btc_dart.PaymentData(pubkey: pair.publicKey);
|
||||||
|
|
||||||
String addressString;
|
String addressString;
|
||||||
|
@ -867,7 +879,7 @@ mixin PaynymWalletInterface {
|
||||||
derivationIndex: derivationIndex,
|
derivationIndex: derivationIndex,
|
||||||
type: AddressType.nonWallet,
|
type: AddressType.nonWallet,
|
||||||
subType: AddressSubType.paynymSend,
|
subType: AddressSubType.paynymSend,
|
||||||
otherData: toPaymentCode.toString(),
|
otherData: await storeCode(toPaymentCode.toString()),
|
||||||
);
|
);
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
|
@ -934,7 +946,7 @@ mixin PaynymWalletInterface {
|
||||||
derivationIndex: derivationIndex,
|
derivationIndex: derivationIndex,
|
||||||
type: addrType,
|
type: addrType,
|
||||||
subType: AddressSubType.paynymReceive,
|
subType: AddressSubType.paynymReceive,
|
||||||
otherData: fromPaymentCode.toString(),
|
otherData: await storeCode(fromPaymentCode.toString()),
|
||||||
);
|
);
|
||||||
|
|
||||||
final myCode = await getPaymentCode(DerivePathType.bip44);
|
final myCode = await getPaymentCode(DerivePathType.bip44);
|
||||||
|
@ -1053,7 +1065,7 @@ mixin PaynymWalletInterface {
|
||||||
derivationIndex: 0,
|
derivationIndex: 0,
|
||||||
type: type,
|
type: type,
|
||||||
subType: AddressSubType.paynymNotification,
|
subType: AddressSubType.paynymNotification,
|
||||||
otherData: paymentCode.toString(),
|
otherData: await storeCode(paymentCode.toString()),
|
||||||
);
|
);
|
||||||
|
|
||||||
await _addDerivation(
|
await _addDerivation(
|
||||||
|
@ -1068,4 +1080,46 @@ mixin PaynymWalletInterface {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// look up a key that corresponds to a payment code string
|
||||||
|
Future<String?> lookupKey(String paymentCodeString) async {
|
||||||
|
final keys =
|
||||||
|
(await _secureStorage.keys).where((e) => e.startsWith(kPCodeKeyPrefix));
|
||||||
|
for (final key in keys) {
|
||||||
|
final value = await _secureStorage.read(key: key);
|
||||||
|
if (value == paymentCodeString) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// fetch a payment code string
|
||||||
|
Future<String?> paymentCodeStringByKey(String key) async {
|
||||||
|
final value = await _secureStorage.read(key: key);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// store payment code string and return the generated key used
|
||||||
|
Future<String> storeCode(String paymentCodeString) async {
|
||||||
|
final key = _generateKey();
|
||||||
|
await _secureStorage.write(key: key, value: paymentCodeString);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generate a new payment code string storage key
|
||||||
|
String _generateKey() {
|
||||||
|
final bytes = _randomBytes(24);
|
||||||
|
return "$kPCodeKeyPrefix${bytes.toHex}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/AaronFeickert/stack_wallet_backup/blob/master/lib/secure_storage.dart#L307-L311
|
||||||
|
/// Generate cryptographically-secure random bytes
|
||||||
|
Uint8List _randomBytes(int n) {
|
||||||
|
final Random rng = Random.secure();
|
||||||
|
return Uint8List.fromList(
|
||||||
|
List<int>.generate(n, (_) => rng.nextInt(0xFF + 1)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const String kPCodeKeyPrefix = "pCode_key_";
|
||||||
|
|
|
@ -46,6 +46,8 @@ abstract class SecureStorageInterface {
|
||||||
MacOsOptions? mOptions,
|
MacOsOptions? mOptions,
|
||||||
WindowsOptions? wOptions,
|
WindowsOptions? wOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Future<List<String>> get keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DesktopSecureStore {
|
class DesktopSecureStore {
|
||||||
|
@ -110,6 +112,10 @@ class DesktopSecureStore {
|
||||||
await isar.encryptedStringValues.deleteByKey(key);
|
await isar.encryptedStringValues.deleteByKey(key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<String>> get keys async {
|
||||||
|
return await isar.encryptedStringValues.where().keyProperty().findAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// all *Options params ignored on desktop
|
/// all *Options params ignored on desktop
|
||||||
|
@ -229,6 +235,15 @@ class SecureStorageWrapper implements SecureStorageInterface {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> get keys async {
|
||||||
|
if (_isDesktop) {
|
||||||
|
return (_store as DesktopSecureStore).keys;
|
||||||
|
} else {
|
||||||
|
return (await (_store as FlutterSecureStorage).readAll()).keys.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock class for testing purposes
|
// Mock class for testing purposes
|
||||||
|
@ -305,4 +320,7 @@ class FakeSecureStorage implements SecureStorageInterface {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get store => throw UnimplementedError();
|
dynamic get store => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> get keys => Future(() => _store.keys.toList());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue