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 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.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(); }