service 2-5-26-2
This commit is contained in:
@@ -2,12 +2,14 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:service/constant/box_name.dart';
|
||||
import 'package:service/constant/links.dart';
|
||||
import 'package:service/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:service/env/env.dart';
|
||||
import 'package:service/controller/functions/security_helper.dart';
|
||||
import 'package:service/main.dart';
|
||||
import 'package:service/print.dart';
|
||||
|
||||
@@ -15,6 +17,8 @@ import '../../constant/api_key.dart';
|
||||
|
||||
class CRUD {
|
||||
static bool _isRefreshingJWT = false;
|
||||
static String? _appSignature;
|
||||
|
||||
static String _lastErrorSignature = '';
|
||||
static DateTime _lastErrorTimestamp = DateTime(2000);
|
||||
static const Duration _errorLogDebounceDuration = Duration(minutes: 1);
|
||||
@@ -81,6 +85,22 @@ class CRUD {
|
||||
return box.read(BoxName.fingerPrint)?.toString() ?? '';
|
||||
}
|
||||
|
||||
String _generateHmac(String body, String timestamp, String nonce) {
|
||||
// نستخدم المفتاح الخاص بالمستخدم (المخزن في البوكس) كـ HMAC Secret
|
||||
final hmacSecret = box.read(BoxName.hmac) ?? '';
|
||||
|
||||
final payload = body + timestamp + nonce;
|
||||
final key = utf8.encode(hmacSecret);
|
||||
final bytes = utf8.encode(payload);
|
||||
final hmacSha256 = Hmac(sha256, key);
|
||||
final result = hmacSha256.convert(bytes).toString();
|
||||
Log.print('🔐 [HMAC-DEBUG] Secret: $hmacSecret');
|
||||
Log.print('🔐 [HMAC-DEBUG] Body(${body.length}): "$body"');
|
||||
Log.print('🔐 [HMAC-DEBUG] TS: $timestamp | Nonce: $nonce');
|
||||
Log.print('🔐 [HMAC-DEBUG] Result: $result');
|
||||
return result;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// _makeRequest — Central Request Handler
|
||||
// ───────────────────────────────────────────────────────────────
|
||||
@@ -91,6 +111,28 @@ class CRUD {
|
||||
}) async {
|
||||
const totalTimeout = Duration(seconds: 60);
|
||||
|
||||
// توليد بيانات الـ HMAC للطلب الحالي
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
final nonce =
|
||||
DateTime.now().microsecondsSinceEpoch.toString(); // Nonce فريد
|
||||
|
||||
// تحويل الـ payload إلى string لمحاكة ما سيصل للسيرفر (php://input)
|
||||
String bodyString = '';
|
||||
if (payload != null && payload.isNotEmpty) {
|
||||
// الـ http.post يرسل البيانات كـ x-www-form-urlencoded
|
||||
bodyString = payload.keys
|
||||
.map((key) =>
|
||||
"$key=${Uri.encodeQueryComponent(payload[key].toString())}")
|
||||
.join("&");
|
||||
}
|
||||
|
||||
final hmacSignature = _generateHmac(bodyString, timestamp, nonce);
|
||||
|
||||
// إضافة هيدرات الـ HMAC
|
||||
headers['X-HMAC-Auth'] = hmacSignature;
|
||||
headers['X-Timestamp'] = timestamp;
|
||||
headers['X-Nonce'] = nonce;
|
||||
|
||||
Future<http.Response> doPost() {
|
||||
final url = Uri.parse(link);
|
||||
return http
|
||||
@@ -105,9 +147,9 @@ class CRUD {
|
||||
|
||||
Log.print('🚀 [REQ-$requestId] $link');
|
||||
Log.print('🔑 [FP-$requestId] ${headers['X-Device-FP']}');
|
||||
Log.print('🔏 [SIGN-$requestId] ${headers['X-App-Signature']}');
|
||||
if (payload != null) Log.print('📦 [PAYLOAD-$requestId] $payload');
|
||||
|
||||
|
||||
while (attempts < 3) {
|
||||
try {
|
||||
attempts++;
|
||||
@@ -190,10 +232,14 @@ class CRUD {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize app signature if null
|
||||
_appSignature ??= await SecurityHelper.getAppSignature();
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Bearer $token',
|
||||
'X-Device-FP': _getFpHeader(),
|
||||
'X-App-Signature': _appSignature ?? '',
|
||||
};
|
||||
|
||||
return await _makeRequest(link: link, payload: payload, headers: headers);
|
||||
@@ -219,8 +265,12 @@ class CRUD {
|
||||
'aud': 'service',
|
||||
};
|
||||
|
||||
// Initialize app signature if null
|
||||
_appSignature ??= await SecurityHelper.getAppSignature();
|
||||
|
||||
final headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-App-Signature': _appSignature ?? '',
|
||||
};
|
||||
|
||||
final response = await _makeRequest(
|
||||
@@ -230,8 +280,17 @@ class CRUD {
|
||||
response is Map &&
|
||||
response['status'] == 'success') {
|
||||
final jwt = response['message']['jwt'];
|
||||
final hmac = response['message']['hmac'];
|
||||
|
||||
Log.print('jwt: $jwt');
|
||||
box.write(BoxName.jwt, c(jwt));
|
||||
Log.print('hmac_key: $hmac');
|
||||
|
||||
await box.write(BoxName.jwt, c(jwt));
|
||||
if (hmac != null) {
|
||||
await box.write(BoxName.hmac, hmac);
|
||||
final verify = box.read(BoxName.hmac);
|
||||
Log.print('✅ Verified stored HMAC: $verify');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
lib/controller/functions/security_helper.dart
Normal file
32
lib/controller/functions/security_helper.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:service/print.dart';
|
||||
|
||||
class SecurityHelper {
|
||||
static const platform = MethodChannel('com.service_intaleq/security');
|
||||
|
||||
static Future<String?> getAppSignature() async {
|
||||
try {
|
||||
final String? signature = await platform.invokeMethod('getAppSignature');
|
||||
final mode = kDebugMode ? 'DEBUG' : 'RELEASE';
|
||||
Log.print('----------------------------------------------------');
|
||||
Log.print('🚀 APP SIGNATURE HASH ($mode): $signature');
|
||||
Log.print('----------------------------------------------------');
|
||||
return signature;
|
||||
|
||||
} on PlatformException catch (e) {
|
||||
Log.print('❌ Failed to get app signature: ${e.message}');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> isDeviceRooted() async {
|
||||
try {
|
||||
final bool isRooted = await platform.invokeMethod('isNativeRooted');
|
||||
return isRooted;
|
||||
} on PlatformException catch (e) {
|
||||
Log.print('❌ Failed to check root: ${e.message}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user