fix:调整新的中继协议中的上报签名逻辑

This commit is contained in:
liyi 2024-12-02 15:43:59 +08:00
parent a92ae906ef
commit 7a4fbe19d8
4 changed files with 221 additions and 60 deletions

View File

@ -1,19 +1,24 @@
import 'package:crc32_checksum/crc32_checksum.dart';
import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart';
import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart'; import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart';
import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart'; import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart';
import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart';
class MessageCommand { class MessageCommand {
/// 线 /// 线
static List<int> goOnlineRelay() { static List<int> goOnlineRelay({
required String FromPeerId,
required String ToPeerId,
}) {
String serializedBytesString = ScpMessage( String serializedBytesString = ScpMessage(
ProtocolFlag: ProtocolFlagConstant.scp01, ProtocolFlag: ProtocolFlagConstant.scp01,
MessageType: MessageTypeConstant.Req, MessageType: MessageTypeConstant.Req,
MessageId: 1, MessageId: 1,
SpTotal: 0, SpTotal: 0,
SpIndex: 0, SpIndex: 0,
FromPeerId: 'ToPeerId', FromPeerId: FromPeerId,
ToPeerId: 'ToPeerId', ToPeerId: ToPeerId,
Payload: 'hello', Payload: 'hello',
PayloadCRC: 55230, PayloadCRC: 55230,
PayloadLength: 5, PayloadLength: 5,
@ -46,15 +51,18 @@ class MessageCommand {
} }
// //
static List<int> heartbeatMessage() { static List<int> heartbeatMessage({
required String FromPeerId,
required String ToPeerId,
}) {
ScpMessage message = ScpMessage( ScpMessage message = ScpMessage(
ProtocolFlag: ProtocolFlagConstant.scp01, ProtocolFlag: ProtocolFlagConstant.scp01,
MessageType: MessageTypeConstant.Req, MessageType: MessageTypeConstant.Req,
MessageId: 1, MessageId: 1,
SpTotal: 0, SpTotal: 0,
SpIndex: 0, SpIndex: 0,
FromPeerId: 'FromPeerId', FromPeerId: FromPeerId,
ToPeerId: 'ToPeerId', ToPeerId: ToPeerId,
Payload: 'hello', Payload: 'hello',
PayloadCRC: 55230, PayloadCRC: 55230,
PayloadLength: 5, PayloadLength: 5,
@ -73,4 +81,9 @@ class MessageCommand {
} }
return bytes; return bytes;
} }
static int calculationCrc(payload){
var checkSumResult = Crc32.calculate(payload);
return checkSumResult;
}
} }

View File

@ -144,85 +144,135 @@ class ScpMessage {
return bytesToHexString; return bytesToHexString;
} }
//
static ScpMessage deserialize(List<int> bytes) { static ScpMessage deserialize(List<int> bytes) {
final message = ScpMessage(); final message = ScpMessage();
int offset = 0; int offset = 0;
// ProtocolFlag (4 bytes) // ProtocolFlag (4 bytes)
if (bytes.length - offset >= 4) { if (bytes.length - offset >= 4) {
message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4)); message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
offset += 4; offset += 4;
} else {
throw FormatException("Invalid ProtocolFlag length");
} }
// MessageType (1 byte) // MessageType (1 byte)
if (bytes.length - offset >= 1) { if (bytes.length - offset >= 1) {
message.MessageType = bytes[offset]; message.MessageType = bytes[offset];
offset += 1; offset += 1;
} else {
throw FormatException("Invalid MessageType length");
} }
// MessageId (2 bytes, little-endian) // MessageId (2 bytes, little-endian)
if (bytes.length - offset >= 2) { if (bytes.length - offset >= 2) {
message.MessageId = (bytes[offset + 1] << 8) | bytes[offset]; message.MessageId = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2; offset += 2;
} else {
throw FormatException("Invalid MessageId length");
} }
// SpTotal (1 byte) // SpTotal (1 byte)
if (bytes.length - offset >= 1) { if (bytes.length - offset >= 1) {
message.SpTotal = bytes[offset]; message.SpTotal = bytes[offset];
offset += 1; offset += 1;
} else {
throw FormatException("Invalid SpTotal length");
} }
// SpIndex (1 byte) // SpIndex (1 byte)
if (bytes.length - offset >= 1) { if (bytes.length - offset >= 1) {
message.SpIndex = bytes[offset]; message.SpIndex = bytes[offset];
offset += 1; offset += 1;
} else {
throw FormatException("Invalid SpIndex length");
} }
// FromPeerId (44) // FromPeerId (44)
if (bytes.length - offset >= 44) { if (bytes.length - offset >= 44) {
message.FromPeerId = utf8.decode(bytes.sublist(offset, offset + 44)); message.FromPeerId = utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
offset += 44; offset += 44;
} else {
throw FormatException("Invalid FromPeerId length");
} }
// ToPeerId (44) // ToPeerId (44)
if (bytes.length - offset >= 44) { if (bytes.length - offset >= 44) {
message.ToPeerId = utf8.decode(bytes.sublist(offset, offset + 44)); message.ToPeerId = utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
offset += 44; offset += 44;
} else {
throw FormatException("Invalid ToPeerId length");
} }
// PayloadType (2 bytes, little-endian) // PayloadType (2 bytes, little-endian)
if (bytes.length - offset >= 2) { if (bytes.length - offset >= 2) {
message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset]; message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2; offset += 2;
} else {
throw FormatException("Invalid PayloadType length");
} }
// PayloadCRC (2 bytes, little-endian) // PayloadCRC (2 bytes, little-endian)
if (bytes.length - offset >= 2) { if (bytes.length - offset >= 2) {
message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset]; message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2; offset += 2;
} else {
throw FormatException("Invalid PayloadCRC length");
} }
// PayloadLength (4 bytes, big-endian) // PayloadLength (4 bytes, little-endian)
if (bytes.length - offset >= 4) { if (bytes.length - offset >= 4) {
// PayloadLength对应的4个字节
print('PayloadLength bytes: ${bytes.sublist(offset, offset + 4)}');
message.PayloadLength = (bytes[offset] | message.PayloadLength = (bytes[offset] |
(bytes[offset + 1] << 8) | (bytes[offset + 1] << 8) |
(bytes[offset + 2] << 16) | (bytes[offset + 2] << 16) |
(bytes[offset + 3] << 24)); (bytes[offset + 3] << 24)); // little-endian
offset += 4; offset += 4;
} else {
throw FormatException("Invalid PayloadLength length");
} }
// Payload () // Payload ()
if (message.PayloadLength != null && if (message.PayloadLength != null && bytes.length - offset >= message.PayloadLength!) {
bytes.length - offset >= message.PayloadLength!) { message.Payload = utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
message.Payload =
utf8.decode(bytes.sublist(offset, offset + message.PayloadLength!));
offset += message.PayloadLength!; offset += message.PayloadLength!;
} else {
throw FormatException("Invalid Payload or PayloadLength");
} }
// PayloadCRC
// if (message.Payload != null) {
// var crcBytes = List<int>.from(utf8.encode(message.Payload!));
// var calculatedCrc = _calculateCrc16(crcBytes);
// if (calculatedCrc != message.PayloadCRC) {
// throw FormatException("PayloadCRC verification failed. Expected: ${message.PayloadCRC}, Actual: $calculatedCrc");
// }
// }
return message; return message;
} }
// CRC-16
static int _calculateCrc16(List<int> data) {
const poly = 0x8005;
int crc = 0xFFFF;
for (final b in data) {
crc ^= b << 8;
for (int i = 0; i < 8; i++) {
if ((crc & 0x8000) != 0) {
crc = (crc << 1) ^ poly;
} else {
crc <<= 1;
}
crc &= 0xFFFF;
}
}
return crc;
}
static String bytesToHex(List<int> bytes) { static String bytesToHex(List<int> bytes) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(''); return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');

View File

@ -2,11 +2,17 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:convert/convert.dart';
import 'package:cryptography/cryptography.dart';
import 'package:encrypt/encrypt.dart';
import 'package:fast_rsa/fast_rsa.dart' as fastRsa;
import 'package:get/get.dart';
import 'package:pointycastle/asn1/asn1_parser.dart'; import 'package:pointycastle/asn1/asn1_parser.dart';
import 'package:pointycastle/asn1/primitives/asn1_integer.dart'; import 'package:pointycastle/asn1/primitives/asn1_integer.dart';
import 'package:pointycastle/asn1/primitives/asn1_sequence.dart'; import 'package:pointycastle/asn1/primitives/asn1_sequence.dart';
import 'package:pointycastle/asymmetric/api.dart'; import 'package:pointycastle/asymmetric/api.dart';
import 'package:pointycastle/digests/sha256.dart'; import 'package:pointycastle/digests/sha256.dart';
import 'package:pointycastle/export.dart' as pc;
import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/flavors.dart'; import 'package:star_lock/flavors.dart';
import 'package:star_lock/network/start_chart_api.dart'; import 'package:star_lock/network/start_chart_api.dart';
@ -55,11 +61,11 @@ class StartChartManage {
String ToPeerId = ''; // ID String ToPeerId = ''; // ID
String FromPeerId = ''; // ID String FromPeerId = ''; // ID
bool _isLoginSuccessfulToStartChart = false; // final String echoPeerId = '3phX8Ng2cZHz5NtP8xAf6nYy2z1BYytoejgjoHrWMGhH';
// //
Future<void> init() async { Future<void> init() async {
// //
await _clientRegister(); await _clientRegister();
// //
@ -70,12 +76,6 @@ class StartChartManage {
// udp服务 // udp服务
await _onlineRelayService(); await _onlineRelayService();
//
_sendHeartbeatMessage();
// 线
await _sendOnlineMessage();
} }
/// ///
@ -85,6 +85,8 @@ class StartChartManage {
await _requestStarChartRegisterNode(); await _requestStarChartRegisterNode();
await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode); await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode);
_log(text: '获取到星图注册节点信息:$requestStarChartRegisterNode'); _log(text: '获取到星图注册节点信息:$requestStarChartRegisterNode');
FromPeerId = requestStarChartRegisterNode.peer!.id ?? '';
ToPeerId = echoPeerId;
} }
// //
@ -98,12 +100,11 @@ class StartChartManage {
if (relayInfoEntity.relay_list?.length != 0) { if (relayInfoEntity.relay_list?.length != 0) {
final data = relayInfoEntity.relay_list?[0]; final data = relayInfoEntity.relay_list?[0];
FromPeerId = data?.peerID ?? '';
final parseUdpUrl = _parseUdpUrl(data?.listenAddr ?? ''); final parseUdpUrl = _parseUdpUrl(data?.listenAddr ?? '');
remoteHost = parseUdpUrl['host'] ?? ''; remoteHost = parseUdpUrl['host'] ?? '';
remotePort = parseUdpUrl['port'] ?? ''; remotePort = parseUdpUrl['port'] ?? '';
_log(text: '中继信息----》${data}');
} }
_log(text: '中继信息----》${relayInfoEntity}');
} }
void closeUdpSocket() { void closeUdpSocket() {
@ -138,6 +139,7 @@ class StartChartManage {
_log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}'); _log(text: '收到消息---> 长度:${dg?.data?.length}, 数据:${dg?.data}');
if (dg?.data != null) { if (dg?.data != null) {
final deserialize = ScpMessage.deserialize(dg!.data); final deserialize = ScpMessage.deserialize(dg!.data);
_log(text: '=============${bytesToHex(dg!.data)}');
_log(text: 'Udp收到结构体数据---》$deserialize'); _log(text: 'Udp收到结构体数据---》$deserialize');
} }
} catch (e) { } catch (e) {
@ -156,15 +158,19 @@ class StartChartManage {
reportInformationData: data, reportInformationData: data,
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
// TODO
_log(text: '星图登录成功'); _log(text: '星图登录成功');
// 线
await _sendOnlineMessage();
//
_sendHeartbeatMessage();
} }
} }
// 线 // 线
Future<void> _sendOnlineMessage() async { Future<void> _sendOnlineMessage() async {
// 线 // 线
final message = MessageCommand.goOnlineRelay(); final message = MessageCommand.goOnlineRelay(
FromPeerId: FromPeerId, ToPeerId: ToPeerId);
await _sendMessage(message: message); await _sendMessage(message: message);
} }
@ -178,7 +184,10 @@ class StartChartManage {
} }
// //
void _sendHeartbeatMessage() { void _sendHeartbeatMessage() async {
//
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
final String relayPeerId = relayInfoEntity?.relay_list?[0].peerID ?? '';
if (_heartBeatTimerRunning) { if (_heartBeatTimerRunning) {
_log(text: '心跳已经开始了. 请勿重复发送心跳包消息'); _log(text: '心跳已经开始了. 请勿重复发送心跳包消息');
return; return;
@ -188,7 +197,10 @@ class StartChartManage {
seconds: heartbeatIntervalTime, seconds: heartbeatIntervalTime,
), ),
(Timer timer) async { (Timer timer) async {
final List<int> message = MessageCommand.heartbeatMessage(); final List<int> message = MessageCommand.heartbeatMessage(
FromPeerId: FromPeerId,
ToPeerId: relayPeerId,
);
await _sendMessage(message: message); await _sendMessage(message: message);
}, },
); );
@ -248,15 +260,16 @@ class StartChartManage {
// //
Future<ReportInformationData> _makeReportInformationData() async { Future<ReportInformationData> _makeReportInformationData() async {
// //
int currentTimestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
// //
final publicKey = await getPublicKey(); final publicKey = await getPublicKey();
// //
final privateKey = await getPrivateKey(); final privateKey = await getPrivateKey();
// //
final sign = await _generateSign( final sign = await _generateSign(
currentTimestamp: currentTimestamp, currentTimestamp: relayInfoEntity!.time ?? 0,
privateKeyHex: privateKey, privateKeyHex: privateKey,
); );
@ -264,8 +277,7 @@ class StartChartManage {
final List<ListenAddrData> listenAddrDataList = final List<ListenAddrData> listenAddrDataList =
await _makeListenAddrDataList(); await _makeListenAddrDataList();
// //
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
final RelayServiceData relayServiceData = RelayServiceData( final RelayServiceData relayServiceData = RelayServiceData(
name: relayInfoEntity?.relay_list?[0].name ?? '', name: relayInfoEntity?.relay_list?[0].name ?? '',
listen_addr: relayInfoEntity?.relay_list?[0].listenAddr ?? '', listen_addr: relayInfoEntity?.relay_list?[0].listenAddr ?? '',
@ -278,7 +290,7 @@ class StartChartManage {
public_key: publicKey, public_key: publicKey,
listen_addr: listenAddrDataList, listen_addr: listenAddrDataList,
relay_service: relayServiceData, relay_service: relayServiceData,
time: currentTimestamp, time: relayInfoEntity.time ?? 0,
sign: sign, sign: sign,
); );
@ -289,7 +301,7 @@ class StartChartManage {
Future<List<ListenAddrData>> _makeListenAddrDataList() async { Future<List<ListenAddrData>> _makeListenAddrDataList() async {
final List<ListenAddrData> listenAddrDataList = []; final List<ListenAddrData> listenAddrDataList = [];
final List<String> localIp = await _getAllIpAddresses(); final List<String> localIp = await _getAllIpAddresses();
// // ip地址
final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo(); final RelayInfoEntity? relayInfoEntity = await Storage.getRelayInfo();
if (relayInfoEntity != null && relayInfoEntity.client_addr != null) { if (relayInfoEntity != null && relayInfoEntity.client_addr != null) {
listenAddrDataList.add( listenAddrDataList.add(
@ -313,7 +325,7 @@ class StartChartManage {
return listenAddrDataList ?? []; return listenAddrDataList ?? [];
} }
/// ip /// IP
Future<List<String>> _getAllIpAddresses() async { Future<List<String>> _getAllIpAddresses() async {
final List<String> ipAddresses = []; final List<String> ipAddresses = [];
try { try {
@ -324,9 +336,21 @@ class StartChartManage {
for (final interface in interfaces) { for (final interface in interfaces) {
for (final address in interface.addresses) { for (final address in interface.addresses) {
if (address.address.isNotEmpty && // IP
!IpConstant.reportExcludeIp.contains(address.address)) { String ipAddress = address.address;
ipAddresses.add(address.address);
// URL
ipAddress = Uri.decodeFull(ipAddress);
// IPv6
if (ipAddress.contains('%')) {
ipAddress = ipAddress.split('%').first;
}
// IP
if (ipAddress.isNotEmpty &&
!IpConstant.reportExcludeIp.contains(ipAddress)) {
ipAddresses.add(ipAddress);
} }
} }
} }
@ -376,30 +400,104 @@ class StartChartManage {
}) async { }) async {
String resultSign = ''; String resultSign = '';
try { try {
// 1. // 1. 32
Uint8List signData = Uint8List(4); Uint8List signData = encodeTimestampToLittleEndianBytes(currentTimestamp);
ByteData.view(signData.buffer)
.setUint32(0, currentTimestamp, Endian.little);
// 2. SHA-256 // 2.
Digest hash = sha256.convert(signData); List<int> privateKeyBytes = hexToBytes(privateKeyHex);
// 3. 使 RSA ( RsaPrivateKey) // 3. PEM
pc.RSAPrivateKey privateKey = final pemPrivateKey =
loadPrivateKey(privateKeyHex); // RSA convertToPemPrivateKey(privateKeyBytes, isPKCS8: true);
Uint8List signature = rsaSign(privateKey, hash.bytes); // 4.
var result = await fastRsa.RSA
// 4. .signPKCS1v15Bytes(signData, fastRsa.Hash.SHA256, pemPrivateKey);
String hexSignature = signature resultSign = hex.encode(result);
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
.join();
resultSign = hexSignature;
} catch (e) { } catch (e) {
_log(text: '❌--->上报信息生成签名时出现错误: $e'); _log(text: '❌--->上报信息生成签名时出现错误: $e');
e.printError();
} }
return resultSign ?? ''; return resultSign ?? '';
} }
// 32
Uint8List encodeTimestampToLittleEndianBytes(int timestamp) {
// 4 ByteData
ByteData byteData = ByteData(4);
// 32 ByteData使
byteData.setUint32(0, timestamp, Endian.little);
// ByteData Uint8List
Uint8List bytes = byteData.buffer.asUint8List();
return bytes;
}
// PEM
String convertToPemPublicKey(List<int> publicKeyBytes,
{bool isPKCS8 = true}) {
// Base64编码的字符串
String base64PublicKey = base64Encode(publicKeyBytes);
// PEM格式的头尾标签
String pemHeader;
String pemFooter;
if (isPKCS8) {
// PEM格式的头尾标签
pemHeader = "-----BEGIN PUBLIC KEY-----";
pemFooter = "-----END PUBLIC KEY-----";
} else {
// PEM格式的头尾标签
pemHeader = "-----BEGIN RSA PUBLIC KEY-----";
pemFooter = "-----END RSA PUBLIC KEY-----";
}
// Base64字符串分行为每行64个字符
const lineLength = 64;
List<String> lines = [];
for (int i = 0; i < base64PublicKey.length; i += lineLength) {
int end = (i + lineLength < base64PublicKey.length)
? i + lineLength
: base64PublicKey.length;
lines.add(base64PublicKey.substring(i, end));
}
// PEM格式字符串
return "$pemHeader\n${lines.join('\n')}\n$pemFooter";
}
String convertToPemPrivateKey(List<int> privateKeyBytes,
{bool isPKCS8 = true}) {
// Base64编码的字符串
String base64PrivateKey = base64Encode(privateKeyBytes);
// PEM格式的头尾标签
String pemHeader;
String pemFooter;
if (isPKCS8) {
pemHeader = "-----BEGIN PRIVATE KEY-----";
pemFooter = "-----END PRIVATE KEY-----";
} else {
pemHeader = "-----BEGIN RSA PRIVATE KEY-----";
pemFooter = "-----END RSA PRIVATE KEY-----";
}
// Base64字符串分行为每行64个字符
const lineLength = 64;
List<String> lines = []; //
for (int i = 0; i < base64PrivateKey.length; i += lineLength) {
int end = (i + lineLength < base64PrivateKey.length)
? i + lineLength
: base64PrivateKey.length;
lines.add(base64PrivateKey.substring(i, end));
}
// PEM格式字符串
return "$pemHeader\n${lines.join('\n')}\n$pemFooter";
}
/// PEM RSA /// PEM RSA
pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) { pc.RSAPrivateKey loadPrivateKey(String privateKeyHex) {
// //

View File

@ -250,7 +250,7 @@ dependencies:
crc32_checksum: ^0.0.2 crc32_checksum: ^0.0.2
cryptography: ^2.7.0 cryptography: ^2.7.0
asn1lib: ^1.0.0 asn1lib: ^1.0.0
fast_rsa: ^3.6.6
dependency_overrides: dependency_overrides:
#强制设置google_maps_flutter_ios 为 2.5.2 #强制设置google_maps_flutter_ios 为 2.5.2