Files
Siro/siro_rider/lib/controller/functions/encrypt_decrypt.dart
2026-06-16 01:17:29 +03:00

124 lines
4.2 KiB
Dart

import 'dart:convert';
import 'dart:math';
import 'package:siro_rider/env/env.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/foundation.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import '../../constant/char_map.dart';
class EncryptionHelper {
static EncryptionHelper? _instance;
late final encrypt.Key key;
late final encrypt.IV iv;
static const String _gcmPrefix = 'GCM:';
EncryptionHelper._(this.key, this.iv);
static EncryptionHelper get instance {
if (_instance == null) {
throw Exception(
"EncryptionHelper is not initialized. Call `await EncryptionHelper.initialize()` in main.");
}
return _instance!;
}
/// Initializes and stores the instance globally
static Future<void> initialize() async {
if (_instance != null) {
debugPrint("EncryptionHelper is already initialized.");
return; // Prevent re-initialization
}
debugPrint("Initializing EncryptionHelper...");
var keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
var initializationVector =
r(Env.initializationVector).toString().split(Env.addd)[0];
// Set the global instance
_instance = EncryptionHelper._(
encrypt.Key.fromUtf8(keyOfApp!),
encrypt.IV.fromUtf8(initializationVector!),
);
debugPrint("EncryptionHelper initialized successfully.");
}
/// ✅ FIX H-04: Encrypts a string using AES-256-GCM (new) with random IV
String encryptData(String plainText) {
try {
// Try GCM first (secure mode with random IV)
final randomIv = _generateRandomIv(12);
// GCM mode in encrypt package handles nonce/IV length automatically (12 bytes recommended)
final gcmEncrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.gcm));
final encrypted = gcmEncrypter.encrypt(plainText, iv: randomIv);
// Prefix with GCM: to distinguish from legacy CBC
return '$_gcmPrefix${randomIv.base64}:${encrypted.base64}';
} catch (e) {
debugPrint('GCM Encryption failed, falling back to CBC: $e');
// Fallback to CBC with a warning
try {
final cbcEncrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = cbcEncrypter.encrypt(plainText, iv: iv);
return encrypted.base64;
} catch (e2) {
debugPrint('CBC Encryption Error: $e2');
return '';
}
}
}
/// ✅ FIX H-04: Decrypts a string (supports both GCM and legacy CBC)
String decryptData(String encryptedText) {
try {
// Check if it's GCM format
if (encryptedText.startsWith(_gcmPrefix)) {
final parts = encryptedText.substring(_gcmPrefix.length).split(':');
if (parts.length != 2) {
debugPrint('Invalid GCM format, falling back to CBC');
return _decryptLegacyCbc(encryptedText);
}
final iv = encrypt.IV.fromBase64(parts[0]);
final encrypted = parts[1];
final gcmEncrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.gcm));
return gcmEncrypter.decrypt(encrypt.Encrypted.fromBase64(encrypted),
iv: iv);
}
// Legacy CBC format
return _decryptLegacyCbc(encryptedText);
} catch (e) {
debugPrint('Decryption Error: $e');
// Try CBC as last resort
try {
return _decryptLegacyCbc(encryptedText);
} catch (_) {
return '';
}
}
}
/// Legacy CBC decryption (backward compatibility)
String _decryptLegacyCbc(String encryptedText) {
final cbcEncrypter =
encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
final encrypted = encrypt.Encrypted.fromBase64(encryptedText);
return cbcEncrypter.decrypt(encrypted, iv: iv);
}
/// Generate cryptographically secure random IV
encrypt.IV _generateRandomIv(int length) {
final random = Random.secure();
final bytes = List<int>.generate(length, (_) => random.nextInt(256));
return encrypt.IV(Uint8List.fromList(bytes));
}
}
r(String string) {
return X.r(X.r(X.r(string, cn), cC), cs).toString();
}
c(String string) {
return X.c(X.c(X.c(string, cn), cC), cs).toString();
}