mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-22 18:44:31 +00:00
fix formatting
This commit is contained in:
parent
9b4c1abf35
commit
aa2ab89f73
11 changed files with 785 additions and 923 deletions
|
@ -1,12 +1,12 @@
|
||||||
|
|
||||||
import 'util.dart';
|
|
||||||
import 'connection.dart';
|
|
||||||
import 'fusion.dart';
|
|
||||||
import 'fusion.pb.dart';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:protobuf/protobuf.dart';
|
import 'package:protobuf/protobuf.dart';
|
||||||
import 'socketwrapper.dart';
|
import 'package:stackwallet/services/cashfusion/connection.dart';
|
||||||
|
import 'package:stackwallet/services/cashfusion/fusion.dart';
|
||||||
|
import 'package:stackwallet/services/cashfusion/fusion.pb.dart';
|
||||||
|
import 'package:stackwallet/services/cashfusion/socketwrapper.dart';
|
||||||
|
import 'package:stackwallet/services/cashfusion/util.dart';
|
||||||
|
|
||||||
typedef PbCreateFunc = GeneratedMessage Function();
|
typedef PbCreateFunc = GeneratedMessage Function();
|
||||||
|
|
||||||
|
@ -43,13 +43,11 @@ Map<Type, PbCreateFunc> pbClassCreators = {
|
||||||
ServerMessage: () => ServerMessage(),
|
ServerMessage: () => ServerMessage(),
|
||||||
CovertMessage: () => CovertMessage(),
|
CovertMessage: () => CovertMessage(),
|
||||||
CovertResponse: () => CovertResponse()
|
CovertResponse: () => CovertResponse()
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Future<void> sendPb(
|
||||||
|
Connection connection, Type pbClass, GeneratedMessage subMsg,
|
||||||
Future<void> sendPb(Connection connection, Type pbClass, GeneratedMessage subMsg, {Duration? timeout}) async {
|
{Duration? timeout}) async {
|
||||||
|
|
||||||
// Construct the outer message with the submessage.
|
// Construct the outer message with the submessage.
|
||||||
|
|
||||||
if (pbClassCreators[pbClass] == null) {
|
if (pbClassCreators[pbClass] == null) {
|
||||||
|
@ -70,11 +68,9 @@ Future<void> sendPb(Connection connection, Type pbClass, GeneratedMessage subMsg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection,
|
||||||
|
Type pbClass, GeneratedMessage subMsg,
|
||||||
Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection, Type pbClass, GeneratedMessage subMsg, {Duration? timeout}) async {
|
{Duration? timeout}) async {
|
||||||
|
|
||||||
|
|
||||||
// Construct the outer message with the submessage.
|
// Construct the outer message with the submessage.
|
||||||
|
|
||||||
if (pbClassCreators[pbClass] == null) {
|
if (pbClassCreators[pbClass] == null) {
|
||||||
|
@ -85,7 +81,8 @@ Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection, Type pb
|
||||||
var pbMessage = pbClassCreators[pbClass]!()..mergeFromMessage(subMsg);
|
var pbMessage = pbClassCreators[pbClass]!()..mergeFromMessage(subMsg);
|
||||||
final msgBytes = pbMessage.writeToBuffer();
|
final msgBytes = pbMessage.writeToBuffer();
|
||||||
try {
|
try {
|
||||||
await connection.sendMessageWithSocketWrapper(socketwrapper, msgBytes, timeout: timeout);
|
await connection.sendMessageWithSocketWrapper(socketwrapper, msgBytes,
|
||||||
|
timeout: timeout);
|
||||||
} on SocketException {
|
} on SocketException {
|
||||||
throw FusionError('Connection closed by remote');
|
throw FusionError('Connection closed by remote');
|
||||||
} on TimeoutException {
|
} on TimeoutException {
|
||||||
|
@ -95,13 +92,12 @@ Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection, Type pb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper,
|
||||||
|
Connection connection, Type pbClass, List<String> expectedFieldNames,
|
||||||
|
{Duration? timeout}) async {
|
||||||
Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper, Connection connection, Type pbClass, List<String> expectedFieldNames, {Duration? timeout}) async {
|
|
||||||
try {
|
try {
|
||||||
|
List<int> blob =
|
||||||
List<int> blob = await connection.recv_message2(socketwrapper, timeout: timeout);
|
await connection.recv_message2(socketwrapper, timeout: timeout);
|
||||||
|
|
||||||
var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob);
|
var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob);
|
||||||
|
|
||||||
|
@ -121,8 +117,8 @@ Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper, Con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw FusionError('None of the expected fields found in the received message');
|
throw FusionError(
|
||||||
|
'None of the expected fields found in the received message');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Handle different exceptions here
|
// Handle different exceptions here
|
||||||
if (e is SocketException) {
|
if (e is SocketException) {
|
||||||
|
@ -134,15 +130,16 @@ Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper, Con
|
||||||
} else if (e is OSError && e.errorCode == 9) {
|
} else if (e is OSError && e.errorCode == 9) {
|
||||||
throw FusionError('Connection closed by local');
|
throw FusionError('Connection closed by local');
|
||||||
} else {
|
} else {
|
||||||
throw FusionError('Communications error: ${e.runtimeType}: ${e.toString()}');
|
throw FusionError(
|
||||||
|
'Communications error: ${e.runtimeType}: ${e.toString()}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Tuple<GeneratedMessage, String>> recvPb(
|
||||||
Future<Tuple<GeneratedMessage, String>> recvPb(Connection connection, Type pbClass, List<String> expectedFieldNames, {Duration? timeout}) async {
|
Connection connection, Type pbClass, List<String> expectedFieldNames,
|
||||||
|
{Duration? timeout}) async {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
List<int> blob = await connection.recv_message(timeout: timeout);
|
List<int> blob = await connection.recv_message(timeout: timeout);
|
||||||
|
|
||||||
var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob);
|
var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob);
|
||||||
|
@ -163,8 +160,8 @@ Future<Tuple<GeneratedMessage, String>> recvPb(Connection connection, Type pbCla
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw FusionError('None of the expected fields found in the received message');
|
throw FusionError(
|
||||||
|
'None of the expected fields found in the received message');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Handle different exceptions here
|
// Handle different exceptions here
|
||||||
if (e is SocketException) {
|
if (e is SocketException) {
|
||||||
|
@ -176,8 +173,8 @@ Future<Tuple<GeneratedMessage, String>> recvPb(Connection connection, Type pbCla
|
||||||
} else if (e is OSError && e.errorCode == 9) {
|
} else if (e is OSError && e.errorCode == 9) {
|
||||||
throw FusionError('Connection closed by local');
|
throw FusionError('Connection closed by local');
|
||||||
} else {
|
} else {
|
||||||
throw FusionError('Communications error: ${e.runtimeType}: ${e.toString()}');
|
throw FusionError(
|
||||||
|
'Communications error: ${e.runtimeType}: ${e.toString()}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import 'socketwrapper.dart';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:convert/convert.dart';
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:stackwallet/services/cashfusion/socketwrapper.dart';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This file might need some fixing up because each time we call fillBuf, we're trying to
|
This file might need some fixing up because each time we call fillBuf, we're trying to
|
||||||
|
@ -22,14 +22,14 @@ class BadFrameError extends Error {
|
||||||
String toString() => message;
|
String toString() => message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Connection> openConnection(
|
||||||
|
String host,
|
||||||
Future<Connection> openConnection(String host, int port,
|
int port, {
|
||||||
{double connTimeout = 5.0,
|
double connTimeout = 5.0,
|
||||||
double defaultTimeout = 5.0,
|
double defaultTimeout = 5.0,
|
||||||
bool ssl = false,
|
bool ssl = false,
|
||||||
dynamic socksOpts}) async {
|
dynamic socksOpts,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
// Dart's Socket class handles connection timeout internally.
|
// Dart's Socket class handles connection timeout internally.
|
||||||
Socket socket = await Socket.connect(host, port);
|
Socket socket = await Socket.connect(host, port);
|
||||||
|
@ -38,25 +38,27 @@ Future<Connection> openConnection(String host, int port,
|
||||||
socket = await SecureSocket.secure(socket);
|
socket = await SecureSocket.secure(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Connection(socket: socket, timeout: Duration(seconds: defaultTimeout.toInt()));
|
return Connection(
|
||||||
|
socket: socket, timeout: Duration(seconds: defaultTimeout.toInt()));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw 'Failed to open connection: $e';
|
throw 'Failed to open connection: $e';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Connection {
|
class Connection {
|
||||||
Duration timeout = Duration(seconds: 1);
|
Duration timeout = Duration(seconds: 1);
|
||||||
Socket? socket;
|
Socket? socket;
|
||||||
|
|
||||||
static const int MAX_MSG_LENGTH = 200 * 1024;
|
static const int MAX_MSG_LENGTH = 200 * 1024;
|
||||||
static final Uint8List magic = Uint8List.fromList([0x76, 0x5b, 0xe8, 0xb4, 0xe4, 0x39, 0x6d, 0xcf]);
|
static final Uint8List magic =
|
||||||
|
Uint8List.fromList([0x76, 0x5b, 0xe8, 0xb4, 0xe4, 0x39, 0x6d, 0xcf]);
|
||||||
final Uint8List recvbuf = Uint8List(0);
|
final Uint8List recvbuf = Uint8List(0);
|
||||||
Connection({required this.socket, this.timeout = const Duration(seconds: 1)});
|
Connection({required this.socket, this.timeout = const Duration(seconds: 1)});
|
||||||
Connection.withoutSocket({this.timeout = const Duration(seconds: 1)});
|
Connection.withoutSocket({this.timeout = const Duration(seconds: 1)});
|
||||||
|
|
||||||
Future<void> sendMessageWithSocketWrapper(SocketWrapper socketwrapper, List<int> msg, {Duration? timeout}) async {
|
Future<void> sendMessageWithSocketWrapper(
|
||||||
|
SocketWrapper socketwrapper, List<int> msg,
|
||||||
|
{Duration? timeout}) async {
|
||||||
timeout ??= this.timeout;
|
timeout ??= this.timeout;
|
||||||
print("DEBUG sendmessage msg sending ");
|
print("DEBUG sendmessage msg sending ");
|
||||||
print(msg);
|
print(msg);
|
||||||
|
@ -76,10 +78,7 @@ class Connection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<void> sendMessage(List<int> msg, {Duration? timeout}) async {
|
Future<void> sendMessage(List<int> msg, {Duration? timeout}) async {
|
||||||
|
|
||||||
timeout ??= this.timeout;
|
timeout ??= this.timeout;
|
||||||
|
|
||||||
final lengthBytes = Uint8List(4);
|
final lengthBytes = Uint8List(4);
|
||||||
|
@ -93,7 +92,6 @@ class Connection {
|
||||||
..addAll(msg);
|
..addAll(msg);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
StreamController<List<int>> controller = StreamController();
|
StreamController<List<int>> controller = StreamController();
|
||||||
|
|
||||||
controller.stream.listen((data) {
|
controller.stream.listen((data) {
|
||||||
|
@ -109,19 +107,18 @@ class Connection {
|
||||||
} finally {
|
} finally {
|
||||||
controller.close();
|
controller.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
} on SocketException catch (e) {
|
} on SocketException catch (e) {
|
||||||
throw TimeoutException('Socket write timed out', timeout);
|
throw TimeoutException('Socket write timed out', timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
socket?.close();
|
socket?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<int>> fillBuf2(SocketWrapper socketwrapper, List<int> recvBuf, int n, {Duration? timeout}) async {
|
Future<List<int>> fillBuf2(
|
||||||
|
SocketWrapper socketwrapper, List<int> recvBuf, int n,
|
||||||
|
{Duration? timeout}) async {
|
||||||
final maxTime = timeout != null ? DateTime.now().add(timeout) : null;
|
final maxTime = timeout != null ? DateTime.now().add(timeout) : null;
|
||||||
|
|
||||||
await for (var data in socketwrapper.socket!.cast<List<int>>()) {
|
await for (var data in socketwrapper.socket!.cast<List<int>>()) {
|
||||||
|
@ -139,7 +136,8 @@ class Connection {
|
||||||
}
|
}
|
||||||
|
|
||||||
recvBuf.addAll(data);
|
recvBuf.addAll(data);
|
||||||
print("DEBUG fillBuf2 2 - data added to recvBuf, new length: ${recvBuf.length}");
|
print(
|
||||||
|
"DEBUG fillBuf2 2 - data added to recvBuf, new length: ${recvBuf.length}");
|
||||||
|
|
||||||
if (recvBuf.length >= n) {
|
if (recvBuf.length >= n) {
|
||||||
print("DEBUG fillBuf2 3 - breaking loop, recvBuf is big enough");
|
print("DEBUG fillBuf2 3 - breaking loop, recvBuf is big enough");
|
||||||
|
@ -163,8 +161,6 @@ class Connection {
|
||||||
});
|
});
|
||||||
return recvBuf;
|
return recvBuf;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
StreamSubscription<List<int>>? subscription; // Declaration moved here
|
StreamSubscription<List<int>>? subscription; // Declaration moved here
|
||||||
subscription = socket!.listen(
|
subscription = socket!.listen(
|
||||||
(List<int> data) {
|
(List<int> data) {
|
||||||
|
@ -180,7 +176,8 @@ class Connection {
|
||||||
onDone: () {
|
onDone: () {
|
||||||
print("DEBUG ON DONE");
|
print("DEBUG ON DONE");
|
||||||
if (recvBuf.length < n) {
|
if (recvBuf.length < n) {
|
||||||
throw SocketException('Connection closed before enough data was received');
|
throw SocketException(
|
||||||
|
'Connection closed before enough data was received');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -197,8 +194,8 @@ class Connection {
|
||||||
return recvBuf;
|
return recvBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<int>> recv_message2(SocketWrapper socketwrapper,
|
||||||
Future<List<int>> recv_message2(SocketWrapper socketwrapper, {Duration? timeout}) async {
|
{Duration? timeout}) async {
|
||||||
if (timeout == null) {
|
if (timeout == null) {
|
||||||
timeout = this.timeout;
|
timeout = this.timeout;
|
||||||
}
|
}
|
||||||
|
@ -237,14 +234,17 @@ class Connection {
|
||||||
throw BadFrameError('Bad magic in frame: ${hex.encode(magic)}');
|
throw BadFrameError('Bad magic in frame: ${hex.encode(magic)}');
|
||||||
}
|
}
|
||||||
|
|
||||||
final byteData = ByteData.view(Uint8List.fromList(recvBuf.sublist(8, 12)).buffer);
|
final byteData =
|
||||||
|
ByteData.view(Uint8List.fromList(recvBuf.sublist(8, 12)).buffer);
|
||||||
final messageLength = byteData.getUint32(0, Endian.big);
|
final messageLength = byteData.getUint32(0, Endian.big);
|
||||||
|
|
||||||
if (messageLength > MAX_MSG_LENGTH) {
|
if (messageLength > MAX_MSG_LENGTH) {
|
||||||
throw BadFrameError('Got a frame with msg_length=$messageLength > $MAX_MSG_LENGTH (max)');
|
throw BadFrameError(
|
||||||
|
'Got a frame with msg_length=$messageLength > $MAX_MSG_LENGTH (max)');
|
||||||
}
|
}
|
||||||
|
|
||||||
print("DEBUG recv_message2 3 - about to read the message body, messageLength: $messageLength");
|
print(
|
||||||
|
"DEBUG recv_message2 3 - about to read the message body, messageLength: $messageLength");
|
||||||
|
|
||||||
print("DEBUG recvfbuf len is ");
|
print("DEBUG recvfbuf len is ");
|
||||||
print(recvBuf.length);
|
print(recvBuf.length);
|
||||||
|
@ -255,13 +255,15 @@ class Connection {
|
||||||
if (recvBuf.length == bytesRead && bytesRead == 12 + messageLength) {
|
if (recvBuf.length == bytesRead && bytesRead == 12 + messageLength) {
|
||||||
final message = recvBuf.sublist(12, 12 + messageLength);
|
final message = recvBuf.sublist(12, 12 + messageLength);
|
||||||
|
|
||||||
print("DEBUG recv_message2 4 - message received, length: ${message.length}");
|
print(
|
||||||
|
"DEBUG recv_message2 4 - message received, length: ${message.length}");
|
||||||
print("DEBUG recv_message2 5 - message content: $message");
|
print("DEBUG recv_message2 5 - message content: $message");
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
} else {
|
} else {
|
||||||
// Throwing exception if the length doesn't match
|
// Throwing exception if the length doesn't match
|
||||||
throw Exception('Message length mismatch: expected ${12 + messageLength} bytes, received ${recvBuf.length} bytes.');
|
throw Exception(
|
||||||
|
'Message length mismatch: expected ${12 + messageLength} bytes, received ${recvBuf.length} bytes.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,13 +275,8 @@ class Connection {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<List<int>> recv_message({Duration? timeout}) async {
|
Future<List<int>> recv_message({Duration? timeout}) async {
|
||||||
|
|
||||||
|
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // END OF CLASS
|
} // END OF CLASS
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'connection.dart';
|
|
||||||
import 'package:protobuf/protobuf.dart' as pb;
|
|
||||||
import 'comms.dart';
|
|
||||||
import 'fusion.pb.dart';
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:protobuf/protobuf.dart' as pb;
|
||||||
|
|
||||||
|
import 'comms.dart';
|
||||||
|
import 'connection.dart';
|
||||||
|
import 'fusion.pb.dart';
|
||||||
|
|
||||||
const int TOR_COOLDOWN_TIME = 660;
|
const int TOR_COOLDOWN_TIME = 660;
|
||||||
const int TIMEOUT_INACTIVE_CONNECTION = 120;
|
const int TIMEOUT_INACTIVE_CONNECTION = 120;
|
||||||
|
|
||||||
|
|
||||||
class FusionError implements Exception {
|
class FusionError implements Exception {
|
||||||
String cause;
|
String cause;
|
||||||
FusionError(this.cause);
|
FusionError(this.cause);
|
||||||
|
@ -21,15 +22,14 @@ class Unrecoverable extends FusionError {
|
||||||
Unrecoverable(String cause) : super(cause);
|
Unrecoverable(String cause) : super(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<bool> isTorPort(String host, int port) async {
|
Future<bool> isTorPort(String host, int port) async {
|
||||||
if (port < 0 || port > 65535) {
|
if (port < 0 || port > 65535) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Socket sock = await Socket.connect(host, port, timeout: Duration(milliseconds: 100));
|
Socket sock =
|
||||||
|
await Socket.connect(host, port, timeout: Duration(milliseconds: 100));
|
||||||
sock.write("GET\n");
|
sock.write("GET\n");
|
||||||
List<int> data = await sock.first;
|
List<int> data = await sock.first;
|
||||||
sock.destroy();
|
sock.destroy();
|
||||||
|
@ -43,8 +43,6 @@ Future<bool> isTorPort(String host, int port) async {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TorLimiter {
|
class TorLimiter {
|
||||||
Queue<DateTime> deque = Queue<DateTime>();
|
Queue<DateTime> deque = Queue<DateTime>();
|
||||||
int lifetime;
|
int lifetime;
|
||||||
|
@ -65,7 +63,6 @@ class TorLimiter {
|
||||||
|
|
||||||
TorLimiter limiter = TorLimiter(TOR_COOLDOWN_TIME);
|
TorLimiter limiter = TorLimiter(TOR_COOLDOWN_TIME);
|
||||||
|
|
||||||
|
|
||||||
double randTrap(Random rng) {
|
double randTrap(Random rng) {
|
||||||
final sixth = 1.0 / 6;
|
final sixth = 1.0 / 6;
|
||||||
final f = rng.nextDouble();
|
final f = rng.nextDouble();
|
||||||
|
@ -80,8 +77,6 @@ double randTrap(Random rng) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CovertConnection {
|
class CovertConnection {
|
||||||
Connection? connection; // replace dynamic with the type of your connection
|
Connection? connection; // replace dynamic with the type of your connection
|
||||||
int? slotNum;
|
int? slotNum;
|
||||||
|
@ -124,7 +119,8 @@ class CovertSlot {
|
||||||
int submitTimeout;
|
int submitTimeout;
|
||||||
pb.GeneratedMessage? subMsg; // The work to be done.
|
pb.GeneratedMessage? subMsg; // The work to be done.
|
||||||
bool done; // Whether last work requested is done.
|
bool done; // Whether last work requested is done.
|
||||||
CovertConnection? covConn; // which CovertConnection is assigned to work on this slot
|
CovertConnection?
|
||||||
|
covConn; // which CovertConnection is assigned to work on this slot
|
||||||
CovertSlot(this.submitTimeout) : done = true;
|
CovertSlot(this.submitTimeout) : done = true;
|
||||||
DateTime? t_submit;
|
DateTime? t_submit;
|
||||||
|
|
||||||
|
@ -138,25 +134,21 @@ class CovertSlot {
|
||||||
throw Unrecoverable('connection is null');
|
throw Unrecoverable('connection is null');
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendPb(connection, CovertMessage, subMsg!, timeout: Duration(seconds: submitTimeout));
|
await sendPb(connection, CovertMessage, subMsg!,
|
||||||
var result = await recvPb(connection, CovertResponse, ['ok', 'error'], timeout: Duration(seconds: submitTimeout));
|
timeout: Duration(seconds: submitTimeout));
|
||||||
|
var result = await recvPb(connection, CovertResponse, ['ok', 'error'],
|
||||||
|
timeout: Duration(seconds: submitTimeout));
|
||||||
|
|
||||||
if (result.item1 == 'error') {
|
if (result.item1 == 'error') {
|
||||||
throw Unrecoverable('error from server: ${result.item2}');
|
throw Unrecoverable('error from server: ${result.item2}');
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
t_submit = DateTime.fromMillisecondsSinceEpoch(0);
|
t_submit = DateTime.fromMillisecondsSinceEpoch(0);
|
||||||
covConn?.tPing = DateTime.fromMillisecondsSinceEpoch(0); // if a submission is done, no ping is needed.
|
covConn?.tPing = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
0); // if a submission is done, no ping is needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PrintError {
|
class PrintError {
|
||||||
// Declare properties here
|
// Declare properties here
|
||||||
}
|
}
|
||||||
|
@ -194,12 +186,11 @@ class CovertSubmitter extends PrintError {
|
||||||
this.num_slots,
|
this.num_slots,
|
||||||
double randSpan, // changed from int to double
|
double randSpan, // changed from int to double
|
||||||
double submit_timeout) // changed from int to double
|
double submit_timeout) // changed from int to double
|
||||||
: slots = List<CovertSlot>.generate(num_slots, (index) => CovertSlot(submit_timeout.toInt())) {
|
: slots = List<CovertSlot>.generate(
|
||||||
|
num_slots, (index) => CovertSlot(submit_timeout.toInt())) {
|
||||||
// constructor body...
|
// constructor body...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void wakeAll() {
|
void wakeAll() {
|
||||||
for (var s in slots) {
|
for (var s in slots) {
|
||||||
if (s.covConn != null) {
|
if (s.covConn != null) {
|
||||||
|
@ -218,7 +209,6 @@ class CovertSubmitter extends PrintError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void stop([Exception? exception]) {
|
void stop([Exception? exception]) {
|
||||||
if (this.stopping) {
|
if (this.stopping) {
|
||||||
// already requested!
|
// already requested!
|
||||||
|
@ -226,13 +216,16 @@ class CovertSubmitter extends PrintError {
|
||||||
}
|
}
|
||||||
this.failureException = exception?.toString();
|
this.failureException = exception?.toString();
|
||||||
this.stopping = true;
|
this.stopping = true;
|
||||||
var timeRemaining = this.stopTStart?.difference(DateTime.now()).inSeconds ?? 0;
|
var timeRemaining =
|
||||||
print("Stopping; connections will close in approximately $timeRemaining seconds");
|
this.stopTStart?.difference(DateTime.now()).inSeconds ?? 0;
|
||||||
|
print(
|
||||||
|
"Stopping; connections will close in approximately $timeRemaining seconds");
|
||||||
this.wakeAll();
|
this.wakeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
// PYTHON USES MULTITHREADING, WHICH ISNT IMPLEMENTED HERE YET
|
// PYTHON USES MULTITHREADING, WHICH ISNT IMPLEMENTED HERE YET
|
||||||
void scheduleConnections(DateTime tStart, Duration tSpan, {int numSpares = 0, int connectTimeout = 10}) {
|
void scheduleConnections(DateTime tStart, Duration tSpan,
|
||||||
|
{int numSpares = 0, int connectTimeout = 10}) {
|
||||||
var newConns = <CovertConnection>[];
|
var newConns = <CovertConnection>[];
|
||||||
|
|
||||||
for (var sNum = 0; sNum < this.slots.length; sNum++) {
|
for (var sNum = 0; sNum < this.slots.length; sNum++) {
|
||||||
|
@ -244,8 +237,6 @@ class CovertSubmitter extends PrintError {
|
||||||
if (myCovConn != null) {
|
if (myCovConn != null) {
|
||||||
newConns.add(myCovConn);
|
newConns.add(myCovConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,16 +249,15 @@ class CovertSubmitter extends PrintError {
|
||||||
for (var covConn in newConns) {
|
for (var covConn in newConns) {
|
||||||
covConn.connNumber = this.countAttempted;
|
covConn.connNumber = this.countAttempted;
|
||||||
this.countAttempted++;
|
this.countAttempted++;
|
||||||
var connTime = tStart.add(Duration(seconds: (tSpan.inSeconds * randTrap(this.rng)).round()));
|
var connTime = tStart.add(
|
||||||
|
Duration(seconds: (tSpan.inSeconds * randTrap(this.rng)).round()));
|
||||||
var randDelay = (this.randSpan ?? 0) * randTrap(this.rng);
|
var randDelay = (this.randSpan ?? 0) * randTrap(this.rng);
|
||||||
|
|
||||||
runConnection(covConn, connTime.millisecondsSinceEpoch, randDelay, connectTimeout);
|
runConnection(
|
||||||
|
covConn, connTime.millisecondsSinceEpoch, randDelay, connectTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void scheduleSubmit(int slotNum, DateTime tStart, dynamic subMsg) {
|
void scheduleSubmit(int slotNum, DateTime tStart, dynamic subMsg) {
|
||||||
var slot = slots[slotNum];
|
var slot = slots[slotNum];
|
||||||
|
|
||||||
|
@ -282,7 +272,6 @@ class CovertSubmitter extends PrintError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void scheduleSubmissions(DateTime tStart, List<dynamic> slotMessages) {
|
void scheduleSubmissions(DateTime tStart, List<dynamic> slotMessages) {
|
||||||
// Convert to list (Dart does not have tuples)
|
// Convert to list (Dart does not have tuples)
|
||||||
slotMessages = List.from(slotMessages);
|
slotMessages = List.from(slotMessages);
|
||||||
|
@ -317,11 +306,11 @@ class CovertSubmitter extends PrintError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future runConnection(CovertConnection covConn, int connTime, double randDelay,
|
||||||
Future runConnection(
|
int connectTimeout) async {
|
||||||
CovertConnection covConn, int connTime, double randDelay, int connectTimeout) async {
|
|
||||||
// Main loop for connection thread
|
// Main loop for connection thread
|
||||||
DateTime connDateTime = DateTime.fromMillisecondsSinceEpoch(connTime * 1000);
|
DateTime connDateTime =
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(connTime * 1000);
|
||||||
while (await covConn.waitWakeupOrTime(connDateTime)) {
|
while (await covConn.waitWakeupOrTime(connDateTime)) {
|
||||||
// if we are woken up before connection and stopping is happening, then just don't make a connection at all
|
// if we are woken up before connection and stopping is happening, then just don't make a connection at all
|
||||||
if (this.stopping) {
|
if (this.stopping) {
|
||||||
|
@ -350,11 +339,11 @@ class CovertSubmitter extends PrintError {
|
||||||
try {
|
try {
|
||||||
final connection = await openConnection(
|
final connection = await openConnection(
|
||||||
this.destAddr!, this.destPort!,
|
this.destAddr!, this.destPort!,
|
||||||
connTimeout: connectTimeout.toDouble(), ssl: this.ssl, socksOpts: proxyOpts);
|
connTimeout: connectTimeout.toDouble(),
|
||||||
|
ssl: this.ssl,
|
||||||
|
socksOpts: proxyOpts);
|
||||||
covConn.connection = connection;
|
covConn.connection = connection;
|
||||||
}
|
} catch (e) {
|
||||||
|
|
||||||
catch (e) {
|
|
||||||
this.countFailed++;
|
this.countFailed++;
|
||||||
|
|
||||||
final tEnd = DateTime.now().millisecondsSinceEpoch;
|
final tEnd = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
@ -364,7 +353,6 @@ class CovertSubmitter extends PrintError {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.countEstablished++;
|
this.countEstablished++;
|
||||||
|
|
||||||
final tEnd = DateTime.now().millisecondsSinceEpoch;
|
final tEnd = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
@ -372,7 +360,6 @@ class CovertSubmitter extends PrintError {
|
||||||
print(
|
print(
|
||||||
'[${covConn.connNumber}] connection established after ${((tEnd - tBegin) / 1000).toStringAsFixed(3)}s');
|
'[${covConn.connNumber}] connection established after ${((tEnd - tBegin) / 1000).toStringAsFixed(3)}s');
|
||||||
|
|
||||||
|
|
||||||
covConn.delay = (randTrap(this.rng) ?? 0) * (this.randSpan ?? 0);
|
covConn.delay = (randTrap(this.rng) ?? 0) * (this.randSpan ?? 0);
|
||||||
|
|
||||||
var lastActionTime = DateTime.now().millisecondsSinceEpoch;
|
var lastActionTime = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
@ -396,7 +383,8 @@ class CovertSubmitter extends PrintError {
|
||||||
}
|
}
|
||||||
// Last preference: wait doing nothing
|
// Last preference: wait doing nothing
|
||||||
if (nextTime == null) {
|
if (nextTime == null) {
|
||||||
nextTime = DateTime.now().add(Duration(seconds: TIMEOUT_INACTIVE_CONNECTION));
|
nextTime = DateTime.now()
|
||||||
|
.add(Duration(seconds: TIMEOUT_INACTIVE_CONNECTION));
|
||||||
action = covConn.inactive;
|
action = covConn.inactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,7 +411,9 @@ class CovertSubmitter extends PrintError {
|
||||||
|
|
||||||
// STATE 3 - stopping
|
// STATE 3 - stopping
|
||||||
while (true) {
|
while (true) {
|
||||||
final stopTime = this.stopTStart?.add(Duration(seconds: randDelay.toInt())) ?? DateTime.now();
|
final stopTime =
|
||||||
|
this.stopTStart?.add(Duration(seconds: randDelay.toInt())) ??
|
||||||
|
DateTime.now();
|
||||||
|
|
||||||
if (!(await covConn.waitWakeupOrTime(stopTime))) {
|
if (!(await covConn.waitWakeupOrTime(stopTime))) {
|
||||||
break;
|
break;
|
||||||
|
@ -442,7 +432,8 @@ class CovertSubmitter extends PrintError {
|
||||||
// Found a spare.
|
// Found a spare.
|
||||||
this.slots[slotNum].covConn = spare;
|
this.slots[slotNum].covConn = spare;
|
||||||
spare.slotNum = slotNum;
|
spare.slotNum = slotNum;
|
||||||
spare.wakeup.complete(); // python code is using set, possibly dealing wiht multi thread...double check this is ok.
|
spare.wakeup
|
||||||
|
.complete(); // python code is using set, possibly dealing wiht multi thread...double check this is ok.
|
||||||
|
|
||||||
covConn.slotNum = null;
|
covConn.slotNum = null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -453,7 +444,6 @@ class CovertSubmitter extends PrintError {
|
||||||
} else {
|
} else {
|
||||||
// Handle the case where the exception is not an instance of Exception
|
// Handle the case where the exception is not an instance of Exception
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -462,7 +452,6 @@ class CovertSubmitter extends PrintError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void checkOk() {
|
void checkOk() {
|
||||||
// Implement checkOk logic here
|
// Implement checkOk logic here
|
||||||
var e = this.failure_exception;
|
var e = this.failure_exception;
|
||||||
|
@ -474,26 +463,21 @@ class CovertSubmitter extends PrintError {
|
||||||
void checkConnected() {
|
void checkConnected() {
|
||||||
// Implement checkConnected logic here
|
// Implement checkConnected logic here
|
||||||
this.checkOk();
|
this.checkOk();
|
||||||
var numMissing = this.slots
|
var numMissing =
|
||||||
.where((s) => s.covConn?.connection == null)
|
this.slots.where((s) => s.covConn?.connection == null).length;
|
||||||
.length;
|
|
||||||
if (numMissing > 0) {
|
if (numMissing > 0) {
|
||||||
throw FusionError(
|
throw FusionError(
|
||||||
"Covert connections were too slow ($numMissing incomplete out of ${this
|
"Covert connections were too slow ($numMissing incomplete out of ${this.slots.length}).");
|
||||||
.slots.length}).");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkDone() {
|
void checkDone() {
|
||||||
// Implement checkDone logic here
|
// Implement checkDone logic here
|
||||||
this.checkOk();
|
this.checkOk();
|
||||||
var numMissing = this.slots
|
var numMissing = this.slots.where((s) => !s.done).length;
|
||||||
.where((s) => !s.done)
|
|
||||||
.length;
|
|
||||||
if (numMissing > 0) {
|
if (numMissing > 0) {
|
||||||
throw FusionError(
|
throw FusionError(
|
||||||
"Covert submissions were too slow ($numMissing incomplete out of ${this
|
"Covert submissions were too slow ($numMissing incomplete out of ${this.slots.length}).");
|
||||||
.slots.length}).");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,17 +1,20 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:pointycastle/pointycastle.dart' hide Mac;
|
|
||||||
import 'package:crypto/crypto.dart' as crypto;
|
import 'package:crypto/crypto.dart' as crypto;
|
||||||
import 'package:cryptography/cryptography.dart';
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
import 'package:pointycastle/pointycastle.dart' hide Mac;
|
||||||
|
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
|
|
||||||
final ECDomainParameters params = ECDomainParameters('secp256k1');
|
final ECDomainParameters params = ECDomainParameters('secp256k1');
|
||||||
final BigInt order = params.n;
|
final BigInt order = params.n;
|
||||||
|
|
||||||
class EncryptionFailed implements Exception {}
|
class EncryptionFailed implements Exception {}
|
||||||
|
|
||||||
class DecryptionFailed implements Exception {}
|
class DecryptionFailed implements Exception {}
|
||||||
|
|
||||||
Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey, {int? padToLength}) async {
|
Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey,
|
||||||
|
{int? padToLength}) async {
|
||||||
ECPoint pubpoint;
|
ECPoint pubpoint;
|
||||||
try {
|
try {
|
||||||
pubpoint = Util.ser_to_point(pubkey.getEncoded(true), params);
|
pubpoint = Util.ser_to_point(pubkey.getEncoded(true), params);
|
||||||
|
@ -27,22 +30,27 @@ Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey, {int? padToLength})
|
||||||
|
|
||||||
var pubpoint_times_nonceSec = pubpoint * nonceSec;
|
var pubpoint_times_nonceSec = pubpoint * nonceSec;
|
||||||
if (pubpoint_times_nonceSec == null) {
|
if (pubpoint_times_nonceSec == null) {
|
||||||
throw Exception('Multiplication of pubpoint with nonceSec resulted in null');
|
throw Exception(
|
||||||
|
'Multiplication of pubpoint with nonceSec resulted in null');
|
||||||
}
|
}
|
||||||
var key = crypto.sha256.convert(Util.point_to_ser(pubpoint_times_nonceSec, true)).bytes;
|
var key = crypto.sha256
|
||||||
|
.convert(Util.point_to_ser(pubpoint_times_nonceSec, true))
|
||||||
|
.bytes;
|
||||||
|
|
||||||
|
var plaintext = Uint8List(4 + message.length)
|
||||||
|
..buffer.asByteData().setUint32(0, message.length, Endian.big)
|
||||||
var plaintext = Uint8List(4 + message.length)..buffer.asByteData().setUint32(0, message.length, Endian.big)..setRange(4, 4 + message.length, message);
|
..setRange(4, 4 + message.length, message);
|
||||||
if (padToLength == null) {
|
if (padToLength == null) {
|
||||||
padToLength = ((plaintext.length + 15) ~/ 16) * 16; // round up to nearest 16
|
padToLength =
|
||||||
|
((plaintext.length + 15) ~/ 16) * 16; // round up to nearest 16
|
||||||
} else if (padToLength % 16 != 0) {
|
} else if (padToLength % 16 != 0) {
|
||||||
throw ArgumentError('$padToLength not multiple of 16');
|
throw ArgumentError('$padToLength not multiple of 16');
|
||||||
}
|
}
|
||||||
if (padToLength < plaintext.length) {
|
if (padToLength < plaintext.length) {
|
||||||
throw ArgumentError('$padToLength < ${plaintext.length}');
|
throw ArgumentError('$padToLength < ${plaintext.length}');
|
||||||
}
|
}
|
||||||
plaintext = Uint8List(padToLength)..setRange(0, message.length + 4, plaintext);
|
plaintext = Uint8List(padToLength)
|
||||||
|
..setRange(0, message.length + 4, plaintext);
|
||||||
|
|
||||||
final secretKey = SecretKey(key);
|
final secretKey = SecretKey(key);
|
||||||
|
|
||||||
|
@ -50,16 +58,20 @@ Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey, {int? padToLength})
|
||||||
|
|
||||||
final cipher = AesCbc.with128bits(macAlgorithm: macAlgorithm);
|
final cipher = AesCbc.with128bits(macAlgorithm: macAlgorithm);
|
||||||
|
|
||||||
|
|
||||||
final nonce = Uint8List(16); // Random nonce
|
final nonce = Uint8List(16); // Random nonce
|
||||||
final secretBox = await cipher.encrypt(plaintext, secretKey: secretKey, nonce: nonce);
|
final secretBox =
|
||||||
|
await cipher.encrypt(plaintext, secretKey: secretKey, nonce: nonce);
|
||||||
|
|
||||||
final ciphertext = secretBox.cipherText;
|
final ciphertext = secretBox.cipherText;
|
||||||
|
|
||||||
return Uint8List(noncePub.length + ciphertext.length + secretBox.mac.bytes.length)
|
return Uint8List(
|
||||||
|
noncePub.length + ciphertext.length + secretBox.mac.bytes.length)
|
||||||
..setRange(0, noncePub.length, noncePub)
|
..setRange(0, noncePub.length, noncePub)
|
||||||
..setRange(noncePub.length, noncePub.length + ciphertext.length, ciphertext)
|
..setRange(noncePub.length, noncePub.length + ciphertext.length, ciphertext)
|
||||||
..setRange(noncePub.length + ciphertext.length, noncePub.length + ciphertext.length + secretBox.mac.bytes.length, secretBox.mac.bytes);
|
..setRange(
|
||||||
|
noncePub.length + ciphertext.length,
|
||||||
|
noncePub.length + ciphertext.length + secretBox.mac.bytes.length,
|
||||||
|
secretBox.mac.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
|
Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
|
||||||
|
@ -75,7 +87,8 @@ Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
|
||||||
final cipher = AesCbc.with128bits(macAlgorithm: Hmac.sha256());
|
final cipher = AesCbc.with128bits(macAlgorithm: Hmac.sha256());
|
||||||
final nonce = Uint8List(16); // Random nonce
|
final nonce = Uint8List(16); // Random nonce
|
||||||
|
|
||||||
final secretBox = SecretBox(ciphertext, mac: Mac(data.sublist(data.length - 16)), nonce: nonce);
|
final secretBox = SecretBox(ciphertext,
|
||||||
|
mac: Mac(data.sublist(data.length - 16)), nonce: nonce);
|
||||||
final plaintext = await cipher.decrypt(secretBox, secretKey: secretKey);
|
final plaintext = await cipher.decrypt(secretBox, secretKey: secretKey);
|
||||||
|
|
||||||
if (plaintext.length < 4) {
|
if (plaintext.length < 4) {
|
||||||
|
@ -86,15 +99,14 @@ Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
|
||||||
ByteData byteData = ByteData.sublistView(uint8list);
|
ByteData byteData = ByteData.sublistView(uint8list);
|
||||||
var msgLength = byteData.getUint32(0, Endian.big);
|
var msgLength = byteData.getUint32(0, Endian.big);
|
||||||
|
|
||||||
|
|
||||||
if (msgLength + 4 > plaintext.length) {
|
if (msgLength + 4 > plaintext.length) {
|
||||||
throw DecryptionFailed();
|
throw DecryptionFailed();
|
||||||
}
|
}
|
||||||
return Uint8List.fromList(plaintext.sublist(4, 4 + msgLength));
|
return Uint8List.fromList(plaintext.sublist(4, 4 + msgLength));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Tuple<Uint8List, Uint8List>> decrypt(Uint8List data, ECPrivateKey privkey) async {
|
Future<Tuple<Uint8List, Uint8List>> decrypt(
|
||||||
|
Uint8List data, ECPrivateKey privkey) async {
|
||||||
if (data.length < 33 + 16 + 16) {
|
if (data.length < 33 + 16 + 16) {
|
||||||
throw DecryptionFailed();
|
throw DecryptionFailed();
|
||||||
}
|
}
|
||||||
|
@ -122,4 +134,3 @@ Future<Tuple<Uint8List, Uint8List>> decrypt(Uint8List data, ECPrivateKey privkey
|
||||||
var decryptedData = await decryptWithSymmkey(data, Uint8List.fromList(key));
|
var decryptedData = await decryptWithSymmkey(data, Uint8List.fromList(key));
|
||||||
return Tuple(decryptedData, Uint8List.fromList(key));
|
return Tuple(decryptedData, Uint8List.fromList(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,122 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'fusion.dart';
|
|
||||||
import 'pedersen.dart';
|
|
||||||
import 'encrypt.dart';
|
|
||||||
import 'validation.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
|
|
||||||
Fusion.foo();
|
|
||||||
|
|
||||||
runApp(const MyApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
|
||||||
const MyApp({super.key});
|
|
||||||
|
|
||||||
// This widget is the root of your application.
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MaterialApp(
|
|
||||||
title: 'Flutter Demo',
|
|
||||||
theme: ThemeData(
|
|
||||||
// This is the theme of your application.
|
|
||||||
//
|
|
||||||
// Try running your application with "flutter run". You'll see the
|
|
||||||
// application has a blue toolbar. Then, without quitting the app, try
|
|
||||||
// changing the primarySwatch below to Colors.green and then invoke
|
|
||||||
// "hot reload" (press "r" in the console where you ran "flutter run",
|
|
||||||
// or simply save your changes to "hot reload" in a Flutter IDE).
|
|
||||||
// Notice that the counter didn't reset back to zero; the application
|
|
||||||
// is not restarted.
|
|
||||||
primarySwatch: Colors.blue,
|
|
||||||
),
|
|
||||||
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
|
||||||
const MyHomePage({super.key, required this.title});
|
|
||||||
|
|
||||||
// This widget is the home page of your application. It is stateful, meaning
|
|
||||||
// that it has a State object (defined below) that contains fields that affect
|
|
||||||
// how it looks.
|
|
||||||
|
|
||||||
// This class is the configuration for the state. It holds the values (in this
|
|
||||||
// case the title) provided by the parent (in this case the App widget) and
|
|
||||||
// used by the build method of the State. Fields in a Widget subclass are
|
|
||||||
// always marked "final".
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
|
||||||
int _counter = 0;
|
|
||||||
|
|
||||||
void _incrementCounter() {
|
|
||||||
setState(() {
|
|
||||||
// This call to setState tells the Flutter framework that something has
|
|
||||||
// changed in this State, which causes it to rerun the build method below
|
|
||||||
// so that the display can reflect the updated values. If we changed
|
|
||||||
// _counter without calling setState(), then the build method would not be
|
|
||||||
// called again, and so nothing would appear to happen.
|
|
||||||
_counter++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// This method is rerun every time setState is called, for instance as done
|
|
||||||
// by the _incrementCounter method above.
|
|
||||||
//
|
|
||||||
// The Flutter framework has been optimized to make rerunning build methods
|
|
||||||
// fast, so that you can just rebuild anything that needs updating rather
|
|
||||||
// than having to individually change instances of widgets.
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
// Here we take the value from the MyHomePage object that was created by
|
|
||||||
// the App.build method, and use it to set our appbar title.
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
// Center is a layout widget. It takes a single child and positions it
|
|
||||||
// in the middle of the parent.
|
|
||||||
child: Column(
|
|
||||||
// Column is also a layout widget. It takes a list of children and
|
|
||||||
// arranges them vertically. By default, it sizes itself to fit its
|
|
||||||
// children horizontally, and tries to be as tall as its parent.
|
|
||||||
//
|
|
||||||
// Invoke "debug painting" (press "p" in the console, choose the
|
|
||||||
// "Toggle Debug Paint" action from the Flutter Inspector in Android
|
|
||||||
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
|
|
||||||
// to see the wireframe for each widget.
|
|
||||||
//
|
|
||||||
// Column has various properties to control how it sizes itself and
|
|
||||||
// how it positions its children. Here we use mainAxisAlignment to
|
|
||||||
// center the children vertically; the main axis here is the vertical
|
|
||||||
// axis because Columns are vertical (the cross axis would be
|
|
||||||
// horizontal).
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
const Text(
|
|
||||||
'You have pushed the button this many times:',
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'$_counter',
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: _incrementCounter,
|
|
||||||
tooltip: 'Increment',
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
import 'package:pointycastle/ecc/api.dart';
|
|
||||||
import 'util.dart';
|
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:pointycastle/ecc/api.dart';
|
||||||
|
import 'package:stackwallet/services/cashfusion/util.dart';
|
||||||
|
|
||||||
ECDomainParameters getDefaultParams() {
|
ECDomainParameters getDefaultParams() {
|
||||||
return ECDomainParameters("secp256k1");
|
return ECDomainParameters("secp256k1");
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,10 @@ class NullPointError implements Exception {
|
||||||
String errMsg() => 'NullPointError: Either Hpoint or HGpoint is null.';
|
String errMsg() => 'NullPointError: Either Hpoint or HGpoint is null.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NonceRangeError implements Exception {
|
class NonceRangeError implements Exception {
|
||||||
final String message;
|
final String message;
|
||||||
NonceRangeError([this.message = "Nonce value must be in the range 0 < nonce < order"]);
|
NonceRangeError(
|
||||||
|
[this.message = "Nonce value must be in the range 0 < nonce < order"]);
|
||||||
String toString() => "NonceRangeError: $message";
|
String toString() => "NonceRangeError: $message";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +26,12 @@ class ResultAtInfinity implements Exception {
|
||||||
|
|
||||||
class InsecureHPoint implements Exception {
|
class InsecureHPoint implements Exception {
|
||||||
final String message;
|
final String message;
|
||||||
InsecureHPoint([this.message = "The H point has a known discrete logarithm, which means the commitment setup is broken"]);
|
InsecureHPoint(
|
||||||
|
[this.message =
|
||||||
|
"The H point has a known discrete logarithm, which means the commitment setup is broken"]);
|
||||||
String toString() => "InsecureHPoint: $message";
|
String toString() => "InsecureHPoint: $message";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PedersenSetup {
|
class PedersenSetup {
|
||||||
late ECPoint _H;
|
late ECPoint _H;
|
||||||
late ECPoint _HG;
|
late ECPoint _HG;
|
||||||
|
@ -53,7 +53,6 @@ class PedersenSetup {
|
||||||
Commitment commit(BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) {
|
Commitment commit(BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) {
|
||||||
return Commitment(this, amount, nonce: nonce, PUncompressed: PUncompressed);
|
return Commitment(this, amount, nonce: nonce, PUncompressed: PUncompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Commitment {
|
class Commitment {
|
||||||
|
@ -62,8 +61,8 @@ class Commitment {
|
||||||
late BigInt nonce;
|
late BigInt nonce;
|
||||||
late Uint8List PUncompressed;
|
late Uint8List PUncompressed;
|
||||||
|
|
||||||
|
Commitment(this.setup, BigInt amount,
|
||||||
Commitment(this.setup, BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) {
|
{BigInt? nonce, Uint8List? PUncompressed}) {
|
||||||
this.nonce = nonce ?? Util.secureRandomBigInt(setup.params.n.bitLength);
|
this.nonce = nonce ?? Util.secureRandomBigInt(setup.params.n.bitLength);
|
||||||
amountMod = amount % setup.params.n;
|
amountMod = amount % setup.params.n;
|
||||||
|
|
||||||
|
@ -84,13 +83,16 @@ class Commitment {
|
||||||
ECPoint? HpointMultiplied = Hpoint * multiplier1;
|
ECPoint? HpointMultiplied = Hpoint * multiplier1;
|
||||||
ECPoint? HGpointMultiplied = HGpoint * multiplier2;
|
ECPoint? HGpointMultiplied = HGpoint * multiplier2;
|
||||||
|
|
||||||
ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null ? HpointMultiplied + HGpointMultiplied : null;
|
ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null
|
||||||
|
? HpointMultiplied + HGpointMultiplied
|
||||||
|
: null;
|
||||||
|
|
||||||
if (Ppoint == setup.params.curve.infinity) {
|
if (Ppoint == setup.params.curve.infinity) {
|
||||||
throw ResultAtInfinity();
|
throw ResultAtInfinity();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.PUncompressed = PUncompressed ?? Ppoint?.getEncoded(false) ?? Uint8List(0);
|
this.PUncompressed =
|
||||||
|
PUncompressed ?? Ppoint?.getEncoded(false) ?? Uint8List(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void calcInitial(PedersenSetup setup, BigInt amount) {
|
void calcInitial(PedersenSetup setup, BigInt amount) {
|
||||||
|
@ -114,7 +116,9 @@ class Commitment {
|
||||||
ECPoint? HpointMultiplied = Hpoint * multiplier1;
|
ECPoint? HpointMultiplied = Hpoint * multiplier1;
|
||||||
ECPoint? HGpointMultiplied = HGpoint * multiplier2;
|
ECPoint? HGpointMultiplied = HGpoint * multiplier2;
|
||||||
|
|
||||||
ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null ? HpointMultiplied + HGpointMultiplied : null;
|
ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null
|
||||||
|
? HpointMultiplied + HGpointMultiplied
|
||||||
|
: null;
|
||||||
|
|
||||||
if (Ppoint == setup.params.curve.infinity) {
|
if (Ppoint == setup.params.curve.infinity) {
|
||||||
throw ResultAtInfinity();
|
throw ResultAtInfinity();
|
||||||
|
@ -124,14 +128,17 @@ class Commitment {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint8List add_points(Iterable<Uint8List> pointsIterable) {
|
static Uint8List add_points(Iterable<Uint8List> pointsIterable) {
|
||||||
ECDomainParameters params = getDefaultParams(); // Using helper function here
|
ECDomainParameters params =
|
||||||
var pointList = pointsIterable.map((pser) => Util.ser_to_point(pser, params)).toList();
|
getDefaultParams(); // Using helper function here
|
||||||
|
var pointList =
|
||||||
|
pointsIterable.map((pser) => Util.ser_to_point(pser, params)).toList();
|
||||||
|
|
||||||
if (pointList.isEmpty) {
|
if (pointList.isEmpty) {
|
||||||
throw ArgumentError('Empty list');
|
throw ArgumentError('Empty list');
|
||||||
}
|
}
|
||||||
|
|
||||||
ECPoint pSum = pointList.first; // Initialize pSum with the first point in the list
|
ECPoint pSum =
|
||||||
|
pointList.first; // Initialize pSum with the first point in the list
|
||||||
|
|
||||||
for (var i = 1; i < pointList.length; i++) {
|
for (var i = 1; i < pointList.length; i++) {
|
||||||
pSum = (pSum + pointList[i])!;
|
pSum = (pSum + pointList[i])!;
|
||||||
|
@ -144,7 +151,6 @@ class Commitment {
|
||||||
return Util.point_to_ser(pSum, false);
|
return Util.point_to_ser(pSum, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Commitment addCommitments(Iterable<Commitment> commitmentIterable) {
|
Commitment addCommitments(Iterable<Commitment> commitmentIterable) {
|
||||||
BigInt ktotal = BigInt.zero; // Changed to BigInt from int
|
BigInt ktotal = BigInt.zero; // Changed to BigInt from int
|
||||||
BigInt atotal = BigInt.zero; // Changed to BigInt from int
|
BigInt atotal = BigInt.zero; // Changed to BigInt from int
|
||||||
|
@ -168,7 +174,8 @@ class Commitment {
|
||||||
|
|
||||||
ktotal = ktotal % setup.params.n; // Changed order to setup.params.n
|
ktotal = ktotal % setup.params.n; // Changed order to setup.params.n
|
||||||
|
|
||||||
if (ktotal == BigInt.zero) { // Changed comparison from 0 to BigInt.zero
|
if (ktotal == BigInt.zero) {
|
||||||
|
// Changed comparison from 0 to BigInt.zero
|
||||||
throw Exception('Nonce range error');
|
throw Exception('Nonce range error');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +189,7 @@ class Commitment {
|
||||||
} else {
|
} else {
|
||||||
PUncompressed = null;
|
PUncompressed = null;
|
||||||
}
|
}
|
||||||
return Commitment(setup, atotal, nonce: ktotal, PUncompressed: PUncompressed);
|
return Commitment(setup, atotal,
|
||||||
|
nonce: ktotal, PUncompressed: PUncompressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
|
|
||||||
|
|
||||||
class Protocol {
|
class Protocol {
|
||||||
static const VERSION = 'alpha13';
|
static const VERSION = 'alpha13';
|
||||||
|
|
||||||
static const FUSE_ID = 'FUZ\x00';
|
static const FUSE_ID = 'FUZ\x00';
|
||||||
|
|
||||||
|
|
||||||
// Safety limits to prevent loss of funds / limit fees:
|
// Safety limits to prevent loss of funds / limit fees:
|
||||||
//(Note that if we enter multiply into the same fusion, our limits apply
|
//(Note that if we enter multiply into the same fusion, our limits apply
|
||||||
//separately for each "player".)
|
//separately for each "player".)
|
||||||
|
@ -56,4 +53,3 @@ class Protocol {
|
||||||
static const STANDARD_TIMEOUT = 3.0;
|
static const STANDARD_TIMEOUT = 3.0;
|
||||||
static const BLAME_VERIFY_TIME = 5.0;
|
static const BLAME_VERIFY_TIME = 5.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,19 @@ class SocketWrapper {
|
||||||
final String serverIP;
|
final String serverIP;
|
||||||
final int serverPort;
|
final int serverPort;
|
||||||
|
|
||||||
late Stream<List<int>> _receiveStream; // create a field for the broadcast stream
|
late Stream<List<int>>
|
||||||
|
_receiveStream; // create a field for the broadcast stream
|
||||||
|
|
||||||
SocketWrapper(this.serverIP, this.serverPort);
|
SocketWrapper(this.serverIP, this.serverPort);
|
||||||
Socket get socket => _socket;
|
Socket get socket => _socket;
|
||||||
|
|
||||||
Stream<List<int>> get receiveStream => _receiveStream; // expose the stream with a getter
|
Stream<List<int>> get receiveStream =>
|
||||||
|
_receiveStream; // expose the stream with a getter
|
||||||
|
|
||||||
Future<void> connect() async {
|
Future<void> connect() async {
|
||||||
_socket = await Socket.connect(serverIP, serverPort);
|
_socket = await Socket.connect(serverIP, serverPort);
|
||||||
_receiveStream = _socket.asBroadcastStream(); // initialize the broadcast stream
|
_receiveStream =
|
||||||
|
_socket.asBroadcastStream(); // initialize the broadcast stream
|
||||||
_socket.done.then((_) {
|
_socket.done.then((_) {
|
||||||
print('......Socket has been closed');
|
print('......Socket has been closed');
|
||||||
});
|
});
|
||||||
|
@ -25,7 +28,8 @@ class SocketWrapper {
|
||||||
|
|
||||||
void status() {
|
void status() {
|
||||||
if (_socket != null) {
|
if (_socket != null) {
|
||||||
print("Socket connected to ${_socket.remoteAddress.address}:${_socket.remotePort}");
|
print(
|
||||||
|
"Socket connected to ${_socket.remoteAddress.address}:${_socket.remotePort}");
|
||||||
} else {
|
} else {
|
||||||
print("Socket is not connected");
|
print("Socket is not connected");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'package:stackwallet/services/cashfusion/fusion.dart';
|
|
||||||
import 'package:pointycastle/ecc/api.dart';
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:crypto/crypto.dart' as crypto;
|
import 'package:crypto/crypto.dart' as crypto;
|
||||||
import 'protocol.dart';
|
import 'package:pointycastle/ecc/api.dart';
|
||||||
|
|
||||||
import 'fusion.pb.dart';
|
import 'fusion.pb.dart';
|
||||||
import 'dart:convert';
|
import 'protocol.dart';
|
||||||
|
|
||||||
class Address {
|
class Address {
|
||||||
String addr = "";
|
String addr = "";
|
||||||
|
|
||||||
Address({required this.addr}); // Constructor updated to accept addr as a named parameter
|
Address(
|
||||||
|
{required this.addr}); // Constructor updated to accept addr as a named parameter
|
||||||
|
|
||||||
Address._create({required this.addr});
|
Address._create({required this.addr});
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ class Address {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Tuple<T1, T2> {
|
class Tuple<T1, T2> {
|
||||||
T1 item1;
|
T1 item1;
|
||||||
T2 item2;
|
T2 item2;
|
||||||
|
@ -49,8 +48,6 @@ class Tuple<T1, T2> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Util {
|
class Util {
|
||||||
|
|
||||||
|
|
||||||
static Uint8List hexToBytes(String hex) {
|
static Uint8List hexToBytes(String hex) {
|
||||||
var result = new Uint8List(hex.length ~/ 2);
|
var result = new Uint8List(hex.length ~/ 2);
|
||||||
for (var i = 0; i < hex.length; i += 2) {
|
for (var i = 0; i < hex.length; i += 2) {
|
||||||
|
@ -65,7 +62,6 @@ class Util {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int randPosition(Uint8List seed, int numPositions, int counter) {
|
static int randPosition(Uint8List seed, int numPositions, int counter) {
|
||||||
// counter to bytes
|
// counter to bytes
|
||||||
var counterBytes = Uint8List(4);
|
var counterBytes = Uint8List(4);
|
||||||
|
@ -77,7 +73,8 @@ class Util {
|
||||||
|
|
||||||
// take the first 8 bytes
|
// take the first 8 bytes
|
||||||
var first8Bytes = digest.bytes.take(8).toList();
|
var first8Bytes = digest.bytes.take(8).toList();
|
||||||
var int64 = ByteData.sublistView(Uint8List.fromList(first8Bytes)).getUint64(0, Endian.big);
|
var int64 = ByteData.sublistView(Uint8List.fromList(first8Bytes))
|
||||||
|
.getUint64(0, Endian.big);
|
||||||
|
|
||||||
// perform the modulo operation
|
// perform the modulo operation
|
||||||
return ((int64 * numPositions) >> 64).toInt();
|
return ((int64 * numPositions) >> 64).toInt();
|
||||||
|
@ -93,7 +90,6 @@ class Util {
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Address getAddressFromOutputScript(Uint8List scriptpubkey) {
|
static Address getAddressFromOutputScript(Uint8List scriptpubkey) {
|
||||||
// Dummy implementation...
|
// Dummy implementation...
|
||||||
|
|
||||||
|
@ -102,19 +98,18 @@ class Util {
|
||||||
return Address.fromString('dummy_address');
|
return Address.fromString('dummy_address');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool schnorrVerify(
|
||||||
static bool schnorrVerify(ECPoint pubkey, List<int> signature, Uint8List messageHash) {
|
ECPoint pubkey, List<int> signature, Uint8List messageHash) {
|
||||||
// Implementation needed: actual Schnorr signature verification
|
// Implementation needed: actual Schnorr signature verification
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static String formatSatoshis(sats, {int numZeros = 8}) {
|
static String formatSatoshis(sats, {int numZeros = 8}) {
|
||||||
// To implement
|
// To implement
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
static void updateWalletLabel(String txid, String label) {
|
|
||||||
|
|
||||||
|
static void updateWalletLabel(String txid, String label) {
|
||||||
// Call the wallet layer.
|
// Call the wallet layer.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,12 +127,13 @@ static List<List<T>> zip<T>(List<T> list1, List<T> list2) {
|
||||||
return List<List<T>>.generate(length, (i) => [list1[i], list2[i]]);
|
return List<List<T>>.generate(length, (i) => [list1[i], list2[i]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<int> calcInitialHash(int tier, Uint8List covertDomainB,
|
||||||
static List<int> calcInitialHash(int tier, Uint8List covertDomainB, int covertPort, bool covertSsl, double beginTime) {
|
int covertPort, bool covertSsl, double beginTime) {
|
||||||
// Converting int to bytes in BigEndian order
|
// Converting int to bytes in BigEndian order
|
||||||
var tierBytes = ByteData(8)..setInt64(0, tier, Endian.big);
|
var tierBytes = ByteData(8)..setInt64(0, tier, Endian.big);
|
||||||
var covertPortBytes = ByteData(4)..setInt32(0, covertPort, Endian.big);
|
var covertPortBytes = ByteData(4)..setInt32(0, covertPort, Endian.big);
|
||||||
var beginTimeBytes = ByteData(8)..setInt64(0, beginTime.toInt(), Endian.big);
|
var beginTimeBytes = ByteData(8)
|
||||||
|
..setInt64(0, beginTime.toInt(), Endian.big);
|
||||||
|
|
||||||
// Define constants
|
// Define constants
|
||||||
const version = Protocol.VERSION;
|
const version = Protocol.VERSION;
|
||||||
|
@ -159,7 +155,12 @@ static List<List<T>> zip<T>(List<T> list1, List<T> list2) {
|
||||||
return digest.bytes;
|
return digest.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int roundTime, List<List<int>> allCommitments, List<List<int>> allComponents) {
|
static List<int> calcRoundHash(
|
||||||
|
List<int> lastHash,
|
||||||
|
List<int> roundPubkey,
|
||||||
|
int roundTime,
|
||||||
|
List<List<int>> allCommitments,
|
||||||
|
List<List<int>> allComponents) {
|
||||||
return listHash([
|
return listHash([
|
||||||
utf8.encode('Cash Fusion Round'),
|
utf8.encode('Cash Fusion Round'),
|
||||||
lastHash,
|
lastHash,
|
||||||
|
@ -179,12 +180,11 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
|
||||||
bytes.addAll(x);
|
bytes.addAll(x);
|
||||||
}
|
}
|
||||||
return crypto.sha256.convert(bytes).bytes;
|
return crypto.sha256.convert(bytes).bytes;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Uint8List get_current_genesis_hash() {
|
static Uint8List get_current_genesis_hash() {
|
||||||
var GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
|
var GENESIS =
|
||||||
|
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
|
||||||
var _lastGenesisHash = hexToBytes(GENESIS).reversed.toList();
|
var _lastGenesisHash = hexToBytes(GENESIS).reversed.toList();
|
||||||
return Uint8List.fromList(_lastGenesisHash);
|
return Uint8List.fromList(_lastGenesisHash);
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,6 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static List<Address> reserve_change_addresses(int number_addresses) {
|
static List<Address> reserve_change_addresses(int number_addresses) {
|
||||||
//implement later based on wallet.
|
//implement later based on wallet.
|
||||||
return [];
|
return [];
|
||||||
|
@ -206,7 +205,8 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint8List bigIntToBytes(BigInt bigInt) {
|
static Uint8List bigIntToBytes(BigInt bigInt) {
|
||||||
return Uint8List.fromList(bigInt.toRadixString(16).padLeft(32, '0').codeUnits);
|
return Uint8List.fromList(
|
||||||
|
bigInt.toRadixString(16).padLeft(32, '0').codeUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Tuple<Uint8List, Uint8List> genKeypair() {
|
static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
|
@ -224,8 +224,6 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
return Tuple(privKey, pubKey);
|
return Tuple(privKey, pubKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Generates a cryptographically secure private key
|
// Generates a cryptographically secure private key
|
||||||
static BigInt _generatePrivateKey(int bitLength) {
|
static BigInt _generatePrivateKey(int bitLength) {
|
||||||
final random = Random.secure();
|
final random = Random.secure();
|
||||||
|
@ -236,7 +234,9 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
List<int> rnd = List<int>.generate(bytes, (_) => random.nextInt(256));
|
List<int> rnd = List<int>.generate(bytes, (_) => random.nextInt(256));
|
||||||
var rndBit = random.nextInt(1 << remBit);
|
var rndBit = random.nextInt(1 << remBit);
|
||||||
rnd.add(rndBit);
|
rnd.add(rndBit);
|
||||||
var privateKey = BigInt.parse(rnd.map((x) => x.toRadixString(16).padLeft(2, '0')).join(), radix: 16);
|
var privateKey = BigInt.parse(
|
||||||
|
rnd.map((x) => x.toRadixString(16).padLeft(2, '0')).join(),
|
||||||
|
radix: 16);
|
||||||
|
|
||||||
return privateKey;
|
return privateKey;
|
||||||
}
|
}
|
||||||
|
@ -251,15 +251,16 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
return BigInt.parse(hexString, radix: 16);
|
return BigInt.parse(hexString, radix: 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Uint8List sha256(Uint8List bytes) {
|
static Uint8List sha256(Uint8List bytes) {
|
||||||
crypto.Digest digest = crypto.sha256.convert(bytes);
|
crypto.Digest digest = crypto.sha256.convert(bytes);
|
||||||
return Uint8List.fromList(digest.bytes);
|
return Uint8List.fromList(digest.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint8List tokenBytes([int nbytes = 32]) {
|
static Uint8List tokenBytes([int nbytes = 32]) {
|
||||||
final Random _random = Random.secure();
|
final Random _random = Random.secure();
|
||||||
|
|
||||||
return Uint8List.fromList(List<int>.generate(nbytes, (i) => _random.nextInt(256)));
|
return Uint8List.fromList(
|
||||||
|
List<int>.generate(nbytes, (i) => _random.nextInt(256)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int componentFee(int size, int feerate) {
|
static int componentFee(int size, int feerate) {
|
||||||
|
@ -269,8 +270,8 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
return ((size * feerate) + 999) ~/ 1000;
|
return ((size * feerate) + 999) ~/ 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ECPoint ser_to_point(
|
||||||
static ECPoint ser_to_point(Uint8List serializedPoint, ECDomainParameters params) {
|
Uint8List serializedPoint, ECDomainParameters params) {
|
||||||
var point = params.curve.decodePoint(serializedPoint);
|
var point = params.curve.decodePoint(serializedPoint);
|
||||||
if (point == null) {
|
if (point == null) {
|
||||||
throw FormatException('Point decoding failed');
|
throw FormatException('Point decoding failed');
|
||||||
|
@ -282,7 +283,6 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
return point.getEncoded(compress);
|
return point.getEncoded(compress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static BigInt secureRandomBigInt(int bitLength) {
|
static BigInt secureRandomBigInt(int bitLength) {
|
||||||
final random = Random.secure();
|
final random = Random.secure();
|
||||||
final bytes = (bitLength + 7) ~/ 8; // ceil division
|
final bytes = (bitLength + 7) ~/ 8; // ceil division
|
||||||
|
@ -292,9 +292,12 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
randomBytes[i] = random.nextInt(256);
|
randomBytes[i] = random.nextInt(256);
|
||||||
}
|
}
|
||||||
|
|
||||||
BigInt randomNumber = BigInt.parse(randomBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(), radix: 16);
|
BigInt randomNumber = BigInt.parse(
|
||||||
|
randomBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(),
|
||||||
|
radix: 16);
|
||||||
return randomNumber;
|
return randomNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ECPoint combinePubKeys(List<ECPoint> pubKeys) {
|
static ECPoint combinePubKeys(List<ECPoint> pubKeys) {
|
||||||
if (pubKeys.isEmpty) throw ArgumentError('pubKeys cannot be empty');
|
if (pubKeys.isEmpty) throw ArgumentError('pubKeys cannot be empty');
|
||||||
|
|
||||||
|
@ -323,8 +326,4 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
||||||
// Check if the point is on the curve
|
// Check if the point is on the curve
|
||||||
return left == right;
|
return left == right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // END OF CLASS
|
} // END OF CLASS
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import 'package:protobuf/protobuf.dart';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:pointycastle/export.dart';
|
||||||
|
|
||||||
|
import 'encrypt.dart' as Encrypt;
|
||||||
|
import 'fusion.dart';
|
||||||
import 'fusion.pb.dart' as pb;
|
import 'fusion.pb.dart' as pb;
|
||||||
import 'pedersen.dart';
|
import 'pedersen.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
import 'encrypt.dart' as Encrypt;
|
|
||||||
import 'protocol.dart';
|
|
||||||
import 'fusion.dart';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:pointycastle/export.dart';
|
|
||||||
import 'package:convert/convert.dart';
|
|
||||||
import 'pedersen.dart';
|
|
||||||
|
|
||||||
class ValidationError implements Exception {
|
class ValidationError implements Exception {
|
||||||
final String message;
|
final String message;
|
||||||
|
@ -31,13 +29,12 @@ int componentContrib(pb.Component component, int feerate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void check(bool condition, String failMessage) {
|
void check(bool condition, String failMessage) {
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
throw ValidationError(failMessage);
|
throw ValidationError(failMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic protoStrictParse(dynamic msg, List<int> blob) {
|
dynamic protoStrictParse(dynamic msg, List<int> blob) {
|
||||||
try {
|
try {
|
||||||
if (msg.mergeFromBuffer(blob) != blob.length) {
|
if (msg.mergeFromBuffer(blob) != blob.length) {
|
||||||
|
@ -63,21 +60,22 @@ dynamic protoStrictParse(dynamic msg, List<int> blob) {
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<pb.InitialCommitment> checkPlayerCommit(pb.PlayerCommit msg,
|
||||||
|
int minExcessFee, int maxExcessFee, int numComponents) {
|
||||||
|
check(msg.initialCommitments.length == numComponents,
|
||||||
|
"wrong number of component commitments");
|
||||||
|
check(msg.blindSigRequests.length == numComponents,
|
||||||
|
"wrong number of blind sig requests");
|
||||||
|
|
||||||
List<pb.InitialCommitment> checkPlayerCommit(
|
check(
|
||||||
pb.PlayerCommit msg,
|
minExcessFee <= msg.excessFee.toInt() &&
|
||||||
int minExcessFee,
|
msg.excessFee.toInt() <= maxExcessFee,
|
||||||
int maxExcessFee,
|
"bad excess fee");
|
||||||
int numComponents
|
|
||||||
) {
|
|
||||||
check(msg.initialCommitments.length == numComponents, "wrong number of component commitments");
|
|
||||||
check(msg.blindSigRequests.length == numComponents, "wrong number of blind sig requests");
|
|
||||||
|
|
||||||
check(minExcessFee <= msg.excessFee.toInt() && msg.excessFee.toInt() <= maxExcessFee, "bad excess fee");
|
|
||||||
|
|
||||||
check(msg.randomNumberCommitment.length == 32, "bad random commit");
|
check(msg.randomNumberCommitment.length == 32, "bad random commit");
|
||||||
check(msg.pedersenTotalNonce.length == 32, "bad nonce");
|
check(msg.pedersenTotalNonce.length == 32, "bad nonce");
|
||||||
check(msg.blindSigRequests.every((r) => r.length == 32), "bad blind sig request");
|
check(msg.blindSigRequests.every((r) => r.length == 32),
|
||||||
|
"bad blind sig request");
|
||||||
|
|
||||||
List<pb.InitialCommitment> commitMessages = [];
|
List<pb.InitialCommitment> commitMessages = [];
|
||||||
for (var cblob in msg.initialCommitments) {
|
for (var cblob in msg.initialCommitments) {
|
||||||
|
@ -85,11 +83,15 @@ List<pb.InitialCommitment> checkPlayerCommit(
|
||||||
check(cmsg.saltedComponentHash.length == 32, "bad salted hash");
|
check(cmsg.saltedComponentHash.length == 32, "bad salted hash");
|
||||||
var P = cmsg.amountCommitment;
|
var P = cmsg.amountCommitment;
|
||||||
check(P.length == 65 && P[0] == 4, "bad commitment point");
|
check(P.length == 65 && P[0] == 4, "bad commitment point");
|
||||||
check(cmsg.communicationKey.length == 33 && (cmsg.communicationKey[0] == 2 || cmsg.communicationKey[0] == 3), "bad communication key");
|
check(
|
||||||
|
cmsg.communicationKey.length == 33 &&
|
||||||
|
(cmsg.communicationKey[0] == 2 || cmsg.communicationKey[0] == 3),
|
||||||
|
"bad communication key");
|
||||||
commitMessages.add(cmsg);
|
commitMessages.add(cmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List HBytes = Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
Uint8List HBytes =
|
||||||
|
Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
||||||
ECDomainParameters params = ECDomainParameters('secp256k1');
|
ECDomainParameters params = ECDomainParameters('secp256k1');
|
||||||
ECPoint? HMaybe = params.curve.decodePoint(HBytes);
|
ECPoint? HMaybe = params.curve.decodePoint(HBytes);
|
||||||
if (HMaybe == null) {
|
if (HMaybe == null) {
|
||||||
|
@ -102,26 +104,28 @@ List<pb.InitialCommitment> checkPlayerCommit(
|
||||||
var pointsum;
|
var pointsum;
|
||||||
// Verify pedersen commitment
|
// Verify pedersen commitment
|
||||||
try {
|
try {
|
||||||
pointsum = Commitment.add_points(commitMessages.map((m) => Uint8List.fromList(m.amountCommitment)).toList());
|
pointsum = Commitment.add_points(commitMessages
|
||||||
claimedCommit = setup.commit(BigInt.from(msg.excessFee.toInt()), nonce: Util.bytesToBigInt(Uint8List.fromList(msg.pedersenTotalNonce)));
|
.map((m) => Uint8List.fromList(m.amountCommitment))
|
||||||
|
.toList());
|
||||||
|
claimedCommit = setup.commit(BigInt.from(msg.excessFee.toInt()),
|
||||||
|
nonce: Util.bytesToBigInt(Uint8List.fromList(msg.pedersenTotalNonce)));
|
||||||
|
|
||||||
check(pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch");
|
check(pointsum == claimedCommit.PUncompressed,
|
||||||
|
"pedersen commitment mismatch");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw ValidationError("pedersen commitment verification error");
|
throw ValidationError("pedersen commitment verification error");
|
||||||
}
|
}
|
||||||
check(pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch");
|
check(
|
||||||
|
pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch");
|
||||||
return commitMessages;
|
return commitMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Tuple<String, int> checkCovertComponent(
|
Tuple<String, int> checkCovertComponent(
|
||||||
pb.CovertComponent msg, ECPoint roundPubkey, int componentFeerate) {
|
pb.CovertComponent msg, ECPoint roundPubkey, int componentFeerate) {
|
||||||
var messageHash = Util.sha256(Uint8List.fromList(msg.component));
|
var messageHash = Util.sha256(Uint8List.fromList(msg.component));
|
||||||
|
|
||||||
check(msg.signature.length == 64, "bad message signature");
|
check(msg.signature.length == 64, "bad message signature");
|
||||||
check(
|
check(Util.schnorrVerify(roundPubkey, msg.signature, messageHash),
|
||||||
Util.schnorrVerify(
|
|
||||||
roundPubkey, msg.signature, messageHash),
|
|
||||||
"bad message signature");
|
"bad message signature");
|
||||||
|
|
||||||
var cmsg = protoStrictParse(pb.Component(), msg.component);
|
var cmsg = protoStrictParse(pb.Component(), msg.component);
|
||||||
|
@ -133,7 +137,8 @@ Tuple<String, int> checkCovertComponent(
|
||||||
var inp = cmsg.input;
|
var inp = cmsg.input;
|
||||||
check(inp.prevTxid.length == 32, "bad txid");
|
check(inp.prevTxid.length == 32, "bad txid");
|
||||||
check(
|
check(
|
||||||
(inp.pubkey.length == 33 && (inp.pubkey[0] == 2 || inp.pubkey[0] == 3)) ||
|
(inp.pubkey.length == 33 &&
|
||||||
|
(inp.pubkey[0] == 2 || inp.pubkey[0] == 3)) ||
|
||||||
(inp.pubkey.length == 65 && inp.pubkey[0] == 4),
|
(inp.pubkey.length == 65 && inp.pubkey[0] == 4),
|
||||||
"bad pubkey");
|
"bad pubkey");
|
||||||
sortKey = 'i' +
|
sortKey = 'i' +
|
||||||
|
@ -146,8 +151,7 @@ Tuple<String, int> checkCovertComponent(
|
||||||
// Basically just checks if its ok address. should throw error if not.
|
// Basically just checks if its ok address. should throw error if not.
|
||||||
addr = Util.getAddressFromOutputScript(out.scriptpubkey);
|
addr = Util.getAddressFromOutputScript(out.scriptpubkey);
|
||||||
|
|
||||||
check(
|
check(out.amount >= Util.dustLimit(out.scriptpubkey.length), "dust output");
|
||||||
out.amount >= Util.dustLimit(out.scriptpubkey.length), "dust output");
|
|
||||||
sortKey = 'o' +
|
sortKey = 'o' +
|
||||||
out.amount.toString() +
|
out.amount.toString() +
|
||||||
String.fromCharCodes(out.scriptpubkey) +
|
String.fromCharCodes(out.scriptpubkey) +
|
||||||
|
@ -168,8 +172,8 @@ pb.InputComponent? validateProofInternal(
|
||||||
List<int> badComponents,
|
List<int> badComponents,
|
||||||
int componentFeerate,
|
int componentFeerate,
|
||||||
) {
|
) {
|
||||||
|
Uint8List HBytes =
|
||||||
Uint8List HBytes = Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
||||||
ECDomainParameters params = ECDomainParameters('secp256k1');
|
ECDomainParameters params = ECDomainParameters('secp256k1');
|
||||||
ECPoint? HMaybe = params.curve.decodePoint(HBytes);
|
ECPoint? HMaybe = params.curve.decodePoint(HBytes);
|
||||||
if (HMaybe == null) {
|
if (HMaybe == null) {
|
||||||
|
@ -246,8 +250,10 @@ Future<dynamic> validateBlame(
|
||||||
if (decrypter == pb.Blames_BlameProof_Decrypter.privkey) {
|
if (decrypter == pb.Blames_BlameProof_Decrypter.privkey) {
|
||||||
var privkey = Uint8List.fromList(blame.privkey);
|
var privkey = Uint8List.fromList(blame.privkey);
|
||||||
check(privkey.length == 32, 'bad blame privkey');
|
check(privkey.length == 32, 'bad blame privkey');
|
||||||
var privkeyHexStr = Util.bytesToHex(privkey); // Convert bytes to hex string.
|
var privkeyHexStr =
|
||||||
var privkeyBigInt = BigInt.parse(privkeyHexStr, radix: 16); // Convert hex string to BigInt.
|
Util.bytesToHex(privkey); // Convert bytes to hex string.
|
||||||
|
var privkeyBigInt =
|
||||||
|
BigInt.parse(privkeyHexStr, radix: 16); // Convert hex string to BigInt.
|
||||||
var privateKey = ECPrivateKey(privkeyBigInt, params); // Create ECPrivateKey
|
var privateKey = ECPrivateKey(privkeyBigInt, params); // Create ECPrivateKey
|
||||||
var pubkeys = Util.pubkeysFromPrivkey(privkeyHexStr);
|
var pubkeys = Util.pubkeysFromPrivkey(privkeyHexStr);
|
||||||
check(destCommit.communicationKey == pubkeys[1], 'bad blame privkey');
|
check(destCommit.communicationKey == pubkeys[1], 'bad blame privkey');
|
||||||
|
@ -282,11 +288,13 @@ Future<dynamic> validateBlame(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!blame.needLookupBlockchain) {
|
if (!blame.needLookupBlockchain) {
|
||||||
throw ValidationError('blame indicated internal inconsistency, none found!');
|
throw ValidationError(
|
||||||
|
'blame indicated internal inconsistency, none found!');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inpComp == null) {
|
if (inpComp == null) {
|
||||||
throw ValidationError('blame indicated blockchain error on a non-input component');
|
throw ValidationError(
|
||||||
|
'blame indicated blockchain error on a non-input component');
|
||||||
}
|
}
|
||||||
|
|
||||||
return inpComp;
|
return inpComp;
|
||||||
|
|
Loading…
Reference in a new issue